diff --git a/.mailmap b/.mailmap new file mode 100644 index 0000000000000..34c3d7321c824 --- /dev/null +++ b/.mailmap @@ -0,0 +1,146 @@ +Adrian-Constantin Popescu +Alex Blewitt +Alex Hoppen +Alex Hoppen > +Alexis Beingessner +Alper Çugun +Amr Aboelela +Ankit Aggarwal +Argyrios Kyrtzidis +Arsen Gasparyan +Ben Cohen +Ben Cohen +Ben Cohen +Ben Langmuir +Brent Royal-Gordon +Brian Croom +Brian Gesiak +Bryan Chan +Calvin Hill +Chris Bieneman +Chris Bieneman +Chris Lattner +Chris Lattner +Chris Lattner +Chris Williams +codester codestergit +Dan Liew <36706441+danliew-apple@users.noreply.github.com> +Daniel Duan +Dante Broggi <34220985+Dante-Broggi@users.noreply.github.com> +Dave +Dave Abrahams +Dave Abrahams +Dave Abrahams +Dave Abrahams +Dave Lee +David Rönnqvist +David Rönnqvist +David Ungar +David Zarzycki +David Zarzycki +David Zarzycki +Davide Italiano +Davide Italiano +Dmitri Gribenko +Doug Coleman +Enrico Granata +Enrico Granata +Erik Eckstein +Erik Eckstein +Erik Verbruggen +Ewa Matejska +Ewa Matejska +Ewa Matejska +Florent Bruneau +Francis Ricci +GauravDS +Graydon Hoare +Greg Parker +Greg Titus +Guillaume Lessard +Hamish +Han Sangjin +Harlan Haskins +Harlan Haskins +Hitster GTD +Huon Wilson +Ingmar Stein +Itai Ferber +Jacob Bandes-Storch +Jacob Mizraji +Janosch Hildebrand +Janosch Hildebrand +Javier Soto +Javier Soto +Joe +Joe +joe DeCapo +Joe Groff +Joe Shajrawi +Joe Shajrawi +Johannes Weiß +John Regner +Karoy Lorentey +Keith Smiley +Kevin Ballard +Kevin Saldaña +Kim Topley +Kosuke Ogawa +Kuba Mracek +Kuba Mracek +Luiz Fernando Silva +Luqman Aden +Marcelo Fabri +Mark Lacey +Mark Lacey +Matt Rajca +Max Moiseev +Max Moiseev +Max Moiseev +Maxwell Swadling +Maxwell Swadling +Mayur Raiturkar +Michael Gottesman +Michael Ilseman +Mike Ash +Mike Ferris +Mishal Awadah +Mishal Shah +Mishal Shah +Nadav Rotem +Nate Cook +Nate Cook +Nate Cook +Nate Cook +Nathan Lanza +Nicole Jacque +Niels Andriesse +Paul Meng +Pavel Yaskevich +Paweł Szot +Paweł Szot +Pete Cooper +Philip Ridgeway +Richard Wei +Rintaro Ishizaki +Robert Widmann +Roman Levenstein +Ross Bayer +Ross Bayer +Russ Bishop +Ryan Lovelett +Shawn Erickson +Slava Pestov +Slava Pestov +Stephen Canon +Stephen Canon +Sukolsak Sakshuwong +Todd Fiala +Toni Suter +Vedant Kumar +Xi Ge +Xin Tong +Xin Tong +Yuka Ezura <2020337+ezura@users.noreply.github.com> +Yurii Samsoniuk +Zac Bowling diff --git a/CHANGELOG.md b/CHANGELOG.md index feed20444999e..82b55061a754c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,7 +33,7 @@ Swift 5.0 class Node { var children = [Node]() - var depth: Int { + var depth: Int = 0 { didSet { if depth < 0 { // Will not recursively call didSet, as setting depth on self (same @@ -49,10 +49,6 @@ Swift 5.0 } } } - - init(depth: Int) { - self.depth = depth - } } ``` @@ -61,6 +57,147 @@ Swift 5.0 Swift 4.2 --------- +* [SE-0185][] + + Protocol conformances are now able to be synthesized in extensions in the same + file as the type definition, allowing automatic synthesis of conditional + conformances to `Hashable`, `Equatable` and `Codable` (both `Encodable` and + `Decodable`). For instance, if there is a generic wrapper type that can only + be `Equatable` when its wrapped type is also `Equatable`, the `==` method can + be automatically constructed by the compiler: + + ```swift + struct Generic { + var property: Param + } + + extension Generic: Equatable where Param: Equatable {} + // Automatically synthesized inside the extension: + // static func ==(lhs: Generic, rhs: Generic) -> Bool { + // return lhs.property == rhs.property + // } + ``` + + Code that wants to be as precise as possible should generally not + conditionally conform to `Codable` directly, but rather its two constituent + protocols `Encodable` and `Decodable`, or else one can only (for instance) + decode a `Generic` if `Param` is `Encodable` in addition to + `Decodable`, even though `Encodable` is likely not required: + + ```swift + // Unnecessarily restrictive: + extension Generic: Codable where Param: Codable {} + + // More precise: + extension Generic: Encodable where Param: Encodable {} + extension Generic: Decodable where Param: Decodable {} + ``` + + Finally, due to `Decodable` having an `init` requirement, it is not possible + to conform to `Decodable` in an extension of a non-final class: such a class + needs to have any `init`s from protocols be `required`, which means they need + to be in the class definition. + + +* [SE-0054][] + + `ImplicitlyUnwrappedOptional` is now an unavailable typealias of `Optional`. + Declarations annotated with `!` have the type `Optional`. If an + expression involving one of these values will not compile successfully with the + type `Optional`, it is implicitly unwrapped, producing a value of type `T`. + + In some cases this change will cause code that previously compiled to + need to be adjusted. Please see [this blog post](https://swift.org/blog/iuo/) + for more information. + +* [SE-0206][] + + The standard library now uses a high-quality, randomly seeded, universal + hash function, represented by the new public `Hasher` struct. + + “Random seeding” varies the result of `hashValue` on each execution of a + Swift program, improving the reliability of the standard library's hashed + collections such as `Set` and `Dictionary`. In particular, random seeding + enables better protection against (accidental or deliberate) hash-flooding + attacks. + + This change fulfills a long-standing prophecy in Hashable's documentation: + + > Hash values are not guaranteed to be equal across different executions of + > your program. Do not save hash values to use during a future execution. + + As a consequence of random seeding, the elements in `Set` and `Dictionary` + values may have a different order on each execution. This may expose some + bugs in existing code that accidentally relies on repeatable ordering. + + Additionally, the `Hashable` protocol now includes an extra function + requirement, `hash(into:)`. The new requirement is designed to be much + easier to implement than the old `hashValue` property, and it generally + provides better hashing. To implement `hash(into:)`, simply feed the exact + same components of your type that you compare in `Equatable`'s `==` + implementation to the supplied `Hasher`: + + ```swift + struct Foo: Hashable { + var a: String? + var b: [Int] + var c: [String: Int] + + static func ==(lhs: Foo, rhs: Foo) -> Bool { + return lhs.a == rhs.a && lhs.b == rhs.b && lhs.c == rhs.c + } + + func hash(into hasher: inout Hasher) { + hasher.combine(a) + hasher.combine(b) + hasher.combine(c) + } + } + ``` + + Automatic synthesis for `Hashable` ([SE-0185]) has been updated to generate + `hash(into:)` implementations. For example, the `==` and `hash(into:)` + implementations above are equivalent to the ones synthesized by the + compiler, and can be removed without changing the meaning of the code. + + Synthesis has also been extended to support deriving `hashValue` from + `hash(into:)`, and vice versa. Therefore, code that only implements + `hashValue` continues to work in Swift 4.2. This new compiler functionality + works for all types that can implement `Hashable`, including classes. + + Note that these changes don't affect Foundation's hashing interface. Classes + that subclass `NSObject` should override the `hash` property, like before. + + In certain controlled environments, such as while running particular tests, + it may be helpful to selectively disable hash seed randomization, so that + hash values and the order of elements in `Set`/`Dictionary` values remain + consistent across executions. You can disable hash seed randomization by + defining the environment variable `SWIFT_DETERMINISTIC_HASHING` with the + value of `1`. The Swift runtime looks at this variable during process + startup and, if it is defined, replaces the random seed with a constant + value. + +* [SR-106][] + + The behavior of `.description` and `.debugDescription` for floating-point + numbers has been changed. Previously these unconditionally printed a fixed + number of decimal digits (e.g. 15 and 17 for Double, respectively). They now + print exactly as many digits as are needed for the resulting string to + convert back to the original source value, and no more. For more details, + see the original bug report and the linked pull request. + +* [SE-0193][] + + Various function-like declarations can now be marked as `@inlinable`, + making their bodies available for optimizations from other modules. + + Inlinable function bodies must only reference public declarations, unless + the referenced declaration is marked as `@usableFromInline`. + + Note that the presence of the attribute itself does not force inlining or + any other optimization to be performed, nor does it have any effect on + optimizations performed within a single module. + * The C `long double` type is now imported as `Float80` on i386 and x86_64 macOS and Linux. The tgmath functions in the Darwin and glibc modules now  support `Float80` as well as `Float` and `Double`. Several tgmath @@ -103,16 +240,18 @@ Swift 4.2 * [SE-0143][] - Runtime query of conditional conformances is now implemented. Therefore, - a dynamic cast such as `value as? P`, where the dynamic type of `value` - conditionally conforms to `P`, will succeed when the conditional - requirements are met. + Runtime query of conditional conformances is now implemented. Therefore, + a dynamic cast such as `value as? P`, where the dynamic type of `value` + conditionally conforms to `P`, will succeed when the conditional + requirements are met. **Add new entries to the top of this section, not here!** Swift 4.1 --------- +### 2018-03-29 (Xcode 9.3) + * [SE-0075][] Compile-time testing for the existence and importability of modules is now @@ -6968,7 +7107,21 @@ Swift 1.0 [SE-0197]: [SE-0198]: [SE-0199]: - +[SE-0200]: +[SE-0201]: +[SE-0202]: +[SE-0203]: +[SE-0204]: +[SE-0205]: +[SE-0206]: +[SE-0207]: +[SE-0208]: +[SE-0209]: +[SE-0210]: +[SE-0211]: +[SE-0212]: + +[SR-106]: [SR-419]: [SR-1009]: [SR-1446]: diff --git a/CMakeLists.txt b/CMakeLists.txt index 310aea1b10e16..e01876df78104 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -180,6 +180,9 @@ option(SWIFT_FORCE_OPTIMIZED_TYPECHECKER "Override the optimization setting of # User-configurable Android specific options. # +set(SWIFT_ANDROID_API_LEVEL "" CACHE STRING + "Version number for the Android API") + set(SWIFT_ANDROID_NDK_PATH "" CACHE STRING "Path to the directory that contains the Android NDK tools that are executable on the build machine") set(SWIFT_ANDROID_NDK_GCC_VERSION "" CACHE STRING @@ -311,6 +314,10 @@ option(SWIFT_ENABLE_RUNTIME_FUNCTION_COUNTERS "Enable runtime function counters and expose the API." FALSE) +option(SWIFT_ENABLE_STDLIBCORE_EXCLUSIVITY_CHECKING + "Build stdlibCore with exclusivity checking enabled" + FALSE) + # # End of user-configurable options. # @@ -563,7 +570,7 @@ endif() # If SWIFT_HOST_VARIANT_ARCH not given, try to detect from the CMAKE_SYSTEM_PROCESSOR. if(SWIFT_HOST_VARIANT_ARCH) - set(SWIFT_HOST_VARIANT_ARCH_default, "${SWIFT_HOST_VARIANT_ARCH}") + set(SWIFT_HOST_VARIANT_ARCH_default "${SWIFT_HOST_VARIANT_ARCH}") else() if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86_64") set(SWIFT_HOST_VARIANT_ARCH_default "x86_64") @@ -750,20 +757,6 @@ endif() # Should we cross-compile the standard library for Android? is_sdk_requested(ANDROID swift_build_android) if(swift_build_android AND NOT "${SWIFT_ANDROID_NDK_PATH}" STREQUAL "") - # Get the prebuilt suffix to create the correct toolchain path when using the NDK - if("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin") - set(_swift_android_prebuilt_suffix "darwin-x86_64") - elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") - set(_swift_android_prebuilt_suffix "linux-x86_64") - endif() - - set(SWIFT_ANDROID_PREBUILT_PATH - "${SWIFT_ANDROID_NDK_PATH}/toolchains/arm-linux-androideabi-${SWIFT_ANDROID_NDK_GCC_VERSION}/prebuilt/${_swift_android_prebuilt_suffix}") - - # Resolve the correct linker based on the file name of CMAKE_LINKER (being 'ld' or 'ld.gold' the options) - get_filename_component(SWIFT_ANDROID_LINKER_NAME "${CMAKE_LINKER}" NAME) - set(SWIFT_SDK_ANDROID_ARCH_armv7_LINKER - "${SWIFT_ANDROID_NDK_PATH}/toolchains/arm-linux-androideabi-${SWIFT_ANDROID_NDK_GCC_VERSION}/prebuilt/${_swift_android_prebuilt_suffix}/bin/arm-linux-androideabi-${SWIFT_ANDROID_LINKER_NAME}") configure_sdk_unix(ANDROID "Android" "android" "android" "armv7" "armv7-none-linux-androideabi" "${SWIFT_ANDROID_SDK_PATH}") @@ -847,8 +840,35 @@ endif() # # Find required dependencies. # -if(NOT CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND "${SWIFT_PATH_TO_LIBICU_BUILD}" STREQUAL "") - find_package(ICU REQUIRED COMPONENTS uc i18n) + +function(swift_icu_variables_set sdk arch result) + string(TOUPPER "${sdk}" sdk) + + set(icu_var_ICU_UC_INCLUDE ${SWIFT_${sdk}_${arch}_ICU_UC_INCLUDE}) + set(icu_var_ICU_UC ${SWIFT_${sdk}_${arch}_ICU_UC}) + set(icu_var_ICU_I18N_INCLUDE ${SWIFT_${sdk}_${arch}_ICU_I18N_INCLUDE}) + set(icu_var_ICU_I18N ${SWIFT_${sdk}_${arch}_ICU_I18N}) + + if(icu_var_ICU_UC_INCLUDE AND icu_var_ICU_UC AND + icu_var_ICU_I18N_INCLUDE AND icu_var_ICU_I18N) + set(${result} TRUE PARENT_SCOPE) + else() + set(${result} FALSE PARENT_SCOPE) + endif() +endfunction() + +# ICU is provided through CoreFoundation on Darwin. On other hosts, assume that +# we are compiling for the build as the host. In such a case, if the ICU +# unicode and i18n include and library paths are not defined, perform a standard +# package lookup. Otherwise, rely on the paths specified by the user. These +# need to be defined when cross-compiling. +if(NOT CMAKE_SYSTEM_NAME STREQUAL "Darwin") + swift_icu_variables_set("${SWIFT_HOST_VARIANT_SDK_default}" + "${SWIFT_HOST_VARIANT_ARCH_default}" + ICU_CONFIGURED) + if("${SWIFT_PATH_TO_LIBICU_BUILD}" STREQUAL "" AND NOT ${ICU_CONFIGURED}) + find_package(ICU REQUIRED COMPONENTS uc i18n) + endif() endif() find_package(PythonInterp REQUIRED) diff --git a/README.md b/README.md index 3e44e9da6b60d..b4fa3d503aaa1 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,23 @@ # Swift Programming Language -| | **Swift** | **Package** | + +| | **Architecture** | **Master** | **Package** | +|---|:---:|:---:|:---:| +| **macOS** | x86_64 |[![Build Status](https://ci.swift.org/job/oss-swift-incremental-RA-osx/lastCompletedBuild/badge/icon)](https://ci.swift.org/job/oss-swift-incremental-RA-osx)|[![Build Status](https://ci.swift.org/job/oss-swift-package-osx/lastCompletedBuild/badge/icon)](https://ci.swift.org/job/oss-swift-package-osx)| +| **Ubuntu 14.04** | x86_64 | [![Build Status](https://ci.swift.org/job/oss-swift-incremental-RA-linux-ubuntu-14_04/lastCompletedBuild/badge/icon)](https://ci.swift.org/job/oss-swift-incremental-RA-linux-ubuntu-14_04)|[![Build Status](https://ci.swift.org/job/oss-swift-package-linux-ubuntu-14_04/lastCompletedBuild/badge/icon)](https://ci.swift.org/job/oss-swift-package-linux-ubuntu-14_04)| +| **Ubuntu 16.04** | x86_64 | [![Build Status](https://ci.swift.org/job/oss-swift-incremental-RA-linux-ubuntu-16_04/lastCompletedBuild/badge/icon)](https://ci.swift.org/job/oss-swift-incremental-RA-linux-ubuntu-16_04)|[![Build Status](https://ci.swift.org/job/oss-swift-package-linux-ubuntu-16_04/lastCompletedBuild/badge/icon)](https://ci.swift.org/job/oss-swift-package-linux-ubuntu-16_04)| +| **Ubuntu 16.10** | x86_64 | [![Build Status](https://ci.swift.org/job/oss-swift-incremental-RA-linux-ubuntu-16_10/lastCompletedBuild/badge/icon)](https://ci.swift.org/job/oss-swift-incremental-RA-linux-ubuntu-16_10)|[![Build Status](https://ci.swift.org/job/oss-swift-package-linux-ubuntu-16_10/lastCompletedBuild/badge/icon)](https://ci.swift.org/job/oss-swift-package-linux-ubuntu-16_10)| + +**Swift Community-Hosted CI Platforms** + +| **OS** | **Architecture** | **Build** | |---|:---:|:---:| -|**macOS** |[![Build Status](https://ci.swift.org/job/oss-swift-incremental-RA-osx/badge/icon)](https://ci.swift.org/job/oss-swift-incremental-RA-osx)|[![Build Status](https://ci.swift.org/job/oss-swift-package-osx/badge/icon)](https://ci.swift.org/job/oss-swift-package-osx)| -|**Ubuntu 14.04** |[![Build Status](https://ci.swift.org/job/oss-swift-incremental-RA-linux-ubuntu-14_04/badge/icon)](https://ci.swift.org/job/oss-swift-incremental-RA-linux-ubuntu-14_04)|[![Build Status](https://ci.swift.org/job/oss-swift-package-linux-ubuntu-14_04/badge/icon)](https://ci.swift.org/job/oss-swift-package-linux-ubuntu-14_04)| -|**Ubuntu 16.04** |[![Build Status](https://ci.swift.org/job/oss-swift-incremental-RA-linux-ubuntu-16_04/badge/icon)](https://ci.swift.org/job/oss-swift-incremental-RA-linux-ubuntu-16_04)|[![Build Status](https://ci.swift.org/job/oss-swift-package-linux-ubuntu-16_04/badge/icon)](https://ci.swift.org/job/oss-swift-package-linux-ubuntu-16_04)| -|**Ubuntu 16.10** |[![Build Status](https://ci.swift.org/job/oss-swift-incremental-RA-linux-ubuntu-16_10/badge/icon)](https://ci.swift.org/job/oss-swift-incremental-RA-linux-ubuntu-16_10)|[![Build Status](https://ci.swift.org/job/oss-swift-package-linux-ubuntu-16_10/badge/icon)](https://ci.swift.org/job/oss-swift-package-linux-ubuntu-16_10)| +|**[Debian 9.1 (Raspberry Pi)](https://github.com/apple/swift-community-hosted-continuous-integration/blob/master/nodes/armv7_debian_stretch.json)** | ARMv7 | [![Build Status](https://ci-external.swift.org/job/oss-swift-RA-linux-debian-9_1/lastCompletedBuild/badge/icon)](https://ci-external.swift.org/job/oss-swift-RA-linux-debian-9_1)| +|**[Fedora 27](https://github.com/apple/swift-community-hosted-continuous-integration/blob/master/nodes/x86_64_fedora_27.json)** | x86_64 |[![Build Status](https://ci-external.swift.org/job/oss-swift-RA-linux-fedora-27/lastCompletedBuild/badge/icon)](https://ci-external.swift.org/job/oss-swift-RA-linux-fedora-27)| +|**[Ubuntu 16.04](https://github.com/apple/swift-community-hosted-continuous-integration/blob/master/nodes/x86_64_ubuntu_16_04.json)** | x86_64 |[![Build Status](https://ci-external.swift.org/job/oss-swift-RA-linux-ubuntu-16.04/lastCompletedBuild/badge/icon)](https://ci-external.swift.org/job/oss-swift-RA-linux-ubuntu-16.04)| +|**[Ubuntu 16.04 (TensorFlow)](https://github.com/apple/swift-community-hosted-continuous-integration/blob/master/nodes/x86_64_ubuntu_16_04_tensorflow.json)** | x86_64 |[![Build Status](https://ci-external.swift.org/job/oss-swift-RA-linux-ubuntu-16.04-tensorflow/lastCompletedBuild/badge/icon)](https://ci-external.swift.org/job/oss-swift-RA-linux-ubuntu-16.04-tensorflow)| +|**[Ubuntu 16.04 ](https://github.com/apple/swift-community-hosted-continuous-integration/blob/master/nodes/ppc64le_16_04.json)** | PPC64LE |[![Build Status](https://ci-external.swift.org/job/oss-swift-4.1-RA-linux-ubuntu-16.04-ppc64le/lastCompletedBuild/badge/icon)](https://ci-external.swift.org/job/oss-swift-4.1-RA-linux-ubuntu-16.04-ppc64le)| **Welcome to Swift!** @@ -42,9 +53,10 @@ well. For more, see the [Code of Conduct](https://swift.org/community/#code-of-c ## Getting Started These instructions give the most direct path to a working Swift development -environment. To build from source you will need 2 GB of disk space for the -source code and over 20 GB of disk space for the build artifacts. A clean -build can take multiple hours, but incremental builds will finish much faster. +environment. To build from source you will need about 2 GB of disk space for the +source code and up to 70 GB of disk space for the build artifacts with full +debugging. Depending on your machine, a clean build can take a few minutes to +several hours. Naturally, incremental builds are much faster. ### System Requirements @@ -76,7 +88,7 @@ Instructions for installing CMake and Ninja directly can be found [below](#build For Ubuntu, you'll need the following development dependencies: - sudo apt-get install git cmake ninja-build clang python uuid-dev libicu-dev icu-devtools libbsd-dev libedit-dev libxml2-dev libsqlite3-dev swig libpython-dev libncurses5-dev pkg-config libblocksruntime-dev libcurl4-openssl-dev autoconf libtool systemtap-sdt-dev tzdata rsync + sudo apt-get install git cmake ninja-build clang python uuid-dev libicu-dev icu-devtools libbsd-dev libedit-dev libxml2-dev libsqlite3-dev swig libpython-dev libncurses5-dev pkg-config libblocksruntime-dev libcurl4-openssl-dev systemtap-sdt-dev tzdata rsync **Note:** LLDB currently requires at least `swig-1.3.40` but will successfully build with version 2 shipped with Ubuntu. diff --git a/benchmark/CMakeLists.txt b/benchmark/CMakeLists.txt index 93339b5185e8c..2a17ac2abccb3 100644 --- a/benchmark/CMakeLists.txt +++ b/benchmark/CMakeLists.txt @@ -63,9 +63,11 @@ set(SWIFT_BENCH_MODULES single-source/DictTest2 single-source/DictTest3 single-source/DictTest4 + single-source/DictTest4Legacy single-source/DictionaryBridge single-source/DictionaryCopy single-source/DictionaryGroup + single-source/DictionaryKeysContains single-source/DictionaryLiteral single-source/DictionaryRemove single-source/DictionarySubscriptDefault @@ -122,6 +124,8 @@ set(SWIFT_BENCH_MODULES single-source/Queue single-source/RC4 single-source/RGBHistogram + single-source/RandomShuffle + single-source/RandomValues single-source/RangeAssignment single-source/RangeIteration single-source/RangeReplaceableCollectionPlusDefault diff --git a/benchmark/multi-source/PrimsSplit/Prims.swift b/benchmark/multi-source/PrimsSplit/Prims.swift index 57d1f6da83c53..df4f294f62b34 100644 --- a/benchmark/multi-source/PrimsSplit/Prims.swift +++ b/benchmark/multi-source/PrimsSplit/Prims.swift @@ -176,10 +176,9 @@ func ==(lhs: Edge, rhs: Edge) -> Bool { } extension Edge : Hashable { - var hashValue: Int { - get { - return start.hashValue ^ end.hashValue - } + func hash(into hasher: inout Hasher) { + hasher.combine(start) + hasher.combine(end) } } diff --git a/benchmark/single-source/AnyHashableWithAClass.swift b/benchmark/single-source/AnyHashableWithAClass.swift index e2b5a761004f4..d3e2ef864df05 100644 --- a/benchmark/single-source/AnyHashableWithAClass.swift +++ b/benchmark/single-source/AnyHashableWithAClass.swift @@ -33,9 +33,11 @@ class TestHashableBase : Hashable { init(_ value: Int) { self.value = value } - var hashValue: Int { - return value + + func hash(into hasher: inout Hasher) { + hasher.combine(value) } + static func == ( lhs: TestHashableBase, rhs: TestHashableBase diff --git a/benchmark/single-source/ArrayAppend.swift b/benchmark/single-source/ArrayAppend.swift index 8ed0959e94641..87bc4ee897440 100644 --- a/benchmark/single-source/ArrayAppend.swift +++ b/benchmark/single-source/ArrayAppend.swift @@ -18,9 +18,11 @@ public let ArrayAppend = [ BenchmarkInfo(name: "ArrayAppend", runFunction: run_ArrayAppend, tags: [.validation, .api, .Array]), BenchmarkInfo(name: "ArrayAppendArrayOfInt", runFunction: run_ArrayAppendArrayOfInt, tags: [.validation, .api, .Array]), BenchmarkInfo(name: "ArrayAppendAscii", runFunction: run_ArrayAppendAscii, tags: [.validation, .api, .Array]), + BenchmarkInfo(name: "ArrayAppendAsciiSubstring", runFunction: run_ArrayAppendAsciiSubstring, tags: [.validation, .api, .Array]), BenchmarkInfo(name: "ArrayAppendFromGeneric", runFunction: run_ArrayAppendFromGeneric, tags: [.validation, .api, .Array]), BenchmarkInfo(name: "ArrayAppendGenericStructs", runFunction: run_ArrayAppendGenericStructs, tags: [.validation, .api, .Array]), BenchmarkInfo(name: "ArrayAppendLatin1", runFunction: run_ArrayAppendLatin1, tags: [.validation, .api, .Array]), + BenchmarkInfo(name: "ArrayAppendLatin1Substring", runFunction: run_ArrayAppendLatin1Substring, tags: [.validation, .api, .Array]), BenchmarkInfo(name: "ArrayAppendLazyMap", runFunction: run_ArrayAppendLazyMap, tags: [.validation, .api, .Array]), BenchmarkInfo(name: "ArrayAppendOptionals", runFunction: run_ArrayAppendOptionals, tags: [.validation, .api, .Array]), BenchmarkInfo(name: "ArrayAppendRepeatCol", runFunction: run_ArrayAppendRepeatCol, tags: [.validation, .api, .Array]), @@ -30,6 +32,7 @@ public let ArrayAppend = [ BenchmarkInfo(name: "ArrayAppendToFromGeneric", runFunction: run_ArrayAppendToFromGeneric, tags: [.validation, .api, .Array]), BenchmarkInfo(name: "ArrayAppendToGeneric", runFunction: run_ArrayAppendToGeneric, tags: [.validation, .api, .Array]), BenchmarkInfo(name: "ArrayAppendUTF16", runFunction: run_ArrayAppendUTF16, tags: [.validation, .api, .Array]), + BenchmarkInfo(name: "ArrayAppendUTF16Substring", runFunction: run_ArrayAppendUTF16Substring, tags: [.validation, .api, .Array]), BenchmarkInfo(name: "ArrayPlusEqualArrayOfInt", runFunction: run_ArrayPlusEqualArrayOfInt, tags: [.validation, .api, .Array]), BenchmarkInfo(name: "ArrayPlusEqualFiveElementCollection", runFunction: run_ArrayPlusEqualFiveElementCollection, tags: [.validation, .api, .Array]), BenchmarkInfo(name: "ArrayPlusEqualSingleElementCollection", runFunction: run_ArrayPlusEqualSingleElementCollection, tags: [.validation, .api, .Array]), @@ -310,7 +313,7 @@ public func run_ArrayAppendAscii(_ N: Int) { for _ in 0..<10 { var nums = [UInt8]() for _ in 0..<10_000 { - nums += s.utf8 + nums += getString(s).utf8 } } } @@ -324,7 +327,7 @@ public func run_ArrayAppendLatin1(_ N: Int) { for _ in 0..<10 { var nums = [UInt8]() for _ in 0..<10_000 { - nums += s.utf8 + nums += getString(s).utf8 } } } @@ -338,7 +341,49 @@ public func run_ArrayAppendUTF16(_ N: Int) { for _ in 0..<10 { var nums = [UInt8]() for _ in 0..<10_000 { - nums += s.utf8 + nums += getString(s).utf8 + } + } + } +} + +// Append the utf8 elements of an ascii string to a [UInt8] +@inline(never) +public func run_ArrayAppendAsciiSubstring(_ N: Int) { + let s = "the quick brown fox jumps over the lazy dog!"[...] + for _ in 0..=4.2) public let BinaryFloatingPointConversionFromBinaryInteger = BenchmarkInfo( name: "BinaryFloatingPointConversionFromBinaryInteger", runFunction: run_BinaryFloatingPointConversionFromBinaryInteger, tags: [.validation, .algorithm] ) +#else +public let BinaryFloatingPointConversionFromBinaryInteger: [BenchmarkInfo] = [] +#endif struct MockBinaryInteger { var _value: T @@ -55,8 +59,8 @@ extension MockBinaryInteger : Comparable { } extension MockBinaryInteger : Hashable { - var hashValue: Int { - return _value.hashValue + func hash(into hasher: inout Hasher) { + hasher.combine(_value) } } @@ -186,6 +190,8 @@ extension MockBinaryInteger : BinaryInteger { } } +#if swift(>=4.2) + @inline(never) public func run_BinaryFloatingPointConversionFromBinaryInteger(_ N: Int) { var xs = [Double]() @@ -199,3 +205,6 @@ public func run_BinaryFloatingPointConversionFromBinaryInteger(_ N: Int) { } CheckResults(xs[getInt(0)] == 1999000) } + +#endif + diff --git a/benchmark/single-source/CSVParsing.swift b/benchmark/single-source/CSVParsing.swift index 62eec0d1e26f7..03671beed0f69 100644 --- a/benchmark/single-source/CSVParsing.swift +++ b/benchmark/single-source/CSVParsing.swift @@ -31,7 +31,7 @@ func parseQuotedField(_ remainder: inout Substring) throws -> Substring? { var result: Substring = "" // we accumulate the result while !remainder.isEmpty { - guard let nextQuoteIndex = remainder.index(of: "\"") else { + guard let nextQuoteIndex = remainder.firstIndex(of: "\"") else { throw ParseError(message: "Expected a closing \"") } diff --git a/benchmark/single-source/DictTest.swift b/benchmark/single-source/DictTest.swift index ef46ce9c01d2e..bf4f10e60fab3 100644 --- a/benchmark/single-source/DictTest.swift +++ b/benchmark/single-source/DictTest.swift @@ -157,8 +157,8 @@ class Box : Hashable { value = v } - var hashValue: Int { - return value.hashValue + func hash(into hasher: inout Hasher) { + hasher.combine(value) } static func ==(lhs: Box, rhs: Box) -> Bool { diff --git a/benchmark/single-source/DictTest2.swift b/benchmark/single-source/DictTest2.swift index 535578b181c2e..8e8ac4e1a8700 100644 --- a/benchmark/single-source/DictTest2.swift +++ b/benchmark/single-source/DictTest2.swift @@ -49,8 +49,8 @@ class Box : Hashable { value = v } - var hashValue: Int { - return value.hashValue + func hash(into hasher: inout Hasher) { + hasher.combine(value) } static func ==(lhs: Box, rhs: Box) -> Bool { diff --git a/benchmark/single-source/DictTest3.swift b/benchmark/single-source/DictTest3.swift index a35bc0be1ef8e..fa173f63d1280 100644 --- a/benchmark/single-source/DictTest3.swift +++ b/benchmark/single-source/DictTest3.swift @@ -56,10 +56,10 @@ class Box : Hashable { value = v } - var hashValue: Int { - return value.hashValue + func hash(into hasher: inout Hasher) { + hasher.combine(value) } - + static func ==(lhs: Box, rhs: Box) -> Bool { return lhs.value == rhs.value } diff --git a/benchmark/single-source/DictTest4.swift b/benchmark/single-source/DictTest4.swift index d2849fae76e47..305d2e3f93324 100644 --- a/benchmark/single-source/DictTest4.swift +++ b/benchmark/single-source/DictTest4.swift @@ -16,8 +16,14 @@ import TestsUtils // exercising the default hash compression function. public let Dictionary4 = [ - BenchmarkInfo(name: "Dictionary4", runFunction: run_Dictionary4, tags: [.validation, .api, .Dictionary]), - BenchmarkInfo(name: "Dictionary4OfObjects", runFunction: run_Dictionary4OfObjects, tags: [.validation, .api, .Dictionary]), + BenchmarkInfo( + name: "Dictionary4", + runFunction: run_Dictionary4, + tags: [.validation, .api, .Dictionary]), + BenchmarkInfo( + name: "Dictionary4OfObjects", + runFunction: run_Dictionary4OfObjects, + tags: [.validation, .api, .Dictionary]), ] struct LargeKey: Hashable { @@ -85,10 +91,10 @@ class Box : Hashable { value = v } - var hashValue: Int { - return value.hashValue + func hash(into hasher: inout Hasher) { + hasher.combine(value) } - + static func ==(lhs: Box, rhs: Box) -> Bool { return lhs.value == rhs.value } diff --git a/benchmark/single-source/DictTest4Legacy.swift b/benchmark/single-source/DictTest4Legacy.swift new file mode 100644 index 0000000000000..a9d724a49b799 --- /dev/null +++ b/benchmark/single-source/DictTest4Legacy.swift @@ -0,0 +1,158 @@ +//===--- DictTest4.swift --------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +import TestsUtils + +// This benchmark mostly measures lookups in dictionaries with complex keys, +// using the legacy hashValue API. + +public let Dictionary4Legacy = [ + BenchmarkInfo( + name: "Dictionary4Legacy", + runFunction: run_Dictionary4Legacy, + tags: [.validation, .api, .Dictionary]), + BenchmarkInfo( + name: "Dictionary4OfObjectsLegacy", + runFunction: run_Dictionary4OfObjectsLegacy, + tags: [.validation, .api, .Dictionary]), +] + +extension Int { + mutating func combine(_ value: Int) { + self = 16777619 &* self ^ value + } +} + +struct LargeKey: Hashable { + let i: Int + let j: Int + let k: Double + let l: UInt32 + let m: Bool + let n: Bool + let o: Bool + let p: Bool + let q: Bool + + var hashValue: Int { + var hash = i.hashValue + hash.combine(j.hashValue) + hash.combine(k.hashValue) + hash.combine(l.hashValue) + hash.combine(m.hashValue) + hash.combine(n.hashValue) + hash.combine(o.hashValue) + hash.combine(p.hashValue) + hash.combine(q.hashValue) + return hash + } + + init(_ value: Int) { + self.i = value + self.j = 2 * value + self.k = Double(value) / 7 + self.l = UInt32(truncatingIfNeeded: value) + self.m = value & 1 == 0 + self.n = value & 2 == 0 + self.o = value & 4 == 0 + self.p = value & 8 == 0 + self.q = value & 16 == 0 + } +} + +@inline(never) +public func run_Dictionary4Legacy(_ N: Int) { + let size1 = 100 + let reps = 20 + let ref_result = "1 99 \(reps) \(reps * 99)" + var hash1 = [LargeKey: Int]() + var hash2 = [LargeKey: Int]() + var res = "" + + for _ in 1...N { + // Test insertions + hash1 = [:] + for i in 0.. : Hashable { + var value: T + + init(_ v: T) { + value = v + } + + func hash(into hasher: inout Hasher) { + hasher.combine(value) + } + + var hashValue: Int { + return value.hashValue + } + + static func ==(lhs: Box, rhs: Box) -> Bool { + return lhs.value == rhs.value + } +} + +@inline(never) +public func run_Dictionary4OfObjectsLegacy(_ N: Int) { + let size1 = 100 + let reps = 20 + let ref_result = "1 99 \(reps) \(reps * 99)" + var hash1 = [Box: Int]() + var hash2 = [Box: Int]() + var res = "" + + for _ in 1...N { + // Test insertions + hash1 = [:] + for i in 0.. : Hashable { value = v } - var hashValue: Int { - return value.hashValue + func hash(into hasher: inout Hasher) { + hasher.combine(value) } static func ==(lhs: Box, rhs: Box) -> Bool { diff --git a/benchmark/single-source/DictionaryKeysContains.swift b/benchmark/single-source/DictionaryKeysContains.swift new file mode 100644 index 0000000000000..845c09ee80a41 --- /dev/null +++ b/benchmark/single-source/DictionaryKeysContains.swift @@ -0,0 +1,76 @@ +//===--- DictionaryKeysContains.swift -------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// This benchmark checks if keys.contains(key) is executed in O(1) +// even when wrapping a NSDictionary +import TestsUtils +import Foundation + +#if _runtime(_ObjC) +public let DictionaryKeysContains = [ + BenchmarkInfo( + name: "DictionaryKeysContainsNative", + runFunction: run_DictionaryKeysContains, + tags: [.validation, .api, .Dictionary], + setUpFunction: setup_DictionaryKeysContainsNative, + tearDownFunction: teardown_DictionaryKeysContains), + BenchmarkInfo( + name: "DictionaryKeysContainsCocoa", + runFunction: run_DictionaryKeysContains, + tags: [.validation, .api, .Dictionary], + setUpFunction: setup_DictionaryKeysContainsCocoa, + tearDownFunction: teardown_DictionaryKeysContains), +] +#else +public let DictionaryKeysContains = [ + BenchmarkInfo( + name: "DictionaryKeysContainsNative", + runFunction: run_DictionaryKeysContains, + tags: [.validation, .api, .Dictionary], + setUpFunction: setup_DictionaryKeysContainsNative, + tearDownFunction: teardown_DictionaryKeysContains), +] +#endif + +private var dictionary: [NSString: NSString]! + +private func setup_DictionaryKeysContainsNative() { + let keyValuePairs = (1...1_000_000).map { + ("\($0)" as NSString, "\($0)" as NSString) + } + dictionary = [NSString: NSString](uniqueKeysWithValues: keyValuePairs) +} + +#if _runtime(_ObjC) +private func setup_DictionaryKeysContainsCocoa() { + let keyValuePairs = (1...1_000_000).map { + ("\($0)" as NSString, "\($0)" as NSString) + } + let nativeDictionary = [NSString: NSString]( + uniqueKeysWithValues: keyValuePairs) + dictionary = (NSDictionary(dictionary: nativeDictionary) + as! [NSString: NSString]) +} +#endif + +private func teardown_DictionaryKeysContains() { + dictionary = nil +} + +@inline(never) +public func run_DictionaryKeysContains(_ N: Int) { + for _ in 0..<(N * 100) { + CheckResults(dictionary.keys.contains("42")) + CheckResults(!dictionary.keys.contains("-1")) + } +} + diff --git a/benchmark/single-source/DictionaryRemove.swift b/benchmark/single-source/DictionaryRemove.swift index 7051df3aeba5f..a8f1203d579fe 100644 --- a/benchmark/single-source/DictionaryRemove.swift +++ b/benchmark/single-source/DictionaryRemove.swift @@ -52,8 +52,8 @@ class Box : Hashable { value = v } - var hashValue: Int { - return value.hashValue + func hash(into hasher: inout Hasher) { + hasher.combine(value) } static func ==(lhs: Box, rhs: Box) -> Bool { diff --git a/benchmark/single-source/DictionarySubscriptDefault.swift b/benchmark/single-source/DictionarySubscriptDefault.swift index 2c90d012d02eb..49266c7021754 100644 --- a/benchmark/single-source/DictionarySubscriptDefault.swift +++ b/benchmark/single-source/DictionarySubscriptDefault.swift @@ -84,8 +84,8 @@ class Box : Hashable, P { value = v } - var hashValue: Int { - return value.hashValue + func hash(into hasher: inout Hasher) { + hasher.combine(value) } static func ==(lhs: Box, rhs: Box) -> Bool { diff --git a/benchmark/single-source/DictionarySwap.swift b/benchmark/single-source/DictionarySwap.swift index 90fe0e3b37d4e..75df14d06c836 100644 --- a/benchmark/single-source/DictionarySwap.swift +++ b/benchmark/single-source/DictionarySwap.swift @@ -83,8 +83,8 @@ class Box : Hashable { value = v } - var hashValue: Int { - return value.hashValue + func hash(into hasher: inout Hasher) { + hasher.combine(value) } static func ==(lhs: Box, rhs: Box) -> Bool { diff --git a/benchmark/single-source/PopFrontGeneric.swift b/benchmark/single-source/PopFrontGeneric.swift index 2c46ecc56c6bf..b8e65afb72a15 100644 --- a/benchmark/single-source/PopFrontGeneric.swift +++ b/benchmark/single-source/PopFrontGeneric.swift @@ -22,7 +22,6 @@ let arrayCount = 1024 // This test case exposes rdar://17440222 which caused rdar://17974483 (popFront // being really slow). -@usableFromInline protocol MyArrayBufferProtocol : MutableCollection, RandomAccessCollection { mutating func myReplace( _ subRange: Range, diff --git a/benchmark/single-source/Prims.swift b/benchmark/single-source/Prims.swift index d763f20bbbedf..04042863b5795 100644 --- a/benchmark/single-source/Prims.swift +++ b/benchmark/single-source/Prims.swift @@ -181,10 +181,9 @@ func ==(lhs: Edge, rhs: Edge) -> Bool { } extension Edge : Hashable { - var hashValue: Int { - get { - return start.hashValue ^ end.hashValue - } + func hash(into hasher: inout Hasher) { + hasher.combine(start) + hasher.combine(end) } } diff --git a/benchmark/single-source/Queue.swift b/benchmark/single-source/Queue.swift index 5776e49c97985..46e5cf4420fb6 100644 --- a/benchmark/single-source/Queue.swift +++ b/benchmark/single-source/Queue.swift @@ -28,7 +28,6 @@ public let QueueConcrete = BenchmarkInfo( // TODO: remove when there is a native equivalent in the std lib extension RangeReplaceableCollection where Self: BidirectionalCollection { - @inlinable public mutating func popLast() -> Element? { if isEmpty { return nil} else { return removeLast() } diff --git a/benchmark/single-source/RGBHistogram.swift b/benchmark/single-source/RGBHistogram.swift index c2853bce9b377..66f931669dbf4 100644 --- a/benchmark/single-source/RGBHistogram.swift +++ b/benchmark/single-source/RGBHistogram.swift @@ -124,8 +124,8 @@ class Box : Hashable { value = v } - var hashValue: Int { - return value.hashValue + func hash(into hasher: inout Hasher) { + hasher.combine(value) } static func ==(lhs: Box, rhs: Box) -> Bool { diff --git a/benchmark/single-source/RandomShuffle.swift b/benchmark/single-source/RandomShuffle.swift new file mode 100644 index 0000000000000..a9869dd7da677 --- /dev/null +++ b/benchmark/single-source/RandomShuffle.swift @@ -0,0 +1,64 @@ +//===--- RandomShuffle.swift ----------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 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 +// +//===----------------------------------------------------------------------===// + +import TestsUtils + +// +// Benchmark that shuffles arrays of integers. Measures the performance of +// shuffling large arrays. +// + +public let RandomShuffle = [ + BenchmarkInfo(name: "RandomShuffleDef", runFunction: run_RandomShuffleDef, + tags: [.api], setUpFunction: setup_RandomShuffle), + BenchmarkInfo(name: "RandomShuffleLCG", runFunction: run_RandomShuffleLCG, + tags: [.api], setUpFunction: setup_RandomShuffle), +] + +/// A linear congruential PRNG. +struct LCRNG: RandomNumberGenerator { + private var state: UInt64 + + init(seed: Int) { + state = UInt64(truncatingIfNeeded: seed) + for _ in 0..<10 { _ = next() } + } + + mutating func next() -> UInt64 { + state = 2862933555777941757 &* state &+ 3037000493 + return state + } +} + +var numbers = Array(0...3_000_000) + +@inline(never) +func setup_RandomShuffle() { + _ = numbers.count +} + +@inline(never) +public func run_RandomShuffleDef(_ N: Int) { + for _ in 0 ..< N { + numbers.shuffle() + blackHole(numbers.first!) + } +} + +@inline(never) +public func run_RandomShuffleLCG(_ N: Int) { + var generator = LCRNG(seed: 0) + for _ in 0 ..< N { + numbers.shuffle(using: &generator) + blackHole(numbers.first!) + } +} diff --git a/benchmark/single-source/RandomValues.swift b/benchmark/single-source/RandomValues.swift new file mode 100644 index 0000000000000..6387e3d5a99f9 --- /dev/null +++ b/benchmark/single-source/RandomValues.swift @@ -0,0 +1,87 @@ +//===--- RandomValues.swift -----------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 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 +// +//===----------------------------------------------------------------------===// + +import TestsUtils + +// +// Benchmark generating lots of random values. Measures the performance of +// the default random generator and the algorithms for generating integers +// and floating-point values. +// + +public let RandomValues = [ + BenchmarkInfo(name: "RandomIntegersDef", runFunction: run_RandomIntegersDef, tags: [.api]), + BenchmarkInfo(name: "RandomIntegersLCG", runFunction: run_RandomIntegersLCG, tags: [.api]), + BenchmarkInfo(name: "RandomDoubleDef", runFunction: run_RandomDoubleDef, tags: [.api]), + BenchmarkInfo(name: "RandomDoubleLCG", runFunction: run_RandomDoubleLCG, tags: [.api]), +] + +/// A linear congruential PRNG. +struct LCRNG: RandomNumberGenerator { + private var state: UInt64 + + init(seed: Int) { + state = UInt64(truncatingIfNeeded: seed) + for _ in 0..<10 { _ = next() } + } + + mutating func next() -> UInt64 { + state = 2862933555777941757 &* state &+ 3037000493 + return state + } +} + +@inline(never) +public func run_RandomIntegersDef(_ N: Int) { + for _ in 0 ..< N { + var x = 0 + for _ in 0 ..< 100_000 { + x &+= Int.random(in: 0...10_000) + } + blackHole(x) + } +} + +@inline(never) +public func run_RandomIntegersLCG(_ N: Int) { + for _ in 0 ..< N { + var x = 0 + var generator = LCRNG(seed: 0) + for _ in 0 ..< 100_000 { + x &+= Int.random(in: 0...10_000, using: &generator) + } + CheckResults(x == 498214315) + } +} + +@inline(never) +public func run_RandomDoubleDef(_ N: Int) { + for _ in 0 ..< N { + var x = 0.0 + for _ in 0 ..< 100_000 { + x += Double.random(in: -1000...1000) + } + blackHole(x) + } +} + +@inline(never) +public func run_RandomDoubleLCG(_ N: Int) { + for _ in 0 ..< N { + var x = 0.0 + var generator = LCRNG(seed: 0) + for _ in 0 ..< 100_000 { + x += Double.random(in: -1000...1000, using: &generator) + } + blackHole(x) + } +} diff --git a/benchmark/single-source/RangeIteration.swift b/benchmark/single-source/RangeIteration.swift index a3e4d3e376528..61042024ed521 100644 --- a/benchmark/single-source/RangeIteration.swift +++ b/benchmark/single-source/RangeIteration.swift @@ -12,6 +12,7 @@ import TestsUtils +#if swift(>=4.2) public let RangeIteration = [ BenchmarkInfo( name: "RangeIterationSigned", @@ -29,6 +30,15 @@ public let RangeIteration = [ tags: [.validation, .api] ), ] +#else +public let RangeIteration = [ + BenchmarkInfo( + name: "RangeIterationSigned", + runFunction: run_RangeIterationSigned, + tags: [.validation, .api] + ) +] +#endif public var check: UInt64 = 0 @@ -50,6 +60,8 @@ public func run_RangeIterationSigned(_ N: Int) { CheckResults(check == 4999950000 * UInt64(N)) } +#if swift(>=4.2) + @inline(never) public func run_RangeIterationSigned64(_ N: Int) { let range: Range = 0..<100000 @@ -75,3 +87,5 @@ public func run_RangeIterationUnsigned(_ N: Int) { CheckResults(check == 4999950000 * UInt64(N)) } + +#endif diff --git a/benchmark/single-source/RemoveWhere.swift b/benchmark/single-source/RemoveWhere.swift index b995bf242cdef..81e1287dd4a19 100644 --- a/benchmark/single-source/RemoveWhere.swift +++ b/benchmark/single-source/RemoveWhere.swift @@ -46,7 +46,7 @@ extension RangeReplaceableCollection { extension RangeReplaceableCollection where Self: MutableCollection { mutating func removeWhere_move(where match: (Element) throws -> Bool) rethrows { - guard var i = try index(where: match) else { return } + guard var i = try firstIndex(where: match) else { return } var j = index(after: i) while j != endIndex { @@ -62,7 +62,7 @@ extension RangeReplaceableCollection where Self: MutableCollection { } mutating func removeWhere_swap(where match: (Element) throws -> Bool) rethrows { - guard var i = try index(where: match) else { return } + guard var i = try firstIndex(where: match) else { return } var j = index(after: i) while j != endIndex { diff --git a/benchmark/single-source/SetTests.swift b/benchmark/single-source/SetTests.swift index 2d1178161cebf..d6f815d175821 100644 --- a/benchmark/single-source/SetTests.swift +++ b/benchmark/single-source/SetTests.swift @@ -122,8 +122,8 @@ class Box : Hashable { value = v } - var hashValue: Int { - return value.hashValue + func hash(into hasher: inout Hasher) { + hasher.combine(value) } static func ==(lhs: Box, rhs: Box) -> Bool { diff --git a/benchmark/single-source/StringBuilder.swift b/benchmark/single-source/StringBuilder.swift index 65eeb00732e20..6c3ec52c10af9 100644 --- a/benchmark/single-source/StringBuilder.swift +++ b/benchmark/single-source/StringBuilder.swift @@ -21,6 +21,10 @@ public let StringBuilder = [ name: "StringBuilder", runFunction: run_StringBuilder, tags: [.validation, .api, .String]), + BenchmarkInfo( + name: "StringBuilderSmallReservingCapacity", + runFunction: run_StringBuilderSmallReservingCapacity, + tags: [.validation, .api, .String]), BenchmarkInfo( name: "StringUTF16Builder", runFunction: run_StringUTF16Builder, @@ -48,8 +52,11 @@ public let StringBuilder = [ ] @inline(never) -func buildString(_ i: String) -> String { +func buildString(_ i: String, reservingCapacity: Bool = false) -> String { var sb = getString(i) + if reservingCapacity { + sb.reserveCapacity(10) + } for str in ["b","c","d","pizza"] { sb += str } @@ -63,6 +70,13 @@ public func run_StringBuilder(_ N: Int) { } } +@inline(never) +public func run_StringBuilderSmallReservingCapacity(_ N: Int) { + for _ in 1...5000*N { + blackHole(buildString("a", reservingCapacity: true)) + } +} + @inline(never) func addString(_ i: String) -> String { let s = getString(i) + "b" + "c" + "d" + "pizza" @@ -179,3 +193,4 @@ public func run_StringWordBuilderReservingCapacity(_ N: Int) { blackHole(buildString( word: "bumfuzzle", count: 50_000 * N, reservingCapacity: true)) } + diff --git a/benchmark/single-source/StringComparison.swift b/benchmark/single-source/StringComparison.swift index 1397f00b12886..240e11d7deb01 100644 --- a/benchmark/single-source/StringComparison.swift +++ b/benchmark/single-source/StringComparison.swift @@ -34,167 +34,275 @@ public let StringComparison = [ BenchmarkInfo( name: "StringComparison_ascii", runFunction: run_StringComparison_ascii, - tags: [.validation, .api, .String]), + tags: [.validation, .api, .String], + setUpFunction: setup_StringComparison_ascii), BenchmarkInfo( name: "StringComparison_latin1", runFunction: run_StringComparison_latin1, - tags: [.validation, .api, .String]), + tags: [.validation, .api, .String], + setUpFunction: setup_StringComparison_latin1), BenchmarkInfo( name: "StringComparison_fastPrenormal", runFunction: run_StringComparison_fastPrenormal, - tags: [.validation, .api, .String]), + tags: [.validation, .api, .String], + setUpFunction: setup_StringComparison_fastPrenormal), BenchmarkInfo( name: "StringComparison_slowerPrenormal", runFunction: run_StringComparison_slowerPrenormal, - tags: [.validation, .api, .String]), + tags: [.validation, .api, .String], + setUpFunction: setup_StringComparison_slowerPrenormal), BenchmarkInfo( name: "StringComparison_nonBMPSlowestPrenormal", runFunction: run_StringComparison_nonBMPSlowestPrenormal, - tags: [.validation, .api, .String]), + tags: [.validation, .api, .String], + setUpFunction: setup_StringComparison_nonBMPSlowestPrenormal), BenchmarkInfo( name: "StringComparison_emoji", runFunction: run_StringComparison_emoji, - tags: [.validation, .api, .String]), + tags: [.validation, .api, .String], + setUpFunction: setup_StringComparison_emoji), BenchmarkInfo( name: "StringComparison_abnormal", runFunction: run_StringComparison_abnormal, - tags: [.validation, .api, .String]), + tags: [.validation, .api, .String], + setUpFunction: setup_StringComparison_abnormal), BenchmarkInfo( name: "StringComparison_zalgo", runFunction: run_StringComparison_zalgo, - tags: [.validation, .api, .String]), + tags: [.validation, .api, .String], + setUpFunction: setup_StringComparison_zalgo), BenchmarkInfo( name: "StringComparison_longSharedPrefix", runFunction: run_StringComparison_longSharedPrefix, - tags: [.validation, .api, .String]), + tags: [.validation, .api, .String], + setUpFunction: setup_StringComparison_longSharedPrefix), ] - - @inline(never) - public func run_StringComparison_ascii(_ N: Int) { - let workload = Workload.ascii - let tripCount = workload.tripCount - let payload = workload.payload - for _ in 1...tripCount*N { - for s1 in payload { - for s2 in payload { - blackHole(s1 < s2) - } + + +var Workload_ascii: Workload? = nil + +@inline(never) +public func setup_StringComparison_ascii() { + if Workload_ascii != nil { + return + } + Workload_ascii = Workload.ascii +} + +@inline(never) +public func run_StringComparison_ascii(_ N: Int) { + let workload = Workload_ascii._unsafelyUnwrappedUnchecked + let tripCount = workload.tripCount + let payload = workload.payload + for _ in 1...tripCount*N { + for s1 in payload { + for s2 in payload { + blackHole(s1 < s2) } } } - - @inline(never) - public func run_StringComparison_latin1(_ N: Int) { - let workload = Workload.latin1 - let tripCount = workload.tripCount - let payload = workload.payload - for _ in 1...tripCount*N { - for s1 in payload { - for s2 in payload { - blackHole(s1 < s2) - } +} + + +var Workload_latin1: Workload? = nil + +@inline(never) +public func setup_StringComparison_latin1() { + if Workload_latin1 != nil { + return + } + Workload_latin1 = Workload.latin1 +} + +@inline(never) +public func run_StringComparison_latin1(_ N: Int) { + let workload = Workload_latin1._unsafelyUnwrappedUnchecked + let tripCount = workload.tripCount + let payload = workload.payload + for _ in 1...tripCount*N { + for s1 in payload { + for s2 in payload { + blackHole(s1 < s2) } } } - - @inline(never) - public func run_StringComparison_fastPrenormal(_ N: Int) { - let workload = Workload.fastPrenormal - let tripCount = workload.tripCount - let payload = workload.payload - for _ in 1...tripCount*N { - for s1 in payload { - for s2 in payload { - blackHole(s1 < s2) - } +} + + +var Workload_fastPrenormal: Workload? = nil + +@inline(never) +public func setup_StringComparison_fastPrenormal() { + if Workload_fastPrenormal != nil { + return + } + Workload_fastPrenormal = Workload.fastPrenormal +} + +@inline(never) +public func run_StringComparison_fastPrenormal(_ N: Int) { + let workload = Workload_fastPrenormal._unsafelyUnwrappedUnchecked + let tripCount = workload.tripCount + let payload = workload.payload + for _ in 1...tripCount*N { + for s1 in payload { + for s2 in payload { + blackHole(s1 < s2) } } } - - @inline(never) - public func run_StringComparison_slowerPrenormal(_ N: Int) { - let workload = Workload.slowerPrenormal - let tripCount = workload.tripCount - let payload = workload.payload - for _ in 1...tripCount*N { - for s1 in payload { - for s2 in payload { - blackHole(s1 < s2) - } +} + + +var Workload_slowerPrenormal: Workload? = nil + +@inline(never) +public func setup_StringComparison_slowerPrenormal() { + if Workload_slowerPrenormal != nil { + return + } + Workload_slowerPrenormal = Workload.slowerPrenormal +} + +@inline(never) +public func run_StringComparison_slowerPrenormal(_ N: Int) { + let workload = Workload_slowerPrenormal._unsafelyUnwrappedUnchecked + let tripCount = workload.tripCount + let payload = workload.payload + for _ in 1...tripCount*N { + for s1 in payload { + for s2 in payload { + blackHole(s1 < s2) } } } - - @inline(never) - public func run_StringComparison_nonBMPSlowestPrenormal(_ N: Int) { - let workload = Workload.nonBMPSlowestPrenormal - let tripCount = workload.tripCount - let payload = workload.payload - for _ in 1...tripCount*N { - for s1 in payload { - for s2 in payload { - blackHole(s1 < s2) - } +} + + +var Workload_nonBMPSlowestPrenormal: Workload? = nil + +@inline(never) +public func setup_StringComparison_nonBMPSlowestPrenormal() { + if Workload_nonBMPSlowestPrenormal != nil { + return + } + Workload_nonBMPSlowestPrenormal = Workload.nonBMPSlowestPrenormal +} + +@inline(never) +public func run_StringComparison_nonBMPSlowestPrenormal(_ N: Int) { + let workload = Workload_nonBMPSlowestPrenormal._unsafelyUnwrappedUnchecked + let tripCount = workload.tripCount + let payload = workload.payload + for _ in 1...tripCount*N { + for s1 in payload { + for s2 in payload { + blackHole(s1 < s2) } } } - - @inline(never) - public func run_StringComparison_emoji(_ N: Int) { - let workload = Workload.emoji - let tripCount = workload.tripCount - let payload = workload.payload - for _ in 1...tripCount*N { - for s1 in payload { - for s2 in payload { - blackHole(s1 < s2) - } +} + + +var Workload_emoji: Workload? = nil + +@inline(never) +public func setup_StringComparison_emoji() { + if Workload_emoji != nil { + return + } + Workload_emoji = Workload.emoji +} + +@inline(never) +public func run_StringComparison_emoji(_ N: Int) { + let workload = Workload_emoji._unsafelyUnwrappedUnchecked + let tripCount = workload.tripCount + let payload = workload.payload + for _ in 1...tripCount*N { + for s1 in payload { + for s2 in payload { + blackHole(s1 < s2) } } } - - @inline(never) - public func run_StringComparison_abnormal(_ N: Int) { - let workload = Workload.abnormal - let tripCount = workload.tripCount - let payload = workload.payload - for _ in 1...tripCount*N { - for s1 in payload { - for s2 in payload { - blackHole(s1 < s2) - } +} + + +var Workload_abnormal: Workload? = nil + +@inline(never) +public func setup_StringComparison_abnormal() { + if Workload_abnormal != nil { + return + } + Workload_abnormal = Workload.abnormal +} + +@inline(never) +public func run_StringComparison_abnormal(_ N: Int) { + let workload = Workload_abnormal._unsafelyUnwrappedUnchecked + let tripCount = workload.tripCount + let payload = workload.payload + for _ in 1...tripCount*N { + for s1 in payload { + for s2 in payload { + blackHole(s1 < s2) } } } - - @inline(never) - public func run_StringComparison_zalgo(_ N: Int) { - let workload = Workload.zalgo - let tripCount = workload.tripCount - let payload = workload.payload - for _ in 1...tripCount*N { - for s1 in payload { - for s2 in payload { - blackHole(s1 < s2) - } +} + + +var Workload_zalgo: Workload? = nil + +@inline(never) +public func setup_StringComparison_zalgo() { + if Workload_zalgo != nil { + return + } + Workload_zalgo = Workload.zalgo +} + +@inline(never) +public func run_StringComparison_zalgo(_ N: Int) { + let workload = Workload_zalgo._unsafelyUnwrappedUnchecked + let tripCount = workload.tripCount + let payload = workload.payload + for _ in 1...tripCount*N { + for s1 in payload { + for s2 in payload { + blackHole(s1 < s2) } } } - - @inline(never) - public func run_StringComparison_longSharedPrefix(_ N: Int) { - let workload = Workload.longSharedPrefix - let tripCount = workload.tripCount - let payload = workload.payload - for _ in 1...tripCount*N { - for s1 in payload { - for s2 in payload { - blackHole(s1 < s2) - } +} + + +var Workload_longSharedPrefix: Workload? = nil + +@inline(never) +public func setup_StringComparison_longSharedPrefix() { + if Workload_longSharedPrefix != nil { + return + } + Workload_longSharedPrefix = Workload.longSharedPrefix +} + +@inline(never) +public func run_StringComparison_longSharedPrefix(_ N: Int) { + let workload = Workload_longSharedPrefix._unsafelyUnwrappedUnchecked + let tripCount = workload.tripCount + let payload = workload.payload + for _ in 1...tripCount*N { + for s1 in payload { + for s2 in payload { + blackHole(s1 < s2) } } } - +} + struct Workload { static let N = 100 @@ -406,4 +514,4 @@ struct Workload { """.lines() ) -} \ No newline at end of file +} diff --git a/benchmark/single-source/StringComparison.swift.gyb b/benchmark/single-source/StringComparison.swift.gyb index 25c5a4051c980..adadb9d76bb83 100644 --- a/benchmark/single-source/StringComparison.swift.gyb +++ b/benchmark/single-source/StringComparison.swift.gyb @@ -37,25 +37,37 @@ public let StringComparison = [ BenchmarkInfo( name: "StringComparison_${Name}", runFunction: run_StringComparison_${Name}, - tags: [.validation, .api, .String]), + tags: [.validation, .api, .String], + setUpFunction: setup_StringComparison_${Name}), % end # Names ] - + % for Name in Names: - @inline(never) - public func run_StringComparison_${Name}(_ N: Int) { - let workload = Workload.${Name} - let tripCount = workload.tripCount - let payload = workload.payload - for _ in 1...tripCount*N { - for s1 in payload { - for s2 in payload { - blackHole(s1 < s2) - } + +var Workload_${Name}: Workload? = nil + +@inline(never) +public func setup_StringComparison_${Name}() { + if Workload_${Name} != nil { + return + } + Workload_${Name} = Workload.${Name} +} + +@inline(never) +public func run_StringComparison_${Name}(_ N: Int) { + let workload = Workload_${Name}._unsafelyUnwrappedUnchecked + let tripCount = workload.tripCount + let payload = workload.payload + for _ in 1...tripCount*N { + for s1 in payload { + for s2 in payload { + blackHole(s1 < s2) } } } - +} + % end # Names struct Workload { @@ -268,4 +280,4 @@ struct Workload { """.lines() ) -} \ No newline at end of file +} diff --git a/benchmark/utils/TestsUtils.swift b/benchmark/utils/TestsUtils.swift index 3c8ce5b37f5a8..d0ddde4212146 100644 --- a/benchmark/utils/TestsUtils.swift +++ b/benchmark/utils/TestsUtils.swift @@ -111,8 +111,8 @@ extension BenchmarkInfo : Comparable { } extension BenchmarkInfo : Hashable { - public var hashValue: Int { - return name.hashValue + public func hash(into hasher: inout Hasher) { + hasher.combine(name) } } @@ -194,3 +194,8 @@ public func getInt(_ x: Int) -> Int { return x } // The same for String. @inline(never) public func getString(_ s: String) -> String { return s } + +// The same for Substring. +@inline(never) +public func getSubstring(_ s: Substring) -> Substring { return s } + diff --git a/benchmark/utils/main.swift b/benchmark/utils/main.swift index b92415562257a..6e875d0802871 100644 --- a/benchmark/utils/main.swift +++ b/benchmark/utils/main.swift @@ -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 - 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 @@ -50,9 +50,11 @@ import DictTest import DictTest2 import DictTest3 import DictTest4 +import DictTest4Legacy import DictionaryBridge import DictionaryCopy import DictionaryGroup +import DictionaryKeysContains import DictionaryLiteral import DictionaryRemove import DictionarySubscriptDefault @@ -110,6 +112,8 @@ import ProtocolDispatch2 import Queue import RC4 import RGBHistogram +import RandomShuffle +import RandomValues import RangeAssignment import RangeIteration import RangeReplaceableCollectionPlusDefault @@ -203,9 +207,11 @@ registerBenchmark(Dictionary) registerBenchmark(Dictionary2) registerBenchmark(Dictionary3) registerBenchmark(Dictionary4) +registerBenchmark(Dictionary4Legacy) registerBenchmark(DictionaryBridge) registerBenchmark(DictionaryCopy) registerBenchmark(DictionaryGroup) +registerBenchmark(DictionaryKeysContains) registerBenchmark(DictionaryLiteral) registerBenchmark(DictionaryRemove) registerBenchmark(DictionarySubscriptDefault) @@ -264,6 +270,8 @@ registerBenchmark(QueueGeneric) registerBenchmark(QueueConcrete) registerBenchmark(RC4Test) registerBenchmark(RGBHistogram) +registerBenchmark(RandomShuffle) +registerBenchmark(RandomValues) registerBenchmark(RangeAssignment) registerBenchmark(RangeIteration) registerBenchmark(RangeReplaceableCollectionPlusDefault) diff --git a/cmake/modules/AddSwift.cmake b/cmake/modules/AddSwift.cmake index bbcd66816051a..c1c90dd62e73a 100644 --- a/cmake/modules/AddSwift.cmake +++ b/cmake/modules/AddSwift.cmake @@ -1,6 +1,7 @@ include(SwiftList) include(SwiftXcodeSupport) include(SwiftWindowsSupport) +include(SwiftAndroidSupport) # SWIFTLIB_DIR is the directory in the build tree where Swift resource files # should be placed. Note that $CMAKE_CFG_INTDIR expands to "." for @@ -132,9 +133,9 @@ function(_add_variant_c_compile_link_flags) if("${CFLAGS_SDK}" STREQUAL "ANDROID") list(APPEND result - "--sysroot=${SWIFT_ANDROID_SDK_PATH}" + "--sysroot=${SWIFT_SDK_ANDROID_ARCH_${CFLAGS_ARCH}_PATH}" # Use the linker included in the Android NDK. - "-B" "${SWIFT_ANDROID_PREBUILT_PATH}/arm-linux-androideabi/bin/") + "-B" "${SWIFT_SDK_ANDROID_ARCH_${CFLAGS_ARCH}_NDK_PREBUILT_PATH}/${SWIFT_SDK_ANDROID_ARCH_${CFLAGS_ARCH}_NDK_TRIPLE}/bin/") endif() if(IS_DARWIN) @@ -227,12 +228,12 @@ function(_add_variant_c_compile_flags) if(NOT "${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC") list(APPEND result -Xclang;--dependent-lib=oldnames) # TODO(compnerd) handle /MT, /MTd, /MD, /MDd - if("${CMAKE_BUILD_TYPE}" STREQUAL "RELEASE") - list(APPEND result "-D_MD") - list(APPEND result -Xclang;--dependent-lib=msvcrt) - else() + if("${CMAKE_BUILD_TYPE}" STREQUAL "DEBUG") list(APPEND result "-D_MDd") list(APPEND result -Xclang;--dependent-lib=msvcrtd) + else() + list(APPEND result "-D_MD") + list(APPEND result -Xclang;--dependent-lib=msvcrt) endif() endif() @@ -287,12 +288,11 @@ function(_add_variant_c_compile_flags) endif() if("${CFLAGS_SDK}" STREQUAL "ANDROID") - # FIXME: Instead of hardcoding paths in the Android NDK, these paths should - # be passed in via ENV, as with the Windows build. - list(APPEND result - "-I${SWIFT_ANDROID_NDK_PATH}/sources/cxx-stl/llvm-libc++/include" - "-I${SWIFT_ANDROID_NDK_PATH}/sources/cxx-stl/llvm-libc++abi/include" - "-I${SWIFT_ANDROID_NDK_PATH}/sources/android/support/include") + swift_android_include_for_arch("${CFLAGS_ARCH}" "${CFLAGS_ARCH}_INCLUDE") + foreach(path IN LISTS ${CFLAGS_ARCH}_INCLUDE) + list(APPEND result "\"${CMAKE_INCLUDE_FLAG_C}${path}\"") + endforeach() + list(APPEND result "-D__ANDROID_API__=${SWIFT_ANDROID_API_LEVEL}") endif() set("${CFLAGS_RESULT_VAR_NAME}" "${result}" PARENT_SCOPE) @@ -303,7 +303,9 @@ function(_add_variant_swift_compile_flags set(result ${${result_var_name}}) # On Windows, we don't set SWIFT_SDK_WINDOWS_PATH_ARCH_{ARCH}_PATH, so don't include it. - if (NOT "${CMAKE_SYSTEM_NAME}" STREQUAL "Windows") + # On Android the sdk is split to two different paths for includes and libs, so these + # need to be set manually. + if (NOT "${sdk}" STREQUAL "WINDOWS" AND NOT "${sdk}" STREQUAL "ANDROID") list(APPEND result "-sdk" "${SWIFT_SDK_${sdk}_ARCH_${arch}_PATH}") endif() @@ -316,6 +318,13 @@ function(_add_variant_swift_compile_flags "-target" "${SWIFT_SDK_${sdk}_ARCH_${arch}_TRIPLE}") endif() + if("${sdk}" STREQUAL "ANDROID") + swift_android_include_for_arch(${arch} ${arch}_swift_include) + foreach(path IN LISTS ${arch}_swift_include) + list(APPEND result "\"${CMAKE_INCLUDE_FLAG_C}${path}\"") + endforeach() + endif() + if(NOT BUILD_STANDALONE) list(APPEND result "-resource-dir" "${SWIFTLIB_DIR}") endif() @@ -402,9 +411,10 @@ function(_add_variant_link_flags) list(APPEND result "-ldl" "-llog" "-latomic" "-licudataswift" "-licui18nswift" "-licuucswift" "${SWIFT_ANDROID_NDK_PATH}/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/libc++_shared.so") - list(APPEND library_search_directories - "${SWIFT_ANDROID_PREBUILT_PATH}/arm-linux-androideabi/lib/armv7-a" - "${SWIFT_ANDROID_PREBUILT_PATH}/lib/gcc/arm-linux-androideabi/${SWIFT_ANDROID_NDK_GCC_VERSION}.x") + swift_android_lib_for_arch(${LFLAGS_ARCH} ${LFLAGS_ARCH}_LIB) + foreach(path IN LISTS ${LFLAGS_ARCH}_LIB) + list(APPEND library_search_directories ${path}) + endforeach() else() # If lto is enabled, we need to add the object path flag so that the LTO code # generator leaves the intermediate object file in a place where it will not @@ -425,13 +435,13 @@ function(_add_variant_link_flags) list(APPEND library_search_directories "${SWIFT_${sdk}_${arch}_ICU_UC_LIBDIR}") endif() if(NOT "${SWIFT_${LFLAGS_SDK}_${LFLAGS_ARCH}_ICU_I18N}" STREQUAL "") - get_filename_component(SWIFT_${sdk}_${arch}_ICU_I18N_LIBDIR "${SWIFT_${sdk}_${arch}_IC_I18N}" DIRECTORY) + get_filename_component(SWIFT_${sdk}_${arch}_ICU_I18N_LIBDIR "${SWIFT_${sdk}_${arch}_ICU_I18N}" DIRECTORY) list(APPEND library_search_directories "${SWIFT_${sdk}_${arch}_ICU_I18N_LIBDIR}") endif() if(NOT SWIFT_COMPILER_IS_MSVC_LIKE) + # FIXME: On Apple platforms, find_program needs to look for "ld64.lld" find_program(LDLLD_PATH "ld.lld") - # Strangely, macOS finds lld and then can't find it when using -fuse-ld= if((SWIFT_ENABLE_LLD_LINKER AND LDLLD_PATH AND NOT APPLE) OR ("${LFLAGS_SDK}" STREQUAL "WINDOWS" AND NOT "${CMAKE_SYSTEM_NAME}" STREQUAL "WINDOWS")) @@ -644,15 +654,47 @@ endfunction() # Sources to add into this library function(_add_swift_library_single target name) set(SWIFTLIB_SINGLE_options - SHARED STATIC OBJECT_LIBRARY IS_STDLIB IS_STDLIB_CORE IS_SDK_OVERLAY - TARGET_LIBRARY FORCE_BUILD_FOR_HOST_SDK - API_NOTES_NON_OVERLAY DONT_EMBED_BITCODE FORCE_BUILD_OPTIMIZED NOSWIFTRT) - cmake_parse_arguments(SWIFTLIB_SINGLE - "${SWIFTLIB_SINGLE_options}" - "MODULE_TARGET;SDK;ARCHITECTURE;INSTALL_IN_COMPONENT;DEPLOYMENT_VERSION_OSX;DEPLOYMENT_VERSION_IOS;DEPLOYMENT_VERSION_TVOS;DEPLOYMENT_VERSION_WATCHOS" - "DEPENDS;LINK_LIBRARIES;FRAMEWORK_DEPENDS;FRAMEWORK_DEPENDS_WEAK;LLVM_COMPONENT_DEPENDS;C_COMPILE_FLAGS;SWIFT_COMPILE_FLAGS;LINK_FLAGS;PRIVATE_LINK_LIBRARIES;INTERFACE_LINK_LIBRARIES;INCORPORATE_OBJECT_LIBRARIES;INCORPORATE_OBJECT_LIBRARIES_SHARED_ONLY;FILE_DEPENDS" - ${ARGN}) + API_NOTES_NON_OVERLAY + DONT_EMBED_BITCODE + FORCE_BUILD_FOR_HOST_SDK + FORCE_BUILD_OPTIMIZED + IS_SDK_OVERLAY + IS_STDLIB + IS_STDLIB_CORE + NOSWIFTRT + OBJECT_LIBRARY + SHARED + STATIC + TARGET_LIBRARY) + set(SWIFTLIB_SINGLE_single_parameter_options + ARCHITECTURE + DEPLOYMENT_VERSION_IOS + DEPLOYMENT_VERSION_OSX + DEPLOYMENT_VERSION_TVOS + DEPLOYMENT_VERSION_WATCHOS + INSTALL_IN_COMPONENT + MODULE_TARGET + SDK) + set(SWIFTLIB_SINGLE_multiple_parameter_options + C_COMPILE_FLAGS + DEPENDS + FILE_DEPENDS + FRAMEWORK_DEPENDS + FRAMEWORK_DEPENDS_WEAK + INCORPORATE_OBJECT_LIBRARIES + INCORPORATE_OBJECT_LIBRARIES_SHARED_ONLY + INTERFACE_LINK_LIBRARIES + LINK_FLAGS + LINK_LIBRARIES + LLVM_COMPONENT_DEPENDS + PRIVATE_LINK_LIBRARIES + SWIFT_COMPILE_FLAGS) + cmake_parse_arguments(SWIFTLIB_SINGLE + "${SWIFTLIB_SINGLE_options}" + "${SWIFTLIB_SINGLE_single_parameter_options}" + "${SWIFTLIB_SINGLE_multiple_parameter_options}" + ${ARGN}) set(SWIFTLIB_SINGLE_SOURCES ${SWIFTLIB_SINGLE_UNPARSED_ARGUMENTS}) translate_flags(SWIFTLIB_SINGLE "${SWIFTLIB_SINGLE_options}") @@ -760,6 +802,7 @@ function(_add_swift_library_single target name) swift_windows_generate_sdk_vfs_overlay(SWIFTLIB_SINGLE_VFS_OVERLAY_FLAGS) foreach(flag ${SWIFTLIB_SINGLE_VFS_OVERLAY_FLAGS}) list(APPEND SWIFTLIB_SINGLE_SWIFT_COMPILE_FLAGS -Xcc;${flag}) + list(APPEND SWIFTLIB_SINGLE_C_COMPILE_FLAGS ${flag}) endforeach() foreach(directory ${SWIFTLIB_INCLUDE}) list(APPEND SWIFTLIB_SINGLE_SWIFT_COMPILE_FLAGS -Xfrontend;-I${directory}) @@ -810,6 +853,8 @@ function(_add_swift_library_single target name) if(NOT "${SWIFTLIB_SINGLE_MODULE_TARGET}" STREQUAL "" AND NOT "${swift_module_dependency_target}" STREQUAL "") add_custom_target("${SWIFTLIB_SINGLE_MODULE_TARGET}" DEPENDS ${swift_module_dependency_target}) + set_target_properties("${SWIFTLIB_SINGLE_MODULE_TARGET}" PROPERTIES + FOLDER "Swift libraries/Modules") endif() # For standalone overlay builds to work @@ -1394,15 +1439,61 @@ endfunction() # Sources to add into this library. function(add_swift_library name) set(SWIFTLIB_options - SHARED STATIC OBJECT_LIBRARY IS_STDLIB IS_STDLIB_CORE IS_SDK_OVERLAY - TARGET_LIBRARY FORCE_BUILD_FOR_HOST_SDK - API_NOTES_NON_OVERLAY DONT_EMBED_BITCODE HAS_SWIFT_CONTENT - FORCE_BUILD_OPTIMIZED NOSWIFTRT) + API_NOTES_NON_OVERLAY + DONT_EMBED_BITCODE + FORCE_BUILD_FOR_HOST_SDK + FORCE_BUILD_OPTIMIZED + HAS_SWIFT_CONTENT + IS_SDK_OVERLAY + IS_STDLIB + IS_STDLIB_CORE + NOSWIFTRT + OBJECT_LIBRARY + SHARED + STATIC + TARGET_LIBRARY) + set(SWIFTLIB_single_parameter_options + DEPLOYMENT_VERSION_IOS + DEPLOYMENT_VERSION_OSX + DEPLOYMENT_VERSION_TVOS + DEPLOYMENT_VERSION_WATCHOS + INSTALL_IN_COMPONENT) + set(SWIFTLIB_multiple_parameter_options + C_COMPILE_FLAGS + DEPENDS + FILE_DEPENDS + FRAMEWORK_DEPENDS + FRAMEWORK_DEPENDS_IOS_TVOS + FRAMEWORK_DEPENDS_OSX + FRAMEWORK_DEPENDS_WEAK + INCORPORATE_OBJECT_LIBRARIES + INCORPORATE_OBJECT_LIBRARIES_SHARED_ONLY + INTERFACE_LINK_LIBRARIES + LINK_FLAGS + LINK_LIBRARIES + LLVM_COMPONENT_DEPENDS + PRIVATE_LINK_LIBRARIES + SWIFT_COMPILE_FLAGS + SWIFT_COMPILE_FLAGS_IOS + SWIFT_COMPILE_FLAGS_OSX + SWIFT_COMPILE_FLAGS_TVOS + SWIFT_COMPILE_FLAGS_WATCHOS + SWIFT_MODULE_DEPENDS + SWIFT_MODULE_DEPENDS_CYGWIN + SWIFT_MODULE_DEPENDS_FREEBSD + SWIFT_MODULE_DEPENDS_HAIKU + SWIFT_MODULE_DEPENDS_IOS + SWIFT_MODULE_DEPENDS_LINUX + SWIFT_MODULE_DEPENDS_OSX + SWIFT_MODULE_DEPENDS_TVOS + SWIFT_MODULE_DEPENDS_WATCHOS + TARGET_SDKS) + cmake_parse_arguments(SWIFTLIB - "${SWIFTLIB_options}" - "INSTALL_IN_COMPONENT;DEPLOYMENT_VERSION_OSX;DEPLOYMENT_VERSION_IOS;DEPLOYMENT_VERSION_TVOS;DEPLOYMENT_VERSION_WATCHOS" - "DEPENDS;LINK_LIBRARIES;SWIFT_MODULE_DEPENDS;SWIFT_MODULE_DEPENDS_OSX;SWIFT_MODULE_DEPENDS_IOS;SWIFT_MODULE_DEPENDS_TVOS;SWIFT_MODULE_DEPENDS_WATCHOS;SWIFT_MODULE_DEPENDS_FREEBSD;SWIFT_MODULE_DEPENDS_LINUX;SWIFT_MODULE_DEPENDS_CYGWIN;SWIFT_MODULE_DEPENDS_HAIKU;FRAMEWORK_DEPENDS;FRAMEWORK_DEPENDS_WEAK;FRAMEWORK_DEPENDS_OSX;FRAMEWORK_DEPENDS_IOS_TVOS;LLVM_COMPONENT_DEPENDS;FILE_DEPENDS;TARGET_SDKS;C_COMPILE_FLAGS;SWIFT_COMPILE_FLAGS;SWIFT_COMPILE_FLAGS_OSX;SWIFT_COMPILE_FLAGS_IOS;SWIFT_COMPILE_FLAGS_TVOS;SWIFT_COMPILE_FLAGS_WATCHOS;LINK_FLAGS;PRIVATE_LINK_LIBRARIES;INTERFACE_LINK_LIBRARIES;INCORPORATE_OBJECT_LIBRARIES;INCORPORATE_OBJECT_LIBRARIES_SHARED_ONLY" - ${ARGN}) + "${SWIFTLIB_options}" + "${SWIFTLIB_single_parameter_options}" + "${SWIFTLIB_multiple_parameter_options}" + ${ARGN}) set(SWIFTLIB_SOURCES ${SWIFTLIB_UNPARSED_ARGUMENTS}) # Infer arguments. @@ -1639,6 +1730,19 @@ function(add_swift_library name) endif() endif() + # We unconditionally removed "-z,defs" from CMAKE_SHARED_LINKER_FLAGS in + # swift_common_standalone_build_config_llvm within SwiftSharedCMakeConfig.cmake, + # where it was added by a call to HandleLLVMOptions. + # + # Rather than applying it to all targets and libraries, we here add it back to + # supported targets and libraries only. + # This is needed for ELF targets only; however, RemoteMirror needs to build + # with undefined symbols. + if("${SWIFT_SDK_${LFLAGS_SDK}_OBJECT_FORMAT}" STREQUAL "ELF" + AND NOT "${name}" STREQUAL "swiftRemoteMirror") + list(APPEND swiftlib_link_flags_all "-Wl,-z,defs") + endif() + # Add this library variant. _add_swift_library_single( ${VARIANT_NAME} @@ -2212,6 +2316,19 @@ function(add_swift_host_tool executable) endif() endfunction() +# This declares a swift host tool that links with libfuzzer. +function(add_swift_fuzzer_host_tool executable) + # First create our target. We do not actually parse the argument since we do + # not care about the arguments, we just pass them all through to + # add_swift_host_tool. + add_swift_host_tool(${executable} ${ARGN}) + + # Then make sure that we pass the -fsanitize=fuzzer flag both on the cflags + # and cxx flags line. + target_compile_options(${executable} PRIVATE "-fsanitize=fuzzer") + target_link_libraries(${executable} PRIVATE "-fsanitize=fuzzer") +endfunction() + macro(add_swift_tool_symlink name dest component) add_llvm_tool_symlink(${name} ${dest} ALWAYS_GENERATE) llvm_install_symlink(${name} ${dest} ALWAYS_GENERATE COMPONENT ${component}) diff --git a/cmake/modules/SwiftAndroidSupport.cmake b/cmake/modules/SwiftAndroidSupport.cmake new file mode 100644 index 0000000000000..7d1a184e304a0 --- /dev/null +++ b/cmake/modules/SwiftAndroidSupport.cmake @@ -0,0 +1,23 @@ +function(swift_android_include_for_arch arch var) + set(paths) + list(APPEND paths + "${SWIFT_ANDROID_NDK_PATH}/sources/cxx-stl/llvm-libc++/include" + "${SWIFT_ANDROID_NDK_PATH}/sources/cxx-stl/llvm-libc++abi/include" + "${SWIFT_ANDROID_NDK_PATH}/sources/android/support/include" + "${SWIFT_ANDROID_NDK_PATH}/sysroot/usr/include" + "${SWIFT_ANDROID_NDK_PATH}/sysroot/usr/include/${SWIFT_SDK_ANDROID_ARCH_${arch}_NDK_TRIPLE}") + set(${var} ${paths} PARENT_SCOPE) +endfunction() + +function(swift_android_lib_for_arch arch var) + set(paths) + + if(arch STREQUAL armv7) + list(APPEND paths + "${SWIFT_SDK_ANDROID_ARCH_${arch}_NDK_PREBUILT_PATH}/${SWIFT_SDK_ANDROID_ARCH_${arch}_NDK_TRIPLE}/lib/armv7-a") + endif() + + list(APPEND paths + "${SWIFT_SDK_ANDROID_ARCH_${arch}_NDK_PREBUILT_PATH}/lib/gcc/${SWIFT_SDK_ANDROID_ARCH_${arch}_NDK_TRIPLE}/${SWIFT_ANDROID_NDK_GCC_VERSION}.x") + set(${var} ${paths} PARENT_SCOPE) +endfunction() diff --git a/cmake/modules/SwiftConfigureSDK.cmake b/cmake/modules/SwiftConfigureSDK.cmake index 56277f7b89528..6635ccb58c48e 100644 --- a/cmake/modules/SwiftConfigureSDK.cmake +++ b/cmake/modules/SwiftConfigureSDK.cmake @@ -6,6 +6,7 @@ set(SWIFT_CONFIGURED_SDKS) include(SwiftWindowsSupport) +include(SwiftAndroidSupport) # Report the given SDK to the user. function(_report_sdk prefix) @@ -14,6 +15,11 @@ function(_report_sdk prefix) message(STATUS " UCRT Version: $ENV{UCRTVersion}") message(STATUS " UCRT SDK Dir: $ENV{UniversalCRTSdkDir}") message(STATUS " VC Dir: $ENV{VCToolsInstallDir}") + if("${CMAKE_BUILD_TYPE}" STREQUAL "DEBUG") + message(STATUS " ${CMAKE_BUILD_TYPE} VC++ CRT: MDd") + else() + message(STATUS " ${CMAKE_BUILD_TYPE} VC++ CRT: MD") + endif() foreach(arch ${SWIFT_SDK_${prefix}_ARCHITECTURES}) swift_windows_include_for_arch(${arch} ${arch}_INCLUDE) @@ -21,6 +27,14 @@ function(_report_sdk prefix) message(STATUS " ${arch} INCLUDE: ${${arch}_INCLUDE}") message(STATUS " ${arch} LIB: ${${arch}_LIB}") endforeach() + elseif("${prefix}" STREQUAL "ANDROID") + message(STATUS " NDK Dir: $ENV{SWIFT_ANDROID_NDK_PATH}") + foreach(arch ${SWIFT_SDK_${prefix}_ARCHITECTURES}) + swift_android_include_for_arch(${arch} ${arch}_INCLUDE) + swift_android_lib_for_arch(${arch} ${arch}_LIB) + message(STATUS " ${arch} INCLUDE: ${${arch}_INCLUDE}") + message(STATUS " ${arch} LIB: ${${arch}_LIB}") + endforeach() else() foreach(arch ${SWIFT_SDK_${prefix}_ARCHITECTURES}) message(STATUS " ${arch} Path: ${SWIFT_SDK_${prefix}_ARCH_${arch}_PATH}") @@ -33,6 +47,15 @@ function(_report_sdk prefix) message(STATUS " Version min name: ${SWIFT_SDK_${prefix}_VERSION_MIN_NAME}") message(STATUS " Triple name: ${SWIFT_SDK_${prefix}_TRIPLE_NAME}") message(STATUS " Architectures: ${SWIFT_SDK_${prefix}_ARCHITECTURES}") + is_darwin_based_sdk(${prefix} IS_DARWIN_BASED_SDK) + if(NOT ${IS_DARWIN_BASED_SDK}) + foreach(arch ${SWIFT_SDK_${prefix}_ARCHITECTURES}) + message(STATUS " ICU i18n INCLUDE (${arch}): ${SWIFT_${prefix}_${arch}_ICU_I18N_INCLUDE}") + message(STATUS " ICU i18n LIB (${arch}): ${SWIFT_${prefix}_${arch}_ICU_I18N}") + message(STATUS " ICU unicode INCLUDE (${arch}): ${SWIFT_${prefix}_${arch}_ICU_UC_INCLUDE}") + message(STATUS " ICU unicode LIB (${arch}): ${SWIFT_${prefix}_${arch}_ICU_UC}") + endforeach() + endif() message(STATUS " Object Format: ${SWIFT_SDK_${prefix}_OBJECT_FORMAT}") foreach(arch ${SWIFT_SDK_${prefix}_ARCHITECTURES}) if(SWIFT_SDK_${prefix}_ARCH_${arch}_LINKER) @@ -137,32 +160,64 @@ macro(configure_sdk_darwin _report_sdk("${prefix}") endmacro() +macro(_configure_sdk_android_specific + prefix name lib_subdir triple_name architectures triple sdkpath) + + foreach(arch ${architectures}) + if("${arch}" STREQUAL "armv7") + set(SWIFT_SDK_ANDROID_ARCH_${arch}_NDK_TRIPLE "arm-linux-androideabi") + set(SWIFT_SDK_ANDROID_ARCH_${arch}_ALT_SPELLING "arm") + else() + message(FATAL_ERROR "unkonwn arch for android SDK: ${arch}") + endif() + + # Get the prebuilt suffix to create the correct toolchain path when using the NDK + if("${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Darwin") + set(_swift_android_prebuilt_suffix "darwin-x86_64") + elseif("${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Linux") + set(_swift_android_prebuilt_suffix "linux-x86_64") + endif() + set(SWIFT_SDK_ANDROID_ARCH_${arch}_NDK_PREBUILT_PATH + "${SWIFT_ANDROID_NDK_PATH}/toolchains/${SWIFT_SDK_ANDROID_ARCH_${arch}_NDK_TRIPLE}-${SWIFT_ANDROID_NDK_GCC_VERSION}/prebuilt/${_swift_android_prebuilt_suffix}") + + # Resolve the correct linker based on the file name of CMAKE_LINKER (being 'ld' or 'ld.gold' the options) + get_filename_component(SWIFT_ANDROID_LINKER_NAME "${CMAKE_LINKER}" NAME) + set(SWIFT_SDK_ANDROID_ARCH_${arch}_LINKER + "${SWIFT_SDK_ANDROID_ARCH_${arch}_NDK_PREBUILT_PATH}/bin/${SWIFT_SDK_ANDROID_ARCH_${arch}_NDK_TRIPLE}-${SWIFT_ANDROID_LINKER_NAME}") + endforeach() +endmacro() + macro(configure_sdk_unix - prefix name lib_subdir triple_name arch triple sdkpath) + prefix name lib_subdir triple_name architectures triple sdkpath) # Note: this has to be implemented as a macro because it sets global # variables. - # Todo: this only supports building an SDK for one target arch only. set(SWIFT_SDK_${prefix}_NAME "${name}") - set(SWIFT_SDK_${prefix}_ARCH_${arch}_PATH "${sdkpath}") set(SWIFT_SDK_${prefix}_VERSION "don't use") set(SWIFT_SDK_${prefix}_BUILD_NUMBER "don't use") set(SWIFT_SDK_${prefix}_DEPLOYMENT_VERSION "") set(SWIFT_SDK_${prefix}_LIB_SUBDIR "${lib_subdir}") set(SWIFT_SDK_${prefix}_VERSION_MIN_NAME "") set(SWIFT_SDK_${prefix}_TRIPLE_NAME "${triple_name}") - set(SWIFT_SDK_${prefix}_ARCHITECTURES "${arch}") + set(SWIFT_SDK_${prefix}_ARCHITECTURES "${architectures}") if("${prefix}" STREQUAL "CYGWIN") set(SWIFT_SDK_${prefix}_OBJECT_FORMAT "COFF") else() set(SWIFT_SDK_${prefix}_OBJECT_FORMAT "ELF") endif() - set(SWIFT_SDK_${prefix}_ARCH_${arch}_TRIPLE "${triple}") + foreach(arch ${architectures}) + set(SWIFT_SDK_${prefix}_ARCH_${arch}_PATH "${sdkpath}") + set(SWIFT_SDK_${prefix}_ARCH_${arch}_TRIPLE "${triple}") + endforeach() # Add this to the list of known SDKs. list(APPEND SWIFT_CONFIGURED_SDKS "${prefix}") + if("${prefix}" STREQUAL "ANDROID") + _configure_sdk_android_specific(${prefix} ${name} ${lib_subdir} ${triple_name} ${architectures} ${triple} ${sdkpath}) + endif() + _report_sdk("${prefix}") endmacro() diff --git a/cmake/modules/SwiftHandleGybSources.cmake b/cmake/modules/SwiftHandleGybSources.cmake index ebf4de94a20ef..8d11417973124 100644 --- a/cmake/modules/SwiftHandleGybSources.cmake +++ b/cmake/modules/SwiftHandleGybSources.cmake @@ -111,12 +111,12 @@ function(handle_gyb_sources dependency_out_var_name sources_var_name arch) "${SWIFT_SOURCE_DIR}/utils/UnicodeData/GraphemeBreakProperty.txt" "${SWIFT_SOURCE_DIR}/utils/UnicodeData/GraphemeBreakTest.txt" "${SWIFT_SOURCE_DIR}/utils/gyb_stdlib_support.py" - "${SWIFT_SOURCE_DIR}/utils/gyb_stdlib_unittest_support.py" "${SWIFT_SOURCE_DIR}/utils/gyb_syntax_support/__init__.py" "${SWIFT_SOURCE_DIR}/utils/gyb_syntax_support/Child.py" "${SWIFT_SOURCE_DIR}/utils/gyb_syntax_support/kinds.py" "${SWIFT_SOURCE_DIR}/utils/gyb_syntax_support/Node.py" "${SWIFT_SOURCE_DIR}/utils/gyb_syntax_support/AttributeNodes.py" + "${SWIFT_SOURCE_DIR}/utils/gyb_syntax_support/AvailabilityNodes.py" "${SWIFT_SOURCE_DIR}/utils/gyb_syntax_support/CommonNodes.py" "${SWIFT_SOURCE_DIR}/utils/gyb_syntax_support/DeclNodes.py" "${SWIFT_SOURCE_DIR}/utils/gyb_syntax_support/ExprNodes.py" diff --git a/cmake/modules/SwiftSharedCMakeConfig.cmake b/cmake/modules/SwiftSharedCMakeConfig.cmake index 8edeed4fb139c..f544d5b894ced 100644 --- a/cmake/modules/SwiftSharedCMakeConfig.cmake +++ b/cmake/modules/SwiftSharedCMakeConfig.cmake @@ -78,10 +78,19 @@ macro(swift_common_standalone_build_config_llvm product is_cross_compiling) include(AddSwiftTableGen) # This imports TableGen from LLVM. include(HandleLLVMOptions) - # HACK: this ugly tweaking is to prevent the propagation of the flag from LLVM - # into swift. The use of this flag pollutes all targets, and we are not able - # to remove it on a per-target basis which breaks cross-compilation. + # HACK: Not all targets support -z,defs as a linker flag. + # + # Normally, LLVM would only add it as an option for known ELF targets; + # however, due to the custom scheme Swift uses for cross-compilation, the + # CMAKE_SHARED_LINKER_FLAGS are determined based on the host system and + # then applied to all targets. This causes issues in cross-compiling to + # Windows from a Linux host. + # + # To work around this, we unconditionally remove the flag here and then + # selectively add it to the per-target link flags; this is currently done + # in add_swift_library within AddSwift.cmake. string(REGEX REPLACE "-Wl,-z,defs" "" CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS}") + string(REGEX REPLACE "-Wl,-z,nodelete" "" CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS}") set(PACKAGE_VERSION "${LLVM_PACKAGE_VERSION}") string(REGEX REPLACE "([0-9]+)\\.[0-9]+(\\.[0-9]+)?" "\\1" PACKAGE_VERSION_MAJOR @@ -201,6 +210,9 @@ macro(swift_common_standalone_build_config product is_cross_compiling) swift_common_standalone_build_config_llvm(${product} ${is_cross_compiling}) swift_common_standalone_build_config_clang(${product} ${is_cross_compiling}) swift_common_standalone_build_config_cmark(${product}) + + # Enable groups for IDE generators (Xcode and MSVC). + set_property(GLOBAL PROPERTY USE_FOLDERS ON) endmacro() # Common cmake project config for unified builds. diff --git a/cmake/modules/SwiftWindowsSupport.cmake b/cmake/modules/SwiftWindowsSupport.cmake index f25f57d88599d..6bd0fb5042ba4 100644 --- a/cmake/modules/SwiftWindowsSupport.cmake +++ b/cmake/modules/SwiftWindowsSupport.cmake @@ -71,7 +71,7 @@ function(swift_windows_generate_sdk_vfs_overlay flags) @ONLY) set(${flags} - -ivfsoverlay;"${CMAKE_BINARY_DIR}/windows-sdk-vfs-overlay.yaml" + -Xclang;-ivfsoverlay;-Xclang;"${CMAKE_BINARY_DIR}/windows-sdk-vfs-overlay.yaml" PARENT_SCOPE) endfunction() diff --git a/docs/ABI/Mangling.rst b/docs/ABI/Mangling.rst index 95accf67aa599..9754ceb5883aa 100644 --- a/docs/ABI/Mangling.rst +++ b/docs/ABI/Mangling.rst @@ -789,6 +789,7 @@ Some kinds need arguments, which precede ``Tf``. ARG-SPEC-KIND ::= 'c' // Consumes n 'type' arguments which are closed over types in argument order // and one 'identifier' argument which is the closure symbol name ARG-SPEC-KIND ::= 'p' CONST-PROP // Constant propagated argument + ARG-SPEC-KIND ::= 'e' 'D'? 'G'? 'X'? // Generic argument, with optional dead, owned=>guaranteed or exploded-specifier ARG-SPEC-KIND ::= 'd' 'G'? 'X'? // Dead argument, with optional owned=>guaranteed or exploded-specifier ARG-SPEC-KIND ::= 'g' 'X'? // Owned => Guaranteed,, with optional exploded-specifier ARG-SPEC-KIND ::= 'x' // Exploded diff --git a/docs/Android.md b/docs/Android.md index ff5ea1f960f0d..8b97f1691dfb6 100644 --- a/docs/Android.md +++ b/docs/Android.md @@ -34,7 +34,7 @@ To follow along with this guide, you'll need: The stdlib is currently only buildable for Android from a Linux environment. Before attempting to build for Android, please make sure you are able to build for Linux by following the instructions in the Swift project README. -2. The latest version of the Android NDK (r14 at the time of this writing), +2. The latest version of the Android NDK (r16 at the time of this writing), available to download here: http://developer.android.com/ndk/downloads/index.html. 3. An Android device with remote debugging enabled. We require remote @@ -51,12 +51,6 @@ needed to `apt-get install libicu-dev icu-devtools`. Similarly, building the Swift stdlib for Android requires the libiconv and libicu libraries. However, you'll need versions of these libraries that work on Android devices. -You may download prebuilt copies of these dependencies, built for Ubuntu 15.10 -and Android NDK r13. Click [here](https://github.com/SwiftAndroid/libiconv-libicu-android/releases/download/android-ndk-r13/libiconv-libicu-armeabi-v7a-ubuntu-15.10-ndk-r13.tar.gz) -to download, then unzip the archive file. - -Alternatively, you may choose to build libiconv and libicu for Android yourself. -If you are using Ubuntu 16.04, it is suggested that you build them yourself, as the prebuilt 15.10 copies have not been tested on 16.04. The steps are as follows: 1. Ensure you have `curl`, `autoconf`, `automake`, `libtool`, and @@ -67,144 +61,29 @@ The steps are as follows: 3. From the command-line, run `which ndk-build`. Confirm that the path to the `ndk-build` executable in the Android NDK you downloaded is displayed. If not, you may need to add the Android NDK directory to your `PATH`. - -#### Adding `swift` suffix to icu libraries - -Android OS has its own icu libraries which are different from the ones we are generating and conflicts with ours. In order to resolve this conflict when linking Swift stdlib dependencies in Android devices, we need to change our icu libraries names by adding `swift` suffix at the end. To do that, enter the `libiconv-libicu-android` directory on the command line, then do the following steps: - -Create `armeabi-v7a` directory and generate `icu` directory by uncompressing `icu4c-55_1-src.tgz` file, as following: - -``` -$ mkdir armeabi-v7a -$ cd armeabi-v7a -$ tar xvf ../icu4c-55_1-src.tgz -``` - -**Edit icu configure file** - -In order to prevent icu configuration from adding `swift` suffix to internal symbols as well, edit `icu/source/configure` file and change: - -``` -if test "$ICULIBSUFFIX" != "" -then - U_HAVE_LIB_SUFFIX=1 - ICULIBSUFFIXCNAME=`echo _$ICULIBSUFFIX | sed 's/^A-Za-z0-9_/_/g'` - UCONFIG_CPPFLAGS="${UCONFIG_CPPFLAGS} -DU_HAVE_LIB_SUFFIX=1 -DU_LIB_SUFFIX_C_NAME=${ICULIBSUFFIXCNAME} " -else - U_HAVE_LIB_SUFFIX=0 -fi -``` - -To: - -``` -#if test "$ICULIBSUFFIX" != "" -#then -# U_HAVE_LIB_SUFFIX=1 -# ICULIBSUFFIXCNAME=`echo _$ICULIBSUFFIX | sed 's/^A-Za-z0-9_/_/g'` -# UCONFIG_CPPFLAGS="${UCONFIG_CPPFLAGS} -DU_HAVE_LIB_SUFFIX=1 -DU_LIB_SUFFIX_C_NAME=${ICULIBSUFFIXCNAME} " -#else - U_HAVE_LIB_SUFFIX=0 -#fi -``` - -**Edit build.sh file** - -Now go back to the `libiconv-libicu-android` root directory: - -``` -$ cd .. -``` - -In order to prevent `build.sh` from regenerating the icu directory and override the `configure` file we just edited, edit `build.sh` and change: - -``` -[ -e ../icu4c-55_1-src.tgz ] || exit 1 -tar xvf ../icu4c-55_1-src.tgz -``` - -To: - -``` -#[ -e ../icu4c-55_1-src.tgz ] || exit 1 -#tar xvf ../icu4c-55_1-src.tgz -``` - -In order to check the existence of `libicuucswift.so` file instead of `libicuuc.so`, change: - -``` -[ -e libicuuc.so ] || { -``` - -To: - -``` -[ -e libicuucswift.so ] || { -``` - -Then add the `swift` suffix to the call of `configure` file, by changing: - -``` -./configure \ ---host=arm-linux-androideabi \ ---prefix=`pwd`/../../ \ ---with-cross-build=`pwd`/cross \ ---enable-static --enable-shared \ -|| exit 1 -``` -To: - -``` -./configure \ ---host=arm-linux-androideabi \ ---prefix=`pwd`/../../ \ ---with-library-suffix=swift \ ---with-cross-build=`pwd`/cross \ ---enable-static --enable-shared \ -|| exit 1 -``` - -Now change the libraries file names on the last step of th build, by adding `swift` at the end of each one, by changing: - -``` -for f in libicudata libicutest libicui18n libicuio libicule libiculx libicutu libicuuc; do -``` - -To: - -``` -for f in libicudataswift libicutestswift libicui18nswift libicuioswift libiculeswift libiculxswift libicutuswift libicuucswift; do -``` - - -**Run build.sh** - -Now we are finally ready to run the `build.sh` file - -``` -$ ./build.sh -``` - -Confirm that the build script created `armeabi-v7a/icu/source/i18n` and - `armeabi-v7a/icu/source/common` directories within your - `libiconv-libicu-android` directory. +4. Change directories into `libiconv-libicu-android`: `cd libiconv-libicu-android` +5. Run the Swift build script: `./build-swift.sh` +6. Confirm that the various `libicuXYZswift.so` libraries are located in the + `armeabi-v7a` directory. ### 2. Building the Swift stdlib for Android Enter your Swift directory, then run the build script, passing paths to the -Android NDK, as well as the directories that contain the `libicuuc.so` and -`libicui18n.so` you downloaded or built in step one: +Android NDK, as well as the directories that contain the `libicuucswift.so` and +`libicui18nswift.so` you downloaded or built in step one: ``` +$ ARM_DIR=path/to/libicu-libiconv-android +$ NDK_PATH=path/to/android-ndk16 $ utils/build-script \ -R \ # Build in ReleaseAssert mode. --android \ # Build for Android. - --android-ndk /path/to/android-ndk-r14 \ # Path to an Android NDK. + --android-ndk $NDK_PATH \ # Path to an Android NDK. --android-api-level 21 \ # The Android API level to target. Swift only supports 21 or greater. - --android-icu-uc /path/to/libicu-android/armeabi-v7a \ - --android-icu-uc-include /path/to/libicu-android/armeabi-v7a/icu/source/common \ - --android-icu-i18n /path/to/libicu-android/armeabi-v7a \ - --android-icu-i18n-include /path/to/libicu-android/armeabi-v7a/icu/source/i18n + --android-icu-uc ${ARM_DIR}/libicuucswift.so \ + --android-icu-uc-include ${ARM_DIR}/icu/source/common \ + --android-icu-i18 ${ARM_DIR}/libicui18nswift.so \ + --android-icu-i18n-include ${ARM_DIR}/icu/source/i18n ``` ### 3. Compiling `hello.swift` to run on an Android device @@ -220,7 +99,7 @@ gold linker in the Android NDK into your `PATH`: ``` $ sudo ln -s \ - /path/to/android-ndk-r14/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/arm-linux-androideabi/bin/ld.gold \ + /path/to/android-ndk-r16/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/arm-linux-androideabi/bin/ld.gold \ /usr/bin/armv7-none-linux-androideabi-ld.gold ``` @@ -228,13 +107,14 @@ Then use the built Swift compiler from the previous step to compile a Swift source file, targeting Android: ``` +$ NDK_PATH="path/to/android-ndk16" $ build/Ninja-ReleaseAssert/swift-linux-x86_64/bin/swiftc \ # The Swift compiler built in the previous step. # The location of the tools used to build Android binaries - -tools-directory /path/to/android-ndk-r14/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/arm-linux-androideabi/bin + -tools-directory ${NDK_PATH}/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/arm-linux-androideabi/bin -target armv7-none-linux-androideabi \ # Targeting android-armv7. - -sdk /path/to/android-ndk-r14/platforms/android-21/arch-arm \ # Use the same NDK path and API version as you used to build the stdlib in the previous step. - -L /path/to/android-ndk-r14/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a \ # Link the Android NDK's libc++ and libgcc. - -L /path/to/android-ndk-r14/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/lib/gcc/arm-linux-androideabi/4.9.x \ + -sdk ${NDK_PATH}/platforms/android-21/arch-arm \ # Use the same NDK path and API version as you used to build the stdlib in the previous step. + -L ${NDK_PATH}/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a \ # Link the Android NDK's libc++ and libgcc. + -L ${NDK_PATH}/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/lib/gcc/arm-linux-androideabi/4.9.x \ hello.swift ``` diff --git a/docs/DriverParseableOutput.rst b/docs/DriverParseableOutput.rst index d267cfbc14627..6e483acc3fade 100644 --- a/docs/DriverParseableOutput.rst +++ b/docs/DriverParseableOutput.rst @@ -33,6 +33,29 @@ This allows the driver to pass as much information as it wants about the ongoing compilation, and programs which parse this data can decide what to use and what to ignore. + +Quasi-PIDs +========== + +In previous versions of this format, certain fields labeled "pid" carry only +legitimate process IDs (PIDs) from the underlying operating system. These PIDs +are used by the driver to associate events that happen to jobs, such as signals +when a job crashes with the file being compiled during that event. Real PIDs are +always positive numbers, in keeping with POSIX semantics and the unsigned +Process ID type on Windows. + +In more recent versions of this format, the Swift driver combines multiple jobs +together into batches, running each batch in a single subprocess. In this mode, +the driver writes messages describing each job as before, but assigns to each a +so-called "quasi-PID" for the "pid" field, which does not correspond to an +operating system process ID. Rather, a quasi-PID is a **negative number** that +can be used **like** a PID, both as a unique identifier for each job that was +run and in order to relate messages and tasks together, but it does not denote a +real process. By using negative numbers for quasi-PIDs, we ensure that no two +jobs will have the same PID: no batch mode job can have the same PID as a real +process. Two jobs with the same PID would violate this uniqueness assumption. + + Message Kinds ============= @@ -46,13 +69,13 @@ Began Message ------------- A "began" message indicates that a new task began. As with all task-based -messages, it will include the task's PID under the "pid" key. It may specify the -task's inputs as an array of paths under the "inputs" key. It may specify the -task's outputs as an array of objects under the "outputs" key. An "outputs" -object will have two fields, a "kind" describing the type of the output, and a -"path" containing the path to the output. A "began" message will specify the -command which was executed under the "command_executable" and "command_arguments" -keys. +messages, it will include the task's PID (or quasi-PID) under the "pid" key. It +may specify the task's inputs as an array of paths under the "inputs" key. It +may specify the task's outputs as an array of objects under the "outputs" +key. An "outputs" object will have two fields, a "kind" describing the type of +the output, and a "path" containing the path to the output. A "began" message +will specify the command which was executed under the "command_executable" and +"command_arguments" keys. Example:: @@ -83,10 +106,10 @@ Finished Message ---------------- A "finished" message indicates that a task finished execution. As with all task- -based messages, it will include the task's PID under the "pid" key. It will -include the exit status of the task under the "exit-status" key. It may include -the stdout/stderr of the task under the "output" key; if this key is missing, -no output was generated by the task. +based messages, it will include the task's PID (or quasi-PID) under the "pid" +key. It will include the exit status of the task under the "exit-status" key. It +may include the stdout/stderr of the task under the "output" key; if this key is +missing, no output was generated by the task. Example:: @@ -102,12 +125,12 @@ Signalled Message ----------------- A "signalled" message indicates that a task exited abnormally due to a signal. -As with all task-based message, it will include the task's PID under the "pid" -key. It may include an error message describing the signal under the -"error-message" key. As with the "finished" message, it may include the +As with all task-based message, it will include the task's PID (or quasi-PID) +under the "pid" key. It may include an error message describing the signal under +the "error-message" key. As with the "finished" message, it may include the stdout/stderr of the task under the "output" key; if this key is missing, no -output was generated by the task. It may include the "signal" key, -the terminating signal number. (This may not be available on all platforms.) +output was generated by the task. It may include the "signal" key, the +terminating signal number. (This may not be available on all platforms.) Example:: diff --git a/docs/Lexicon.rst b/docs/Lexicon.rst index 602d79fc1f93d..4787fd0e5c292 100644 --- a/docs/Lexicon.rst +++ b/docs/Lexicon.rst @@ -339,6 +339,10 @@ source code, tests, and commit messages. See also the `LLVM lexicon`_. script mode The parsing mode that allows top-level imperative code in a source file. + + Sema + Short for 'Semantic Analysis', the compiler pass that performs type checking, + validation, and expression rewriting before SILGen. SIL "Swift Intermediate Language". A high-level IR used by the Swift compiler diff --git a/docs/Random.md b/docs/Random.md new file mode 100644 index 0000000000000..98905f87f21f1 --- /dev/null +++ b/docs/Random.md @@ -0,0 +1,16 @@ +# Random APIs + +More documentation to come. + +## Platform-Specific Default Random + +The implementation of the default random generator varies by platform. The implementation +on each platform must be thread-safe and automatically seeded, and should be +cryptographically secure to the extent possible. Currently supported platforms have the +following implementation details: + +- Apple platforms use `arc4random_buf(3)`. +- Linux, FreeBSD, and other UNIX-like platforms use `getrandom(2)` when available; +otherwise, they read from `/dev/urandom`. +- Fuchsia platforms use `getentropy(3)`. +- Windows paltforms use `BCryptGenRandom`. diff --git a/docs/SIL.rst b/docs/SIL.rst index c2001d9dfa236..5450b8bd53ca5 100644 --- a/docs/SIL.rst +++ b/docs/SIL.rst @@ -2555,7 +2555,7 @@ begin_access :: - sil-instruction ::= 'begin_access' '[' sil-access ']' '[' sil-enforcement ']' sil-operand + sil-instruction ::= 'begin_access' '[' sil-access ']' '[' sil-enforcement ']' '[no_nested_conflict]'? '[builtin]'? sil-operand ':' sil-type sil-access ::= init sil-access ::= read sil-access ::= modify @@ -2564,6 +2564,8 @@ begin_access sil-enforcement ::= static sil-enforcement ::= dynamic sil-enforcement ::= unsafe + %1 = begin_access [read] [unknown] %0 : $*T + // %0 must be of $*T type. Begins an access to the target memory. @@ -2597,6 +2599,19 @@ It must always use ``static`` enforcement. initialized. They may use ``unknown`` enforcement only in the ``raw`` SIL stage. +A ``no_nested_conflict`` access has no potentially conflicting access within +its scope (on any control flow path between it and its corresponding +``end_access``). Consequently, the access will not need to be tracked by the +runtime for the duration of its scope. This access may still conflict with an +outer access scope; therefore may still require dynamic enforcement at a single +point. + +A ``builtin`` access was emitted for a user-controlled Builtin (e.g. the +standard library's KeyPath access). Non-builtin accesses are auto-generated by +the compiler to enforce formal access that derives from the language. A +``builtin`` access is always fully enforced regardless of the compilation mode +because it may be used to enforce access outside of the current module. + end_access `````````` @@ -2610,6 +2625,58 @@ If the ``begin_access`` is ``init`` or ``deinit``, the ``end_access`` may be an ``abort``, indicating that the described transition did not in fact take place. +begin_unpaired_access +````````````````````` + +:: + + sil-instruction ::= 'begin_unpaired_access' '[' sil-access ']' '[' sil-enforcement ']' '[no_nested_conflict]'? '[builtin]'? sil-operand : sil-type, sil-operand : $*Builtin.UnsafeValueBuffer + sil-access ::= init + sil-access ::= read + sil-access ::= modify + sil-access ::= deinit + sil-enforcement ::= unknown + sil-enforcement ::= static + sil-enforcement ::= dynamic + sil-enforcement ::= unsafe + %2 = begin_unpaired_access [read] [dynamic] %0 : $*T, %1 : $*Builtin.UnsafeValueBuffer + // %0 must be of $*T type. + +Begins an access to the target memory. This has the same semantics and obeys all +the same constraints as ``begin_access``. With the following exceptions: + +- ``begin_unpaired_access`` has an additional operand for the scratch buffer + used to uniquely identify this access within its scope. + +- An access initiated by ``begin_unpaired_access`` must end with + ``end_unpaired_access`` unless it has the ``no_nested_conflict`` flag. A + ``begin_unpaired_access`` with ``no_nested_conflict`` is effectively an + instantaneous access with no associated scope. + +- The associated ``end_unpaired_access`` must use the same scratch buffer. + +end_unpaired_access +``````````````````` + +:: + + sil-instruction ::= 'end_unpaired_access' ( '[' 'abort' ']' )? '[' sil-enforcement ']' sil-operand : $*Builtin.UnsafeValueBuffer + sil-enforcement ::= unknown + sil-enforcement ::= static + sil-enforcement ::= dynamic + sil-enforcement ::= unsafe + %1 = end_unpaired_access [dynamic] %0 : $*Builtin.UnsafeValueBuffer + +Ends an access. This has the same semantics and constraints as ``end_access`` with the following exceptions: + +- The single operand refers to the scratch buffer that uniquely identified the + access with this scope. + +- The enforcement level is reiterated, since the corresponding + ``begin_unpaired_access`` may not be statically discoverable. It must be + identical to the ``begin_unpaired_access`` enforcement. + + Reference Counting ~~~~~~~~~~~~~~~~~~ @@ -2880,6 +2947,25 @@ Performs a copy of an Objective-C block. Unlike retains of other reference-counted types, this can produce a different value from the operand if the block is copied from the stack to the heap. +copy_block_without_escaping +``````````````````````````` +:: + + sil-instruction :: 'copy_block_without_escaping' sil-operand 'withoutEscaping' sil-operand + + %1 = copy_block %0 : $@convention(block) T -> U withoutEscaping %1 : $T -> U + +Performs a copy of an Objective-C block. Unlike retains of other +reference-counted types, this can produce a different value from the operand if +the block is copied from the stack to the heap. + +Additionally, consumes the ``withoutEscaping`` operand ``%1`` which is the +closure sentinel. SILGen emits these instructions when it passes @noescape +swift closures to Objective C. A mandatory SIL pass will lower this instruction +into a ``copy_block`` and a ``is_escaping``/``cond_fail``/``destroy_value`` at +the end of the lifetime of the objective c closure parameter to check whether +the sentinel closure was escaped. + builtin "unsafeGuaranteed" `````````````````````````` @@ -4649,6 +4735,14 @@ Converts an escaping (non-trivial) function type to an ``@noescape`` trivial function type. Something must guarantee the lifetime of the input ``%0`` for the duration of the use ``%1``. +A ``convert_escape_to_noescape [not_guaranteed] %opd`` indicates that the +lifetime of its operand was not guaranteed by SILGen and a mandatory pass must +be run to ensure the lifetime of ``%opd``` for the conversion's uses. + +A ``convert_escape_to_noescape [escaped]`` indiciates that the result was +passed to a function (materializeForSet) which escapes the closure in a way not +expressed by the convert's users. The mandatory pass must ensure the lifetime +in a conservative way. thin_function_to_pointer ```````````````````````` @@ -4673,6 +4767,24 @@ Decodes the bit representation of the specified ``Builtin.BridgeObject`` value, returning two bits: the first indicates whether the object is an Objective-C object, the second indicates whether it is an Objective-C tagged pointer value. +value_to_bridge_object +`````````````````````` +:: + + sil-instruction ::= 'value_to_bridge_object' sil-operand + + %1 = value_to_bridge_object %0 : $T + // %1 will be of type Builtin.BridgeObject + +Sets the BridgeObject to a tagged pointer representation holding its operands +by tagging and shifting the operand if needed:: + + value_to_bridge_object %x === + (x << _swift_abi_ObjCReservedLowBits) | _swift_BridgeObject_TaggedPointerBits + +``%x`` thus must not be using any high bits shifted away or the tag bits post-shift. +ARC operations on such tagged values are NOPs. + ref_to_bridge_object ```````````````````` :: diff --git a/docs/Ubuntu14.md b/docs/Ubuntu14.md index fa3e6992721fb..51e6a0eca94e5 100644 --- a/docs/Ubuntu14.md +++ b/docs/Ubuntu14.md @@ -11,7 +11,7 @@ sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-3.6 ## Upgrade CMake You'll need to upgrade your CMake toolchain to a supported version by building a local copy. The minimum required version of CMake may change, and may not be available on Ubuntu 14.04 in the future. ```bash -wget http://www.cmake.org/files/v3.5/cmake-3.5.2.tar.gz +wget https://cmake.org/files/v3.5/cmake-3.5.2.tar.gz tar xf cmake-3.5.2.tar.gz cd cmake-3.5.2 ./configure diff --git a/docs/WindowsBuild.md b/docs/WindowsBuild.md index cfa2357d17fe6..082d901d9051c 100644 --- a/docs/WindowsBuild.md +++ b/docs/WindowsBuild.md @@ -126,8 +126,8 @@ set llvm_bin_dir="%swift_source_dir%/build/Ninja-RelWithDebInfoAssert/llvm-windo ``` ### 7. Build Swift -- This must be done from within a developer command prompt and could take up to - two hours depending on your system. +- This must be done from within a developer command prompt and could take hours + depending on your system. - You may need to adjust the `SWIFT_WINDOWS_LIB_DIRECTORY` parameter depending on your target platform or Windows SDK version. ```cmd @@ -142,9 +142,9 @@ cmake -G "Ninja" "%swift_source_dir%/swift"^ -DSWIFT_PATH_TO_LLVM_BUILD="%swift_source_dir%/build/Ninja-RelWithDebInfoAssert/llvm-windows-amd64"^ -DSWIFT_PATH_TO_CLANG_SOURCE="%swift_source_dir%/llvm/tools/clang"^ -DSWIFT_PATH_TO_CLANG_BUILD="%swift_source_dir%/build/Ninja-RelWithDebInfoAssert/llvm-windows-amd64"^ - -DICU_UC_INCLUDE_DIRS="%swift_source_dir%/icu/include"^ + -DICU_UC_INCLUDE_DIR="%swift_source_dir%/icu/include"^ -DICU_UC_LIBRARY_DIRS="%swift_source_dir%/icu/lib64"^ - -DICU_I18N_INCLUDE_DIRS="%swift_source_dir%/icu/include"^ + -DICU_I18N_INCLUDE_DIR="%swift_source_dir%/icu/include"^ -DICU_I18N_LIBRARY_DIRS="%swift_source_dir%/icu/lib64"^ -DICU_UC_LIB_NAME="icuuc"^ -DICU_I18N_LIB_NAME="icuin"^ @@ -172,6 +172,25 @@ cmake -G "Visual Studio 15" "%swift_source_dir%/swift"^ ... ``` +### 8. Build lldb +- This must be done from within a developer command prompt and could take hours + depending on your system. +```cmd +mkdir "%swift_source_dir%/build/Ninja-RelWithDebInfoAssert/lldb-windows-amd64" +pushd "%swift_source_dir%/build/Ninja-RelWithDebInfoAssert/lldb-windows-amd64" +cmake -G "Ninja" "%swift_source_dir%/lldb"^ + -DCMAKE_BUILD_TYPE=RelWithDebInfo^ + -DLLDB_PATH_TO_CMARK_SOURCE="%swift_source_dir%/cmark"^ + -DLLDB_PATH_TO_CMARK_BUILD="%swift_source_dir%/build/Ninja-RelWithDebInfoAssert/cmark-windows-amd64"^ + -DLLDB_PATH_TO_LLVM_SOURCE="%swift_source_dir%/llvm"^ + -DLLDB_PATH_TO_LLVM_BUILD="%swift_source_dir%/build/Ninja-RelWithDebInfoAssert/llvm-windows-amd64"^ + -DLLDB_PATH_TO_CLANG_SOURCE="%swift_source_dir%/clang"^ + -DLLDB_PATH_TO_CLANG_BUILD="%swift_source_dir%/build/Ninja-RelWithDebInfoAssert/llvm-windows-amd64"^ + -DLLVM_ENABLE_ASSERTIONS=YES +popd +cmake --build "%swift_source_dir%/build/Ninja-RelWithDebInfoAssert/lldb-windows-amd64" +``` + ## MSVC Follow instructions 1-6 for `clang-cl`, but run the following instead to build Swift diff --git a/include/swift/ABI/MetadataValues.h b/include/swift/ABI/MetadataValues.h index e92dd07294c05..0a82e16342034 100644 --- a/include/swift/ABI/MetadataValues.h +++ b/include/swift/ABI/MetadataValues.h @@ -54,9 +54,20 @@ enum class MetadataKind : uint32_t { #define ABSTRACTMETADATAKIND(name, start, end) \ name##_Start = start, name##_End = end, #include "MetadataKind.def" + + /// The largest possible non-isa-pointer metadata kind value. + /// + /// This is included in the enumeration to prevent against attempts to + /// exhaustively match metadata kinds. Future Swift runtimes or compilers + /// may introduce new metadata kinds, so for forward compatibility, the + /// runtime must tolerate metadata with unknown kinds. + /// This specific value is not mapped to a valid metadata kind at this time, + /// however. + LastEnumerated = 2047, }; -const unsigned LastEnumeratedMetadataKind = 2047; +const unsigned LastEnumeratedMetadataKind = + (unsigned)MetadataKind::LastEnumerated; /// Try to translate the 'isa' value of a type/heap metadata into a value /// of the MetadataKind enum. @@ -127,6 +138,8 @@ class TargetValueWitnessFlags { } /// True if the type requires out-of-line allocation of its storage. + /// This can be the case because the value requires more storage or if it is + /// not bitwise takable. bool isInlineStorage() const { return !(Data & IsNonInline); } constexpr TargetValueWitnessFlags withInlineStorage(bool isInline) const { return TargetValueWitnessFlags((Data & ~IsNonInline) | diff --git a/include/swift/ABI/ValueWitness.def b/include/swift/ABI/ValueWitness.def index b50da3edbf0df..52df0c16636c6 100644 --- a/include/swift/ABI/ValueWitness.def +++ b/include/swift/ABI/ValueWitness.def @@ -172,14 +172,6 @@ FUNCTION_VALUE_WITNESS(assignWithTake, MUTABLE_VALUE_TYPE, (MUTABLE_VALUE_TYPE, MUTABLE_VALUE_TYPE, TYPE_TYPE)) -/// T *(*initializeBufferWithTakeOfBuffer)(B *dest, B *src, M *self); -/// Given an invalid buffer, initialize it by taking the value out of -/// the source buffer. -FUNCTION_VALUE_WITNESS(initializeBufferWithTakeOfBuffer, - InitializeBufferWithTakeOfBuffer, - MUTABLE_VALUE_TYPE, - (MUTABLE_BUFFER_TYPE, MUTABLE_BUFFER_TYPE, TYPE_TYPE)) - /// unsigned (*getEnumTagSinglePayload)(const T* enum, UINT_TYPE emptyCases) /// Given an instance of valid single payload enum with a payload of this /// witness table's type (e.g Optional) , get the tag of the enum. @@ -228,7 +220,7 @@ BEGIN_VALUE_WITNESS_RANGE(RequiredTypeLayoutWitness, /// The ValueWitnessIsNonPOD bit is set if the type is not POD. /// /// The ValueWitnessIsNonInline bit is set if the type cannot be -/// represented in a fixed-size buffer. +/// represented in a fixed-size buffer or if it is not bitwise takable. /// /// The Enum_HasExtraInhabitants bit is set if the type's binary /// representation has "extra inhabitants" that do not form valid values of diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index 0b417008822dd..a2eca5b11c618 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -21,7 +21,6 @@ #include "swift/AST/ClangModuleLoader.h" #include "swift/AST/Identifier.h" #include "swift/AST/SearchPathOptions.h" -#include "swift/AST/SubstitutionList.h" #include "swift/AST/Type.h" #include "swift/AST/TypeAlignments.h" #include "swift/Basic/LangOptions.h" @@ -95,7 +94,6 @@ namespace swift { class SourceManager; class ValueDecl; class DiagnosticEngine; - class Substitution; class TypeCheckerDebugConsumer; struct RawComment; class DocComment; @@ -186,19 +184,26 @@ class SILLayout; // From SIL /// DispatchQueues. Summary: if you think you need a global or static variable, /// you probably need to put it here instead. -class ASTContext { +class ASTContext final { ASTContext(const ASTContext&) = delete; void operator=(const ASTContext&) = delete; + ASTContext(LangOptions &langOpts, SearchPathOptions &SearchPathOpts, + SourceManager &SourceMgr, DiagnosticEngine &Diags); + public: // Members that should only be used by ASTContext.cpp. struct Implementation; - Implementation &Impl; - + Implementation &getImpl() const; + friend ConstraintCheckerArenaRAII; -public: - ASTContext(LangOptions &langOpts, SearchPathOptions &SearchPathOpts, - SourceManager &SourceMgr, DiagnosticEngine &Diags); + + void operator delete(void *Data) throw(); + + static ASTContext *get(LangOptions &langOpts, + SearchPathOptions &SearchPathOpts, + SourceManager &SourceMgr, + DiagnosticEngine &Diags); ~ASTContext(); /// \brief The language options used for translation. @@ -471,12 +476,8 @@ class ASTContext { /// Retrieve the declaration of Swift.==(Int, Int) -> Bool. FuncDecl *getEqualIntDecl() const; - /// Retrieve the declaration of - /// Swift._combineHashValues(Int, Int) -> Int. - FuncDecl *getCombineHashValuesDecl() const; - - /// Retrieve the declaration of Swift._mixInt(Int) -> Int. - FuncDecl *getMixIntDecl() const; + /// Retrieve the declaration of Swift._hashValue(for: H) -> Int. + FuncDecl *getHashValueForDecl() const; /// Retrieve the declaration of Array.append(element:) FuncDecl *getArrayAppendElementDecl() const; @@ -533,6 +534,11 @@ class ASTContext { /// nested within it. void addExternalDecl(Decl *decl); + /// Add a declaration that was synthesized to a per-source file list if + /// if is part of a source file, or the external declarations list if + /// it is part of an imported type context. + void addSynthesizedDecl(Decl *decl); + /// Add a cleanup function to be called when the ASTContext is deallocated. void addCleanup(std::function cleanup); @@ -739,30 +745,11 @@ class ASTContext { /// \param generic The generic conformance. /// /// \param substitutions The set of substitutions required to produce the - /// specialized conformance from the generic conformance. This list is - /// copied so passing a temporary is permitted. - ProtocolConformance * - getSpecializedConformance(Type type, - ProtocolConformance *generic, - SubstitutionList substitutions, - bool alreadyCheckedCollapsed = false); - - /// \brief Produce a specialized conformance, which takes a generic - /// conformance and substitutions written in terms of the generic - /// conformance's signature. - /// - /// \param type The type for which we are retrieving the conformance. - /// - /// \param generic The generic conformance. - /// - /// \param substitutions The set of substitutions required to produce the - /// specialized conformance from the generic conformance. The keys must - /// be generic parameters, not archetypes, so for example passing in - /// TypeBase::getContextSubstitutionMap() is OK. + /// specialized conformance from the generic conformance. ProtocolConformance * getSpecializedConformance(Type type, ProtocolConformance *generic, - const SubstitutionMap &substitutions); + SubstitutionMap substitutions); /// \brief Produce an inherited conformance, for subclasses of a type /// that already conforms to a protocol. diff --git a/include/swift/AST/Attr.def b/include/swift/AST/Attr.def index 87c509bc1be9f..8378d9ba704eb 100644 --- a/include/swift/AST/Attr.def +++ b/include/swift/AST/Attr.def @@ -18,14 +18,30 @@ #define DECL_ATTR(SPELLING, CLASS, OPTIONS, CODE) #endif +#ifndef CONTEXTUAL_DECL_ATTR +#define CONTEXTUAL_DECL_ATTR(SPELLING, CLASS, OPTIONS, CODE) \ + DECL_ATTR(SPELLING, CLASS, OPTIONS, CODE) +#endif + #ifndef SIMPLE_DECL_ATTR -#define SIMPLE_DECL_ATTR(X, CLASS, OPTIONS, CODE) DECL_ATTR(X, CLASS, OPTIONS, CODE) +#define SIMPLE_DECL_ATTR(X, CLASS, OPTIONS, CODE) \ + DECL_ATTR(X, CLASS, OPTIONS, CODE) +#endif + +#ifndef CONTEXTUAL_SIMPLE_DECL_ATTR +#define CONTEXTUAL_SIMPLE_DECL_ATTR(X, CLASS, OPTIONS, CODE) \ + SIMPLE_DECL_ATTR(X, CLASS, OPTIONS, CODE) #endif #ifndef DECL_ATTR_ALIAS #define DECL_ATTR_ALIAS(SPELLING, CLASS) #endif +#ifndef CONTEXTUAL_DECL_ATTR_ALIAS +#define CONTEXTUAL_DECL_ATTR_ALIAS(SPELLING, CLASS) \ + DECL_ATTR_ALIAS(SPELLING, CLASS) +#endif + #ifndef TYPE_ATTR #define TYPE_ATTR(X) #endif @@ -82,239 +98,285 @@ TYPE_ATTR(thick) // SIMPLE_DECL_ATTR is the same, but the class becomes // SimpleDeclAttr. // +// Please help ease code review/audits: +// - Please indent once, not to the opening '('. +// - Please place the "OnXYZ" flags together on the next line. +// - Please place the non-OnXYZ flags together on the next to last line. +// - Please place the unique number on the last line. If the attribute is NOT +// serialized, then please place that flag on the last line too. For example: +// 123) +// NotSerialized, 321) +// - Please sort attributes by serialization number. +// - Please create a "NOTE" comment if a unique number is skipped. DECL_ATTR(_silgen_name, SILGenName, - OnFunc | OnConstructor | OnDestructor | LongAttribute | - UserInaccessible, 0) - + OnAbstractFunction | + LongAttribute | UserInaccessible, + 0) DECL_ATTR(available, Available, - OnFunc | OnStruct | OnEnum | OnClass | OnProtocol | OnVar | - OnConstructor | OnDestructor | OnTypeAlias | OnSubscript | - OnEnumElement | OnExtension | AllowMultipleAttributes | LongAttribute, 1) - -SIMPLE_DECL_ATTR(final, Final, - OnClass | OnFunc | OnVar | OnSubscript|DeclModifier, 2) - + OnAbstractFunction | OnGenericType | OnVar | OnSubscript | OnEnumElement | + OnExtension | + AllowMultipleAttributes | LongAttribute, + 1) +CONTEXTUAL_SIMPLE_DECL_ATTR(final, Final, + OnClass | OnFunc | OnAccessor | OnVar | OnSubscript | + DeclModifier, + 2) DECL_ATTR(objc, ObjC, - OnFunc | OnClass | OnProtocol | OnExtension | OnVar | OnSubscript | - OnConstructor | OnDestructor | OnEnum | OnEnumElement, 3) - -SIMPLE_DECL_ATTR(required, Required, - OnConstructor|DeclModifier, 4) - -SIMPLE_DECL_ATTR(optional, Optional, - OnConstructor|OnFunc|OnVar|OnSubscript|DeclModifier, 5) - -/// NOTE: 6 is unused - -SIMPLE_DECL_ATTR(noreturn, NoReturn, OnFunc, 7) - -SIMPLE_DECL_ATTR(_exported, Exported, OnImport | UserInaccessible, 8) - + OnAbstractFunction | OnClass | OnProtocol | OnExtension | OnVar | + OnSubscript | OnEnum | OnEnumElement, + 3) +CONTEXTUAL_SIMPLE_DECL_ATTR(required, Required, + OnConstructor | + DeclModifier, + 4) +CONTEXTUAL_SIMPLE_DECL_ATTR(optional, Optional, + OnConstructor | OnFunc | OnAccessor | OnVar | OnSubscript | + DeclModifier, + 5) +// NOTE: 6 is unused +SIMPLE_DECL_ATTR(noreturn, NoReturn, + OnFunc | OnAccessor, + 7) +SIMPLE_DECL_ATTR(_exported, Exported, + OnImport | + UserInaccessible, + 8) SIMPLE_DECL_ATTR(dynamicMemberLookup, DynamicMemberLookup, - OnClass | OnStruct | OnEnum | OnProtocol, 9) - + OnNominalType, + 9) SIMPLE_DECL_ATTR(NSCopying, NSCopying, - OnVar, 10) - + OnVar, + 10) SIMPLE_DECL_ATTR(IBAction, IBAction, - OnFunc, 11) - + OnFunc, + 11) SIMPLE_DECL_ATTR(IBDesignable, IBDesignable, - OnClass | OnExtension, 12) - + OnClass | OnExtension, + 12) SIMPLE_DECL_ATTR(IBInspectable, IBInspectable, - OnVar, 13) - + OnVar, + 13) SIMPLE_DECL_ATTR(IBOutlet, IBOutlet, - OnVar, 14) - -SIMPLE_DECL_ATTR(NSManaged, NSManaged, OnVar | OnFunc, 15) - -SIMPLE_DECL_ATTR(lazy, Lazy, OnVar|DeclModifier, 16) - -SIMPLE_DECL_ATTR(LLDBDebuggerFunction, LLDBDebuggerFunction, OnFunc | - UserInaccessible, 17) - + OnVar, + 14) +SIMPLE_DECL_ATTR(NSManaged, NSManaged, + OnVar | OnFunc | OnAccessor, + 15) +CONTEXTUAL_SIMPLE_DECL_ATTR(lazy, Lazy, DeclModifier | + OnVar, + 16) +SIMPLE_DECL_ATTR(LLDBDebuggerFunction, LLDBDebuggerFunction, + OnFunc | + UserInaccessible, + 17) SIMPLE_DECL_ATTR(UIApplicationMain, UIApplicationMain, - OnClass, 18) - + OnClass, + 18) SIMPLE_DECL_ATTR(unsafe_no_objc_tagged_pointer, UnsafeNoObjCTaggedPointer, - OnProtocol | UserInaccessible, 19) - -DECL_ATTR(inline, Inline, OnFunc | OnConstructor, 20) - + OnProtocol | + UserInaccessible, + 19) +DECL_ATTR(inline, Inline, + OnFunc | OnAccessor | OnConstructor, + 20) DECL_ATTR(_semantics, Semantics, - OnFunc | OnConstructor | OnDestructor | OnSubscript | - AllowMultipleAttributes | UserInaccessible, 21) - -SIMPLE_DECL_ATTR(dynamic, Dynamic, - OnFunc | OnVar | OnSubscript | OnConstructor | DeclModifier, 22) - -SIMPLE_DECL_ATTR(infix , Infix , OnFunc | OnOperator | DeclModifier, 23) -SIMPLE_DECL_ATTR(prefix , Prefix , OnFunc | OnOperator | DeclModifier, 24) -SIMPLE_DECL_ATTR(postfix, Postfix, OnFunc | OnOperator | DeclModifier, 25) - + OnAbstractFunction | OnSubscript | + AllowMultipleAttributes | UserInaccessible, + 21) +CONTEXTUAL_SIMPLE_DECL_ATTR(dynamic, Dynamic, + OnFunc | OnAccessor | OnVar | OnSubscript | OnConstructor | + DeclModifier, + 22) +CONTEXTUAL_SIMPLE_DECL_ATTR(infix, Infix, + OnFunc | OnOperator | + DeclModifier, + 23) +CONTEXTUAL_SIMPLE_DECL_ATTR(prefix, Prefix, + OnFunc | OnOperator | + DeclModifier, + 24) +CONTEXTUAL_SIMPLE_DECL_ATTR(postfix, Postfix, + OnFunc | OnOperator | + DeclModifier, + 25) SIMPLE_DECL_ATTR(_transparent, Transparent, - OnFunc|OnConstructor|OnVar|UserInaccessible, 26) + OnFunc | OnAccessor | OnConstructor | OnVar | UserInaccessible, + 26) SIMPLE_DECL_ATTR(requires_stored_property_inits, RequiresStoredPropertyInits, - OnClass, 27) - + OnClass, + 27) SIMPLE_DECL_ATTR(nonobjc, NonObjC, - OnExtension | OnFunc | OnVar | OnSubscript | OnConstructor, 30) - + OnExtension | OnFunc | OnAccessor | OnVar | OnSubscript | OnConstructor, + 30) SIMPLE_DECL_ATTR(_fixed_layout, FixedLayout, - OnVar | OnClass | OnStruct | UserInaccessible, 31) - + OnVar | OnClass | OnStruct | + UserInaccessible, + 31) SIMPLE_DECL_ATTR(inlinable, Inlinable, - OnVar | OnSubscript | OnFunc | OnConstructor | OnDestructor | - UserInaccessible, 32) - + OnVar | OnSubscript | OnAbstractFunction, + 32) DECL_ATTR(_specialize, Specialize, - OnConstructor | OnFunc | AllowMultipleAttributes | LongAttribute - | UserInaccessible, 33) - -SIMPLE_DECL_ATTR(objcMembers, ObjCMembers, OnClass, 34) - -// Non-serialized attributes. - -SIMPLE_DECL_ATTR(__consuming, Consuming, OnFunc | DeclModifier | NotSerialized, - /* Not serialized */ 40) -SIMPLE_DECL_ATTR(mutating, Mutating, OnFunc | DeclModifier | NotSerialized, - /* Not serialized */ 41) -SIMPLE_DECL_ATTR(nonmutating, NonMutating, OnFunc | DeclModifier | NotSerialized, - /* Not serialized */ 42) - -SIMPLE_DECL_ATTR(convenience, Convenience, - OnConstructor|DeclModifier|NotSerialized, 43) - -SIMPLE_DECL_ATTR(override, Override, - OnFunc | OnVar | OnSubscript | OnConstructor | DeclModifier | - NotSerialized, - /* Not serialized */44) - -SIMPLE_DECL_ATTR(sil_stored, SILStored, OnVar | NotSerialized | SILOnly, - /* Not serialized */45) - + OnConstructor | OnFunc | OnAccessor | + AllowMultipleAttributes | LongAttribute | UserInaccessible, + 33) +SIMPLE_DECL_ATTR(objcMembers, ObjCMembers, + OnClass, + 34) +CONTEXTUAL_SIMPLE_DECL_ATTR(__consuming, Consuming, + OnFunc | OnAccessor | + DeclModifier | + NotSerialized, 40) +CONTEXTUAL_SIMPLE_DECL_ATTR(mutating, Mutating, + OnFunc | OnAccessor | + DeclModifier | + NotSerialized, 41) +CONTEXTUAL_SIMPLE_DECL_ATTR(nonmutating, NonMutating, + OnFunc | OnAccessor | + DeclModifier | + NotSerialized, 42) +CONTEXTUAL_SIMPLE_DECL_ATTR(convenience, Convenience, + OnConstructor | + DeclModifier | + NotSerialized, 43) +CONTEXTUAL_SIMPLE_DECL_ATTR(override, Override, + OnFunc | OnAccessor | OnVar | OnSubscript | OnConstructor | + DeclModifier | + NotSerialized, 44) +SIMPLE_DECL_ATTR(sil_stored, SILStored, + OnVar | + SILOnly | + NotSerialized, 45) DECL_ATTR(private, AccessControl, - OnFunc | OnExtension | OnTypeAlias | OnStruct | OnEnum | - OnClass | OnProtocol | OnVar | OnSubscript | OnConstructor | - DeclModifier | NotSerialized, - /* Not serialized */ 46) + OnFunc | OnAccessor | OnExtension | OnGenericType | OnVar | OnSubscript | + OnConstructor | + DeclModifier | + NotSerialized, 46) DECL_ATTR_ALIAS(fileprivate, AccessControl) DECL_ATTR_ALIAS(internal, AccessControl) DECL_ATTR_ALIAS(public, AccessControl) - - +CONTEXTUAL_DECL_ATTR_ALIAS(open, AccessControl) DECL_ATTR(__setter_access, SetterAccess, - OnVar | OnSubscript | DeclModifier | NotSerialized | RejectByParser, - /* Not serialized */ 47) - -DECL_ATTR(__raw_doc_comment, RawDocComment, OnAnyDecl | - NotSerialized | RejectByParser, /* Not serialized */48) - -// Also handles unowned and unowned(weak). -DECL_ATTR(weak, ReferenceOwnership, OnVar | OnParam | DeclModifier | NotSerialized, - /* Not serialized */49) -DECL_ATTR_ALIAS(unowned, ReferenceOwnership) - -DECL_ATTR(effects, Effects, OnFunc | OnConstructor | OnDestructor | - UserInaccessible, 50) - -DECL_ATTR(__objc_bridged, ObjCBridged, OnClass | NotSerialized | RejectByParser, - /* Not serialized */51) - + OnVar | OnSubscript | + DeclModifier | RejectByParser | + NotSerialized, 47) +DECL_ATTR(__raw_doc_comment, RawDocComment, + OnAnyDecl | + RejectByParser | + NotSerialized, 48) +CONTEXTUAL_DECL_ATTR(weak, ReferenceOwnership, + OnVar | OnParam | + DeclModifier | + NotSerialized, 49) +CONTEXTUAL_DECL_ATTR_ALIAS(unowned, ReferenceOwnership) +DECL_ATTR(effects, Effects, + OnAbstractFunction | + UserInaccessible, + 50) +DECL_ATTR(__objc_bridged, ObjCBridged, + OnClass | + RejectByParser | + NotSerialized, 51) SIMPLE_DECL_ATTR(NSApplicationMain, NSApplicationMain, - OnClass, 52) - + OnClass, + 52) SIMPLE_DECL_ATTR(_objc_non_lazy_realization, ObjCNonLazyRealization, - OnClass | UserInaccessible, 53) - + OnClass | + UserInaccessible, + 53) DECL_ATTR(__synthesized_protocol, SynthesizedProtocol, - OnStruct | OnEnum | OnClass | NotSerialized | RejectByParser, - /* Not serialized */54) - + OnConcreteNominalType | + RejectByParser | + NotSerialized, 54) SIMPLE_DECL_ATTR(testable, Testable, - OnImport | NotSerialized | UserInaccessible, - /* Not serialized */ 55) - -DECL_ATTR(_alignment, Alignment, OnStruct | OnEnum | UserInaccessible, 56) - + OnImport | + UserInaccessible | + NotSerialized, 55) +DECL_ATTR(_alignment, Alignment, + OnStruct | OnEnum | + UserInaccessible, + 56) SIMPLE_DECL_ATTR(rethrows, Rethrows, - OnFunc | OnConstructor | RejectByParser, 57) - + OnFunc | OnAccessor | OnConstructor | + RejectByParser, + 57) DECL_ATTR(_swift_native_objc_runtime_base, SwiftNativeObjCRuntimeBase, - OnClass | UserInaccessible, 59) - -SIMPLE_DECL_ATTR(indirect, Indirect, - OnEnum | OnEnumElement | DeclModifier, - 60) - + OnClass | + UserInaccessible, + 59) +CONTEXTUAL_SIMPLE_DECL_ATTR(indirect, Indirect, DeclModifier | + OnEnum | OnEnumElement, + 60) SIMPLE_DECL_ATTR(warn_unqualified_access, WarnUnqualifiedAccess, - OnFunc /*| OnVar*/ | LongAttribute, 61) - + OnFunc | OnAccessor /*| OnVar*/ | + LongAttribute, + 61) SIMPLE_DECL_ATTR(_show_in_interface, ShowInInterface, - OnProtocol | UserInaccessible, 62) - + OnProtocol | + UserInaccessible, + 62) DECL_ATTR(_cdecl, CDecl, - OnFunc | LongAttribute | UserInaccessible, 63) - + OnFunc | OnAccessor | + LongAttribute | UserInaccessible, + 63) SIMPLE_DECL_ATTR(usableFromInline, UsableFromInline, - OnFunc | OnVar | OnSubscript | OnConstructor | - OnDestructor | OnStruct | OnEnum | OnClass | - OnProtocol | LongAttribute | UserInaccessible, - 64) - + OnAbstractFunction | OnVar | OnSubscript | OnNominalType | + LongAttribute, + 64) SIMPLE_DECL_ATTR(discardableResult, DiscardableResult, - OnFunc | OnConstructor | LongAttribute, 65) - -SIMPLE_DECL_ATTR(GKInspectable, GKInspectable, OnVar, 66) - + OnFunc | OnAccessor | OnConstructor | + LongAttribute, + 65) +SIMPLE_DECL_ATTR(GKInspectable, GKInspectable, + OnVar, + 66) DECL_ATTR(_implements, Implements, - OnFunc | OnVar | OnSubscript | OnTypeAlias - | NotSerialized | UserInaccessible, - /* Not serialized */ 67) - + OnFunc | OnAccessor | OnVar | OnSubscript | OnTypeAlias | + UserInaccessible | + NotSerialized, 67) DECL_ATTR(_objcRuntimeName, ObjCRuntimeName, - OnClass | NotSerialized | UserInaccessible | RejectByParser, - /*Not serialized */ 68) - + OnClass | + UserInaccessible | RejectByParser | + NotSerialized, 68) SIMPLE_DECL_ATTR(_staticInitializeObjCMetadata, StaticInitializeObjCMetadata, - OnClass | NotSerialized | LongAttribute | RejectByParser, - /*Not serialized */ 69) - + OnClass | LongAttribute | RejectByParser | + NotSerialized, 69) DECL_ATTR(_restatedObjCConformance, RestatedObjCConformance, - OnProtocol | NotSerialized | LongAttribute | RejectByParser, - /*Not serialized */ 70) - + OnProtocol | + LongAttribute | RejectByParser | + NotSerialized, 70) // HACK: Attribute needed to preserve source compatibility by downgrading errors // due to an SDK change in Dispatch SIMPLE_DECL_ATTR(_downgrade_exhaustivity_check, DowngradeExhaustivityCheck, - OnEnumElement | LongAttribute | UserInaccessible, 71) - -SIMPLE_DECL_ATTR(_implicitly_unwrapped_optional, - ImplicitlyUnwrappedOptional, - OnFunc | OnVar | OnParam | OnSubscript | OnConstructor - | RejectByParser, 72) - + OnEnumElement | + LongAttribute | UserInaccessible, + 71) +SIMPLE_DECL_ATTR(_implicitly_unwrapped_optional, ImplicitlyUnwrappedOptional, + OnFunc | OnAccessor | OnVar | OnParam | OnSubscript | OnConstructor | + RejectByParser, + 72) DECL_ATTR(_optimize, Optimize, - OnFunc | OnConstructor | OnDestructor | OnSubscript | OnVar | - UserInaccessible, 73) - + OnAbstractFunction | OnSubscript | OnVar | + UserInaccessible, + 73) DECL_ATTR(_clangImporterSynthesizedType, ClangImporterSynthesizedType, - OnClass | OnStruct | OnEnum | OnProtocol | OnTypeAlias | - LongAttribute | NotSerialized | RejectByParser | UserInaccessible, - /*Not serialized*/74) - + OnGenericType | + LongAttribute | RejectByParser | UserInaccessible | + NotSerialized, 74) SIMPLE_DECL_ATTR(_weakLinked, WeakLinked, - OnEnum | OnStruct | OnClass | OnProtocol | - OnFunc | OnVar | OnSubscript | OnConstructor | OnEnumElement | - UserInaccessible, - 75) - -SIMPLE_DECL_ATTR(_frozen, Frozen, OnEnum | UserInaccessible, 76) + OnNominalType | OnFunc | OnAccessor | OnVar | OnSubscript | OnConstructor | + OnEnumElement | + UserInaccessible, + 75) +SIMPLE_DECL_ATTR(_frozen, Frozen, + OnEnum | + UserInaccessible, + 76) #undef TYPE_ATTR #undef DECL_ATTR_ALIAS +#undef CONTEXTUAL_DECL_ATTR_ALIAS #undef SIMPLE_DECL_ATTR +#undef CONTEXTUAL_SIMPLE_DECL_ATTR #undef DECL_ATTR +#undef CONTEXTUAL_DECL_ATTR diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index 78f21c4f6242c..d7059161ce130 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -17,6 +17,7 @@ #ifndef SWIFT_ATTR_H #define SWIFT_ATTR_H +#include "swift/Basic/InlineBitfield.h" #include "swift/Basic/SourceLoc.h" #include "swift/Basic/UUID.h" #include "swift/Basic/STLExtras.h" @@ -181,141 +182,165 @@ enum class DeclKind : uint8_t; /// Represents one declaration attribute. class DeclAttribute : public AttributeBase { friend class DeclAttributes; -protected: - class DeclAttrBitFields { - friend class DeclAttribute; - - // The kind. - unsigned Kind : 8; - - // Whether this attribute was implicitly added. - unsigned Implicit : 1; - - unsigned Invalid : 1; - }; - enum { NumDeclAttrBits = 10 }; - static_assert(NumDeclAttrBits <= 32, "fits in an unsigned"); - - class ObjCAttrBitFields { - friend class ObjCAttr; - unsigned : NumDeclAttrBits; - - /// Whether this attribute has location information that trails the main - /// record, which contains the locations of the parentheses and any names. - unsigned HasTrailingLocationInfo : 1; - - /// Whether the name is implicit, produced as the result of caching. - unsigned ImplicitName : 1; - - /// Whether the @objc was inferred using Swift 3's deprecated inference - /// rules. - unsigned Swift3Inferred : 1; - }; - enum { NumObjCAttrBits = NumDeclAttrBits + 3 }; - static_assert(NumObjCAttrBits <= 32, "fits in an unsigned"); - - class AccessControlAttrBitFields { - friend class AbstractAccessControlAttr; - unsigned : NumDeclAttrBits; - - unsigned AccessLevel : 3; - }; - enum { NumAccessControlAttrBits = NumDeclAttrBits + 3 }; - static_assert(NumAccessControlAttrBits <= 32, "fits in an unsigned"); +protected: union { - DeclAttrBitFields DeclAttrBits; - ObjCAttrBitFields ObjCAttrBits; - AccessControlAttrBitFields AccessControlAttrBits; - }; + uint64_t OpaqueBits; + + SWIFT_INLINE_BITFIELD_BASE(DeclAttribute, bitmax(NumDeclAttrKindBits,8)+1+1, + Kind : bitmax(NumDeclAttrKindBits,8), + // Whether this attribute was implicitly added. + Implicit : 1, + + Invalid : 1 + ); + + SWIFT_INLINE_BITFIELD(ObjCAttr, DeclAttribute, 1+1+1, + /// Whether this attribute has location information that trails the main + /// record, which contains the locations of the parentheses and any names. + HasTrailingLocationInfo : 1, + + /// Whether the name is implicit, produced as the result of caching. + ImplicitName : 1, + + /// Whether the @objc was inferred using Swift 3's deprecated inference + /// rules. + Swift3Inferred : 1 + ); + + SWIFT_INLINE_BITFIELD(AbstractAccessControlAttr, DeclAttribute, 3, + AccessLevel : 3 + ); + + SWIFT_INLINE_BITFIELD_FULL(AlignmentAttr, DeclAttribute, 32, + : NumPadBits, + // The alignment value. + Value : 32 + ); + + SWIFT_INLINE_BITFIELD(ClangImporterSynthesizedTypeAttr, DeclAttribute, 1, + kind : 1 + ); + + SWIFT_INLINE_BITFIELD(EffectsAttr, DeclAttribute, NumEffectsKindBits, + kind : NumEffectsKindBits + ); + + SWIFT_INLINE_BITFIELD(InlineAttr, DeclAttribute, NumInlineKindBits, + kind : NumInlineKindBits + ); + + SWIFT_INLINE_BITFIELD(OptimizeAttr, DeclAttribute, NumOptimizationModeBits, + mode : NumOptimizationModeBits + ); + + SWIFT_INLINE_BITFIELD(ReferenceOwnershipAttr, DeclAttribute, + NumReferenceOwnershipBits, + ownership : NumReferenceOwnershipBits + ); + + SWIFT_INLINE_BITFIELD_FULL(SpecializeAttr, DeclAttribute, 1+1+32, + exported : 1, + kind : 1, + : NumPadBits, + numRequirements : 32 + ); + + SWIFT_INLINE_BITFIELD(SynthesizedProtocolAttr, DeclAttribute, + NumKnownProtocolKindBits, + kind : NumKnownProtocolKindBits + ); + } Bits; DeclAttribute *Next = nullptr; DeclAttribute(DeclAttrKind DK, SourceLoc AtLoc, SourceRange Range, bool Implicit) : AttributeBase(AtLoc, Range) { - DeclAttrBits.Kind = static_cast(DK); - DeclAttrBits.Implicit = Implicit; - DeclAttrBits.Invalid = 0; + Bits.OpaqueBits = 0; + Bits.DeclAttribute.Kind = static_cast(DK); + Bits.DeclAttribute.Implicit = Implicit; + Bits.DeclAttribute.Invalid = false; } +private: + // NOTE: We cannot use DeclKind due to layering. Even if we could, there is no + // guarantee that the first DeclKind starts at zero. This is only used to + // build "OnXYZ" flags. + enum class DeclKindIndex : unsigned { +#define DECL(Name, _) Name, +#define LAST_DECL(Name) Last_Decl = Name +#include "swift/AST/DeclNodes.def" + }; + public: - enum DeclAttrOptions { + enum DeclAttrOptions : uint64_t { + // There is one entry for each DeclKind, and some higher level buckets + // below. These are used in Attr.def to control which kinds of declarations + // an attribute can be attached to. +#define DECL(Name, _) On##Name = 1ull << unsigned(DeclKindIndex::Name), +#include "swift/AST/DeclNodes.def" + + // Abstract class aggregations for use in Attr.def. + OnValue = 0 +#define DECL(Name, _) +#define VALUE_DECL(Name, _) |On##Name +#include "swift/AST/DeclNodes.def" + , + + OnNominalType = 0 +#define DECL(Name, _) +#define NOMINAL_TYPE_DECL(Name, _) |On##Name +#include "swift/AST/DeclNodes.def" + , + OnConcreteNominalType = OnNominalType & ~OnProtocol, + OnGenericType = OnNominalType | OnTypeAlias, + + OnAbstractFunction = 0 +#define DECL(Name, _) +#define ABSTRACT_FUNCTION_DECL(Name, _) |On##Name +#include "swift/AST/DeclNodes.def" + , + + OnOperator = 0 +#define DECL(Name, _) +#define OPERATOR_DECL(Name, _) |On##Name +#include "swift/AST/DeclNodes.def" + , + + OnAnyDecl = 0 +#define DECL(Name, _) |On##Name +#include "swift/AST/DeclNodes.def" + , + /// True if multiple instances of this attribute are allowed on a single /// declaration. - AllowMultipleAttributes = 1 << 0, + AllowMultipleAttributes = 1ull << (unsigned(DeclKindIndex::Last_Decl) + 1), /// True if this is a decl modifier - i.e., that it should not be spelled /// with an @. - DeclModifier = 1 << 1, + DeclModifier = 1ull << (unsigned(DeclKindIndex::Last_Decl) + 2), /// True if this is a long attribute that should be printed on its own line. /// /// Currently has no effect on DeclModifier attributes. - LongAttribute = 1 << 2, + LongAttribute = 1ull << (unsigned(DeclKindIndex::Last_Decl) + 3), /// True if this shouldn't be serialized. - NotSerialized = 1 << 3, + NotSerialized = 1ull << (unsigned(DeclKindIndex::Last_Decl) + 4), /// True if this attribute is only valid when parsing a .sil file. - SILOnly = 1 << 4, + SILOnly = 1ull << (unsigned(DeclKindIndex::Last_Decl) + 5), /// The attribute should be reported by parser as unknown. - RejectByParser = 1 << 5, + RejectByParser = 1ull << (unsigned(DeclKindIndex::Last_Decl) + 6), /// Whether client code cannot use the attribute. - UserInaccessible = 1 << 6, - - // There is one entry for each DeclKind here, and some higher level buckets - // down below. These are used in Attr.def to control which kinds of - // declarations an attribute can be attached to. - OnPrecedenceGroup = 1 << 7, - OnImport = 1 << 8, - OnExtension = 1 << 9, - OnPatternBinding = 1 << 10, - OnEnumCase = 1 << 11, - OnTopLevelCode = 1 << 12, - OnIfConfig = 1 << 13, - OnInfixOperator = 1 << 14, // "infix operator" - OnPrefixOperator = 1 << 15, // "prefix operator" - OnPostfixOperator = 1 << 16, // "postfix operator" - - OnEnum = 1 << 17, - OnStruct = 1 << 18, - OnClass = 1 << 19, - OnProtocol = 1 << 20, - OnTypeAlias = 1 << 21, - OnVar = 1 << 22, - OnSubscript = 1 << 23, - - OnConstructor = 1 << 24, - OnDestructor = 1 << 25, - OnFunc = 1 << 26, - OnAccessor = OnFunc, - OnEnumElement = 1 << 27, - - OnGenericTypeParam = 1 << 28, - OnAssociatedType = 1 << 29, - OnParam = 1 << 30, - OnModule = 1 << 31, - - // Cannot have any attributes. - OnMissingMember = 0, - OnPoundDiagnostic = 0, - - // More coarse-grained aggregations for use in Attr.def. - OnOperator = OnInfixOperator|OnPrefixOperator|OnPostfixOperator, - - OnAnyDecl = OnImport|OnExtension|OnPatternBinding|OnEnumCase| - OnTopLevelCode|OnIfConfig|OnInfixOperator|OnPrefixOperator| - OnPostfixOperator|OnEnum|OnStruct|OnClass|OnProtocol| - OnTypeAlias|OnVar|OnSubscript|OnConstructor|OnDestructor| - OnFunc|OnEnumElement|OnGenericTypeParam|OnAssociatedType| - OnParam|OnPrecedenceGroup + UserInaccessible = 1ull << (unsigned(DeclKindIndex::Last_Decl) + 7), }; - static unsigned getOptions(DeclAttrKind DK); + static uint64_t getOptions(DeclAttrKind DK); - unsigned getOptions() const { + uint64_t getOptions() const { return getOptions(getKind()); } @@ -326,22 +351,22 @@ class DeclAttribute : public AttributeBase { public: DeclAttrKind getKind() const { - return static_cast(DeclAttrBits.Kind); + return static_cast(Bits.DeclAttribute.Kind); } /// Whether this attribute was implicitly added. - bool isImplicit() const { return DeclAttrBits.Implicit; } + bool isImplicit() const { return Bits.DeclAttribute.Implicit; } /// Set whether this attribute was implicitly added. - void setImplicit(bool Implicit) { - DeclAttrBits.Implicit = Implicit; + void setImplicit(bool Implicit = true) { + Bits.DeclAttribute.Implicit = Implicit; } /// Returns true if this attribute was find to be invalid in some way by /// semantic analysis. In that case, the attribute should not be considered, /// the attribute node should be only used to retrieve source information. - bool isInvalid() const { return DeclAttrBits.Invalid; } - void setInvalid() { DeclAttrBits.Invalid = true; } + bool isInvalid() const { return Bits.DeclAttribute.Invalid; } + void setInvalid() { Bits.DeclAttribute.Invalid = true; } bool isValid() const { return !isInvalid(); } @@ -518,11 +543,11 @@ class AlignmentAttr : public DeclAttribute { public: AlignmentAttr(unsigned Value, SourceLoc AtLoc, SourceRange Range, bool Implicit) - : DeclAttribute(DAK_Alignment, AtLoc, Range, Implicit), - Value(Value) {} - - // The alignment value. - const unsigned Value; + : DeclAttribute(DAK_Alignment, AtLoc, Range, Implicit) { + Bits.AlignmentAttr.Value = Value; + } + + unsigned getValue() const { return Bits.AlignmentAttr.Value; } static bool classof(const DeclAttribute *DA) { return DA->getKind() == DAK_Alignment; @@ -722,9 +747,9 @@ class ObjCAttr final : public DeclAttribute, : DeclAttribute(DAK_ObjC, SourceLoc(), SourceRange(), /*Implicit=*/true), NameData(nullptr) { - ObjCAttrBits.HasTrailingLocationInfo = false; - ObjCAttrBits.ImplicitName = implicitName; - ObjCAttrBits.Swift3Inferred = false; + Bits.ObjCAttr.HasTrailingLocationInfo = false; + Bits.ObjCAttr.ImplicitName = implicitName; + Bits.ObjCAttr.Swift3Inferred = false; if (name) { NameData = name->getOpaqueValue(); @@ -737,7 +762,7 @@ class ObjCAttr final : public DeclAttribute, /// Determine whether this attribute has trailing location information. bool hasTrailingLocationInfo() const { - return ObjCAttrBits.HasTrailingLocationInfo; + return Bits.ObjCAttr.HasTrailingLocationInfo; } /// Retrieve the trailing location information. @@ -815,7 +840,7 @@ class ObjCAttr final : public DeclAttribute, /// Determine whether the name associated with this attribute was /// implicit. - bool isNameImplicit() const { return ObjCAttrBits.ImplicitName; } + bool isNameImplicit() const { return Bits.ObjCAttr.ImplicitName; } /// Set the name of this entity. void setName(ObjCSelector name, bool implicit) { @@ -825,23 +850,23 @@ class ObjCAttr final : public DeclAttribute, if (hasTrailingLocationInfo() && (!hasName() || getName()->getNumSelectorPieces() < name.getNumSelectorPieces())) { - ObjCAttrBits.HasTrailingLocationInfo = false; + Bits.ObjCAttr.HasTrailingLocationInfo = false; } NameData = name.getOpaqueValue(); - ObjCAttrBits.ImplicitName = implicit; + Bits.ObjCAttr.ImplicitName = implicit; } /// Determine whether this attribute was inferred based on Swift 3's /// deprecated @objc inference rules. bool isSwift3Inferred() const { - return ObjCAttrBits.Swift3Inferred; + return Bits.ObjCAttr.Swift3Inferred; } /// Set whether this attribute was inferred based on Swift 3's deprecated /// @objc inference rules. void setSwift3Inferred(bool inferred = true) { - ObjCAttrBits.Swift3Inferred = inferred; + Bits.ObjCAttr.Swift3Inferred = inferred; } /// Clear the name of this entity. @@ -874,13 +899,13 @@ class AbstractAccessControlAttr : public DeclAttribute { AbstractAccessControlAttr(DeclAttrKind DK, SourceLoc atLoc, SourceRange range, AccessLevel access, bool implicit) : DeclAttribute(DK, atLoc, range, implicit) { - AccessControlAttrBits.AccessLevel = static_cast(access); + Bits.AbstractAccessControlAttr.AccessLevel = static_cast(access); assert(getAccess() == access && "not enough bits for access control"); } public: AccessLevel getAccess() const { - return static_cast(AccessControlAttrBits.AccessLevel); + return static_cast(Bits.AbstractAccessControlAttr.AccessLevel); } static bool classof(const DeclAttribute *DA) { @@ -918,16 +943,16 @@ class SetterAccessAttr : public AbstractAccessControlAttr { /// Represents an inline attribute. class InlineAttr : public DeclAttribute { - InlineKind Kind; public: InlineAttr(SourceLoc atLoc, SourceRange range, InlineKind kind) - : DeclAttribute(DAK_Inline, atLoc, range, /*Implicit=*/false), - Kind(kind) {} + : DeclAttribute(DAK_Inline, atLoc, range, /*Implicit=*/false) { + Bits.InlineAttr.kind = unsigned(kind); + } InlineAttr(InlineKind kind) : InlineAttr(SourceLoc(), SourceRange(), kind) {} - InlineKind getKind() const { return Kind; } + InlineKind getKind() const { return InlineKind(Bits.InlineAttr.kind); } static bool classof(const DeclAttribute *DA) { return DA->getKind() == DAK_Inline; } @@ -935,16 +960,18 @@ class InlineAttr : public DeclAttribute { /// Represents the optimize attribute. class OptimizeAttr : public DeclAttribute { - OptimizationMode Mode; public: OptimizeAttr(SourceLoc atLoc, SourceRange range, OptimizationMode mode) - : DeclAttribute(DAK_Optimize, atLoc, range, /*Implicit=*/false), - Mode(mode) {} + : DeclAttribute(DAK_Optimize, atLoc, range, /*Implicit=*/false) { + Bits.OptimizeAttr.mode = unsigned(mode); + } OptimizeAttr(OptimizationMode mode) : OptimizeAttr(SourceLoc(), SourceRange(), mode) {} - OptimizationMode getMode() const { return Mode; } + OptimizationMode getMode() const { + return OptimizationMode(Bits.OptimizeAttr.mode); + } static bool classof(const DeclAttribute *DA) { return DA->getKind() == DAK_Optimize; } @@ -952,16 +979,16 @@ class OptimizeAttr : public DeclAttribute { /// Represents the side effects attribute. class EffectsAttr : public DeclAttribute { - EffectsKind Kind; public: EffectsAttr(SourceLoc atLoc, SourceRange range, EffectsKind kind) - : DeclAttribute(DAK_Effects, atLoc, range, /*Implicit=*/false), - Kind(kind) {} + : DeclAttribute(DAK_Effects, atLoc, range, /*Implicit=*/false) { + Bits.EffectsAttr.kind = unsigned(kind); + } EffectsAttr(EffectsKind kind) : EffectsAttr(SourceLoc(), SourceRange(), kind) {} - EffectsKind getKind() const { return Kind; } + EffectsKind getKind() const { return EffectsKind(Bits.EffectsAttr.kind); } static bool classof(const DeclAttribute *DA) { return DA->getKind() == DAK_Effects; } @@ -971,18 +998,19 @@ class EffectsAttr : public DeclAttribute { /// Represents weak/unowned/unowned(unsafe) decl modifiers. class ReferenceOwnershipAttr : public DeclAttribute { - const ReferenceOwnership ownership; - public: ReferenceOwnershipAttr(SourceRange range, ReferenceOwnership kind) : DeclAttribute(DAK_ReferenceOwnership, range.Start, range, - /*Implicit=*/false), - ownership(kind) {} + /*Implicit=*/false) { + Bits.ReferenceOwnershipAttr.ownership = unsigned(kind); + } ReferenceOwnershipAttr(ReferenceOwnership kind) : ReferenceOwnershipAttr(SourceRange(), kind) {} - ReferenceOwnership get() const { return ownership; } + ReferenceOwnership get() const { + return ReferenceOwnership(Bits.ReferenceOwnershipAttr.ownership); + } /// Returns a copy of this attribute without any source information. ReferenceOwnershipAttr *clone(ASTContext &context) const { @@ -1045,21 +1073,22 @@ class ObjCBridgedAttr : public DeclAttribute { /// rather, it is introduced by the Clang importer to indicate /// synthesized conformances. class SynthesizedProtocolAttr : public DeclAttribute { - KnownProtocolKind ProtocolKind; LazyConformanceLoader *Loader; public: SynthesizedProtocolAttr(KnownProtocolKind protocolKind, LazyConformanceLoader *Loader) : DeclAttribute(DAK_SynthesizedProtocol, SourceLoc(), SourceRange(), - /*Implicit=*/true), - ProtocolKind(protocolKind), Loader(Loader) + /*Implicit=*/true), Loader(Loader) { + Bits.SynthesizedProtocolAttr.kind = unsigned(protocolKind); } /// Retrieve the known protocol kind naming the protocol to be /// synthesized. - KnownProtocolKind getProtocolKind() const { return ProtocolKind; } + KnownProtocolKind getProtocolKind() const { + return KnownProtocolKind(Bits.SynthesizedProtocolAttr.kind); + } /// Retrieve the lazy loader that will be used to populate the /// synthesized conformance. @@ -1074,16 +1103,14 @@ class SynthesizedProtocolAttr : public DeclAttribute { /// type list. class SpecializeAttr : public DeclAttribute { public: + // NOTE: When adding new kinds, you must update the inline bitfield macro. enum class SpecializationKind { Full, Partial }; private: - unsigned numRequirements; TrailingWhereClause *trailingWhereClause; - SpecializationKind kind; - bool exported; Requirement *getRequirementsData() { return reinterpret_cast(this+1); @@ -1113,25 +1140,25 @@ class SpecializeAttr : public DeclAttribute { ArrayRef getRequirements() const; MutableArrayRef getRequirements() { - return { getRequirementsData(), numRequirements }; + return { getRequirementsData(), Bits.SpecializeAttr.numRequirements }; } void setRequirements(ASTContext &Ctx, ArrayRef requirements); bool isExported() const { - return exported; + return Bits.SpecializeAttr.exported; } SpecializationKind getSpecializationKind() const { - return kind; + return SpecializationKind(Bits.SpecializeAttr.kind); } bool isFullSpecialization() const { - return kind == SpecializationKind::Full; + return getSpecializationKind() == SpecializationKind::Full; } bool isPartialSpecialization() const { - return kind == SpecializationKind::Partial; + return getSpecializationKind() == SpecializationKind::Partial; } static bool classof(const DeclAttribute *DA) { @@ -1214,6 +1241,7 @@ class RestatedObjCConformanceAttr : public DeclAttribute { /// Used to control manglings. class ClangImporterSynthesizedTypeAttr : public DeclAttribute { public: + // NOTE: When adding new kinds, you must update the inline bitfield macro. enum class Kind : char { /// A struct synthesized by the importer to represent an NSError with a /// particular domain, as specified by an enum with the \c ns_error_domain @@ -1236,18 +1264,22 @@ class ClangImporterSynthesizedTypeAttr : public DeclAttribute { /// /// Must be a valid Swift identifier as well, for mangling purposes. const StringRef originalTypeName; - const Kind kind; explicit ClangImporterSynthesizedTypeAttr(StringRef originalTypeName, Kind kind) : DeclAttribute(DAK_ClangImporterSynthesizedType, SourceLoc(), SourceRange(), /*Implicit=*/true), - originalTypeName(originalTypeName), kind(kind) { + originalTypeName(originalTypeName) { assert(!originalTypeName.empty()); + Bits.ClangImporterSynthesizedTypeAttr.kind = unsigned(kind); + } + + Kind getKind() const { + return Kind(Bits.ClangImporterSynthesizedTypeAttr.kind); } StringRef getManglingName() const { - return manglingNameForKind(kind); + return manglingNameForKind(getKind()); } static StringRef manglingNameForKind(Kind kind) { diff --git a/include/swift/AST/AttrKind.h b/include/swift/AST/AttrKind.h index e67569a7aec02..5c02ce667520a 100644 --- a/include/swift/AST/AttrKind.h +++ b/include/swift/AST/AttrKind.h @@ -17,6 +17,7 @@ #ifndef SWIFT_ATTRKIND_H #define SWIFT_ATTRKIND_H +#include "swift/Basic/InlineBitfield.h" #include "swift/Config.h" #include "llvm/Support/DataTypes.h" @@ -64,9 +65,13 @@ enum class AccessLevel : uint8_t { enum class InlineKind : uint8_t { Never = 0, - Always = 1 + Always = 1, + Last_InlineKind = Always }; +enum : unsigned { NumInlineKindBits = + countBitsUsed(static_cast(InlineKind::Last_InlineKind)) }; + /// This enum represents the possible values of the @effects attribute. /// These values are ordered from the strongest guarantee to the weakest, /// so please do not reorder existing values. @@ -75,9 +80,13 @@ enum class EffectsKind : uint8_t { ReadOnly, ReleaseNone, ReadWrite, - Unspecified + Unspecified, + Last_EffectsKind = Unspecified }; +enum : unsigned { NumEffectsKindBits = + countBitsUsed(static_cast(EffectsKind::Last_EffectsKind)) }; + enum DeclAttrKind : unsigned { #define DECL_ATTR(_, NAME, ...) DAK_##NAME, @@ -85,6 +94,9 @@ enum DeclAttrKind : unsigned { DAK_Count }; +enum : unsigned { NumDeclAttrKindBits = + countBitsUsed(static_cast(DeclAttrKind::DAK_Count - 1)) }; + // Define enumerators for each type attribute, e.g. TAK_weak. enum TypeAttrKind { #define TYPE_ATTR(X) TAK_##X, diff --git a/include/swift/AST/Builtins.def b/include/swift/AST/Builtins.def index 2f05bbcdf48b4..ab17d2a0109f8 100644 --- a/include/swift/AST/Builtins.def +++ b/include/swift/AST/Builtins.def @@ -272,11 +272,16 @@ BUILTIN_SIL_OPERATION(CastReference, "castReference", Special) /// reinterpretCast has type T -> U. BUILTIN_SIL_OPERATION(ReinterpretCast, "reinterpretCast", Special) -/// addressof ([inout] T) -> Builtin.RawPointer -/// Returns a RawPointer pointing to an lvalue. The returned pointer is only -/// valid within the scope of the statement for logical lvalues. +/// addressof (inout T) -> Builtin.RawPointer +/// Returns a RawPointer pointing to a physical lvalue. The returned pointer is +/// only valid for the duration of the original binding. BUILTIN_SIL_OPERATION(AddressOf, "addressof", Special) +/// addressOfBorrow (__shared T) -> Builtin.RawPointer +/// Returns a RawPointer pointing to a borrowed rvalue. The returned pointer is only +/// valid within the scope of the borrow. +BUILTIN_SIL_OPERATION(AddressOfBorrow, "addressOfBorrow", Special) + /// GepRaw(Builtin.RawPointer, Builtin.Word) -> Builtin.RawPointer /// /// Adds index bytes to a base pointer. @@ -294,6 +299,27 @@ BUILTIN_SIL_OPERATION(Gep, "gep", Integer) /// allocated element type 'E'. BUILTIN_SIL_OPERATION(GetTailAddr, "getTailAddr", Integer) +/// performInstantaneousReadAccess(Builtin.RawPointer, T.Type) -> () +/// Begin and then immediately end a read access to the given raw pointer, +/// which will be treated as an address of type 'T'. +BUILTIN_SIL_OPERATION(PerformInstantaneousReadAccess, + "performInstantaneousReadAccess", Special) + +/// beginUnpairedModifyAccess(Builtin.RawPointer, Builtin.RawPointer, +/// T.Type) -> () +/// Begins but does not end a 'modify' access to the first raw pointer argument. +/// The second raw pointer must be a pointer to an UnsafeValueBuffer, which +/// will be used by the runtime to record the access. The lifetime of the +/// value buffer must be longer than that of the access itself. The accessed +/// address will be treated as having type 'T'. +BUILTIN_SIL_OPERATION(BeginUnpairedModifyAccess, "beginUnpairedModifyAccess", + Special) + +/// endUnpairedAccess(Builtin.RawPointer) -> () +/// Ends an in-progress unpaired access. The raw pointer argument must be +/// be a pointer to an UnsafeValueBuffer that records an in progress access. +BUILTIN_SIL_OPERATION(EndUnpairedAccess, "endUnpairedAccess", Special) + /// condfail(Int1) -> () /// Triggers a runtime failure if the condition is true. BUILTIN_SIL_OPERATION(CondFail, "condfail", Special) diff --git a/include/swift/AST/ClangNode.h b/include/swift/AST/ClangNode.h index f1051a9a01fcd..cbf120840b661 100644 --- a/include/swift/AST/ClangNode.h +++ b/include/swift/AST/ClangNode.h @@ -111,7 +111,7 @@ class ClangNode { namespace llvm { template struct PointerLikeTypeTraits> { - using Box = swift::detail::ClangNodeBox; + using Box = ::swift::detail::ClangNodeBox; static inline void *getAsVoidPointer(Box P) { return const_cast(static_cast(P.value)); diff --git a/include/swift/AST/ConcreteDeclRef.h b/include/swift/AST/ConcreteDeclRef.h index d9c378e0039c1..a62d57ee32752 100644 --- a/include/swift/AST/ConcreteDeclRef.h +++ b/include/swift/AST/ConcreteDeclRef.h @@ -18,12 +18,11 @@ #define SWIFT_AST_CONCRETEDECLREF_H #include "swift/Basic/LLVM.h" -#include "swift/AST/SubstitutionList.h" +#include "swift/AST/SubstitutionMap.h" #include "swift/AST/TypeAlignments.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/PointerUnion.h" #include "llvm/Support/Compiler.h" -#include "llvm/Support/TrailingObjects.h" #include namespace swift { @@ -36,100 +35,49 @@ class ValueDecl; /// providing substitutions for all type parameters of the original, /// underlying declaration. class ConcreteDeclRef { - /// A specialized declaration reference, which provides substitutions - /// that fully specialize a generic declaration. - class SpecializedDeclRef final : - private llvm::TrailingObjects { - friend TrailingObjects; - - /// The declaration. - ValueDecl *TheDecl; - - /// The number of substitutions, which are tail allocated. - unsigned NumSubstitutions; - - SpecializedDeclRef(ValueDecl *decl, SubstitutionList substitutions) - : TheDecl(decl), NumSubstitutions(substitutions.size()) - { - std::uninitialized_copy(substitutions.begin(), substitutions.end(), - getTrailingObjects()); - } - - public: - /// Retrieve the generic declaration. - ValueDecl *getDecl() const { return TheDecl; } - - /// Retrieve the substitutions. - SubstitutionList getSubstitutions() const { - return {getTrailingObjects(), NumSubstitutions}; - } - - /// Allocate a new specialized declaration reference. - static SpecializedDeclRef *create(ASTContext &ctx, ValueDecl *decl, - SubstitutionList substitutions); - }; - - llvm::PointerUnion Data; - - friend struct llvm::PointerLikeTypeTraits; + /// The declaration. + ValueDecl *decl = nullptr; + + /// The substitutions. + SubstitutionMap substitutions; public: /// Create an empty declaration reference. - ConcreteDeclRef() : Data() { } + ConcreteDeclRef() { } /// Construct a reference to the given value. - ConcreteDeclRef(ValueDecl *decl) : Data(decl) { } + ConcreteDeclRef(ValueDecl *decl) : decl(decl) { } /// Construct a reference to the given value, specialized with the given /// substitutions. /// - /// \param ctx The ASTContext in which to allocate the specialized - /// declaration reference. - /// /// \param decl The declaration to which this reference refers, which will /// be specialized by applying the given substitutions. /// /// \param substitutions The complete set of substitutions to apply to the - /// given declaration. This array will be copied into the ASTContext by the - /// constructor. - ConcreteDeclRef(ASTContext &ctx, ValueDecl *decl, - SubstitutionList substitutions) { - if (substitutions.empty()) - Data = decl; - else - Data = SpecializedDeclRef::create(ctx, decl, substitutions); - } + /// given declaration. + ConcreteDeclRef(ValueDecl *decl, SubstitutionMap substitutions) + : decl(decl), substitutions(substitutions) { } /// Determine whether this declaration reference refers to anything. - explicit operator bool() const { return !Data.isNull(); } + explicit operator bool() const { return decl != nullptr; } /// Retrieve the declarations to which this reference refers. - ValueDecl *getDecl() const { - if (Data.is()) - return Data.get(); - - return Data.get()->getDecl(); - } + ValueDecl *getDecl() const { return decl; } /// Retrieve a reference to the declaration this one overrides. - ConcreteDeclRef - getOverriddenDecl(ASTContext &ctx) const; + ConcreteDeclRef getOverriddenDecl() const; /// Determine whether this reference specializes the declaration to which /// it refers. - bool isSpecialized() const { return Data.is(); } + bool isSpecialized() const { return !substitutions.empty(); } /// For a specialized reference, return the set of substitutions applied to /// the declaration reference. - SubstitutionList getSubstitutions() const { - if (!isSpecialized()) - return { }; - - return Data.get()->getSubstitutions(); - } + SubstitutionMap getSubstitutions() const { return substitutions; } - bool operator==(ConcreteDeclRef rhs) const { - return Data == rhs.Data; + friend bool operator==(ConcreteDeclRef lhs, ConcreteDeclRef rhs) { + return lhs.decl == rhs.decl && lhs.substitutions == rhs.substitutions; } /// Dump a debug representation of this reference. @@ -139,29 +87,4 @@ class ConcreteDeclRef { } // end namespace swift -namespace llvm { - template<> struct PointerLikeTypeTraits { - typedef llvm::PointerUnion - DataPointer; - typedef PointerLikeTypeTraits DataTraits; - - public: - static inline void * - getAsVoidPointer(swift::ConcreteDeclRef ref) { - return ref.Data.getOpaqueValue(); - } - - static inline swift::ConcreteDeclRef getFromVoidPointer(void *ptr) { - swift::ConcreteDeclRef ref; - ref.Data = DataPointer::getFromOpaqueValue(ptr); - return ref; - } - - enum { - NumLowBitsAvailable = DataTraits::NumLowBitsAvailable - }; - }; -} // end namespace llvm - #endif diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index aa2e2f4782566..69b91695c7378 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -122,7 +122,8 @@ enum class DescriptiveDeclKind : uint8_t { PrecedenceGroup, TypeAlias, GenericTypeParam, - AssociatedType, + AssociatedType, + Type, Enum, Struct, Class, @@ -130,6 +131,7 @@ enum class DescriptiveDeclKind : uint8_t { GenericEnum, GenericStruct, GenericClass, + GenericType, Subscript, Constructor, Destructor, @@ -149,6 +151,7 @@ enum class DescriptiveDeclKind : uint8_t { EnumElement, Module, MissingMember, + Requirement, }; /// Keeps track of stage of circularity checking for the given protocol. @@ -367,7 +370,7 @@ class alignas(1 << DeclAlignInBits) Decl { DefaultArgumentResilienceExpansion : 1 ); - SWIFT_INLINE_BITFIELD(AbstractFunctionDecl, ValueDecl, 3+8+5+1+1+1+1+1, + SWIFT_INLINE_BITFIELD(AbstractFunctionDecl, ValueDecl, 3+8+5+1+1+1+1+1+1, /// \see AbstractFunctionDecl::BodyKind BodyKind : 3, @@ -390,7 +393,11 @@ class alignas(1 << DeclAlignInBits) Decl { HasComputedNeedsNewVTableEntry : 1, /// The ResilienceExpansion to use for default arguments. - DefaultArgumentResilienceExpansion : 1 + DefaultArgumentResilienceExpansion : 1, + + /// Whether this member was synthesized as part of a derived + /// protocol conformance. + Synthesized : 1 ); SWIFT_INLINE_BITFIELD(FuncDecl, AbstractFunctionDecl, 1+2+1+1+2, @@ -2146,6 +2153,7 @@ class ValueDecl : public Decl { DeclName Name; SourceLoc NameLoc; llvm::PointerIntPair> TypeAndAccess; + unsigned LocalDiscriminator = 0; private: bool isUsableFromInline() const; @@ -2170,11 +2178,6 @@ class ValueDecl : public Decl { } public: - /// \brief Return true if this is a definition of a decl, not a forward - /// declaration (e.g. of a function) that is implemented outside of the - /// swift code. - bool isDefinition() const; - /// \brief Return true if this protocol member is a protocol requirement. /// /// Asserts if this is not a member of a protocol. @@ -2243,17 +2246,17 @@ class ValueDecl : public Decl { /// being used), features that affect formal access such as \c \@testable are /// taken into account. /// - /// If \p isUsageFromInline is true, the presence of the \c @usableFromInline - /// attribute will treat internal declarations as public. This is normally + /// If \p treatUsableFromInlineAsPublic is true, declarations marked with the + /// \c @usableFromInline attribute are treated as public. This is normally /// false for name lookup and other source language concerns, but true when /// computing the linkage of generated functions. /// /// \sa getFormalAccessScope AccessLevel getFormalAccess(const DeclContext *useDC = nullptr, - bool isUsageFromInline = false) const { + bool treatUsableFromInlineAsPublic = false) const { assert(hasAccess() && "access not computed yet"); AccessLevel result = TypeAndAccess.getInt().getValue(); - if (isUsageFromInline && + if (treatUsableFromInlineAsPublic && result == AccessLevel::Internal && isUsableFromInline()) { assert(!useDC); @@ -2265,6 +2268,18 @@ class ValueDecl : public Decl { return result; } + /// If this declaration is a member of a protocol extension, return the + /// minimum of the given access level and the protocol's access level. + /// + /// Otherwise, return the given access level unmodified. + /// + /// This is used when checking name lookup visibility. Protocol extension + /// members can be found when performing name lookup on a concrete type; + /// if the concrete type is visible from the lookup context but the + /// protocol is not, we do not want the protocol extension members to be + /// visible. + AccessLevel adjustAccessLevelForProtocolExtension(AccessLevel access) const; + /// Determine whether this Decl has either Private or FilePrivate access. bool isOutermostPrivateOrFilePrivateScope() const; @@ -2277,17 +2292,29 @@ class ValueDecl : public Decl { /// taken into account. /// /// \invariant - /// value.isAccessibleFrom(value.getFormalAccessScope()) + /// value.isAccessibleFrom( + /// value.getFormalAccessScope().getDeclContext()) + /// + /// If \p treatUsableFromInlineAsPublic is true, declarations marked with the + /// \c @usableFromInline attribute are treated as public. This is normally + /// false for name lookup and other source language concerns, but true when + /// computing the linkage of generated functions. /// /// \sa getFormalAccess /// \sa isAccessibleFrom AccessScope getFormalAccessScope(const DeclContext *useDC = nullptr, - bool respectVersionedAttr = false) const; + bool treatUsableFromInlineAsPublic = false) const; - /// Copy the formal access level and @usableFromInline attribute from source. - void copyFormalAccessFrom(ValueDecl *source); + /// Copy the formal access level and @usableFromInline attribute from + /// \p source. + /// + /// If \p sourceIsParentContext is true, an access level of \c private will + /// be copied as \c fileprivate, to ensure that this declaration will be + /// available everywhere \p source is. + void copyFormalAccessFrom(const ValueDecl *source, + bool sourceIsParentContext = false); /// Returns the access level that actually controls how a declaration should /// be emitted and may be used. @@ -2319,7 +2346,14 @@ class ValueDecl : public Decl { /// A public declaration is accessible everywhere. /// /// If \p DC is null, returns true only if this declaration is public. - bool isAccessibleFrom(const DeclContext *DC) const; + /// + /// If \p forConformance is true, we ignore the visibility of the protocol + /// when evaluating protocol extension members. This language rule allows a + /// protocol extension of a private protocol to provide default + /// implementations for the requirements of a public protocol, even when + /// the default implementations are not visible to name lookup. + bool isAccessibleFrom(const DeclContext *DC, + bool forConformance = false) const; /// Retrieve the "interface" type of this value, which uses /// GenericTypeParamType if the declaration is generic. For a generic @@ -2343,10 +2377,6 @@ class ValueDecl : public Decl { /// of an enum or protocol. bool isInstanceMember() const; - /// needsCapture - Check whether referring to this decl from a nested - /// function requires capturing it. - bool needsCapture() const; - /// Retrieve the context discriminator for this local value, which /// is the index of this declaration in the sequence of /// discriminated declarations with the same name in the current @@ -2485,6 +2515,14 @@ class TypeDecl : public ValueDecl { /// Compute an ordering between two type declarations that is ABI-stable. static int compare(const TypeDecl *type1, const TypeDecl *type2); + + /// Compute an ordering between two type declarations that is ABI-stable. + /// This version takes a pointer-to-a-pointer for use with + /// llvm::array_pod_sort() and similar. + template + static int compare(T * const* type1, T * const* type2) { + return compare(*type1, *type2); + } }; /// A type declaration that can have generic parameters attached to it. Because @@ -4443,7 +4481,11 @@ class AbstractStorageDecl : public ValueDecl { /// context. /// /// If \p DC is null, returns true only if the setter is public. - bool isSetterAccessibleFrom(const DeclContext *DC) const; + /// + /// See \c isAccessibleFrom for a discussion of the \p forConformance + /// parameter. + bool isSetterAccessibleFrom(const DeclContext *DC, + bool forConformance=false) const; /// Determine how this storage declaration should actually be accessed. AccessStrategy getAccessStrategy(AccessSemantics semantics, @@ -5062,6 +5104,7 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl { Bits.AbstractFunctionDecl.HasComputedNeedsNewVTableEntry = false; Bits.AbstractFunctionDecl.DefaultArgumentResilienceExpansion = unsigned(ResilienceExpansion::Maximal); + Bits.AbstractFunctionDecl.Synthesized = false; // Verify no bitfield truncation. assert(Bits.AbstractFunctionDecl.NumParameterLists == NumParameterLists); @@ -5210,6 +5253,14 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl { return Bits.AbstractFunctionDecl.NeedsNewVTableEntry; } + bool isSynthesized() const { + return Bits.AbstractFunctionDecl.Synthesized; + } + + void setSynthesized(bool value = true) { + Bits.AbstractFunctionDecl.Synthesized = value; + } + private: void computeNeedsNewVTableEntry(); @@ -6660,7 +6711,8 @@ inline bool ValueDecl::isImportAsMember() const { inline bool Decl::isPotentiallyOverridable() const { if (isa(this) || isa(this) || - isa(this)) { + isa(this) || + isa(this)) { return getDeclContext()->getAsClassOrClassExtensionContext(); } else { return false; diff --git a/include/swift/AST/DeclNodes.def b/include/swift/AST/DeclNodes.def index ccffcaaac09e9..eca225a8f88c1 100644 --- a/include/swift/AST/DeclNodes.def +++ b/include/swift/AST/DeclNodes.def @@ -116,6 +116,13 @@ #define NOMINAL_TYPE_DECL(Id, Parent) ITERABLE_GENERIC_VALUE_DECL(Id, Parent) #endif +/// ABSTRACT_FUNCTION_DECL(Id, Parent) +/// Used for subclasses of AbstractFunction. The default behavior is +/// to do the same as for GENERIC_VALUE_DECL. +#ifndef ABSTRACT_FUNCTION_DECL +#define ABSTRACT_FUNCTION_DECL(Id, Parent) GENERIC_VALUE_DECL(Id, Parent) +#endif + /// VALUE_DECL(Id, Parent) /// Used for subclasses of ValueDecl. The default behavior is to do /// the same as for Decl. @@ -159,10 +166,10 @@ ABSTRACT_DECL(Value, Decl) GENERIC_VALUE_DECL(Subscript, AbstractStorageDecl) DECL_RANGE(AbstractStorage, Var, Subscript) ABSTRACT_DECL(AbstractFunction, ValueDecl) - GENERIC_VALUE_DECL(Constructor, AbstractFunctionDecl) - GENERIC_VALUE_DECL(Destructor, AbstractFunctionDecl) - GENERIC_VALUE_DECL(Func, AbstractFunctionDecl) - GENERIC_VALUE_DECL(Accessor, FuncDecl) + ABSTRACT_FUNCTION_DECL(Constructor, AbstractFunctionDecl) + ABSTRACT_FUNCTION_DECL(Destructor, AbstractFunctionDecl) + ABSTRACT_FUNCTION_DECL(Func, AbstractFunctionDecl) + ABSTRACT_FUNCTION_DECL(Accessor, FuncDecl) DECL_RANGE(AbstractFunction, Constructor, Accessor) VALUE_DECL(EnumElement, ValueDecl) DECL_RANGE(Value, Enum, EnumElement) @@ -194,8 +201,10 @@ LAST_DECL(PostfixOperator) #undef CONTEXT_VALUE_DECL #undef GENERIC_VALUE_DECL #undef ITERABLE_GENERIC_VALUE_DECL +#undef ABSTRACT_FUNCTION_DECL #undef VALUE_DECL #undef DECL_RANGE #undef ABSTRACT_DECL +#undef OPERATOR_DECL #undef DECL #undef LAST_DECL diff --git a/include/swift/AST/DiagnosticConsumer.h b/include/swift/AST/DiagnosticConsumer.h index 578e432e5dd87..16470488f4796 100644 --- a/include/swift/AST/DiagnosticConsumer.h +++ b/include/swift/AST/DiagnosticConsumer.h @@ -100,7 +100,7 @@ class DiagnosticConsumer { const DiagnosticInfo &Info) = 0; /// \returns true if an error occurred while finishing-up. - virtual bool finishProcessing() { return false; } + virtual bool finishProcessing(SourceManager &) { return false; } }; /// \brief DiagnosticConsumer that discards all diagnostics. @@ -131,6 +131,8 @@ class FileSpecificDiagnosticConsumer : public DiagnosticConsumer { /// be associated with. An empty string means that a consumer is not /// associated with any particular buffer, and should only receive diagnostics /// that are not in any of the other consumers' files. + /// A null pointer for the DiagnosticConsumer means that diagnostics for this + /// file should not be emitted. using ConsumerPair = std::pair>; @@ -138,9 +140,22 @@ class FileSpecificDiagnosticConsumer : public DiagnosticConsumer { /// All consumers owned by this FileSpecificDiagnosticConsumer. const SmallVector SubConsumers; - using ConsumersOrderedByRangeEntry = - std::pair; +public: + // The commented-out consts are there because the data does not change + // but the swap method gets called on this structure. + struct ConsumerSpecificInformation { + /*const*/ CharSourceRange range; + /// The DiagnosticConsumer may be empty if those diagnostics are not to be + /// emitted. + DiagnosticConsumer * /*const*/ consumer; + bool hasAnErrorBeenEmitted = false; + + ConsumerSpecificInformation(const CharSourceRange range, + DiagnosticConsumer *const consumer) + : range(range), consumer(consumer) {} + }; +private: /// The consumers owned by this FileSpecificDiagnosticConsumer, sorted by /// the end locations of each file so that a lookup by position can be done /// using binary search. @@ -149,16 +164,20 @@ class FileSpecificDiagnosticConsumer : public DiagnosticConsumer { /// This allows diagnostics to be emitted before files are actually opened, /// as long as they don't have source locations. /// - /// \see #consumerForLocation - SmallVector ConsumersOrderedByRange; + /// \see #consumerSpecificInformationForLocation + SmallVector ConsumersOrderedByRange; /// Indicates which consumer to send Note diagnostics too. /// /// Notes are always considered attached to the error, warning, or remark /// that was most recently emitted. /// - /// If null, Note diagnostics are sent to every consumer. - DiagnosticConsumer *ConsumerForSubsequentNotes = nullptr; + /// If None, Note diagnostics are sent to every consumer. + /// If null, diagnostics are suppressed. + Optional + ConsumerSpecificInfoForSubsequentNotes = None; + + bool HasAnErrorBeenConsumed = false; public: /// Takes ownership of the DiagnosticConsumers specified in \p consumers. @@ -174,12 +193,22 @@ class FileSpecificDiagnosticConsumer : public DiagnosticConsumer { ArrayRef FormatArgs, const DiagnosticInfo &Info) override; - bool finishProcessing() override; + bool finishProcessing(SourceManager &) override; private: + /// In batch mode, any error causes failure for all primary files, but + /// Xcode will only see an error for a particular primary in that primary's + /// serialized diagnostics file. So, emit errors for all other primaries here. + void addNonSpecificErrors(SourceManager &SM); + void computeConsumersOrderedByRange(SourceManager &SM); - DiagnosticConsumer *consumerForLocation(SourceManager &SM, - SourceLoc loc) const; + + /// Returns nullptr if diagnostic is to be suppressed, + /// None if diagnostic is to be distributed to every consumer, + /// a particular consumer if diagnostic goes there. + Optional + consumerSpecificInformationForLocation(SourceManager &SM, + SourceLoc loc) const; }; } // end namespace swift diff --git a/include/swift/AST/DiagnosticEngine.h b/include/swift/AST/DiagnosticEngine.h index 389984de016c0..ee37c39b335e7 100644 --- a/include/swift/AST/DiagnosticEngine.h +++ b/include/swift/AST/DiagnosticEngine.h @@ -30,6 +30,7 @@ namespace swift { class ValueDecl; enum class PatternKind : uint8_t; + enum class ReferenceOwnership : uint8_t; enum class StaticSpellingKind : uint8_t; enum class DescriptiveDeclKind : uint8_t; enum DeclAttrKind : unsigned; @@ -76,6 +77,7 @@ namespace swift { Type, TypeRepr, PatternKind, + ReferenceOwnership, StaticSpellingKind, DescriptiveDeclKind, DeclAttribute, @@ -103,6 +105,7 @@ namespace swift { Type TypeVal; TypeRepr *TyR; PatternKind PatternKindVal; + ReferenceOwnership ReferenceOwnershipVal; StaticSpellingKind StaticSpellingKindVal; DescriptiveDeclKind DescriptiveDeclKindVal; const DeclAttribute *DeclAttributeVal; @@ -162,6 +165,10 @@ namespace swift { DiagnosticArgument(PatternKind K) : Kind(DiagnosticArgumentKind::PatternKind), PatternKindVal(K) {} + DiagnosticArgument(ReferenceOwnership RO) + : Kind(DiagnosticArgumentKind::ReferenceOwnership), + ReferenceOwnershipVal(RO) {} + DiagnosticArgument(StaticSpellingKind SSK) : Kind(DiagnosticArgumentKind::StaticSpellingKind), StaticSpellingKindVal(SSK) {} @@ -237,6 +244,11 @@ namespace swift { return PatternKindVal; } + ReferenceOwnership getAsReferenceOwnership() const { + assert(Kind == DiagnosticArgumentKind::ReferenceOwnership); + return ReferenceOwnershipVal; + } + StaticSpellingKind getAsStaticSpellingKind() const { assert(Kind == DiagnosticArgumentKind::StaticSpellingKind); return StaticSpellingKindVal; @@ -747,8 +759,8 @@ namespace swift { /// \returns true if any diagnostic consumer gave an error while invoking //// \c finishProcessing. - bool finishProcessing(); - + bool finishProcessing(SourceManager &); + /// \brief Format the given diagnostic text and place the result in the given /// buffer. static void formatDiagnosticText( @@ -769,6 +781,9 @@ namespace swift { /// \brief Send all tentative diagnostics to all diagnostic consumers and /// delete them. void emitTentativeDiagnostics(); + + public: + static const char *diagnosticStringFor(const DiagID id); }; /// \brief Represents a diagnostic transaction. While a transaction is diff --git a/include/swift/AST/DiagnosticsCommon.def b/include/swift/AST/DiagnosticsCommon.def index a531f3df9de1a..f671c43b25306 100644 --- a/include/swift/AST/DiagnosticsCommon.def +++ b/include/swift/AST/DiagnosticsCommon.def @@ -49,8 +49,7 @@ ERROR(error_no_group_info,none, "no group info found for file: '%0'", (StringRef)) NOTE(previous_decldef,none, - "previous %select{declaration|definition}0 of %1 is here", - (bool, DeclBaseName)) + "previous definition of %0 is here", (DeclBaseName)) NOTE(brace_stmt_suggest_do,none, "did you mean to use a 'do' statement?", ()) diff --git a/include/swift/AST/DiagnosticsDriver.def b/include/swift/AST/DiagnosticsDriver.def index 94ddc5b19f386..90b0ae64b80d8 100644 --- a/include/swift/AST/DiagnosticsDriver.def +++ b/include/swift/AST/DiagnosticsDriver.def @@ -37,6 +37,10 @@ DIAG(NOTE,ID,Options,Text,Signature) #endif +#ifndef REMARK +# define REMARK(ID,Options,Text,Signature) \ +DIAG(REMARK,ID,Options,Text,Signature) +#endif WARNING(warning_parallel_execution_not_supported,none, "parallel execution not supported; falling back to serial execution", @@ -61,7 +65,7 @@ ERROR(error_cannot_specify__o_for_multiple_outputs,none, "cannot specify -o when generating multiple output files", ()) ERROR(error_unable_to_load_output_file_map, none, - "unable to load output file map: %0", (StringRef)) + "unable to load output file map '%1': %0", (StringRef, StringRef)) ERROR(error_no_output_file_map_specified,none, "no output file map specified", ()) @@ -126,6 +130,9 @@ ERROR(error_input_changed_during_build,none, ERROR(error_conflicting_options, none, "conflicting options '%0' and '%1'", (StringRef, StringRef)) +ERROR(error_option_not_supported, none, + "'%0' is not supported with '%1'", + (StringRef, StringRef)) WARNING(warn_ignore_embed_bitcode, none, "ignoring -embed-bitcode since no object file is being generated", ()) @@ -144,6 +151,16 @@ WARNING(warn_opt_remark_disabled, none, WARNING(warn_ignoring_batch_mode,none, "ignoring '-enable-batch-mode' because '%0' was also specified", (StringRef)) +WARNING(warn_use_filelists_deprecated, none, + "the option '-driver-use-filelists' is deprecated; use " + "'-driver-filelist-threshold=0' instead", ()) + +WARNING(warn_emit_public_type_metadata_accessors_deprecated, none, + "the option '-emit-public-type-metadata-accessors' is no longer " + "needed and is deprecated; consider removing it", ()) + +REMARK(remark_using_batch_mode,none, "using batch mode", ()) + #ifndef DIAG_NO_UNDEF # if defined(DIAG) # undef DIAG diff --git a/include/swift/AST/DiagnosticsFrontend.def b/include/swift/AST/DiagnosticsFrontend.def index 873f0815ad7d3..3335dc16e728e 100644 --- a/include/swift/AST/DiagnosticsFrontend.def +++ b/include/swift/AST/DiagnosticsFrontend.def @@ -235,6 +235,8 @@ WARNING(cannot_assign_value_to_conditional_compilation_flag,none, ERROR(error_optimization_remark_pattern, none, "%0 in '%1'", (StringRef, StringRef)) +ERROR(error_compilation_stopped_by_errors_in_other_files,none, "compilation stopped by errors in other files", ()) + #ifndef DIAG_NO_UNDEF # if defined(DIAG) # undef DIAG diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index 7d70616957f0a..9285e8ad8bc54 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -37,7 +37,7 @@ #endif //============================================================================== -// Lexing and Parsing diagnostics +// MARK: Lexing and Parsing diagnostics //============================================================================== NOTE(opening_brace,none, @@ -99,7 +99,7 @@ WARNING(escaped_parameter_name,none, (StringRef)) //------------------------------------------------------------------------------ -// Lexer diagnostics +// MARK: Lexer diagnostics //------------------------------------------------------------------------------ WARNING(lex_nul_character,none, @@ -121,6 +121,8 @@ ERROR(lex_invalid_curly_quote,none, NOTE(lex_confusable_character,none, "unicode character '%0' looks similar to '%1'; did you mean to use '%1'?", (StringRef, StringRef)) +WARNING(lex_nonbreaking_space,none, + "non-breaking space (U+00A0) used instead of regular space", ()) ERROR(lex_unterminated_block_comment,none, "unterminated '/*' comment", ()) @@ -184,7 +186,7 @@ WARNING(lex_editor_placeholder_in_playground,none, ERROR(lex_conflict_marker_in_file,none, "source control conflict marker in source file", ()) //------------------------------------------------------------------------------ -// Declaration parsing diagnostics +// MARK: Declaration parsing diagnostics //------------------------------------------------------------------------------ NOTE(note_in_decl_extension,none, @@ -206,8 +208,7 @@ ERROR(number_cant_start_decl_name,none, ERROR(expected_identifier_after_case_comma,none, "expected identifier after comma in enum 'case' declaration", ()) ERROR(decl_redefinition,none, - "%select{declaration|definition}0 conflicts with previous value", - (bool)) + "definition conflicts with previous value", ()) ERROR(let_cannot_be_computed_property,none, "'let' declarations cannot be computed properties", ()) ERROR(let_cannot_be_observing_property,none, @@ -333,8 +334,6 @@ ERROR(associated_type_generic_parameter_list,PointsToFirstBadToken, // Func ERROR(func_decl_without_paren,PointsToFirstBadToken, "expected '(' in argument list of function declaration", ()) -ERROR(enum_element_decl_without_paren,PointsToFirstBadToken, - "expected '(' in argument list of enum element declaration", ()) ERROR(static_func_decl_global_scope,none, "%select{%error|static methods|class methods}0 may only be declared on a type", (StaticSpellingKind)) @@ -364,6 +363,8 @@ ERROR(expected_lbrace_class,PointsToFirstBadToken, "expected '{' in class", ()) ERROR(expected_rbrace_class,none, "expected '}' in class", ()) +ERROR(expected_colon_class, PointsToFirstBadToken, + "expected ':' to begin inheritance clause",()) // Protocol ERROR(generic_arguments_protocol,PointsToFirstBadToken, @@ -701,7 +702,7 @@ ERROR(sil_coverage_invalid_operator, none, "expected '+' or '-'", ()) //------------------------------------------------------------------------------ -// Type parsing diagnostics +// MARK: Type parsing diagnostics //------------------------------------------------------------------------------ ERROR(expected_type,PointsToFirstBadToken, @@ -799,7 +800,7 @@ ERROR(sil_box_expected_r_angle,none, "expected '>' to complete SIL box generic argument list", ()) //------------------------------------------------------------------------------ -// Layout constraint diagnostics +// MARK: Layout constraint diagnostics //------------------------------------------------------------------------------ ERROR(layout_size_should_be_positive,none, @@ -812,7 +813,7 @@ ERROR(layout_constraints_only_inside_specialize_attr,none, "layout constraints are only allowed inside '_specialize' attributes", ()) //------------------------------------------------------------------------------ -// Pattern parsing diagnostics +// MARK: Pattern parsing diagnostics //------------------------------------------------------------------------------ ERROR(expected_pattern,PointsToFirstBadToken, @@ -1078,7 +1079,7 @@ ERROR(try_on_var_let,none, "'try' must be placed on the initial value expression", ()) //------------------------------------------------------------------------------ -// Expression parsing diagnostics +// MARK: Expression parsing diagnostics //------------------------------------------------------------------------------ ERROR(expected_expr,none, @@ -1256,7 +1257,7 @@ ERROR(expr_dynamictype_deprecated,PointsToFirstBadToken, "'.dynamicType' is deprecated. Use 'type(of: ...)' instead", ()) //------------------------------------------------------------------------------ -// Attribute-parsing diagnostics +// MARK: Attribute-parsing diagnostics //------------------------------------------------------------------------------ ERROR(replace_equal_with_colon_for_value,none, @@ -1265,6 +1266,8 @@ ERROR(expected_attribute_name,none, "expected an attribute name", ()) ERROR(unknown_attribute,none, "unknown attribute '%0'", (StringRef)) +ERROR(unexpected_lparen_in_attribute,none, + "unexpected '(' in attribute '%0'", (StringRef)) ERROR(duplicate_attribute,none, "duplicate %select{attribute|modifier}0", (bool)) NOTE(previous_attribute,none, @@ -1318,6 +1321,8 @@ ERROR(attr_renamed, none, "'@%0' has been renamed to '@%1'", (StringRef, StringRef)) WARNING(attr_renamed_warning, none, "'@%0' has been renamed to '@%1'", (StringRef, StringRef)) +ERROR(attr_name_close_match, none, + "no attribute named '@%0'; did you mean '@%1'?", (StringRef, StringRef)) // availability ERROR(attr_availability_platform,none, @@ -1451,7 +1456,7 @@ ERROR(attr_implements_expected_member_name,PointsToFirstBadToken, "expected a member name as second parameter in '_implements' attribute", ()) //------------------------------------------------------------------------------ -// Generics parsing diagnostics +// MARK: Generics parsing diagnostics //------------------------------------------------------------------------------ ERROR(expected_rangle_generics_param,PointsToFirstBadToken, "expected '>' to complete generic parameter list", ()) @@ -1485,7 +1490,7 @@ ERROR(where_inside_brackets,none, "must be written following the declaration's type", ()) //------------------------------------------------------------------------------ -// Conditional compilation parsing diagnostics +// MARK: Conditional compilation parsing diagnostics //------------------------------------------------------------------------------ ERROR(unsupported_conditional_compilation_binary_expression,none, "expected '&&' or '||' expression", ()) @@ -1540,7 +1545,7 @@ WARNING(likely_simulator_platform_condition,none, ()) //------------------------------------------------------------------------------ -// Availability query parsing diagnostics +// MARK: Availability query parsing diagnostics //------------------------------------------------------------------------------ ERROR(avail_query_expected_condition,PointsToFirstBadToken, "expected availability condition", ()) @@ -1581,10 +1586,10 @@ ERROR(availability_query_repeated_platform, none, "version for '%0' already specified", (StringRef)) //------------------------------------------------------------------------------ -// syntax parsing diagnostics +// MARK: syntax parsing diagnostics //------------------------------------------------------------------------------ -WARNING(unknown_syntax_entity, PointsToFirstBadToken, - "unknown %0 syntax exists in the source", (StringRef)) +ERROR(unknown_syntax_entity, PointsToFirstBadToken, + "unknown %0 syntax exists in the source", (StringRef)) #ifndef DIAG_NO_UNDEF # if defined(DIAG) diff --git a/include/swift/AST/DiagnosticsSIL.def b/include/swift/AST/DiagnosticsSIL.def index be0eaded360ce..4a5ddb7931aa4 100644 --- a/include/swift/AST/DiagnosticsSIL.def +++ b/include/swift/AST/DiagnosticsSIL.def @@ -300,9 +300,26 @@ ERROR(shifting_all_significant_bits,none, ERROR(static_report_error, none, "static report error", ()) +ERROR(non_physical_addressof,none, + "addressof only works with purely physical lvalues; " + "use `withUnsafePointer` or `withUnsafeBytes` unless you're implementing " + "`withUnsafePointer` or `withUnsafeBytes`", ()) +ERROR(non_borrowed_indirect_addressof,none, + "addressof only works with borrowable in-memory rvalues; " + "use `withUnsafePointer` or `withUnsafeBytes` unless you're implementing " + "`withUnsafePointer` or `withUnsafeBytes`", ()) + REMARK(opt_remark_passed, none, "%0", (StringRef)) REMARK(opt_remark_missed, none, "%0", (StringRef)) +// Float-point to integer conversions +ERROR(float_to_int_overflow, none, + "invalid%select{| implicit}2 conversion: '%0' overflows %1", (StringRef, Type, bool)) + +ERROR(negative_fp_literal_overflow_unsigned, none, + "negative literal '%0' cannot be converted to %select{|unsigned }2%1", + (StringRef, Type, bool)) + #ifndef DIAG_NO_UNDEF # if defined(DIAG) # undef DIAG diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index c32fea7fe4171..e081ec05dd5ea 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -37,16 +37,23 @@ DIAG(NOTE,ID,Options,Text,Signature) #endif - -NOTE(type_declared_here,none, - "type declared here", ()) +NOTE(kind_declname_declared_here,none, + "%0 %1 declared here", (DescriptiveDeclKind, DeclName)) +NOTE(kind_identifier_declared_here,none, + "%0 %1 declared here", (DescriptiveDeclKind, Identifier)) NOTE(decl_declared_here,none, "%0 declared here", (DeclName)) +NOTE(identifier_declared_here,none, + "%0 declared here", (Identifier)) +NOTE(kind_declared_here,none, + "%0 declared here", (DescriptiveDeclKind)) +NOTE(implicit_member_declared_here,none, + "%1 '%0' is implicitly declared", (StringRef, StringRef)) NOTE(extended_type_declared_here,none, "extended type declared here", ()) //------------------------------------------------------------------------------ -// Constraint solver diagnostics +// MARK: Constraint solver diagnostics //------------------------------------------------------------------------------ ERROR(ambiguous_member_overload_set,none, @@ -64,8 +71,19 @@ ERROR(could_not_find_tuple_member,none, ERROR(could_not_find_value_member,none, "value of type %0 has no member %1", (Type, DeclName)) +ERROR(could_not_find_value_member_corrected,none, + "value of type %0 has no member %1; did you mean %2?", + (Type, DeclName, DeclName)) ERROR(could_not_find_type_member,none, "type %0 has no member %1", (Type, DeclName)) +ERROR(could_not_find_type_member_corrected,none, + "type %0 has no member %1; did you mean %2?", + (Type, DeclName, DeclName)) + +ERROR(could_not_find_subscript_member_did_you_mean,none, + "value of type %0 has no property or method named 'subscript'; " + "did you mean to use the subscript operator?", + (Type)) ERROR(could_not_find_enum_case,none, "enum type %0 has no case %1; did you mean %2", (Type, DeclName, DeclName)) @@ -302,7 +320,7 @@ ERROR(cannot_convert_initializer_value,none, ERROR(cannot_convert_initializer_value_protocol,none, "value of type %0 does not conform to specified type %1", (Type,Type)) ERROR(cannot_convert_initializer_value_nil,none, - "nil cannot initialize specified type %0", (Type)) + "'nil' cannot initialize specified type %0", (Type)) ERROR(cannot_convert_to_return_type,none, "cannot convert return expression of type %0 to return type %1", @@ -310,7 +328,7 @@ ERROR(cannot_convert_to_return_type,none, ERROR(cannot_convert_to_return_type_protocol,none, "return expression of type %0 does not conform to %1", (Type,Type)) ERROR(cannot_convert_to_return_type_nil,none, - "nil is incompatible with return type %0", (Type)) + "'nil' is incompatible with return type %0", (Type)) ERROR(cannot_convert_thrown_type,none, "thrown expression type %0 does not conform to 'Error'", (Type)) @@ -325,7 +343,7 @@ ERROR(cannot_throw_nil,none, ERROR(cannot_convert_raw_initializer_value,none, "cannot convert value of type %0 to raw type %1", (Type,Type)) ERROR(cannot_convert_raw_initializer_value_nil,none, - "cannot convert nil to raw type %0", (Type)) + "cannot convert 'nil' to raw type %0", (Type)) ERROR(cannot_convert_default_arg_value,none, "default argument value of type %0 cannot be converted to type %1", @@ -348,7 +366,7 @@ ERROR(cannot_convert_partial_argument_value_protocol,none, "in argument type %0, %1 does not conform to expected type %2", (Type, Type, Type)) ERROR(cannot_convert_argument_value_nil,none, - "nil is not compatible with expected argument type %0", (Type)) + "'nil' is not compatible with expected argument type %0", (Type)) ERROR(cannot_convert_closure_result,none, "cannot convert value of type %0 to closure result type %1", @@ -357,7 +375,7 @@ ERROR(cannot_convert_closure_result_protocol,none, "result value of type %0 does not conform to closure result type %1", (Type, Type)) ERROR(cannot_convert_closure_result_nil,none, - "nil is not compatible with closure result type %0", (Type)) + "'nil' is not compatible with closure result type %0", (Type)) ERROR(destructor_not_accessible,none, "deinitializers cannot be accessed", ()) @@ -370,7 +388,7 @@ ERROR(cannot_convert_array_element_protocol,none, "value of type %0 does not conform to expected element type %1", (Type, Type)) ERROR(cannot_convert_array_element_nil,none, - "nil is not compatible with expected element type %0", (Type)) + "'nil' is not compatible with expected element type %0", (Type)) // Dictionary Key ERROR(cannot_convert_dict_key,none, @@ -380,7 +398,7 @@ ERROR(cannot_convert_dict_key_protocol,none, "value of type %0 does not conform to expected dictionary key type %1", (Type, Type)) ERROR(cannot_convert_dict_key_nil,none, - "nil is not compatible with expected dictionary key type %0", (Type)) + "'nil' is not compatible with expected dictionary key type %0", (Type)) // Dictionary Value ERROR(cannot_convert_dict_value,none, @@ -390,7 +408,7 @@ ERROR(cannot_convert_dict_value_protocol,none, "value of type %0 does not conform to expected dictionary value type %1", (Type, Type)) ERROR(cannot_convert_dict_value_nil,none, - "nil is not compatible with expected dictionary value type %0", (Type)) + "'nil' is not compatible with expected dictionary value type %0", (Type)) // Coerce Expr ERROR(cannot_convert_coerce,none, @@ -400,7 +418,7 @@ ERROR(cannot_convert_coerce_protocol,none, "value of type %0 does not conform to %1 in coercion", (Type, Type)) ERROR(cannot_convert_coerce_nil,none, - "nil is not compatible with type %0 in coercion", (Type)) + "'nil' is not compatible with type %0 in coercion", (Type)) // Assign Expr ERROR(cannot_convert_assign,none, @@ -410,7 +428,7 @@ ERROR(cannot_convert_assign_protocol,none, "value of type %0 does not conform to %1 in assignment", (Type, Type)) ERROR(cannot_convert_assign_nil,none, - "nil cannot be assigned to type %0", (Type)) + "'nil' cannot be assigned to type %0", (Type)) ERROR(throws_functiontype_mismatch,none, @@ -539,7 +557,7 @@ ERROR(cannot_return_value_from_void_func,none, "unexpected non-void return value in void function", ()) //------------------------------------------------------------------------------ -// Name Binding +// MARK: Name Binding //------------------------------------------------------------------------------ ERROR(sema_no_import,Fatal, @@ -577,6 +595,9 @@ ERROR(serialization_missing_single_dependency,Fatal, "missing required module '%0'", (StringRef)) ERROR(serialization_missing_dependencies,Fatal, "missing required modules: %0", (StringRef)) +ERROR(serialization_circular_dependency,Fatal, + "circular dependency between modules '%0' and %1", + (StringRef, Identifier)) ERROR(serialization_missing_shadowed_module,Fatal, "cannot load underlying module for %0", (Identifier)) ERROR(serialization_name_mismatch,Fatal, @@ -654,6 +675,9 @@ ERROR(unspaced_unary_operator,none, ERROR(use_unresolved_identifier,none, "use of unresolved %select{identifier|operator}1 %0", (DeclName, bool)) +ERROR(use_unresolved_identifier_corrected,none, + "use of unresolved %select{identifier|operator}1 %0; did you mean %2?", + (DeclName, bool, DeclName)) NOTE(confusable_character,none, "%select{identifier|operator}0 '%1' contains possibly confused characters; " "did you mean to use '%2'?", @@ -666,10 +690,6 @@ NOTE(note_typo_candidate_implicit_member,none, "did you mean the implicitly-synthesized %1 '%0'?", (StringRef, StringRef)) NOTE(note_remapped_type,none, "did you mean to use '%0'?", (StringRef)) -ERROR(identifier_init_failure,none, - "could not infer type for %0", (Identifier)) -ERROR(pattern_used_in_type,none, - "%0 used within its own type", (Identifier)) NOTE(note_module_as_type,none, "cannot use module %0 as a type", (Identifier)) @@ -749,22 +769,20 @@ ERROR(precedence_group_lower_within_module,none, "precedence group cannot be given lower precedence than group in same" " module; make the other precedence group higher than this one instead", ()) -NOTE(precedence_group_declared_here,none,"precedence group declared here", - ()) ERROR(precedence_group_redeclared,none, "precedence group redeclared", ()) NOTE(previous_precedence_group_decl,none, "previous precedence group declaration here", ()) //------------------------------------------------------------------------------ -// Type Check Coercions +// MARK: Type Check Coercions //------------------------------------------------------------------------------ ERROR(tuple_conversion_not_expressible,none, "cannot express tuple conversion %0 to %1", (Type, Type)) //------------------------------------------------------------------------------ -// Expression Type Checking Errors +// MARK: Expression Type Checking Errors //------------------------------------------------------------------------------ ERROR(types_not_convertible,none, "%1 is not %select{convertible to|a subtype of}0 %2", @@ -840,7 +858,7 @@ WARNING(conditional_downcast_same_type,none, (Type, Type, unsigned)) WARNING(is_expr_same_type,none, "checking a value with optional type %0 against dynamic type %1 " - "succeeds whenever the value is non-'nil'; did you mean to use " + "succeeds whenever the value is non-nil; did you mean to use " "'!= nil'?", (Type, Type)) WARNING(downcast_to_unrelated,none, "cast from %0 to unrelated type %1 always fails", (Type, Type)) @@ -999,7 +1017,7 @@ ERROR(c_function_pointer_from_function_with_context,none, (bool, unsigned)) //------------------------------------------------------------------------------ -// Type Check Declarations +// MARK: Type Check Declarations //------------------------------------------------------------------------------ ERROR(var_type_not_materializable,none, @@ -1389,7 +1407,7 @@ NOTE(objc_generic_extension_using_type_parameter_try_objc,none, ERROR(type_does_not_conform,none, "type %0 does not conform to protocol %1", (Type, Type)) ERROR(cannot_use_nil_with_this_type,none, - "nil cannot be used in context expecting type %0", (Type)) + "'nil' cannot be used in context expecting type %0", (Type)) ERROR(use_of_equal_instead_of_equality,none, "use of '=' in a boolean context, did you mean '=='?", ()) @@ -1447,6 +1465,28 @@ ERROR(objc_generics_cannot_conditionally_conform,none, "type %0 cannot conditionally conform to protocol %1 because " "the type uses the Objective-C generics model", (Type, Type)) +ERROR(objc_protocol_cannot_have_conditional_conformance,none, + "type %0 cannot conditionally conform to @objc protocol %1 because " + "Objective-C does not support conditional conformances", + (Type, Type)) +ERROR(objc_protocol_in_generic_extension,none, + "conformance of " + "%select{|subclass of a }2%select{class from generic context|generic class}3" + " %0 to @objc protocol %1 cannot be in an extension", + (Type, Type, bool, bool)) +ERROR(conditional_conformances_cannot_imply_conformances,none, + "conditional conformance of type %0 to protocol %1 does not imply conformance to " + "inherited protocol %2", + (Type, Type, Type)) +NOTE(note_explicitly_state_conditional_conformance_different,none, + "did you mean to explicitly state the conformance with different bounds?", ()) +NOTE(note_explicitly_state_conditional_conformance_relaxed,none, + "did you mean to explicitly state the conformance with relaxed bounds?", ()) +NOTE(note_explicitly_state_conditional_conformance_same,none, + "did you mean to explicitly state the conformance with the same bounds?", ()) +NOTE(note_explicitly_state_conditional_conformance_noneditor,none, + "did you mean to explicitly state the conformance like '%0where ...'?", + (StringRef)) ERROR(protocol_has_missing_requirements,none, "type %0 cannot conform to protocol %1 because it has requirements that " "cannot be satisfied", (Type, Type)) @@ -1466,7 +1506,7 @@ ERROR(witness_argument_name_mismatch,none, "%select{method|initializer}0 %1 has different argument labels from those " "required by protocol %2 (%3)", (bool, DeclName, Type, DeclName)) ERROR(witness_initializer_not_required,none, - "initializer requirement %0 can only be satisfied by a `required` " + "initializer requirement %0 can only be satisfied by a 'required' " "initializer in%select{| the definition of}1 non-final class %2", (DeclName, bool, Type)) ERROR(witness_initializer_failability,none, @@ -1488,12 +1528,12 @@ NOTE(witness_self_weaken_same_type,none, "consider weakening the same-type requirement %0 == %1 to a superclass " "requirement", (Type, Type)) ERROR(witness_requires_dynamic_self,none, - "method %0 in non-final class %1 must return `Self` to conform to " + "method %0 in non-final class %1 must return 'Self' to conform to " "protocol %2", (DeclName, Type, Type)) ERROR(witness_requires_class_implementation,none, "method %0 in non-final class %1 cannot be implemented in a " - "protocol extension because it returns `Self` and has associated type " + "protocol extension because it returns 'Self' and has associated type " "requirements", (DeclName, Type)) ERROR(witness_not_accessible_proto,none, @@ -1521,6 +1561,9 @@ ERROR(type_witness_objc_generic_parameter,none, "type %0 involving Objective-C type parameter%select{| %1}2 cannot be " "used for associated type %3 of protocol %4", (Type, Type, bool, DeclName, DeclName)) +NOTE(witness_fix_access,none, + "mark the %0 as '%select{%error|fileprivate|internal|public|%error}1' to " + "satisfy the requirement", (DescriptiveDeclKind, AccessLevel)) ERROR(protocol_refine_access,none, "%select{protocol must be declared %select{" @@ -1647,8 +1690,6 @@ NOTE(protocol_witness_nonconform_type,none, "possibly intended match %0 does not " "%select{inherit from|conform to}2 %1", (Type, Type, bool)) -NOTE(protocol_requirement_here,none, - "requirement %0 declared here", (DeclName)) NOTE(protocol_conformance_here,none, "%select{|class }0%1 declares conformance to protocol %2 here", (bool, DeclName, DeclName)) @@ -1668,10 +1709,19 @@ ERROR(witness_unavailable,none, ERROR(redundant_conformance,none, "redundant conformance of %0 to protocol %1", (Type, DeclName)) +ERROR(redundant_conformance_conditional,none, + "conflicting conformance of %0 to protocol %1; there cannot be more " + "than one conformance, even with different conditional bounds", + (Type, DeclName)) WARNING(redundant_conformance_adhoc,none, "conformance of %0 to protocol %1 was already stated in " "%select{the protocol's|the type's}2 module %3", (Type, DeclName, bool, Identifier)) +WARNING(redundant_conformance_adhoc_conditional,none, + "conformance of %0 to protocol %1 conflicts with that stated in " + "%select{the protocol's|the type's}2 module %3 and will be ignored; " + "there cannot be more than one conformance, even with different conditional bounds", + (Type, DeclName, bool, Identifier)) NOTE(redundant_conformance_witness_ignored,none, "%0 %1 will not be used to satisfy the conformance to %2", (DescriptiveDeclKind, DeclName, DeclName)) @@ -1702,8 +1752,6 @@ ERROR(typealias_outside_of_protocol,none, ERROR(circular_protocol_def,none, "circular protocol inheritance %0", (StringRef)) -NOTE(protocol_here,none, - "protocol %0 declared here", (Identifier)) ERROR(objc_protocol_inherits_non_objc_protocol,none, "@objc protocol %0 cannot refine non-@objc protocol %1", (Type, Type)) WARNING(protocol_composition_with_postfix,none, @@ -1931,9 +1979,8 @@ ERROR(override_argument_name_mismatch,none, "of overridden %select{method|initializer}0 %2", (bool, DeclName, DeclName)) ERROR(override_ownership_mismatch,none, - "cannot override %select{strong|weak|unowned|unowned(unsafe)}0 property " - "with %select{strong|weak|unowned|unowned(unsafe)}1 property", - (/*ReferenceOwnership*/unsigned, /*ReferenceOwnership*/unsigned)) + "cannot override %0 property with %1 property", + (ReferenceOwnership, ReferenceOwnership)) ERROR(override_dynamic_self_mismatch,none, "cannot override a Self return type with a non-Self return type", ()) @@ -1979,10 +2026,18 @@ ERROR(override_mutable_covariant_property,none, ERROR(override_mutable_covariant_subscript,none, "cannot override mutable subscript of type %0 with covariant type %1", (Type, Type)) -ERROR(decl_already_final,none, +ERROR(static_decl_already_final,none, "static declarations are already final", ()) ERROR(open_decl_cannot_be_final,none, "%0 cannot be declared both 'final' and 'open'", (DescriptiveDeclKind)) +ERROR(implicitly_final_cannot_be_open,none, + "%select{'let' properties|members of 'final' classes|" + "static declarations}0 are implicitly 'final'; use 'public' instead of " + "'open'", (unsigned)) +WARNING(implicitly_final_cannot_be_open_swift4,none, + "%select{'let' properties|members of 'final' classes|" + "static declarations}0 are implicitly 'final'; use 'public' instead of " + "'open'", (unsigned)) WARNING(override_swift3_objc_inference,none, "override of %0 %1 from extension of %2 depends on deprecated " @@ -2014,8 +2069,6 @@ ERROR(superclass_of_open_not_open,none, "superclass %0 of open class must be open", (Type)) ERROR(circular_class_inheritance,none, "circular class inheritance %0", (StringRef)) -NOTE(class_here,none, - "class %0 declared here", (Identifier)) ERROR(inheritance_from_final_class,none, "inheritance from a final class %0", (Identifier)) ERROR(inheritance_from_unspecialized_objc_generic_class,none, @@ -2083,8 +2136,6 @@ WARNING(enum_raw_type_access_warn,none, "%select{a private|a fileprivate|an internal|%error|%error}2 type", (bool, AccessLevel, AccessLevel, bool)) -NOTE(enum_here,none, - "enum %0 declared here", (Identifier)) ERROR(empty_enum_raw_type,none, "an enum with no cases cannot declare a raw type", ()) ERROR(enum_raw_value_without_raw_type,none, @@ -2114,8 +2165,17 @@ NOTE(construct_raw_representable_from_unwrapped_value,none, "construct %0 from unwrapped %1 value", (Type, Type)) // Derived conformances -ERROR(cannot_synthesize_in_extension,none, - "implementation of %0 cannot be automatically synthesized in an extension", (Type)) +ERROR(swift3_cannot_synthesize_in_extension,none, + "implementation of %0 cannot be automatically synthesized in an extension " + "in Swift 3", (Type)) +ERROR(cannot_synthesize_init_in_extension_of_nonfinal,none, + "implementation of %0 for non-final class cannot be automatically " + "synthesized in extension because initializer requirement %1 can only be " + "be satisfied by a 'required' initializer in the class definition", + (Type, DeclName)) +ERROR(cannot_synthesize_in_crossfile_extension,none, + "implementation of %0 cannot be automatically synthesized in an extension " + "in a different file to the type", (Type)) ERROR(broken_case_iterable_requirement,none, "CaseIterable protocol is broken: unexpected requirement", ()) @@ -2125,6 +2185,8 @@ ERROR(broken_equatable_requirement,none, "Equatable protocol is broken: unexpected requirement", ()) ERROR(broken_hashable_requirement,none, "Hashable protocol is broken: unexpected requirement", ()) +ERROR(broken_hashable_no_hasher,none, + "Hashable protocol is broken: Hasher type not found", ()) ERROR(broken_errortype_requirement,none, "Error protocol is broken: unexpected requirement", ()) ERROR(broken_int_hashable_conformance,none, @@ -2178,8 +2240,6 @@ ERROR(property_behavior_not_protocol,none, "property behavior name must refer to a protocol", ()) ERROR(property_behavior_protocol_reqt_ambiguous,none, "property behavior protocol has ambiguous %0 member", (Identifier)) -NOTE(property_behavior_protocol_reqt_here,none, - "%0 declared here", (Identifier)) ERROR(property_behavior_protocol_no_value,none, "property behavior protocol does not have a 'value' property", ()) ERROR(property_behavior_protocol_no_initStorage,none, @@ -2233,7 +2293,7 @@ ERROR(property_behavior_conformance_broken,none, "property behavior for %0 in type %1 is broken", (DeclName, Type)) //------------------------------------------------------------------------------ -// Type Check Attributes +// MARK: Type Check Attributes //------------------------------------------------------------------------------ ERROR(attr_only_one_decl_kind,none, @@ -2356,6 +2416,8 @@ ERROR(lazy_must_be_property,none, "lazy is only valid for members of a struct or class", ()) ERROR(lazy_not_observable,none, "lazy properties must not have observers", ()) +ERROR(lazy_not_strong,none, + "lazy properties cannot be %0", (ReferenceOwnership)) // Debugger function attribute. ERROR(attr_for_debugger_support_only,none, @@ -2373,7 +2435,7 @@ ERROR(implements_attr_protocol_not_conformed_to,none, (DeclName, DeclName)) //------------------------------------------------------------------------------ -// Type Check Expressions +// MARK: Type Check Expressions //------------------------------------------------------------------------------ NOTE(found_candidate,none, @@ -2385,6 +2447,9 @@ ERROR(no_MaxBuiltinIntegerType_found,none, "standard library error: _MaxBuiltinIntegerType is not properly defined", ()) ERROR(no_MaxBuiltinFloatType_found,none, "standard library error: _MaxBuiltinFloatType is not properly defined", ()) +ERROR(integer_literal_overflows_maxwidth, none, + "integer literal needs %1 bits, exceeding limit of %0 bits", + (unsigned, unsigned)) ERROR(no_member_of_module,none, "module %0 has no member named %1", (Identifier, DeclName)) @@ -2499,6 +2564,9 @@ NOTE(silence_inject_forced_downcast,none, ERROR(conditional_downcast_foreign,none, "conditional downcast to CoreFoundation type %0 will always succeed", (Type)) +NOTE(note_explicitly_compare_cftypeid,none, + "did you mean to explicitly compare the CFTypeIDs of %0 and %1?", + (DeclBaseName, Type)) ERROR(optional_used_as_boolean,none, "optional type %0 cannot be used as a boolean; " @@ -2546,7 +2614,7 @@ ERROR(missing_protocol,none, ERROR(nil_literal_broken_proto,none, "protocol 'ExpressibleByNilLiteral' is broken", ()) ERROR(array_protocol_broken,none, - "ExpressibleByArrayLiteral protocol definition is broken", ()) + "protocol 'ExpressibleByArrayLiteral' is broken", ()) ERROR(builtin_integer_literal_broken_proto,none, "protocol '_ExpressibleByBuiltinIntegerLiteral' is broken", ()) ERROR(integer_literal_broken_proto,none, @@ -2608,8 +2676,6 @@ ERROR(type_parameter_count_mismatch,none, (Identifier, unsigned, unsigned, bool)) ERROR(generic_type_requires_arguments,none, "reference to generic type %0 requires arguments in <...>", (Type)) -NOTE(generic_type_declared_here,none, - "generic type %0 declared here", (Identifier)) NOTE(descriptive_generic_type_declared_here,none, "%0 declared here", (StringRef)) @@ -2710,6 +2776,15 @@ NOTE(fix_unqualified_access_top_level_multi,none, "use '%0' to reference the %1 in module %2", (StringRef, DescriptiveDeclKind, Identifier)) +WARNING(warn_deprecated_conditional_conformance_outer_access,none, + "use of %0 as reference to %1 in %2 %3 will change in future versions of Swift to reference %4 in %5 %6 " + "which comes via a conditional conformance", + (DeclName, DescriptiveDeclKind, DescriptiveDeclKind, DeclName, + DescriptiveDeclKind, DescriptiveDeclKind, DeclName)) +NOTE(fix_deprecated_conditional_conformance_outer_access,none, + "use '%0' to continue to reference the %1", + (StringRef, DescriptiveDeclKind, Identifier)) + ERROR(unsupported_special_decl_ref, none, "referencing %0 as a function value is not implemented", (Identifier)) @@ -2765,7 +2840,7 @@ WARNING(use_of_qq_on_non_optional_value,none, "left side of nil coalescing operator '?""?' has non-optional type %0, " "so the right side is never used", (Type)) WARNING(nonoptional_compare_to_nil,none, - "comparing non-optional value of type %0 to nil always returns" + "comparing non-optional value of type %0 to 'nil' always returns" " %select{false|true}1", (Type, bool)) WARNING(optional_check_nonoptional,none, "non-optional expression of type %0 used in a check for optionals", @@ -2824,7 +2899,7 @@ ERROR(capture_across_type_decl,none, (DescriptiveDeclKind, Identifier)) //------------------------------------------------------------------------------ -// Type Check Statements +// MARK: Type Check Statements //------------------------------------------------------------------------------ ERROR(jump_out_of_defer,none, @@ -2864,7 +2939,7 @@ WARNING(expression_unused_result_call,none, WARNING(expression_unused_result_operator,none, "result of operator %0 is unused", (DeclName)) WARNING(expression_unused_result_unknown, none, - "result of call is unused, but produces %0", (Type)) + "result of call to %select{function|closure}0 returning %1 is unused", (bool, Type)) WARNING(expression_unused_result, none, "expression of type %0 is unused", (Type)) WARNING(expression_unused_init_result,none, @@ -2878,6 +2953,8 @@ WARNING(expression_unused_literal,none, ERROR(assignment_lhs_not_lvalue,none, "cannot assign to immutable expression of type %0", (Type)) +ERROR(assignment_lhs_is_apply_expression,none, + "expression is not assignable: %0", (StringRef)) ERROR(assignment_lhs_is_immutable_variable,none, "cannot assign to value: %0", (StringRef)) ERROR(assignment_lhs_is_immutable_property,none, @@ -2938,6 +3015,15 @@ ERROR(type_mismatch_multiple_pattern_list,none, ERROR(type_mismatch_fallthrough_pattern_list,none, "pattern variable bound to type %0, fallthrough case bound to type %1", (Type, Type)) +ERROR(unknown_case_must_be_catchall,none, + "'@unknown' is only supported for catch-all cases (\"case _\")", ()) +ERROR(unknown_case_where_clause,none, + "'where' cannot be used with '@unknown'", ()) +ERROR(unknown_case_multiple_patterns,none, + "'@unknown' cannot be applied to multiple patterns", ()) +ERROR(unknown_case_must_be_last,none, + "'@unknown' can only be applied to the last case in a switch", ()) + WARNING(where_on_one_item, none, "'where' only applies to the second pattern match in this case", ()) @@ -2952,7 +3038,7 @@ ERROR(trailing_closure_requires_parens,none, " context", ()) //------------------------------------------------------------------------------ -// Type Check Patterns +// MARK: Type Check Patterns //------------------------------------------------------------------------------ ERROR(cannot_infer_type_for_pattern,none, @@ -3021,6 +3107,15 @@ WARNING(type_inferred_to_undesirable_type,none, "which may be unexpected", (Identifier, Type, bool)) NOTE(add_explicit_type_annotation_to_silence,none, "add an explicit type annotation to silence this warning", ()) + +WARNING(unowned_assignment_immediate_deallocation,none, + "instance will be immediately deallocated because " + "%select{variable|property}2 %0 is %1", + (Identifier, ReferenceOwnership, /*Is Property*/unsigned)) +NOTE(unowned_assignment_requires_strong,none, + "a strong reference is required to prevent the instance from being " + "deallocated", ()) + ERROR(isa_collection_downcast_pattern_value_unimplemented,none, "collection downcast in cast pattern is not implemented; use an explicit " "downcast to %0 instead", (Type)) @@ -3030,7 +3125,7 @@ WARNING(swift3_ignore_specialized_enum_element_call,none, "which will be rejected in future version of Swift", ()) //------------------------------------------------------------------------------ -// Error-handling diagnostics +// MARK: Error-handling diagnostics //------------------------------------------------------------------------------ @@ -3106,7 +3201,7 @@ WARNING(no_throw_in_do_with_catch,none, "'catch' block is unreachable because no errors are thrown in 'do' block", ()) //------------------------------------------------------------------------------ -// Type Check Types +// MARK: Type Check Types //------------------------------------------------------------------------------ ERROR(unsupported_recursive_struct,none, @@ -3144,18 +3239,21 @@ ERROR(bool_intrinsics_not_found,none, ERROR(class_super_access,none, "class %select{must be declared %select{" "%select{private|fileprivate|internal|%error|%error}2|private or fileprivate}3" - "|cannot be declared " - "%select{in this context|fileprivate|internal|public|open}1}0 " - "because its superclass is " - "%select{private|fileprivate|internal|%error|%error}2", - (bool, AccessLevel, AccessLevel, bool)) + "|cannot be declared %select{in this context|fileprivate|internal|public|open}1}0 " + "because its superclass " + "%select{is %select{private|fileprivate|internal|%error|%error}2" + "|uses %select{a private|a fileprivate|an internal|%error|%error}2 " + "type as a generic parameter}4", + (bool, AccessLevel, AccessLevel, bool, bool)) WARNING(class_super_access_warn,none, - "class %select{should be declared " - "%select{private|fileprivate|internal|%error|%error}2" - "|should not be declared %select{in this context|fileprivate|internal|public|open}1}0 " - "because its superclass is " - "%select{private|fileprivate|internal|%error|%error}2", - (bool, AccessLevel, AccessLevel, bool)) + "class %select{should be declared " + "%select{private|fileprivate|internal|%error|%error}2" + "|should not be declared %select{in this context|fileprivate|internal|public|open}1}0 " + "because its superclass " + "%select{is %select{private|fileprivate|internal|%error|%error}2" + "|uses %select{a private|a fileprivate|an internal|%error|%error}2 " + "type as a generic parameter}4", + (bool, AccessLevel, AccessLevel, bool, bool)) ERROR(dot_protocol_on_non_existential,none, "cannot use 'Protocol' with non-protocol type %0", (Type)) ERROR(tuple_single_element,none, @@ -3174,24 +3272,25 @@ ERROR(implicitly_unwrapped_optional_in_illegal_position,none, // Ownership ERROR(invalid_ownership_type,none, - "'%select{strong|weak|unowned|unowned}0' may only be applied to " - "class and class-bound protocol types, not %1", - (/*ReferenceOwnership*/unsigned, Type)) + "%0 may only be applied to class and class-bound protocol types, not %1", + (ReferenceOwnership, Type)) ERROR(invalid_ownership_protocol_type,none, - "'%select{strong|weak|unowned|unowned}0' must not be applied to " - "non-class-bound %1; consider adding a protocol conformance that has a class bound", - (/*ReferenceOwnership*/unsigned, Type)) + "%0 must not be applied to non-class-bound %1; " + "consider adding a protocol conformance that has a class bound", + (ReferenceOwnership, Type)) +ERROR(invalid_ownership_with_optional,none, + "%0 variable cannot have optional type", (ReferenceOwnership)) ERROR(invalid_weak_ownership_not_optional,none, "'weak' variable should have optional type %0", (Type)) ERROR(invalid_weak_let,none, "'weak' must be a mutable variable, because it may change at runtime", ()) ERROR(ownership_invalid_in_protocols,none, - "'%select{strong|weak|unowned|unowned}0' cannot be applied to a property declaration in a protocol", - (/*ReferenceOwnership*/unsigned)) + "%0 cannot be applied to a property declaration in a protocol", + (ReferenceOwnership)) WARNING(ownership_invalid_in_protocols_compat_warning,none, - "'%select{strong|weak|unowned|unowned}0' should not be applied to a property declaration " + "%0 should not be applied to a property declaration " "in a protocol and will be disallowed in future versions", - (/*ReferenceOwnership*/unsigned)) + (ReferenceOwnership)) // required ERROR(required_initializer_nonclass,none, @@ -3260,9 +3359,12 @@ ERROR(sil_metatype_multiple_reprs,none, ()) //------------------------------------------------------------------------------ -// @objc and @nonobjc +// MARK: @objc and @nonobjc //------------------------------------------------------------------------------ +ERROR(objc_interop_disabled,none, + "Objective-C interoperability is disabled", ()) + ERROR(attr_used_without_required_module, none, "%0 attribute used without importing module %1", (DeclAttribute, Identifier)) @@ -3295,10 +3397,10 @@ WARNING(objc_inference_swift3_objc_derived,none, "deprecated", ()) NOTE(objc_inference_swift3_addobjc,none, - "add `@objc` to continue exposing an Objective-C entry point (Swift 3 " + "add '@objc' to continue exposing an Objective-C entry point (Swift 3 " "behavior)", ()) NOTE(objc_inference_swift3_addnonobjc,none, - "add `@nonobjc` to suppress the Objective-C entry point (Swift 4 " + "add '@nonobjc' to suppress the Objective-C entry point (Swift 4 " "behavior)", ()) ERROR(objc_for_generic_class,none, @@ -3559,7 +3661,7 @@ ERROR(nonobjc_not_allowed,none, #undef OBJC_ATTR_SELECT //------------------------------------------------------------------------------ -// dynamic +// MARK: dynamic //------------------------------------------------------------------------------ ERROR(dynamic_not_in_class,none, @@ -3575,42 +3677,49 @@ ERROR(dynamic_requires_objc,none, (DescriptiveDeclKind, DeclName)) //------------------------------------------------------------------------------ -// @available +// MARK: @available //------------------------------------------------------------------------------ ERROR(availability_decl_unavailable, none, - "%0 is unavailable", (DeclName)) + "%select{getter for |setter for |}0%1 is unavailable", + (unsigned, DeclName)) #define REPLACEMENT_DECL_KIND_SELECT "select{| instance method| property}" ERROR(availability_decl_unavailable_rename, none, - "%0 has been %select{renamed to|replaced by}1" - "%" REPLACEMENT_DECL_KIND_SELECT "2 '%3'", - (DeclName, bool, unsigned, StringRef)) + "%select{getter for |setter for |}0%1 has been " + "%select{renamed to|replaced by}2%" REPLACEMENT_DECL_KIND_SELECT "3 " + "'%4'", + (unsigned, DeclName, bool, unsigned, StringRef)) ERROR(availability_decl_unavailable_rename_msg, none, - "%0 has been %select{renamed to|replaced by}1" - "%" REPLACEMENT_DECL_KIND_SELECT "2 '%3': %4", - (DeclName, bool, unsigned, StringRef, StringRef)) + "%select{getter for |setter for |}0%1 has been " + "%select{renamed to|replaced by}2%" REPLACEMENT_DECL_KIND_SELECT "3 " + "'%4': %5", + (unsigned, DeclName, bool, unsigned, StringRef, StringRef)) ERROR(availability_decl_unavailable_msg, none, - "%0 is unavailable: %1", (DeclName, StringRef)) + "%select{getter for |setter for |}0%1 is unavailable: %2", + (unsigned, DeclName, StringRef)) ERROR(availability_decl_unavailable_in_swift, none, - "%0 is unavailable in Swift", (DeclName)) + "%select{getter for |setter for |}0%1 is unavailable in Swift", + (unsigned, DeclName)) ERROR(availability_decl_unavailable_in_swift_msg, none, - "%0 is unavailable in Swift: %1", (DeclName, StringRef)) + "%select{getter for |setter for |}0%1 is unavailable in Swift: %2", + (unsigned, DeclName, StringRef)) NOTE(availability_marked_unavailable, none, - "%0 has been explicitly marked unavailable here", (DeclName)) + "%select{getter for |setter for |}0%1 has been explicitly marked " + "unavailable here", (unsigned, DeclName)) NOTE(availability_introduced_in_swift, none, - "%0 was introduced in Swift %1", - (DeclName, clang::VersionTuple)) + "%select{getter for |setter for |}0%1 was introduced in Swift %2", + (unsigned, DeclName, clang::VersionTuple)) NOTE(availability_obsoleted, none, - "%0 was obsoleted in %1 %2", - (DeclName, StringRef, clang::VersionTuple)) + "%select{getter for |setter for |}0%1 was obsoleted in %2 %3", + (unsigned, DeclName, StringRef, clang::VersionTuple)) WARNING(availability_deprecated, none, "%select{getter for |setter for |}0%1 %select{is|%select{is|was}4}2 " @@ -3671,10 +3780,6 @@ WARNING(availability_query_useless_enclosing_scope, none, NOTE(availability_query_useless_enclosing_scope_here, none, "enclosing scope here", ()) -WARNING(availability_extension_platform_agnostic, none, - "'@available' without an OS is ignored on extensions; " - "apply the attribute to each member instead", ()) - ERROR(availability_global_script_no_potential, none, "global variable cannot be marked potentially " "unavailable with '@available' in script mode", ()) @@ -3690,9 +3795,6 @@ ERROR(availability_protocol_requires_version, NOTE(availability_protocol_requirement_here, none, "protocol requirement here", ()) -NOTE(availability_conformance_introduced_here, none, - "conformance introduced here", ()) - // This doesn't display as an availability diagnostic, but it's // implemented there and fires when these subscripts are marked // unavailable, so it seems appropriate to put it here. @@ -3700,7 +3802,7 @@ ERROR(availabilty_string_subscript_migration, none, "subscripts returning String were obsoleted in Swift 4; explicitly construct a String from subscripted result", ()) //------------------------------------------------------------------------------ -// @discardableResult +// MARK: @discardableResult //------------------------------------------------------------------------------ WARNING(discardable_result_on_void_never_function, none, @@ -3708,7 +3810,7 @@ WARNING(discardable_result_on_void_never_function, none, (bool)) //------------------------------------------------------------------------------ -// Resilience diagnostics +// MARK: Resilience diagnostics //------------------------------------------------------------------------------ ERROR(fixed_layout_attr_on_internal_type, @@ -3717,15 +3819,18 @@ ERROR(fixed_layout_attr_on_internal_type, "%select{private|fileprivate|internal|%error|%error}1", (DeclName, AccessLevel)) -ERROR(versioned_attr_with_explicit_access, +ERROR(usable_from_inline_attr_with_explicit_access, none, "'@usableFromInline' attribute can only be applied to internal " "declarations, but %0 is %select{private|fileprivate|%error|public|open}1", (DeclName, AccessLevel)) -ERROR(versioned_attr_in_protocol,none, +WARNING(inlinable_implies_usable_from_inline,none, + "'@inlinable' declaration is already '@usableFromInline'",()) + +ERROR(usable_from_inline_attr_in_protocol,none, "'@usableFromInline' attribute cannot be used in protocols", ()) -ERROR(versioned_dynamic_not_supported,none, +ERROR(usable_from_inline_dynamic_not_supported,none, "'@usableFromInline' attribute cannot be applied to 'dynamic' declarations", ()) #define FRAGILE_FUNC_KIND \ @@ -3746,10 +3851,10 @@ ERROR(resilience_decl_unavailable, #undef FRAGILE_FUNC_KIND -NOTE(resilience_decl_declared_here, +NOTE(resilience_decl_declared_here_public, none, "%0 %1 is not public", (DescriptiveDeclKind, DeclName)) -NOTE(resilience_decl_declared_here_versioned, +NOTE(resilience_decl_declared_here, none, "%0 %1 is not '@usableFromInline' or public", (DescriptiveDeclKind, DeclName)) ERROR(class_designated_init_inlinable_resilient,none, @@ -3769,7 +3874,7 @@ ERROR(inlinable_decl_not_public, (DeclBaseName, AccessLevel)) //------------------------------------------------------------------------------ -// @_specialize diagnostics +// MARK: @_specialize diagnostics //------------------------------------------------------------------------------ ERROR(specialize_attr_nongeneric_trailing_where,none, @@ -3798,7 +3903,7 @@ ERROR(specialize_attr_unsupported_kind_of_req,none, "Only same-type and layout requirements are supported by '_specialize' attribute", ()) //------------------------------------------------------------------------------ -// Variable usage diagnostics +// MARK: Variable usage diagnostics //------------------------------------------------------------------------------ WARNING(pbd_never_used_stmtcond, none, @@ -3838,7 +3943,7 @@ WARNING(variable_never_read, none, (Identifier, unsigned)) //------------------------------------------------------------------------------ -// Circular reference diagnostics +// MARK: Circular reference diagnostics //------------------------------------------------------------------------------ ERROR(circular_reference, none, "circular reference", ()) @@ -3850,7 +3955,7 @@ NOTE(circular_reference_through, none, "through reference here", ()) //------------------------------------------------------------------------------ -// Debug diagnostics +// MARK: Debug diagnostics //------------------------------------------------------------------------------ WARNING(debug_long_function_body, none, @@ -3864,7 +3969,7 @@ WARNING(debug_long_expression, none, (unsigned, unsigned)) //------------------------------------------------------------------------------ -// Pattern match diagnostics +// MARK: Pattern match diagnostics //------------------------------------------------------------------------------ @@ -3872,10 +3977,19 @@ ERROR(empty_switch_stmt,none, "'switch' statement body must have at least one 'case' or 'default' " "block; do you want to add a default case?",()) ERROR(non_exhaustive_switch,none, "switch must be exhaustive", ()) +ERROR(possibly_non_exhaustive_switch,none, + "the compiler is unable to check that this switch is exhaustive in reasonable time", + ()) + NOTE(missing_several_cases,none, "do you want to add " "%select{missing cases|a default clause}0" "?", (bool)) +NOTE(missing_unknown_case,none, + "handle unknown values using \"@unknown default\"", ()) + +NOTE(non_exhaustive_switch_drop_unknown,none, + "remove '@unknown' to handle remaining values", ()) NOTE(missing_particular_case,none, "add missing case: '%0'", (StringRef)) @@ -3887,9 +4001,7 @@ WARNING(redundant_particular_literal_case,none, NOTE(redundant_particular_literal_case_here,none, "first occurrence of identical literal pattern is here", ()) -// HACK: Downgrades the above to warnings if any of the cases is marked -// @_downgrade_exhaustivity_check. -WARNING(non_exhaustive_switch_warn_swift3,none, "switch must be exhaustive", ()) +WARNING(non_exhaustive_switch_warn,none, "switch must be exhaustive", ()) #ifndef DIAG_NO_UNDEF # if defined(DIAG) diff --git a/include/swift/AST/ExistentialLayout.h b/include/swift/AST/ExistentialLayout.h index ca196496bca3e..7610fcc4f3a59 100644 --- a/include/swift/AST/ExistentialLayout.h +++ b/include/swift/AST/ExistentialLayout.h @@ -70,18 +70,22 @@ struct ExistentialLayout { } typedef ArrayRefView ProtocolTypeArrayRef; - ProtocolTypeArrayRef getProtocols() const { + ProtocolTypeArrayRef getProtocols() const & { + if (singleProtocol) + return llvm::makeArrayRef(&singleProtocol, 1); return protocols; } + /// The returned ArrayRef may point directly to \c this->singleProtocol, so + /// calling this on a temporary is likely to be incorrect. + ProtocolTypeArrayRef getProtocols() const && = delete; LayoutConstraint getLayoutConstraint() const; private: - // Inline storage for 'protocols' member above when computing - // layout of a single ProtocolType + // The protocol from a ProtocolType Type singleProtocol; - /// Zero or more protocol constraints. + /// Zero or more protocol constraints from a ProtocolCompositionType ArrayRef protocols; }; diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index c6be668fa4e85..d49f6b9833a86 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -156,6 +156,10 @@ class alignas(8) Expr { SWIFT_INLINE_BITFIELD_EMPTY(LiteralExpr, Expr); SWIFT_INLINE_BITFIELD_EMPTY(IdentityExpr, Expr); + SWIFT_INLINE_BITFIELD(LookupExpr, Expr, 1, + IsSuper : 1 + ); + SWIFT_INLINE_BITFIELD_EMPTY(DynamicLookupExpr, LookupExpr); SWIFT_INLINE_BITFIELD(ParenExpr, IdentityExpr, 1, /// \brief Whether we're wrapping a trailing closure expression. @@ -182,9 +186,8 @@ class alignas(8) Expr { FunctionRefKind : 2 ); - SWIFT_INLINE_BITFIELD(MemberRefExpr, Expr, 2+1, - Semantics : 2, // an AccessSemantics - IsSuper : 1 + SWIFT_INLINE_BITFIELD(MemberRefExpr, LookupExpr, 2, + Semantics : 2 // an AccessSemantics ); SWIFT_INLINE_BITFIELD_FULL(TupleElementExpr, Expr, 32, @@ -210,9 +213,8 @@ class alignas(8) Expr { FunctionRefKind : 2 ); - SWIFT_INLINE_BITFIELD_FULL(SubscriptExpr, Expr, 2+1+16+1+1, + SWIFT_INLINE_BITFIELD_FULL(SubscriptExpr, LookupExpr, 2+1+1+16, Semantics : 2, // an AccessSemantics - IsSuper : 1, /// Whether the SubscriptExpr also has source locations for the argument /// label. HasArgLabelLocs : 1, @@ -223,7 +225,7 @@ class alignas(8) Expr { NumArgLabels : 16 ); - SWIFT_INLINE_BITFIELD_FULL(DynamicSubscriptExpr, Expr, 1+1+16, + SWIFT_INLINE_BITFIELD_FULL(DynamicSubscriptExpr, DynamicLookupExpr, 1+1+16, /// Whether the DynamicSubscriptExpr also has source locations for the /// argument label. HasArgLabelLocs : 1, @@ -403,7 +405,7 @@ class alignas(8) Expr { Type getType() const { return Ty; } /// setType - Sets the type of this expression. - void setType(Type T) { Ty = T; } + void setType(Type T); /// \brief Return the source range of the expression. SourceRange getSourceRange() const; @@ -459,13 +461,13 @@ class alignas(8) Expr { /// Enumerate each immediate child expression of this node, invoking the /// specific functor on it. This ignores statements and other non-expression /// children. - void forEachImmediateChildExpr(const std::function &callback); + void forEachImmediateChildExpr(llvm::function_ref callback); /// Enumerate each expr node within this expression subtree, invoking the /// specific functor on it. This ignores statements and other non-expression /// children, and if there is a closure within the expression, this does not /// walk into the body of it (unless it is single-expression). - void forEachChildExpr(const std::function &callback); + void forEachChildExpr(llvm::function_ref callback); /// Determine whether this expression refers to a type by name. /// @@ -547,6 +549,10 @@ class alignas(8) Expr { /// a builtin operator. bool isInfixOperator() const; + /// Returns true if this is a reference to the implicit self of function. + bool isSelfExprOf(const AbstractFunctionDecl *AFD, + bool sameBase = false) const; + /// Produce a mapping from each subexpression to its parent /// expression, with the provided expression serving as the root of /// the parent map. @@ -717,13 +723,20 @@ class ErrorExpr : public Expr { /// can help us preserve the context of the code completion position. class CodeCompletionExpr : public Expr { SourceRange Range; + bool Activated; + public: CodeCompletionExpr(SourceRange Range, Type Ty = Type()) : Expr(ExprKind::CodeCompletion, /*Implicit=*/true, Ty), - Range(Range) {} + Range(Range) { + Activated = false; + } SourceRange getSourceRange() const { return Range; } + bool isActivated() const { return Activated; } + void setActivated() { Activated = true; } + static bool classof(const Expr *E) { return E->getKind() == ExprKind::CodeCompletion; } @@ -826,6 +839,8 @@ class IntegerLiteralExpr : public NumberLiteralExpr { APInt getValue() const; static APInt getValue(StringRef Text, unsigned BitWidth, bool Negative); + APInt getRawMagnitude() const; + static bool classof(const Expr *E) { return E->getKind() == ExprKind::IntegerLiteral; } @@ -1482,16 +1497,60 @@ class UnresolvedDeclRefExpr : public Expr { return E->getKind() == ExprKind::UnresolvedDeclRef; } }; + +/// LookupExpr - This abstract class represents 'a.b', 'a[]', etc where we +/// are referring to a member of a type, such as a property, variable, etc. +class LookupExpr : public Expr { + Expr *Base; + ConcreteDeclRef Member; + +protected: + explicit LookupExpr(ExprKind Kind, Expr *base, ConcreteDeclRef member, + bool Implicit) + : Expr(Kind, Implicit), Base(base), Member(member) { + Bits.LookupExpr.IsSuper = false; + assert(Base); + } + +public: + /// Retrieve the base of the expression. + Expr *getBase() const { return Base; } + + /// Replace the base of the expression. + void setBase(Expr *E) { Base = E; } + + /// Retrieve the member to which this access refers. + ConcreteDeclRef getMember() const { return Member; } + + /// Determine whether the operation has a known underlying declaration or not. + bool hasDecl() const { return static_cast(Member); } + /// Retrieve the declaration that this /// operation refers to. + /// Only valid when \c hasDecl() is true. + ConcreteDeclRef getDecl() const { + assert(hasDecl() && "No subscript declaration known!"); + return getMember(); + } + + /// Determine whether this reference refers to the superclass's property. + bool isSuper() const { return Bits.LookupExpr.IsSuper; } + + /// Set whether this reference refers to the superclass's property. + void setIsSuper(bool isSuper) { Bits.LookupExpr.IsSuper = isSuper; } + + static bool classof(const Expr *E) { + return E->getKind() >= ExprKind::First_LookupExpr && + E->getKind() <= ExprKind::Last_LookupExpr; + } +}; + /// MemberRefExpr - This represents 'a.b' where we are referring to a member /// of a type, such as a property or variable. /// /// Note that methods found via 'dot' syntax are expressed as DotSyntaxCallExpr /// nodes, because 'a.f' is actually an application of 'a' (the implicit object /// argument) to the function 'f'. -class MemberRefExpr : public Expr { - Expr *Base; - ConcreteDeclRef Member; +class MemberRefExpr : public LookupExpr { SourceLoc DotLoc; DeclNameLoc NameLoc; @@ -1499,12 +1558,8 @@ class MemberRefExpr : public Expr { MemberRefExpr(Expr *base, SourceLoc dotLoc, ConcreteDeclRef member, DeclNameLoc loc, bool Implicit, AccessSemantics semantics = AccessSemantics::Ordinary); - Expr *getBase() const { return Base; } - ConcreteDeclRef getMember() const { return Member; } - DeclNameLoc getNameLoc() const { return NameLoc; } SourceLoc getDotLoc() const { return DotLoc; } - - void setBase(Expr *E) { Base = E; } + DeclNameLoc getNameLoc() const { return NameLoc; } /// Return true if this member access is direct, meaning that it /// does not call the getter or setter. @@ -1512,17 +1567,9 @@ class MemberRefExpr : public Expr { return (AccessSemantics) Bits.MemberRefExpr.Semantics; } - /// Determine whether this member reference refers to the - /// superclass's property. - bool isSuper() const { return Bits.MemberRefExpr.IsSuper; } - - /// Set whether this member reference refers to the superclass's - /// property. - void setIsSuper(bool isSuper) { Bits.MemberRefExpr.IsSuper = isSuper; } - SourceLoc getLoc() const { return NameLoc.getBaseNameLoc(); } SourceLoc getStartLoc() const { - SourceLoc BaseStartLoc = Base->getStartLoc(); + SourceLoc BaseStartLoc = getBase()->getStartLoc(); if (BaseStartLoc.isInvalid() || NameLoc.isInvalid()) { return NameLoc.getBaseNameLoc(); } else { @@ -1541,24 +1588,12 @@ class MemberRefExpr : public Expr { /// Common base for expressions that involve dynamic lookup, which /// determines at runtime whether a particular method, property, or /// subscript is available. -class DynamicLookupExpr : public Expr { +class DynamicLookupExpr : public LookupExpr { protected: - Expr *Base; - ConcreteDeclRef Member; - explicit DynamicLookupExpr(ExprKind kind, ConcreteDeclRef member, Expr *base) - : Expr(kind, /*Implicit=*/false), Base(base), Member(member) { } + : LookupExpr(kind, base, member, /*Implicit=*/false) { } public: - /// Retrieve the member to which this access refers. - ConcreteDeclRef getMember() const { return Member; } - - /// Retrieve the base of the expression. - Expr *getBase() const { return Base; } - - /// Replace the base of the expression. - void setBase(Expr *base) { Base = base; } - static bool classof(const Expr *E) { return E->getKind() >= ExprKind::First_DynamicLookupExpr && E->getKind() <= ExprKind::Last_DynamicLookupExpr; @@ -1601,7 +1636,7 @@ class DynamicMemberRefExpr : public DynamicLookupExpr { SourceLoc getLoc() const { return NameLoc.getBaseNameLoc(); } SourceLoc getStartLoc() const { - SourceLoc BaseStartLoc = Base->getStartLoc(); + SourceLoc BaseStartLoc = getBase()->getStartLoc(); if (BaseStartLoc.isInvalid() || NameLoc.isInvalid()) { return NameLoc.getBaseNameLoc(); } else { @@ -1669,12 +1704,6 @@ class DynamicSubscriptExpr final ConcreteDeclRef decl, bool implicit); - /// Retrieve the base of the expression. - Expr *getBase() const { return Base; } - - /// Replace the base of the expression. - void setBase(Expr *base) { Base = base; } - /// getIndex - Retrieve the index of the subscript expression, i.e., the /// "offset" into the base value. Expr *getIndex() const { return Index; } @@ -1695,7 +1724,7 @@ class DynamicSubscriptExpr final SourceLoc getLoc() const { return Index->getStartLoc(); } - SourceLoc getStartLoc() const { return Base->getStartLoc(); } + SourceLoc getStartLoc() const { return getBase()->getStartLoc(); } SourceLoc getEndLoc() const { return Index->getEndLoc(); } static bool classof(const Expr *E) { @@ -2223,12 +2252,10 @@ class DictionaryExpr final : public CollectionExpr, /// type-checked and well-formed subscript expression refers to a subscript /// declaration, which provides a getter and (optionally) a setter that will /// be used to perform reads/writes. -class SubscriptExpr final : public Expr, +class SubscriptExpr final : public LookupExpr, public TrailingCallArguments { friend TrailingCallArguments; - ConcreteDeclRef TheDecl; - Expr *Base; Expr *Index; SubscriptExpr(Expr *base, Expr *index, ArrayRef argLabels, @@ -2260,11 +2287,6 @@ class SubscriptExpr final : public Expr, AccessSemantics semantics = AccessSemantics::Ordinary); - /// getBase - Retrieve the base of the subscript expression, i.e., the - /// value being indexed. - Expr *getBase() const { return Base; } - void setBase(Expr *E) { Base = E; } - /// getIndex - Retrieve the index of the subscript expression, i.e., the /// "offset" into the base value. Expr *getIndex() const { return Index; } @@ -2289,30 +2311,11 @@ class SubscriptExpr final : public Expr, return (AccessSemantics) Bits.SubscriptExpr.Semantics; } - /// Determine whether this member reference refers to the - /// superclass's property. - bool isSuper() const { return Bits.SubscriptExpr.IsSuper; } - - /// Set whether this member reference refers to the superclass's - /// property. - void setIsSuper(bool isSuper) { Bits.SubscriptExpr.IsSuper = isSuper; } - - /// Determine whether subscript operation has a known underlying - /// subscript declaration or not. - bool hasDecl() const { return static_cast(TheDecl); } - - /// Retrieve the subscript declaration that this subscripting - /// operation refers to. Only valid when \c hasDecl() is true. - ConcreteDeclRef getDecl() const { - assert(hasDecl() && "No subscript declaration known!"); - return TheDecl; - } - SourceLoc getLoc() const { return Index->getStartLoc(); } - SourceLoc getStartLoc() const { return Base->getStartLoc(); } + SourceLoc getStartLoc() const { return getBase()->getStartLoc(); } SourceLoc getEndLoc() const { auto end = Index->getEndLoc(); - return end.isValid() ? end : Base->getEndLoc(); + return end.isValid() ? end : getBase()->getEndLoc(); } static bool classof(const Expr *E) { @@ -2353,11 +2356,16 @@ class UnresolvedDotExpr : public Expr { SourceLoc DotLoc; DeclNameLoc NameLoc; DeclName Name; + ArrayRef OuterAlternatives; + public: - UnresolvedDotExpr(Expr *subexpr, SourceLoc dotloc, DeclName name, - DeclNameLoc nameloc, bool Implicit) - : Expr(ExprKind::UnresolvedDot, Implicit), SubExpr(subexpr), DotLoc(dotloc), - NameLoc(nameloc), Name(name) { + UnresolvedDotExpr( + Expr *subexpr, SourceLoc dotloc, DeclName name, DeclNameLoc nameloc, + bool Implicit, + ArrayRef outerAlternatives = ArrayRef()) + : Expr(ExprKind::UnresolvedDot, Implicit), SubExpr(subexpr), + DotLoc(dotloc), NameLoc(nameloc), Name(name), + OuterAlternatives(outerAlternatives) { Bits.UnresolvedDotExpr.FunctionRefKind = static_cast(NameLoc.isCompound() ? FunctionRefKind::Compound : FunctionRefKind::Unapplied); @@ -2380,6 +2388,10 @@ class UnresolvedDotExpr : public Expr { DeclName getName() const { return Name; } DeclNameLoc getNameLoc() const { return NameLoc; } + ArrayRef getOuterAlternatives() const { + return OuterAlternatives; + } + /// Retrieve the kind of function reference. FunctionRefKind getFunctionRefKind() const { return static_cast(Bits.UnresolvedDotExpr.FunctionRefKind); @@ -4014,7 +4026,7 @@ class PrefixUnaryExpr : public ApplyExpr { } }; -/// PostfixUnaryExpr - Prefix unary expressions like '!y'. +/// PostfixUnaryExpr - Postfix unary expressions like 'y!'. class PostfixUnaryExpr : public ApplyExpr { public: PostfixUnaryExpr(Expr *Fn, Expr *Arg, Type Ty = Type()) diff --git a/include/swift/AST/ExprNodes.def b/include/swift/AST/ExprNodes.def index 3aa95b64c822d..30b3c9f8c0013 100644 --- a/include/swift/AST/ExprNodes.def +++ b/include/swift/AST/ExprNodes.def @@ -88,11 +88,14 @@ ABSTRACT_EXPR(OverloadSetRef, Expr) UNCHECKED_EXPR(OverloadedDeclRef, OverloadSetRefExpr) EXPR_RANGE(OverloadSetRef, OverloadedDeclRef, OverloadedDeclRef) UNCHECKED_EXPR(UnresolvedDeclRef, Expr) -EXPR(MemberRef, Expr) -ABSTRACT_EXPR(DynamicLookup, Expr) - EXPR(DynamicMemberRef, DynamicLookupExpr) - EXPR(DynamicSubscript, DynamicLookupExpr) - EXPR_RANGE(DynamicLookup, DynamicMemberRef, DynamicSubscript) +ABSTRACT_EXPR(Lookup, Expr) + EXPR(MemberRef, LookupExpr) + EXPR(Subscript, LookupExpr) + ABSTRACT_EXPR(DynamicLookup, LookupExpr) + EXPR(DynamicMemberRef, DynamicLookupExpr) + EXPR(DynamicSubscript, DynamicLookupExpr) + EXPR_RANGE(DynamicLookup, DynamicMemberRef, DynamicSubscript) + EXPR_RANGE(Lookup, MemberRef, DynamicSubscript) UNCHECKED_EXPR(UnresolvedSpecialize, Expr) UNCHECKED_EXPR(UnresolvedMember, Expr) UNCHECKED_EXPR(UnresolvedDot, Expr) @@ -111,7 +114,6 @@ ABSTRACT_EXPR(Collection, Expr) EXPR(Array, CollectionExpr) EXPR(Dictionary, CollectionExpr) EXPR_RANGE(Collection, Array, Dictionary) -EXPR(Subscript, Expr) EXPR(KeyPathApplication, Expr) EXPR(TupleElement, Expr) EXPR(CaptureList, Expr) diff --git a/include/swift/AST/GenericEnvironment.h b/include/swift/AST/GenericEnvironment.h index 6d9997af51a1a..48dc309594c12 100644 --- a/include/swift/AST/GenericEnvironment.h +++ b/include/swift/AST/GenericEnvironment.h @@ -17,7 +17,6 @@ #ifndef SWIFT_AST_GENERIC_ENVIRONMENT_H #define SWIFT_AST_GENERIC_ENVIRONMENT_H -#include "swift/AST/SubstitutionList.h" #include "swift/AST/SubstitutionMap.h" #include "swift/AST/GenericSignature.h" #include "swift/AST/GenericParamKey.h" @@ -170,7 +169,7 @@ class alignas(1 << DeclAlignInBits) GenericEnvironment final /// generic parameter types by their sugared form. Type getSugaredType(Type type) const; - SubstitutionList getForwardingSubstitutions() const; + SubstitutionMap getForwardingSubstitutionMap() const; void dump(raw_ostream &os) const; diff --git a/include/swift/AST/GenericSignature.h b/include/swift/AST/GenericSignature.h index 34818029cf1c2..dbd1a5d832823 100644 --- a/include/swift/AST/GenericSignature.h +++ b/include/swift/AST/GenericSignature.h @@ -19,8 +19,7 @@ #include "swift/AST/PrintOptions.h" #include "swift/AST/Requirement.h" -#include "swift/AST/SubstitutionList.h" -#include "swift/AST/Types.h" +#include "swift/AST/Type.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/SmallVector.h" @@ -32,7 +31,6 @@ namespace swift { class GenericSignatureBuilder; class ProtocolConformanceRef; class ProtocolType; -class Substitution; class SubstitutionMap; /// An access path used to find a particular protocol conformance within @@ -177,9 +175,9 @@ class alignas(1 << TypeAlignInBits) GenericSignature final return Mem; } - /// Build an interface type substitution map from a vector of Substitutions - /// that correspond to the generic parameters in this generic signature. - SubstitutionMap getSubstitutionMap(SubstitutionList args) const; + /// Build a substitution map for this generic signature by looking up + /// substitutions in the given substitution map. + SubstitutionMap getSubstitutionMap(SubstitutionMap subs) const; /// Build an interface type substitution map from a type substitution function /// and conformance lookup function. @@ -193,47 +191,22 @@ class alignas(1 << TypeAlignInBits) GenericSignature final Optional lookupConformance(CanType depTy, ProtocolDecl *proto) const; - /// Build an array of substitutions from an interface type substitution map, - /// using the given function to look up conformances. - void getSubstitutions(const SubstitutionMap &subMap, - SmallVectorImpl &result) const; - - /// Enumerate all of the dependent types in the type signature that will - /// occur in substitution lists (in order), along with the set of - /// conformance requirements placed on that dependent type. - /// - /// \param fn Callback function that will receive each (type, requirements) - /// pair, in the order they occur within a list of substitutions. If this - /// returns \c true, the enumeration will be aborted. - /// - /// \returns true if any call to \c fn returned \c true, otherwise \c false. - bool enumeratePairedRequirements( - llvm::function_ref)> fn) const; - /// Return a vector of all generic parameters that are not subject to /// a concrete same-type constraint. SmallVector getSubstitutableParams() const; /// Check if the generic signature makes all generic parameters /// concrete. - bool areAllParamsConcrete() const { - return !enumeratePairedRequirements( - [](Type, ArrayRef) -> bool { - return true; - }); - } + bool areAllParamsConcrete() const; - /// Return the size of a SubstitutionList built from this signature. - /// - /// Don't add new calls of this -- the representation of SubstitutionList - /// will be changing soon. - unsigned getSubstitutionListSize() const { + /// Compute the number of conformance requirements in this signature. + unsigned getNumConformanceRequirements() const { unsigned result = 0; - enumeratePairedRequirements( - [&](Type, ArrayRef) -> bool { - result++; - return false; - }); + for (const auto &req : getRequirements()) { + if (req.getKind() == RequirementKind::Conformance) + ++result; + } + return result; } @@ -350,14 +323,6 @@ CanGenericSignature::CanGenericSignature(GenericSignature *Signature) { assert(!Signature || Signature->isCanonical()); } - -inline ArrayRef> -CanGenericSignature::getGenericParams() const{ - auto params = Signature->getGenericParams().getOriginalArray(); - auto base = static_cast*>( - params.data()); - return {base, params.size()}; -} } // end namespace swift diff --git a/include/swift/AST/GenericSignatureBuilder.h b/include/swift/AST/GenericSignatureBuilder.h index 763002a0c54f4..afea016f841cb 100644 --- a/include/swift/AST/GenericSignatureBuilder.h +++ b/include/swift/AST/GenericSignatureBuilder.h @@ -508,7 +508,7 @@ class GenericSignatureBuilder { Optional operator()(CanType dependentType, Type conformingReplacementType, - ProtocolType *conformedProtocol) const { + ProtocolDecl *conformedProtocol) const { return builder->lookupConformance(dependentType, conformingReplacementType, conformedProtocol); @@ -522,7 +522,7 @@ class GenericSignatureBuilder { /// Lookup a protocol conformance in a module-agnostic manner. Optional lookupConformance(CanType dependentType, Type conformingReplacementType, - ProtocolType *conformedProtocol); + ProtocolDecl *conformedProtocol); /// Retrieve the lazy resolver, if there is one. diff --git a/include/swift/AST/IRGenOptions.h b/include/swift/AST/IRGenOptions.h index 75363de0f7fe3..25405ef09e009 100644 --- a/include/swift/AST/IRGenOptions.h +++ b/include/swift/AST/IRGenOptions.h @@ -106,8 +106,8 @@ class IRGenOptions { /// \brief Whether we should run LLVM optimizations after IRGen. unsigned DisableLLVMOptzns : 1; - /// \brief Whether we should run LLVM ARC optimizations after IRGen. - unsigned DisableLLVMARCOpts : 1; + /// Whether we should run swift specific LLVM optimizations after IRGen. + unsigned DisableSwiftSpecificLLVMOptzns : 1; /// \brief Whether we should run LLVM SLP vectorizer. unsigned DisableLLVMSLPVectorizer : 1; @@ -149,6 +149,12 @@ class IRGenOptions { /// Emit names of struct stored properties and enum cases. unsigned EnableReflectionNames : 1; + /// Enables resilient class layout. + unsigned EnableClassResilience : 1; + + /// Bypass resilience when accessing resilient frameworks. + unsigned EnableResilienceBypass : 1; + /// Should we try to build incrementally by not emitting an object file if it /// has the same IR hash as the module that we are preparing to emit? /// @@ -176,12 +182,13 @@ class IRGenOptions { Verify(true), OptMode(OptimizationMode::NotSet), Sanitizers(OptionSet()), DebugInfoKind(IRGenDebugInfoKind::None), UseJIT(false), - DisableLLVMOptzns(false), DisableLLVMARCOpts(false), + DisableLLVMOptzns(false), DisableSwiftSpecificLLVMOptzns(false), DisableLLVMSLPVectorizer(false), DisableFPElim(true), Playground(false), EmitStackPromotionChecks(false), PrintInlineTree(false), EmbedMode(IRGenEmbedMode::None), HasValueNamesSetting(false), ValueNames(false), EnableReflectionMetadata(true), - EnableReflectionNames(true), UseIncrementalLLVMCodeGen(true), + EnableReflectionNames(true), EnableClassResilience(false), + EnableResilienceBypass(false), UseIncrementalLLVMCodeGen(true), UseSwiftCall(false), GenerateProfile(false), CmdArgs(), SanitizeCoverage(llvm::SanitizerCoverageOptions()) {} @@ -190,7 +197,7 @@ class IRGenOptions { unsigned getLLVMCodeGenOptionsHash() { unsigned Hash = (unsigned)OptMode; Hash = (Hash << 1) | DisableLLVMOptzns; - Hash = (Hash << 1) | DisableLLVMARCOpts; + Hash = (Hash << 1) | DisableSwiftSpecificLLVMOptzns; return Hash; } diff --git a/include/swift/AST/Initializer.h b/include/swift/AST/Initializer.h index 053cb8aed9913..c3ce60d0005a6 100644 --- a/include/swift/AST/Initializer.h +++ b/include/swift/AST/Initializer.h @@ -162,7 +162,7 @@ class DefaultArgumentInitializer : public Initializer { /// Change the parent of this context. This is necessary because /// the function signature is parsed before the function /// declaration/expression itself is built. - void changeFunction(DeclContext *parent, MutableArrayRef paramLists); + void changeFunction(DeclContext *parent, ArrayRef paramLists); static bool classof(const DeclContext *DC) { if (auto init = dyn_cast(DC)) diff --git a/include/swift/AST/KnownDecls.def b/include/swift/AST/KnownDecls.def index 7f69ba612566c..b90de4037f56e 100644 --- a/include/swift/AST/KnownDecls.def +++ b/include/swift/AST/KnownDecls.def @@ -63,6 +63,8 @@ FUNC_DECL(BridgeAnyObjectToAny, FUNC_DECL(ConvertToAnyHashable, "_convertToAnyHashable") FUNC_DECL(DiagnoseUnexpectedNilOptional, "_diagnoseUnexpectedNilOptional") +FUNC_DECL(DiagnoseUnexpectedEnumCase, "_diagnoseUnexpectedEnumCase") +FUNC_DECL(DiagnoseUnexpectedEnumCaseValue, "_diagnoseUnexpectedEnumCaseValue") FUNC_DECL(GetErrorEmbeddedNSError, "_getErrorEmbeddedNSError") diff --git a/include/swift/AST/KnownIdentifiers.def b/include/swift/AST/KnownIdentifiers.def index bdd26b65003f5..288837cd8d604 100644 --- a/include/swift/AST/KnownIdentifiers.def +++ b/include/swift/AST/KnownIdentifiers.def @@ -33,6 +33,7 @@ IDENTIFIER(atIndexedSubscript) IDENTIFIER_(bridgeToObjectiveC) IDENTIFIER_WITH_NAME(code_, "_code") IDENTIFIER(CodingKeys) +IDENTIFIER(combine) IDENTIFIER(container) IDENTIFIER(CoreGraphics) IDENTIFIER(CoreMedia) @@ -57,13 +58,17 @@ IDENTIFIER(error) IDENTIFIER(errorDomain) IDENTIFIER(forKeyedSubscript) IDENTIFIER(Foundation) +IDENTIFIER(for) IDENTIFIER(forKey) IDENTIFIER(from) IDENTIFIER(fromRaw) +IDENTIFIER(hash) +IDENTIFIER(hasher) IDENTIFIER(hashValue) IDENTIFIER(initialize) IDENTIFIER(initStorage) IDENTIFIER(initialValue) +IDENTIFIER(into) IDENTIFIER(intValue) IDENTIFIER(Key) IDENTIFIER(KeyedDecodingContainer) diff --git a/include/swift/AST/KnownProtocols.h b/include/swift/AST/KnownProtocols.h index f388fdd827f65..f70bc072bdb9d 100644 --- a/include/swift/AST/KnownProtocols.h +++ b/include/swift/AST/KnownProtocols.h @@ -13,6 +13,7 @@ #ifndef SWIFT_AST_KNOWNPROTOCOLS_H #define SWIFT_AST_KNOWNPROTOCOLS_H +#include "swift/Basic/InlineBitfield.h" #include "swift/Config.h" namespace llvm { @@ -37,6 +38,9 @@ enum : uint8_t { #include "swift/AST/KnownProtocols.def" }; +enum : unsigned { NumKnownProtocolKindBits = + countBitsUsed(static_cast(NumKnownProtocols - 1)) }; + /// Retrieve the name of the given known protocol. llvm::StringRef getProtocolName(KnownProtocolKind kind); diff --git a/include/swift/AST/KnownStdlibTypes.def b/include/swift/AST/KnownStdlibTypes.def index d450b13479537..74ea5fa7719c5 100644 --- a/include/swift/AST/KnownStdlibTypes.def +++ b/include/swift/AST/KnownStdlibTypes.def @@ -50,6 +50,7 @@ KNOWN_STDLIB_TYPE_DECL(Sequence, NominalTypeDecl, 1) KNOWN_STDLIB_TYPE_DECL(Dictionary, NominalTypeDecl, 2) KNOWN_STDLIB_TYPE_DECL(AnyHashable, NominalTypeDecl, 0) KNOWN_STDLIB_TYPE_DECL(MutableCollection, ProtocolDecl, 1) +KNOWN_STDLIB_TYPE_DECL(Hasher, NominalTypeDecl, 0) KNOWN_STDLIB_TYPE_DECL(AnyKeyPath, NominalTypeDecl, 0) KNOWN_STDLIB_TYPE_DECL(PartialKeyPath, NominalTypeDecl, 1) diff --git a/include/swift/AST/LazyResolver.h b/include/swift/AST/LazyResolver.h index 5a4a808d95ac3..b23ca8a7fc09b 100644 --- a/include/swift/AST/LazyResolver.h +++ b/include/swift/AST/LazyResolver.h @@ -33,7 +33,6 @@ class NominalTypeDecl; class NormalProtocolConformance; class ProtocolConformance; class ProtocolDecl; -class Substitution; class TypeDecl; class ValueDecl; class VarDecl; @@ -90,6 +89,16 @@ class LazyResolver { /// considered to be members of the extended type. virtual void resolveExtension(ExtensionDecl *ext) = 0; + using ConformanceConstructionInfo = std::pair; + /// Resolve enough of an extension to find which protocols it is declaring + /// conformance to. + /// + /// This can be called to ensure that the "extension Foo: Bar, Baz" part of + /// the extension is understood. + virtual void resolveExtensionForConformanceConstruction( + ExtensionDecl *ext, + SmallVectorImpl &protocols) = 0; + /// Resolve any implicitly-declared constructors within the given nominal. virtual void resolveImplicitConstructors(NominalTypeDecl *nominal) = 0; @@ -101,71 +110,6 @@ class LazyResolver { DeclContext *dc) = 0; }; -/// An implementation of LazyResolver that delegates to another. -class DelegatingLazyResolver : public LazyResolver { -protected: - LazyResolver &Principal; -public: - DelegatingLazyResolver(LazyResolver &principal) : Principal(principal) {} - ~DelegatingLazyResolver(); // v-table anchor - - void resolveTypeWitness(const NormalProtocolConformance *conformance, - AssociatedTypeDecl *assocType) override { - Principal.resolveTypeWitness(conformance, assocType); - } - - void resolveWitness(const NormalProtocolConformance *conformance, - ValueDecl *requirement) override { - Principal.resolveWitness(conformance, requirement); - } - - void resolveAccessControl(ValueDecl *VD) override { - Principal.resolveAccessControl(VD); - } - - void resolveDeclSignature(ValueDecl *VD) override { - Principal.resolveDeclSignature(VD); - } - - void resolveInheritanceClause( - llvm::PointerUnion decl) override { - Principal.resolveInheritanceClause(decl); - } - - void resolveSuperclass(ClassDecl *classDecl) override { - Principal.resolveSuperclass(classDecl); - } - - void resolveRawType(EnumDecl *enumDecl) override { - Principal.resolveRawType(enumDecl); - } - - void resolveInheritedProtocols(ProtocolDecl *protocol) override { - Principal.resolveInheritedProtocols(protocol); - } - - void bindExtension(ExtensionDecl *ext) override { - Principal.bindExtension(ext); - } - - void resolveExtension(ExtensionDecl *ext) override { - Principal.resolveExtension(ext); - } - - void resolveImplicitConstructors(NominalTypeDecl *nominal) override { - Principal.resolveImplicitConstructors(nominal); - } - - void resolveImplicitMember(NominalTypeDecl *nominal, DeclName member) override { - Principal.resolveImplicitMember(nominal, member); - } - - void markConformanceUsed(ProtocolConformanceRef conformance, - DeclContext *dc) override { - return Principal.markConformanceUsed(conformance, dc); - } -}; - class LazyMemberLoader; /// Context data for lazy deserialization. diff --git a/include/swift/AST/Module.h b/include/swift/AST/Module.h index c9048ab673254..b01d0489fd41f 100644 --- a/include/swift/AST/Module.h +++ b/include/swift/AST/Module.h @@ -28,6 +28,7 @@ #include "swift/Basic/OptionSet.h" #include "swift/Basic/STLExtras.h" #include "swift/Basic/SourceLoc.h" +#include "swift/Parse/SyntaxParsingCache.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/STLExtras.h" @@ -206,6 +207,7 @@ class ModuleDecl : public DeclContext, public TypeDecl { unsigned TestingEnabled : 1; unsigned FailedToLoad : 1; unsigned ResilienceStrategy : 1; + unsigned HasResolvedImports : 1; } Flags; ModuleDecl(Identifier name, ASTContext &ctx); @@ -257,6 +259,13 @@ class ModuleDecl : public DeclContext, public TypeDecl { Flags.FailedToLoad = failed; } + bool hasResolvedImports() const { + return Flags.HasResolvedImports; + } + void setHasResolvedImports() { + Flags.HasResolvedImports = true; + } + ResilienceStrategy getResilienceStrategy() const { return ResilienceStrategy(Flags.ResilienceStrategy); } @@ -400,23 +409,17 @@ class ModuleDecl : public DeclContext, public TypeDecl { /// /// \param topLevelAccessPath If present, include the top-level module in the /// results, with the given access path. - /// \param includePrivateTopLevelImports If true, imports listed in all - /// file units within this module are traversed. Otherwise (the - /// default), only re-exported imports are traversed. /// \param fn A callback of type bool(ImportedModule) or void(ImportedModule). /// Return \c false to abort iteration. /// /// \return True if the traversal ran to completion, false if it ended early /// due to the callback. bool forAllVisibleModules(AccessPathTy topLevelAccessPath, - bool includePrivateTopLevelImports, llvm::function_ref fn); bool forAllVisibleModules(AccessPathTy topLevelAccessPath, - bool includePrivateTopLevelImports, llvm::function_ref fn) { return forAllVisibleModules(topLevelAccessPath, - includePrivateTopLevelImports, [=](const ImportedModule &import) -> bool { fn(import); return true; @@ -425,19 +428,10 @@ class ModuleDecl : public DeclContext, public TypeDecl { template bool forAllVisibleModules(AccessPathTy topLevelAccessPath, - bool includePrivateTopLevelImports, Fn &&fn) { using RetTy = typename std::result_of::type; llvm::function_ref wrapped{std::forward(fn)}; - return forAllVisibleModules(topLevelAccessPath, - includePrivateTopLevelImports, - wrapped); - } - - template - bool forAllVisibleModules(AccessPathTy topLevelAccessPath, Fn &&fn) { - return forAllVisibleModules(topLevelAccessPath, false, - std::forward(fn)); + return forAllVisibleModules(topLevelAccessPath, wrapped); } /// @} @@ -845,6 +839,10 @@ class SourceFile final : public FileUnit { /// The list of top-level declarations in the source file. std::vector Decls; + /// A cache of syntax nodes that can be reused when creating the syntax tree + /// for this file. + SyntaxParsingCache *SyntaxParsingCache = nullptr; + /// The list of local type declarations in the source file. llvm::SetVector LocalTypeDecls; @@ -854,6 +852,14 @@ class SourceFile final : public FileUnit { /// complete, we diagnose. llvm::SetVector AttrsRequiringFoundation; + /// A set of synthesized declarations that need to be type checked. + llvm::SmallVector SynthesizedDecls; + + /// We might perform type checking on the same source file more than once, + /// if its the main file or a REPL instance, so keep track of the last + /// checked synthesized declaration to avoid duplicating work. + unsigned LastCheckedSynthesizedDecl = 0; + /// A mapping from Objective-C selectors to the methods that have /// those selectors. llvm::DenseMap> diff --git a/include/swift/AST/NameLookup.h b/include/swift/AST/NameLookup.h index 51b6b46896115..96e100e504756 100644 --- a/include/swift/AST/NameLookup.h +++ b/include/swift/AST/NameLookup.h @@ -86,19 +86,37 @@ struct LookupResultEntry { /// unqualified lookup (i.e. lookup for a plain identifier). class UnqualifiedLookup { public: + enum class Flags { + /// This lookup is known to not affect downstream files. + KnownPrivate = 0x01, + /// This lookup should only return types. + TypeLookup = 0x02, + /// This lookup should consider declarations within protocols to which the + /// context type conforms. + AllowProtocolMembers = 0x04, + /// Don't check access when doing lookup into a type. + IgnoreAccessControl = 0x08, + /// This lookup should include results from outside the innermost scope with + /// results. + IncludeOuterResults = 0x10, + }; + using Options = OptionSet; + /// \brief Lookup an unqualified identifier \p Name in the context. /// /// If the current DeclContext is nested in a function body, the SourceLoc /// is used to determine which declarations in that body are visible. - UnqualifiedLookup(DeclName Name, DeclContext *DC, - LazyResolver *TypeResolver, - bool IsKnownPrivate = false, - SourceLoc Loc = SourceLoc(), - bool IsTypeLookup = false, - bool AllowProtocolMembers = false, - bool IgnoreAccessControl = false); + UnqualifiedLookup(DeclName Name, DeclContext *DC, LazyResolver *TypeResolver, + SourceLoc Loc = SourceLoc(), Options options = Options()); SmallVector Results; + /// \brief The index of the first result that isn't from the innermost scope + /// with results. + /// + /// That is, \c makeArrayRef(Results).take_front(IndexOfFirstOuterResults) + /// will be Results from the innermost scope that had results, and the + /// remaining elements of Results will be from parent scopes of this one. + size_t IndexOfFirstOuterResult; /// \brief Return true if anything was found by the name lookup. bool isSuccess() const { return !Results.empty(); } @@ -107,6 +125,11 @@ class UnqualifiedLookup { TypeDecl *getSingleTypeResult(); }; +inline UnqualifiedLookup::Options operator|(UnqualifiedLookup::Flags flag1, + UnqualifiedLookup::Flags flag2) { + return UnqualifiedLookup::Options(flag1) | flag2; +} + /// Describes the reason why a certain declaration is visible. enum class DeclVisibilityKind { /// Declaration is a local variable or type. diff --git a/include/swift/AST/Ownership.h b/include/swift/AST/Ownership.h index a3dbfe58d60ce..d7976114d640a 100644 --- a/include/swift/AST/Ownership.h +++ b/include/swift/AST/Ownership.h @@ -19,6 +19,8 @@ #ifndef SWIFT_OWNERSHIP_H #define SWIFT_OWNERSHIP_H +#include "swift/Basic/InlineBitfield.h" +#include "llvm/Support/raw_ostream.h" #include namespace swift { @@ -38,8 +40,16 @@ enum class ReferenceOwnership : uint8_t { /// \brief an 'unowned(unsafe)' reference Unmanaged, + + Last_Kind = Unmanaged }; +enum : unsigned { NumReferenceOwnershipBits = + countBitsUsed(static_cast(ReferenceOwnership::Last_Kind)) }; + +/// Diagnostic printing of \c StaticSpellingKind. +llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, ReferenceOwnership RO); + /// Different kinds of value ownership supported by Swift. enum class ValueOwnership : uint8_t { /// \brief the context-dependent default ownership (sometimes shared, diff --git a/include/swift/AST/Pattern.h b/include/swift/AST/Pattern.h index 7d5cfe775222c..2210c0cdd0043 100644 --- a/include/swift/AST/Pattern.h +++ b/include/swift/AST/Pattern.h @@ -126,7 +126,10 @@ class alignas(8) Pattern { /// Set the type of this pattern, given that it was previously not /// type-checked. - void setType(Type ty) { Ty = ty; } + void setType(Type ty) { + assert(!ty || !ty->hasTypeVariable()); + Ty = ty; + } /// Retrieve the delayed interface type of this pattern, if it has one. /// @@ -163,14 +166,14 @@ class alignas(8) Pattern { /// \brief apply the specified function to all variables referenced in this /// pattern. - void forEachVariable(const std::function &f) const; + void forEachVariable(llvm::function_ref f) const; /// \brief apply the specified function to all pattern nodes recursively in /// this pattern. This is a pre-order traversal. - void forEachNode(const std::function &f); + void forEachNode(llvm::function_ref f); - void forEachNode(const std::function &f) const { - const std::function &f2 = f; + void forEachNode(llvm::function_ref f) const { + llvm::function_ref f2 = f; const_cast(this)->forEachNode(f2); } diff --git a/include/swift/AST/ProtocolConformance.h b/include/swift/AST/ProtocolConformance.h index 9e29750a5f94f..1ed2a75366992 100644 --- a/include/swift/AST/ProtocolConformance.h +++ b/include/swift/AST/ProtocolConformance.h @@ -18,7 +18,6 @@ #include "swift/AST/ConcreteDeclRef.h" #include "swift/AST/Decl.h" -#include "swift/AST/SubstitutionList.h" #include "swift/AST/Type.h" #include "swift/AST/Types.h" #include "swift/AST/TypeAlignments.h" @@ -115,6 +114,11 @@ class alignas(1 << DeclAlignInBits) ProtocolConformance { /// Retrieve the state of this conformance. ProtocolConformanceState getState() const; + /// Get the kind of source from which this conformance comes. + ConformanceEntryKind getSourceKind() const; + /// Get the protocol conformance which implied this implied conformance. + NormalProtocolConformance *getImplyingConformance() const; + /// Determine whether this conformance is complete. bool isComplete() const { return getState() == ProtocolConformanceState::Complete; @@ -349,6 +353,20 @@ class NormalProtocolConformance : public ProtocolConformance, /// Also stores the "invalid" bit. llvm::PointerIntPair ContextAndInvalid; + /// \brief The reason that this conformance exists. + /// + /// Either Explicit (e.g. 'struct Foo: Protocol {}' or 'extension Foo: + /// Protocol {}'), Synthesized (e.g. RawRepresentable for 'enum Foo: Int {}') + /// or Implied (e.g. 'Foo : Protocol' in 'protocol Other: Protocol {} struct + /// Foo: Other {}'). In only the latter case, the conformance is non-null and + /// points to the conformance that implies this one. + /// + /// This should never be Inherited: that is handled by + /// InheritedProtocolConformance. + llvm::PointerIntPair + SourceKindAndImplyingConformance = {nullptr, + ConformanceEntryKind::Explicit}; + /// \brief The mapping of individual requirements in the protocol over to /// the declarations that satisfy those requirements. mutable WitnessMap Mapping; @@ -447,6 +465,28 @@ class NormalProtocolConformance : public ProtocolConformance, SignatureConformances = {}; } + /// Get the kind of source from which this conformance comes. + ConformanceEntryKind getSourceKind() const { + return SourceKindAndImplyingConformance.getInt(); + } + + /// Get the protocol conformance which implied this implied conformance. + NormalProtocolConformance *getImplyingConformance() const { + assert(getSourceKind() == ConformanceEntryKind::Implied); + return SourceKindAndImplyingConformance.getPointer(); + } + + void setSourceKindAndImplyingConformance( + ConformanceEntryKind sourceKind, + NormalProtocolConformance *implyingConformance) { + assert(sourceKind != ConformanceEntryKind::Inherited && + "a normal conformance cannot be inherited"); + assert((sourceKind == ConformanceEntryKind::Implied) == + (bool)implyingConformance && + "an implied conformance needs something that implies it"); + SourceKindAndImplyingConformance = {implyingConformance, sourceKind}; + } + /// Determine whether this conformance is lazily loaded. /// /// This only matters to the AST verifier. @@ -610,7 +650,7 @@ class SpecializedProtocolConformance : public ProtocolConformance, /// The substitutions applied to the generic conformance to produce this /// conformance. - SubstitutionList GenericSubstitutions; + SubstitutionMap GenericSubstitutions; /// The mapping from associated type requirements to their substitutions. /// @@ -626,7 +666,7 @@ class SpecializedProtocolConformance : public ProtocolConformance, SpecializedProtocolConformance(Type conformingType, ProtocolConformance *genericConformance, - SubstitutionList substitutions); + SubstitutionMap substitutions); public: /// Get the generic conformance from which this conformance was derived, @@ -635,15 +675,9 @@ class SpecializedProtocolConformance : public ProtocolConformance, return GenericConformance; } - /// Get the substitutions used to produce this specialized conformance from - /// the generic conformance. - SubstitutionList getGenericSubstitutions() const { - return GenericSubstitutions; - } - /// Get the substitution map representing the substitutions used to produce /// this specialized conformance. - SubstitutionMap getSubstitutionMap() const; + SubstitutionMap getSubstitutionMap() const { return GenericSubstitutions; } /// Get any requirements that must be satisfied for this conformance to apply. ArrayRef getConditionalRequirements() const { @@ -666,6 +700,15 @@ class SpecializedProtocolConformance : public ProtocolConformance, return GenericConformance->getState(); } + /// Get the kind of source from which this conformance comes. + ConformanceEntryKind getSourceKind() const { + return GenericConformance->getSourceKind(); + } + /// Get the protocol conformance which implied this implied conformance. + NormalProtocolConformance *getImplyingConformance() const { + return GenericConformance->getImplyingConformance(); + } + bool hasTypeWitness(AssociatedTypeDecl *assocType, LazyResolver *resolver = nullptr) const; @@ -694,16 +737,15 @@ class SpecializedProtocolConformance : public ProtocolConformance, } void Profile(llvm::FoldingSetNodeID &ID) { - Profile(ID, getType(), getGenericConformance(), - getGenericSubstitutions()); + Profile(ID, getType(), getGenericConformance(), getSubstitutionMap()); } static void Profile(llvm::FoldingSetNodeID &ID, Type type, ProtocolConformance *genericConformance, - SubstitutionList subs) { + SubstitutionMap subs) { ID.AddPointer(type.getPointer()); ID.AddPointer(genericConformance); - profileSubstitutionList(ID, subs); + subs.profile(ID); } static bool classof(const ProtocolConformance *conformance) { @@ -768,6 +810,13 @@ class InheritedProtocolConformance : public ProtocolConformance, return InheritedConformance->getState(); } + /// Get the kind of source from which this conformance comes. + ConformanceEntryKind getSourceKind() const { + return ConformanceEntryKind::Inherited; + } + /// Get the protocol conformance which implied this implied conformance. + NormalProtocolConformance *getImplyingConformance() const { return nullptr; } + bool hasTypeWitness(AssociatedTypeDecl *assocType, LazyResolver *resolver = nullptr) const { return InheritedConformance->hasTypeWitness(assocType, resolver); diff --git a/include/swift/AST/RequirementEnvironment.h b/include/swift/AST/RequirementEnvironment.h index ce5165ccf079c..34ada04007927 100644 --- a/include/swift/AST/RequirementEnvironment.h +++ b/include/swift/AST/RequirementEnvironment.h @@ -114,7 +114,7 @@ class RequirementEnvironment { /// Retrieve the substitution map that maps the interface types of the /// requirement to the interface types of the synthetic environment. - const SubstitutionMap &getRequirementToSyntheticMap() const { + SubstitutionMap getRequirementToSyntheticMap() const { return reqToSyntheticEnvMap; } }; diff --git a/include/swift/AST/SILOptions.h b/include/swift/AST/SILOptions.h index e0c0eb460343d..309c5297efb27 100644 --- a/include/swift/AST/SILOptions.h +++ b/include/swift/AST/SILOptions.h @@ -42,20 +42,6 @@ class SILOptions { /// The number of threads for multi-threaded code generation. int NumThreads = 0; - enum LinkingMode { - /// Skip SIL linking. - LinkNone, - - /// Perform normal SIL linking. - LinkNormal, - - /// Link all functions during SIL linking. - LinkAll - }; - - /// Controls how to perform SIL linking. - LinkingMode LinkMode = LinkNormal; - /// Controls whether to pull in SIL from partial modules during the /// merge modules step. Could perhaps be merged with the link mode /// above but the interactions between all the flags are tricky. diff --git a/include/swift/AST/Stmt.h b/include/swift/AST/Stmt.h index 3f813dcc5564d..c6ea9c8843cd0 100644 --- a/include/swift/AST/Stmt.h +++ b/include/swift/AST/Stmt.h @@ -20,13 +20,13 @@ #include "swift/AST/Availability.h" #include "swift/AST/AvailabilitySpec.h" #include "swift/AST/ASTNode.h" -#include "swift/AST/Expr.h" #include "swift/AST/IfConfigClause.h" #include "swift/AST/TypeAlignments.h" #include "swift/Basic/NullablePtr.h" #include "llvm/Support/TrailingObjects.h" namespace swift { + class AnyPattern; class ASTContext; class ASTWalker; class Decl; @@ -128,7 +128,7 @@ class alignas(8) Stmt { LLVM_ATTRIBUTE_DEPRECATED( void dump() const LLVM_ATTRIBUTE_USED, "only for use within the debugger"); - void print(raw_ostream &OS, unsigned Indent = 0) const; + void print(raw_ostream &OS, const ASTContext *Ctx = nullptr, unsigned Indent = 0) const; // Only allow allocation of Exprs using the allocator in ASTContext // or by doing a placement new. @@ -814,17 +814,40 @@ class ForEachStmt : public LabeledStmt { /// A pattern and an optional guard expression used in a 'case' statement. class CaseLabelItem { + enum class Kind { + /// A normal pattern + Normal = 0, + /// `default` + Default, + }; + Pattern *CasePattern; SourceLoc WhereLoc; - llvm::PointerIntPair GuardExprAndIsDefault; + llvm::PointerIntPair GuardExprAndKind; + + CaseLabelItem(Kind kind, Pattern *casePattern, SourceLoc whereLoc, + Expr *guardExpr) + : CasePattern(casePattern), WhereLoc(whereLoc), + GuardExprAndKind(guardExpr, kind) {} public: CaseLabelItem(const CaseLabelItem &) = default; - CaseLabelItem(bool IsDefault, Pattern *CasePattern, SourceLoc WhereLoc, - Expr *GuardExpr) - : CasePattern(CasePattern), WhereLoc(WhereLoc), - GuardExprAndIsDefault(GuardExpr, IsDefault) {} + CaseLabelItem(Pattern *casePattern, SourceLoc whereLoc, Expr *guardExpr) + : CaseLabelItem(Kind::Normal, casePattern, whereLoc, guardExpr) {} + explicit CaseLabelItem(Pattern *casePattern) + : CaseLabelItem(casePattern, SourceLoc(), nullptr) {} + + static CaseLabelItem getDefault(AnyPattern *pattern, + SourceLoc whereLoc, + Expr *guardExpr) { + assert(pattern); + return CaseLabelItem(Kind::Default, reinterpret_cast(pattern), + whereLoc, guardExpr); + } + static CaseLabelItem getDefault(AnyPattern *pattern) { + return getDefault(pattern, SourceLoc(), nullptr); + } SourceLoc getWhereLoc() const { return WhereLoc; } @@ -838,14 +861,16 @@ class CaseLabelItem { /// Return the guard expression if present, or null if the case label has /// no guard. - Expr *getGuardExpr() { return GuardExprAndIsDefault.getPointer(); } + Expr *getGuardExpr() { return GuardExprAndKind.getPointer(); } const Expr *getGuardExpr() const { - return GuardExprAndIsDefault.getPointer(); + return GuardExprAndKind.getPointer(); } - void setGuardExpr(Expr *e) { GuardExprAndIsDefault.setPointer(e); } + void setGuardExpr(Expr *e) { GuardExprAndKind.setPointer(e); } /// Returns true if this is syntactically a 'default' label. - bool isDefault() const { return GuardExprAndIsDefault.getInt(); } + bool isDefault() const { + return GuardExprAndKind.getInt() == Kind::Default; + } }; /// A 'case' or 'default' block of a switch statement. Only valid as the @@ -865,19 +890,21 @@ class CaseStmt final : public Stmt, private llvm::TrailingObjects { friend TrailingObjects; + SourceLoc UnknownAttrLoc; SourceLoc CaseLoc; SourceLoc ColonLoc; llvm::PointerIntPair BodyAndHasBoundDecls; CaseStmt(SourceLoc CaseLoc, ArrayRef CaseLabelItems, - bool HasBoundDecls, SourceLoc ColonLoc, Stmt *Body, - Optional Implicit); + bool HasBoundDecls, SourceLoc UnknownAttrLoc, SourceLoc ColonLoc, + Stmt *Body, Optional Implicit); public: static CaseStmt *create(ASTContext &C, SourceLoc CaseLoc, ArrayRef CaseLabelItems, - bool HasBoundDecls, SourceLoc ColonLoc, Stmt *Body, + bool HasBoundDecls, SourceLoc UnknownAttrLoc, + SourceLoc ColonLoc, Stmt *Body, Optional Implicit = None); ArrayRef getCaseLabelItems() const { @@ -896,7 +923,11 @@ class CaseStmt final : public Stmt, /// Get the source location of the 'case' or 'default' of the first label. SourceLoc getLoc() const { return CaseLoc; } - SourceLoc getStartLoc() const { return getLoc(); } + SourceLoc getStartLoc() const { + if (UnknownAttrLoc.isValid()) + return UnknownAttrLoc; + return getLoc(); + } SourceLoc getEndLoc() const { return getBody()->getEndLoc(); } SourceRange getLabelItemsRange() const { return ColonLoc.isValid() ? SourceRange(getLoc(), ColonLoc) : getSourceRange(); @@ -904,6 +935,14 @@ class CaseStmt final : public Stmt, bool isDefault() { return getCaseLabelItems()[0].isDefault(); } + bool hasUnknownAttr() const { + // Note: This representation doesn't allow for synthesized @unknown cases. + // However, that's probably sensible; the purpose of @unknown is for + // diagnosing otherwise-non-exhaustive switches, and the user can't edit + // a synthesized case. + return UnknownAttrLoc.isValid(); + } + static bool classof(const Stmt *S) { return S->getKind() == StmtKind::Case; } }; @@ -983,7 +1022,7 @@ class BreakStmt : public Stmt { SourceLoc Loc; Identifier TargetName; // Named target statement, if specified in the source. SourceLoc TargetLoc; - LabeledStmt *Target; // Target stmt, wired up by Sema. + LabeledStmt *Target = nullptr; // Target stmt, wired up by Sema. public: BreakStmt(SourceLoc Loc, Identifier TargetName, SourceLoc TargetLoc, Optional implicit = None) @@ -1018,7 +1057,7 @@ class ContinueStmt : public Stmt { SourceLoc Loc; Identifier TargetName; // Named target statement, if specified in the source. SourceLoc TargetLoc; - LabeledStmt *Target; + LabeledStmt *Target = nullptr; public: ContinueStmt(SourceLoc Loc, Identifier TargetName, SourceLoc TargetLoc, diff --git a/include/swift/AST/Substitution.h b/include/swift/AST/Substitution.h deleted file mode 100644 index 14489645f2a46..0000000000000 --- a/include/swift/AST/Substitution.h +++ /dev/null @@ -1,68 +0,0 @@ -//===--- Substitution.h - Swift Generic Substitution ASTs -------*- 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 -// -//===----------------------------------------------------------------------===// -// -// This file defines the Substitution class. -// -//===----------------------------------------------------------------------===// - -#ifndef SWIFT_AST_SUBSTITUTION_H -#define SWIFT_AST_SUBSTITUTION_H - -#include "swift/AST/Type.h" -#include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/Optional.h" - -namespace llvm { - class raw_ostream; -} - -namespace swift { - class GenericEnvironment; - class SubstitutionMap; - -/// Substitution - A substitution into a generic specialization. -class Substitution { - Type Replacement; - ArrayRef Conformance; - -public: - /// The replacement type. - Type getReplacement() const { return Replacement; } - - /// The protocol conformances for the replacement. These appear in the same - /// order as Archetype->getConformsTo() for the substituted archetype. - const ArrayRef getConformances() const { - return Conformance; - } - - Substitution() {} - - Substitution(Type Replacement, ArrayRef Conformance); - - /// Checks whether the current substitution is canonical. - bool isCanonical() const; - - /// Get the canonicalized substitution. If wasCanonical is not nullptr, - /// store there whether the current substitution was canonical already. - Substitution getCanonicalSubstitution(bool *wasCanonical = nullptr) const; - - bool operator!=(const Substitution &other) const { return !(*this == other); } - bool operator==(const Substitution &other) const; - void print(llvm::raw_ostream &os, - const PrintOptions &PO = PrintOptions()) const; - void dump() const; - void dump(llvm::raw_ostream &os, unsigned indent = 0) const; -}; - -} // end namespace swift - -#endif diff --git a/include/swift/AST/SubstitutionList.h b/include/swift/AST/SubstitutionList.h deleted file mode 100644 index 84de1f94080c6..0000000000000 --- a/include/swift/AST/SubstitutionList.h +++ /dev/null @@ -1,55 +0,0 @@ -//===--- SubstitutionList.h - Compact SubstitutionMap -----------*- 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 -// -//===----------------------------------------------------------------------===// -// -// This file defines the SubstitutionList class, which is a memory-efficient -// representation of a SubstitutionMap, intended to be stored in AST nodes and -// SIL instructions. -// -//===----------------------------------------------------------------------===// - -#ifndef SWIFT_AST_SUBSTITUTION_LIST_H -#define SWIFT_AST_SUBSTITUTION_LIST_H - -#include "swift/AST/Substitution.h" -#include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/FoldingSet.h" - -namespace llvm { -class FoldingSetNodeID; -} // end namespace llvm - -namespace swift { - -// FIXME: Soon this will change. -using SubstitutionList = ArrayRef; - -void dump(SubstitutionList subs); - -/// Create a canonicalized substitution list from subs. -/// subs is the substitution list to be canonicalized. -/// canSubs is an out-parameter, which is used to store the results in case -/// the list of substitutions was not canonical. -/// The function returns a list of canonicalized substitutions. -/// If the substitution list subs was canonical already, it will be returned and -/// canSubs out-parameter will be empty. -/// If something had to be canonicalized, then the canSubs out-parameter will be -/// populated and the returned SubstitutionList would refer to canSubs storage. -SubstitutionList -getCanonicalSubstitutionList(SubstitutionList subs, - SmallVectorImpl &canSubs); - -/// Profile the substitution list for use in a folding set. -void profileSubstitutionList(llvm::FoldingSetNodeID &id, SubstitutionList subs); - -} // end namespace swift - -#endif diff --git a/include/swift/AST/SubstitutionMap.h b/include/swift/AST/SubstitutionMap.h index cc4eb8cba3fc1..5b549414b765f 100644 --- a/include/swift/AST/SubstitutionMap.h +++ b/include/swift/AST/SubstitutionMap.h @@ -12,17 +12,6 @@ // // This file defines the SubstitutionMap class. // -// This is a data structure type describing the mapping of abstract types to -// replacement types, together with associated conformances to use for deriving -// nested types. -// -// Depending on how the SubstitutionMap is constructed, the abstract types are -// either archetypes or interface types. Care must be exercised to only look up -// one or the other. -// -// SubstitutionMaps are constructed by calling the getSubstitutionMap() method -// on a GenericSignature or GenericEnvironment. -// //===----------------------------------------------------------------------===// #ifndef SWIFT_AST_SUBSTITUTION_MAP_H @@ -31,10 +20,8 @@ #include "swift/AST/ProtocolConformanceRef.h" #include "swift/AST/Type.h" #include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseMapInfo.h" #include "llvm/ADT/Optional.h" -#include "llvm/ADT/SmallPtrSet.h" -#include "llvm/ADT/SmallVector.h" namespace llvm { class FoldingSetNodeID; @@ -55,53 +42,109 @@ enum class CombineSubstitutionMaps { AtIndex }; +/// SubstitutionMap is a data structure type that describes the mapping of +/// abstract types to replacement types, together with associated conformances +/// to use for deriving nested types and conformances. +/// +/// Substitution maps are primarily used when performing substitutions into +/// any entity that can reference type parameters, e.g., types (via +/// Type::subst()) and conformances (via ProtocolConformanceRef::subst()). +/// +/// SubstitutionMaps are constructed by calling the getSubstitutionMap() method +/// on a GenericSignature or (equivalently) by calling one of the static +/// \c SubstitutionMap::get() methods. However, most substitution maps are +/// computed using higher-level entry points such as +/// TypeBase::getMemberSubstitutionMap(). +/// +/// Substitution maps are ASTContext-allocated and are uniqued on construction, +/// so they can be used as fields in AST nodes. class SubstitutionMap { - /// The generic signature for which we are performing substitutions. - GenericSignature *genericSig; - - /// The replacement types for the generic type parameters. - std::unique_ptr replacementTypes; +public: + /// Stored data for a substitution map, which uses tail allocation for the + /// replacement types and conformances. + class Storage; - // FIXME: Switch to a more efficient representation that corresponds to - // the conformance requirements in the GenericSignature. - llvm::DenseMap> - conformanceMap; +private: + /// The storage needed to describe the set of substitutions. + /// + /// When null, this substitution map is empty, having neither a generic + /// signature nor any replacement types/conformances. + Storage *storage = nullptr; +public: /// Retrieve the array of replacement types, which line up with the /// generic parameters. /// /// Note that the types may be null, for cases where the generic parameter /// is concrete but hasn't been queried yet. - ArrayRef getReplacementTypes() const; - - MutableArrayRef getReplacementTypes(); - -public: - SubstitutionMap() - : SubstitutionMap(static_cast(nullptr)) { } - - SubstitutionMap(GenericSignature *genericSig); + /// + /// Prefer \c getReplacementTypes, this is public for printing purposes. + ArrayRef getReplacementTypesBuffer() const; - SubstitutionMap(GenericEnvironment *genericEnv); +private: + MutableArrayRef getReplacementTypesBuffer(); - SubstitutionMap(SubstitutionMap &&other) = default; - SubstitutionMap &operator=(SubstitutionMap &&other) = default; + /// Retrieve a mutable reference to the buffer of conformances. + MutableArrayRef getConformancesBuffer(); - SubstitutionMap(const SubstitutionMap &other); + /// Form a substitution map for the given generic signature with the + /// specified replacement types and conformances. + SubstitutionMap(GenericSignature *genericSig, + ArrayRef replacementTypes, + ArrayRef conformances); - SubstitutionMap &operator=(const SubstitutionMap &other); + explicit SubstitutionMap(Storage *storage) : storage(storage) { } - ~SubstitutionMap(); +public: + /// Build an empty substitution map. + SubstitutionMap() { } + + /// Build an interface type substitution map for the given generic + /// signature and a vector of Substitutions that correspond to the + /// requirements of this generic signature. + static SubstitutionMap get(GenericSignature *genericSig, + ArrayRef replacementTypes, + ArrayRef conformances) { + return SubstitutionMap(genericSig, replacementTypes, conformances); + } + + /// Build an interface type substitution map for the given generic + /// signature using the mapping in the given substitutions. + static SubstitutionMap get(GenericSignature *genericSig, + SubstitutionMap substitutions); + + /// Build an interface type substitution map for the given generic signature + /// from a type substitution function and conformance lookup function. + static SubstitutionMap get(GenericSignature *genericSig, + TypeSubstitutionFn subs, + LookupConformanceFn lookupConformance); /// Retrieve the generic signature describing the environment in which /// substitutions occur. - GenericSignature *getGenericSignature() const { return genericSig; } + GenericSignature *getGenericSignature() const; + + /// Retrieve the array of protocol conformances, which line up with the + /// requirements of the generic signature. + ArrayRef getConformances() const; + /// Look up a conformance for the given type to the given protocol. Optional lookupConformance(CanType type, ProtocolDecl *proto) const; /// Whether the substitution map is empty. - bool empty() const { return getGenericSignature() == nullptr; } + bool empty() const; + + /// Whether the substitution has any substitutable parameters, i.e., + /// it is non-empty and at least one of the type parameters can be + /// substituted (i.e., is not mapped to a concrete type). + bool hasAnySubstitutableParams() const; + + /// Whether the substitution map is non-empty. + explicit operator bool() const { return !empty(); } + + /// Retrieve the array of replacement types, which line up with the + /// generic parameters. + ArrayRef getReplacementTypes() const; /// Query whether any replacement types in the map contain archetypes. bool hasArchetypes() const; @@ -113,9 +156,15 @@ class SubstitutionMap { /// Query whether any replacement type sin the map contain dynamic Self. bool hasDynamicSelf() const; + /// Whether the replacement types are all canonical. + bool isCanonical() const; + + /// Return the canonical form of this substitution map. + SubstitutionMap getCanonical() const; + /// Apply a substitution to all replacement types in the map. Does not /// change keys. - SubstitutionMap subst(const SubstitutionMap &subMap) const; + SubstitutionMap subst(SubstitutionMap subMap) const; /// Apply a substitution to all replacement types in the map. Does not /// change keys. @@ -159,8 +208,8 @@ class SubstitutionMap { /// /// The 'how' parameter determines if we're looking at the depth or index. static SubstitutionMap - combineSubstitutionMaps(const SubstitutionMap &firstSubMap, - const SubstitutionMap &secondSubMap, + combineSubstitutionMaps(SubstitutionMap firstSubMap, + SubstitutionMap secondSubMap, CombineSubstitutionMaps how, unsigned baseDepthOrIndex, unsigned origDepthOrIndex, @@ -173,14 +222,42 @@ class SubstitutionMap { /// Verify that this substitution map is valid. void verify() const; + /// Whether to dump the full substitution map, or just a minimal useful subset + /// (on a single line). + enum class DumpStyle { Minimal, Full }; /// Dump the contents of this substitution map for debugging purposes. - void dump(llvm::raw_ostream &out) const; + void dump(llvm::raw_ostream &out, DumpStyle style = DumpStyle::Full, + unsigned indent = 0) const; LLVM_ATTRIBUTE_DEPRECATED(void dump() const, "only for use in the debugger"); /// Profile the substitution map, for use with LLVM's FoldingSet. void profile(llvm::FoldingSetNodeID &id) const; + const void *getOpaqueValue() const { return storage; } + + static SubstitutionMap getFromOpaqueValue(const void *ptr) { + return SubstitutionMap(const_cast((const Storage *)ptr)); + } + + static SubstitutionMap getEmptyKey() { + return SubstitutionMap( + (Storage *)llvm::DenseMapInfo::getEmptyKey()); + } + + static SubstitutionMap getTombstoneKey() { + return SubstitutionMap( + (Storage *)llvm::DenseMapInfo::getTombstoneKey()); + } + + friend bool operator ==(SubstitutionMap lhs, SubstitutionMap rhs) { + return lhs.storage == rhs.storage; + } + + friend bool operator !=(SubstitutionMap lhs, SubstitutionMap rhs) { + return lhs.storage != rhs.storage; + } + private: friend class GenericSignature; friend class GenericEnvironment; @@ -191,15 +268,63 @@ class SubstitutionMap { /// stored inside the map. In most cases, you should call Type::subst() /// instead, since that will resolve member types also. Type lookupSubstitution(CanSubstitutableType type) const; +}; - // You should not need to call these directly to build SubstitutionMaps; - // instead, use GenericSignature::getSubstitutionMap() or - // GenericEnvironment::getSubstitutionMap(). +/// A function object suitable for use as a \c TypeSubstitutionFn that +/// queries an underlying \c SubstitutionMap. +struct QuerySubstitutionMap { + SubstitutionMap subMap; - void addSubstitution(CanGenericTypeParamType type, Type replacement); - void addConformance(CanType type, ProtocolConformanceRef conformance); + Type operator()(SubstitutableType *type) const; +}; + +/// Functor class suitable for use as a \c LookupConformanceFn to look up a +/// conformance in a \c SubstitutionMap. +class LookUpConformanceInSubstitutionMap { + SubstitutionMap Subs; +public: + explicit LookUpConformanceInSubstitutionMap(SubstitutionMap Subs) + : Subs(Subs) {} + + Optional + operator()(CanType dependentType, + Type conformingReplacementType, + ProtocolDecl *conformedProtocol) const; }; } // end namespace swift +namespace llvm { + template <> + struct PointerLikeTypeTraits { + static void *getAsVoidPointer(swift::SubstitutionMap map) { + return const_cast(map.getOpaqueValue()); + } + static swift::SubstitutionMap getFromVoidPointer(const void *ptr) { + return swift::SubstitutionMap::getFromOpaqueValue(ptr); + } + + /// Note: Assuming storage is at leaste 4-byte aligned. + enum { NumLowBitsAvailable = 2 }; + }; + + // Substitution maps hash just like pointers. + template<> struct DenseMapInfo { + static swift::SubstitutionMap getEmptyKey() { + return swift::SubstitutionMap::getEmptyKey(); + } + static swift::SubstitutionMap getTombstoneKey() { + return swift::SubstitutionMap::getTombstoneKey(); + } + static unsigned getHashValue(swift::SubstitutionMap map) { + return DenseMapInfo::getHashValue(map.getOpaqueValue()); + } + static bool isEqual(swift::SubstitutionMap lhs, + swift::SubstitutionMap rhs) { + return lhs.getOpaqueValue() == rhs.getOpaqueValue(); + } + }; + +} + #endif diff --git a/include/swift/AST/Type.h b/include/swift/AST/Type.h index 349b58392b5f5..60e6fe7cf00bd 100644 --- a/include/swift/AST/Type.h +++ b/include/swift/AST/Type.h @@ -88,19 +88,11 @@ struct QueryTypeSubstitutionMapOrIdentity { Type operator()(SubstitutableType *type) const; }; -/// A function object suitable for use as a \c TypeSubstitutionFn that -/// queries an underlying \c SubstitutionMap. -struct QuerySubstitutionMap { - const SubstitutionMap &subMap; - - Type operator()(SubstitutableType *type) const; -}; - /// Function used to resolve conformances. using GenericFunction = auto(CanType dependentType, - Type conformingReplacementType, - ProtocolType *conformedProtocol) - ->Optional; + Type conformingReplacementType, + ProtocolDecl *conformedProtocol) + -> Optional; using LookupConformanceFn = llvm::function_ref; /// Functor class suitable for use as a \c LookupConformanceFn to look up a @@ -114,21 +106,7 @@ class LookUpConformanceInModule { Optional operator()(CanType dependentType, Type conformingReplacementType, - ProtocolType *conformedProtocol) const; -}; - -/// Functor class suitable for use as a \c LookupConformanceFn to look up a -/// conformance in a \c SubstitutionMap. -class LookUpConformanceInSubstitutionMap { - const SubstitutionMap &Subs; -public: - explicit LookUpConformanceInSubstitutionMap(const SubstitutionMap &Subs) - : Subs(Subs) {} - - Optional - operator()(CanType dependentType, - Type conformingReplacementType, - ProtocolType *conformedProtocol) const; + ProtocolDecl *conformedProtocol) const; }; /// Functor class suitable for use as a \c LookupConformanceFn that provides @@ -139,7 +117,7 @@ class MakeAbstractConformanceForGenericType { Optional operator()(CanType dependentType, Type conformingReplacementType, - ProtocolType *conformedProtocol) const; + ProtocolDecl *conformedProtocol) const; }; /// Functor class suitable for use as a \c LookupConformanceFn that fetches @@ -153,7 +131,7 @@ class LookUpConformanceInSignature { Optional operator()(CanType dependentType, Type conformingReplacementType, - ProtocolType *conformedProtocol) const; + ProtocolDecl *conformedProtocol) const; }; /// Flags that can be passed when substituting into a type. @@ -314,7 +292,7 @@ class Type { /// \param options Options that affect the substitutions. /// /// \returns the substituted type, or a null type if an error occurred. - Type subst(const SubstitutionMap &substitutions, + Type subst(SubstitutionMap substitutions, SubstOptions options = None) const; /// Replace references to substitutable types with new, concrete types and @@ -419,6 +397,12 @@ class CanType : public Type { }); } + bool findIf(llvm::function_ref fn) const { + return Type::findIf([&fn](Type t) { + return fn(CanType(t)); + }); + } + // Provide a few optimized accessors that are really type-class queries. /// Do values of this type have reference semantics? diff --git a/include/swift/AST/TypeAlignments.h b/include/swift/AST/TypeAlignments.h index f4c4b78777393..f360a10befd34 100644 --- a/include/swift/AST/TypeAlignments.h +++ b/include/swift/AST/TypeAlignments.h @@ -45,7 +45,6 @@ namespace swift { class ProtocolConformance; class SILFunction; class Stmt; - class Substitution; class TypeVariableType; class TypeBase; class TypeDecl; diff --git a/include/swift/AST/TypeLoc.h b/include/swift/AST/TypeLoc.h index 42601aa3d2797..cd231b56caeac 100644 --- a/include/swift/AST/TypeLoc.h +++ b/include/swift/AST/TypeLoc.h @@ -65,9 +65,7 @@ struct TypeLoc { bool isNull() const { return getType().isNull() && TyR == nullptr; } void setInvalidType(ASTContext &C); - void setType(Type Ty, bool validated = false) { - TAndValidBit.setPointerAndInt(Ty, validated); - } + void setType(Type Ty, bool validated = false); TypeLoc clone(ASTContext &ctx) const; }; diff --git a/include/swift/AST/TypeMatcher.h b/include/swift/AST/TypeMatcher.h index 34cedc0e259fc..8f092c572bafe 100644 --- a/include/swift/AST/TypeMatcher.h +++ b/include/swift/AST/TypeMatcher.h @@ -141,43 +141,25 @@ class TypeMatcher { return mismatch(firstTuple.getPointer(), secondType, sugaredFirstType); } - template - bool handleReferenceStorageType( - CanTypeWrapper firstStorage, - Type secondType, Type sugaredFirstType) { - if (auto secondStorage = secondType->getAs()) { - return this->visit( - firstStorage.getReferentType(), - secondStorage->getReferentType(), - sugaredFirstType->getAs() - ->getReferentType()); + bool visitReferenceStorageType(CanReferenceStorageType firstStorage, + Type secondType, Type sugaredFirstType) { + auto _secondStorage = secondType->getCanonicalType(); + if (firstStorage->getKind() == _secondStorage->getKind()) { + auto secondStorage = cast(_secondStorage); + return this->visit(firstStorage.getReferentType(), + secondStorage->getReferentType(), + sugaredFirstType->castTo() + ->getReferentType()); } return mismatch(firstStorage.getPointer(), secondType, sugaredFirstType); } - bool visitUnownedStorageType(CanUnownedStorageType firstStorage, - Type secondType, Type sugaredFirstType) { - return handleReferenceStorageType(firstStorage, secondType, - sugaredFirstType); - } - - bool visitUnmanagedStorageType(CanUnmanagedStorageType firstStorage, - Type secondType, Type sugaredFirstType) { - return handleReferenceStorageType(firstStorage, secondType, - sugaredFirstType); - } - - bool visitWeakStorageType(CanWeakStorageType firstStorage, - Type secondType, Type sugaredFirstType) { - return handleReferenceStorageType(firstStorage, secondType, - sugaredFirstType); - } - - template - bool handleNominalType(CanTypeWrapper firstNominal, - Type secondType, Type sugaredFirstType) { - if (auto secondNominal = secondType->getAs()) { + bool visitNominalType(CanNominalType firstNominal, + Type secondType, Type sugaredFirstType) { + auto _secondNominal = secondType->getCanonicalType(); + if (firstNominal->getKind() == _secondNominal->getKind()) { + auto secondNominal = cast(_secondNominal); if (firstNominal->getDecl() != secondNominal->getDecl()) return mismatch(firstNominal.getPointer(), secondNominal, sugaredFirstType); @@ -185,7 +167,7 @@ class TypeMatcher { if (firstNominal.getParent()) return this->visit(firstNominal.getParent(), secondNominal->getParent(), - sugaredFirstType->castTo() + sugaredFirstType->castTo() ->getParent()); return true; @@ -194,52 +176,20 @@ class TypeMatcher { return mismatch(firstNominal.getPointer(), secondType, sugaredFirstType); } - bool visitEnumType(CanEnumType firstEnum, Type secondType, - Type sugaredFirstType) { - return handleNominalType(firstEnum, secondType, sugaredFirstType); - } - - bool visitStructType(CanStructType firstStruct, Type secondType, - Type sugaredFirstType) { - return handleNominalType(firstStruct, secondType, sugaredFirstType); - } - - bool visitClassType(CanClassType firstClass, Type secondType, - Type sugaredFirstType) { - return handleNominalType(firstClass, secondType, sugaredFirstType); - } - - bool visitProtocolType(CanProtocolType firstProtocol, Type secondType, - Type sugaredFirstType) { - return handleNominalType(firstProtocol, secondType, sugaredFirstType); - } - - template - bool handleAnyMetatypeType(CanTypeWrapper firstMeta, - Type secondType, Type sugaredFirstType) { - if (auto secondMeta = secondType->getAs()) { - if (firstMeta->getKind() != secondMeta->getKind()) - return mismatch(firstMeta.getPointer(), secondMeta, sugaredFirstType); - + bool visitAnyMetatypeType(CanAnyMetatypeType firstMeta, + Type secondType, Type sugaredFirstType) { + auto _secondMeta = secondType->getCanonicalType(); + if (firstMeta->getKind() == _secondMeta->getKind()) { + auto secondMeta = cast(_secondMeta); return this->visit(firstMeta.getInstanceType(), secondMeta->getInstanceType(), - sugaredFirstType->castTo() + sugaredFirstType->castTo() ->getInstanceType()); } return mismatch(firstMeta.getPointer(), secondType, sugaredFirstType); } - bool visitMetatypeType(CanMetatypeType firstMeta, Type secondType, - Type sugaredFirstType) { - return handleAnyMetatypeType(firstMeta, secondType, sugaredFirstType); - } - - bool visitExistentialMetatypeType(CanExistentialMetatypeType firstMeta, - Type secondType, Type sugaredFirstType) { - return handleAnyMetatypeType(firstMeta, secondType, sugaredFirstType); - } - TRIVIAL_CASE(ModuleType) TRIVIAL_CASE(DynamicSelfType) TRIVIAL_CASE(ArchetypeType) @@ -332,15 +282,15 @@ class TypeMatcher { return mismatch(firstUBGT.getPointer(), secondType, sugaredFirstType); } - template - bool handleBoundGenericType(CanTypeWrapper firstBGT, - Type secondType, Type sugaredFirstType) { - if (auto secondBGT = secondType->getAs()) { + bool visitBoundGenericType(CanBoundGenericType firstBGT, + Type secondType, Type sugaredFirstType) { + auto _secondBGT = secondType->getCanonicalType(); + if (firstBGT->getKind() == _secondBGT->getKind()) { + auto secondBGT = cast(_secondBGT); if (firstBGT->getDecl() != secondBGT->getDecl()) return mismatch(firstBGT.getPointer(), secondBGT, sugaredFirstType); - auto sugaredFirstBGT - = sugaredFirstType->castTo(); + auto sugaredFirstBGT = sugaredFirstType->castTo(); if (firstBGT->getParent() && !this->visit(firstBGT.getParent(), secondBGT->getParent(), sugaredFirstBGT->getParent())) @@ -360,21 +310,6 @@ class TypeMatcher { return mismatch(firstBGT.getPointer(), secondType, sugaredFirstType); } - bool visitBoundGenericClassType(CanBoundGenericClassType firstBGT, - Type secondType, Type sugaredFirstType) { - return handleBoundGenericType(firstBGT, secondType, sugaredFirstType); - } - - bool visitBoundGenericEnumType(CanBoundGenericEnumType firstBGT, - Type secondType, Type sugaredFirstType) { - return handleBoundGenericType(firstBGT, secondType, sugaredFirstType); - } - - bool visitBoundGenericStructType(CanBoundGenericStructType firstBGT, - Type secondType, Type sugaredFirstType) { - return handleBoundGenericType(firstBGT, secondType, sugaredFirstType); - } - TRIVIAL_CASE(TypeVariableType) #undef TRIVIAL_CASE diff --git a/include/swift/AST/TypeRepr.h b/include/swift/AST/TypeRepr.h index b731336fe6446..49465cbbf00a4 100644 --- a/include/swift/AST/TypeRepr.h +++ b/include/swift/AST/TypeRepr.h @@ -92,6 +92,11 @@ class alignas(8) TypeRepr { NumTypes : 32 ); + SWIFT_INLINE_BITFIELD_FULL(SILBoxTypeRepr, TypeRepr, 32, + NumGenericArgs : NumPadBits, + NumFields : 32 + ); + } Bits; TypeRepr(TypeReprKind K) { @@ -996,29 +1001,44 @@ class FixedTypeRepr : public TypeRepr { friend class TypeRepr; }; +class SILBoxTypeReprField { + SourceLoc VarOrLetLoc; + llvm::PointerIntPair FieldTypeAndMutable; + +public: + SILBoxTypeReprField(SourceLoc loc, bool isMutable, TypeRepr *fieldType) + : VarOrLetLoc(loc), FieldTypeAndMutable(fieldType, isMutable) { + } + + SourceLoc getLoc() const { return VarOrLetLoc; } + TypeRepr *getFieldType() const { return FieldTypeAndMutable.getPointer(); } + bool isMutable() const { return FieldTypeAndMutable.getInt(); } +}; + /// SIL-only TypeRepr for box types. /// /// Boxes are either concrete: { var Int, let String } /// or generic: { var T, let String } -class SILBoxTypeRepr : public TypeRepr { +class SILBoxTypeRepr final : public TypeRepr, + private llvm::TrailingObjects { + friend TrailingObjects; GenericParamList *GenericParams; GenericEnvironment *GenericEnv = nullptr; SourceLoc LBraceLoc, RBraceLoc; SourceLoc ArgLAngleLoc, ArgRAngleLoc; + + size_t numTrailingObjects(OverloadToken) const { + return Bits.SILBoxTypeRepr.NumFields; + } + size_t numTrailingObjects(OverloadToken) const { + return Bits.SILBoxTypeRepr.NumGenericArgs; + } public: - struct Field { - SourceLoc VarOrLetLoc; - bool Mutable; - TypeRepr *FieldType; - }; - -private: - ArrayRef Fields; - ArrayRef GenericArgs; - -public: + using Field = SILBoxTypeReprField; + SILBoxTypeRepr(GenericParamList *GenericParams, SourceLoc LBraceLoc, ArrayRef Fields, SourceLoc RBraceLoc, @@ -1026,9 +1046,17 @@ class SILBoxTypeRepr : public TypeRepr { SourceLoc ArgRAngleLoc) : TypeRepr(TypeReprKind::SILBox), GenericParams(GenericParams), LBraceLoc(LBraceLoc), RBraceLoc(RBraceLoc), - ArgLAngleLoc(ArgLAngleLoc), ArgRAngleLoc(ArgRAngleLoc), - Fields(Fields), GenericArgs(GenericArgs) - {} + ArgLAngleLoc(ArgLAngleLoc), ArgRAngleLoc(ArgRAngleLoc) + { + Bits.SILBoxTypeRepr.NumFields = Fields.size(); + Bits.SILBoxTypeRepr.NumGenericArgs = GenericArgs.size(); + + std::uninitialized_copy(Fields.begin(), Fields.end(), + getTrailingObjects()); + + std::uninitialized_copy(GenericArgs.begin(), GenericArgs.end(), + getTrailingObjects()); + } static SILBoxTypeRepr *create(ASTContext &C, GenericParamList *GenericParams, @@ -1043,10 +1071,12 @@ class SILBoxTypeRepr : public TypeRepr { } ArrayRef getFields() const { - return Fields; + return {getTrailingObjects(), + Bits.SILBoxTypeRepr.NumFields}; } ArrayRef getGenericArguments() const { - return GenericArgs; + return {getTrailingObjects(), + Bits.SILBoxTypeRepr.NumGenericArgs}; } GenericParamList *getGenericParams() const { @@ -1075,6 +1105,7 @@ class SILBoxTypeRepr : public TypeRepr { }; inline bool TypeRepr::isSimple() const { + // NOTE: Please keep this logic in sync with TypeBase::hasSimpleTypeRepr(). switch (getKind()) { case TypeReprKind::Attributed: case TypeReprKind::Error: diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index 61fe2730106be..f14fbd2af7198 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -24,7 +24,7 @@ #include "swift/AST/ProtocolConformanceRef.h" #include "swift/AST/Requirement.h" #include "swift/AST/SILLayout.h" -#include "swift/AST/SubstitutionList.h" +#include "swift/AST/SubstitutionMap.h" #include "swift/AST/Type.h" #include "swift/AST/TypeAlignments.h" #include "swift/Basic/ArrayRefView.h" @@ -254,10 +254,14 @@ class alignas(1 << TypeAlignInBits) TypeBase { TypeBase(const TypeBase&) = delete; void operator=(const TypeBase&) = delete; - /// CanonicalType - This field is always set to the ASTContext for canonical - /// types, and is otherwise lazily populated by ASTContext when the canonical - /// form of a non-canonical type is requested. - llvm::PointerUnion CanonicalType; + /// This union contains to the ASTContext for canonical types, and is + /// otherwise lazily populated by ASTContext when the canonical form of a + /// non-canonical type is requested. The disposition of the union is stored + /// outside of the union for performance. See Bits.TypeBase.IsCanonical. + union { + CanType CanonicalType; + const ASTContext *Context; + }; /// Returns true if the given type is a sugared type. /// @@ -274,11 +278,14 @@ class alignas(1 << TypeAlignInBits) TypeBase { union { uint64_t OpaqueBits; SWIFT_INLINE_BITFIELD_BASE(TypeBase, bitmax(NumTypeKindBits,8) + - RecursiveTypeProperties::BitWidth, + RecursiveTypeProperties::BitWidth + 1, /// Kind - The discriminator that indicates what subclass of type this is. Kind : bitmax(NumTypeKindBits,8), - Properties : RecursiveTypeProperties::BitWidth + Properties : RecursiveTypeProperties::BitWidth, + + /// Whether this type is canonical or not. + IsCanonical : 1 ); SWIFT_INLINE_BITFIELD(ErrorType, TypeBase, 1, @@ -332,11 +339,6 @@ class alignas(1 << TypeAlignInBits) TypeBase { CoroutineKind : 2 ); - SWIFT_INLINE_BITFIELD_FULL(SILBoxType, TypeBase, 32, - : NumPadBits, - NumGenericArgs : 32 - ); - SWIFT_INLINE_BITFIELD(AnyMetatypeType, TypeBase, 2, /// The representation of the metatype. /// @@ -373,14 +375,14 @@ class alignas(1 << TypeAlignInBits) TypeBase { GenericArgCount : 32 ); - SWIFT_INLINE_BITFIELD_FULL(NameAliasType, SugarType, 1+16, + SWIFT_INLINE_BITFIELD_FULL(NameAliasType, SugarType, 1+1, : NumPadBits, /// Whether we have a parent type. HasParent : 1, - /// The number of substitutions. - NumSubstitutions : 16 + /// Whether we have a substitution map. + HasSubstitutionMap : 1 ); } Bits; @@ -388,12 +390,15 @@ class alignas(1 << TypeAlignInBits) TypeBase { protected: TypeBase(TypeKind kind, const ASTContext *CanTypeCtx, RecursiveTypeProperties properties) - : CanonicalType((TypeBase*)nullptr) { + : Context(nullptr) { Bits.OpaqueBits = 0; Bits.TypeBase.Kind = static_cast(kind); + Bits.TypeBase.IsCanonical = false; // If this type is canonical, switch the CanonicalType union to ASTContext. - if (CanTypeCtx) - CanonicalType = CanTypeCtx; + if (CanTypeCtx) { + Bits.TypeBase.IsCanonical = true; + Context = CanTypeCtx; + } setRecursiveProperties(properties); } @@ -407,7 +412,7 @@ class alignas(1 << TypeAlignInBits) TypeBase { TypeKind getKind() const { return static_cast(Bits.TypeBase.Kind); } /// isCanonical - Return true if this is a canonical type. - bool isCanonical() const { return CanonicalType.is(); } + bool isCanonical() const { return Bits.TypeBase.IsCanonical; } /// hasCanonicalTypeComputed - Return true if we've already computed a /// canonical version of this type. @@ -419,12 +424,12 @@ class alignas(1 << TypeAlignInBits) TypeBase { public: /// getCanonicalType - Return the canonical version of this type, which has /// sugar from all levels stripped off. - CanType getCanonicalType() { + CanType getCanonicalType() const { if (isCanonical()) - return CanType(this); + return CanType(const_cast(this)); if (hasCanonicalTypeComputed()) - return CanType(CanonicalType.get()); - return computeCanonicalType(); + return CanonicalType; + return const_cast(this)->computeCanonicalType(); } /// getCanonicalType - Stronger canonicalization which folds away equivalent @@ -438,11 +443,10 @@ class alignas(1 << TypeAlignInBits) TypeBase { /// getASTContext - Return the ASTContext that this type belongs to. ASTContext &getASTContext() { // If this type is canonical, it has the ASTContext in it. - if (CanonicalType.is()) - return const_cast(*CanonicalType.get()); + if (isCanonical()) + return *const_cast(Context); // If not, canonicalize it to get the Context. - return const_cast(*getCanonicalType()-> - CanonicalType.get()); + return *const_cast(getCanonicalType()->Context); } /// isEqual - Return true if these two types are equal, ignoring sugar. @@ -546,7 +550,7 @@ class alignas(1 << TypeAlignInBits) TypeBase { /// /// To determine whether there is an opened existential type /// anywhere in the type, use \c hasOpenedExistential. - bool isOpenedExistential(); + bool isOpenedExistential() const; /// Determine whether the type is an opened existential type with Error inside bool isOpenedExistentialWithError(); @@ -669,8 +673,9 @@ class alignas(1 << TypeAlignInBits) TypeBase { /// Break an existential down into a set of constraints. ExistentialLayout getExistentialLayout(); - /// Determines the element type of a known *UnsafeMutablePointer - /// variant, or returns null if the type is not a pointer. + /// Determines the element type of a known + /// [Autoreleasing]Unsafe[Mutable][Raw]Pointer variant, or returns null if the + /// type is not a pointer. Type getAnyPointerElementType(PointerTypeKind &PTK); Type getAnyPointerElementType() { PointerTypeKind Ignore; @@ -793,6 +798,23 @@ class alignas(1 << TypeAlignInBits) TypeBase { /// `A`. Type getSuperclassForDecl(const ClassDecl *classDecl); + /// \brief Whether this type or its superclasses has some form of generic + /// context. + /// + /// For example, given + /// + /// class A {} + /// class B : A {} + /// struct C { + /// struct Inner {} + /// } + /// class D {} + /// class E: D {} + /// + /// Calling hasGenericAncestry() on `B` returns `A`, on `C.Inner` + /// returns `C.Inner`, but on `E` it returns null. + Type getGenericAncestor(); + /// \brief True if this type is the superclass of another type, or a generic /// type that could be bound to the superclass. /// @@ -818,7 +840,7 @@ class alignas(1 << TypeAlignInBits) TypeBase { /// paramsAndResultMatch which determines in a client-specific way /// whether the parameters and result of the types match. bool matchesFunctionType(Type other, TypeMatchOptions matchOptions, - std::function paramsAndResultMatch); + llvm::function_ref paramsAndResultMatch); /// \brief Determines whether this type has a retainable pointer /// representation, i.e. whether it is representable as a single, @@ -1055,6 +1077,9 @@ class alignas(1 << TypeAlignInBits) TypeBase { const PrintOptions &PO = PrintOptions()) const; void print(ASTPrinter &Printer, const PrintOptions &PO) const; + /// Does this type have grammatically simple syntax? + bool hasSimpleTypeRepr() const; + /// Return the name of the type as a string, for use in diagnostics only. std::string getString(const PrintOptions &PO = PrintOptions()) const; @@ -1572,8 +1597,7 @@ class SugarType : public TypeBase { /// set of substitutions to apply to make the type concrete. class NameAliasType final : public SugarType, public llvm::FoldingSetNode, - llvm::TrailingObjects + llvm::TrailingObjects { TypeAliasDecl *typealias; @@ -1581,41 +1605,25 @@ class NameAliasType final friend TrailingObjects; NameAliasType(TypeAliasDecl *typealias, Type parent, - const SubstitutionMap &substitutions, Type underlying, - RecursiveTypeProperties properties); - - unsigned getNumSubstitutions() const { - return Bits.NameAliasType.NumSubstitutions; - } + SubstitutionMap substitutions, Type underlying, + RecursiveTypeProperties properties); size_t numTrailingObjects(OverloadToken) const { return Bits.NameAliasType.HasParent ? 1 : 0; } - size_t numTrailingObjects(OverloadToken) const { - return getNumSubstitutions() > 0 ? 1 : 0; - } - - size_t numTrailingObjects(OverloadToken) const { - return getNumSubstitutions(); - } - - /// Retrieve the set of substitutions to be applied to the declaration to - /// produce the underlying type. - SubstitutionList getSubstitutionList() const { - return {getTrailingObjects(), getNumSubstitutions()}; + size_t numTrailingObjects(OverloadToken) const { + return Bits.NameAliasType.HasSubstitutionMap ? 1 : 0; } /// Retrieve the generic signature used for substitutions. GenericSignature *getGenericSignature() const { - return getNumSubstitutions() > 0 - ? *getTrailingObjects() - : nullptr; + return getSubstitutionMap().getGenericSignature(); } public: static NameAliasType *get(TypeAliasDecl *typealias, Type parent, - const SubstitutionMap &substitutions, + SubstitutionMap substitutions, Type underlying); /// \brief Returns the declaration that declares this type. @@ -1628,12 +1636,17 @@ class NameAliasType final /// written before ".", if provided. Type getParent() const { return Bits.NameAliasType.HasParent ? *getTrailingObjects() - : Type(); + : Type(); } /// Retrieve the substitution map applied to the declaration's underlying /// to produce the described type. - SubstitutionMap getSubstitutionMap() const; + SubstitutionMap getSubstitutionMap() const { + if (!Bits.NameAliasType.HasSubstitutionMap) + return SubstitutionMap(); + + return *getTrailingObjects(); + } /// Get the innermost generic arguments, which correspond to the generic /// arguments that are directly applied to the typealias declaration in @@ -1647,7 +1660,7 @@ class NameAliasType final void Profile(llvm::FoldingSetNodeID &id) const; static void Profile(llvm::FoldingSetNodeID &id, TypeAliasDecl *typealias, - Type parent, const SubstitutionMap &substitutions, + Type parent, SubstitutionMap substitutions, Type underlying); // Implement isa/cast/dyncast/etc. @@ -1746,6 +1759,10 @@ class ParameterTypeFlags { return value.toRaw() == other.value.toRaw(); } + bool operator!=(const ParameterTypeFlags &other) const { + return value.toRaw() != other.value.toRaw(); + } + uint8_t toRaw() const { return value.toRaw(); } }; @@ -2634,6 +2651,14 @@ class AnyFunctionType : public TypeBase { /// Whether the parameter is marked 'owned' bool isOwned() const { return Flags.isOwned(); } + + bool operator==(Param const &b) const { + return Label == b.Label && getType()->isEqual(b.getType()) && + Flags == b.Flags; + } + bool operator!=(Param const &b) const { return !(*this == b); } + + Param getWithoutLabel() const { return Param(Ty, Identifier(), Flags); } }; class CanParam : public Param { @@ -2821,6 +2846,10 @@ class AnyFunctionType : public TypeBase { return composeInput(ctx, params.getOriginalArray(), canonicalVararg); } + /// \brief Given two arrays of parameters determine if they are equal. + static bool equalParams(ArrayRef a, + ArrayRef b); + Type getInput() const { return Input; } Type getResult() const { return Output; } ArrayRef getParams() const; @@ -2965,7 +2994,8 @@ decomposeArgType(Type type, ArrayRef argumentLabels); /// Break the parameter list into an array of booleans describing whether /// the argument type at each index has a default argument associated with /// it. -void computeDefaultMap(Type type, const ValueDecl *paramOwner, unsigned level, +void computeDefaultMap(ArrayRef params, + const ValueDecl *paramOwner, unsigned level, SmallVectorImpl &outDefaultMap); /// Turn a param list into a symbolic and printable representation that does not @@ -3027,17 +3057,7 @@ class GenericFunctionType final : public AnyFunctionType, /// Substitute the given generic arguments into this generic /// function type and return the resulting non-generic type. - FunctionType *substGenericArgs(SubstitutionList subs); - - /// Substitute the given generic arguments into this generic - /// function type and return the resulting non-generic type. - FunctionType *substGenericArgs(const SubstitutionMap &subs); - - /// Substitute the given generic arguments into this generic - /// function type using the given substitution and conformance lookup - /// callbacks. - FunctionType *substGenericArgs(TypeSubstitutionFn subs, - LookupConformanceFn conformances); + FunctionType *substGenericArgs(SubstitutionMap subs); void Profile(llvm::FoldingSetNodeID &ID) { Profile(ID, getGenericSignature(), getInput(), getResult(), @@ -4007,9 +4027,7 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode, isABICompatibleWith(CanSILFunctionType other) const; CanSILFunctionType substGenericArgs(SILModule &silModule, - SubstitutionList subs); - CanSILFunctionType substGenericArgs(SILModule &silModule, - const SubstitutionMap &subs); + SubstitutionMap subs); CanSILFunctionType substGenericArgs(SILModule &silModule, TypeSubstitutionFn subs, LookupConformanceFn conformances); @@ -4043,31 +4061,22 @@ typedef CanTypeWrapper CanSILBoxType; /// The SIL-only type for boxes, which represent a reference to a (non-class) /// refcounted value referencing an aggregate with a given lowered layout. -class SILBoxType final : public TypeBase, - public llvm::FoldingSetNode, - private llvm::TrailingObjects +class SILBoxType final : public TypeBase, public llvm::FoldingSetNode { - friend TrailingObjects; - SILLayout *Layout; - - static RecursiveTypeProperties - getRecursivePropertiesFromSubstitutions(SubstitutionList Args); + SubstitutionMap Substitutions; SILBoxType(ASTContext &C, - SILLayout *Layout, SubstitutionList Args); + SILLayout *Layout, SubstitutionMap Substitutions); public: static CanSILBoxType get(ASTContext &C, SILLayout *Layout, - SubstitutionList Args); + SubstitutionMap Substitutions); SILLayout *getLayout() const { return Layout; } - SubstitutionList getGenericArgs() const { - return llvm::makeArrayRef(getTrailingObjects(), - Bits.SILBoxType.NumGenericArgs); - } - + SubstitutionMap getSubstitutions() const { return Substitutions; } + // In SILType.h: CanType getFieldLoweredType(SILModule &M, unsigned index) const; SILType getFieldType(SILModule &M, unsigned index) const; @@ -4085,11 +4094,11 @@ class SILBoxType final : public TypeBase, /// Produce a profile of this box, for use in a folding set. static void Profile(llvm::FoldingSetNodeID &id, SILLayout *Layout, - SubstitutionList Args); + SubstitutionMap Args); /// \brief Produce a profile of this box, for use in a folding set. void Profile(llvm::FoldingSetNodeID &id) { - Profile(id, getLayout(), getGenericArgs()); + Profile(id, getLayout(), getSubstitutions()); } }; DEFINE_EMPTY_CAN_TYPE_WRAPPER(SILBoxType, Type) @@ -4288,11 +4297,6 @@ class ProtocolType : public NominalType, public llvm::FoldingSetNode { static bool visitAllProtocols(ArrayRef protocols, llvm::function_ref fn); - /// Compare two protocols to provide them with a stable ordering for - /// use in sorting. - static int compareProtocols(ProtocolDecl * const* PP1, - ProtocolDecl * const* PP2); - void Profile(llvm::FoldingSetNodeID &ID) { Profile(ID, getDecl(), getParent()); } @@ -4363,7 +4367,7 @@ class ProtocolCompositionType final : public TypeBase, bool requiresClass(); /// True if the class requirement is stated directly via '& AnyObject'. - bool hasExplicitAnyObject() { + bool hasExplicitAnyObject() const { return Bits.ProtocolCompositionType.HasExplicitAnyObject; } @@ -5096,7 +5100,7 @@ inline bool TypeBase::isClassExistentialType() { return false; } -inline bool TypeBase::isOpenedExistential() { +inline bool TypeBase::isOpenedExistential() const { if (!hasOpenedExistential()) return false; @@ -5255,7 +5259,13 @@ inline CanType CanType::getWithoutSpecifierTypeImpl(CanType type) { inline CanType CanType::getNominalParent() const { return cast(*this).getParent(); } - + +inline bool CanType::isActuallyCanonicalOrNull() const { + return getPointer() == nullptr || + getPointer() == llvm::DenseMapInfo::getTombstoneKey() || + getPointer()->isCanonical(); +} + inline Type TupleTypeElt::getVarargBaseTy() const { TypeBase *T = getType().getPointer(); if (auto *AT = dyn_cast(T)) @@ -5352,6 +5362,34 @@ inline TypeBase *TypeBase::getDesugaredType() { return cast(this)->getSinglyDesugaredType()->getDesugaredType(); } +inline bool TypeBase::hasSimpleTypeRepr() const { + // NOTE: Please keep this logic in sync with TypeRepr::isSimple(). + switch (getKind()) { + case TypeKind::Function: + case TypeKind::GenericFunction: + return false; + + case TypeKind::Metatype: + case TypeKind::ExistentialMetatype: + return !cast(this)->hasRepresentation(); + + case TypeKind::Archetype: + return !cast(this)->isOpenedExistential(); + + case TypeKind::ProtocolComposition: { + // 'Any', 'AnyObject' and single protocol compositions are simple + auto composition = cast(this); + auto memberCount = composition->getMembers().size(); + if (composition->hasExplicitAnyObject()) + return memberCount == 0; + return memberCount <= 1; + } + + default: + return true; + } +} + } // end namespace swift namespace llvm { @@ -5378,7 +5416,6 @@ struct DenseMapInfo { } }; - } #endif diff --git a/include/swift/AST/Witness.h b/include/swift/AST/Witness.h index 02b4a1dc5a72c..302ad86e38fd7 100644 --- a/include/swift/AST/Witness.h +++ b/include/swift/AST/Witness.h @@ -19,7 +19,6 @@ #define SWIFT_AST_WITNESS_H #include "swift/AST/ConcreteDeclRef.h" -#include "swift/AST/SubstitutionList.h" #include "llvm/ADT/PointerUnion.h" #include "llvm/Support/Compiler.h" @@ -93,7 +92,7 @@ class Witness { /// the witness declaration from the synthetic environment. ConcreteDeclRef declRef; GenericEnvironment *syntheticEnvironment; - SubstitutionList reqToSyntheticEnvSubs; + SubstitutionMap reqToSyntheticEnvSubs; }; llvm::PointerUnion storage; @@ -130,9 +129,9 @@ class Witness { /// \param reqToSyntheticEnvSubs The mapping from the interface types of the /// requirement into the interface types of the synthetic environment. Witness(ValueDecl *decl, - SubstitutionList substitutions, + SubstitutionMap substitutions, GenericEnvironment *syntheticEnv, - SubstitutionList reqToSyntheticEnvSubs); + SubstitutionMap reqToSyntheticEnvSubs); /// Retrieve the witness declaration reference, which includes the /// substitutions needed to use the witness from the synthetic environment @@ -150,29 +149,28 @@ class Witness { /// Determines whether there is a witness at all. explicit operator bool() const { return !storage.isNull(); } - /// Determine whether this witness requires any substitutions. - bool requiresSubstitution() const { return storage.is(); } - /// Retrieve the substitutions required to use this witness from the /// synthetic environment. /// /// The substitutions are substitutions for the witness, providing interface /// types from the synthetic environment. - SubstitutionList getSubstitutions() const { + SubstitutionMap getSubstitutions() const { return getDeclRef().getSubstitutions(); } /// Retrieve the synthetic generic environment. GenericEnvironment *getSyntheticEnvironment() const { - assert(requiresSubstitution() && "No substitutions required for witness"); - return storage.get()->syntheticEnvironment; + if (auto *storedWitness = storage.dyn_cast()) + return storedWitness->syntheticEnvironment; + return nullptr; } /// Retrieve the substitution map that maps the interface types of the /// requirement to the interface types of the synthetic environment. - SubstitutionList getRequirementToSyntheticSubs() const { - assert(requiresSubstitution() && "No substitutions required for witness"); - return storage.get()->reqToSyntheticEnvSubs; + SubstitutionMap getRequirementToSyntheticSubs() const { + if (auto *storedWitness = storage.dyn_cast()) + return storedWitness->reqToSyntheticEnvSubs; + return {}; } LLVM_ATTRIBUTE_DEPRECATED( diff --git a/include/swift/Basic/LLVM.h b/include/swift/Basic/LLVM.h index 4d3f26548d249..7411d4bffd4fe 100644 --- a/include/swift/Basic/LLVM.h +++ b/include/swift/Basic/LLVM.h @@ -30,6 +30,7 @@ namespace llvm { // Containers. class StringRef; + class StringLiteral; class Twine; template class SmallPtrSetImpl; template class SmallPtrSet; @@ -64,6 +65,7 @@ namespace swift { using llvm::SmallPtrSet; using llvm::SmallString; using llvm::StringRef; + using llvm::StringLiteral; using llvm::Twine; using llvm::SmallVectorImpl; using llvm::SmallVector; diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index affb6c0d42619..2f565910ac9b3 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -258,7 +258,9 @@ namespace swift { /// Whether collect tokens during parsing for syntax coloring. bool CollectParsedToken = false; - /// Whether to parse syntax tree. + /// Whether to parse syntax tree. If the syntax tree is built, the generated + /// AST may not be correct when syntax nodes are reused as part of + /// incrementals parsing. bool BuildSyntaxTree = false; /// Whether to verify the parsed syntax tree and emit related diagnostics. diff --git a/include/swift/Basic/Lazy.h b/include/swift/Basic/Lazy.h index 7bf049eacb36f..2cf270490a12d 100644 --- a/include/swift/Basic/Lazy.h +++ b/include/swift/Basic/Lazy.h @@ -45,9 +45,9 @@ namespace swift { /// A template for lazily-constructed, zero-initialized, leaked-on-exit /// global objects. template class Lazy { - typename std::aligned_storage::type Value; + alignas(T) char Value[sizeof(T)] = { 0 }; - OnceToken_t OnceToken; + OnceToken_t OnceToken = {}; static void defaultInitCallback(void *ValueAddr) { ::new (ValueAddr) T(); diff --git a/include/swift/Basic/OptimizationMode.h b/include/swift/Basic/OptimizationMode.h index e57b8e6721703..9f1e6b93fc479 100644 --- a/include/swift/Basic/OptimizationMode.h +++ b/include/swift/Basic/OptimizationMode.h @@ -13,6 +13,7 @@ #ifndef SWIFT_BASIC_OPTIMIZATIONMODE_H #define SWIFT_BASIC_OPTIMIZATIONMODE_H +#include "swift/Basic/InlineBitfield.h" #include "llvm/Support/DataTypes.h" namespace swift { @@ -27,6 +28,9 @@ enum class OptimizationMode : uint8_t { LastMode = ForSize }; +enum : unsigned { NumOptimizationModeBits = + countBitsUsed(static_cast(OptimizationMode::LastMode)) }; + } // end namespace swift #endif // SWIFT_BASIC_OPTIMIZATIONMODE_H diff --git a/include/swift/Basic/SourceLoc.h b/include/swift/Basic/SourceLoc.h index 295e46c01a576..40baaeae143c8 100644 --- a/include/swift/Basic/SourceLoc.h +++ b/include/swift/Basic/SourceLoc.h @@ -68,7 +68,8 @@ class SourceLoc { void print(raw_ostream &OS, const SourceManager &SM, unsigned &LastBufferID) const; - void printLineAndColumn(raw_ostream &OS, const SourceManager &SM) const; + void printLineAndColumn(raw_ostream &OS, const SourceManager &SM, + unsigned BufferID = 0) const; void print(raw_ostream &OS, const SourceManager &SM) const { unsigned Tmp = ~0U; diff --git a/include/swift/Basic/Statistic.h b/include/swift/Basic/Statistic.h index 7f9e48b11fff3..228a13956de10 100644 --- a/include/swift/Basic/Statistic.h +++ b/include/swift/Basic/Statistic.h @@ -205,16 +205,16 @@ class FrontendStatsTracer /// entity type, and produce a tracer that's either active or inert depending /// on whether the provided \p Reporter is null (nullptr means "tracing is /// disabled"). - FrontendStatsTracer(UnifiedStatsReporter *Reporter, StringRef EventName); - FrontendStatsTracer(UnifiedStatsReporter *Reporter, StringRef EventName, + FrontendStatsTracer(UnifiedStatsReporter *Reporter, StringRef EventName); + FrontendStatsTracer(UnifiedStatsReporter *Reporter, StringRef EventName, const Decl *D); - FrontendStatsTracer(UnifiedStatsReporter *Reporter, StringRef EventName, + FrontendStatsTracer(UnifiedStatsReporter *Reporter, StringRef EventName, const ProtocolConformance *P); - FrontendStatsTracer(UnifiedStatsReporter *Reporter, StringRef EventName, + FrontendStatsTracer(UnifiedStatsReporter *Reporter, StringRef EventName, const clang::Decl *D); - FrontendStatsTracer(UnifiedStatsReporter *Reporter, StringRef EventName, + FrontendStatsTracer(UnifiedStatsReporter *Reporter, StringRef EventName, const Expr *E); - FrontendStatsTracer(UnifiedStatsReporter *Reporter, StringRef EventName, + FrontendStatsTracer(UnifiedStatsReporter *Reporter, StringRef EventName, const SILFunction *F); }; @@ -225,20 +225,61 @@ class FrontendStatsTracer // for those entity types. If you want to trace those types, it's assumed you're // linking with the object files that define the tracer. -template<> const UnifiedStatsReporter::TraceFormatter* +template <> +const UnifiedStatsReporter::TraceFormatter * FrontendStatsTracer::getTraceFormatter(); -template<> const UnifiedStatsReporter::TraceFormatter* +template <> +const UnifiedStatsReporter::TraceFormatter * FrontendStatsTracer::getTraceFormatter(); -template<> const UnifiedStatsReporter::TraceFormatter* +template <> +const UnifiedStatsReporter::TraceFormatter * FrontendStatsTracer::getTraceFormatter(); -template<> const UnifiedStatsReporter::TraceFormatter* +template <> +const UnifiedStatsReporter::TraceFormatter * FrontendStatsTracer::getTraceFormatter(); -template<> const UnifiedStatsReporter::TraceFormatter* +template <> +const UnifiedStatsReporter::TraceFormatter * FrontendStatsTracer::getTraceFormatter(); -} +// Provide inline definitions for the delegating constructors. These avoid +// introducing a circular dependency between libParse and libSIL. They are +// marked as `inline` explicitly to prevent ODR violations due to multiple +// emissions. We cannot force the inlining by defining them in the declaration +// due to the explicit template specializations of the `getTraceFormatter`, +// which is declared in the `FrontendStatsTracer` scope (the nested name +// specifier scope cannot be used to declare them). + +inline FrontendStatsTracer::FrontendStatsTracer(UnifiedStatsReporter *R, + StringRef S) + : FrontendStatsTracer(R, S, nullptr, nullptr) {} + +inline FrontendStatsTracer::FrontendStatsTracer(UnifiedStatsReporter *R, + StringRef S, const Decl *D) + : FrontendStatsTracer(R, S, D, getTraceFormatter()) {} + +inline FrontendStatsTracer::FrontendStatsTracer(UnifiedStatsReporter *R, + StringRef S, + const ProtocolConformance *P) + : FrontendStatsTracer(R, S, P, + getTraceFormatter()) {} + +inline FrontendStatsTracer::FrontendStatsTracer(UnifiedStatsReporter *R, + StringRef S, + const clang::Decl *D) + : FrontendStatsTracer(R, S, D, getTraceFormatter()) {} + +inline FrontendStatsTracer::FrontendStatsTracer(UnifiedStatsReporter *R, + StringRef S, const Expr *E) + : FrontendStatsTracer(R, S, E, getTraceFormatter()) {} + +inline FrontendStatsTracer::FrontendStatsTracer(UnifiedStatsReporter *R, + StringRef S, + const SILFunction *F) + : FrontendStatsTracer(R, S, F, getTraceFormatter()) {} + +} // namespace swift #endif // SWIFT_BASIC_STATISTIC_H diff --git a/include/swift/Basic/Statistics.def b/include/swift/Basic/Statistics.def index 36a4fe2f9230a..0a64bbedd56b7 100644 --- a/include/swift/Basic/Statistics.def +++ b/include/swift/Basic/Statistics.def @@ -41,6 +41,12 @@ DRIVER_STATISTIC(NumDriverJobsSkipped) /// EXIT_SUCCESS. DRIVER_STATISTIC(NumProcessFailures) +/// Total number of driver poll() calls on subprocess pipes. +DRIVER_STATISTIC(NumDriverPipePolls) + +/// Total number of driver read() calls on subprocess pipes. +DRIVER_STATISTIC(NumDriverPipeReads) + /// Next 10 statistics count dirtying-events in the driver's dependency graph, /// which it uses to decide which files are invalid (and thus which files to /// build). There are two dimensions to each dirtying event: @@ -143,6 +149,11 @@ FRONTEND_STATISTIC(Sema, NumConformancesDeserialized) /// expression typechecker did". FRONTEND_STATISTIC(Sema, NumConstraintScopes) +/// Number of constraints considered per attempt to +/// contract constraint graph edges. +/// This is a measure of complexity of the contraction algorithm. +FRONTEND_STATISTIC(Sema, NumConstraintsConsideredForEdgeContraction) + /// Number of declarations that were deserialized. A rough proxy for the amount /// of material loaded from other modules. FRONTEND_STATISTIC(Sema, NumDeclsDeserialized) diff --git a/include/swift/Basic/TaskQueue.h b/include/swift/Basic/TaskQueue.h index d67f66d6a9e97..4f6ef8ab68d89 100644 --- a/include/swift/Basic/TaskQueue.h +++ b/include/swift/Basic/TaskQueue.h @@ -23,6 +23,7 @@ #include namespace swift { +class UnifiedStatsReporter; namespace sys { class Task; // forward declared to allow for platform-specific implementations @@ -46,13 +47,18 @@ class TaskQueue { /// The number of tasks to execute in parallel. unsigned NumberOfParallelTasks; + /// Optional place to count I/O and subprocess events. + UnifiedStatsReporter *Stats; + public: /// \brief Create a new TaskQueue instance. /// /// \param NumberOfParallelTasks indicates the number of tasks which should /// be run in parallel. If 0, the TaskQueue will choose the most appropriate /// number of parallel tasks for the current system. - TaskQueue(unsigned NumberOfParallelTasks = 0); + /// \param USR Optional stats reporter to count I/O and subprocess events. + TaskQueue(unsigned NumberOfParallelTasks = 0, + UnifiedStatsReporter *USR = nullptr); virtual ~TaskQueue(); // TODO: remove once -Wdocumentation stops warning for \param, \returns on diff --git a/include/swift/Demangling/Demangle.h b/include/swift/Demangling/Demangle.h index c4673e48b3cfa..41a3f953bea22 100644 --- a/include/swift/Demangling/Demangle.h +++ b/include/swift/Demangling/Demangle.h @@ -94,6 +94,8 @@ enum class FunctionSigSpecializationParamKind : unsigned { Dead = 1 << 6, OwnedToGuaranteed = 1 << 7, SROA = 1 << 8, + GuaranteedToOwned = 1 << 9, + ExistentialToGeneric = 1 << 10, }; /// The pass that caused the specialization to occur. We use this to make sure @@ -466,6 +468,14 @@ void mangleIdentifier(const char *data, size_t length, /// This should always round-trip perfectly with demangleSymbolAsNode. std::string mangleNode(const NodePointer &root); +using SymbolicResolver = llvm::function_ref; + +/// \brief Remangle a demangled parse tree, using a callback to resolve +/// symbolic references. +/// +/// This should always round-trip perfectly with demangleSymbolAsNode. +std::string mangleNode(const NodePointer &root, SymbolicResolver resolver); + /// Remangle in the old mangling scheme. /// /// This is only used for objc-runtime names and should be removed as soon as diff --git a/include/swift/Demangling/DemangleNodes.def b/include/swift/Demangling/DemangleNodes.def index 27cb3be631f80..83670b5388807 100644 --- a/include/swift/Demangling/DemangleNodes.def +++ b/include/swift/Demangling/DemangleNodes.def @@ -36,6 +36,7 @@ NODE(BoundGenericClass) NODE(BoundGenericEnum) NODE(BoundGenericStructure) NODE(BoundGenericOtherNominalType) +NODE(BoundGenericTypeAlias) NODE(BuiltinTypeName) NODE(CFunctionPointer) CONTEXT_NODE(Class) diff --git a/include/swift/Demangling/TypeDecoder.h b/include/swift/Demangling/TypeDecoder.h index a98fab45e8e61..e20cf2fc83647 100644 --- a/include/swift/Demangling/TypeDecoder.h +++ b/include/swift/Demangling/TypeDecoder.h @@ -29,7 +29,7 @@ namespace Demangle { /// Strip generic arguments from the "spine" of a context node, producing a /// bare context to be used in (e.g.) forming nominal type descriptors. -NodePointer stripGenericArgsFromContextNode(const NodePointer &node, +NodePointer stripGenericArgsFromContextNode(NodePointer node, NodeFactory &factory); /// Describe a function parameter, parameterized on the type @@ -127,7 +127,7 @@ class TypeDecoder { case NodeKind::BoundGenericEnum: case NodeKind::BoundGenericStructure: case NodeKind::BoundGenericOtherNominalType: { - if (Node->getNumChildren() != 2) + if (Node->getNumChildren() < 2) return BuiltType(); BuiltNominalTypeDecl typeDecl = BuiltNominalTypeDecl(); @@ -161,7 +161,7 @@ class TypeDecoder { // Handle lowered metatypes in a hackish way. If the representation // was not thin, force the resulting typeref to have a non-empty // representation. - if (Node->getNumChildren() == 2) { + if (Node->getNumChildren() >= 2) { auto repr = Node->getChild(i++); if (repr->getKind() != NodeKind::MetatypeRepresentation || !repr->hasText()) @@ -482,7 +482,7 @@ class TypeDecoder { if (node->getNumChildren() < 2) return false; - auto moduleOrParentType = node->getChild(0); + auto parentContext = node->getChild(0); // Nested types are handled a bit funny here because a // nominal typeref always stores its full mangled name, @@ -490,14 +490,22 @@ class TypeDecoder { // mangled name already includes the module and parent // types, if any. nominalNode = node; - if (moduleOrParentType->getKind() != NodeKind::Module) { - parent = decodeMangledType(moduleOrParentType); - if (!parent) return false; - + switch (parentContext->getKind()) { + case Node::Kind::Module: + break; + case Node::Kind::Extension: + // Decode the type being extended. + if (parentContext->getNumChildren() < 2) + return false; + parentContext = parentContext->getChild(1); + LLVM_FALLTHROUGH; + default: + parent = decodeMangledType(parentContext); // Remove any generic arguments from the context node, producing a - // node that reference the nominal type declaration. + // node that references the nominal type declaration. nominalNode = stripGenericArgsFromContextNode(node, Builder.getNodeFactory()); + break; } } typeDecl = Builder.createNominalTypeDecl(nominalNode); @@ -507,7 +515,7 @@ class TypeDecoder { } BuiltProtocolDecl decodeMangledProtocolType( - const Demangle::NodePointer &node) { + const Demangle::NodePointer &node) { if (node->getKind() == NodeKind::Type) return decodeMangledProtocolType(node->getChild(0)); diff --git a/include/swift/Driver/Compilation.h b/include/swift/Driver/Compilation.h index 41a82b658dab1..f6f11c0fa22d1 100644 --- a/include/swift/Driver/Compilation.h +++ b/include/swift/Driver/Compilation.h @@ -72,6 +72,10 @@ enum class PreserveOnSignal : bool { class Compilation { friend class PerformJobsState; +public: + /// The filelist threshold value to pass to ensure file lists are never used + static const size_t NEVER_USE_FILELIST = SIZE_MAX; + private: /// The DiagnosticEngine to which this Compilation should emit diagnostics. DiagnosticEngine &Diags; @@ -143,14 +147,14 @@ class Compilation { /// The number of commands which this compilation should attempt to run in /// parallel. - unsigned NumberOfParallelCommands; + const unsigned NumberOfParallelCommands; /// Indicates whether this Compilation should use skip execution of /// subtasks during performJobs() by using a dummy TaskQueue. /// /// \note For testing purposes only; similar user-facing features should be /// implemented separately, as the dummy TaskQueue may provide faked output. - bool SkipTaskExecution; + const bool SkipTaskExecution; /// Indicates whether this Compilation should continue execution of subtasks /// even if they returned an error status. @@ -160,25 +164,33 @@ class Compilation { /// of date. bool EnableIncrementalBuild; + /// When true, emit duplicated compilation record file whose filename is + /// suffixed with '~moduleonly'. + /// + /// This compilation record is used by '-emit-module'-only incremental builds + /// so that module-only builds do not affect compilation record file for + /// normal builds, while module-only incremental builds are able to use + /// artifacts of normal builds if they are already up to date. + bool OutputCompilationRecordForModuleOnlyBuild = false; + /// Indicates whether groups of parallel frontend jobs should be merged /// together and run in composite "batch jobs" when possible, to reduce /// redundant work. - bool EnableBatchMode; + const bool EnableBatchMode; /// Provides a randomization seed to batch-mode partitioning, for debugging. - unsigned BatchSeed; + const unsigned BatchSeed; /// In order to test repartitioning, set to true if - /// -driver-force-one-batch-repartition is present. This is cleared after the - /// forced repartition happens. - bool ForceOneBatchRepartition = false; + /// -driver-force-one-batch-repartition is present. + const bool ForceOneBatchRepartition = false; /// True if temporary files should not be deleted. - bool SaveTemps; + const bool SaveTemps; /// When true, dumps information on how long each compilation task took to /// execute. - bool ShowDriverTimeCompilation; + const bool ShowDriverTimeCompilation; /// When non-null, record various high-level counters to this. std::unique_ptr Stats; @@ -195,6 +207,10 @@ class Compilation { /// -emit-loaded-module-trace, so no other job needs to do it. bool PassedEmitLoadedModuleTraceToFrontendJob = false; + /// The limit for the number of files to pass on the command line. Beyond this + /// limit filelists will be used. + size_t FilelistThreshold; + template static T *unwrap(const std::unique_ptr &p) { return p.get(); @@ -211,7 +227,11 @@ class Compilation { std::unique_ptr InputArgs, std::unique_ptr TranslatedArgs, InputFileList InputsWithTypes, + std::string CompilationRecordPath, + bool OutputCompilationRecordForModuleOnlyBuild, StringRef ArgsHash, llvm::sys::TimePoint<> StartTime, + llvm::sys::TimePoint<> LastBuildTime, + size_t FilelistThreshold, unsigned NumberOfParallelCommands = 1, bool EnableIncrementalBuild = false, bool EnableBatchMode = false, @@ -296,13 +316,8 @@ class Compilation { ShowJobLifecycle = value; } - void setCompilationRecordPath(StringRef path) { - assert(CompilationRecordPath.empty() && "already set"); - CompilationRecordPath = path; - } - - void setLastBuildTime(llvm::sys::TimePoint<> time) { - LastBuildTime = time; + size_t getFilelistThreshold() const { + return FilelistThreshold; } /// Requests the path to a file containing all input source files. This can diff --git a/include/swift/Driver/Driver.h b/include/swift/Driver/Driver.h index 1c5ae08f164fc..436ada5149f95 100644 --- a/include/swift/Driver/Driver.h +++ b/include/swift/Driver/Driver.h @@ -64,7 +64,17 @@ class OutputInfo { /// A compilation using a single frontend invocation without -primary-file. SingleCompile, - /// A single process that batches together multiple StandardCompile jobs. + /// A single process that batches together multiple StandardCompile Jobs. + /// + /// Note: this is a transient value to use _only_ for the individual + /// BatchJobs that are the temporary containers for multiple StandardCompile + /// Jobs built by ToolChain::constructBatchJob. + /// + /// In particular, the driver treats a batch-mode-enabled Compilation as + /// having OutputInfo::CompilerMode == StandardCompile, with the + /// Compilation::BatchModeEnabled flag set to true, _not_ as a + /// BatchModeCompile Compilation. The top-level OutputInfo::CompilerMode for + /// a Compilation should never be BatchModeCompile. BatchModeCompile, /// Invoke the REPL @@ -389,8 +399,11 @@ class Driver { /// there is an actual conflict. /// \param Args The input arguments. /// \param Inputs The inputs to the driver. + /// \param BatchModeOut An out-parameter flag that indicates whether to + /// batch the jobs of the resulting \c Mode::StandardCompile compilation. OutputInfo::Mode computeCompilerMode(const llvm::opt::DerivedArgList &Args, - const InputFileList &Inputs) const; + const InputFileList &Inputs, + bool &BatchModeOut) const; }; } // end namespace driver diff --git a/include/swift/Driver/Job.h b/include/swift/Driver/Job.h index cfdbe34209c97..1f0736a63b456 100644 --- a/include/swift/Driver/Job.h +++ b/include/swift/Driver/Job.h @@ -26,6 +26,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Option/Option.h" #include "llvm/Support/Chrono.h" +#include "llvm/Support/Program.h" #include "llvm/Support/raw_ostream.h" #include @@ -237,6 +238,11 @@ class Job { using EnvironmentVector = std::vector>; + /// If positive, contains llvm::ProcessID for a real Job on the host OS. If + /// negative, contains a quasi-PID, which identifies a Job that's a member of + /// a BatchJob _without_ denoting an operating system process. + using PID = int64_t; + private: /// The action which caused the creation of this Job, and the conditions /// under which it must be run. @@ -328,6 +334,15 @@ class Job { void printCommandLineAndEnvironment(raw_ostream &Stream, StringRef Terminator = "\n") const; + /// Call the provided Callback with any Jobs (and their possibly-quasi-PIDs) + /// contained within this Job; if this job is not a BatchJob, just pass \c + /// this and the provided \p OSPid back to the Callback. + virtual void forEachContainedJobAndPID( + llvm::sys::ProcessInfo::ProcessId OSPid, + llvm::function_ref Callback) const { + Callback(this, static_cast(OSPid)); + } + void dump() const LLVM_ATTRIBUTE_USED; static void printArguments(raw_ostream &Stream, @@ -345,18 +360,38 @@ class Job { /// See ToolChain::jobsAreBatchCombinable for details. class BatchJob : public Job { - SmallVector CombinedJobs; + + /// The set of constituents making up the batch. + const SmallVector CombinedJobs; + + /// A negative number to use as the base value for assigning quasi-PID to Jobs + /// in the \c CombinedJobs array. Quasi-PIDs count _down_ from this value. + const Job::PID QuasiPIDBase; + public: BatchJob(const JobAction &Source, SmallVectorImpl &&Inputs, std::unique_ptr Output, const char *Executable, llvm::opt::ArgStringList Arguments, EnvironmentVector ExtraEnvironment, std::vector Infos, - ArrayRef Combined); + ArrayRef Combined, Job::PID &NextQuasiPID); ArrayRef getCombinedJobs() const { return CombinedJobs; } + + /// Call the provided callback for each Job in the batch, passing the + /// corresponding quasi-PID with each Job. + void forEachContainedJobAndPID( + llvm::sys::ProcessInfo::ProcessId OSPid, + llvm::function_ref Callback) const override { + Job::PID QPid = QuasiPIDBase; + assert(QPid < 0); + for (auto const *J : CombinedJobs) { + assert(QPid != std::numeric_limits::min()); + Callback(J, QPid--); + } + } }; } // end namespace driver diff --git a/include/swift/Driver/ParseableOutput.h b/include/swift/Driver/ParseableOutput.h index d25b16759e768..af14ea387e87d 100644 --- a/include/swift/Driver/ParseableOutput.h +++ b/include/swift/Driver/ParseableOutput.h @@ -28,17 +28,15 @@ class Job; namespace parseable_output { -using swift::sys::ProcessId; - /// \brief Emits a "began" message to the given stream. -void emitBeganMessage(raw_ostream &os, const Job &Cmd, ProcessId Pid); +void emitBeganMessage(raw_ostream &os, const Job &Cmd, int64_t Pid); /// \brief Emits a "finished" message to the given stream. -void emitFinishedMessage(raw_ostream &os, const Job &Cmd, ProcessId Pid, +void emitFinishedMessage(raw_ostream &os, const Job &Cmd, int64_t Pid, int ExitStatus, StringRef Output); /// \brief Emits a "signalled" message to the given stream. -void emitSignalledMessage(raw_ostream &os, const Job &Cmd, ProcessId Pid, +void emitSignalledMessage(raw_ostream &os, const Job &Cmd, int64_t Pid, StringRef ErrorMsg, StringRef Output, Optional Signal); /// \brief Emits a "skipped" message to the given stream. diff --git a/include/swift/Driver/ToolChain.h b/include/swift/Driver/ToolChain.h index c86cdbaeba3a3..c18f59b78e09a 100644 --- a/include/swift/Driver/ToolChain.h +++ b/include/swift/Driver/ToolChain.h @@ -15,7 +15,9 @@ #include "swift/Basic/LLVM.h" #include "swift/Driver/Action.h" +#include "swift/Driver/Job.h" #include "swift/Frontend/FileTypes.h" +#include "swift/Option/Options.h" #include "llvm/ADT/Triple.h" #include "llvm/Option/Option.h" @@ -23,11 +25,11 @@ namespace swift { namespace driver { - class CommandOutput; - class Compilation; - class Driver; - class Job; - class OutputInfo; +class CommandOutput; +class Compilation; +class Driver; +class Job; +class OutputInfo; /// A ToolChain is responsible for turning abstract Actions into concrete, /// runnable Jobs. @@ -47,16 +49,13 @@ class ToolChain { ToolChain(const Driver &D, const llvm::Triple &T) : D(D), Triple(T) {} /// A special name used to identify the Swift executable itself. - constexpr static const char * const SWIFT_EXECUTABLE_NAME = "swift"; + constexpr static const char *const SWIFT_EXECUTABLE_NAME = "swift"; /// Packs together the supplementary information about the job being created. class JobContext { private: Compilation &C; - /// The limit for passing a list of files on the command line. - static const size_t TOO_MANY_FILES = 128; - public: ArrayRef Inputs; ArrayRef InputActions; @@ -125,45 +124,36 @@ class ToolChain { InvocationInfo(const char *name, llvm::opt::ArgStringList args = {}, decltype(ExtraEnvironment) extraEnv = {}) - : ExecutableName(name), Arguments(std::move(args)), - ExtraEnvironment(std::move(extraEnv)) {} + : ExecutableName(name), Arguments(std::move(args)), + ExtraEnvironment(std::move(extraEnv)) {} }; - virtual InvocationInfo - constructInvocation(const CompileJobAction &job, - const JobContext &context) const; - virtual InvocationInfo - constructInvocation(const InterpretJobAction &job, - const JobContext &context) const; - virtual InvocationInfo - constructInvocation(const BackendJobAction &job, - const JobContext &context) const; - virtual InvocationInfo - constructInvocation(const MergeModuleJobAction &job, - const JobContext &context) const; - virtual InvocationInfo - constructInvocation(const ModuleWrapJobAction &job, - const JobContext &context) const; - - virtual InvocationInfo - constructInvocation(const REPLJobAction &job, - const JobContext &context) const; - - virtual InvocationInfo - constructInvocation(const GenerateDSYMJobAction &job, - const JobContext &context) const; + virtual InvocationInfo constructInvocation(const CompileJobAction &job, + const JobContext &context) const; + virtual InvocationInfo constructInvocation(const InterpretJobAction &job, + const JobContext &context) const; + virtual InvocationInfo constructInvocation(const BackendJobAction &job, + const JobContext &context) const; + virtual InvocationInfo constructInvocation(const MergeModuleJobAction &job, + const JobContext &context) const; + virtual InvocationInfo constructInvocation(const ModuleWrapJobAction &job, + const JobContext &context) const; + + virtual InvocationInfo constructInvocation(const REPLJobAction &job, + const JobContext &context) const; + + virtual InvocationInfo constructInvocation(const GenerateDSYMJobAction &job, + const JobContext &context) const; virtual InvocationInfo constructInvocation(const VerifyDebugInfoJobAction &job, const JobContext &context) const; - virtual InvocationInfo - constructInvocation(const GeneratePCHJobAction &job, - const JobContext &context) const; + virtual InvocationInfo constructInvocation(const GeneratePCHJobAction &job, + const JobContext &context) const; virtual InvocationInfo constructInvocation(const AutolinkExtractJobAction &job, const JobContext &context) const; - virtual InvocationInfo - constructInvocation(const LinkJobAction &job, - const JobContext &context) const; + virtual InvocationInfo constructInvocation(const LinkJobAction &job, + const JobContext &context) const; /// Searches for the given executable in appropriate paths relative to the /// Swift binary. @@ -179,6 +169,34 @@ class ToolChain { /// This method is invoked by findProgramRelativeToSwift(). virtual std::string findProgramRelativeToSwiftImpl(StringRef name) const; + void addInputsOfType(llvm::opt::ArgStringList &Arguments, + ArrayRef Inputs, + file_types::ID InputType, + const char *PrefixArgument = nullptr) const; + + void addInputsOfType(llvm::opt::ArgStringList &Arguments, + ArrayRef Jobs, + const llvm::opt::ArgList &Args, file_types::ID InputType, + const char *PrefixArgument = nullptr) const; + + void addPrimaryInputsOfType(llvm::opt::ArgStringList &Arguments, + ArrayRef Jobs, + const llvm::opt::ArgList &Args, + file_types::ID InputType, + const char *PrefixArgument = nullptr) const; + + /// Get the runtime library link path, which is platform-specific and found + /// relative to the compiler. + void getRuntimeLibraryPath(SmallVectorImpl &runtimeLibPath, + const llvm::opt::ArgList &args, bool shared) const; + + void addPathEnvironmentVariableIfNeeded(Job::EnvironmentVector &env, + const char *name, + const char *separator, + options::ID optionID, + const llvm::opt::ArgList &args, + StringRef extraEntry = "") const; + public: virtual ~ToolChain() = default; @@ -190,8 +208,7 @@ class ToolChain { /// /// This method dispatches to the various \c constructInvocation methods, /// which may be overridden by platform-specific subclasses. - std::unique_ptr constructJob(const JobAction &JA, - Compilation &C, + std::unique_ptr constructJob(const JobAction &JA, Compilation &C, SmallVectorImpl &&inputs, ArrayRef inputActions, std::unique_ptr output, @@ -217,23 +234,45 @@ class ToolChain { /// Construct a \c BatchJob that subsumes the work of a set of Jobs. Any pair /// of elements in \p Jobs are assumed to satisfy the equivalence relation \c /// jobsAreBatchCombinable, i.e. they should all be "the same" job in in all - /// ways other than their choices of inputs. + /// ways other than their choices of inputs. The provided \p NextQuasiPID + /// should be a negative number that persists between calls; this method will + /// decrement it to assign quasi-PIDs to each of the \p Jobs passed. std::unique_ptr constructBatchJob(ArrayRef Jobs, + int64_t &NextQuasiPID, Compilation &C) const; /// Return the default language type to use for the given extension. /// If the extension is empty or is otherwise not recognized, return /// the invalid type \c TY_INVALID. - virtual file_types::ID lookupTypeForExtension(StringRef Ext) const; + file_types::ID lookupTypeForExtension(StringRef Ext) const; + + /// Copies the path for the directory clang libraries would be stored in on + /// the current toolchain. + void getClangLibraryPath(const llvm::opt::ArgList &Args, + SmallString<128> &LibPath) const; - /// Check whether a clang library with a given name exists. + /// Returns the name the clang library for a given sanitizer would have on + /// the current toolchain. + /// + /// \param Sanitizer Sanitizer name. + /// \param shared Whether the library is shared + virtual std::string sanitizerRuntimeLibName(StringRef Sanitizer, + bool shared = true) const = 0; + + /// Returns whether a given sanitizer exists for the current toolchain. /// - /// \param args Invocation arguments. /// \param sanitizer Sanitizer name. /// \param shared Whether the library is shared - virtual bool sanitizerRuntimeLibExists(const llvm::opt::ArgList &args, - StringRef sanitizer, - bool shared=true) const; + bool sanitizerRuntimeLibExists(const llvm::opt::ArgList &args, + StringRef sanitizer, bool shared = true) const; + + /// Adds a runtime library to the arguments list for linking. + /// + /// \param LibName The library name + /// \param Arguments The arguments list to append to + void addLinkRuntimeLib(const llvm::opt::ArgList &Args, + llvm::opt::ArgStringList &Arguments, + StringRef LibName) const; }; } // end namespace driver } // end namespace swift diff --git a/include/swift/Frontend/Frontend.h b/include/swift/Frontend/Frontend.h index a1f59ae00a1e1..0ccb5ec2f095d 100644 --- a/include/swift/Frontend/Frontend.h +++ b/include/swift/Frontend/Frontend.h @@ -34,6 +34,7 @@ #include "swift/Migrator/MigratorOptions.h" #include "swift/Parse/CodeCompletionCallbacks.h" #include "swift/Parse/Parser.h" +#include "swift/Parse/SyntaxParsingCache.h" #include "swift/Sema/SourceLoader.h" #include "swift/Serialization/Validation.h" #include "swift/Subsystems.h" @@ -68,6 +69,9 @@ class CompilerInvocation { MigratorOptions MigratorOpts; SILOptions SILOpts; IRGenOptions IRGenOpts; + /// The \c SyntaxParsingCache to use when parsing the main file of this + /// invocation + SyntaxParsingCache *MainFileSyntaxParsingCache = nullptr; llvm::MemoryBuffer *CodeCompletionBuffer = nullptr; @@ -217,6 +221,14 @@ class CompilerInvocation { IRGenOptions &getIRGenOptions() { return IRGenOpts; } const IRGenOptions &getIRGenOptions() const { return IRGenOpts; } + void setMainFileSyntaxParsingCache(SyntaxParsingCache *Cache) { + MainFileSyntaxParsingCache = Cache; + } + + SyntaxParsingCache *getMainFileSyntaxParsingCache() const { + return MainFileSyntaxParsingCache; + } + void setParseStdlib() { FrontendOpts.ParseStdlib = true; } @@ -336,7 +348,8 @@ class CompilerInstance { std::unique_ptr Context; std::unique_ptr TheSILModule; - DependencyTracker *DepTracker = nullptr; + /// Null if no tracker. + std::unique_ptr DepTracker; ModuleDecl *MainModule = nullptr; SerializedModuleLoader *SML = nullptr; @@ -408,13 +421,11 @@ class CompilerInstance { Diagnostics.addConsumer(*DC); } - void setDependencyTracker(DependencyTracker *DT) { + void createDependencyTracker() { assert(!Context && "must be called before setup()"); - DepTracker = DT; - } - DependencyTracker *getDependencyTracker() { - return DepTracker; + DepTracker = llvm::make_unique(); } + DependencyTracker *getDependencyTracker() { return DepTracker.get(); } /// Set the SIL module for this compilation instance. /// diff --git a/include/swift/Frontend/FrontendInputsAndOutputs.h b/include/swift/Frontend/FrontendInputsAndOutputs.h index be87323f5e785..4cc461cd2ed04 100644 --- a/include/swift/Frontend/FrontendInputsAndOutputs.h +++ b/include/swift/Frontend/FrontendInputsAndOutputs.h @@ -13,7 +13,6 @@ #ifndef SWIFT_FRONTEND_FRONTENDINPUTS_H #define SWIFT_FRONTEND_FRONTENDINPUTS_H -#include "swift/AST/Module.h" #include "swift/Basic/PrimarySpecificPaths.h" #include "swift/Basic/SupplementaryOutputPaths.h" #include "swift/Frontend/InputFile.h" @@ -29,6 +28,8 @@ class MemoryBuffer; namespace swift { +class DiagnosticEngine; + /// Information about all the inputs and outputs to the frontend. class FrontendInputsAndOutputs { @@ -103,6 +104,10 @@ class FrontendInputsAndOutputs { bool forEachPrimaryInput(llvm::function_ref fn) const; + /// If \p fn returns true, exit early and return true. + bool + forEachNonPrimaryInput(llvm::function_ref fn) const; + unsigned primaryInputCount() const { return PrimaryInputsInOrder.size(); } // Primary count readers: @@ -139,7 +144,7 @@ class FrontendInputsAndOutputs { bool isInputPrimary(StringRef file) const; - unsigned numberOfPrimaryInputsEndingWith(const char *extension) const; + unsigned numberOfPrimaryInputsEndingWith(StringRef extension) const; // Multi-facet readers diff --git a/include/swift/Frontend/FrontendOptions.h b/include/swift/Frontend/FrontendOptions.h index 8a0ef03ee3088..468769712bfc5 100644 --- a/include/swift/Frontend/FrontendOptions.h +++ b/include/swift/Frontend/FrontendOptions.h @@ -13,7 +13,6 @@ #ifndef SWIFT_FRONTEND_FRONTENDOPTIONS_H #define SWIFT_FRONTEND_FRONTENDOPTIONS_H -#include "swift/AST/Module.h" #include "swift/Frontend/FrontendInputsAndOutputs.h" #include "swift/Frontend/InputFile.h" #include "llvm/ADT/Hashing.h" @@ -39,7 +38,7 @@ class FrontendOptions { InputFileKind InputKind = InputFileKind::IFK_Swift; void forAllOutputPaths(const InputFile &input, - std::function fn) const; + llvm::function_ref fn) const; bool isOutputFileDirectory() const; @@ -90,6 +89,11 @@ class FrontendOptions { /// the expression type checker run before we consider an expression /// too complex. unsigned SolverExpressionTimeThreshold = 0; + + /// If non-zero, overrides the default threshold for how many times + /// the Space::minus function is called before we consider switch statement + /// exhaustiveness checking to be too complex. + unsigned SwitchCheckingInvocationThreshold = 0; /// The module for which we should verify all of the generic signatures. std::string VerifyGenericSignaturesInModule; @@ -296,7 +300,7 @@ class FrontendOptions { static bool doesActionProduceOutput(ActionType); static bool doesActionProduceTextualOutput(ActionType); static bool needsProperModuleName(ActionType); - static const char *suffixForPrincipalOutputFileForAction(ActionType); + static StringRef suffixForPrincipalOutputFileForAction(ActionType); }; } diff --git a/include/swift/IDE/APIDigesterData.h b/include/swift/IDE/APIDigesterData.h index 69d3dd2f6b42c..3696253637f52 100644 --- a/include/swift/IDE/APIDigesterData.h +++ b/include/swift/IDE/APIDigesterData.h @@ -27,7 +27,7 @@ namespace api { // The node kind appearing in the tree that describes the content of the SDK enum class SDKNodeKind: uint8_t { -#define NODE_KIND(NAME) NAME, +#define NODE_KIND(NAME, VALUE) NAME, #include "DigesterEnums.def" }; @@ -129,10 +129,33 @@ struct CommonDiffItem: public APIDiffItem { } } + bool isStringRepresentableChange() const { + switch(DiffKind) { + case NodeAnnotation::DictionaryKeyUpdate: + case NodeAnnotation::OptionalDictionaryKeyUpdate: + case NodeAnnotation::ArrayMemberUpdate: + case NodeAnnotation::OptionalArrayMemberUpdate: + case NodeAnnotation::SimpleStringRepresentableUpdate: + case NodeAnnotation::SimpleOptionalStringRepresentableUpdate: + return true; + default: + return false; + } + } + StringRef getNewName() const { assert(isRename()); return RightComment; } APIDiffItemKind getKind() const override { return APIDiffItemKind::ADK_CommonDiffItem; } + + bool rightCommentUnderscored() const { + DeclNameViewer Viewer(RightComment); + auto HasUnderScore = + [](StringRef S) { return S.find('_') != StringRef::npos; }; + auto Args = Viewer.args(); + return HasUnderScore(Viewer.base()) || + std::any_of(Args.begin(), Args.end(), HasUnderScore); + } }; @@ -224,6 +247,7 @@ enum class TypeMemberDiffItemSubKind { HoistSelfOnly, HoistSelfAndRemoveParam, HoistSelfAndUseProperty, + FuncRename, }; struct TypeMemberDiffItem: public APIDiffItem { @@ -320,6 +344,21 @@ struct OverloadedFuncInfo: public APIDiffItem { } }; +struct NameCorrectionInfo { + StringRef OriginalName; + StringRef CorrectedName; + StringRef ModuleName; + NameCorrectionInfo(StringRef OriginalName, StringRef CorrectedName, + StringRef ModuleName): OriginalName(OriginalName), + CorrectedName(CorrectedName), ModuleName(ModuleName) {} + bool operator<(NameCorrectionInfo Other) const { + if (ModuleName != Other.ModuleName) + return ModuleName.compare(Other.ModuleName) < 0; + else + return OriginalName.compare(Other.OriginalName) < 0; + } +}; + /// APIDiffItem store is the interface that migrator should communicates with; /// Given a key, usually the usr of the system entity under migration, the store /// should return a slice of related changes in the same format of @@ -329,6 +368,7 @@ struct APIDiffItemStore { struct Implementation; Implementation &Impl; static void serialize(llvm::raw_ostream &os, ArrayRef Items); + static void serialize(llvm::raw_ostream &os, ArrayRef Items); APIDiffItemStore(const APIDiffItemStore& that) = delete; APIDiffItemStore(); ~APIDiffItemStore(); @@ -347,7 +387,8 @@ namespace json { template<> struct ScalarEnumerationTraits { static void enumeration(Output &out, ide::api::SDKNodeKind &value) { -#define NODE_KIND(X) out.enumCase(value, #X, ide::api::SDKNodeKind::X); +#define NODE_KIND(KEY, VALUE) \ + out.enumCase(value, #VALUE, ide::api::SDKNodeKind::KEY); #include "swift/IDE/DigesterEnums.def" } }; diff --git a/include/swift/IDE/DigesterEnums.def b/include/swift/IDE/DigesterEnums.def index 390324174136a..26fb0cbb5b564 100644 --- a/include/swift/IDE/DigesterEnums.def +++ b/include/swift/IDE/DigesterEnums.def @@ -1,11 +1,15 @@ #ifndef NODE_KIND -#define NODE_KIND(NAME) +#define NODE_KIND(NAME, EXTERNAL_NAME) #endif #ifndef NODE_ANNOTATION #define NODE_ANNOTATION(NAME) #endif +#ifndef NODE_ANNOTATION_CHANGE_KIND +#define NODE_ANNOTATION_CHANGE_KIND(NAME) NODE_ANNOTATION(NAME) +#endif + #ifndef DECL_ATTR #define DECL_ATTR(NAME) #endif @@ -34,47 +38,62 @@ #define SPECIAL_CASE_ID(NAME) #endif -NODE_KIND(Root) -NODE_KIND(TypeDecl) -NODE_KIND(TypeNominal) -NODE_KIND(TypeFunc) -NODE_KIND(TypeNameAlias) -NODE_KIND(Function) -NODE_KIND(Constructor) -NODE_KIND(Getter) -NODE_KIND(Setter) -NODE_KIND(Var) -NODE_KIND(TypeAlias) +NODE_KIND(Root, Root) +NODE_KIND(TypeNominal, TypeNominal) +NODE_KIND(TypeFunc, TypeFunc) +NODE_KIND(TypeAlias, TypeNameAlias) +NODE_KIND(DeclType, TypeDecl) +NODE_KIND(DeclFunction, Function) +NODE_KIND(DeclConstructor, Constructor) +NODE_KIND(DeclGetter, Getter) +NODE_KIND(DeclSetter, Setter) +NODE_KIND(DeclVar, Var) +NODE_KIND(DeclTypeAlias, TypeAlias) NODE_ANNOTATION(Added) NODE_ANNOTATION(Removed) NODE_ANNOTATION(Updated) -NODE_ANNOTATION(RemovingErrorParam) -NODE_ANNOTATION(ImplicitOptionalToOptional) -NODE_ANNOTATION(OptionalToImplicitOptional) -NODE_ANNOTATION(WrapOptional) -NODE_ANNOTATION(WrapImplicitOptional) -NODE_ANNOTATION(UnwrapOptional) -NODE_ANNOTATION(GenericParamUpCast) -NODE_ANNOTATION(GenericParamDownCast) -NODE_ANNOTATION(TypeAliasChangeFromInt) -NODE_ANNOTATION(GetterToProperty) -NODE_ANNOTATION(SetterToProperty) NODE_ANNOTATION(PropertyName) -NODE_ANNOTATION(TypeRewritten) NODE_ANNOTATION(TypeRewrittenLeft) NODE_ANNOTATION(TypeRewrittenRight) -NODE_ANNOTATION(ModernizeEnum) -NODE_ANNOTATION(UnwrapUnmanaged) NODE_ANNOTATION(RemovedDecl) -NODE_ANNOTATION(Rename) NODE_ANNOTATION(RenameOldName) NODE_ANNOTATION(RenameNewName) NODE_ANNOTATION(NowThrowing) NODE_ANNOTATION(NowMutating) NODE_ANNOTATION(StaticChange) NODE_ANNOTATION(OwnershipChange) -NODE_ANNOTATION(DictionaryKeyUpdate) +NODE_ANNOTATION(RawTypeLeft) +NODE_ANNOTATION(RawTypeRight) + +NODE_ANNOTATION_CHANGE_KIND(ImplicitOptionalToOptional) +NODE_ANNOTATION_CHANGE_KIND(OptionalToImplicitOptional) +NODE_ANNOTATION_CHANGE_KIND(WrapOptional) +NODE_ANNOTATION_CHANGE_KIND(WrapImplicitOptional) +NODE_ANNOTATION_CHANGE_KIND(UnwrapOptional) +NODE_ANNOTATION_CHANGE_KIND(GetterToProperty) +NODE_ANNOTATION_CHANGE_KIND(SetterToProperty) +NODE_ANNOTATION_CHANGE_KIND(DictionaryKeyUpdate) +NODE_ANNOTATION_CHANGE_KIND(OptionalDictionaryKeyUpdate) +NODE_ANNOTATION_CHANGE_KIND(ArrayMemberUpdate) +NODE_ANNOTATION_CHANGE_KIND(OptionalArrayMemberUpdate) +NODE_ANNOTATION_CHANGE_KIND(SimpleStringRepresentableUpdate) +NODE_ANNOTATION_CHANGE_KIND(SimpleOptionalStringRepresentableUpdate) +NODE_ANNOTATION_CHANGE_KIND(TypeAliasDeclToRawRepresentable) + +NODE_ANNOTATION_CHANGE_KIND(RevertDictionaryKeyUpdate) +NODE_ANNOTATION_CHANGE_KIND(RevertOptionalDictionaryKeyUpdate) +NODE_ANNOTATION_CHANGE_KIND(RevertArrayMemberUpdate) +NODE_ANNOTATION_CHANGE_KIND(RevertOptionalArrayMemberUpdate) +NODE_ANNOTATION_CHANGE_KIND(RevertSimpleStringRepresentableUpdate) +NODE_ANNOTATION_CHANGE_KIND(RevertSimpleOptionalStringRepresentableUpdate) + +NODE_ANNOTATION_CHANGE_KIND(ModernizeEnum) +NODE_ANNOTATION_CHANGE_KIND(UnwrapUnmanaged) +NODE_ANNOTATION_CHANGE_KIND(Rename) + +// Keep type rewritten the last one. +NODE_ANNOTATION_CHANGE_KIND(TypeRewritten) DECL_ATTR(deprecated) DECL_ATTR(fixedLayout) @@ -107,6 +126,8 @@ KNOWN_TYPE(Unmanaged) KNOWN_TYPE(Function) KNOWN_TYPE(Dictionary) KNOWN_TYPE(String) +KNOWN_TYPE(Array) +KNOWN_TYPE(Int) KNOWN_PROTOCOL(RawRepresentable) @@ -161,5 +182,6 @@ SPECIAL_CASE_ID(ToUIntMax) #undef KNOWN_PROTOCOL #undef KEY #undef DECL_ATTR +#undef NODE_ANNOTATION_CHANGE_KIND #undef NODE_ANNOTATION #undef NODE_KIND diff --git a/include/swift/IDE/RefactoringKinds.def b/include/swift/IDE/RefactoringKinds.def index ecd3667c0dbcb..3764fdf3fc92d 100644 --- a/include/swift/IDE/RefactoringKinds.def +++ b/include/swift/IDE/RefactoringKinds.def @@ -10,6 +10,10 @@ #define RANGE_REFACTORING(KIND, NAME, ID) SEMANTIC_REFACTORING(KIND, NAME, ID) #endif +#ifndef INTERNAL_RANGE_REFACTORING +#define INTERNAL_RANGE_REFACTORING(KIND, NAME, ID) RANGE_REFACTORING(KIND, NAME, ID) +#endif + #ifndef CURSOR_REFACTORING #define CURSOR_REFACTORING(KIND, NAME, ID) SEMANTIC_REFACTORING(KIND, NAME, ID) #endif @@ -40,7 +44,7 @@ CURSOR_REFACTORING(LocalizeString, "Localize String", localize.string) CURSOR_REFACTORING(SimplifyNumberLiteral, "Simplify Long Number Literal", simplify.long.number.literal) -CURSOR_REFACTORING(CollapseNestedIfExpr, "Collapse Nested If Expression", collapse.nested.if.expr) +CURSOR_REFACTORING(CollapseNestedIfStmt, "Collapse Nested If Statements", collapse.nested.ifstmt) CURSOR_REFACTORING(ConvertToDoCatch, "Convert To Do/Catch", convert.do.catch) @@ -60,7 +64,14 @@ RANGE_REFACTORING(ExpandTernaryExpr, "Expand Ternary Expression", expand.ternary RANGE_REFACTORING(ConvertToTernaryExpr, "Convert To Ternary Expression", convert.ternary.expr) +// These internal refactorings are designed to be helpful for working on +// the compiler/standard library, etc., but are likely to be just confusing and +// noise for general development. + +INTERNAL_RANGE_REFACTORING(ReplaceBodiesWithFatalError, "Replace Function Bodies With 'fatalError()'", replace.bodies.with.fatalError) + #undef CURSOR_REFACTORING +#undef INTERNAL_RANGE_REFACTORING #undef RANGE_REFACTORING #undef SEMANTIC_REFACTORING #undef REFACTORING diff --git a/include/swift/IDE/Utils.h b/include/swift/IDE/Utils.h index fd027a613aff9..96ffe530426df 100644 --- a/include/swift/IDE/Utils.h +++ b/include/swift/IDE/Utils.h @@ -87,7 +87,7 @@ bool initInvocationByClangArguments(ArrayRef ArgList, /// Visits all overridden declarations exhaustively from VD, including protocol /// conformances and clang declarations. void walkOverriddenDecls(const ValueDecl *VD, - std::function)> Fn); void collectModuleNames(StringRef SDKPath, std::vector &Modules); diff --git a/include/swift/IRGen/IRGenPublic.h b/include/swift/IRGen/IRGenPublic.h index d3447695e1ccd..a5857a6995379 100644 --- a/include/swift/IRGen/IRGenPublic.h +++ b/include/swift/IRGen/IRGenPublic.h @@ -14,9 +14,12 @@ namespace llvm { class LLVMContext; + template class SmallVector; } namespace swift { +class ASTContext; +class LinkLibrary; class SILModule; namespace irgen { diff --git a/include/swift/IRGen/Linking.h b/include/swift/IRGen/Linking.h index 5c809a4c06d58..3fb1be9551bec 100644 --- a/include/swift/IRGen/Linking.h +++ b/include/swift/IRGen/Linking.h @@ -14,6 +14,7 @@ #define SWIFT_IRGEN_LINKING_H #include "swift/AST/Decl.h" +#include "swift/AST/Module.h" #include "swift/AST/ProtocolAssociations.h" #include "swift/AST/ProtocolConformance.h" #include "swift/AST/Types.h" @@ -45,7 +46,7 @@ class UniversalLinkageInfo { bool IsWholeModule; - UniversalLinkageInfo(IRGenModule &IGM); + explicit UniversalLinkageInfo(IRGenModule &IGM); UniversalLinkageInfo(const llvm::Triple &triple, bool hasMultipleIGMs, bool isWholeModule); @@ -844,17 +845,15 @@ class LinkInfo { public: /// Compute linkage information for the given + static LinkInfo get(IRGenModule &IGM, const LinkEntity &entity, + ForDefinition_t forDefinition); + static LinkInfo get(const UniversalLinkageInfo &linkInfo, ModuleDecl *swiftModule, const LinkEntity &entity, ForDefinition_t forDefinition); - static LinkInfo get(IRGenModule &IGM, const LinkEntity &entity, - ForDefinition_t forDefinition); - - static LinkInfo get(const UniversalLinkageInfo &linkInfo, - StringRef name, - SILLinkage linkage, - ForDefinition_t isDefinition, + static LinkInfo get(const UniversalLinkageInfo &linkInfo, StringRef name, + SILLinkage linkage, ForDefinition_t isDefinition, bool isWeakImported); StringRef getName() const { @@ -881,6 +880,9 @@ class LinkInfo { llvm::GlobalValue::VisibilityTypes Visibility, llvm::GlobalValue::DLLStorageClassTypes DLLStorage); }; + +StringRef encodeForceLoadSymbolName(llvm::SmallVectorImpl &buf, + StringRef name); } } @@ -911,5 +913,4 @@ template <> struct llvm::DenseMapInfo { LHS.SecondaryPointer == RHS.SecondaryPointer && LHS.Data == RHS.Data; } }; - #endif diff --git a/include/swift/Migrator/EditorAdapter.h b/include/swift/Migrator/EditorAdapter.h index b6a9ec40e6d28..1a00e504f914a 100644 --- a/include/swift/Migrator/EditorAdapter.h +++ b/include/swift/Migrator/EditorAdapter.h @@ -51,6 +51,8 @@ class EditorAdapter { /// below. That doesn't handle duplicate or redundant changes. mutable llvm::SmallSet Replacements; + bool CacheEnabled; + /// A running transactional collection of basic edit operations. /// Clang uses this transaction concept to cancel a batch of edits due to /// incompatibilities, such as those due to macro expansions, but we don't @@ -82,7 +84,7 @@ class EditorAdapter { public: EditorAdapter(swift::SourceManager &SwiftSrcMgr, clang::SourceManager &ClangSrcMgr) - : SwiftSrcMgr(SwiftSrcMgr), ClangSrcMgr(ClangSrcMgr), + : SwiftSrcMgr(SwiftSrcMgr), ClangSrcMgr(ClangSrcMgr), CacheEnabled(true), Edits(clang::edit::Commit(ClangSrcMgr, clang::LangOptions())) {} /// Lookup the BufferID in the SwiftToClangBufferMap. If it doesn't exist, @@ -128,6 +130,8 @@ class EditorAdapter { const clang::edit::Commit &getEdits() const { return Edits; } + void enableCache() { CacheEnabled = true; } + void disableCache() { CacheEnabled = false; } }; } // end namespace migrator diff --git a/include/swift/Migrator/FixitFilter.h b/include/swift/Migrator/FixitFilter.h index ab0b7428191be..b6c64d66264c0 100644 --- a/include/swift/Migrator/FixitFilter.h +++ b/include/swift/Migrator/FixitFilter.h @@ -61,6 +61,18 @@ struct FixitFilter { Info.ID == diag::invalid_ibinspectable.ID || Info.ID == diag::invalid_ibaction_decl.ID) return false; + + // The Migrator only applies changes from the APIDiffMigratorPass in the + // primary file, so if a nominal type was renamed, for example, any members + // users have added in an extension in a separate file may not be visible, + // due to the extension rename not having been applied. The below diag(s) + // can provide undesireable fixits that rename references of these + // no-longer-visible members to similar still-visible ones. + // Note: missing_argument_lables and extra_argument_labels are filtered out + // elsewhere + if (Info.ID == diag::wrong_argument_labels.ID) + return false; + // Adding type(of:) interacts poorly with the swift migrator by // invalidating some inits with type errors. if (Info.ID == diag::init_not_instance_member.ID) @@ -130,6 +142,7 @@ struct FixitFilter { Info.ID == diag::objc_inference_swift3_objc_derived.ID || Info.ID == diag::missing_several_cases.ID || Info.ID == diag::missing_particular_case.ID || + Info.ID == diag::missing_unknown_case.ID || Info.ID == diag::paren_void_probably_void.ID || Info.ID == diag::make_decl_objc.ID || Info.ID == diag::optional_req_nonobjc_near_match_add_objc.ID) diff --git a/include/swift/Option/FrontendOptions.td b/include/swift/Option/FrontendOptions.td index 1c3c5823f9c3d..4a578778a45a4 100644 --- a/include/swift/Option/FrontendOptions.td +++ b/include/swift/Option/FrontendOptions.td @@ -81,6 +81,9 @@ def verify_generic_signatures : Separate<["-"], "verify-generic-signatures">, MetaVarName<"">, HelpText<"Verify the generic signatures in the given module">; +def verify_syntax_tree : Flag<["-"], "verify-syntax-tree">, + HelpText<"Verify that no unknown nodes exist in the libSyntax tree">; + def show_diagnostics_after_fatal : Flag<["-"], "show-diagnostics-after-fatal">, HelpText<"Keep emitting subsequent diagnostics after a fatal error">; @@ -214,8 +217,8 @@ def disable_llvm_optzns : Flag<["-"], "disable-llvm-optzns">, def disable_sil_perf_optzns : Flag<["-"], "disable-sil-perf-optzns">, HelpText<"Don't run SIL performance optimization passes">; -def disable_llvm_arc_opts : Flag<["-"], "disable-llvm-arc-opts">, - HelpText<"Don't run LLVM ARC optimization passes.">; +def disable_swift_specific_llvm_optzns : Flag<["-"], "disable-swift-specific-llvm-optzns">, + HelpText<"Don't run Swift specific LLVM optimization passes.">; def disable_llvm_slp_vectorizer : Flag<["-"], "disable-llvm-slp-vectorizer">, HelpText<"Don't run LLVM SLP vectorizer">; @@ -243,9 +246,6 @@ def stack_promotion_limit : Separate<["-"], "stack-promotion-limit">, HelpText<"Limit the size of stack promoted objects to the provided number " "of bytes.">; -def disable_sil_linking : Flag<["-"], "disable-sil-linking">, - HelpText<"Don't link SIL functions">; - def dump_clang_diagnostics : Flag<["-"], "dump-clang-diagnostics">, HelpText<"Dump Clang diagnostics to stderr">; @@ -345,6 +345,9 @@ def warn_long_expression_type_checking_EQ : Joined<["-"], "warn-long-expression- def solver_expression_time_threshold_EQ : Joined<["-"], "solver-expression-time-threshold=">; +def switch_checking_invocation_threshold_EQ : Joined<["-"], + "switch-checking-invocation-threshold=">; + def enable_source_import : Flag<["-"], "enable-source-import">, HelpText<"Enable importing of Swift source files">; @@ -393,9 +396,6 @@ def sil_unroll_threshold : Separate<["-"], "sil-unroll-threshold">, def sil_merge_partial_modules : Flag<["-"], "sil-merge-partial-modules">, HelpText<"Merge SIL from all partial swiftmodules into the final module">; -def sil_link_all : Flag<["-"], "sil-link-all">, - HelpText<"Link all SIL functions">; - def sil_verify_all : Flag<["-"], "sil-verify-all">, HelpText<"Verify SIL after each transform">; @@ -453,6 +453,12 @@ def enable_resilience : Flag<["-"], "enable-resilience">, HelpText<"Compile the module to export resilient interfaces for all " "public declarations by default">; +def enable_class_resilience : Flag<["-"], "enable-class-resilience">, + HelpText<"Enable resilient layout for classes containing resilient value types">; + +def enable_resilience_bypass : Flag<["-"], "enable-resilience-bypass">, + HelpText<"Completely bypass resilience when accessing types in resilient frameworks">; + def group_info_path : Separate<["-"], "group-info-path">, HelpText<"The path to collect the group information of the compiled module">; diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index 1125bf77da1fc..dfbf752818a91 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -102,6 +102,11 @@ def driver_show_job_lifecycle : Flag<["-"], "driver-show-job-lifecycle">, HelpText<"Show every step in the lifecycle of driver jobs">; def driver_use_filelists : Flag<["-"], "driver-use-filelists">, InternalDebugOpt, HelpText<"Pass input files as filelists whenever possible">; +def driver_filelist_threshold : Separate<["-"], "driver-filelist-threshold">, + InternalDebugOpt, HelpText<"Pass input or output file names as filelists if there are more than ">, + MetaVarName<"">; +def driver_filelist_threshold_EQ : Joined<["-"], "driver-filelist-threshold=">, + Alias; def driver_batch_seed : Separate<["-"], "driver-batch-seed">, InternalDebugOpt, HelpText<"Use the given seed value to randomize batch-mode partitions">; @@ -350,6 +355,11 @@ def warn_swift3_objc_inference : Flag<["-"], "warn-swift3-objc-inference">, Alias, Flags<[FrontendOption, DoesNotAffectIncrementalBuild, HelpHidden]>; +def emit_public_type_metadata_accessors : + Flag<["-"], "emit-public-type-metadata-accessors">, + Flags<[FrontendOption]>, + HelpText<"Emit all type metadata accessors as public (deprecated: now does nothing)">; + def Rpass_EQ : Joined<["-"], "Rpass=">, Flags<[FrontendOption]>, HelpText<"Report performed transformations by optimization passes whose " @@ -745,6 +755,10 @@ def index_store_path : Separate<["-"], "index-store-path">, Flags<[FrontendOption, ArgumentIsPath]>, MetaVarName<"">, HelpText<"Store indexing data to ">; +def index_ignore_system_modules : Flag<["-"], "index-ignore-system-modules">, + Flags<[NoInteractiveOption]>, + HelpText<"Avoid indexing system modules">; + def enforce_exclusivity_EQ : Joined<["-"], "enforce-exclusivity=">, Flags<[FrontendOption]>, MetaVarName<"">, HelpText<"Enforce law of exclusivity">; diff --git a/include/swift/Parse/Lexer.h b/include/swift/Parse/Lexer.h index 644e9a73fd07d..d09502b03e2c9 100644 --- a/include/swift/Parse/Lexer.h +++ b/include/swift/Parse/Lexer.h @@ -192,6 +192,15 @@ class Lexer { lex(Result, LeadingTrivia, TrailingTrivia); } + /// Reset the lexer's buffer pointer to \p Offset bytes after the buffer + /// start. + void resetToOffset(size_t Offset) { + assert(BufferStart + Offset <= BufferEnd && "Offset after buffer end"); + + CurPtr = BufferStart + Offset; + lexImpl(); + } + bool isKeepingComments() const { return RetainComments == CommentRetentionMode::ReturnAsTokens; } @@ -318,7 +327,11 @@ class Lexer { /// Retrieve the string used to indent the line that contains the given /// source location. - static StringRef getIndentationForLine(SourceManager &SM, SourceLoc Loc); + /// + /// If \c ExtraIndentation is not null, it will be set to an appropriate + /// additional intendation for adding code in a smaller scope "within" \c Loc. + static StringRef getIndentationForLine(SourceManager &SM, SourceLoc Loc, + StringRef *ExtraIndentation = nullptr); /// \brief Determines if the given string is a valid non-operator /// identifier, without escaping characters. diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index 83330a47aa1a6..46d0091aa84f9 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -19,10 +19,9 @@ #include "swift/AST/ASTContext.h" #include "swift/AST/ASTNode.h" -#include "swift/AST/DiagnosticsParse.h" #include "swift/AST/Expr.h" +#include "swift/AST/DiagnosticsParse.h" #include "swift/AST/LayoutConstraint.h" -#include "swift/AST/Module.h" #include "swift/AST/Pattern.h" #include "swift/AST/Stmt.h" #include "swift/Basic/OptionSet.h" @@ -42,17 +41,18 @@ namespace llvm { } namespace swift { + class CodeCompletionCallbacks; class DefaultArgumentInitializer; + class DelayedParsingCallbacks; class DiagnosticEngine; + class Expr; class Lexer; - class ScopeInfo; - struct TypeLoc; - class TupleType; + class PersistentParserState; class SILParserTUStateBase; + class ScopeInfo; class SourceManager; - class PersistentParserState; - class CodeCompletionCallbacks; - class DelayedParsingCallbacks; + class TupleType; + struct TypeLoc; struct EnumElementInfo; @@ -181,9 +181,7 @@ class Parser { return L->isCodeCompletion() && !CodeCompletion; } - bool allowTopLevelCode() const { - return SF.isScriptMode(); - } + bool allowTopLevelCode() const; const std::vector &getSplitTokens() { return SplitTokens; } @@ -468,6 +466,16 @@ class Parser { return consumeToken(); } + SourceLoc consumeArgumentLabel(Identifier &Result) { + assert(Tok.canBeArgumentLabel()); + assert(Result.empty()); + if (!Tok.is(tok::kw__)) { + Tok.setKind(tok::identifier); + Result = Context.getIdentifier(Tok.getText()); + } + return consumeToken(); + } + /// \brief Retrieve the location just past the end of the previous /// source location. SourceLoc getEndOfPreviousLoc(); @@ -523,6 +531,13 @@ class Parser { /// \brief Skip until the next '#else', '#endif' or until eof. void skipUntilConditionalBlockClose(); + /// If the parser is generating only a syntax tree, try loading the current + /// node from a previously generated syntax tree. + /// Returns \c true if the node has been loaded and inserted into the current + /// syntax tree. In this case the parser should behave as if the node has + /// successfully been created. + bool loadCurrentSyntaxNodeFromCache(); + /// Parse an #endif. bool parseEndIfDirective(SourceLoc &Loc); @@ -677,7 +692,7 @@ class Parser { ParserStatus parseList(tok RightK, SourceLoc LeftLoc, SourceLoc &RightLoc, bool AllowSepAfterLast, Diag<> ErrorDiag, syntax::SyntaxKind Kind, - std::function callback); + llvm::function_ref callback); void consumeTopLevelDecl(ParserPosition BeginParserPosition, TopLevelCodeDecl *TLCD); @@ -763,17 +778,40 @@ class Parser { ParserStatus parseLineDirective(bool isLine = false); void setLocalDiscriminator(ValueDecl *D); + void setLocalDiscriminatorToParamList(ParameterList *PL); /// Parse the optional attributes before a declaration. bool parseDeclAttributeList(DeclAttributes &Attributes, bool &FoundCodeCompletionToken); + /// Parse the optional modifiers before a declaration. + bool parseDeclModifierList(DeclAttributes &Attributes, SourceLoc &StaticLoc, + StaticSpellingKind &StaticSpelling); + + /// Parse an availability attribute of the form + /// @available(*, introduced: 1.0, deprecated: 3.1). + /// \return \p nullptr if the platform name is invalid + ParserResult + parseExtendedAvailabilitySpecList(SourceLoc AtLoc, SourceLoc AttrLoc, + StringRef AttrName); + + /// Parse the Objective-C selector inside @objc + void parseObjCSelector(SmallVector &Names, + SmallVector &NameLocs, + bool &IsNullarySelector); + /// Parse the @_specialize attribute. /// \p closingBrace is the expected closing brace, which can be either ) or ] /// \p Attr is where to store the parsed attribute bool parseSpecializeAttribute(swift::tok ClosingBrace, SourceLoc AtLoc, SourceLoc Loc, SpecializeAttr *&Attr); + /// Parse the arguments inside the @_specialize attribute + bool parseSpecializeAttributeArguments( + swift::tok ClosingBrace, bool &DiscardAttribute, Optional &Exported, + Optional &Kind, + TrailingWhereClause *&TrailingWhereClause); + /// Parse the @_implements attribute. /// \p Attr is where to store the parsed attribute ParserResult parseImplementsAttribute(SourceLoc AtLoc, @@ -828,8 +866,7 @@ class Parser { ParserResult parseDeclStruct(ParseDeclOptions Flags, DeclAttributes &Attributes); ParserResult - parseDeclClass(SourceLoc ClassLoc, - ParseDeclOptions Flags, DeclAttributes &Attributes); + parseDeclClass(ParseDeclOptions Flags, DeclAttributes &Attributes); ParserResult parseDeclVar(ParseDeclOptions Flags, DeclAttributes &Attributes, SmallVectorImpl &Decls, @@ -988,7 +1025,7 @@ class Parser { /// Set the parsed context for all the initializers to the given /// function. - void setFunctionContext(DeclContext *DC, MutableArrayRef paramList); + void setFunctionContext(DeclContext *DC, ArrayRef paramList); DefaultArgumentInfo(bool inTypeContext) { NextIndex = inTypeContext ? 1 : 0; diff --git a/include/swift/Parse/SyntaxParsingCache.h b/include/swift/Parse/SyntaxParsingCache.h new file mode 100644 index 0000000000000..f5e088220604d --- /dev/null +++ b/include/swift/Parse/SyntaxParsingCache.h @@ -0,0 +1,102 @@ +//===----------- SyntaxParsingCache.h -================----------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_PARSE_SYNTAXPARSINGCACHE_H +#define SWIFT_PARSE_SYNTAXPARSINGCACHE_H + +#include "swift/Syntax/SyntaxNodes.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/raw_ostream.h" + +namespace { + +/// A single edit to the original source file in which a continuous range of +/// characters have been replaced by a new string +struct SourceEdit { + /// The byte offset from which on characters were replaced. + size_t Start; + + /// The byte offset to which on characters were replaced. + size_t End; + + /// The length of the string that replaced the range described above. + size_t ReplacementLength; + + /// The length of the range that has been replaced + size_t originalLength() { return End - Start; } + + /// Check if the characters replaced by this edit fall into the given range + /// or are directly adjacent to it + bool intersectsOrTouchesRange(size_t RangeStart, size_t RangeEnd) { + return !(End <= RangeStart || Start >= RangeEnd); + } +}; + +} // anonymous namespace + +namespace swift { + +using namespace swift::syntax; + +class SyntaxParsingCache { + /// The syntax tree prior to the edit + SourceFileSyntax OldSyntaxTree; + + /// The edits that were made from the source file that created this cache to + /// the source file that is now parsed incrementally + llvm::SmallVector Edits; + + /// Whether or not information about reused nodes shall be recored in + /// \c ReusedRanges + bool RecordReuseInformation = false; + + /// If \c RecordReuseInformation buffer offsets of ranges that have been + /// successfully looked up in this cache are stored. + std::vector> ReusedRanges; + +public: + SyntaxParsingCache(SourceFileSyntax OldSyntaxTree) + : OldSyntaxTree(OldSyntaxTree) {} + + /// Add an edit that transformed the source file which created this cache into + /// the source file that is now being parsed incrementally. The order in which + /// the edits are added using this method needs to be the same order in which + /// the edits were applied to the source file. + void addEdit(size_t Start, size_t End, size_t ReplacementLength) { + Edits.push_back({Start, End, ReplacementLength}); + } + + /// Check if a syntax node of the given kind at the given position can be + /// reused for a new syntax tree. + llvm::Optional lookUp(size_t NewPosition, SyntaxKind Kind); + + /// Turn recording of reused ranges on + void setRecordReuseInformation() { RecordReuseInformation = true; } + + /// Return the ranges of the new source file that have been successfully + /// looked up in this cache as a (start, end) pair of byte offsets in the + /// post-edit file. + std::vector> getReusedRanges() const { + return ReusedRanges; + } + +private: + llvm::Optional lookUpFrom(const Syntax &Node, size_t Position, + SyntaxKind Kind); + + bool nodeCanBeReused(const Syntax &Node, size_t Position, + SyntaxKind Kind) const; +}; + +} // namespace swift + +#endif // SWIFT_SYNTAX_PARSING_CACHE_H diff --git a/include/swift/Parse/SyntaxParsingContext.h b/include/swift/Parse/SyntaxParsingContext.h index 1a07f7b84315d..80e343be40730 100644 --- a/include/swift/Parse/SyntaxParsingContext.h +++ b/include/swift/Parse/SyntaxParsingContext.h @@ -20,6 +20,7 @@ namespace swift { class SourceFile; +class SyntaxParsingCache; class Token; class DiagnosticEngine; @@ -74,9 +75,17 @@ class alignas(1 << SyntaxAlignInBits) SyntaxParsingContext { // Storage for Collected parts. std::vector> Storage; + SyntaxArena &Arena; + + /// A cache of nodes that can be reused when creating the current syntax + /// tree + SyntaxParsingCache *SyntaxCache = nullptr; + RootContextData(SourceFile &SF, DiagnosticEngine &Diags, - SourceManager &SourceMgr, unsigned BufferID) - : SF(SF), Diags(Diags), SourceMgr(SourceMgr), BufferID(BufferID) {} + SourceManager &SourceMgr, unsigned BufferID, + SyntaxArena &Arena, SyntaxParsingCache *SyntaxCache) + : SF(SF), Diags(Diags), SourceMgr(SourceMgr), BufferID(BufferID), + Arena(Arena), SyntaxCache(SyntaxCache) {} }; private: @@ -97,6 +106,9 @@ class alignas(1 << SyntaxAlignInBits) SyntaxParsingContext { // Discard all parts in the context. Discard, + // The node has been loaded from the cache and all parts shall be discarded. + LoadedFromCache, + // Construct SourceFile syntax to the specified SF. Root, @@ -112,9 +124,7 @@ class alignas(1 << SyntaxAlignInBits) SyntaxParsingContext { // Reference to the SyntaxParsingContext *&CtxtHolder; - SyntaxArena &Arena; - - std::vector> &Storage; + RootContextData *RootData; // Offet for 'Storage' this context owns from. const size_t Offset; @@ -138,7 +148,7 @@ class alignas(1 << SyntaxAlignInBits) SyntaxParsingContext { void createNodeInPlace(SyntaxKind Kind, size_t N); ArrayRef> getParts() const { - return makeArrayRef(Storage).drop_front(Offset); + return makeArrayRef(getStorage()).drop_front(Offset); } RC makeUnknownSyntax(SyntaxKind Kind, @@ -154,11 +164,12 @@ class alignas(1 << SyntaxAlignInBits) SyntaxParsingContext { /// Designated constructor for child context. SyntaxParsingContext(SyntaxParsingContext *&CtxtHolder) : RootDataOrParent(CtxtHolder), CtxtHolder(CtxtHolder), - Arena(CtxtHolder->Arena), - Storage(CtxtHolder->Storage), Offset(Storage.size()), + RootData(CtxtHolder->RootData), Offset(RootData->Storage.size()), Enabled(CtxtHolder->isEnabled()) { assert(CtxtHolder->isTopOfContextStack() && "SyntaxParsingContext cannot have multiple children"); + assert(CtxtHolder->Mode != AccumulationMode::LoadedFromCache && + "Cannot create child context for a node loaded from the cache"); CtxtHolder = this; } @@ -174,20 +185,41 @@ class alignas(1 << SyntaxAlignInBits) SyntaxParsingContext { ~SyntaxParsingContext(); + /// Try loading the current node from the \c SyntaxParsingCache by looking up + /// if an unmodified node exists at \p LexerOffset of the same kind. If a node + /// is found, replace the node that is currently being constructed by the + /// parsing context with the node from the cache and return the number of + /// bytes the loaded node took up in the original source. The lexer should + /// pretend it has read these bytes and continue from the advanced offset. + /// If nothing is found \c 0 is returned. + size_t loadFromCache(size_t LexerOffset); + void disable() { Enabled = false; } bool isEnabled() const { return Enabled; } bool isRoot() const { return RootDataOrParent.is(); } bool isTopOfContextStack() const { return this == CtxtHolder; } - SyntaxParsingContext *getParent() { + SyntaxParsingContext *getParent() const { return RootDataOrParent.get(); } - RootContextData &getRootData() { - return *getRoot()->RootDataOrParent.get(); + RootContextData *getRootData() { return RootData; } + + const RootContextData *getRootData() const { return RootData; } + + std::vector> &getStorage() { return getRootData()->Storage; } + + const std::vector> &getStorage() const { + return getRootData()->Storage; + } + + SyntaxParsingCache *getSyntaxParsingCache() const { + return getRootData()->SyntaxCache; } - SyntaxParsingContext *getRoot(); + SyntaxArena &getArena() const { return getRootData()->Arena; } + + const SyntaxParsingContext *getRoot() const; /// Add RawSyntax to the parts. void addRawSyntax(RC Raw); @@ -201,6 +233,7 @@ class alignas(1 << SyntaxAlignInBits) SyntaxParsingContext { template llvm::Optional popIf() { + auto &Storage = getStorage(); assert(Storage.size() > Offset); if (auto Node = make(Storage.back()).getAs()) { Storage.pop_back(); @@ -210,6 +243,7 @@ class alignas(1 << SyntaxAlignInBits) SyntaxParsingContext { } TokenSyntax popToken() { + auto &Storage = getStorage(); assert(Storage.size() > Offset); assert(Storage.back()->getKind() == SyntaxKind::Token); auto Node = make(std::move(Storage.back())); @@ -255,6 +289,18 @@ class alignas(1 << SyntaxAlignInBits) SyntaxParsingContext { /// the syntax tree before closing the root context. void finalizeRoot(); + /// Make a missing node corresponding to the given token kind and text, and + /// push this node into the context. The synthesized node can help + /// the creation of valid syntax nodes. + void synthesize(tok Kind, StringRef Text = ""); + + /// Make a missing node corresponding to the given node kind, and + /// push this node into the context. + void synthesize(SyntaxKind Kind); + + /// Dump the nodes that are in the storage stack of the SyntaxParsingContext + LLVM_ATTRIBUTE_DEPRECATED(void dumpStorage() const LLVM_ATTRIBUTE_USED, + "Only meant for use in the debugger"); }; } // namespace swift diff --git a/include/swift/Parse/Token.h b/include/swift/Parse/Token.h index 0d61930e0d8d6..9d1a99a179751 100644 --- a/include/swift/Parse/Token.h +++ b/include/swift/Parse/Token.h @@ -22,6 +22,7 @@ #include "swift/Syntax/TokenKinds.h" #include "swift/Config.h" #include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSwitch.h" namespace swift { @@ -38,17 +39,17 @@ class Token { /// \brief Whether this token is the first token on the line. unsigned AtStartOfLine : 1; - /// \brief The length of the comment that precedes the token. - /// - /// Hopefully 128 Mib is enough. - unsigned CommentLength : 27; - /// \brief Whether this token is an escaped `identifier` token. unsigned EscapedIdentifier : 1; /// Modifiers for string literals unsigned MultilineString : 1; + // Padding bits == 32 - sizeof(Kind) * 8 - 3; + + /// \brief The length of the comment that precedes the token. + unsigned CommentLength; + /// Text - The actual string covered by the token in the source buffer. StringRef Text; @@ -59,15 +60,12 @@ class Token { } public: - Token() : Kind(tok::NUM_TOKENS), AtStartOfLine(false), CommentLength(0), - EscapedIdentifier(false) {} - - Token(tok Kind, StringRef Text, unsigned CommentLength) - : Kind(Kind), AtStartOfLine(false), CommentLength(CommentLength), - EscapedIdentifier(false), MultilineString(false), + Token(tok Kind, StringRef Text, unsigned CommentLength = 0) + : Kind(Kind), AtStartOfLine(false), EscapedIdentifier(false), + MultilineString(false), CommentLength(CommentLength), Text(Text) {} - Token(tok Kind, StringRef Text): Token(Kind, Text, 0) {}; + Token() : Token(tok::NUM_TOKENS, {}, 0) {} tok getKind() const { return Kind; } void setKind(tok K) { Kind = K; } @@ -139,34 +137,14 @@ class Token { if (isNot(tok::identifier) || isEscapedIdentifier() || Text.empty()) return false; - switch (Text[0]) { - case 'c': - return Text == "convenience"; - case 'd': - return Text == "dynamic"; - case 'f': - return Text == "final"; - case 'i': - return Text == "indirect" || Text == "infix"; - case 'l': - return Text == "lazy"; - case 'm': - return Text == "mutating"; - case 'n': - return Text == "nonmutating"; - case 'o': - return Text == "open" || Text == "override" || Text == "optional"; - case 'p': - return Text == "prefix" || Text == "postfix"; - case 'r': - return Text == "required"; - case 'u': - return Text == "unowned"; - case 'w': - return Text == "weak"; - default: - return false; - } + return llvm::StringSwitch(Text) +#define CONTEXTUAL_CASE(KW) .Case(#KW, true) +#define CONTEXTUAL_DECL_ATTR(KW, ...) CONTEXTUAL_CASE(KW) +#define CONTEXTUAL_DECL_ATTR_ALIAS(KW, ...) CONTEXTUAL_CASE(KW) +#define CONTEXTUAL_SIMPLE_DECL_ATTR(KW, ...) CONTEXTUAL_CASE(KW) +#include "swift/AST/Attr.def" +#undef CONTEXTUAL_CASE + .Default(false); } bool isContextualPunctuator(StringRef ContextPunc) const { diff --git a/include/swift/Reflection/ReflectionContext.h b/include/swift/Reflection/ReflectionContext.h index 92c3b2d5ecd16..a58d9e64c26c9 100644 --- a/include/swift/Reflection/ReflectionContext.h +++ b/include/swift/Reflection/ReflectionContext.h @@ -38,9 +38,9 @@ #if defined(__APPLE__) && defined(__MACH__) #ifndef __LP64__ -typedef const struct mach_header MachHeader; +using MachHeader = const struct mach_header; #else -typedef const struct mach_header_64 MachHeader; +using MachHeader = const struct mach_header_64; #endif #endif @@ -148,14 +148,14 @@ class ReflectionContext // The docs say "not all sections may be present." We'll succeed if ANY of // them are present. Not sure if that's the right thing to do. - auto FieldMd = findSection(Header, "__swift4_fieldmd"); + auto FieldMd = findSection(Header, "__swift5_fieldmd"); auto AssocTyMd = - findSection(Header, "__swift4_assocty"); + findSection(Header, "__swift5_assocty"); auto BuiltinTyMd = - findSection(Header, "__swift4_builtin"); - auto CaptureMd = findSection(Header, "__swift4_capture"); - auto TyperefMd = findSection(Header, "__swift4_typeref"); - auto ReflStrMd = findSection(Header, "__swift4_reflstr"); + findSection(Header, "__swift5_builtin"); + auto CaptureMd = findSection(Header, "__swift5_capture"); + auto TyperefMd = findSection(Header, "__swift5_typeref"); + auto ReflStrMd = findSection(Header, "__swift5_reflstr"); bool success = FieldMd.second || AssocTyMd.second || BuiltinTyMd.second || CaptureMd.second || TyperefMd.second || ReflStrMd.second; diff --git a/include/swift/Reflection/TypeRef.h b/include/swift/Reflection/TypeRef.h index 8894627f8058c..295bc526a01f0 100644 --- a/include/swift/Reflection/TypeRef.h +++ b/include/swift/Reflection/TypeRef.h @@ -41,19 +41,19 @@ enum class TypeRefKind { // MSVC reports an error if we use "template" // Clang reports an error if we don't use "template" #if defined(__clang__) || defined(__GNUC__) -#define DEPENDENT_TEMPLATE template +# define DEPENDENT_TEMPLATE template #else -#define DEPENDENT_TEMPLATE +# define DEPENDENT_TEMPLATE #endif #define FIND_OR_CREATE_TYPEREF(Allocator, TypeRefTy, ...) \ auto ID = Profile(__VA_ARGS__); \ - const auto Entry = Allocator.DEPENDENT_TEMPLATE TypeRefTy##s.find(ID); \ - if (Entry != Allocator.DEPENDENT_TEMPLATE TypeRefTy##s.end()) \ + const auto Entry = Allocator.TypeRefTy##s.find(ID); \ + if (Entry != Allocator.TypeRefTy##s.end()) \ return Entry->second; \ const auto TR = \ Allocator.DEPENDENT_TEMPLATE makeTypeRef(__VA_ARGS__); \ - Allocator.DEPENDENT_TEMPLATE TypeRefTy##s.insert({ID, TR}); \ + Allocator.TypeRefTy##s.insert({ID, TR}); \ return TR; /// An identifier containing the unique bit pattern made up of all of the diff --git a/include/swift/Remote/MetadataReader.h b/include/swift/Remote/MetadataReader.h index cbf3f0572d80b..2173e255622b6 100644 --- a/include/swift/Remote/MetadataReader.h +++ b/include/swift/Remote/MetadataReader.h @@ -441,7 +441,8 @@ class MetadataReader { case MetadataKind::ErrorObject: // Treat these all as Builtin.NativeObject for type lowering purposes. return Builder.createBuiltinType("Bo"); - case MetadataKind::Opaque: { + case MetadataKind::Opaque: + default: { auto BuiltOpaque = Builder.getOpaqueType(); TypeCache[MetadataAddress] = BuiltOpaque; return BuiltOpaque; @@ -924,8 +925,6 @@ class MetadataReader { return _readMetadata(address); case MetadataKind::ObjCClassWrapper: return _readMetadata(address); - case MetadataKind::Opaque: - return _readMetadata(address); case MetadataKind::Optional: return _readMetadata(address); case MetadataKind::Struct: @@ -946,6 +945,9 @@ class MetadataReader { return _readMetadata(address, totalSize); } + case MetadataKind::Opaque: + default: + return _readMetadata(address); } // We can fall out here if the value wasn't actually a valid @@ -1491,7 +1493,7 @@ class MetadataReader { namespace llvm { template struct simplify_type> { - typedef const T *SimpleType; + using SimpleType = const T *; static SimpleType getSimplifiedValue(swift::remote::RemoteRef value) { return value.getLocalBuffer(); diff --git a/include/swift/Runtime/Atomic.h b/include/swift/Runtime/Atomic.h new file mode 100644 index 0000000000000..3f9c1a3edf5be --- /dev/null +++ b/include/swift/Runtime/Atomic.h @@ -0,0 +1,31 @@ +//===--- Atomic.h - Utilities for atomic operations. ------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Utilities for atomic operations, to use with std::atomic. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_RUNTIME_ATOMIC_H +#define SWIFT_RUNTIME_ATOMIC_H + +// FIXME: Workaround for rdar://problem/18889711. 'Consume' does not require +// a barrier on ARM64, but LLVM doesn't know that. Although 'relaxed' +// is formally UB by C++11 language rules, we should be OK because neither +// the processor model nor the optimizer can realistically reorder our uses +// of 'consume'. +#if __arm64__ || __arm__ +# define SWIFT_MEMORY_ORDER_CONSUME (std::memory_order_relaxed) +#else +# define SWIFT_MEMORY_ORDER_CONSUME (std::memory_order_consume) +#endif + +#endif diff --git a/include/swift/Runtime/Concurrent.h b/include/swift/Runtime/Concurrent.h index 7c85ce50b8806..003088c485fbf 100644 --- a/include/swift/Runtime/Concurrent.h +++ b/include/swift/Runtime/Concurrent.h @@ -12,10 +12,15 @@ #ifndef SWIFT_RUNTIME_CONCURRENTUTILS_H #define SWIFT_RUNTIME_CONCURRENTUTILS_H #include +#include #include #include #include +#include #include "llvm/Support/Allocator.h" +#include "Atomic.h" +#include "Debug.h" +#include "Mutex.h" #if defined(__FreeBSD__) || defined(__CYGWIN__) || defined(__HAIKU__) #include @@ -406,6 +411,136 @@ class ConcurrentMap } }; + +/// An append-only array that can be read without taking locks. Writes +/// are still locked and serialized, but only with respect to other +/// writes. +template struct ConcurrentReadableArray { +private: + /// The struct used for the array's storage. The `Elem` member is + /// considered to be the first element of a variable-length array, + /// whose size is determined by the allocation. The `Capacity` member + /// from `ConcurrentReadableArray` indicates how large it can be. + struct Storage { + std::atomic Count; + typename std::aligned_storage::type Elem; + + static Storage *allocate(size_t capacity) { + auto size = sizeof(Storage) + (capacity - 1) * sizeof(Storage().Elem); + auto *ptr = reinterpret_cast(malloc(size)); + if (!ptr) swift::crash("Could not allocate memory."); + ptr->Count.store(0, std::memory_order_relaxed); + return ptr; + } + + void deallocate() { + for (size_t i = 0; i < Count; i++) { + data()[i].~ElemTy(); + } + free(this); + } + + ElemTy *data() { + return reinterpret_cast(&Elem); + } + }; + + size_t Capacity; + std::atomic ReaderCount; + std::atomic Elements; + Mutex WriterLock; + std::vector FreeList; + + void incrementReaders() { + ReaderCount.fetch_add(1, std::memory_order_acquire); + } + + void decrementReaders() { + ReaderCount.fetch_sub(1, std::memory_order_release); + } + + void deallocateFreeList() { + for (Storage *storage : FreeList) + storage->deallocate(); + FreeList.clear(); + FreeList.shrink_to_fit(); + } + +public: + struct Snapshot { + ConcurrentReadableArray *Array; + const ElemTy *Start; + size_t Count; + + Snapshot(ConcurrentReadableArray *array, const ElemTy *start, size_t count) + : Array(array), Start(start), Count(count) {} + + Snapshot(const Snapshot &other) + : Array(other.Array), Start(other.Start), Count(other.Count) { + Array->incrementReaders(); + } + + ~Snapshot() { + Array->decrementReaders(); + } + + const ElemTy *begin() { return Start; } + const ElemTy *end() { return Start + Count; } + size_t count() { return Count; } + }; + + // This type cannot be safely copied, moved, or deleted. + ConcurrentReadableArray(const ConcurrentReadableArray &) = delete; + ConcurrentReadableArray(ConcurrentReadableArray &&) = delete; + ConcurrentReadableArray &operator=(const ConcurrentReadableArray &) = delete; + + ConcurrentReadableArray() : Capacity(0), ReaderCount(0), Elements(nullptr) {} + + ~ConcurrentReadableArray() { + assert(ReaderCount.load(std::memory_order_acquire) == 0 && + "deallocating ConcurrentReadableArray with outstanding snapshots"); + deallocateFreeList(); + } + + void push_back(const ElemTy &elem) { + ScopedLock guard(WriterLock); + + auto *storage = Elements.load(std::memory_order_relaxed); + auto count = storage ? storage->Count.load(std::memory_order_relaxed) : 0; + if (count >= Capacity) { + auto newCapacity = std::max((size_t)16, count * 2); + auto *newStorage = Storage::allocate(newCapacity); + if (storage) { + std::copy(storage->data(), storage->data() + count, newStorage->data()); + newStorage->Count.store(count, std::memory_order_relaxed); + FreeList.push_back(storage); + } + + storage = newStorage; + Capacity = newCapacity; + Elements.store(storage, std::memory_order_release); + } + + new(&storage->data()[count]) ElemTy(elem); + storage->Count.store(count + 1, std::memory_order_release); + + if (ReaderCount.load(std::memory_order_acquire) == 0) + deallocateFreeList(); + } + + Snapshot snapshot() { + incrementReaders(); + auto *storage = Elements.load(SWIFT_MEMORY_ORDER_CONSUME); + if (storage == nullptr) { + return Snapshot(this, nullptr, 0); + } + + auto count = storage->Count.load(std::memory_order_acquire); + const auto *ptr = storage->data(); + return Snapshot(this, ptr, count); + } +}; + } // end namespace swift #endif // SWIFT_RUNTIME_CONCURRENTUTILS_H diff --git a/include/swift/Runtime/Debug.h b/include/swift/Runtime/Debug.h index a6a36bcc52ed2..0ecc8f860a245 100644 --- a/include/swift/Runtime/Debug.h +++ b/include/swift/Runtime/Debug.h @@ -46,19 +46,19 @@ extern struct crashreporter_annotations_t gCRAnnotations; } LLVM_ATTRIBUTE_ALWAYS_INLINE -static void CRSetCrashLogMessage(const char *message) { +static inline void CRSetCrashLogMessage(const char *message) { gCRAnnotations.message = reinterpret_cast(message); } LLVM_ATTRIBUTE_ALWAYS_INLINE -static const char *CRGetCrashLogMessage() { +static inline const char *CRGetCrashLogMessage() { return reinterpret_cast(gCRAnnotations.message); } #else LLVM_ATTRIBUTE_ALWAYS_INLINE -static void CRSetCrashLogMessage(const char *) {} +static inline void CRSetCrashLogMessage(const char *) {} #endif @@ -82,13 +82,6 @@ static inline void crash(const char *message) { swift_runtime_unreachable("Expected compiler to crash."); } -/// Report a corrupted type object. -LLVM_ATTRIBUTE_NORETURN -LLVM_ATTRIBUTE_ALWAYS_INLINE // Minimize trashed registers -static inline void _failCorruptType(const Metadata *type) { - swift::crash("Corrupt Swift type object"); -} - // swift::fatalError() halts with a crash log message, // but makes no attempt to preserve register state. LLVM_ATTRIBUTE_NORETURN diff --git a/include/swift/Runtime/Exclusivity.h b/include/swift/Runtime/Exclusivity.h index aa75a4647fcd0..d3a4ffcffd45a 100644 --- a/include/swift/Runtime/Exclusivity.h +++ b/include/swift/Runtime/Exclusivity.h @@ -58,6 +58,19 @@ void swift_endAccess(ValueBuffer *buffer); SWIFT_RUNTIME_EXPORT bool _swift_disableExclusivityChecking; +#ifndef NDEBUG + +/// Dump all accesses currently tracked by the runtime. +/// +/// This is a debug routine that is intended to be used from the debugger and is +/// compiled out when asserts are disabled. The intention is that it allows one +/// to dump the access state to easily see if/when exclusivity violations will +/// happen. This eases debugging. +SWIFT_RUNTIME_EXPORT +void swift_dumpTrackedAccesses(); + +#endif + } // end namespace swift #endif diff --git a/include/swift/Runtime/HeapObject.h b/include/swift/Runtime/HeapObject.h index 1aa1eb28975f5..e5ac69da8d90b 100644 --- a/include/swift/Runtime/HeapObject.h +++ b/include/swift/Runtime/HeapObject.h @@ -231,6 +231,8 @@ SWIFT_RUNTIME_EXPORT size_t swift_retainCount(HeapObject *object); SWIFT_RUNTIME_EXPORT size_t swift_unownedRetainCount(HeapObject *object); +SWIFT_RUNTIME_EXPORT +size_t swift_weakRetainCount(HeapObject *object); /// Is this pointer a non-null unique reference to an object /// that uses Swift reference counting? @@ -284,11 +286,19 @@ bool swift_isUniquelyReferencedOrPinned_nonNull_native( /// one. /// This runtime call will print an error message with file name and location if /// the closure is escaping but it will not abort. +/// +/// \p type: 0 - withoutActuallyEscaping verification +/// Was the closure passed to a withoutActuallyEscaping block +/// escaped in the block? +/// 1 - @objc closure sentinel verfication +/// Was the closure passed to Objective-C escaped? SWIFT_RUNTIME_EXPORT bool swift_isEscapingClosureAtFileLocation(const struct HeapObject *object, const unsigned char *filename, int32_t filenameLength, - int32_t line); + int32_t line, + int32_t column, + unsigned type); /// Deallocate the given memory. /// diff --git a/include/swift/Runtime/Metadata.h b/include/swift/Runtime/Metadata.h index b68d5503edd3f..897f2b6dbe9df 100644 --- a/include/swift/Runtime/Metadata.h +++ b/include/swift/Runtime/Metadata.h @@ -278,13 +278,15 @@ struct TargetValueBuffer { using ValueBuffer = TargetValueBuffer; /// Can a value with the given size and alignment be allocated inline? -constexpr inline bool canBeInline(size_t size, size_t alignment) { - return size <= sizeof(ValueBuffer) && alignment <= alignof(ValueBuffer); +constexpr inline bool canBeInline(bool isBitwiseTakable, size_t size, + size_t alignment) { + return isBitwiseTakable && size <= sizeof(ValueBuffer) && + alignment <= alignof(ValueBuffer); } template -constexpr inline bool canBeInline() { - return canBeInline(sizeof(T), alignof(T)); +constexpr inline bool canBeInline(bool isBitwiseTakable) { + return canBeInline(isBitwiseTakable, sizeof(T), alignof(T)); } struct ValueWitnessTable; @@ -345,8 +347,8 @@ struct ValueWitnessTable { /// Would values of a type with the given layout requirements be /// allocated inline? - static bool isValueInline(size_t size, size_t alignment) { - return (size <= sizeof(ValueBuffer) && + static bool isValueInline(bool isBitwiseTakable, size_t size, size_t alignment) { + return (isBitwiseTakable && size <= sizeof(ValueBuffer) && alignment <= alignof(ValueBuffer)); } @@ -819,22 +821,9 @@ struct TargetMetadata { case MetadataKind::ForeignClass: return true; - case MetadataKind::Function: - case MetadataKind::Struct: - case MetadataKind::Enum: - case MetadataKind::Optional: - case MetadataKind::Opaque: - case MetadataKind::Tuple: - case MetadataKind::Existential: - case MetadataKind::Metatype: - case MetadataKind::ExistentialMetatype: - case MetadataKind::HeapLocalVariable: - case MetadataKind::HeapGenericLocalVariable: - case MetadataKind::ErrorObject: + default: return false; } - - swift_runtime_unreachable("Unhandled MetadataKind in switch."); } /// Is this metadata for an existential type? @@ -843,24 +832,10 @@ struct TargetMetadata { case MetadataKind::ExistentialMetatype: case MetadataKind::Existential: return true; - - case MetadataKind::Metatype: - case MetadataKind::Class: - case MetadataKind::ObjCClassWrapper: - case MetadataKind::ForeignClass: - case MetadataKind::Struct: - case MetadataKind::Enum: - case MetadataKind::Optional: - case MetadataKind::Opaque: - case MetadataKind::Tuple: - case MetadataKind::Function: - case MetadataKind::HeapLocalVariable: - case MetadataKind::HeapGenericLocalVariable: - case MetadataKind::ErrorObject: + + default: return false; } - - swift_runtime_unreachable("Unhandled MetadataKind in switch."); } /// Is this either type metadata or a class object for any kind of class? @@ -944,20 +919,9 @@ struct TargetMetadata { case MetadataKind::ForeignClass: return static_cast *>(this) ->Description; - case MetadataKind::Opaque: - case MetadataKind::Tuple: - case MetadataKind::Function: - case MetadataKind::Existential: - case MetadataKind::ExistentialMetatype: - case MetadataKind::Metatype: - case MetadataKind::ObjCClassWrapper: - case MetadataKind::HeapLocalVariable: - case MetadataKind::HeapGenericLocalVariable: - case MetadataKind::ErrorObject: + default: return nullptr; } - - swift_runtime_unreachable("Unhandled MetadataKind in switch."); } /// Get the class object for this type if it has one, or return null if the @@ -1706,12 +1670,12 @@ struct TargetStructMetadata : public TargetValueMetadata { // argument array. /// Get a pointer to the field offset vector, if present, or null. - const StoredPointer *getFieldOffsets() const { + const uint32_t *getFieldOffsets() const { auto offset = getDescription()->FieldOffsetVectorOffset; if (offset == 0) return nullptr; auto asWords = reinterpret_cast(this); - return reinterpret_cast(asWords + offset); + return reinterpret_cast(asWords + offset); } static constexpr int32_t getGenericArgumentOffset() { @@ -4154,6 +4118,10 @@ swift_getObjCClassMetadata(const ClassMetadata *theClass); SWIFT_RUNTIME_EXPORT const ClassMetadata * swift_getObjCClassFromMetadata(const Metadata *theClass); + +SWIFT_RUNTIME_EXPORT +const ClassMetadata * +swift_getObjCClassFromObject(HeapObject *object); #endif /// \brief Fetch a unique type metadata object for a foreign type. @@ -4161,6 +4129,13 @@ SWIFT_RUNTIME_EXPORT const ForeignTypeMetadata * swift_getForeignTypeMetadata(ForeignTypeMetadata *nonUnique); +/// \brief Fetch a unique witness table for a foreign witness table. +SWIFT_RUNTIME_EXPORT +const WitnessTable * +swift_getForeignWitnessTable(const WitnessTable *nonUniqueWitnessCandidate, + const TypeContextDescriptor *forForeignType, + const ProtocolDescriptor *forProtocol); + /// \brief Fetch a uniqued metadata for a tuple type. /// /// The labels argument is null if and only if there are no element @@ -4212,7 +4187,7 @@ void swift_initStructMetadata(StructMetadata *self, StructLayoutFlags flags, size_t numFields, const TypeLayout * const *fieldTypes, - size_t *fieldOffsets); + uint32_t *fieldOffsets); /// Relocate the metadata for a class and copy fields from the given template. /// The final size of the metadata is calculated at runtime from the size of @@ -4385,6 +4360,12 @@ void swift_getFieldAt( const Metadata *type, unsigned index, std::function callback); +#if !NDEBUG +/// Verify that the given metadata pointer correctly roundtrips its +/// mangled name through the demangler. +void verifyMangledNameRoundtrip(const Metadata *metadata); +#endif + } // end namespace swift #pragma clang diagnostic pop diff --git a/include/swift/Runtime/RuntimeFunctions.def b/include/swift/Runtime/RuntimeFunctions.def index 3600d5f11ca38..05a97216cd5d9 100644 --- a/include/swift/Runtime/RuntimeFunctions.def +++ b/include/swift/Runtime/RuntimeFunctions.def @@ -229,7 +229,7 @@ FUNCTION(BridgeObjectRetainN, swift_bridgeObjectRetain_n, C_CC, RETURNS(BridgeObjectPtrTy), ARGS(BridgeObjectPtrTy, Int32Ty), - ATTRS(NoUnwind)) + ATTRS(NoUnwind, FirstParamReturned)) // void swift_bridgeObjectRelease_n(void *ptr, int32_t n); FUNCTION(BridgeObjectReleaseN, swift_bridgeObjectRelease_n, @@ -243,7 +243,7 @@ FUNCTION(NonAtomicBridgeObjectRetainN, swift_nonatomic_bridgeObjectRetain_n, C_CC, RETURNS(BridgeObjectPtrTy), ARGS(BridgeObjectPtrTy, Int32Ty), - ATTRS(NoUnwind)) + ATTRS(NoUnwind, FirstParamReturned)) // void swift_nonatomic_bridgeObjectRelease_n(void *ptr, int32_t n); FUNCTION(NonAtomicBridgeObjectReleaseN, swift_nonatomic_bridgeObjectRelease_n, @@ -328,7 +328,7 @@ FUNCTION(NonAtomicUnknownRelease, swift_nonatomic_unknownRelease, C_CC, FUNCTION(BridgeObjectStrongRetain, swift_bridgeObjectRetain, C_CC, RETURNS(BridgeObjectPtrTy), ARGS(BridgeObjectPtrTy), - ATTRS(NoUnwind)) + ATTRS(NoUnwind, FirstParamReturned)) // void swift_bridgeRelease(void *ptr); FUNCTION(BridgeObjectStrongRelease, swift_bridgeObjectRelease, C_CC, @@ -340,7 +340,7 @@ FUNCTION(BridgeObjectStrongRelease, swift_bridgeObjectRelease, C_CC, FUNCTION(NonAtomicBridgeObjectStrongRetain, swift_nonatomic_bridgeObjectRetain, C_CC, RETURNS(BridgeObjectPtrTy), ARGS(BridgeObjectPtrTy), - ATTRS(NoUnwind)) + ATTRS(NoUnwind, FirstParamReturned)) // void swift_nonatomic_bridgeRelease(void *ptr); FUNCTION(NonAtomicBridgeObjectStrongRelease, swift_nonatomic_bridgeObjectRelease, C_CC, @@ -680,11 +680,13 @@ FUNCTION(IsUniquelyReferencedOrPinned_nonNull_native, // bool swift_isEscapingClosureAtFileLocation(const struct HeapObject *object, // const unsigned char *filename, // int32_t filenameLength, -// int32_t line); +// int32_t line, +// int32_t col, +// unsigned type); FUNCTION(IsEscapingClosureAtFileLocation, swift_isEscapingClosureAtFileLocation, C_CC, RETURNS(Int1Ty), - ARGS(RefCountedPtrTy, Int8PtrTy, Int32Ty, Int32Ty), + ARGS(RefCountedPtrTy, Int8PtrTy, Int32Ty, Int32Ty, Int32Ty, Int32Ty), ATTRS(NoUnwind, ZExt)) // void swift_arrayInitWithCopy(opaque*, opaque*, size_t, type*); @@ -801,6 +803,15 @@ FUNCTION(GetForeignTypeMetadata, swift_getForeignTypeMetadata, C_CC, ARGS(TypeMetadataPtrTy), ATTRS(NoUnwind, ReadNone)) // only writes to runtime-private fields +// WitnessTable *swift_getForeignWitnessTable(const WitnessTable *candidate, +// const TypeContextDescriptor +// *forType); +FUNCTION(GetForeignWitnessTable, swift_getForeignWitnessTable, C_CC, + RETURNS(WitnessTablePtrTy), + ARGS(WitnessTablePtrTy, TypeContextDescriptorPtrTy, + ProtocolDescriptorPtrTy), + ATTRS(NoUnwind, ReadNone)) + // MetadataResponse swift_getGenericMetadata(MetadataRequest request, // const void * const *arguments, // TypeContextDescriptor *type); @@ -827,7 +838,7 @@ FUNCTION(AllocateGenericValueMetadata, swift_allocateGenericValueMetadata, ATTRS(NoUnwind)) // MetadataResponse swift_checkMetadataState(MetadataRequest request, -// cosnt Metadata *type); +// const Metadata *type); FUNCTION(CheckMetadataState, swift_checkMetadataState, SwiftCC, RETURNS(TypeMetadataResponseTy), ARGS(SizeTy, TypeMetadataPtrTy), @@ -869,6 +880,12 @@ FUNCTION(GetObjCClassFromMetadata, swift_getObjCClassFromMetadata, C_CC, ARGS(TypeMetadataPtrTy), ATTRS(NoUnwind, ReadNone)) +// Metadata *swift_getObjCClassFromObject(id object); +FUNCTION(GetObjCClassFromObject, swift_getObjCClassFromObject, C_CC, + RETURNS(ObjCClassPtrTy), + ARGS(ObjCPtrTy), + ATTRS(NoUnwind, ReadNone)) + // MetadataResponse swift_getTupleTypeMetadata(MetadataRequest request, // TupleTypeFlags flags, // Metadata * const *elts, @@ -942,13 +959,13 @@ FUNCTION(InitClassMetadata, // StructLayoutFlags flags, // size_t numFields, // TypeLayout * const *fieldTypes, -// size_t *fieldOffsets); +// uint32_t *fieldOffsets); FUNCTION(InitStructMetadata, swift_initStructMetadata, C_CC, RETURNS(VoidTy), ARGS(TypeMetadataPtrTy, SizeTy, SizeTy, Int8PtrPtrTy->getPointerTo(0), - SizeTy->getPointerTo()), + Int32Ty->getPointerTo()), ATTRS(NoUnwind)) // void swift_initEnumMetadataSingleCase(Metadata *enumType, diff --git a/include/swift/SIL/BranchPropagatedUser.h b/include/swift/SIL/BranchPropagatedUser.h new file mode 100644 index 0000000000000..c726693200620 --- /dev/null +++ b/include/swift/SIL/BranchPropagatedUser.h @@ -0,0 +1,121 @@ +//===--- BranchPropagatedUser.h -------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SIL_BRANCHPROPAGATEDUSER_H +#define SWIFT_SIL_BRANCHPROPAGATEDUSER_H + +#include "swift/SIL/SILBasicBlock.h" +#include "swift/SIL/SILInstruction.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/Support/PointerLikeTypeTraits.h" + +namespace swift { + +/// This is a class that models normal users and also cond_br users that are +/// associated with the block in the target block. This is safe to do since in +/// Semantic SIL, cond_br with non-trivial arguments are not allowed to have +/// critical edges. In non-semantic SIL, it is expected that any user of +/// BranchPropagatedUser and friends break all such critical edges. +class BranchPropagatedUser { + using InnerTy = llvm::PointerIntPair; + InnerTy User; + +public: + BranchPropagatedUser(SILInstruction *I) : User(I) { + assert(!isa(I)); + } + + BranchPropagatedUser(CondBranchInst *I) : User(I) {} + + BranchPropagatedUser(CondBranchInst *I, unsigned SuccessorIndex) + : User(I, SuccessorIndex) { + assert(SuccessorIndex == CondBranchInst::TrueIdx || + SuccessorIndex == CondBranchInst::FalseIdx); + } + + BranchPropagatedUser(const BranchPropagatedUser &Other) : User(Other.User) {} + BranchPropagatedUser &operator=(const BranchPropagatedUser &Other) { + User = Other.User; + return *this; + } + + operator SILInstruction *() { return User.getPointer(); } + operator const SILInstruction *() const { return User.getPointer(); } + + SILInstruction *getInst() const { return User.getPointer(); } + + SILBasicBlock *getParent() const { + if (!isCondBranchUser()) { + return getInst()->getParent(); + } + + auto *CBI = cast(getInst()); + unsigned Number = getCondBranchSuccessorID(); + if (Number == CondBranchInst::TrueIdx) + return CBI->getTrueBB(); + return CBI->getFalseBB(); + } + + bool isCondBranchUser() const { + return isa(User.getPointer()); + } + + unsigned getCondBranchSuccessorID() const { + assert(isCondBranchUser()); + return User.getInt(); + } + + SILBasicBlock::iterator getIterator() const { + return User.getPointer()->getIterator(); + } + + void *getAsOpaqueValue() const { + return llvm::PointerLikeTypeTraits::getAsVoidPointer(User); + } + + static BranchPropagatedUser getFromOpaqueValue(void *p) { + InnerTy TmpUser = + llvm::PointerLikeTypeTraits::getFromVoidPointer(p); + if (auto *CBI = dyn_cast(TmpUser.getPointer())) { + return BranchPropagatedUser(CBI, TmpUser.getInt()); + } + return BranchPropagatedUser(TmpUser.getPointer()); + } + + enum { + NumLowBitsAvailable = + llvm::PointerLikeTypeTraits::NumLowBitsAvailable + }; +}; + +} // namespace swift + +namespace llvm { + +template <> struct PointerLikeTypeTraits { +public: + using BranchPropagatedUser = swift::BranchPropagatedUser; + + static void *getAsVoidPointer(BranchPropagatedUser v) { + return v.getAsOpaqueValue(); + } + + static BranchPropagatedUser getFromVoidPointer(void *p) { + return BranchPropagatedUser::getFromOpaqueValue(p); + } + + enum { NumLowBitsAvailable = BranchPropagatedUser::NumLowBitsAvailable }; +}; + +} // namespace llvm + +#endif diff --git a/include/swift/SIL/CFG.h b/include/swift/SIL/CFG.h index 11fb15d760bf2..9d19165680048 100644 --- a/include/swift/SIL/CFG.h +++ b/include/swift/SIL/CFG.h @@ -92,7 +92,7 @@ struct GraphTraits static NodeRef getEntryNode(GraphType F) { return &F->front(); } - typedef pointer_iterator nodes_iterator; + using nodes_iterator = pointer_iterator; static nodes_iterator nodes_begin(GraphType F) { return nodes_iterator(F->begin()); } @@ -104,12 +104,12 @@ struct GraphTraits template <> struct GraphTraits > : public GraphTraits > { - typedef Inverse GraphType; - typedef NodeRef NodeRef; + using GraphType = Inverse; + using NodeRef = NodeRef; static NodeRef getEntryNode(GraphType F) { return &F.Graph->front(); } - typedef pointer_iterator nodes_iterator; + using nodes_iterator = pointer_iterator; static nodes_iterator nodes_begin(GraphType F) { return nodes_iterator(F.Graph->begin()); } diff --git a/include/swift/SIL/DebugUtils.h b/include/swift/SIL/DebugUtils.h index 3df6dd5f3aa31..823dea6eccb3c 100644 --- a/include/swift/SIL/DebugUtils.h +++ b/include/swift/SIL/DebugUtils.h @@ -43,18 +43,12 @@ namespace swift { class SILInstruction; -/// Returns true if the instruction \p Inst is an instruction which is only -/// relevant for debug information and has no other impact on program semantics. -inline bool isDebugInst(SILInstruction *Inst) { - return isa(Inst) || isa(Inst); -} - /// Deletes all of the debug instructions that use \p Inst. inline void deleteAllDebugUses(ValueBase *Inst) { for (auto UI = Inst->use_begin(), E = Inst->use_end(); UI != E;) { auto *Inst = UI->getUser(); UI++; - if (isDebugInst(Inst)) + if (Inst->isDebugInstruction()) Inst->eraseFromParent(); } } @@ -76,7 +70,7 @@ template class DebugUseIterator return; SILInstruction *User = BaseIterator->getUser(); - if (isDebugInst(User) != nonDebugInsts) + if (User->isDebugInstruction() != nonDebugInsts) return; BaseIterator++; @@ -188,7 +182,7 @@ inline void eraseFromParentWithDebugInsts(SILInstruction *I, while (!result->use_empty()) { foundAny = true; auto *User = result->use_begin()->getUser(); - assert(isDebugInst(User)); + assert(User->isDebugInstruction()); if (InstIter != SILBasicBlock::iterator() && InstIter != I->getParent()->end() && &*InstIter == User) { @@ -210,6 +204,11 @@ inline void eraseFromParentWithDebugInsts(SILInstruction *I) { eraseFromParentWithDebugInsts(I, nullIter); } +/// Return true if the def-use graph rooted at \p V contains any non-debug, +/// non-trivial users. +bool hasNonTrivialNonDebugTransitiveUsers( + PointerUnion V); + } // end namespace swift #endif /* SWIFT_SIL_DEBUGUTILS_H */ diff --git a/include/swift/SIL/Dominance.h b/include/swift/SIL/Dominance.h index 091a9a60ac9c3..505c1cd9749df 100644 --- a/include/swift/SIL/Dominance.h +++ b/include/swift/SIL/Dominance.h @@ -192,7 +192,7 @@ namespace llvm { /// iterable by generic graph iterators. template <> struct GraphTraits { using ChildIteratorType = swift::DominanceInfoNode::iterator; - typedef swift::DominanceInfoNode *NodeRef; + using NodeRef = swift::DominanceInfoNode *; static NodeRef getEntryNode(NodeRef N) { return N; } static inline ChildIteratorType child_begin(NodeRef N) { return N->begin(); } @@ -201,7 +201,7 @@ template <> struct GraphTraits { template <> struct GraphTraits { using ChildIteratorType = swift::DominanceInfoNode::const_iterator; - typedef const swift::DominanceInfoNode *NodeRef; + using NodeRef = const swift::DominanceInfoNode *; static NodeRef getEntryNode(NodeRef N) { return N; } static inline ChildIteratorType child_begin(NodeRef N) { return N->begin(); } diff --git a/include/swift/SIL/InstructionUtils.h b/include/swift/SIL/InstructionUtils.h index 4f4755c632dd9..7d2775780c74b 100644 --- a/include/swift/SIL/InstructionUtils.h +++ b/include/swift/SIL/InstructionUtils.h @@ -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 - 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 @@ -115,23 +115,6 @@ bool onlyAffectsRefCount(SILInstruction *user); /// recursively. SILValue stripConvertFunctions(SILValue V); -/// Given an address accessed by an instruction that reads or modifies -/// memory, return the base address of the formal access. If the given address -/// is produced by an initialization sequence, which cannot correspond to a -/// formal access, then return an invalid SILValue. -/// -/// This must return a valid SILValue for the address operand of begin_access. -SILValue findAccessedAddressBase(SILValue sourceAddr); - -/// 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). -/// -/// If this returns false, then the address can be safely accessed without -/// a begin_access marker. To determine whether to emit begin_access: -/// base = findAccessedAddressBase(address) -/// needsAccessMarker = base && baseAddressNeedsFormalAccess(base) -bool isPossibleFormalAccessBase(SILValue baseAddress); - /// 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); @@ -148,13 +131,6 @@ struct LLVM_LIBRARY_VISIBILITY FindClosureResult { /// by a reabstraction thunk. FindClosureResult findClosureForAppliedArg(SILValue V); -/// Visit each address accessed by the given memory operation. -/// -/// This only visits instructions that modify memory in some user-visible way, -/// which could be considered part of a formal access. -void visitAccessedAddress(SILInstruction *I, - std::function visitor); - /// A utility class for evaluating whether a newly parsed or deserialized /// function has qualified or unqualified ownership. /// diff --git a/include/swift/SIL/MemAccessUtils.h b/include/swift/SIL/MemAccessUtils.h new file mode 100644 index 0000000000000..562f5d8cde48a --- /dev/null +++ b/include/swift/SIL/MemAccessUtils.h @@ -0,0 +1,397 @@ +//===--- MemAccessUtils.h - Utilities for SIL memory access. ----*- C++ -*-===// +// +// 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 +// +// These utilities live in SIL/ so they be used by SIL verification. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SIL_MEMACCESSUTILS_H +#define SWIFT_SIL_MEMACCESSUTILS_H + +#include "swift/SIL/Projection.h" +#include "swift/SIL/InstructionUtils.h" +#include "swift/SIL/SILInstruction.h" +#include "llvm/ADT/DenseMap.h" + +namespace swift { + +// stripAddressAccess() is declared in InstructionUtils.h. + +inline bool accessKindMayConflict(SILAccessKind a, SILAccessKind b) { + return !(a == SILAccessKind::Read && b == SILAccessKind::Read); +} + +/// Represents the identity of a stored class property as a combination +/// of a base and a single projection. Eventually the goal is to make this +/// more precise and consider, casts, etc. +class ObjectProjection { + SILValue object; + Projection proj; + +public: + ObjectProjection(SILValue object, const Projection &proj) + : object(object), proj(proj) { + assert(object->getType().isObject()); + } + + SILValue getObject() const { return object; } + const Projection &getProjection() const { return proj; } + + bool operator==(const ObjectProjection &other) const { + return object == other.object && proj == other.proj; + } + + bool operator!=(const ObjectProjection &other) const { + return object != other.object || proj != other.proj; + } +}; + +/// Represents the identity of a storage object being accessed. +/// +/// 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. +/// +/// 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. +class AccessedStorage { +public: + /// Enumerate over all valid begin_access bases. Clients can use a covered + /// switch to warn if findAccessedAddressBase ever adds a case. + enum Kind : uint8_t { + Box, + Stack, + Global, + Class, + Argument, + Nested, + Unidentified, + NumKindBits = countBitsUsed(static_cast(Unidentified)) + }; + + static const char *getKindName(Kind k); + + /// If the given address source is an identified access base, return the kind + /// of access base. Otherwise, return Unidentified. + static AccessedStorage::Kind classify(SILValue base); + +protected: + // Form a bitfield that is effectively a union over any pass-specific data + // with the fields used within this class as a common prefix. + // + // This allows passes to embed analysis flags, and reserves enough space to + // embed a unique index. + // + // AccessedStorageAnalysis defines an StorageAccessInfo object that maps each + // storage object within a function to its unique storage index and summary + // information of that storage object. + // + // AccessEnforcementOpts defines an AccessEnforcementOptsInfo object that maps + // each begin_access to its storage object, unique access index, and summary + // info for that access. + union { + uint64_t OpaqueBits; + SWIFT_INLINE_BITFIELD_BASE(AccessedStorage, bitmax(NumKindBits, 8), + Kind : bitmax(NumKindBits, 8)); + + // Define bits for use in AccessedStorageAnalysis. Each identified storage + // object is mapped to one instance of this subclass. + SWIFT_INLINE_BITFIELD_FULL(StorageAccessInfo, AccessedStorage, + 64 - NumAccessedStorageBits, + accessKind : NumSILAccessKindBits, + noNestedConflict : 1, + storageIndex : 64 - (NumAccessedStorageBits + + NumSILAccessKindBits + + 1)); + + // Define bits for use in the AccessEnforcementOpts pass. Each begin_access + // in the function is mapped to one instance of this subclass. Reserve a + // bit for a seenNestedConflict flag, which is the per-begin-access result + // of pass-specific analysis. The remaning bits are sufficient to index all + // begin_[unpaired_]access instructions. + // + // `AccessedStorage` refers to the AccessedStorageBitfield defined above, + // setting aside enough bits for common data. + SWIFT_INLINE_BITFIELD_FULL(AccessEnforcementOptsInfo, AccessedStorage, + 64 - NumAccessedStorageBits, + seenNestedConflict : 1, + beginAccessIndex : 63 - NumAccessedStorageBits); + } Bits; + +private: + union { + SILValue value; + unsigned paramIndex; + SILGlobalVariable *global; + ObjectProjection objProj; + }; + + void initKind(Kind k) { + Bits.OpaqueBits = 0; + Bits.AccessedStorage.Kind = k; + } + +public: + AccessedStorage() : value() { initKind(Unidentified); } + + AccessedStorage(SILValue base, Kind kind); + + AccessedStorage(SILValue object, Projection projection) + : objProj(object, projection) { + initKind(Class); + } + + // Return true if this is a valid storage object. + operator bool() const { return getKind() != Unidentified || value; } + + Kind getKind() const { return static_cast(Bits.AccessedStorage.Kind); } + + SILValue getValue() const { + assert(getKind() != Argument && getKind() != Global && getKind() != Class); + return value; + } + + unsigned getParamIndex() const { + assert(getKind() == Argument); + return paramIndex; + } + + SILArgument *getArgument(SILFunction *F) const { + assert(getKind() == Argument); + return F->getArgument(paramIndex); + } + + SILGlobalVariable *getGlobal() const { + assert(getKind() == Global); + return global; + } + + const ObjectProjection &getObjectProjection() const { + assert(getKind() == Class); + return objProj; + } + + bool hasIdenticalBase(const AccessedStorage &other) const { + if (getKind() != other.getKind()) + return false; + + switch (getKind()) { + case Box: + case Stack: + case Nested: + case Unidentified: + return value == other.value; + case Argument: + return paramIndex == other.paramIndex; + case Global: + return global == other.global; + case Class: + return objProj == other.objProj; + } + } + + bool isLocal() const { + switch (getKind()) { + case Box: + case Stack: + return true; + case Global: + case Class: + case Argument: + case Nested: + case Unidentified: + return false; + } + } + + bool isUniquelyIdentified() const { + switch (getKind()) { + case Box: + case Stack: + case Global: + return true; + case Class: + case Argument: + case Nested: + case Unidentified: + return false; + } + } + + bool isDistinctFrom(const AccessedStorage &other) const { + return isUniquelyIdentified() && other.isUniquelyIdentified() + && !hasIdenticalBase(other); + } + + /// Returns the ValueDecl for the underlying storage, if it can be + /// determined. Otherwise returns null. For diagnostics and checking via the + /// ValueDecl if we are processing a `let` variable. + const ValueDecl *getDecl(SILFunction *F) const; + + void print(raw_ostream &os) const; + void dump() const; + +private: + // Disable direct comparison because we allow subclassing with bitfields. + // Currently, we use DenseMapInfo to unique storage, which defines key + // equalilty only in terms of the base AccessedStorage class bits. + bool operator==(const AccessedStorage &) const = delete; + bool operator!=(const AccessedStorage &) const = delete; +}; +} // end namespace swift + +namespace llvm { + +/// Enable using AccessedStorage as a key in DenseMap. +/// Do *not* include any extra pass data in key equality. +template <> struct DenseMapInfo { + static swift::AccessedStorage getEmptyKey() { + return swift::AccessedStorage(swift::SILValue::getFromOpaqueValue( + llvm::DenseMapInfo::getEmptyKey()), + swift::AccessedStorage::Unidentified); + } + + static swift::AccessedStorage getTombstoneKey() { + return swift::AccessedStorage( + swift::SILValue::getFromOpaqueValue( + llvm::DenseMapInfo::getTombstoneKey()), + swift::AccessedStorage::Unidentified); + } + + static unsigned getHashValue(swift::AccessedStorage storage) { + switch (storage.getKind()) { + case swift::AccessedStorage::Box: + case swift::AccessedStorage::Stack: + case swift::AccessedStorage::Nested: + case swift::AccessedStorage::Unidentified: + return DenseMapInfo::getHashValue(storage.getValue()); + case swift::AccessedStorage::Argument: + return storage.getParamIndex(); + case swift::AccessedStorage::Global: + return DenseMapInfo::getHashValue(storage.getGlobal()); + case swift::AccessedStorage::Class: { + const swift::ObjectProjection &P = storage.getObjectProjection(); + return llvm::hash_combine(P.getObject(), P.getProjection()); + } + } + llvm_unreachable("Unhandled AccessedStorageKind"); + } + + static bool isEqual(swift::AccessedStorage LHS, swift::AccessedStorage RHS) { + if (LHS.getKind() != RHS.getKind()) + return false; + + switch (LHS.getKind()) { + case swift::AccessedStorage::Box: + case swift::AccessedStorage::Stack: + case swift::AccessedStorage::Nested: + case swift::AccessedStorage::Unidentified: + return LHS.getValue() == RHS.getValue(); + case swift::AccessedStorage::Argument: + return LHS.getParamIndex() == RHS.getParamIndex(); + case swift::AccessedStorage::Global: + return LHS.getGlobal() == RHS.getGlobal(); + case swift::AccessedStorage::Class: + return LHS.getObjectProjection() == RHS.getObjectProjection(); + } + llvm_unreachable("Unhandled AccessedStorageKind"); + } +}; + +} // end namespace llvm + +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. +/// +/// 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. +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. +/// +/// This is identical to findAccessedStorage(), but never returns Nested +/// storage. +AccessedStorage findAccessedStorageNonNested(SILValue sourceAddr); + +/// Return true if the given address operand is used by a memory operation that +/// initializes the memory at that address, implying that the previous value is +/// uninitialized. +bool memInstMustInitialize(Operand *memOper); + +/// 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). +/// +/// 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) +/// needsAccessMarker = storage && isPossibleFormalAccessBase(storage) +/// +/// Warning: This is only valid for SIL with well-formed accessed. For example, +/// it will not handle address-type phis. Optimization passes after +/// DiagnoseStaticExclusivity may violate these assumptions. +bool isPossibleFormalAccessBase(const AccessedStorage &storage, SILFunction *F); + +/// Visit each address accessed by the given memory operation. +/// +/// This only visits instructions that modify memory in some user-visible way, +/// which could be considered part of a formal access. +void visitAccessedAddress(SILInstruction *I, + llvm::function_ref visitor); + +/// Perform a RAUW operation on begin_access with it's own source operand. +/// Then erase the begin_access and all associated end_access instructions. +/// Return an iterator to the following instruction. +/// +/// The caller should use this iterator rather than assuming that the +/// instruction following this begin_access was not also erased. +SILBasicBlock::iterator removeBeginAccess(BeginAccessInst *beginAccess); + +} // end namespace swift + +#endif diff --git a/include/swift/SIL/OwnershipChecker.h b/include/swift/SIL/OwnershipChecker.h deleted file mode 100644 index f5491d3b6621d..0000000000000 --- a/include/swift/SIL/OwnershipChecker.h +++ /dev/null @@ -1,53 +0,0 @@ -//===--- OwnershipChecker.h -------------------------------------*- C++ -*-===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -#ifndef SWIFT_SIL_OWNERSHIPCHECKER_H -#define SWIFT_SIL_OWNERSHIPCHECKER_H - -#include "llvm/ADT/SmallVector.h" -#include "llvm/ADT/SmallPtrSet.h" - -namespace swift { - -class SILBasicBlock; -class SILInstruction; -class SILModule; -class SILValue; -class DeadEndBlocks; - -/// This class is a higher level interface to the ownership checker meant for -/// use with SILPasses. It uses the actual checker as an internal PImpl detail -/// so types/etc do not leak. -struct OwnershipChecker { - /// The module that we are in. - SILModule &Mod; - - /// A cache of dead-end basic blocks that we use to determine if we can - /// ignore "leaks". - DeadEndBlocks &DEBlocks; - - /// The list of regular users from the last run of the checker. - llvm::SmallVector RegularUsers; - - /// The list of regular users from the last run of the checker. - llvm::SmallVector LifetimeEndingUsers; - - /// The live blocks for the SILValue we processed. This can be used to - /// determine if a block is in the "live" region of our SILInstruction. - llvm::SmallPtrSet LiveBlocks; - - bool checkValue(SILValue Value); -}; - -} // end swift namespace - -#endif diff --git a/include/swift/SIL/OwnershipUtils.h b/include/swift/SIL/OwnershipUtils.h new file mode 100644 index 0000000000000..dbd7d731d4ce9 --- /dev/null +++ b/include/swift/SIL/OwnershipUtils.h @@ -0,0 +1,101 @@ +//===--- OwnershipUtils.h ------------------------------------*- C++ -*----===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SIL_OWNERSHIPUTILS_H +#define SWIFT_SIL_OWNERSHIPUTILS_H + +#include "swift/Basic/LLVM.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallVector.h" + +namespace swift { + +class SILBasicBlock; +class SILInstruction; +class SILModule; +class SILValue; +class DeadEndBlocks; +class BranchPropagatedUser; + +namespace ownership { + +struct ErrorBehaviorKind { + enum inner_t { + Invalid = 0, + ReturnFalse = 1, + PrintMessage = 2, + Assert = 4, + PrintMessageAndReturnFalse = PrintMessage | ReturnFalse, + PrintMessageAndAssert = PrintMessage | Assert, + } Value; + + ErrorBehaviorKind() : Value(Invalid) {} + ErrorBehaviorKind(inner_t Inner) : Value(Inner) { assert(Value != Invalid); } + + bool shouldAssert() const { + assert(Value != Invalid); + return Value & Assert; + } + + bool shouldPrintMessage() const { + assert(Value != Invalid); + return Value & PrintMessage; + } + + bool shouldReturnFalse() const { + assert(Value != Invalid); + return Value & ReturnFalse; + } +}; + +} // end namespace ownership + +/// This class is a higher level interface to the ownership checker meant for +/// use with SILPasses. It uses the actual checker as an internal PImpl detail +/// so types/etc do not leak. +struct OwnershipChecker { + /// The module that we are in. + SILModule &Mod; + + /// A cache of dead-end basic blocks that we use to determine if we can + /// ignore "leaks". + DeadEndBlocks &DEBlocks; + + /// The list of regular users from the last run of the checker. + SmallVector RegularUsers; + + /// The list of regular users from the last run of the checker. + SmallVector LifetimeEndingUsers; + + /// The live blocks for the SILValue we processed. This can be used to + /// determine if a block is in the "live" region of our SILInstruction. + SmallPtrSet LiveBlocks; + + bool checkValue(SILValue Value); +}; + +/// Returns true if: +/// +/// 1. No consuming uses are reachable from any other consuming use, from any +/// non-consuming uses, or from the producer instruction. +/// 2. The consuming use set jointly post dominates producers and all non +/// consuming uses. +bool valueHasLinearLifetime(SILValue value, + ArrayRef consumingUses, + ArrayRef nonConsumingUses, + SmallPtrSetImpl &visitedBlocks, + DeadEndBlocks &deBlocks, + ownership::ErrorBehaviorKind errorBehavior); + +} // namespace swift + +#endif diff --git a/include/swift/SIL/PatternMatch.h b/include/swift/SIL/PatternMatch.h index 793de6d9b4c51..8741023794374 100644 --- a/include/swift/SIL/PatternMatch.h +++ b/include/swift/SIL/PatternMatch.h @@ -140,17 +140,17 @@ struct OneOf_match; template struct OneOf_match { - typedef T0 Ty; + using Ty = T0; }; template struct OneOf_match { - typedef match_combine_or Ty; + using Ty = match_combine_or; }; template struct OneOf_match { - typedef typename OneOf_match, Arguments ...>::Ty Ty; + using Ty = typename OneOf_match, Arguments...>::Ty; }; /// This is a vararg version of m_CombineOr. It is a boolean "or" of @@ -584,18 +584,18 @@ struct Apply_match; template struct Apply_match { - typedef Callee_match Ty; + using Ty = Callee_match; }; template struct Apply_match { - typedef match_combine_and, Argument_match> Ty; + using Ty = match_combine_and, Argument_match>; }; template struct Apply_match { - typedef match_combine_and::Ty, - Argument_match > Ty; + using Ty = match_combine_and::Ty, + Argument_match>; }; /// Match only an ApplyInst's Callee. diff --git a/include/swift/SIL/Projection.h b/include/swift/SIL/Projection.h index 2371cb70a4abe..1fdcc0dfc43f3 100644 --- a/include/swift/SIL/Projection.h +++ b/include/swift/SIL/Projection.h @@ -474,6 +474,8 @@ class Projection { createAggFromFirstLevelProjections(SILBuilder &B, SILLocation Loc, SILType BaseType, llvm::SmallVectorImpl &Values); + + void print(raw_ostream &os, SILType baseType) const; private: /// Convenience method for getting the raw underlying index as a pointer. TypeBase *getPointer() const { @@ -685,8 +687,8 @@ class ProjectionPath { void verify(SILModule &M); - raw_ostream &print(raw_ostream &OS, SILModule &M); - void dump(SILModule &M); + raw_ostream &print(raw_ostream &OS, SILModule &M) const; + void dump(SILModule &M) const; }; /// Returns the hashcode for the new projection path. @@ -836,6 +838,9 @@ class ProjectionTree { SILModule &Mod; /// The allocator we use to allocate ProjectionTreeNodes in the tree. + /// + /// FIXME: This should be a reference to an outside allocator. We shouldn't + /// have each ProjectionTree have its own allocator. llvm::SpecificBumpPtrAllocator Allocator; // A common pattern is a 3 field struct. @@ -924,8 +929,7 @@ class ProjectionTree { return false; } - - void getLeafTypes(llvm::SmallVectorImpl &OutArray) const { + void getLiveLeafTypes(llvm::SmallVectorImpl &OutArray) const { for (unsigned LeafIndex : LiveLeafIndices) { const ProjectionTreeNode *Node = getNode(LeafIndex); assert(Node->IsLive && "We are only interested in leafs that are live"); @@ -933,8 +937,8 @@ class ProjectionTree { } } - void - getLeafNodes(llvm::SmallVectorImpl &Out) const { + void getLiveLeafNodes( + llvm::SmallVectorImpl &Out) const { for (unsigned LeafIndex : LiveLeafIndices) { const ProjectionTreeNode *Node = getNode(LeafIndex); assert(Node->IsLive && "We are only interested in leafs that are live"); @@ -943,9 +947,7 @@ class ProjectionTree { } /// Return the number of live leafs in the projection. - size_t liveLeafCount() const { - return LiveLeafIndices.size(); - } + unsigned getLiveLeafCount() const { return LiveLeafIndices.size(); } void createTreeFromValue(SILBuilder &B, SILLocation Loc, SILValue NewBase, llvm::SmallVectorImpl &Leafs) const; diff --git a/include/swift/SIL/SILBasicBlock.h b/include/swift/SIL/SILBasicBlock.h index 84381b37b546a..1f6b12fcf206f 100644 --- a/include/swift/SIL/SILBasicBlock.h +++ b/include/swift/SIL/SILBasicBlock.h @@ -163,10 +163,10 @@ public llvm::ilist_node, public SILAllocated { ArrayRef getArguments() const { return ArgumentList; } using PHIArgumentArrayRefTy = - TransformArrayRef>; + TransformArrayRef; PHIArgumentArrayRefTy getPHIArguments() const; using FunctionArgumentArrayRefTy = - TransformArrayRef>; + TransformArrayRef; FunctionArgumentArrayRefTy getFunctionArguments() const; unsigned getNumArguments() const { return ArgumentList.size(); } diff --git a/include/swift/SIL/SILBuilder.h b/include/swift/SIL/SILBuilder.h index a198c8c069496..f80ee71fdbc30 100644 --- a/include/swift/SIL/SILBuilder.h +++ b/include/swift/SIL/SILBuilder.h @@ -247,7 +247,7 @@ class SILBuilder { static SILType getPartialApplyResultType(SILType Ty, unsigned ArgCount, SILModule &M, - SubstitutionList subs, + SubstitutionMap subs, ParameterConvention calleeConvention); //===--------------------------------------------------------------------===// @@ -357,7 +357,7 @@ class SILBuilder { } ApplyInst *createApply( - SILLocation Loc, SILValue Fn, SubstitutionList Subs, + SILLocation Loc, SILValue Fn, SubstitutionMap Subs, ArrayRef Args, bool isNonThrowing, const GenericSpecializationInformation *SpecializationInfo = nullptr) { return insert(ApplyInst::create(getSILDebugLocation(Loc), Fn, Subs, Args, @@ -370,12 +370,12 @@ class SILBuilder { const GenericSpecializationInformation *SpecializationInfo = nullptr) { SILFunctionConventions conventions(Fn->getType().castTo(), getModule()); - return createApply(Loc, Fn, SubstitutionList(), Args, isNonThrowing, + return createApply(Loc, Fn, SubstitutionMap(), Args, isNonThrowing, SpecializationInfo); } TryApplyInst *createTryApply( - SILLocation Loc, SILValue fn, SubstitutionList subs, + SILLocation Loc, SILValue fn, SubstitutionMap subs, ArrayRef args, SILBasicBlock *normalBB, SILBasicBlock *errorBB, const GenericSpecializationInformation *SpecializationInfo = nullptr) { return insertTerminator(TryApplyInst::create(getSILDebugLocation(Loc), @@ -386,7 +386,7 @@ class SILBuilder { } PartialApplyInst *createPartialApply( - SILLocation Loc, SILValue Fn, SubstitutionList Subs, + SILLocation Loc, SILValue Fn, SubstitutionMap Subs, ArrayRef Args, ParameterConvention CalleeConvention, const GenericSpecializationInformation *SpecializationInfo = nullptr) { return insert(PartialApplyInst::create(getSILDebugLocation(Loc), Fn, @@ -396,7 +396,7 @@ class SILBuilder { } BeginApplyInst *createBeginApply( - SILLocation Loc, SILValue Fn, SubstitutionList Subs, + SILLocation Loc, SILValue Fn, SubstitutionMap Subs, ArrayRef Args, bool isNonThrowing, const GenericSpecializationInformation *SpecializationInfo = nullptr) { return insert(BeginApplyInst::create(getSILDebugLocation(Loc), Fn, Subs, @@ -415,7 +415,7 @@ class SILBuilder { } BuiltinInst *createBuiltin(SILLocation Loc, Identifier Name, SILType ResultTy, - SubstitutionList Subs, + SubstitutionMap Subs, ArrayRef Args) { return insert(BuiltinInst::create(getSILDebugLocation(Loc), Name, ResultTy, Subs, Args, getModule())); @@ -457,14 +457,13 @@ class SILBuilder { assert(Args[0]->getType() == Args[1]->getType() && "Binary operands must match"); assert(Args[2]->getType().is() && - Args[2]->getType().getSwiftRValueType()->isBuiltinIntegerType(1) && + Args[2]->getType().getASTType()->isBuiltinIntegerType(1) && "Must have a third Int1 operand"); SILType OpdTy = Args[0]->getType(); SILType Int1Ty = Args[2]->getType(); - TupleTypeElt ResultElts[] = {OpdTy.getSwiftRValueType(), - Int1Ty.getSwiftRValueType()}; + TupleTypeElt ResultElts[] = {OpdTy.getASTType(), Int1Ty.getASTType()}; Type ResultTy = TupleType::get(ResultElts, getASTContext()); SILType SILResultTy = SILType::getPrimitiveObjectType(ResultTy->getCanonicalType()); @@ -582,7 +581,7 @@ class SILBuilder { KeyPathInst *createKeyPath(SILLocation Loc, KeyPathPattern *Pattern, - SubstitutionList Subs, + SubstitutionMap Subs, ArrayRef Args, SILType Ty) { return insert(KeyPathInst::create(getSILDebugLocation(Loc), @@ -670,10 +669,11 @@ class SILBuilder { BeginAccessInst *createBeginAccess(SILLocation loc, SILValue address, SILAccessKind accessKind, SILAccessEnforcement enforcement, - bool noNestedConflict) { + bool noNestedConflict, + bool fromBuiltin) { return insert(new (getModule()) BeginAccessInst( getSILDebugLocation(loc), address, accessKind, enforcement, - noNestedConflict)); + noNestedConflict, fromBuiltin)); } EndAccessInst *createEndAccess(SILLocation loc, SILValue address, @@ -686,18 +686,19 @@ class SILBuilder { createBeginUnpairedAccess(SILLocation loc, SILValue address, SILValue buffer, SILAccessKind accessKind, SILAccessEnforcement enforcement, - bool noNestedConflict) { + bool noNestedConflict, + bool fromBuiltin) { return insert(new (getModule()) BeginUnpairedAccessInst( getSILDebugLocation(loc), address, buffer, accessKind, enforcement, - noNestedConflict)); + noNestedConflict, fromBuiltin)); } - EndUnpairedAccessInst *createEndUnpairedAccess(SILLocation loc, - SILValue buffer, - SILAccessEnforcement enforcement, - bool aborted) { + EndUnpairedAccessInst * + createEndUnpairedAccess(SILLocation loc, SILValue buffer, + SILAccessEnforcement enforcement, bool aborted, + bool fromBuiltin) { return insert(new (getModule()) EndUnpairedAccessInst( - getSILDebugLocation(loc), buffer, enforcement, aborted)); + getSILDebugLocation(loc), buffer, enforcement, aborted, fromBuiltin)); } AssignInst *createAssign(SILLocation Loc, SILValue Src, SILValue DestAddr) { @@ -729,10 +730,10 @@ class SILBuilder { MarkUninitializedBehaviorInst * createMarkUninitializedBehavior(SILLocation Loc, SILValue initStorageFunc, - SubstitutionList initStorageSubs, + SubstitutionMap initStorageSubs, SILValue storage, SILValue setterFunc, - SubstitutionList setterSubs, + SubstitutionMap setterSubs, SILValue self, SILType ty) { return insert(MarkUninitializedBehaviorInst::create(getModule(), @@ -799,10 +800,10 @@ class SILBuilder { ConvertEscapeToNoEscapeInst * createConvertEscapeToNoEscape(SILLocation Loc, SILValue Op, SILType Ty, - bool lifetimeGuaranteed) { + bool isEscapedByUser, bool lifetimeGuaranteed) { return insert(ConvertEscapeToNoEscapeInst::create( getSILDebugLocation(Loc), Op, Ty, getFunction(), OpenedArchetypes, - lifetimeGuaranteed)); + isEscapedByUser, lifetimeGuaranteed)); } ThinFunctionToPointerInst * @@ -1446,7 +1447,7 @@ class SILBuilder { InitBlockStorageHeaderInst * createInitBlockStorageHeader(SILLocation Loc, SILValue BlockStorage, SILValue InvokeFunction, SILType BlockType, - SubstitutionList Subs) { + SubstitutionMap Subs) { return insert(InitBlockStorageHeaderInst::create(getFunction(), getSILDebugLocation(Loc), BlockStorage, InvokeFunction, BlockType, Subs)); } @@ -1482,6 +1483,14 @@ class SILBuilder { return insert(new (getModule()) CopyBlockInst(getSILDebugLocation(Loc), Operand)); } + + CopyBlockWithoutEscapingInst * + createCopyBlockWithoutEscaping(SILLocation Loc, SILValue Block, + SILValue Closure) { + return insert(new (getModule()) CopyBlockWithoutEscapingInst( + getSILDebugLocation(Loc), Block, Closure)); + } + StrongRetainInst *createStrongRetain(SILLocation Loc, SILValue Operand, Atomicity atomicity) { assert(isParsing || !getFunction().hasQualifiedOwnership()); @@ -1561,10 +1570,11 @@ class SILBuilder { getSILDebugLocation(Loc), value, Int1Ty)); } IsEscapingClosureInst *createIsEscapingClosure(SILLocation Loc, - SILValue operand) { + SILValue operand, + unsigned VerificationType) { auto Int1Ty = SILType::getBuiltinIntegerType(1, getASTContext()); return insert(new (getModule()) IsEscapingClosureInst( - getSILDebugLocation(Loc), operand, Int1Ty)); + getSILDebugLocation(Loc), operand, Int1Ty, VerificationType)); } DeallocStackInst *createDeallocStack(SILLocation Loc, SILValue operand) { @@ -2035,7 +2045,7 @@ class SILBuilder { void appendOperandTypeName(SILType OpdTy, llvm::SmallString<16> &Name) { if (auto BuiltinIntTy = - dyn_cast(OpdTy.getSwiftRValueType())) { + dyn_cast(OpdTy.getASTType())) { if (BuiltinIntTy == BuiltinIntegerType::getWordType(getASTContext())) { Name += "_Word"; } else { @@ -2043,7 +2053,7 @@ class SILBuilder { Name += "_Int" + llvm::utostr(NumBits); } } else { - assert(OpdTy.getSwiftRValueType() == getASTContext().TheRawPointerType); + assert(OpdTy.getASTType() == getASTContext().TheRawPointerType); Name += "_RawPointer"; } } diff --git a/include/swift/SIL/SILCloner.h b/include/swift/SIL/SILCloner.h index 4c18ec2feb153..bc264bce7dcfd 100644 --- a/include/swift/SIL/SILCloner.h +++ b/include/swift/SIL/SILCloner.h @@ -107,16 +107,32 @@ class SILCloner : protected SILInstructionVisitor { const SILDebugScope *getOpScope(const SILDebugScope *DS) { return asImpl().remapScope(DS); } - SmallVector getOpSubstitutions(SubstitutionList Subs) { - SmallVector NewSubs; - for (auto Sub : Subs) { - NewSubs.push_back(getOpSubstitution(Sub)); + + SubstitutionMap remapSubstitutionMap(SubstitutionMap Subs) { + return Subs; + } + + SubstitutionMap getOpSubstitutionMap(SubstitutionMap Subs) { + // If we have open existentials to substitute, check whether that's + // relevant to this this particular substitution. + if (!OpenedExistentialSubs.empty()) { + for (auto ty : Subs.getReplacementTypes()) { + // If we found a type containing an opened existential, substitute + // open existentials throughout the substitution map. + if (ty->hasOpenedExistential()) { + Subs = Subs.subst(QueryTypeSubstitutionMapOrIdentity{ + OpenedExistentialSubs}, + MakeAbstractConformanceForGenericType()); + break; + } + } } - return NewSubs; + + return asImpl().remapSubstitutionMap(Subs).getCanonical(); } - + SILType getTypeInClonedContext(SILType Ty) { - auto objectTy = Ty.getSwiftRValueType(); + auto objectTy = Ty.getASTType(); // Do not substitute opened existential types, if we do not have any. if (!objectTy->hasOpenedExistential()) return Ty; @@ -160,21 +176,17 @@ class SILCloner : protected SILInstructionVisitor { ProtocolConformanceRef getOpConformance(Type ty, ProtocolConformanceRef conformance) { - auto newConformance = - conformance.subst(ty, - [&](SubstitutableType *t) -> Type { - if (t->isOpenedExistential()) { - auto found = OpenedExistentialSubs.find( - t->castTo()); - if (found != OpenedExistentialSubs.end()) - return found->second; - return t; - } - return t; - }, - MakeAbstractConformanceForGenericType()); + // If we have open existentials to substitute, do so now. + if (ty->hasOpenedExistential() && !OpenedExistentialSubs.empty()) { + conformance = + conformance.subst(ty, + QueryTypeSubstitutionMapOrIdentity{ + OpenedExistentialSubs}, + MakeAbstractConformanceForGenericType()); + } + return asImpl().remapConformance(getASTTypeInClonedContext(ty), - newConformance); + conformance); } ArrayRef @@ -186,14 +198,6 @@ class SILCloner : protected SILInstructionVisitor { return ty->getASTContext().AllocateCopy(newConformances); } - Substitution getOpSubstitution(Substitution sub) { - CanType newReplacement = - getOpASTType(sub.getReplacement()->getCanonicalType()); - auto conformances = getOpConformances(sub.getReplacement(), - sub.getConformances()); - return Substitution(newReplacement, conformances); - } - SILValue getOpValue(SILValue Value) { return asImpl().remapValue(Value); } @@ -561,7 +565,7 @@ SILCloner::visitBuiltinInst(BuiltinInst *Inst) { getBuilder().createBuiltin(getOpLocation(Inst->getLoc()), Inst->getName(), getOpType(Inst->getType()), - getOpSubstitutions(Inst->getSubstitutions()), + getOpSubstitutionMap(Inst->getSubstitutions()), Args)); } @@ -573,7 +577,7 @@ SILCloner::visitApplyInst(ApplyInst *Inst) { doPostProcess(Inst, getBuilder().createApply(getOpLocation(Inst->getLoc()), getOpValue(Inst->getCallee()), - getOpSubstitutions(Inst->getSubstitutions()), + getOpSubstitutionMap(Inst->getSubstitutionMap()), Args, Inst->isNonThrowing(), GenericSpecializationInformation::create( @@ -588,7 +592,8 @@ SILCloner::visitTryApplyInst(TryApplyInst *Inst) { doPostProcess(Inst, getBuilder().createTryApply(getOpLocation(Inst->getLoc()), getOpValue(Inst->getCallee()), - getOpSubstitutions(Inst->getSubstitutions()), + getOpSubstitutionMap( + Inst->getSubstitutionMap()), Args, getOpBasicBlock(Inst->getNormalBB()), getOpBasicBlock(Inst->getErrorBB()), @@ -605,10 +610,8 @@ SILCloner::visitPartialApplyInst(PartialApplyInst *Inst) { getBuilder().createPartialApply( getOpLocation(Inst->getLoc()), getOpValue(Inst->getCallee()), - getOpSubstitutions(Inst->getSubstitutions()), Args, - Inst->getType() - .getSwiftRValueType() - ->getAs() + getOpSubstitutionMap(Inst->getSubstitutionMap()), Args, + Inst->getType().getAs() ->getCalleeConvention(), GenericSpecializationInformation::create(Inst, getBuilder()))); @@ -622,7 +625,8 @@ SILCloner::visitBeginApplyInst(BeginApplyInst *Inst) { doPostProcess(Inst, getBuilder().createBeginApply(getOpLocation(Inst->getLoc()), getOpValue(Inst->getCallee()), - getOpSubstitutions(Inst->getSubstitutions()), + getOpSubstitutionMap( + Inst->getSubstitutionMap()), Args, Inst->isNonThrowing(), GenericSpecializationInformation::create( @@ -792,7 +796,8 @@ void SILCloner::visitBeginAccessInst(BeginAccessInst *Inst) { getOpValue(Inst->getOperand()), Inst->getAccessKind(), Inst->getEnforcement(), - Inst->hasNoNestedConflict())); + Inst->hasNoNestedConflict(), + Inst->isFromBuiltin())); } template @@ -814,18 +819,19 @@ void SILCloner::visitBeginUnpairedAccessInst( getOpValue(Inst->getBuffer()), Inst->getAccessKind(), Inst->getEnforcement(), - Inst->hasNoNestedConflict())); + Inst->hasNoNestedConflict(), + Inst->isFromBuiltin())); } template void SILCloner::visitEndUnpairedAccessInst( EndUnpairedAccessInst *Inst) { getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); - doPostProcess( - Inst, getBuilder().createEndUnpairedAccess(getOpLocation(Inst->getLoc()), - getOpValue(Inst->getOperand()), - Inst->getEnforcement(), - Inst->isAborting())); + doPostProcess(Inst, + getBuilder().createEndUnpairedAccess( + getOpLocation(Inst->getLoc()), + getOpValue(Inst->getOperand()), Inst->getEnforcement(), + Inst->isAborting(), Inst->isFromBuiltin())); } template @@ -857,10 +863,11 @@ SILCloner::visitMarkUninitializedBehaviorInst( getBuilder().createMarkUninitializedBehavior( getOpLocation(Inst->getLoc()), getOpValue(Inst->getInitStorageFunc()), - getOpSubstitutions(Inst->getInitStorageSubstitutions()), + getOpSubstitutionMap( + Inst->getInitStorageSubstitutions()), getOpValue(Inst->getStorage()), getOpValue(Inst->getSetterFunc()), - getOpSubstitutions(Inst->getSetterSubstitutions()), + getOpSubstitutionMap(Inst->getSetterSubstitutions()), getOpValue(Inst->getSelf()), getOpType(Inst->getType()))); } @@ -986,10 +993,11 @@ template void SILCloner::visitConvertEscapeToNoEscapeInst( ConvertEscapeToNoEscapeInst *Inst) { getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); - doPostProcess( - Inst, getBuilder().createConvertEscapeToNoEscape( - getOpLocation(Inst->getLoc()), getOpValue(Inst->getOperand()), - getOpType(Inst->getType()), Inst->isLifetimeGuaranteed())); + doPostProcess(Inst, + getBuilder().createConvertEscapeToNoEscape( + getOpLocation(Inst->getLoc()), + getOpValue(Inst->getOperand()), getOpType(Inst->getType()), + Inst->isEscapedByUser(), Inst->isLifetimeGuaranteed())); } template @@ -1668,8 +1676,7 @@ template void SILCloner::visitOpenExistentialAddrInst(OpenExistentialAddrInst *Inst) { // Create a new archetype for this opened existential type. - auto archetypeTy - = Inst->getType().getSwiftRValueType()->castTo(); + auto archetypeTy = Inst->getType().castTo(); registerOpenedExistentialRemapping( archetypeTy, ArchetypeType::getOpened(archetypeTy->getOpenedExistentialType())); @@ -1686,8 +1693,7 @@ template void SILCloner::visitOpenExistentialValueInst( OpenExistentialValueInst *Inst) { // Create a new archetype for this opened existential type. - auto archetypeTy = - Inst->getType().getSwiftRValueType()->castTo(); + auto archetypeTy = Inst->getType().castTo(); registerOpenedExistentialRemapping( archetypeTy, ArchetypeType::getOpened(archetypeTy->getOpenedExistentialType())); @@ -1704,8 +1710,8 @@ void SILCloner:: visitOpenExistentialMetatypeInst(OpenExistentialMetatypeInst *Inst) { // Create a new archetype for this opened existential type. - CanType openedType = Inst->getType().getSwiftRValueType(); - CanType exType = Inst->getOperand()->getType().getSwiftRValueType(); + auto openedType = Inst->getType().getASTType(); + auto exType = Inst->getOperand()->getType().getASTType(); while (auto exMetatype = dyn_cast(exType)) { exType = exMetatype.getInstanceType(); openedType = cast(openedType).getInstanceType(); @@ -1737,8 +1743,7 @@ void SILCloner:: visitOpenExistentialRefInst(OpenExistentialRefInst *Inst) { // Create a new archetype for this opened existential type. - auto archetypeTy - = Inst->getType().getSwiftRValueType()->castTo(); + auto archetypeTy = Inst->getType().castTo(); registerOpenedExistentialRemapping( archetypeTy, ArchetypeType::getOpened(archetypeTy->getOpenedExistentialType())); @@ -1755,8 +1760,7 @@ void SILCloner:: visitOpenExistentialBoxInst(OpenExistentialBoxInst *Inst) { // Create a new archetype for this opened existential type. - auto archetypeTy - = Inst->getType().getSwiftRValueType()->castTo(); + auto archetypeTy = Inst->getType().castTo(); registerOpenedExistentialRemapping( archetypeTy, ArchetypeType::getOpened(archetypeTy->getOpenedExistentialType())); @@ -1773,8 +1777,7 @@ void SILCloner:: visitOpenExistentialBoxValueInst(OpenExistentialBoxValueInst *Inst) { // Create a new archetype for this opened existential type. - auto archetypeTy - = Inst->getType().getSwiftRValueType()->castTo(); + auto archetypeTy = Inst->getType().castTo(); registerOpenedExistentialRemapping( archetypeTy, ArchetypeType::getOpened(archetypeTy->getOpenedExistentialType())); @@ -1879,6 +1882,16 @@ SILCloner::visitCopyBlockInst(CopyBlockInst *Inst) { getOpValue(Inst->getOperand()))); } +template +void SILCloner::visitCopyBlockWithoutEscapingInst( + CopyBlockWithoutEscapingInst *Inst) { + getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); + doPostProcess(Inst, + Builder.createCopyBlockWithoutEscaping( + getOpLocation(Inst->getLoc()), getOpValue(Inst->getBlock()), + getOpValue(Inst->getClosure()))); +} + template void SILCloner::visitStrongRetainInst(StrongRetainInst *Inst) { @@ -2020,8 +2033,9 @@ void SILCloner::visitIsEscapingClosureInst( IsEscapingClosureInst *Inst) { getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); doPostProcess( - Inst, getBuilder().createIsEscapingClosure( - getOpLocation(Inst->getLoc()), getOpValue(Inst->getOperand()))); + Inst, getBuilder().createIsEscapingClosure(getOpLocation(Inst->getLoc()), + getOpValue(Inst->getOperand()), + Inst->getVerificationType())); } template @@ -2422,7 +2436,7 @@ void SILCloner::visitInitBlockStorageHeaderInst( getOpValue(Inst->getBlockStorage()), getOpValue(Inst->getInvokeFunction()), getOpType(Inst->getType()), - getOpSubstitutions(Inst->getSubstitutions()))); + getOpSubstitutionMap(Inst->getSubstitutions()))); } template @@ -2463,7 +2477,7 @@ void SILCloner::visitKeyPathInst(KeyPathInst *Inst) { doPostProcess(Inst, getBuilder().createKeyPath( getOpLocation(Inst->getLoc()), Inst->getPattern(), - getOpSubstitutions(Inst->getSubstitutions()), + getOpSubstitutionMap(Inst->getSubstitutions()), opValues, getOpType(Inst->getType()))); } diff --git a/include/swift/SIL/SILCoverageMap.h b/include/swift/SIL/SILCoverageMap.h index 4174797ff2530..a0e41c3b61d8b 100644 --- a/include/swift/SIL/SILCoverageMap.h +++ b/include/swift/SIL/SILCoverageMap.h @@ -145,7 +145,7 @@ namespace llvm { template <> struct ilist_traits<::swift::SILCoverageMap> : public ilist_default_traits<::swift::SILCoverageMap> { - typedef ::swift::SILCoverageMap SILCoverageMap; + using SILCoverageMap = ::swift::SILCoverageMap; public: static void deleteNode(SILCoverageMap *VT) { VT->~SILCoverageMap(); } diff --git a/include/swift/SIL/SILDebuggerClient.h b/include/swift/SIL/SILDebuggerClient.h index 7c097067e1bd0..e6c2bf7a2bd27 100644 --- a/include/swift/SIL/SILDebuggerClient.h +++ b/include/swift/SIL/SILDebuggerClient.h @@ -27,7 +27,7 @@ class SILBuilder; class SILDebuggerClient : public DebuggerClient { public: - typedef SmallVectorImpl ResultVector; + using ResultVector = SmallVectorImpl; SILDebuggerClient(ASTContext &C) : DebuggerClient(C) { } virtual ~SILDebuggerClient() = default; diff --git a/include/swift/SIL/SILDeclRef.h b/include/swift/SIL/SILDeclRef.h index 133e501520abf..38b00d6741231 100644 --- a/include/swift/SIL/SILDeclRef.h +++ b/include/swift/SIL/SILDeclRef.h @@ -80,8 +80,8 @@ enum ForDefinition_t : bool { /// declaration, such as uncurry levels of a function, the allocating and /// initializing entry points of a constructor, etc. struct SILDeclRef { - typedef llvm::PointerUnion Loc; - + using Loc = llvm::PointerUnion; + /// Represents the "kind" of the SILDeclRef. For some Swift decls there /// are multiple SIL entry points, and the kind is used to distinguish them. enum class Kind : unsigned { diff --git a/include/swift/SIL/SILDefaultWitnessTable.h b/include/swift/SIL/SILDefaultWitnessTable.h index 1c747b2e678ae..0a95ef2e99404 100644 --- a/include/swift/SIL/SILDefaultWitnessTable.h +++ b/include/swift/SIL/SILDefaultWitnessTable.h @@ -174,7 +174,7 @@ namespace llvm { template <> struct ilist_traits<::swift::SILDefaultWitnessTable> : public ilist_default_traits<::swift::SILDefaultWitnessTable> { - typedef ::swift::SILDefaultWitnessTable SILDefaultWitnessTable; + using SILDefaultWitnessTable = ::swift::SILDefaultWitnessTable; public: static void deleteNode(SILDefaultWitnessTable *WT) { WT->~SILDefaultWitnessTable(); } diff --git a/include/swift/SIL/SILFunction.h b/include/swift/SIL/SILFunction.h index 668c35005bcfe..7fc2cf269fc86 100644 --- a/include/swift/SIL/SILFunction.h +++ b/include/swift/SIL/SILFunction.h @@ -17,13 +17,13 @@ #ifndef SWIFT_SIL_SILFUNCTION_H #define SWIFT_SIL_SILFUNCTION_H +#include "swift/AST/ASTNode.h" #include "swift/AST/ResilienceExpansion.h" #include "swift/Basic/ProfileCounter.h" #include "swift/SIL/SILBasicBlock.h" #include "swift/SIL/SILDebugScope.h" #include "swift/SIL/SILLinkage.h" #include "swift/SIL/SILPrintContext.h" -#include "swift/SIL/SILProfiler.h" #include "llvm/ADT/StringMap.h" /// The symbol name used for the program entry point function. @@ -35,6 +35,7 @@ namespace swift { class ASTContext; class SILInstruction; class SILModule; +class SILProfiler; enum IsBare_t { IsNotBare, IsBare }; enum IsTransparent_t { IsNotTransparent, IsTransparent }; @@ -97,7 +98,7 @@ class SILSpecializeAttr final { class SILFunction : public llvm::ilist_node, public SILAllocated { public: - typedef llvm::iplist BlockListType; + using BlockListType = llvm::iplist; private: friend class SILBasicBlock; @@ -120,8 +121,8 @@ class SILFunction /// Only set if this function is a specialization of another function. const GenericSpecializationInformation *SpecializationInfo; - /// The forwarding substitutions, lazily computed. - Optional ForwardingSubs; + /// The forwarding substitution map, lazily computed. + SubstitutionMap ForwardingSubMap; /// The collection of all BasicBlocks in the SILFunction. Empty for external /// function references. @@ -266,10 +267,7 @@ class SILFunction Profiler = InheritedProfiler; } - void createProfiler(ASTNode Root) { - assert(!Profiler && "Function already has a profiler"); - Profiler = SILProfiler::create(Module, Root); - } + void createProfiler(ASTNode Root, ForDefinition_t forDefinition); void discardProfiler() { Profiler = nullptr; } @@ -685,7 +683,7 @@ class SILFunction /// Return the identity substitutions necessary to forward this call if it is /// generic. - SubstitutionList getForwardingSubstitutions(); + SubstitutionMap getForwardingSubstitutionMap(); //===--------------------------------------------------------------------===// // Block List Access @@ -694,9 +692,9 @@ class SILFunction BlockListType &getBlocks() { return BlockList; } const BlockListType &getBlocks() const { return BlockList; } - typedef BlockListType::iterator iterator; - typedef BlockListType::reverse_iterator reverse_iterator; - typedef BlockListType::const_iterator const_iterator; + using iterator = BlockListType::iterator; + using reverse_iterator = BlockListType::reverse_iterator; + using const_iterator = BlockListType::const_iterator; bool empty() const { return BlockList.empty(); } iterator begin() { return BlockList.begin(); } @@ -881,7 +879,7 @@ namespace llvm { template <> struct ilist_traits<::swift::SILFunction> : public ilist_default_traits<::swift::SILFunction> { - typedef ::swift::SILFunction SILFunction; + using SILFunction = ::swift::SILFunction; public: static void deleteNode(SILFunction *V) { V->~SILFunction(); } diff --git a/include/swift/SIL/SILFunctionConventions.h b/include/swift/SIL/SILFunctionConventions.h index d26e981ccf76b..dcfce9d578a71 100644 --- a/include/swift/SIL/SILFunctionConventions.h +++ b/include/swift/SIL/SILFunctionConventions.h @@ -325,7 +325,7 @@ class SILFunctionConventions { // // The argument indices below relate to full applies in which the caller and // callee indices match. Partial apply indices are shifted on the caller - // side. See ApplySite::getCallArgIndexOfFirstAppliedArg(). + // side. See ApplySite::getCalleeArgIndexOfFirstAppliedArg(). //===--------------------------------------------------------------------===// unsigned getSILArgIndexOfFirstIndirectResult() const { return 0; } diff --git a/include/swift/SIL/SILGlobalVariable.h b/include/swift/SIL/SILGlobalVariable.h index de41c5231fe74..a1d07bbb49714 100644 --- a/include/swift/SIL/SILGlobalVariable.h +++ b/include/swift/SIL/SILGlobalVariable.h @@ -214,7 +214,7 @@ namespace llvm { template <> struct ilist_traits<::swift::SILGlobalVariable> : public ilist_default_traits<::swift::SILGlobalVariable> { - typedef ::swift::SILGlobalVariable SILGlobalVariable; + using SILGlobalVariable = ::swift::SILGlobalVariable; public: static void deleteNode(SILGlobalVariable *V) {} diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index f062aa7f9f0c2..0fe45915256c6 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -19,7 +19,9 @@ #include "swift/AST/Builtins.h" #include "swift/AST/Decl.h" +#include "swift/AST/GenericSignature.h" #include "swift/AST/ProtocolConformanceRef.h" +#include "swift/AST/SubstitutionMap.h" #include "swift/AST/TypeAlignments.h" #include "swift/Basic/Compiler.h" #include "swift/Basic/NullablePtr.h" @@ -63,7 +65,6 @@ class SILArgument; class SILUndef; class Stmt; class StringLiteralExpr; -class Substitution; class ValueDecl; class VarDecl; class FunctionRefInst; @@ -153,6 +154,9 @@ class SILInstructionResultArray { class iterator; + friend bool operator==(iterator, iterator); + friend bool operator!=(iterator, iterator); + iterator begin() const; iterator end() const; @@ -166,7 +170,7 @@ class SILInstructionResultArray { reverse_range getReversedValues() const; using type_range = llvm::iterator_range< - llvm::mapped_iterator, SILType>>; + llvm::mapped_iterator>; type_range getTypes() const; bool operator==(const SILInstructionResultArray &rhs); @@ -192,18 +196,6 @@ class SILInstructionResultArray { /// Please do not use this outside of this class. It is only meant to speedup /// MultipleValueInstruction::getIndexOfResult(SILValue). const ValueBase *back() const; - - /// Return the offset 1 past the end of the array or None if we are not - /// actually storing anything. - Optional getStartOffset() const { - return empty() ? None : Optional(0); - } - - /// Return the offset 1 past the end of the array or None if we are not - /// actually storing anything. - Optional getEndOffset() const { - return empty() ? None : Optional(size()); - } }; class SILInstructionResultArray::iterator { @@ -215,7 +207,7 @@ class SILInstructionResultArray::iterator { SILInstructionResultArray Parent; /// The index into the parent array. - Optional Index; + unsigned Index; public: using difference_type = int; @@ -225,34 +217,31 @@ class SILInstructionResultArray::iterator { using iterator_category = std::bidirectional_iterator_tag; iterator() = default; - iterator(const SILInstructionResultArray &Parent, - Optional Index = 0) + iterator(const SILInstructionResultArray &Parent, unsigned Index = 0) : Parent(Parent), Index(Index) {} - SILValue operator*() const { return Parent[Index.getValue()]; } - SILValue operator*() { return Parent[Index.getValue()]; } + SILValue operator*() const { return Parent[Index]; } SILValue operator->() const { return operator*(); } - SILValue operator->() { return operator*(); } iterator &operator++() { - ++Index.getValue(); + ++Index; return *this; } iterator operator++(int) { iterator copy = *this; - ++Index.getValue(); + ++Index; return copy; } iterator &operator--() { - --Index.getValue(); + --Index; return *this; } iterator operator--(int) { iterator copy = *this; - --Index.getValue(); + --Index; return copy; } @@ -597,6 +586,13 @@ class SILInstruction /// you perform such optimizations like e.g. jump-threading. bool isTriviallyDuplicatable() const; + /// Returns true if the instruction is only relevant for debug + /// informations and has no other impact on program semantics. + bool isDebugInstruction() const { + return getKind() == SILInstructionKind::DebugValueInst || + getKind() == SILInstructionKind::DebugValueAddrInst; + } + /// Returns true if the instruction is a meta instruction which is /// relevant for debug information and does not get lowered to a real /// instruction. @@ -921,11 +917,12 @@ class MultipleValueInstructionTrailingObjectstemplate getTrailingObjects(); + auto **ParentPtr = this->TrailingObjects::template + getTrailingObjects(); *ParentPtr = static_cast(Parent); - auto *DataPtr = this->template getTrailingObjects(); + auto *DataPtr = this->TrailingObjects::template + getTrailingObjects(); for (unsigned i : range(NumResults)) { ::new (&DataPtr[i]) DerivedResult(i, Types[i], OwnershipKinds[i], std::forward(OtherArgs)...); @@ -938,7 +935,8 @@ class MultipleValueInstructionTrailingObjectstemplate getTrailingObjects(); + auto *DataPtr = this->TrailingObjects::template + getTrailingObjects(); // We call the DerivedResult destructors to ensure that: // // 1. If our derived results have any stored data that need to be cleaned @@ -952,7 +950,8 @@ class MultipleValueInstructionTrailingObjects getAllResultsBuffer() const { - auto *ptr = this->template getTrailingObjects(); + auto *ptr = this->TrailingObjects::template + getTrailingObjects(); return { ptr, NumResults }; } @@ -990,17 +989,16 @@ class NonValueInstruction : public SILInstruction { } /// A helper class for defining some basic boilerplate. -template ::value> + std::is_base_of::value> class InstructionBase; -template -class InstructionBase : public Base { +template +class InstructionBase : public InstBase { protected: template - InstructionBase(As &&...args) - : Base(Kind, std::forward(args)...) {} + InstructionBase(As &&... args) : InstBase(Kind, std::forward(args)...) {} public: /// Override to statically return the kind. @@ -1016,12 +1014,11 @@ class InstructionBase : public Base { } }; -template -class InstructionBase : public Base { +template +class InstructionBase : public InstBase { protected: template - InstructionBase(As &&...args) - : Base(Kind, std::forward(args)...) {} + InstructionBase(As &&... args) : InstBase(Kind, std::forward(args)...) {} public: static constexpr SILInstructionKind getKind() { @@ -1081,8 +1078,8 @@ class InstructionBaseWithTrailingOperands protected: friend llvm::TrailingObjects; - typedef llvm::TrailingObjects - TrailingObjects; + using TrailingObjects = + llvm::TrailingObjects; using TrailingObjects::totalSizeToAlloc; @@ -1158,8 +1155,9 @@ class UnaryInstructionWithTypeDependentOperandsBase friend InstructionBaseWithTrailingOperands; - typedef InstructionBaseWithTrailingOperands< - Kind, Derived, Operand, OtherTrailingTypes...> TrailingObjects; + using TrailingObjects = + InstructionBaseWithTrailingOperands; public: template @@ -1214,36 +1212,37 @@ struct SILDebugVariable { /// A DebugVariable where storage for the strings has been /// tail-allocated following the parent SILInstruction. class TailAllocatedDebugVariable { + using int_type = uint32_t; union { - uint32_t RawValue; + int_type RawValue; struct { /// Whether this is a debug variable at all. - unsigned HasValue : 1; + int_type HasValue : 1; /// True if this is a let-binding. - unsigned Constant : 1; - /// The source function argument position from left to right - /// starting with 1 or 0 if this is a local variable. - unsigned ArgNo : 16; + int_type Constant : 1; /// When this is nonzero there is a tail-allocated string storing /// variable name present. This typically only happens for /// instructions that were created from parsing SIL assembler. - unsigned NameLength : 14; + int_type NameLength : 14; + /// The source function argument position from left to right + /// starting with 1 or 0 if this is a local variable. + int_type ArgNo : 16; } Data; - }; + } Bits; public: TailAllocatedDebugVariable(Optional, char *buf); - TailAllocatedDebugVariable(uint32_t RawValue) : RawValue(RawValue) {} - uint32_t getRawValue() const { return RawValue; } + TailAllocatedDebugVariable(int_type RawValue) { Bits.RawValue = RawValue; } + int_type getRawValue() const { return Bits.RawValue; } - unsigned getArgNo() const { return Data.ArgNo; } - void setArgNo(unsigned N) { Data.ArgNo = N; } + unsigned getArgNo() const { return Bits.Data.ArgNo; } + void setArgNo(unsigned N) { Bits.Data.ArgNo = N; } /// Returns the name of the source variable, if it is stored in the /// instruction. StringRef getName(const char *buf) const; - bool isLet() const { return Data.Constant; } + bool isLet() const { return Bits.Data.Constant; } Optional get(VarDecl *VD, const char *buf) const { - if (!Data.HasValue) + if (!Bits.Data.HasValue) return None; if (VD) return SILDebugVariable(VD->getName().empty() ? "" : VD->getName().str(), @@ -1270,6 +1269,8 @@ class AllocationInst : public SingleValueInstruction { DEFINE_ABSTRACT_SINGLE_VALUE_INST_BOILERPLATE(AllocationInst) }; +class DeallocStackInst; + /// AllocStackInst - This represents the allocation of an unboxed (i.e., no /// reference count) stack memory. The memory is provided uninitialized. class AllocStackInst final @@ -1342,6 +1343,9 @@ class AllocStackInst final MutableArrayRef getTypeDependentOperands() { return getAllOperands(); } + + /// Return a single dealloc_stack user or null. + DeallocStackInst *getSingleDeallocStack() const; }; /// The base class for AllocRefInst and AllocRefDynamicInst. @@ -1615,20 +1619,20 @@ class GenericSpecializationInformation { /// The original function that was specialized. SILFunction *Parent; /// Substitutions used to produce this specialization. - SubstitutionList Subs; + SubstitutionMap Subs; GenericSpecializationInformation(SILFunction *Caller, SILFunction *Parent, - SubstitutionList Subs); + SubstitutionMap Subs); public: static const GenericSpecializationInformation *create(SILFunction *Caller, SILFunction *Parent, - SubstitutionList Subs); + SubstitutionMap Subs); static const GenericSpecializationInformation *create(SILInstruction *Inst, SILBuilder &B); const SILFunction *getCaller() const { return Caller; } const SILFunction *getParent() const { return Parent; } - SubstitutionList getSubstitutions() const { return Subs; } + SubstitutionMap getSubstitutions() const { return Subs; } }; class PartialApplyInst; @@ -1664,41 +1668,35 @@ class ApplyInstBase : public Base { /// points to the specialization info of the inlined function. const GenericSpecializationInformation *SpecializationInfo; - /// The number of tail-allocated substitutions, allocated after the operand - /// list's tail allocation. - unsigned NumSubstitutions: 31; - /// Used for apply_inst instructions: true if the called function has an /// error result but is not actually throwing. unsigned NonThrowing: 1; /// The number of call arguments as required by the callee. - unsigned NumCallArguments; + unsigned NumCallArguments : 31; /// The total number of type-dependent operands. unsigned NumTypeDependentOperands; + /// The substitutions being applied to the callee. + SubstitutionMap Substitutions; + Impl &asImpl() { return static_cast(*this); } const Impl &asImpl() const { return static_cast(*this); } - MutableArrayRef getSubstitutionsBuffer() { - return { asImpl().template getTrailingObjects(), - NumSubstitutions }; - } - protected: template ApplyInstBase(SILInstructionKind kind, SILDebugLocation DebugLoc, SILValue callee, - SILType substCalleeType, SubstitutionList subs, + SILType substCalleeType, SubstitutionMap subs, ArrayRef args, ArrayRef typeDependentOperands, const GenericSpecializationInformation *specializationInfo, As... baseArgs) : Base(kind, DebugLoc, baseArgs...), SubstCalleeType(substCalleeType), SpecializationInfo(specializationInfo), - NumSubstitutions(subs.size()), NonThrowing(false), - NumCallArguments(args.size()), - NumTypeDependentOperands(typeDependentOperands.size()) { + NonThrowing(false), NumCallArguments(args.size()), + NumTypeDependentOperands(typeDependentOperands.size()), + Substitutions(subs) { // Initialize the operands. auto allOperands = getAllOperands(); @@ -1710,10 +1708,6 @@ class ApplyInstBase : public Base { new (&allOperands[NumStaticOperands + args.size() + i]) Operand(this, typeDependentOperands[i]); } - - // Initialize the substitutions. - memcpy(getSubstitutionsBuffer().data(), subs.begin(), - sizeof(subs[0]) * subs.size()); } ~ApplyInstBase() { @@ -1724,10 +1718,6 @@ class ApplyInstBase : public Base { template friend class llvm::TrailingObjects; - unsigned numTrailingObjects(OverloadToken) const { - return NumSubstitutions; - } - unsigned numTrailingObjects(OverloadToken) const { return getNumAllOperands(); } @@ -1747,6 +1737,13 @@ class ApplyInstBase : public Base { SILValue getCallee() const { return getAllOperands()[Callee].get(); } + /// Gets the origin of the callee by looking through function type conversions + /// until we find a function_ref, partial_apply, or unrecognized value. + /// + /// This is defined out of line to work around incomplete definition + /// issues. It is at the bottom of the file. + SILValue getCalleeOrigin() const; + /// Gets the referenced function by looking through partial apply, /// convert_function, and thin to thick function until we find a function_ref. /// @@ -1790,13 +1787,12 @@ class ApplyInstBase : public Base { } /// True if this application has generic substitutions. - bool hasSubstitutions() const { return NumSubstitutions != 0; } + bool hasSubstitutions() const { + return Substitutions.hasAnySubstitutableParams(); + } /// The substitutions used to bind the generic arguments of this function. - SubstitutionList getSubstitutions() const { - return { asImpl().template getTrailingObjects(), - NumSubstitutions }; - } + SubstitutionMap getSubstitutionMap() const { return Substitutions; } /// Return the total number of operands of this instruction. unsigned getNumAllOperands() const { @@ -1892,7 +1888,6 @@ class ApplyInstBase using super::getSubstCalleeType; using super::getSubstCalleeConv; using super::hasSubstitutions; - using super::getSubstitutions; using super::getNumArguments; using super::getArgument; using super::getArguments; @@ -1987,12 +1982,12 @@ class ApplyInstBase class ApplyInst final : public InstructionBase>, - public llvm::TrailingObjects { + public llvm::TrailingObjects { friend SILBuilder; ApplyInst(SILDebugLocation DebugLoc, SILValue Callee, SILType SubstCalleeType, SILType ReturnType, - SubstitutionList Substitutions, + SubstitutionMap Substitutions, ArrayRef Args, ArrayRef TypeDependentOperands, bool isNonThrowing, @@ -2000,7 +1995,7 @@ class ApplyInst final static ApplyInst * create(SILDebugLocation DebugLoc, SILValue Callee, - SubstitutionList Substitutions, ArrayRef Args, + SubstitutionMap Substitutions, ArrayRef Args, bool isNonThrowing, Optional ModuleConventions, SILFunction &F, SILOpenedArchetypesState &OpenedArchetypes, const GenericSpecializationInformation *SpecializationInfo); @@ -2019,12 +2014,12 @@ class PartialApplyInst final : public InstructionBase>, - public llvm::TrailingObjects { + public llvm::TrailingObjects { friend SILBuilder; PartialApplyInst(SILDebugLocation DebugLoc, SILValue Callee, SILType SubstCalleeType, - SubstitutionList Substitutions, + SubstitutionMap Substitutions, ArrayRef Args, ArrayRef TypeDependentOperands, SILType ClosureType, @@ -2032,7 +2027,7 @@ class PartialApplyInst final static PartialApplyInst * create(SILDebugLocation DebugLoc, SILValue Callee, ArrayRef Args, - SubstitutionList Substitutions, ParameterConvention CalleeConvention, + SubstitutionMap Substitutions, ParameterConvention CalleeConvention, SILFunction &F, SILOpenedArchetypesState &OpenedArchetypes, const GenericSpecializationInformation *SpecializationInfo); @@ -2078,7 +2073,7 @@ class BeginApplyInst final BeginApplyInst, BeginApplyResult, // These must be earlier trailing objects because their // count fields are initialized by an earlier base class. - InitialTrailingObjects> { + InitialTrailingObjects> { friend SILBuilder; template @@ -2093,7 +2088,7 @@ class BeginApplyInst final SILType substCalleeType, ArrayRef allResultTypes, ArrayRef allResultOwnerships, - SubstitutionList substitutions, + SubstitutionMap substitutions, ArrayRef args, ArrayRef typeDependentOperands, bool isNonThrowing, @@ -2101,7 +2096,7 @@ class BeginApplyInst final static BeginApplyInst * create(SILDebugLocation debugLoc, SILValue Callee, - SubstitutionList substitutions, ArrayRef args, + SubstitutionMap substitutions, ArrayRef args, bool isNonThrowing, Optional moduleConventions, SILFunction &F, SILOpenedArchetypesState &openedArchetypes, const GenericSpecializationInformation *specializationInfo); @@ -2338,7 +2333,7 @@ class KeyPathPatternComponent { ComputedPropertyId::ValueType IdValue; } Computed; // Valid if Kind == External - ArrayRef ExternalSubstitutions; + SubstitutionMap ExternalSubstitutions; }; ArrayRef Indices; struct { @@ -2370,7 +2365,7 @@ class KeyPathPatternComponent { /// Constructor for external components KeyPathPatternComponent(AbstractStorageDecl *externalStorage, - ArrayRef substitutions, + SubstitutionMap substitutions, ArrayRef indices, SILFunction *indicesEqual, SILFunction *indicesHash, @@ -2531,7 +2526,7 @@ class KeyPathPatternComponent { return (AbstractStorageDecl*)ValueAndKind.getPointer(); } - ArrayRef getExternalSubstitutions() const { + SubstitutionMap getExternalSubstitutions() const { assert(getKind() == Kind::External && "not an external property"); return ExternalSubstitutions; @@ -2583,7 +2578,7 @@ class KeyPathPatternComponent { static KeyPathPatternComponent forExternal(AbstractStorageDecl *externalDecl, - ArrayRef substitutions, + SubstitutionMap substitutions, ArrayRef indices, SILFunction *indicesEquals, SILFunction *indicesHash, @@ -2672,29 +2667,27 @@ class KeyPathPattern final class KeyPathInst final : public InstructionBase, - private llvm::TrailingObjects { + private llvm::TrailingObjects { friend SILBuilder; friend TrailingObjects; KeyPathPattern *Pattern; - unsigned NumSubstitutions, NumOperands; + unsigned NumOperands; + SubstitutionMap Substitutions; static KeyPathInst *create(SILDebugLocation Loc, KeyPathPattern *Pattern, - SubstitutionList Subs, + SubstitutionMap Subs, ArrayRef Args, SILType Ty, SILFunction &F); KeyPathInst(SILDebugLocation Loc, KeyPathPattern *Pattern, - SubstitutionList Subs, + SubstitutionMap Subs, ArrayRef Args, SILType Ty); - size_t numTrailingObjects(OverloadToken) const { - return NumSubstitutions; - } size_t numTrailingObjects(OverloadToken) const { return NumOperands; } @@ -2708,11 +2701,8 @@ class KeyPathInst final } MutableArrayRef getAllOperands(); - MutableArrayRef getSubstitutions(); - SubstitutionList getSubstitutions() const { - return const_cast(this)->getSubstitutions(); - } - + SubstitutionMap getSubstitutions() const { return Substitutions; } + void dropReferencedPattern(); ~KeyPathInst(); @@ -2723,18 +2713,21 @@ class KeyPathInst final class BuiltinInst final : public InstructionBaseWithTrailingOperands< SILInstructionKind::BuiltinInst, BuiltinInst, - SingleValueInstruction, Substitution> { + SingleValueInstruction> { friend SILBuilder; /// The name of the builtin to invoke. Identifier Name; + /// The substitutions. + SubstitutionMap Substitutions; + BuiltinInst(SILDebugLocation DebugLoc, Identifier Name, SILType ReturnType, - SubstitutionList Substitutions, ArrayRef Args); + SubstitutionMap Substitutions, ArrayRef Args); static BuiltinInst *create(SILDebugLocation DebugLoc, Identifier Name, SILType ReturnType, - SubstitutionList Substitutions, + SubstitutionMap Substitutions, ArrayRef Args, SILModule &M); public: @@ -2773,20 +2766,12 @@ class BuiltinInst final /// True if this builtin application has substitutions, which represent type /// parameters to the builtin. bool hasSubstitutions() const { - return SILInstruction::Bits.BuiltinInst.NumSubstitutions != 0; + return Substitutions.hasAnySubstitutableParams(); } /// Return the type parameters to the builtin. - SubstitutionList getSubstitutions() const { - return {getTrailingObjects(), - SILInstruction::Bits.BuiltinInst.NumSubstitutions}; - } - /// Return the type parameters to the builtin. - MutableArrayRef getSubstitutions() { - return {getTrailingObjects(), - SILInstruction::Bits.BuiltinInst.NumSubstitutions}; - } - + SubstitutionMap getSubstitutions() const { return Substitutions; } + /// The arguments to the builtin. OperandValueArrayRef getArguments() const { return OperandValueArrayRef(getAllOperands()); @@ -3199,8 +3184,10 @@ enum class SILAccessKind : uint8_t { Deinit, // This enum is encoded. - Last = Deinit + Last = Deinit, }; +enum { NumSILAccessKindBits = 2 }; + StringRef getSILAccessKindName(SILAccessKind kind); /// Different kinds of exclusivity enforcement for accesses. @@ -3238,12 +3225,14 @@ class BeginAccessInst BeginAccessInst(SILDebugLocation loc, SILValue lvalue, SILAccessKind accessKind, SILAccessEnforcement enforcement, - bool noNestedConflict) + bool noNestedConflict, bool fromBuiltin) : UnaryInstructionBase(loc, lvalue, lvalue->getType()) { SILInstruction::Bits.BeginAccessInst.AccessKind = unsigned(accessKind); SILInstruction::Bits.BeginAccessInst.Enforcement = unsigned(enforcement); SILInstruction::Bits.BeginAccessInst.NoNestedConflict = unsigned(noNestedConflict); + SILInstruction::Bits.BeginAccessInst.FromBuiltin = + unsigned(fromBuiltin); static_assert(unsigned(SILAccessKind::Last) < (1 << 2), "reserve sufficient bits for serialized SIL"); @@ -3287,6 +3276,13 @@ class BeginAccessInst SILInstruction::Bits.BeginAccessInst.NoNestedConflict = noNestedConflict; } + /// Return true if this access marker was emitted for a user-controlled + /// Builtin. Return false if this access marker was auto-generated by the + /// compiler to enforce formal access that derives from the language. + bool isFromBuiltin() const { + return SILInstruction::Bits.BeginAccessInst.FromBuiltin; + } + SILValue getSource() const { return getOperand(); } @@ -3367,7 +3363,8 @@ class BeginUnpairedAccessInst BeginUnpairedAccessInst(SILDebugLocation loc, SILValue addr, SILValue buffer, SILAccessKind accessKind, SILAccessEnforcement enforcement, - bool noNestedConflict) + bool noNestedConflict, + bool fromBuiltin) : InstructionBase(loc), Operands(this, addr, buffer) { SILInstruction::Bits.BeginUnpairedAccessInst.AccessKind = unsigned(accessKind); @@ -3375,6 +3372,8 @@ class BeginUnpairedAccessInst unsigned(enforcement); SILInstruction::Bits.BeginUnpairedAccessInst.NoNestedConflict = unsigned(noNestedConflict); + SILInstruction::Bits.BeginUnpairedAccessInst.FromBuiltin = + unsigned(fromBuiltin); } public: @@ -3409,6 +3408,13 @@ class BeginUnpairedAccessInst noNestedConflict; } + /// Return true if this access marker was emitted for a user-controlled + /// Builtin. Return false if this access marker was auto-generated by the + /// compiler to enforce formal access that derives from the language. + bool isFromBuiltin() const { + return SILInstruction::Bits.BeginUnpairedAccessInst.FromBuiltin; + } + SILValue getSource() const { return Operands[0].get(); } @@ -3437,11 +3443,13 @@ class EndUnpairedAccessInst private: EndUnpairedAccessInst(SILDebugLocation loc, SILValue buffer, - SILAccessEnforcement enforcement, bool aborting = false) + SILAccessEnforcement enforcement, bool aborting, + bool fromBuiltin) : UnaryInstructionBase(loc, buffer) { SILInstruction::Bits.EndUnpairedAccessInst.Enforcement = unsigned(enforcement); SILInstruction::Bits.EndUnpairedAccessInst.Aborting = aborting; + SILInstruction::Bits.EndUnpairedAccessInst.FromBuiltin = fromBuiltin; } public: @@ -3467,6 +3475,13 @@ class EndUnpairedAccessInst unsigned(enforcement); } + /// Return true if this access marker was emitted for a user-controlled + /// Builtin. Return false if this access marker was auto-generated by the + /// compiler to enforce formal access that derives from the language. + bool isFromBuiltin() const { + return SILInstruction::Bits.EndUnpairedAccessInst.FromBuiltin; + } + SILValue getBuffer() const { return getOperand(); } @@ -3579,15 +3594,14 @@ class MarkUninitializedInst /// This is only valid in Raw SIL. class MarkUninitializedBehaviorInst final : public InstructionBase, - private llvm::TrailingObjects + SingleValueInstruction> { friend SILBuilder; - friend TrailingObjects; FixedOperandList<4> Operands; - unsigned NumInitStorageSubstitutions, NumSetterSubstitutions; - + SubstitutionMap InitStorageSubstitutions; + SubstitutionMap SetterSubstitutions; + enum { // The initialization function for the storage. InitStorageFunc, @@ -3599,26 +3613,22 @@ class MarkUninitializedBehaviorInst final Self, }; - size_t numTrailingObjects(OverloadToken) { - return NumInitStorageSubstitutions + NumSetterSubstitutions; - } - MarkUninitializedBehaviorInst(SILDebugLocation DebugLoc, SILValue InitStorage, - SubstitutionList InitStorageSubs, + SubstitutionMap InitStorageSubs, SILValue Storage, SILValue Setter, - SubstitutionList SetterSubs, + SubstitutionMap SetterSubs, SILValue Self, SILType Ty); static MarkUninitializedBehaviorInst *create(SILModule &M, SILDebugLocation DebugLoc, SILValue InitStorage, - SubstitutionList InitStorageSubs, + SubstitutionMap InitStorageSubs, SILValue Storage, SILValue Setter, - SubstitutionList SetterSubs, + SubstitutionMap SetterSubs, SILValue Self, SILType Ty); @@ -3626,8 +3636,8 @@ class MarkUninitializedBehaviorInst final SILValue getInitStorageFunc() const { return Operands[InitStorageFunc].get(); } - SubstitutionList getInitStorageSubstitutions() const { - return {getTrailingObjects(), NumInitStorageSubstitutions}; + SubstitutionMap getInitStorageSubstitutions() const { + return InitStorageSubstitutions; } SILValue getStorage() const { return Operands[Storage].get(); @@ -3636,9 +3646,8 @@ class MarkUninitializedBehaviorInst final SILValue getSetterFunc() const { return Operands[SetterFunc].get(); } - SubstitutionList getSetterSubstitutions() const { - return {getTrailingObjects() + NumInitStorageSubstitutions, - NumSetterSubstitutions}; + SubstitutionMap getSetterSubstitutions() const { + return SetterSubstitutions; } SILValue getSelf() const { return Operands[Self].get(); @@ -3739,7 +3748,7 @@ class LoadReferenceInstBase : public UnaryInstructionBase { static SILType getResultType(SILType operandTy) { assert(operandTy.isAddress() && "loading from non-address operand?"); - auto refType = cast(operandTy.getSwiftRValueType()); + auto refType = cast(operandTy.getASTType()); return SILType::getPrimitiveObjectType(refType.getReferentType()); } @@ -3987,13 +3996,18 @@ class ConvertEscapeToNoEscapeInst final friend SILBuilder; bool lifetimeGuaranteed; + bool isEscaped; // Even if we can analyze this + // instruction the user might have + // escaped it. ConvertEscapeToNoEscapeInst(SILDebugLocation DebugLoc, SILValue Operand, ArrayRef TypeDependentOperands, - SILType Ty, bool isLifetimeGuaranteed) + SILType Ty, bool isEscapedByUser, + bool isLifetimeGuaranteed) : UnaryInstructionWithTypeDependentOperandsBase( DebugLoc, Operand, TypeDependentOperands, Ty), - lifetimeGuaranteed(isLifetimeGuaranteed) { + lifetimeGuaranteed(isLifetimeGuaranteed), + isEscaped(isEscapedByUser) { assert(!Operand->getType().castTo()->isNoEscape()); assert(Ty.castTo()->isNoEscape()); } @@ -4001,11 +4015,17 @@ class ConvertEscapeToNoEscapeInst final static ConvertEscapeToNoEscapeInst * create(SILDebugLocation DebugLoc, SILValue Operand, SILType Ty, SILFunction &F, SILOpenedArchetypesState &OpenedArchetypes, - bool lifetimeGuaranteed); + bool isEscapedByUser, bool lifetimeGuaranteed); public: bool isLifetimeGuaranteed() const { return lifetimeGuaranteed; } + + bool isEscapedByUser() const { + return isEscaped; + } + + void setEscapedByUser(bool isEscaped = true) { this->isEscaped = isEscaped; } }; /// ThinFunctionToPointerInst - Convert a thin function pointer to a @@ -4477,13 +4497,13 @@ class UnconditionalCheckedCastInst final /// Returns the formal type of the source value. CanType getSourceType() const { // This instruction is only used with types that allow this. - return getOperand()->getType().getSwiftRValueType(); + return getOperand()->getType().getASTType(); } /// Returns the formal target type. CanType getTargetType() const { // This instruction is only used with types that allow this. - return getType().getSwiftRValueType(); + return getType().getASTType(); } }; @@ -4930,7 +4950,7 @@ class TupleInst final } TupleType *getTupleType() const { - return getType().getSwiftRValueType()->castTo(); + return getType().castTo(); } /// Search the operands of this tuple for a unique non-trivial elt. If we find @@ -5409,7 +5429,7 @@ class TupleExtractInst } TupleType *getTupleType() const { - return getOperand()->getType().getSwiftRValueType()->castTo(); + return getOperand()->getType().castTo(); } unsigned getNumTupleElts() const { @@ -5442,7 +5462,7 @@ class TupleElementAddrInst TupleType *getTupleType() const { - return getOperand()->getType().getSwiftRValueType()->castTo(); + return getOperand()->getType().castTo(); } }; @@ -5947,8 +5967,8 @@ class InitExistentialMetatypeInst final /// instruction erases Decoder.Type.Type to Printable.Type.Type, /// this method returns Decoder. CanType getFormalErasedObjectType() const { - CanType exType = getType().getSwiftRValueType(); - CanType concreteType = getOperand()->getType().getSwiftRValueType(); + auto exType = getType().getASTType(); + auto concreteType = getOperand()->getType().getASTType(); while (auto exMetatype = dyn_cast(exType)) { exType = exMetatype.getInstanceType(); concreteType = cast(concreteType).getInstanceType(); @@ -6006,39 +6026,28 @@ class InitBlockStorageHeaderInst friend SILBuilder; enum { BlockStorage, InvokeFunction }; - unsigned NumSubstitutions; + SubstitutionMap Substitutions; FixedOperandList<2> Operands; - Substitution *getSubstitutionsStorage() { - return reinterpret_cast(Operands.asArray().end()); - } - const Substitution *getSubstitutionsStorage() const { - return reinterpret_cast(Operands.asArray().end()); - } - InitBlockStorageHeaderInst(SILDebugLocation DebugLoc, SILValue BlockStorage, SILValue InvokeFunction, SILType BlockType, - SubstitutionList Subs) + SubstitutionMap Subs) : InstructionBase(DebugLoc, BlockType), - NumSubstitutions(Subs.size()), + Substitutions(Subs), Operands(this, BlockStorage, InvokeFunction) { - memcpy(getSubstitutionsStorage(), Subs.begin(), - sizeof(Subs[0]) * Subs.size()); } static InitBlockStorageHeaderInst *create(SILFunction &F, SILDebugLocation DebugLoc, SILValue BlockStorage, SILValue InvokeFunction, SILType BlockType, - SubstitutionList Subs); + SubstitutionMap Subs); public: /// Get the block storage address to be initialized. SILValue getBlockStorage() const { return Operands[BlockStorage].get(); } /// Get the invoke function to form the block around. SILValue getInvokeFunction() const { return Operands[InvokeFunction].get(); } - SubstitutionList getSubstitutions() const { - return {getSubstitutionsStorage(), NumSubstitutions}; - } + SubstitutionMap getSubstitutions() const { return Substitutions; } ArrayRef getAllOperands() const { return Operands.asArray(); } MutableArrayRef getAllOperands() { return Operands.asArray(); } @@ -6184,7 +6193,6 @@ class MarkDependenceInst SingleValueInstruction> { friend SILBuilder; - enum { Value, Base }; FixedOperandList<2> Operands; MarkDependenceInst(SILDebugLocation DebugLoc, SILValue value, SILValue base) @@ -6192,6 +6200,8 @@ class MarkDependenceInst Operands{this, value, base} {} public: + enum { Value, Base }; + SILValue getValue() const { return Operands[Value].get(); } SILValue getBase() const { return Operands[Base].get(); } @@ -6218,6 +6228,35 @@ class CopyBlockInst : UnaryInstructionBase(DebugLoc, operand, operand->getType()) {} }; +class CopyBlockWithoutEscapingInst + : public InstructionBase { + friend SILBuilder; + + FixedOperandList<2> Operands; + + CopyBlockWithoutEscapingInst(SILDebugLocation DebugLoc, SILValue block, + SILValue closure) + : InstructionBase(DebugLoc, block->getType()), Operands{this, block, + closure} {} + +public: + enum { Block, Closure }; + + SILValue getBlock() const { return Operands[Block].get(); } + SILValue getClosure() const { return Operands[Closure].get(); } + + void setBlock(SILValue block) { + Operands[Block].set(block); + } + void setClosure(SILValue closure) { + Operands[Closure].set(closure); + } + + ArrayRef getAllOperands() const { return Operands.asArray(); } + MutableArrayRef getAllOperands() { return Operands.asArray(); } +}; + class CopyValueInst : public UnaryInstructionBase { @@ -6279,9 +6318,17 @@ class IsEscapingClosureInst SingleValueInstruction> { friend SILBuilder; + unsigned VerificationType; + IsEscapingClosureInst(SILDebugLocation DebugLoc, SILValue Operand, - SILType BoolTy) - : UnaryInstructionBase(DebugLoc, Operand, BoolTy) {} + SILType BoolTy, unsigned VerificationType) + : UnaryInstructionBase(DebugLoc, Operand, BoolTy), + VerificationType(VerificationType) {} + +public: + enum { WithoutActuallyEscaping, ObjCEscaping }; + + unsigned getVerificationType() const { return VerificationType; } }; //===----------------------------------------------------------------------===// @@ -6608,29 +6655,35 @@ class TermInst : public NonValueInstruction { using succblock_iterator = TransformIterator>; + SILBasicBlock *(*)(const SILSuccessor &)>; using const_succblock_iterator = TransformIterator< const SILSuccessor *, - std::function>; + const SILBasicBlock *(*)(const SILSuccessor &)>; succblock_iterator succblock_begin() { - using FuncTy = std::function; - FuncTy F(&SILSuccessor::getBB); - return makeTransformIterator(getSuccessors().begin(), F); + return succblock_iterator(getSuccessors().begin(), + [](const SILSuccessor &succ) -> SILBasicBlock * { + return succ.getBB(); + }); } succblock_iterator succblock_end() { - using FuncTy = std::function; - FuncTy F(&SILSuccessor::getBB); - return makeTransformIterator(getSuccessors().end(), F); + return succblock_iterator(getSuccessors().end(), + [](const SILSuccessor &succ) -> SILBasicBlock * { + return succ.getBB(); + }); } const_succblock_iterator succblock_begin() const { - using FuncTy = std::function; - FuncTy F(&SILSuccessor::getBB); - return makeTransformIterator(getSuccessors().begin(), F); + return const_succblock_iterator( + getSuccessors().begin(), + [](const SILSuccessor &succ) -> const SILBasicBlock * { + return succ.getBB(); + }); } const_succblock_iterator succblock_end() const { - using FuncTy = std::function; - FuncTy F(&SILSuccessor::getBB); - return makeTransformIterator(getSuccessors().end(), F); + return const_succblock_iterator( + getSuccessors().end(), + [](const SILSuccessor &succ) -> const SILBasicBlock * { + return succ.getBB(); + }); } SILBasicBlock *getSingleSuccessorBlock() { @@ -6653,23 +6706,26 @@ class TermInst : public NonValueInstruction { using SuccessorBlockListTy = TransformRange>; + SILBasicBlock *(*)(const SILSuccessor &)>; using ConstSuccessorBlockListTy = - TransformRange>; + TransformRange; /// Return the range of SILBasicBlocks that are successors of this block. SuccessorBlockListTy getSuccessorBlocks() { - using FuncTy = std::function; - FuncTy F(&SILSuccessor::getBB); - return makeTransformRange(getSuccessors(), F); + return SuccessorBlockListTy(getSuccessors(), + [](const SILSuccessor &succ) -> SILBasicBlock* { + return succ.getBB(); + }); } /// Return the range of SILBasicBlocks that are successors of this block. ConstSuccessorBlockListTy getSuccessorBlocks() const { - using FuncTy = std::function; - FuncTy F(&SILSuccessor::getBB); - return makeTransformRange(getSuccessors(), F); + return ConstSuccessorBlockListTy( + getSuccessors(), + [](const SILSuccessor &succ) -> const SILBasicBlock * { + return succ.getBB(); + }); } DEFINE_ABSTRACT_NON_VALUE_INST_BOILERPLATE(TermInst) @@ -7314,13 +7370,13 @@ class CheckedCastBranchInst final: /// Returns the formal type of the source value. CanType getSourceType() const { // This instruction is only used with types that allow this. - return getOperand()->getType().getSwiftRValueType(); + return getOperand()->getType().getASTType(); } /// Returns the formal target type. CanType getTargetType() const { // This instruction is only used with types that allow this. - return getCastType().getSwiftRValueType(); + return getCastType().getASTType(); } SILType getCastType() const { return DestTy; } @@ -7369,13 +7425,13 @@ class CheckedCastValueBranchInst final /// Returns the formal type of the source value. CanType getSourceType() const { // This instruction is only used with types that allow this. - return getOperand()->getType().getSwiftRValueType(); + return getOperand()->getType().getASTType(); } /// Returns the formal target type. CanType getTargetType() const { // This instruction is only used with types that allow this. - return getCastType().getSwiftRValueType(); + return getCastType().getASTType(); } SILType getCastType() const { return DestTy; } @@ -7489,11 +7545,11 @@ class TryApplyInstBase : public TermInst { class TryApplyInst final : public InstructionBase>, - public llvm::TrailingObjects { + public llvm::TrailingObjects { friend SILBuilder; TryApplyInst(SILDebugLocation DebugLoc, SILValue callee, - SILType substCalleeType, SubstitutionList substitutions, + SILType substCalleeType, SubstitutionMap substitutions, ArrayRef args, ArrayRef TypeDependentOperands, SILBasicBlock *normalBB, SILBasicBlock *errorBB, @@ -7501,7 +7557,7 @@ class TryApplyInst final static TryApplyInst * create(SILDebugLocation DebugLoc, SILValue callee, - SubstitutionList substitutions, ArrayRef args, + SubstitutionMap substitutions, ArrayRef args, SILBasicBlock *normalBB, SILBasicBlock *errorBB, SILFunction &F, SILOpenedArchetypesState &OpenedArchetypes, const GenericSpecializationInformation *SpecializationInfo); @@ -7574,6 +7630,12 @@ class ApplySite { FOREACH_IMPL_RETURN(getCallee()); } + /// Return the callee value by looking through function conversions until we + /// find a function_ref, partial_apply, or unrecognized callee value. + SILValue getCalleeOrigin() const { + FOREACH_IMPL_RETURN(getCalleeOrigin()); + } + /// Gets the referenced function by looking through partial apply, /// convert_function, and thin to thick function until we find a function_ref. SILFunction *getCalleeFunction() const { @@ -7636,18 +7698,8 @@ class ApplySite { } /// The substitutions used to bind the generic arguments of this function. - SubstitutionList getSubstitutions() const { - FOREACH_IMPL_RETURN(getSubstitutions()); - } - - /// Return a begin iterator for the substitution array. - auto subs_begin() const -> decltype(getSubstitutions().begin()) { - return getSubstitutions().begin(); - } - - /// Return an end iterator for the substitution array. - auto subs_end() const -> decltype(getSubstitutions().end()) { - return getSubstitutions().end(); + SubstitutionMap getSubstitutionMap() const { + FOREACH_IMPL_RETURN(getSubstitutionMap()); } /// The arguments passed to this instruction. @@ -7698,8 +7750,15 @@ class ApplySite { case SILInstructionKind::TryApplyInst: return 0; case SILInstructionKind::PartialApplyInst: - // The arguments to partial_apply are a suffix of the arguments to the - // the actually-called function. + // The arguments to partial_apply are a suffix of the partial_apply's + // callee. Note that getSubstCalleeConv is function type of the callee + // argument passed to this apply, not necessarilly the function type of + // the underlying callee function (i.e. it is based on the `getCallee` + // type, not the `getCalleeOrigin` type). + // + // pa1 = partial_apply f(c) : $(a, b, c) + // pa2 = partial_apply pa1(b) : $(a, b) + // apply pa2(a) return getSubstCalleeConv().getNumSILArguments() - getNumArguments(); default: llvm_unreachable("not implemented for this instruction!"); @@ -7850,29 +7909,37 @@ class FullApplySite : public ApplySite { // PartialApplyInst being defined, but PartialApplyInst is a subclass of // ApplyInstBase, so we can not place ApplyInstBase after it. template -SILFunction *ApplyInstBase::getCalleeFunction() const { +SILValue ApplyInstBase::getCalleeOrigin() const { SILValue Callee = getCallee(); - while (true) { - if (auto *FRI = dyn_cast(Callee)) { - return FRI->getReferencedFunction(); - } - - if (auto *PAI = dyn_cast(Callee)) { - Callee = PAI->getCallee(); - continue; - } - if (auto *TTTFI = dyn_cast(Callee)) { Callee = TTTFI->getCallee(); continue; } - if (auto *CFI = dyn_cast(Callee)) { Callee = CFI->getConverted(); continue; } + if (auto *CETN = dyn_cast(Callee)) { + Callee = CETN->getOperand(); + continue; + } + return Callee; + } +} +template +SILFunction *ApplyInstBase::getCalleeFunction() const { + SILValue Callee = getCalleeOrigin(); + + while (true) { + if (auto *FRI = dyn_cast(Callee)) + return FRI->getReferencedFunction(); + + if (auto *PAI = dyn_cast(Callee)) { + Callee = PAI->getCalleeOrigin(); + continue; + } return nullptr; } } diff --git a/include/swift/SIL/SILLocation.h b/include/swift/SIL/SILLocation.h index d3f44f3e59009..fb8418c31f16f 100644 --- a/include/swift/SIL/SILLocation.h +++ b/include/swift/SIL/SILLocation.h @@ -65,7 +65,7 @@ class SILLocation { using type = Pattern; }; - typedef llvm::PointerUnion4 ASTNodeTy; + using ASTNodeTy = llvm::PointerUnion4; public: enum LocationKind : unsigned { @@ -419,7 +419,7 @@ class SILLocation { } /// Fingerprint a DebugLoc for use in a DenseMap. - typedef std::pair, StringRef> DebugLocKey; + using DebugLocKey = std::pair, StringRef>; struct DebugLocHash : public DebugLocKey { DebugLocHash(DebugLoc L) : DebugLocKey({{L.Line, L.Column}, L.Filename}) {} }; diff --git a/include/swift/SIL/SILModule.h b/include/swift/SIL/SILModule.h index 636290959ac35..07451cf6c806f 100644 --- a/include/swift/SIL/SILModule.h +++ b/include/swift/SIL/SILModule.h @@ -19,7 +19,6 @@ #include "swift/AST/ASTContext.h" #include "swift/AST/Builtins.h" -#include "swift/AST/Module.h" #include "swift/AST/SILLayout.h" #include "swift/AST/SILOptions.h" #include "swift/Basic/LangOptions.h" @@ -39,6 +38,7 @@ #include "swift/SIL/TypeLowering.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/MapVector.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/PointerIntPair.h" #include "llvm/ADT/SetVector.h" @@ -57,8 +57,10 @@ class Output; namespace swift { class AnyFunctionType; class ASTContext; + class FileUnit; class FuncDecl; class KeyPathPattern; + class ModuleDecl; class SILUndef; class SourceFile; class SerializedSILLoader; @@ -107,8 +109,17 @@ class SILModule { using PropertyListType = llvm::ilist; using WitnessTableListType = llvm::ilist; using DefaultWitnessTableListType = llvm::ilist; - using CoverageMapListType = llvm::ilist; - using LinkingMode = SILOptions::LinkingMode; + using CoverageMapCollectionType = + llvm::MapVector; + + enum class LinkingMode : uint8_t { + /// Link functions with non-public linkage. Used by the mandatory pipeline. + LinkNormal, + + /// Link all functions. Used by the performance pipeine. + LinkAll + }; + using ActionCallback = std::function; private: @@ -185,8 +196,8 @@ class SILModule { /// The list of SILGlobalVariables in the module. GlobalListType silGlobals; - // The list of SILCoverageMaps in the module. - CoverageMapListType coverageMaps; + // The map of SILCoverageMaps in the module. + CoverageMapCollectionType coverageMaps; // The list of SILProperties in the module. PropertyListType properties; @@ -217,7 +228,7 @@ class SILModule { // Callbacks registered by the SIL optimizer to run on each deserialized // function body. This is intentionally a stateless type because the // ModuleDecl and SILFunction should be sufficient context. - typedef void (*SILFunctionBodyCallback)(ModuleDecl *, SILFunction *F); + using SILFunctionBodyCallback = void (*)(ModuleDecl *, SILFunction *F); SmallVector DeserializationCallbacks; /// The SILLoader used when linking functions into this module. @@ -335,15 +346,12 @@ class SILModule { /// later parse SIL bodies directly into, without converting from an AST. static std::unique_ptr createEmptyModule(ModuleDecl *M, SILOptions &Options, - bool WholeModule = false) { - return std::unique_ptr( - new SILModule(M, Options, M, WholeModule)); - } + bool WholeModule = false); /// Get the Swift module associated with this SIL module. ModuleDecl *getSwiftModule() const { return TheSwiftModule; } /// Get the AST context used for type uniquing etc. by this SIL module. - ASTContext &getASTContext() const { return TheSwiftModule->getASTContext(); } + ASTContext &getASTContext() const; SourceManager &getSourceManager() const { return getASTContext().SourceMgr; } /// Get the Swift DeclContext associated with this SIL module. @@ -455,10 +463,12 @@ class SILModule { return {silGlobals.begin(), silGlobals.end()}; } - using coverage_map_iterator = CoverageMapListType::iterator; - using coverage_map_const_iterator = CoverageMapListType::const_iterator; - CoverageMapListType &getCoverageMapList() { return coverageMaps; } - const CoverageMapListType &getCoverageMapList() const { return coverageMaps; } + using coverage_map_iterator = CoverageMapCollectionType::iterator; + using coverage_map_const_iterator = CoverageMapCollectionType::const_iterator; + CoverageMapCollectionType &getCoverageMaps() { return coverageMaps; } + const CoverageMapCollectionType &getCoverageMaps() const { + return coverageMaps; + } llvm::yaml::Output *getOptRecordStream() { return OptRecordStream.get(); } void setOptRecordStream(std::unique_ptr &&Stream, @@ -486,12 +496,16 @@ class SILModule { /// \return null if this module has no such function SILFunction *lookUpFunction(SILDeclRef fnRef); + /// Attempt to deserialize the SILFunction. Returns true if deserialization + /// succeeded, false otherwise. + bool loadFunction(SILFunction *F); + /// Attempt to link the SILFunction. Returns true if linking succeeded, false /// otherwise. /// /// \return false if the linking failed. - bool linkFunction(SILFunction *Fun, - LinkingMode LinkAll = LinkingMode::LinkNormal); + bool linkFunction(SILFunction *F, + LinkingMode LinkMode = LinkingMode::LinkNormal); /// Check if a given function exists in any of the modules with a /// required linkage, i.e. it can be linked by linkFunction. @@ -504,12 +518,6 @@ class SILModule { /// i.e. it can be linked by linkFunction. bool hasFunction(StringRef Name); - /// Link in all Witness Tables in the module. - void linkAllWitnessTables(); - - /// Link in all VTables in the module. - void linkAllVTables(); - /// Link all definitions in all segments that are logically part of /// the same AST module. void linkAllFromCurrentModule(); @@ -624,6 +632,14 @@ class SILModule { PGOReader = std::move(IPR); } + /// Can value operations (copies and destroys) on the given lowered type + /// be performed in this module? + bool isTypeABIAccessible(SILType type); + + /// Can type metadata for the given formal type be fetched in + /// the given module? + bool isTypeMetadataAccessible(CanType type); + /// \brief Run the SIL verifier to make sure that all Functions follow /// invariants. void verify() const; @@ -708,6 +724,11 @@ class SILModule { bool isDefaultAtomic() const { return ! getOptions().AssumeSingleThreaded; } + + /// Returns true if SIL entities associated with declarations in the given + /// declaration context ought to be serialized as part of this module. + bool shouldSerializeEntitiesAssociatedWithDeclContext(const DeclContext *DC) + const; }; inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const SILModule &M){ diff --git a/include/swift/SIL/SILNode.h b/include/swift/SIL/SILNode.h index e0c7340c80908..b483cf0656607 100644 --- a/include/swift/SIL/SILNode.h +++ b/include/swift/SIL/SILNode.h @@ -162,11 +162,6 @@ class alignas(8) SILNode { // Ensure that TupleInst bitfield does not overflow. IBWTO_BITFIELD_EMPTY(TupleInst, SingleValueInstruction); - IBWTO_BITFIELD(BuiltinInst, SingleValueInstruction, - 32-NumSingleValueInstructionBits, - NumSubstitutions : 32-NumSingleValueInstructionBits - ); - IBWTO_BITFIELD(ObjectInst, SingleValueInstruction, 32-NumSingleValueInstructionBits, NumBaseElements : 32-NumSingleValueInstructionBits @@ -263,24 +258,28 @@ class alignas(8) SILNode { SWIFT_INLINE_BITFIELD(BeginAccessInst, SingleValueInstruction, NumSILAccessKindBits+NumSILAccessEnforcementBits - + 1, + + 1 + 1, AccessKind : NumSILAccessKindBits, Enforcement : NumSILAccessEnforcementBits, - NoNestedConflict : 1 + NoNestedConflict : 1, + FromBuiltin : 1 ); SWIFT_INLINE_BITFIELD(BeginUnpairedAccessInst, NonValueInstruction, - NumSILAccessKindBits + NumSILAccessEnforcementBits + 1, + NumSILAccessKindBits + NumSILAccessEnforcementBits + + 1 + 1, AccessKind : NumSILAccessKindBits, Enforcement : NumSILAccessEnforcementBits, - NoNestedConflict : 1); + NoNestedConflict : 1, + FromBuiltin : 1); SWIFT_INLINE_BITFIELD(EndAccessInst, NonValueInstruction, 1, Aborting : 1 ); SWIFT_INLINE_BITFIELD(EndUnpairedAccessInst, NonValueInstruction, - NumSILAccessEnforcementBits + 1, + NumSILAccessEnforcementBits + 1 + 1, Enforcement : NumSILAccessEnforcementBits, - Aborting : 1); + Aborting : 1, + FromBuiltin : 1); SWIFT_INLINE_BITFIELD(StoreInst, NonValueInstruction, NumStoreOwnershipQualifierBits, diff --git a/include/swift/SIL/SILNodes.def b/include/swift/SIL/SILNodes.def index 089bddc348b55..3b9a71c3fe7a6 100644 --- a/include/swift/SIL/SILNodes.def +++ b/include/swift/SIL/SILNodes.def @@ -361,6 +361,8 @@ ABSTRACT_VALUE_AND_INST(SingleValueInstruction, ValueBase, SILInstruction) SingleValueInstruction, None, DoesNotRelease) SINGLE_VALUE_INST(CopyBlockInst, copy_block, SingleValueInstruction, MayHaveSideEffects, DoesNotRelease) + SINGLE_VALUE_INST(CopyBlockWithoutEscapingInst, copy_block_without_escaping, + SingleValueInstruction, MayHaveSideEffects, DoesNotRelease) SINGLE_VALUE_INST(CopyValueInst, copy_value, SingleValueInstruction, MayHaveSideEffects, DoesNotRelease) SINGLE_VALUE_INST(CopyUnownedValueInst, copy_unowned_value, diff --git a/include/swift/SIL/SILProfiler.h b/include/swift/SIL/SILProfiler.h index ee2f8d40addd2..c7cfb3b595860 100644 --- a/include/swift/SIL/SILProfiler.h +++ b/include/swift/SIL/SILProfiler.h @@ -19,17 +19,21 @@ #define SWIFT_SIL_PROFILER_H #include "swift/AST/ASTNode.h" -#include "swift/AST/Stmt.h" #include "swift/Basic/ProfileCounter.h" #include "swift/SIL/SILAllocated.h" +#include "swift/SIL/SILDeclRef.h" #include "llvm/ADT/DenseMap.h" namespace swift { class AbstractFunctionDecl; class SILCoverageMap; +class SILFunction; class SILModule; +/// Returns whether the given AST node requires profiling instrumentation. +bool doesASTRequireProfiling(SILModule &M, ASTNode N); + /// SILProfiler - Maps AST nodes to profile counters. class SILProfiler : public SILAllocated { private: @@ -61,7 +65,8 @@ class SILProfiler : public SILAllocated { : M(M), Root(Root), EmitCoverageMapping(EmitCoverageMapping) {} public: - static SILProfiler *create(SILModule &M, ASTNode N); + static SILProfiler *create(SILModule &M, ForDefinition_t forDefinition, + ASTNode N); /// Check if the function is set up for profiling. bool hasRegionCounters() const { return NumRegionCounters != 0; } @@ -88,9 +93,6 @@ class SILProfiler : public SILAllocated { return RegionCounterMap; } - /// Increment the number of counter updates associated with this profiler. - void recordCounterUpdate(); - private: /// Map counters to ASTNodes and set them up for profiling the function. void assignRegionCounters(); diff --git a/include/swift/SIL/SILProperty.h b/include/swift/SIL/SILProperty.h index a59c3f415c1d1..3a3c46b5d36d8 100644 --- a/include/swift/SIL/SILProperty.h +++ b/include/swift/SIL/SILProperty.h @@ -79,7 +79,7 @@ namespace llvm { template <> struct ilist_traits<::swift::SILProperty> : public ilist_default_traits<::swift::SILProperty> { - typedef ::swift::SILProperty SILProperty; + using SILProperty = ::swift::SILProperty; public: static void deleteNode(SILProperty *VT) { VT->~SILProperty(); } diff --git a/include/swift/SIL/SILType.h b/include/swift/SIL/SILType.h index 052eb0f634f3e..14c9b7996ed98 100644 --- a/include/swift/SIL/SILType.h +++ b/include/swift/SIL/SILType.h @@ -138,7 +138,7 @@ class SILType { /// Returns the \p Category variant of this type. SILType getCategoryType(SILValueCategory Category) const { - return SILType(getSwiftRValueType(), Category); + return SILType(getASTType(), Category); } /// Returns the variant of this type that matches \p Ty.getCategory() @@ -149,20 +149,26 @@ class SILType { /// Returns the address variant of this type. Instructions which /// manipulate memory will generally work with object addresses. SILType getAddressType() const { - return SILType(getSwiftRValueType(), SILValueCategory::Address); + return SILType(getASTType(), SILValueCategory::Address); } /// Returns the object variant of this type. Note that address-only /// types are not legal to manipulate directly as objects in SIL. SILType getObjectType() const { - return SILType(getSwiftRValueType(), SILValueCategory::Object); + return SILType(getASTType(), SILValueCategory::Object); } - /// Returns the Swift type referenced by this SIL type. - CanType getSwiftRValueType() const { + /// Returns the canonical AST type referenced by this SIL type. + CanType getASTType() const { return CanType(value.getPointer()); } + // FIXME -- Temporary until LLDB adopts getASTType() + LLVM_ATTRIBUTE_DEPRECATED(CanType getSwiftRValueType() const, + "Please use getASTType()") { + return getASTType(); + } + /// Returns the AbstractCC of a function type. /// The SILType must refer to a function type. SILFunctionTypeRepresentation getFunctionRepresentation() const { @@ -173,18 +179,18 @@ class SILType { /// cast fails. template typename CanTypeWrapperTraits::type - getAs() const { return dyn_cast(getSwiftRValueType()); } + getAs() const { return dyn_cast(getASTType()); } /// Cast the Swift type referenced by this SIL type, which must be of the /// specified subtype. template typename CanTypeWrapperTraits::type - castTo() const { return cast(getSwiftRValueType()); } + castTo() const { return cast(getASTType()); } /// Returns true if the Swift type referenced by this SIL type is of the /// specified subtype. template - bool is() const { return isa(getSwiftRValueType()); } + bool is() const { return isa(getASTType()); } bool isVoid() const { return value.getPointer()->isVoid(); @@ -193,22 +199,22 @@ class SILType { /// Retrieve the ClassDecl for a type that maps to a Swift class or /// bound generic class type. ClassDecl *getClassOrBoundGenericClass() const { - return getSwiftRValueType().getClassOrBoundGenericClass(); + return getASTType().getClassOrBoundGenericClass(); } /// Retrieve the StructDecl for a type that maps to a Swift struct or /// bound generic struct type. StructDecl *getStructOrBoundGenericStruct() const { - return getSwiftRValueType().getStructOrBoundGenericStruct(); + return getASTType().getStructOrBoundGenericStruct(); } /// Retrieve the EnumDecl for a type that maps to a Swift enum or /// bound generic enum type. EnumDecl *getEnumOrBoundGenericEnum() const { - return getSwiftRValueType().getEnumOrBoundGenericEnum(); + return getASTType().getEnumOrBoundGenericEnum(); } /// Retrieve the NominalTypeDecl for a type that maps to a Swift /// nominal or bound generic nominal type. NominalTypeDecl *getNominalOrBoundGenericNominal() const { - return getSwiftRValueType().getNominalOrBoundGenericNominal(); + return getASTType().getNominalOrBoundGenericNominal(); } /// True if the type is an address type. @@ -274,43 +280,43 @@ class SILType { /// Returns true if the referenced type has reference semantics. bool hasReferenceSemantics() const { - return getSwiftRValueType().hasReferenceSemantics(); + return getASTType().hasReferenceSemantics(); } /// Returns true if the referenced type is any sort of class-reference type, /// meaning anything with reference semantics that is not a function type. bool isAnyClassReferenceType() const { - return getSwiftRValueType().isAnyClassReferenceType(); + return getASTType().isAnyClassReferenceType(); } /// Returns true if the referenced type is guaranteed to have a /// single-retainable-pointer representation. bool hasRetainablePointerRepresentation() const { - return getSwiftRValueType()->hasRetainablePointerRepresentation(); + return getASTType()->hasRetainablePointerRepresentation(); } /// Returns true if the referenced type is an existential type. bool isExistentialType() const { - return getSwiftRValueType().isExistentialType(); + return getASTType().isExistentialType(); } /// Returns true if the referenced type is any kind of existential type. bool isAnyExistentialType() const { - return getSwiftRValueType().isAnyExistentialType(); + return getASTType().isAnyExistentialType(); } /// Returns true if the referenced type is a class existential type. bool isClassExistentialType() const { - return getSwiftRValueType()->isClassExistentialType(); + return getASTType()->isClassExistentialType(); } /// Returns true if the referenced type is an opened existential type /// (which is actually a kind of archetype). bool isOpenedExistential() const { - return getSwiftRValueType()->isOpenedExistential(); + return getASTType()->isOpenedExistential(); } /// Returns true if the referenced type is expressed in terms of one /// or more opened existential types. bool hasOpenedExistential() const { - return getSwiftRValueType()->hasOpenedExistential(); + return getASTType()->hasOpenedExistential(); } /// Returns the representation used by an existential type. If the concrete @@ -332,12 +338,12 @@ class SILType { /// True if the type contains a type parameter. bool hasTypeParameter() const { - return getSwiftRValueType()->hasTypeParameter(); + return getASTType()->hasTypeParameter(); } /// True if the type is bridgeable to an ObjC object pointer type. bool isBridgeableObjectType() const { - return getSwiftRValueType()->isBridgeableObjectType(); + return getASTType()->isBridgeableObjectType(); } static bool isClassOrClassMetatype(Type t) { @@ -350,34 +356,23 @@ class SILType { /// True if the type is a class type or class metatype type. bool isClassOrClassMetatype() { - return isObject() && isClassOrClassMetatype(getSwiftRValueType()); + return isObject() && isClassOrClassMetatype(getASTType()); } /// True if the type involves any archetypes. bool hasArchetype() const { - return getSwiftRValueType()->hasArchetype(); + return getASTType()->hasArchetype(); } /// Returns the ASTContext for the referenced Swift type. const ASTContext &getASTContext() const { - return getSwiftRValueType()->getASTContext(); + return getASTType()->getASTContext(); } /// True if the given type has at least the size and alignment of a native /// pointer. bool isPointerSizeAndAligned(); - /// Return true if the layout of `toType` is an ABI compatible prefix of - /// `fromType` ignoring reference types. `fromType` may be larger than - /// `toType` and still be unsafe castable. `fromType` may contain references - /// in positions where `toType` does not contain references and still be - /// unsafe castable. This is used solely to determine whether an address cast - /// can be promoted to a cast between aggregates of scalar values without - /// confusing IRGen. - static bool canPerformABICompatibleUnsafeCastValue(SILType fromType, - SILType toType, - SILModule &M); - /// True if `operTy` can be cast by single-reference value into `resultTy`. static bool canRefCast(SILType operTy, SILType resultTy, SILModule &M); @@ -417,28 +412,26 @@ class SILType { /// Return the immediate superclass type of this type, or null if /// it's the most-derived type. SILType getSuperclass() const { - auto superclass = getSwiftRValueType()->getSuperclass(); + auto superclass = getASTType()->getSuperclass(); if (!superclass) return SILType(); return SILType::getPrimitiveObjectType(superclass->getCanonicalType()); } /// Return true if Ty is a subtype of this exact SILType, or false otherwise. bool isExactSuperclassOf(SILType Ty) const { - return getSwiftRValueType()->isExactSuperclassOf(Ty.getSwiftRValueType()); + return getASTType()->isExactSuperclassOf(Ty.getASTType()); } /// Return true if Ty is a subtype of this SILType, or if this SILType /// contains archetypes that can be found to form a supertype of Ty, or false /// otherwise. bool isBindableToSuperclassOf(SILType Ty) const { - return getSwiftRValueType()->isBindableToSuperclassOf( - Ty.getSwiftRValueType()); + return getASTType()->isBindableToSuperclassOf(Ty.getASTType()); } /// Look through reference-storage types on this type. SILType getReferenceStorageReferentType() const { - return SILType(getSwiftRValueType().getReferenceStorageReferent(), - getCategory()); + return SILType(getASTType().getReferenceStorageReferent(), getCategory()); } /// Transform the function type SILType by replacing all of its interface @@ -446,14 +439,7 @@ class SILType { /// /// Only call this with function types! SILType substGenericArgs(SILModule &M, - SubstitutionList Subs) const; - - /// Transform the function type SILType by replacing all of its interface - /// generic args with the appropriate item from the substitution. - /// - /// Only call this with function types! - SILType substGenericArgs(SILModule &M, - const SubstitutionMap &SubMap) const; + SubstitutionMap SubMap) const; /// If the original type is generic, pass the signature as genericSig. /// @@ -464,7 +450,7 @@ class SILType { LookupConformanceFn conformances, CanGenericSignature genericSig=CanGenericSignature()) const; - SILType subst(SILModule &silModule, const SubstitutionMap &subs) const; + SILType subst(SILModule &silModule, SubstitutionMap subs) const; /// Return true if this type references a "ref" type that has a single pointer /// representation. Class existentials do not always qualify. @@ -490,17 +476,10 @@ class SILType { /// Unwraps one level of optional type. /// Returns the lowered T if the given type is Optional. /// Otherwise directly returns the given type. - SILType unwrapAnyOptionalType() const; - - /// Wraps one level of optional type. - /// - /// Returns the lowered Optional if the given type is T. - /// - /// \arg F The SILFunction where the SILType is used. - SILType wrapAnyOptionalType(SILFunction &F) const; + SILType unwrapOptionalType() const; /// Returns true if this is the AnyObject SILType; - bool isAnyObject() const { return getSwiftRValueType()->isAnyObject(); } + bool isAnyObject() const { return getASTType()->isAnyObject(); } /// Returns the underlying referent SILType of an @sil_unowned or @sil_weak /// Type. diff --git a/include/swift/SIL/SILVTable.h b/include/swift/SIL/SILVTable.h index fb9cc7235b426..285b3f7435d99 100644 --- a/include/swift/SIL/SILVTable.h +++ b/include/swift/SIL/SILVTable.h @@ -174,7 +174,7 @@ namespace llvm { template <> struct ilist_traits<::swift::SILVTable> : public ilist_default_traits<::swift::SILVTable> { - typedef ::swift::SILVTable SILVTable; + using SILVTable = ::swift::SILVTable; static void deleteNode(SILVTable *VT) { VT->~SILVTable(); } diff --git a/include/swift/SIL/SILVTableVisitor.h b/include/swift/SIL/SILVTableVisitor.h index abf4f2415dfc0..9549cfa25b2a4 100644 --- a/include/swift/SIL/SILVTableVisitor.h +++ b/include/swift/SIL/SILVTableVisitor.h @@ -18,11 +18,55 @@ #ifndef SWIFT_SIL_SILVTABLEVISITOR_H #define SWIFT_SIL_SILVTABLEVISITOR_H +#include + #include "swift/AST/Decl.h" #include "swift/AST/Types.h" +#include "swift/AST/ASTMangler.h" namespace swift { +// Utility class for deterministically ordering vtable entries for +// synthesized methods. +struct SortedFuncList { + using Entry = std::pair; + SmallVector elts; + bool sorted = false; + + void add(AbstractFunctionDecl *afd) { + Mangle::ASTMangler mangler; + std::string mangledName; + if (auto *cd = dyn_cast(afd)) + mangledName = mangler.mangleConstructorEntity(cd, 0, 0); + else + mangledName = mangler.mangleEntity(afd, 0); + + elts.push_back(std::make_pair(mangledName, afd)); + } + + bool empty() { return elts.empty(); } + + void sort() { + assert(!sorted); + sorted = true; + std::sort(elts.begin(), + elts.end(), + [](const Entry &lhs, const Entry &rhs) -> bool { + return lhs.first < rhs.first; + }); + } + + decltype(elts)::const_iterator begin() const { + assert(sorted); + return elts.begin(); + } + + decltype(elts)::const_iterator end() const { + assert(sorted); + return elts.end(); + } +}; + /// A CRTP class for visiting virtually-dispatched methods of a class. /// /// You must override these two methods in your subclass: @@ -76,19 +120,47 @@ template class SILVTableVisitor { } } + void maybeAddMember(Decl *member) { + if (auto *fd = dyn_cast(member)) + maybeAddMethod(fd); + else if (auto *cd = dyn_cast(member)) + maybeAddConstructor(cd); + else if (auto *placeholder = dyn_cast(member)) + asDerived().addPlaceholder(placeholder); + } + protected: void addVTableEntries(ClassDecl *theClass) { // Imported classes do not have a vtable. if (!theClass->hasKnownSwiftImplementation()) return; + // Note that while vtable order is not ABI, we still want it to be + // consistent between translation units. + // + // So, sort synthesized members by their mangled name, since they + // are added lazily during type checking, with the remaining ones + // forced at the end. + SortedFuncList synthesizedMembers; + for (auto member : theClass->getMembers()) { - if (auto *fd = dyn_cast(member)) - maybeAddMethod(fd); - else if (auto *cd = dyn_cast(member)) - maybeAddConstructor(cd); - else if (auto *placeholder = dyn_cast(member)) - asDerived().addPlaceholder(placeholder); + if (auto *afd = dyn_cast(member)) { + if (afd->isSynthesized()) { + synthesizedMembers.add(afd); + continue; + } + } + + maybeAddMember(member); + } + + if (synthesizedMembers.empty()) + return; + + synthesizedMembers.sort(); + + for (const auto &pair : synthesizedMembers) { + maybeAddMember(pair.second); } } }; diff --git a/include/swift/SIL/SILValue.h b/include/swift/SIL/SILValue.h index dedf7539c7a0b..d7d14ba5fe787 100644 --- a/include/swift/SIL/SILValue.h +++ b/include/swift/SIL/SILValue.h @@ -440,7 +440,7 @@ class Operand { inline SILValue getSILValueType(const Operand &op) { return op.get(); } -typedef ArrayRefView OperandValueArrayRef; +using OperandValueArrayRef = ArrayRefView; /// An iterator over all uses of a ValueBase. class ValueBaseUseIterator : public std::iterator struct simplify_type { - typedef ::swift::ValueBase *SimpleType; + using SimpleType = ::swift::ValueBase *; static SimpleType getSimplifiedValue(::swift::SILValue Val) { return Val; } diff --git a/include/swift/SIL/SILWitnessTable.h b/include/swift/SIL/SILWitnessTable.h index 6e509544072fb..6de1914aa7488 100644 --- a/include/swift/SIL/SILWitnessTable.h +++ b/include/swift/SIL/SILWitnessTable.h @@ -318,7 +318,7 @@ namespace llvm { template <> struct ilist_traits<::swift::SILWitnessTable> : public ilist_default_traits<::swift::SILWitnessTable> { - typedef ::swift::SILWitnessTable SILWitnessTable; + using SILWitnessTable = ::swift::SILWitnessTable; public: static void deleteNode(SILWitnessTable *WT) { WT->~SILWitnessTable(); } diff --git a/include/swift/SIL/SILWitnessVisitor.h b/include/swift/SIL/SILWitnessVisitor.h index 8b645efab0a56..69c0dd3582c0f 100644 --- a/include/swift/SIL/SILWitnessVisitor.h +++ b/include/swift/SIL/SILWitnessVisitor.h @@ -58,12 +58,21 @@ template class SILWitnessVisitor : public ASTVisitor { if (haveAddedAssociatedTypes) return; haveAddedAssociatedTypes = true; + SmallVector associatedTypes; for (Decl *member : protocol->getMembers()) { if (auto associatedType = dyn_cast(member)) { - // TODO: only add associated types when they're new? - asDerived().addAssociatedType(AssociatedType(associatedType)); + associatedTypes.push_back(associatedType); } } + + // Sort associated types by name, for resilience. + llvm::array_pod_sort(associatedTypes.begin(), associatedTypes.end(), + TypeDecl::compare); + + for (auto *associatedType : associatedTypes) { + // TODO: only add associated types when they're new? + asDerived().addAssociatedType(AssociatedType(associatedType)); + } }; for (const auto &reqt : protocol->getRequirementSignature()) { diff --git a/include/swift/SIL/TypeLowering.h b/include/swift/SIL/TypeLowering.h index 09fa63a308d75..b8f23fe1967e0 100644 --- a/include/swift/SIL/TypeLowering.h +++ b/include/swift/SIL/TypeLowering.h @@ -96,35 +96,120 @@ enum IsDependent_t : unsigned { IsNotDependent = false, IsDependent = true }; - + +/// Is a lowered SIL type trivial? That is, are copies ultimately just +/// bit-copies, and it takes no work to destroy a value? +enum IsTrivial_t : bool { + IsNotTrivial = false, + IsTrivial = true +}; + +/// Is a lowered SIL type fixed-ABI? That is, can the current context +/// assign it a fixed size and alignment and perform value operations on it +/// (such as copies, destroys, constructions, and projections) without +/// metadata? +/// +/// Note that a fully concrete type can be non-fixed-ABI without being +/// itself resilient if it contains a subobject which is not fixed-ABI. +/// +/// Also note that we're only concerned with the external value ABI here: +/// resilient class types are still fixed-ABI, indirect enum cases do not +/// affect the fixed-ABI-ness of the enum, and so on. +enum IsFixedABI_t : bool { + IsNotFixedABI = false, + IsFixedABI = true +}; + +/// Is a lowered SIL type address-only? That is, is the current context +/// required to keep the value in memory for some reason? +/// +/// A type might be address-only because: +/// +/// - it is not fixed-size (e.g. because it is a resilient type) and +/// therefore cannot be loaded into a statically-boundable set of +/// registers; or +/// +/// - it is semantically bound to memory, either because its storage +/// address is used by the language runtime to implement its semantics +/// (as with a weak reference) or because its representation is somehow +/// address-dependent (as with something like a relative pointer). +/// +/// An address-only type can be fixed-layout and/or trivial. +/// A non-fixed-layout type is always address-only. +enum IsAddressOnly_t : bool { + IsNotAddressOnly = false, + IsAddressOnly = true +}; + +/// Is this type somewhat like a reference-counted type? +enum IsReferenceCounted_t : bool { + IsNotReferenceCounted = false, + IsReferenceCounted = true +}; + /// Extended type information used by SIL. class TypeLowering { public: - enum IsTrivial_t : bool { IsNotTrivial, IsTrivial }; - enum IsAddressOnly_t : bool { IsNotAddressOnly, IsAddressOnly }; - enum IsReferenceCounted_t : bool { - IsNotReferenceCounted, - IsReferenceCounted + class RecursiveProperties { + // These are chosen so that bitwise-or merges the flags properly. + enum : unsigned { + NonTrivialFlag = 1 << 0, + NonFixedABIFlag = 1 << 1, + AddressOnlyFlag = 1 << 2, + }; + + uint8_t Flags; + public: + /// Construct a default RecursiveProperties, which corresponds to + /// a trivial, loadable, fixed-layout type. + constexpr RecursiveProperties() : Flags(0) {} + + constexpr RecursiveProperties(IsTrivial_t isTrivial, + IsFixedABI_t isFixedABI, + IsAddressOnly_t isAddressOnly) + : Flags((isTrivial ? 0U : NonTrivialFlag) | + (isAddressOnly ? AddressOnlyFlag : 0U) | + (isFixedABI ? 0U : NonFixedABIFlag)) {} + + static constexpr RecursiveProperties forReference() { + return {IsNotTrivial, IsFixedABI, IsNotAddressOnly}; + } + + static constexpr RecursiveProperties forOpaque() { + return {IsNotTrivial, IsNotFixedABI, IsAddressOnly}; + } + + void addSubobject(RecursiveProperties other) { + Flags |= other.Flags; + } + + IsTrivial_t isTrivial() const { + return IsTrivial_t((Flags & NonTrivialFlag) == 0); + } + IsFixedABI_t isFixedABI() const { + return IsFixedABI_t((Flags & NonFixedABIFlag) == 0); + } + IsAddressOnly_t isAddressOnly() const { + return IsAddressOnly_t((Flags & AddressOnlyFlag) != 0); + } + + void setNonTrivial() { Flags |= NonTrivialFlag; } + void setNonFixedABI() { Flags |= NonFixedABIFlag; } + void setAddressOnly() { Flags |= AddressOnlyFlag; } }; private: /// The SIL type of values with this Swift type. SILType LoweredType; - enum : unsigned { - IsTrivialFlag = 0x1, - IsAddressOnlyFlag = 0x2, - IsReferenceCountedFlag = 0x4, - }; - unsigned Flags; + RecursiveProperties Properties; + unsigned ReferenceCounted : 1; protected: - TypeLowering(SILType type, IsTrivial_t isTrivial, - IsAddressOnly_t isAddressOnly, + TypeLowering(SILType type, RecursiveProperties properties, IsReferenceCounted_t isRefCounted) - : LoweredType(type), Flags((isTrivial ? IsTrivialFlag : 0U) | - (isAddressOnly ? IsAddressOnlyFlag : 0U) | - (isRefCounted ? IsReferenceCountedFlag : 0U)) {} + : LoweredType(type), Properties(properties), + ReferenceCounted(isRefCounted) {} public: TypeLowering(const TypeLowering &) = delete; @@ -143,12 +228,16 @@ class TypeLowering { /// This is independent of whether the SIL result is address type. bool isFormallyReturnedIndirectly() const { return isAddressOnly(); } + RecursiveProperties getRecursiveProperties() const { + return Properties; + } + /// isAddressOnly - Returns true if the type is an address-only type. A type /// is address-only if it is a resilient value type, or if it is a fragile /// value type with a resilient member. In either case, the full layout of /// values of the type is unavailable to the compiler. bool isAddressOnly() const { - return Flags & IsAddressOnlyFlag; + return Properties.isAddressOnly(); } /// isLoadable - Returns true if the type is loadable, in other words, its /// full layout is available to the compiler. This is the inverse of @@ -156,17 +245,24 @@ class TypeLowering { bool isLoadable() const { return !isAddressOnly(); } + + /// isFixedABI - Returns true if the type has a known fixed layout. + /// If this is true, value operations on the type can be performed even if + /// the type is inaccessible. + bool isFixedABI() const { + return Properties.isFixedABI(); + } /// Returns true if the type is trivial, meaning it is a loadable /// value type with no reference type members that require releasing. bool isTrivial() const { - return Flags & IsTrivialFlag; + return Properties.isTrivial(); } /// Returns true if the type is a scalar reference-counted reference, which /// can be retained and released. bool isReferenceCounted() const { - return Flags & IsReferenceCountedFlag; + return ReferenceCounted; } /// getLoweredType - Get the type used to represent values of the Swift type @@ -187,7 +283,7 @@ class TypeLowering { /// return on a variable of this type. SILType getSemanticType() const { // If you change this, change getSemanticTypeLowering() too. - auto storageType = getLoweredType().getSwiftRValueType(); + auto storageType = getLoweredType().getASTType(); if (auto refType = dyn_cast(storageType)) return SILType::getPrimitiveType(refType.getReferentType(), SILValueCategory::Object); @@ -639,7 +735,7 @@ class TypeConverter { } static bool isIndirectPlusZeroSelfParameter(SILType T) { - return isIndirectPlusZeroSelfParameter(T.getSwiftRValueType()); + return isIndirectPlusZeroSelfParameter(T.getASTType()); } /// Lowers a Swift type to a SILType, and returns the SIL TypeLowering @@ -907,7 +1003,7 @@ class TypeConverter { inline const TypeLowering & TypeLowering::getSemanticTypeLowering(TypeConverter &TC) const { // If you change this, change getSemanticType() too. - auto storageType = getLoweredType().getSwiftRValueType(); + auto storageType = getLoweredType().getASTType(); if (auto refType = dyn_cast(storageType)) return TC.getTypeLowering(refType.getReferentType()); return *this; @@ -938,7 +1034,7 @@ class GenericContextScope { namespace llvm { template<> struct DenseMapInfo { - typedef swift::Lowering::TypeConverter::CachingTypeKey CachingTypeKey; + using CachingTypeKey = swift::Lowering::TypeConverter::CachingTypeKey; using APCachingKey = swift::Lowering::AbstractionPattern::CachingKey; using CachingKeyInfo = DenseMapInfo; @@ -967,7 +1063,7 @@ namespace llvm { }; template<> struct DenseMapInfo { - typedef swift::Lowering::TypeConverter::OverrideKey OverrideKey; + using OverrideKey = swift::Lowering::TypeConverter::OverrideKey; using SILDeclRefInfo = DenseMapInfo; diff --git a/include/swift/SIL/TypeSubstCloner.h b/include/swift/SIL/TypeSubstCloner.h index b5b4ae4609d48..c987e73c30366 100644 --- a/include/swift/SIL/TypeSubstCloner.h +++ b/include/swift/SIL/TypeSubstCloner.h @@ -34,27 +34,19 @@ class TypeSubstCloner : public SILClonerWithScopes { friend class SILInstructionVisitor; friend class SILCloner; - typedef SILClonerWithScopes super; + using super = SILClonerWithScopes; void postProcess(SILInstruction *Orig, SILInstruction *Cloned) { llvm_unreachable("Clients need to explicitly call a base class impl!"); } - void computeSubsMap() { - if (auto genericSig = Original.getLoweredFunctionType() - ->getGenericSignature()) { - SubsMap = genericSig->getSubstitutionMap(ApplySubs); - } - } - // A helper class for cloning different kinds of apply instructions. // Supports cloning of self-recursive functions. class ApplySiteCloningHelper { SILValue Callee; - SubstitutionList Subs; + SubstitutionMap Subs; SmallVector Args; - SmallVector NewSubsList; - SmallVector RecursiveSubsList; + SubstitutionMap RecursiveSubs; public: ApplySiteCloningHelper(ApplySite AI, TypeSubstCloner &Cloner) @@ -66,13 +58,12 @@ class TypeSubstCloner : public SILClonerWithScopes { Builder.setCurrentDebugScope(Cloner.super::getOpScope(AI.getDebugScope())); // Remap substitutions. - NewSubsList = Cloner.getOpSubstitutions(AI.getSubstitutions()); - Subs = NewSubsList; + Subs = Cloner.getOpSubstitutionMap(AI.getSubstitutionMap()); if (!Cloner.Inlining) { FunctionRefInst *FRI = dyn_cast(AI.getCallee()); if (FRI && FRI->getReferencedFunction() == AI.getFunction() && - Subs == Cloner.ApplySubs) { + Subs == Cloner.SubsMap) { // Handle recursions by replacing the apply to the callee with an // apply to the newly specialized function, but only if substitutions // are the same. @@ -83,24 +74,23 @@ class TypeSubstCloner : public SILClonerWithScopes { // Compute substitutions for the specialized function. These // substitutions may be different from the original ones, e.g. // there can be less substitutions. - GenSig->getSubstitutions(AI.getFunction() - ->getLoweredFunctionType() - ->getGenericSignature() - ->getSubstitutionMap(Subs), - RecursiveSubsList); + RecursiveSubs = AI.getFunction() + ->getLoweredFunctionType() + ->getGenericSignature() + ->getSubstitutionMap(Subs); + // Use the new set of substitutions to compute the new // substituted callee type. RecursiveSubstCalleeSILType = LoweredFnTy->substGenericArgs( - AI.getModule(), RecursiveSubsList); + AI.getModule(), RecursiveSubs); } // The specialized recursive function may have different calling // convention for parameters. E.g. some of former indirect parameters // may become direct. Some of indirect return values may become // direct. Do not replace the callee in that case. - if (SubstCalleeSILType.getSwiftRValueType() == - RecursiveSubstCalleeSILType) { - Subs = RecursiveSubsList; + if (SubstCalleeSILType.getASTType() == RecursiveSubstCalleeSILType) { + Subs = RecursiveSubs; Callee = Builder.createFunctionRef( Cloner.getOpLocation(AI.getLoc()), &Builder.getFunction()); SubstCalleeSILType = @@ -122,7 +112,7 @@ class TypeSubstCloner : public SILClonerWithScopes { return Callee; } - SubstitutionList getSubstitutions() const { + SubstitutionMap getSubstitutions() const { return Subs; } }; @@ -144,27 +134,25 @@ class TypeSubstCloner : public SILClonerWithScopes { TypeSubstCloner(SILFunction &To, SILFunction &From, - SubstitutionList ApplySubs, + SubstitutionMap ApplySubs, SILOpenedArchetypesTracker &OpenedArchetypesTracker, bool Inlining = false) : SILClonerWithScopes(To, OpenedArchetypesTracker, Inlining), SwiftMod(From.getModule().getSwiftModule()), + SubsMap(ApplySubs), Original(From), - ApplySubs(ApplySubs), Inlining(Inlining) { - computeSubsMap(); } TypeSubstCloner(SILFunction &To, SILFunction &From, - SubstitutionList ApplySubs, + SubstitutionMap ApplySubs, bool Inlining = false) : SILClonerWithScopes(To, Inlining), SwiftMod(From.getModule().getSwiftModule()), + SubsMap(ApplySubs), Original(From), - ApplySubs(ApplySubs), Inlining(Inlining) { - computeSubsMap(); } @@ -188,6 +176,10 @@ class TypeSubstCloner : public SILClonerWithScopes { LookUpConformanceInSubstitutionMap(SubsMap)); } + SubstitutionMap remapSubstitutionMap(SubstitutionMap Subs) { + return Subs.subst(SubsMap); + } + void visitApplyInst(ApplyInst *Inst) { ApplySiteCloningHelper Helper(ApplySite(Inst), *this); ApplyInst *N = @@ -294,8 +286,6 @@ class TypeSubstCloner : public SILClonerWithScopes { llvm::DenseMap TypeCache; /// The original function to specialize. SILFunction &Original; - /// The substitutions used at the call site. - SubstitutionList ApplySubs; /// True, if used for inlining. bool Inlining; }; diff --git a/include/swift/SILOptimizer/Analysis/ARCAnalysis.h b/include/swift/SILOptimizer/Analysis/ARCAnalysis.h index 31e508f7c82f0..61dfcc338d5dd 100644 --- a/include/swift/SILOptimizer/Analysis/ARCAnalysis.h +++ b/include/swift/SILOptimizer/Analysis/ARCAnalysis.h @@ -45,8 +45,8 @@ bool isRetainInstruction(SILInstruction *II); /// Return true if this is a release instruction. bool isReleaseInstruction(SILInstruction *II); -using RetainList = llvm::SmallVector; -using ReleaseList = llvm::SmallVector; +using RetainList = TinyPtrVector; +using ReleaseList = TinyPtrVector; /// \returns True if the user \p User decrements the ref count of \p Ptr. bool mayDecrementRefCount(SILInstruction *User, SILValue Ptr, diff --git a/include/swift/SILOptimizer/Analysis/AccessedStorageAnalysis.h b/include/swift/SILOptimizer/Analysis/AccessedStorageAnalysis.h new file mode 100644 index 0000000000000..b0e6af4d271bf --- /dev/null +++ b/include/swift/SILOptimizer/Analysis/AccessedStorageAnalysis.h @@ -0,0 +1,263 @@ +//===--- AccessedStorageAnalysis.h - Accessed Storage Analysis --*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file implements an interprocedural analysis pass that summarizes the +// dynamically enforced formal accesses within a function. These summaries are +// used by AccessEnforcementOpts to locally fold access scopes and remove +// dynamic checks based on whole module analysis. +// +// Note: This interprocedural analysis can be easily augmented to simultaneously +// compute FunctionSideEffects, without using a separate analysis, by adding +// FunctionSideEffects as a member of FunctionAccessedStorage. However, passes +// that use AccessedStorageAnalysis do not currently need SideEffectAnalysis. +// +//===----------------------------------------------------------------------===// +#ifndef SWIFT_SILOPTIMIZER_ANALYSIS_ACCESSED_STORAGE_ANALYSIS_H_ +#define SWIFT_SILOPTIMIZER_ANALYSIS_ACCESSED_STORAGE_ANALYSIS_H_ + +#include "swift/SIL/MemAccessUtils.h" +#include "swift/SIL/SILFunction.h" +#include "swift/SIL/SILInstruction.h" +#include "swift/SILOptimizer/Analysis/SideEffectAnalysis.h" + +namespace swift { + +/// Information about a formal access within a function pertaining to a +/// particular AccessedStorage location. +class StorageAccessInfo : public AccessedStorage { +public: + StorageAccessInfo(AccessedStorage storage, SILAccessKind accessKind, + bool noNestedConflict) + : AccessedStorage(storage) { + Bits.StorageAccessInfo.accessKind = unsigned(accessKind); + Bits.StorageAccessInfo.noNestedConflict = noNestedConflict; + Bits.StorageAccessInfo.storageIndex = 0; + } + + // Initialize AccessedStorage from the given storage argument and fill in + // subclass fields from otherStorageInfo. + StorageAccessInfo(AccessedStorage storage, StorageAccessInfo otherStorageInfo) + : StorageAccessInfo(storage, otherStorageInfo.getAccessKind(), + otherStorageInfo.hasNoNestedConflict()) {} + + template + StorageAccessInfo(AccessedStorage storage, B *beginAccess) + : StorageAccessInfo(storage, beginAccess->getAccessKind(), + beginAccess->hasNoNestedConflict()) { + // Currently limited to dynamic Read/Modify access. + assert(beginAccess->getEnforcement() == SILAccessEnforcement::Dynamic); + } + + /// Get the merged access kind of all accesses on this storage. If any access + /// is a Modify, the return Modify, otherwise return Read. + SILAccessKind getAccessKind() const { + return SILAccessKind(Bits.StorageAccessInfo.accessKind); + } + + void setAccessKind(SILAccessKind accessKind) { + Bits.StorageAccessInfo.accessKind = unsigned(accessKind); + } + + /// Get a unique index for this accessed storage within a function. + unsigned getStorageIndex() const { + return Bits.StorageAccessInfo.storageIndex; + } + + void setStorageIndex(unsigned index) { + Bits.StorageAccessInfo.storageIndex = index; + assert(unsigned(Bits.StorageAccessInfo.storageIndex) == index); + } + + /// Return true if all accesses of this storage within a function have the + /// [no_nested_conflict] flag set. + bool hasNoNestedConflict() const { + return Bits.StorageAccessInfo.noNestedConflict; + } + + void setNoNestedConflict(bool val) { + Bits.StorageAccessInfo.noNestedConflict = val; + } + + bool mergeFrom(const StorageAccessInfo &RHS); + + void print(raw_ostream &os) const; + void dump() const; +}; +} // namespace swift + +// Use the same DenseMapInfo for StorageAccessInfo as for AccessedStorage. None +// of the subclass bitfields participate in the Key. +template <> struct llvm::DenseMapInfo { + static swift::StorageAccessInfo getEmptyKey() { + auto key = DenseMapInfo::getEmptyKey(); + return static_cast(key); + } + + static swift::StorageAccessInfo getTombstoneKey() { + auto key = DenseMapInfo::getTombstoneKey(); + return static_cast(key); + } + static unsigned getHashValue(swift::StorageAccessInfo storage) { + return DenseMapInfo::getHashValue(storage); + } + static bool isEqual(swift::StorageAccessInfo LHS, + swift::StorageAccessInfo RHS) { + return DenseMapInfo::isEqual(LHS, RHS); + } +}; + +namespace swift { +/// The per-function result of AccessedStorageAnalysis. +/// +/// Records each unique AccessedStorage in a set of StorageAccessInfo +/// objects. Hashing and equality only sees the AccesedStorage data. The +/// additional StorageAccessInfo bits are recorded as results of this analysis. +/// +/// Any unidentified accesses are summarized as a single unidentifiedAccess +/// property. +class FunctionAccessedStorage { + using AccessedStorageSet = llvm::SmallDenseSet; + + AccessedStorageSet storageAccessSet; + Optional unidentifiedAccess; + +public: + FunctionAccessedStorage() {} + + // --------------------------------------------------------------------------- + // Accessing the results. + + bool hasUnidentifiedAccess() const { return unidentifiedAccess != None; } + + /// Return true if the analysis has determined all accesses of otherStorage + /// have the [no_nested_conflict] flag set. + /// + /// Only call this if there is no unidentifiedAccess in the function and the + /// given storage is uniquely identified. + bool hasNoNestedConflict(const AccessedStorage &otherStorage) const; + + /// Does any of the accesses represented by this FunctionAccessedStorage + /// object conflict with the given access kind and storage. + bool mayConflictWith(SILAccessKind otherAccessKind, + const AccessedStorage &otherStorage) const; + + /// Raw access to the result for a given AccessedStorage location. + StorageAccessInfo + getStorageAccessInfo(const AccessedStorage &otherStorage) const; + + // --------------------------------------------------------------------------- + // Constructing the results. + + void clear() { + storageAccessSet.clear(); + unidentifiedAccess = None; + } + + /// Sets the most conservative effects, if we don't know anything about the + /// function. + void setWorstEffects() { + storageAccessSet.clear(); + unidentifiedAccess = SILAccessKind::Modify; + } + + /// Summarize the given function's effects using this FunctionAccessedStorage + /// object. + // + // Return true if the function's' effects have been fully summarized without + // visiting it's body. + bool summarizeFunction(SILFunction *F); + + /// Summarize the callee side effects of a call instruction using this + /// FunctionAccessedStorage object without analyzing the callee function + /// bodies or scheduling the callees for bottom-up propagation. + /// + /// The side effects are represented from the callee's perspective. Parameter + /// effects are not translated into information on the caller's argument, and + /// local effects are not dropped. + /// + /// Return true if this call-site's effects are summarized without visiting + /// the callee. + /// + /// TODO: Summarize ArraySemanticsCall accesses. + bool summarizeCall(FullApplySite fullApply) { + assert(storageAccessSet.empty() && "expected uninitialized results."); + return false; + } + + /// Merge effects directly from \p RHS. + bool mergeFrom(const FunctionAccessedStorage &RHS); + + /// Merge the effects represented in calleeAccess into this + /// FunctionAccessedStorage object. calleeAccess must correspond to at least + /// one callee at the apply site `fullApply`. Merging drops any local effects, + /// and translates parameter effects into effects on the caller-side + /// arguments. + /// + /// The full caller-side effects at a call site can be obtained with + /// AccessedStorageAnalysis::getCallSiteEffects(). + bool mergeFromApply(const FunctionAccessedStorage &calleeAccess, + FullApplySite fullApply); + + /// Analyze the side-effects of a single SIL instruction \p I. + /// Visited callees are added to \p BottomUpOrder until \p RecursionDepth + /// reaches MaxRecursionDepth. + void analyzeInstruction(SILInstruction *I); + + void print(raw_ostream &os) const; + void dump() const; + +protected: + std::pair + insertStorageAccess(StorageAccessInfo storageAccess) { + storageAccess.setStorageIndex(storageAccessSet.size()); + return storageAccessSet.insert(storageAccess); + } + + bool updateUnidentifiedAccess(SILAccessKind accessKind); + + bool mergeAccesses( + const FunctionAccessedStorage &other, + std::function + transformStorage); + + template void visitBeginAccess(B *beginAccess); +}; + +/// Summarizes the dynamic accesses performed within a function and its +/// callees. +/// +/// When summarizing multiple accesses, a Read access indicates that all +/// accesses are read only. A Write access indicates potential reads and writes. +/// +/// Unidentified accesses are not recorded individually. Rather, the function is +/// marked as potentially executing unidentified reads or writes. An incomplete +/// function, without a known callee set, is considered to have unidentified +/// writes. +/// +/// Use the GenericFunctionEffectAnalysis API to get the results of the analysis: +/// - geEffects(SILFunction*) +/// - getCallSiteEffects(FunctionEffects &callEffects, FullApplySite fullApply) +class AccessedStorageAnalysis + : public GenericFunctionEffectAnalysis { +public: + AccessedStorageAnalysis() + : GenericFunctionEffectAnalysis( + AnalysisKind::AccessedStorage) {} + + static bool classof(const SILAnalysis *S) { + return S->getKind() == AnalysisKind::AccessedStorage; + } +}; + +} // end namespace swift + +#endif // SWIFT_SILOPTIMIZER_ANALYSIS_ACCESSED_STORAGE_ANALYSIS_H_ diff --git a/include/swift/SILOptimizer/Analysis/Analysis.def b/include/swift/SILOptimizer/Analysis/Analysis.def index 8cbf23cf37ef0..f0ed98afbc004 100644 --- a/include/swift/SILOptimizer/Analysis/Analysis.def +++ b/include/swift/SILOptimizer/Analysis/Analysis.def @@ -24,6 +24,7 @@ #endif ANALYSIS(AccessSummary) +ANALYSIS(AccessedStorage) ANALYSIS(Alias) ANALYSIS(BasicCallee) ANALYSIS(Caller) diff --git a/include/swift/SILOptimizer/Analysis/ArraySemantic.h b/include/swift/SILOptimizer/Analysis/ArraySemantic.h index 42a96c0bd47b9..2c4853594609f 100644 --- a/include/swift/SILOptimizer/Analysis/ArraySemantic.h +++ b/include/swift/SILOptimizer/Analysis/ArraySemantic.h @@ -155,7 +155,7 @@ class ArraySemanticsCall { bool replaceByAppendingValues(SILModule &M, SILFunction *AppendFn, SILFunction *ReserveFn, const llvm::SmallVectorImpl &Vals, - ArrayRef Subs); + SubstitutionMap Subs); /// Hoist the call to the insert point. void hoist(SILInstruction *InsertBefore, DominanceInfo *DT) { diff --git a/include/swift/SILOptimizer/Analysis/ClosureScope.h b/include/swift/SILOptimizer/Analysis/ClosureScope.h index 139c79dc7fcf4..4459fb7c13038 100644 --- a/include/swift/SILOptimizer/Analysis/ClosureScope.h +++ b/include/swift/SILOptimizer/Analysis/ClosureScope.h @@ -177,7 +177,7 @@ class TopDownClosureFunctionOrder { // Visit all functions in a module, visiting each closure scope function // before // the closure function itself. - void visitFunctions(std::function visitor); + void visitFunctions(llvm::function_ref visitor); }; } // end namespace swift diff --git a/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h b/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h index 2c9ed079f4c85..db62cb1a5cc28 100644 --- a/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h +++ b/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h @@ -769,12 +769,6 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// address of a contained property escapes, but not the object itself. bool canEscapeTo(SILValue V, FullApplySite FAS); - /// Returns true if the value \p V or its content can escape to the - /// function call \p FAS. - /// This is the same as above, except that it returns true if an address of - /// a contained property escapes. - bool canObjectOrContentEscapeTo(SILValue V, FullApplySite FAS); - /// Returns true if the value \p V can escape to the release-instruction \p /// RI. This means that \p RI may release \p V or any called destructor may /// access (or release) \p V. diff --git a/include/swift/SILOptimizer/Analysis/RCIdentityAnalysis.h b/include/swift/SILOptimizer/Analysis/RCIdentityAnalysis.h index 595ee733bca58..3ef88860943d0 100644 --- a/include/swift/SILOptimizer/Analysis/RCIdentityAnalysis.h +++ b/include/swift/SILOptimizer/Analysis/RCIdentityAnalysis.h @@ -50,9 +50,19 @@ class RCIdentityFunctionInfo { SILValue getRCIdentityRoot(SILValue V); /// Return all recursive users of V, looking through users which propagate - /// RCIdentity. *NOTE* This ignores obvious ARC escapes where the a potential + /// RCIdentity. + /// + /// *NOTE* This ignores obvious ARC escapes where the a potential /// user of the RC is not managed by ARC. For instance /// unchecked_trivial_bit_cast. + void getRCUses(SILValue V, llvm::SmallVectorImpl &Uses); + + /// A helper method that calls getRCUses and then maps each operand to the + /// operands user and then uniques the list. + /// + /// *NOTE* The routine asserts that the passed in Users array is empty for + /// simplicity. If needed this can be changed, but it is not necessary given + /// current uses. void getRCUsers(SILValue V, llvm::SmallVectorImpl &Users); void handleDeleteNotification(SILNode *node) { diff --git a/include/swift/SILOptimizer/Analysis/SideEffectAnalysis.h b/include/swift/SILOptimizer/Analysis/SideEffectAnalysis.h index a950db41037ca..7fc56cbc3fd4e 100644 --- a/include/swift/SILOptimizer/Analysis/SideEffectAnalysis.h +++ b/include/swift/SILOptimizer/Analysis/SideEffectAnalysis.h @@ -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 - 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 @@ -34,103 +34,215 @@ enum class RetainObserveKind { RetainObserveKindEnd }; -/// The SideEffectAnalysis provides information about side-effects of SIL -/// functions. Side-effect information is provided per function and includes: -/// Does the function read or write memory? Does the function retain or release -/// objects? etc. -/// For details see SideEffectAnalysis::FunctionEffects and -/// SideEffectAnalysis::Effects. -class SideEffectAnalysis : public BottomUpIPAnalysis { +/// Generic base class for any bottom up analysis that summarizes per-function +/// "effects" by first computing local effects, then propagating those effects +/// bottom-up through the call graph. +/// +/// FunctionEffects constraints: +/// - void clear() +/// - void setWorstEffects() +/// - bool summarizeFunction(SILFunction *) +/// - bool summarizeCall(FullApplySite) +/// - bool mergeFrom(const FunctionSideEffects &) +/// - bool mergeFromApply(const FunctionEffects &, FullApplySite) +/// - void analyzeInstruction(SILInstruction *) +template +class GenericFunctionEffectAnalysis : public BottomUpIPAnalysis { + + /// Stores the analysis data, e.g. side-effects, for a function. + struct FunctionInfo : public FunctionInfoBase { + + /// The function effects. + FunctionEffects functionEffects; + + /// Back-link to the function. + SILFunction *F; + + /// Used during recomputation to indicate if the side-effects of a caller + /// must be updated. + bool needUpdateCallers = false; + + FunctionInfo(SILFunction *F) : F(F) {} + + /// Clears the analysis data on invalidation. + void clear() { functionEffects.clear(); } + }; + + typedef BottomUpFunctionOrder FunctionOrder; + + enum { + /// The maximum call-graph recursion depth for recomputing the analysis. + /// This is a relatively small number to reduce compile time in case of + /// large cycles in the call-graph. + /// In case of no cycles, we should not hit this limit at all because the + /// pass manager processes functions in bottom-up order. + MaxRecursionDepth = 5 + }; + + /// All the function effect information for the whole module. + llvm::DenseMap functionInfoMap; + + /// The allocator for the map of values in FunctionInfoMap. + llvm::SpecificBumpPtrAllocator allocator; + + /// Callee analysis, used for determining the callees at call sites. + BasicCalleeAnalysis *BCA; + public: + GenericFunctionEffectAnalysis(AnalysisKind kind) : BottomUpIPAnalysis(kind) {} - using MemoryBehavior = SILInstruction::MemoryBehavior; - /// Set \p dest if \p src is set and return true if \p dest was not set - /// before. - static bool updateFlag(bool &dest, bool src) { - if (src && !dest) { - dest = src; - return true; - } - return false; + const FunctionEffects &getEffects(SILFunction *F) { + FunctionInfo *functionInfo = getFunctionInfo(F); + if (!functionInfo->isValid()) + recompute(functionInfo); + return functionInfo->functionEffects; } - /// Side-effect information for the function (global effects) or a specific - /// parameter of the function. See FunctionEffects. - class Effects { - bool Reads = false; - bool Writes = false; - bool Retains = false; - bool Releases = false; - - /// Sets the most conservative effects. - void setWorstEffects() { - Reads = true; - Writes = true; - Retains = true; - Releases = true; - } + /// Get the merged effects of all callees at the given call site from the + /// callee's perspective (don't transform parameter effects). + void getCalleeEffects(FunctionEffects &calleeEffects, + FullApplySite fullApply); + + /// Get the merge effects of all callees at the given call site from the + /// caller's perspective. Parameter effects are translated into information + /// for the caller's arguments, and local effects are dropped. + void getCallSiteEffects(FunctionEffects &callEffects, + FullApplySite fullApply) { + FunctionEffects calleeEffects; + getCalleeEffects(calleeEffects, fullApply); + callEffects.mergeFromApply(calleeEffects, fullApply); + } - /// Clears all effects. - void clear() { - Reads = false; - Writes = false; - Retains = false; - Releases = false; - } + virtual void initialize(SILPassManager *PM) override; - friend class SideEffectAnalysis; - - public: - - /// Does the function read from memory (excluding reads from locally - /// allocated memory)? - bool mayRead() const { return Reads; } - - /// Does the function write to memory (excluding writes to locally - /// allocated memory)? - bool mayWrite() const { return Writes; } - - /// Does the function retain objects (excluding retains of locally - /// allocated objects)? - bool mayRetain() const { return Retains; } - - /// Does the function release objects (excluding releases of locally - /// allocated objects)? - bool mayRelease() const { return Releases; } - - /// Gets the memory behavior considering the global effects and - /// all parameter effects. If \p ScanKind equals ignoreRetains then retain - /// instructions are considered as side effects. - MemoryBehavior getMemBehavior(RetainObserveKind ScanKind) const { - if (mayRelease()) - return MemoryBehavior::MayHaveSideEffects; - - if (ScanKind == RetainObserveKind::ObserveRetains && mayRetain()) - return MemoryBehavior::MayHaveSideEffects; - - if (mayWrite()) - return mayRead() ? MemoryBehavior::MayReadWrite : - MemoryBehavior::MayWrite; - - if (mayRead()) - return MemoryBehavior::MayRead; - - return MemoryBehavior::None; - } + /// Invalidate all information in this analysis. + virtual void invalidate() override; + + /// Invalidate all of the information for a specific function. + virtual void invalidate(SILFunction *F, InvalidationKind K) override; - /// Merge effects from \p RHS. - bool mergeFrom(const Effects &RHS) { - bool Changed = false; - Changed |= updateFlag(Reads, RHS.Reads); - Changed |= updateFlag(Writes, RHS.Writes); - Changed |= updateFlag(Retains, RHS.Retains); - Changed |= updateFlag(Releases, RHS.Releases); - return Changed; + /// Notify the analysis about a newly created function. + virtual void notifyAddFunction(SILFunction *F) override {} + + /// Notify the analysis about a function which will be deleted from the + /// module. + virtual void notifyDeleteFunction(SILFunction *F) override { + invalidate(F, InvalidationKind::Nothing); + } + + /// Notify the analysis about changed witness or vtables. + virtual void invalidateFunctionTables() override {} + +private: + /// Gets or creates FunctionEffects for \p F. + FunctionInfo *getFunctionInfo(SILFunction *F) { + FunctionInfo *&functionInfo = functionInfoMap[F]; + if (!functionInfo) { + functionInfo = new (allocator.Allocate()) FunctionInfo(F); } - }; + return functionInfo; + } + + /// Analyze the side-effects of a function, including called functions. + /// Visited callees are added to \p BottomUpOrder until \p RecursionDepth + /// reaches MaxRecursionDepth. + void analyzeFunction(FunctionInfo *functionInfo, FunctionOrder &bottomUpOrder, + int recursionDepth); + + void analyzeCall(FunctionInfo *functionInfo, FullApplySite fullApply, + FunctionOrder &bottomUpOrder, int recursionDepth); + + /// Recomputes the side-effect information for the function \p Initial and + /// all called functions, up to a recursion depth of MaxRecursionDepth. + void recompute(FunctionInfo *initialInfo); +}; + +/// Set \p dest if \p src is set and return true if \p dest was not set +/// before. +static bool changedFlagByInPlaceOr(bool &dest, bool src) { + if (src && !dest) { + dest = src; + return true; + } + return false; +} + +/// Side-effect information for the function (global effects) or a specific +/// parameter of the function. See FunctionSideEffects. +class FunctionSideEffectFlags { + friend class FunctionSideEffects; + using MemoryBehavior = SILInstruction::MemoryBehavior; + + bool Reads = false; + bool Writes = false; + bool Retains = false; + bool Releases = false; + + /// Sets the most conservative effects. + void setWorstEffects() { + Reads = true; + Writes = true; + Retains = true; + Releases = true; + } + + /// Clears all effects. + void clear() { + Reads = false; + Writes = false; + Retains = false; + Releases = false; + } + +public: + /// Does the function read from memory (excluding reads from locally + /// allocated memory)? + bool mayRead() const { return Reads; } + + /// Does the function write to memory (excluding writes to locally + /// allocated memory)? + bool mayWrite() const { return Writes; } + + /// Does the function retain objects (excluding retains of locally + /// allocated objects)? + bool mayRetain() const { return Retains; } + + /// Does the function release objects (excluding releases of locally + /// allocated objects)? + bool mayRelease() const { return Releases; } + + /// Gets the memory behavior considering the global effects and + /// all parameter effects. If \p ScanKind equals ignoreRetains then retain + /// instructions are considered as side effects. + MemoryBehavior getMemBehavior(RetainObserveKind ScanKind) const { + if (mayRelease()) + return MemoryBehavior::MayHaveSideEffects; + + if (ScanKind == RetainObserveKind::ObserveRetains && mayRetain()) + return MemoryBehavior::MayHaveSideEffects; + + if (mayWrite()) + return mayRead() ? MemoryBehavior::MayReadWrite + : MemoryBehavior::MayWrite; + + if (mayRead()) + return MemoryBehavior::MayRead; + + return MemoryBehavior::None; + } + + /// Merge effects from \p RHS. + bool mergeFrom(const FunctionSideEffectFlags &RHS) { + bool Changed = false; + Changed |= changedFlagByInPlaceOr(Reads, RHS.Reads); + Changed |= changedFlagByInPlaceOr(Writes, RHS.Writes); + Changed |= changedFlagByInPlaceOr(Retains, RHS.Retains); + Changed |= changedFlagByInPlaceOr(Releases, RHS.Releases); + return Changed; + } friend raw_ostream &operator<<(raw_ostream &os, - const SideEffectAnalysis::Effects &Effects) { + const FunctionSideEffectFlags &Effects) { if (Effects.mayRead()) os << 'r'; if (Effects.mayWrite()) @@ -141,267 +253,201 @@ class SideEffectAnalysis : public BottomUpIPAnalysis { os << '-'; return os; } +}; - /// Summarizes the side-effects of a function. The side-effect information - /// is divided into global effects and effects for specific function - /// parameters. - /// If a side-effect can be associated to a specific function parameter, it is - /// not added to the global effects of the function. E.g. if a memory write is - /// only done through an @inout parameter, the mayWrite side-effect is only - /// accounted for this parameter. - /// Effects for a parameter make only sense if the parameter is implemented as - /// a pointer or contains a pointer: - /// *) The parameter is an address parameter, e.g. @out, @inout, etc. - /// *) The parameter is a reference - /// *) The parameter is a value type (e.g. struct) and contains a reference. - /// In this case the effects refer to all references in the value type. - /// E.g. if a struct contains 2 references, a mayWrite effect means that - /// memory is written to one of the referenced objects (or to both). - class FunctionEffects { - - /// Side-effects which can be associated to a parameter. - llvm::SmallVector ParamEffects; - - /// All other side-effects which cannot be associated to a parameter. - Effects GlobalEffects; - - /// Side-effects on locally allocated storage. Such side-effects are not - /// relevant to optimizations. The LocalEffects are only used to return - /// "something" for local storage in getEffectsOn(). - Effects LocalEffects; - - /// Does the function allocate objects, boxes, etc., i.e. everything which - /// has a reference count. - bool AllocsObjects = false; - - /// Can this function trap or exit the program in any way? - bool Traps = false; - - /// Does this function read a reference count other than with retain or - /// release instructions, e.g. isUnique? - bool ReadsRC = false; - - /// Returns the effects for an address or reference. This might be a - /// parameter, the LocalEffects or, if the value cannot be associated to one - /// of them, the GlobalEffects. - Effects *getEffectsOn(SILValue Addr); - - FunctionEffects(unsigned numParams) : ParamEffects(numParams) { } - - /// Sets the most conservative effects, if we don't know anything about the - /// function. - void setWorstEffects() { - GlobalEffects.setWorstEffects(); - AllocsObjects = true; - Traps = true; - ReadsRC = true; - } - - /// Clears all effects. - void clear() { - GlobalEffects.clear(); - for (Effects &PE : ParamEffects) - PE.clear(); - AllocsObjects = false; - Traps = false; - ReadsRC = false; - } - - /// Merge the flags from \p RHS. - bool mergeFlags(const FunctionEffects &RHS) { - bool Changed = false; - Changed |= updateFlag(Traps, RHS.Traps); - Changed |= updateFlag(AllocsObjects, RHS.AllocsObjects); - Changed |= updateFlag(ReadsRC, RHS.ReadsRC); - return Changed; - } - - friend class SideEffectAnalysis; - - public: - - /// Constructs "empty" function effects. - FunctionEffects() { } - - /// Does the function allocate objects, boxes, etc., i.e. everything which - /// has a reference count. - bool mayAllocObjects() const { return AllocsObjects; } - - /// Can this function trap or exit the program in any way? - bool mayTrap() const { return Traps; } - - /// Does this function read a reference count other than with retain or - /// release instructions, e.g. isUnique? - bool mayReadRC() const { return ReadsRC; } - - /// Gets the memory behavior considering the global effects and - /// all parameter effects. If \p ScanKind equals ignoreRetains then retain - /// instructions are considered as side effects. - MemoryBehavior getMemBehavior(RetainObserveKind ScanKind) const; - - /// Get the global effects for the function. These are effects which cannot - /// be associated to a specific parameter, e.g. writes to global variables - /// or writes to unknown pointers. - const Effects &getGlobalEffects() const { return GlobalEffects; } - - /// Get the array of parameter effects. If a side-effect can be associated - /// to a specific parameter, it is contained here instead of the global - /// effects. - /// Note that if a parameter effect is mayRelease(), it means that the - /// global function effects can be anything, because the destructor of an - /// object can have arbitrary side effects. - ArrayRef getParameterEffects() const { return ParamEffects; } - - /// Merge effects from \p RHS. - bool mergeFrom(const FunctionEffects &RHS); - - /// Merge effects from a function apply site within the function. - bool mergeFromApply(const FunctionEffects &CalleeEffects, - SILInstruction *FAS); - - /// Merge effects from an apply site within the function. - bool mergeFromApply(const FunctionEffects &CalleeEffects, - FullApplySite FAS); - - /// Print the function effects. - void dump() const; - }; +/// Summarizes the side-effects of a function. The side-effect information +/// is divided into global effects and effects for specific function +/// parameters. +/// If a side-effect can be associated to a specific function parameter, it is +/// not added to the global effects of the function. E.g. if a memory write is +/// only done through an @inout parameter, the mayWrite side-effect is only +/// accounted for this parameter. +/// Effects for a parameter make only sense if the parameter is implemented as +/// a pointer or contains a pointer: +/// *) The parameter is an address parameter, e.g. @out, @inout, etc. +/// *) The parameter is a reference +/// *) The parameter is a value type (e.g. struct) and contains a reference. +/// In this case the effects refer to all references in the value type. +/// E.g. if a struct contains 2 references, a mayWrite effect means that +/// memory is written to one of the referenced objects (or to both). +class FunctionSideEffects { + using MemoryBehavior = SILInstruction::MemoryBehavior; - friend raw_ostream &operator<<(raw_ostream &os, - const SideEffectAnalysis::FunctionEffects &Effects) { - os << "func=" << Effects.getGlobalEffects(); - int ParamIdx = 0; - for (auto &E : Effects.getParameterEffects()) { - os << ",param" << ParamIdx++ << "=" << E; - } - if (Effects.mayAllocObjects()) - os << ";alloc"; - if (Effects.mayTrap()) - os << ";trap"; - if (Effects.mayReadRC()) - os << ";readrc"; - return os; - } + /// Side-effects which can be associated to a parameter. + llvm::SmallVector ParamEffects; -private: + /// All other side-effects which cannot be associated to a parameter. + FunctionSideEffectFlags GlobalEffects; - /// Stores the analysis data, i.e. the side-effects, for a function. - struct FunctionInfo : public FunctionInfoBase { + /// Side-effects on locally allocated storage. Such side-effects are not + /// relevant to optimizations. The LocalEffects are only used to return + /// "something" for local storage in getEffectsOn(). + FunctionSideEffectFlags LocalEffects; - /// The side-effects of the function. - FunctionEffects FE; + /// Does the function allocate objects, boxes, etc., i.e. everything which + /// has a reference count. + bool AllocsObjects = false; - /// Back-link to the function. - SILFunction *F; + /// Can this function trap or exit the program in any way? + bool Traps = false; - /// Used during recomputation to indicate if the side-effects of a caller - /// must be updated. - bool NeedUpdateCallers = false; + /// Does this function read a reference count other than with retain or + /// release instructions, e.g. isUnique? + bool ReadsRC = false; - FunctionInfo(SILFunction *F) : - FE(F->empty() ? 0 : F->getArguments().size()), F(F) { } + /// Returns the effects for an address or reference. This might be a + /// parameter, the LocalEffects or, if the value cannot be associated to one + /// of them, the GlobalEffects. + FunctionSideEffectFlags *getEffectsOn(SILValue Addr); - /// Clears the analysis data on invalidation. - void clear() { FE.clear(); } - }; - - typedef BottomUpFunctionOrder FunctionOrder; +public: + /// Constructs "empty" function effects. This effects object can later be + /// populated by summarizeFunction or summarizeCall. + FunctionSideEffects() {} + + /// Sets the most conservative effects, if we don't know anything about the + /// function. + void setWorstEffects() { + GlobalEffects.setWorstEffects(); + AllocsObjects = true; + Traps = true; + ReadsRC = true; + } - enum { - /// The maximum call-graph recursion depth for recomputing the analysis. - /// This is a relatively small number to reduce compile time in case of - /// large cycles in the call-graph. - /// In case of no cycles, we should not hit this limit at all because the - /// pass manager processes functions in bottom-up order. - MaxRecursionDepth = 5 - }; + /// Clears all effects. + void clear() { + ParamEffects.clear(); + GlobalEffects.clear(); + AllocsObjects = false; + Traps = false; + ReadsRC = false; + } - /// All the side-effect information for the whole module. - llvm::DenseMap Function2Info; - - /// The allocator for the map values in Function2Info. - llvm::SpecificBumpPtrAllocator Allocator; - - /// Callee analysis, used for determining the callees at call sites. - BasicCalleeAnalysis *BCA; + /// Merge the flags from \p RHS. + bool mergeFlags(const FunctionSideEffects &RHS) { + bool Changed = false; + Changed |= changedFlagByInPlaceOr(Traps, RHS.Traps); + Changed |= changedFlagByInPlaceOr(AllocsObjects, RHS.AllocsObjects); + Changed |= changedFlagByInPlaceOr(ReadsRC, RHS.ReadsRC); + return Changed; + } - /// Get the side-effects of a function, which has an @effects attribute. - /// Returns true if \a F has an @effects attribute which could be handled. - static bool getDefinedEffects(FunctionEffects &Effects, SILFunction *F); - - /// Get the side-effects of a semantic call. - /// Return true if \p ASC could be handled. - bool getSemanticEffects(FunctionEffects &Effects, ArraySemanticsCall ASC); - - /// Analyze the side-effects of a function, including called functions. - /// Visited callees are added to \p BottomUpOrder until \p RecursionDepth - /// reaches MaxRecursionDepth. - void analyzeFunction(FunctionInfo *FInfo, - FunctionOrder &BottomUpOrder, - int RecursionDepth); + // Summarize the given function's effects using this FunctionSideEffects + // object. + // + // Return true if the function's' effects have been fully summarized without + // visiting it's body. + bool summarizeFunction(SILFunction *F); + + /// Summarize the callee side effects of a call instruction using this + /// FunctionSideEffects object without analyzing the callee function bodies or + /// scheduling the callees for bottom-up propagation. + /// + /// The side effects are represented from the callee's perspective. Parameter + /// effects are not translated into information on the caller's argument, and + /// local effects are not dropped. + /// + /// Return true if this call-site's effects are summarized without visiting + /// the callee. + bool summarizeCall(FullApplySite fullApply); + + /// Merge effects directly from \p RHS. + bool mergeFrom(const FunctionSideEffects &RHS); + + /// Merge the effects represented in CalleeEffects into this + /// FunctionSideEffects object. CalleeEffects must correspond to at least one + /// callee at the apply site `FAS`. Merging drops any local effects, and + /// translates parameter effects into effects on the caller-side arguments. + /// + /// The full caller-side effects at a call site can be obtained with + /// SideEffectsAnalysis::getCallSiteEffects(). + bool mergeFromApply(const FunctionSideEffects &CalleeEffects, + FullApplySite FAS); /// Analyze the side-effects of a single SIL instruction \p I. /// Visited callees are added to \p BottomUpOrder until \p RecursionDepth /// reaches MaxRecursionDepth. - void analyzeInstruction(FunctionInfo *FInfo, - SILInstruction *I, - FunctionOrder &BottomUpOrder, - int RecursionDepth); + void analyzeInstruction(SILInstruction *I); - /// Gets or creates FunctionEffects for \p F. - FunctionInfo *getFunctionInfo(SILFunction *F) { - FunctionInfo *&FInfo = Function2Info[F]; - if (!FInfo) { - FInfo = new (Allocator.Allocate()) FunctionInfo(F); - } - return FInfo; - } + /// Print the function effects. + void dump() const; - /// Recomputes the side-effect information for the function \p Initial and - /// all called functions, up to a recursion depth of MaxRecursionDepth. - void recompute(FunctionInfo *Initial); + /// Does the function allocate objects, boxes, etc., i.e. everything which + /// has a reference count. + bool mayAllocObjects() const { return AllocsObjects; } -public: - SideEffectAnalysis() - : BottomUpIPAnalysis(AnalysisKind::SideEffect) {} + /// Can this function trap or exit the program in any way? + bool mayTrap() const { return Traps; } - static bool classof(const SILAnalysis *S) { - return S->getKind() == AnalysisKind::SideEffect; + /// Does this function read a reference count other than with retain or + /// release instructions, e.g. isUnique? + bool mayReadRC() const { return ReadsRC; } + + /// Gets the memory behavior considering the global effects and + /// all parameter effects. If \p ScanKind equals ignoreRetains then retain + /// instructions are considered as side effects. + MemoryBehavior getMemBehavior(RetainObserveKind ScanKind) const; + + /// Get the global effects for the function. These are effects which cannot + /// be associated to a specific parameter, e.g. writes to global variables + /// or writes to unknown pointers. + const FunctionSideEffectFlags &getGlobalEffects() const { + return GlobalEffects; } - - virtual void initialize(SILPassManager *PM) override; - - /// Get the side-effects of a function. - const FunctionEffects &getEffects(SILFunction *F) { - FunctionInfo *FInfo = getFunctionInfo(F); - if (!FInfo->isValid()) - recompute(FInfo); - return FInfo->FE; + + /// Get the array of parameter effects. If a side-effect can be associated + /// to a specific parameter, it is contained here instead of the global + /// effects. + /// Note that if a parameter effect is mayRelease(), it means that the + /// global function effects can be anything, because the destructor of an + /// object can have arbitrary side effects. + ArrayRef getParameterEffects() const { + return ParamEffects; } - /// Get the side-effects of a call site. - void getEffects(FunctionEffects &ApplyEffects, FullApplySite FAS); - - /// Invalidate all information in this analysis. - virtual void invalidate() override; - - /// Invalidate all of the information for a specific function. - virtual void invalidate(SILFunction *F, InvalidationKind K) override; +protected: + /// Set the side-effects of a function, which has an @effects attribute. + /// Returns true if \a F has an @effects attribute which could be handled. + bool setDefinedEffects(SILFunction *F); - /// Notify the analysis about a newly created function. - virtual void notifyAddFunction(SILFunction *F) override { } + /// Set the side-effects of a semantic call. + /// Return true if \p ASC could be handled. + bool setSemanticEffects(ArraySemanticsCall ASC); - /// Notify the analysis about a function which will be deleted from the - /// module. - virtual void notifyDeleteFunction(SILFunction *F) override { - invalidate(F, InvalidationKind::Nothing); + friend raw_ostream &operator<<(raw_ostream &os, + const FunctionSideEffects &Effects) { + os << "func=" << Effects.getGlobalEffects(); + int ParamIdx = 0; + for (auto &E : Effects.getParameterEffects()) { + os << ",param" << ParamIdx++ << "=" << E; + } + if (Effects.mayAllocObjects()) + os << ";alloc"; + if (Effects.mayTrap()) + os << ";trap"; + if (Effects.mayReadRC()) + os << ";readrc"; + return os; } +}; - /// Notify the analysis about changed witness or vtables. - virtual void invalidateFunctionTables() override { } +/// The SideEffectAnalysis provides information about side-effects of SIL +/// functions. Side-effect information is provided per function and includes: +/// Does the function read or write memory? Does the function retain or release +/// objects? etc. +/// For details see FunctionSideEffects. +class SideEffectAnalysis + : public GenericFunctionEffectAnalysis { +public: + SideEffectAnalysis() + : GenericFunctionEffectAnalysis( + AnalysisKind::SideEffect) {} + + static bool classof(const SILAnalysis *S) { + return S->getKind() == AnalysisKind::SideEffect; + } }; } // end namespace swift -#endif - +#endif // SWIFT_SILOPTIMIZER_ANALYSIS_SIDEEFFECTANALYSIS_H_ diff --git a/include/swift/SILOptimizer/PassManager/Passes.def b/include/swift/SILOptimizer/PassManager/Passes.def index c74bf64a0ff01..8d9cab90ce43d 100644 --- a/include/swift/SILOptimizer/PassManager/Passes.def +++ b/include/swift/SILOptimizer/PassManager/Passes.def @@ -54,10 +54,14 @@ PASS(AADumper, "aa-dump", "Dump Alias Analysis over all Pairs") PASS(ABCOpt, "abcopts", "Array Bounds Check Optimization") +PASS(AccessEnforcementOpts, "access-enforcement-opts", + "Access Enforcement Optimization") PASS(AccessEnforcementSelection, "access-enforcement-selection", "Access Enforcement Selection") PASS(AccessSummaryDumper, "access-summary-dump", "Dump Address Parameter Access Summary") +PASS(AccessedStorageDumper, "accessed-storage-dump", + "Dump Accessed Storage Summary") PASS(AccessMarkerElimination, "access-marker-elim", "Access Marker Elimination.") PASS(AddressLowering, "address-lowering", @@ -90,6 +94,8 @@ PASS(CapturePropagation, "capture-prop", "Captured Constant Propagation") PASS(ClosureSpecializer, "closure-specialize", "Closure Specialization on Constant Function Arguments") +PASS(ClosureLifetimeFixup, "closure-lifetime-fixup", + "Closure Lifetime Fixup") PASS(CodeSinking, "code-sinking", "Code Sinking") PASS(ComputeDominanceInfo, "compute-dominance-info", @@ -226,20 +232,24 @@ PASS(LateReleaseHoisting, "late-release-hoisting", "Late SIL release Hoisting Preserving Epilogues") IRGEN_PASS(LoadableByAddress, "loadable-address", "SIL Large Loadable type by-address lowering.") +PASS(MandatorySILLinker, "mandatory-linker", + "Deserialize all referenced SIL functions that are shared or transparent") +PASS(PerformanceSILLinker, "performance-linker", + "Deserialize all referenced SIL functions") +PASS(RawSILInstLowering, "raw-sil-inst-lowering", + "Lower all raw SIL instructions to canonical equivalents.") PASS(RemovePins, "remove-pins", "Remove SIL pin/unpin pairs") PASS(TempRValueOpt, "temp-rvalue-opt", "Remove short-lived immutable temporary copies") PASS(SideEffectsDumper, "side-effects-dump", "Print Side-Effect Information for all Functions") -PASS(SILCleanup, "cleanup", - "SIL Cleanup Preparation for IRGen") +PASS(IRGenPrepare, "irgen-prepare", + "Cleanup SIL in preparation for IRGen") 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(SILLinker, "linker", - "Link all SIL Referenced within the Module via Deserialization") PASS(SROA, "sroa", "Scalar Replacement of Aggregate Stack Objects") PASS(SROABBArgs, "sroa-bb-args", diff --git a/include/swift/SILOptimizer/PassManager/Passes.h b/include/swift/SILOptimizer/PassManager/Passes.h index 47e3a317351ef..74a55bd3f3203 100644 --- a/include/swift/SILOptimizer/PassManager/Passes.h +++ b/include/swift/SILOptimizer/PassManager/Passes.h @@ -55,13 +55,6 @@ namespace swift { /// \brief Remove dead functions from \p M. void performSILDeadFunctionElimination(SILModule *M); - /// \brief Link a SILFunction declaration to the actual definition in the - /// serialized modules. - /// - /// \param M the SILModule on which to operate - /// \param LinkAll when true, always link. For testing purposes. - void performSILLinking(SILModule *M, bool LinkAll = false); - /// \brief Convert SIL to a lowered form suitable for IRGen. void runSILLoweringPasses(SILModule &M); diff --git a/include/swift/SILOptimizer/Utils/FunctionSignatureOptUtils.h b/include/swift/SILOptimizer/Utils/FunctionSignatureOptUtils.h deleted file mode 100644 index fb5402afa2bc0..0000000000000 --- a/include/swift/SILOptimizer/Utils/FunctionSignatureOptUtils.h +++ /dev/null @@ -1,208 +0,0 @@ -//===--- FunctionSignatureOptUtils.h ----------------------------*- C++ -*-===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -#ifndef SWIFT_SIL_FUNCTIONSIGOPTUTILS_H -#define SWIFT_SIL_FUNCTIONSIGOPTUTILS_H - -#include "swift/SIL/SILInstruction.h" -#include "swift/SIL/SILValue.h" -#include "swift/SIL/SILArgument.h" -#include "swift/SIL/SILFunction.h" -#include "swift/SIL/SILDebugScope.h" -#include "swift/SIL/Projection.h" -#include "swift/SILOptimizer/Analysis/Analysis.h" -#include "swift/SILOptimizer/Analysis/AliasAnalysis.h" -#include "swift/SILOptimizer/Analysis/ARCAnalysis.h" -#include "swift/SILOptimizer/Analysis/CallerAnalysis.h" -#include "swift/SILOptimizer/Analysis/RCIdentityAnalysis.h" -#include "swift/SILOptimizer/Utils/Local.h" -#include "swift/SILOptimizer/PassManager/PassManager.h" - -namespace swift { - -/// A structure that maintains all of the information about a specific -/// SILArgument that we are tracking. -struct ArgumentDescriptor { - /// The argument that we are tracking original data for. - SILFunctionArgument *Arg; - - /// Parameter Info. - Optional PInfo; - - /// The original index of this argument. - unsigned Index; - - /// The original decl of this Argument. - const ValueDecl *Decl; - - /// Was this parameter originally dead? - bool IsEntirelyDead; - - /// Should the argument be exploded ? - bool Explode; - - /// This parameter is owned to guaranteed. - bool OwnedToGuaranteed; - - /// Is this parameter an indirect result? - bool IsIndirectResult; - - /// If non-null, this is the release in the return block of the callee, which - /// is associated with this parameter if it is @owned. If the parameter is not - /// @owned or we could not find such a release in the callee, this is null. - ReleaseList CalleeRelease; - - /// The same as CalleeRelease, but the release in the throw block, if it is a - /// function which has a throw block. - ReleaseList CalleeReleaseInThrowBlock; - - /// The projection tree of this arguments. - ProjectionTree ProjTree; - - ArgumentDescriptor() = delete; - - /// Initialize this argument descriptor with all information from A that we - /// use in our optimization. - /// - /// *NOTE* We cache a lot of data from the argument and maintain a reference - /// to the original argument. The reason why we do this is to make sure we - /// have access to the original argument's state if we modify the argument - /// when optimizing. - ArgumentDescriptor(SILFunctionArgument *A) - : Arg(A), - PInfo(A->getKnownParameterInfo()), - Index(A->getIndex()), - Decl(A->getDecl()), IsEntirelyDead(false), Explode(false), - OwnedToGuaranteed(false), IsIndirectResult(A->isIndirectResult()), - CalleeRelease(), CalleeReleaseInThrowBlock(), - ProjTree(A->getModule(), A->getType()) { - if (!A->isIndirectResult()) { - PInfo = Arg->getKnownParameterInfo(); - } - } - - ArgumentDescriptor(const ArgumentDescriptor &) = delete; - ArgumentDescriptor(ArgumentDescriptor &&) = default; - ArgumentDescriptor &operator=(const ArgumentDescriptor &) = delete; - ArgumentDescriptor &operator=(ArgumentDescriptor &&) = default; - - /// \returns true if this argument's convention is P. - bool hasConvention(SILArgumentConvention P) const { - return Arg->hasConvention(P); - } - - bool canOptimizeLiveArg() const { - if (Arg->getType().isObject()) - return true; - // @in arguments of generic types can be processed. - if (Arg->getType().getSwiftRValueType()->hasArchetype() && - Arg->getType().isAddress() && - (Arg->hasConvention(SILArgumentConvention::Indirect_In) || - Arg->hasConvention(SILArgumentConvention::Indirect_In_Guaranteed))) - return true; - return false; - } - - /// Return true if it's both legal and a good idea to explode this argument. - bool shouldExplode(ConsumedArgToEpilogueReleaseMatcher &ERM) const { - // We cannot optimize the argument. - if (!canOptimizeLiveArg()) - return false; - - // See if the projection tree consists of potentially multiple levels of - // structs containing one field. In such a case, there is no point in - // exploding the argument. - // - // Also, in case of a type can not be exploded, e.g an enum, we treat it - // as a singleton. - if (ProjTree.isSingleton()) - return false; - - auto Ty = Arg->getType().getObjectType(); - if (!shouldExpand(Arg->getModule(), Ty)) { - return false; - } - - // If this argument is @owned and we can not find all the releases for it - // try to explode it, maybe we can find some of the releases and O2G some - // of its components. - // - // This is a potentially a very profitable optimization. Ignore other - // heuristics. - if (hasConvention(SILArgumentConvention::Direct_Owned) && - ERM.hasSomeReleasesForArgument(Arg)) - return true; - - size_t explosionSize = ProjTree.liveLeafCount(); - return explosionSize >= 1 && explosionSize <= 3; - } - - llvm::Optional - getTransformedOwnershipKind(SILType SubTy) { - if (IsEntirelyDead) - return None; - if (SubTy.isTrivial(Arg->getModule())) - return Optional(ValueOwnershipKind::Trivial); - if (OwnedToGuaranteed) - return Optional(ValueOwnershipKind::Guaranteed); - return Arg->getOwnershipKind(); - } -}; - -/// A structure that maintains all of the information about a specific -/// direct result that we are tracking. -struct ResultDescriptor { - /// The original parameter info of this argument. - SILResultInfo ResultInfo; - - /// If non-null, this is the release in the return block of the callee, which - /// is associated with this parameter if it is @owned. If the parameter is not - /// @owned or we could not find such a release in the callee, this is null. - llvm::SmallSetVector CalleeRetain; - - /// This is owned to guaranteed. - bool OwnedToGuaranteed; - - /// Initialize this argument descriptor with all information from A that we - /// use in our optimization. - /// - /// *NOTE* We cache a lot of data from the argument and maintain a reference - /// to the original argument. The reason why we do this is to make sure we - /// have access to the original argument's state if we modify the argument - /// when optimizing. - ResultDescriptor() {} - ResultDescriptor(SILResultInfo RI) - : ResultInfo(RI), CalleeRetain(), OwnedToGuaranteed(false) {} - - ResultDescriptor(const ResultDescriptor &) = delete; - ResultDescriptor(ResultDescriptor &&) = default; - ResultDescriptor &operator=(const ResultDescriptor &) = delete; - ResultDescriptor &operator=(ResultDescriptor &&) = default; - - /// \returns true if this argument's ParameterConvention is P. - bool hasConvention(ResultConvention R) const { - return ResultInfo.getConvention() == R; - } -}; - -/// Returns true if F is a function which the pass know show to specialize -/// function signatures for. -bool canSpecializeFunction(SILFunction *F, - const CallerAnalysis::FunctionInfo *FuncInfo, - bool OptForPartialApply); - -/// Return true if this argument is used in a non-trivial way. -bool hasNonTrivialNonDebugUse(SILArgument *Arg); - -} // end namespace swift - -#endif diff --git a/include/swift/SILOptimizer/Utils/GenericCloner.h b/include/swift/SILOptimizer/Utils/GenericCloner.h index 5384c73a18649..0c49cae8add50 100644 --- a/include/swift/SILOptimizer/Utils/GenericCloner.h +++ b/include/swift/SILOptimizer/Utils/GenericCloner.h @@ -41,7 +41,7 @@ class GenericCloner : public TypeSubstCloner { GenericCloner(SILFunction *F, IsSerialized_t Serialized, const ReabstractionInfo &ReInfo, - SubstitutionList ParamSubs, + SubstitutionMap ParamSubs, StringRef NewName, CloneCollector::CallbackType Callback) : TypeSubstCloner(*initCloned(F, Serialized, ReInfo, NewName), *F, @@ -55,7 +55,7 @@ class GenericCloner : public TypeSubstCloner { cloneFunction(SILFunction *F, IsSerialized_t Serialized, const ReabstractionInfo &ReInfo, - SubstitutionList ParamSubs, + SubstitutionMap ParamSubs, StringRef NewName, CloneCollector::CallbackType Callback =nullptr) { // Clone and specialize the function. diff --git a/include/swift/SILOptimizer/Utils/Generics.h b/include/swift/SILOptimizer/Utils/Generics.h index 2f269472b73b5..02d5f30879135 100644 --- a/include/swift/SILOptimizer/Utils/Generics.h +++ b/include/swift/SILOptimizer/Utils/Generics.h @@ -82,21 +82,18 @@ class ReabstractionInfo { // any transformations performed by the generic specializer. // // Maps callee's generic parameters to caller's archetypes. - SubstitutionList CalleeParamSubs; + SubstitutionMap CalleeParamSubMap; // Set of substitutions to be used to invoke a specialized function. // // Maps generic parameters of the specialized callee function to caller's // archetypes. - SubstitutionList CallerParamSubs; + SubstitutionMap CallerParamSubMap; // Replaces archetypes of the original callee with archetypes // or concrete types, if they were made concrete) of the specialized // callee. - // - // Maps original callee's generic parameters to specialized - // callee archetypes. - SubstitutionList ClonerParamSubs; + SubstitutionMap ClonerParamSubMap; // Reference to the original generic non-specialized callee function. SILFunction *Callee; @@ -117,18 +114,18 @@ class ReabstractionInfo { // Create a new substituted type with the updated signature. CanSILFunctionType createSubstitutedType(SILFunction *OrigF, - const SubstitutionMap &SubstMap, + SubstitutionMap SubstMap, bool HasUnboundGenericParams); void createSubstitutedAndSpecializedTypes(); bool prepareAndCheck(ApplySite Apply, SILFunction *Callee, - SubstitutionList ParamSubs, + SubstitutionMap ParamSubs, OptRemark::Emitter *ORE = nullptr); void performFullSpecializationPreparation(SILFunction *Callee, - SubstitutionList ParamSubs); + SubstitutionMap ParamSubs); void performPartialSpecializationPreparation(SILFunction *Caller, SILFunction *Callee, - SubstitutionList ParamSubs); + SubstitutionMap ParamSubs); void finishPartialSpecializationPreparation( FunctionSignaturePartialSpecializer &FSPS); @@ -139,7 +136,7 @@ class ReabstractionInfo { /// If specialization is not possible getSpecializedType() will return an /// invalid type. ReabstractionInfo(ApplySite Apply, SILFunction *Callee, - SubstitutionList ParamSubs, + SubstitutionMap ParamSubs, bool ConvertIndirectToDirect = true, OptRemark::Emitter *ORE = nullptr); @@ -209,16 +206,16 @@ class ReabstractionInfo { return SpecializedGenericSig; } - SubstitutionList getCallerParamSubstitutions() const { - return CallerParamSubs; + SubstitutionMap getCallerParamSubstitutionMap() const { + return CallerParamSubMap; } - SubstitutionList getClonerParamSubstitutions() const { - return ClonerParamSubs; + SubstitutionMap getClonerParamSubstitutionMap() const { + return ClonerParamSubMap; } - SubstitutionList getCalleeParamSubstitutions() const { - return CalleeParamSubs; + SubstitutionMap getCalleeParamSubstitutionMap() const { + return CalleeParamSubMap; } /// Create a specialized function type for a specific substituted type \p @@ -247,7 +244,7 @@ class ReabstractionInfo { /// Returns true if a given apply can be specialized. static bool canBeSpecialized(ApplySite Apply, SILFunction *Callee, - SubstitutionList ParamSubs); + SubstitutionMap ParamSubs); /// Returns the apply site for the current generic specialization. ApplySite getApply() const { @@ -262,7 +259,7 @@ class ReabstractionInfo { class GenericFuncSpecializer { SILModule &M; SILFunction *GenericFunc; - SubstitutionList ParamSubs; + SubstitutionMap ParamSubs; IsSerialized_t Serialized; const ReabstractionInfo &ReInfo; @@ -271,7 +268,7 @@ class GenericFuncSpecializer { public: GenericFuncSpecializer(SILFunction *GenericFunc, - SubstitutionList ParamSubs, + SubstitutionMap ParamSubs, IsSerialized_t Serialized, const ReabstractionInfo &ReInfo); diff --git a/include/swift/SILOptimizer/Utils/Local.h b/include/swift/SILOptimizer/Utils/Local.h index 9ef601c1fbe96..c517baeb1eb0e 100644 --- a/include/swift/SILOptimizer/Utils/Local.h +++ b/include/swift/SILOptimizer/Utils/Local.h @@ -63,7 +63,7 @@ NullablePtr createDecrementBefore(SILValue Ptr, void recursivelyDeleteTriviallyDeadInstructions( ArrayRef I, bool Force = false, - std::function C = [](SILInstruction *){}); + llvm::function_ref C = [](SILInstruction *){}); /// \brief If the given instruction is dead, delete it along with its dead /// operands. @@ -76,7 +76,7 @@ void recursivelyDeleteTriviallyDeadInstructions( SILInstruction *I, bool Force = false, - std::function C = [](SILInstruction *){}); + llvm::function_ref C = [](SILInstruction *){}); /// \brief Perform a fast local check to see if the instruction is dead. /// @@ -96,7 +96,7 @@ collectUsesOfValue(SILValue V, llvm::SmallPtrSetImpl &Insts); /// instruction itself) void eraseUsesOfInstruction( SILInstruction *Inst, - std::function C = [](SILInstruction *){}); + llvm::function_ref C = [](SILInstruction *){}); /// \brief Recursively erase all of the uses of the value (but not the /// value itself) @@ -124,10 +124,6 @@ ProjectBoxInst *getOrCreateProjectBox(AllocBoxInst *ABI, unsigned Index); /// if possible. void replaceDeadApply(ApplySite Old, ValueBase *New); -/// \brief Return true if the substitution list contains replacement types -/// that are dependent on the type parameters of the caller. -bool hasArchetypes(SubstitutionList Subs); - /// \brief Return true if any call inside the given function may bind dynamic /// 'Self' to a generic argument of the callee. bool mayBindDynamicSelf(SILFunction *F); diff --git a/include/swift/SILOptimizer/Utils/SILInliner.h b/include/swift/SILOptimizer/Utils/SILInliner.h index c84fd37b92172..3c1a7884dcb4c 100644 --- a/include/swift/SILOptimizer/Utils/SILInliner.h +++ b/include/swift/SILOptimizer/Utils/SILInliner.h @@ -61,7 +61,7 @@ class SILInliner : public TypeSubstCloner { public: SILInliner(SILFunction &To, SILFunction &From, InlineKind IKind, - SubstitutionList ApplySubs, + SubstitutionMap ApplySubs, SILOpenedArchetypesTracker &OpenedArchetypesTracker, CloneCollector::CallbackType Callback = nullptr) : TypeSubstCloner(To, From, ApplySubs, diff --git a/include/swift/SILOptimizer/Utils/SpecializationMangler.h b/include/swift/SILOptimizer/Utils/SpecializationMangler.h index 2df199748a783..f78fd3af560f1 100644 --- a/include/swift/SILOptimizer/Utils/SpecializationMangler.h +++ b/include/swift/SILOptimizer/Utils/SpecializationMangler.h @@ -65,17 +65,17 @@ class SpecializationMangler : public Mangle::ASTMangler { // The mangler for specialized generic functions. class GenericSpecializationMangler : public SpecializationMangler { - SubstitutionList Subs; + SubstitutionMap SubMap; bool isReAbstracted; public: GenericSpecializationMangler(SILFunction *F, - SubstitutionList Subs, + SubstitutionMap SubMap, IsSerialized_t Serialized, bool isReAbstracted) : SpecializationMangler(SpecializationPass::GenericSpecializer, Serialized, F), - Subs(Subs), isReAbstracted(isReAbstracted) {} + SubMap(SubMap), isReAbstracted(isReAbstracted) {} std::string mangle(); }; @@ -127,6 +127,8 @@ class FunctionSignatureSpecializationMangler : public SpecializationMangler { Dead=32, OwnedToGuaranteed=64, SROA=128, + GuaranteedToOwned=256, + ExistentialToGeneric=512, First_OptionSetEntry=32, LastOptionSetEntry=32768, }; @@ -149,6 +151,8 @@ class FunctionSignatureSpecializationMangler : public SpecializationMangler { ThinToThickFunctionInst *TTTFI); void setArgumentDead(unsigned OrigArgIdx); void setArgumentOwnedToGuaranteed(unsigned OrigArgIdx); + void setArgumentGuaranteedToOwned(unsigned OrigArgIdx); + void setArgumentExistentialToGeneric(unsigned OrigArgIdx); void setArgumentSROA(unsigned OrigArgIdx); void setArgumentBoxToValue(unsigned OrigArgIdx); void setArgumentBoxToStack(unsigned OrigArgIdx); diff --git a/include/swift/Sema/IDETypeChecking.h b/include/swift/Sema/IDETypeChecking.h index 40ae35076035d..9f05f11eb669b 100644 --- a/include/swift/Sema/IDETypeChecking.h +++ b/include/swift/Sema/IDETypeChecking.h @@ -147,8 +147,8 @@ namespace swift { bool IsSynthesized; }; - typedef llvm::function_ref)> - ExtensionGroupOperation; + using ExtensionGroupOperation = + llvm::function_ref)>; class SynthesizedExtensionAnalyzer { struct Implementation; diff --git a/include/swift/Sema/TypeCheckRequest.h b/include/swift/Sema/TypeCheckRequest.h index 1764a14222059..e2c60ec865c53 100644 --- a/include/swift/Sema/TypeCheckRequest.h +++ b/include/swift/Sema/TypeCheckRequest.h @@ -105,7 +105,7 @@ class TypeCheckRequest { public: // The payload types. #define TYPE_CHECK_REQUEST_PAYLOAD(PayloadName,...) \ - typedef __VA_ARGS__ PayloadName##PayloadType; + using PayloadName##PayloadType = __VA_ARGS__; #include "swift/Sema/TypeCheckRequestPayloads.def" @@ -181,7 +181,7 @@ class TypeCheckRequest { /// A callback used to check whether a particular dependency of this /// operation has been satisfied. If so, it returns \c false. If not, /// the dependency will be recorded and this operation returns \c true. -typedef llvm::function_ref UnsatisfiedDependency; +using UnsatisfiedDependency = llvm::function_ref; // Create requestXXX functions to more easily form type check requests // of the appropriate type. diff --git a/include/swift/Sema/TypeCheckRequestKinds.def b/include/swift/Sema/TypeCheckRequestKinds.def index d7c35360bd5d4..e783285a03280 100644 --- a/include/swift/Sema/TypeCheckRequestKinds.def +++ b/include/swift/Sema/TypeCheckRequestKinds.def @@ -45,10 +45,6 @@ TYPE_CHECK_REQUEST(TypeCheckRawType, Enum) /// Compute the set of inherited protocols for a given protocol. TYPE_CHECK_REQUEST(InheritedProtocols, Protocol) -/// Partially resolve a type, forming enough of its structure to make -/// structural queries but not determining correctness. -TYPE_CHECK_REQUEST(ResolveTypeRepr, TypeResolution) - /// Resolve the given type declaration to the requested type /// resolution stage. TYPE_CHECK_REQUEST(ResolveTypeDecl, TypeDeclResolution) diff --git a/include/swift/Serialization/DeclTypeRecordNodes.def b/include/swift/Serialization/DeclTypeRecordNodes.def index a60fb20c5d551..efe47c4ebe330 100644 --- a/include/swift/Serialization/DeclTypeRecordNodes.def +++ b/include/swift/Serialization/DeclTypeRecordNodes.def @@ -92,7 +92,7 @@ TYPE(INOUT) TYPE(ARCHETYPE) TYPE(PROTOCOL_COMPOSITION) TYPE(BOUND_GENERIC) -TRAILING_INFO(BOUND_GENERIC_SUBSTITUTION) + TYPE(GENERIC_FUNCTION) TYPE(ARRAY_SLICE) TYPE(DICTIONARY) @@ -166,6 +166,7 @@ TRAILING_INFO(GENERIC_REQUIREMENT) TRAILING_INFO(LAYOUT_REQUIREMENT) OTHER(GENERIC_SIGNATURE, 244) OTHER(SIL_GENERIC_ENVIRONMENT, 245) +OTHER(SUBSTITUTION_MAP, 246) OTHER(LOCAL_DISCRIMINATOR, 248) OTHER(PRIVATE_DISCRIMINATOR, 249) diff --git a/include/swift/Serialization/ModuleFile.h b/include/swift/Serialization/ModuleFile.h index 300e5943dad2a..256a01f6eace2 100644 --- a/include/swift/Serialization/ModuleFile.h +++ b/include/swift/Serialization/ModuleFile.h @@ -156,8 +156,6 @@ class ModuleFile bool isHeader() const { return IsHeader; } bool isScoped() const { return IsScoped; } - void forceExported() { IsExported = true; } - std::string getPrettyPrintedPath() const; }; @@ -310,6 +308,9 @@ class ModuleFile /// Generic environments referenced by this module. std::vector> GenericEnvironments; + /// Substitution maps referenced by this module. + std::vector> SubstitutionMaps; + /// Represents an identifier that may or may not have been deserialized yet. /// /// If \c Offset is non-zero, the identifier has not been loaded yet. @@ -399,12 +400,6 @@ class ModuleFile /// Whether this module file comes from a framework. unsigned IsFramework : 1; - /// THIS SETTING IS OBSOLETE BUT IS STILL USED BY OLDER MODULES. - /// - /// Whether this module has a shadowed module that's part of its public - /// interface. - unsigned HasUnderlyingModule : 1; - /// Whether or not ImportDecls is valid. unsigned ComputedImportDecls : 1; @@ -844,12 +839,9 @@ class ModuleFile GenericEnvironment *getGenericEnvironment( serialization::GenericEnvironmentID ID); - /// Reads a substitution record from \c DeclTypeCursor. - /// - /// If the record at the cursor is not a substitution, returns None. - Optional maybeReadSubstitution(llvm::BitstreamCursor &Cursor, - GenericEnvironment *genericEnv = - nullptr); + /// Returns the substitution map for the given ID, deserializing it if + /// needed. + SubstitutionMap getSubstitutionMap(serialization::SubstitutionMapID id); /// Recursively reads a protocol conformance from the given cursor. ProtocolConformanceRef readConformance(llvm::BitstreamCursor &Cursor, diff --git a/include/swift/Serialization/ModuleFormat.h b/include/swift/Serialization/ModuleFormat.h index 15e3dc349c571..e792d959bb89b 100644 --- a/include/swift/Serialization/ModuleFormat.h +++ b/include/swift/Serialization/ModuleFormat.h @@ -55,7 +55,7 @@ const uint16_t VERSION_MAJOR = 0; /// describe what change you made. The content of this comment isn't important; /// it just ensures a conflict if two people change the module format. /// Don't worry about adhering to the 80-column limit for this line. -const uint16_t VERSION_MINOR = 408; // Last change: begin_access [nontracking] +const uint16_t VERSION_MINOR = 419; // Last change: Remove discriminator from LocalDeclTableInfo. using DeclIDField = BCFixed<31>; @@ -88,6 +88,11 @@ using GenericSignatureIDField = DeclIDField; using GenericEnvironmentID = DeclID; using GenericEnvironmentIDField = DeclIDField; +// SubstitutionMapID must be the same as DeclID because it is stored in the +// same way. +using SubstitutionMapID = DeclID; +using SubstitutionMapIDField = DeclIDField; + // ModuleID must be the same as IdentifierID because it is stored the same way. using ModuleID = IdentifierID; using ModuleIDField = IdentifierIDField; @@ -583,7 +588,7 @@ namespace input_block { LINK_LIBRARY, IMPORTED_HEADER, IMPORTED_HEADER_CONTENTS, - MODULE_FLAGS, + MODULE_FLAGS, // [unused] SEARCH_PATH }; @@ -616,11 +621,6 @@ namespace input_block { BCBlob >; - using ModuleFlagsLayout = BCRecordLayout< - MODULE_FLAGS, - BCFixed<1> // has underlying module? [[UNUSED]] - >; - using SearchPathLayout = BCRecordLayout< SEARCH_PATH, BCFixed<1>, // framework? @@ -649,10 +649,10 @@ namespace decls_block { using NameAliasTypeLayout = BCRecordLayout< NAME_ALIAS_TYPE, - DeclIDField, // typealias decl - TypeIDField, // parent type - TypeIDField // underlying type - // trailing substitutions + DeclIDField, // typealias decl + TypeIDField, // parent type + TypeIDField, // underlying type + SubstitutionMapIDField // substitution map >; using GenericTypeParamTypeLayout = BCRecordLayout< @@ -751,13 +751,6 @@ namespace decls_block { BCArray // generic arguments >; - using BoundGenericSubstitutionLayout = BCRecordLayout< - BOUND_GENERIC_SUBSTITUTION, - TypeIDField, // replacement - BCVBR<5> - // Trailed by protocol conformance info (if any) - >; - using GenericFunctionTypeLayout = BCRecordLayout< GENERIC_FUNCTION_TYPE, TypeIDField, // input @@ -799,8 +792,8 @@ namespace decls_block { using SILBoxTypeLayout = BCRecordLayout< SIL_BOX_TYPE, - SILLayoutIDField // layout - // trailing substitutions + SILLayoutIDField, // layout + SubstitutionMapIDField // substitutions >; template @@ -1224,6 +1217,14 @@ namespace decls_block { BCArray // generic parameter types >; + using SubstitutionMapLayout = BCRecordLayout< + SUBSTITUTION_MAP, + GenericSignatureIDField, // generic signature + BCVBR<5>, // # of conformances + BCArray // replacement types + // Conformances trail the record. + >; + using SILGenericEnvironmentLayout = BCRecordLayout< SIL_GENERIC_ENVIRONMENT, BCArray // (generic parameter name, sugared interface @@ -1278,9 +1279,9 @@ namespace decls_block { using SpecializedProtocolConformanceLayout = BCRecordLayout< SPECIALIZED_PROTOCOL_CONFORMANCE, - TypeIDField, // conforming type - BCVBR<5> // # of substitutions for the conformance - // followed by substitution records for the conformance + TypeIDField, // conforming type + SubstitutionMapIDField // substitution map + // trailed by the underlying conformance >; using InheritedProtocolConformanceLayout = BCRecordLayout< @@ -1599,7 +1600,8 @@ namespace index_block { DECL_MEMBER_NAMES, GENERIC_SIGNATURE_OFFSETS, - LastRecordKind = GENERIC_SIGNATURE_OFFSETS, + SUBSTITUTION_MAP_OFFSETS, + LastRecordKind = SUBSTITUTION_MAP_OFFSETS, }; constexpr const unsigned RecordIDFieldWidth = 5; diff --git a/include/swift/Serialization/Validation.h b/include/swift/Serialization/Validation.h index dbd86e1b73c03..1942d5e3e77e8 100644 --- a/include/swift/Serialization/Validation.h +++ b/include/swift/Serialization/Validation.h @@ -43,6 +43,10 @@ enum class Status { /// The module file is an overlay for a Clang module, which can't be found. MissingShadowedModule, + /// The module file depends on a module that is still being loaded, i.e. + /// there is a circular dependency. + CircularDependency, + /// The module file depends on a bridging header that can't be loaded. FailedToLoadBridgingHeader, diff --git a/include/swift/Strings.h b/include/swift/Strings.h index f471278208a8b..4d02d9d3e67df 100644 --- a/include/swift/Strings.h +++ b/include/swift/Strings.h @@ -14,73 +14,81 @@ #define SWIFT_STRINGS_H namespace swift { - /// The extension for serialized modules. - static const char SERIALIZED_MODULE_EXTENSION[] = "swiftmodule"; - /// The extension for serialized documentation comments. - static const char SERIALIZED_MODULE_DOC_EXTENSION[] = "swiftdoc"; - /// The extension for PCH files. - static const char PCH_EXTENSION[] = "pch"; - /// The extension for replacement map files. - static const char REMAP_EXTENSION[] = "remap"; - /// The extension for SIL files. - static const char SIL_EXTENSION[] = "sil"; - /// The extension for SIB files. - static const char SIB_EXTENSION[] = "sib"; - /// The extension for LLVM IR files. - static const char LLVM_BC_EXTENSION[] = "bc"; - static const char LLVM_IR_EXTENSION[] = "ll"; - /// The name of the standard library, which is a reserved module name. - static const char STDLIB_NAME[] = "Swift"; - /// The name of the Onone support library, which is a reserved module name. - static const char SWIFT_ONONE_SUPPORT[] = "SwiftOnoneSupport"; - /// The name of the SwiftShims module, which contains private stdlib decls. - static const char SWIFT_SHIMS_NAME[] = "SwiftShims"; - /// The name of the Builtin module, which contains Builtin functions. - static const char BUILTIN_NAME[] = "Builtin"; - /// The prefix of module names used by LLDB to capture Swift expressions - static const char LLDB_EXPRESSIONS_MODULE_NAME_PREFIX[] = "__lldb_expr_"; - /// The name of the fake module used to hold imported Objective-C things. - static const char MANGLING_MODULE_OBJC[] = "__C"; - /// The name of the fake module used to hold synthesized ClangImporter things. - static const char MANGLING_MODULE_CLANG_IMPORTER[] = "__C_Synthesized"; +/// The extension for serialized modules. +constexpr static const char SERIALIZED_MODULE_EXTENSION[] = "swiftmodule"; +/// The extension for serialized documentation comments. +constexpr static const char SERIALIZED_MODULE_DOC_EXTENSION[] = "swiftdoc"; +/// The extension for PCH files. +constexpr static const char PCH_EXTENSION[] = "pch"; +/// The extension for replacement map files. +constexpr static const char REMAP_EXTENSION[] = "remap"; +/// The extension for SIL files. +constexpr static const char SIL_EXTENSION[] = "sil"; +/// The extension for SIB files. +constexpr static const char SIB_EXTENSION[] = "sib"; +/// The extension for LLVM IR files. +constexpr static const char LLVM_BC_EXTENSION[] = "bc"; +constexpr static const char LLVM_IR_EXTENSION[] = "ll"; +/// The name of the standard library, which is a reserved module name. +constexpr static const char STDLIB_NAME[] = "Swift"; +/// The name of the Onone support library, which is a reserved module name. +constexpr static const char SWIFT_ONONE_SUPPORT[] = "SwiftOnoneSupport"; +/// The name of the SwiftShims module, which contains private stdlib decls. +constexpr static const char SWIFT_SHIMS_NAME[] = "SwiftShims"; +/// The name of the Builtin module, which contains Builtin functions. +constexpr static const char BUILTIN_NAME[] = "Builtin"; +/// The prefix of module names used by LLDB to capture Swift expressions +constexpr static const char LLDB_EXPRESSIONS_MODULE_NAME_PREFIX[] = + "__lldb_expr_"; - /// The name of the Builtin type prefix - static const char BUILTIN_TYPE_NAME_PREFIX[] = "Builtin."; - /// The name of the Builtin type for Int - static const char BUILTIN_TYPE_NAME_INT[] = "Builtin.Int"; - /// The name of the Builtin type for Int8 - static const char BUILTIN_TYPE_NAME_INT8[] = "Builtin.Int8"; - /// The name of the Builtin type for Int16 - static const char BUILTIN_TYPE_NAME_INT16[] = "Builtin.Int16"; - /// The name of the Builtin type for Int32 - static const char BUILTIN_TYPE_NAME_INT32[] = "Builtin.Int32"; - /// The name of the Builtin type for Int64 - static const char BUILTIN_TYPE_NAME_INT64[] = "Builtin.Int64"; - /// The name of the Builtin type for Int128 - static const char BUILTIN_TYPE_NAME_INT128[] = "Builtin.Int128"; - /// The name of the Builtin type for Int256 - static const char BUILTIN_TYPE_NAME_INT256[] = "Builtin.Int256"; - /// The name of the Builtin type for Int512 - static const char BUILTIN_TYPE_NAME_INT512[] = "Builtin.Int512"; - /// The name of the Builtin type for Float - static const char BUILTIN_TYPE_NAME_FLOAT[] = "Builtin.Float"; - /// The name of the Builtin type for NativeObject - static const char BUILTIN_TYPE_NAME_NATIVEOBJECT[] = "Builtin.NativeObject"; - /// The name of the Builtin type for BridgeObject - static const char BUILTIN_TYPE_NAME_BRIDGEOBJECT[] = "Builtin.BridgeObject"; - /// The name of the Builtin type for RawPointer - static const char BUILTIN_TYPE_NAME_RAWPOINTER[] = "Builtin.RawPointer"; - /// The name of the Builtin type for UnsafeValueBuffer - static const char BUILTIN_TYPE_NAME_UNSAFEVALUEBUFFER[] = "Builtin.UnsafeValueBuffer"; - /// The name of the Builtin type for UnknownObject - static const char BUILTIN_TYPE_NAME_UNKNOWNOBJECT[] = "Builtin.UnknownObject"; - /// The name of the Builtin type for Vector - static const char BUILTIN_TYPE_NAME_VEC[] = "Builtin.Vec"; - /// The name of the Builtin type for SILToken - static const char BUILTIN_TYPE_NAME_SILTOKEN[] = "Builtin.SILToken"; - /// The name of the Builtin type for Word - static const char BUILTIN_TYPE_NAME_WORD[] = "Builtin.Word"; +/// The name of the fake module used to hold imported Objective-C things. +constexpr static const char MANGLING_MODULE_OBJC[] = "__C"; +/// The name of the fake module used to hold synthesized ClangImporter things. +constexpr static const char MANGLING_MODULE_CLANG_IMPORTER[] = + "__C_Synthesized"; + +/// The name of the Builtin type prefix +constexpr static const char BUILTIN_TYPE_NAME_PREFIX[] = "Builtin."; +/// The name of the Builtin type for Int +constexpr static const char BUILTIN_TYPE_NAME_INT[] = "Builtin.Int"; +/// The name of the Builtin type for Int8 +constexpr static const char BUILTIN_TYPE_NAME_INT8[] = "Builtin.Int8"; +/// The name of the Builtin type for Int16 +constexpr static const char BUILTIN_TYPE_NAME_INT16[] = "Builtin.Int16"; +/// The name of the Builtin type for Int32 +constexpr static const char BUILTIN_TYPE_NAME_INT32[] = "Builtin.Int32"; +/// The name of the Builtin type for Int64 +constexpr static const char BUILTIN_TYPE_NAME_INT64[] = "Builtin.Int64"; +/// The name of the Builtin type for Int128 +constexpr static const char BUILTIN_TYPE_NAME_INT128[] = "Builtin.Int128"; +/// The name of the Builtin type for Int256 +constexpr static const char BUILTIN_TYPE_NAME_INT256[] = "Builtin.Int256"; +/// The name of the Builtin type for Int512 +constexpr static const char BUILTIN_TYPE_NAME_INT512[] = "Builtin.Int512"; +/// The name of the Builtin type for Float +constexpr static const char BUILTIN_TYPE_NAME_FLOAT[] = "Builtin.Float"; +/// The name of the Builtin type for NativeObject +constexpr static const char BUILTIN_TYPE_NAME_NATIVEOBJECT[] = + "Builtin.NativeObject"; +/// The name of the Builtin type for BridgeObject +constexpr static const char BUILTIN_TYPE_NAME_BRIDGEOBJECT[] = + "Builtin.BridgeObject"; +/// The name of the Builtin type for RawPointer +constexpr static const char BUILTIN_TYPE_NAME_RAWPOINTER[] = + "Builtin.RawPointer"; +/// The name of the Builtin type for UnsafeValueBuffer +constexpr static const char BUILTIN_TYPE_NAME_UNSAFEVALUEBUFFER[] = + "Builtin.UnsafeValueBuffer"; +/// The name of the Builtin type for UnknownObject +constexpr static const char BUILTIN_TYPE_NAME_UNKNOWNOBJECT[] = + "Builtin.UnknownObject"; +/// The name of the Builtin type for Vector +constexpr static const char BUILTIN_TYPE_NAME_VEC[] = "Builtin.Vec"; +/// The name of the Builtin type for SILToken +constexpr static const char BUILTIN_TYPE_NAME_SILTOKEN[] = "Builtin.SILToken"; +/// The name of the Builtin type for Word +constexpr static const char BUILTIN_TYPE_NAME_WORD[] = "Builtin.Word"; } // end namespace swift #endif // SWIFT_STRINGS_H diff --git a/include/swift/Subsystems.h b/include/swift/Subsystems.h index 95e16cb603946..1bb3d80971051 100644 --- a/include/swift/Subsystems.h +++ b/include/swift/Subsystems.h @@ -189,7 +189,8 @@ namespace swift { unsigned StartElem = 0, unsigned WarnLongFunctionBodies = 0, unsigned WarnLongExpressionTypeChecking = 0, - unsigned ExpressionTimeoutThreshold = 0); + unsigned ExpressionTimeoutThreshold = 0, + unsigned SwitchCheckingInvocationThreshold = 0); /// Now that we have type-checked an entire module, perform any type /// checking that requires the full module, e.g., Objective-C method diff --git a/include/swift/Syntax/AtomicCache.h b/include/swift/Syntax/AtomicCache.h index 1d4802e025afd..8e447fa4862cb 100644 --- a/include/swift/Syntax/AtomicCache.h +++ b/include/swift/Syntax/AtomicCache.h @@ -15,6 +15,7 @@ #include #include "swift/Syntax/References.h" +#include "llvm/ADT/STLExtras.h" namespace swift { @@ -38,7 +39,7 @@ class AtomicCache { /// Gets the value inside the cache, or creates it atomically using the /// provided lambda if it doesn't already exist. - RC getOrCreate(std::function ()> Create) const { + RC getOrCreate(llvm::function_ref()> Create) const { auto &Ptr = *reinterpret_cast *>(&Storage); // If an atomic load gets an initialized value, then return Storage. diff --git a/include/swift/Syntax/RawSyntax.h b/include/swift/Syntax/RawSyntax.h index 04726c91e6ade..9961d5be255fe 100644 --- a/include/swift/Syntax/RawSyntax.h +++ b/include/swift/Syntax/RawSyntax.h @@ -232,18 +232,24 @@ class RawSyntax final /// memory. unsigned ManualMemory : 1; }; - enum { NumRawSyntaxBits = bitmax(NumSyntaxKindBits, 8) + 1 }; + enum { NumRawSyntaxBits = bitmax(NumSyntaxKindBits, 8) + 1 + 1 }; // For "layout" nodes. struct { - uint64_t : bitmax(NumRawSyntaxBits, 32); + static_assert(NumRawSyntaxBits <= 32, + "Only 32 bits reserved for standard syntax bits"); + uint64_t : bitmax(NumRawSyntaxBits, 32); // align to 32 bits /// Number of children this "layout" node has. unsigned NumChildren : 32; + /// Number of bytes this node takes up spelled out in the source code + unsigned TextLength : 32; }; // For "token" nodes. struct { - uint64_t : bitmax(NumRawSyntaxBits, 16); + static_assert(NumRawSyntaxBits <= 16, + "Only 16 bits reserved for standard syntax bits"); + uint64_t : bitmax(NumRawSyntaxBits, 16); // align to 16 bits /// The kind of token this "token" node represents. unsigned TokenKind : 16; /// Number of leading trivia pieces. @@ -263,8 +269,10 @@ class RawSyntax final return isToken() ? Bits.NumLeadingTrivia + Bits.NumTrailingTrivia : 0; } + /// Constructor for creating layout nodes RawSyntax(SyntaxKind Kind, ArrayRef> Layout, SourcePresence Presence, bool ManualMemory); + /// Constructor for creating token nodes RawSyntax(tok TokKind, OwnedString Text, ArrayRef LeadingTrivia, ArrayRef TrailingTrivia, @@ -434,6 +442,22 @@ class RawSyntax final return getLayout()[Index]; } + /// Return the number of bytes this node takes when spelled out in the source + size_t getTextLength() { + // For tokens the computation of the length is fast enough to justify the + // space for caching it. For layout nodes, we cache the length to avoid + // traversing the tree + + // FIXME: Or would it be sensible to cache the size of token nodes as well? + if (isToken()) { + AbsolutePosition Pos; + accumulateAbsolutePosition(Pos); + return Pos.getOffset(); + } else { + return Bits.TextLength; + } + } + /// @} /// \name Transform routines for "layout" nodes. @@ -458,6 +482,10 @@ class RawSyntax final llvm::Optional accumulateAbsolutePosition(AbsolutePosition &Pos) const; + /// Advance the provided AbsolutePosition by the first trivia of this node. + /// Return true if we found this trivia; otherwise false. + bool accumulateLeadingTrivia(AbsolutePosition &Pos) const; + /// Print this piece of syntax recursively. void print(llvm::raw_ostream &OS, SyntaxPrintOptions Opts) const; diff --git a/include/swift/Syntax/Syntax.h b/include/swift/Syntax/Syntax.h index 7e8c1879c62f2..1e97e5842aed4 100644 --- a/include/swift/Syntax/Syntax.h +++ b/include/swift/Syntax/Syntax.h @@ -132,7 +132,10 @@ class Syntax { /// Returns the child index of this node in its parent, /// if it has one, otherwise 0. - CursorIndex getIndexInParent() const; + CursorIndex getIndexInParent() const { return getData().getIndexInParent(); } + + /// Return the number of bytes this node takes when spelled out in the source + size_t getTextLength() const { return getRaw()->getTextLength(); } /// Returns true if this syntax node represents a token. bool isToken() const; @@ -190,7 +193,21 @@ class Syntax { /// Get the absolute position of this raw syntax: its offset, line, /// and column. - AbsolutePosition getAbsolutePosition() const; + AbsolutePosition getAbsolutePosition() const { + return Data->getAbsolutePosition(); + } + + /// Get the absolute end position (exclusively) of this raw syntax: its offset, + /// line, and column. + AbsolutePosition getAbsoluteEndPosition() const { + return Data->getAbsoluteEndPosition(); + } + + /// Get the absolute position without skipping the leading trivia of this + /// node. + AbsolutePosition getAbsolutePositionWithLeadingTrivia() const { + return Data->getAbsolutePositionWithLeadingTrivia(); + } // TODO: hasSameStructureAs ? }; diff --git a/include/swift/Syntax/SyntaxData.h b/include/swift/Syntax/SyntaxData.h index cf46800a0ecdc..758da33bd5dd4 100644 --- a/include/swift/Syntax/SyntaxData.h +++ b/include/swift/Syntax/SyntaxData.h @@ -78,6 +78,9 @@ class SyntaxData final /// If there is no parent, this is 0. const CursorIndex IndexInParent; + /// Cache the absolute position of this node. + Optional PositionCache; + size_t numTrailingObjects(OverloadToken>) const { return Raw->getNumChildren(); } @@ -130,6 +133,17 @@ class SyntaxData final } public: + /// Get the node immediately before this current node. Return 0 if we cannot + /// find such node. + RC getPreviousNode() const; + + /// Get the node immediately after this current node. Return 0 if we cannot + /// find such node. + RC getNextNode() const; + + /// Get the first token node in this tree + RC getFirstToken() const; + ~SyntaxData() { for (auto &I : getChildren()) I.~AtomicCache(); @@ -235,6 +249,18 @@ class SyntaxData final }); } + /// Calculate the absolute position of this node, use cache if the cache + /// is populated. + AbsolutePosition getAbsolutePosition() const; + + /// Calculate the absolute end position of this node, use cache of the immediate + /// next node if populated. + AbsolutePosition getAbsoluteEndPosition() const; + + /// Get the absolute position without skipping the leading trivia of this + /// node. + AbsolutePosition getAbsolutePositionWithLeadingTrivia() const; + /// Returns true if the data node represents type syntax. bool isType() const; @@ -256,6 +282,9 @@ class SyntaxData final /// Dump a debug description of the syntax data for debugging to /// standard error. void dump(llvm::raw_ostream &OS) const; + + LLVM_ATTRIBUTE_DEPRECATED(void dump() const LLVM_ATTRIBUTE_USED, + "Only meant for use in the debugger"); }; } // end namespace syntax diff --git a/include/swift/Syntax/SyntaxKind.h.gyb b/include/swift/Syntax/SyntaxKind.h.gyb index d092734da62b3..066a451bdf76f 100644 --- a/include/swift/Syntax/SyntaxKind.h.gyb +++ b/include/swift/Syntax/SyntaxKind.h.gyb @@ -74,6 +74,8 @@ bool isUnknownKind(SyntaxKind Kind); SyntaxKind getUnknownKind(SyntaxKind Kind); } // end namespace syntax +bool parserShallOmitWhenNoChildren(syntax::SyntaxKind Kind); + namespace json { /// Serialization traits for SyntaxKind. @@ -108,4 +110,8 @@ struct ScalarEnumerationTraits { } // end namespace yaml } // end namespace llvm + +namespace llvm { + raw_ostream &operator<<(raw_ostream &OS, swift::syntax::SyntaxKind Kind); +} // end namespace llvm #endif // SWIFT_SYNTAX_KIND_H diff --git a/include/swift/TBDGen/TBDGen.h b/include/swift/TBDGen/TBDGen.h index ba9121cdba3d2..eca9386c05ae6 100644 --- a/include/swift/TBDGen/TBDGen.h +++ b/include/swift/TBDGen/TBDGen.h @@ -23,13 +23,23 @@ namespace swift { class FileUnit; class ModuleDecl; +/// \brief Options for controlling the exact set of symbols included in the TBD +/// output. +struct TBDGenOptions { + /// \brief Whether this compilation has multiple IRGen instances. + bool HasMultipleIGMs; + /// \brief The install-name used for the compilation. + llvm::StringRef InstallName; + /// \brief The module link name (for force loading). + llvm::StringRef ModuleLinkName; +}; + void enumeratePublicSymbols(FileUnit *module, llvm::StringSet<> &symbols, - bool hasMultipleIGMs); + TBDGenOptions &opts); void enumeratePublicSymbols(ModuleDecl *module, llvm::StringSet<> &symbols, - bool hasMultipleIGMs); + TBDGenOptions &opts); -void writeTBDFile(ModuleDecl *M, llvm::raw_ostream &os, bool hasMultipleIGMs, - llvm::StringRef installName); +void writeTBDFile(ModuleDecl *M, llvm::raw_ostream &os, TBDGenOptions &opts); } // end namespace swift diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 6ef9d089c958d..387f2aaef3f16 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -16,12 +16,14 @@ #include "swift/AST/ASTContext.h" #include "ForeignRepresentationInfo.h" +#include "SubstitutionMapStorage.h" #include "swift/AST/ConcreteDeclRef.h" #include "swift/AST/DiagnosticEngine.h" #include "swift/AST/DiagnosticsSema.h" #include "swift/AST/ExistentialLayout.h" #include "swift/AST/ForeignErrorConvention.h" #include "swift/AST/GenericEnvironment.h" +#include "swift/AST/GenericSignature.h" #include "swift/AST/GenericSignatureBuilder.h" #include "swift/AST/KnownProtocols.h" #include "swift/AST/LazyResolver.h" @@ -30,6 +32,7 @@ #include "swift/AST/ParameterList.h" #include "swift/AST/ProtocolConformance.h" #include "swift/AST/RawComment.h" +#include "swift/AST/SubstitutionMap.h" #include "swift/AST/SILLayout.h" #include "swift/AST/TypeCheckerDebugConsumer.h" #include "swift/Basic/Compiler.h" @@ -67,7 +70,6 @@ STATISTIC(NumCollapsedSpecializedProtocolConformances, #define SWIFT_GSB_EXPENSIVE_ASSERTIONS 0 LazyResolver::~LazyResolver() = default; -DelegatingLazyResolver::~DelegatingLazyResolver() = default; void ModuleLoader::anchor() {} void ClangModuleLoader::anchor() {} @@ -189,12 +191,9 @@ FOR_KNOWN_FOUNDATION_TYPES(CACHE_FOUNDATION_DECL) /// func ==(Int, Int) -> Bool FuncDecl *EqualIntDecl = nullptr; - /// func _combineHashValues(Int, Int) -> Int - FuncDecl *CombineHashValuesDecl = nullptr; + /// func _hashValue(for: H) -> Int + FuncDecl *HashValueForDecl = nullptr; - /// func _mixInt(Int) -> Int - FuncDecl *MixIntDecl = nullptr; - /// func append(Element) -> void FuncDecl *ArrayAppendElementDecl = nullptr; @@ -227,10 +226,6 @@ FOR_KNOWN_FOUNDATION_TYPES(CACHE_FOUNDATION_DECL) /// \brief Map from Swift declarations to brief comments. llvm::DenseMap BriefComments; - /// \brief Map from local declarations to their discriminators. - /// Missing entries implicitly have value 0. - llvm::DenseMap LocalDiscriminators; - /// \brief Map from declarations to foreign error conventions. /// This applies to both actual imported functions and to @objc functions. llvm::DenseMap InheritedConformances; + /// The set of substitution maps (uniqued by their storage). + llvm::FoldingSet SubstitutionMaps; + ~Arena() { for (auto &conformance : SpecializedConformances) conformance.~SpecializedProtocolConformance(); @@ -414,27 +412,53 @@ ASTContext::Implementation::~Implementation() { ConstraintCheckerArenaRAII:: ConstraintCheckerArenaRAII(ASTContext &self, llvm::BumpPtrAllocator &allocator) - : Self(self), Data(self.Impl.CurrentConstraintSolverArena.release()) + : Self(self), Data(self.getImpl().CurrentConstraintSolverArena.release()) { - Self.Impl.CurrentConstraintSolverArena.reset( + Self.getImpl().CurrentConstraintSolverArena.reset( new ASTContext::Implementation::ConstraintSolverArena(allocator)); } ConstraintCheckerArenaRAII::~ConstraintCheckerArenaRAII() { - Self.Impl.CurrentConstraintSolverArena.reset( + Self.getImpl().CurrentConstraintSolverArena.reset( (ASTContext::Implementation::ConstraintSolverArena *)Data); } static ModuleDecl *createBuiltinModule(ASTContext &ctx) { auto M = ModuleDecl::create(ctx.getIdentifier(BUILTIN_NAME), ctx); M->addFile(*new (ctx) BuiltinUnit(*M)); + M->setHasResolvedImports(); return M; } +inline ASTContext::Implementation &ASTContext::getImpl() const { + auto pointer = reinterpret_cast(const_cast(this)); + auto offset = llvm::alignAddr((void*)sizeof(*this), alignof(Implementation)); + return *reinterpret_cast(pointer + offset); +} + +void ASTContext::operator delete(void *Data) throw() { + AlignedFree(Data); +} + +ASTContext *ASTContext::get(LangOptions &langOpts, + SearchPathOptions &SearchPathOpts, + SourceManager &SourceMgr, + DiagnosticEngine &Diags) { + // If more than two data structures are concatentated, then the aggregate + // size math needs to become more complicated due to per-struct alignment + // constraints. + auto align = std::max(alignof(ASTContext), alignof(Implementation)); + auto size = llvm::alignTo(sizeof(ASTContext) + sizeof(Implementation), align); + auto mem = AlignedAlloc(size, align); + auto impl = reinterpret_cast((char*)mem + sizeof(ASTContext)); + impl = reinterpret_cast(llvm::alignAddr(impl,alignof(Implementation))); + new (impl) Implementation(); + return new (mem) ASTContext(langOpts, SearchPathOpts, SourceMgr, Diags); +} + ASTContext::ASTContext(LangOptions &langOpts, SearchPathOptions &SearchPathOpts, SourceManager &SourceMgr, DiagnosticEngine &Diags) - : Impl(*new Implementation()), - LangOpts(langOpts), + : LangOpts(langOpts), SearchPathOpts(SearchPathOpts), SourceMgr(SourceMgr), Diags(Diags), @@ -481,43 +505,43 @@ ASTContext::ASTContext(LangOptions &langOpts, SearchPathOptions &SearchPathOpts, // Record the initial set of search paths. for (StringRef path : SearchPathOpts.ImportSearchPaths) - Impl.SearchPathsSet[path] |= SearchPathKind::Import; + getImpl().SearchPathsSet[path] |= SearchPathKind::Import; for (const auto &framepath : SearchPathOpts.FrameworkSearchPaths) - Impl.SearchPathsSet[framepath.Path] |= SearchPathKind::Framework; + getImpl().SearchPathsSet[framepath.Path] |= SearchPathKind::Framework; } ASTContext::~ASTContext() { - delete &Impl; + getImpl().~Implementation(); } llvm::BumpPtrAllocator &ASTContext::getAllocator(AllocationArena arena) const { switch (arena) { case AllocationArena::Permanent: - return Impl.Allocator; + return getImpl().Allocator; case AllocationArena::ConstraintSolver: - assert(Impl.CurrentConstraintSolverArena != nullptr); - return Impl.CurrentConstraintSolverArena->Allocator; + assert(getImpl().CurrentConstraintSolverArena != nullptr); + return getImpl().CurrentConstraintSolverArena->Allocator; } llvm_unreachable("bad AllocationArena"); } syntax::SyntaxArena &ASTContext::getSyntaxArena() const { - return Impl.TheSyntaxArena; + return getImpl().TheSyntaxArena; } LazyResolver *ASTContext::getLazyResolver() const { - return Impl.Resolver; + return getImpl().Resolver; } /// Set the lazy resolver for this context. void ASTContext::setLazyResolver(LazyResolver *resolver) { if (resolver) { - assert(Impl.Resolver == nullptr && "already have a resolver"); - Impl.Resolver = resolver; + assert(getImpl().Resolver == nullptr && "already have a resolver"); + getImpl().Resolver = resolver; } else { - assert(Impl.Resolver != nullptr && "no resolver to remove"); - Impl.Resolver = resolver; + assert(getImpl().Resolver != nullptr && "no resolver to remove"); + getImpl().Resolver = resolver; } } @@ -528,7 +552,7 @@ Identifier ASTContext::getIdentifier(StringRef Str) const { if (Str.data() == nullptr) return Identifier(nullptr); - auto I = Impl.IdentifierTable.insert(std::make_pair(Str, char())).first; + auto I = getImpl().IdentifierTable.insert(std::make_pair(Str, char())).first; return Identifier(I->getKeyData()); } @@ -564,8 +588,8 @@ static NominalTypeDecl *findStdlibType(const ASTContext &ctx, StringRef name, } FuncDecl *ASTContext::getPlusFunctionOnRangeReplaceableCollection() const { - if (Impl.PlusFunctionOnRangeReplaceableCollection) { - return Impl.PlusFunctionOnRangeReplaceableCollection; + if (getImpl().PlusFunctionOnRangeReplaceableCollection) { + return getImpl().PlusFunctionOnRangeReplaceableCollection; } // Find all of the declarations with this name in the Swift module. SmallVector Results; @@ -578,17 +602,17 @@ FuncDecl *ASTContext::getPlusFunctionOnRangeReplaceableCollection() const { if (Req.getKind() == RequirementKind::Conformance && Req.getSecondType()->getNominalOrBoundGenericNominal() == getRangeReplaceableCollectionDecl()) { - Impl.PlusFunctionOnRangeReplaceableCollection = FD; + getImpl().PlusFunctionOnRangeReplaceableCollection = FD; } } } } - return Impl.PlusFunctionOnRangeReplaceableCollection; + return getImpl().PlusFunctionOnRangeReplaceableCollection; } FuncDecl *ASTContext::getPlusFunctionOnString() const { - if (Impl.PlusFunctionOnString) { - return Impl.PlusFunctionOnString; + if (getImpl().PlusFunctionOnString) { + return getImpl().PlusFunctionOnString; } // Find all of the declarations with this name in the Swift module. SmallVector Results; @@ -609,20 +633,20 @@ FuncDecl *ASTContext::getPlusFunctionOnString() const { }; if (CheckIfStringParam(ParamLists[1]->get(0)) && CheckIfStringParam(ParamLists[1]->get(1))) { - Impl.PlusFunctionOnString = FD; + getImpl().PlusFunctionOnString = FD; break; } } } - return Impl.PlusFunctionOnString; + return getImpl().PlusFunctionOnString; } #define KNOWN_STDLIB_TYPE_DECL(NAME, DECL_CLASS, NUM_GENERIC_PARAMS) \ DECL_CLASS *ASTContext::get##NAME##Decl() const { \ - if (!Impl.NAME##Decl) \ - Impl.NAME##Decl = dyn_cast_or_null( \ + if (!getImpl().NAME##Decl) \ + getImpl().NAME##Decl = dyn_cast_or_null( \ findStdlibType(*this, #NAME, NUM_GENERIC_PARAMS)); \ - return Impl.NAME##Decl; \ + return getImpl().NAME##Decl; \ } #include "swift/AST/KnownStdlibTypes.def" @@ -640,15 +664,15 @@ ProtocolDecl *ASTContext::getErrorDecl() const { } EnumElementDecl *ASTContext::getOptionalSomeDecl() const { - if (!Impl.OptionalSomeDecl) - Impl.OptionalSomeDecl = getOptionalDecl()->getUniqueElement(/*hasVal*/true); - return Impl.OptionalSomeDecl; + if (!getImpl().OptionalSomeDecl) + getImpl().OptionalSomeDecl = getOptionalDecl()->getUniqueElement(/*hasVal*/true); + return getImpl().OptionalSomeDecl; } EnumElementDecl *ASTContext::getOptionalNoneDecl() const { - if (!Impl.OptionalNoneDecl) - Impl.OptionalNoneDecl =getOptionalDecl()->getUniqueElement(/*hasVal*/false); - return Impl.OptionalNoneDecl; + if (!getImpl().OptionalNoneDecl) + getImpl().OptionalNoneDecl =getOptionalDecl()->getUniqueElement(/*hasVal*/false); + return getImpl().OptionalNoneDecl; } static VarDecl *getPointeeProperty(VarDecl *&cache, @@ -682,23 +706,23 @@ VarDecl * ASTContext::getPointerPointeePropertyDecl(PointerTypeKind ptrKind) const { switch (ptrKind) { case PTK_UnsafeMutableRawPointer: - return getPointeeProperty(Impl.UnsafeMutableRawPointerMemoryDecl, + return getPointeeProperty(getImpl().UnsafeMutableRawPointerMemoryDecl, &ASTContext::getUnsafeMutableRawPointerDecl, *this); case PTK_UnsafeRawPointer: - return getPointeeProperty(Impl.UnsafeRawPointerMemoryDecl, + return getPointeeProperty(getImpl().UnsafeRawPointerMemoryDecl, &ASTContext::getUnsafeRawPointerDecl, *this); case PTK_UnsafeMutablePointer: - return getPointeeProperty(Impl.UnsafeMutablePointerMemoryDecl, + return getPointeeProperty(getImpl().UnsafeMutablePointerMemoryDecl, &ASTContext::getUnsafeMutablePointerDecl, *this); case PTK_UnsafePointer: - return getPointeeProperty(Impl.UnsafePointerMemoryDecl, + return getPointeeProperty(getImpl().UnsafePointerMemoryDecl, &ASTContext::getUnsafePointerDecl, *this); case PTK_AutoreleasingUnsafeMutablePointer: - return getPointeeProperty(Impl.AutoreleasingUnsafeMutablePointerMemoryDecl, + return getPointeeProperty(getImpl().AutoreleasingUnsafeMutablePointerMemoryDecl, &ASTContext::getAutoreleasingUnsafeMutablePointerDecl, *this); } @@ -706,14 +730,14 @@ ASTContext::getPointerPointeePropertyDecl(PointerTypeKind ptrKind) const { } CanType ASTContext::getAnyObjectType() const { - if (Impl.AnyObjectType) { - return Impl.AnyObjectType; + if (getImpl().AnyObjectType) { + return getImpl().AnyObjectType; } - Impl.AnyObjectType = CanType( + getImpl().AnyObjectType = CanType( ProtocolCompositionType::get( *this, {}, /*HasExplicitAnyObject=*/true)); - return Impl.AnyObjectType; + return getImpl().AnyObjectType; } CanType ASTContext::getNeverType() const { @@ -724,8 +748,8 @@ CanType ASTContext::getNeverType() const { } TypeAliasDecl *ASTContext::getVoidDecl() const { - if (Impl.VoidDecl) { - return Impl.VoidDecl; + if (getImpl().VoidDecl) { + return getImpl().VoidDecl; } // Go find 'Void' in the Swift module. @@ -733,16 +757,16 @@ TypeAliasDecl *ASTContext::getVoidDecl() const { lookupInSwiftModule("Void", results); for (auto result : results) { if (auto typeAlias = dyn_cast(result)) { - Impl.VoidDecl = typeAlias; + getImpl().VoidDecl = typeAlias; return typeAlias; } } - return Impl.VoidDecl; + return getImpl().VoidDecl; } StructDecl *ASTContext::getObjCBoolDecl() const { - if (!Impl.ObjCBoolDecl) { + if (!getImpl().ObjCBoolDecl) { SmallVector results; auto *Context = const_cast(this); if (ModuleDecl *M = Context->getModuleByName(Id_ObjectiveC.str())) { @@ -751,7 +775,7 @@ StructDecl *ASTContext::getObjCBoolDecl() const { for (auto result : results) { if (auto structDecl = dyn_cast(result)) { if (structDecl->getGenericParams() == nullptr) { - Impl.ObjCBoolDecl = structDecl; + getImpl().ObjCBoolDecl = structDecl; break; } } @@ -759,12 +783,12 @@ StructDecl *ASTContext::getObjCBoolDecl() const { } } - return Impl.ObjCBoolDecl; + return getImpl().ObjCBoolDecl; } #define GET_FOUNDATION_DECL(NAME) \ ClassDecl *ASTContext::get##NAME##Decl() const { \ - if (!Impl.NAME##Decl) { \ + if (!getImpl().NAME##Decl) { \ if (ModuleDecl *M = getLoadedModule(Id_Foundation)) { \ /* Note: use unqualified lookup so we find NSError regardless of */ \ /* whether it's defined in the Foundation module or the Clang */ \ @@ -773,14 +797,14 @@ ClassDecl *ASTContext::get##NAME##Decl() const { \ if (auto type = lookup.getSingleTypeResult()) { \ if (auto classDecl = dyn_cast(type)) { \ if (classDecl->getGenericParams() == nullptr) { \ - Impl.NAME##Decl = classDecl; \ + getImpl().NAME##Decl = classDecl; \ } \ } \ } \ } \ } \ \ - return Impl.NAME##Decl; \ + return getImpl().NAME##Decl; \ } FOR_KNOWN_FOUNDATION_TYPES(GET_FOUNDATION_DECL) @@ -791,8 +815,8 @@ ProtocolDecl *ASTContext::getProtocol(KnownProtocolKind kind) const { // Check whether we've already looked for and cached this protocol. unsigned index = (unsigned)kind; assert(index < NumKnownProtocols && "Number of known protocols is wrong"); - if (Impl.KnownProtocols[index]) - return Impl.KnownProtocols[index]; + if (getImpl().KnownProtocols[index]) + return getImpl().KnownProtocols[index]; // Find all of the declarations with this name in the appropriate module. SmallVector results; @@ -819,7 +843,7 @@ ProtocolDecl *ASTContext::getProtocol(KnownProtocolKind kind) const { for (auto result : results) { if (auto protocol = dyn_cast(result)) { - Impl.KnownProtocols[index] = protocol; + getImpl().KnownProtocols[index] = protocol; return protocol; } } @@ -952,8 +976,8 @@ static FuncDecl *lookupLibraryIntrinsicFunc(const ASTContext &ctx, } FuncDecl *ASTContext::getEqualIntDecl() const { - if (Impl.EqualIntDecl) - return Impl.EqualIntDecl; + if (getImpl().EqualIntDecl) + return getImpl().EqualIntDecl; if (!getIntDecl() || !getBoolDecl()) return nullptr; @@ -971,13 +995,13 @@ FuncDecl *ASTContext::getEqualIntDecl() const { }; auto decl = lookupOperatorFunc<32>(*this, "==", intType, callback); - Impl.EqualIntDecl = decl; + getImpl().EqualIntDecl = decl; return decl; } FuncDecl *ASTContext::getGetBoolDecl(LazyResolver *resolver) const { - if (Impl.GetBoolDecl) - return Impl.GetBoolDecl; + if (getImpl().GetBoolDecl) + return getImpl().GetBoolDecl; auto callback = [&](Type inputType, Type resultType) { // Look for the signature (Builtin.Int1) -> Bool @@ -986,53 +1010,38 @@ FuncDecl *ASTContext::getGetBoolDecl(LazyResolver *resolver) const { }; auto decl = lookupLibraryIntrinsicFunc(*this, "_getBool", resolver, callback); - Impl.GetBoolDecl = decl; - return decl; -} - -FuncDecl *ASTContext::getCombineHashValuesDecl() const { - if (Impl.CombineHashValuesDecl) - return Impl.CombineHashValuesDecl; - - auto resolver = getLazyResolver(); - auto intType = getIntDecl()->getDeclaredType(); - - auto callback = [&](Type inputType, Type resultType) { - // Look for the signature (Int, Int) -> Int - auto tupleType = dyn_cast(inputType.getPointer()); - assert(tupleType); - return tupleType->getNumElements() == 2 && - tupleType->getElementType(0)->isEqual(intType) && - tupleType->getElementType(1)->isEqual(intType) && - resultType->isEqual(intType); - }; - - auto decl = lookupLibraryIntrinsicFunc( - *this, "_combineHashValues", resolver, callback); - Impl.CombineHashValuesDecl = decl; + getImpl().GetBoolDecl = decl; return decl; } -FuncDecl *ASTContext::getMixIntDecl() const { - if (Impl.MixIntDecl) - return Impl.MixIntDecl; - - auto resolver = getLazyResolver(); - auto intType = getIntDecl()->getDeclaredType(); - - auto callback = [&](Type inputType, Type resultType) { - // Look for the signature (Int) -> Int - return inputType->isEqual(intType) && resultType->isEqual(intType); - }; +FuncDecl *ASTContext::getHashValueForDecl() const { + if (getImpl().HashValueForDecl) + return getImpl().HashValueForDecl; - auto decl = lookupLibraryIntrinsicFunc(*this, "_mixInt", resolver, callback); - Impl.MixIntDecl = decl; - return decl; + SmallVector results; + lookupInSwiftModule("_hashValue", results); + for (auto result : results) { + auto *fd = dyn_cast(result); + if (!fd) + continue; + auto paramLists = fd->getParameterLists(); + if (paramLists.size() != 1 || paramLists[0]->size() != 1) + continue; + auto paramDecl = paramLists[0]->get(0); + if (paramDecl->getArgumentName() != Id_for) + continue; + auto genericParams = fd->getGenericParams(); + if (!genericParams || genericParams->size() != 1) + continue; + getImpl().HashValueForDecl = fd; + return fd; + } + return nullptr; } FuncDecl *ASTContext::getArrayAppendElementDecl() const { - if (Impl.ArrayAppendElementDecl) - return Impl.ArrayAppendElementDecl; + if (getImpl().ArrayAppendElementDecl) + return getImpl().ArrayAppendElementDecl; auto AppendFunctions = getArrayDecl()->lookupDirect(getIdentifier("append")); @@ -1071,7 +1080,7 @@ FuncDecl *ASTContext::getArrayAppendElementDecl() const { if (!FnDecl->getResultInterfaceType()->isVoid()) return nullptr; - Impl.ArrayAppendElementDecl = FnDecl; + getImpl().ArrayAppendElementDecl = FnDecl; return FnDecl; } } @@ -1079,8 +1088,8 @@ FuncDecl *ASTContext::getArrayAppendElementDecl() const { } FuncDecl *ASTContext::getArrayReserveCapacityDecl() const { - if (Impl.ArrayReserveCapacityDecl) - return Impl.ArrayReserveCapacityDecl; + if (getImpl().ArrayReserveCapacityDecl) + return getImpl().ArrayReserveCapacityDecl; auto ReserveFunctions = getArrayDecl()->lookupDirect( getIdentifier("reserveCapacityForAppend")); @@ -1131,7 +1140,7 @@ FuncDecl *ASTContext::getArrayReserveCapacityDecl() const { if (!FnDecl->getResultInterfaceType()->isVoid()) return nullptr; - Impl.ArrayReserveCapacityDecl = FnDecl; + getImpl().ArrayReserveCapacityDecl = FnDecl; return FnDecl; } } @@ -1140,8 +1149,8 @@ FuncDecl *ASTContext::getArrayReserveCapacityDecl() const { FuncDecl * ASTContext::getUnimplementedInitializerDecl(LazyResolver *resolver) const { - if (Impl.UnimplementedInitializerDecl) - return Impl.UnimplementedInitializerDecl; + if (getImpl().UnimplementedInitializerDecl) + return getImpl().UnimplementedInitializerDecl; // Look for the function. Type input, output; @@ -1153,14 +1162,14 @@ ASTContext::getUnimplementedInitializerDecl(LazyResolver *resolver) const { // FIXME: Check inputs and outputs. - Impl.UnimplementedInitializerDecl = decl; + getImpl().UnimplementedInitializerDecl = decl; return decl; } FuncDecl * ASTContext::getUndefinedDecl(LazyResolver *resolver) const { - if (Impl.UndefinedDecl) - return Impl.UndefinedDecl; + if (getImpl().UndefinedDecl) + return getImpl().UndefinedDecl; // Look for the function. CanType input, output; @@ -1168,13 +1177,13 @@ ASTContext::getUndefinedDecl(LazyResolver *resolver) const { if (!decl) return nullptr; - Impl.UndefinedDecl = decl; + getImpl().UndefinedDecl = decl; return decl; } FuncDecl *ASTContext::getIsOSVersionAtLeastDecl(LazyResolver *resolver) const { - if (Impl.IsOSVersionAtLeastDecl) - return Impl.IsOSVersionAtLeastDecl; + if (getImpl().IsOSVersionAtLeastDecl) + return getImpl().IsOSVersionAtLeastDecl; // Look for the function. Type input, output; @@ -1197,7 +1206,7 @@ FuncDecl *ASTContext::getIsOSVersionAtLeastDecl(LazyResolver *resolver) const { if (!isBuiltinInt1Type(output)) return nullptr; - Impl.IsOSVersionAtLeastDecl = decl; + getImpl().IsOSVersionAtLeastDecl = decl; return decl; } @@ -1283,10 +1292,10 @@ ASTContext::associateInfixOperators(PrecedenceGroupDecl *left, // then flip the result. if (uintptr_t(left) < uintptr_t(right)) { - return computeAssociativity(Impl.AssociativityCache, left, right); + return computeAssociativity(getImpl().AssociativityCache, left, right); } - switch (computeAssociativity(Impl.AssociativityCache, right, left)) { + switch (computeAssociativity(getImpl().AssociativityCache, right, left)) { case Associativity::Left: return Associativity::Right; case Associativity::Right: return Associativity::Left; case Associativity::None: return Associativity::None; @@ -1306,7 +1315,7 @@ static FuncDecl *findLibraryFunction(const ASTContext &ctx, FuncDecl *&cache, #define FUNC_DECL(Name, Id) \ FuncDecl *ASTContext::get##Name(LazyResolver *resolver) const { \ - return findLibraryFunction(*this, Impl.Get##Name, Id, resolver); \ + return findLibraryFunction(*this, getImpl().Get##Name, Id, resolver); \ } #include "swift/AST/KnownDecls.def" @@ -1340,8 +1349,18 @@ void ASTContext::addExternalDecl(Decl *decl) { ExternalDefinitions.insert(decl); } +void ASTContext::addSynthesizedDecl(Decl *decl) { + auto *mod = cast(decl->getDeclContext()->getModuleScopeContext()); + if (mod->getKind() == FileUnitKind::ClangModule) { + ExternalDefinitions.insert(decl); + return; + } + + cast(mod)->SynthesizedDecls.push_back(decl); +} + void ASTContext::addCleanup(std::function cleanup) { - Impl.Cleanups.push_back(std::move(cleanup)); + getImpl().Cleanups.push_back(std::move(cleanup)); } bool ASTContext::hadError() const { @@ -1357,7 +1376,7 @@ static AllocationArena getArena(RecursiveTypeProperties properties) { void ASTContext::addSearchPath(StringRef searchPath, bool isFramework, bool isSystem) { - OptionSet &loaded = Impl.SearchPathsSet[searchPath]; + OptionSet &loaded = getImpl().SearchPathsSet[searchPath]; auto kind = isFramework ? SearchPathKind::Framework : SearchPathKind::Import; if (loaded.contains(kind)) return; @@ -1375,16 +1394,16 @@ void ASTContext::addSearchPath(StringRef searchPath, bool isFramework, void ASTContext::addModuleLoader(std::unique_ptr loader, bool IsClang) { if (IsClang) { - assert(!Impl.TheClangModuleLoader && "Already have a Clang module loader"); - Impl.TheClangModuleLoader = + assert(!getImpl().TheClangModuleLoader && "Already have a Clang module loader"); + getImpl().TheClangModuleLoader = static_cast(loader.get()); } - Impl.ModuleLoaders.push_back(std::move(loader)); + getImpl().ModuleLoaders.push_back(std::move(loader)); } void ASTContext::loadExtensions(NominalTypeDecl *nominal, unsigned previousGeneration) { - for (auto &loader : Impl.ModuleLoaders) { + for (auto &loader : getImpl().ModuleLoaders) { loader->loadExtensions(nominal, previousGeneration); } } @@ -1395,7 +1414,7 @@ void ASTContext::loadObjCMethods( bool isInstanceMethod, unsigned previousGeneration, llvm::TinyPtrVector &methods) { - for (auto &loader : Impl.ModuleLoaders) { + for (auto &loader : getImpl().ModuleLoaders) { loader->loadObjCMethods(classDecl, selector, isInstanceMethod, previousGeneration, methods); } @@ -1404,7 +1423,7 @@ void ASTContext::loadObjCMethods( void ASTContext::verifyAllLoadedModules() const { #ifndef NDEBUG FrontendStatsTracer tracer(Stats, "verify-all-loaded-modules"); - for (auto &loader : Impl.ModuleLoaders) + for (auto &loader : getImpl().ModuleLoaders) loader->verifyAllModules(); for (auto &topLevelModulePair : LoadedModules) { @@ -1415,14 +1434,15 @@ void ASTContext::verifyAllLoadedModules() const { } ClangModuleLoader *ASTContext::getClangModuleLoader() const { - return Impl.TheClangModuleLoader; + return getImpl().TheClangModuleLoader; } static void recordKnownProtocol(ModuleDecl *Stdlib, StringRef Name, KnownProtocolKind Kind) { Identifier ID = Stdlib->getASTContext().getIdentifier(Name); - UnqualifiedLookup Lookup(ID, Stdlib, nullptr, /*IsKnownPrivate=*/true, - SourceLoc(), /*IsTypeLookup=*/true); + UnqualifiedLookup Lookup(ID, Stdlib, nullptr, SourceLoc(), + UnqualifiedLookup::Flags::KnownPrivate | + UnqualifiedLookup::Flags::TypeLookup); if (auto Proto = dyn_cast_or_null(Lookup.getSingleTypeResult())) Proto->setKnownProtocolKind(Kind); @@ -1459,14 +1479,14 @@ void ASTContext::registerGenericSignatureBuilder( GenericSignature *sig, GenericSignatureBuilder &&builder) { auto canSig = sig->getCanonicalSignature(); - auto known = Impl.GenericSignatureBuilders.find(canSig); - if (known != Impl.GenericSignatureBuilders.end()) { + auto known = getImpl().GenericSignatureBuilders.find(canSig); + if (known != getImpl().GenericSignatureBuilders.end()) { ++NumRegisteredGenericSignatureBuildersAlready; return; } ++NumRegisteredGenericSignatureBuilders; - Impl.GenericSignatureBuilders[canSig] = + getImpl().GenericSignatureBuilders[canSig] = llvm::make_unique(std::move(builder)); } @@ -1474,15 +1494,15 @@ GenericSignatureBuilder *ASTContext::getOrCreateGenericSignatureBuilder( CanGenericSignature sig) { // Check whether we already have a generic signature builder for this // signature and module. - auto known = Impl.GenericSignatureBuilders.find(sig); - if (known != Impl.GenericSignatureBuilders.end()) + auto known = getImpl().GenericSignatureBuilders.find(sig); + if (known != getImpl().GenericSignatureBuilders.end()) return known->second.get(); // Create a new generic signature builder with the given signature. auto builder = new GenericSignatureBuilder(*this); // Store this generic signature builder (no generic environment yet). - Impl.GenericSignatureBuilders[sig] = + getImpl().GenericSignatureBuilders[sig] = std::unique_ptr(builder); builder->addGenericSignature(sig); @@ -1553,12 +1573,12 @@ GenericSignatureBuilder *ASTContext::getOrCreateGenericSignatureBuilder( GenericEnvironment *ASTContext::getOrCreateCanonicalGenericEnvironment( GenericSignatureBuilder *builder, GenericSignature *sig) { - auto known = Impl.CanonicalGenericEnvironments.find(builder); - if (known != Impl.CanonicalGenericEnvironments.end()) + auto known = getImpl().CanonicalGenericEnvironments.find(builder); + if (known != getImpl().CanonicalGenericEnvironments.end()) return known->second; auto env = sig->createGenericEnvironment(); - Impl.CanonicalGenericEnvironments[builder] = env; + getImpl().CanonicalGenericEnvironments[builder] = env; return env; } @@ -1604,7 +1624,7 @@ static int compareSimilarAssociatedTypes(AssociatedTypeDecl *const *lhs, AssociatedTypeDecl *const *rhs) { auto lhsProto = (*lhs)->getProtocol(); auto rhsProto = (*rhs)->getProtocol(); - return ProtocolType::compareProtocols(&lhsProto, &rhsProto); + return TypeDecl::compare(lhsProto, rhsProto); } ArrayRef AssociatedTypeDecl::getOverriddenDecls() const { @@ -1615,8 +1635,8 @@ ArrayRef AssociatedTypeDecl::getOverriddenDecls() const { return { }; // Look up the overrides. - auto known = getASTContext().Impl.AssociatedTypeOverrides.find(this); - assert(known != getASTContext().Impl.AssociatedTypeOverrides.end()); + auto known = getASTContext().getImpl().AssociatedTypeOverrides.find(this); + assert(known != getASTContext().getImpl().AssociatedTypeOverrides.end()); return known->second; } @@ -1680,7 +1700,7 @@ ArrayRef AssociatedTypeDecl::setOverriddenDecls( Bits.AssociatedTypeDecl.HasOverridden = true; auto overriddenCopy = ctx.AllocateCopy(overridden); auto inserted = - ctx.Impl.AssociatedTypeOverrides.insert({this, overriddenCopy}).second; + ctx.getImpl().AssociatedTypeOverrides.insert({this, overriddenCopy}).second; (void)inserted; assert(inserted && "Already recorded associated type overrides"); return overriddenCopy; @@ -1696,7 +1716,7 @@ bool ASTContext::canImportModule(std::pair ModulePath) { return false; // Otherwise, ask the module loaders. - for (auto &importer : Impl.ModuleLoaders) { + for (auto &importer : getImpl().ModuleLoaders) { if (importer->canImportModule(ModulePath)) { return true; } @@ -1714,7 +1734,7 @@ ASTContext::getModule(ArrayRef> ModulePath) { return M; auto moduleID = ModulePath[0]; - for (auto &importer : Impl.ModuleLoaders) { + for (auto &importer : getImpl().ModuleLoaders) { if (ModuleDecl *M = importer->loadModule(moduleID.second, ModulePath)) { if (ModulePath.size() == 1 && (ModulePath[0].first == StdlibModuleName || @@ -1753,45 +1773,27 @@ ModuleDecl *ASTContext::getStdlibModule(bool loadIfAbsent) { } Optional ASTContext::getRawComment(const Decl *D) { - auto Known = Impl.RawComments.find(D); - if (Known == Impl.RawComments.end()) + auto Known = getImpl().RawComments.find(D); + if (Known == getImpl().RawComments.end()) return None; return Known->second; } void ASTContext::setRawComment(const Decl *D, RawComment RC) { - Impl.RawComments[D] = RC; + getImpl().RawComments[D] = RC; } Optional ASTContext::getBriefComment(const Decl *D) { - auto Known = Impl.BriefComments.find(D); - if (Known == Impl.BriefComments.end()) + auto Known = getImpl().BriefComments.find(D); + if (Known == getImpl().BriefComments.end()) return None; return Known->second; } void ASTContext::setBriefComment(const Decl *D, StringRef Comment) { - Impl.BriefComments[D] = Comment; -} - -unsigned ValueDecl::getLocalDiscriminator() const { - assert(getDeclContext()->isLocalContext()); - auto &discriminators = getASTContext().Impl.LocalDiscriminators; - auto it = discriminators.find(this); - if (it == discriminators.end()) - return 0; - return it->second; -} - -void ValueDecl::setLocalDiscriminator(unsigned index) { - assert(getDeclContext()->isLocalContext()); - if (!index) { - assert(!getASTContext().Impl.LocalDiscriminators.count(this)); - return; - } - getASTContext().Impl.LocalDiscriminators.insert({this, index}); + getImpl().BriefComments[D] = Comment; } NormalProtocolConformance * @@ -1828,7 +1830,7 @@ ASTContext::getConformance(Type conformingType, // Did we already record the normal conformance? void *insertPos; auto &normalConformances = - Impl.getArena(AllocationArena::Permanent).NormalConformances; + getImpl().getArena(AllocationArena::Permanent).NormalConformances; if (auto result = normalConformances.FindNodeOrInsertPos(id, insertPos)) return result; @@ -1871,16 +1873,13 @@ static ProtocolConformance *collapseSpecializedConformance( ProtocolConformance * ASTContext::getSpecializedConformance(Type type, ProtocolConformance *generic, - SubstitutionList substitutions, - bool alreadyCheckedCollapsed) { + SubstitutionMap substitutions) { // If we are performing a substitution that would get us back to the // a prior conformance (e.g., mapping into and then out of a conformance), // return the existing conformance. - if (!alreadyCheckedCollapsed) { - if (auto existing = collapseSpecializedConformance(type, generic)) { - ++NumCollapsedSpecializedProtocolConformances; - return existing; - } + if (auto existing = collapseSpecializedConformance(type, generic)) { + ++NumCollapsedSpecializedProtocolConformances; + return existing; } llvm::FoldingSetNodeID id; @@ -1891,12 +1890,11 @@ ASTContext::getSpecializedConformance(Type type, // Did we already record the specialized conformance? void *insertPos; - auto &specializedConformances = Impl.getArena(arena).SpecializedConformances; + auto &specializedConformances = getImpl().getArena(arena).SpecializedConformances; if (auto result = specializedConformances.FindNodeOrInsertPos(id, insertPos)) return result; // Build a new specialized conformance. - substitutions = AllocateCopy(substitutions, arena); auto result = new (*this, arena) SpecializedProtocolConformance(type, generic, substitutions); @@ -1904,26 +1902,6 @@ ASTContext::getSpecializedConformance(Type type, return result; } -ProtocolConformance * -ASTContext::getSpecializedConformance(Type type, - ProtocolConformance *generic, - const SubstitutionMap &subMap) { - // If we are performing a substitution that would get us back to the - // a prior conformance (e.g., mapping into and then out of a conformance), - // return the existing conformance. - if (auto existing = collapseSpecializedConformance(type, generic)) { - ++NumCollapsedSpecializedProtocolConformances; - return existing; - } - - SmallVector subs; - if (auto *genericSig = generic->getGenericSignature()) - genericSig->getSubstitutions(subMap, subs); - - return getSpecializedConformance(type, generic, subs, - /*alreadyCheckedCollapsed=*/true); -} - InheritedProtocolConformance * ASTContext::getInheritedConformance(Type type, ProtocolConformance *inherited) { llvm::FoldingSetNodeID id; @@ -1934,7 +1912,7 @@ ASTContext::getInheritedConformance(Type type, ProtocolConformance *inherited) { // Did we already record the normal protocol conformance? void *insertPos; - auto &inheritedConformances = Impl.getArena(arena).InheritedConformances; + auto &inheritedConformances = getImpl().getArena(arena).InheritedConformances; if (auto result = inheritedConformances.FindNodeOrInsertPos(id, insertPos)) return result; @@ -1948,8 +1926,8 @@ ASTContext::getInheritedConformance(Type type, ProtocolConformance *inherited) { LazyContextData *ASTContext::getOrCreateLazyContextData( const DeclContext *dc, LazyMemberLoader *lazyLoader) { - auto known = Impl.LazyContexts.find(dc); - if (known != Impl.LazyContexts.end()) { + auto known = getImpl().LazyContexts.find(dc); + if (known != getImpl().LazyContexts.end()) { // Make sure we didn't provide an incompatible lazy loader. assert(!lazyLoader || lazyLoader == known->second->loader); return known->second; @@ -1960,14 +1938,14 @@ LazyContextData *ASTContext::getOrCreateLazyContextData( if (isa(dc) || isa(dc)) { auto *contextData = Allocate(); contextData->loader = lazyLoader; - Impl.LazyContexts[dc] = contextData; + getImpl().LazyContexts[dc] = contextData; return contextData; } // Create new lazy generic context data with the given loader. auto *contextData = Allocate(); contextData->loader = lazyLoader; - Impl.LazyContexts[dc] = contextData; + getImpl().LazyContexts[dc] = contextData; return contextData; } @@ -1994,23 +1972,23 @@ LazyGenericContextData *ASTContext::getOrCreateLazyGenericContextData( void ASTContext::addDelayedConformanceDiag( NormalProtocolConformance *conformance, DelayedConformanceDiag fn) { - Impl.DelayedConformanceDiags[conformance].push_back(std::move(fn)); + getImpl().DelayedConformanceDiags[conformance].push_back(std::move(fn)); } void ASTContext:: addDelayedMissingWitnesses(NormalProtocolConformance *conformance, ArrayRef witnesses) { - auto &bucket = Impl.DelayedMissingWitnesses[conformance]; + auto &bucket = getImpl().DelayedMissingWitnesses[conformance]; bucket.insert(bucket.end(), witnesses.begin(), witnesses.end()); } std::vector ASTContext:: takeDelayedMissingWitnesses(NormalProtocolConformance *conformance) { std::vector result; - auto known = Impl.DelayedMissingWitnesses.find(conformance); - if (known != Impl.DelayedMissingWitnesses.end()) { + auto known = getImpl().DelayedMissingWitnesses.find(conformance); + if (known != getImpl().DelayedMissingWitnesses.end()) { result = std::move(known->second); - Impl.DelayedMissingWitnesses.erase(known); + getImpl().DelayedMissingWitnesses.erase(known); } return result; } @@ -2018,10 +1996,10 @@ takeDelayedMissingWitnesses(NormalProtocolConformance *conformance) { std::vector ASTContext::takeDelayedConformanceDiags(NormalProtocolConformance *conformance){ std::vector result; - auto known = Impl.DelayedConformanceDiags.find(conformance); - if (known != Impl.DelayedConformanceDiags.end()) { + auto known = getImpl().DelayedConformanceDiags.find(conformance); + if (known != getImpl().DelayedConformanceDiags.end()) { result = std::move(known->second); - Impl.DelayedConformanceDiags.erase(known); + getImpl().DelayedConformanceDiags.erase(known); } return result; } @@ -2032,25 +2010,24 @@ size_t ASTContext::getTotalMemory() const { // ExternalDefinitions ? llvm::capacity_in_bytes(CanonicalGenericTypeParamTypeNames) + // RemappedTypes ? - sizeof(Impl) + - Impl.Allocator.getTotalMemory() + - Impl.Cleanups.capacity() + - llvm::capacity_in_bytes(Impl.ModuleLoaders) + - llvm::capacity_in_bytes(Impl.RawComments) + - llvm::capacity_in_bytes(Impl.BriefComments) + - llvm::capacity_in_bytes(Impl.LocalDiscriminators) + - llvm::capacity_in_bytes(Impl.ModuleTypes) + - llvm::capacity_in_bytes(Impl.GenericParamTypes) + - // Impl.GenericFunctionTypes ? - // Impl.SILFunctionTypes ? - llvm::capacity_in_bytes(Impl.SILBlockStorageTypes) + - llvm::capacity_in_bytes(Impl.IntegerTypes) + - // Impl.ProtocolCompositionTypes ? - // Impl.BuiltinVectorTypes ? - // Impl.GenericSignatures ? - // Impl.CompoundNames ? - Impl.OpenedExistentialArchetypes.getMemorySize() + - Impl.Permanent.getTotalMemory(); + sizeof(getImpl()) + + getImpl().Allocator.getTotalMemory() + + getImpl().Cleanups.capacity() + + llvm::capacity_in_bytes(getImpl().ModuleLoaders) + + llvm::capacity_in_bytes(getImpl().RawComments) + + llvm::capacity_in_bytes(getImpl().BriefComments) + + llvm::capacity_in_bytes(getImpl().ModuleTypes) + + llvm::capacity_in_bytes(getImpl().GenericParamTypes) + + // getImpl().GenericFunctionTypes ? + // getImpl().SILFunctionTypes ? + llvm::capacity_in_bytes(getImpl().SILBlockStorageTypes) + + llvm::capacity_in_bytes(getImpl().IntegerTypes) + + // getImpl().ProtocolCompositionTypes ? + // getImpl().BuiltinVectorTypes ? + // getImpl().GenericSignatures ? + // getImpl().CompoundNames ? + getImpl().OpenedExistentialArchetypes.getMemorySize() + + getImpl().Permanent.getTotalMemory(); Size += getSolverMemory(); @@ -2060,9 +2037,9 @@ size_t ASTContext::getTotalMemory() const { size_t ASTContext::getSolverMemory() const { size_t Size = 0; - if (Impl.CurrentConstraintSolverArena) { - Size += Impl.CurrentConstraintSolverArena->getTotalMemory(); - Size += Impl.CurrentConstraintSolverArena->Allocator.getBytesAllocated(); + if (getImpl().CurrentConstraintSolverArena) { + Size += getImpl().CurrentConstraintSolverArena->getTotalMemory(); + Size += getImpl().CurrentConstraintSolverArena->Allocator.getBytesAllocated(); } return Size; @@ -2275,6 +2252,9 @@ bool swift::fixDeclarationName(InFlightDiagnostic &diag, ValueDecl *decl, bool swift::fixDeclarationObjCName(InFlightDiagnostic &diag, ValueDecl *decl, Optional targetNameOpt, bool ignoreImpliedName) { + if (decl->isImplicit()) + return false; + // Subscripts cannot be renamed, so handle them directly. if (isa(decl)) { diag.fixItInsert(decl->getAttributeInsertionLoc(/*forModifier=*/false), @@ -2349,9 +2329,12 @@ bool swift::fixDeclarationObjCName(InFlightDiagnostic &diag, ValueDecl *decl, void ASTContext::diagnoseAttrsRequiringFoundation(SourceFile &SF) { bool ImportsFoundationModule = false; - if (SF.Kind == SourceFileKind::SIL || - !LangOpts.EnableObjCAttrRequiresFoundation) - return; + if (LangOpts.EnableObjCInterop) { + if (!LangOpts.EnableObjCAttrRequiresFoundation) + return; + if (SF.Kind == SourceFileKind::SIL) + return; + } SF.forAllVisibleModules([&](ModuleDecl::ImportedModule import) { if (import.second->getName() == Id_Foundation) @@ -2362,6 +2345,9 @@ void ASTContext::diagnoseAttrsRequiringFoundation(SourceFile &SF) { return; for (auto Attr : SF.AttrsRequiringFoundation) { + if (!LangOpts.EnableObjCInterop) + Diags.diagnose(Attr->getLocation(), diag::objc_interop_disabled) + .fixItRemove(Attr->getRangeWithAt()); Diags.diagnose(Attr->getLocation(), diag::attr_used_without_required_module, Attr, Id_Foundation) @@ -2374,7 +2360,7 @@ void ASTContext::recordObjCMethod(AbstractFunctionDecl *func) { if (func->hasClangNode()) return; - Impl.ObjCMethods.push_back(func); + getImpl().ObjCMethods.push_back(func); } /// Lookup for an Objective-C method with the given selector in the @@ -2436,7 +2422,7 @@ static AbstractFunctionDecl *lookupObjCMethodInType( void AbstractFunctionDecl::setForeignErrorConvention( const ForeignErrorConvention &conv) { assert(hasThrows() && "setting error convention on non-throwing decl"); - auto &conventionsMap = getASTContext().Impl.ForeignErrorConventions; + auto &conventionsMap = getASTContext().getImpl().ForeignErrorConventions; assert(!conventionsMap.count(this) && "error convention already set"); conventionsMap.insert({this, conv}); } @@ -2447,7 +2433,7 @@ AbstractFunctionDecl::getForeignErrorConvention() const { return None; if (!hasThrows()) return None; - auto &conventionsMap = getASTContext().Impl.ForeignErrorConventions; + auto &conventionsMap = getASTContext().getImpl().ForeignErrorConventions; auto it = conventionsMap.find(this); if (it == conventionsMap.end()) return None; return it->second; @@ -2464,10 +2450,10 @@ bool ASTContext::diagnoseUnintendedObjCMethodOverrides(SourceFile &sf) { return false; }; - Impl.ObjCMethods.erase(std::remove_if(Impl.ObjCMethods.begin(), - Impl.ObjCMethods.end(), + getImpl().ObjCMethods.erase(std::remove_if(getImpl().ObjCMethods.begin(), + getImpl().ObjCMethods.end(), captureMethodInSourceFile), - Impl.ObjCMethods.end()); + getImpl().ObjCMethods.end()); // If no Objective-C methods were defined in this file, we're done. if (methods.empty()) @@ -2559,7 +2545,7 @@ bool ASTContext::diagnoseUnintendedObjCMethodOverrides(SourceFile &sf) { void ASTContext::recordObjCMethodConflict(ClassDecl *classDecl, ObjCSelector selector, bool isInstance) { - Impl.ObjCMethodConflicts.push_back(std::make_tuple(classDecl, selector, + getImpl().ObjCMethodConflicts.push_back(std::make_tuple(classDecl, selector, isInstance)); } @@ -2633,14 +2619,14 @@ static bool shouldAssociateConflictWithSourceFile( bool ASTContext::diagnoseObjCMethodConflicts(SourceFile &sf) { // If there were no conflicts, we're done. - if (Impl.ObjCMethodConflicts.empty()) + if (getImpl().ObjCMethodConflicts.empty()) return false; // Partition the set of conflicts to put the conflicts that involve // this source file at the end. auto firstLocalConflict - = std::partition(Impl.ObjCMethodConflicts.begin(), - Impl.ObjCMethodConflicts.end(), + = std::partition(getImpl().ObjCMethodConflicts.begin(), + getImpl().ObjCMethodConflicts.end(), [&](const ObjCMethodConflict &conflict) -> bool { auto decls = getObjCMethodConflictDecls(conflict); if (shouldAssociateConflictWithSourceFile(sf, decls)) { @@ -2659,7 +2645,7 @@ bool ASTContext::diagnoseObjCMethodConflicts(SourceFile &sf) { // If there were no local conflicts, we're done. unsigned numLocalConflicts - = Impl.ObjCMethodConflicts.end() - firstLocalConflict; + = getImpl().ObjCMethodConflicts.end() - firstLocalConflict; if (numLocalConflicts == 0) return false; @@ -2728,15 +2714,15 @@ bool ASTContext::diagnoseObjCMethodConflicts(SourceFile &sf) { } // Erase the local conflicts from the list of conflicts. - Impl.ObjCMethodConflicts.erase(firstLocalConflict, - Impl.ObjCMethodConflicts.end()); + getImpl().ObjCMethodConflicts.erase(firstLocalConflict, + getImpl().ObjCMethodConflicts.end()); return anyConflicts; } void ASTContext::recordObjCUnsatisfiedOptReq(DeclContext *dc, AbstractFunctionDecl *req) { - Impl.ObjCUnsatisfiedOptReqs.push_back(ObjCUnsatisfiedOptReq(dc, req)); + getImpl().ObjCUnsatisfiedOptReqs.push_back(ObjCUnsatisfiedOptReq(dc, req)); } /// Retrieve the source location associated with this declaration @@ -2750,21 +2736,21 @@ static SourceLoc getDeclContextLoc(DeclContext *dc) { bool ASTContext::diagnoseObjCUnsatisfiedOptReqConflicts(SourceFile &sf) { // If there are no unsatisfied, optional @objc requirements, we're done. - if (Impl.ObjCUnsatisfiedOptReqs.empty()) + if (getImpl().ObjCUnsatisfiedOptReqs.empty()) return false; // Partition the set of unsatisfied requirements to put the // conflicts that involve this source file at the end. auto firstLocalReq - = std::partition(Impl.ObjCUnsatisfiedOptReqs.begin(), - Impl.ObjCUnsatisfiedOptReqs.end(), + = std::partition(getImpl().ObjCUnsatisfiedOptReqs.begin(), + getImpl().ObjCUnsatisfiedOptReqs.end(), [&](const ObjCUnsatisfiedOptReq &unsatisfied) -> bool { return &sf != unsatisfied.first->getParentSourceFile(); }); // If there were no local unsatisfied requirements, we're done. unsigned numLocalReqs - = Impl.ObjCUnsatisfiedOptReqs.end() - firstLocalReq; + = getImpl().ObjCUnsatisfiedOptReqs.end() - firstLocalReq; if (numLocalReqs == 0) return false; @@ -2851,15 +2837,15 @@ bool ASTContext::diagnoseObjCUnsatisfiedOptReqConflicts(SourceFile &sf) { true, classDecl->getFullName(), protocolName); - Diags.diagnose(req, diag::protocol_requirement_here, - reqDiagInfo.second); + Diags.diagnose(req, diag::kind_declname_declared_here, + DescriptiveDeclKind::Requirement, reqDiagInfo.second); anyDiagnosed = true; } // Erase the local unsatisfied requirements from the list. - Impl.ObjCUnsatisfiedOptReqs.erase(firstLocalReq, - Impl.ObjCUnsatisfiedOptReqs.end()); + getImpl().ObjCUnsatisfiedOptReqs.erase(firstLocalReq, + getImpl().ObjCUnsatisfiedOptReqs.end()); return anyDiagnosed; } @@ -2888,9 +2874,9 @@ StringRef ASTContext::getSwiftName(KnownFoundationEntity kind) { //===----------------------------------------------------------------------===// NameAliasType::NameAliasType(TypeAliasDecl *typealias, Type parent, - const SubstitutionMap &substitutions, - Type underlying, - RecursiveTypeProperties properties) + SubstitutionMap substitutions, + Type underlying, + RecursiveTypeProperties properties) : SugarType(TypeKind::NameAlias, underlying, properties), typealias(typealias) { // Record the parent (or absence of a parent). @@ -2903,23 +2889,16 @@ NameAliasType::NameAliasType(TypeAliasDecl *typealias, Type parent, // Record the substitutions. if (auto genericSig = substitutions.getGenericSignature()) { - SmallVector flatSubs; - genericSig->getSubstitutions(substitutions, flatSubs); - Bits.NameAliasType.NumSubstitutions = flatSubs.size(); - std::copy(flatSubs.begin(), flatSubs.end(), - getTrailingObjects()); - - *getTrailingObjects() = genericSig; + Bits.NameAliasType.HasSubstitutionMap = true; + *getTrailingObjects() = substitutions; } else { - Bits.NameAliasType.NumSubstitutions = 0; + Bits.NameAliasType.HasSubstitutionMap = false; } } -NameAliasType *NameAliasType::get( - TypeAliasDecl *typealias, - Type parent, - const SubstitutionMap &substitutions, - Type underlying) { +NameAliasType *NameAliasType::get(TypeAliasDecl *typealias, Type parent, + SubstitutionMap substitutions, + Type underlying) { // Compute the recursive properties. // auto properties = underlying->getRecursiveProperties(); @@ -2949,16 +2928,13 @@ NameAliasType *NameAliasType::get( // Did we already record this type? void *insertPos; - auto &types = ctx.Impl.getArena(arena).NameAliasTypes; + auto &types = ctx.getImpl().getArena(arena).NameAliasTypes; if (auto result = types.FindNodeOrInsertPos(id, insertPos)) return result; // Build a new type. - unsigned numSubstitutions = - genericSig ? genericSig->getSubstitutionListSize() : 0; - auto size = - totalSizeToAlloc( - parent ? 1 : 0, genericSig ? 1 : 0, numSubstitutions); + auto size = totalSizeToAlloc(parent ? 1 : 0, + genericSig ? 1 : 0); auto mem = ctx.Allocate(size, alignof(NameAliasType), arena); auto result = new (mem) NameAliasType(typealias, parent, substitutions, underlying, storedProperties); @@ -2974,7 +2950,7 @@ void NameAliasType::Profile(llvm::FoldingSetNodeID &id) const { void NameAliasType::Profile( llvm::FoldingSetNodeID &id, TypeAliasDecl *typealias, - Type parent, const SubstitutionMap &substitutions, + Type parent, SubstitutionMap substitutions, Type underlying) { id.AddPointer(typealias); id.AddPointer(parent.getPointer()); @@ -2992,7 +2968,7 @@ Type ErrorType::get(Type originalType) { auto arena = getArena(originalProperties); auto &ctx = originalType->getASTContext(); - auto &entry = ctx.Impl.getArena(arena).ErrorTypesWithOriginal[originalType]; + auto &entry = ctx.getImpl().getArena(arena).ErrorTypesWithOriginal[originalType]; if (entry) return entry; void *mem = ctx.Allocate(sizeof(ErrorType) + sizeof(Type), @@ -3005,7 +2981,7 @@ Type ErrorType::get(Type originalType) { BuiltinIntegerType *BuiltinIntegerType::get(BuiltinIntegerWidth BitWidth, const ASTContext &C) { - BuiltinIntegerType *&Result = C.Impl.IntegerTypes[BitWidth]; + BuiltinIntegerType *&Result = C.getImpl().IntegerTypes[BitWidth]; if (Result == nullptr) Result = new (C, AllocationArena::Permanent) BuiltinIntegerType(BitWidth,C); return Result; @@ -3019,14 +2995,14 @@ BuiltinVectorType *BuiltinVectorType::get(const ASTContext &context, void *insertPos; if (BuiltinVectorType *vecType - = context.Impl.BuiltinVectorTypes.FindNodeOrInsertPos(id, insertPos)) + = context.getImpl().BuiltinVectorTypes.FindNodeOrInsertPos(id, insertPos)) return vecType; assert(elementType->isCanonical() && "Non-canonical builtin vector?"); BuiltinVectorType *vecTy = new (context, AllocationArena::Permanent) BuiltinVectorType(context, elementType, numElements); - context.Impl.BuiltinVectorTypes.InsertNode(vecTy, insertPos); + context.getImpl().BuiltinVectorTypes.InsertNode(vecTy, insertPos); return vecTy; } @@ -3040,7 +3016,7 @@ ParenType *ParenType::get(const ASTContext &C, Type underlying, auto properties = underlying->getRecursiveProperties(); auto arena = getArena(properties); ParenType *&Result = - C.Impl.getArena(arena).ParenTypes[{underlying, fl.toRaw()}]; + C.getImpl().getArena(arena).ParenTypes[{underlying, fl.toRaw()}]; if (Result == nullptr) { Result = new (C, arena) ParenType(underlying, properties, fl); @@ -3095,7 +3071,7 @@ Type TupleType::get(ArrayRef Fields, const ASTContext &C) { TupleType::Profile(ID, Fields); if (TupleType *TT - = C.Impl.getArena(arena).TupleTypes.FindNodeOrInsertPos(ID,InsertPos)) + = C.getImpl().getArena(arena).TupleTypes.FindNodeOrInsertPos(ID,InsertPos)) return TT; bool IsCanonical = true; // All canonical elts means this is canonical. @@ -3112,7 +3088,7 @@ Type TupleType::get(ArrayRef Fields, const ASTContext &C) { alignof(TupleType), arena); auto New = new (mem) TupleType(Fields, IsCanonical ? &C : nullptr, properties, hasInOut); - C.Impl.getArena(arena).TupleTypes.InsertNode(New, InsertPos); + C.getImpl().getArena(arena).TupleTypes.InsertNode(New, InsertPos); return New; } @@ -3237,13 +3213,13 @@ get(GenericTypeDecl *TheDecl, Type Parent, const ASTContext &C) { if (Parent) properties |= Parent->getRecursiveProperties(); auto arena = getArena(properties); - if (auto unbound = C.Impl.getArena(arena).UnboundGenericTypes + if (auto unbound = C.getImpl().getArena(arena).UnboundGenericTypes .FindNodeOrInsertPos(ID, InsertPos)) return unbound; auto result = new (C, arena) UnboundGenericType(TheDecl, Parent, C, properties); - C.Impl.getArena(arena).UnboundGenericTypes.InsertNode(result, InsertPos); + C.getImpl().getArena(arena).UnboundGenericTypes.InsertNode(result, InsertPos); return result; } @@ -3294,7 +3270,7 @@ BoundGenericType *BoundGenericType::get(NominalTypeDecl *TheDecl, void *InsertPos = nullptr; if (BoundGenericType *BGT = - C.Impl.getArena(arena).BoundGenericTypes.FindNodeOrInsertPos(ID, + C.getImpl().getArena(arena).BoundGenericTypes.FindNodeOrInsertPos(ID, InsertPos)) return BGT; @@ -3327,7 +3303,7 @@ BoundGenericType *BoundGenericType::get(NominalTypeDecl *TheDecl, } else { llvm_unreachable("Unhandled NominalTypeDecl"); } - C.Impl.getArena(arena).BoundGenericTypes.InsertNode(newType, InsertPos); + C.getImpl().getArena(arena).BoundGenericTypes.InsertNode(newType, InsertPos); return newType; } @@ -3370,11 +3346,11 @@ EnumType *EnumType::get(EnumDecl *D, Type Parent, const ASTContext &C) { void *insertPos = nullptr; if (auto enumTy - = C.Impl.getArena(arena).EnumTypes.FindNodeOrInsertPos(id, insertPos)) + = C.getImpl().getArena(arena).EnumTypes.FindNodeOrInsertPos(id, insertPos)) return enumTy; auto enumTy = new (C, arena) EnumType(D, Parent, C, properties); - C.Impl.getArena(arena).EnumTypes.InsertNode(enumTy, insertPos); + C.getImpl().getArena(arena).EnumTypes.InsertNode(enumTy, insertPos); return enumTy; } @@ -3397,11 +3373,11 @@ StructType *StructType::get(StructDecl *D, Type Parent, const ASTContext &C) { void *insertPos = nullptr; if (auto structTy - = C.Impl.getArena(arena).StructTypes.FindNodeOrInsertPos(id, insertPos)) + = C.getImpl().getArena(arena).StructTypes.FindNodeOrInsertPos(id, insertPos)) return structTy; auto structTy = new (C, arena) StructType(D, Parent, C, properties); - C.Impl.getArena(arena).StructTypes.InsertNode(structTy, insertPos); + C.getImpl().getArena(arena).StructTypes.InsertNode(structTy, insertPos); return structTy; } @@ -3424,11 +3400,11 @@ ClassType *ClassType::get(ClassDecl *D, Type Parent, const ASTContext &C) { void *insertPos = nullptr; if (auto classTy - = C.Impl.getArena(arena).ClassTypes.FindNodeOrInsertPos(id, insertPos)) + = C.getImpl().getArena(arena).ClassTypes.FindNodeOrInsertPos(id, insertPos)) return classTy; auto classTy = new (C, arena) ClassType(D, Parent, C, properties); - C.Impl.getArena(arena).ClassTypes.InsertNode(classTy, insertPos); + C.getImpl().getArena(arena).ClassTypes.InsertNode(classTy, insertPos); return classTy; } @@ -3457,7 +3433,7 @@ ProtocolCompositionType::build(const ASTContext &C, ArrayRef Members, auto arena = getArena(properties); if (auto compTy - = C.Impl.getArena(arena).ProtocolCompositionTypes + = C.getImpl().getArena(arena).ProtocolCompositionTypes .FindNodeOrInsertPos(ID, InsertPos)) return compTy; @@ -3468,7 +3444,7 @@ ProtocolCompositionType::build(const ASTContext &C, ArrayRef Members, Members, HasExplicitAnyObject, properties); - C.Impl.getArena(arena).ProtocolCompositionTypes.InsertNode(compTy, InsertPos); + C.getImpl().getArena(arena).ProtocolCompositionTypes.InsertNode(compTy, InsertPos); return compTy; } @@ -3482,7 +3458,7 @@ ReferenceStorageType *ReferenceStorageType::get(Type T, auto arena = getArena(properties); auto key = uintptr_t(T.getPointer()) | unsigned(ownership); - auto &entry = C.Impl.getArena(arena).ReferenceStorageTypes[key]; + auto &entry = C.getImpl().getArena(arena).ReferenceStorageTypes[key]; if (entry) return entry; @@ -3527,7 +3503,7 @@ MetatypeType *MetatypeType::get(Type T, Optional Repr, else reprKey = 0; - MetatypeType *&Entry = Ctx.Impl.getArena(arena).MetatypeTypes[{T, reprKey}]; + MetatypeType *&Entry = Ctx.getImpl().getArena(arena).MetatypeTypes[{T, reprKey}]; if (Entry) return Entry; return Entry = new (Ctx, arena) MetatypeType( @@ -3552,7 +3528,7 @@ ExistentialMetatypeType::get(Type T, Optional repr, else reprKey = 0; - auto &entry = ctx.Impl.getArena(arena).ExistentialMetatypeTypes[{T, reprKey}]; + auto &entry = ctx.getImpl().getArena(arena).ExistentialMetatypeTypes[{T, reprKey}]; if (entry) return entry; return entry = new (ctx, arena) ExistentialMetatypeType( @@ -3575,7 +3551,7 @@ ExistentialMetatypeType::ExistentialMetatypeType(Type T, ModuleType *ModuleType::get(ModuleDecl *M) { ASTContext &C = M->getASTContext(); - ModuleType *&Entry = C.Impl.ModuleTypes[M]; + ModuleType *&Entry = C.getImpl().ModuleTypes[M]; if (Entry) return Entry; return Entry = new (C, AllocationArena::Permanent) ModuleType(M, C); @@ -3588,7 +3564,7 @@ DynamicSelfType *DynamicSelfType::get(Type selfType, const ASTContext &ctx) { auto properties = selfType->getRecursiveProperties(); auto arena = getArena(properties); - auto &dynamicSelfTypes = ctx.Impl.getArena(arena).DynamicSelfTypes; + auto &dynamicSelfTypes = ctx.getImpl().getArena(arena).DynamicSelfTypes; auto known = dynamicSelfTypes.find(selfType); if (known != dynamicSelfTypes.end()) return known->second; @@ -3687,6 +3663,19 @@ Type AnyFunctionType::composeInput(ASTContext &ctx, ArrayRef params, return TupleType::get(elements, ctx); } +bool AnyFunctionType::equalParams(ArrayRef a, + ArrayRef b) { + if (a.size() != b.size()) + return false; + + for (unsigned i = 0, n = a.size(); i != n; ++i) { + if (a[i] != b[i]) + return false; + } + + return true; +} + FunctionType *FunctionType::get(ArrayRef params, Type result, const ExtInfo &info, bool canonicalVararg) { @@ -3703,7 +3692,7 @@ FunctionType *FunctionType::get(Type input, Type result, const ASTContext &C = input->getASTContext(); FunctionType *&Entry - = C.Impl.getArena(arena).FunctionTypes[{input, {result, attrKey} }]; + = C.getImpl().getArena(arena).FunctionTypes[{input, {result, attrKey} }]; if (Entry) return Entry; SmallVector params; @@ -3776,7 +3765,7 @@ GenericFunctionType::get(GenericSignature *sig, // Do we already have this generic function type? void *insertPos; if (auto result - = ctx.Impl.GenericFunctionTypes.FindNodeOrInsertPos(id, insertPos)) { + = ctx.getImpl().GenericFunctionTypes.FindNodeOrInsertPos(id, insertPos)) { return result; } @@ -3790,7 +3779,7 @@ GenericFunctionType::get(GenericSignature *sig, && sig->isCanonicalTypeInContext(output); if (auto result - = ctx.Impl.GenericFunctionTypes.FindNodeOrInsertPos(id, insertPos)) { + = ctx.getImpl().GenericFunctionTypes.FindNodeOrInsertPos(id, insertPos)) { return result; } @@ -3805,7 +3794,7 @@ GenericFunctionType::get(GenericSignature *sig, isCanonical ? &ctx : nullptr, properties); - ctx.Impl.GenericFunctionTypes.InsertNode(result, insertPos); + ctx.getImpl().GenericFunctionTypes.InsertNode(result, insertPos); return result; } @@ -3825,13 +3814,13 @@ GenericFunctionType::GenericFunctionType( GenericTypeParamType *GenericTypeParamType::get(unsigned depth, unsigned index, const ASTContext &ctx) { - auto known = ctx.Impl.GenericParamTypes.find({ depth, index }); - if (known != ctx.Impl.GenericParamTypes.end()) + auto known = ctx.getImpl().GenericParamTypes.find({ depth, index }); + if (known != ctx.getImpl().GenericParamTypes.end()) return known->second; auto result = new (ctx, AllocationArena::Permanent) GenericTypeParamType(depth, index, ctx); - ctx.Impl.GenericParamTypes[{depth, index}] = result; + ctx.getImpl().GenericParamTypes[{depth, index}] = result; return result; } @@ -3980,15 +3969,15 @@ SILFunctionType::SILFunctionType(GenericSignature *genericSig, ExtInfo ext, CanSILBlockStorageType SILBlockStorageType::get(CanType captureType) { ASTContext &ctx = captureType->getASTContext(); - auto found = ctx.Impl.SILBlockStorageTypes.find(captureType); - if (found != ctx.Impl.SILBlockStorageTypes.end()) + auto found = ctx.getImpl().SILBlockStorageTypes.find(captureType); + if (found != ctx.getImpl().SILBlockStorageTypes.end()) return CanSILBlockStorageType(found->second); void *mem = ctx.Allocate(sizeof(SILBlockStorageType), alignof(SILBlockStorageType)); SILBlockStorageType *storageTy = new (mem) SILBlockStorageType(captureType); - ctx.Impl.SILBlockStorageTypes.insert({captureType, storageTy}); + ctx.getImpl().SILBlockStorageTypes.insert({captureType, storageTy}); return CanSILBlockStorageType(storageTy); } @@ -4012,7 +4001,7 @@ CanSILFunctionType SILFunctionType::get(GenericSignature *genericSig, // Do we already have this generic function type? void *insertPos; if (auto result - = ctx.Impl.SILFunctionTypes.FindNodeOrInsertPos(id, insertPos)) + = ctx.getImpl().SILFunctionTypes.FindNodeOrInsertPos(id, insertPos)) return CanSILFunctionType(result); // All SILFunctionTypes are canonical. @@ -4049,7 +4038,7 @@ CanSILFunctionType SILFunctionType::get(GenericSignature *genericSig, new (mem) SILFunctionType(genericSig, ext, coroutineKind, callee, params, yields, normalResults, errorResult, ctx, properties, witnessMethodConformance); - ctx.Impl.SILFunctionTypes.InsertNode(fnType, insertPos); + ctx.getImpl().SILFunctionTypes.InsertNode(fnType, insertPos); return CanSILFunctionType(fnType); } @@ -4060,7 +4049,7 @@ ArraySliceType *ArraySliceType::get(Type base) { const ASTContext &C = base->getASTContext(); - ArraySliceType *&entry = C.Impl.getArena(arena).ArraySliceTypes[base]; + ArraySliceType *&entry = C.getImpl().getArena(arena).ArraySliceTypes[base]; if (entry) return entry; return entry = new (C, arena) ArraySliceType(C, base, properties); @@ -4074,7 +4063,7 @@ DictionaryType *DictionaryType::get(Type keyType, Type valueType) { const ASTContext &C = keyType->getASTContext(); DictionaryType *&entry - = C.Impl.getArena(arena).DictionaryTypes[{keyType, valueType}]; + = C.getImpl().getArena(arena).DictionaryTypes[{keyType, valueType}]; if (entry) return entry; return entry = new (C, arena) DictionaryType(C, keyType, valueType, @@ -4087,7 +4076,7 @@ OptionalType *OptionalType::get(Type base) { const ASTContext &C = base->getASTContext(); - OptionalType *&entry = C.Impl.getArena(arena).OptionalTypes[base]; + OptionalType *&entry = C.getImpl().getArena(arena).OptionalTypes[base]; if (entry) return entry; return entry = new (C, arena) OptionalType(C, base, properties); @@ -4104,11 +4093,11 @@ ProtocolType *ProtocolType::get(ProtocolDecl *D, Type Parent, void *insertPos = nullptr; if (auto protoTy - = C.Impl.getArena(arena).ProtocolTypes.FindNodeOrInsertPos(id, insertPos)) + = C.getImpl().getArena(arena).ProtocolTypes.FindNodeOrInsertPos(id, insertPos)) return protoTy; auto protoTy = new (C, arena) ProtocolType(D, Parent, C, properties); - C.Impl.getArena(arena).ProtocolTypes.InsertNode(protoTy, insertPos); + C.getImpl().getArena(arena).ProtocolTypes.InsertNode(protoTy, insertPos); return protoTy; } @@ -4135,7 +4124,7 @@ LValueType *LValueType::get(Type objectTy) { auto arena = getArena(properties); auto &C = objectTy->getASTContext(); - auto &entry = C.Impl.getArena(arena).LValueTypes[objectTy]; + auto &entry = C.getImpl().getArena(arena).LValueTypes[objectTy]; if (entry) return entry; @@ -4154,7 +4143,7 @@ InOutType *InOutType::get(Type objectTy) { auto arena = getArena(properties); auto &C = objectTy->getASTContext(); - auto &entry = C.Impl.getArena(arena).InOutTypes[objectTy]; + auto &entry = C.getImpl().getArena(arena).InOutTypes[objectTy]; if (entry) return entry; @@ -4170,7 +4159,7 @@ DependentMemberType *DependentMemberType::get(Type base, Identifier name) { llvm::PointerUnion stored(name); const ASTContext &ctx = base->getASTContext(); - auto *&known = ctx.Impl.getArena(arena).DependentMemberTypes[ + auto *&known = ctx.getImpl().getArena(arena).DependentMemberTypes[ {base, stored.getOpaqueValue()}]; if (!known) { const ASTContext *canonicalCtx = base->isCanonical() ? &ctx : nullptr; @@ -4189,7 +4178,7 @@ DependentMemberType *DependentMemberType::get(Type base, llvm::PointerUnion stored(assocType); const ASTContext &ctx = base->getASTContext(); - auto *&known = ctx.Impl.getArena(arena).DependentMemberTypes[ + auto *&known = ctx.getImpl().getArena(arena).DependentMemberTypes[ {base, stored.getOpaqueValue()}]; if (!known) { const ASTContext *canonicalCtx = base->isCanonical() ? &ctx : nullptr; @@ -4202,7 +4191,7 @@ DependentMemberType *DependentMemberType::get(Type base, CanArchetypeType ArchetypeType::getOpened(Type existential, Optional knownID) { auto &ctx = existential->getASTContext(); - auto &openedExistentialArchetypes = ctx.Impl.OpenedExistentialArchetypes; + auto &openedExistentialArchetypes = ctx.getImpl().OpenedExistentialArchetypes; // If we know the ID already... if (knownID) { // ... and we already have an archetype for that ID, return it. @@ -4301,6 +4290,76 @@ CapturingTypeCheckerDebugConsumer::~CapturingTypeCheckerDebugConsumer() { delete Log; } +void SubstitutionMap::Storage::Profile( + llvm::FoldingSetNodeID &id, + GenericSignature *genericSig, + ArrayRef replacementTypes, + ArrayRef conformances) { + id.AddPointer(genericSig); + if (!genericSig) return; + + // Profile those replacement types that corresponding to canonical generic + // parameters within the generic signature. + id.AddInteger(replacementTypes.size()); + auto genericParams = genericSig->getGenericParams(); + for (unsigned i : indices(genericParams)) { + auto gp = genericParams[i]; + if (genericSig->isCanonicalTypeInContext(gp->getCanonicalType())) + id.AddPointer(replacementTypes[i].getPointer()); + else + id.AddPointer(nullptr); + } + + // Conformances. + id.AddInteger(conformances.size()); + for (auto conformance : conformances) + id.AddPointer(conformance.getOpaqueValue()); +} + +SubstitutionMap::Storage *SubstitutionMap::Storage::get( + GenericSignature *genericSig, + ArrayRef replacementTypes, + ArrayRef conformances) { + // If there is no generic signature, we need no storage. + if (!genericSig) { + assert(replacementTypes.empty()); + assert(conformances.empty()); + return nullptr; + } + + // Figure out which arena this should go in. + RecursiveTypeProperties properties; + for (auto type : replacementTypes) { + if (type) + properties |= type->getRecursiveProperties(); + } + + // Profile the substitution map. + llvm::FoldingSetNodeID id; + SubstitutionMap::Storage::Profile(id, genericSig, replacementTypes, + conformances); + + auto arena = getArena(properties); + + // Did we already record this substitution map? + auto &ctx = genericSig->getASTContext(); + void *insertPos; + auto &substitutionMaps = ctx.getImpl().getArena(arena).SubstitutionMaps; + if (auto result = substitutionMaps.FindNodeOrInsertPos(id, insertPos)) + return result; + + // Allocate the appropriate amount of storage for the signature and its + // replacement types and conformances. + auto size = Storage::totalSizeToAlloc( + replacementTypes.size(), + conformances.size()); + auto mem = ctx.Allocate(size, alignof(Storage), arena); + + auto result = new (mem) Storage(genericSig, replacementTypes, conformances); + substitutionMaps.InsertNode(result, insertPos); + return result; +} + void GenericSignature::Profile(llvm::FoldingSetNodeID &ID, TypeArrayView genericParams, ArrayRef requirements) { @@ -4345,7 +4404,7 @@ GenericSignature::get(TypeArrayView params, auto &ctx = getASTContext(params, requirements); void *insertPos; - if (auto *sig = ctx.Impl.GenericSignatures.FindNodeOrInsertPos(ID, + if (auto *sig = ctx.getImpl().GenericSignatures.FindNodeOrInsertPos(ID, insertPos)) { if (isKnownCanonical) sig->CanonicalSignatureOrASTContext = &ctx; @@ -4359,7 +4418,7 @@ GenericSignature::get(TypeArrayView params, void *mem = ctx.Allocate(bytes, alignof(GenericSignature)); auto newSig = new (mem) GenericSignature(params, requirements, isKnownCanonical); - ctx.Impl.GenericSignatures.InsertNode(newSig, insertPos); + ctx.getImpl().GenericSignatures.InsertNode(newSig, insertPos); return newSig; } @@ -4396,7 +4455,7 @@ void DeclName::initialize(ASTContext &C, DeclBaseName baseName, void *insert = nullptr; if (CompoundDeclName *compoundName - = C.Impl.CompoundNames.FindNodeOrInsertPos(id, insert)) { + = C.getImpl().CompoundNames.FindNodeOrInsertPos(id, insert)) { SimpleOrCompound = compoundName; return; } @@ -4408,7 +4467,7 @@ void DeclName::initialize(ASTContext &C, DeclBaseName baseName, std::uninitialized_copy(argumentNames.begin(), argumentNames.end(), compoundName->getArgumentNames().begin()); SimpleOrCompound = compoundName; - C.Impl.CompoundNames.InsertNode(compoundName, insert); + C.getImpl().CompoundNames.InsertNode(compoundName, insert); } /// Build a compound value name given a base name and a set of argument names @@ -4486,11 +4545,11 @@ ASTContext::getForeignRepresentationInfo(NominalTypeDecl *nominal, auto info = ForeignRepresentationInfo::forTrivial(); if (allowOptional) info = ForeignRepresentationInfo::forTrivialWithOptional(); - Impl.ForeignRepresentableCache.insert({type, info}); + getImpl().ForeignRepresentableCache.insert({type, info}); } }; - if (Impl.ForeignRepresentableCache.empty()) { + if (getImpl().ForeignRepresentableCache.empty()) { // Pre-populate the foreign-representable cache with known types. if (auto stdlib = getStdlibModule()) { addTrivial(getIdentifier("OpaquePointer"), stdlib, true); @@ -4548,8 +4607,8 @@ ASTContext::getForeignRepresentationInfo(NominalTypeDecl *nominal, // Determine whether we know anything about this nominal type // yet. If we've never seen this nominal type before, or if we have // an out-of-date negative cached value, we'll have to go looking. - auto known = Impl.ForeignRepresentableCache.find(nominal); - bool wasNotFoundInCache = known == Impl.ForeignRepresentableCache.end(); + auto known = getImpl().ForeignRepresentableCache.find(nominal); + bool wasNotFoundInCache = known == getImpl().ForeignRepresentableCache.end(); // For the REPL. We might have initialized the cache above before CoreGraphics // was loaded. @@ -4566,8 +4625,8 @@ ASTContext::getForeignRepresentationInfo(NominalTypeDecl *nominal, if (nominal->getName() == typeName && wasNotFoundInCache) { if (auto module = getLoadedModule(moduleName)) { addTrivial(typeName, module, allowOptional); - known = Impl.ForeignRepresentableCache.find(nominal); - wasNotFoundInCache = known == Impl.ForeignRepresentableCache.end(); + known = getImpl().ForeignRepresentableCache.find(nominal); + wasNotFoundInCache = known == getImpl().ForeignRepresentableCache.end(); } } }; @@ -4619,7 +4678,7 @@ ASTContext::getForeignRepresentationInfo(NominalTypeDecl *nominal, result = ForeignRepresentationInfo::forNone(CurrentGeneration); // Cache the result. - known = Impl.ForeignRepresentableCache.insert({ nominal, *result }).first; + known = getImpl().ForeignRepresentableCache.insert({ nominal, *result }).first; } // Map a cache entry to a result for this specific @@ -4783,20 +4842,20 @@ Type ASTContext::getBridgedToObjC(const DeclContext *dc, Type type, } CanGenericSignature ASTContext::getSingleGenericParameterSignature() const { - if (auto theSig = Impl.SingleGenericParameterSignature) + if (auto theSig = getImpl().SingleGenericParameterSignature) return theSig; auto param = GenericTypeParamType::get(0, 0, *this); auto sig = GenericSignature::get(param, { }); auto canonicalSig = CanGenericSignature(sig); - Impl.SingleGenericParameterSignature = canonicalSig; + getImpl().SingleGenericParameterSignature = canonicalSig; return canonicalSig; } CanGenericSignature ASTContext::getExistentialSignature(CanType existential, ModuleDecl *mod) { - auto found = Impl.ExistentialSignatures.find(existential); - if (found != Impl.ExistentialSignatures.end()) + auto found = getImpl().ExistentialSignatures.find(existential); + if (found != getImpl().ExistentialSignatures.end()) return found->second; assert(existential.isExistentialType()); @@ -4814,7 +4873,7 @@ CanGenericSignature ASTContext::getExistentialSignature(CanType existential, CanGenericSignature genericSig(std::move(builder).computeGenericSignature(SourceLoc())); - auto result = Impl.ExistentialSignatures.insert( + auto result = getImpl().ExistentialSignatures.insert( std::make_pair(existential, genericSig)); assert(result.second); (void) result; @@ -4831,7 +4890,7 @@ SILLayout *SILLayout::get(ASTContext &C, // Return an existing layout if there is one. void *insertPos; - auto &Layouts = C.Impl.SILLayouts; + auto &Layouts = C.getImpl().SILLayouts; if (auto existing = Layouts.FindNodeOrInsertPos(id, insertPos)) return existing; @@ -4847,25 +4906,20 @@ SILLayout *SILLayout::get(ASTContext &C, CanSILBoxType SILBoxType::get(ASTContext &C, SILLayout *Layout, - SubstitutionList Args) { - llvm::FoldingSetNodeID id; - + SubstitutionMap Substitutions) { // Canonicalize substitutions. - SmallVector CanArgs; - Args = getCanonicalSubstitutionList(Args, CanArgs); - - Profile(id, Layout, Args); + Substitutions = Substitutions.getCanonical(); // Return an existing layout if there is one. void *insertPos; - auto &SILBoxTypes = C.Impl.SILBoxTypes; - + auto &SILBoxTypes = C.getImpl().SILBoxTypes; + llvm::FoldingSetNodeID id; + Profile(id, Layout, Substitutions); if (auto existing = SILBoxTypes.FindNodeOrInsertPos(id, insertPos)) return CanSILBoxType(existing); - void *memory = C.Allocate(totalSizeToAlloc(Args.size()), - alignof(SILBoxType)); - auto newBox = ::new (memory) SILBoxType(C, Layout, Args); + auto newBox = new (C, AllocationArena::Permanent) SILBoxType(C, Layout, + Substitutions); SILBoxTypes.InsertNode(newBox, insertPos); return CanSILBoxType(newBox); } @@ -4875,12 +4929,20 @@ CanSILBoxType SILBoxType::get(ASTContext &C, CanSILBoxType SILBoxType::get(CanType boxedType) { auto &ctx = boxedType->getASTContext(); auto singleGenericParamSignature = ctx.getSingleGenericParameterSignature(); + auto genericParam = singleGenericParamSignature->getGenericParams()[0]; auto layout = SILLayout::get(ctx, singleGenericParamSignature, - SILField(CanType(singleGenericParamSignature - ->getGenericParams()[0]), - /*mutable*/ true)); + SILField(CanType(genericParam), + /*mutable*/ true)); + + SubstitutionMap subMap = + singleGenericParamSignature->getSubstitutionMap( + [&](SubstitutableType *type) -> Type { + if (type->isEqual(genericParam)) return boxedType; - return get(boxedType->getASTContext(), layout, Substitution(boxedType, {})); + return nullptr; + }, + MakeAbstractConformanceForGenericType()); + return get(boxedType->getASTContext(), layout, subMap); } LayoutConstraint @@ -4905,7 +4967,7 @@ LayoutConstraint LayoutConstraint::getLayoutConstraint(LayoutConstraintKind Kind void *InsertPos = nullptr; if (LayoutConstraintInfo *Layout = - C.Impl.getArena(AllocationArena::Permanent) + C.getImpl().getArena(AllocationArena::Permanent) .LayoutConstraints.FindNodeOrInsertPos(ID, InsertPos)) return LayoutConstraint(Layout); @@ -4914,7 +4976,7 @@ LayoutConstraint LayoutConstraint::getLayoutConstraint(LayoutConstraintKind Kind ? new (C, AllocationArena::Permanent) LayoutConstraintInfo(Kind, SizeInBits, Alignment) : new (C, AllocationArena::Permanent) LayoutConstraintInfo(Kind); - C.Impl.getArena(AllocationArena::Permanent) + C.getImpl().getArena(AllocationArena::Permanent) .LayoutConstraints.InsertNode(New, InsertPos); return LayoutConstraint(New); } diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index 04aeaccaf7ee4..3d01c3e2252d6 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -23,6 +23,7 @@ #include "swift/AST/ParameterList.h" #include "swift/AST/ProtocolConformance.h" #include "swift/AST/TypeVisitor.h" +#include "swift/Basic/Defer.h" #include "swift/Basic/QuotedString.h" #include "swift/Basic/STLExtras.h" #include "llvm/ADT/APFloat.h" @@ -424,7 +425,7 @@ namespace { void printRec(Decl *D) { D->dump(OS, Indent + 2); } void printRec(Expr *E) { E->print(OS, Indent + 2); } - void printRec(Stmt *S) { S->print(OS, Indent + 2); } + void printRec(Stmt *S, const ASTContext &Ctx) { S->print(OS, &Ctx, Indent + 2); } void printRec(TypeRepr *T); void printRec(const Pattern *P) { PrintPattern(OS, Indent+2).visit(const_cast(P)); @@ -552,7 +553,7 @@ namespace { void printRec(Decl *D) { PrintDecl(OS, Indent + 2).visit(D); } void printRec(Expr *E) { E->print(OS, Indent+2); } - void printRec(Stmt *S) { S->print(OS, Indent+2); } + void printRec(Stmt *S, const ASTContext &Ctx) { S->print(OS, &Ctx, Indent+2); } void printRec(Pattern *P) { PrintPattern(OS, Indent+2).visit(P); } void printRec(TypeRepr *T); @@ -574,6 +575,13 @@ namespace { if (D->isImplicit()) PrintWithColorRAII(OS, DeclModifierColor) << " implicit"; + auto R = D->getSourceRange(); + if (R.isValid()) { + PrintWithColorRAII(OS, RangeColor) << " range="; + R.print(PrintWithColorRAII(OS, RangeColor).getOS(), + D->getASTContext().SourceMgr, /*PrintText=*/false); + } + if (D->TrailingSemiLoc.isValid()) PrintWithColorRAII(OS, DeclModifierColor) << " trailing_semi"; } @@ -983,7 +991,7 @@ namespace { PrintWithColorRAII(OS, ParenthesisColor) << ')'; } - void printParameterList(const ParameterList *params) { + void printParameterList(const ParameterList *params, const ASTContext *ctx = nullptr) { OS.indent(Indent); PrintWithColorRAII(OS, ParenthesisColor) << '('; PrintWithColorRAII(OS, ParameterColor) << "parameter_list"; @@ -992,6 +1000,19 @@ namespace { OS << '\n'; printParameter(P); } + + if (!ctx && params->size() != 0 && params->get(0)) + ctx = ¶ms->get(0)->getASTContext(); + + if (ctx) { + auto R = params->getSourceRange(); + if (R.isValid()) { + PrintWithColorRAII(OS, RangeColor) << " range="; + R.print(PrintWithColorRAII(OS, RangeColor).getOS(), + ctx->SourceMgr, /*PrintText=*/false); + } + } + PrintWithColorRAII(OS, ParenthesisColor) << ')'; Indent -= 2; } @@ -1000,7 +1021,7 @@ namespace { for (auto pl : D->getParameterLists()) { OS << '\n'; Indent += 2; - printParameterList(pl); + printParameterList(pl, &D->getASTContext()); Indent -= 2; } if (auto FD = dyn_cast(D)) { @@ -1017,7 +1038,7 @@ namespace { } if (auto Body = D->getBody(/*canSynthesize=*/false)) { OS << '\n'; - printRec(Body); + printRec(Body, D->getASTContext()); } } @@ -1064,12 +1085,12 @@ namespace { printCommon(TLCD, "top_level_code_decl"); if (TLCD->getBody()) { OS << "\n"; - printRec(TLCD->getBody()); + printRec(TLCD->getBody(), static_cast(TLCD)->getASTContext()); } PrintWithColorRAII(OS, ParenthesisColor) << ')'; } - void printASTNodes(const ArrayRef &Elements, StringRef Name) { + void printASTNodes(const ArrayRef &Elements, const ASTContext &Ctx, StringRef Name) { OS.indent(Indent); PrintWithColorRAII(OS, ParenthesisColor) << "("; PrintWithColorRAII(OS, ASTNodeColor) << Name; @@ -1078,7 +1099,7 @@ namespace { if (auto *SubExpr = Elt.dyn_cast()) printRec(SubExpr); else if (auto *SubStmt = Elt.dyn_cast()) - printRec(SubStmt); + printRec(SubStmt, Ctx); else printRec(Elt.get()); } @@ -1101,7 +1122,7 @@ namespace { OS << '\n'; Indent += 2; - printASTNodes(Clause.Elements, "elements"); + printASTNodes(Clause.Elements, ICD->getASTContext(), "elements"); Indent -= 2; } @@ -1348,9 +1369,11 @@ namespace { class PrintStmt : public StmtVisitor { public: raw_ostream &OS; + const ASTContext *Ctx; unsigned Indent; - PrintStmt(raw_ostream &os, unsigned indent) : OS(os), Indent(indent) { + PrintStmt(raw_ostream &os, const ASTContext *ctx, unsigned indent) + : OS(os), Ctx(ctx), Indent(indent) { } void printRec(Stmt *S) { @@ -1417,6 +1440,15 @@ class PrintStmt : public StmtVisitor { if (S->isImplicit()) OS << " implicit"; + if (Ctx) { + auto R = S->getSourceRange(); + if (R.isValid()) { + PrintWithColorRAII(OS, RangeColor) << " range="; + R.print(PrintWithColorRAII(OS, RangeColor).getOS(), + Ctx->SourceMgr, /*PrintText=*/false); + } + } + if (S->TrailingSemiLoc.isValid()) OS << " trailing_semi"; @@ -1424,13 +1456,12 @@ class PrintStmt : public StmtVisitor { } void visitBraceStmt(BraceStmt *S) { - printASTNodes(S->getElements(), "brace_stmt"); + printCommon(S, "brace_stmt"); + printASTNodes(S->getElements()); + PrintWithColorRAII(OS, ParenthesisColor) << ')'; } - void printASTNodes(const ArrayRef &Elements, StringRef Name) { - OS.indent(Indent); - PrintWithColorRAII(OS, ParenthesisColor) << "("; - PrintWithColorRAII(OS, ASTNodeColor) << Name; + void printASTNodes(const ArrayRef &Elements) { for (auto Elt : Elements) { OS << '\n'; if (auto *SubExpr = Elt.dyn_cast()) @@ -1440,7 +1471,6 @@ class PrintStmt : public StmtVisitor { else printRec(Elt.get()); } - PrintWithColorRAII(OS, ParenthesisColor) << ')'; } void visitReturnStmt(ReturnStmt *S) { @@ -1556,11 +1586,15 @@ class PrintStmt : public StmtVisitor { } void visitCaseStmt(CaseStmt *S) { printCommon(S, "case_stmt"); + if (S->hasUnknownAttr()) + OS << " @unknown"; for (const auto &LabelItem : S->getCaseLabelItems()) { OS << '\n'; OS.indent(Indent + 2); PrintWithColorRAII(OS, ParenthesisColor) << '('; PrintWithColorRAII(OS, StmtColor) << "case_label_item"; + if (LabelItem.isDefault()) + OS << " default"; if (auto *CasePattern = LabelItem.getPattern()) { OS << '\n'; printRec(CasePattern); @@ -1620,8 +1654,8 @@ void Stmt::dump() const { llvm::errs() << '\n'; } -void Stmt::print(raw_ostream &OS, unsigned Indent) const { - PrintStmt(OS, Indent).visit(const_cast(this)); +void Stmt::print(raw_ostream &OS, const ASTContext *Ctx, unsigned Indent) const { + PrintStmt(OS, Ctx, Indent).visit(const_cast(this)); } //===----------------------------------------------------------------------===// @@ -1657,6 +1691,7 @@ class PrintExpr : public ExprVisitor { Indent += 2; OS.indent(Indent); PrintWithColorRAII(OS, ParenthesisColor) << '('; + PrintWithColorRAII(OS, ExprColor) << label; OS << '\n'; printRec(E); PrintWithColorRAII(OS, ParenthesisColor) << ')'; @@ -1666,7 +1701,7 @@ class PrintExpr : public ExprVisitor { /// FIXME: This should use ExprWalker to print children. void printRec(Decl *D) { D->dump(OS, Indent + 2); } - void printRec(Stmt *S) { S->print(OS, Indent + 2); } + void printRec(Stmt *S, const ASTContext &Ctx) { S->print(OS, &Ctx, Indent + 2); } void printRec(const Pattern *P) { PrintPattern(OS, Indent+2).visit(const_cast(P)); } @@ -1715,6 +1750,15 @@ class PrintExpr : public ExprVisitor { return OS; } + + void printSemanticExpr(Expr * semanticExpr) { + if (semanticExpr == nullptr) { + return; + } + + OS << '\n'; + printRecLabeled(semanticExpr, "semantic_expr"); + } void visitErrorExpr(ErrorExpr *E) { printCommon(E, "error_expr"); @@ -1775,6 +1819,7 @@ class PrintExpr : public ExprVisitor { OS << '\n'; printRec(Segment); } + printSemanticExpr(E->getSemanticExpr()); PrintWithColorRAII(OS, ParenthesisColor) << ')'; } void visitMagicIdentifierLiteralExpr(MagicIdentifierLiteralExpr *E) { @@ -1798,6 +1843,8 @@ class PrintExpr : public ExprVisitor { printArgumentLabels(E->getArgumentLabels()); OS << "\n"; printRec(E->getArg()); + printSemanticExpr(E->getSemanticExpr()); + PrintWithColorRAII(OS, ParenthesisColor) << ')'; } void visitDiscardAssignmentExpr(DiscardAssignmentExpr *E) { @@ -1902,7 +1949,7 @@ class PrintExpr : public ExprVisitor { OS << '\n'; printRec(E->getArgument()); } - OS << "')"; + OS << ")"; } void visitDotSelfExpr(DotSelfExpr *E) { printCommon(E, "dot_self_expr"); @@ -1949,19 +1996,16 @@ class PrintExpr : public ExprVisitor { OS << '\n'; printRec(elt); } + printSemanticExpr(E->getSemanticExpr()); PrintWithColorRAII(OS, ParenthesisColor) << ')'; } void visitDictionaryExpr(DictionaryExpr *E) { printCommon(E, "dictionary_expr"); - if (auto semaE = E->getSemanticExpr()) { - OS << '\n'; - printRec(semaE); - return; - } for (auto elt : E->getElements()) { OS << '\n'; printRec(elt); } + printSemanticExpr(E->getSemanticExpr()); PrintWithColorRAII(OS, ParenthesisColor) << ')'; } void visitSubscriptExpr(SubscriptExpr *E) { @@ -2047,7 +2091,6 @@ class PrintExpr : public ExprVisitor { if (auto defaultArgsOwner = E->getDefaultArgsOwner()) { OS << " default_args_owner="; defaultArgsOwner.dump(OS); - dump(defaultArgsOwner.getSubstitutions()); } OS << "\n"; @@ -2269,14 +2312,14 @@ class PrintExpr : public ExprVisitor { if (E->getParameters()) { OS << '\n'; - PrintDecl(OS, Indent+2).printParameterList(E->getParameters()); + PrintDecl(OS, Indent+2).printParameterList(E->getParameters(), &E->getASTContext()); } OS << '\n'; if (E->hasSingleExpressionBody()) { printRec(E->getSingleExpressionBody()); } else { - printRec(E->getBody()); + printRec(E->getBody(), E->getASTContext()); } PrintWithColorRAII(OS, ParenthesisColor) << ')'; } @@ -2285,7 +2328,7 @@ class PrintExpr : public ExprVisitor { if (E->getParameters()) { OS << '\n'; - PrintDecl(OS, Indent+2).printParameterList(E->getParameters()); + PrintDecl(OS, Indent+2).printParameterList(E->getParameters(), &E->getASTContext()); } OS << '\n'; @@ -2462,6 +2505,7 @@ class PrintExpr : public ExprVisitor { OS << '\n'; printRec(ExpTyR); } + printSemanticExpr(E->getSemanticExpr()); PrintWithColorRAII(OS, ParenthesisColor) << ')'; } void visitObjCSelectorExpr(ObjCSelectorExpr *E) { @@ -2788,77 +2832,74 @@ void TypeRepr::dump() const { llvm::errs() << '\n'; } -void Substitution::dump() const { - dump(llvm::errs()); -} - -void Substitution::dump(llvm::raw_ostream &out, unsigned indent) const { - out.indent(indent); - print(out); - out << '\n'; - - for (auto &c : Conformance) { - c.dump(out, indent + 2); - } -} - -void ProtocolConformanceRef::dump() const { - dump(llvm::errs()); -} - -void ProtocolConformanceRef::dump(llvm::raw_ostream &out, - unsigned indent) const { - if (isConcrete()) { - getConcrete()->dump(out, indent); +// Recursive helpers to avoid infinite recursion for recursive protocol +// conformances. +static void dumpProtocolConformanceRec( + const ProtocolConformance *conformance, llvm::raw_ostream &out, + unsigned indent, + llvm::SmallPtrSetImpl &visited); + +static void dumpSubstitutionMapRec( + SubstitutionMap map, llvm::raw_ostream &out, + SubstitutionMap::DumpStyle style, unsigned indent, + llvm::SmallPtrSetImpl &visited); + +static void dumpProtocolConformanceRefRec( + const ProtocolConformanceRef conformance, llvm::raw_ostream &out, + unsigned indent, + llvm::SmallPtrSetImpl &visited) { + if (conformance.isConcrete()) { + dumpProtocolConformanceRec(conformance.getConcrete(), out, indent, visited); } else { out.indent(indent) << "(abstract_conformance protocol=" - << getAbstract()->getName(); + << conformance.getAbstract()->getName(); PrintWithColorRAII(out, ParenthesisColor) << ')'; - out << '\n'; - } -} - -void swift::dump(SubstitutionList subs) { - unsigned i = 0; - for (const auto &s : subs) { - llvm::errs() << i++ << ": "; - s.dump(); } } -void ProtocolConformance::dump() const { - auto &out = llvm::errs(); - dump(out); - out << '\n'; -} +static void dumpProtocolConformanceRec( + const ProtocolConformance *conformance, llvm::raw_ostream &out, + unsigned indent, + llvm::SmallPtrSetImpl &visited) { + // A recursive conformance shouldn't have its contents printed, or there's + // infinite recursion. (This also avoids printing things that occur multiple + // times in a conformance hierarchy.) + auto shouldPrintDetails = visited.insert(conformance).second; -void ProtocolConformance::dump(llvm::raw_ostream &out, unsigned indent) const { auto printCommon = [&](StringRef kind) { out.indent(indent); PrintWithColorRAII(out, ParenthesisColor) << '('; - out << kind << "_conformance type=" << getType() - << " protocol=" << getProtocol()->getName(); + out << kind << "_conformance type=" << conformance->getType() + << " protocol=" << conformance->getProtocol()->getName(); + + if (!shouldPrintDetails) + out << " (details printed above)"; }; - switch (getKind()) { + switch (conformance->getKind()) { case ProtocolConformanceKind::Normal: { - auto normal = cast(this); + auto normal = cast(conformance); printCommon("normal"); + if (!shouldPrintDetails) + break; + // Maybe print information about the conforming context? if (normal->isLazilyLoaded()) { out << " lazy"; } else { - forEachTypeWitness(nullptr, [&](const AssociatedTypeDecl *req, - Type ty, const TypeDecl *) -> bool { - out << '\n'; - out.indent(indent + 2); - PrintWithColorRAII(out, ParenthesisColor) << '('; - out << "assoc_type req=" << req->getName() << " type="; - PrintWithColorRAII(out, TypeColor) << ty; - PrintWithColorRAII(out, ParenthesisColor) << ')'; - return false; - }); + normal->forEachTypeWitness( + nullptr, + [&](const AssociatedTypeDecl *req, Type ty, + const TypeDecl *) -> bool { + out << '\n'; + out.indent(indent + 2); + PrintWithColorRAII(out, ParenthesisColor) << '('; + out << "assoc_type req=" << req->getName() << " type="; + PrintWithColorRAII(out, TypeColor) << Type(ty->getDesugaredType()); + PrintWithColorRAII(out, ParenthesisColor) << ')'; + return false; + }); normal->forEachValueWitness(nullptr, [&](const ValueDecl *req, Witness witness) { out << '\n'; @@ -2874,12 +2915,13 @@ void ProtocolConformance::dump(llvm::raw_ostream &out, unsigned indent) const { } PrintWithColorRAII(out, ParenthesisColor) << ')'; }); - } - for (auto conformance : normal->getSignatureConformances()) { - out << '\n'; - conformance.dump(out, indent + 2); + for (auto sigConf : normal->getSignatureConformances()) { + out << '\n'; + dumpProtocolConformanceRefRec(sigConf, out, indent + 2, visited); + } } + for (auto requirement : normal->getConditionalRequirements()) { out << '\n'; out.indent(indent + 2); @@ -2889,27 +2931,35 @@ void ProtocolConformance::dump(llvm::raw_ostream &out, unsigned indent) const { } case ProtocolConformanceKind::Inherited: { - auto conf = cast(this); + auto conf = cast(conformance); printCommon("inherited"); + if (!shouldPrintDetails) + break; + out << '\n'; - conf->getInheritedConformance()->dump(out, indent + 2); + dumpProtocolConformanceRec(conf->getInheritedConformance(), out, indent + 2, + visited); break; } case ProtocolConformanceKind::Specialized: { - auto conf = cast(this); + auto conf = cast(conformance); printCommon("specialized"); + if (!shouldPrintDetails) + break; + + out << '\n'; + dumpSubstitutionMapRec(conf->getSubstitutionMap(), out, + SubstitutionMap::DumpStyle::Full, indent + 2, + visited); out << '\n'; - for (auto sub : conf->getGenericSubstitutions()) { - sub.dump(out, indent + 2); - out << '\n'; - } for (auto subReq : conf->getConditionalRequirements()) { out.indent(indent + 2); subReq.dump(out); out << '\n'; } - conf->getGenericConformance()->dump(out, indent + 2); + dumpProtocolConformanceRec(conf->getGenericConformance(), out, indent + 2, + visited); break; } } @@ -2917,6 +2967,101 @@ void ProtocolConformance::dump(llvm::raw_ostream &out, unsigned indent) const { PrintWithColorRAII(out, ParenthesisColor) << ')'; } +static void dumpSubstitutionMapRec( + SubstitutionMap map, llvm::raw_ostream &out, + SubstitutionMap::DumpStyle style, unsigned indent, + llvm::SmallPtrSetImpl &visited) { + auto *genericSig = map.getGenericSignature(); + out.indent(indent); + + auto printParen = [&](char p) { + PrintWithColorRAII(out, ParenthesisColor) << p; + }; + printParen('('); + SWIFT_DEFER { printParen(')'); }; + out << "substitution_map generic_signature="; + if (genericSig == nullptr) { + out << ""; + return; + } + + genericSig->print(out); + auto genericParams = genericSig->getGenericParams(); + auto replacementTypes = + static_cast(map).getReplacementTypesBuffer(); + for (unsigned i : indices(genericParams)) { + if (style == SubstitutionMap::DumpStyle::Minimal) { + out << " "; + } else { + out << "\n"; + out.indent(indent + 2); + } + printParen('('); + out << "substitution "; + genericParams[i]->print(out); + out << " -> "; + if (replacementTypes[i]) + replacementTypes[i]->print(out); + else + out << "<>"; + printParen(')'); + } + // A minimal dump doesn't need the details about the conformances, a lot of + // that info can be inferred from the signature. + if (style == SubstitutionMap::DumpStyle::Minimal) + return; + + auto conformances = map.getConformances(); + for (const auto &req : genericSig->getRequirements()) { + if (req.getKind() != RequirementKind::Conformance) + continue; + + out << "\n"; + out.indent(indent + 2); + printParen('('); + out << "conformance type="; + req.getFirstType()->print(out); + out << "\n"; + dumpProtocolConformanceRefRec(conformances.front(), out, indent + 4, + visited); + + printParen(')'); + conformances = conformances.slice(1); + } +} + +void ProtocolConformanceRef::dump() const { + dump(llvm::errs()); + llvm::errs() << '\n'; +} + +void ProtocolConformanceRef::dump(llvm::raw_ostream &out, + unsigned indent) const { + llvm::SmallPtrSet visited; + dumpProtocolConformanceRefRec(*this, out, indent, visited); +} +void ProtocolConformance::dump() const { + auto &out = llvm::errs(); + dump(out); + out << '\n'; +} + +void ProtocolConformance::dump(llvm::raw_ostream &out, unsigned indent) const { + llvm::SmallPtrSet visited; + dumpProtocolConformanceRec(this, out, indent, visited); +} + +void SubstitutionMap::dump(llvm::raw_ostream &out, DumpStyle style, + unsigned indent) const { + llvm::SmallPtrSet visited; + dumpSubstitutionMapRec(*this, out, style, indent, visited); +} + +void SubstitutionMap::dump() const { + dump(llvm::errs()); + llvm::errs() << "\n"; +} + //===----------------------------------------------------------------------===// // Dumping for Types. //===----------------------------------------------------------------------===// diff --git a/lib/AST/ASTMangler.cpp b/lib/AST/ASTMangler.cpp index e22aa0bd68e5a..e3424e484072d 100644 --- a/lib/AST/ASTMangler.cpp +++ b/lib/AST/ASTMangler.cpp @@ -739,15 +739,31 @@ void ASTMangler::appendType(Type type) { auto aliasTy = cast(tybase); // It's not possible to mangle the context of the builtin module. - // FIXME: We also cannot yet mangle references to typealiases that - // involve generics. + // For the DWARF output we want to mangle the type alias + context, + // unless the type alias references a builtin type. TypeAliasDecl *decl = aliasTy->getDecl(); if (decl->getModuleContext() == decl->getASTContext().TheBuiltinModule) { return appendType(aliasTy->getSinglyDesugaredType()); } - // For the DWARF output we want to mangle the type alias + context, - // unless the type alias references a builtin type. + if (type->isSpecialized()) { + // Try to mangle the entire name as a substitution. + if (tryMangleSubstitution(tybase)) + return; + + appendAnyGenericType(decl); + bool isFirstArgList = true; + if (auto *nominalType = type->getAs()) { + if (nominalType->getParent()) + type = nominalType->getParent(); + } + appendBoundGenericArgs(type, isFirstArgList); + appendRetroactiveConformances(type); + appendOperator("G"); + addSubstitution(type.getPointer()); + return; + } + return appendAnyGenericType(decl); } @@ -837,20 +853,23 @@ void ASTMangler::appendType(Type type) { case TypeKind::Struct: case TypeKind::BoundGenericClass: case TypeKind::BoundGenericEnum: - case TypeKind::BoundGenericStruct: + case TypeKind::BoundGenericStruct: { + // We can't use getAnyNominal here because this can be TypeAliasDecl only + // in case of UnboundGenericType. Such mangling happens in, for instance, + // SourceKit 'cursorinfo' request. + auto *Decl = type->getAnyGeneric(); if (type->isSpecialized()) { // Try to mangle the entire name as a substitution. - if (tryMangleSubstitution(type.getPointer())) + if (tryMangleSubstitution(tybase)) return; - NominalTypeDecl *NDecl = type->getAnyNominal(); - if (isStdlibType(NDecl) && NDecl->getName().str() == "Optional") { + if (isStdlibType(Decl) && Decl->getName().str() == "Optional") { auto GenArgs = type->castTo()->getGenericArgs(); assert(GenArgs.size() == 1); appendType(GenArgs[0]); appendOperator("Sg"); } else { - appendAnyGenericType(NDecl); + appendAnyGenericType(Decl); bool isFirstArgList = true; appendBoundGenericArgs(type, isFirstArgList); appendRetroactiveConformances(type); @@ -859,8 +878,9 @@ void ASTMangler::appendType(Type type) { addSubstitution(type.getPointer()); return; } - appendAnyGenericType(tybase->getAnyNominal()); + appendAnyGenericType(Decl); return; + } case TypeKind::SILFunction: return appendImplFunctionType(cast(tybase)); @@ -982,8 +1002,8 @@ void ASTMangler::appendType(Type type) { if (auto sig = layout->getGenericSignature()) { fieldsList.clear(); - for (auto &arg : box->getGenericArgs()) { - fieldsList.push_back(TupleTypeElt(arg.getReplacement())); + for (Type type : box->getSubstitutions().getReplacementTypes()) { + fieldsList.push_back(TupleTypeElt(type)); } appendTypeList(TupleType::get(fieldsList, tybase->getASTContext()) ->getCanonicalType()); diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index d61f5f124909f..e6d1d8fc94f48 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -32,6 +32,7 @@ #include "swift/AST/Types.h" #include "swift/Basic/Defer.h" #include "swift/Basic/PrimitiveParsing.h" +#include "swift/Basic/QuotedString.h" #include "swift/Basic/STLExtras.h" #include "swift/Basic/StringExtras.h" #include "swift/Config.h" @@ -611,8 +612,8 @@ class PrintAST : public ASTVisitor { bool openBracket = true, bool closeBracket = true); void printMembers(ArrayRef members, bool needComma = false, bool openBracket = true, bool closeBracket = true); - void printNominalDeclGenericParams(NominalTypeDecl *decl); - void printNominalDeclGenericRequirements(NominalTypeDecl *decl); + void printGenericDeclGenericParams(GenericContext *decl); + void printGenericDeclGenericRequirements(GenericContext *decl); void printInherited(const Decl *decl); void printEnumElement(EnumElementDecl *elt); @@ -624,7 +625,7 @@ class PrintAST : public ASTVisitor { bool Curried, bool ArgNameIsAPIByDefault); void printParameterList(ParameterList *PL, Type paramListTy, bool isCurried, - std::function isAPINameByDefault); + llvm::function_ref isAPINameByDefault); /// \brief Print the function parameters in curried or selector style, /// to match the original function declaration. @@ -1655,13 +1656,13 @@ void PrintAST::printMembers(ArrayRef members, bool needComma, Printer << "}"; } -void PrintAST::printNominalDeclGenericParams(NominalTypeDecl *decl) { +void PrintAST::printGenericDeclGenericParams(GenericContext *decl) { if (decl->getGenericParams()) if (auto GenericSig = decl->getGenericSignature()) printGenericSignature(GenericSig, PrintParams | InnermostOnly); } -void PrintAST::printNominalDeclGenericRequirements(NominalTypeDecl *decl) { +void PrintAST::printGenericDeclGenericRequirements(GenericContext *decl) { if (decl->getGenericParams()) if (auto GenericSig = decl->getGenericSignature()) printGenericSignature(GenericSig, PrintRequirements | InnermostOnly); @@ -1776,10 +1777,7 @@ void PrintAST::printSynthesizedExtension(Type ExtendedType, printExtendedTypeName(ExtendedType, Printer, Options); printInherited(ExtDecl); - - if (ExtDecl->getGenericParams()) - if (auto *GenericSig = ExtDecl->getGenericSignature()) - printGenericSignature(GenericSig, PrintRequirements | InnermostOnly); + printGenericDeclGenericRequirements(ExtDecl); } if (Options.TypeDefinitions) { printMembersOfDecl(ExtDecl, false, @@ -1933,9 +1931,7 @@ void PrintAST::visitTypeAliasDecl(TypeAliasDecl *decl) { [&]{ Printer.printName(decl->getName()); }, [&]{ // Signature - if (decl->getGenericParams()) - if (auto *genericSig = decl->getGenericSignature()) - printGenericSignature(genericSig, PrintParams | InnermostOnly); + printGenericDeclGenericParams(decl); }); bool ShouldPrint = true; Type Ty = decl->getUnderlyingTypeLoc().getType(); @@ -1947,6 +1943,7 @@ void PrintAST::visitTypeAliasDecl(TypeAliasDecl *decl) { if (ShouldPrint) { Printer << " = "; printTypeLoc(decl->getUnderlyingTypeLoc()); + printGenericDeclGenericRequirements(decl); } } @@ -2008,10 +2005,10 @@ void PrintAST::visitEnumDecl(EnumDecl *decl) { [&]{ Printer.printName(decl->getName()); }, [&]{ // Signature - printNominalDeclGenericParams(decl); + printGenericDeclGenericParams(decl); }); printInherited(decl); - printNominalDeclGenericRequirements(decl); + printGenericDeclGenericRequirements(decl); } if (Options.TypeDefinitions) { printMembersOfDecl(decl, false, true, @@ -2036,10 +2033,10 @@ void PrintAST::visitStructDecl(StructDecl *decl) { [&]{ Printer.printName(decl->getName()); }, [&]{ // Signature - printNominalDeclGenericParams(decl); + printGenericDeclGenericParams(decl); }); printInherited(decl); - printNominalDeclGenericRequirements(decl); + printGenericDeclGenericRequirements(decl); } if (Options.TypeDefinitions) { printMembersOfDecl(decl, false, true, @@ -2064,11 +2061,11 @@ void PrintAST::visitClassDecl(ClassDecl *decl) { [&]{ Printer.printName(decl->getName()); }, [&]{ // Signature - printNominalDeclGenericParams(decl); + printGenericDeclGenericParams(decl); }); printInherited(decl); - printNominalDeclGenericRequirements(decl); + printGenericDeclGenericRequirements(decl); } if (Options.TypeDefinitions) { @@ -2314,7 +2311,7 @@ void PrintAST::printOneParameter(const ParamDecl *param, void PrintAST::printParameterList(ParameterList *PL, Type paramListTy, bool isCurried, - std::function isAPINameByDefault) { + llvm::function_ref isAPINameByDefault) { SmallVector paramFlags; if (paramListTy && !paramListTy->hasError()) { if (auto parenTy = dyn_cast(paramListTy.getPointer())) { @@ -2511,10 +2508,7 @@ void PrintAST::visitFuncDecl(FuncDecl *decl) { Printer << " "; } }, [&] { // Parameters - if (decl->isGeneric()) - if (auto *genericSig = decl->getGenericSignature()) - printGenericSignature(genericSig, PrintParams | InnermostOnly); - + printGenericDeclGenericParams(decl); printFunctionParameters(decl); }); @@ -2538,9 +2532,7 @@ void PrintAST::visitFuncDecl(FuncDecl *decl) { printTypeLoc(ResultTyLoc); Printer.printStructurePost(PrintStructureKind::FunctionReturnType); } - if (decl->isGeneric()) - if (auto *genericSig = decl->getGenericSignature()) - printGenericSignature(genericSig, PrintRequirements | InnermostOnly); + printGenericDeclGenericRequirements(decl); } if (auto BodyFunc = Options.FunctionBody) { @@ -2596,11 +2588,15 @@ void PrintAST::printEnumElement(EnumElementDecl *elt) { Printer.printStructurePost(PrintStructureKind::NumberLiteral); break; } - case ExprKind::StringLiteral: + case ExprKind::StringLiteral: { Printer.callPrintStructurePre(PrintStructureKind::StringLiteral); - Printer << "\"" << cast(raw)->getValue() << "\""; + llvm::SmallString<32> str; + llvm::raw_svector_ostream os(str); + os << QuotedString(cast(raw)->getValue()); + Printer << str; Printer.printStructurePost(PrintStructureKind::StringLiteral); break; + } default: break; // Incorrect raw value; skip it for error recovery. } @@ -2639,10 +2635,7 @@ void PrintAST::visitSubscriptDecl(SubscriptDecl *decl) { recordDeclLoc(decl, [&]{ Printer << "subscript"; }, [&] { // Parameters - if (decl->isGeneric()) - if (auto *genericSig = decl->getGenericSignature()) - printGenericSignature(genericSig, PrintParams | InnermostOnly); - + printGenericDeclGenericParams(decl); printParameterList(decl->getIndices(), decl->hasInterfaceType() ? decl->getIndicesInterfaceType() @@ -2661,9 +2654,7 @@ void PrintAST::visitSubscriptDecl(SubscriptDecl *decl) { else printTypeLoc(elementTy); Printer.printStructurePost(PrintStructureKind::FunctionReturnType); - if (decl->isGeneric()) - if (auto *genericSig = decl->getGenericSignature()) - printGenericSignature(genericSig, PrintRequirements | InnermostOnly); + printGenericDeclGenericRequirements(decl); printAccessors(decl); } @@ -2699,16 +2690,11 @@ void PrintAST::visitConstructorDecl(ConstructorDecl *decl) { break; } - if (decl->isGeneric()) - if (auto *genericSig = decl->getGenericSignature()) - printGenericSignature(genericSig, PrintParams | InnermostOnly); - + printGenericDeclGenericParams(decl); printFunctionParameters(decl); }); - if (decl->isGeneric()) - if (auto *genericSig = decl->getGenericSignature()) - printGenericSignature(genericSig, PrintRequirements | InnermostOnly); + printGenericDeclGenericRequirements(decl); if (auto BodyFunc = Options.FunctionBody) { Printer << " {"; @@ -2965,6 +2951,9 @@ void PrintAST::visitSwitchStmt(SwitchStmt *stmt) { } void PrintAST::visitCaseStmt(CaseStmt *CS) { + if (CS->hasUnknownAttr()) + Printer << "@unknown "; + if (CS->isDefault()) { Printer << tok::kw_default; } else { @@ -3063,35 +3052,6 @@ void Pattern::print(llvm::raw_ostream &OS, const PrintOptions &Options) const { Printer.printPattern(this); } -static bool isSimple(Type type) { - switch (type->getKind()) { - case TypeKind::Function: - case TypeKind::GenericFunction: - return false; - - case TypeKind::Metatype: - case TypeKind::ExistentialMetatype: - return !cast(type.getPointer())->hasRepresentation(); - - case TypeKind::Archetype: { - auto arch = type->getAs(); - return !arch->isOpenedExistential(); - } - - case TypeKind::ProtocolComposition: { - // 'Any', 'AnyObject' and single protocol compositions are simple - auto composition = type->getAs(); - auto memberCount = composition->getMembers().size(); - if (composition->hasExplicitAnyObject()) - return memberCount == 0; - return memberCount <= 1; - } - - default: - return true; - } -} - //===----------------------------------------------------------------------===// // Type Printing //===----------------------------------------------------------------------===// @@ -3122,12 +3082,12 @@ class TypePrinter : public TypeVisitor { return; } - if (!isSimple(T)) { - Printer << "("; + if (T->hasSimpleTypeRepr()) { visit(T); - Printer << ")"; } else { + Printer << "("; visit(T); + Printer << ")"; } } @@ -3759,11 +3719,11 @@ class TypePrinter : public TypeVisitor { } // The arguments to the layout, if any, do come from the outer environment. - if (!T->getGenericArgs().empty()) { + if (auto subMap = T->getSubstitutions()) { Printer << " <"; - interleave(T->getGenericArgs(), - [&](const Substitution &arg) { - visit(arg.getReplacement()); + interleave(subMap.getReplacementTypes(), + [&](Type type) { + visit(type); }, [&]{ Printer << ", "; }); @@ -4124,12 +4084,12 @@ std::string Type::getStringAsComponent(const PrintOptions &PO) const { std::string Result; llvm::raw_string_ostream OS(Result); - if (!isSimple(*this)) { - OS << "("; + if (getPointer()->hasSimpleTypeRepr()) { print(OS, PO); - OS << ")"; } else { + OS << "("; print(OS, PO); + OS << ")"; } return OS.str(); @@ -4139,12 +4099,12 @@ std::string TypeBase::getStringAsComponent(const PrintOptions &PO) const { std::string Result; llvm::raw_string_ostream OS(Result); - if (!isSimple(const_cast(this))) { - OS << "("; + if (hasSimpleTypeRepr()) { print(OS, PO); - OS << ")"; } else { + OS << "("; print(OS, PO); + OS << ")"; } return OS.str(); @@ -4202,9 +4162,10 @@ void ProtocolConformance::printName(llvm::raw_ostream &os, case ProtocolConformanceKind::Specialized: { auto spec = cast(this); os << "specialize <"; - interleave(spec->getGenericSubstitutions(), - [&](const Substitution &s) { s.print(os, PO); }, + interleave(spec->getSubstitutionMap().getReplacementTypes(), + [&](Type type) { type.print(os, PO); }, [&] { os << ", "; }); + os << "> ("; spec->getGenericConformance()->printName(os, PO); os << ")"; @@ -4220,11 +4181,6 @@ void ProtocolConformance::printName(llvm::raw_ostream &os, } } -void Substitution::print(llvm::raw_ostream &os, - const PrintOptions &PO) const { - Replacement->print(os, PO); -} - void swift::printEnumElementsAsCases( llvm::DenseSet &UnhandledElements, llvm::raw_ostream &OS) { diff --git a/lib/AST/ASTVerifier.cpp b/lib/AST/ASTVerifier.cpp index 51aa108135bbe..9d001416d3cc6 100644 --- a/lib/AST/ASTVerifier.cpp +++ b/lib/AST/ASTVerifier.cpp @@ -578,9 +578,9 @@ class Verifier : public ASTWalker { verifyChecked(type, visitedArchetypes); } - void verifyChecked( - Type type, - llvm::SmallPtrSet &visitedArchetypes) { + void + verifyChecked(Type type, + llvm::SmallPtrSetImpl &visitedArchetypes) { if (!type) return; @@ -2114,11 +2114,26 @@ class Verifier : public ASTWalker { } void verifyChecked(ValueDecl *VD) { - if (!VD->hasAccess() && !VD->getDeclContext()->isLocalContext() && - !isa(VD) && !isa(VD)) { - dumpRef(VD); - Out << " does not have access"; - abort(); + if (VD->hasAccess()) { + if (VD->getFormalAccess() == AccessLevel::Open) { + if (!isa(VD) && !VD->isPotentiallyOverridable()) { + Out << "decl cannot be 'open'\n"; + VD->dump(Out); + abort(); + } + if (VD->isFinal()) { + Out << "decl cannot be both 'open' and 'final'\n"; + VD->dump(Out); + abort(); + } + } + } else { + if (!VD->getDeclContext()->isLocalContext() && + !isa(VD) && !isa(VD)) { + dumpRef(VD); + Out << " does not have access"; + abort(); + } } // Make sure that there are no archetypes in the interface type. @@ -2401,6 +2416,10 @@ class Verifier : public ASTWalker { } } + void verifyChecked(SubstitutionMap substitutions){ + // FIXME: Check replacement types without forcing anything. + } + /// Check the given explicit protocol conformance. void verifyConformance(Decl *decl, ProtocolConformance *conformance) { PrettyStackTraceDecl debugStack("verifying protocol conformance", decl); @@ -2507,13 +2526,15 @@ class Verifier : public ASTWalker { // Check the witness substitutions. const auto &witness = normal->getWitness(req, nullptr); - if (witness.requiresSubstitution()) { - GenericEnv.push_back({witness.getSyntheticEnvironment()}); - for (const auto &sub : witness.getSubstitutions()) { - verifyChecked(sub.getReplacement()); - } + if (auto *genericEnv = witness.getSyntheticEnvironment()) + GenericEnv.push_back({genericEnv}); + + verifyChecked(witness.getRequirementToSyntheticSubs()); + verifyChecked(witness.getSubstitutions()); + + if (auto *genericEnv = witness.getSyntheticEnvironment()) { assert(GenericEnv.back().storage.dyn_cast() - == witness.getSyntheticEnvironment()); + == genericEnv); GenericEnv.pop_back(); } @@ -3394,9 +3415,8 @@ class Verifier : public ASTWalker { /// \brief Verify that the given source ranges is contained within the /// parent's source range. - void checkSourceRanges(SourceRange Current, - ASTWalker::ParentTy Parent, - std::function printEntity) { + void checkSourceRanges(SourceRange Current, ASTWalker::ParentTy Parent, + llvm::function_ref printEntity) { SourceRange Enclosing; if (Parent.isNull()) return; diff --git a/lib/AST/ASTWalker.cpp b/lib/AST/ASTWalker.cpp index 1d0861d508895..4fedbe7813fad 100644 --- a/lib/AST/ASTWalker.cpp +++ b/lib/AST/ASTWalker.cpp @@ -1705,7 +1705,7 @@ bool Traversal::visitFixedTypeRepr(FixedTypeRepr *T) { bool Traversal::visitSILBoxTypeRepr(SILBoxTypeRepr *T) { for (auto &field : T->getFields()) { - if (doIt(field.FieldType)) + if (doIt(field.getFieldType())) return true; } for (auto &arg : T->getGenericArguments()) { diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index 671b16c39fe3c..4192524b65e33 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -78,7 +78,7 @@ bool DeclAttribute::canAttributeAppearOnDecl(DeclAttrKind DK, const Decl *D) { } bool DeclAttribute::canAttributeAppearOnDeclKind(DeclAttrKind DAK, DeclKind DK) { - unsigned Options = getOptions(DAK); + auto Options = getOptions(DAK); switch (DK) { #define DECL(Id, Parent) case DeclKind::Id: return (Options & On##Id) != 0; #include "swift/AST/DeclNodes.def" @@ -370,7 +370,7 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options, case DAK_Alignment: Printer.printAttrName("@_alignment"); - Printer << "(" << cast(this)->Value << ")"; + Printer << "(" << cast(this)->getValue() << ")"; break; case DAK_SILGenName: @@ -538,7 +538,7 @@ void DeclAttribute::print(llvm::raw_ostream &OS, const Decl *D) const { print(P, PrintOptions(), D); } -unsigned DeclAttribute::getOptions(DeclAttrKind DK) { +uint64_t DeclAttribute::getOptions(DeclAttrKind DK) { switch (DK) { case DAK_Count: llvm_unreachable("getOptions needs a valid attribute"); @@ -664,17 +664,17 @@ ObjCAttr::ObjCAttr(SourceLoc atLoc, SourceRange baseRange, NameData = name->getOpaqueValue(); // Store location information. - ObjCAttrBits.HasTrailingLocationInfo = true; + Bits.ObjCAttr.HasTrailingLocationInfo = true; getTrailingLocations()[0] = parenRange.Start; getTrailingLocations()[1] = parenRange.End; std::memcpy(getTrailingLocations().slice(2).data(), nameLocs.data(), nameLocs.size() * sizeof(SourceLoc)); } else { - ObjCAttrBits.HasTrailingLocationInfo = false; + Bits.ObjCAttr.HasTrailingLocationInfo = false; } - ObjCAttrBits.ImplicitName = false; - ObjCAttrBits.Swift3Inferred = false; + Bits.ObjCAttr.ImplicitName = false; + Bits.ObjCAttr.Swift3Inferred = false; } ObjCAttr *ObjCAttr::create(ASTContext &Ctx, Optional name, @@ -865,17 +865,20 @@ SpecializeAttr::SpecializeAttr(SourceLoc atLoc, SourceRange range, bool exported, SpecializationKind kind) : DeclAttribute(DAK_Specialize, atLoc, range, /*Implicit=*/false), - numRequirements(0), trailingWhereClause(clause), - kind(kind), exported(exported) { + trailingWhereClause(clause) { + Bits.SpecializeAttr.exported = exported; + Bits.SpecializeAttr.kind = unsigned(kind); + Bits.SpecializeAttr.numRequirements = 0; } SpecializeAttr::SpecializeAttr(SourceLoc atLoc, SourceRange range, ArrayRef requirements, bool exported, SpecializationKind kind) - : DeclAttribute(DAK_Specialize, atLoc, range, /*Implicit=*/false), - numRequirements(0), kind(kind), exported(exported) { - numRequirements = requirements.size(); + : DeclAttribute(DAK_Specialize, atLoc, range, /*Implicit=*/false) { + Bits.SpecializeAttr.exported = exported; + Bits.SpecializeAttr.kind = unsigned(kind); + Bits.SpecializeAttr.numRequirements = requirements.size(); std::copy(requirements.begin(), requirements.end(), getRequirementsData()); } @@ -886,7 +889,7 @@ void SpecializeAttr::setRequirements(ASTContext &Ctx, assert(requirements.size() <= numClauseRequirements); if (!numClauseRequirements) return; - numRequirements = requirements.size(); + Bits.SpecializeAttr.numRequirements = requirements.size(); std::copy(requirements.begin(), requirements.end(), getRequirementsData()); } diff --git a/lib/AST/Builtins.cpp b/lib/AST/Builtins.cpp index 6d2a22036e977..797e39f72bd48 100644 --- a/lib/AST/Builtins.cpp +++ b/lib/AST/Builtins.cpp @@ -738,6 +738,31 @@ static ValueDecl *getGetTailAddrOperation(ASTContext &Context, Identifier Id, return builder.build(Id); } +static ValueDecl *getBeginUnpairedAccessOperation(ASTContext &Context, + Identifier Id) { + BuiltinGenericSignatureBuilder builder(Context); + builder.addParameter(makeConcrete(Context.TheRawPointerType)); + builder.addParameter(makeConcrete(Context.TheRawPointerType)); + builder.addParameter(makeMetatype(makeGenericParam(0))); + builder.setResult(makeConcrete(Context.TheEmptyTupleType)); + return builder.build(Id); +} + +static ValueDecl * +getPerformInstantaneousReadAccessOperation(ASTContext &Context, + Identifier Id) { + BuiltinGenericSignatureBuilder builder(Context); + builder.addParameter(makeConcrete(Context.TheRawPointerType)); + builder.addParameter(makeMetatype(makeGenericParam(0))); + builder.setResult(makeConcrete(Context.TheEmptyTupleType)); + return builder.build(Id); +} + +static ValueDecl *getEndUnpairedAccessOperation(ASTContext &Context, + Identifier Id) { + return getBuiltinFunction(Id, { Context.TheRawPointerType }, + Context.TheEmptyTupleType); +} static ValueDecl *getSizeOrAlignOfOperation(ASTContext &Context, Identifier Id) { BuiltinGenericSignatureBuilder builder(Context); @@ -966,6 +991,15 @@ static ValueDecl *getAddressOfOperation(ASTContext &Context, Identifier Id) { return builder.build(Id); } +static ValueDecl *getAddressOfBorrowOperation(ASTContext &Context, + Identifier Id) { + // (T) -> RawPointer + BuiltinGenericSignatureBuilder builder(Context); + builder.addParameter(makeGenericParam()); + builder.setResult(makeConcrete(Context.TheRawPointerType)); + return builder.build(Id); +} + static ValueDecl *getTypeJoinOperation(ASTContext &Context, Identifier Id) { // (T.Type, U.Type) -> V.Type BuiltinGenericSignatureBuilder builder(Context, 3); @@ -1644,6 +1678,18 @@ ValueDecl *swift::getBuiltinValueDecl(ASTContext &Context, Identifier Id) { if (Types.size() != 1) return nullptr; return getGetTailAddrOperation(Context, Id, Types[0]); + case BuiltinValueKind::PerformInstantaneousReadAccess: + if (!Types.empty()) return nullptr; + return getPerformInstantaneousReadAccessOperation(Context, Id); + + case BuiltinValueKind::BeginUnpairedModifyAccess: + if (!Types.empty()) return nullptr; + return getBeginUnpairedAccessOperation(Context, Id); + + case BuiltinValueKind::EndUnpairedAccess: + if (!Types.empty()) return nullptr; + return getEndUnpairedAccessOperation(Context, Id); + #define BUILTIN(id, name, Attrs) #define BUILTIN_BINARY_OPERATION(id, name, attrs, overload) case BuiltinValueKind::id: #include "swift/AST/Builtins.def" @@ -1777,7 +1823,11 @@ ValueDecl *swift::getBuiltinValueDecl(ASTContext &Context, Identifier Id) { case BuiltinValueKind::AddressOf: if (!Types.empty()) return nullptr; return getAddressOfOperation(Context, Id); - + + case BuiltinValueKind::AddressOfBorrow: + if (!Types.empty()) return nullptr; + return getAddressOfBorrowOperation(Context, Id); + case BuiltinValueKind::CondFail: return getCondFailOperation(Context, Id); diff --git a/lib/AST/CMakeLists.txt b/lib/AST/CMakeLists.txt index b4da5edc35c7e..1ab18cc1e68b6 100644 --- a/lib/AST/CMakeLists.txt +++ b/lib/AST/CMakeLists.txt @@ -48,8 +48,6 @@ add_swift_library(swiftAST STATIC SyntaxASTMap.cpp SILLayout.cpp Stmt.cpp - Substitution.cpp - SubstitutionList.cpp SubstitutionMap.cpp SwiftNameTranslation.cpp Type.cpp diff --git a/lib/AST/ConcreteDeclRef.cpp b/lib/AST/ConcreteDeclRef.cpp index 09a455d6dd21d..c81cf9c1624a3 100644 --- a/lib/AST/ConcreteDeclRef.cpp +++ b/lib/AST/ConcreteDeclRef.cpp @@ -25,17 +25,7 @@ #include "llvm/Support/raw_ostream.h" using namespace swift; -ConcreteDeclRef::SpecializedDeclRef * -ConcreteDeclRef::SpecializedDeclRef::create( - ASTContext &ctx, ValueDecl *decl, - SubstitutionList substitutions) { - size_t size = totalSizeToAlloc(substitutions.size()); - void *memory = ctx.Allocate(size, alignof(SpecializedDeclRef)); - return new (memory) SpecializedDeclRef(decl, substitutions); -} - -ConcreteDeclRef -ConcreteDeclRef::getOverriddenDecl(ASTContext &ctx) const { +ConcreteDeclRef ConcreteDeclRef::getOverriddenDecl() const { auto *derivedDecl = getDecl(); auto *baseDecl = derivedDecl->getOverriddenDecl(); @@ -44,16 +34,15 @@ ConcreteDeclRef::getOverriddenDecl(ASTContext &ctx) const { auto *derivedSig = derivedDecl->getInnermostDeclContext() ->getGenericSignatureOfContext(); - SmallVector subs = {}; + SubstitutionMap subs; if (baseSig) { Optional derivedSubMap; if (derivedSig) - derivedSubMap = derivedSig->getSubstitutionMap(getSubstitutions()); - auto subMap = SubstitutionMap::getOverrideSubstitutions( - baseDecl, derivedDecl, derivedSubMap); - baseSig->getSubstitutions(subMap, subs); + derivedSubMap = getSubstitutions(); + subs = SubstitutionMap::getOverrideSubstitutions(baseDecl, derivedDecl, + derivedSubMap); } - return ConcreteDeclRef(ctx, baseDecl, subs); + return ConcreteDeclRef(baseDecl, subs); } void ConcreteDeclRef::dump(raw_ostream &os) { @@ -67,26 +56,7 @@ void ConcreteDeclRef::dump(raw_ostream &os) { // If specialized, dump the substitutions. if (isSpecialized()) { os << " [with "; - interleave(getSubstitutions(), - [&](const Substitution &sub) { - os << sub.getReplacement().getString(); - - if (sub.getConformances().size()) { - os << '['; - interleave(sub.getConformances(), - [&](ProtocolConformanceRef c) { - if (c.isConcrete()) { - c.getConcrete()->printName(os); - } else { - os << "abstract:" - << c.getAbstract()->getName(); - } - }, - [&] { os << ", "; }); - os << ']'; - } - }, - [&] { os << ", "; }); + getSubstitutions().dump(os, SubstitutionMap::DumpStyle::Minimal); os << ']'; } } diff --git a/lib/AST/ConformanceLookupTable.cpp b/lib/AST/ConformanceLookupTable.cpp index ab66f22f4988a..e3c9b9d758420 100644 --- a/lib/AST/ConformanceLookupTable.cpp +++ b/lib/AST/ConformanceLookupTable.cpp @@ -184,6 +184,8 @@ void ConformanceLookupTable::forEachInStage(ConformanceStage stage, : nominal->FirstExtension) { lastProcessed.setPointer(next); + SmallVector protocols; + // If we have conformances we can load, do so. // FIXME: This could be lazier. auto loader = next->takeConformanceLoader(); @@ -191,6 +193,9 @@ void ConformanceLookupTable::forEachInStage(ConformanceStage stage, SmallVector conformances; loader.first->loadAllConformances(next, loader.second, conformances); loadAllConformances(next, conformances); + for (auto conf : conformances) { + protocols.push_back({SourceLoc(), conf->getProtocol()}); + } } else if (next->getParentSourceFile()) { if (!resolver) { // We have a parsed extension that we can't resolve well enough to @@ -202,10 +207,10 @@ void ConformanceLookupTable::forEachInStage(ConformanceStage stage, // Resolve this extension. delayedExtensionDecls.remove(next); - resolver->resolveExtension(next); + resolver->resolveExtensionForConformanceConstruction(next, protocols); } - extensionFunc(next); + extensionFunc(next, protocols); } // If we delayed any extension declarations because we didn't have a resolver @@ -216,8 +221,10 @@ void ConformanceLookupTable::forEachInStage(ConformanceStage stage, auto ext = delayedExtensionDecls.back(); delayedExtensionDecls.remove(ext); - resolver->resolveExtension(ext); - extensionFunc(ext); + SmallVector protocols; + + resolver->resolveExtensionForConformanceConstruction(ext, protocols); + extensionFunc(ext, protocols); } } } @@ -284,23 +291,23 @@ void ConformanceLookupTable::updateLookupTable(NominalTypeDecl *nominal, switch (stage) { case ConformanceStage::RecordedExplicit: // Record all of the explicit conformances. - forEachInStage(stage, nominal, resolver, - [&](NominalTypeDecl *nominal) { - if (resolver) - resolver->resolveInheritanceClause(nominal); - - addProtocols(nominal->getInherited(), - ConformanceSource::forExplicit(nominal), - resolver); - }, - [&](ExtensionDecl *ext) { - if (resolver) - resolver->resolveInheritanceClause(ext); - - addProtocols(ext->getInherited(), - ConformanceSource::forExplicit(ext), - resolver); - }); + forEachInStage( + stage, nominal, resolver, + [&](NominalTypeDecl *nominal) { + if (resolver) + resolver->resolveInheritanceClause(nominal); + + addProtocols(nominal->getInherited(), + ConformanceSource::forExplicit(nominal), resolver); + }, + [&](ExtensionDecl *ext, + ArrayRef protos) { + // The extension decl may not be validated, so we can't use + // its inherited protocols directly. + auto source = ConformanceSource::forExplicit(ext); + for (auto locAndProto : protos) + addProtocol(locAndProto.second, locAndProto.first, source); + }); break; case ConformanceStage::Inherited: @@ -333,15 +340,17 @@ void ConformanceLookupTable::updateLookupTable(NominalTypeDecl *nominal, auto circularSuperclass = superclassDecl->getSuperclassDecl(); do { - forEachInStage(stage, superclassDecl, resolver, - [&](NominalTypeDecl *superclass) { - inheritConformances(classDecl, superclassDecl, - nullptr, resolver); - }, - [&](ExtensionDecl *ext) { - inheritConformances(classDecl, superclassDecl, ext, - resolver); - }); + forEachInStage( + stage, superclassDecl, resolver, + [&](NominalTypeDecl *superclass) { + inheritConformances(classDecl, superclassDecl, nullptr, + resolver); + }, + [&](ExtensionDecl *ext, + ArrayRef protos) { + (void)protos; + inheritConformances(classDecl, superclassDecl, ext, resolver); + }); superclassDecl = superclassDecl->getSuperclassDecl(); if (circularSuperclass) circularSuperclass = circularSuperclass->getSuperclassDecl(); @@ -358,13 +367,16 @@ void ConformanceLookupTable::updateLookupTable(NominalTypeDecl *nominal, updateLookupTable(nominal, ConformanceStage::Inherited, resolver); // Expand inherited conformances. - forEachInStage(stage, nominal, resolver, - [&](NominalTypeDecl *nominal) { - expandImpliedConformances(nominal, nominal, resolver); - }, - [&](ExtensionDecl *ext) { - expandImpliedConformances(nominal, ext, resolver); - }); + forEachInStage( + stage, nominal, resolver, + [&](NominalTypeDecl *nominal) { + expandImpliedConformances(nominal, nominal, resolver); + }, + [&](ExtensionDecl *ext, + ArrayRef protos) { + (void)protos; + expandImpliedConformances(nominal, ext, resolver); + }); break; case ConformanceStage::Resolved: @@ -376,13 +388,12 @@ void ConformanceLookupTable::updateLookupTable(NominalTypeDecl *nominal, /// us to compute conformances again. bool anyChanged = false; forEachInStage(stage, nominal, resolver, - [&](NominalTypeDecl *nominal) { - anyChanged = true; - }, - [&](ExtensionDecl *ext) { + [&](NominalTypeDecl *nominal) { anyChanged = true; }, + [&](ExtensionDecl *ext, + ArrayRef) { anyChanged = true; }); - + if (anyChanged) { // Compute the conformances for each protocol. bool anySuperseded = false; @@ -446,7 +457,7 @@ bool ConformanceLookupTable::addProtocol(ProtocolDecl *protocol, SourceLoc loc, case ConformanceEntryKind::Implied: // Ignore implied circular protocol inheritance - if (existingEntry->getProtocol() == protocol) + if (existingEntry->getDeclContext() == dc) return false; // An implied conformance is better than a synthesized one, unless @@ -631,6 +642,24 @@ ConformanceLookupTable::Ordering ConformanceLookupTable::compareConformances( } } + // Prefer the least conditional implier, which we approximate by seeing if one + // of the contexts syntactically has no generic requirements. This misses + // redundant cases like `struct Foo {} extension Foo: P where T: P {}` + // (Foo : P is unconditional), but isConstrainedExtension doesn't fly as it + // requires the generic signature of the extension to exist, which requires + // conformances to exist, which is what we're doing here. + auto hasAdditionalRequirements = [&](ConformanceEntry *entry) { + if (auto ED = dyn_cast(entry->getDeclContext())) + if (auto TWC = ED->getTrailingWhereClause()) + return !TWC->getRequirements().empty(); + + return false; + }; + bool lhsHasReqs = hasAdditionalRequirements(lhs); + bool rhsHasReqs = hasAdditionalRequirements(rhs); + if (lhsHasReqs != rhsHasReqs) + return lhsHasReqs ? Ordering::After : Ordering::Before; + // If the two conformances come from the same file, pick the first context // in the file. auto lhsSF = lhs->getDeclContext()->getParentSourceFile(); @@ -777,9 +806,9 @@ DeclContext *ConformanceLookupTable::getConformingContext( return entry->getDeclContext(); } -ProtocolConformance *ConformanceLookupTable::getConformance( - NominalTypeDecl *nominal, - ConformanceEntry *entry) { +ProtocolConformance * +ConformanceLookupTable::getConformance(NominalTypeDecl *nominal, + ConformanceEntry *entry) { // If we already have a conformance, we're done. if (auto conformance = entry->getConformance()) return conformance; @@ -794,13 +823,20 @@ ProtocolConformance *ConformanceLookupTable::getConformance( if (!conformingDC) return nullptr; + // Everything about this conformance is nailed down, so we can now ensure that + // the extension is fully resolved. + if (auto resolver = nominal->getASTContext().getLazyResolver()) { + if (auto ED = dyn_cast(conformingDC)) { + resolver->resolveExtension(ED); + } + } + auto *conformingNominal = conformingDC->getAsNominalTypeOrNominalTypeExtensionContext(); // Form the conformance. Type type = entry->getDeclContext()->getDeclaredInterfaceType(); - ProtocolConformance *conformance; - ASTContext &ctx = nominal->getASTContext(); + ASTContext &ctx = nominal->getASTContext(); if (entry->getKind() == ConformanceEntryKind::Inherited) { // For an inherited conformance, the conforming nominal type will // be different from the nominal type. @@ -817,9 +853,8 @@ ProtocolConformance *ConformanceLookupTable::getConformance( protocol); // Form the inherited conformance. - conformance = ctx.getInheritedConformance( - type, - inheritedConformance->getConcrete()); + entry->Conformance = + ctx.getInheritedConformance(type, inheritedConformance->getConcrete()); } else { // Create or find the normal conformance. Type conformingType = conformingDC->getDeclaredInterfaceType(); @@ -828,9 +863,22 @@ ProtocolConformance *ConformanceLookupTable::getConformance( ? conformingNominal->getLoc() : cast(conformingDC)->getLoc(); - conformance = ctx.getConformance(conformingType, protocol, conformanceLoc, - conformingDC, - ProtocolConformanceState::Incomplete); + auto normalConf = + ctx.getConformance(conformingType, protocol, conformanceLoc, + conformingDC, ProtocolConformanceState::Incomplete); + // Invalid code may cause the getConformance call below to loop, so break + // the infinite recursion by setting this eagerly to shortcircuit with the + // early return at the start of this function. + entry->Conformance = normalConf; + + NormalProtocolConformance *implyingConf = nullptr; + if (entry->Source.getKind() == ConformanceEntryKind::Implied) { + auto implyingEntry = entry->Source.getImpliedSource(); + implyingConf = getConformance(conformingNominal, implyingEntry) + ->getRootNormalConformance(); + } + normalConf->setSourceKindAndImplyingConformance(entry->Source.getKind(), + implyingConf); // If the conformance was synthesized by the ClangImporter, give it a // lazy loader that will be used to populate the conformance. @@ -852,17 +900,14 @@ ProtocolConformance *ConformanceLookupTable::getConformance( if (otherProto == impliedProto) { // Set the conformance loader to the loader stashed inside // the attribute. - cast(conformance) - ->setLazyLoader(attr->getLazyLoader(), /*context=*/0); + normalConf->setLazyLoader(attr->getLazyLoader(), /*context=*/0); break; } } } } - // Record the conformance. - entry->Conformance = conformance; - return conformance; + return entry->Conformance.get(); } void ConformanceLookupTable::addSynthesizedConformance(NominalTypeDecl *nominal, @@ -1065,7 +1110,7 @@ int ConformanceLookupTable::compareProtocolConformances( // Otherwise, sort by protocol. ProtocolDecl *lhsProto = lhs->getProtocol(); ProtocolDecl *rhsProto = rhs->getProtocol(); - return ProtocolType::compareProtocols(&lhsProto, &rhsProto); + return TypeDecl::compare(lhsProto, rhsProto); } void ConformanceLookupTable::getAllConformances( diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 96e175d0fa458..70041fa1d8509 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -252,6 +252,7 @@ StringRef Decl::getDescriptiveKindName(DescriptiveDeclKind K) { ENTRY(TypeAlias, "type alias"); ENTRY(GenericTypeParam, "generic parameter"); ENTRY(AssociatedType, "associated type"); + ENTRY(Type, "type"); ENTRY(Enum, "enum"); ENTRY(Struct, "struct"); ENTRY(Class, "class"); @@ -259,6 +260,7 @@ StringRef Decl::getDescriptiveKindName(DescriptiveDeclKind K) { ENTRY(GenericEnum, "generic enum"); ENTRY(GenericStruct, "generic struct"); ENTRY(GenericClass, "generic class"); + ENTRY(GenericType, "generic type"); ENTRY(Subscript, "subscript"); ENTRY(Constructor, "initializer"); ENTRY(Destructor, "deinitializer"); @@ -278,6 +280,7 @@ StringRef Decl::getDescriptiveKindName(DescriptiveDeclKind K) { ENTRY(EnumElement, "enum element"); ENTRY(Module, "module"); ENTRY(MissingMember, "missing member placeholder"); + ENTRY(Requirement, "requirement"); } #undef ENTRY llvm_unreachable("bad DescriptiveDeclKind"); @@ -296,6 +299,17 @@ llvm::raw_ostream &swift::operator<<(llvm::raw_ostream &OS, llvm_unreachable("bad StaticSpellingKind"); } +llvm::raw_ostream &swift::operator<<(llvm::raw_ostream &OS, + ReferenceOwnership RO) { + switch (RO) { + case ReferenceOwnership::Strong: return OS << "'strong'"; + case ReferenceOwnership::Weak: return OS << "'weak'"; + case ReferenceOwnership::Unowned: return OS << "'unowned'"; + case ReferenceOwnership::Unmanaged: return OS << "'unowned(unsafe)'"; + } + llvm_unreachable("bad ReferenceOwnership kind"); +} + DeclContext *Decl::getInnermostDeclContext() const { if (auto func = dyn_cast(this)) return const_cast(func); @@ -1519,7 +1533,7 @@ bool AbstractStorageDecl::isFormallyResilient() const { // Private and (unversioned) internal variables always have a // fixed layout. if (!getFormalAccessScope(/*useDC=*/nullptr, - /*respectVersionedAttr=*/true).isPublic()) + /*treatUsableFromInlineAsPublic=*/true).isPublic()) return false; // If we're an instance property of a nominal type, query the type. @@ -1556,47 +1570,6 @@ bool AbstractStorageDecl::isResilient(ModuleDecl *M, llvm_unreachable("bad resilience expansion"); } - -bool ValueDecl::isDefinition() const { - switch (getKind()) { - case DeclKind::Import: - case DeclKind::Extension: - case DeclKind::PatternBinding: - case DeclKind::EnumCase: - case DeclKind::TopLevelCode: - case DeclKind::InfixOperator: - case DeclKind::PrefixOperator: - case DeclKind::PostfixOperator: - case DeclKind::IfConfig: - case DeclKind::PoundDiagnostic: - case DeclKind::PrecedenceGroup: - case DeclKind::MissingMember: - assert(!isa(this)); - llvm_unreachable("non-value decls shouldn't get here"); - - case DeclKind::Func: - case DeclKind::Accessor: - case DeclKind::Constructor: - case DeclKind::Destructor: - return cast(this)->hasBody(); - - case DeclKind::Subscript: - case DeclKind::Var: - case DeclKind::Param: - case DeclKind::Enum: - case DeclKind::EnumElement: - case DeclKind::Struct: - case DeclKind::Class: - case DeclKind::TypeAlias: - case DeclKind::GenericTypeParam: - case DeclKind::AssociatedType: - case DeclKind::Protocol: - case DeclKind::Module: - return true; - } - llvm_unreachable("bad DeclKind"); -} - bool ValueDecl::isInstanceMember() const { DeclContext *DC = getDeclContext(); if (!DC->isTypeContext()) @@ -1661,12 +1634,14 @@ bool ValueDecl::isInstanceMember() const { llvm_unreachable("bad DeclKind"); } -bool ValueDecl::needsCapture() const { - // We don't need to capture anything from non-local contexts. - if (!getDeclContext()->isLocalContext()) - return false; - // We don't need to capture types. - return !isa(this); +unsigned ValueDecl::getLocalDiscriminator() const { + return LocalDiscriminator; +} + +void ValueDecl::setLocalDiscriminator(unsigned index) { + assert(getDeclContext()->isLocalContext()); + assert(LocalDiscriminator == 0 && "LocalDiscriminator is set multiple times"); + LocalDiscriminator = index; } ValueDecl *ValueDecl::getOverriddenDecl() const { @@ -1872,7 +1847,7 @@ OverloadSignature ValueDecl::getOverloadSignature() const { } if (auto *extension = dyn_cast(getDeclContext())) - if (extension->getGenericSignature()) + if (extension->isGeneric()) signature.InExtensionOfGenericType = true; return signature; @@ -2084,6 +2059,9 @@ bool ValueDecl::canInferObjCFromRequirement(ValueDecl *requirement) { } SourceLoc ValueDecl::getAttributeInsertionLoc(bool forModifier) const { + if (isImplicit()) + return SourceLoc(); + if (auto var = dyn_cast(this)) { // [attrs] var ... // The attributes are part of the VarDecl, but the 'var' is part of the PBD. @@ -2106,12 +2084,16 @@ SourceLoc ValueDecl::getAttributeInsertionLoc(bool forModifier) const { bool ValueDecl::isUsableFromInline() const { assert(getFormalAccess() == AccessLevel::Internal); - if (getAttrs().hasAttribute()) + if (getAttrs().hasAttribute() || + getAttrs().hasAttribute()) return true; - if (auto *accessor = dyn_cast(this)) - if (accessor->getStorage()->getAttrs().hasAttribute()) + if (auto *accessor = dyn_cast(this)) { + auto *storage = accessor->getStorage(); + if (storage->getAttrs().hasAttribute() || + storage->getAttrs().hasAttribute()) return true; + } if (auto *EED = dyn_cast(this)) if (EED->getParentEnum()->getAttrs().hasAttribute()) @@ -2140,8 +2122,9 @@ static AccessLevel getTestableAccess(const ValueDecl *decl) { } AccessLevel ValueDecl::getEffectiveAccess() const { - auto effectiveAccess = getFormalAccess(/*useDC=*/nullptr, - /*respectVersionedAttr=*/true); + auto effectiveAccess = + getFormalAccess(/*useDC=*/nullptr, + /*treatUsableFromInlineAsPublic=*/true); // Handle @testable. switch (effectiveAccess) { @@ -2204,28 +2187,31 @@ AccessLevel ValueDecl::getFormalAccessImpl(const DeclContext *useDC) const { return getFormalAccess(); } -AccessScope ValueDecl::getFormalAccessScope(const DeclContext *useDC, - bool respectVersionedAttr) const { +AccessScope +ValueDecl::getFormalAccessScope(const DeclContext *useDC, + bool treatUsableFromInlineAsPublic) const { const DeclContext *result = getDeclContext(); - AccessLevel access = getFormalAccess(useDC, respectVersionedAttr); + AccessLevel access = getFormalAccess(useDC, treatUsableFromInlineAsPublic); while (!result->isModuleScopeContext()) { if (result->isLocalContext() || access == AccessLevel::Private) return AccessScope(result, true); if (auto enclosingNominal = dyn_cast(result)) { - access = std::min(access, - enclosingNominal->getFormalAccess(useDC, - respectVersionedAttr)); + auto enclosingAccess = + enclosingNominal->getFormalAccess(useDC, + treatUsableFromInlineAsPublic); + access = std::min(access, enclosingAccess); } else if (auto enclosingExt = dyn_cast(result)) { // Just check the base type. If it's a constrained extension, Sema should // have already enforced access more strictly. if (auto extendedTy = enclosingExt->getExtendedType()) { if (auto nominal = extendedTy->getAnyNominal()) { - access = std::min(access, - nominal->getFormalAccess(useDC, - respectVersionedAttr)); + auto nominalAccess = + nominal->getFormalAccess(useDC, + treatUsableFromInlineAsPublic); + access = std::min(access, nominalAccess); } } @@ -2251,13 +2237,31 @@ AccessScope ValueDecl::getFormalAccessScope(const DeclContext *useDC, llvm_unreachable("unknown access level"); } -void ValueDecl::copyFormalAccessFrom(ValueDecl *source) { +void ValueDecl::copyFormalAccessFrom(const ValueDecl *source, + bool sourceIsParentContext) { if (!hasAccess()) { - setAccess(source->getFormalAccess()); + AccessLevel access = source->getFormalAccess(); + + // To make something have the same access as a 'private' parent, it has to + // be 'fileprivate' or greater. + if (sourceIsParentContext && access == AccessLevel::Private) + access = AccessLevel::FilePrivate; + + // Only certain declarations can be 'open'. + if (access == AccessLevel::Open && !isPotentiallyOverridable()) { + assert(!isa(this) && + "copying 'open' onto a class has complications"); + access = AccessLevel::Public; + } + + setAccess(access); } // Inherit the @usableFromInline attribute. - if (source->getAttrs().hasAttribute()) { + if (source->getAttrs().hasAttribute() && + !getAttrs().hasAttribute() && + !getAttrs().hasAttribute() && + DeclAttribute::canAttributeAppearOnDecl(DAK_UsableFromInline, this)) { auto &ctx = getASTContext(); auto *clonedAttr = new (ctx) UsableFromInlineAttr(/*implicit=*/true); getAttrs().add(clonedAttr); @@ -2332,7 +2336,7 @@ bool NominalTypeDecl::isFormallyResilient() const { // Private and (unversioned) internal types always have a // fixed layout. if (!getFormalAccessScope(/*useDC=*/nullptr, - /*respectVersionedAttr=*/true).isPublic()) + /*treatUsableFromInlineAsPublic=*/true).isPublic()) return false; // Check for an explicit @_fixed_layout or @_frozen attribute. @@ -2806,7 +2810,7 @@ void ClassDecl::addImplicitDestructor() { setHasDestructor(); // Propagate access control and versioned-ness. - DD->copyFormalAccessFrom(this); + DD->copyFormalAccessFrom(this, /*sourceIsParentContext*/true); // Wire up generic environment of DD. DD->setGenericEnvironment(getGenericEnvironmentOfContext()); @@ -4550,7 +4554,7 @@ void ParamDecl::setDefaultArgumentInitContext(Initializer *initContext) { } void DefaultArgumentInitializer::changeFunction( - DeclContext *parent, MutableArrayRef paramLists) { + DeclContext *parent, ArrayRef paramLists) { if (parent->isLocalContext()) { setParent(parent); } @@ -5464,21 +5468,6 @@ ConstructorDecl::getDelegatingOrChainedInitKind(DiagnosticEngine *diags, DiagnosticEngine *diags) : Decl(decl), Diags(diags) { } - bool isSelfExpr(Expr *E) { - E = E->getSemanticsProvidingExpr(); - - if (auto ATSE = dyn_cast(E)) - E = ATSE->getSubExpr(); - if (auto IOE = dyn_cast(E)) - E = IOE->getSubExpr(); - if (auto LE = dyn_cast(E)) - E = LE->getSubExpr(); - if (auto DRE = dyn_cast(E)) - return DRE->getDecl() == Decl->getImplicitSelfDecl(); - - return false; - } - bool walkToDeclPre(class Decl *D) override { // Don't walk into further nominal decls. return !isa(D); @@ -5516,7 +5505,7 @@ ConstructorDecl::getDelegatingOrChainedInitKind(DiagnosticEngine *diags, BodyInitKind myKind; if (arg->isSuperExpr()) myKind = BodyInitKind::Chained; - else if (isSelfExpr(arg)) + else if (arg->isSelfExprOf(Decl, /*sameBase*/true)) myKind = BodyInitKind::Delegating; else { // We're constructing something else. diff --git a/lib/AST/DeclContext.cpp b/lib/AST/DeclContext.cpp index 9bf4218e29853..f4861fc895077 100644 --- a/lib/AST/DeclContext.cpp +++ b/lib/AST/DeclContext.cpp @@ -364,8 +364,10 @@ ResilienceExpansion DeclContext::getResilienceExpansion() const { // if the type is formally fixed layout. if (isa(dc)) { if (auto *NTD = dyn_cast(dc->getParent())) { - if (!NTD->getFormalAccessScope(/*useDC=*/nullptr, - /*respectVersionedAttr=*/true).isPublic()) + auto nominalAccess = + NTD->getFormalAccessScope(/*useDC=*/nullptr, + /*treatUsableFromInlineAsPublic=*/true); + if (!nominalAccess.isPublic()) return ResilienceExpansion::Maximal; if (NTD->isFormallyResilient()) @@ -386,10 +388,13 @@ ResilienceExpansion DeclContext::getResilienceExpansion() const { if (!AFD->hasAccess()) break; + auto funcAccess = + AFD->getFormalAccessScope(/*useDC=*/nullptr, + /*treatUsableFromInlineAsPublic=*/true); + // If the function is not externally visible, we will not be serializing // its body. - if (!AFD->getFormalAccessScope(/*useDC=*/nullptr, - /*respectVersionedAttr=*/true).isPublic()) + if (!funcAccess.isPublic()) break; // Bodies of public transparent and always-inline functions are @@ -816,6 +821,9 @@ IterableDeclContext::castDeclToIterableDeclContext(const Decl *D) { /// declaration or extension, the supplied context is returned. static const DeclContext * getPrivateDeclContext(const DeclContext *DC, const SourceFile *useSF) { + if (DC->getASTContext().isSwiftVersion3()) + return DC; + auto NTD = DC->getAsNominalTypeOrNominalTypeExtensionContext(); if (!NTD) return DC; diff --git a/lib/AST/DiagnosticConsumer.cpp b/lib/AST/DiagnosticConsumer.cpp index 3678e1a065a09..d676db2da230c 100644 --- a/lib/AST/DiagnosticConsumer.cpp +++ b/lib/AST/DiagnosticConsumer.cpp @@ -17,6 +17,7 @@ #define DEBUG_TYPE "swift-ast" #include "swift/AST/DiagnosticConsumer.h" #include "swift/AST/DiagnosticEngine.h" +#include "swift/AST/DiagnosticsFrontend.h" #include "swift/Basic/Defer.h" #include "swift/Basic/SourceManager.h" #include "llvm/ADT/STLExtras.h" @@ -71,7 +72,8 @@ void FileSpecificDiagnosticConsumer::computeConsumersOrderedByRange( Optional bufferID = SM.getIDForBufferIdentifier(pair.first); assert(bufferID.hasValue() && "consumer registered for unknown file"); CharSourceRange range = SM.getRangeForBuffer(bufferID.getValue()); - ConsumersOrderedByRange.emplace_back(range, pair.second.get()); + ConsumersOrderedByRange.emplace_back( + ConsumerSpecificInformation(range, pair.second.get())); } // Sort the "map" by buffer /end/ location, for use with std::lower_bound @@ -79,39 +81,32 @@ void FileSpecificDiagnosticConsumer::computeConsumersOrderedByRange( // ranges must not be overlapping, but since we need to check end locations // later it's consistent to sort by that here.) std::sort(ConsumersOrderedByRange.begin(), ConsumersOrderedByRange.end(), - [](const ConsumersOrderedByRangeEntry &left, - const ConsumersOrderedByRangeEntry &right) -> bool { - auto compare = std::less(); - return compare(getRawLoc(left.first.getEnd()).getPointer(), - getRawLoc(right.first.getEnd()).getPointer()); - }); + [](const ConsumerSpecificInformation &left, + const ConsumerSpecificInformation &right) -> bool { + auto compare = std::less(); + return compare(getRawLoc(left.range.getEnd()).getPointer(), + getRawLoc(right.range.getEnd()).getPointer()); + }); // Check that the ranges are non-overlapping. If the files really are all // distinct, this should be trivially true, but if it's ever not we might end // up mis-filing diagnostics. assert(ConsumersOrderedByRange.end() == - std::adjacent_find(ConsumersOrderedByRange.begin(), - ConsumersOrderedByRange.end(), - [](const ConsumersOrderedByRangeEntry &left, - const ConsumersOrderedByRangeEntry &right) { - return left.first.overlaps(right.first); - }) && + std::adjacent_find(ConsumersOrderedByRange.begin(), + ConsumersOrderedByRange.end(), + [](const ConsumerSpecificInformation &left, + const ConsumerSpecificInformation &right) { + return left.range.overlaps(right.range); + }) && "overlapping ranges despite having distinct files"); } -DiagnosticConsumer * -FileSpecificDiagnosticConsumer::consumerForLocation(SourceManager &SM, - SourceLoc loc) const { - // If there's only one consumer, we'll use it no matter what, because... - // - ...all diagnostics within the file will go to that consumer. - // - ...all diagnostics not within the file will not be claimed by any - // consumer, and so will go to all (one) consumers. - if (SubConsumers.size() == 1) - return SubConsumers.front().second.get(); - +Optional +FileSpecificDiagnosticConsumer::consumerSpecificInformationForLocation( + SourceManager &SM, SourceLoc loc) const { // Diagnostics with invalid locations always go to every consumer. if (loc.isInvalid()) - return nullptr; + return None; // This map is generated on first use and cached, to allow the // FileSpecificDiagnosticConsumer to be set up before the source files are @@ -121,7 +116,7 @@ FileSpecificDiagnosticConsumer::consumerForLocation(SourceManager &SM, // It's possible to get here while a bridging header PCH is being // attached-to, if there's some sort of AST-reader warning or error, which // happens before CompilerInstance::setUpInputs(), at which point _no_ - // source buffers are loaded in yet. In that case we return nullptr, rather + // source buffers are loaded in yet. In that case we return None, rather // than trying to build a nonsensical map (and actually crashing since we // can't find buffers for the inputs). assert(!SubConsumers.empty()); @@ -129,7 +124,7 @@ FileSpecificDiagnosticConsumer::consumerForLocation(SourceManager &SM, assert(llvm::none_of(SubConsumers, [&](const ConsumerPair &pair) { return SM.getIDForBufferIdentifier(pair.first).hasValue(); })); - return nullptr; + return None; } auto *mutableThis = const_cast(this); mutableThis->computeConsumersOrderedByRange(SM); @@ -139,23 +134,22 @@ FileSpecificDiagnosticConsumer::consumerForLocation(SourceManager &SM, // that /might/ contain 'loc'. Specifically, since the ranges are sorted // by end location, it's looking for the first range where the end location // is greater than or equal to 'loc'. - auto possiblyContainingRangeIter = - std::lower_bound(ConsumersOrderedByRange.begin(), - ConsumersOrderedByRange.end(), - loc, - [](const ConsumersOrderedByRangeEntry &entry, - SourceLoc loc) -> bool { - auto compare = std::less(); - return compare(getRawLoc(entry.first.getEnd()).getPointer(), - getRawLoc(loc).getPointer()); - }); + const ConsumerSpecificInformation *possiblyContainingRangeIter = + std::lower_bound( + ConsumersOrderedByRange.begin(), ConsumersOrderedByRange.end(), loc, + [](const ConsumerSpecificInformation &entry, SourceLoc loc) -> bool { + auto compare = std::less(); + return compare(getRawLoc(entry.range.getEnd()).getPointer(), + getRawLoc(loc).getPointer()); + }); if (possiblyContainingRangeIter != ConsumersOrderedByRange.end() && - possiblyContainingRangeIter->first.contains(loc)) { - return possiblyContainingRangeIter->second; + possiblyContainingRangeIter->range.contains(loc)) { + return const_cast( + possiblyContainingRangeIter); } - return nullptr; + return None; } void FileSpecificDiagnosticConsumer::handleDiagnostic( @@ -163,39 +157,76 @@ void FileSpecificDiagnosticConsumer::handleDiagnostic( StringRef FormatString, ArrayRef FormatArgs, const DiagnosticInfo &Info) { - DiagnosticConsumer *specificConsumer; + HasAnErrorBeenConsumed |= Kind == DiagnosticKind::Error; + + Optional consumerSpecificInfo; switch (Kind) { case DiagnosticKind::Error: case DiagnosticKind::Warning: case DiagnosticKind::Remark: - specificConsumer = consumerForLocation(SM, Loc); - ConsumerForSubsequentNotes = specificConsumer; + consumerSpecificInfo = consumerSpecificInformationForLocation(SM, Loc); + ConsumerSpecificInfoForSubsequentNotes = consumerSpecificInfo; break; case DiagnosticKind::Note: - specificConsumer = ConsumerForSubsequentNotes; + consumerSpecificInfo = ConsumerSpecificInfoForSubsequentNotes; break; } - - if (specificConsumer) { - specificConsumer->handleDiagnostic(SM, Loc, Kind, FormatString, FormatArgs, - Info); - } else { + if (!consumerSpecificInfo.hasValue()) { for (auto &subConsumer : SubConsumers) { - subConsumer.second->handleDiagnostic(SM, Loc, Kind, FormatString, - FormatArgs, Info); + if (subConsumer.second) { + subConsumer.second->handleDiagnostic(SM, Loc, Kind, FormatString, + FormatArgs, Info); + } } + return; } + if (!consumerSpecificInfo.getValue()->consumer) + return; // Suppress non-primary diagnostic in batch mode. + + consumerSpecificInfo.getValue()->consumer->handleDiagnostic( + SM, Loc, Kind, FormatString, FormatArgs, Info); + consumerSpecificInfo.getValue()->hasAnErrorBeenEmitted |= + Kind == DiagnosticKind::Error; } -bool FileSpecificDiagnosticConsumer::finishProcessing() { +bool FileSpecificDiagnosticConsumer::finishProcessing(SourceManager &SM) { + addNonSpecificErrors(SM); + // Deliberately don't use std::any_of here because we don't want early-exit // behavior. + bool hadError = false; for (auto &subConsumer : SubConsumers) - hadError |= subConsumer.second->finishProcessing(); + hadError |= subConsumer.second && subConsumer.second->finishProcessing(SM); return hadError; } +static void produceNonSpecificError( + FileSpecificDiagnosticConsumer::ConsumerSpecificInformation &consumerInfo, + SourceManager &SM) { + Diagnostic diagnostic( + diag::error_compilation_stopped_by_errors_in_other_files); + + // Stolen from DiagnosticEngine::emitDiagnostic + DiagnosticInfo Info; + Info.ID = diagnostic.getID(); + + consumerInfo.consumer->handleDiagnostic( + SM, consumerInfo.range.getStart(), DiagnosticKind::Error, + DiagnosticEngine::diagnosticStringFor(diagnostic.getID()), {}, Info); +} + +void FileSpecificDiagnosticConsumer::addNonSpecificErrors(SourceManager &SM) { + if (!HasAnErrorBeenConsumed) + return; + for (auto &info : ConsumersOrderedByRange) { + if (!info.hasAnErrorBeenEmitted && info.consumer) { + produceNonSpecificError(info, SM); + info.hasAnErrorBeenEmitted = true; + } + } +} + void NullDiagnosticConsumer::handleDiagnostic( SourceManager &SM, SourceLoc Loc, DiagnosticKind Kind, StringRef FormatString, ArrayRef FormatArgs, diff --git a/lib/AST/DiagnosticEngine.cpp b/lib/AST/DiagnosticEngine.cpp index 95cd868ee8cb2..a781b91176d1d 100644 --- a/lib/AST/DiagnosticEngine.cpp +++ b/lib/AST/DiagnosticEngine.cpp @@ -251,10 +251,10 @@ bool DiagnosticEngine::isDiagnosticPointsToFirstBadToken(DiagID ID) const { return storedDiagnosticInfos[(unsigned) ID].pointsToFirstBadToken; } -bool DiagnosticEngine::finishProcessing() { +bool DiagnosticEngine::finishProcessing(SourceManager &SM) { bool hadError = false; for (auto &Consumer : Consumers) { - hadError |= Consumer->finishProcessing(); + hadError |= Consumer->finishProcessing(SM); } return hadError; } @@ -472,6 +472,19 @@ static void formatDiagnosticArgument(StringRef Modifier, assert(Modifier.empty() && "Improper modifier for PatternKind argument"); Out << Arg.getAsPatternKind(); break; + + case DiagnosticArgumentKind::ReferenceOwnership: + if (Modifier == "select") { + formatSelectionArgument(ModifierArguments, Args, + unsigned(Arg.getAsReferenceOwnership()), + FormatOpts, Out); + } else { + assert(Modifier.empty() && + "Improper modifier for ReferenceOwnership argument"); + Out << Arg.getAsReferenceOwnership(); + } + break; + case DiagnosticArgumentKind::StaticSpellingKind: if (Modifier == "select") { formatSelectionArgument(ModifierArguments, Args, @@ -809,9 +822,11 @@ void DiagnosticEngine::emitDiagnostic(const Diagnostic &diagnostic) { Info.FixIts = diagnostic.getFixIts(); for (auto &Consumer : Consumers) { Consumer->handleDiagnostic(SourceMgr, loc, toDiagnosticKind(behavior), - diagnosticStrings[(unsigned)Info.ID], - diagnostic.getArgs(), - Info); + diagnosticStringFor(Info.ID), + diagnostic.getArgs(), Info); } } +const char *DiagnosticEngine::diagnosticStringFor(const DiagID id) { + return diagnosticStrings[(unsigned)id]; +} diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index 4ef8e34495d87..f9f60dd66fee8 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -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 - 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 @@ -117,6 +117,11 @@ namespace { }; } // end anonymous namespace +void Expr::setType(Type T) { + assert(!T || !T->hasTypeVariable()); + Ty = T; +} + template static SourceRange getSourceRangeImpl(const T *E) { static_assert(isOverriddenFromExpr(&T::getSourceRange) || (isOverriddenFromExpr(&T::getStartLoc) && @@ -188,7 +193,7 @@ Expr *Expr::getSemanticsProvidingExpr() { Expr *Expr::getValueProvidingExpr() { Expr *E = getSemanticsProvidingExpr(); - if (auto TE = dyn_cast(this)) + if (auto TE = dyn_cast(E)) return TE->getSubExpr()->getValueProvidingExpr(); // TODO: @@ -535,12 +540,12 @@ ConcreteDeclRef Expr::getReferencedDecl() const { /// specific functor on it. This ignores statements and other non-expression /// children. void Expr:: -forEachImmediateChildExpr(const std::function &callback) { +forEachImmediateChildExpr(llvm::function_ref callback) { struct ChildWalker : ASTWalker { - const std::function &callback; + llvm::function_ref callback; Expr *ThisNode; - ChildWalker(const std::function &callback, Expr *ThisNode) + ChildWalker(llvm::function_ref callback, Expr *ThisNode) : callback(callback), ThisNode(ThisNode) {} std::pair walkToExprPre(Expr *E) override { @@ -571,11 +576,11 @@ forEachImmediateChildExpr(const std::function &callback) { /// Enumerate each immediate child expression of this node, invoking the /// specific functor on it. This ignores statements and other non-expression /// children. -void Expr::forEachChildExpr(const std::function &callback) { +void Expr::forEachChildExpr(llvm::function_ref callback) { struct ChildWalker : ASTWalker { - const std::function &callback; + llvm::function_ref callback; - ChildWalker(const std::function &callback) + ChildWalker(llvm::function_ref callback) : callback(callback) {} std::pair walkToExprPre(Expr *E) override { @@ -991,15 +996,19 @@ LiteralExpr *LiteralExpr::shallowClone( return Result; } - - +/// A wrapper around LLVM::getAsInteger that can be used on Swift interger +/// literals. It avoids misinterpreting decimal numbers prefixed with 0 as +/// octal numbers. +static bool getAsInteger(StringRef Text, llvm::APInt &Value) { + // swift encodes octal differently from C + bool IsCOctal = Text.size() > 1 && Text[0] == '0' && isdigit(Text[1]); + return Text.getAsInteger(IsCOctal ? 10 : 0, Value); +} static APInt getIntegerLiteralValue(bool IsNegative, StringRef Text, unsigned BitWidth) { llvm::APInt Value(BitWidth, 0); - // swift encodes octal differently from C - bool IsCOctal = Text.size() > 1 && Text[0] == '0' && isdigit(Text[1]); - bool Error = Text.getAsInteger(IsCOctal ? 10 : 0, Value); + bool Error = getAsInteger(Text, Value); assert(!Error && "Invalid IntegerLiteral formed"); (void)Error; if (IsNegative) Value = -Value; @@ -1012,6 +1021,15 @@ APInt IntegerLiteralExpr::getValue(StringRef Text, unsigned BitWidth, bool Negat return getIntegerLiteralValue(Negative, Text, BitWidth); } +/// Returns the raw magnitude of the literal text without any truncation. +APInt IntegerLiteralExpr::getRawMagnitude() const { + llvm::APInt Value(64, 0); + bool Error = getAsInteger(getDigitsText(), Value); + assert(!Error && "Invalid IntegerLiteral formed"); + (void)Error; + return Value; +} + APInt IntegerLiteralExpr::getValue() const { assert(!getType().isNull() && "Semantic analysis has not completed"); assert(!getType()->hasError() && "Should have a valid type"); @@ -1337,12 +1355,10 @@ ConstructorDecl *OtherConstructorDeclRefExpr::getDecl() const { MemberRefExpr::MemberRefExpr(Expr *base, SourceLoc dotLoc, ConcreteDeclRef member, DeclNameLoc nameLoc, bool Implicit, AccessSemantics semantics) - : Expr(ExprKind::MemberRef, Implicit), Base(base), - Member(member), DotLoc(dotLoc), NameLoc(nameLoc) { + : LookupExpr(ExprKind::MemberRef, base, member, Implicit), + DotLoc(dotLoc), NameLoc(nameLoc) { Bits.MemberRefExpr.Semantics = (unsigned) semantics; - Bits.MemberRefExpr.IsSuper = false; - assert(Member); } Type OverloadSetRefExpr::getBaseType() const { @@ -1559,8 +1575,17 @@ static ValueDecl *getCalledValue(Expr *E) { if (auto *DRE = dyn_cast(E)) return DRE->getDecl(); + if (auto *OCRE = dyn_cast(E)) + return OCRE->getDecl(); + + // Look through SelfApplyExpr. + if (auto *SAE = dyn_cast(E)) + return SAE->getCalledValue(); + Expr *E2 = E->getValueProvidingExpr(); - if (E != E2) return getCalledValue(E2); + if (E != E2) + return getCalledValue(E2); + return nullptr; } @@ -1574,10 +1599,9 @@ SubscriptExpr::SubscriptExpr(Expr *base, Expr *index, bool hasTrailingClosure, ConcreteDeclRef decl, bool implicit, AccessSemantics semantics) - : Expr(ExprKind::Subscript, implicit, Type()), - TheDecl(decl), Base(base), Index(index) { + : LookupExpr(ExprKind::Subscript, base, decl, implicit), + Index(index) { Bits.SubscriptExpr.Semantics = (unsigned) semantics; - Bits.SubscriptExpr.IsSuper = false; Bits.SubscriptExpr.NumArgLabels = argLabels.size(); Bits.SubscriptExpr.HasArgLabelLocs = !argLabelLocs.empty(); Bits.SubscriptExpr.HasTrailingClosure = hasTrailingClosure; @@ -2150,6 +2174,23 @@ TypeExpr *TypeExpr::createImplicitHack(SourceLoc Loc, Type Ty, ASTContext &C) { return Res; } +bool Expr::isSelfExprOf(const AbstractFunctionDecl *AFD, bool sameBase) const { + auto *E = getSemanticsProvidingExpr(); + + if (auto IOE = dyn_cast(E)) + E = IOE->getSubExpr(); + + while (auto ICE = dyn_cast(E)) { + if (sameBase && isa(ICE)) + return false; + E = ICE->getSubExpr(); + } + + if (auto DRE = dyn_cast(E)) + return DRE->getDecl() == AFD->getImplicitSelfDecl(); + + return false; +} ArchetypeType *OpenExistentialExpr::getOpenedArchetype() const { auto type = getOpaqueValue()->getType()->getRValueType(); diff --git a/lib/AST/GenericEnvironment.cpp b/lib/AST/GenericEnvironment.cpp index a40db120e578d..292f46c8f82a7 100644 --- a/lib/AST/GenericEnvironment.cpp +++ b/lib/AST/GenericEnvironment.cpp @@ -218,17 +218,11 @@ Type GenericEnvironment::getSugaredType(Type type) const { }); } -SubstitutionList -GenericEnvironment::getForwardingSubstitutions() const { +SubstitutionMap GenericEnvironment::getForwardingSubstitutionMap() const { auto *genericSig = getGenericSignature(); - - SubstitutionMap subMap = genericSig->getSubstitutionMap( + return genericSig->getSubstitutionMap( QueryInterfaceTypeSubstitutions(this), MakeAbstractConformanceForGenericType()); - - SmallVector result; - genericSig->getSubstitutions(subMap, result); - return genericSig->getASTContext().AllocateCopy(result); } std::pair diff --git a/lib/AST/GenericSignature.cpp b/lib/AST/GenericSignature.cpp index c8d446b9e57e5..104e046085f17 100644 --- a/lib/AST/GenericSignature.cpp +++ b/lib/AST/GenericSignature.cpp @@ -100,17 +100,57 @@ GenericSignature::getInnermostGenericParams() const { SmallVector GenericSignature::getSubstitutableParams() const { - SmallVector result; + // Figure out which generic parameters are concrete or same-typed to another + // generic parameter. + auto genericParams = getGenericParams(); + auto genericParamsAreNotSubstitutable = + SmallVector(genericParams.size(), false); + for (auto req : getRequirements()) { + if (req.getKind() != RequirementKind::SameType) continue; - enumeratePairedRequirements([&](Type depTy, ArrayRef) -> bool { - if (auto *paramTy = depTy->getAs()) - result.push_back(paramTy); - return false; - }); + GenericTypeParamType *gp; + if (auto secondGP = req.getSecondType()->getAs()) { + // If two generic parameters are same-typed, then the left-hand one + // is canonical. + gp = secondGP; + } else { + // If an associated type is same-typed, it doesn't constrain the generic + // parameter itself. + if (req.getSecondType()->isTypeParameter()) continue; + + // Otherwise, the generic parameter is concrete. + gp = req.getFirstType()->getAs(); + if (!gp) continue; + } + + unsigned index = GenericParamKey(gp).findIndexIn(genericParams); + genericParamsAreNotSubstitutable[index] = true; + } + + // Collect the generic parameters that are substitutable. + SmallVector result; + for (auto index : indices(genericParams)) { + auto gp = genericParams[index]; + if (!genericParamsAreNotSubstitutable[index]) + result.push_back(gp); + } return result; } +bool GenericSignature::areAllParamsConcrete() const { + unsigned numConcreteGenericParams = 0; + for (const auto &req : getRequirements()) { + if (req.getKind() != RequirementKind::SameType) continue; + if (!req.getFirstType()->is()) continue; + if (req.getSecondType()->isTypeParameter()) continue; + + ++numConcreteGenericParams; + } + + return numConcreteGenericParams == getGenericParams().size(); +} + ASTContext &GenericSignature::getASTContext( TypeArrayView params, ArrayRef requirements) { @@ -271,7 +311,7 @@ GenericSignature::getCanonical(TypeArrayView params, auto prevProto = prevReqt.getSecondType()->castTo()->getDecl(); auto proto = reqt.getSecondType()->castTo()->getDecl(); - assert(ProtocolType::compareProtocols(&prevProto, &proto) < 0 && + assert(TypeDecl::compare(prevProto, proto) < 0 && "Out-of-order conformance requirements"); } #endif @@ -335,226 +375,19 @@ GenericSignature::lookupConformance(CanType type, ProtocolDecl *proto) const { return M->lookupConformance(type, proto); } -bool GenericSignature::enumeratePairedRequirements( - llvm::function_ref)> fn) const { - // We'll be walking through the list of requirements. - ArrayRef reqs = getRequirements(); - unsigned curReqIdx = 0, numReqs = reqs.size(); - - // ... and walking through the list of generic parameters. - auto genericParams = getGenericParams(); - unsigned curGenericParamIdx = 0, numGenericParams = genericParams.size(); - - // Figure out which generic parameters are complete. - SmallVector genericParamsAreConcrete(genericParams.size(), false); - for (auto req : reqs) { - if (req.getKind() != RequirementKind::SameType) continue; - if (req.getSecondType()->isTypeParameter()) continue; - - auto gp = req.getFirstType()->getAs(); - if (!gp) continue; - - unsigned index = GenericParamKey(gp).findIndexIn(genericParams); - genericParamsAreConcrete[index] = true; - } - - /// Local function to 'catch up' to the next dependent type we're going to - /// visit, calling the function for each of the generic parameters in the - /// generic parameter list prior to this parameter. - auto enumerateGenericParamsUpToDependentType = [&](CanType depTy) -> bool { - // Figure out where we should stop when enumerating generic parameters. - unsigned stopDepth, stopIndex; - if (auto gp = dyn_cast_or_null(depTy)) { - stopDepth = gp->getDepth(); - stopIndex = gp->getIndex(); - } else { - stopDepth = genericParams.back()->getDepth() + 1; - stopIndex = 0; - } - - // Enumerate generic parameters up to the stopping point, calling the - // callback function for each one - while (curGenericParamIdx != numGenericParams) { - auto curGenericParam = genericParams[curGenericParamIdx]; - - // If the current generic parameter is before our stopping point, call - // the function. - if (curGenericParam->getDepth() < stopDepth || - (curGenericParam->getDepth() == stopDepth && - curGenericParam->getIndex() < stopIndex)) { - if (!genericParamsAreConcrete[curGenericParamIdx] && - fn(curGenericParam, { })) - return true; - - ++curGenericParamIdx; - continue; - } - - // If the current generic parameter is at our stopping point, we're - // done. - if (curGenericParam->getDepth() == stopDepth && - curGenericParam->getIndex() == stopIndex) { - ++curGenericParamIdx; - return false; - } - - // Otherwise, there's nothing to do. - break; - } - - return false; - }; - - // Walk over all of the requirements. - while (curReqIdx != numReqs) { - // "Catch up" by enumerating generic parameters up to this dependent type. - CanType depTy = reqs[curReqIdx].getFirstType()->getCanonicalType(); - if (enumerateGenericParamsUpToDependentType(depTy)) return true; - - // Utility to skip over non-conformance constraints that apply to this - // type. - auto skipNonConformanceConstraints = [&] { - while (curReqIdx != numReqs && - reqs[curReqIdx].getKind() != RequirementKind::Conformance && - reqs[curReqIdx].getFirstType()->getCanonicalType() == depTy) { - ++curReqIdx; - } - }; - - // First, skip past any non-conformance constraints on this type. - skipNonConformanceConstraints(); - - // Collect all of the conformance constraints for this dependent type. - unsigned startIdx = curReqIdx; - unsigned endIdx = curReqIdx; - while (curReqIdx != numReqs && - reqs[curReqIdx].getKind() == RequirementKind::Conformance && - reqs[curReqIdx].getFirstType()->getCanonicalType() == depTy) { - ++curReqIdx; - endIdx = curReqIdx; - } - - // Skip any trailing non-conformance constraints. - skipNonConformanceConstraints(); - - // If there were any conformance constraints, or we have a generic - // parameter we can't skip, invoke the callback. - if ((startIdx != endIdx || - (isa(depTy) && - !genericParamsAreConcrete[ - GenericParamKey(cast(depTy)) - .findIndexIn(genericParams)])) && - fn(depTy, reqs.slice(startIdx, endIdx-startIdx))) - return true; - } - - // Catch up on any remaining generic parameters. - return enumerateGenericParamsUpToDependentType(CanType()); -} - SubstitutionMap -GenericSignature::getSubstitutionMap(SubstitutionList subs) const { - SubstitutionMap result(const_cast(this)); - - enumeratePairedRequirements( - [&](Type depTy, ArrayRef reqts) -> bool { - auto sub = subs.front(); - subs = subs.slice(1); - - auto canTy = depTy->getCanonicalType(); - if (auto paramTy = dyn_cast(canTy)) - result.addSubstitution(paramTy, - sub.getReplacement()); - - auto conformances = sub.getConformances(); - assert(reqts.size() == conformances.size()); - - for (unsigned i = 0, e = conformances.size(); i < e; i++) { - assert(reqts[i].getSecondType()->getAnyNominal() == - conformances[i].getRequirement()); - result.addConformance(canTy, conformances[i]); - } - - return false; - }); - - assert(subs.empty() && "did not use all substitutions?!"); - result.verify(); - return result; +GenericSignature::getSubstitutionMap(SubstitutionMap subs) const { + return SubstitutionMap::get(const_cast(this), subs); } SubstitutionMap GenericSignature:: getSubstitutionMap(TypeSubstitutionFn subs, LookupConformanceFn lookupConformance) const { - SubstitutionMap subMap(const_cast(this)); - - // Enumerate all of the requirements that require substitution. - enumeratePairedRequirements([&](Type depTy, ArrayRef reqs) { - auto canTy = depTy->getCanonicalType(); - - // Compute the replacement type. - Type currentReplacement = depTy.subst(subs, lookupConformance, - SubstFlags::UseErrorType); - if (auto paramTy = dyn_cast(canTy)) - if (!currentReplacement->hasError()) - subMap.addSubstitution(paramTy, currentReplacement); - - // Collect the conformances. - for (auto req: reqs) { - assert(req.getKind() == RequirementKind::Conformance); - auto protoType = req.getSecondType()->castTo(); - if (auto conformance = lookupConformance(canTy, - currentReplacement, - protoType)) { - subMap.addConformance(canTy, *conformance); - } - } - - return false; - }); - - subMap.verify(); - return subMap; + return SubstitutionMap::get(const_cast(this), + subs, lookupConformance); } -void GenericSignature:: -getSubstitutions(const SubstitutionMap &subMap, - SmallVectorImpl &result) const { - - // Enumerate all of the requirements that require substitution. - enumeratePairedRequirements([&](Type depTy, ArrayRef reqs) { - auto &ctx = getASTContext(); - - // Compute the replacement type. - Type currentReplacement = depTy.subst(subMap); - if (!currentReplacement) - currentReplacement = ErrorType::get(depTy); - - // Collect the conformances. - SmallVector currentConformances; - for (auto req: reqs) { - assert(req.getKind() == RequirementKind::Conformance); - auto protoDecl = req.getSecondType()->castTo()->getDecl(); - if (auto conformance = subMap.lookupConformance(depTy->getCanonicalType(), - protoDecl)) { - currentConformances.push_back(*conformance); - } else { - if (!currentReplacement->hasError()) - currentReplacement = ErrorType::get(currentReplacement); - currentConformances.push_back(ProtocolConformanceRef(protoDecl)); - } - } - - // Add it to the final substitution list. - result.push_back({ - currentReplacement, - ctx.AllocateCopy(currentConformances) - }); - - return false; - }); -} bool GenericSignature::requiresClass(Type type) { if (!type->isTypeParameter()) return false; @@ -723,7 +556,7 @@ bool GenericSignature::isRequirementSatisfied(Requirement requirement) { return conformsToProtocol(canFirstType, protocol); else return (bool)GSB->lookupConformance(/*dependentType=*/CanType(), - canFirstType, protocolType); + canFirstType, protocol); } case RequirementKind::SameType: { @@ -807,7 +640,7 @@ bool GenericSignature::isCanonicalTypeInContext(Type type) { } bool GenericSignature::isCanonicalTypeInContext(Type type, - GenericSignatureBuilder &builder) { + GenericSignatureBuilder &builder) { // If the type isn't independently canonical, it's certainly not canonical // in this context. if (!type->isCanonical()) @@ -835,7 +668,7 @@ bool GenericSignature::isCanonicalTypeInContext(Type type, } CanType GenericSignature::getCanonicalTypeInContext(Type type, - GenericSignatureBuilder &builder) { + GenericSignatureBuilder &builder) { type = type->getCanonicalType(); // All the contextual canonicality rules apply to type parameters, so if the @@ -889,6 +722,14 @@ GenericEnvironment *CanGenericSignature::getGenericEnvironment() const { *this); } +ArrayRef> +CanGenericSignature::getGenericParams() const{ + auto params = Signature->getGenericParams().getOriginalArray(); + auto base = static_cast*>( + params.data()); + return {base, params.size()}; +} + /// Remove all of the associated type declarations from the given type /// parameter, producing \c DependentMemberTypes with names alone. static Type eraseAssociatedTypes(Type type) { diff --git a/lib/AST/GenericSignatureBuilder.cpp b/lib/AST/GenericSignatureBuilder.cpp index 05236e0d29f9b..d1146f380ca68 100644 --- a/lib/AST/GenericSignatureBuilder.cpp +++ b/lib/AST/GenericSignatureBuilder.cpp @@ -552,7 +552,6 @@ namespace { EquivalenceClassVizNode node; BaseIterator base; - BaseIterator baseEnd; public: using difference_type = ptrdiff_t; @@ -563,7 +562,7 @@ namespace { EquivalenceClassVizIterator(EquivalenceClassVizNode node, BaseIterator base, BaseIterator baseEnd) - : node(node), base(base), baseEnd(baseEnd) { + : node(node), base(base) { } BaseIterator &getBase() { return base; } @@ -1309,7 +1308,7 @@ const RequirementSource *RequirementSource::withoutRedundantSubpath( case Concrete: return parent->withoutRedundantSubpath(builder, start, end) - ->viaParent(builder, getAssociatedType()); + ->viaConcrete(builder, getProtocolConformance()); case Derived: return parent->withoutRedundantSubpath(builder, start, end) @@ -1776,7 +1775,7 @@ FloatingRequirementSource FloatingRequirementSource::asInferred( bool FloatingRequirementSource::isRecursive( Type rootType, GenericSignatureBuilder &builder) const { - llvm::SmallSet, 4> visitedAssocReqs; + llvm::SmallSet, 32> visitedAssocReqs; for (auto storedSource = storage.dyn_cast(); storedSource; storedSource = storedSource->parent) { // FIXME: isRecursive() is completely misnamed @@ -2073,7 +2072,7 @@ static int compareAssociatedTypes(AssociatedTypeDecl *assocType1, // - by protocol, so t_n_m.`P.T` < t_n_m.`Q.T` (given P < Q) auto proto1 = assocType1->getProtocol(); auto proto2 = assocType2->getProtocol(); - if (int compareProtocols = ProtocolType::compareProtocols(&proto1, &proto2)) + if (int compareProtocols = TypeDecl::compare(proto1, proto2)) return compareProtocols; // Error case: if we have two associated types with the same name in the @@ -2323,6 +2322,12 @@ Type EquivalenceClass::getTypeInContext(GenericSignatureBuilder &builder, if (recursiveConcreteType) return ErrorType::get(anchor); + // Prevent recursive substitution. + this->recursiveConcreteType = true; + SWIFT_DEFER { + this->recursiveConcreteType = false; + }; + return genericEnv->mapTypeIntoContext(concreteType, builder.getLookupConformanceFn()); } @@ -2382,6 +2387,12 @@ Type EquivalenceClass::getTypeInContext(GenericSignatureBuilder &builder, // Substitute into the superclass. Type superclass = this->recursiveSuperclassType ? Type() : this->superclass; if (superclass && superclass->hasTypeParameter()) { + // Prevent recursive substitution. + this->recursiveSuperclassType = true; + SWIFT_DEFER { + this->recursiveSuperclassType = false; + }; + superclass = genericEnv->mapTypeIntoContext( superclass, builder.getLookupConformanceFn()); @@ -2628,9 +2639,7 @@ GenericSignatureBuilder::resolveConcreteConformance(ResolvedType type, // Lookup the conformance of the concrete type to this protocol. auto conformance = lookupConformance(type.getDependentType(*this)->getCanonicalType(), - concrete, - proto->getDeclaredInterfaceType() - ->castTo()); + concrete, proto); if (!conformance) { if (!concrete->hasError() && concreteSource->getLoc().isValid()) { Impl->HadAnyError = true; @@ -2661,9 +2670,7 @@ const RequirementSource *GenericSignatureBuilder::resolveSuperConformance( // Lookup the conformance of the superclass to this protocol. auto conformance = lookupConformance(type.getDependentType(*this)->getCanonicalType(), - superclass, - proto->getDeclaredInterfaceType() - ->castTo()); + superclass, proto); if (!conformance) return nullptr; // Conformance to this protocol is redundant; update the requirement source @@ -3814,16 +3821,16 @@ GenericSignatureBuilder::getLookupConformanceFn() Optional GenericSignatureBuilder::lookupConformance(CanType dependentType, Type conformingReplacementType, - ProtocolType *conformedProtocol) { + ProtocolDecl *conformedProtocol) { if (conformingReplacementType->isTypeParameter()) - return ProtocolConformanceRef(conformedProtocol->getDecl()); + return ProtocolConformanceRef(conformedProtocol); // Figure out which module to look into. // FIXME: When lookupConformance() starts respecting modules, we'll need // to do some filtering here. - ModuleDecl *searchModule = conformedProtocol->getDecl()->getParentModule(); + ModuleDecl *searchModule = conformedProtocol->getParentModule(); auto result = searchModule->lookupConformance(conformingReplacementType, - conformedProtocol->getDecl()); + conformedProtocol); if (result && getLazyResolver()) getLazyResolver()->markConformanceUsed(*result, searchModule); @@ -4715,7 +4722,7 @@ ConstraintResult GenericSignatureBuilder::addTypeRequirement( // getLookupConformanceFns used in here don't use that parameter anyway. auto dependentType = CanType(); auto conformance = - getLookupConformanceFn()(dependentType, subjectType, proto); + getLookupConformanceFn()(dependentType, subjectType, proto->getDecl()); // FIXME: diagnose if there's no conformance. if (conformance) { @@ -6981,17 +6988,24 @@ void GenericSignatureBuilder::checkSameTypeConstraints( void GenericSignatureBuilder::checkConcreteTypeConstraints( TypeArrayView genericParams, EquivalenceClass *equivClass) { + // Resolve any thus-far-unresolved dependent types. + Type resolvedConcreteType = + resolveDependentMemberTypes(*this, equivClass->concreteType); + checkConstraintList( genericParams, equivClass->concreteTypeConstraints, [&](const ConcreteConstraint &constraint) { - return constraint.value->isEqual(equivClass->concreteType); + if (constraint.value->isEqual(resolvedConcreteType)) + return true; + + auto resolvedType = + resolveDependentMemberTypes(*this, constraint.value); + return resolvedType->isEqual(resolvedConcreteType); }, [&](const Constraint &constraint) { Type concreteType = constraint.value; // If the concrete type is equivalent, the constraint is redundant. - // FIXME: Should check this constraint after substituting in the - // archetype anchors for each dependent type. if (concreteType->isEqual(equivClass->concreteType)) return ConstraintRelation::Redundant; @@ -7006,9 +7020,7 @@ void GenericSignatureBuilder::checkConcreteTypeConstraints( diag::redundant_same_type_to_concrete, diag::same_type_redundancy_here); - // Resolve any thus-far-unresolved dependent types. - equivClass->concreteType = - resolveDependentMemberTypes(*this, equivClass->concreteType); + equivClass->concreteType = resolvedConcreteType; } void GenericSignatureBuilder::checkSuperclassConstraints( @@ -7016,23 +7028,20 @@ void GenericSignatureBuilder::checkSuperclassConstraints( EquivalenceClass *equivClass) { assert(equivClass->superclass && "No superclass constraint?"); - // FIXME: We should be substituting in the canonical type in context so - // we can resolve superclass requirements, e.g., if you had: - // - // class Foo - // class Bar: Foo - // - // func foo>(...) { ... } - // - // then the second `U: Foo` constraint introduces a `T == Int` - // constraint, and we will need to perform that substitution for this final - // check. + // Resolve any this-far-unresolved dependent types. + Type resolvedSuperclass = + resolveDependentMemberTypes(*this, equivClass->superclass); auto representativeConstraint = checkConstraintList( genericParams, equivClass->superclassConstraints, [&](const ConcreteConstraint &constraint) { - return constraint.value->isEqual(equivClass->superclass); + if (constraint.value->isEqual(resolvedSuperclass)) + return true; + + Type resolvedType = + resolveDependentMemberTypes(*this, constraint.value); + return resolvedType->isEqual(equivClass->superclass); }, [&](const Constraint &constraint) { Type superclass = constraint.value; @@ -7049,15 +7058,16 @@ void GenericSignatureBuilder::checkSuperclassConstraints( diag::superclass_redundancy_here); // Resolve any this-far-unresolved dependent types. - equivClass->superclass = - resolveDependentMemberTypes(*this, equivClass->superclass); + equivClass->superclass = resolvedSuperclass; // If we have a concrete type, check it. // FIXME: Substitute into the concrete type. if (equivClass->concreteType) { + Type resolvedConcreteType = + resolveDependentMemberTypes(*this, equivClass->concreteType); auto existing = equivClass->findAnyConcreteConstraintAsWritten(); // Make sure the concrete type fulfills the superclass requirement. - if (!equivClass->superclass->isExactSuperclassOf(equivClass->concreteType)){ + if (!equivClass->superclass->isExactSuperclassOf(resolvedConcreteType)){ Impl->HadAnyError = true; if (existing) { Diags.diagnose(existing->source->getLoc(), diag::type_does_not_inherit, @@ -7077,7 +7087,7 @@ void GenericSignatureBuilder::checkSuperclassConstraints( diag::type_does_not_inherit, representativeConstraint.getSubjectDependentType( genericParams), - equivClass->concreteType, equivClass->superclass); + resolvedConcreteType, equivClass->superclass); } } else if (representativeConstraint.source->shouldDiagnoseRedundancy(true) && existing && @@ -7286,7 +7296,7 @@ void GenericSignatureBuilder::enumerateRequirements( // Sort the protocols in canonical order. llvm::array_pod_sort(protocols.begin(), protocols.end(), - ProtocolType::compareProtocols); + TypeDecl::compare); // Enumerate the conformance requirements. for (auto proto : protocols) { diff --git a/lib/AST/LookupVisibleDecls.cpp b/lib/AST/LookupVisibleDecls.cpp index 6bf11f5de768b..3567268a246a1 100644 --- a/lib/AST/LookupVisibleDecls.cpp +++ b/lib/AST/LookupVisibleDecls.cpp @@ -124,6 +124,10 @@ static bool areTypeDeclsVisibleInLookupMode(LookupState LS) { static bool isDeclVisibleInLookupMode(ValueDecl *Member, LookupState LS, const DeclContext *FromContext, LazyResolver *TypeResolver) { + // Accessors are never visible directly in the source language. + if (isa(Member)) + return false; + if (TypeResolver) { TypeResolver->resolveDeclSignature(Member); TypeResolver->resolveAccessControl(Member); @@ -872,9 +876,23 @@ void swift::lookupVisibleDecls(VisibleDeclConsumer &Consumer, LS = LS.withOnMetatype(); } - GenericParamList *GenericParams = DC->getGenericParamsOfContext(); + // We don't look for generic parameters if we are in the context of a + // nominal type: they will be looked up anyways via `lookupVisibleMemberDecls`. + if (DC && !isa(DC)) { + if (auto *decl = DC->getAsDeclOrDeclExtensionContext()) { + if (auto GC = decl->getAsGenericContext()) { + auto params = GC->getGenericParams(); + namelookup::FindLocalVal(SM, Loc, Consumer).checkGenericParams(params); + } + } + } + + if (auto *SE = dyn_cast(DC)) { + ExtendedType = SE->getDeclContext()->getSelfTypeInContext(); + DC = DC->getParent(); + BaseDecl = DC->getAsNominalTypeOrNominalTypeExtensionContext(); + } else if (auto *AFD = dyn_cast(DC)) { - if (auto *AFD = dyn_cast(DC)) { // Look for local variables; normally, the parser resolves these // for us, but it can't do the right thing inside local types. // FIXME: when we can parse and typecheck the function body partially for @@ -917,14 +935,9 @@ void swift::lookupVisibleDecls(VisibleDeclConsumer &Consumer, BaseDecl = ND; } - if (BaseDecl && ExtendedType) { + if (BaseDecl && ExtendedType) ::lookupVisibleMemberDecls(ExtendedType, Consumer, DC, LS, Reason, TypeResolver, nullptr); - } - - // Check any generic parameters for something with the given name. - namelookup::FindLocalVal(SM, Loc, Consumer) - .checkGenericParams(GenericParams); DC = DC->getParent(); Reason = DeclVisibilityKind::MemberOfOutsideNominal; diff --git a/lib/AST/Module.cpp b/lib/AST/Module.cpp index 866018d2df4f0..ece111e9c75b5 100644 --- a/lib/AST/Module.cpp +++ b/lib/AST/Module.cpp @@ -190,6 +190,12 @@ void SourceLookupCache::doPopulateCache(Range decls, } if (auto *NTD = dyn_cast(D)) doPopulateCache(NTD->getMembers(), true); + + // Avoid populating the cache with the members of invalid extension + // declarations. These members can be used to point validation inside of + // a malformed context. + if (D->isInvalid()) continue; + if (auto *ED = dyn_cast(D)) doPopulateCache(ED->getMembers(), true); } @@ -348,7 +354,7 @@ void SourceLookupCache::invalidate() { ModuleDecl::ModuleDecl(Identifier name, ASTContext &ctx) : DeclContext(DeclContextKind::Module, nullptr), TypeDecl(DeclKind::Module, &ctx, name, SourceLoc(), { }), - Flags({0, 0, 0}) { + Flags() { ctx.addDestructorCleanup(*this); setImplicit(); setInterfaceType(ModuleType::get(this)); @@ -1121,24 +1127,19 @@ bool ModuleDecl::isSystemModule() const { return false; } -template -static bool forAllImportedModules(ModuleDecl *topLevel, - ModuleDecl::AccessPathTy thisPath, - bool includePrivateTopLevelImports, - const Callback &fn) { +template +static bool +forAllImportedModules(ModuleDecl *topLevel, ModuleDecl::AccessPathTy thisPath, + llvm::function_ref fn) { using ImportedModule = ModuleDecl::ImportedModule; using AccessPathTy = ModuleDecl::AccessPathTy; llvm::SmallSet visited; SmallVector stack; - // Even if we're processing the top-level module like any other, we may - // still want to include non-exported modules. - ModuleDecl::ImportFilter filter = respectVisibility ? ModuleDecl::ImportFilter::Public - : ModuleDecl::ImportFilter::All; - ModuleDecl::ImportFilter topLevelFilter = - includePrivateTopLevelImports ? ModuleDecl::ImportFilter::All : filter; - topLevel->getImportedModules(stack, topLevelFilter); + auto filter = respectVisibility ? ModuleDecl::ImportFilter::Public + : ModuleDecl::ImportFilter::All; + topLevel->getImportedModules(stack, filter); // Make sure the top-level module is first; we want pre-order-ish traversal. AccessPathTy overridingPath; @@ -1176,11 +1177,10 @@ static bool forAllImportedModules(ModuleDecl *topLevel, return true; } -bool ModuleDecl::forAllVisibleModules(AccessPathTy thisPath, - bool includePrivateTopLevelImports, - llvm::function_ref fn) { - return forAllImportedModules(this, thisPath, - includePrivateTopLevelImports, fn); +bool +ModuleDecl::forAllVisibleModules(AccessPathTy thisPath, + llvm::function_ref fn) { + return forAllImportedModules(this, thisPath, fn); } bool FileUnit::forAllVisibleModules( @@ -1208,8 +1208,15 @@ void ModuleDecl::collectLinkLibraries(LinkLibraryCallback callback) { void SourceFile::collectLinkLibraries(ModuleDecl::LinkLibraryCallback callback) const { - for (auto importPair : Imports) - importPair.first.second->collectLinkLibraries(callback); + forAllImportedModules(getParentModule(), /*thisPath*/{}, + [=](ModuleDecl::ImportedModule import) -> bool { + swift::ModuleDecl *next = import.second; + if (next->getName() == getParentModule()->getName()) + return true; + + next->collectLinkLibraries(callback); + return true; + }); } bool ModuleDecl::walk(ASTWalker &Walker) { diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index 3ccda2863f8e2..f93caada3357b 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -453,24 +453,42 @@ static DeclVisibilityKind getLocalDeclVisibilityKind(const ASTScope *scope) { } UnqualifiedLookup::UnqualifiedLookup(DeclName Name, DeclContext *DC, - LazyResolver *TypeResolver, - bool IsKnownNonCascading, - SourceLoc Loc, bool IsTypeLookup, - bool AllowProtocolMembers, - bool IgnoreAccessControl) { + LazyResolver *TypeResolver, SourceLoc Loc, + Options options) + : IndexOfFirstOuterResult(0) +{ ModuleDecl &M = *DC->getParentModule(); ASTContext &Ctx = M.getASTContext(); const SourceManager &SM = Ctx.SourceMgr; DebuggerClient *DebugClient = M.getDebugClient(); - NamedDeclConsumer Consumer(Name, Results, IsTypeLookup); + auto isOriginallyTypeLookup = options.contains(Flags::TypeLookup); + NamedDeclConsumer Consumer(Name, Results, isOriginallyTypeLookup); + + NLOptions baseNLOptions = NL_UnqualifiedDefault; + if (options.contains(Flags::AllowProtocolMembers)) + baseNLOptions |= NL_ProtocolMembers; + if (isOriginallyTypeLookup) + baseNLOptions |= NL_OnlyTypes; + if (options.contains(Flags::IgnoreAccessControl)) + baseNLOptions |= NL_IgnoreAccessControl; Optional isCascadingUse; - if (IsKnownNonCascading) + if (options.contains(Flags::KnownPrivate)) isCascadingUse = false; SmallVector UnavailableInnerResults; + auto shouldReturnBasedOnResults = [&](bool noMoreOuterResults = false) { + if (Results.empty()) + return false; + + if (IndexOfFirstOuterResult == 0) + IndexOfFirstOuterResult = Results.size(); + + return !options.contains(Flags::IncludeOuterResults) || noMoreOuterResults; + }; + if (Loc.isValid() && DC->getParentSourceFile()->Kind != SourceFileKind::REPL && Ctx.LangOpts.EnableASTScopeLookup) { @@ -506,7 +524,7 @@ UnqualifiedLookup::UnqualifiedLookup(DeclName Name, DeclContext *DC, } // If we found anything, we're done. - if (!Results.empty()) + if (shouldReturnBasedOnResults()) return; // When we are in the body of a method, get the 'self' declaration. @@ -575,22 +593,17 @@ UnqualifiedLookup::UnqualifiedLookup(DeclName Name, DeclContext *DC, if (lookupType->hasError()) continue; + NLOptions options = baseNLOptions; // Perform lookup into the type. - NLOptions options = NL_UnqualifiedDefault; if (isCascadingUse.getValue()) options |= NL_KnownCascadingDependency; else options |= NL_KnownNonCascadingDependency; - if (AllowProtocolMembers) - options |= NL_ProtocolMembers; - if (IsTypeLookup) - options |= NL_OnlyTypes; - if (IgnoreAccessControl) - options |= NL_IgnoreAccessControl; - SmallVector lookup; dc->lookupQualified(lookupType, Name, options, TypeResolver, lookup); + + auto startIndex = Results.size(); for (auto result : lookup) { auto *baseDC = dc; if (!isa(result) && selfDC) baseDC = selfDC; @@ -607,15 +620,17 @@ UnqualifiedLookup::UnqualifiedLookup(DeclName Name, DeclContext *DC, .isUnavailableInSwiftVersion(effectiveVersion); }; - // If all of the results we found are unavailable, keep looking. - if (std::all_of(Results.begin(), Results.end(), - unavailableLookupResult)) { - UnavailableInnerResults.append(Results.begin(), Results.end()); - Results.clear(); + // If all of the results we just found are unavailable, keep looking. + auto begin = Results.begin() + startIndex; + if (std::all_of(begin, Results.end(), unavailableLookupResult)) { + UnavailableInnerResults.append(begin, Results.end()); + Results.erase(begin, Results.end()); } else { if (DebugClient) filterForDiscriminator(Results, DebugClient); - return; + + if (shouldReturnBasedOnResults()) + return; } } @@ -652,7 +667,7 @@ UnqualifiedLookup::UnqualifiedLookup(DeclName Name, DeclContext *DC, if (auto *selfParam = PBI->getImplicitSelfDecl()) { Consumer.foundDecl(selfParam, DeclVisibilityKind::FunctionParameter); - if (!Results.empty()) + if (shouldReturnBasedOnResults()) return; DC = DC->getParent(); @@ -698,11 +713,11 @@ UnqualifiedLookup::UnqualifiedLookup(DeclName Name, DeclContext *DC, namelookup::FindLocalVal localVal(SM, Loc, Consumer); localVal.visit(AFD->getBody()); - if (!Results.empty()) + if (shouldReturnBasedOnResults()) return; for (auto *PL : AFD->getParameterLists()) localVal.checkParameterList(PL); - if (!Results.empty()) + if (shouldReturnBasedOnResults()) return; } if (!isCascadingUse.hasValue() || isCascadingUse.getValue()) @@ -741,10 +756,10 @@ UnqualifiedLookup::UnqualifiedLookup(DeclName Name, DeclContext *DC, if (auto *CE = dyn_cast(ACE)) { namelookup::FindLocalVal localVal(SM, Loc, Consumer); localVal.visit(CE->getBody()); - if (!Results.empty()) + if (shouldReturnBasedOnResults()) return; localVal.checkParameterList(CE->getParameters()); - if (!Results.empty()) + if (shouldReturnBasedOnResults()) return; } } @@ -781,27 +796,21 @@ UnqualifiedLookup::UnqualifiedLookup(DeclName Name, DeclContext *DC, namelookup::FindLocalVal localVal(SM, Loc, Consumer); localVal.checkGenericParams(GenericParams); - if (!Results.empty()) + if (shouldReturnBasedOnResults()) return; } if (BaseDC && !ExtendedType->hasError()) { - NLOptions options = NL_UnqualifiedDefault; + NLOptions options = baseNLOptions; if (isCascadingUse.getValue()) options |= NL_KnownCascadingDependency; else options |= NL_KnownNonCascadingDependency; - if (AllowProtocolMembers) - options |= NL_ProtocolMembers; - if (IsTypeLookup) - options |= NL_OnlyTypes; - if (IgnoreAccessControl) - options |= NL_IgnoreAccessControl; - SmallVector Lookup; DC->lookupQualified(ExtendedType, Name, options, TypeResolver, Lookup); bool FoundAny = false; + auto startIndex = Results.size(); for (auto Result : Lookup) { // In Swift 3 mode, unqualified lookup skips static methods when // performing lookup from instance context. @@ -846,15 +855,16 @@ UnqualifiedLookup::UnqualifiedLookup(DeclName Name, DeclContext *DC, }; // If all of the results we found are unavailable, keep looking. - if (std::all_of(Results.begin(), Results.end(), - unavailableLookupResult)) { - UnavailableInnerResults.append(Results.begin(), Results.end()); - Results.clear(); - FoundAny = false; + auto begin = Results.begin() + startIndex; + if (std::all_of(begin, Results.end(), unavailableLookupResult)) { + UnavailableInnerResults.append(begin, Results.end()); + Results.erase(begin, Results.end()); } else { if (DebugClient) filterForDiscriminator(Results, DebugClient); - return; + + if (shouldReturnBasedOnResults()) + return; } } } @@ -873,7 +883,7 @@ UnqualifiedLookup::UnqualifiedLookup(DeclName Name, DeclContext *DC, namelookup::FindLocalVal localVal(SM, Loc, Consumer); localVal.checkGenericParams(dcGenericParams); - if (!Results.empty()) + if (shouldReturnBasedOnResults()) return; if (!isa(DC)) @@ -896,16 +906,16 @@ UnqualifiedLookup::UnqualifiedLookup(DeclName Name, DeclContext *DC, // local types. namelookup::FindLocalVal localVal(SM, Loc, Consumer); localVal.checkSourceFile(*SF); - if (!Results.empty()) + if (shouldReturnBasedOnResults()) return; } } } // TODO: Does the debugger client care about compound names? - if (Name.isSimpleName() - && DebugClient && DebugClient->lookupOverrides(Name.getBaseName(), DC, - Loc, IsTypeLookup, Results)) + if (Name.isSimpleName() && DebugClient && + DebugClient->lookupOverrides(Name.getBaseName(), DC, Loc, + isOriginallyTypeLookup, Results)) return; recordLookupOfTopLevelName(DC, Name, isCascadingUse.getValue()); @@ -917,8 +927,8 @@ UnqualifiedLookup::UnqualifiedLookup(DeclName Name, DeclContext *DC, using namespace namelookup; SmallVector CurModuleResults; - auto resolutionKind = - IsTypeLookup ? ResolutionKind::TypesOnly : ResolutionKind::Overloadable; + auto resolutionKind = isOriginallyTypeLookup ? ResolutionKind::TypesOnly + : ResolutionKind::Overloadable; lookupInModule(&M, {}, Name, CurModuleResults, NLKind::UnqualifiedLookup, resolutionKind, TypeResolver, DC, extraImports); @@ -930,20 +940,19 @@ UnqualifiedLookup::UnqualifiedLookup(DeclName Name, DeclContext *DC, // Now add any names the DebugClient knows about to the lookup. if (Name.isSimpleName() && DebugClient) - DebugClient->lookupAdditions(Name.getBaseName(), DC, Loc, IsTypeLookup, - Results); + DebugClient->lookupAdditions(Name.getBaseName(), DC, Loc, + isOriginallyTypeLookup, Results); // If we've found something, we're done. - if (!Results.empty()) + if (shouldReturnBasedOnResults(/*noMoreOuterResults=*/true)) return; // If we still haven't found anything, but we do have some // declarations that are "unavailable in the current Swift", drop // those in. - if (!UnavailableInnerResults.empty()) { - Results = std::move(UnavailableInnerResults); + Results = std::move(UnavailableInnerResults); + if (shouldReturnBasedOnResults(/*noMoreOuterResults=*/true)) return; - } if (!Name.isSimpleName()) return; @@ -951,7 +960,8 @@ UnqualifiedLookup::UnqualifiedLookup(DeclName Name, DeclContext *DC, // Look for a module with the given name. if (Name.isSimpleName(M.getName())) { Results.push_back(LookupResultEntry(&M)); - return; + if (shouldReturnBasedOnResults(/*noMoreOuterResults=*/true)) + return; } ModuleDecl *desiredModule = Ctx.getLoadedModule(Name.getBaseIdentifier()); @@ -966,6 +976,8 @@ UnqualifiedLookup::UnqualifiedLookup(DeclName Name, DeclContext *DC, return true; }); } + // Make sure we've recorded the inner-result-boundary. + (void)shouldReturnBasedOnResults(/*noMoreOuterResults=*/true); } TypeDecl* UnqualifiedLookup::getSingleTypeResult() { @@ -1507,12 +1519,36 @@ void ClassDecl::recordObjCMethod(AbstractFunctionDecl *method) { vec.push_back(method); } -static bool checkAccess(const DeclContext *useDC, const DeclContext *sourceDC, - AccessLevel access) { +AccessLevel +ValueDecl::adjustAccessLevelForProtocolExtension(AccessLevel access) const { + if (auto *ext = dyn_cast(getDeclContext())) { + if (auto *protocol = ext->getAsProtocolOrProtocolExtensionContext()) { + // Note: it gets worse. The standard library has public methods + // in protocol extensions of a @usableFromInline internal protocol, + // and expects these extension methods to witness public protocol + // requirements. Which works at the ABI level, so let's keep + // supporting that here by passing 'isUsageFromInline'. + auto protoAccess = protocol->getFormalAccess(/*useDC=*/nullptr, + /*isUsageFromInline=*/true); + if (protoAccess == AccessLevel::Private) + protoAccess = AccessLevel::FilePrivate; + access = std::min(access, protoAccess); + } + } + + return access; +} + +static bool checkAccess(const DeclContext *useDC, const ValueDecl *VD, + AccessLevel access, bool forConformance) { if (!useDC) return access >= AccessLevel::Public; - assert(sourceDC && "ValueDecl being accessed must have a valid DeclContext"); + auto *sourceDC = VD->getDeclContext(); + + if (!forConformance) + access = VD->adjustAccessLevelForProtocolExtension(access); + switch (access) { case AccessLevel::Private: return (useDC == sourceDC || @@ -1536,11 +1572,14 @@ static bool checkAccess(const DeclContext *useDC, const DeclContext *sourceDC, llvm_unreachable("bad access level"); } -bool ValueDecl::isAccessibleFrom(const DeclContext *DC) const { - return checkAccess(DC, getDeclContext(), getFormalAccess()); +bool ValueDecl::isAccessibleFrom(const DeclContext *DC, + bool forConformance) const { + auto access = getFormalAccess(); + return checkAccess(DC, this, access, forConformance); } -bool AbstractStorageDecl::isSetterAccessibleFrom(const DeclContext *DC) const { +bool AbstractStorageDecl::isSetterAccessibleFrom(const DeclContext *DC, + bool forConformance) const { assert(isSettable(DC)); // If a stored property does not have a setter, it is still settable from the @@ -1552,7 +1591,8 @@ bool AbstractStorageDecl::isSetterAccessibleFrom(const DeclContext *DC) const { if (isa(this)) return true; - return checkAccess(DC, getDeclContext(), getSetterFormalAccess()); + auto access = getSetterFormalAccess(); + return checkAccess(DC, this, access, forConformance); } Type AbstractStorageDecl::getStorageInterfaceType() const { diff --git a/lib/AST/Pattern.cpp b/lib/AST/Pattern.cpp index 606e4219095c6..2a21ce228aaba 100644 --- a/lib/AST/Pattern.cpp +++ b/lib/AST/Pattern.cpp @@ -190,7 +190,7 @@ namespace { /// \brief apply the specified function to all variables referenced in this /// pattern. -void Pattern::forEachVariable(const std::function &fn) const { +void Pattern::forEachVariable(llvm::function_ref fn) const { switch (getKind()) { case PatternKind::Any: case PatternKind::Bool: @@ -235,7 +235,7 @@ void Pattern::forEachVariable(const std::function &fn) const { /// \brief apply the specified function to all pattern nodes recursively in /// this pattern. This is a pre-order traversal. -void Pattern::forEachNode(const std::function &f) { +void Pattern::forEachNode(llvm::function_ref f) { f(this); switch (getKind()) { diff --git a/lib/AST/ProtocolConformance.cpp b/lib/AST/ProtocolConformance.cpp index 57e2f68c37925..02996fc3eec5f 100644 --- a/lib/AST/ProtocolConformance.cpp +++ b/lib/AST/ProtocolConformance.cpp @@ -21,7 +21,6 @@ #include "swift/AST/GenericEnvironment.h" #include "swift/AST/Module.h" #include "swift/AST/ProtocolConformance.h" -#include "swift/AST/Substitution.h" #include "swift/AST/Types.h" #include "swift/AST/TypeWalker.h" #include "swift/Basic/Statistic.h" @@ -38,17 +37,20 @@ STATISTIC(NumConformanceLookupTables, "# of conformance lookup tables built"); using namespace swift; -Witness::Witness(ValueDecl *decl, SubstitutionList substitutions, +Witness::Witness(ValueDecl *decl, SubstitutionMap substitutions, GenericEnvironment *syntheticEnv, - SubstitutionList reqToSynthesizedEnvSubs) { - auto &ctx = decl->getASTContext(); + SubstitutionMap reqToSynthesizedEnvSubs) { + if (!syntheticEnv && substitutions.empty() && + reqToSynthesizedEnvSubs.empty()) { + storage = decl; + return; + } - auto declRef = ConcreteDeclRef(ctx, decl, substitutions); + auto &ctx = decl->getASTContext(); + auto declRef = ConcreteDeclRef(decl, substitutions); auto storedMem = ctx.Allocate(sizeof(StoredWitness), alignof(StoredWitness)); - auto stored = new (storedMem) - StoredWitness{declRef, syntheticEnv, - ctx.AllocateCopy(reqToSynthesizedEnvSubs)}; - ctx.addDestructorCleanup(*stored); + auto stored = new (storedMem) StoredWitness{declRef, syntheticEnv, + reqToSynthesizedEnvSubs}; storage = stored; } @@ -116,8 +118,7 @@ ProtocolConformanceRef::subst(Type origType, // Check the conformance map. if (auto result = conformances(origType->getCanonicalType(), - substType, - proto->getDeclaredType())) { + substType, proto)) { return *result; } @@ -199,6 +200,13 @@ ProtocolConformanceState ProtocolConformance::getState() const { CONFORMANCE_SUBCLASS_DISPATCH(getState, ()) } +ConformanceEntryKind ProtocolConformance::getSourceKind() const { + CONFORMANCE_SUBCLASS_DISPATCH(getSourceKind, ()) +} +NormalProtocolConformance *ProtocolConformance::getImplyingConformance() const { + CONFORMANCE_SUBCLASS_DISPATCH(getImplyingConformance, ()) +} + bool ProtocolConformance::hasTypeWitness(AssociatedTypeDecl *assocType, LazyResolver *resolver) const { @@ -765,7 +773,7 @@ void NormalProtocolConformance::setWitness(ValueDecl *requirement, SpecializedProtocolConformance::SpecializedProtocolConformance( Type conformingType, ProtocolConformance *genericConformance, - SubstitutionList substitutions) + SubstitutionMap substitutions) : ProtocolConformance(ProtocolConformanceKind::Specialized, conformingType), GenericConformance(genericConformance), GenericSubstitutions(substitutions) @@ -791,14 +799,6 @@ SpecializedProtocolConformance::SpecializedProtocolConformance( } } -SubstitutionMap SpecializedProtocolConformance::getSubstitutionMap() const { - auto *genericSig = GenericConformance->getGenericSignature(); - if (genericSig) - return genericSig->getSubstitutionMap(GenericSubstitutions); - - return SubstitutionMap(); -} - bool SpecializedProtocolConformance::hasTypeWitness( AssociatedTypeDecl *assocType, LazyResolver *resolver) const { @@ -881,8 +881,9 @@ SpecializedProtocolConformance::getAssociatedConformance(Type assocType, } ConcreteDeclRef -SpecializedProtocolConformance::getWitnessDeclRef(ValueDecl *requirement, - LazyResolver *resolver) const { +SpecializedProtocolConformance::getWitnessDeclRef( + ValueDecl *requirement, + LazyResolver *resolver) const { auto baseWitness = GenericConformance->getWitnessDeclRef(requirement, resolver); if (!baseWitness || !baseWitness.isSpecialized()) return baseWitness; @@ -890,21 +891,15 @@ SpecializedProtocolConformance::getWitnessDeclRef(ValueDecl *requirement, auto specializationMap = getSubstitutionMap(); auto witnessDecl = baseWitness.getDecl(); - auto witnessSig = - witnessDecl->getInnermostDeclContext()->getGenericSignatureOfContext(); - auto witnessMap = - witnessSig->getSubstitutionMap(baseWitness.getSubstitutions()); + auto witnessMap = baseWitness.getSubstitutions(); auto combinedMap = witnessMap.subst(specializationMap); - SmallVector substSubs; - witnessSig->getSubstitutions(combinedMap, substSubs); - // Fast path if the substitutions didn't change. - if (SubstitutionList(substSubs) == baseWitness.getSubstitutions()) + if (combinedMap == baseWitness.getSubstitutions()) return baseWitness; - return ConcreteDeclRef(witnessDecl->getASTContext(), witnessDecl, substSubs); + return ConcreteDeclRef(witnessDecl, combinedMap); } ProtocolConformanceRef @@ -1181,8 +1176,7 @@ DeclContext::getLocalProtocols( // Sort if required. if (sorted) { - llvm::array_pod_sort(result.begin(), result.end(), - &ProtocolType::compareProtocols); + llvm::array_pod_sort(result.begin(), result.end(), TypeDecl::compare); } return result; @@ -1250,74 +1244,13 @@ bool ProtocolConformance::isCanonical() const { auto genericConformance = spec->getGenericConformance(); if (!genericConformance->isCanonical()) return false; - auto specSubs = spec->getGenericSubstitutions(); - for (const auto &sub : specSubs) { - if (!sub.isCanonical()) - return false; - } + if (!spec->getSubstitutionMap().isCanonical()) return false; return true; } } llvm_unreachable("bad ProtocolConformanceKind"); } -Substitution Substitution::getCanonicalSubstitution(bool *wasCanonical) const { - bool createdNewCanonicalConformances = false; - bool createdCanReplacement = false; - SmallVector newCanConformances; - - CanType canReplacement = getReplacement()->getCanonicalType(); - - if (!getReplacement()->isCanonical()) { - createdCanReplacement = true; - } - - for (auto conf : getConformances()) { - if (conf.isCanonical()) { - newCanConformances.push_back(conf); - continue; - } - newCanConformances.push_back(conf.getCanonicalConformanceRef()); - createdNewCanonicalConformances = true; - } - - ArrayRef canConformances = getConformances(); - if (createdNewCanonicalConformances) { - auto &C = canReplacement->getASTContext(); - canConformances = C.AllocateCopy(newCanConformances); - } - - if (createdCanReplacement || createdNewCanonicalConformances) { - if (wasCanonical) - *wasCanonical = false; - return Substitution(canReplacement, canConformances); - } - if (wasCanonical) - *wasCanonical = true; - return *this; -} - -SubstitutionList -swift::getCanonicalSubstitutionList(SubstitutionList subs, - SmallVectorImpl &canSubs) { - bool subListWasCanonical = true; - for (auto &sub : subs) { - bool subWasCanonical = false; - auto canSub = sub.getCanonicalSubstitution(&subWasCanonical); - if (!subWasCanonical) - subListWasCanonical = false; - canSubs.push_back(canSub); - } - - if (subListWasCanonical) { - canSubs.clear(); - return subs; - } - - subs = canSubs; - return subs; -} - /// Check of all types used by the conformance are canonical. ProtocolConformance *ProtocolConformance::getCanonicalConformance() { if (isCanonical()) @@ -1343,13 +1276,10 @@ ProtocolConformance *ProtocolConformance::getCanonicalConformance() { // Substitute the substitutions in the specialized conformance. auto spec = cast(this); auto genericConformance = spec->getGenericConformance(); - auto specSubs = spec->getGenericSubstitutions(); - SmallVector newSpecSubs; - auto canSpecSubs = getCanonicalSubstitutionList(specSubs, newSpecSubs); return Ctx.getSpecializedConformance( - getType()->getCanonicalType(), - genericConformance->getCanonicalConformance(), - newSpecSubs.empty() ? canSpecSubs : Ctx.AllocateCopy(canSpecSubs)); + getType()->getCanonicalType(), + genericConformance->getCanonicalConformance(), + spec->getSubstitutionMap().getCanonical()); } } llvm_unreachable("bad ProtocolConformanceKind"); diff --git a/lib/AST/RequirementEnvironment.cpp b/lib/AST/RequirementEnvironment.cpp index 8a29803b8fb3c..a7cd8cddc8862 100644 --- a/lib/AST/RequirementEnvironment.cpp +++ b/lib/AST/RequirementEnvironment.cpp @@ -110,10 +110,8 @@ RequirementEnvironment::RequirementEnvironment( return substGenericParam; }, [selfType, substConcreteType, conformance, conformanceDC, &ctx]( - CanType type, Type replacement, ProtocolType *protoType) + CanType type, Type replacement, ProtocolDecl *proto) -> Optional { - auto proto = protoType->getDecl(); - // The protocol 'Self' conforms concretely to the conforming type. if (type->isEqual(selfType)) { ProtocolConformance *specialized = conformance; @@ -209,13 +207,6 @@ RequirementEnvironment::RequirementEnvironment( // Next, add each of the requirements (mapped from the requirement's // interface types into the abstract type parameters). for (auto &rawReq : reqSig->getRequirements()) { - // FIXME: This should not be necessary, since the constraint is redundant, - // but we need it to work around some crashes for now. - if (rawReq.getKind() == RequirementKind::Conformance && - rawReq.getFirstType()->isEqual(selfType) && - rawReq.getSecondType()->isEqual(proto->getDeclaredType())) - continue; - if (auto req = rawReq.subst(reqToSyntheticEnvMap)) builder.addRequirement(*req, source, conformanceDC->getParentModule()); } diff --git a/lib/AST/Stmt.cpp b/lib/AST/Stmt.cpp index ea1a4fe13d89f..ae94dd9c69636 100644 --- a/lib/AST/Stmt.cpp +++ b/lib/AST/Stmt.cpp @@ -368,10 +368,10 @@ SourceLoc CaseLabelItem::getEndLoc() const { } CaseStmt::CaseStmt(SourceLoc CaseLoc, ArrayRef CaseLabelItems, - bool HasBoundDecls, SourceLoc ColonLoc, Stmt *Body, - Optional Implicit) + bool HasBoundDecls, SourceLoc UnknownAttrLoc, + SourceLoc ColonLoc, Stmt *Body, Optional Implicit) : Stmt(StmtKind::Case, getDefaultImplicitFlag(Implicit, CaseLoc)), - CaseLoc(CaseLoc), ColonLoc(ColonLoc), + UnknownAttrLoc(UnknownAttrLoc), CaseLoc(CaseLoc), ColonLoc(ColonLoc), BodyAndHasBoundDecls(Body, HasBoundDecls) { Bits.CaseStmt.NumPatterns = CaseLabelItems.size(); assert(Bits.CaseStmt.NumPatterns > 0 && @@ -387,12 +387,13 @@ CaseStmt::CaseStmt(SourceLoc CaseLoc, ArrayRef CaseLabelItems, CaseStmt *CaseStmt::create(ASTContext &C, SourceLoc CaseLoc, ArrayRef CaseLabelItems, - bool HasBoundDecls, SourceLoc ColonLoc, Stmt *Body, + bool HasBoundDecls, SourceLoc UnknownAttrLoc, + SourceLoc ColonLoc, Stmt *Body, Optional Implicit) { void *Mem = C.Allocate(totalSizeToAlloc(CaseLabelItems.size()), alignof(CaseStmt)); - return ::new (Mem) CaseStmt(CaseLoc, CaseLabelItems, HasBoundDecls, ColonLoc, - Body, Implicit); + return ::new (Mem) CaseStmt(CaseLoc, CaseLabelItems, HasBoundDecls, + UnknownAttrLoc, ColonLoc, Body, Implicit); } SwitchStmt *SwitchStmt::create(LabeledStmtInfo LabelInfo, SourceLoc SwitchLoc, diff --git a/lib/AST/Substitution.cpp b/lib/AST/Substitution.cpp deleted file mode 100644 index 5eeed6e194c0b..0000000000000 --- a/lib/AST/Substitution.cpp +++ /dev/null @@ -1,54 +0,0 @@ -//===--- Substitution.cpp - Type substitutions ----------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// -// -// This file implements the Substitution class and operations on it. -// -//===----------------------------------------------------------------------===// - -#include "swift/AST/Substitution.h" - -#include "swift/AST/ASTContext.h" -#include "swift/AST/GenericEnvironment.h" -#include "swift/AST/Module.h" -#include "swift/AST/ProtocolConformance.h" -#include "swift/AST/SubstitutionMap.h" -#include "swift/AST/Types.h" -#include "llvm/ADT/DenseMap.h" - -using namespace swift; - -bool Substitution::operator==(const Substitution &other) const { - // The archetypes may be missing, but we can compare them directly - // because archetypes are always canonical. - return - Replacement->isEqual(other.Replacement) && - Conformance.equals(other.Conformance); -} - -Substitution::Substitution(Type Replacement, - ArrayRef Conformance) - : Replacement(Replacement), Conformance(Conformance) -{ - // The replacement type must be materializable. - assert(Replacement->isMaterializable() - && "cannot substitute with a non-materializable type"); -} - -bool Substitution::isCanonical() const { - if (!getReplacement()->isCanonical()) - return false; - for (auto conf : getConformances()) { - if (!conf.isCanonical()) - return false; - } - return true; -} diff --git a/lib/AST/SubstitutionList.cpp b/lib/AST/SubstitutionList.cpp deleted file mode 100644 index b84fd662d5515..0000000000000 --- a/lib/AST/SubstitutionList.cpp +++ /dev/null @@ -1,33 +0,0 @@ -//===--- SubstitutionList.cpp - Compact SubstitutionMap -------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// -// -// This file defines the SubstitutionList class, which is a memory-efficient -// representation of a SubstitutionMap, intended to be stored in AST nodes and -// SIL instructions. -// -//===----------------------------------------------------------------------===// - -#include "swift/AST/SubstitutionList.h" -#include "swift/AST/ProtocolConformanceRef.h" -#include "llvm/ADT/FoldingSet.h" - -using namespace swift; - -void swift::profileSubstitutionList(llvm::FoldingSetNodeID &id, - SubstitutionList subs) { - id.AddInteger(subs.size()); - for (auto &sub : subs) { - id.AddPointer(sub.getReplacement().getPointer()); - for (auto conformance : sub.getConformances()) - id.AddPointer(conformance.getOpaqueValue()); - } -} diff --git a/lib/AST/SubstitutionMap.cpp b/lib/AST/SubstitutionMap.cpp index 59784ff916fdd..cdb536c255f8a 100644 --- a/lib/AST/SubstitutionMap.cpp +++ b/lib/AST/SubstitutionMap.cpp @@ -23,6 +23,7 @@ //===----------------------------------------------------------------------===// #include "swift/AST/SubstitutionMap.h" +#include "SubstitutionMapStorage.h" #include "swift/AST/ASTContext.h" #include "swift/AST/Decl.h" #include "swift/AST/GenericEnvironment.h" @@ -34,46 +35,77 @@ using namespace swift; -ArrayRef SubstitutionMap::getReplacementTypes() const { - if (empty()) return { }; - - return llvm::makeArrayRef(replacementTypes.get(), - genericSig->getGenericParams().size()); +SubstitutionMap::Storage::Storage( + GenericSignature *genericSig, + ArrayRef replacementTypes, + ArrayRef conformances) + : genericSig(genericSig), + numConformanceRequirements(genericSig->getNumConformanceRequirements()) +{ + assert(replacementTypes.size() == getNumReplacementTypes()); + assert(conformances.size() == numConformanceRequirements); + + std::copy(replacementTypes.begin(), replacementTypes.end(), + getReplacementTypes().data()); + std::copy(conformances.begin(), conformances.end(), + getConformances().data()); + populatedAllReplacements = false; } -MutableArrayRef SubstitutionMap::getReplacementTypes() { - if (empty()) return { }; +SubstitutionMap::SubstitutionMap( + GenericSignature *genericSig, + ArrayRef replacementTypes, + ArrayRef conformances) + : storage(Storage::get(genericSig, replacementTypes, conformances)) { } - return MutableArrayRef(replacementTypes.get(), - genericSig->getGenericParams().size()); +ArrayRef SubstitutionMap::getReplacementTypesBuffer() const { + return storage ? storage->getReplacementTypes() : ArrayRef(); +} +MutableArrayRef SubstitutionMap::getReplacementTypesBuffer() { + return storage ? storage->getReplacementTypes() : MutableArrayRef(); } -SubstitutionMap::SubstitutionMap(GenericSignature *genericSig) : genericSig(genericSig) { - if (genericSig) { - replacementTypes.reset(new Type [genericSig->getGenericParams().size()]); - } +MutableArrayRef +SubstitutionMap::getConformancesBuffer() { + return storage ? storage->getConformances() + : MutableArrayRef(); } -SubstitutionMap::SubstitutionMap(GenericEnvironment *genericEnv) - : SubstitutionMap(genericEnv->getGenericSignature()) { } +ArrayRef SubstitutionMap::getConformances() const { + return storage ? storage->getConformances() + : ArrayRef(); +} -SubstitutionMap::SubstitutionMap(const SubstitutionMap &other) - : SubstitutionMap(other.getGenericSignature()) -{ - std::copy(other.getReplacementTypes().begin(), - other.getReplacementTypes().end(), - getReplacementTypes().begin()); +ArrayRef SubstitutionMap::getReplacementTypes() const { + if (empty()) return { }; + + // Make sure we've filled in all of the replacement types. + if (!storage->populatedAllReplacements) { + for (auto gp : getGenericSignature()->getGenericParams()) { + (void)lookupSubstitution(cast(gp->getCanonicalType())); + } - conformanceMap = other.conformanceMap; + storage->populatedAllReplacements = true; + } + + return getReplacementTypesBuffer(); +} + +GenericSignature *SubstitutionMap::getGenericSignature() const { + return storage ? storage->getGenericSignature() : nullptr; } -SubstitutionMap &SubstitutionMap::operator=(const SubstitutionMap &other) { - *this = SubstitutionMap(other); - return *this; +bool SubstitutionMap::empty() const { + return getGenericSignature() == nullptr; } -SubstitutionMap::~SubstitutionMap() { } +bool SubstitutionMap::hasAnySubstitutableParams() const { + auto genericSig = getGenericSignature(); + if (!genericSig) return false; + + return !genericSig->areAllParamsConcrete(); +} bool SubstitutionMap::hasArchetypes() const { for (Type replacementTy : getReplacementTypes()) { @@ -99,7 +131,110 @@ bool SubstitutionMap::hasDynamicSelf() const { return false; } +bool SubstitutionMap::isCanonical() const { + if (empty()) return true; + + if (!getGenericSignature()->isCanonical()) return false; + + for (Type replacementTy : getReplacementTypes()) { + if (replacementTy && !replacementTy->isCanonical()) + return false; + } + + for (auto conf : getConformances()) { + if (!conf.isCanonical()) + return false; + } + + return true; +} + +SubstitutionMap SubstitutionMap::getCanonical() const { + if (empty()) return *this; + + auto canonicalSig = getGenericSignature()->getCanonicalSignature(); + SmallVector replacementTypes; + for (Type replacementType : getReplacementTypes()) { + if (replacementType) + replacementTypes.push_back(replacementType->getCanonicalType()); + else + replacementTypes.push_back(nullptr); + } + + SmallVector conformances; + for (auto conf : getConformances()) { + conformances.push_back(conf.getCanonicalConformanceRef()); + } + + return SubstitutionMap::get(canonicalSig, + ArrayRef(replacementTypes), + ArrayRef(conformances)); +} + + +SubstitutionMap SubstitutionMap::get(GenericSignature *genericSig, + SubstitutionMap substitutions) { + if (!genericSig) { + assert(!substitutions.hasAnySubstitutableParams() && + "Shouldn't have substitutions here"); + return SubstitutionMap(); + } + + return SubstitutionMap::get(genericSig, + [&](SubstitutableType *type) -> Type { + return substitutions.lookupSubstitution( + CanSubstitutableType(type)); + }, + LookUpConformanceInSubstitutionMap(substitutions)); +} + +/// Build an interface type substitution map for the given generic signature +/// from a type substitution function and conformance lookup function. +SubstitutionMap SubstitutionMap::get(GenericSignature *genericSig, + TypeSubstitutionFn subs, + LookupConformanceFn lookupConformance) { + if (!genericSig) { + return SubstitutionMap(); + } + + // Form the replacement types. + SmallVector replacementTypes; + replacementTypes.reserve(genericSig->getGenericParams().size()); + for (auto gp : genericSig->getGenericParams()) { + // Don't eagerly form replacements for non-canonical generic parameters. + if (!genericSig->isCanonicalTypeInContext(gp->getCanonicalType())) { + replacementTypes.push_back(Type()); + continue; + } + + // Record the replacement. + Type replacement = Type(gp).subst(subs, lookupConformance, + SubstFlags::UseErrorType); + replacementTypes.push_back(replacement); + } + + // Form the stored conformances. + SmallVector conformances; + for (const auto &req : genericSig->getRequirements()) { + if (req.getKind() != RequirementKind::Conformance) continue; + + CanType depTy = req.getFirstType()->getCanonicalType(); + auto replacement = depTy.subst(subs, lookupConformance, + SubstFlags::UseErrorType); + auto protoType = req.getSecondType()->castTo(); + auto proto = protoType->getDecl(); + auto conformance = lookupConformance(depTy, replacement, proto) + .getValueOr(ProtocolConformanceRef(proto)); + conformances.push_back(conformance); + } + + return SubstitutionMap(genericSig, replacementTypes, conformances); +} + Type SubstitutionMap::lookupSubstitution(CanSubstitutableType type) const { + if (empty()) + return Type(); + // If we have an archetype, map out of the context so we can compute a // conformance access path. if (auto archetype = dyn_cast(type)) { @@ -115,7 +250,7 @@ Type SubstitutionMap::lookupSubstitution(CanSubstitutableType type) const { // have. auto genericParam = cast(type); auto mutableThis = const_cast(this); - auto replacementTypes = mutableThis->getReplacementTypes(); + auto replacementTypes = mutableThis->getReplacementTypesBuffer(); auto genericSig = getGenericSignature(); assert(genericSig); auto genericParams = genericSig->getGenericParams(); @@ -140,30 +275,38 @@ Type SubstitutionMap::lookupSubstitution(CanSubstitutableType type) const { // Substitute into the replacement type. replacementType = concreteType.subst(*this); + + // If the generic signature is canonical, canonicalize the replacement type. + if (getGenericSignature()->isCanonical()) + replacementType = replacementType->getCanonicalType(); + return replacementType; } - // Not known. - return Type(); -} + // The generic parameter may not be canonical. Retrieve the canonical + // type, which will be dependent. + CanType canonicalType = genericSig->getCanonicalTypeInContext(genericParam); -void SubstitutionMap:: -addSubstitution(CanGenericTypeParamType type, Type replacement) { - assert(getGenericSignature() && - "cannot add entries to empty substitution map"); + // If nothing changed, we don't have a replacement. + if (canonicalType == type) return Type(); - auto replacementTypes = getReplacementTypes(); - auto genericParams = getGenericSignature()->getGenericParams(); - auto replacementIndex = GenericParamKey(type).findIndexIn(genericParams); + // If we're left with a substitutable type, substitute into that. + // First, set the replacement type to an error, to block infinite recursion. + replacementType = ErrorType::get(type); - assert((!replacementTypes[replacementIndex] || - replacementTypes[replacementIndex]->isEqual(replacement))); + replacementType = lookupSubstitution(cast(canonicalType)); - replacementTypes[replacementIndex] = replacement; + // If the generic signature is canonical, canonicalize the replacement type. + if (getGenericSignature()->isCanonical()) + replacementType = replacementType->getCanonicalType(); + + return replacementType; } Optional SubstitutionMap::lookupConformance(CanType type, ProtocolDecl *proto) const { + if (empty()) return None; + // If we have an archetype, map out of the context so we can compute a // conformance access path. if (auto archetype = dyn_cast(type)) { @@ -178,13 +321,18 @@ SubstitutionMap::lookupConformance(CanType type, ProtocolDecl *proto) const { // Retrieve the starting conformance from the conformance map. auto getInitialConformance = [&](Type type, ProtocolDecl *proto) -> Optional { - auto known = conformanceMap.find(type->getCanonicalType()); - if (known == conformanceMap.end()) - return None; - - for (auto conformance : known->second) { - if (conformance.getRequirement() == proto) - return conformance; + unsigned conformanceIndex = 0; + for (const auto &req : getGenericSignature()->getRequirements()) { + if (req.getKind() != RequirementKind::Conformance) + continue; + + // Is this the conformance we're looking for? + if (req.getFirstType()->isEqual(type) && + req.getSecondType()->castTo()->getDecl() == proto) { + return getConformances()[conformanceIndex]; + } + + ++conformanceIndex; } return None; @@ -200,7 +348,7 @@ SubstitutionMap::lookupConformance(CanType type, ProtocolDecl *proto) const { return LookUpConformanceInSignature(*getGenericSignature())( type->getCanonicalType(), superclass, - proto->getDeclaredType()); + proto); } return None; @@ -273,47 +421,32 @@ SubstitutionMap::lookupConformance(CanType type, ProtocolDecl *proto) const { return conformance; } -void SubstitutionMap:: -addConformance(CanType type, ProtocolConformanceRef conformance) { - assert(!isa(type)); - conformanceMap[type].push_back(conformance); -} - SubstitutionMap SubstitutionMap::mapReplacementTypesOutOfContext() const { return subst(MapTypeOutOfContext(), MakeAbstractConformanceForGenericType()); } -SubstitutionMap SubstitutionMap::subst(const SubstitutionMap &subMap) const { +SubstitutionMap SubstitutionMap::subst(SubstitutionMap subMap) const { return subst(QuerySubstitutionMap{subMap}, LookUpConformanceInSubstitutionMap(subMap)); } SubstitutionMap SubstitutionMap::subst(TypeSubstitutionFn subs, LookupConformanceFn conformances) const { - SubstitutionMap result(*this); - - for (auto &replacementType : result.getReplacementTypes()) { - if (replacementType) { - replacementType = replacementType.subst(subs, conformances, - SubstFlags::UseErrorType); - } - } - - for (auto iter = result.conformanceMap.begin(), - end = result.conformanceMap.end(); - iter != end; ++iter) { - auto origType = Type(iter->first).subst( - *this, SubstFlags::UseErrorType); - for (auto citer = iter->second.begin(), - cend = iter->second.end(); - citer != cend; ++citer) { - *citer = citer->subst(origType, subs, conformances); - } - } - - result.verify(); + if (empty()) return SubstitutionMap(); - return result; + return getGenericSignature()->getSubstitutionMap( + [&](SubstitutableType *type) { + return Type(type).subst(*this, SubstFlags::UseErrorType) + .subst(subs, conformances, SubstFlags::UseErrorType); + }, + [&](CanType dependentType, Type replacementType, + ProtocolDecl *proto) ->Optional { + auto conformance = + lookupConformance(dependentType, proto) + .getValueOr(ProtocolConformanceRef(proto)); + auto substType = dependentType.subst(*this, SubstFlags::UseErrorType); + return conformance.subst(substType, subs, conformances); + }); } SubstitutionMap @@ -331,10 +464,9 @@ SubstitutionMap::getProtocolSubstitutions(ProtocolDecl *protocol, // inside generic types. return Type(); }, - [&](CanType origType, Type replacementType, ProtocolType *protoType) + [&](CanType origType, Type replacementType, ProtocolDecl *protoType) -> Optional { - if (origType->isEqual(protocolSelfType) && - protoType->getDecl() == protocol) + if (origType->isEqual(protocolSelfType) && protoType == protocol) return conformance; // This will need to change if we ever support protocols @@ -406,8 +538,8 @@ SubstitutionMap::getOverrideSubstitutions(const ClassDecl *baseClass, } SubstitutionMap -SubstitutionMap::combineSubstitutionMaps(const SubstitutionMap &firstSubMap, - const SubstitutionMap &secondSubMap, +SubstitutionMap::combineSubstitutionMaps(SubstitutionMap firstSubMap, + SubstitutionMap secondSubMap, CombineSubstitutionMaps how, unsigned firstDepthOrIndex, unsigned secondDepthOrIndex, @@ -444,13 +576,12 @@ SubstitutionMap::combineSubstitutionMaps(const SubstitutionMap &firstSubMap, return Type(replacement).subst(secondSubMap); return Type(type).subst(firstSubMap); }, - [&](CanType type, Type substType, ProtocolType *conformedProtocol) { + [&](CanType type, Type substType, ProtocolDecl *conformedProtocol) { auto replacement = type.transform(replaceGenericParameter); if (replacement) return secondSubMap.lookupConformance(replacement->getCanonicalType(), - conformedProtocol->getDecl()); - return firstSubMap.lookupConformance(type, - conformedProtocol->getDecl()); + conformedProtocol); + return firstSubMap.lookupConformance(type, conformedProtocol); }); } @@ -491,69 +622,7 @@ void SubstitutionMap::verify() const { #endif } -void SubstitutionMap::dump(llvm::raw_ostream &out) const { - auto *genericSig = getGenericSignature(); - if (genericSig == nullptr) { - out << "Empty substitution map\n"; - return; - } - out << "Generic signature: "; - genericSig->print(out); - out << "\n"; - out << "Substitutions:\n"; - auto genericParams = genericSig->getGenericParams(); - auto replacementTypes = getReplacementTypes(); - for (unsigned i : indices(genericParams)) { - out.indent(2); - genericParams[i]->print(out); - out << " -> "; - if (replacementTypes[i]) - replacementTypes[i]->print(out); - else - out << "<>"; - out << "\n"; - } - - out << "\nConformance map:\n"; - for (const auto &conformances : conformanceMap) { - out.indent(2); - conformances.first->print(out); - out << " -> ["; - interleave(conformances.second.begin(), conformances.second.end(), - [&](ProtocolConformanceRef conf) { - conf.dump(out); - }, - [&] { - out << ", "; - }); - out << "]\n"; - } -} - -void SubstitutionMap::dump() const { - return dump(llvm::errs()); -} - void SubstitutionMap::profile(llvm::FoldingSetNodeID &id) const { - // Generic signature. - id.AddPointer(genericSig); - - if (empty() || !genericSig) return; - - // Replacement types. - for (Type gp : genericSig->getGenericParams()) { - id.AddPointer(gp.subst(*this).getPointer()); - } - - // Conformance requirements. - for (const auto &req : genericSig->getRequirements()) { - if (req.getKind() != RequirementKind::Conformance) - continue; - - auto conformance = - lookupConformance(req.getFirstType()->getCanonicalType(), - req.getSecondType()->castTo()->getDecl()); - id.AddPointer(conformance ? conformance->getOpaqueValue() : nullptr); - } + id.AddPointer(storage); } diff --git a/lib/AST/SubstitutionMapStorage.h b/lib/AST/SubstitutionMapStorage.h new file mode 100644 index 0000000000000..5329456a3e9e1 --- /dev/null +++ b/lib/AST/SubstitutionMapStorage.h @@ -0,0 +1,117 @@ +//===--- SubstitutionMapStorage.h - Substitution Map Storage ----*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines the SubstitutionMap::Storage class, which is used as the +// backing storage for SubstitutionMap. +// +//===----------------------------------------------------------------------===// +#ifndef SWIFT_AST_SUBSTITUTION_MAP_STORAGE_H +#define SWIFT_AST_SUBSTITUTION_MAP_STORAGE_H + +#include "swift/AST/GenericSignature.h" +#include "swift/AST/SubstitutionMap.h" +#include "llvm/Support/TrailingObjects.h" +#include "llvm/ADT/FoldingSet.h" + +namespace swift { + +class SubstitutionMap::Storage final + : public llvm::FoldingSetNode, + llvm::TrailingObjects +{ + friend TrailingObjects; + + /// The generic signature for which we are performing substitutions. + GenericSignature * const genericSig; + + /// The number of conformance requirements, cached to avoid constantly + /// recomputing it on conformance-buffer access. + const unsigned numConformanceRequirements : 31; + + /// Whether we've populated all replacement types already. + unsigned populatedAllReplacements : 1; + + Storage() = delete; + + Storage(GenericSignature *genericSig, + ArrayRef replacementTypes, + ArrayRef conformances); + + friend class SubstitutionMap; + +private: + unsigned getNumReplacementTypes() const { + return genericSig->getGenericParams().size(); + } + + size_t numTrailingObjects(OverloadToken) const { + return getNumReplacementTypes(); + } + + size_t numTrailingObjects(OverloadToken) const { + return numConformanceRequirements; + } + +public: + /// Form storage for the given generic signature and its replacement + /// types and conformances. + static Storage *get(GenericSignature *genericSig, + ArrayRef replacementTypes, + ArrayRef conformances); + + /// Retrieve the generic signature that describes the shape of this + /// storage. + GenericSignature *getGenericSignature() const { return genericSig; } + + /// Retrieve the array of replacement types, which line up with the + /// generic parameters. + /// + /// Note that the types may be null, for cases where the generic parameter + /// is concrete but hasn't been queried yet. + ArrayRef getReplacementTypes() const { + return llvm::makeArrayRef(getTrailingObjects(), + getNumReplacementTypes()); + } + + MutableArrayRef getReplacementTypes() { + return MutableArrayRef(getTrailingObjects(), + getNumReplacementTypes()); + } + + /// Retrieve the array of protocol conformances, which line up with the + /// requirements of the generic signature. + ArrayRef getConformances() const { + return llvm::makeArrayRef(getTrailingObjects(), + numConformanceRequirements); + } + MutableArrayRef getConformances() { + return MutableArrayRef( + getTrailingObjects(), + numConformanceRequirements); + } + + /// Profile the substitution map storage, for use with LLVM's FoldingSet. + void Profile(llvm::FoldingSetNodeID &id) const { + Profile(id, getGenericSignature(), getReplacementTypes(), + getConformances()); + } + + /// Profile the substitution map storage, for use with LLVM's FoldingSet. + static void Profile(llvm::FoldingSetNodeID &id, + GenericSignature *genericSig, + ArrayRef replacementTypes, + ArrayRef conformances); +}; + +} + +#endif // SWIFT_AST_SUBSTITUTION_MAP_STORAGE_H diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 15344131fb2b1..308b536275324 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -66,6 +66,11 @@ Type QuerySubstitutionMap::operator()(SubstitutableType *type) const { return subMap.lookupSubstitution(key); } +void TypeLoc::setType(Type Ty, bool validated) { + assert(!Ty || !Ty->hasTypeVariable()); + TAndValidBit.setPointerAndInt(Ty, validated); +} + bool TypeLoc::isError() const { assert(wasValidated() && "Type not yet validated"); return getType()->hasError(); @@ -97,12 +102,6 @@ void *TypeBase::operator new(size_t bytes, const ASTContext &ctx, return ctx.Allocate(bytes, alignment, arena); } -bool CanType::isActuallyCanonicalOrNull() const { - return getPointer() == nullptr || - getPointer() == llvm::DenseMapInfo::getTombstoneKey() || - getPointer()->isCanonical(); -} - NominalTypeDecl *CanType::getAnyNominal() const { return dyn_cast_or_null(getAnyGeneric()); } @@ -249,7 +248,6 @@ ExistentialLayout::ExistentialLayout(ProtocolType *type) { containsNonObjCProtocol = !protoDecl->isObjC(); singleProtocol = type; - protocols = { &singleProtocol, 1 }; } ExistentialLayout::ExistentialLayout(ProtocolCompositionType *type) { @@ -800,8 +798,9 @@ swift::decomposeArgType(Type type, ArrayRef argumentLabels) { return result; } -void swift::computeDefaultMap(Type type, const ValueDecl *paramOwner, - unsigned level, SmallVectorImpl &outDefaultMap) { +void swift::computeDefaultMap(ArrayRef params, + const ValueDecl *paramOwner, unsigned level, + SmallVectorImpl &outDefaultMap) { // Find the corresponding parameter list. const ParameterList *paramList = nullptr; if (paramOwner) { @@ -816,35 +815,29 @@ void swift::computeDefaultMap(Type type, const ValueDecl *paramOwner, paramList = enumElement->getParameterList(); } } - - switch (type->getKind()) { - case TypeKind::Tuple: { - auto tupleTy = cast(type.getPointer()); + switch (params.size()) { + case 0: + break; + + case 1: + outDefaultMap.push_back(paramList && paramList->size() == 1 && + paramList->get(0)->isDefaultArgument()); + break; + + default: // Arguments and parameters are not guaranteed to always line-up // perfectly, e.g. failure diagnostics tries to match argument type // to different "candidate" parameters. - if (paramList && tupleTy->getNumElements() != paramList->size()) + if (paramList && params.size() != paramList->size()) paramList = nullptr; - for (auto i : range(0, tupleTy->getNumElements())) { + for (auto i : range(0, params.size())) { outDefaultMap.push_back(paramList && paramList->get(i)->isDefaultArgument()); } break; } - - case TypeKind::Paren: { - outDefaultMap.push_back(paramList && paramList->size() == 1 && - paramList->get(0)->isDefaultArgument()); - break; - } - - default: { - outDefaultMap.push_back(false); - break; - } - } } /// Turn a param list into a symbolic and printable representation that does not @@ -942,12 +935,11 @@ static void addProtocols(Type T, /// \brief Add the protocol (or protocols) in the type T to the stack of /// protocols, checking whether any of the protocols had already been seen and /// zapping those in the original list that we find again. -static void addMinimumProtocols(Type T, - SmallVectorImpl &Protocols, - llvm::SmallDenseMap &Known, - llvm::SmallPtrSet &Visited, - SmallVector &Stack, - bool &ZappedAny) { +static void +addMinimumProtocols(Type T, SmallVectorImpl &Protocols, + llvm::SmallDenseMap &Known, + llvm::SmallPtrSetImpl &Visited, + SmallVector &Stack, bool &ZappedAny) { if (auto Proto = T->getAs()) { auto KnownPos = Known.find(Proto->getDecl()); if (KnownPos != Known.end()) { @@ -972,12 +964,6 @@ static void addMinimumProtocols(Type T, } } -/// \brief Compare two protocols to establish an ordering between them. -int ProtocolType::compareProtocols(ProtocolDecl * const* PP1, - ProtocolDecl * const* PP2) { - return TypeDecl::compare(*PP1, *PP2); -} - bool ProtocolType::visitAllProtocols( ArrayRef protocols, llvm::function_ref fn) { @@ -1053,7 +1039,7 @@ void ProtocolType::canonicalizeProtocols( // Sort the set of protocols by module + name, to give a stable // ordering. - llvm::array_pod_sort(protocols.begin(), protocols.end(), compareProtocols); + llvm::array_pod_sort(protocols.begin(), protocols.end(), TypeDecl::compare); } static Type @@ -1257,8 +1243,7 @@ CanType TypeBase::computeCanonicalType() { // Cache the canonical type for future queries. assert(Result && "Case not implemented!"); - CanonicalType = Result; - return CanType(Result); + return CanonicalType = CanType(Result); } CanType TypeBase::getCanonicalType(GenericSignature *sig) { @@ -1347,33 +1332,24 @@ Type SugarType::getSinglyDesugaredTypeSlow() { return UnderlyingType; } -SubstitutionMap NameAliasType::getSubstitutionMap() const { - if (auto genericSig = getGenericSignature()) - return genericSig->getSubstitutionMap(getSubstitutionList()); - - return SubstitutionMap(); -} - SmallVector NameAliasType::getInnermostGenericArgs() const { SmallVector result; // If the typealias is not generic, there are no generic arguments if (!typealias->isGeneric()) return result; - auto genericSig = typealias->getGenericSignature(); - if (!genericSig) return result; - - // If the substitution list was empty, bail out. - if (getSubstitutionList().empty()) return result; + // If the substitution map is empty, bail out. + auto subMap = getSubstitutionMap(); + if (subMap.empty()) return result; // Retrieve the substitutions for the generic parameters (only). + auto genericSig = subMap.getGenericSignature(); unsigned numAllGenericParams = genericSig->getGenericParams().size(); - auto genericArgSubs = getSubstitutionList().slice(0, numAllGenericParams); - - // Copy the replacement types for the innermost generic arguments. unsigned numMyGenericParams = typealias->getGenericParams()->size(); - for (const auto &sub : genericArgSubs.take_back(numMyGenericParams)) { - result.push_back(sub.getReplacement()); + result.reserve(numMyGenericParams); + unsigned startIndex = numAllGenericParams - numMyGenericParams; + for (auto gp : genericSig->getGenericParams().slice(startIndex)) { + result.push_back(Type(gp).subst(subMap, SubstFlags::UseErrorType)); } return result; } @@ -1673,33 +1649,29 @@ bool TypeBase::isBindableTo(Type b) { moduleDecl, decl, decl->getGenericEnvironment()); auto *genericSig = decl->getGenericSignature(); - auto result = genericSig->enumeratePairedRequirements( - [&](Type t, ArrayRef reqts) -> bool { - auto orig = t.subst(origSubMap)->getCanonicalType(); - auto subst = t.subst(substSubMap)->getCanonicalType(); - if (!visit(orig, subst)) - return true; - - auto canTy = t->getCanonicalType(); - for (auto reqt : reqts) { - auto *proto = reqt.getSecondType()->castTo() - ->getDecl(); - auto origConf = *origSubMap.lookupConformance(canTy, proto); - auto substConf = *substSubMap.lookupConformance(canTy, proto); - - if (origConf.isConcrete()) { - if (!substConf.isConcrete()) - return true; - if (origConf.getConcrete()->getRootNormalConformance() - != substConf.getConcrete()->getRootNormalConformance()) - return true; - } - } + for (auto gp : genericSig->getGenericParams()) { + auto orig = Type(gp).subst(origSubMap)->getCanonicalType(); + auto subst = Type(gp).subst(substSubMap)->getCanonicalType(); + if (!visit(orig, subst)) return false; - }); + } - if (result) - return false; + for (const auto &req : genericSig->getRequirements()) { + if (req.getKind() != RequirementKind::Conformance) continue; + + auto canTy = req.getFirstType()->getCanonicalType(); + auto *proto = req.getSecondType()->castTo()->getDecl(); + auto origConf = *origSubMap.lookupConformance(canTy, proto); + auto substConf = *substSubMap.lookupConformance(canTy, proto); + + if (origConf.isConcrete()) { + if (!substConf.isConcrete()) + return false; + if (origConf.getConcrete()->getRootNormalConformance() + != substConf.getConcrete()->getRootNormalConformance()) + return false; + } + } // Same decl should always either have or not have a parent. assert((bool)bgt->getParent() == (bool)substBGT->getParent()); @@ -2233,7 +2205,7 @@ namespace { static bool matchesFunctionType(CanAnyFunctionType fn1, CanAnyFunctionType fn2, TypeMatchOptions matchMode, OptionalUnwrapping insideOptional, - std::function paramsAndResultMatch) { + llvm::function_ref paramsAndResultMatch) { // FIXME: Handle generic functions in non-ABI matches. if (!matchMode.contains(TypeMatchFlags::AllowABICompatible)) { if (!isa(fn1) || !isa(fn2)) @@ -2342,7 +2314,7 @@ static bool matches(CanType t1, CanType t2, TypeMatchOptions matchMode, if (!fn1) return false; - std::function paramsAndResultMatch = [=]() { + auto paramsAndResultMatch = [&]() { // Inputs are contravariant, results are covariant. return (matches(fn2.getInput(), fn1.getInput(), matchMode, ParameterPosition::Parameter, OptionalUnwrapping::None) && @@ -2378,7 +2350,7 @@ bool TypeBase::matchesParameter(Type other, TypeMatchOptions matchMode) { } bool TypeBase::matchesFunctionType(Type other, TypeMatchOptions matchMode, - std::function paramsAndResultMatch) { + llvm::function_ref paramsAndResultMatch) { auto thisFnTy = dyn_cast(getCanonicalType()); auto otherFnTy = dyn_cast(other->getCanonicalType()); @@ -2790,25 +2762,12 @@ bool AnyFunctionType::isCanonicalFunctionInputType(Type input) { } FunctionType * -GenericFunctionType::substGenericArgs(SubstitutionList args) { - return substGenericArgs(getGenericSignature()->getSubstitutionMap(args)); -} - -FunctionType * -GenericFunctionType::substGenericArgs(const SubstitutionMap &subs) { +GenericFunctionType::substGenericArgs(SubstitutionMap subs) { Type input = getInput().subst(subs); Type result = getResult().subst(subs); return FunctionType::get(input, result, getExtInfo()); } -FunctionType * -GenericFunctionType::substGenericArgs(TypeSubstitutionFn subs, - LookupConformanceFn conformances) { - Type input = getInput().subst(subs, conformances); - Type result = getResult().subst(subs, conformances); - return FunctionType::get(input, result, getExtInfo()); -} - static Type getMemberForBaseType(LookupConformanceFn lookupConformances, Type origBase, Type substBase, @@ -2875,8 +2834,7 @@ static Type getMemberForBaseType(LookupConformanceFn lookupConformances, auto proto = assocType->getProtocol(); Optional conformance = lookupConformances(origBase->getCanonicalType(), - substBase, - proto->getDeclaredType()); + substBase, proto); if (!conformance) return failed(); if (!conformance->isConcrete()) return failed(); @@ -2909,39 +2867,39 @@ static Type getMemberForBaseType(LookupConformanceFn lookupConformances, Optional LookUpConformanceInModule::operator()(CanType dependentType, Type conformingReplacementType, - ProtocolType *conformedProtocol) const { + ProtocolDecl *conformedProtocol) const { if (conformingReplacementType->isTypeParameter()) - return ProtocolConformanceRef(conformedProtocol->getDecl()); + return ProtocolConformanceRef(conformedProtocol); return M->lookupConformance(conformingReplacementType, - conformedProtocol->getDecl()); + conformedProtocol); } Optional LookUpConformanceInSubstitutionMap::operator()(CanType dependentType, Type conformingReplacementType, - ProtocolType *conformedProtocol) const { - return Subs.lookupConformance(dependentType, conformedProtocol->getDecl()); + ProtocolDecl *conformedProtocol) const { + return Subs.lookupConformance(dependentType, conformedProtocol); } Optional MakeAbstractConformanceForGenericType::operator()(CanType dependentType, Type conformingReplacementType, - ProtocolType *conformedProtocol) const { + ProtocolDecl *conformedProtocol) const { assert((conformingReplacementType->is() || conformingReplacementType->is()) && "replacement requires looking up a concrete conformance"); - return ProtocolConformanceRef(conformedProtocol->getDecl()); + return ProtocolConformanceRef(conformedProtocol); } Optional LookUpConformanceInSignature::operator()(CanType dependentType, Type conformingReplacementType, - ProtocolType *conformedProtocol) const { + ProtocolDecl *conformedProtocol) const { // FIXME: Should pass dependentType instead, once // GenericSignature::lookupConformance() does the right thing return Sig.lookupConformance(conformingReplacementType->getCanonicalType(), - conformedProtocol->getDecl()); + conformedProtocol); } Type DependentMemberType::substBaseType(ModuleDecl *module, @@ -3077,23 +3035,12 @@ static Type substType(Type derivedType, // Special-case handle SILBoxTypes; we want to structurally substitute the // substitutions. if (auto boxTy = dyn_cast(type)) { - if (boxTy->getGenericArgs().empty()) - return Type(boxTy); - - auto subMap = boxTy->getLayout()->getGenericSignature() - ->getSubstitutionMap(boxTy->getGenericArgs()); - subMap = subMap.subst(substitutions, lookupConformances); - - SmallVector newSubs; - boxTy->getLayout()->getGenericSignature() - ->getSubstitutions(subMap, newSubs); - for (auto &arg : newSubs) { - arg = Substitution(arg.getReplacement()->getCanonicalType(), - arg.getConformances()); - } + auto subMap = boxTy->getSubstitutions(); + auto newSubMap = subMap.subst(substitutions, lookupConformances); + return SILBoxType::get(boxTy->getASTContext(), boxTy->getLayout(), - newSubs); + newSubMap); } // We only substitute for substitutable types and dependent member types. @@ -3160,7 +3107,7 @@ static Type substType(Type derivedType, }); } -Type Type::subst(const SubstitutionMap &substitutions, +Type Type::subst(SubstitutionMap substitutions, SubstOptions options) const { return substType(*this, QuerySubstitutionMap{substitutions}, @@ -3199,19 +3146,21 @@ const DependentMemberType *TypeBase::findUnresolvedDependentMemberType() { return unresolvedDepMemTy; } - -Type TypeBase::getSuperclassForDecl(const ClassDecl *baseClass) { - Type t(this); - +static Type getConcreteTypeForSuperclassTraversing(Type t) { if (!t->getAnyNominal()) { if (auto archetype = t->getAs()) { - t = archetype->getSuperclass(); + return archetype->getSuperclass(); } else if (auto dynamicSelfTy = t->getAs()) { - t = dynamicSelfTy->getSelfType(); + return dynamicSelfTy->getSelfType(); } else if (auto compositionTy = t->getAs()) { - t = compositionTy->getExistentialLayout().superclass; + return compositionTy->getExistentialLayout().superclass; } } + return t; +} + +Type TypeBase::getSuperclassForDecl(const ClassDecl *baseClass) { + Type t = getConcreteTypeForSuperclassTraversing(this); while (t) { // If we have a class-constrained archetype or class-constrained @@ -3228,6 +3177,24 @@ Type TypeBase::getSuperclassForDecl(const ClassDecl *baseClass) { llvm_unreachable("no inheritance relationship between given classes"); } +Type TypeBase::getGenericAncestor() { + Type t = getConcreteTypeForSuperclassTraversing(this); + + while (t && !t->hasError()) { + auto NTD = t->getAnyNominal(); + assert(NTD && "expected nominal type in NTD"); + if (!NTD) + return Type(); + + if (NTD->isGenericContext()) + return t; + + t = t->getSuperclass(); + } + + return Type(); +} + TypeSubstitutionMap TypeBase::getContextSubstitutions(const DeclContext *dc, GenericEnvironment *genericEnv) { @@ -3399,7 +3366,7 @@ Type TypeBase::adjustSuperclassMemberDeclType(const ValueDecl *baseDecl, genericMemberType->getExtInfo()); } - auto type = memberType.subst(subs); + auto type = memberType.subst(subs, SubstFlags::UseErrorType); if (isa(baseDecl)) { type = type->replaceSelfParameterType(this); @@ -3540,9 +3507,10 @@ case TypeKind::Id: // This interface isn't suitable for updating the substitution map in a // generic SILBox. auto boxTy = cast(base); - for (auto &arg : boxTy->getGenericArgs()) - assert(arg.getReplacement()->isEqual(arg.getReplacement().transformRec(fn)) + for (Type type : boxTy->getSubstitutions().getReplacementTypes()) { + assert(type->isEqual(type.transformRec(fn)) && "SILBoxType can't be transformed"); + } #endif return base; } @@ -4128,37 +4096,27 @@ bool TypeBase::usesNativeReferenceCounting(ResilienceExpansion resilience) { // void SILBoxType::Profile(llvm::FoldingSetNodeID &id, SILLayout *Layout, - SubstitutionList Args) { + SubstitutionMap Substitutions) { id.AddPointer(Layout); - profileSubstitutionList(id, Args); + Substitutions.profile(id); } -SILBoxType::SILBoxType(ASTContext &C, - SILLayout *Layout, SubstitutionList Args) -: TypeBase(TypeKind::SILBox, &C, - getRecursivePropertiesFromSubstitutions(Args)), Layout(Layout) { - Bits.SILBoxType.NumGenericArgs = Args.size(); -#ifndef NDEBUG - // Check that the generic args are reasonable for the box's signature. - if (Layout->getGenericSignature()) - (void)Layout->getGenericSignature()->getSubstitutionMap(Args); - for (auto &arg : Args) - assert(arg.getReplacement()->isCanonical() && - "box arguments must be canonical types!"); -#endif - std::uninitialized_copy(Args.begin(), Args.end(), - getTrailingObjects()); -} - -RecursiveTypeProperties SILBoxType:: -getRecursivePropertiesFromSubstitutions(SubstitutionList Params) { +static RecursiveTypeProperties getRecursivePropertiesOfMap( + SubstitutionMap subMap) { RecursiveTypeProperties props; - for (auto ¶m : Params) { - props |= param.getReplacement()->getRecursiveProperties(); + for (auto replacementType : subMap.getReplacementTypes()) { + if (replacementType) props |= replacementType->getRecursiveProperties(); } return props; } +SILBoxType::SILBoxType(ASTContext &C, + SILLayout *Layout, SubstitutionMap Substitutions) + : TypeBase(TypeKind::SILBox, &C, getRecursivePropertiesOfMap(Substitutions)), + Layout(Layout), Substitutions(Substitutions) { + assert(Substitutions.isCanonical()); +} + Type TypeBase::openAnyExistentialType(ArchetypeType *&opened) { assert(isAnyExistentialType()); if (auto metaty = getAs()) { diff --git a/lib/AST/TypeRepr.cpp b/lib/AST/TypeRepr.cpp index f8483642fc91d..9ac6c106a3574 100644 --- a/lib/AST/TypeRepr.cpp +++ b/lib/AST/TypeRepr.cpp @@ -237,8 +237,8 @@ TypeRepr *CloneVisitor::visitSILBoxTypeRepr(SILBoxTypeRepr *type) { SmallVector cloneArgs; for (auto &field : type->getFields()) - cloneFields.push_back({field.VarOrLetLoc, field.Mutable, - visit(field.FieldType)}); + cloneFields.push_back({field.getLoc(), field.isMutable(), + visit(field.getFieldType())}); for (auto *arg : type->getGenericArguments()) cloneArgs.push_back(visit(arg)); @@ -471,10 +471,11 @@ SILBoxTypeRepr *SILBoxTypeRepr::create(ASTContext &C, SourceLoc RBraceLoc, SourceLoc ArgLAngleLoc, ArrayRef GenericArgs, SourceLoc ArgRAngleLoc) { - return new (C) SILBoxTypeRepr(GenericParams, - LBraceLoc, C.AllocateCopy(Fields), RBraceLoc, - ArgLAngleLoc, C.AllocateCopy(GenericArgs), - ArgRAngleLoc); + auto size = totalSizeToAlloc(Fields.size(), + GenericArgs.size()); + auto mem = C.Allocate(size, alignof(SILBoxTypeRepr)); + return new (mem) SILBoxTypeRepr(GenericParams, LBraceLoc, Fields, RBraceLoc, + ArgLAngleLoc, GenericArgs, ArgRAngleLoc); } SourceLoc SILBoxTypeRepr::getStartLocImpl() const { diff --git a/lib/AST/TypeWalker.cpp b/lib/AST/TypeWalker.cpp index a3c3a62b6546a..dfa74dc19ca7d 100644 --- a/lib/AST/TypeWalker.cpp +++ b/lib/AST/TypeWalker.cpp @@ -39,9 +39,6 @@ class Traversal : public TypeVisitor if (auto parent = ty->getParent()) if (doIt(parent)) return true; - if (doIt(ty->getSinglyDesugaredType())) - return true; - for (auto arg : ty->getInnermostGenericArgs()) if (doIt(arg)) return true; @@ -180,8 +177,8 @@ class Traversal : public TypeVisitor } bool visitSILBoxType(SILBoxType *ty) { - for (auto &arg : ty->getGenericArgs()) { - if (doIt(arg.getReplacement())) + for (Type type : ty->getSubstitutions().getReplacementTypes()) { + if (type && doIt(type)) return true; } return false; diff --git a/lib/Basic/SourceLoc.cpp b/lib/Basic/SourceLoc.cpp index f55af89e92a86..d0761c8924dcd 100644 --- a/lib/Basic/SourceLoc.cpp +++ b/lib/Basic/SourceLoc.cpp @@ -212,14 +212,14 @@ void SourceRange::widen(SourceRange Other) { End = Other.End; } -void SourceLoc::printLineAndColumn(raw_ostream &OS, - const SourceManager &SM) const { +void SourceLoc::printLineAndColumn(raw_ostream &OS, const SourceManager &SM, + unsigned BufferID) const { if (isInvalid()) { OS << ""; return; } - auto LineAndCol = SM.getLineAndColumn(*this); + auto LineAndCol = SM.getLineAndColumn(*this, BufferID); OS << "line:" << LineAndCol.first << ':' << LineAndCol.second; } diff --git a/lib/Basic/Statistic.cpp b/lib/Basic/Statistic.cpp index accc584ceceb4..78bcdeb96d155 100644 --- a/lib/Basic/Statistic.cpp +++ b/lib/Basic/Statistic.cpp @@ -453,35 +453,6 @@ FrontendStatsTracer::FrontendStatsTracer( FrontendStatsTracer::FrontendStatsTracer() = default; -FrontendStatsTracer::FrontendStatsTracer(UnifiedStatsReporter *R, StringRef S) - : FrontendStatsTracer(R, S, nullptr, nullptr) -{} - -FrontendStatsTracer::FrontendStatsTracer(UnifiedStatsReporter *R, StringRef S, - const Decl *D) - : FrontendStatsTracer(R, S, D, getTraceFormatter()) -{} - -FrontendStatsTracer::FrontendStatsTracer(UnifiedStatsReporter *R, StringRef S, - const ProtocolConformance *P) - : FrontendStatsTracer(R, S, P, - getTraceFormatter()) {} - -FrontendStatsTracer::FrontendStatsTracer(UnifiedStatsReporter *R, StringRef S, - const Expr *E) - : FrontendStatsTracer(R, S, E, getTraceFormatter()) -{} - -FrontendStatsTracer::FrontendStatsTracer(UnifiedStatsReporter *R, StringRef S, - const clang::Decl *D) - : FrontendStatsTracer(R, S, D, getTraceFormatter()) -{} - -FrontendStatsTracer::FrontendStatsTracer(UnifiedStatsReporter *R, StringRef S, - const SILFunction *F) - : FrontendStatsTracer(R, S, F, getTraceFormatter()) -{} - FrontendStatsTracer& FrontendStatsTracer::operator=(FrontendStatsTracer&& other) { @@ -650,7 +621,7 @@ UnifiedStatsReporter::~UnifiedStatsReporter() raw_fd_ostream ostream(StatsFilename, EC, fs::F_Append | fs::F_Text); if (EC) { llvm::errs() << "Error opening -stats-output-dir file '" - << TraceFilename << "' for writing\n"; + << StatsFilename << "' for writing\n"; return; } diff --git a/lib/Basic/TaskQueue.cpp b/lib/Basic/TaskQueue.cpp index f1f62f7bea6c3..aec9ebd14ffc8 100644 --- a/lib/Basic/TaskQueue.cpp +++ b/lib/Basic/TaskQueue.cpp @@ -29,8 +29,10 @@ using namespace swift::sys; #include "Default/TaskQueue.inc" #endif -TaskQueue::TaskQueue(unsigned NumberOfParallelTasks) - : NumberOfParallelTasks(NumberOfParallelTasks) {} +TaskQueue::TaskQueue(unsigned NumberOfParallelTasks, + UnifiedStatsReporter *USR) + : NumberOfParallelTasks(NumberOfParallelTasks), + Stats(USR){} TaskQueue::~TaskQueue() = default; diff --git a/lib/Basic/Unix/TaskQueue.inc b/lib/Basic/Unix/TaskQueue.inc index 816f799173632..0faf29d25fc20 100644 --- a/lib/Basic/Unix/TaskQueue.inc +++ b/lib/Basic/Unix/TaskQueue.inc @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "swift/Basic/TaskQueue.h" +#include "swift/Basic/Statistic.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/DenseMap.h" @@ -71,25 +72,25 @@ class Task { int ErrorPipe; /// The current state of the Task. - enum { - Preparing, - Executing, - Finished - } State; + enum class TaskState { Preparing, Executing, Finished } State; /// Once the Task has finished, this contains the buffered output of the Task. std::string Output; - /// Once the Task has finished, if SeparateErrors is true, this contains the errors - /// from the Task. + /// Once the Task has finished, if SeparateErrors is true, this contains the + /// errors from the Task. std::string Errors; + /// Optional place to count I/O and subprocess events. + UnifiedStatsReporter *Stats; + public: Task(const char *ExecPath, ArrayRef Args, - ArrayRef Env, void *Context, bool SeparateErrors) + ArrayRef Env, void *Context, bool SeparateErrors, + UnifiedStatsReporter *USR) : ExecPath(ExecPath), Args(Args), Env(Env), Context(Context), SeparateErrors(SeparateErrors), Pid(-1), Pipe(-1), ErrorPipe(-1), - State(Preparing) { + State(TaskState::Preparing), Stats(USR) { assert((Env.empty() || Env.back() == nullptr) && "Env must either be empty or null-terminated!"); } @@ -104,12 +105,15 @@ public: int getErrorPipe() const { return ErrorPipe; } /// \brief Begins execution of this Task. - /// \returns true on error, false on success + /// \returns true on error. bool execute(); /// \brief Reads data from the pipes, if any is available. - /// \returns true on error, false on success - bool readFromPipes(); + /// + /// If \p UntilEnd is true, reads until the end of the stream; otherwise reads + /// once (possibly with a retry on EINTR), and returns. + /// \returns true on error. + bool readFromPipes(bool UntilEnd); /// \brief Performs any post-execution work for this Task, such as reading /// piped output and closing the pipe. @@ -120,8 +124,8 @@ public: } // end namespace swift bool Task::execute() { - assert(State < Executing && "This Task cannot be executed twice!"); - State = Executing; + assert(State < TaskState::Executing && "This Task cannot be executed twice!"); + State = TaskState::Executing; // Construct argv. SmallVector Argv; @@ -172,9 +176,9 @@ bool Task::execute() { } // Spawn the subtask. - int spawnErr = posix_spawn(&Pid, ExecPath, &FileActions, nullptr, - const_cast(argvp), - const_cast(envp)); + int spawnErr = + posix_spawn(&Pid, ExecPath, &FileActions, nullptr, + const_cast(argvp), const_cast(envp)); posix_spawn_file_actions_destroy(&FileActions); close(FullPipe[1]); @@ -187,7 +191,7 @@ bool Task::execute() { if (SeparateErrors) { close(FullErrorPipe[0]); } - State = Finished; + State = TaskState::Finished; return true; } #else @@ -198,7 +202,7 @@ bool Task::execute() { if (SeparateErrors) { close(FullErrorPipe[0]); } - State = Finished; + State = TaskState::Finished; Pid = 0; break; } @@ -241,39 +245,53 @@ bool Task::execute() { return false; } -static bool readFromAPipe(int Pipe, std::string &Output) { +/// Read the data in \p Pipe, and append it to \p Output. +/// \p Pipe must be in blocking mode, and must contain unread data. +/// If \p UntilEnd is true, keep reading, and possibly blocking, till the pipe +/// is closed. If \p UntilEnd is false, just read once. Return true if error +static bool readFromAPipe(std::string &Output, int Pipe, + UnifiedStatsReporter *Stats, bool UntilEnd) { char outputBuffer[1024]; ssize_t readBytes = 0; while ((readBytes = read(Pipe, outputBuffer, sizeof(outputBuffer))) != 0) { if (readBytes < 0) { if (errno == EINTR) // read() was interrupted, so try again. + // Q: Why isn't there a counter to break out of this loop if there are + // more than some number of EINTRs? + // A: EINTR on a blocking read means only one thing: the syscall was + // interrupted and the program should retry. So there is no need to + // stop retrying after any particular number of interruptions (any + // more than the program would stop reading after a particular number + // of bytes or whatever). continue; return true; } - Output.append(outputBuffer, readBytes); + if (Stats) + Stats->getDriverCounters().NumDriverPipeReads++; + if (!UntilEnd) + break; } - return false; } -bool Task::readFromPipes() { - bool Ret = readFromAPipe(Pipe, Output); +bool Task::readFromPipes(bool UntilEnd) { + bool Ret = readFromAPipe(Output, Pipe, Stats, UntilEnd); if (SeparateErrors) { - Ret |= readFromAPipe(ErrorPipe, Errors); + Ret |= readFromAPipe(Errors, ErrorPipe, Stats, UntilEnd); } return Ret; } void Task::finishExecution() { - assert(State == Executing && + assert(State == TaskState::Executing && "This Task must be executing to finish execution!"); - State = Finished; + State = TaskState::Finished; // Read the output of the command, so we can use it later. - readFromPipes(); + readFromPipes(/*UntilEnd*/ false); close(Pipe); if (SeparateErrors) { @@ -302,159 +320,323 @@ void TaskQueue::addTask(const char *ExecPath, ArrayRef Args, ArrayRef Env, void *Context, bool SeparateErrors) { std::unique_ptr T( - new Task(ExecPath, Args, Env, Context, SeparateErrors)); + new Task(ExecPath, Args, Env, Context, SeparateErrors, Stats)); QueuedTasks.push(std::move(T)); } -bool TaskQueue::execute(TaskBeganCallback Began, TaskFinishedCallback Finished, - TaskSignalledCallback Signalled) { +/// Owns Tasks, handles correspondence between Tasks, file descriptors, and +/// process IDs. +/// FIXME: only handles stdout pipes, ignores stderr pipes. +class TaskMap { using PidToTaskMap = llvm::DenseMap>; + PidToTaskMap TasksByPid; - // Stores the current executing Tasks, organized by pid. - PidToTaskMap ExecutingTasks; +public: + TaskMap() = default; - // Maintains the current fds we're checking with poll. - std::vector PollFds; + bool empty() const { return TasksByPid.empty(); } + unsigned size() const { return TasksByPid.size(); } - bool SubtaskFailed = false; + void add(std::unique_ptr T) { TasksByPid[T->getPid()] = std::move(T); } - unsigned MaxNumberOfParallelTasks = getNumberOfParallelTasks(); + Task &findTaskForFd(const int fd) { + auto predicate = [&fd](PidToTaskMap::value_type &value) -> bool { + return value.second->getPipe() == fd; + }; + auto iter = std::find_if(TasksByPid.begin(), TasksByPid.end(), predicate); + assert(iter != TasksByPid.end() && + "All outstanding fds must be associated with a Task"); + return *iter->second; + } - if (MaxNumberOfParallelTasks == 0) - MaxNumberOfParallelTasks = 1; + void destroyTask(Task &T) { TasksByPid.erase(T.getPid()); } +}; - while ((!QueuedTasks.empty() && !SubtaskFailed) || - !ExecutingTasks.empty()) { - // Enqueue additional tasks, if we have additional tasks, we aren't - // already at the parallel limit, and no earlier subtasks have failed. - while (!SubtaskFailed && !QueuedTasks.empty() && - ExecutingTasks.size() < MaxNumberOfParallelTasks) { - std::unique_ptr T(QueuedTasks.front().release()); - QueuedTasks.pop(); - if (T->execute()) - return true; +/// Concurrently execute the tasks in the TaskQueue, collecting the outputs from +/// each task. +/// Maintain invarients connecting tasks to execute, tasks currently executing, +/// and fds being polled. These invarients include: +/// A task is not in both TasksToBeExecuted and TasksBeingExecuted, +/// A task is executing iff it is in TasksBeingExecuted, +/// A task is executing iff any of its fds being polled are in FdsBeingPolled +/// (These should be all of its output fds, but today is only stdout.) +/// When a task has finished executing, wait for it to die, takes +/// action appropriate to the cause of death, then reclaim its +/// storage. +class TaskMonitor { + std::queue> &TasksToBeExecuted; + TaskMap TasksBeingExecuted; + + std::vector FdsBeingPolled; + + const unsigned MaxNumberOfParallelTasks; - pid_t Pid = T->getPid(); +public: + struct Callbacks { + const TaskQueue::TaskBeganCallback TaskBegan; + const TaskQueue::TaskFinishedCallback TaskFinished; + const TaskQueue::TaskSignalledCallback TaskSignalled; + const std::function PolledAnFd; + }; - if (Began) { - Began(Pid, T->getContext()); - } +private: + Callbacks callbacks; - PollFds.push_back({ T->getPipe(), POLLIN | POLLPRI | POLLHUP, 0 }); - // We should also poll T->getErrorPipe(), but this introduces timing - // issues with shutting down the task after reading getPipe(). - ExecutingTasks[Pid] = std::move(T); - } +public: + TaskMonitor(std::queue> &TasksToBeExecuted, + const unsigned NumberOfParallelTasks, const Callbacks &callbacks) + : TasksToBeExecuted(TasksToBeExecuted), + MaxNumberOfParallelTasks( + NumberOfParallelTasks == 0 ? 1 : NumberOfParallelTasks), + callbacks(callbacks) {} + + /// Run the tasks to be executed. + /// \return true on error. + bool executeTasks(); + +private: + bool isFinishedExecutingTasks() const { + return TasksBeingExecuted.empty() && TasksToBeExecuted.empty(); + } - assert(!PollFds.empty() && - "We should only call poll() if we have fds to watch!"); - int ReadyFdCount = poll(PollFds.data(), PollFds.size(), -1); - if (ReadyFdCount == -1) { - // Recover from error, if possible. - if (errno == EAGAIN || errno == EINTR) - continue; + /// Start up tasks if we aren't already at the parallel limit, and no earlier + /// subtasks have failed. + /// \return true on error. + bool startUpSomeTasks(); + + /// \return true on error. + bool beginExecutingATask(Task &T); + + /// Enter the task and its outputs in this TaskMonitor's data structures so + /// it can be polled. + void startPollingFdsOfTask(const Task &T); + + void stopPolling(ArrayRef FinishedFds); + + enum class PollResult { HardError, SoftError, NoError }; + PollResult pollTheFds(); + + /// \return None on error. + Optional> readFromReadyFdsReturningFinishedOnes(); + + /// Ensure that events bits returned from polling are what's expected. + void verifyEvents(short events) const; + + void readDataIfAvailable(short events, int fd, Task &T) const; + + bool didTaskHangup(short events) const; +}; + +bool TaskMonitor::executeTasks() { + while (!isFinishedExecutingTasks()) { + if (startUpSomeTasks()) return true; - } - // Holds all fds which have finished during this loop iteration. - std::vector FinishedFds; - - for (struct pollfd &fd : PollFds) { - if (fd.revents & POLLIN || fd.revents & POLLPRI || fd.revents & POLLHUP || - fd.revents & POLLERR) { - // An event which we care about occurred. Find the appropriate Task. - auto predicate = [&fd](PidToTaskMap::value_type &value) -> bool { - return value.second->getPipe() == fd.fd; - }; - - auto iter = std::find_if(ExecutingTasks.begin(), ExecutingTasks.end(), - predicate); - assert(iter != ExecutingTasks.end() && - "All outstanding fds must be associated with an executing Task"); - Task &T = *iter->second; - if (fd.revents & POLLIN || fd.revents & POLLPRI) { - // There's data available to read. - T.readFromPipes(); - } - - if (fd.revents & POLLHUP || fd.revents & POLLERR) { - // This fd was "hung up" or had an error, so we need to wait for the - // Task and then clean up. - pid_t Pid; - int Status; - do { - Status = 0; - Pid = waitpid(T.getPid(), &Status, 0); - assert(Pid != 0 && - "We do not pass WNOHANG, so we should always get a pid"); - if (Pid < 0 && (errno == ECHILD || errno == EINVAL)) - return true; - } while (Pid < 0); - - assert(Pid == T.getPid() && - "We asked to wait for this Task, but we got another Pid!"); - - T.finishExecution(); - - if (WIFEXITED(Status)) { - int Result = WEXITSTATUS(Status); - - if (Finished) { - // If we have a TaskFinishedCallback, only set SubtaskFailed to - // true if the callback returns StopExecution. - SubtaskFailed = Finished(T.getPid(), Result, T.getOutput(), - T.getErrors(), T.getContext()) == - TaskFinishedResponse::StopExecution; - } else if (Result != 0) { - // Since we don't have a TaskFinishedCallback, treat a subtask - // which returned a nonzero exit code as having failed. - SubtaskFailed = true; - } - } else if (WIFSIGNALED(Status)) { - // The process exited due to a signal. - int Signal = WTERMSIG(Status); - - StringRef ErrorMsg = strsignal(Signal); - - if (Signalled) { - TaskFinishedResponse Response = - Signalled(T.getPid(), ErrorMsg, T.getOutput(), T.getErrors(), - T.getContext(), Signal); - if (Response == TaskFinishedResponse::StopExecution) - // If we have a TaskCrashedCallback, only set SubtaskFailed to - // true if the callback returns StopExecution. - SubtaskFailed = true; - } else { - // Since we don't have a TaskCrashedCallback, treat a crashing - // subtask as having failed. - SubtaskFailed = true; - } - } - - ExecutingTasks.erase(Pid); - FinishedFds.push_back(fd.fd); - } - } else if (fd.revents & POLLNVAL) { - // We passed an invalid fd; this should never happen, - // since we always mark fds as finished after calling - // Task::finishExecution() (which closes the Task's fd). - llvm_unreachable("Asked poll() to watch a closed fd"); - } - - fd.revents = 0; + switch (pollTheFds()) { + case PollResult::HardError: + return true; + case PollResult::SoftError: + continue; + case PollResult::NoError: + break; } + Optional> FinishedFds = + readFromReadyFdsReturningFinishedOnes(); + if (!FinishedFds) + return true; - // Remove any fds which we've closed from PollFds. - for (int fd : FinishedFds) { - auto predicate = [&fd] (struct pollfd &i) { - return i.fd == fd; - }; + stopPolling(*FinishedFds); + } + return false; +} - auto iter = std::find_if(PollFds.begin(), PollFds.end(), predicate); - assert(iter != PollFds.end() && "The finished fd must be in PollFds!"); - PollFds.erase(iter); - } +bool TaskMonitor::startUpSomeTasks() { + while (!TasksToBeExecuted.empty() && + TasksBeingExecuted.size() < MaxNumberOfParallelTasks) { + std::unique_ptr T(TasksToBeExecuted.front().release()); + TasksToBeExecuted.pop(); + if (beginExecutingATask(*T)) + return true; + startPollingFdsOfTask(*T); + TasksBeingExecuted.add(std::move(T)); } + return false; +} + +void TaskMonitor::startPollingFdsOfTask(const Task &T) { + FdsBeingPolled.push_back({T.getPipe(), POLLIN | POLLPRI | POLLHUP, 0}); + // We should also poll T->getErrorPipe(), but this introduces timing + // issues with shutting down the task after reading getPipe(). +} + +TaskMonitor::PollResult TaskMonitor::pollTheFds() { + assert(!FdsBeingPolled.empty() && + "We should only call poll() if we have fds to watch!"); + int ReadyFdCount = poll(FdsBeingPolled.data(), FdsBeingPolled.size(), -1); + if (callbacks.PolledAnFd) + callbacks.PolledAnFd(); + if (ReadyFdCount != -1) + return PollResult::NoError; + return errno == EAGAIN || errno == EINTR ? PollResult::SoftError + : PollResult::HardError; +} + +bool TaskMonitor::beginExecutingATask(Task &T) { + if (T.execute()) + return true; + if (callbacks.TaskBegan) + callbacks.TaskBegan(T.getPid(), T.getContext()); + return false; +} + +static bool +cleanUpAHungUpTask(Task &T, + const TaskQueue::TaskFinishedCallback FinishedCallback, + TaskQueue::TaskSignalledCallback SignalledCallback); +static Optional waitForPid(const pid_t pidToWaitFor); +static bool +cleanUpAfterSignal(int Status, const Task &T, + const TaskQueue::TaskSignalledCallback SignalledCallback); +static bool +cleanUpAfterExit(int Status, const Task &T, + const TaskQueue::TaskFinishedCallback FinishedCallback); + +Optional> +TaskMonitor::readFromReadyFdsReturningFinishedOnes() { + std::vector finishedFds; + for (struct pollfd &fd : FdsBeingPolled) { + const int fileDes = fd.fd; + const short receivedEvents = fd.revents; + fd.revents = 0; + verifyEvents(receivedEvents); + Task &T = TasksBeingExecuted.findTaskForFd(fileDes); + readDataIfAvailable(receivedEvents, fileDes, T); + if (!didTaskHangup(receivedEvents)) + continue; + finishedFds.push_back(fileDes); + const bool hadError = + cleanUpAHungUpTask(T, callbacks.TaskFinished, callbacks.TaskSignalled); + TasksBeingExecuted.destroyTask(T); + if (hadError) + return None; + } + return finishedFds; +} + +void TaskMonitor::verifyEvents(const short events) const { + // We passed an invalid fd; this should never happen, + // since we always mark fds as finished after calling + // Task::finishExecution() (which closes the Task's fd). + assert((events & POLLNVAL) == 0 && "Asked poll() to watch a closed fd"); + const short expectedEvents = POLLIN | POLLPRI | POLLHUP | POLLERR; + assert((events & ~expectedEvents) == 0 && "Received unexpected event"); +} + +void TaskMonitor::readDataIfAvailable(const short events, const int fd, + Task &T) const { + if (events & (POLLIN | POLLPRI)) { + // There's data available to read. Read _some_ of it here, but not + // necessarily _all_, since the pipe is in blocking mode and we might + // have other input pending (or soon -- before this subprocess is done + // writing) from other subprocesses. + // + // FIXME: longer term, this should probably either be restructured to + // use O_NONBLOCK, or at very least poll the stderr file descriptor as + // well; the whole loop here is a bit of a mess. + T.readFromPipes(/*UntilEnd*/ false); + } +} + +bool TaskMonitor::didTaskHangup(const short events) const { + return (events & (POLLHUP | POLLERR)) != 0; +} + +static bool +cleanUpAHungUpTask(Task &T, + const TaskQueue::TaskFinishedCallback FinishedCallback, + const TaskQueue::TaskSignalledCallback SignalledCallback) { + const Optional StatusIfOK = waitForPid(T.getPid()); + if (!StatusIfOK) + return true; + + T.finishExecution(); + int Status = *StatusIfOK; + return WIFEXITED(Status) + ? cleanUpAfterExit(Status, T, FinishedCallback) + : WIFSIGNALED(Status) + ? cleanUpAfterSignal(Status, T, SignalledCallback) + : false /* Can this case ever happen? */; +} + +static Optional waitForPid(const pid_t pidToWaitFor) { + for (;;) { + int Status = 0; + const pid_t pidFromWait = waitpid(pidToWaitFor, &Status, 0); + if (pidFromWait == pidToWaitFor) + return Status; + assert(pidFromWait == -1 && + "Did not pass WNOHANG, should only get pidToWaitFor or -1"); + if (errno == ECHILD || errno == EINVAL) + return None; + } +} + +static bool +cleanUpAfterExit(int Status, const Task &T, + const TaskQueue::TaskFinishedCallback FinishedCallback) { + const int Result = WEXITSTATUS(Status); + if (!FinishedCallback) { + // Since we don't have a TaskFinishedCallback, treat a subtask + // which returned a nonzero exit code as having failed. + return Result != 0; + } + // If we have a TaskFinishedCallback, only have an error if the callback + // returns StopExecution. + return TaskFinishedResponse::StopExecution == + FinishedCallback(T.getPid(), Result, T.getOutput(), T.getErrors(), + T.getContext()); +} + +static bool +cleanUpAfterSignal(int Status, const Task &T, + const TaskQueue::TaskSignalledCallback SignalledCallback) { + // The process exited due to a signal. + const int Signal = WTERMSIG(Status); + StringRef ErrorMsg = strsignal(Signal); + + if (!SignalledCallback) { + // Since we don't have a TaskCrashedCallback, treat a crashing + // subtask as having failed. + return true; + } + // If we have a TaskCrashedCallback, only return an error if the callback + // returns StopExecution. + return TaskFinishedResponse::StopExecution == + SignalledCallback(T.getPid(), ErrorMsg, T.getOutput(), T.getErrors(), + T.getContext(), Signal); +} + +void TaskMonitor::stopPolling(ArrayRef FinishedFds) { + // Remove any fds which we've closed from FdsBeingPolled. + for (int fd : FinishedFds) { + auto predicate = [&fd](struct pollfd &i) { return i.fd == fd; }; + auto iter = + std::find_if(FdsBeingPolled.begin(), FdsBeingPolled.end(), predicate); + assert(iter != FdsBeingPolled.end() && + "The finished fd must be in FdsBeingPolled!"); + FdsBeingPolled.erase(iter); + } +} - return SubtaskFailed; +bool TaskQueue::execute(TaskBeganCallback BeganCallback, + TaskFinishedCallback FinishedCallback, + TaskSignalledCallback SignalledCallback) { + TaskMonitor::Callbacks callbacks{ + BeganCallback, FinishedCallback, SignalledCallback, [&] { + if (Stats) + ++Stats->getDriverCounters().NumDriverPipePolls; + }}; + + TaskMonitor TE(QueuedTasks, getNumberOfParallelTasks(), callbacks); + return TE.executeTasks(); } diff --git a/lib/ClangImporter/CFDatabase.def b/lib/ClangImporter/CFDatabase.def index 035aa29ebf558..85540a92e66b4 100644 --- a/lib/ClangImporter/CFDatabase.def +++ b/lib/ClangImporter/CFDatabase.def @@ -18,12 +18,12 @@ # error "Must define NON_CF_TYPE(NAME) before including this file." #endif -// Accelerate/vImage +// MARK: Accelerate/vImage CF_TYPE(vImageCVImageFormatRef) CF_TYPE(vImageConstCVImageFormatRef) CF_TYPE(vImageConverterRef) -// AddressBook +// MARK: AddressBook CF_TYPE(ABAddressBookRef) CF_TYPE(ABGroupRef) CF_TYPE(ABMultiValueRef) @@ -33,7 +33,7 @@ CF_TYPE(ABPickerRef) CF_TYPE(ABRecordRef) CF_TYPE(ABSearchElementRef) -// ApplicationServices/ATS +// MARK: ApplicationServices/ATS NON_CF_TYPE(ATSFontContainerRef) NON_CF_TYPE(ATSFontFamilyRef) NON_CF_TYPE(ATSFontNotificationInfoRef) @@ -42,13 +42,13 @@ NON_CF_TYPE(ATSFontRef) NON_CF_TYPE(ATSGlyphRef) NON_CF_TYPE(ATSULineRef) -// ApplicationServices/ColorSync +// MARK: ApplicationServices/ColorSync CF_TYPE(ColorSyncCMMRef) CF_TYPE(ColorSyncMutableProfileRef) CF_TYPE(ColorSyncProfileRef) CF_TYPE(ColorSyncTransformRef) -// ApplicationServices/HIServices +// MARK: ApplicationServices/HIServices CF_TYPE(AXObserverRef) CF_TYPE(AXUIElementRef) CF_TYPE(AXValueRef) @@ -59,18 +59,18 @@ NON_CF_TYPE(IconSuiteRef) CF_TYPE(PasteboardRef) CF_TYPE(TranslationRef) -// ApplicationServices/LangAnalysis +// MARK: ApplicationServices/LangAnalysis NON_CF_TYPE(DCMDictionaryRef) NON_CF_TYPE(DCMDictionaryStreamRef) NON_CF_TYPE(DCMObjectRef) NON_CF_TYPE(LAContextRef) NON_CF_TYPE(LAEnvironmentRef) -// ApplicationServices/QD +// MARK: ApplicationServices/QD NON_CF_TYPE(ATSUStyleSettingRef) NON_CF_TYPE(WindowRef) -// AudioToolbox +// MARK: AudioToolbox NON_CF_TYPE(AUEventListenerRef) NON_CF_TYPE(AUMIDIControllerRef) NON_CF_TYPE(AUParameterListenerRef) // CFExclusions.def @@ -82,7 +82,7 @@ NON_CF_TYPE(AudioQueueTimelineRef) // CFExclusions.def NON_CF_TYPE(CAClockRef) // CFExclusions.def NON_CF_TYPE(ExtAudioFileRef) // CFExclusions.def -// CFNetwork +// MARK: CFNetwork CF_TYPE(CFHTTPAuthenticationRef) CF_TYPE(CFHTTPMessageRef) CF_TYPE(CFHostRef) @@ -91,13 +91,13 @@ CF_TYPE(CFNetServiceBrowserRef) CF_TYPE(CFNetServiceMonitorRef) CF_TYPE(CFNetServiceRef) -// Carbon/CarbonSound +// MARK: Carbon/CarbonSound NON_CF_TYPE(ModRef) -// Carbon/CommonPanels +// MARK: Carbon/CommonPanels CF_TYPE(FCFontDescriptorRef) -// Carbon/HIToolbox +// MARK: Carbon/HIToolbox CF_TYPE(ControlRef) NON_CF_TYPE(DataBrowserItemDataRef) NON_CF_TYPE(DialogRef) @@ -129,22 +129,22 @@ CF_TYPE(TISInputSourceRef) NON_CF_TYPE(ToolboxObjectClassRef) NON_CF_TYPE(WindowGroupRef) -// Carbon/Ink +// MARK: Carbon/Ink CF_TYPE(InkStrokeRef) CF_TYPE(InkTextRef) -// Carbon/NavigationServices +// MARK: Carbon/NavigationServices CF_TYPE(NavDialogRef) -// CommonCrypto +// MARK: CommonCrypto NON_CF_TYPE(CCCryptorRef) // CFExclusions.def -// CoreAudio +// MARK: CoreAudio NON_CF_TYPE(AudioHardwarePlugInRef) // CFExclusions.def NON_CF_TYPE(AudioServerPlugInDriverRef) NON_CF_TYPE(AudioServerPlugInHostRef) -// CoreFoundation +// MARK: CoreFoundation CF_TYPE(CFAllocatorRef) CF_TYPE(CFArrayRef) CF_TYPE(CFAttributedStringRef) @@ -202,7 +202,7 @@ CF_TYPE(CFXMLNodeRef) CF_TYPE(CFXMLParserRef) CF_TYPE(CFXMLTreeRef) -// CoreGraphics +// MARK: CoreGraphics CF_TYPE(CGColorRef) CF_TYPE(CGColorSpaceRef) CF_TYPE(CGContextRef) @@ -235,13 +235,13 @@ CF_TYPE(CGPathRef) CF_TYPE(CGPatternRef) CF_TYPE(CGShadingRef) -// CoreGraphics private +// MARK: CoreGraphics private CF_TYPE(QLPreviewBubbleRef) CF_TYPE(CGRegionRef) CF_TYPE(CGRegionEnumeratorRef) CF_TYPE(CGSWindowBackdropRef) -// CoreMIDI +// MARK: CoreMIDI NON_CF_TYPE(MIDIClientRef) // CFExclusions.def NON_CF_TYPE(MIDIDeviceListRef) // CFExclusions.def NON_CF_TYPE(MIDIDeviceRef) // CFExclusions.def @@ -253,7 +253,7 @@ NON_CF_TYPE(MIDIPortRef) // CFExclusions.def NON_CF_TYPE(MIDISetupRef) // CFExclusions.def NON_CF_TYPE(MIDIThruConnectionRef) // CFExclusions.def -// CoreMedia +// MARK: CoreMedia CF_TYPE(CMAttachmentBearerRef) CF_TYPE(CMAudioFormatDescriptionRef) CF_TYPE(CMBlockBufferRef) @@ -273,14 +273,14 @@ CF_TYPE(CMTimeCodeFormatDescriptionRef) CF_TYPE(CMTimebaseRef) CF_TYPE(CMVideoFormatDescriptionRef) -// CoreMediaIO +// MARK: CoreMediaIO NON_CF_TYPE(CMIOHardwarePlugInRef) -// CoreServices/AE +// MARK: CoreServices/AE NON_CF_TYPE(AERemoteProcessResolverRef) NON_CF_TYPE(AEStreamRef) -// CoreServices/CarbonCore +// MARK: CoreServices/CarbonCore NON_CF_TYPE(CollatorRef) NON_CF_TYPE(FNSubscriptionRef) CF_TYPE(FSFileOperationRef) @@ -293,24 +293,24 @@ NON_CF_TYPE(TextBreakLocatorRef) NON_CF_TYPE(ThreadTaskRef) NON_CF_TYPE(UCTypeSelectRef) -// CoreServices/DictionaryServices +// MARK: CoreServices/DictionaryServices CF_TYPE(DCSDictionaryRef) -// CoreServices/FSEvents +// MARK: CoreServices/FSEvents NON_CF_TYPE(ConstFSEventStreamRef) // CFExclusions.def NON_CF_TYPE(FSEventStreamRef) // CFExclusions.def -// CoreServices/LaunchServices +// MARK: CoreServices/LaunchServices NON_CF_TYPE(IconRef) CF_TYPE(LSSharedFileListItemRef) CF_TYPE(LSSharedFileListRef) -// CoreServices/Metadata +// MARK: CoreServices/Metadata CF_TYPE(MDItemRef) CF_TYPE(MDLabelRef) CF_TYPE(MDQueryRef) -// CoreServices/OSServices +// MARK: CoreServices/OSServices CF_TYPE(CSIdentityAuthorityRef) CF_TYPE(CSIdentityQueryRef) CF_TYPE(CSIdentityRef) @@ -320,7 +320,7 @@ CF_TYPE(KCSearchRef) CF_TYPE(WSMethodInvocationRef) CF_TYPE(WSProtocolHandlerRef) -// CoreServices/SearchKit +// MARK: CoreServices/SearchKit CF_TYPE(SKDocumentRef) CF_TYPE(SKIndexDocumentIteratorRef) CF_TYPE(SKIndexRef) @@ -329,7 +329,7 @@ CF_TYPE(SKSearchRef) CF_TYPE(SKSearchResultsRef) CF_TYPE(SKSummaryRef) -// CoreText +// MARK: CoreText CF_TYPE(CTFontCollectionRef) CF_TYPE(CTFontDescriptorRef) CF_TYPE(CTFontRef) @@ -345,7 +345,7 @@ CF_TYPE(CTRunRef) CF_TYPE(CTTextTabRef) CF_TYPE(CTTypesetterRef) -// CoreVideo +// MARK: CoreVideo CF_TYPE(CVBufferRef) CF_TYPE(CVDisplayLinkRef) CF_TYPE(CVImageBufferRef) @@ -360,10 +360,10 @@ CF_TYPE(CVOpenGLTextureRef) CF_TYPE(CVPixelBufferPoolRef) CF_TYPE(CVPixelBufferRef) -// DVDPlayback +// MARK: DVDPlayback NON_CF_TYPE(DVDEventCallBackRef) -// DiscRecording +// MARK: DiscRecording CF_TYPE(DRAudioTrackRef) CF_TYPE(DRBurnRef) CF_TYPE(DRCDTextBlockRef) @@ -377,17 +377,17 @@ CF_TYPE(DRNotificationCenterRef) CF_TYPE(DRTrackRef) CF_TYPE(DRTypeRef) -// DiscRecordingUI +// MARK: DiscRecordingUI CF_TYPE(DRBurnSessionRef) CF_TYPE(DREraseSessionRef) -// DiskArbitration +// MARK: DiskArbitration CF_TYPE(DAApprovalSessionRef) CF_TYPE(DADiskRef) CF_TYPE(DADissenterRef) CF_TYPE(DASessionRef) -// FWAUserLib +// MARK: FWAUserLib NON_CF_TYPE(FWAAudioPlugRef) NON_CF_TYPE(FWAAudioStreamRef) NON_CF_TYPE(FWADeviceRef) @@ -398,10 +398,10 @@ NON_CF_TYPE(FWAMIDIPlugRef) NON_CF_TYPE(FWAMIDIStreamRef) NON_CF_TYPE(FWARef) -// GLKit +// MARK: GLKit CF_TYPE(GLKMatrixStackRef) -// IOBluetooth +// MARK: IOBluetooth CF_TYPE(IOBluetoothDeviceRef) CF_TYPE(IOBluetoothL2CAPChannelRef) CF_TYPE(IOBluetoothObjectRef) @@ -413,12 +413,12 @@ CF_TYPE(IOBluetoothUserNotificationRef) NON_CF_TYPE(OBEXSessionRef) NON_CF_TYPE(PrivOBEXSessionDataRef) -// IOBluetoothUI +// MARK: IOBluetoothUI CF_TYPE(IOBluetoothDeviceSelectorControllerRef) CF_TYPE(IOBluetoothPairingControllerRef) CF_TYPE(IOBluetoothServiceBrowserControllerRef) -// IOKit +// MARK: IOKit NON_CF_TYPE(IOBlitMemoryRef) NON_CF_TYPE(IOFWAsyncStreamListenerInterfaceRef) NON_CF_TYPE(IOFireWireLibAsyncStreamCommandRef) @@ -436,17 +436,17 @@ NON_CF_TYPE(IONotificationPortRef) NON_CF_TYPE(IOStreamRef) NON_CF_TYPE(IOVideoDeviceRef) -// IOSurface +// MARK: IOSurface CF_TYPE(IOSurfaceRef) -// ImageIO +// MARK: ImageIO CF_TYPE(CGImageDestinationRef) CF_TYPE(CGImageMetadataRef) CF_TYPE(CGImageMetadataTagRef) CF_TYPE(CGImageSourceRef) CF_TYPE(CGMutableImageMetadataRef) -// JavaScriptCore +// MARK: JavaScriptCore NON_CF_TYPE(JSClassRef) NON_CF_TYPE(JSContextGroupRef) NON_CF_TYPE(JSContextRef) @@ -457,38 +457,38 @@ NON_CF_TYPE(JSPropertyNameArrayRef) NON_CF_TYPE(JSStringRef) NON_CF_TYPE(JSValueRef) -// JavaVM/JavaRuntimeSupport +// MARK: JavaVM/JavaRuntimeSupport CF_TYPE(JRSUIControlRef) CF_TYPE(JRSUIRendererRef) -// Kernel +// MARK: Kernel NON_CF_TYPE(HIDPreparsedDataRef) NON_CF_TYPE(IOFBCursorRef) NON_CF_TYPE(IONotificationRef) NON_CF_TYPE(UNDReplyRef) NON_CF_TYPE(UNDServerRef) -// LatentSemanticMapping +// MARK: LatentSemanticMapping CF_TYPE(LSMMapRef) CF_TYPE(LSMResultRef) CF_TYPE(LSMTextRef) -// MediaToolbox +// MARK: MediaToolbox CF_TYPE(MTAudioProcessingTapRef) -// OpenDirectory/CFOpenDirectory +// MARK: OpenDirectory/CFOpenDirectory CF_TYPE(ODContextRef) CF_TYPE(ODNodeRef) CF_TYPE(ODQueryRef) CF_TYPE(ODRecordRef) CF_TYPE(ODSessionRef) -// QuickLook +// MARK: QuickLook CF_TYPE(QLPreviewRequestRef) CF_TYPE(QLThumbnailRef) CF_TYPE(QLThumbnailRequestRef) -// QuickTime +// MARK: QuickTime NON_CF_TYPE(HandleDataRef) NON_CF_TYPE(ICMCompressionFrameOptionsRef) NON_CF_TYPE(ICMCompressionSessionOptionsRef) @@ -516,7 +516,7 @@ NON_CF_TYPE(RTPPacketGroupRef) NON_CF_TYPE(RTPPacketRef) NON_CF_TYPE(RTPPacketRepeatedDataRef) -// Security +// MARK: Security NON_CF_TYPE(AuthorizationEngineRef) // CFExclusions.def NON_CF_TYPE(AuthorizationMechanismRef) NON_CF_TYPE(AuthorizationPluginRef) @@ -554,7 +554,7 @@ CF_TYPE(SecTrustRef) CF_TYPE(SecTrustedApplicationRef) NON_CF_TYPE(SecureDownloadRef) // CFExclusions.def -// SystemConfiguration +// MARK: SystemConfiguration CF_TYPE(SCBondInterfaceRef) CF_TYPE(SCBondStatusRef) CF_TYPE(SCDynamicStoreRef) @@ -567,7 +567,7 @@ CF_TYPE(SCNetworkSetRef) CF_TYPE(SCPreferencesRef) CF_TYPE(SCVLANInterfaceRef) -// VideoToolbox +// MARK: VideoToolbox CF_TYPE(VTCompressionSessionRef) CF_TYPE(VTDecompressionSessionRef) CF_TYPE(VTFrameSiloRef) @@ -575,10 +575,10 @@ CF_TYPE(VTMultiPassStorageRef) CF_TYPE(VTPixelTransferSessionRef) CF_TYPE(VTSessionRef) -// WebKit +// MARK: WebKit NON_CF_TYPE(JRIGlobalRef) -// dns_sd +// MARK: dns_sd NON_CF_TYPE(DNSRecordRef) NON_CF_TYPE(DNSServiceRef) NON_CF_TYPE(TXTRecordRef) diff --git a/lib/ClangImporter/ClangDiagnosticConsumer.cpp b/lib/ClangImporter/ClangDiagnosticConsumer.cpp index e5f088fe17698..bff677a856764 100644 --- a/lib/ClangImporter/ClangDiagnosticConsumer.cpp +++ b/lib/ClangImporter/ClangDiagnosticConsumer.cpp @@ -66,8 +66,18 @@ namespace { StringRef Message, ArrayRef Ranges, clang::DiagOrStoredDiag Info) override { - if (shouldSuppressDiagInSwiftBuffers(Info) && isInSwiftBuffers(Loc)) - return; + if (isInSwiftBuffers(Loc)) { + // FIXME: Ideally, we'd report non-suppressed diagnostics on synthetic + // buffers, printing their names (eg. :...) but + // this risks printing _excerpts_ of those buffers to stderr too; at + // present the synthetic buffers are "large blocks of null bytes" which + // we definitely don't want to print out. So until we have some clever + // way to print the name but suppress printing excerpts, we just replace + // the Loc with an invalid one here, which suppresses both. + Loc = clang::FullSourceLoc(); + if (shouldSuppressDiagInSwiftBuffers(Info)) + return; + } callback(Loc, Level, Message); } diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index 217d6f6eb94cf..d43189160d36a 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -27,6 +27,7 @@ #include "swift/AST/Module.h" #include "swift/AST/NameLookup.h" #include "swift/AST/Types.h" +#include "swift/Basic/Defer.h" #include "swift/Basic/Platform.h" #include "swift/Basic/Range.h" #include "swift/Basic/StringExtras.h" @@ -439,6 +440,11 @@ getNormalInvocationArguments(std::vector &invocationArgStrs, SHIMS_INCLUDE_FLAG, searchPathOpts.RuntimeResourcePath, }); + // Enable Position Independence. `-fPIC` is not supported on Windows, which + // is implicitly position independent. + if (!triple.isOSWindows()) + invocationArgStrs.insert(invocationArgStrs.end(), {"-fPIC"}); + // Enable modules. invocationArgStrs.insert(invocationArgStrs.end(), { "-fmodules", @@ -458,6 +464,19 @@ getNormalInvocationArguments(std::vector &invocationArgStrs, if (!triple.isOSDarwin()) invocationArgStrs.insert(invocationArgStrs.end(), {"-fobjc-runtime=ios-7.0"}); + + // Define macros that Swift bridging headers use. + invocationArgStrs.insert(invocationArgStrs.end(), { + "-DSWIFT_CLASS_EXTRA=__attribute__((__annotate__(" + "\"" SWIFT_NATIVE_ANNOTATION_STRING "\")))", + "-DSWIFT_PROTOCOL_EXTRA=__attribute__((__annotate__(" + "\"" SWIFT_NATIVE_ANNOTATION_STRING "\")))", + "-DSWIFT_EXTENSION_EXTRA=__attribute__((__annotate__(" + "\"" SWIFT_NATIVE_ANNOTATION_STRING "\")))", + "-DSWIFT_ENUM_EXTRA=__attribute__((__annotate__(" + "\"" SWIFT_NATIVE_ANNOTATION_STRING "\")))", + }); + } else { invocationArgStrs.insert(invocationArgStrs.end(), {"-x", "c", "-std=gnu11"}); } @@ -465,16 +484,6 @@ getNormalInvocationArguments(std::vector &invocationArgStrs, // Set C language options. if (triple.isOSDarwin()) { invocationArgStrs.insert(invocationArgStrs.end(), { - // Define macros that Swift bridging headers use. - "-DSWIFT_CLASS_EXTRA=__attribute__((annotate(\"" - SWIFT_NATIVE_ANNOTATION_STRING "\")))", - "-DSWIFT_PROTOCOL_EXTRA=__attribute__((annotate(\"" - SWIFT_NATIVE_ANNOTATION_STRING "\")))", - "-DSWIFT_EXTENSION_EXTRA=__attribute__((annotate(\"" - SWIFT_NATIVE_ANNOTATION_STRING "\")))", - "-DSWIFT_ENUM_EXTRA=__attribute__((annotate(\"" - SWIFT_NATIVE_ANNOTATION_STRING "\")))", - // Avoid including the iso646.h header because some headers from OS X // frameworks are broken by it. "-D_ISO646_H_", "-D__ISO646_H", @@ -603,12 +612,6 @@ getNormalInvocationArguments(std::vector &invocationArgStrs, // Enable API notes alongside headers/in frameworks. invocationArgStrs.push_back("-fapinotes-modules"); - - // Add API notes paths. - for (const auto &searchPath : searchPathOpts.ImportSearchPaths) { - invocationArgStrs.push_back("-iapinotes-modules"); - invocationArgStrs.push_back(searchPath); - } invocationArgStrs.push_back("-iapinotes-modules"); invocationArgStrs.push_back(searchPathOpts.RuntimeLibraryImportPath); invocationArgStrs.push_back("-fapinotes-swift-version=" + @@ -710,16 +713,32 @@ bool ClangImporter::canReadPCH(StringRef PCHFilename) { CompilerInstance &CI = *Impl.Instance; auto clangDiags = CompilerInstance::createDiagnostics( new clang::DiagnosticOptions()); + + // Note: Reusing the file manager is safe; this is a component that's already + // reused when building PCM files for the module cache. clang::SourceManager clangSrcMgr(*clangDiags, CI.getFileManager()); auto FID = clangSrcMgr.createFileID( llvm::make_unique(1, "
")); clangSrcMgr.setMainFileID(FID); + + // Note: Reusing the real HeaderSearch is dangerous, but it's not easy to + // copy. We put in some effort to reset it to the way it was below. + clang::HeaderSearch &headerSearchInfo = + CI.getPreprocessor().getHeaderSearchInfo(); + assert(headerSearchInfo.getExternalLookup() == nullptr && + "already configured an ASTReader"); + + // Note: Reusing the PCM cache is safe because that's already shared when + // building PCM files for the module cache. Using the top-level compiler + // instance as a module loader does seem a little dangerous but does not + // appear to cause problems at the moment. clang::Preprocessor PP(CI.getInvocation().getPreprocessorOptsPtr(), *clangDiags, CI.getLangOpts(), clangSrcMgr, CI.getPCMCache(), - CI.getPreprocessor().getHeaderSearchInfo(), CI, + headerSearchInfo, + (clang::ModuleLoader &)CI, /*IILookup=*/nullptr, /*OwnsHeaderSearch=*/false); PP.Initialize(CI.getTarget()); @@ -727,6 +746,8 @@ bool ClangImporter::canReadPCH(StringRef PCHFilename) { PP.getIdentifierTable(), PP.getSelectorTable(), PP.getBuiltinInfo()); + // Note: Reusing the PCHContainerReader or ModuleFileExtensions could be + // dangerous. std::unique_ptr Reader(new clang::ASTReader( PP, &ctx, CI.getPCHContainerReader(), CI.getFrontendOpts().ModuleFileExtensions, @@ -735,6 +756,12 @@ bool ClangImporter::canReadPCH(StringRef PCHFilename) { /*AllowPCHWithCompilerErrors*/ false, /*AllowConfigurationMismatch*/ false, /*ValidateSystemInputs*/ true)); + SWIFT_DEFER { + assert(headerSearchInfo.getExternalLookup() == Reader.get() || + headerSearchInfo.getExternalLookup() == nullptr); + headerSearchInfo.SetExternalLookup(nullptr); + headerSearchInfo.SetExternalSource(nullptr); + }; ctx.InitBuiltinTypes(CI.getTarget()); auto result = Reader->ReadAST(PCHFilename, @@ -865,46 +892,43 @@ ClangImporter::create(ASTContext &ctx, } } - // FIXME: These can't be controlled from the command line. - llvm::IntrusiveRefCntPtr diagnosticOpts{ - new clang::DiagnosticOptions - }; + // Create a new Clang compiler invocation. + { + // Set up a temporary diagnostic client to report errors from parsing the + // command line, which may be important for Swift clients if, for example, + // they're using -Xcc options. Unfortunately this diagnostic engine has to + // use the default options because the /actual/ options haven't been parsed + // yet. + // + // The long-term client for Clang diagnostics is set up below, after the + // clang::CompilerInstance is created. + llvm::IntrusiveRefCntPtr tempDiagOpts{ + new clang::DiagnosticOptions + }; - std::unique_ptr diagClient{ - new ClangDiagnosticConsumer(importer->Impl, *diagnosticOpts, - importerOpts.DumpClangDiagnostics) - }; - auto clangDiags = - clang::CompilerInstance::createDiagnostics(diagnosticOpts.get(), - diagClient.release()); + ClangDiagnosticConsumer tempDiagClient{importer->Impl, *tempDiagOpts, + importerOpts.DumpClangDiagnostics}; + llvm::IntrusiveRefCntPtr tempClangDiags = + clang::CompilerInstance::createDiagnostics(tempDiagOpts.get(), + &tempDiagClient, + /*owned*/false); - // Create a new Clang compiler invocation. - importer->Impl.Invocation = - clang::createInvocationFromCommandLine(invocationArgs, clangDiags); - if (!importer->Impl.Invocation) - return nullptr; + importer->Impl.Invocation = + clang::createInvocationFromCommandLine(invocationArgs, tempClangDiags); + if (!importer->Impl.Invocation) + return nullptr; + } - // Don't stop emitting messages if we ever can't load a module. - // FIXME: This is actually a general problem: any "fatal" error could mess up - // the CompilerInvocation when we're not in "show diagnostics after fatal - // error" mode. - clangDiags->setSeverity(clang::diag::err_module_not_found, - clang::diag::Severity::Error, - clang::SourceLocation()); - clangDiags->setSeverity(clang::diag::err_module_not_built, - clang::diag::Severity::Error, - clang::SourceLocation()); - clangDiags->setSuppressAfterFatalError( - !ctx.Diags.getShowDiagnosticsAfterFatalError()); - - // Create an almost-empty memory buffer. - auto sourceBuffer = llvm::MemoryBuffer::getMemBuffer( - "extern int __swift __attribute__((unavailable));", - Implementation::moduleImportBufferName); - clang::PreprocessorOptions &ppOpts = - importer->Impl.Invocation->getPreprocessorOpts(); - ppOpts.addRemappedFile(Implementation::moduleImportBufferName, - sourceBuffer.release()); + { + // Create an almost-empty memory buffer. + auto sourceBuffer = llvm::MemoryBuffer::getMemBuffer( + "extern int __swift __attribute__((unavailable));", + Implementation::moduleImportBufferName); + clang::PreprocessorOptions &ppOpts = + importer->Impl.Invocation->getPreprocessorOpts(); + ppOpts.addRemappedFile(Implementation::moduleImportBufferName, + sourceBuffer.release()); + } // Install a Clang module file extension to build Swift name lookup tables. importer->Impl.Invocation->getFrontendOpts().ModuleFileExtensions.push_back( @@ -915,20 +939,44 @@ ClangImporter::create(ASTContext &ctx, importer->Impl.InferImportAsMember)); // Create a compiler instance. - auto PCHContainerOperations = - std::make_shared(); - PCHContainerOperations->registerWriter( - llvm::make_unique()); - PCHContainerOperations->registerReader( - llvm::make_unique()); - importer->Impl.Instance.reset( - new clang::CompilerInstance(PCHContainerOperations)); + { + auto PCHContainerOperations = + std::make_shared(); + PCHContainerOperations->registerWriter( + llvm::make_unique()); + PCHContainerOperations->registerReader( + llvm::make_unique()); + importer->Impl.Instance.reset( + new clang::CompilerInstance(std::move(PCHContainerOperations))); + } auto &instance = *importer->Impl.Instance; + instance.setInvocation(importer->Impl.Invocation); if (tracker) instance.addDependencyCollector(tracker->getClangCollector()); - instance.setDiagnostics(&*clangDiags); - instance.setInvocation(importer->Impl.Invocation); + { + // Now set up the real client for Clang diagnostics---configured with proper + // options---as opposed to the temporary one we made above. + auto actualDiagClient = llvm::make_unique( + importer->Impl, instance.getDiagnosticOpts(), + importerOpts.DumpClangDiagnostics); + instance.createDiagnostics(actualDiagClient.release()); + } + + // Don't stop emitting messages if we ever can't load a module. + // FIXME: This is actually a general problem: any "fatal" error could mess up + // the CompilerInvocation when we're not in "show diagnostics after fatal + // error" mode. + clang::DiagnosticsEngine &clangDiags = instance.getDiagnostics(); + clangDiags.setSeverity(clang::diag::err_module_not_found, + clang::diag::Severity::Error, + clang::SourceLocation()); + clangDiags.setSeverity(clang::diag::err_module_not_built, + clang::diag::Severity::Error, + clang::SourceLocation()); + clangDiags.setSuppressAfterFatalError( + !ctx.Diags.getShowDiagnosticsAfterFatalError()); + // Create the associated action. importer->Impl.Action.reset(new ParsingAction(ctx, *importer, @@ -945,7 +993,7 @@ ClangImporter::create(ASTContext &ctx, // Create the target instance. instance.setTarget( - clang::TargetInfo::CreateTargetInfo(*clangDiags, + clang::TargetInfo::CreateTargetInfo(clangDiags, instance.getInvocation().TargetOpts)); if (!instance.hasTarget()) return nullptr; @@ -1032,6 +1080,7 @@ ClangImporter::create(ASTContext &ctx, importer->Impl.ImportedHeaderUnit = new (ctx) ClangModuleUnit(*importedHeaderModule, importer->Impl, nullptr); importedHeaderModule->addFile(*importer->Impl.ImportedHeaderUnit); + importedHeaderModule->setHasResolvedImports(); importer->Impl.IsReadingBridgingPCH = false; @@ -1535,6 +1584,7 @@ ModuleDecl *ClangImporter::Implementation::finishLoadingClangModule( result = ModuleDecl::create(name, SwiftContext); // Silence error messages about testably importing a Clang module. result->setTestingEnabled(); + result->setHasResolvedImports(); wrapperUnit = new (SwiftContext) ClangModuleUnit(*result, *this, clangModule); @@ -1661,7 +1711,6 @@ ClangImporter::Implementation::Implementation(ASTContext &ctx, nameImporter() {} ClangImporter::Implementation::~Implementation() { - assert(NumCurrentImportingEntities == 0); #ifndef NDEBUG SwiftContext.SourceMgr.verifyAllBuffers(); #endif @@ -1678,6 +1727,7 @@ ClangModuleUnit *ClangImporter::Implementation::getWrapperForModule( auto wrapper = ModuleDecl::create(name, SwiftContext); // Silence error messages about testably importing a Clang module. wrapper->setTestingEnabled(); + wrapper->setHasResolvedImports(); auto file = new (SwiftContext) ClangModuleUnit(*wrapper, *this, underlying); @@ -2405,6 +2455,15 @@ void ClangModuleUnit::getTopLevelDecls(SmallVectorImpl &results) const { results.push_back(extension); } + auto findEnclosingExtension = [](Decl *importedDecl) -> ExtensionDecl * { + for (auto importedDC = importedDecl->getDeclContext(); + !importedDC->isModuleContext(); + importedDC = importedDC->getParent()) { + if (auto ext = dyn_cast(importedDC)) + return ext; + } + return nullptr; + }; // Retrieve all of the globals that will be mapped to members. // FIXME: Since we don't represent Clang submodules as Swift @@ -2412,21 +2471,33 @@ void ClangModuleUnit::getTopLevelDecls(SmallVectorImpl &results) const { llvm::SmallPtrSet knownExtensions; for (auto entry : lookupTable->allGlobalsAsMembers()) { auto decl = entry.get(); - auto importedDecl = - owner.importDecl(decl, owner.CurrentVersion); + auto importedDecl = owner.importDecl(decl, owner.CurrentVersion); if (!importedDecl) continue; // Find the enclosing extension, if there is one. - ExtensionDecl *ext = nullptr; - for (auto importedDC = importedDecl->getDeclContext(); - !importedDC->isModuleContext(); - importedDC = importedDC->getParent()) { - ext = dyn_cast(importedDC); - if (ext) break; - } - if (!ext) continue; + ExtensionDecl *ext = findEnclosingExtension(importedDecl); + if (ext && knownExtensions.insert(ext).second) + results.push_back(ext); + + // If this is a compatibility typealias, the canonical type declaration + // may exist in another extension. + auto alias = dyn_cast(importedDecl); + if (!alias || !alias->isCompatibilityAlias()) continue; + + auto aliasedTy = alias->getUnderlyingTypeLoc().getType(); + ext = nullptr; + importedDecl = nullptr; + + // Note: We can't use getAnyGeneric() here because `aliasedTy` + // might be typealias. + if (auto Ty = dyn_cast(aliasedTy.getPointer())) + importedDecl = Ty->getDecl(); + else if (auto Ty = dyn_cast(aliasedTy.getPointer())) + importedDecl = Ty->getDecl(); + if (!importedDecl) continue; - if (knownExtensions.insert(ext).second) + ext = findEnclosingExtension(importedDecl); + if (ext && knownExtensions.insert(ext).second) results.push_back(ext); } } @@ -2812,6 +2883,11 @@ void ClangModuleUnit::collectLinkLibraries( ModuleDecl::LinkLibraryCallback callback) const { if (!clangModule) return; + + // Skip this lib name in favor of export_as name. + if (clangModule->UseExportAsModuleLinkName) + return; + for (auto clangLinkLib : clangModule->LinkLibraries) { LibraryKind kind; if (clangLinkLib.IsFramework) @@ -2984,43 +3060,70 @@ ModuleDecl *ClangModuleUnit::getAdapterModule() const { void ClangModuleUnit::getImportedModules( SmallVectorImpl &imports, ModuleDecl::ImportFilter filter) const { - if (filter != ModuleDecl::ImportFilter::Public) + switch (filter) { + case ModuleDecl::ImportFilter::All: + case ModuleDecl::ImportFilter::Private: imports.push_back({ModuleDecl::AccessPathTy(), owner.getStdlibModule()}); + break; + case ModuleDecl::ImportFilter::Public: + break; + } SmallVector imported; if (!clangModule) { // This is the special "imported headers" module. - if (filter != ModuleDecl::ImportFilter::Private) { + switch (filter) { + case ModuleDecl::ImportFilter::All: + case ModuleDecl::ImportFilter::Public: imported.append(owner.ImportedHeaderExports.begin(), owner.ImportedHeaderExports.end()); + break; + + case ModuleDecl::ImportFilter::Private: + break; } } else { clangModule->getExportedModules(imported); - if (filter != ModuleDecl::ImportFilter::Public) { - if (filter == ModuleDecl::ImportFilter::All) { - llvm::SmallPtrSet knownModules; - imported.append(clangModule->Imports.begin(), clangModule->Imports.end()); - imported.erase(std::remove_if(imported.begin(), imported.end(), - [&](clang::Module *mod) -> bool { - return !knownModules.insert(mod).second; - }), - imported.end()); - } else { - llvm::SmallPtrSet knownModules(imported.begin(), - imported.end()); - SmallVector privateImports; - std::copy_if(clangModule->Imports.begin(), clangModule->Imports.end(), - std::back_inserter(privateImports), [&](clang::Module *mod) { - return knownModules.count(mod) == 0; - }); - imported.swap(privateImports); - } + + switch (filter) { + case ModuleDecl::ImportFilter::All: { + llvm::SmallPtrSet knownModules; + imported.append(clangModule->Imports.begin(), clangModule->Imports.end()); + imported.erase(std::remove_if(imported.begin(), imported.end(), + [&](clang::Module *mod) -> bool { + return !knownModules.insert(mod).second; + }), + imported.end()); + + // FIXME: The parent module isn't exactly a private import, but it is + // needed for link dependencies. + if (clangModule->Parent) + imported.push_back(clangModule->Parent); + + break; + } + + case ModuleDecl::ImportFilter::Private: { + llvm::SmallPtrSet knownModules(imported.begin(), + imported.end()); + SmallVector privateImports; + std::copy_if(clangModule->Imports.begin(), clangModule->Imports.end(), + std::back_inserter(privateImports), [&](clang::Module *mod) { + return knownModules.count(mod) == 0; + }); + imported.swap(privateImports); // FIXME: The parent module isn't exactly a private import, but it is // needed for link dependencies. if (clangModule->Parent) imported.push_back(clangModule->Parent); + + break; + } + + case ModuleDecl::ImportFilter::Public: + break; } } diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 2a4e4413ce67e..3071be1b08685 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -105,9 +105,9 @@ static bool isInSystemModule(DeclContext *D) { return cast(D->getModuleScopeContext())->isSystemModule(); } -static AccessLevel getOverridableAccessLevel(DeclContext *dc) { - return (dc->getAsProtocolOrProtocolExtensionContext() - ? AccessLevel::Public : AccessLevel::Open); +static AccessLevel getOverridableAccessLevel(const DeclContext *dc) { + return (dc->getAsClassOrClassExtensionContext() + ? AccessLevel::Open : AccessLevel::Public); } /// Create a typedpattern(namedpattern(decl)) @@ -386,35 +386,6 @@ static bool isNSDictionaryMethod(const clang::ObjCMethodDecl *MD, return true; } -void ClangImporter::Implementation::forEachDistinctName( - const clang::NamedDecl *decl, - llvm::function_ref action) { - using ImportNameKey = std::pair; - SmallVector seenNames; - - ImportedName newName = importFullName(decl, CurrentVersion); - ImportNameKey key(newName, newName.getEffectiveContext()); - if (action(newName, CurrentVersion)) - seenNames.push_back(key); - - CurrentVersion.forEachOtherImportNameVersion( - [&](ImportNameVersion nameVersion) { - // Check to see if the name is different. - ImportedName newName = importFullName(decl, nameVersion); - ImportNameKey key(newName, newName.getEffectiveContext()); - bool seen = llvm::any_of(seenNames, - [&key](const ImportNameKey &existing) -> bool { - if (key.first != existing.first) - return false; - return key.second.equalsWithoutResolving(existing.second); - }); - if (seen) - return; - if (action(newName, nameVersion)) - seenNames.push_back(key); - }); -} - // Build the init(rawValue:) initializer for an imported NS_ENUM. // enum NSSomeEnum: RawType { // init?(rawValue: RawType) { @@ -476,10 +447,12 @@ makeEnumRawValueConstructor(ClangImporter::Implementation &Impl, paramRef->setType(param->getType()); auto reinterpretCast - = getBuiltinValueDecl(C, C.getIdentifier("reinterpretCast")); - ConcreteDeclRef concreteDeclRef(C, reinterpretCast, - { Substitution(rawTy, {}), - Substitution(enumTy, {}) }); + = cast( + getBuiltinValueDecl(C, C.getIdentifier("reinterpretCast"))); + SubstitutionMap subMap = + SubstitutionMap::get(reinterpretCast->getGenericSignature(), + { rawTy, enumTy }, { }); + ConcreteDeclRef concreteDeclRef(reinterpretCast, subMap); auto reinterpretCastRef = new (C) DeclRefExpr(concreteDeclRef, DeclNameLoc(), /*implicit*/ true); reinterpretCastRef->setType(FunctionType::get({rawTy}, enumTy)); @@ -487,6 +460,7 @@ makeEnumRawValueConstructor(ClangImporter::Implementation &Impl, auto reinterpreted = CallExpr::createImplicit(C, reinterpretCastRef, { paramRef }, { Identifier() }); reinterpreted->setType(enumTy); + reinterpreted->setThrows(false); auto assign = new (C) AssignExpr(selfRef, SourceLoc(), reinterpreted, /*implicit*/ true); @@ -502,7 +476,7 @@ makeEnumRawValueConstructor(ClangImporter::Implementation &Impl, ctorDecl->setBody(body); ctorDecl->setBodyTypeCheckedIfPresent(); - C.addExternalDecl(ctorDecl); + Impl.registerExternalDecl(ctorDecl); return ctorDecl; } @@ -562,10 +536,13 @@ static AccessorDecl *makeEnumRawValueGetter(ClangImporter::Implementation &Impl, selfRef->setType(selfDecl->getType()); auto reinterpretCast - = getBuiltinValueDecl(C, C.getIdentifier("reinterpretCast")); - ConcreteDeclRef concreteDeclRef(C, reinterpretCast, - { Substitution(enumTy, {}), - Substitution(rawTy, {}) }); + = cast( + getBuiltinValueDecl(C, C.getIdentifier("reinterpretCast"))); + SubstitutionMap subMap = + SubstitutionMap::get(reinterpretCast->getGenericSignature(), + { enumTy, rawTy }, { }); + ConcreteDeclRef concreteDeclRef(reinterpretCast, subMap); + auto reinterpretCastRef = new (C) DeclRefExpr(concreteDeclRef, DeclNameLoc(), /*implicit*/ true); reinterpretCastRef->setType(FunctionType::get({enumTy}, rawTy)); @@ -573,6 +550,7 @@ static AccessorDecl *makeEnumRawValueGetter(ClangImporter::Implementation &Impl, auto reinterpreted = CallExpr::createImplicit(C, reinterpretCastRef, { selfRef }, { Identifier() }); reinterpreted->setType(rawTy); + reinterpreted->setThrows(false); auto ret = new (C) ReturnStmt(SourceLoc(), reinterpreted); auto body = BraceStmt::create(C, SourceLoc(), ASTNode(ret), SourceLoc(), @@ -580,7 +558,7 @@ static AccessorDecl *makeEnumRawValueGetter(ClangImporter::Implementation &Impl, getterDecl->setBody(body); getterDecl->setBodyTypeCheckedIfPresent(); - C.addExternalDecl(getterDecl); + Impl.registerExternalDecl(getterDecl); return getterDecl; } @@ -663,7 +641,7 @@ static AccessorDecl *makeStructRawValueGetter( getterDecl->setBody(body); getterDecl->setBodyTypeCheckedIfPresent(); - C.addExternalDecl(getterDecl); + Impl.registerExternalDecl(getterDecl); return getterDecl; } @@ -830,7 +808,7 @@ makeIndirectFieldAccessors(ClangImporter::Implementation &Impl, /*implicit*/ true); getterDecl->setBody(body); getterDecl->getAttrs().add(new (C) TransparentAttr(/*implicit*/ true)); - C.addExternalDecl(getterDecl); + Impl.registerExternalDecl(getterDecl); } // Synthesize the setter body @@ -855,7 +833,7 @@ makeIndirectFieldAccessors(ClangImporter::Implementation &Impl, /*implicit*/ true); setterDecl->setBody(body); setterDecl->getAttrs().add(new (C) TransparentAttr(/*implicit*/ true)); - C.addExternalDecl(setterDecl); + Impl.registerExternalDecl(setterDecl); } return { getterDecl, setterDecl }; @@ -911,12 +889,13 @@ makeUnionFieldAccessors(ClangImporter::Implementation &Impl, auto reinterpreted = CallExpr::createImplicit(C, reinterpretCastRef, { selfRef }, { Identifier() }); + reinterpreted->setThrows(false); auto ret = new (C) ReturnStmt(SourceLoc(), reinterpreted); auto body = BraceStmt::create(C, SourceLoc(), ASTNode(ret), SourceLoc(), /*implicit*/ true); getterDecl->setBody(body); getterDecl->getAttrs().add(new (C) TransparentAttr(/*implicit*/ true)); - C.addExternalDecl(getterDecl); + Impl.registerExternalDecl(getterDecl); } // Synthesize the setter body @@ -950,7 +929,7 @@ makeUnionFieldAccessors(ClangImporter::Implementation &Impl, /*implicit*/ true); setterDecl->setBody(body); setterDecl->getAttrs().add(new (C) TransparentAttr(/*implicit*/ true)); - C.addExternalDecl(setterDecl); + Impl.registerExternalDecl(setterDecl); } return { getterDecl, setterDecl }; @@ -1204,9 +1183,12 @@ createDefaultConstructor(ClangImporter::Implementation &Impl, // Construct the right-hand call to Builtin.zeroInitializer. Identifier zeroInitID = context.getIdentifier("zeroInitializer"); - auto zeroInitializerFunc = getBuiltinValueDecl(context, zeroInitID); - ConcreteDeclRef concreteDeclRef(context, zeroInitializerFunc, - { Substitution(selfType, {}) }); + auto zeroInitializerFunc = + cast(getBuiltinValueDecl(context, zeroInitID)); + SubstitutionMap subMap = + SubstitutionMap::get(zeroInitializerFunc->getGenericSignature(), + llvm::makeArrayRef(selfType), { }); + ConcreteDeclRef concreteDeclRef(zeroInitializerFunc, subMap); auto zeroInitializerRef = new (context) DeclRefExpr(concreteDeclRef, DeclNameLoc(), /*implicit*/ true); @@ -2164,7 +2146,9 @@ namespace { if (canonicalVersion != getActiveSwiftVersion()) { auto activeName = Impl.importFullName(D, getActiveSwiftVersion()); if (activeName && - activeName.getDeclName() == canonicalName.getDeclName()) { + activeName.getDeclName() == canonicalName.getDeclName() && + activeName.getEffectiveContext().equalsWithoutResolving( + canonicalName.getEffectiveContext())) { return ImportedName(); } } @@ -2181,7 +2165,9 @@ namespace { if (!alternateName) return ImportedName(); - if (alternateName.getDeclName() == canonicalName.getDeclName()) { + if (alternateName.getDeclName() == canonicalName.getDeclName() && + alternateName.getEffectiveContext().equalsWithoutResolving( + canonicalName.getEffectiveContext())) { if (getVersion() == getActiveSwiftVersion()) { assert(canonicalVersion != getActiveSwiftVersion()); return alternateName; @@ -2348,19 +2334,31 @@ namespace { PlatformAgnosticAvailabilityKind::UnavailableInSwift); } else { unsigned majorVersion = getVersion().majorVersionNumber(); + unsigned minorVersion = getVersion().minorVersionNumber(); if (getVersion() < getActiveSwiftVersion()) { // A Swift 2 name, for example, was obsoleted in Swift 3. + // However, a Swift 4 name is obsoleted in Swift 4.2. + // FIXME: it would be better to have a unified place + // to represent Swift versions for API versioning. + clang::VersionTuple obsoletedVersion = + (majorVersion == 4 && minorVersion < 2) + ? clang::VersionTuple(4, 2) + : clang::VersionTuple(majorVersion + 1); attr = AvailableAttr::createPlatformAgnostic( ctx, /*Message*/StringRef(), ctx.AllocateCopy(renamed.str()), PlatformAgnosticAvailabilityKind::SwiftVersionSpecific, - clang::VersionTuple(majorVersion + 1)); + obsoletedVersion); } else { // Future names are introduced in their future version. assert(getVersion() > getActiveSwiftVersion()); + clang::VersionTuple introducedVersion = + (majorVersion == 4 && minorVersion == 2) + ? clang::VersionTuple(4, 2) + : clang::VersionTuple(majorVersion); attr = new (ctx) AvailableAttr( SourceLoc(), SourceRange(), PlatformKind::none, /*Message*/StringRef(), ctx.AllocateCopy(renamed.str()), - /*Introduced*/clang::VersionTuple(majorVersion), SourceRange(), + /*Introduced*/introducedVersion, SourceRange(), /*Deprecated*/clang::VersionTuple(), SourceRange(), /*Obsoleted*/clang::VersionTuple(), SourceRange(), PlatformAgnosticAvailabilityKind::SwiftVersionSpecific, @@ -2526,7 +2524,7 @@ namespace { clang::QualType ClangType = Decl->getUnderlyingType(); SwiftType = Impl.importTypeIgnoreIUO( ClangType, ImportTypeKind::Typedef, isInSystemModule(DC), - getTypedefBridgeability(ClangType), OTK_Optional); + getTypedefBridgeability(Decl, ClangType), OTK_Optional); } if (!SwiftType) @@ -2639,6 +2637,7 @@ namespace { AccessLevel::Public, Loc, name, Loc, None, nullptr, dc); structDecl->computeType(); structDecl->setCheckedInheritanceClause(); + structDecl->setAddedImplicitInitializers(); auto options = getDefaultMakeStructRawValuedOptions(); options |= MakeStructRawValuedFlags::MakeUnlabeledValueInit; @@ -2689,6 +2688,7 @@ namespace { errorWrapper = new (C) StructDecl(loc, name, loc, None, nullptr, dc); errorWrapper->computeType(); errorWrapper->setValidationStarted(); + errorWrapper->setAddedImplicitInitializers(); errorWrapper->setAccess(AccessLevel::Public); errorWrapper->getAttrs().add( new (Impl.SwiftContext) FixedLayoutAttr(/*IsImplicit*/true)); @@ -3124,6 +3124,7 @@ namespace { Impl.importSourceLoc(decl->getLocation()), None, nullptr, dc); result->computeType(); + result->setAddedImplicitInitializers(); Impl.ImportedDecls[{decl->getCanonicalDecl(), getVersion()}] = result; // FIXME: Figure out what to do with superclasses in C++. One possible @@ -4265,7 +4266,7 @@ namespace { /// given vector, guarded by the known set of protocols. void addProtocols(ProtocolDecl *protocol, SmallVectorImpl &protocols, - llvm::SmallPtrSet &known); + llvm::SmallPtrSetImpl &known); // Import the given Objective-C protocol list, along with any // implicitly-provided protocols, and attach them to the given @@ -5300,6 +5301,7 @@ SwiftDeclConverter::importSwiftNewtype(const clang::TypedefNameDecl *decl, decl, AccessLevel::Public, Loc, name, Loc, None, nullptr, dc); structDecl->computeType(); structDecl->setCheckedInheritanceClause(); + structDecl->setAddedImplicitInitializers(); // Import the type of the underlying storage auto storedUnderlyingType = Impl.importTypeIgnoreIUO( @@ -5578,6 +5580,7 @@ SwiftDeclConverter::importAsOptionSetType(DeclContext *dc, Identifier name, decl, AccessLevel::Public, Loc, name, Loc, None, nullptr, dc); structDecl->computeType(); structDecl->setCheckedInheritanceClause(); + structDecl->setAddedImplicitInitializers(); makeStructRawValued(Impl, structDecl, underlyingType, {KnownProtocolKind::OptionSet}); @@ -6766,7 +6769,7 @@ SwiftDeclConverter::importAccessor(clang::ObjCMethodDecl *clangAccessor, void SwiftDeclConverter::addProtocols( ProtocolDecl *protocol, SmallVectorImpl &protocols, - llvm::SmallPtrSet &known) { + llvm::SmallPtrSetImpl &known) { if (!known.insert(protocol).second) return; @@ -7742,36 +7745,12 @@ ClangImporter::Implementation::importDeclImpl(const clang::NamedDecl *ClangDecl, } void ClangImporter::Implementation::startedImportingEntity() { - ++NumCurrentImportingEntities; ++NumTotalImportedEntities; // FIXME: (transitional) increment the redundant "always-on" counter. if (SwiftContext.Stats) SwiftContext.Stats->getFrontendCounters().NumTotalClangImportedEntities++; } -void ClangImporter::Implementation::finishedImportingEntity() { - assert(NumCurrentImportingEntities && - "finishedImportingEntity not paired with startedImportingEntity"); - if (NumCurrentImportingEntities == 1) { - // We decrease NumCurrentImportingEntities only after pending actions - // are finished, to avoid recursively re-calling finishPendingActions(). - finishPendingActions(); - } - --NumCurrentImportingEntities; -} - -void ClangImporter::Implementation::finishPendingActions() { - if (RegisteredExternalDecls.empty()) - return; - - if (!hasFinishedTypeChecking()) { - for (auto *D : RegisteredExternalDecls) - SwiftContext.addExternalDecl(D); - } - - RegisteredExternalDecls.clear(); -} - /// Look up associated type requirements in the conforming type. static void finishTypeWitnesses( NormalProtocolConformance *conformance) { @@ -7982,7 +7961,7 @@ Decl *ClangImporter::Implementation::importDeclAndCacheImpl( bool TypedefIsSuperfluous = false; bool HadForwardDeclaration = false; - ImportingEntityRAII ImportingEntity(*this); + startedImportingEntity(); Decl *Result = importDeclImpl(ClangDecl, version, TypedefIsSuperfluous, HadForwardDeclaration); if (!Result) @@ -8479,7 +8458,7 @@ void ClangImporter::Implementation::loadAllMembersIntoExtension( return; // Get ready to actually load the members. - ImportingEntityRAII Importing(*this); + startedImportingEntity(); // Load the members. for (auto entry : table->lookupGlobalsAsMembers(effectiveClangContext)) { @@ -8579,13 +8558,11 @@ void ClangImporter::Implementation::loadAllMembersOfObjcContainer( loadMembersOfBaseImportedFromClang(ext); } - ImportingEntityRAII Importing(*this); + startedImportingEntity(); SmallVector members; collectMembersToAdd(objcContainer, D, DC, members); - // Add the members now, before ~ImportingEntityRAII does work that might - // involve them. for (auto member : members) { IDC->addMember(member); } diff --git a/lib/ClangImporter/ImportMacro.cpp b/lib/ClangImporter/ImportMacro.cpp index b16928848f9ca..74d4d7041ccd8 100644 --- a/lib/ClangImporter/ImportMacro.cpp +++ b/lib/ClangImporter/ImportMacro.cpp @@ -672,7 +672,8 @@ ValueDecl *ClangImporter::Implementation::importMacro(Identifier name, known->second.push_back({macro, nullptr}); } - ImportingEntityRAII ImportingEntity(*this); + startedImportingEntity(); + // We haven't tried to import this macro yet. Do so now, and cache the // result. diff --git a/lib/ClangImporter/ImportName.cpp b/lib/ClangImporter/ImportName.cpp index 8fcb6eb90de32..3712dd927e24d 100644 --- a/lib/ClangImporter/ImportName.cpp +++ b/lib/ClangImporter/ImportName.cpp @@ -1779,6 +1779,40 @@ ImportedName NameImporter::importName(const clang::NamedDecl *decl, return res; } +bool NameImporter::forEachDistinctImportName( + const clang::NamedDecl *decl, ImportNameVersion activeVersion, + llvm::function_ref action) { + using ImportNameKey = std::pair; + SmallVector seenNames; + + ImportedName newName = importName(decl, activeVersion); + if (!newName) + return true; + ImportNameKey key(newName.getDeclName(), newName.getEffectiveContext()); + if (action(newName, activeVersion)) + seenNames.push_back(key); + + activeVersion.forEachOtherImportNameVersion( + [&](ImportNameVersion nameVersion) { + // Check to see if the name is different. + ImportedName newName = importName(decl, nameVersion); + if (!newName) + return; + ImportNameKey key(newName.getDeclName(), newName.getEffectiveContext()); + + bool seen = llvm::any_of( + seenNames, [&key](const ImportNameKey &existing) -> bool { + return key.first == existing.first && + key.second.equalsWithoutResolving(existing.second); + }); + if (seen) + return; + if (action(newName, nameVersion)) + seenNames.push_back(key); + }); + return false; +} + const InheritedNameSet *NameImporter::getAllPropertyNames( clang::ObjCInterfaceDecl *classDecl, bool forInstance) { @@ -1846,4 +1880,3 @@ const InheritedNameSet *NameImporter::getAllPropertyNames( return known->second.get(); } - diff --git a/lib/ClangImporter/ImportName.h b/lib/ClangImporter/ImportName.h index dc67c5cf481ce..c16b069f9dda4 100644 --- a/lib/ClangImporter/ImportName.h +++ b/lib/ClangImporter/ImportName.h @@ -334,6 +334,28 @@ class NameImporter { clang::DeclarationName preferredName = clang::DeclarationName()); + /// Attempts to import the name of \p decl with each possible + /// ImportNameVersion. \p action will be called with each unique name. + /// + /// In this case, "unique" means either the full name is distinct or the + /// effective context is distinct. This method does not attempt to handle + /// "unresolved" contexts in any special way---if one name references a + /// particular Clang declaration and the other has an unresolved context that + /// will eventually reference that declaration, the contexts will still be + /// considered distinct. + /// + /// If \p action returns false, the current name will \e not be added to the + /// set of seen names. + /// + /// The active name for \p activeVerion is always first, followed by the + /// other names in the order of + /// ImportNameVersion::forEachOtherImportNameVersion. + /// + /// Returns \c true if it fails to import name for the active version. + bool forEachDistinctImportName( + const clang::NamedDecl *decl, ImportNameVersion activeVersion, + llvm::function_ref action); + /// Imports the name of the given Clang macro into Swift. Identifier importMacroName(const clang::IdentifierInfo *clangIdentifier, const clang::MacroInfo *macro); diff --git a/lib/ClangImporter/ImportType.cpp b/lib/ClangImporter/ImportType.cpp index 1cea178bc52ea..fc58c940602e9 100644 --- a/lib/ClangImporter/ImportType.cpp +++ b/lib/ClangImporter/ImportType.cpp @@ -663,12 +663,11 @@ namespace { auto underlyingType = type->desugar(); // Figure out the bridgeability we would normally use for this typedef. - auto typedefBridgeability = getTypedefBridgeability(underlyingType); + auto typedefBridgeability = + getTypedefBridgeability(type->getDecl(), underlyingType); // Figure out the typedef we should actually use. - auto underlyingBridgeability = - (Bridging == Bridgeability::Full - ? typedefBridgeability : Bridgeability::None); + auto underlyingBridgeability = Bridging; SwiftTypeConverter innerConverter(Impl, AllowNSUIntegerAsInt, underlyingBridgeability); auto underlyingResult = innerConverter.Visit(underlyingType); @@ -685,7 +684,8 @@ namespace { #ifndef NDEBUG switch (underlyingResult.Hint) { case ImportHint::Block: - // Blocks change in all sorts of ways, due to bridging. + case ImportHint::ObjCBridged: + // Bridging is fine for Objective-C and blocks. break; case ImportHint::NSUInteger: // NSUInteger might be imported as Int rather than UInt depending @@ -1088,7 +1088,6 @@ namespace { static bool canBridgeTypes(ImportTypeKind importKind) { switch (importKind) { case ImportTypeKind::Abstract: - case ImportTypeKind::Typedef: case ImportTypeKind::Value: case ImportTypeKind::Variable: case ImportTypeKind::AuditedVariable: @@ -1104,6 +1103,7 @@ static bool canBridgeTypes(ImportTypeKind importKind) { case ImportTypeKind::Property: case ImportTypeKind::PropertyWithReferenceSemantics: case ImportTypeKind::BridgedValue: + case ImportTypeKind::Typedef: return true; } @@ -1286,7 +1286,7 @@ static ImportedType adjustTypeForConcreteImport( // In a bridgeable context, or in the direct structure of a typedef, // we would prefer to instead use the default Swift convention. if (hint == ImportHint::Block) { - if (canBridgeTypes(importKind) || importKind == ImportTypeKind::Typedef) { + if (canBridgeTypes(importKind)) { auto fTy = importedType->castTo(); FunctionType::ExtInfo einfo = fTy->getExtInfo(); if (einfo.getRepresentation() != FunctionTypeRepresentation::Swift) { @@ -1330,13 +1330,18 @@ static ImportedType adjustTypeForConcreteImport( // bridge, do so. if (hint == ImportHint::ObjCBridged && canBridgeTypes(importKind) && - importKind != ImportTypeKind::PropertyWithReferenceSemantics) { + importKind != ImportTypeKind::PropertyWithReferenceSemantics && + !(importKind == ImportTypeKind::Typedef && + bridging == Bridgeability::None)) { // id and Any can be bridged without Foundation. There would be // bootstrapping issues with the ObjectiveC module otherwise. if (hint.BridgedType->isAny() || impl.tryLoadFoundationModule() || impl.ImportForwardDeclarations) { - importedType = hint.BridgedType; + + // Set the bridged type if it wasn't done already. + if (!importedType->isEqual(hint.BridgedType)) + importedType = hint.BridgedType; } } @@ -2015,9 +2020,6 @@ ImportedType ClangImporter::Implementation::importMethodType( continue; } - if (kind == SpecialMethodKind::NSDictionarySubscriptGetter) - nonNullArgs.empty(); - // Import the parameter type into Swift. // Check nullability of the parameter. @@ -2487,7 +2489,7 @@ Type ClangImporter::Implementation::getSugaredTypeReference(TypeDecl *type) { } return NameAliasType::get(typealias, parentType, SubstitutionMap(), - typealias->getUnderlyingTypeLoc().getType()); + typealias->getUnderlyingTypeLoc().getType()); } return type->getDeclaredInterfaceType(); diff --git a/lib/ClangImporter/ImporterImpl.h b/lib/ClangImporter/ImporterImpl.h index 080f8f0931941..d42fb88394c12 100644 --- a/lib/ClangImporter/ImporterImpl.h +++ b/lib/ClangImporter/ImporterImpl.h @@ -196,8 +196,14 @@ enum class Bridgeability { /// /// In either case we end up losing sugar at some uses sites, so this is more /// about what the right default is. -static inline Bridgeability getTypedefBridgeability(clang::QualType type) { - return type->isBlockPointerType() ? Bridgeability::Full : Bridgeability::None; +static inline Bridgeability getTypedefBridgeability( + const clang::TypedefNameDecl *decl, + clang::QualType type) { + return decl->hasAttr() + ? Bridgeability::Full + : type->isBlockPointerType() + ? Bridgeability::Full + : Bridgeability::None; } /// \brief Describes the kind of the C type that can be mapped to a stdlib @@ -557,11 +563,6 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation /// Records those modules that we have looked up. llvm::DenseMap checkedModules; - /// External Decls that we have imported but not passed to the ASTContext yet. - SmallVector RegisteredExternalDecls; - - unsigned NumCurrentImportingEntities = 0; - /// Mapping from delayed conformance IDs to the set of delayed /// protocol conformances. llvm::DenseMap> @@ -576,19 +577,6 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation ImportedProtocols; void startedImportingEntity(); - void finishedImportingEntity(); - void finishPendingActions(); - - struct ImportingEntityRAII { - Implementation &Impl; - - ImportingEntityRAII(Implementation &Impl) : Impl(Impl) { - Impl.startedImportingEntity(); - } - ~ImportingEntityRAII() { - Impl.finishedImportingEntity(); - } - }; public: importer::PlatformAvailability platformAvailability; @@ -624,7 +612,8 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation public: void registerExternalDecl(Decl *D) { - RegisteredExternalDecls.push_back(D); + if (!hasFinishedTypeChecking()) + SwiftContext.addExternalDecl(D); } void recordImplicitUnwrapForDecl(Decl *decl, bool isIUO) { @@ -1363,7 +1352,9 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation void forEachDistinctName( const clang::NamedDecl *decl, llvm::function_ref action); + importer::ImportNameVersion)> action) { + getNameImporter().forEachDistinctImportName(decl, CurrentVersion, action); + } /// Dump the Swift-specific name lookup tables we generate. void dumpSwiftLookupTables(); diff --git a/lib/ClangImporter/SwiftLookupTable.cpp b/lib/ClangImporter/SwiftLookupTable.cpp index 9d8ba90be55a9..fa4e0a03aa950 100644 --- a/lib/ClangImporter/SwiftLookupTable.cpp +++ b/lib/ClangImporter/SwiftLookupTable.cpp @@ -446,13 +446,10 @@ void SwiftLookupTable::addEntry(DeclName name, SingleEntry newEntry, // Translate the context. auto contextOpt = translateContext(effectiveContext); if (!contextOpt) { - // If it is a declaration with a swift_name attribute, we might be - // able to resolve this later. + // We might be able to resolve this later. if (auto decl = newEntry.dyn_cast()) { - if (decl->hasAttr()) { - UnresolvedEntries.push_back( - std::make_tuple(name, newEntry, effectiveContext)); - } + UnresolvedEntries.push_back( + std::make_tuple(name, newEntry, effectiveContext)); } return; @@ -1636,37 +1633,30 @@ void importer::addEntryToLookupTable(SwiftLookupTable &table, // If we have a name to import as, add this entry to the table. auto currentVersion = ImportNameVersion::fromOptions(nameImporter.getLangOpts()); - if (auto importedName = nameImporter.importName(named, currentVersion)) { - SmallPtrSet distinctNames; - distinctNames.insert(importedName.getDeclName()); - table.addEntry(importedName.getDeclName(), named, - importedName.getEffectiveContext()); - - // Also add the subscript entry, if needed. - if (importedName.isSubscriptAccessor()) - table.addEntry(DeclName(nameImporter.getContext(), - DeclBaseName::createSubscript(), - ArrayRef()), - named, importedName.getEffectiveContext()); - - currentVersion.forEachOtherImportNameVersion( - [&](ImportNameVersion alternateVersion) { - auto alternateName = nameImporter.importName(named, alternateVersion); - if (!alternateName) + auto failed = nameImporter.forEachDistinctImportName( + named, currentVersion, + [&](ImportedName importedName, ImportNameVersion version) { + table.addEntry(importedName.getDeclName(), named, + importedName.getEffectiveContext()); + + // Also add the subscript entry, if needed. + if (version == currentVersion && importedName.isSubscriptAccessor()) { + table.addEntry(DeclName(nameImporter.getContext(), + DeclBaseName::createSubscript(), + {Identifier()}), + named, importedName.getEffectiveContext()); + } + + return true; + }); + if (failed) { + if (auto category = dyn_cast(named)) { + // If the category is invalid, don't add it. + if (category->isInvalidDecl()) return; - // FIXME: What if the DeclNames are the same but the contexts are - // different? - if (distinctNames.insert(alternateName.getDeclName()).second) { - table.addEntry(alternateName.getDeclName(), named, - alternateName.getEffectiveContext()); - } - }); - } else if (auto category = dyn_cast(named)) { - // If the category is invalid, don't add it. - if (category->isInvalidDecl()) - return; - table.addCategory(category); + table.addCategory(category); + } } // Walk the members of any context that can have nested members. @@ -1787,9 +1777,11 @@ void importer::finalizeLookupTable(SwiftLookupTable &table, auto decl = entry.get(); auto swiftName = decl->getAttr(); - nameImporter.getContext().Diags.diagnose( - SourceLoc(), diag::unresolvable_clang_decl, decl->getNameAsString(), - swiftName->getName()); + if (swiftName) { + nameImporter.getContext().Diags.diagnose( + SourceLoc(), diag::unresolvable_clang_decl, decl->getNameAsString(), + swiftName->getName()); + } } } } @@ -1865,4 +1857,3 @@ SwiftNameLookupExtension::createExtensionReader( // Return the new reader. return std::move(tableReader); } - diff --git a/lib/Demangling/Demangler.cpp b/lib/Demangling/Demangler.cpp index 23bea19cb962d..2e69e307e91d4 100644 --- a/lib/Demangling/Demangler.cpp +++ b/lib/Demangling/Demangler.cpp @@ -130,8 +130,10 @@ swift::Demangle::makeSymbolicMangledNameStringRef(const char *base) { auto end = base; while (*end != '\0') { // Skip over symbolic references. - if (*end == '\1') - end += 4; + if (*end >= '\x01' && *end <= '\x17') + end += sizeof(uint32_t); + if (*end >= '\x18' && *end <= '\x1F') + end += sizeof(void*); ++end; } return StringRef(base, end - base); @@ -262,8 +264,8 @@ bool swift::Demangle::isStruct(llvm::StringRef mangledName) { return isStructNode(Dem.demangleType(mangledName)); } -namespace swift { -namespace Demangle { +using namespace swift; +using namespace Demangle; ////////////////////////////////// // Node member functions // @@ -923,6 +925,7 @@ NodePointer Demangler::popTypeAndGetAnyGeneric() { NodePointer Demangler::demangleBuiltinType() { NodePointer Ty = nullptr; + const int maxTypeSize = 4096; // a very conservative upper bound switch (nextChar()) { case 'b': Ty = createNode(Node::Kind::BuiltinTypeName, @@ -934,7 +937,7 @@ NodePointer Demangler::demangleBuiltinType() { break; case 'f': { int size = demangleIndex() - 1; - if (size <= 0) + if (size <= 0 || size > maxTypeSize) return nullptr; CharVector name; name.append(BUILTIN_TYPE_NAME_FLOAT, *this); @@ -944,7 +947,7 @@ NodePointer Demangler::demangleBuiltinType() { } case 'i': { int size = demangleIndex() - 1; - if (size <= 0) + if (size <= 0 || size > maxTypeSize) return nullptr; CharVector name; name.append(BUILTIN_TYPE_NAME_INT, *this); @@ -954,7 +957,7 @@ NodePointer Demangler::demangleBuiltinType() { } case 'v': { int elts = demangleIndex() - 1; - if (elts <= 0) + if (elts <= 0 || elts > maxTypeSize) return nullptr; NodePointer EltType = popTypeAndGetChild(); if (!EltType || EltType->getKind() != Node::Kind::BuiltinTypeName || @@ -1321,6 +1324,9 @@ NodePointer Demangler::demangleBoundGenericArgs(NodePointer Nominal, case Node::Kind::OtherNominalType: kind = Node::Kind::BoundGenericOtherNominalType; break; + case Node::Kind::TypeAlias: + kind = Node::Kind::BoundGenericTypeAlias; + break; default: return nullptr; } @@ -1756,6 +1762,8 @@ NodePointer Demangler::demangleThunkOrSpecialization() { if (node) { if (node->getKind() == Node::Kind::DependentGenericSignature) { auto decl = popNode(); + if (!decl) + return nullptr; result = createWithChildren(nodeKind, decl, /*sig*/ node); } else { result = createWithChild(nodeKind, /*decl*/ node); @@ -1974,10 +1982,31 @@ NodePointer Demangler::demangleFuncSpecParam(Node::IndexType ParamIdx) { return nullptr; } } + case 'e': { + unsigned Value = + unsigned(FunctionSigSpecializationParamKind::ExistentialToGeneric); + if (nextIf('D')) + Value |= unsigned(FunctionSigSpecializationParamKind::Dead); + if (nextIf('G')) + Value |= + unsigned(FunctionSigSpecializationParamKind::OwnedToGuaranteed); + if (nextIf('O')) + Value |= + unsigned(FunctionSigSpecializationParamKind::GuaranteedToOwned); + if (nextIf('X')) + Value |= unsigned(FunctionSigSpecializationParamKind::SROA); + return addChild( + Param, + createNode(Node::Kind::FunctionSignatureSpecializationParamKind, + Value)); + } case 'd': { unsigned Value = unsigned(FunctionSigSpecializationParamKind::Dead); if (nextIf('G')) Value |= unsigned(FunctionSigSpecializationParamKind::OwnedToGuaranteed); + if (nextIf('O')) + Value |= + unsigned(FunctionSigSpecializationParamKind::GuaranteedToOwned); if (nextIf('X')) Value |= unsigned(FunctionSigSpecializationParamKind::SROA); return addChild(Param, createNode( @@ -1991,6 +2020,16 @@ NodePointer Demangler::demangleFuncSpecParam(Node::IndexType ParamIdx) { return addChild(Param, createNode( Node::Kind::FunctionSignatureSpecializationParamKind, Value)); } + case 'o': { + unsigned Value = + unsigned(FunctionSigSpecializationParamKind::GuaranteedToOwned); + if (nextIf('X')) + Value |= unsigned(FunctionSigSpecializationParamKind::SROA); + return addChild( + Param, + createNode(Node::Kind::FunctionSignatureSpecializationParamKind, + Value)); + } case 'x': return addChild(Param, createNode( Node::Kind::FunctionSignatureSpecializationParamKind, @@ -2677,6 +2716,3 @@ NodePointer Demangler::demangleObjCTypeName() { return Global; } - -} // namespace Demangle -} // namespace swift diff --git a/lib/Demangling/NodePrinter.cpp b/lib/Demangling/NodePrinter.cpp index 7703600722c60..98e81beebffdd 100644 --- a/lib/Demangling/NodePrinter.cpp +++ b/lib/Demangling/NodePrinter.cpp @@ -267,6 +267,7 @@ class NodePrinter { case Node::Kind::BoundGenericEnum: case Node::Kind::BoundGenericStructure: case Node::Kind::BoundGenericOtherNominalType: + case Node::Kind::BoundGenericTypeAlias: case Node::Kind::BuiltinTypeName: case Node::Kind::Class: case Node::Kind::DependentGenericType: @@ -813,8 +814,11 @@ unsigned NodePrinter::printFunctionSigSpecializationParam(NodePointer Node, assert( ((V & unsigned(FunctionSigSpecializationParamKind::OwnedToGuaranteed)) || + (V & unsigned(FunctionSigSpecializationParamKind::GuaranteedToOwned)) || (V & unsigned(FunctionSigSpecializationParamKind::SROA)) || - (V & unsigned(FunctionSigSpecializationParamKind::Dead))) && + (V & unsigned(FunctionSigSpecializationParamKind::Dead))|| + (V & unsigned( + FunctionSigSpecializationParamKind::ExistentialToGeneric))) && "Invalid OptionSet"); print(Node->getChild(Idx++)); return Idx; @@ -1213,11 +1217,18 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { uint64_t raw = Node->getIndex(); bool printedOptionSet = false; + if (raw & + uint64_t(FunctionSigSpecializationParamKind::ExistentialToGeneric)) { + printedOptionSet = true; + Printer << "Existential To Protocol Constrained Generic"; + } + if (raw & uint64_t(FunctionSigSpecializationParamKind::Dead)) { + if (printedOptionSet) + Printer << " and "; printedOptionSet = true; Printer << "Dead"; } - if (raw & uint64_t(FunctionSigSpecializationParamKind::OwnedToGuaranteed)) { if (printedOptionSet) Printer << " and "; @@ -1225,6 +1236,13 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { Printer << "Owned To Guaranteed"; } + if (raw & uint64_t(FunctionSigSpecializationParamKind::GuaranteedToOwned)) { + if (printedOptionSet) + Printer << " and "; + printedOptionSet = true; + Printer << "Guaranteed To Owned"; + } + if (raw & uint64_t(FunctionSigSpecializationParamKind::SROA)) { if (printedOptionSet) Printer << " and "; @@ -1260,8 +1278,10 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { case FunctionSigSpecializationParamKind::ClosureProp: Printer << "Closure Propagated"; return nullptr; + case FunctionSigSpecializationParamKind::ExistentialToGeneric: case FunctionSigSpecializationParamKind::Dead: case FunctionSigSpecializationParamKind::OwnedToGuaranteed: + case FunctionSigSpecializationParamKind::GuaranteedToOwned: case FunctionSigSpecializationParamKind::SROA: printer_unreachable("option sets should have been handled earlier"); } @@ -1540,6 +1560,7 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { case Node::Kind::BoundGenericStructure: case Node::Kind::BoundGenericEnum: case Node::Kind::BoundGenericOtherNominalType: + case Node::Kind::BoundGenericTypeAlias: printBoundGeneric(Node); return nullptr; case Node::Kind::DynamicSelf: diff --git a/lib/Demangling/OldDemangler.cpp b/lib/Demangling/OldDemangler.cpp index 75e25ad89b6f1..c615370cfdeff 100644 --- a/lib/Demangling/OldDemangler.cpp +++ b/lib/Demangling/OldDemangler.cpp @@ -100,13 +100,21 @@ class NameSource { /// Return the next character without claiming it. Asserts that /// there is at least one remaining character. - char peek() { return Text.front(); } + char peek() { + if (isEmpty()) { + // Return an otherwise unused character to prevent crashes for malformed + // symbols. + return '.'; + } + return Text.front(); + } /// Claim and return the next character. Asserts that there is at /// least one remaining character. char next() { char c = peek(); - advanceOffset(1); + if (!isEmpty()) + advanceOffset(1); return c; } @@ -641,6 +649,11 @@ class OldDemangler { unsigned(FunctionSigSpecializationParamKind::OwnedToGuaranteed); } + if (Mangled.nextIf('o')) { + Value |= + unsigned(FunctionSigSpecializationParamKind::GuaranteedToOwned); + } + if (Mangled.nextIf('s')) { Value |= unsigned(FunctionSigSpecializationParamKind::SROA); } diff --git a/lib/Demangling/OldRemangler.cpp b/lib/Demangling/OldRemangler.cpp index 4b3cce6eb3c1f..55e8e97bd381e 100644 --- a/lib/Demangling/OldRemangler.cpp +++ b/lib/Demangling/OldRemangler.cpp @@ -641,6 +641,9 @@ void Remangler::mangleFunctionSignatureSpecializationParam(Node *node) { if (kindValue & unsigned(FunctionSigSpecializationParamKind::OwnedToGuaranteed)) Out << 'g'; + if (kindValue & + unsigned(FunctionSigSpecializationParamKind::GuaranteedToOwned)) + Out << 'o'; if (kindValue & unsigned(FunctionSigSpecializationParamKind::SROA)) Out << 's'; Out << '_'; @@ -1872,6 +1875,11 @@ void Remangler::mangleBoundGenericOtherNominalType(Node *node) { mangleAnyNominalType(node, ctx); } +void Remangler::mangleBoundGenericTypeAlias(Node *node) { + EntityContext ctx; + mangleAnyNominalType(node, ctx); +} + void Remangler::mangleTypeList(Node *node) { mangleChildNodes(node); // all types Out << '_'; diff --git a/lib/Demangling/Remangler.cpp b/lib/Demangling/Remangler.cpp index a5f7f89ea7f6e..6639c7c6212bc 100644 --- a/lib/Demangling/Remangler.cpp +++ b/lib/Demangling/Remangler.cpp @@ -152,6 +152,9 @@ class Remangler { // nested generics. This factory owns them. NodeFactory Factory; + // A callback for resolving symbolic references. + SymbolicResolver Resolver; + StringRef getBufferStr() const { return Buffer.getStringRef(); } void resetBuffer(size_t toPos) { Buffer.resetSize(toPos); } @@ -284,7 +287,8 @@ class Remangler { #include "swift/Demangling/DemangleNodes.def" public: - Remangler(DemanglerPrinter &Buffer) : Buffer(Buffer) {} + Remangler(DemanglerPrinter &Buffer, + SymbolicResolver Resolver) : Buffer(Buffer), Resolver(Resolver) {} void mangle(Node *node) { switch (node->getKind()) { @@ -455,6 +459,7 @@ void Remangler::mangleAnyNominalType(Node *node) { case Node::Kind::Enum: return mangleAnyGenericType(node, "O"); case Node::Kind::Class: return mangleAnyGenericType(node, "C"); case Node::Kind::OtherNominalType: return mangleAnyGenericType(node, "XY"); + case Node::Kind::TypeAlias: return mangleAnyGenericType(node, "a"); default: unreachable("bad nominal type kind"); } @@ -473,7 +478,8 @@ void Remangler::mangleGenericArgs(Node *node, char &Separator) { case Node::Kind::BoundGenericOtherNominalType: case Node::Kind::BoundGenericStructure: case Node::Kind::BoundGenericEnum: - case Node::Kind::BoundGenericClass: { + case Node::Kind::BoundGenericClass: + case Node::Kind::BoundGenericTypeAlias: { NodePointer unboundType = node->getChild(0); assert(unboundType->getKind() == Node::Kind::Type); NodePointer nominalType = unboundType->getChild(0); @@ -585,13 +591,8 @@ void Remangler::mangleBoundGenericOtherNominalType(Node *node) { mangleAnyNominalType(node); } -template -static bool stripPrefix(StringRef &string, const char (&data)[N]) { - constexpr size_t prefixLength = N - 1; - if (!string.startswith(StringRef(data, prefixLength))) - return false; - string = string.drop_front(prefixLength); - return true; +void Remangler::mangleBoundGenericTypeAlias(Node *node) { + mangleAnyNominalType(node); } void Remangler::mangleBuiltinTypeName(Node *node) { @@ -612,17 +613,17 @@ void Remangler::mangleBuiltinTypeName(Node *node) { Buffer << 't'; } else if (text == BUILTIN_TYPE_NAME_WORD) { Buffer << 'w'; - } else if (stripPrefix(text, BUILTIN_TYPE_NAME_INT)) { + } else if (text.consume_front(BUILTIN_TYPE_NAME_INT)) { Buffer << 'i' << text << '_'; - } else if (stripPrefix(text, BUILTIN_TYPE_NAME_FLOAT)) { + } else if (text.consume_front(BUILTIN_TYPE_NAME_FLOAT)) { Buffer << 'f' << text << '_'; - } else if (stripPrefix(text, BUILTIN_TYPE_NAME_VEC)) { + } else if (text.consume_front(BUILTIN_TYPE_NAME_VEC)) { auto split = text.split('x'); if (split.second == "RawPointer") { Buffer << 'p'; - } else if (stripPrefix(split.second, "Float")) { + } else if (split.second.consume_front("Float")) { Buffer << 'f' << split.second << '_'; - } else if (stripPrefix(split.second, "Int")) { + } else if (split.second.consume_front("Int")) { Buffer << 'i' << split.second << '_'; } else { unreachable("unexpected builtin vector type"); @@ -1018,14 +1019,34 @@ void Remangler::mangleFunctionSignatureSpecializationParam(Node *node) { Buffer << 'x'; return; default: - if (kindValue & unsigned(FunctionSigSpecializationParamKind::Dead)) { + if (kindValue & + unsigned( + FunctionSigSpecializationParamKind::ExistentialToGeneric)) { + Buffer << 'e'; + if (kindValue & unsigned(FunctionSigSpecializationParamKind::Dead)) + Buffer << 'D'; + if (kindValue & + unsigned(FunctionSigSpecializationParamKind::OwnedToGuaranteed)) + Buffer << 'G'; + if (kindValue & + unsigned(FunctionSigSpecializationParamKind::GuaranteedToOwned)) + Buffer << 'O'; + } else if (kindValue & + unsigned(FunctionSigSpecializationParamKind::Dead)) { Buffer << 'd'; if (kindValue & unsigned(FunctionSigSpecializationParamKind::OwnedToGuaranteed)) Buffer << 'G'; + if (kindValue & + unsigned(FunctionSigSpecializationParamKind::GuaranteedToOwned)) + Buffer << 'O'; } else if (kindValue & unsigned(FunctionSigSpecializationParamKind::OwnedToGuaranteed)) { Buffer << 'g'; + } else if (kindValue & + unsigned( + FunctionSigSpecializationParamKind::GuaranteedToOwned)) { + Buffer << 'o'; } if (kindValue & unsigned(FunctionSigSpecializationParamKind::SROA)) Buffer << 'X'; @@ -2003,18 +2024,25 @@ void Remangler::mangleUnresolvedSymbolicReference(Node *node) { } void Remangler::mangleSymbolicReference(Node *node) { - unreachable("should not try to mangle a symbolic reference; " - "resolve it to a non-symbolic demangling tree instead"); + return mangle(Resolver((const void *)node->getIndex())); } } // anonymous namespace /// The top-level interface to the remangler. std::string Demangle::mangleNode(const NodePointer &node) { + return mangleNode(node, [](const void *) -> NodePointer { + unreachable("should not try to mangle a symbolic reference; " + "resolve it to a non-symbolic demangling tree instead"); + }); +} + +std::string +Demangle::mangleNode(const NodePointer &node, SymbolicResolver resolver) { if (!node) return ""; DemanglerPrinter printer; - Remangler(printer).mangle(node); + Remangler(printer, resolver).mangle(node); return std::move(printer).str(); } @@ -2025,6 +2053,7 @@ bool Demangle::isSpecialized(Node *node) { case Node::Kind::BoundGenericEnum: case Node::Kind::BoundGenericClass: case Node::Kind::BoundGenericOtherNominalType: + case Node::Kind::BoundGenericTypeAlias: return true; case Node::Kind::Structure: @@ -2060,7 +2089,8 @@ NodePointer Demangle::getUnspecialized(Node *node, NodeFactory &Factory) { case Node::Kind::BoundGenericStructure: case Node::Kind::BoundGenericEnum: case Node::Kind::BoundGenericClass: - case Node::Kind::BoundGenericOtherNominalType: { + case Node::Kind::BoundGenericOtherNominalType: + case Node::Kind::BoundGenericTypeAlias: { NodePointer unboundType = node->getChild(0); assert(unboundType->getKind() == Node::Kind::Type); NodePointer nominalType = unboundType->getChild(0); diff --git a/lib/Demangling/TypeDecoder.cpp b/lib/Demangling/TypeDecoder.cpp index a30f03759ffcb..1664e29fc0daa 100644 --- a/lib/Demangling/TypeDecoder.cpp +++ b/lib/Demangling/TypeDecoder.cpp @@ -18,7 +18,7 @@ using namespace swift; using namespace Demangle; -NodePointer Demangle::stripGenericArgsFromContextNode(const NodePointer &node, +NodePointer Demangle::stripGenericArgsFromContextNode(NodePointer node, NodeFactory &factory) { switch (node->getKind()) { case Demangle::Node::Kind::BoundGenericClass: @@ -53,6 +53,23 @@ NodePointer Demangle::stripGenericArgsFromContextNode(const NodePointer &node, newNode->addChild(node->getChild(i), factory); return newNode; } + + case Demangle::Node::Kind::Extension: { + // Strip generic arguments from the extended type. + if (node->getNumChildren() < 2) + return node; + + auto newExtended = stripGenericArgsFromContextNode(node->getChild(1), + factory); + if (newExtended == node->getChild(1)) return node; + + auto newNode = factory.createNode(Node::Kind::Extension); + newNode->addChild(node->getChild(0), factory); + newNode->addChild(newExtended, factory); + if (node->getNumChildren() == 3) + newNode->addChild(node->getChild(2), factory); + return newNode; + } case Demangle::Node::Kind::Module: // Modules terminate the recursion. diff --git a/lib/Driver/CMakeLists.txt b/lib/Driver/CMakeLists.txt index b0419e4bb369b..cf531112cc4b5 100644 --- a/lib/Driver/CMakeLists.txt +++ b/lib/Driver/CMakeLists.txt @@ -1,6 +1,7 @@ set(swiftDriver_sources Action.cpp Compilation.cpp + DarwinToolChains.cpp DependencyGraph.cpp Driver.cpp FrontendUtil.cpp @@ -9,6 +10,8 @@ set(swiftDriver_sources PrettyStackTrace.cpp ToolChain.cpp ToolChains.cpp + UnixToolChains.cpp + WindowsToolChains.cpp ) set(swiftDriver_targetDefines) diff --git a/lib/Driver/Compilation.cpp b/lib/Driver/Compilation.cpp index dbc8176165637..8fc5e2b9ffff5 100644 --- a/lib/Driver/Compilation.cpp +++ b/lib/Driver/Compilation.cpp @@ -102,7 +102,12 @@ Compilation::Compilation(DiagnosticEngine &Diags, std::unique_ptr InputArgs, std::unique_ptr TranslatedArgs, InputFileList InputsWithTypes, - StringRef ArgsHash, llvm::sys::TimePoint<> StartTime, + std::string CompilationRecordPath, + bool OutputCompilationRecordForModuleOnlyBuild, + StringRef ArgsHash, + llvm::sys::TimePoint<> StartTime, + llvm::sys::TimePoint<> LastBuildTime, + size_t FilelistThreshold, unsigned NumberOfParallelCommands, bool EnableIncrementalBuild, bool EnableBatchMode, @@ -117,17 +122,23 @@ Compilation::Compilation(DiagnosticEngine &Diags, Level(Level), RawInputArgs(std::move(InputArgs)), TranslatedArgs(std::move(TranslatedArgs)), - InputFilesWithTypes(std::move(InputsWithTypes)), ArgsHash(ArgsHash), + InputFilesWithTypes(std::move(InputsWithTypes)), + CompilationRecordPath(CompilationRecordPath), + ArgsHash(ArgsHash), BuildStartTime(StartTime), + LastBuildTime(LastBuildTime), NumberOfParallelCommands(NumberOfParallelCommands), SkipTaskExecution(SkipTaskExecution), EnableIncrementalBuild(EnableIncrementalBuild), + OutputCompilationRecordForModuleOnlyBuild( + OutputCompilationRecordForModuleOnlyBuild), EnableBatchMode(EnableBatchMode), BatchSeed(BatchSeed), ForceOneBatchRepartition(ForceOneBatchRepartition), SaveTemps(SaveTemps), ShowDriverTimeCompilation(ShowDriverTimeCompilation), - Stats(std::move(StatsReporter)) { + Stats(std::move(StatsReporter)), + FilelistThreshold(FilelistThreshold) { }; static bool writeFilelistIfNecessary(const Job *job, const ArgList &args, @@ -163,6 +174,16 @@ namespace driver { /// TaskQueue. CommandSet BatchJobs; + /// Persistent counter for allocating quasi-PIDs to Jobs combined into + /// BatchJobs. Quasi-PIDs are _negative_ PID-like unique keys used to + /// masquerade BatchJob constituents as (quasi)processes, when writing + /// parseable output to consumers that don't understand the idea of a batch + /// job. They are negative in order to avoid possibly colliding with real + /// PIDs (which are always positive). We start at -1000 here as a crude but + /// harmless hedge against colliding with an errno value that might slip + /// into the stream of real PIDs (say, due to a TaskQueue bug). + int64_t NextBatchQuasiPID = -1000; + /// All jobs which have finished execution or which have been determined /// that they don't need to run. CommandSet FinishedCommands; @@ -302,6 +323,10 @@ namespace driver { } } + bool isBatchJob(const Job *MaybeBatchJob) const { + return BatchJobs.count(MaybeBatchJob) != 0; + } + /// Callback which will be called immediately after a task has started. This /// callback may be used to provide output indicating that the task began. void taskBegan(ProcessId Pid, void *Context) { @@ -332,7 +357,9 @@ namespace driver { BeganCmd->printCommandLine(llvm::errs()); break; case OutputLevel::Parseable: - parseable_output::emitBeganMessage(llvm::errs(), *BeganCmd, Pid); + BeganCmd->forEachContainedJobAndPID(Pid, [&](const Job *J, Job::PID P) { + parseable_output::emitBeganMessage(llvm::errs(), *J, P); + }); break; } } @@ -482,13 +509,16 @@ namespace driver { break; case OutputLevel::Parseable: // Parseable output was requested. - parseable_output::emitFinishedMessage(llvm::errs(), *FinishedCmd, Pid, - ReturnCode, Output); + FinishedCmd->forEachContainedJobAndPID( + Pid, [&](const Job *J, Job::PID P) { + parseable_output::emitFinishedMessage(llvm::errs(), *J, P, + ReturnCode, Output); + }); break; } } - if (BatchJobs.count(FinishedCmd) != 0) { + if (isBatchJob(FinishedCmd)) { return unpackAndFinishBatch(ReturnCode, Output, Errors, static_cast(FinishedCmd)); } @@ -517,6 +547,11 @@ namespace driver { ReturnCode); } + // See how ContinueBuildingAfterErrors gets set up in Driver.cpp for + // more info. + assert((Comp.ContinueBuildingAfterErrors || !Comp.EnableBatchMode) && + "batch mode diagnostics require ContinueBuildingAfterErrors"); + return Comp.ContinueBuildingAfterErrors ? TaskFinishedResponse::ContinueExecution : TaskFinishedResponse::StopExecution; @@ -546,8 +581,11 @@ namespace driver { if (Comp.Level == OutputLevel::Parseable) { // Parseable output was requested. - parseable_output::emitSignalledMessage(llvm::errs(), *SignalledCmd, - Pid, ErrorMsg, Output, Signal); + SignalledCmd->forEachContainedJobAndPID( + Pid, [&](const Job *J, Job::PID P) { + parseable_output::emitSignalledMessage(llvm::errs(), *J, P, + ErrorMsg, Output, Signal); + }); } else { // Otherwise, send the buffered output to stderr, though only if we // support getting buffered output. @@ -582,7 +620,8 @@ namespace driver { if (Comp.SkipTaskExecution) TQ.reset(new DummyTaskQueue(Comp.NumberOfParallelCommands)); else - TQ.reset(new TaskQueue(Comp.NumberOfParallelCommands)); + TQ.reset(new TaskQueue(Comp.NumberOfParallelCommands, + Comp.Stats.get())); if (Comp.ShowIncrementalBuildDecisions || Comp.Stats) IncrementalTracer = &ActualIncrementalTracer; } @@ -729,7 +768,7 @@ namespace driver { llvm::outs() << "Forming batch job from " << Batch.size() << " constituents\n"; auto const &TC = Comp.getToolChain(); - auto J = TC.constructBatchJob(Batch, Comp); + auto J = TC.constructBatchJob(Batch, NextBatchQuasiPID, Comp); if (J) Batches.push_back(Comp.addJob(std::move(J))); } @@ -1192,6 +1231,12 @@ int Compilation::performJobsImpl(bool &abnormalExit) { checkForOutOfDateInputs(Diags, InputInfo); writeCompilationRecord(CompilationRecordPath, ArgsHash, BuildStartTime, InputInfo); + + if (OutputCompilationRecordForModuleOnlyBuild) { + // TODO: Optimize with clonefile(2) ? + llvm::sys::fs::copy_file(CompilationRecordPath, + CompilationRecordPath + "~moduleonly"); + } } abnormalExit = State.hadAnyAbnormalExit(); diff --git a/lib/Driver/DarwinToolChains.cpp b/lib/Driver/DarwinToolChains.cpp new file mode 100644 index 0000000000000..5f156b025eaf3 --- /dev/null +++ b/lib/Driver/DarwinToolChains.cpp @@ -0,0 +1,452 @@ +//===------ DarwinToolChains.cpp - Job invocations (Darwin-specific) ------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "ToolChains.h" + +#include "swift/Basic/Dwarf.h" +#include "swift/Basic/LLVM.h" +#include "swift/Basic/Platform.h" +#include "swift/Basic/Range.h" +#include "swift/Basic/TaskQueue.h" +#include "swift/Config.h" +#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" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/ProfileData/InstrProf.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/Program.h" + +using namespace swift; +using namespace swift::driver; +using namespace llvm::opt; + +std::string +toolchains::Darwin::findProgramRelativeToSwiftImpl(StringRef name) const { + StringRef swiftPath = getDriver().getSwiftProgramPath(); + StringRef swiftBinDir = llvm::sys::path::parent_path(swiftPath); + + // See if we're in an Xcode toolchain. + bool hasToolchain = false; + llvm::SmallString<128> path{swiftBinDir}; + llvm::sys::path::remove_filename(path); // bin + llvm::sys::path::remove_filename(path); // usr + if (llvm::sys::path::extension(path) == ".xctoolchain") { + hasToolchain = true; + llvm::sys::path::remove_filename(path); // *.xctoolchain + llvm::sys::path::remove_filename(path); // Toolchains + llvm::sys::path::append(path, "usr", "bin"); + } + + StringRef paths[] = {swiftBinDir, path}; + auto pathsRef = llvm::makeArrayRef(paths); + if (!hasToolchain) + pathsRef = pathsRef.drop_back(); + + auto result = llvm::sys::findProgramByName(name, pathsRef); + if (result) + return result.get(); + return {}; +} + +ToolChain::InvocationInfo +toolchains::Darwin::constructInvocation(const InterpretJobAction &job, + const JobContext &context) const { + InvocationInfo II = ToolChain::constructInvocation(job, context); + + SmallString<128> runtimeLibraryPath; + getRuntimeLibraryPath(runtimeLibraryPath, context.Args, /*Shared=*/true); + + addPathEnvironmentVariableIfNeeded(II.ExtraEnvironment, "DYLD_LIBRARY_PATH", + ":", options::OPT_L, context.Args, + runtimeLibraryPath); + addPathEnvironmentVariableIfNeeded(II.ExtraEnvironment, "DYLD_FRAMEWORK_PATH", + ":", options::OPT_F, context.Args); + // FIXME: Add options::OPT_Fsystem paths to DYLD_FRAMEWORK_PATH as well. + return II; +} + +static StringRef +getDarwinLibraryNameSuffixForTriple(const llvm::Triple &triple) { + switch (getDarwinPlatformKind(triple)) { + case DarwinPlatformKind::MacOS: + return "osx"; + case DarwinPlatformKind::IPhoneOS: + return "ios"; + case DarwinPlatformKind::IPhoneOSSimulator: + return "iossim"; + case DarwinPlatformKind::TvOS: + return "tvos"; + case DarwinPlatformKind::TvOSSimulator: + return "tvossim"; + case DarwinPlatformKind::WatchOS: + return "watchos"; + case DarwinPlatformKind::WatchOSSimulator: + return "watchossim"; + } + llvm_unreachable("Unsupported Darwin platform"); +} + +std::string toolchains::Darwin::sanitizerRuntimeLibName(StringRef Sanitizer, + bool shared) const { + return (Twine("libclang_rt.") + Sanitizer + "_" + + getDarwinLibraryNameSuffixForTriple(this->getTriple()) + + (shared ? "_dynamic.dylib" : ".a")) + .str(); +} + +static void addLinkRuntimeLibRPath(const ArgList &Args, + ArgStringList &Arguments, + StringRef DarwinLibName, + const ToolChain &TC) { + // Adding the rpaths might negatively interact when other rpaths are involved, + // so we should make sure we add the rpaths last, after all user-specified + // rpaths. This is currently true from this place, but we need to be + // careful if this function is ever called before user's rpaths are emitted. + assert(DarwinLibName.endswith(".dylib") && "must be a dynamic library"); + + // Add @executable_path to rpath to support having the dylib copied with + // the executable. + Arguments.push_back("-rpath"); + Arguments.push_back("@executable_path"); + + // Add the path to the resource dir to rpath to support using the dylib + // from the default location without copying. + + SmallString<128> ClangLibraryPath; + TC.getClangLibraryPath(Args, ClangLibraryPath); + + Arguments.push_back("-rpath"); + Arguments.push_back(Args.MakeArgString(ClangLibraryPath)); +} + +static void addLinkSanitizerLibArgsForDarwin(const ArgList &Args, + ArgStringList &Arguments, + StringRef Sanitizer, + const ToolChain &TC, + bool shared = true) { + // Sanitizer runtime libraries requires C++. + Arguments.push_back("-lc++"); + // Add explicit dependency on -lc++abi, as -lc++ doesn't re-export + // all RTTI-related symbols that are used. + Arguments.push_back("-lc++abi"); + + auto LibName = TC.sanitizerRuntimeLibName(Sanitizer, shared); + TC.addLinkRuntimeLib(Args, Arguments, LibName); + + if (shared) + addLinkRuntimeLibRPath(Args, Arguments, LibName, TC); +} + +/// Runs xcrun -f clang in order to find the location of Clang for +/// the currently active Xcode. +/// +/// We get the "currently active" part by passing through the DEVELOPER_DIR +/// environment variable (along with the rest of the environment). +static bool findXcodeClangPath(llvm::SmallVectorImpl &path) { + assert(path.empty()); + + auto xcrunPath = llvm::sys::findProgramByName("xcrun"); + if (!xcrunPath.getError()) { + const char *args[] = {"-f", "clang", nullptr}; + sys::TaskQueue queue; + queue.addTask(xcrunPath->c_str(), args, /*Env=*/llvm::None, + /*Context=*/nullptr, + /*SeparateErrors=*/true); + queue.execute(nullptr, + [&path](sys::ProcessId PID, int returnCode, StringRef output, + StringRef errors, + void *unused) -> sys::TaskFinishedResponse { + if (returnCode == 0) { + output = output.rtrim(); + path.append(output.begin(), output.end()); + } + return sys::TaskFinishedResponse::ContinueExecution; + }); + } + + return !path.empty(); +} + +static void addVersionString(const ArgList &inputArgs, ArgStringList &arguments, + unsigned major, unsigned minor, unsigned micro) { + llvm::SmallString<8> buf; + llvm::raw_svector_ostream os{buf}; + os << major << '.' << minor << '.' << micro; + arguments.push_back(inputArgs.MakeArgString(os.str())); +} + +ToolChain::InvocationInfo +toolchains::Darwin::constructInvocation(const LinkJobAction &job, + const JobContext &context) const { + assert(context.Output.getPrimaryOutputType() == file_types::TY_Image && + "Invalid linker output type."); + + if (context.Args.hasFlag(options::OPT_static_executable, + options::OPT_no_static_executable, false)) { + llvm::report_fatal_error("-static-executable is not supported on Darwin"); + } + + const Driver &D = getDriver(); + const llvm::Triple &Triple = getTriple(); + + // Configure the toolchain. + // By default, use the system `ld` to link. + const char *LD = "ld"; + if (const Arg *A = context.Args.getLastArg(options::OPT_tools_directory)) { + StringRef toolchainPath(A->getValue()); + + // If there is a 'ld' in the toolchain folder, use that instead. + if (auto toolchainLD = + llvm::sys::findProgramByName("ld", {toolchainPath})) { + LD = context.Args.MakeArgString(toolchainLD.get()); + } + } + + InvocationInfo II = {LD}; + ArgStringList &Arguments = II.Arguments; + + if (context.shouldUseInputFileList()) { + Arguments.push_back("-filelist"); + Arguments.push_back(context.getTemporaryFilePath("inputs", "LinkFileList")); + II.FilelistInfos.push_back({Arguments.back(), file_types::TY_Object, + FilelistInfo::WhichFiles::Input}); + } else { + addPrimaryInputsOfType(Arguments, context.Inputs, context.Args, + file_types::TY_Object); + } + + addInputsOfType(Arguments, context.InputActions, file_types::TY_Object); + + if (context.OI.CompilerMode == OutputInfo::Mode::SingleCompile) + addInputsOfType(Arguments, context.Inputs, context.Args, + file_types::TY_SwiftModuleFile, "-add_ast_path"); + else + addPrimaryInputsOfType(Arguments, context.Inputs, context.Args, + file_types::TY_SwiftModuleFile, "-add_ast_path"); + + // Add all .swiftmodule file inputs as arguments, preceded by the + // "-add_ast_path" linker option. + addInputsOfType(Arguments, context.InputActions, + file_types::TY_SwiftModuleFile, "-add_ast_path"); + + switch (job.getKind()) { + case LinkKind::None: + llvm_unreachable("invalid link kind"); + case LinkKind::Executable: + // The default for ld; no extra flags necessary. + break; + case LinkKind::DynamicLibrary: + Arguments.push_back("-dylib"); + break; + } + + assert(Triple.isOSDarwin()); + + // FIXME: If we used Clang as a linker instead of going straight to ld, + // we wouldn't have to replicate Clang's logic here. + bool wantsObjCRuntime = false; + if (Triple.isiOS()) + wantsObjCRuntime = Triple.isOSVersionLT(9); + else if (Triple.isMacOSX()) + wantsObjCRuntime = Triple.isMacOSXVersionLT(10, 11); + + if (context.Args.hasFlag(options::OPT_link_objc_runtime, + options::OPT_no_link_objc_runtime, + /*Default=*/wantsObjCRuntime)) { + llvm::SmallString<128> ARCLiteLib(D.getSwiftProgramPath()); + llvm::sys::path::remove_filename(ARCLiteLib); // 'swift' + llvm::sys::path::remove_filename(ARCLiteLib); // 'bin' + llvm::sys::path::append(ARCLiteLib, "lib", "arc"); + + if (!llvm::sys::fs::is_directory(ARCLiteLib)) { + // If we don't have a 'lib/arc/' directory, find the "arclite" library + // relative to the Clang in the active Xcode. + ARCLiteLib.clear(); + if (findXcodeClangPath(ARCLiteLib)) { + llvm::sys::path::remove_filename(ARCLiteLib); // 'clang' + llvm::sys::path::remove_filename(ARCLiteLib); // 'bin' + llvm::sys::path::append(ARCLiteLib, "lib", "arc"); + } + } + + if (!ARCLiteLib.empty()) { + llvm::sys::path::append(ARCLiteLib, "libarclite_"); + ARCLiteLib += getPlatformNameForTriple(Triple); + ARCLiteLib += ".a"; + + Arguments.push_back("-force_load"); + Arguments.push_back(context.Args.MakeArgString(ARCLiteLib)); + + // Arclite depends on CoreFoundation. + Arguments.push_back("-framework"); + Arguments.push_back("CoreFoundation"); + } else { + // FIXME: We should probably diagnose this, but this is not a place where + // we can emit diagnostics. Silently ignore it for now. + } + } + + context.Args.AddAllArgValues(Arguments, options::OPT_Xlinker); + context.Args.AddAllArgs(Arguments, options::OPT_linker_option_Group); + for (const Arg *arg : + context.Args.filtered(options::OPT_F, options::OPT_Fsystem)) { + Arguments.push_back("-F"); + Arguments.push_back(arg->getValue()); + } + + if (context.Args.hasArg(options::OPT_enable_app_extension)) { + // Keep this string fixed in case the option used by the + // compiler itself changes. + Arguments.push_back("-application_extension"); + } + + // Linking sanitizers will add rpaths, which might negatively interact when + // other rpaths are involved, so we should make sure we add the rpaths after + // all user-specified rpaths. + if (context.OI.SelectedSanitizers & SanitizerKind::Address) + addLinkSanitizerLibArgsForDarwin(context.Args, Arguments, "asan", *this); + + if (context.OI.SelectedSanitizers & SanitizerKind::Thread) + addLinkSanitizerLibArgsForDarwin(context.Args, Arguments, "tsan", *this); + + // Only link in libFuzzer for executables. + if (job.getKind() == LinkKind::Executable && + (context.OI.SelectedSanitizers & SanitizerKind::Fuzzer)) + addLinkSanitizerLibArgsForDarwin(context.Args, Arguments, "fuzzer", *this, + /*shared=*/false); + + if (context.Args.hasArg(options::OPT_embed_bitcode, + options::OPT_embed_bitcode_marker)) { + Arguments.push_back("-bitcode_bundle"); + } + + if (!context.OI.SDKPath.empty()) { + Arguments.push_back("-syslibroot"); + Arguments.push_back(context.Args.MakeArgString(context.OI.SDKPath)); + } + + Arguments.push_back("-lobjc"); + Arguments.push_back("-lSystem"); + + Arguments.push_back("-arch"); + Arguments.push_back(context.Args.MakeArgString(getTriple().getArchName())); + + // Add the runtime library link path, which is platform-specific and found + // relative to the compiler. + SmallString<128> RuntimeLibPath; + getRuntimeLibraryPath(RuntimeLibPath, context.Args, /*Shared=*/true); + + // Link the standard library. + Arguments.push_back("-L"); + if (context.Args.hasFlag(options::OPT_static_stdlib, + options::OPT_no_static_stdlib, false)) { + SmallString<128> StaticRuntimeLibPath; + getRuntimeLibraryPath(StaticRuntimeLibPath, context.Args, /*Shared=*/false); + Arguments.push_back(context.Args.MakeArgString(StaticRuntimeLibPath)); + Arguments.push_back("-lc++"); + Arguments.push_back("-framework"); + Arguments.push_back("Foundation"); + Arguments.push_back("-force_load_swift_libs"); + } else { + Arguments.push_back(context.Args.MakeArgString(RuntimeLibPath)); + // FIXME: We probably shouldn't be adding an rpath here unless we know ahead + // of time the standard library won't be copied. SR-1967 + Arguments.push_back("-rpath"); + Arguments.push_back(context.Args.MakeArgString(RuntimeLibPath)); + } + + if (context.Args.hasArg(options::OPT_profile_generate)) { + SmallString<128> LibProfile(RuntimeLibPath); + llvm::sys::path::remove_filename(LibProfile); // remove platform name + llvm::sys::path::append(LibProfile, "clang", "lib", "darwin"); + + StringRef RT; + if (Triple.isiOS()) { + if (Triple.isTvOS()) + RT = "tvos"; + else + RT = "ios"; + } else if (Triple.isWatchOS()) { + RT = "watchos"; + } else { + assert(Triple.isMacOSX()); + RT = "osx"; + } + + StringRef Sim; + if (tripleIsAnySimulator(Triple)) { + Sim = "sim"; + } + + llvm::sys::path::append(LibProfile, + "libclang_rt.profile_" + RT + Sim + ".a"); + + // FIXME: Continue accepting the old path for simulator libraries for now. + if (!Sim.empty() && !llvm::sys::fs::exists(LibProfile)) { + llvm::sys::path::remove_filename(LibProfile); + llvm::sys::path::append(LibProfile, "libclang_rt.profile_" + RT + ".a"); + } + + Arguments.push_back(context.Args.MakeArgString(LibProfile)); + } + + // FIXME: Properly handle deployment targets. + assert(Triple.isiOS() || Triple.isWatchOS() || Triple.isMacOSX()); + if (Triple.isiOS()) { + bool isiOSSimulator = tripleIsiOSSimulator(Triple); + if (Triple.isTvOS()) { + if (isiOSSimulator) + Arguments.push_back("-tvos_simulator_version_min"); + else + Arguments.push_back("-tvos_version_min"); + } else { + if (isiOSSimulator) + Arguments.push_back("-ios_simulator_version_min"); + else + Arguments.push_back("-iphoneos_version_min"); + } + unsigned major, minor, micro; + Triple.getiOSVersion(major, minor, micro); + addVersionString(context.Args, Arguments, major, minor, micro); + } else if (Triple.isWatchOS()) { + if (tripleIsWatchSimulator(Triple)) + Arguments.push_back("-watchos_simulator_version_min"); + else + Arguments.push_back("-watchos_version_min"); + unsigned major, minor, micro; + Triple.getOSVersion(major, minor, micro); + addVersionString(context.Args, Arguments, major, minor, micro); + } else { + Arguments.push_back("-macosx_version_min"); + unsigned major, minor, micro; + Triple.getMacOSXVersion(major, minor, micro); + addVersionString(context.Args, Arguments, major, minor, micro); + } + + Arguments.push_back("-no_objc_category_merging"); + + // This should be the last option, for convenience in checking output. + Arguments.push_back("-o"); + Arguments.push_back( + context.Args.MakeArgString(context.Output.getPrimaryOutputFilename())); + + return II; +} diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index c83f85ed414b6..66cfd26c18b94 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -119,88 +119,137 @@ ArrayRef Driver::getArgsWithoutProgramNameAndDriverMode( return Args; } -static void validateArgs(DiagnosticEngine &diags, const ArgList &Args) { - if (Args.hasArgNoClaim(options::OPT_import_underlying_module) && - Args.hasArgNoClaim(options::OPT_import_objc_header)) { +static void validateBridgingHeaderArgs(DiagnosticEngine &diags, + const ArgList &args) { + if (args.hasArgNoClaim(options::OPT_import_underlying_module) && + args.hasArgNoClaim(options::OPT_import_objc_header)) { diags.diagnose({}, diag::error_framework_bridging_header); } +} + +static void validateDeploymentTarget(DiagnosticEngine &diags, + const ArgList &args) { + const Arg *A = args.getLastArg(options::OPT_target); + if (!A) + return; // Check minimum supported OS versions. - if (const Arg *A = Args.getLastArg(options::OPT_target)) { - llvm::Triple triple(llvm::Triple::normalize(A->getValue())); - if (triple.isMacOSX()) { - if (triple.isMacOSXVersionLT(10, 9)) - diags.diagnose(SourceLoc(), diag::error_os_minimum_deployment, - "OS X 10.9"); - } else if (triple.isiOS()) { - if (triple.isTvOS()) { - if (triple.isOSVersionLT(9, 0)) { - diags.diagnose(SourceLoc(), diag::error_os_minimum_deployment, - "tvOS 9.0"); - return; - } - } - if (triple.isOSVersionLT(7)) + llvm::Triple triple(llvm::Triple::normalize(A->getValue())); + if (triple.isMacOSX()) { + if (triple.isMacOSXVersionLT(10, 9)) + diags.diagnose(SourceLoc(), diag::error_os_minimum_deployment, + "OS X 10.9"); + } else if (triple.isiOS()) { + if (triple.isTvOS()) { + if (triple.isOSVersionLT(9, 0)) { diags.diagnose(SourceLoc(), diag::error_os_minimum_deployment, - "iOS 7"); - if (triple.isArch32Bit() && !triple.isOSVersionLT(11)) { - diags.diagnose(SourceLoc(), diag::error_ios_maximum_deployment_32, - triple.getOSMajorVersion()); - } - } else if (triple.isWatchOS()) { - if (triple.isOSVersionLT(2, 0)) { - diags.diagnose(SourceLoc(), diag::error_os_minimum_deployment, - "watchOS 2.0"); - return; + "tvOS 9.0"); + return; } } + if (triple.isOSVersionLT(7)) + diags.diagnose(SourceLoc(), diag::error_os_minimum_deployment, + "iOS 7"); + if (triple.isArch32Bit() && !triple.isOSVersionLT(11)) { + diags.diagnose(SourceLoc(), diag::error_ios_maximum_deployment_32, + triple.getOSMajorVersion()); + } + } else if (triple.isWatchOS()) { + if (triple.isOSVersionLT(2, 0)) { + diags.diagnose(SourceLoc(), diag::error_os_minimum_deployment, + "watchOS 2.0"); + return; + } } +} - // Check for conflicting warning control flags - if (Args.hasArg(options::OPT_suppress_warnings) && - Args.hasArg(options::OPT_warnings_as_errors)) { +static void validateWarningControlArgs(DiagnosticEngine &diags, + const ArgList &args) { + if (args.hasArg(options::OPT_suppress_warnings) && + args.hasArg(options::OPT_warnings_as_errors)) { diags.diagnose(SourceLoc(), diag::error_conflicting_options, "-warnings-as-errors", "-suppress-warnings"); } +} - // Check for conflicting profiling flags - const Arg *ProfileGenerate = Args.getLastArg(options::OPT_profile_generate); - const Arg *ProfileUse = Args.getLastArg(options::OPT_profile_use); - if (ProfileGenerate && ProfileUse) +static void validateProfilingArgs(DiagnosticEngine &diags, + const ArgList &args) { + const Arg *ProfileGenerate = args.getLastArg(options::OPT_profile_generate); + const Arg *ProfileUse = args.getLastArg(options::OPT_profile_use); + if (ProfileGenerate && ProfileUse) { diags.diagnose(SourceLoc(), diag::error_conflicting_options, "-profile-generate", "-profile-use"); + } // Check if the profdata is missing - if (ProfileUse && !llvm::sys::fs::exists(ProfileUse->getValue())) + if (ProfileUse && !llvm::sys::fs::exists(ProfileUse->getValue())) { diags.diagnose(SourceLoc(), diag::error_profile_missing, - ProfileUse->getValue()); + ProfileUse->getValue()); + } +} +static void validateDebugInfoArgs(DiagnosticEngine &diags, + const ArgList &args) { // Check for missing debug option when verifying debug info. - if (Args.hasArg(options::OPT_verify_debug_info)) { - bool hasDebugOption = true; - Arg *Arg = Args.getLastArg(swift::options::OPT_g_Group); - if (!Arg || Arg->getOption().matches(swift::options::OPT_gnone)) - hasDebugOption = false; - if (!hasDebugOption) + if (args.hasArg(options::OPT_verify_debug_info)) { + Arg *debugOpt = args.getLastArg(swift::options::OPT_g_Group); + if (!debugOpt || debugOpt->getOption().matches(swift::options::OPT_gnone)) { diags.diagnose(SourceLoc(), diag::verify_debug_info_requires_debug_option); + } } +} - for (const Arg *A : Args.filtered(options::OPT_D)) { +static void validateCompilationConditionArgs(DiagnosticEngine &diags, + const ArgList &args) { + for (const Arg *A : args.filtered(options::OPT_D)) { StringRef name = A->getValue(); - if (name.find('=') != StringRef::npos) + if (name.find('=') != StringRef::npos) { diags.diagnose(SourceLoc(), diag::cannot_assign_value_to_conditional_compilation_flag, name); - else if (name.startswith("-D")) + } else if (name.startswith("-D")) { diags.diagnose(SourceLoc(), diag::redundant_prefix_compilation_flag, name); - else if (!Lexer::isIdentifier(name)) + } else if (!Lexer::isIdentifier(name)) { diags.diagnose(SourceLoc(), diag::invalid_conditional_compilation_flag, name); + } } } +static void validateAutolinkingArgs(DiagnosticEngine &diags, + const ArgList &args) { + auto *forceLoadArg = args.getLastArg(options::OPT_autolink_force_load); + if (!forceLoadArg) + return; + auto *incrementalArg = args.getLastArg(options::OPT_incremental); + if (!incrementalArg) + return; + + // Note: -incremental can itself be overridden by other arguments later + // on, but since -autolink-force-load is a rare and not-really-recommended + // option it's not worth modeling that complexity here (or moving the + // check somewhere else). + diags.diagnose(SourceLoc(), diag::error_option_not_supported, + forceLoadArg->getSpelling(), incrementalArg->getSpelling()); +} + +/// Perform miscellaneous early validation of arguments. +static void validateArgs(DiagnosticEngine &diags, const ArgList &args) { + validateBridgingHeaderArgs(diags, args); + validateDeploymentTarget(diags, args); + validateWarningControlArgs(diags, args); + validateProfilingArgs(diags, args); + validateDebugInfoArgs(diags, args); + validateCompilationConditionArgs(diags, args); + validateAutolinkingArgs(diags, args); + + if (args.hasArg(options::OPT_emit_public_type_metadata_accessors)) + diags.diagnose(SourceLoc(), + diag::warn_emit_public_type_metadata_accessors_deprecated); +} + std::unique_ptr Driver::buildToolChain(const llvm::opt::InputArgList &ArgList) { @@ -223,7 +272,9 @@ Driver::buildToolChain(const llvm::opt::InputArgList &ArgList) { case llvm::Triple::FreeBSD: return llvm::make_unique(*this, target); case llvm::Triple::Win32: - return llvm::make_unique(*this, target); + if (target.isWindowsCygwinEnvironment()) + return llvm::make_unique(*this, target); + return llvm::make_unique(*this, target); case llvm::Triple::Haiku: return llvm::make_unique(*this, target); default: @@ -268,6 +319,47 @@ class Driver::InputInfoMap }; using InputInfoMap = Driver::InputInfoMap; +/// Get the filename for build record. Returns true if failed. +/// Additionally, set 'outputBuildRecordForModuleOnlyBuild' to true if this is +/// full compilation with swiftmodule. +static bool getCompilationRecordPath(std::string &buildRecordPath, + bool &outputBuildRecordForModuleOnlyBuild, + const OutputInfo &OI, + const Optional &OFM, + DiagnosticEngine *Diags) { + if (!OFM) { + // FIXME: This should work without an output file map. We should have + // another way to specify a build record and where to put intermediates. + if (Diags) + Diags->diagnose(SourceLoc(), diag::incremental_requires_output_file_map); + return true; + } + + if (auto *masterOutputMap = OFM->getOutputMapForSingleOutput()) + buildRecordPath = masterOutputMap->lookup(file_types::TY_SwiftDeps); + + if (buildRecordPath.empty()) { + if (Diags) + Diags->diagnose(SourceLoc(), + diag::incremental_requires_build_record_entry, + file_types::getTypeName(file_types::TY_SwiftDeps)); + return true; + } + + // In 'emit-module' only mode, use build-record filename suffixed with + // '~moduleonly'. So that module-only mode doesn't mess up build-record + // file for full compilation. + if (OI.CompilerOutputType == file_types::TY_SwiftModuleFile) { + buildRecordPath = buildRecordPath.append("~moduleonly"); + } else if (OI.ShouldTreatModuleAsTopLevelOutput) { + // If we emit module along with full compilation, emit build record + // file for '-emit-module' only mode as well. + outputBuildRecordForModuleOnlyBuild = true; + } + + return false; +} + static bool failedToReadOutOfDateMap(bool ShowIncrementalBuildDecisions, StringRef buildRecordPath, StringRef reason = "") { @@ -282,7 +374,9 @@ static bool failedToReadOutOfDateMap(bool ShowIncrementalBuildDecisions, return true; } -static bool populateOutOfDateMap(InputInfoMap &map, StringRef argsHashStr, +static bool populateOutOfDateMap(InputInfoMap &map, + llvm::sys::TimePoint<> &LastBuildTime, + StringRef argsHashStr, const InputFileList &inputs, StringRef buildRecordPath, bool ShowIncrementalBuildDecisions) { @@ -392,7 +486,7 @@ static bool populateOutOfDateMap(InputInfoMap &map, StringRef argsHashStr, llvm::sys::TimePoint<> timeVal; if (readTimeValue(i->getValue(), timeVal)) return true; - map[nullptr] = { InputInfo::NeedsCascadingBuild, timeVal }; + LastBuildTime = timeVal; } else if (keyStr == compilation_record::getName(TopLevelKey::Inputs)) { auto *inputMap = dyn_cast(i->getValue()); @@ -493,7 +587,7 @@ static bool populateOutOfDateMap(InputInfoMap &map, StringRef argsHashStr, } // warn if -embed-bitcode is set and the output type is not an object -static void validateEmbedBitcode(DerivedArgList &Args, OutputInfo &OI, +static void validateEmbedBitcode(DerivedArgList &Args, const OutputInfo &OI, DiagnosticEngine &Diags) { if (Args.hasArg(options::OPT_embed_bitcode) && OI.CompilerOutputType != file_types::TY_Object) { @@ -502,6 +596,31 @@ static void validateEmbedBitcode(DerivedArgList &Args, OutputInfo &OI, } } +/// Gets the filelist threshold to use. Diagnoses and returns true on error. +static bool getFilelistThreshold(DerivedArgList &Args, size_t &FilelistThreshold, + DiagnosticEngine &Diags) { + FilelistThreshold = 128; + + // claim and diagnose deprecated -driver-use-filelists + bool HasUseFilelists = Args.hasArg(options::OPT_driver_use_filelists); + if (HasUseFilelists) + Diags.diagnose(SourceLoc(), diag::warn_use_filelists_deprecated); + + if (const Arg *A = Args.getLastArg(options::OPT_driver_filelist_threshold)) { + // Use the supplied threshold + if (StringRef(A->getValue()).getAsInteger(10, FilelistThreshold)) { + Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value, + A->getAsString(Args), A->getValue()); + return true; + } + } else if (HasUseFilelists) { + // Treat -driver-use-filelists as -driver-filelist-threshold=0 + FilelistThreshold = 0; + } // else stick with the default + + return false; +} + std::unique_ptr Driver::buildCompilation(const ToolChain &TC, std::unique_ptr ArgList) { @@ -551,13 +670,7 @@ Driver::buildCompilation(const ToolChain &TC, Incremental = false; } - bool BatchMode = ArgList->hasFlag(options::OPT_enable_batch_mode, - options::OPT_disable_batch_mode, - false); - bool SaveTemps = ArgList->hasArg(options::OPT_save_temps); - bool ContinueBuildingAfterErrors = - ArgList->hasArg(options::OPT_continue_building_after_errors); bool ShowDriverTimeCompilation = ArgList->hasArg(options::OPT_driver_time_compilation); @@ -588,8 +701,30 @@ Driver::buildCompilation(const ToolChain &TC, // Determine the OutputInfo for the driver. OutputInfo OI; + bool BatchMode = false; + OI.CompilerMode = computeCompilerMode(*TranslatedArgList, Inputs, BatchMode); buildOutputInfo(TC, *TranslatedArgList, BatchMode, Inputs, OI); + // Note: Batch mode handling of serialized diagnostics requires that all + // batches get to run, in order to make sure that all diagnostics emitted + // during the compilation end up in at least one serialized diagnostic file. + // Therefore, treat batch mode as implying -continue-building-after-errors. + // (This behavior could be limited to only when serialized diagnostics are + // being emitted, but this seems more consistent and less surprising for + // users.) + // FIXME: We don't really need (or want) a full ContinueBuildingAfterErrors. + // If we fail to precompile a bridging header, for example, there's no need + // to go on to compilation of source files, and if compilation of source files + // fails, we shouldn't try to link. Instead, we'd want to let all jobs finish + // but not schedule any new ones. + const bool ContinueBuildingAfterErrors = + BatchMode || ArgList->hasArg(options::OPT_continue_building_after_errors); + + // Issue a remark to facilitate recognizing the use of batch mode in the build + // log. + if (BatchMode) + Diags.diagnose(SourceLoc(), diag::remark_using_batch_mode); + if (Diags.hadAnyError()) return nullptr; @@ -637,40 +772,22 @@ Driver::buildCompilation(const ToolChain &TC, return nullptr; } + std::string buildRecordPath; + bool outputBuildRecordForModuleOnlyBuild = false; + getCompilationRecordPath(buildRecordPath, outputBuildRecordForModuleOnlyBuild, + OI, OFM, Incremental ? &Diags : nullptr); + SmallString<32> ArgsHash; computeArgsHash(ArgsHash, *TranslatedArgList); - + llvm::sys::TimePoint<> LastBuildTime = llvm::sys::TimePoint<>::min(); InputInfoMap outOfDateMap; bool rebuildEverything = true; - if (Incremental) { - if (!OFM) { - // FIXME: This should work without an output file map. We should have - // another way to specify a build record and where to put intermediates. - Diags.diagnose(SourceLoc(), diag::incremental_requires_output_file_map); - + if (Incremental && !buildRecordPath.empty()) { + if (populateOutOfDateMap(outOfDateMap, LastBuildTime, ArgsHash, Inputs, + buildRecordPath, ShowIncrementalBuildDecisions)) { + // FIXME: Distinguish errors from "file removed", which is benign. } else { - StringRef buildRecordPath; - if (auto *masterOutputMap = OFM->getOutputMapForSingleOutput()) { - auto iter = masterOutputMap->find(file_types::TY_SwiftDeps); - if (iter != masterOutputMap->end()) - buildRecordPath = iter->second; - } - - if (buildRecordPath.empty()) { - Diags.diagnose(SourceLoc(), - diag::incremental_requires_build_record_entry, - file_types::getTypeName(file_types::TY_SwiftDeps)); - rebuildEverything = true; - - } else { - if (populateOutOfDateMap(outOfDateMap, ArgsHash, Inputs, - buildRecordPath, - ShowIncrementalBuildDecisions)) { - // FIXME: Distinguish errors from "file removed", which is benign. - } else { - rebuildEverything = false; - } - } + rebuildEverything = false; } } @@ -683,6 +800,10 @@ Driver::buildCompilation(const ToolChain &TC, } } + size_t DriverFilelistThreshold; + if (getFilelistThreshold(*TranslatedArgList, DriverFilelistThreshold, Diags)) + return nullptr; + OutputLevel Level = OutputLevel::Normal; if (const Arg *A = ArgList->getLastArg(options::OPT_driver_print_jobs, options::OPT_v, @@ -697,20 +818,27 @@ Driver::buildCompilation(const ToolChain &TC, llvm_unreachable("Unknown OutputLevel argument!"); } - std::unique_ptr C(new Compilation(Diags, TC, OI, Level, - std::move(ArgList), - std::move(TranslatedArgList), - std::move(Inputs), - ArgsHash, StartTime, - NumberOfParallelCommands, - Incremental, - BatchMode, - DriverBatchSeed, - DriverForceOneBatchRepartition, - DriverSkipExecution, - SaveTemps, - ShowDriverTimeCompilation, - std::move(StatsReporter))); + std::unique_ptr C( + new Compilation(Diags, TC, OI, Level, + std::move(ArgList), + std::move(TranslatedArgList), + std::move(Inputs), + buildRecordPath, + outputBuildRecordForModuleOnlyBuild, + ArgsHash, + StartTime, + LastBuildTime, + DriverFilelistThreshold, + NumberOfParallelCommands, + Incremental, + BatchMode, + DriverBatchSeed, + DriverForceOneBatchRepartition, + DriverSkipExecution, + SaveTemps, + ShowDriverTimeCompilation, + std::move(StatsReporter))); + // Construct the graph of Actions. SmallVector TopLevelActions; buildActions(TopLevelActions, TC, OI, OFM ? OFM.getPointer() : nullptr, @@ -748,17 +876,6 @@ Driver::buildCompilation(const ToolChain &TC, if (rebuildEverything) C->disableIncrementalBuild(); - if (OFM) { - if (auto *masterOutputMap = OFM->getOutputMapForSingleOutput()) { - C->setCompilationRecordPath( - masterOutputMap->lookup(file_types::TY_SwiftDeps)); - - auto buildEntry = outOfDateMap.find(nullptr); - if (buildEntry != outOfDateMap.end()) - C->setLastBuildTime(buildEntry->second.previousModTime); - } - } - if (Diags.hadAnyError()) return nullptr; @@ -776,7 +893,7 @@ static Arg *makeInputArg(const DerivedArgList &Args, OptTable &Opts, return A; } -using RemainingArgsHandler = std::function; +using RemainingArgsHandler = llvm::function_ref; std::unique_ptr parseArgsUntil(const llvm::opt::OptTable& Opts, @@ -1102,8 +1219,6 @@ void Driver::buildOutputInfo(const ToolChain &TC, const DerivedArgList &Args, ? file_types::TY_Nothing : file_types::TY_Object; - OI.CompilerMode = computeCompilerMode(Args, Inputs); - if (const Arg *A = Args.getLastArg(options::OPT_num_threads)) { if (BatchMode) { Diags.diagnose(SourceLoc(), diag::warning_cannot_multithread_batch_mode); @@ -1373,26 +1488,31 @@ void Driver::buildOutputInfo(const ToolChain &TC, const DerivedArgList &Args, OutputInfo::Mode Driver::computeCompilerMode(const DerivedArgList &Args, - const InputFileList &Inputs) const { + const InputFileList &Inputs, + bool &BatchModeOut) const { if (driverKind == Driver::DriverKind::Interactive) return Inputs.empty() ? OutputInfo::Mode::REPL : OutputInfo::Mode::Immediate; - const Arg *ArgRequiringWMO = Args.getLastArg( + const Arg *ArgRequiringSingleCompile = Args.getLastArg( options::OPT_whole_module_optimization, options::OPT_index_file); - if (!ArgRequiringWMO) + BatchModeOut = Args.hasFlag(options::OPT_enable_batch_mode, + options::OPT_disable_batch_mode, + false); + + if (!ArgRequiringSingleCompile) return OutputInfo::Mode::StandardCompile; - // Test for -enable-batch-mode, rather than the BatchMode flag that is - // passed into the caller because the diagnostic is intended to warn against - // overriding *explicit* batch mode. No warning should be given if in batch - // mode by default. - if (Args.hasArg(options::OPT_enable_batch_mode)) + // Override batch mode if given -wmo or -index-file. + if (BatchModeOut) { + BatchModeOut = false; + // Emit a warning about such overriding (FIXME: we might conditionalize + // this based on the user or xcode passing -disable-batch-mode). Diags.diagnose(SourceLoc(), diag::warn_ignoring_batch_mode, - ArgRequiringWMO->getOption().getPrefixedName()); - + ArgRequiringSingleCompile->getOption().getPrefixedName()); + } return OutputInfo::Mode::SingleCompile; } @@ -1734,7 +1854,8 @@ Driver::buildOutputFileMap(const llvm::opt::DerivedArgList &Args, OutputFileMap::loadFromPath(A->getValue(), workingDirectory); if (auto Err = OFM.takeError()) { Diags.diagnose(SourceLoc(), diag::error_unable_to_load_output_file_map, - llvm::toString(std::move(Err))); + llvm::toString(std::move(Err)), A->getValue()); + return None; } return *OFM; } @@ -2166,6 +2287,16 @@ Job *Driver::buildJobsForAction(Compilation &C, const JobAction *JA, PrimaryInput = Out.getPrimaryOutputFilenames()[i]; } + // With -index-file option, the primary input is the one passed with + // -index-file-path. + // FIXME: Figure out how this better fits within the driver infrastructure. + if (JA->getType() == file_types::TY_IndexData) { + if (Arg *A = C.getArgs().getLastArg(options::OPT_index_file_path)) { + BaseInput = A->getValue(); + PrimaryInput = A->getValue(); + } + } + const TypeToPathMap *OutputMap = nullptr; if (OFM) { if (isa(JA)) { @@ -2689,7 +2820,8 @@ void Driver::printHelp(bool ShowHidden) const { ExcludedFlagsBitmask |= HelpHidden; getOpts().PrintHelp(llvm::outs(), Name.c_str(), "Swift compiler", - IncludedFlagsBitmask, ExcludedFlagsBitmask); + IncludedFlagsBitmask, ExcludedFlagsBitmask, + /*ShowAllAliases*/false); } bool OutputInfo::mightHaveExplicitPrimaryInputs( diff --git a/lib/Driver/FrontendUtil.cpp b/lib/Driver/FrontendUtil.cpp index c419884038269..f8e663e577c41 100644 --- a/lib/Driver/FrontendUtil.cpp +++ b/lib/Driver/FrontendUtil.cpp @@ -34,6 +34,17 @@ swift::driver::createCompilerInvocation(ArrayRef Argv, // frontend command. Args.push_back("-force-single-frontend-invocation"); + // Explictly disable batch mode to avoid a spurious warning when combining + // -enable-batch-mode with -force-single-frontend-invocation. This is an + // implementation detail. + Args.push_back("-disable-batch-mode"); + + // Avoid using filelists + std::string neverThreshold = + std::to_string(Compilation::NEVER_USE_FILELIST); + Args.push_back("-driver-filelist-threshold"); + Args.push_back(neverThreshold.c_str()); + // Force the driver into batch mode by specifying "swiftc" as the name. Driver TheDriver("swiftc", "swiftc", Args, Diags); diff --git a/lib/Driver/Job.cpp b/lib/Driver/Job.cpp index 48c93f7b01783..3fd29e03383d6 100644 --- a/lib/Driver/Job.cpp +++ b/lib/Driver/Job.cpp @@ -409,7 +409,13 @@ BatchJob::BatchJob(const JobAction &Source, const char *Executable, llvm::opt::ArgStringList Arguments, EnvironmentVector ExtraEnvironment, std::vector Infos, - ArrayRef Combined) + ArrayRef Combined, int64_t &NextQuasiPID) : Job(Source, std::move(Inputs), std::move(Output), Executable, Arguments, ExtraEnvironment, Infos), - CombinedJobs(Combined.begin(), Combined.end()) {} + CombinedJobs(Combined.begin(), Combined.end()), + QuasiPIDBase(NextQuasiPID) { + + assert(QuasiPIDBase < 0); + NextQuasiPID -= CombinedJobs.size(); + assert(NextQuasiPID < 0); +} diff --git a/lib/Driver/ParseableOutput.cpp b/lib/Driver/ParseableOutput.cpp index 9fb441de79e60..e3ede0048fa65 100644 --- a/lib/Driver/ParseableOutput.cpp +++ b/lib/Driver/ParseableOutput.cpp @@ -155,9 +155,9 @@ class DetailedCommandBasedMessage : public CommandBasedMessage { }; class TaskBasedMessage : public CommandBasedMessage { - ProcessId Pid; + int64_t Pid; public: - TaskBasedMessage(StringRef Kind, const Job &Cmd, ProcessId Pid) : + TaskBasedMessage(StringRef Kind, const Job &Cmd, int64_t Pid) : CommandBasedMessage(Kind, Cmd), Pid(Pid) {} void provideMapping(swift::json::Output &out) override { @@ -167,9 +167,9 @@ class TaskBasedMessage : public CommandBasedMessage { }; class BeganMessage : public DetailedCommandBasedMessage { - ProcessId Pid; + int64_t Pid; public: - BeganMessage(const Job &Cmd, ProcessId Pid) : + BeganMessage(const Job &Cmd, int64_t Pid) : DetailedCommandBasedMessage("began", Cmd), Pid(Pid) {} void provideMapping(swift::json::Output &out) override { @@ -181,7 +181,7 @@ class BeganMessage : public DetailedCommandBasedMessage { class TaskOutputMessage : public TaskBasedMessage { std::string Output; public: - TaskOutputMessage(StringRef Kind, const Job &Cmd, ProcessId Pid, + TaskOutputMessage(StringRef Kind, const Job &Cmd, int64_t Pid, StringRef Output) : TaskBasedMessage(Kind, Cmd, Pid), Output(Output) {} @@ -194,7 +194,7 @@ class TaskOutputMessage : public TaskBasedMessage { class FinishedMessage : public TaskOutputMessage { int ExitStatus; public: - FinishedMessage(const Job &Cmd, ProcessId Pid, StringRef Output, + FinishedMessage(const Job &Cmd, int64_t Pid, StringRef Output, int ExitStatus) : TaskOutputMessage("finished", Cmd, Pid, Output), ExitStatus(ExitStatus) {} @@ -209,7 +209,7 @@ class SignalledMessage : public TaskOutputMessage { std::string ErrorMsg; Optional Signal; public: - SignalledMessage(const Job &Cmd, ProcessId Pid, StringRef Output, + SignalledMessage(const Job &Cmd, int64_t Pid, StringRef Output, StringRef ErrorMsg, Optional Signal) : TaskOutputMessage("signalled", Cmd, Pid, Output), ErrorMsg(ErrorMsg), Signal(Signal) {} @@ -253,20 +253,20 @@ static void emitMessage(raw_ostream &os, Message &msg) { } void parseable_output::emitBeganMessage(raw_ostream &os, - const Job &Cmd, ProcessId Pid) { + const Job &Cmd, int64_t Pid) { BeganMessage msg(Cmd, Pid); emitMessage(os, msg); } void parseable_output::emitFinishedMessage(raw_ostream &os, - const Job &Cmd, ProcessId Pid, + const Job &Cmd, int64_t Pid, int ExitStatus, StringRef Output) { FinishedMessage msg(Cmd, Pid, Output, ExitStatus); emitMessage(os, msg); } void parseable_output::emitSignalledMessage(raw_ostream &os, - const Job &Cmd, ProcessId Pid, + const Job &Cmd, int64_t Pid, StringRef ErrorMsg, StringRef Output, Optional Signal) { diff --git a/lib/Driver/ToolChain.cpp b/lib/Driver/ToolChain.cpp index caf9044764def..dd8455eca873b 100644 --- a/lib/Driver/ToolChain.cpp +++ b/lib/Driver/ToolChain.cpp @@ -50,8 +50,7 @@ const char * ToolChain::JobContext::getTemporaryFilePath(const llvm::Twine &name, StringRef suffix) const { SmallString<128> buffer; - std::error_code EC = - llvm::sys::fs::createTemporaryFile(name, suffix, buffer); + std::error_code EC = llvm::sys::fs::createTemporaryFile(name, suffix, buffer); if (EC) { // FIXME: This should not take down the entire process. llvm::report_fatal_error("unable to create temporary file for filelist"); @@ -63,13 +62,10 @@ ToolChain::JobContext::getTemporaryFilePath(const llvm::Twine &name, return C.getArgs().MakeArgString(buffer.str()); } -std::unique_ptr -ToolChain::constructJob(const JobAction &JA, - Compilation &C, - SmallVectorImpl &&inputs, - ArrayRef inputActions, - std::unique_ptr output, - const OutputInfo &OI) const { +std::unique_ptr ToolChain::constructJob( + const JobAction &JA, Compilation &C, SmallVectorImpl &&inputs, + ArrayRef inputActions, + std::unique_ptr output, const OutputInfo &OI) const { JobContext context{C, inputs, inputActions, *output, OI}; auto invocationInfo = [&]() -> InvocationInfo { @@ -77,17 +73,17 @@ ToolChain::constructJob(const JobAction &JA, #define CASE(K) \ case Action::Kind::K: \ return constructInvocation(cast(JA), context); - CASE(CompileJob) - CASE(InterpretJob) - CASE(BackendJob) - CASE(MergeModuleJob) - CASE(ModuleWrapJob) - CASE(LinkJob) - CASE(GenerateDSYMJob) - CASE(VerifyDebugInfoJob) - CASE(GeneratePCHJob) - CASE(AutolinkExtractJob) - CASE(REPLJob) + CASE(CompileJob) + CASE(InterpretJob) + CASE(BackendJob) + CASE(MergeModuleJob) + CASE(ModuleWrapJob) + CASE(LinkJob) + CASE(GenerateDSYMJob) + CASE(VerifyDebugInfoJob) + CASE(GeneratePCHJob) + CASE(AutolinkExtractJob) + CASE(REPLJob) #undef CASE case Action::Kind::Input: llvm_unreachable("not a JobAction"); @@ -153,8 +149,7 @@ file_types::ID ToolChain::lookupTypeForExtension(StringRef Ext) const { /// Return a _single_ TY_Swift InputAction, if one exists; /// if 0 or >1 such inputs exist, return nullptr. -static const InputAction* -findSingleSwiftInput(const CompileJobAction *CJA) { +static const InputAction *findSingleSwiftInput(const CompileJobAction *CJA) { auto Inputs = CJA->getInputs(); const InputAction *IA = nullptr; for (auto const *I : Inputs) { @@ -172,8 +167,7 @@ findSingleSwiftInput(const CompileJobAction *CJA) { return IA; } -static bool -jobsHaveSameExecutableNames(const Job *A, const Job *B) { +static bool jobsHaveSameExecutableNames(const Job *A, const Job *B) { // Jobs that get here (that are derived from CompileJobActions) should always // have the same executable name -- it should always be SWIFT_EXECUTABLE_NAME // -- but we check here just to be sure / fail gracefully in non-assert @@ -185,16 +179,14 @@ jobsHaveSameExecutableNames(const Job *A, const Job *B) { return true; } -static bool -jobsHaveSameOutputTypes(const Job *A, const Job *B) { +static bool jobsHaveSameOutputTypes(const Job *A, const Job *B) { if (A->getOutput().getPrimaryOutputType() != B->getOutput().getPrimaryOutputType()) return false; return A->getOutput().hasSameAdditionalOutputTypes(B->getOutput()); } -static bool -jobsHaveSameEnvironment(const Job *A, const Job *B) { +static bool jobsHaveSameEnvironment(const Job *A, const Job *B) { auto AEnv = A->getExtraEnvironment(); auto BEnv = B->getExtraEnvironment(); if (AEnv.size() != BEnv.size()) @@ -208,8 +200,7 @@ jobsHaveSameEnvironment(const Job *A, const Job *B) { return true; } -bool -ToolChain::jobIsBatchable(const Compilation &C, const Job *A) const { +bool ToolChain::jobIsBatchable(const Compilation &C, const Job *A) const { // FIXME: There might be a tighter criterion to use here? if (C.getOutputInfo().CompilerMode != OutputInfo::Mode::StandardCompile) return false; @@ -219,13 +210,11 @@ ToolChain::jobIsBatchable(const Compilation &C, const Job *A) const { return findSingleSwiftInput(CJActA) != nullptr; } -bool -ToolChain::jobsAreBatchCombinable(const Compilation &C, - const Job *A, const Job *B) const { +bool ToolChain::jobsAreBatchCombinable(const Compilation &C, const Job *A, + const Job *B) const { assert(jobIsBatchable(C, A)); assert(jobIsBatchable(C, B)); - return (jobsHaveSameExecutableNames(A, B) && - jobsHaveSameOutputTypes(A, B) && + return (jobsHaveSameExecutableNames(A, B) && jobsHaveSameOutputTypes(A, B) && jobsHaveSameEnvironment(A, B)); } @@ -286,15 +275,16 @@ mergeBatchInputs(ArrayRef jobs, /// discovered yet). So long as this is true, we need to make sure any batch job /// we build names its inputs in an order that's a subsequence of the sequence /// of inputs the driver was initially invoked with. -static void sortJobsToMatchCompilationInputs(ArrayRef unsortedJobs, - SmallVectorImpl &sortedJobs, - Compilation &C) { +static void +sortJobsToMatchCompilationInputs(ArrayRef unsortedJobs, + SmallVectorImpl &sortedJobs, + Compilation &C) { llvm::StringMap jobsByInput; for (const Job *J : unsortedJobs) { const CompileJobAction *CJA = cast(&J->getSource()); - const InputAction* IA = findSingleSwiftInput(CJA); - auto R = jobsByInput.insert(std::make_pair(IA->getInputArg().getValue(), - J)); + const InputAction *IA = findSingleSwiftInput(CJA); + auto R = + jobsByInput.insert(std::make_pair(IA->getInputArg().getValue(), J)); assert(R.second); } for (const InputPair &P : C.getInputFiles()) { @@ -310,8 +300,8 @@ static void sortJobsToMatchCompilationInputs(ArrayRef unsortedJobs, /// on \p BatchJob, to build the \c InvocationInfo. std::unique_ptr ToolChain::constructBatchJob(ArrayRef unsortedJobs, - Compilation &C) const -{ + Job::PID &NextQuasiPID, + Compilation &C) const { if (unsortedJobs.empty()) return nullptr; @@ -336,21 +326,9 @@ ToolChain::constructBatchJob(ArrayRef unsortedJobs, JobContext context{C, inputJobs.getArrayRef(), inputActions.getArrayRef(), *output, OI}; auto invocationInfo = constructInvocation(*batchCJA, context); - return llvm::make_unique(*batchCJA, - inputJobs.takeVector(), - std::move(output), - executablePath, - std::move(invocationInfo.Arguments), - std::move(invocationInfo.ExtraEnvironment), - std::move(invocationInfo.FilelistInfos), - sortedJobs); -} - -bool -ToolChain::sanitizerRuntimeLibExists(const ArgList &args, - StringRef sanitizerName, - bool shared) const { - // Assume no sanitizers are supported by default. - // This method should be overriden by a platform-specific subclass. - return false; + return llvm::make_unique( + *batchCJA, inputJobs.takeVector(), std::move(output), executablePath, + std::move(invocationInfo.Arguments), + std::move(invocationInfo.ExtraEnvironment), + std::move(invocationInfo.FilelistInfos), sortedJobs, NextQuasiPID); } diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index f3a6ae37e0d53..d7adb879e3e67 100644 --- a/lib/Driver/ToolChains.cpp +++ b/lib/Driver/ToolChains.cpp @@ -17,12 +17,12 @@ #include "swift/Basic/Platform.h" #include "swift/Basic/Range.h" #include "swift/Basic/TaskQueue.h" +#include "swift/Config.h" #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 "swift/Config.h" #include "clang/Basic/Version.h" #include "clang/Driver/Util.h" #include "llvm/ADT/StringSwitch.h" @@ -39,33 +39,25 @@ using namespace swift::driver; using namespace llvm::opt; bool ToolChain::JobContext::shouldUseInputFileList() const { - if (Args.hasArg(options::OPT_driver_use_filelists)) - return true; - return getTopLevelInputFiles().size() > TOO_MANY_FILES; + return getTopLevelInputFiles().size() > C.getFilelistThreshold(); } bool ToolChain::JobContext::shouldUsePrimaryInputFileListInFrontendInvocation() const { - if (Args.hasArg(options::OPT_driver_use_filelists)) - return true; - return InputActions.size() > TOO_MANY_FILES; + return InputActions.size() > C.getFilelistThreshold(); } bool ToolChain::JobContext::shouldUseMainOutputFileListInFrontendInvocation() const { - if (Args.hasArg(options::OPT_driver_use_filelists)) - return true; - return Output.getPrimaryOutputFilenames().size() > TOO_MANY_FILES; + return Output.getPrimaryOutputFilenames().size() > C.getFilelistThreshold(); } bool ToolChain::JobContext:: shouldUseSupplementaryOutputFileMapInFrontendInvocation() const { - if (Args.hasArg(options::OPT_driver_use_filelists)) - return true; static const unsigned UpperBoundOnSupplementaryOutputFileTypes = file_types::TY_INVALID; return InputActions.size() * UpperBoundOnSupplementaryOutputFileTypes > - TOO_MANY_FILES; + C.getFilelistThreshold(); } bool ToolChain::JobContext::shouldFilterFrontendInputsByType() const { @@ -74,10 +66,10 @@ bool ToolChain::JobContext::shouldFilterFrontendInputsByType() const { return OI.CompilerMode != OutputInfo::Mode::SingleCompile; } -static void addInputsOfType(ArgStringList &Arguments, - ArrayRef Inputs, - file_types::ID InputType, - const char *PrefixArgument = nullptr) { +void ToolChain::addInputsOfType(ArgStringList &Arguments, + ArrayRef Inputs, + file_types::ID InputType, + const char *PrefixArgument) const { for (auto &Input : Inputs) { if (Input->getType() != InputType) continue; @@ -87,11 +79,11 @@ static void addInputsOfType(ArgStringList &Arguments, } } -static void addInputsOfType(ArgStringList &Arguments, - ArrayRef Jobs, - const llvm::opt::ArgList &Args, - file_types::ID InputType, - const char *PrefixArgument = nullptr) { +void ToolChain::addInputsOfType(ArgStringList &Arguments, + ArrayRef Jobs, + const llvm::opt::ArgList &Args, + file_types::ID InputType, + const char *PrefixArgument) const { for (const Job *Cmd : Jobs) { auto output = Cmd->getOutput().getAnyOutputForType(InputType); if (!output.empty()) { @@ -102,11 +94,11 @@ static void addInputsOfType(ArgStringList &Arguments, } } -static void addPrimaryInputsOfType(ArgStringList &Arguments, - ArrayRef Jobs, - const llvm::opt::ArgList &Args, - file_types::ID InputType, - const char *PrefixArgument = nullptr) { +void ToolChain::addPrimaryInputsOfType(ArgStringList &Arguments, + ArrayRef Jobs, + const llvm::opt::ArgList &Args, + file_types::ID InputType, + const char *PrefixArgument) const { for (const Job *Cmd : Jobs) { auto &outputInfo = Cmd->getOutput(); if (outputInfo.getPrimaryOutputType() == InputType) { @@ -137,8 +129,7 @@ static bool addOutputsOfType(ArgStringList &Arguments, /// Handle arguments common to all invocations of the frontend (compilation, /// module-merging, LLDB's REPL, etc). -static void addCommonFrontendArgs(const ToolChain &TC, - const OutputInfo &OI, +static void addCommonFrontendArgs(const ToolChain &TC, const OutputInfo &OI, const CommandOutput &output, const ArgList &inputArgs, ArgStringList &arguments) { @@ -270,8 +261,8 @@ ToolChain::constructInvocation(const CompileJobAction &job, context.addFrontendInputAndOutputArguments(Arguments, II.FilelistInfos); // Forward migrator flags. - if (auto DataPath = context.Args.getLastArg(options:: - OPT_api_diff_data_file)) { + if (auto DataPath = + context.Args.getLastArg(options::OPT_api_diff_data_file)) { Arguments.push_back("-api-diff-data-file"); Arguments.push_back(DataPath->getValue()); } @@ -291,10 +282,9 @@ ToolChain::constructInvocation(const CompileJobAction &job, bool ForwardAsIs = true; bool bridgingPCHIsEnabled = context.Args.hasFlag(options::OPT_enable_bridging_pch, - options::OPT_disable_bridging_pch, - true); + options::OPT_disable_bridging_pch, true); bool usePersistentPCH = bridgingPCHIsEnabled && - context.Args.hasArg(options::OPT_pch_output_dir); + context.Args.hasArg(options::OPT_pch_output_dir); if (!usePersistentPCH) { for (auto *IJ : context.Inputs) { if (!IJ->getOutput().getAnyOutputForType(file_types::TY_PCH).empty()) { @@ -366,7 +356,8 @@ ToolChain::constructInvocation(const CompileJobAction &job, if (context.Args.hasArg(options::OPT_index_store_path)) { context.Args.AddLastArg(Arguments, options::OPT_index_store_path); - Arguments.push_back("-index-system-modules"); + if (!context.Args.hasArg(options::OPT_index_ignore_system_modules)) + Arguments.push_back("-index-system-modules"); } return II; @@ -461,6 +452,11 @@ void ToolChain::JobContext::addFrontendInputAndOutputArguments( const bool UseSupplementaryOutputFileList = shouldUseSupplementaryOutputFileMapInFrontendInvocation(); + assert((C.getFilelistThreshold() != Compilation::NEVER_USE_FILELIST || + !UseFileList && !UsePrimaryFileList && + !UseSupplementaryOutputFileList) && + "No filelists are used if FilelistThreshold=NEVER_USE_FILELIST"); + if (UseFileList) { Arguments.push_back("-filelist"); Arguments.push_back(getAllSourcesPath()); @@ -616,7 +612,8 @@ ToolChain::constructInvocation(const BackendJobAction &job, FrontendModeOption = "-S"; break; case file_types::TY_Nothing: - // We were told to output nothing, so get the last mode option and use that. + // We were told to output nothing, so get the last mode option and use + // that. if (const Arg *A = context.Args.getLastArg(options::OPT_modes_Group)) FrontendModeOption = A->getSpelling().data(); else @@ -660,7 +657,7 @@ ToolChain::constructInvocation(const BackendJobAction &job, } assert(FrontendModeOption != nullptr && "No frontend mode option specified!"); - + Arguments.push_back(FrontendModeOption); // Add input arguments. @@ -677,7 +674,7 @@ ToolChain::constructInvocation(const BackendJobAction &job, assert(context.Inputs.size() == 1 && "The backend expects one input!"); Arguments.push_back("-primary-file"); const Job *Cmd = context.Inputs.front(); - + // In multi-threaded compilation, the backend job must select the correct // output file of the compilation job. auto OutNames = Cmd->getOutput().getPrimaryOutputFilenames(); @@ -795,8 +792,8 @@ ToolChain::constructInvocation(const MergeModuleJobAction &job, "The MergeModule tool only produces swiftmodule files!"); Arguments.push_back("-o"); - Arguments.push_back(context.Args.MakeArgString( - context.Output.getPrimaryOutputFilename())); + Arguments.push_back( + context.Args.MakeArgString(context.Output.getPrimaryOutputFilename())); return II; } @@ -820,10 +817,10 @@ ToolChain::constructInvocation(const ModuleWrapJobAction &job, Arguments.push_back("-target"); Arguments.push_back(context.Args.MakeArgString(getTriple().str())); - + Arguments.push_back("-o"); - Arguments.push_back(context.Args.MakeArgString( - context.Output.getPrimaryOutputFilename())); + Arguments.push_back( + context.Args.MakeArgString(context.Output.getPrimaryOutputFilename())); return {SWIFT_EXECUTABLE_NAME, Arguments}; } @@ -875,7 +872,6 @@ ToolChain::constructInvocation(const REPLJobAction &job, return {"lldb", Arguments}; } - ToolChain::InvocationInfo ToolChain::constructInvocation(const GenerateDSYMJobAction &job, const JobContext &context) const { @@ -890,8 +886,8 @@ ToolChain::constructInvocation(const GenerateDSYMJobAction &job, Arguments.push_back(context.Args.MakeArgString(inputPath)); Arguments.push_back("-o"); - Arguments.push_back(context.Args.MakeArgString( - context.Output.getPrimaryOutputFilename())); + Arguments.push_back( + context.Args.MakeArgString(context.Output.getPrimaryOutputFilename())); return {"dsymutil", Arguments}; } @@ -942,13 +938,12 @@ ToolChain::constructInvocation(const GeneratePCHJobAction &job, if (job.isPersistentPCH()) { Arguments.push_back("-emit-pch"); Arguments.push_back("-pch-output-dir"); - Arguments.push_back( - context.Args.MakeArgString(job.getPersistentPCHDir())); + Arguments.push_back(context.Args.MakeArgString(job.getPersistentPCHDir())); } else { Arguments.push_back("-emit-pch"); Arguments.push_back("-o"); - Arguments.push_back(context.Args.MakeArgString( - context.Output.getPrimaryOutputFilename())); + Arguments.push_back( + context.Args.MakeArgString(context.Output.getPrimaryOutputFilename())); } return {SWIFT_EXECUTABLE_NAME, Arguments}; @@ -966,77 +961,9 @@ ToolChain::constructInvocation(const LinkJobAction &job, llvm_unreachable("linking not implemented for this toolchain"); } -std::string -toolchains::Darwin::findProgramRelativeToSwiftImpl(StringRef name) const { - StringRef swiftPath = getDriver().getSwiftProgramPath(); - StringRef swiftBinDir = llvm::sys::path::parent_path(swiftPath); - - // See if we're in an Xcode toolchain. - bool hasToolchain = false; - llvm::SmallString<128> path{swiftBinDir}; - llvm::sys::path::remove_filename(path); // bin - llvm::sys::path::remove_filename(path); // usr - if (llvm::sys::path::extension(path) == ".xctoolchain") { - hasToolchain = true; - llvm::sys::path::remove_filename(path); // *.xctoolchain - llvm::sys::path::remove_filename(path); // Toolchains - llvm::sys::path::append(path, "usr", "bin"); - } - - StringRef paths[] = { swiftBinDir, path }; - auto pathsRef = llvm::makeArrayRef(paths); - if (!hasToolchain) - pathsRef = pathsRef.drop_back(); - - auto result = llvm::sys::findProgramByName(name, pathsRef); - if (result) - return result.get(); - return {}; -} - -static void addVersionString(const ArgList &inputArgs, ArgStringList &arguments, - unsigned major, unsigned minor, unsigned micro) { - llvm::SmallString<8> buf; - llvm::raw_svector_ostream os{buf}; - os << major << '.' << minor << '.' << micro; - arguments.push_back(inputArgs.MakeArgString(os.str())); -} - -/// Runs xcrun -f clang in order to find the location of Clang for -/// the currently active Xcode. -/// -/// We get the "currently active" part by passing through the DEVELOPER_DIR -/// environment variable (along with the rest of the environment). -static bool findXcodeClangPath(llvm::SmallVectorImpl &path) { - assert(path.empty()); - - auto xcrunPath = llvm::sys::findProgramByName("xcrun"); - if (!xcrunPath.getError()) { - const char *args[] = {"-f", "clang", nullptr}; - sys::TaskQueue queue; - queue.addTask(xcrunPath->c_str(), args, /*Env=*/llvm::None, - /*Context=*/nullptr, - /*SeparateErrors=*/true); - queue.execute(nullptr, [&path](sys::ProcessId PID, int returnCode, - StringRef output, StringRef errors, - void *unused) -> sys::TaskFinishedResponse { - if (returnCode == 0) { - output = output.rtrim(); - path.append(output.begin(), output.end()); - } - return sys::TaskFinishedResponse::ContinueExecution; - }); - } - - return !path.empty(); -} - -static void addPathEnvironmentVariableIfNeeded(Job::EnvironmentVector &env, - const char *name, - const char *separator, - options::ID optionID, - const ArgList &args, - StringRef extraEntry = "") { +void ToolChain::addPathEnvironmentVariableIfNeeded( + Job::EnvironmentVector &env, const char *name, const char *separator, + options::ID optionID, const ArgList &args, StringRef extraEntry) const { auto linkPathOptions = args.filtered(optionID); if (linkPathOptions.begin() == linkPathOptions.end() && extraEntry.empty()) return; @@ -1057,12 +984,31 @@ static void addPathEnvironmentVariableIfNeeded(Job::EnvironmentVector &env, env.emplace_back(name, args.MakeArgString(newPaths)); } +void ToolChain::addLinkRuntimeLib(const ArgList &Args, ArgStringList &Arguments, + StringRef LibName) const { + SmallString<128> P; + getClangLibraryPath(Args, P); + llvm::sys::path::append(P, LibName); + Arguments.push_back(Args.MakeArgString(P)); +} + +void ToolChain::getClangLibraryPath(const ArgList &Args, + SmallString<128> &LibPath) const { + const llvm::Triple &T = getTriple(); + + getRuntimeLibraryPath(LibPath, Args, /*Shared=*/true); + // Remove platform name. + llvm::sys::path::remove_filename(LibPath); + llvm::sys::path::append(LibPath, "clang", "lib", + T.isOSDarwin() ? "darwin" + : getPlatformNameForTriple(T)); +} + /// Get the runtime library link path, which is platform-specific and found /// relative to the compiler. -static void getRuntimeLibraryPath(SmallVectorImpl &runtimeLibPath, - const llvm::opt::ArgList &args, - const ToolChain &TC, - bool shared) { +void ToolChain::getRuntimeLibraryPath(SmallVectorImpl &runtimeLibPath, + 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. @@ -1070,734 +1016,23 @@ static void getRuntimeLibraryPath(SmallVectorImpl &runtimeLibPath, StringRef value = A->getValue(); runtimeLibPath.append(value.begin(), value.end()); } else { - auto programPath = TC.getDriver().getSwiftProgramPath(); + auto programPath = getDriver().getSwiftProgramPath(); runtimeLibPath.append(programPath.begin(), programPath.end()); llvm::sys::path::remove_filename(runtimeLibPath); // remove /swift llvm::sys::path::remove_filename(runtimeLibPath); // remove /bin - llvm::sys::path::append(runtimeLibPath, "lib", shared ? "swift" : "swift_static"); + llvm::sys::path::append(runtimeLibPath, "lib", + shared ? "swift" : "swift_static"); } llvm::sys::path::append(runtimeLibPath, - getPlatformNameForTriple(TC.getTriple())); -} - -static void getClangLibraryPath(const ToolChain &TC, const ArgList &Args, - SmallString<128> &LibPath) { - const llvm::Triple &T = TC.getTriple(); - - getRuntimeLibraryPath(LibPath, Args, TC, /*Shared=*/ true); - // Remove platform name. - llvm::sys::path::remove_filename(LibPath); - llvm::sys::path::append(LibPath, "clang", "lib", - T.isOSDarwin() ? "darwin" - : getPlatformNameForTriple(T)); -} - -ToolChain::InvocationInfo -toolchains::Darwin::constructInvocation(const InterpretJobAction &job, - const JobContext &context) const { - InvocationInfo II = ToolChain::constructInvocation(job, context); - - SmallString<128> runtimeLibraryPath; - getRuntimeLibraryPath(runtimeLibraryPath, context.Args, *this, /*Shared=*/ true); - - addPathEnvironmentVariableIfNeeded(II.ExtraEnvironment, "DYLD_LIBRARY_PATH", - ":", options::OPT_L, context.Args, - runtimeLibraryPath); - addPathEnvironmentVariableIfNeeded(II.ExtraEnvironment, "DYLD_FRAMEWORK_PATH", - ":", options::OPT_F, context.Args); - // FIXME: Add options::OPT_Fsystem paths to DYLD_FRAMEWORK_PATH as well. - return II; + getPlatformNameForTriple(getTriple())); } -static StringRef -getDarwinLibraryNameSuffixForTriple(const llvm::Triple &triple) { - switch (getDarwinPlatformKind(triple)) { - case DarwinPlatformKind::MacOS: - return "osx"; - case DarwinPlatformKind::IPhoneOS: - return "ios"; - case DarwinPlatformKind::IPhoneOSSimulator: - return "iossim"; - case DarwinPlatformKind::TvOS: - return "tvos"; - case DarwinPlatformKind::TvOSSimulator: - return "tvossim"; - case DarwinPlatformKind::WatchOS: - return "watchos"; - case DarwinPlatformKind::WatchOSSimulator: - return "watchossim"; - } - llvm_unreachable("Unsupported Darwin platform"); -} - -static std::string -getSanitizerRuntimeLibNameForDarwin(StringRef Sanitizer, - const llvm::Triple &Triple, - bool shared = true) { - return (Twine("libclang_rt.") - + Sanitizer + "_" - + getDarwinLibraryNameSuffixForTriple(Triple) - + (shared ? "_dynamic.dylib" : ".a")).str(); -} - -static std::string -getSanitizerRuntimeLibNameForLinux(StringRef Sanitizer, const llvm::Triple &Triple) { - return (Twine("libclang_rt.") + Sanitizer + "-" + - Triple.getArchName() + ".a").str(); -} - -bool toolchains::Darwin::sanitizerRuntimeLibExists( - const ArgList &args, StringRef sanitizer, bool shared) const { +bool ToolChain::sanitizerRuntimeLibExists(const ArgList &args, + StringRef sanitizerName, + bool shared) const { SmallString<128> sanitizerLibPath; - getClangLibraryPath(*this, args, sanitizerLibPath); + getClangLibraryPath(args, sanitizerLibPath); llvm::sys::path::append(sanitizerLibPath, - getSanitizerRuntimeLibNameForDarwin( - sanitizer, this->getTriple(), shared)); + sanitizerRuntimeLibName(sanitizerName, shared)); return llvm::sys::fs::exists(sanitizerLibPath.str()); } - -bool toolchains::GenericUnix::sanitizerRuntimeLibExists( - const ArgList &args, StringRef sanitizer, bool shared) const { - SmallString<128> sanitizerLibPath; - getClangLibraryPath(*this, args, sanitizerLibPath); - - // All libraries are static for linux. - llvm::sys::path::append(sanitizerLibPath, - getSanitizerRuntimeLibNameForLinux(sanitizer, this->getTriple())); - return llvm::sys::fs::exists(sanitizerLibPath.str()); -} - - -static void -addLinkRuntimeLibForDarwin(const ArgList &Args, ArgStringList &Arguments, - StringRef DarwinLibName, bool AddRPath, - const ToolChain &TC) { - SmallString<128> ClangLibraryPath; - getClangLibraryPath(TC, Args, ClangLibraryPath); - - SmallString<128> P(ClangLibraryPath); - llvm::sys::path::append(P, DarwinLibName); - Arguments.push_back(Args.MakeArgString(P)); - - // Adding the rpaths might negatively interact when other rpaths are involved, - // so we should make sure we add the rpaths last, after all user-specified - // rpaths. This is currently true from this place, but we need to be - // careful if this function is ever called before user's rpaths are emitted. - if (AddRPath) { - assert(DarwinLibName.endswith(".dylib") && "must be a dynamic library"); - - // Add @executable_path to rpath to support having the dylib copied with - // the executable. - Arguments.push_back("-rpath"); - Arguments.push_back("@executable_path"); - - // Add the path to the resource dir to rpath to support using the dylib - // from the default location without copying. - Arguments.push_back("-rpath"); - Arguments.push_back(Args.MakeArgString(ClangLibraryPath)); - } -} - -static void -addLinkRuntimeLibForLinux(const ArgList &Args, ArgStringList &Arguments, - StringRef LinuxLibName, - const ToolChain &TC) { - SmallString<128> Dir; - getRuntimeLibraryPath(Dir, Args, TC, /*Shared=*/ true); - // Remove platform name. - llvm::sys::path::remove_filename(Dir); - llvm::sys::path::append(Dir, "clang", "lib", "linux"); - SmallString<128> P(Dir); - llvm::sys::path::append(P, LinuxLibName); - Arguments.push_back(Args.MakeArgString(P)); -} - -static void -addLinkSanitizerLibArgsForDarwin(const ArgList &Args, - ArgStringList &Arguments, - StringRef Sanitizer, - const ToolChain &TC, - bool shared = true - ) { - // Sanitizer runtime libraries requires C++. - Arguments.push_back("-lc++"); - // Add explicit dependency on -lc++abi, as -lc++ doesn't re-export - // all RTTI-related symbols that are used. - Arguments.push_back("-lc++abi"); - - addLinkRuntimeLibForDarwin(Args, Arguments, - getSanitizerRuntimeLibNameForDarwin(Sanitizer, TC.getTriple(), shared), - /*AddRPath=*/ shared, TC); -} - -static void -addLinkSanitizerLibArgsForLinux(const ArgList &Args, - ArgStringList &Arguments, - StringRef Sanitizer, const ToolChain &TC) { - addLinkRuntimeLibForLinux(Args, Arguments, - getSanitizerRuntimeLibNameForLinux(Sanitizer, TC.getTriple()), TC); - - // Code taken from - // https://github.com/apple/swift-clang/blob/ab3cbe7/lib/Driver/Tools.cpp#L3264-L3276 - // There's no libpthread or librt on RTEMS. - if (TC.getTriple().getOS() != llvm::Triple::RTEMS) { - Arguments.push_back("-lpthread"); - Arguments.push_back("-lrt"); - } - Arguments.push_back("-lm"); - - // There's no libdl on FreeBSD or RTEMS. - if (TC.getTriple().getOS() != llvm::Triple::FreeBSD && - TC.getTriple().getOS() != llvm::Triple::RTEMS) - Arguments.push_back("-ldl"); -} - -ToolChain::InvocationInfo -toolchains::Darwin::constructInvocation(const LinkJobAction &job, - const JobContext &context) const { - assert(context.Output.getPrimaryOutputType() == file_types::TY_Image && - "Invalid linker output type."); - - if (context.Args.hasFlag(options::OPT_static_executable, - options::OPT_no_static_executable, - false)) { - llvm::report_fatal_error("-static-executable is not supported on Darwin"); - } - - const Driver &D = getDriver(); - const llvm::Triple &Triple = getTriple(); - - // Configure the toolchain. - // By default, use the system `ld` to link. - const char *LD = "ld"; - if (const Arg *A = context.Args.getLastArg(options::OPT_tools_directory)) { - StringRef toolchainPath(A->getValue()); - - // If there is a 'ld' in the toolchain folder, use that instead. - if (auto toolchainLD = llvm::sys::findProgramByName("ld", {toolchainPath})) { - LD = context.Args.MakeArgString(toolchainLD.get()); - } - } - - InvocationInfo II = {LD}; - ArgStringList &Arguments = II.Arguments; - - if (context.shouldUseInputFileList()) { - Arguments.push_back("-filelist"); - Arguments.push_back(context.getTemporaryFilePath("inputs", "LinkFileList")); - II.FilelistInfos.push_back({Arguments.back(), file_types::TY_Object, - FilelistInfo::WhichFiles::Input}); - } else { - addPrimaryInputsOfType(Arguments, context.Inputs, context.Args, - file_types::TY_Object); - } - - addInputsOfType(Arguments, context.InputActions, file_types::TY_Object); - - if (context.OI.CompilerMode == OutputInfo::Mode::SingleCompile) - addInputsOfType(Arguments, context.Inputs, context.Args, - file_types::TY_SwiftModuleFile, "-add_ast_path"); - else - addPrimaryInputsOfType(Arguments, context.Inputs, context.Args, - file_types::TY_SwiftModuleFile, "-add_ast_path"); - - // Add all .swiftmodule file inputs as arguments, preceded by the - // "-add_ast_path" linker option. - addInputsOfType(Arguments, context.InputActions, - file_types::TY_SwiftModuleFile, "-add_ast_path"); - - switch (job.getKind()) { - case LinkKind::None: - llvm_unreachable("invalid link kind"); - case LinkKind::Executable: - // The default for ld; no extra flags necessary. - break; - case LinkKind::DynamicLibrary: - Arguments.push_back("-dylib"); - break; - } - - assert(Triple.isOSDarwin()); - - // FIXME: If we used Clang as a linker instead of going straight to ld, - // we wouldn't have to replicate Clang's logic here. - bool wantsObjCRuntime = false; - if (Triple.isiOS()) - wantsObjCRuntime = Triple.isOSVersionLT(9); - else if (Triple.isMacOSX()) - wantsObjCRuntime = Triple.isMacOSXVersionLT(10, 11); - - if (context.Args.hasFlag(options::OPT_link_objc_runtime, - options::OPT_no_link_objc_runtime, - /*Default=*/wantsObjCRuntime)) { - llvm::SmallString<128> ARCLiteLib(D.getSwiftProgramPath()); - llvm::sys::path::remove_filename(ARCLiteLib); // 'swift' - llvm::sys::path::remove_filename(ARCLiteLib); // 'bin' - llvm::sys::path::append(ARCLiteLib, "lib", "arc"); - - if (!llvm::sys::fs::is_directory(ARCLiteLib)) { - // If we don't have a 'lib/arc/' directory, find the "arclite" library - // relative to the Clang in the active Xcode. - ARCLiteLib.clear(); - if (findXcodeClangPath(ARCLiteLib)) { - llvm::sys::path::remove_filename(ARCLiteLib); // 'clang' - llvm::sys::path::remove_filename(ARCLiteLib); // 'bin' - llvm::sys::path::append(ARCLiteLib, "lib", "arc"); - } - } - - if (!ARCLiteLib.empty()) { - llvm::sys::path::append(ARCLiteLib, "libarclite_"); - ARCLiteLib += getPlatformNameForTriple(Triple); - ARCLiteLib += ".a"; - - Arguments.push_back("-force_load"); - Arguments.push_back(context.Args.MakeArgString(ARCLiteLib)); - - // Arclite depends on CoreFoundation. - Arguments.push_back("-framework"); - Arguments.push_back("CoreFoundation"); - } else { - // FIXME: We should probably diagnose this, but this is not a place where - // we can emit diagnostics. Silently ignore it for now. - } - } - - context.Args.AddAllArgValues(Arguments, options::OPT_Xlinker); - context.Args.AddAllArgs(Arguments, options::OPT_linker_option_Group); - for (const Arg *arg : context.Args.filtered(options::OPT_F, - options::OPT_Fsystem)) { - Arguments.push_back("-F"); - Arguments.push_back(arg->getValue()); - } - - if (context.Args.hasArg(options::OPT_enable_app_extension)) { - // Keep this string fixed in case the option used by the - // compiler itself changes. - Arguments.push_back("-application_extension"); - } - - // Linking sanitizers will add rpaths, which might negatively interact when - // other rpaths are involved, so we should make sure we add the rpaths after - // all user-specified rpaths. - if (context.OI.SelectedSanitizers & SanitizerKind::Address) - addLinkSanitizerLibArgsForDarwin(context.Args, Arguments, "asan", *this); - - if (context.OI.SelectedSanitizers & SanitizerKind::Thread) - addLinkSanitizerLibArgsForDarwin(context.Args, Arguments, "tsan", *this); - - // Only link in libFuzzer for executables. - if (job.getKind() == LinkKind::Executable && - (context.OI.SelectedSanitizers & SanitizerKind::Fuzzer)) - addLinkSanitizerLibArgsForDarwin( - context.Args, Arguments, "fuzzer", *this, /*shared=*/false); - - if (context.Args.hasArg(options::OPT_embed_bitcode, - options::OPT_embed_bitcode_marker)) { - Arguments.push_back("-bitcode_bundle"); - } - - if (!context.OI.SDKPath.empty()) { - Arguments.push_back("-syslibroot"); - Arguments.push_back(context.Args.MakeArgString(context.OI.SDKPath)); - } - - Arguments.push_back("-lobjc"); - Arguments.push_back("-lSystem"); - - Arguments.push_back("-arch"); - Arguments.push_back(context.Args.MakeArgString(getTriple().getArchName())); - - // Add the runtime library link path, which is platform-specific and found - // relative to the compiler. - SmallString<128> RuntimeLibPath; - getRuntimeLibraryPath(RuntimeLibPath, context.Args, *this, /*Shared=*/ true); - - // Link the standard library. - Arguments.push_back("-L"); - if (context.Args.hasFlag(options::OPT_static_stdlib, - options::OPT_no_static_stdlib, - false)) { - SmallString<128> StaticRuntimeLibPath; - getRuntimeLibraryPath(StaticRuntimeLibPath, context.Args, *this, /*Shared=*/ false); - Arguments.push_back(context.Args.MakeArgString(StaticRuntimeLibPath)); - Arguments.push_back("-lc++"); - Arguments.push_back("-framework"); - Arguments.push_back("Foundation"); - Arguments.push_back("-force_load_swift_libs"); - } else { - Arguments.push_back(context.Args.MakeArgString(RuntimeLibPath)); - // FIXME: We probably shouldn't be adding an rpath here unless we know ahead - // of time the standard library won't be copied. SR-1967 - Arguments.push_back("-rpath"); - Arguments.push_back(context.Args.MakeArgString(RuntimeLibPath)); - } - - if (context.Args.hasArg(options::OPT_profile_generate)) { - SmallString<128> LibProfile(RuntimeLibPath); - llvm::sys::path::remove_filename(LibProfile); // remove platform name - llvm::sys::path::append(LibProfile, "clang", "lib", "darwin"); - - StringRef RT; - if (Triple.isiOS()) { - if (Triple.isTvOS()) - RT = "tvos"; - else - RT = "ios"; - } else if (Triple.isWatchOS()) { - RT = "watchos"; - } else { - assert(Triple.isMacOSX()); - RT = "osx"; - } - - StringRef Sim; - if (tripleIsAnySimulator(Triple)) { - Sim = "sim"; - } - - llvm::sys::path::append(LibProfile, - "libclang_rt.profile_" + RT + Sim + ".a"); - - // FIXME: Continue accepting the old path for simulator libraries for now. - if (!Sim.empty() && !llvm::sys::fs::exists(LibProfile)) { - llvm::sys::path::remove_filename(LibProfile); - llvm::sys::path::append(LibProfile, - "libclang_rt.profile_" + RT + ".a"); - } - - Arguments.push_back(context.Args.MakeArgString(LibProfile)); - } - - // FIXME: Properly handle deployment targets. - assert(Triple.isiOS() || Triple.isWatchOS() || Triple.isMacOSX()); - if (Triple.isiOS()) { - bool isiOSSimulator = tripleIsiOSSimulator(Triple); - if (Triple.isTvOS()) { - if (isiOSSimulator) - Arguments.push_back("-tvos_simulator_version_min"); - else - Arguments.push_back("-tvos_version_min"); - } else { - if (isiOSSimulator) - Arguments.push_back("-ios_simulator_version_min"); - else - Arguments.push_back("-iphoneos_version_min"); - } - unsigned major, minor, micro; - Triple.getiOSVersion(major, minor, micro); - addVersionString(context.Args, Arguments, major, minor, micro); - } else if (Triple.isWatchOS()) { - if (tripleIsWatchSimulator(Triple)) - Arguments.push_back("-watchos_simulator_version_min"); - else - Arguments.push_back("-watchos_version_min"); - unsigned major, minor, micro; - Triple.getOSVersion(major, minor, micro); - addVersionString(context.Args, Arguments, major, minor, micro); - } else { - Arguments.push_back("-macosx_version_min"); - unsigned major, minor, micro; - Triple.getMacOSXVersion(major, minor, micro); - addVersionString(context.Args, Arguments, major, minor, micro); - } - - Arguments.push_back("-no_objc_category_merging"); - - // This should be the last option, for convenience in checking output. - Arguments.push_back("-o"); - Arguments.push_back(context.Args.MakeArgString( - context.Output.getPrimaryOutputFilename())); - - return II; -} - -ToolChain::InvocationInfo -toolchains::GenericUnix::constructInvocation(const InterpretJobAction &job, - const JobContext &context) const { - InvocationInfo II = ToolChain::constructInvocation(job, context); - - SmallString<128> runtimeLibraryPath; - getRuntimeLibraryPath(runtimeLibraryPath, context.Args, *this, /*Shared=*/ true); - - addPathEnvironmentVariableIfNeeded(II.ExtraEnvironment, "LD_LIBRARY_PATH", - ":", options::OPT_L, context.Args, - runtimeLibraryPath); - return II; -} - - -ToolChain::InvocationInfo -toolchains::GenericUnix::constructInvocation(const AutolinkExtractJobAction &job, - const JobContext &context) const { - assert(context.Output.getPrimaryOutputType() == file_types::TY_AutolinkFile); - - ArgStringList Arguments; - addPrimaryInputsOfType(Arguments, context.Inputs, context.Args, - file_types::TY_Object); - addInputsOfType(Arguments, context.InputActions, file_types::TY_Object); - - Arguments.push_back("-o"); - Arguments.push_back(context.Args.MakeArgString( - context.Output.getPrimaryOutputFilename())); - - return {"swift-autolink-extract", Arguments}; -} - -std::string toolchains::GenericUnix::getDefaultLinker() const { - switch(getTriple().getArch()) { - case llvm::Triple::arm: - case llvm::Triple::armeb: - case llvm::Triple::thumb: - case llvm::Triple::thumbeb: - // BFD linker has issues wrt relocation of the protocol conformance - // section on these targets, it also generates COPY relocations for - // final executables, as such, unless specified, we default to gold - // linker. - return "gold"; - case llvm::Triple::x86_64: - case llvm::Triple::ppc64: - case llvm::Triple::ppc64le: - case llvm::Triple::systemz: - // BFD linker has issues wrt relocations against protected symbols. - return "gold"; - default: - // Otherwise, use the default BFD linker. - return ""; - } -} - -std::string toolchains::GenericUnix::getTargetForLinker() const { - return getTriple().str(); -} - -bool toolchains::GenericUnix::shouldProvideRPathToLinker() const { - return true; -} - -ToolChain::InvocationInfo -toolchains::GenericUnix::constructInvocation(const LinkJobAction &job, - const JobContext &context) const { - assert(context.Output.getPrimaryOutputType() == file_types::TY_Image && - "Invalid linker output type."); - - ArgStringList Arguments; - - switch (job.getKind()) { - case LinkKind::None: - llvm_unreachable("invalid link kind"); - case LinkKind::Executable: - // Default case, nothing extra needed. - break; - case LinkKind::DynamicLibrary: - Arguments.push_back("-shared"); - break; - } - - // Select the linker to use. - std::string Linker; - if (const Arg *A = context.Args.getLastArg(options::OPT_use_ld)) { - Linker = A->getValue(); - } else { - Linker = getDefaultLinker(); - } - if (!Linker.empty()) { -#if defined(__HAIKU__) - // For now, passing -fuse-ld on Haiku doesn't work as swiftc doesn't recognise - // it. Passing -use-ld= as the argument works fine. - Arguments.push_back(context.Args.MakeArgString("-use-ld=" + Linker)); -#else - Arguments.push_back(context.Args.MakeArgString("-fuse-ld=" + Linker)); -#endif - } - - // Configure the toolchain. - // By default, use the system clang++ to link. - const char * Clang = "clang++"; - if (const Arg *A = context.Args.getLastArg(options::OPT_tools_directory)) { - StringRef toolchainPath(A->getValue()); - - // If there is a clang in the toolchain folder, use that instead. - if (auto toolchainClang = llvm::sys::findProgramByName("clang++", {toolchainPath})) { - Clang = context.Args.MakeArgString(toolchainClang.get()); - } - - // Look for binutils in the toolchain folder. - Arguments.push_back("-B"); - Arguments.push_back(context.Args.MakeArgString(A->getValue())); - } - - if (getTriple().getOS() == llvm::Triple::Linux && - job.getKind() == LinkKind::Executable) { - Arguments.push_back("-pie"); - } - - std::string Target = getTargetForLinker(); - if (!Target.empty()) { - Arguments.push_back("-target"); - Arguments.push_back(context.Args.MakeArgString(Target)); - } - - bool staticExecutable = false; - bool staticStdlib = false; - - if (context.Args.hasFlag(options::OPT_static_executable, - options::OPT_no_static_executable, - false)) { - staticExecutable = true; - } else if (context.Args.hasFlag(options::OPT_static_stdlib, - options::OPT_no_static_stdlib, - false)) { - staticStdlib = true; - } - - SmallString<128> SharedRuntimeLibPath; - getRuntimeLibraryPath(SharedRuntimeLibPath, context.Args, *this, /*Shared=*/ true); - - SmallString<128> StaticRuntimeLibPath; - getRuntimeLibraryPath(StaticRuntimeLibPath, context.Args, *this, /*Shared=*/ false); - - // Add the runtime library link path, which is platform-specific and found - // relative to the compiler. - if (!(staticExecutable || staticStdlib) && shouldProvideRPathToLinker()) { - // FIXME: We probably shouldn't be adding an rpath here unless we know - // ahead of time the standard library won't be copied. - Arguments.push_back("-Xlinker"); - Arguments.push_back("-rpath"); - Arguments.push_back("-Xlinker"); - Arguments.push_back(context.Args.MakeArgString(SharedRuntimeLibPath)); - } - - SmallString<128> swiftrtPath = SharedRuntimeLibPath; - llvm::sys::path::append(swiftrtPath, - swift::getMajorArchitectureName(getTriple())); - llvm::sys::path::append(swiftrtPath, "swiftrt.o"); - Arguments.push_back(context.Args.MakeArgString(swiftrtPath)); - - addPrimaryInputsOfType(Arguments, context.Inputs, context.Args, - file_types::TY_Object); - addInputsOfType(Arguments, context.InputActions, file_types::TY_Object); - - for (const Arg *arg : context.Args.filtered(options::OPT_F, - options::OPT_Fsystem)) { - if (arg->getOption().matches(options::OPT_Fsystem)) - Arguments.push_back("-iframework"); - else - Arguments.push_back(context.Args.MakeArgString(arg->getSpelling())); - Arguments.push_back(arg->getValue()); - } - - if (!context.OI.SDKPath.empty()) { - Arguments.push_back("--sysroot"); - Arguments.push_back(context.Args.MakeArgString(context.OI.SDKPath)); - } - - // Add any autolinking scripts to the arguments - for (const Job *Cmd : context.Inputs) { - auto &OutputInfo = Cmd->getOutput(); - if (OutputInfo.getPrimaryOutputType() == file_types::TY_AutolinkFile) - Arguments.push_back(context.Args.MakeArgString( - Twine("@") + OutputInfo.getPrimaryOutputFilename())); - } - - // Link the standard library. - Arguments.push_back("-L"); - - if (staticExecutable) { - Arguments.push_back(context.Args.MakeArgString(StaticRuntimeLibPath)); - - SmallString<128> linkFilePath = StaticRuntimeLibPath; - llvm::sys::path::append(linkFilePath, "static-executable-args.lnk"); - auto linkFile = linkFilePath.str(); - - if (llvm::sys::fs::is_regular_file(linkFile)) { - Arguments.push_back(context.Args.MakeArgString(Twine("@") + linkFile)); - } else { - llvm::report_fatal_error("-static-executable not supported on this platform"); - } - } - else if (staticStdlib) { - Arguments.push_back(context.Args.MakeArgString(StaticRuntimeLibPath)); - - SmallString<128> linkFilePath = StaticRuntimeLibPath; - llvm::sys::path::append(linkFilePath, "static-stdlib-args.lnk"); - auto linkFile = linkFilePath.str(); - if (llvm::sys::fs::is_regular_file(linkFile)) { - Arguments.push_back(context.Args.MakeArgString(Twine("@") + linkFile)); - } else { - llvm::report_fatal_error(linkFile + " not found"); - } - } - else { - Arguments.push_back(context.Args.MakeArgString(SharedRuntimeLibPath)); - Arguments.push_back("-lswiftCore"); - } - - // Explicitly pass the target to the linker - Arguments.push_back(context.Args.MakeArgString("--target=" + getTriple().str())); - - if (getTriple().getOS() == llvm::Triple::Linux) { - //Make sure we only add SanitizerLibs for executables - if (job.getKind() == LinkKind::Executable) { - if (context.OI.SelectedSanitizers & SanitizerKind::Address) - addLinkSanitizerLibArgsForLinux(context.Args, Arguments, "asan", *this); - - if (context.OI.SelectedSanitizers & SanitizerKind::Thread) - addLinkSanitizerLibArgsForLinux(context.Args, Arguments, "tsan", *this); - - if (context.OI.SelectedSanitizers & SanitizerKind::Fuzzer) - addLinkRuntimeLibForLinux(context.Args, Arguments, - getSanitizerRuntimeLibNameForLinux( - "fuzzer", this->getTriple()), *this); - } - } - - if (context.Args.hasArg(options::OPT_profile_generate)) { - SmallString<128> LibProfile(SharedRuntimeLibPath); - llvm::sys::path::remove_filename(LibProfile); // remove platform name - llvm::sys::path::append(LibProfile, "clang", "lib"); - - llvm::sys::path::append(LibProfile, getTriple().getOSName(), - Twine("libclang_rt.profile-") + - getTriple().getArchName() + - ".a"); - Arguments.push_back(context.Args.MakeArgString(LibProfile)); - Arguments.push_back(context.Args.MakeArgString( - Twine("-u", llvm::getInstrProfRuntimeHookVarName()))); - } - - context.Args.AddAllArgs(Arguments, options::OPT_Xlinker); - context.Args.AddAllArgs(Arguments, options::OPT_linker_option_Group); - - // This should be the last option, for convenience in checking output. - Arguments.push_back("-o"); - Arguments.push_back(context.Args.MakeArgString( - context.Output.getPrimaryOutputFilename())); - - return {Clang, Arguments}; -} - -std::string -toolchains::Android::getTargetForLinker() const { - // Explicitly set the linker target to "androideabi", as opposed to the - // llvm::Triple representation of "armv7-none-linux-android". - // This is the only ABI we currently support for Android. - assert( - getTriple().getArch() == llvm::Triple::arm && - getTriple().getSubArch() == llvm::Triple::SubArchType::ARMSubArch_v7 && - "Only armv7 targets are supported for Android"); - return "armv7-none-linux-androideabi"; -} - -bool toolchains::Android::shouldProvideRPathToLinker() const { - return false; -} - -std::string toolchains::Cygwin::getDefaultLinker() const { - // Cygwin uses the default BFD linker, even on ARM. - return ""; -} - -std::string toolchains::Cygwin::getTargetForLinker() const { - return ""; -} diff --git a/lib/Driver/ToolChains.h b/lib/Driver/ToolChains.h index 64f70386c40ec..0db33283c4090 100644 --- a/lib/Driver/ToolChains.h +++ b/lib/Driver/ToolChains.h @@ -33,10 +33,20 @@ class LLVM_LIBRARY_VISIBILITY Darwin : public ToolChain { public: Darwin(const Driver &D, const llvm::Triple &Triple) : ToolChain(D, Triple) {} ~Darwin() = default; - bool sanitizerRuntimeLibExists(const llvm::opt::ArgList &args, - StringRef sanitizerLibName, - bool shared) - const override; + std::string sanitizerRuntimeLibName(StringRef Sanitizer, + bool shared = true) const override; +}; + +class LLVM_LIBRARY_VISIBILITY Windows : public ToolChain { +protected: + InvocationInfo constructInvocation(const LinkJobAction &job, + const JobContext &context) const override; + +public: + Windows(const Driver &D, const llvm::Triple &Triple) : ToolChain(D, Triple) {} + ~Windows() = default; + std::string sanitizerRuntimeLibName(StringRef Sanitizer, + bool shared = true) const override; }; class LLVM_LIBRARY_VISIBILITY GenericUnix : public ToolChain { @@ -71,12 +81,11 @@ class LLVM_LIBRARY_VISIBILITY GenericUnix : public ToolChain { const JobContext &context) const override; public: - GenericUnix(const Driver &D, const llvm::Triple &Triple) : ToolChain(D, Triple) {} + GenericUnix(const Driver &D, const llvm::Triple &Triple) + : ToolChain(D, Triple) {} ~GenericUnix() = default; - bool sanitizerRuntimeLibExists(const llvm::opt::ArgList &args, - StringRef sanitizerLibName, - bool shared) - const override; + std::string sanitizerRuntimeLibName(StringRef Sanitizer, + bool shared = true) const override; }; class LLVM_LIBRARY_VISIBILITY Android : public GenericUnix { @@ -84,8 +93,10 @@ class LLVM_LIBRARY_VISIBILITY Android : public GenericUnix { std::string getTargetForLinker() const override; bool shouldProvideRPathToLinker() const override; + public: - Android(const Driver &D, const llvm::Triple &Triple) : GenericUnix(D, Triple) {} + Android(const Driver &D, const llvm::Triple &Triple) + : GenericUnix(D, Triple) {} ~Android() = default; }; @@ -96,7 +107,8 @@ class LLVM_LIBRARY_VISIBILITY Cygwin : public GenericUnix { std::string getTargetForLinker() const override; public: - Cygwin(const Driver &D, const llvm::Triple &Triple) : GenericUnix(D, Triple) {} + Cygwin(const Driver &D, const llvm::Triple &Triple) + : GenericUnix(D, Triple) {} ~Cygwin() = default; }; @@ -105,4 +117,3 @@ class LLVM_LIBRARY_VISIBILITY Cygwin : public GenericUnix { } // end namespace swift #endif - diff --git a/lib/Driver/UnixToolChains.cpp b/lib/Driver/UnixToolChains.cpp new file mode 100644 index 0000000000000..e738362f6b1c6 --- /dev/null +++ b/lib/Driver/UnixToolChains.cpp @@ -0,0 +1,348 @@ +//===------ UnixToolChains.cpp - Job invocations (non-Darwin Unix) --------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "ToolChains.h" + +#include "swift/Basic/Dwarf.h" +#include "swift/Basic/LLVM.h" +#include "swift/Basic/Platform.h" +#include "swift/Basic/Range.h" +#include "swift/Basic/TaskQueue.h" +#include "swift/Config.h" +#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" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/ProfileData/InstrProf.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/Program.h" + +using namespace swift; +using namespace swift::driver; +using namespace llvm::opt; + +static void addLinkSanitizerLibArgsForLinux(const ArgList &Args, + ArgStringList &Arguments, + StringRef Sanitizer, + const ToolChain &TC) { + TC.addLinkRuntimeLib(Args, Arguments, TC.sanitizerRuntimeLibName(Sanitizer)); + + // Code taken from + // https://github.com/apple/swift-clang/blob/ab3cbe7/lib/Driver/Tools.cpp#L3264-L3276 + // There's no libpthread or librt on RTEMS. + if (TC.getTriple().getOS() != llvm::Triple::RTEMS) { + Arguments.push_back("-lpthread"); + Arguments.push_back("-lrt"); + } + Arguments.push_back("-lm"); + + // There's no libdl on FreeBSD or RTEMS. + if (TC.getTriple().getOS() != llvm::Triple::FreeBSD && + TC.getTriple().getOS() != llvm::Triple::RTEMS) + Arguments.push_back("-ldl"); +} + +std::string +toolchains::GenericUnix::sanitizerRuntimeLibName(StringRef Sanitizer, + bool shared) const { + return (Twine("libclang_rt.") + Sanitizer + "-" + + this->getTriple().getArchName() + ".a") + .str(); +} + +ToolChain::InvocationInfo +toolchains::GenericUnix::constructInvocation(const InterpretJobAction &job, + const JobContext &context) const { + InvocationInfo II = ToolChain::constructInvocation(job, context); + + SmallString<128> runtimeLibraryPath; + getRuntimeLibraryPath(runtimeLibraryPath, context.Args, + /*Shared=*/true); + + addPathEnvironmentVariableIfNeeded(II.ExtraEnvironment, "LD_LIBRARY_PATH", + ":", options::OPT_L, context.Args, + runtimeLibraryPath); + return II; +} + +ToolChain::InvocationInfo toolchains::GenericUnix::constructInvocation( + const AutolinkExtractJobAction &job, const JobContext &context) const { + assert(context.Output.getPrimaryOutputType() == file_types::TY_AutolinkFile); + + ArgStringList Arguments; + addPrimaryInputsOfType(Arguments, context.Inputs, context.Args, + file_types::TY_Object); + addInputsOfType(Arguments, context.InputActions, file_types::TY_Object); + + Arguments.push_back("-o"); + Arguments.push_back( + context.Args.MakeArgString(context.Output.getPrimaryOutputFilename())); + + return {"swift-autolink-extract", Arguments}; +} + +std::string toolchains::GenericUnix::getDefaultLinker() const { + switch (getTriple().getArch()) { + case llvm::Triple::arm: + case llvm::Triple::armeb: + case llvm::Triple::thumb: + case llvm::Triple::thumbeb: + // BFD linker has issues wrt relocation of the protocol conformance + // section on these targets, it also generates COPY relocations for + // final executables, as such, unless specified, we default to gold + // linker. + return "gold"; + case llvm::Triple::x86_64: + case llvm::Triple::ppc64: + case llvm::Triple::ppc64le: + case llvm::Triple::systemz: + // BFD linker has issues wrt relocations against protected symbols. + return "gold"; + default: + // Otherwise, use the default BFD linker. + return ""; + } +} + +std::string toolchains::GenericUnix::getTargetForLinker() const { + return getTriple().str(); +} + +bool toolchains::GenericUnix::shouldProvideRPathToLinker() const { + return true; +} + +ToolChain::InvocationInfo +toolchains::GenericUnix::constructInvocation(const LinkJobAction &job, + const JobContext &context) const { + assert(context.Output.getPrimaryOutputType() == file_types::TY_Image && + "Invalid linker output type."); + + ArgStringList Arguments; + + switch (job.getKind()) { + case LinkKind::None: + llvm_unreachable("invalid link kind"); + case LinkKind::Executable: + // Default case, nothing extra needed. + break; + case LinkKind::DynamicLibrary: + Arguments.push_back("-shared"); + break; + } + + // Select the linker to use. + std::string Linker; + if (const Arg *A = context.Args.getLastArg(options::OPT_use_ld)) { + Linker = A->getValue(); + } else { + Linker = getDefaultLinker(); + } + if (!Linker.empty()) { +#if defined(__HAIKU__) + // For now, passing -fuse-ld on Haiku doesn't work as swiftc doesn't + // recognise it. Passing -use-ld= as the argument works fine. + Arguments.push_back(context.Args.MakeArgString("-use-ld=" + Linker)); +#else + Arguments.push_back(context.Args.MakeArgString("-fuse-ld=" + Linker)); +#endif + } + + // Configure the toolchain. + // By default, use the system clang++ to link. + const char *Clang = "clang++"; + if (const Arg *A = context.Args.getLastArg(options::OPT_tools_directory)) { + StringRef toolchainPath(A->getValue()); + + // If there is a clang in the toolchain folder, use that instead. + if (auto toolchainClang = + llvm::sys::findProgramByName("clang++", {toolchainPath})) { + Clang = context.Args.MakeArgString(toolchainClang.get()); + } + + // Look for binutils in the toolchain folder. + Arguments.push_back("-B"); + Arguments.push_back(context.Args.MakeArgString(A->getValue())); + } + + if (getTriple().getOS() == llvm::Triple::Linux && + job.getKind() == LinkKind::Executable) { + Arguments.push_back("-pie"); + } + + std::string Target = getTargetForLinker(); + if (!Target.empty()) { + Arguments.push_back("-target"); + Arguments.push_back(context.Args.MakeArgString(Target)); + } + + bool staticExecutable = false; + bool staticStdlib = false; + + if (context.Args.hasFlag(options::OPT_static_executable, + options::OPT_no_static_executable, false)) { + staticExecutable = true; + } else if (context.Args.hasFlag(options::OPT_static_stdlib, + options::OPT_no_static_stdlib, false)) { + staticStdlib = true; + } + + SmallString<128> SharedRuntimeLibPath; + getRuntimeLibraryPath(SharedRuntimeLibPath, context.Args, /*Shared=*/true); + + SmallString<128> StaticRuntimeLibPath; + getRuntimeLibraryPath(StaticRuntimeLibPath, context.Args, /*Shared=*/false); + + // Add the runtime library link path, which is platform-specific and found + // relative to the compiler. + if (!(staticExecutable || staticStdlib) && shouldProvideRPathToLinker()) { + // FIXME: We probably shouldn't be adding an rpath here unless we know + // ahead of time the standard library won't be copied. + Arguments.push_back("-Xlinker"); + Arguments.push_back("-rpath"); + Arguments.push_back("-Xlinker"); + Arguments.push_back(context.Args.MakeArgString(SharedRuntimeLibPath)); + } + + SmallString<128> swiftrtPath = SharedRuntimeLibPath; + llvm::sys::path::append(swiftrtPath, + swift::getMajorArchitectureName(getTriple())); + llvm::sys::path::append(swiftrtPath, "swiftrt.o"); + Arguments.push_back(context.Args.MakeArgString(swiftrtPath)); + + addPrimaryInputsOfType(Arguments, context.Inputs, context.Args, + file_types::TY_Object); + addInputsOfType(Arguments, context.InputActions, file_types::TY_Object); + + for (const Arg *arg : + context.Args.filtered(options::OPT_F, options::OPT_Fsystem)) { + if (arg->getOption().matches(options::OPT_Fsystem)) + Arguments.push_back("-iframework"); + else + Arguments.push_back(context.Args.MakeArgString(arg->getSpelling())); + Arguments.push_back(arg->getValue()); + } + + if (!context.OI.SDKPath.empty()) { + Arguments.push_back("--sysroot"); + Arguments.push_back(context.Args.MakeArgString(context.OI.SDKPath)); + } + + // Add any autolinking scripts to the arguments + for (const Job *Cmd : context.Inputs) { + auto &OutputInfo = Cmd->getOutput(); + if (OutputInfo.getPrimaryOutputType() == file_types::TY_AutolinkFile) + Arguments.push_back(context.Args.MakeArgString( + Twine("@") + OutputInfo.getPrimaryOutputFilename())); + } + + // Link the standard library. + Arguments.push_back("-L"); + + if (staticExecutable) { + Arguments.push_back(context.Args.MakeArgString(StaticRuntimeLibPath)); + + SmallString<128> linkFilePath = StaticRuntimeLibPath; + llvm::sys::path::append(linkFilePath, "static-executable-args.lnk"); + auto linkFile = linkFilePath.str(); + + if (llvm::sys::fs::is_regular_file(linkFile)) { + Arguments.push_back(context.Args.MakeArgString(Twine("@") + linkFile)); + } else { + llvm::report_fatal_error( + "-static-executable not supported on this platform"); + } + } else if (staticStdlib) { + Arguments.push_back(context.Args.MakeArgString(StaticRuntimeLibPath)); + + SmallString<128> linkFilePath = StaticRuntimeLibPath; + llvm::sys::path::append(linkFilePath, "static-stdlib-args.lnk"); + auto linkFile = linkFilePath.str(); + if (llvm::sys::fs::is_regular_file(linkFile)) { + Arguments.push_back(context.Args.MakeArgString(Twine("@") + linkFile)); + } else { + llvm::report_fatal_error(linkFile + " not found"); + } + } else { + Arguments.push_back(context.Args.MakeArgString(SharedRuntimeLibPath)); + Arguments.push_back("-lswiftCore"); + } + + // Explicitly pass the target to the linker + Arguments.push_back( + context.Args.MakeArgString("--target=" + getTriple().str())); + + if (getTriple().getOS() == llvm::Triple::Linux) { + // Make sure we only add SanitizerLibs for executables + if (job.getKind() == LinkKind::Executable) { + if (context.OI.SelectedSanitizers & SanitizerKind::Address) + addLinkSanitizerLibArgsForLinux(context.Args, Arguments, "asan", *this); + + if (context.OI.SelectedSanitizers & SanitizerKind::Thread) + addLinkSanitizerLibArgsForLinux(context.Args, Arguments, "tsan", *this); + + if (context.OI.SelectedSanitizers & SanitizerKind::Fuzzer) + addLinkRuntimeLib(context.Args, Arguments, + sanitizerRuntimeLibName("fuzzer")); + } + } + + if (context.Args.hasArg(options::OPT_profile_generate)) { + SmallString<128> LibProfile(SharedRuntimeLibPath); + llvm::sys::path::remove_filename(LibProfile); // remove platform name + llvm::sys::path::append(LibProfile, "clang", "lib"); + + llvm::sys::path::append(LibProfile, getTriple().getOSName(), + Twine("libclang_rt.profile-") + + getTriple().getArchName() + ".a"); + Arguments.push_back(context.Args.MakeArgString(LibProfile)); + Arguments.push_back(context.Args.MakeArgString( + Twine("-u", llvm::getInstrProfRuntimeHookVarName()))); + } + + context.Args.AddAllArgs(Arguments, options::OPT_Xlinker); + context.Args.AddAllArgs(Arguments, options::OPT_linker_option_Group); + + // This should be the last option, for convenience in checking output. + Arguments.push_back("-o"); + Arguments.push_back( + context.Args.MakeArgString(context.Output.getPrimaryOutputFilename())); + + return {Clang, Arguments}; +} + +std::string toolchains::Android::getTargetForLinker() const { + // Explicitly set the linker target to "androideabi", as opposed to the + // llvm::Triple representation of "armv7-none-linux-android". + // This is the only ABI we currently support for Android. + assert(getTriple().getArch() == llvm::Triple::arm && + getTriple().getSubArch() == llvm::Triple::SubArchType::ARMSubArch_v7 && + "Only armv7 targets are supported for Android"); + return "armv7-none-linux-androideabi"; +} + +bool toolchains::Android::shouldProvideRPathToLinker() const { return false; } + +std::string toolchains::Cygwin::getDefaultLinker() const { + // Cygwin uses the default BFD linker, even on ARM. + return ""; +} + +std::string toolchains::Cygwin::getTargetForLinker() const { return ""; } diff --git a/lib/Driver/WindowsToolChains.cpp b/lib/Driver/WindowsToolChains.cpp new file mode 100644 index 0000000000000..8318d60969659 --- /dev/null +++ b/lib/Driver/WindowsToolChains.cpp @@ -0,0 +1,172 @@ +//===---- WindowsToolChains.cpp - Job invocations (Windows-specific) ------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "ToolChains.h" + +#include "swift/Basic/Dwarf.h" +#include "swift/Basic/LLVM.h" +#include "swift/Basic/Platform.h" +#include "swift/Basic/Range.h" +#include "swift/Basic/TaskQueue.h" +#include "swift/Config.h" +#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" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/ProfileData/InstrProf.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/Program.h" + +using namespace swift; +using namespace swift::driver; +using namespace llvm::opt; + +std::string toolchains::Windows::sanitizerRuntimeLibName(StringRef Sanitizer, + bool shared) const { + return (Twine("clang_rt.") + Sanitizer + "-" + + this->getTriple().getArchName() + ".lib") + .str(); +} + +ToolChain::InvocationInfo +toolchains::Windows::constructInvocation(const LinkJobAction &job, + const JobContext &context) const { + assert(context.Output.getPrimaryOutputType() == file_types::TY_Image && + "Invalid linker output type."); + + ArgStringList Arguments; + + switch (job.getKind()) { + case LinkKind::None: + llvm_unreachable("invalid link kind"); + case LinkKind::Executable: + // Default case, nothing extra needed. + break; + case LinkKind::DynamicLibrary: + Arguments.push_back("-shared"); + break; + } + + // Select the linker to use. + std::string Linker; + if (const Arg *A = context.Args.getLastArg(options::OPT_use_ld)) { + Linker = A->getValue(); + } + if (!Linker.empty()) + Arguments.push_back(context.Args.MakeArgString("-fuse-ld=" + Linker)); + + // Configure the toolchain. + // By default, use the system clang++ to link. + const char *Clang = nullptr; + if (const Arg *A = context.Args.getLastArg(options::OPT_tools_directory)) { + StringRef toolchainPath(A->getValue()); + + // If there is a clang in the toolchain folder, use that instead. + if (auto toolchainClang = + llvm::sys::findProgramByName("clang++", {toolchainPath})) + Clang = context.Args.MakeArgString(toolchainClang.get()); + } + if (Clang == nullptr) { + if (auto pathClang = llvm::sys::findProgramByName("clang++", None)) + Clang = context.Args.MakeArgString(pathClang.get()); + } + assert(Clang && + "clang++ was not found in the toolchain directory or system path."); + + std::string Target = getTriple().str(); + if (!Target.empty()) { + Arguments.push_back("-target"); + Arguments.push_back(context.Args.MakeArgString(Target)); + } + + SmallString<128> SharedRuntimeLibPath; + getRuntimeLibraryPath(SharedRuntimeLibPath, context.Args, + /*Shared=*/true); + + // Link the standard library. + Arguments.push_back("-L"); + if (context.Args.hasFlag(options::OPT_static_stdlib, + options::OPT_no_static_stdlib, false)) { + SmallString<128> StaticRuntimeLibPath; + getRuntimeLibraryPath(StaticRuntimeLibPath, context.Args, + /*Shared=*/false); + + // Since Windows has separate libraries per architecture, link against the + // architecture specific version of the static library. + Arguments.push_back(context.Args.MakeArgString(StaticRuntimeLibPath + "/" + + getTriple().getArchName())); + } else { + Arguments.push_back(context.Args.MakeArgString(SharedRuntimeLibPath + "/" + + getTriple().getArchName())); + } + + SmallString<128> swiftrtPath = SharedRuntimeLibPath; + llvm::sys::path::append(swiftrtPath, + swift::getMajorArchitectureName(getTriple())); + llvm::sys::path::append(swiftrtPath, "swiftrt.o"); + Arguments.push_back(context.Args.MakeArgString(swiftrtPath)); + + addPrimaryInputsOfType(Arguments, context.Inputs, context.Args, + file_types::TY_Object); + addInputsOfType(Arguments, context.InputActions, file_types::TY_Object); + + for (const Arg *arg : + context.Args.filtered(options::OPT_F, options::OPT_Fsystem)) { + if (arg->getOption().matches(options::OPT_Fsystem)) + Arguments.push_back("-iframework"); + else + Arguments.push_back(context.Args.MakeArgString(arg->getSpelling())); + Arguments.push_back(arg->getValue()); + } + + if (!context.OI.SDKPath.empty()) { + Arguments.push_back("-I"); + Arguments.push_back(context.Args.MakeArgString(context.OI.SDKPath)); + } + + if (job.getKind() == LinkKind::Executable) { + if (context.OI.SelectedSanitizers & SanitizerKind::Address) + addLinkRuntimeLib(context.Args, Arguments, + sanitizerRuntimeLibName("asan")); + } + + if (context.Args.hasArg(options::OPT_profile_generate)) { + SmallString<128> LibProfile(SharedRuntimeLibPath); + llvm::sys::path::remove_filename(LibProfile); // remove platform name + llvm::sys::path::append(LibProfile, "clang", "lib"); + + llvm::sys::path::append(LibProfile, getTriple().getOSName(), + Twine("clang_rt.profile-") + + getTriple().getArchName() + ".lib"); + Arguments.push_back(context.Args.MakeArgString(LibProfile)); + Arguments.push_back(context.Args.MakeArgString( + Twine("-u", llvm::getInstrProfRuntimeHookVarName()))); + } + + context.Args.AddAllArgs(Arguments, options::OPT_Xlinker); + context.Args.AddAllArgs(Arguments, options::OPT_linker_option_Group); + + // This should be the last option, for convenience in checking output. + Arguments.push_back("-o"); + Arguments.push_back( + context.Args.MakeArgString(context.Output.getPrimaryOutputFilename())); + + return {Clang, Arguments}; +} diff --git a/lib/Frontend/ArgsToFrontendInputsConverter.h b/lib/Frontend/ArgsToFrontendInputsConverter.h index f6b1e03b8b979..5d2c9fdb42768 100644 --- a/lib/Frontend/ArgsToFrontendInputsConverter.h +++ b/lib/Frontend/ArgsToFrontendInputsConverter.h @@ -16,7 +16,9 @@ #include "swift/AST/DiagnosticConsumer.h" #include "swift/AST/DiagnosticEngine.h" #include "swift/Frontend/FrontendOptions.h" +#include "llvm/ADT/SetVector.h" #include "llvm/Option/ArgList.h" +#include namespace swift { diff --git a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp index 30c17e6c67c44..b60d47c3c56a7 100644 --- a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp +++ b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp @@ -78,6 +78,8 @@ bool ArgsToFrontendOptionsConverter::convert( Opts.WarnLongExpressionTypeChecking); setUnsignedIntegerArgument(OPT_solver_expression_time_threshold_EQ, 10, Opts.SolverExpressionTimeThreshold); + setUnsignedIntegerArgument(OPT_switch_checking_invocation_threshold_EQ, 10, + Opts.SwitchCheckingInvocationThreshold); Opts.DebuggerTestingTransform = Args.hasArg(OPT_debugger_testing_transform); @@ -212,10 +214,10 @@ void ArgsToFrontendOptionsConverter::computeTBDOptions() { } void ArgsToFrontendOptionsConverter::setUnsignedIntegerArgument( - options::ID optionID, unsigned max, unsigned &valueToSet) { + options::ID optionID, unsigned radix, unsigned &valueToSet) { if (const Arg *A = Args.getLastArg(optionID)) { unsigned attempt; - if (StringRef(A->getValue()).getAsInteger(max, attempt)) { + if (StringRef(A->getValue()).getAsInteger(radix, attempt)) { Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value, A->getAsString(Args), A->getValue()); } else { diff --git a/lib/Frontend/ArgsToFrontendOptionsConverter.h b/lib/Frontend/ArgsToFrontendOptionsConverter.h index 87a34a215b842..437b0b6b9ae5b 100644 --- a/lib/Frontend/ArgsToFrontendOptionsConverter.h +++ b/lib/Frontend/ArgsToFrontendOptionsConverter.h @@ -47,7 +47,7 @@ class ArgsToFrontendOptionsConverter { void computePrintStatsOptions(); void computeTBDOptions(); - void setUnsignedIntegerArgument(options::ID optionID, unsigned max, + void setUnsignedIntegerArgument(options::ID optionID, unsigned radix, unsigned &valueToSet); bool setUpForSILOrLLVM(); diff --git a/lib/Frontend/ArgsToFrontendOutputsConverter.cpp b/lib/Frontend/ArgsToFrontendOutputsConverter.cpp index d4bf41e335ada..d2f8cf1216fb8 100644 --- a/lib/Frontend/ArgsToFrontendOutputsConverter.cpp +++ b/lib/Frontend/ArgsToFrontendOutputsConverter.cpp @@ -118,7 +118,7 @@ OutputFilesComputer::create(const llvm::opt::ArgList &args, } return OutputFilesComputer( - args, diags, inputsAndOutputs, std::move(outputFileArguments), + diags, inputsAndOutputs, std::move(outputFileArguments), outputDirectoryArgument, firstInput, requestedAction, args.getLastArg(options::OPT_module_name), FrontendOptions::suffixForPrincipalOutputFileForAction(requestedAction), @@ -126,14 +126,14 @@ OutputFilesComputer::create(const llvm::opt::ArgList &args, } OutputFilesComputer::OutputFilesComputer( - const llvm::opt::ArgList &args, DiagnosticEngine &diags, + DiagnosticEngine &diags, const FrontendInputsAndOutputs &inputsAndOutputs, std::vector outputFileArguments, const StringRef outputDirectoryArgument, const StringRef firstInput, const FrontendOptions::ActionType requestedAction, const llvm::opt::Arg *moduleNameArg, const StringRef suffix, const bool hasTextualOutput) - : Args(args), Diags(diags), InputsAndOutputs(inputsAndOutputs), + : Diags(diags), InputsAndOutputs(inputsAndOutputs), OutputFileArguments(outputFileArguments), OutputDirectoryArgument(outputDirectoryArgument), FirstInput(firstInput), RequestedAction(requestedAction), ModuleNameArg(moduleNameArg), diff --git a/lib/Frontend/ArgsToFrontendOutputsConverter.h b/lib/Frontend/ArgsToFrontendOutputsConverter.h index 6273d418dac95..b99a8abc30123 100644 --- a/lib/Frontend/ArgsToFrontendOutputsConverter.h +++ b/lib/Frontend/ArgsToFrontendOutputsConverter.h @@ -53,7 +53,6 @@ class ArgsToFrontendOutputsConverter { }; class OutputFilesComputer { - const llvm::opt::ArgList &Args; DiagnosticEngine &Diags; const FrontendInputsAndOutputs &InputsAndOutputs; const std::vector OutputFileArguments; @@ -64,7 +63,7 @@ class OutputFilesComputer { const StringRef Suffix; const bool HasTextualOutput; - OutputFilesComputer(const llvm::opt::ArgList &args, DiagnosticEngine &diags, + OutputFilesComputer(DiagnosticEngine &diags, const FrontendInputsAndOutputs &inputsAndOutputs, std::vector outputFileArguments, StringRef outputDirectoryArgument, StringRef firstInput, diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 174de57b408bb..333a091cbc314 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -214,6 +214,11 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, Opts.NamedLazyMemberLoading &= !Args.hasArg(OPT_disable_named_lazy_member_loading); Opts.DebugGenericSignatures |= Args.hasArg(OPT_debug_generic_signatures); + if (Args.hasArg(OPT_verify_syntax_tree)) { + Opts.BuildSyntaxTree = true; + Opts.VerifySyntaxTree = true; + } + Opts.DebuggerSupport |= Args.hasArg(OPT_debugger_support); if (Opts.DebuggerSupport) Opts.EnableDollarIdentifiers = true; @@ -312,7 +317,7 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, Opts.EnableNonFrozenEnumExhaustivityDiagnostics = Args.hasFlag(OPT_enable_nonfrozen_enum_exhaustivity_diagnostics, OPT_disable_nonfrozen_enum_exhaustivity_diagnostics, - Opts.EnableNonFrozenEnumExhaustivityDiagnostics); + Opts.isSwiftVersionAtLeast(5)); if (Arg *A = Args.getLastArg(OPT_Rpass_EQ)) Opts.OptimizationRemarkPassedPattern = @@ -576,16 +581,6 @@ static bool ParseSILArgs(SILOptions &Opts, ArgList &Args, } } - if (const Arg *A = Args.getLastArg(OPT_disable_sil_linking, - OPT_sil_link_all)) { - if (A->getOption().matches(OPT_disable_sil_linking)) - Opts.LinkMode = SILOptions::LinkNone; - else if (A->getOption().matches(OPT_sil_link_all)) - Opts.LinkMode = SILOptions::LinkAll; - else - llvm_unreachable("Unknown SIL linking option!"); - } - if (Args.hasArg(OPT_sil_merge_partial_modules)) Opts.MergePartialModules = true; @@ -814,7 +809,8 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args, } Opts.DisableLLVMOptzns |= Args.hasArg(OPT_disable_llvm_optzns); - Opts.DisableLLVMARCOpts |= Args.hasArg(OPT_disable_llvm_arc_opts); + Opts.DisableSwiftSpecificLLVMOptzns |= + Args.hasArg(OPT_disable_swift_specific_llvm_optzns); Opts.DisableLLVMSLPVectorizer |= Args.hasArg(OPT_disable_llvm_slp_vectorizer); if (Args.hasArg(OPT_disable_llvm_verify)) Opts.Verify = false; @@ -909,6 +905,14 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args, Opts.EnableReflectionNames = false; } + if (Args.hasArg(OPT_enable_class_resilience)) { + Opts.EnableClassResilience = true; + } + + if (Args.hasArg(OPT_enable_resilience_bypass)) { + Opts.EnableResilienceBypass = true; + } + for (const auto &Lib : Args.getAllArgValues(options::OPT_autolink_library)) Opts.LinkLibraries.push_back(LinkLibrary(Lib, LibraryKind::Library)); @@ -920,12 +924,11 @@ static std::string getScriptFileName(StringRef name, bool isSwiftVersion3) { return (Twine(name) + langVer + ".json").str(); } -bool ParseMigratorArgs(MigratorOptions &Opts, - const FrontendOptions &FrontendOpts, - const llvm::Triple &Triple, - const bool isSwiftVersion3, - StringRef ResourcePath, const ArgList &Args, - DiagnosticEngine &Diags) { +static bool ParseMigratorArgs(MigratorOptions &Opts, + LangOptions &LangOpts, + const FrontendOptions &FrontendOpts, + StringRef ResourcePath, const ArgList &Args, + DiagnosticEngine &Diags) { using namespace options; Opts.KeepObjcVisibility |= Args.hasArg(OPT_migrate_keep_objc_visibility); @@ -950,9 +953,13 @@ bool ParseMigratorArgs(MigratorOptions &Opts, if (auto DataPath = Args.getLastArg(OPT_api_diff_data_file)) { Opts.APIDigesterDataStorePaths.push_back(DataPath->getValue()); } else { + auto &Triple = LangOpts.Target; + bool isSwiftVersion3 = LangOpts.isSwiftVersion3(); + bool Supported = true; llvm::SmallString<128> dataPath(ResourcePath); llvm::sys::path::append(dataPath, "migrator"); + if (Triple.isMacOSX()) llvm::sys::path::append(dataPath, getScriptFileName("macos", isSwiftVersion3)); @@ -991,6 +998,9 @@ bool ParseMigratorArgs(MigratorOptions &Opts, // supplementary output for the whole compilation instead of one per input, // so it's probably not worth it. FrontendOpts.InputsAndOutputs.assertMustNotBeMoreThanOnePrimaryInput(); + + // Always disable typo-correction in the migrator. + LangOpts.TypoCorrectionLimit = 0; } return false; @@ -1061,8 +1071,7 @@ bool CompilerInvocation::parseArgs( return true; } - if (ParseMigratorArgs(MigratorOpts, FrontendOpts, LangOpts.Target, - LangOpts.isSwiftVersion3(), + if (ParseMigratorArgs(MigratorOpts, LangOpts, FrontendOpts, SearchPathOpts.RuntimeResourcePath, ParsedArgs, Diags)) { return true; } diff --git a/lib/Frontend/Frontend.cpp b/lib/Frontend/Frontend.cpp index de8dd86966108..0784305e0b5ad 100644 --- a/lib/Frontend/Frontend.cpp +++ b/lib/Frontend/Frontend.cpp @@ -149,9 +149,9 @@ bool CompilerInstance::setup(const CompilerInvocation &Invok) { Invocation.getLangOptions().AttachCommentsToDecls = true; } - Context.reset(new ASTContext(Invocation.getLangOptions(), - Invocation.getSearchPathOptions(), SourceMgr, - Diagnostics)); + Context.reset(ASTContext::get(Invocation.getLangOptions(), + Invocation.getSearchPathOptions(), SourceMgr, + Diagnostics)); if (setUpModuleLoaders()) return true; @@ -197,10 +197,10 @@ bool CompilerInstance::setUpModuleLoaders() { Context->addModuleLoader(SourceLoader::create(*Context, !immediate, enableResilience, - DepTracker)); + getDependencyTracker())); } { - auto SML = SerializedModuleLoader::create(*Context, DepTracker); + auto SML = SerializedModuleLoader::create(*Context, getDependencyTracker()); this->SML = SML.get(); Context->addModuleLoader(std::move(SML)); } @@ -210,7 +210,7 @@ bool CompilerInstance::setUpModuleLoaders() { // knowledge. auto clangImporter = ClangImporter::create(*Context, Invocation.getClangImporterOptions(), - Invocation.getPCHHash(), DepTracker); + Invocation.getPCHHash(), getDependencyTracker()); if (!clangImporter) { Diagnostics.diagnose(SourceLoc(), diag::error_clang_importer_create_fail); return true; @@ -614,13 +614,22 @@ void CompilerInstance::parseAndCheckTypes( TypeCheckOptions); } + assert(llvm::all_of(MainModule->getFiles(), [](const FileUnit *File) -> bool { + auto *SF = dyn_cast(File); + if (!SF) + return true; + return SF->ASTStage >= SourceFile::NameBound; + }) && "some files have not yet had their imports resolved"); + MainModule->setHasResolvedImports(); + const auto &options = Invocation.getFrontendOptions(); forEachFileToTypeCheck([&](SourceFile &SF) { performTypeChecking(SF, PersistentState.getTopLevelContext(), TypeCheckOptions, /*curElem*/ 0, options.WarnLongFunctionBodies, options.WarnLongExpressionTypeChecking, - options.SolverExpressionTimeThreshold); + options.SolverExpressionTimeThreshold, + options.SwitchCheckingInvocationThreshold); }); // Even if there were no source files, we should still record known @@ -743,7 +752,8 @@ void CompilerInstance::parseAndTypeCheckMainFile( TypeCheckOptions, CurTUElem, options.WarnLongFunctionBodies, options.WarnLongExpressionTypeChecking, - options.SolverExpressionTimeThreshold); + options.SolverExpressionTimeThreshold, + options.SwitchCheckingInvocationThreshold); } CurTUElem = MainFile.Decls.size(); } while (!Done); @@ -861,6 +871,7 @@ void CompilerInstance::performParseOnly(bool EvaluateConditionals) { if (MainBufferID != NO_SUCH_BUFFER) { SourceFile &MainFile = MainModule->getMainSourceFile(Invocation.getSourceFileKind()); + MainFile.SyntaxParsingCache = Invocation.getMainFileSyntaxParsingCache(); bool Done; do { diff --git a/lib/Frontend/FrontendInputsAndOutputs.cpp b/lib/Frontend/FrontendInputsAndOutputs.cpp index 0cb77fa2d0430..fad6b90633b0f 100644 --- a/lib/Frontend/FrontendInputsAndOutputs.cpp +++ b/lib/Frontend/FrontendInputsAndOutputs.cpp @@ -97,6 +97,13 @@ bool FrontendInputsAndOutputs::forEachPrimaryInput( return false; } +bool FrontendInputsAndOutputs::forEachNonPrimaryInput( + llvm::function_ref fn) const { + return forEachInput([&](const InputFile &f) -> bool { + return f.isPrimary() ? false : fn(f); + }); +} + void FrontendInputsAndOutputs::assertMustNotBeMoreThanOnePrimaryInput() const { assert(!hasMultiplePrimaryInputs() && "have not implemented >1 primary input yet"); @@ -134,7 +141,7 @@ bool FrontendInputsAndOutputs::isInputPrimary(StringRef file) const { } unsigned FrontendInputsAndOutputs::numberOfPrimaryInputsEndingWith( - const char *extension) const { + StringRef extension) const { unsigned n = 0; (void)forEachPrimaryInput([&](const InputFile &input) -> bool { if (llvm::sys::path::extension(input.file()).endswith(extension)) diff --git a/lib/Frontend/FrontendOptions.cpp b/lib/Frontend/FrontendOptions.cpp index f3634f338efda..b1a785f9ec6f5 100644 --- a/lib/Frontend/FrontendOptions.cpp +++ b/lib/Frontend/FrontendOptions.cpp @@ -108,7 +108,7 @@ bool FrontendOptions::shouldActionOnlyParse(ActionType action) { } void FrontendOptions::forAllOutputPaths( - const InputFile &input, std::function fn) const { + const InputFile &input, llvm::function_ref fn) const { if (RequestedAction != FrontendOptions::ActionType::EmitModuleOnly && RequestedAction != FrontendOptions::ActionType::MergeModules) { if (InputsAndOutputs.isWholeModule()) @@ -127,11 +127,11 @@ void FrontendOptions::forAllOutputPaths( } } -const char * +StringRef FrontendOptions::suffixForPrincipalOutputFileForAction(ActionType action) { switch (action) { case ActionType::NoneAction: - return nullptr; + return StringRef(); case ActionType::Parse: case ActionType::Typecheck: @@ -142,7 +142,7 @@ FrontendOptions::suffixForPrincipalOutputFileForAction(ActionType action) { case ActionType::PrintAST: case ActionType::DumpScopeMaps: case ActionType::DumpTypeRefinementContexts: - return nullptr; + return StringRef(); case ActionType::EmitPCH: return PCH_EXTENSION; @@ -162,7 +162,7 @@ FrontendOptions::suffixForPrincipalOutputFileForAction(ActionType action) { case ActionType::Immediate: case ActionType::REPL: // These modes have no frontend-generated output. - return nullptr; + return StringRef(); case ActionType::EmitAssembly: return "s"; diff --git a/lib/Frontend/SerializedDiagnosticConsumer.cpp b/lib/Frontend/SerializedDiagnosticConsumer.cpp index ffd9024b2f025..bf92c314d5c6a 100644 --- a/lib/Frontend/SerializedDiagnosticConsumer.cpp +++ b/lib/Frontend/SerializedDiagnosticConsumer.cpp @@ -140,7 +140,7 @@ class SerializedDiagnosticConsumer : public DiagnosticConsumer { assert(CalledFinishProcessing && "did not call finishProcessing()"); } - bool finishProcessing() override { + bool finishProcessing(SourceManager &SM) override { assert(!CalledFinishProcessing && "called finishProcessing() multiple times"); CalledFinishProcessing = true; @@ -160,8 +160,7 @@ class SerializedDiagnosticConsumer : public DiagnosticConsumer { llvm::sys::fs::F_None)); if (EC) { // Create a temporary diagnostics engine to print the error to stderr. - SourceManager dummyMgr; - DiagnosticEngine DE(dummyMgr); + DiagnosticEngine DE(SM); PrintingDiagnosticConsumer PDC; DE.addConsumer(PDC); DE.diagnose(SourceLoc(), diag::cannot_open_serialized_file, diff --git a/lib/FrontendTool/FrontendTool.cpp b/lib/FrontendTool/FrontendTool.cpp index 4cdec2f586724..20603e002fba4 100644 --- a/lib/FrontendTool/FrontendTool.cpp +++ b/lib/FrontendTool/FrontendTool.cpp @@ -61,6 +61,7 @@ #include "swift/SILOptimizer/PassManager/Passes.h" #include "swift/Syntax/Serialization/SyntaxSerialization.h" #include "swift/Syntax/SyntaxNodes.h" +#include "swift/TBDGen/TBDGen.h" // FIXME: We're just using CompilerInstance::createOutputFile. // This API should be sunk down to LLVM. @@ -404,7 +405,7 @@ class JSONFixitWriter } } - bool finishProcessing() override { + bool finishProcessing(SourceManager &) override { std::error_code EC; std::unique_ptr OS; OS.reset(new llvm::raw_fd_ostream(FixitsOutputPath, @@ -764,20 +765,24 @@ static void emitReferenceDependenciesForAllPrimaryInputsIfNeeded( static bool writeTBDIfNeeded(CompilerInvocation &Invocation, CompilerInstance &Instance) { - if (!Invocation.getFrontendOptions().InputsAndOutputs.hasTBDPath()) + const auto &frontendOpts = Invocation.getFrontendOptions(); + if (!frontendOpts.InputsAndOutputs.hasTBDPath()) return false; const std::string &TBDPath = Invocation.getTBDPathForWholeModule(); assert(!TBDPath.empty() && "If not WMO, getTBDPathForWholeModule should have failed"); - auto installName = Invocation.getFrontendOptions().TBDInstallName.empty() + auto installName = frontendOpts.TBDInstallName.empty() ? "lib" + Invocation.getModuleName().str() + ".dylib" - : Invocation.getFrontendOptions().TBDInstallName; + : frontendOpts.TBDInstallName; - return writeTBD(Instance.getMainModule(), - Invocation.getSILOptions().hasMultipleIGMs(), TBDPath, - installName); + TBDGenOptions opts; + opts.InstallName = installName; + opts.HasMultipleIGMs = Invocation.getSILOptions().hasMultipleIGMs(); + opts.ModuleLinkName = frontendOpts.ModuleLinkName; + + return writeTBD(Instance.getMainModule(), TBDPath, opts); } static std::deque @@ -884,7 +889,8 @@ static bool performCompile(CompilerInstance &Instance, return compileLLVMIR(Invocation, Instance, Stats); if (FrontendOptions::shouldActionOnlyParse(Action)) - Instance.performParseOnly(); + Instance.performParseOnly(/*EvaluateConditionals*/ + Action == FrontendOptions::ActionType::EmitImportedModules); else Instance.performSema(); @@ -947,9 +953,13 @@ static bool performCompile(CompilerInstance &Instance, // We've just been told to perform a typecheck, so we can return now. if (Action == FrontendOptions::ActionType::Typecheck) { - const bool hadPrintAsObjCError = printAsObjCIfNeeded( - Invocation.getObjCHeaderOutputPathForAtMostOnePrimary(), - Instance.getMainModule(), opts.ImplicitObjCHeaderPath, moduleIsPublic); + const bool hadPrintAsObjCError = + Invocation.getFrontendOptions() + .InputsAndOutputs.hasObjCHeaderOutputPath() && + printAsObjCIfNeeded( + Invocation.getObjCHeaderOutputPathForAtMostOnePrimary(), + Instance.getMainModule(), opts.ImplicitObjCHeaderPath, + moduleIsPublic); const bool hadEmitIndexDataError = emitIndexData(Invocation, Instance); @@ -979,13 +989,6 @@ static bool performCompile(CompilerInstance &Instance, return false; } -/// If we are asked to link all, link all. -static void linkAllIfNeeded(const CompilerInvocation &Invocation, - SILModule *SM) { - if (Invocation.getSILOptions().LinkMode == SILOptions::LinkAll) - performSILLinking(SM, true); -} - /// Perform "stable" optimizations that are invariant across compiler versions. static bool performMandatorySILPasses(CompilerInvocation &Invocation, SILModule *SM, @@ -1007,8 +1010,6 @@ static bool performMandatorySILPasses(CompilerInvocation &Invocation, return true; } - linkAllIfNeeded(Invocation, SM); - if (Invocation.getSILOptions().MergePartialModules) SM->linkAllFromCurrentModule(); return false; @@ -1142,7 +1143,8 @@ static bool validateTBDIfNeeded(CompilerInvocation &Invocation, !inputFileKindCanHaveTBDValidated(Invocation.getInputKind())) return false; - const auto mode = Invocation.getFrontendOptions().ValidateTBDAgainstIR; + const auto &frontendOpts = Invocation.getFrontendOptions(); + const auto mode = frontendOpts.ValidateTBDAgainstIR; // Ensure all cases are covered by using a switch here. switch (mode) { case FrontendOptions::TBDValidationMode::None: @@ -1151,12 +1153,14 @@ static bool validateTBDIfNeeded(CompilerInvocation &Invocation, case FrontendOptions::TBDValidationMode::MissingFromTBD: break; } - const auto hasMultipleIGMs = Invocation.getSILOptions().hasMultipleIGMs(); + TBDGenOptions opts; + opts.HasMultipleIGMs = Invocation.getSILOptions().hasMultipleIGMs(); + opts.ModuleLinkName = frontendOpts.ModuleLinkName; + const bool allSymbols = mode == FrontendOptions::TBDValidationMode::All; - return MSF.is() ? validateTBD(MSF.get(), IRModule, - hasMultipleIGMs, allSymbols) - : validateTBD(MSF.get(), IRModule, - hasMultipleIGMs, allSymbols); + return MSF.is() + ? validateTBD(MSF.get(), IRModule, opts, allSymbols) + : validateTBD(MSF.get(), IRModule, opts, allSymbols); } static bool generateCode(CompilerInvocation &Invocation, @@ -1210,12 +1214,10 @@ static bool performCompileStepsPostSILGen( // We've been told to emit SIL after SILGen, so write it now. if (Action == FrontendOptions::ActionType::EmitSILGen) { - linkAllIfNeeded(Invocation, SM.get()); return writeSIL(*SM, PSPs, Instance, Invocation); } if (Action == FrontendOptions::ActionType::EmitSIBGen) { - linkAllIfNeeded(Invocation, SM.get()); serializeSIB(SM.get(), PSPs, Instance.getASTContext(), MSF); return Context.hadError(); } @@ -1537,6 +1539,19 @@ createDispatchingDiagnosticConsumerIfNeeded( subConsumers.emplace_back(input.file(), std::move(subConsumer)); return false; }); + // For batch mode, the compiler must swallow diagnostics pertaining to + // non-primary files in order to avoid Xcode showing the same diagnostic + // multiple times. So, create a diagnostic "eater" for those non-primary + // files. + // To avoid introducing bugs into WMO or single-file modes, test for multiple + // primaries. + if (inputsAndOutputs.hasMultiplePrimaryInputs()) { + inputsAndOutputs.forEachNonPrimaryInput( + [&](const InputFile &input) -> bool { + subConsumers.emplace_back(input.file(), nullptr); + return false; + }); + } if (subConsumers.empty()) return nullptr; @@ -1644,7 +1659,7 @@ int swift::performFrontend(ArrayRef Args, auto finishDiagProcessing = [&](int retValue) -> int { FinishDiagProcessingCheckRAII.CalledFinishDiagProcessing = true; - bool err = Instance->getDiags().finishProcessing(); + bool err = Instance->getDiags().finishProcessing(Instance->getSourceMgr()); return retValue ? retValue : err; }; @@ -1706,7 +1721,7 @@ int swift::performFrontend(ArrayRef Args, std::unique_ptr Options(createSwiftOptTable()); Options->PrintHelp(llvm::outs(), displayName(MainExecutablePath).c_str(), "Swift frontend", IncludedFlagsBitmask, - ExcludedFlagsBitmask); + ExcludedFlagsBitmask, /*ShowAllAliases*/false); return finishDiagProcessing(0); } @@ -1749,15 +1764,10 @@ int swift::performFrontend(ArrayRef Args, enableDiagnosticVerifier(Instance->getSourceMgr()); } - DependencyTracker depTracker; - { - const FrontendInputsAndOutputs &io = - Invocation.getFrontendOptions().InputsAndOutputs; - if (io.hasDependencyTrackerPath() || - !Invocation.getFrontendOptions().IndexStorePath.empty()) { - Instance->setDependencyTracker(&depTracker); - } - } + if (Invocation.getFrontendOptions() + .InputsAndOutputs.hasDependencyTrackerPath() || + !Invocation.getFrontendOptions().IndexStorePath.empty()) + Instance->createDependencyTracker(); if (Instance->setup(Invocation)) { return finishDiagProcessing(1); diff --git a/lib/FrontendTool/TBD.cpp b/lib/FrontendTool/TBD.cpp index 2c933899c64e7..0a634be4518a6 100644 --- a/lib/FrontendTool/TBD.cpp +++ b/lib/FrontendTool/TBD.cpp @@ -38,8 +38,8 @@ static std::vector sortSymbols(llvm::StringSet<> &symbols) { return sorted; } -bool swift::writeTBD(ModuleDecl *M, bool hasMultipleIGMs, - StringRef OutputFilename, StringRef installName) { +bool swift::writeTBD(ModuleDecl *M, StringRef OutputFilename, + TBDGenOptions &Opts) { std::error_code EC; llvm::raw_fd_ostream OS(OutputFilename, EC, llvm::sys::fs::F_None); if (EC) { @@ -48,7 +48,7 @@ bool swift::writeTBD(ModuleDecl *M, bool hasMultipleIGMs, return true; } - writeTBDFile(M, OS, hasMultipleIGMs, installName); + writeTBDFile(M, OS, Opts); return false; } @@ -117,18 +117,18 @@ static bool validateSymbolSet(DiagnosticEngine &diags, } bool swift::validateTBD(ModuleDecl *M, llvm::Module &IRModule, - bool hasMultipleIGMs, bool diagnoseExtraSymbolsInTBD) { + TBDGenOptions &opts, bool diagnoseExtraSymbolsInTBD) { llvm::StringSet<> symbols; - enumeratePublicSymbols(M, symbols, hasMultipleIGMs); + enumeratePublicSymbols(M, symbols, opts); return validateSymbolSet(M->getASTContext().Diags, symbols, IRModule, diagnoseExtraSymbolsInTBD); } bool swift::validateTBD(FileUnit *file, llvm::Module &IRModule, - bool hasMultipleIGMs, bool diagnoseExtraSymbolsInTBD) { + TBDGenOptions &opts, bool diagnoseExtraSymbolsInTBD) { llvm::StringSet<> symbols; - enumeratePublicSymbols(file, symbols, hasMultipleIGMs); + enumeratePublicSymbols(file, symbols, opts); return validateSymbolSet(file->getParentModule()->getASTContext().Diags, symbols, IRModule, diagnoseExtraSymbolsInTBD); diff --git a/lib/FrontendTool/TBD.h b/lib/FrontendTool/TBD.h index bda0922455e29..c6e8610e3d32d 100644 --- a/lib/FrontendTool/TBD.h +++ b/lib/FrontendTool/TBD.h @@ -23,13 +23,13 @@ namespace swift { class ModuleDecl; class FileUnit; class FrontendOptions; +struct TBDGenOptions; -bool writeTBD(ModuleDecl *M, bool hasMultipleIGMs, StringRef OutputFilename, - llvm::StringRef installName); +bool writeTBD(ModuleDecl *M, StringRef OutputFilename, TBDGenOptions &Opts); bool inputFileKindCanHaveTBDValidated(InputFileKind kind); -bool validateTBD(ModuleDecl *M, llvm::Module &IRModule, bool hasMultipleIGMs, +bool validateTBD(ModuleDecl *M, llvm::Module &IRModule, TBDGenOptions &opts, bool diagnoseExtraSymbolsInTBD); -bool validateTBD(FileUnit *M, llvm::Module &IRModule, bool hasMultipleIGMs, +bool validateTBD(FileUnit *M, llvm::Module &IRModule, TBDGenOptions &opts, bool diagnoseExtraSymbolsInTBD); } diff --git a/lib/IDE/APIDigesterData.cpp b/lib/IDE/APIDigesterData.cpp index cc6d032ed29c7..0b7fb14107a7f 100644 --- a/lib/IDE/APIDigesterData.cpp +++ b/lib/IDE/APIDigesterData.cpp @@ -24,7 +24,7 @@ using namespace api; inline raw_ostream &swift::ide::api:: operator<<(raw_ostream &Out, const SDKNodeKind Value) { switch (Value) { -#define NODE_KIND(Name) case SDKNodeKind::Name: return Out << #Name; +#define NODE_KIND(Name, Value) case SDKNodeKind::Name: return Out << #Value; #include "swift/IDE/DigesterEnums.def" } llvm_unreachable("Undefined SDK node kind."); @@ -39,14 +39,14 @@ operator<<(raw_ostream &Out, const NodeAnnotation Value) { SDKNodeKind swift::ide::api::parseSDKNodeKind(StringRef Content) { return llvm::StringSwitch(Content) -#define NODE_KIND(NAME) .Case(#NAME, SDKNodeKind::NAME) +#define NODE_KIND(NAME, VALUE) .Case(#VALUE, SDKNodeKind::NAME) #include "swift/IDE/DigesterEnums.def" ; } NodeAnnotation swift::ide::api::parseSDKNodeAnnotation(StringRef Content) { return llvm::StringSwitch(Content) -#define NODE_ANNOTATION(NAME) .Case(#NAME, NodeAnnotation::NAME) +#define NODE_ANNOTATION_CHANGE_KIND(NAME) .Case(#NAME, NodeAnnotation::NAME) #include "swift/IDE/DigesterEnums.def" ; } @@ -148,9 +148,10 @@ swift::ide::api::TypeMemberDiffItem::getSubKind() const { assert(OldName.argSize() == 0); assert(!removedIndex); return TypeMemberDiffItemSubKind::GlobalFuncToStaticProperty; - } else if (oldTypeName.empty()){ + } else if (oldTypeName.empty()) { + // we can handle this as a simple function rename. assert(NewName.argSize() == OldName.argSize()); - return TypeMemberDiffItemSubKind::SimpleReplacement; + return TypeMemberDiffItemSubKind::FuncRename; } else { assert(NewName.argSize() == OldName.argSize()); return TypeMemberDiffItemSubKind::QualifiedReplacement; @@ -444,6 +445,25 @@ struct ArrayTraits> { return const_cast(seq[index]); } }; + +template<> +struct ObjectTraits { + static void mapping(Output &out, NameCorrectionInfo &value) { + out.mapRequired(getKeyContent(DiffItemKeyKind::KK_OldPrintedName),value.OriginalName); + out.mapRequired(getKeyContent(DiffItemKeyKind::KK_NewPrintedName), value.CorrectedName); + out.mapRequired(getKeyContent(DiffItemKeyKind::KK_ModuleName), value.ModuleName); + } +}; +template<> +struct ArrayTraits> { + static size_t size(Output &out, ArrayRef &seq) { + return seq.size(); + } + static NameCorrectionInfo &element(Output &, ArrayRef &seq, + size_t index) { + return const_cast(seq[index]); + } +}; } // namespace json } // namespace swift @@ -453,10 +473,32 @@ serialize(llvm::raw_ostream &os, ArrayRef Items) { yout << Items; } +void swift::ide::api::APIDiffItemStore:: +serialize(llvm::raw_ostream &os, ArrayRef Items) { + json::Output yout(os); + yout << Items; +} + struct swift::ide::api::APIDiffItemStore::Implementation { private: llvm::SmallVector, 2> AllBuffer; llvm::BumpPtrAllocator Allocator; + + static bool shouldInclude(APIDiffItem *Item) { + if (auto *CI = dyn_cast(Item)) { + if (CI->rightCommentUnderscored()) + return false; + + // Ignore constructor's return value rewritten. + if (CI->DiffKind == NodeAnnotation::TypeRewritten && + CI->NodeKind == SDKNodeKind::DeclConstructor && + CI->getChildIndices().front() == 0) { + return false; + } + } + return true; + } + public: llvm::StringMap> Data; bool PrintUsr; @@ -482,14 +524,13 @@ struct swift::ide::api::APIDiffItemStore::Implementation { APIDiffItem *Item = serializeDiffItem(Allocator, cast(&*It)); auto &Bag = Data[Item->getKey()]; - if (std::find_if(Bag.begin(), Bag.end(), + if (shouldInclude(Item) && std::find_if(Bag.begin(), Bag.end(), [&](APIDiffItem* I) { return *Item == *I; }) == Bag.end()) { Bag.push_back(Item); AllItems.push_back(Item); } } } - } }; diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp index e263b7fd3c493..1c8e1109a022f 100644 --- a/lib/IDE/CodeCompletion.cpp +++ b/lib/IDE/CodeCompletion.cpp @@ -939,10 +939,15 @@ static CodeCompletionResult::ExpectedTypeRelation calculateTypeRelation( Ty->is() || ExpectedTy->is()) return CodeCompletionResult::ExpectedTypeRelation::Unrelated; - if (Ty->isEqual(ExpectedTy)) - return CodeCompletionResult::ExpectedTypeRelation::Identical; - if (isConvertibleTo(Ty, ExpectedTy, *DC)) - return CodeCompletionResult::ExpectedTypeRelation::Convertible; + + // Equality/Conversion of GenericTypeParameterType won't account for + // requirements – ignore them + if (!Ty->hasTypeParameter() && !ExpectedTy->hasTypeParameter()) { + if (Ty->isEqual(ExpectedTy)) + return CodeCompletionResult::ExpectedTypeRelation::Identical; + if (isConvertibleTo(Ty, ExpectedTy, *DC)) + return CodeCompletionResult::ExpectedTypeRelation::Convertible; + } if (auto FT = Ty->getAs()) { if (FT->getResult()->isVoid()) return CodeCompletionResult::ExpectedTypeRelation::Invalid; @@ -1313,7 +1318,7 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks { if (!DC) return; auto *CD = DC->getAsClassOrClassExtensionContext(); - if (CD == nullptr) + if (!CD) return; Type ST = CD->getSuperclass(); if (ST.isNull() || ST->is()) @@ -1539,7 +1544,7 @@ protocolForLiteralKind(CodeCompletionLiteralKind kind) { static bool hasTrivialTrailingClosure(const FuncDecl *FD, AnyFunctionType *funcType) { SmallVector defaultMap; - computeDefaultMap(funcType->getInput(), FD, + computeDefaultMap(funcType->getParams(), FD, /*level*/ FD->isInstanceMember() ? 1 : 0, defaultMap); bool OneArg = defaultMap.size() == 1; @@ -1827,36 +1832,47 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { void addImportModuleNames() { // FIXME: Add user-defined swift modules - SmallVector Modules; - Ctx.getVisibleTopLevelClangModules(Modules); - std::sort(Modules.begin(), Modules.end(), - [](clang::Module* LHS , clang::Module* RHS) { - return LHS->getTopLevelModuleName().compare_lower( - RHS->getTopLevelModuleName()) < 0; + SmallVector ModuleNames; + + // Collect clang module names. + { + SmallVector ClangModules; + Ctx.getVisibleTopLevelClangModules(ClangModules); + for (auto *M : ClangModules) { + if (!M->isAvailable()) + continue; + if (M->getTopLevelModuleName().startswith("_")) + continue; + if (M->getTopLevelModuleName() == Ctx.SwiftShimsModuleName.str()) + continue; + + ModuleNames.push_back(M->getTopLevelModuleName()); + } + } + + std::sort(ModuleNames.begin(), ModuleNames.end(), + [](StringRef LHS, StringRef RHS) { + return LHS.compare_lower(RHS) < 0; }); + llvm::StringSet<> ImportedModules; collectImportedModules(ImportedModules); - for (auto *M : Modules) { - if (M->isAvailable() && - !M->getTopLevelModuleName().startswith("_") && - M->getTopLevelModuleName() != CurrDeclContext->getASTContext(). - SwiftShimsModuleName.str()) { - auto MD = ModuleDecl::create(Ctx.getIdentifier(M->getTopLevelModuleName()), - Ctx); - CodeCompletionResultBuilder Builder(Sink, - CodeCompletionResult::ResultKind:: - Declaration, - SemanticContextKind::OtherModule, - ExpectedTypes); - Builder.setAssociatedDecl(MD); - Builder.addTextChunk(MD->getNameStr()); - Builder.addTypeAnnotation("Module"); - - // Imported modules are not recommended. - if (ImportedModules.count(MD->getNameStr()) != 0) - Builder.setNotRecommended(CodeCompletionResult::NotRecommendedReason:: - Redundant); - } + + for (auto ModuleName : ModuleNames) { + auto MD = ModuleDecl::create(Ctx.getIdentifier(ModuleName), Ctx); + CodeCompletionResultBuilder Builder( + Sink, + CodeCompletionResult::ResultKind::Declaration, + SemanticContextKind::OtherModule, + ExpectedTypes); + Builder.setAssociatedDecl(MD); + Builder.addTextChunk(MD->getNameStr()); + Builder.addTypeAnnotation("Module"); + + // Imported modules are not recommended. + if (ImportedModules.count(MD->getNameStr()) != 0) + Builder.setNotRecommended( + CodeCompletionResult::NotRecommendedReason::Redundant); } } @@ -2822,23 +2838,29 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { } void addKeyword(StringRef Name, Type TypeAnnotation, - SemanticContextKind SK = SemanticContextKind::None) { + SemanticContextKind SK = SemanticContextKind::None, + CodeCompletionKeywordKind KeyKind + = CodeCompletionKeywordKind::None) { CodeCompletionResultBuilder Builder( Sink, CodeCompletionResult::ResultKind::Keyword, SK, ExpectedTypes); addLeadingDot(Builder); Builder.addTextChunk(Name); + Builder.setKeywordKind(KeyKind); if (!TypeAnnotation.isNull()) addTypeAnnotation(Builder, TypeAnnotation); } - void addKeyword(StringRef Name, StringRef TypeAnnotation) { + void addKeyword(StringRef Name, StringRef TypeAnnotation, + CodeCompletionKeywordKind KeyKind + = CodeCompletionKeywordKind::None) { CodeCompletionResultBuilder Builder( Sink, CodeCompletionResult::ResultKind::Keyword, SemanticContextKind::None, ExpectedTypes); addLeadingDot(Builder); Builder.addTextChunk(Name); + Builder.setKeywordKind(KeyKind); if (!TypeAnnotation.empty()) Builder.addTypeAnnotation(TypeAnnotation); } @@ -3243,14 +3265,13 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { ExprType = ExprType->getRValueType(); this->ExprType = ExprType; if (ExprType->hasTypeParameter()) { - DeclContext *DC; - if (VD) { + DeclContext *DC = nullptr; + if (VD) DC = VD->getInnermostDeclContext(); - this->ExprType = DC->mapTypeIntoContext(ExprType); - } else if (auto NTD = ExprType->getRValueInstanceType()->getAnyNominal()) { + else if (auto NTD = ExprType->getRValueInstanceType()->getAnyNominal()) DC = NTD; - this->ExprType = DC->mapTypeIntoContext(ExprType); - } + if (DC) + ExprType = DC->mapTypeIntoContext(ExprType); } // Handle special cases @@ -4852,20 +4873,14 @@ static void addDeclKeywords(CodeCompletionResultSink &Sink) { auto AddCSKeyword = [&](StringRef Name) { AddKeyword(Name, CodeCompletionKeywordKind::None); }; - AddCSKeyword("weak"); - AddCSKeyword("unowned"); - AddCSKeyword("optional"); - AddCSKeyword("required"); - AddCSKeyword("lazy"); - AddCSKeyword("final"); - AddCSKeyword("dynamic"); - AddCSKeyword("prefix"); - AddCSKeyword("postfix"); - AddCSKeyword("infix"); - AddCSKeyword("override"); - AddCSKeyword("mutating"); - AddCSKeyword("nonmutating"); - AddCSKeyword("convenience"); + +#define CONTEXTUAL_CASE(KW) AddCSKeyword(#KW); +#define CONTEXTUAL_DECL_ATTR(KW, ...) CONTEXTUAL_CASE(KW) +#define CONTEXTUAL_DECL_ATTR_ALIAS(KW, ...) CONTEXTUAL_CASE(KW) +#define CONTEXTUAL_SIMPLE_DECL_ATTR(KW, ...) CONTEXTUAL_CASE(KW) +#include +#undef CONTEXTUAL_CASE + } static void addStmtKeywords(CodeCompletionResultSink &Sink, bool MaybeFuncBody) { @@ -5306,6 +5321,11 @@ void CodeCompletionCallbacksImpl::doneParsing() { if (isDynamicLookup(*ExprType)) Lookup.setIsDynamicLookup(); + if (!ExprType.getValue()->getAs()) + Lookup.addKeyword("self", (*ExprType)->getRValueType(), + SemanticContextKind::CurrentNominal, + CodeCompletionKeywordKind::kw_self); + if (isa(ParsedExpr) || isa(ParsedExpr)) Lookup.setIsUnwrappedOptional(true); @@ -5354,6 +5374,11 @@ void CodeCompletionCallbacksImpl::doneParsing() { Lookup.setIsDynamicLookup(); Lookup.getValueExprCompletions(*ExprType, ReferencedDecl.getDecl()); Lookup.getOperatorCompletions(ParsedExpr, leadingSequenceExprs); + + if (!ExprType.getValue()->getAs()) + Lookup.addKeyword("self", (*ExprType)->getRValueType(), + SemanticContextKind::CurrentNominal, + CodeCompletionKeywordKind::kw_self); break; } diff --git a/lib/IDE/Formatting.cpp b/lib/IDE/Formatting.cpp index 3b8f1569b294c..cd4835ec4dc33 100644 --- a/lib/IDE/Formatting.cpp +++ b/lib/IDE/Formatting.cpp @@ -668,7 +668,7 @@ class FormatWalker : public SourceEntityWalker { SiblingCollector SCollector; /// Sometimes, target is a part of "parent", for instance, "#else" is a part - /// of an ifconfigstmt, so that ifconfigstmt is not really the parent of "#else". + /// of an IfConfigDecl, so that IfConfigDecl is not really the parent of "#else". bool isTargetPartOf(swift::ASTWalker::ParentTy Parent) { if (auto Conf = dyn_cast_or_null(Parent.getAsDecl())) { for (auto Clause : Conf->getClauses()) { diff --git a/lib/IDE/ModuleInterfacePrinting.cpp b/lib/IDE/ModuleInterfacePrinting.cpp index 3780a9135a2ed..7ee9cbc774a2b 100644 --- a/lib/IDE/ModuleInterfacePrinting.cpp +++ b/lib/IDE/ModuleInterfacePrinting.cpp @@ -622,10 +622,11 @@ void swift::ide::printSubmoduleInterface( // For sub-decls, all extensions should be printed. SynthesizedExtensionAnalyzer::MergeGroupKind::All, [&](ArrayRef Decls) { + // Whether we've started the extension merge group in printing. + bool Opened = false; for (auto ET : Decls) { - AdjustedOptions.BracketOptions = { - ET.Ext, Decls.front().Ext == ET.Ext, - Decls.back().Ext == ET.Ext, true}; + AdjustedOptions.BracketOptions = { ET.Ext, !Opened, + Decls.back().Ext == ET.Ext, true}; if (AdjustedOptions.BracketOptions.shouldOpenExtension( ET.Ext)) Printer << "\n"; @@ -636,7 +637,8 @@ void swift::ide::printSubmoduleInterface( else AdjustedOptions.initForSynthesizedExtension(NTD); } - ET.Ext->print(Printer, AdjustedOptions); + // Set opened if we actually printed this extension. + Opened |= ET.Ext->print(Printer, AdjustedOptions); if (ET.IsSynthesized) AdjustedOptions.clearSynthesizedExtension(); if (AdjustedOptions.BracketOptions.shouldCloseExtension( diff --git a/lib/IDE/Refactoring.cpp b/lib/IDE/Refactoring.cpp index 397a73e63d86c..88a382e37d237 100644 --- a/lib/IDE/Refactoring.cpp +++ b/lib/IDE/Refactoring.cpp @@ -30,6 +30,7 @@ #include "swift/Subsystems.h" #include "clang/Rewrite/Core/RewriteBuffer.h" #include "llvm/ADT/StringSet.h" +#include "llvm/ADT/SmallPtrSet.h" using namespace swift; using namespace swift::ide; @@ -907,6 +908,15 @@ ExtractCheckResult checkExtractConditions(ResolvedRangeInfo &RangeInfo, return ExtractCheckResult(); } + // Disallow extracting certain kinds of statements. + if (RangeInfo.Kind == RangeKind::SingleStatement) { + Stmt *S = RangeInfo.ContainedNodes[0].get(); + + // These aren't independent statement. + if (isa(S) || isa(S) || isa(S)) + return ExtractCheckResult(); + } + // Disallow extracting literals. if (RangeInfo.Kind == RangeKind::SingleExpression) { Expr *E = RangeInfo.ContainedNodes[0].get(); @@ -1587,109 +1597,141 @@ bool RefactoringActionMoveMembersToExtension::performChange() { return false; } -struct CollapsibleNestedIfInfo { - IfStmt *OuterIf; - IfStmt *InnerIf; - bool FinishedOuterIf; - bool FoundNonCollapsibleItem; - CollapsibleNestedIfInfo(): - OuterIf(nullptr), InnerIf(nullptr), - FinishedOuterIf(false), FoundNonCollapsibleItem(false) {} - bool isValid() { - return OuterIf && InnerIf && FinishedOuterIf && !FoundNonCollapsibleItem; +namespace { +// A SingleDecl range may not include all decls actually declared in that range: +// a var decl has accessors that aren't included. This will find those missing +// decls. +class FindAllSubDecls : public SourceEntityWalker { + llvm::SmallPtrSetImpl &Found; + public: + FindAllSubDecls(llvm::SmallPtrSetImpl &found) + : Found(found) {} + + bool walkToDeclPre(Decl *D, CharSourceRange range) { + // Record this Decl, and skip its contents if we've already touched it. + if (!Found.insert(D).second) + return false; + + if (auto ASD = dyn_cast(D)) { + llvm::SmallVector accessors; + ASD->getAllAccessorFunctions(accessors); + Found.insert(accessors.begin(), accessors.end()); + } + return true; } }; +} +bool RefactoringActionReplaceBodiesWithFatalError::isApplicable( + ResolvedRangeInfo Info, DiagnosticEngine &Diag) { + switch (Info.Kind) { + case RangeKind::SingleDecl: + case RangeKind::MultiTypeMemberDecl: { + llvm::SmallPtrSet Found; + for (auto decl : Info.DeclaredDecls) { + FindAllSubDecls(Found).walk(decl.VD); + } + for (auto decl : Found) { + auto AFD = dyn_cast(decl); + if (AFD && !AFD->isImplicit()) + return true; + } + + return false; + } + case RangeKind::SingleExpression: + case RangeKind::PartOfExpression: + case RangeKind::SingleStatement: + case RangeKind::MultiStatement: + case RangeKind::Invalid: + return false; + } +} + +bool RefactoringActionReplaceBodiesWithFatalError::performChange() { + const StringRef replacement = "{\nfatalError()\n}"; + llvm::SmallPtrSet Found; + for (auto decl : RangeInfo.DeclaredDecls) { + FindAllSubDecls(Found).walk(decl.VD); + } + for (auto decl : Found) { + auto AFD = dyn_cast(decl); + if (!AFD || AFD->isImplicit()) + continue; -static CollapsibleNestedIfInfo findCollapseNestedIfTarget(ResolvedCursorInfo CursorInfo) { + auto range = AFD->getBodySourceRange(); + // If we're in replacement mode (i.e. have an edit consumer), we can + // rewrite the function body. + auto charRange = Lexer::getCharSourceRangeFromSourceRange(SM, range); + EditConsumer.accept(SM, charRange, replacement); + + } + return false; +} + +static std::pair +findCollapseNestedIfTarget(ResolvedCursorInfo CursorInfo) { if (CursorInfo.Kind != CursorInfoKind::StmtStart) - return CollapsibleNestedIfInfo(); - struct IfStmtFinder: public SourceEntityWalker { - SourceLoc StartLoc; - CollapsibleNestedIfInfo IfInfo; - IfStmtFinder(SourceLoc StartLoc): StartLoc(StartLoc), IfInfo() {} - bool finishedInnerIfButNotFinishedOuterIf() { - return IfInfo.InnerIf && !IfInfo.FinishedOuterIf; - } - bool walkToStmtPre(Stmt *S) { - if (finishedInnerIfButNotFinishedOuterIf()) { - IfInfo.FoundNonCollapsibleItem = true; - return false; - } + return {}; - bool StmtIsOuterIfBrace = - IfInfo.OuterIf && !IfInfo.InnerIf && S->getKind() == StmtKind::Brace; - if (StmtIsOuterIfBrace) { - return true; - } + // Ensure the statement is 'if' statement. It must not have 'else' clause. + IfStmt *OuterIf = dyn_cast(CursorInfo.TrailingStmt); + if (!OuterIf) + return {}; + if (OuterIf->getElseStmt()) + return {}; - auto *IFS = dyn_cast(S); - if (!IFS) { - return false; - } - if (!IfInfo.OuterIf) { - IfInfo.OuterIf = IFS; - return true; - } else { - IfInfo.InnerIf = IFS; - return false; - } - } - bool walkToStmtPost(Stmt *S) { - assert(S != IfInfo.InnerIf && "Should not traverse inner if statement"); - if (S == IfInfo.OuterIf) { - IfInfo.FinishedOuterIf = true; - } - return true; - } - bool walkToDeclPre(Decl *D, CharSourceRange Range) { - if (finishedInnerIfButNotFinishedOuterIf()) { - IfInfo.FoundNonCollapsibleItem = true; - return false; - } - return true; - } - bool walkToExprPre(Expr *E) { - if (finishedInnerIfButNotFinishedOuterIf()) { - IfInfo.FoundNonCollapsibleItem = true; - return false; - } - return true; - } + // The body must contain a sole inner 'if' statement. + auto Body = dyn_cast_or_null(OuterIf->getThenStmt()); + if (!Body || Body->getNumElements() != 1) + return {}; + + IfStmt *InnerIf = + dyn_cast_or_null(Body->getElement(0).dyn_cast()); + if (!InnerIf) + return {}; + + // Inner 'if' statement also cannot have 'else' clause. + if (InnerIf->getElseStmt()) + return {}; - } Walker(CursorInfo.TrailingStmt->getStartLoc()); - Walker.walk(CursorInfo.TrailingStmt); - return Walker.IfInfo; + return {OuterIf, InnerIf}; } -bool RefactoringActionCollapseNestedIfExpr:: -isApplicable(ResolvedCursorInfo Tok, DiagnosticEngine &Diag) { - return findCollapseNestedIfTarget(Tok).isValid(); +bool RefactoringActionCollapseNestedIfStmt:: +isApplicable(ResolvedCursorInfo CursorInfo, DiagnosticEngine &Diag) { + return findCollapseNestedIfTarget(CursorInfo).first; } -bool RefactoringActionCollapseNestedIfExpr::performChange() { +bool RefactoringActionCollapseNestedIfStmt::performChange() { auto Target = findCollapseNestedIfTarget(CursorInfo); - if (!Target.isValid()) + if (!Target.first) return true; - auto OuterIfConds = Target.OuterIf->getCond().vec(); - auto InnerIfConds = Target.InnerIf->getCond().vec(); + auto OuterIf = Target.first; + auto InnerIf = Target.second; - EditorConsumerInsertStream OS(EditConsumer, SM, - Lexer::getCharSourceRangeFromSourceRange( - SM, Target.OuterIf->getSourceRange())); - - OS << tok::kw_if << " "; - for (auto CI = OuterIfConds.begin(); CI != OuterIfConds.end(); ++CI) { - OS << (CI != OuterIfConds.begin() ? ", " : ""); - OS << Lexer::getCharSourceRangeFromSourceRange( - SM, CI->getSourceRange()).str(); - } - for (auto CI = InnerIfConds.begin(); CI != InnerIfConds.end(); ++CI) { - OS << ", " << Lexer::getCharSourceRangeFromSourceRange( - SM, CI->getSourceRange()).str(); - } - auto ThenStatementText = Lexer::getCharSourceRangeFromSourceRange( - SM, Target.InnerIf->getThenStmt()->getSourceRange()).str(); - OS << " " << ThenStatementText; + EditorConsumerInsertStream OS( + EditConsumer, SM, + Lexer::getCharSourceRangeFromSourceRange(SM, OuterIf->getSourceRange())); + + OS << tok::kw_if << " "; + + // Emit conditions. + bool first = true; + for (auto &C : llvm::concat(OuterIf->getCond(), + InnerIf->getCond())) { + if (first) + first = false; + else + OS << ", "; + OS << Lexer::getCharSourceRangeFromSourceRange(SM, C.getSourceRange()) + .str(); + } + + // Emit body. + OS << " "; + OS << Lexer::getCharSourceRangeFromSourceRange( + SM, InnerIf->getThenStmt()->getSourceRange()) + .str(); return false; } @@ -2695,6 +2737,10 @@ static CallExpr *findTrailingClosureTarget(SourceManager &SM, return nullptr; CallExpr *CE = cast(Finder.getContexts().back().get()); + if (CE->hasTrailingClosure()) + // Call expression already has a trailing closure. + return nullptr; + // The last arugment is a closure? Expr *Args = CE->getArg(); if (!Args) @@ -3023,9 +3069,14 @@ collectAvailableRefactorings(SourceFile *SF, RangeConfig Range, Range.getEnd(SF->getASTContext().SourceMgr)); ResolvedRangeInfo Result = Resolver.resolve(); + bool enableInternalRefactoring = getenv("SWIFT_ENABLE_INTERNAL_REFACTORING_ACTIONS"); + #define RANGE_REFACTORING(KIND, NAME, ID) \ if (RefactoringAction##KIND::isApplicable(Result, DiagEngine)) \ Scratch.push_back(RefactoringKind::KIND); +#define INTERNAL_RANGE_REFACTORING(KIND, NAME, ID) \ + if (enableInternalRefactoring) \ + RANGE_REFACTORING(KIND, NAME, ID) #include "swift/IDE/RefactoringKinds.def" RangeStartMayNeedRename = rangeStartMayNeedRename(Result); diff --git a/lib/IDE/SourceEntityWalker.cpp b/lib/IDE/SourceEntityWalker.cpp index 98f1a66f0faa2..188cadf702180 100644 --- a/lib/IDE/SourceEntityWalker.cpp +++ b/lib/IDE/SourceEntityWalker.cpp @@ -211,11 +211,18 @@ std::pair SemaAnnotator::walkToStmtPre(Stmt *S) { bool TraverseChildren = SEWalker.walkToStmtPre(S); if (TraverseChildren) { if (auto *DeferS = dyn_cast(S)) { + // Since 'DeferStmt::getTempDecl()' is marked as implicit, we manually + // walk into the body. if (auto *FD = DeferS->getTempDecl()) { auto *RetS = FD->getBody()->walk(*this); - // Already walked children. - return { false, RetS }; + assert(RetS == FD->getBody()); + (void)RetS; } + bool Continue = SEWalker.walkToStmtPost(DeferS); + if (!Continue) + Cancelled = true; + // Already walked children. + return { false, Continue ? DeferS : nullptr }; } } return { TraverseChildren, S }; diff --git a/lib/IDE/SwiftSourceDocInfo.cpp b/lib/IDE/SwiftSourceDocInfo.cpp index 6b1fc5f205c3d..d81d9326a2c8f 100644 --- a/lib/IDE/SwiftSourceDocInfo.cpp +++ b/lib/IDE/SwiftSourceDocInfo.cpp @@ -581,7 +581,7 @@ std::pair NameMatcher::walkToPatternPre(Pattern *P) { if (isDone() || shouldSkip(P->getSourceRange())) return std::make_pair(false, P); - tryResolve(ASTWalker::ParentTy(P), P->getLoc()); + tryResolve(ASTWalker::ParentTy(P), P->getStartLoc()); return std::make_pair(!isDone(), P); } diff --git a/lib/IDE/SyntaxModel.cpp b/lib/IDE/SyntaxModel.cpp index eee9ad9b2c332..03f149eda1984 100644 --- a/lib/IDE/SyntaxModel.cpp +++ b/lib/IDE/SyntaxModel.cpp @@ -16,6 +16,7 @@ #include "swift/AST/Decl.h" #include "swift/AST/Expr.h" #include "swift/AST/Pattern.h" +#include "swift/AST/ParameterList.h" #include "swift/AST/Module.h" #include "swift/AST/Stmt.h" #include "swift/AST/TypeRepr.h" @@ -677,11 +678,16 @@ std::pair ModelASTWalker::walkToStmtPre(Stmt *S) { } } else if (auto *DeferS = dyn_cast(S)) { + // Since 'DeferStmt::getTempDecl()' is marked as implicit, we manually walk + // into the body. if (auto *FD = DeferS->getTempDecl()) { auto *RetS = FD->getBody()->walk(*this); - // Already walked children. - return { false, RetS }; + assert(RetS == FD->getBody()); + (void)RetS; + walkToStmtPost(DeferS); } + // Already walked children. + return { false, DeferS }; } return { true, S }; @@ -870,8 +876,15 @@ bool ModelASTWalker::walkToDeclPre(Decl *D) { SN.Kind = SyntaxStructureKind::EnumElement; SN.Range = charSourceRangeFromSourceRange(SM, EnumElemD->getSourceRange()); - SN.NameRange = CharSourceRange(EnumElemD->getNameLoc(), - EnumElemD->getName().getLength()); + if (auto ParamList = EnumElemD->getParameterList()) { + SourceRange NameRange = SourceRange(EnumElemD->getNameLoc(), + ParamList->getSourceRange().End); + SN.NameRange = charSourceRangeFromSourceRange(SM, NameRange); + } else { + SN.NameRange = CharSourceRange(EnumElemD->getNameLoc(), + EnumElemD->getName().getLength()); + } + if (auto *E = EnumElemD->getRawValueExpr()) { SourceRange ElemRange = E->getSourceRange(); SN.Elements.emplace_back(SyntaxStructureElementKind::InitExpr, diff --git a/lib/IDE/TypeReconstruction.cpp b/lib/IDE/TypeReconstruction.cpp index 0256f66e7ff76..03eaa60b63533 100644 --- a/lib/IDE/TypeReconstruction.cpp +++ b/lib/IDE/TypeReconstruction.cpp @@ -596,7 +596,7 @@ FindNamedDecls(ASTContext *ast, const DeclBaseName &name, VisitNodeResult &resul // FIXME: Need a more complete/robust lookup mechanism that can handle // declarations in sub-stmts, etc. UnqualifiedLookup lookup(name, FD, ast->getLazyResolver(), - /*IsKnownPrivate=*/false, FD->getEndLoc()); + FD->getEndLoc()); if (!lookup.isSuccess()) { result._error = "no decl found in function"; } else { @@ -668,9 +668,10 @@ static DeclKind GetKindAsDeclKind(Demangle::Node::Kind node_kind) { return DeclKind::Enum; case Demangle::Node::Kind::Protocol: return DeclKind::Protocol; + case Demangle::Node::Kind::Variable: + return DeclKind::Var; default: llvm_unreachable("Missing alias"); - // FIXME: can we 'log' getNodeKindString(node_kind)) } } @@ -755,6 +756,51 @@ static void VisitNodeAssociatedTypeRef( ident->getText().str().c_str()); } +static void VisitNodeGenericTypealias(ASTContext *ast, + Demangle::NodePointer cur_node, + VisitNodeResult &result) { + VisitNodeResult generic_type_result; + VisitNodeResult template_types_result; + + Demangle::Node::iterator end = cur_node->end(); + for (Demangle::Node::iterator pos = cur_node->begin(); pos != end; ++pos) { + const Demangle::Node::Kind child_node_kind = (*pos)->getKind(); + switch (child_node_kind) { + case Demangle::Node::Kind::Type: + VisitNode(ast, *pos, generic_type_result); + break; + case Demangle::Node::Kind::TypeList: + VisitNode(ast, *pos, template_types_result); + break; + default: + break; + } + } + + if (generic_type_result._decls.size() != 1 || + generic_type_result._types.size() != 1 || + template_types_result._types.empty()) + return; + + auto *genericTypeAlias = + cast(generic_type_result._decls.front()); + GenericSignature *signature = genericTypeAlias->getGenericSignature(); + // FIXME: handle conformances. + SubstitutionMap subMap = + SubstitutionMap::get(signature, template_types_result._types, + ArrayRef({})); + Type parentType; + if (auto nominal = genericTypeAlias->getDeclContext() + ->getAsNominalTypeOrNominalTypeExtensionContext()) { + parentType = nominal->getDeclaredInterfaceType(); + } + NameAliasType *NAT = + NameAliasType::get(genericTypeAlias, parentType, subMap, + genericTypeAlias->getUnderlyingTypeLoc().getType()); + result._types.push_back(NAT); + result._decls.push_back(genericTypeAlias); +} + static void VisitNodeBoundGeneric( ASTContext *ast, Demangle::NodePointer cur_node, VisitNodeResult &result) { @@ -1832,7 +1878,7 @@ static void VisitNodeInOut( VisitNodeResult type_result; VisitNode(ast, cur_node->getFirstChild(), type_result); if (type_result._types.size() == 1 && type_result._types[0]) { - result._types.push_back(Type(InOutType::get(type_result._types[0]))); + result._types.push_back(InOutType::get(type_result._types[0])); } else { result._error = "couldn't resolve referent type"; } @@ -2049,16 +2095,17 @@ static void VisitNodeTupleElement( } } - if (tuple_type_result._error.empty() && - tuple_type_result._types.size() == 1) { - if (!tuple_name.empty()) - result._tuple_type_element = - TupleTypeElt(tuple_type_result._types.front().getPointer(), - ast->getIdentifier(tuple_name)); - else - result._tuple_type_element = - TupleTypeElt(tuple_type_result._types.front().getPointer()); - } + if (!tuple_type_result._error.empty() || tuple_type_result._types.size() != 1) + return; + + auto tupleType = tuple_type_result._types.front(); + auto typeFlags = ParameterTypeFlags(); + typeFlags = typeFlags.withInOut(tupleType->is()); + if (auto *inOutTy = tupleType->getAs()) + tupleType = inOutTy->getObjectType(); + Identifier idName = + tuple_name.empty() ? Identifier() : ast->getIdentifier(tuple_name); + result._tuple_type_element = TupleTypeElt(tupleType, idName, typeFlags); } static void VisitNodeTypeList( @@ -2202,6 +2249,10 @@ static void VisitNode( VisitNodeBoundGeneric(ast, node, result); break; + case Demangle::Node::Kind::BoundGenericTypeAlias: + VisitNodeGenericTypealias(ast, node, result); + break; + case Demangle::Node::Kind::BuiltinTypeName: VisitNodeBuiltinTypeName(ast, node, result); break; diff --git a/lib/IDE/Utils.cpp b/lib/IDE/Utils.cpp index 4482146783c80..37edb171926ca 100644 --- a/lib/IDE/Utils.cpp +++ b/lib/IDE/Utils.cpp @@ -375,7 +375,7 @@ static void walkOverriddenClangDecls(const clang::NamedDecl *D, const FnTy &Fn){ void ide::walkOverriddenDecls(const ValueDecl *VD, - std::function)> Fn) { for (auto CurrOver = VD; CurrOver; CurrOver = CurrOver->getOverriddenDecl()) { if (CurrOver != VD) diff --git a/lib/IRGen/AllocStackHoisting.cpp b/lib/IRGen/AllocStackHoisting.cpp index acb15f7c082cf..9bd43945f517e 100644 --- a/lib/IRGen/AllocStackHoisting.cpp +++ b/lib/IRGen/AllocStackHoisting.cpp @@ -144,19 +144,7 @@ void Partition::assignStackLocation( /// Returns a single dealloc_stack user of the alloc_stack or nullptr otherwise. static SILInstruction *getSingleDeallocStack(AllocStackInst *ASI) { - SILInstruction *Dealloc = nullptr; - for (auto *U : ASI->getUses()) { - auto *Inst = U->getUser(); - if (isa(Inst)) { - if (Dealloc == nullptr) { - Dealloc = Inst; - continue; - } - // Already saw a dealloc_stack. - return nullptr; - } - } - return Dealloc; + return ASI->getSingleDeallocStack(); } namespace { diff --git a/lib/IRGen/CallEmission.h b/lib/IRGen/CallEmission.h index c6ab1310a76ea..db9d3cb385556 100644 --- a/lib/IRGen/CallEmission.h +++ b/lib/IRGen/CallEmission.h @@ -75,7 +75,7 @@ class CallEmission { const Callee &getCallee() const { return CurCallee; } - SubstitutionList getSubstitutions() const { + SubstitutionMap getSubstitutions() const { return CurCallee.getSubstitutions(); } diff --git a/lib/IRGen/Callee.h b/lib/IRGen/Callee.h index 41c24ff8183b2..a6e136a988657 100644 --- a/lib/IRGen/Callee.h +++ b/lib/IRGen/Callee.h @@ -25,7 +25,6 @@ #include "Signature.h" namespace swift { - class Substitution; namespace irgen { class Callee; @@ -41,13 +40,13 @@ namespace irgen { /// The archetype substitutions under which the function is being /// called. - std::vector Substitutions; + SubstitutionMap Substitutions; CalleeInfo(CanSILFunctionType origFnType, CanSILFunctionType substFnType, - SubstitutionList substitutions) + SubstitutionMap substitutions) : OrigFnType(origFnType), SubstFnType(substFnType), - Substitutions(substitutions.begin(), substitutions.end()) { + Substitutions(substitutions) { } }; @@ -159,8 +158,11 @@ namespace irgen { return Info.SubstFnType; } - bool hasSubstitutions() const { return !Info.Substitutions.empty(); } - SubstitutionList getSubstitutions() const { return Info.Substitutions; } + bool hasSubstitutions() const { + return Info.Substitutions.hasAnySubstitutableParams(); + } + + SubstitutionMap getSubstitutions() const { return Info.Substitutions; } const FunctionPointer &getFunctionPointer() const { return Fn; } diff --git a/lib/IRGen/ConstantBuilder.h b/lib/IRGen/ConstantBuilder.h index c82049ff74149..44e1715eba511 100644 --- a/lib/IRGen/ConstantBuilder.h +++ b/lib/IRGen/ConstantBuilder.h @@ -106,7 +106,7 @@ class ConstantAggregateBuilderBase Size getNextOffsetFromGlobal() const { return Size(super::getNextOffsetFromGlobal().getQuantity()); } - + void addAlignmentPadding(Alignment align) { auto misalignment = getNextOffsetFromGlobal() % IGM().getPointerAlignment(); if (misalignment != Size(0)) @@ -119,10 +119,26 @@ class ConstantAggregateBuilderBase class ConstantArrayBuilder : public clang::CodeGen::ConstantArrayBuilderTemplateBase< ConstantInitBuilderTraits> { +private: + llvm::Type *EltTy; + public: - template - ConstantArrayBuilder(As &&... args) - : ConstantArrayBuilderTemplateBase(std::forward(args)...) {} + ConstantArrayBuilder(InitBuilder &builder, + AggregateBuilderBase *parent, + llvm::Type *eltTy) + : ConstantArrayBuilderTemplateBase(builder, parent, eltTy), EltTy(eltTy) {} + + void addAlignmentPadding(Alignment align) { + auto misalignment = getNextOffsetFromGlobal() % align; + if (misalignment == Size(0)) + return; + + auto eltSize = IGM().DataLayout.getTypeStoreSize(EltTy); + assert(misalignment.getValue() % eltSize == 0); + + for (unsigned i = 0, n = misalignment.getValue() / eltSize; i != n; ++i) + add(llvm::Constant::getNullValue(EltTy)); + } }; class ConstantStructBuilder diff --git a/lib/IRGen/DebugTypeInfo.cpp b/lib/IRGen/DebugTypeInfo.cpp index 49faa97c0b92d..dee595841b1aa 100644 --- a/lib/IRGen/DebugTypeInfo.cpp +++ b/lib/IRGen/DebugTypeInfo.cpp @@ -103,7 +103,7 @@ DebugTypeInfo DebugTypeInfo::getGlobal(SILGlobalVariable *GV, // the type hasn't been mucked with by an optimization pass. DeclContext *DC = nullptr; GenericEnvironment *GE = nullptr; - auto LowTy = GV->getLoweredType().getSwiftRValueType(); + auto LowTy = GV->getLoweredType().getASTType(); auto *Type = LowTy.getPointer(); if (auto *Decl = GV->getDecl()) { DC = Decl->getDeclContext(); diff --git a/lib/IRGen/DebugTypeInfo.h b/lib/IRGen/DebugTypeInfo.h index 9d1a15d9f261f..a998462718433 100644 --- a/lib/IRGen/DebugTypeInfo.h +++ b/lib/IRGen/DebugTypeInfo.h @@ -96,11 +96,9 @@ class DebugTypeInfo { /// LValues, inout args, and Archetypes are implicitly indirect by /// virtue of their DWARF type. // - // FIXME: Should this check if the lowered SILType is address only - // instead? Otherwise optionals of archetypes etc will still have - // 'isImplicitlyIndirect()' return false. + // FIXME: There exists an inverse workaround in LLDB. Both should be removed. bool isImplicitlyIndirect() const { - return Type->hasLValueType() || isArchetype() || Type->is(); + return isArchetype(); } bool isNull() const { return Type == nullptr; } diff --git a/lib/IRGen/FixedTypeInfo.h b/lib/IRGen/FixedTypeInfo.h index 9cabc7fb9359c..ac362534424bb 100644 --- a/lib/IRGen/FixedTypeInfo.h +++ b/lib/IRGen/FixedTypeInfo.h @@ -35,11 +35,6 @@ namespace irgen { /// implementing a type that has a statically known layout. class FixedTypeInfo : public TypeInfo { private: - /// The storage size of this type in bytes. This may be zero even - /// for well-formed and complete types, such as a trivial enum or - /// tuple. - Size StorageSize; - /// The spare bit mask for this type. SpareBits[0] is the LSB of the first /// byte. This may be empty if the type has no spare bits. SpareBitVector SpareBits; @@ -49,31 +44,36 @@ class FixedTypeInfo : public TypeInfo { const SpareBitVector &spareBits, Alignment align, IsPOD_t pod, IsBitwiseTakable_t bt, IsFixedSize_t alwaysFixedSize, - SpecialTypeInfoKind stik = STIK_Fixed) - : TypeInfo(type, align, pod, bt, alwaysFixedSize, stik), - StorageSize(size), SpareBits(spareBits) { + SpecialTypeInfoKind stik = SpecialTypeInfoKind::Fixed) + : TypeInfo(type, align, pod, bt, alwaysFixedSize, IsABIAccessible, stik), + SpareBits(spareBits) { assert(SpareBits.size() == size.getValueInBits()); assert(isFixedSize()); + Bits.FixedTypeInfo.Size = size.getValue(); + assert(Bits.FixedTypeInfo.Size == size.getValue() && "truncation"); } FixedTypeInfo(llvm::Type *type, Size size, SpareBitVector &&spareBits, Alignment align, IsPOD_t pod, IsBitwiseTakable_t bt, IsFixedSize_t alwaysFixedSize, - SpecialTypeInfoKind stik = STIK_Fixed) - : TypeInfo(type, align, pod, bt, alwaysFixedSize, stik), - StorageSize(size), SpareBits(std::move(spareBits)) { + SpecialTypeInfoKind stik = SpecialTypeInfoKind::Fixed) + : TypeInfo(type, align, pod, bt, alwaysFixedSize, IsABIAccessible, stik), + SpareBits(std::move(spareBits)) { assert(SpareBits.size() == size.getValueInBits()); assert(isFixedSize()); + Bits.FixedTypeInfo.Size = size.getValue(); + assert(Bits.FixedTypeInfo.Size == size.getValue() && "truncation"); } public: // This is useful for metaprogramming. static bool isFixed() { return true; } + static IsABIAccessible_t isABIAccessible() { return IsABIAccessible; } /// Whether this type is known to be empty. bool isKnownEmpty(ResilienceExpansion expansion) const { - return (isFixedSize(expansion) && StorageSize.isZero()); + return (isFixedSize(expansion) && getFixedSize().isZero()); } StackAddress allocateStack(IRGenFunction &IGF, SILType T, @@ -99,7 +99,8 @@ class FixedTypeInfo : public TypeInfo { llvm::Constant *getStaticStride(IRGenModule &IGM) const override; void completeFixed(Size size, Alignment alignment) { - StorageSize = size; + Bits.FixedTypeInfo.Size = size.getValue(); + assert(Bits.FixedTypeInfo.Size == size.getValue() && "truncation"); setStorageAlignment(alignment); } @@ -110,7 +111,7 @@ class FixedTypeInfo : public TypeInfo { /// Returns the known, fixed size required to store a value of this type. Size getFixedSize() const { - return StorageSize; + return Size(Bits.FixedTypeInfo.Size); } /// Returns the (assumed fixed) stride of the storage for this @@ -120,7 +121,7 @@ class FixedTypeInfo : public TypeInfo { /// The stride is at least one, even for zero-sized types, like the empty /// tuple. Size getFixedStride() const { - Size s = StorageSize.roundUpToAlignment(getFixedAlignment()); + Size s = getFixedSize().roundUpToAlignment(getFixedAlignment()); if (s.isZero()) s = Size(1); return s; diff --git a/lib/IRGen/GenArchetype.cpp b/lib/IRGen/GenArchetype.cpp index a74471d787b2a..27f50f8cf968c 100644 --- a/lib/IRGen/GenArchetype.cpp +++ b/lib/IRGen/GenArchetype.cpp @@ -89,7 +89,8 @@ namespace { class OpaqueArchetypeTypeInfo : public ResilientTypeInfo { - OpaqueArchetypeTypeInfo(llvm::Type *type) : ResilientTypeInfo(type) {} + OpaqueArchetypeTypeInfo(llvm::Type *type) + : ResilientTypeInfo(type, IsABIAccessible) {} public: static const OpaqueArchetypeTypeInfo *create(llvm::Type *type) { @@ -99,7 +100,7 @@ class OpaqueArchetypeTypeInfo void collectMetadataForOutlining(OutliningMetadataCollector &collector, SILType T) const override { // We'll need formal type metadata for this archetype. - collector.collectFormalTypeMetadata(T.getSwiftRValueType()); + collector.collectTypeMetadataForLayout(T); } }; @@ -173,19 +174,6 @@ llvm::Value *irgen::emitArchetypeWitnessTableRef(IRGenFunction &IGF, auto wtable = IGF.tryGetLocalTypeData(archetype, localDataKind); if (wtable) return wtable; - // It can happen with class constraints that Sema will consider a - // constraint to be abstract, but the minimized signature will - // eliminate it as concrete. Handle this by performing a concrete - // lookup. - // TODO: maybe Sema shouldn't ever do this? - if (Type classBound = archetype->getSuperclass()) { - auto conformance = - IGF.IGM.getSwiftModule()->lookupConformance(classBound, protocol); - if (conformance && conformance->isConcrete()) { - return emitWitnessTableRef(IGF, archetype, *conformance); - } - } - // If we don't have an environment, this must be an implied witness table // reference. // FIXME: eliminate this path when opened types have generic environments. diff --git a/lib/IRGen/GenBuiltin.cpp b/lib/IRGen/GenBuiltin.cpp index 847a35ba80784..c176966981eb7 100644 --- a/lib/IRGen/GenBuiltin.cpp +++ b/lib/IRGen/GenBuiltin.cpp @@ -86,15 +86,15 @@ static void emitCompareBuiltin(IRGenFunction &IGF, Explosion &result, static void emitTypeTraitBuiltin(IRGenFunction &IGF, Explosion &out, Explosion &args, - SubstitutionList substitutions, + SubstitutionMap substitutions, TypeTraitResult (TypeBase::*trait)()) { - assert(substitutions.size() == 1 + assert(substitutions.getReplacementTypes().size() == 1 && "type trait should have gotten single type parameter"); args.claimNext(); // Lower away the trait to a tristate 0 = no, 1 = yes, 2 = maybe. unsigned result; - switch ((substitutions[0].getReplacement().getPointer()->*trait)()) { + switch ((substitutions.getReplacementTypes()[0].getPointer()->*trait)()) { case TypeTraitResult::IsNot: result = 0; break; @@ -119,7 +119,7 @@ getLoweredTypeAndTypeInfo(IRGenModule &IGM, Type unloweredType) { void irgen::emitBuiltinCall(IRGenFunction &IGF, const BuiltinInfo &Builtin, Identifier FnId, SILType resultType, Explosion &args, Explosion &out, - SubstitutionList substitutions) { + SubstitutionMap substitutions) { if (Builtin.ID == BuiltinValueKind::UnsafeGuaranteedEnd) { // Just consume the incoming argument. assert(args.size() == 1 && "Expecting one incoming argument"); @@ -146,7 +146,7 @@ void irgen::emitBuiltinCall(IRGenFunction &IGF, const BuiltinInfo &Builtin, if (Builtin.ID == BuiltinValueKind::Sizeof) { (void)args.claimAll(); auto valueTy = getLoweredTypeAndTypeInfo(IGF.IGM, - substitutions[0].getReplacement()); + substitutions.getReplacementTypes()[0]); out.add(valueTy.second.getSize(IGF, valueTy.first)); return; } @@ -154,7 +154,7 @@ void irgen::emitBuiltinCall(IRGenFunction &IGF, const BuiltinInfo &Builtin, if (Builtin.ID == BuiltinValueKind::Strideof) { (void)args.claimAll(); auto valueTy = getLoweredTypeAndTypeInfo(IGF.IGM, - substitutions[0].getReplacement()); + substitutions.getReplacementTypes()[0]); out.add(valueTy.second.getStride(IGF, valueTy.first)); return; } @@ -162,7 +162,7 @@ void irgen::emitBuiltinCall(IRGenFunction &IGF, const BuiltinInfo &Builtin, if (Builtin.ID == BuiltinValueKind::Alignof) { (void)args.claimAll(); auto valueTy = getLoweredTypeAndTypeInfo(IGF.IGM, - substitutions[0].getReplacement()); + substitutions.getReplacementTypes()[0]); // The alignof value is one greater than the alignment mask. out.add(IGF.Builder.CreateAdd( valueTy.second.getAlignmentMask(IGF, valueTy.first), @@ -173,7 +173,7 @@ void irgen::emitBuiltinCall(IRGenFunction &IGF, const BuiltinInfo &Builtin, if (Builtin.ID == BuiltinValueKind::IsPOD) { (void)args.claimAll(); auto valueTy = getLoweredTypeAndTypeInfo(IGF.IGM, - substitutions[0].getReplacement()); + substitutions.getReplacementTypes()[0]); out.add(valueTy.second.getIsPOD(IGF, valueTy.first)); return; } @@ -206,37 +206,57 @@ void irgen::emitBuiltinCall(IRGenFunction &IGF, const BuiltinInfo &Builtin, if (IID == llvm::Intrinsic::instrprof_increment) { // If we import profiling intrinsics from a swift module but profiling is // not enabled, ignore the increment. - if (!IGF.getSILModule().getOptions().GenerateProfile) + SILModule &SILMod = IGF.getSILModule(); + const auto &Opts = SILMod.getOptions(); + if (!Opts.GenerateProfile) { + (void)args.claimAll(); return; + } // Extract the PGO function name. auto *NameGEP = cast(args.claimNext()); auto *NameGV = dyn_cast(NameGEP->stripPointerCasts()); - if (NameGV) { - auto *NameC = NameGV->getInitializer(); - StringRef Name = cast(NameC)->getRawDataValues(); - StringRef PGOFuncName = Name.rtrim(StringRef("\0", 1)); - - // Point the increment call to the right function name variable. - std::string PGOFuncNameVar = llvm::getPGOFuncNameVarName( - PGOFuncName, llvm::GlobalValue::LinkOnceAnyLinkage); - auto *FuncNamePtr = IGF.IGM.Module.getNamedGlobal(PGOFuncNameVar); - if (!FuncNamePtr) - FuncNamePtr = llvm::createPGOFuncNameVar( - *IGF.IGM.getModule(), llvm::GlobalValue::LinkOnceAnyLinkage, - PGOFuncName); - - llvm::SmallVector Indices(2, NameGEP->getOperand(1)); - NameGEP = llvm::ConstantExpr::getGetElementPtr( - ((llvm::PointerType *)FuncNamePtr->getType())->getElementType(), - FuncNamePtr, makeArrayRef(Indices)); + + // TODO: The SIL optimizer may rewrite the name argument in a way that + // makes it impossible to lower. Until that issue is fixed, defensively + // refuse to lower ill-formed intrinsics (rdar://39146527). + if (!NameGV) { + (void)args.claimAll(); + return; } + auto *NameC = NameGV->getInitializer(); + StringRef Name = cast(NameC)->getRawDataValues(); + StringRef PGOFuncName = Name.rtrim(StringRef("\0", 1)); + + // Point the increment call to the right function name variable. + std::string PGOFuncNameVar = llvm::getPGOFuncNameVarName( + PGOFuncName, llvm::GlobalValue::LinkOnceAnyLinkage); + auto *FuncNamePtr = IGF.IGM.Module.getNamedGlobal(PGOFuncNameVar); + if (!FuncNamePtr) + FuncNamePtr = llvm::createPGOFuncNameVar( + *IGF.IGM.getModule(), llvm::GlobalValue::LinkOnceAnyLinkage, + PGOFuncName); + + llvm::SmallVector Indices(2, NameGEP->getOperand(1)); + NameGEP = llvm::ConstantExpr::getGetElementPtr( + ((llvm::PointerType *)FuncNamePtr->getType())->getElementType(), + FuncNamePtr, makeArrayRef(Indices)); + // Replace the placeholder value with the new GEP. Explosion replacement; replacement.add(NameGEP); replacement.add(args.claimAll()); args = std::move(replacement); + + if (Opts.EmitProfileCoverageMapping) { + // Update the associated coverage mapping: it's now safe to emit, because + // a symtab entry for this function is guaranteed (r://39146527). + auto &coverageMaps = SILMod.getCoverageMaps(); + auto CovMapIt = coverageMaps.find(PGOFuncName); + if (CovMapIt != coverageMaps.end()) + CovMapIt->second->setSymtabEntryGuaranteed(); + } } if (IID != llvm::Intrinsic::not_intrinsic) { @@ -793,7 +813,7 @@ if (Builtin.ID == BuiltinValueKind::id) { \ llvm::Value *count = args.claimNext(); auto valueTy = getLoweredTypeAndTypeInfo(IGF.IGM, - substitutions[0].getReplacement()); + substitutions.getReplacementTypes()[0]); ptr = IGF.Builder.CreateBitCast(ptr, valueTy.second.getStorageType()->getPointerTo()); @@ -819,7 +839,7 @@ if (Builtin.ID == BuiltinValueKind::id) { \ llvm::Value *count = args.claimNext(); auto valueTy = getLoweredTypeAndTypeInfo(IGF.IGM, - substitutions[0].getReplacement()); + substitutions.getReplacementTypes()[0]); dest = IGF.Builder.CreateBitCast(dest, valueTy.second.getStorageType()->getPointerTo()); @@ -876,7 +896,7 @@ if (Builtin.ID == BuiltinValueKind::id) { \ if (Builtin.ID == BuiltinValueKind::ZeroInitializer) { // Build a zero initializer of the result type. auto valueTy = getLoweredTypeAndTypeInfo(IGF.IGM, - substitutions[0].getReplacement()); + substitutions.getReplacementTypes()[0]); auto schema = valueTy.second.getSchema(); for (auto &elt : schema) { out.add(llvm::Constant::getNullValue(elt.getScalarType())); @@ -886,7 +906,7 @@ if (Builtin.ID == BuiltinValueKind::id) { \ if (Builtin.ID == BuiltinValueKind::GetObjCTypeEncoding) { (void)args.claimAll(); - Type valueTy = substitutions[0].getReplacement(); + Type valueTy = substitutions.getReplacementTypes()[0]; // Get the type encoding for the associated clang type. auto clangTy = IGF.IGM.getClangType(valueTy->getCanonicalType()); std::string encoding; diff --git a/lib/IRGen/GenBuiltin.h b/lib/IRGen/GenBuiltin.h index dee62ed29b0ff..0a5ddf656a67f 100644 --- a/lib/IRGen/GenBuiltin.h +++ b/lib/IRGen/GenBuiltin.h @@ -18,7 +18,7 @@ #ifndef SWIFT_IRGEN_GENBUILTIN_H #define SWIFT_IRGEN_GENBUILTIN_H -#include "swift/AST/SubstitutionList.h" +#include "swift/AST/SubstitutionMap.h" #include "swift/Basic/LLVM.h" namespace swift { @@ -34,7 +34,7 @@ namespace irgen { void emitBuiltinCall(IRGenFunction &IGF, const BuiltinInfo &builtin, Identifier fnId, SILType resultType, Explosion &args, Explosion &result, - SubstitutionList substitutions); + SubstitutionMap substitutions); } // end namespace irgen } // end namespace swift diff --git a/lib/IRGen/GenCall.cpp b/lib/IRGen/GenCall.cpp index b2157cefb4909..0ba283a86cbdf 100644 --- a/lib/IRGen/GenCall.cpp +++ b/lib/IRGen/GenCall.cpp @@ -1637,10 +1637,8 @@ void CallEmission::emitToMemory(Address addr, // result that's actually being passed indirectly. // // TODO: SIL address lowering should be able to handle such cases earlier. - CanType origResultType = - origFnType->getDirectFormalResultsType().getSwiftRValueType(); - CanType substResultType = - substFnType->getDirectFormalResultsType().getSwiftRValueType(); + auto origResultType = origFnType->getDirectFormalResultsType().getASTType(); + auto substResultType = substFnType->getDirectFormalResultsType().getASTType(); if (origResultType->hasTypeParameter()) origResultType = IGF.IGM.getGenericEnvironment() diff --git a/lib/IRGen/GenCast.cpp b/lib/IRGen/GenCast.cpp index b4c5de758ffe5..42489c46ba087 100644 --- a/lib/IRGen/GenCast.cpp +++ b/lib/IRGen/GenCast.cpp @@ -90,15 +90,14 @@ FailableCastResult irgen::emitClassIdenticalCast(IRGenFunction &IGF, SILType fromType, SILType toType) { // Check metatype objects directly. Don't try to find their meta-metatype. - bool isMetatype = isa(fromType.getSwiftRValueType()); - if (isMetatype) { - auto metaType = cast(toType.getSwiftRValueType()); + auto isMetatype = false; + if (auto metaType = toType.getAs()) { + isMetatype = true; assert(metaType->getRepresentation() != MetatypeRepresentation::ObjC && "not implemented"); toType = IGF.IGM.getLoweredType(metaType.getInstanceType()); } // Emit a reference to the heap metadata for the target type. - const bool allowConservative = true; // If we're allowed to do a conservative check, try to just use the // global class symbol. If the class has been re-allocated, this @@ -106,17 +105,16 @@ FailableCastResult irgen::emitClassIdenticalCast(IRGenFunction &IGF, // test might fail; but it's a much faster check. // TODO: use ObjC class references llvm::Value *targetMetadata; - if (allowConservative && - (targetMetadata = - tryEmitConstantHeapMetadataRef(IGF.IGM, toType.getSwiftRValueType(), - /*allowUninitialized*/ true))) { + if ((targetMetadata = + tryEmitConstantHeapMetadataRef(IGF.IGM, toType.getASTType(), + /*allowUninitialized*/ false))) { // ok } else { targetMetadata - = emitClassHeapMetadataRef(IGF, toType.getSwiftRValueType(), + = emitClassHeapMetadataRef(IGF, toType.getASTType(), MetadataValueType::ObjCClass, MetadataState::Complete, - /*allowUninitialized*/ allowConservative); + /*allowUninitialized*/ false); } // Handle checking a metatype object's type by directly comparing the address @@ -160,7 +158,7 @@ llvm::Value *irgen::emitClassDowncast(IRGenFunction &IGF, llvm::Value *from, // If the destination type is known to have a Swift-compatible // implementation, use the most specific entrypoint. if (destClass && destClass->hasKnownSwiftImplementation()) { - metadataRef = IGF.emitTypeMetadataRef(toType.getSwiftRValueType()); + metadataRef = IGF.emitTypeMetadataRef(toType.getASTType()); switch (mode) { case CheckedCastMode::Unconditional: @@ -175,7 +173,7 @@ llvm::Value *irgen::emitClassDowncast(IRGenFunction &IGF, llvm::Value *from, // class-bounded archetype, use the most general cast entrypoint. } else if (toType.is() || destClass->getForeignClassKind()==ClassDecl::ForeignKind::CFType) { - metadataRef = IGF.emitTypeMetadataRef(toType.getSwiftRValueType()); + metadataRef = IGF.emitTypeMetadataRef(toType.getASTType()); switch (mode) { case CheckedCastMode::Unconditional: @@ -506,8 +504,8 @@ void irgen::emitScalarExistentialDowncast(IRGenFunction &IGF, CheckedCastMode mode, Optional metatypeKind, Explosion &ex) { - auto srcInstanceType = srcType.getSwiftRValueType(); - auto destInstanceType = destType.getSwiftRValueType(); + auto srcInstanceType = srcType.getASTType(); + auto destInstanceType = destType.getASTType(); while (auto metatypeType = dyn_cast( destInstanceType)) { destInstanceType = metatypeType.getInstanceType(); @@ -720,7 +718,9 @@ void irgen::emitScalarExistentialDowncast(IRGenFunction &IGF, } } else { // Get the type metadata for the instance. - metadataValue = emitDynamicTypeOfHeapObject(IGF, value, srcType); + metadataValue = emitDynamicTypeOfHeapObject(IGF, value, + MetatypeRepresentation::Thick, + srcType); } // Look up witness tables for the protocols that need them. @@ -805,8 +805,35 @@ void irgen::emitScalarCheckedCast(IRGenFunction &IGF, assert(sourceType.isObject()); assert(targetType.isObject()); - if (auto sourceOptObjectType = sourceType.getOptionalObjectType()) { + llvm::BasicBlock *nilCheckBB = nullptr; + llvm::BasicBlock *nilMergeBB = nullptr; + + // Merge the nil check and return the merged result: either nil or the value. + auto returnNilCheckedResult = [&](IRBuilder &Builder, + Explosion &nonNilResult) { + if (nilCheckBB) { + auto notNilBB = Builder.GetInsertBlock(); + Builder.CreateBr(nilMergeBB); + + Builder.emitBlock(nilMergeBB); + // Insert result phi. + Explosion result; + while (!nonNilResult.empty()) { + auto val = nonNilResult.claimNext(); + auto valTy = cast(val->getType()); + auto nil = llvm::ConstantPointerNull::get(valTy); + auto phi = Builder.CreatePHI(valTy, 2); + phi->addIncoming(nil, nilCheckBB); + phi->addIncoming(val, notNilBB); + result.add(phi); + } + out = std::move(result); + } else { + out = std::move(nonNilResult); + } + }; + if (auto sourceOptObjectType = sourceType.getOptionalObjectType()) { // Translate the value from an enum representation to a possibly-null // representation. Note that we assume that this projection is safe // for the particular case of an optional class-reference or metatype @@ -818,6 +845,22 @@ void irgen::emitScalarCheckedCast(IRGenFunction &IGF, assert(value.empty()); value = std::move(optValue); sourceType = sourceOptObjectType; + + // We need a null-check because the runtime function can't handle null in + // some of the cases. + if (targetType.isExistentialType()) { + auto &Builder = IGF.Builder; + auto val = value.getAll()[0]; + auto isNotNil = Builder.CreateICmpNE( + val, llvm::ConstantPointerNull::get( + cast(val->getType()))); + auto *isNotNilContBB = llvm::BasicBlock::Create(IGF.IGM.getLLVMContext()); + nilMergeBB = llvm::BasicBlock::Create(IGF.IGM.getLLVMContext()); + nilCheckBB = Builder.GetInsertBlock(); + Builder.CreateCondBr(isNotNil, isNotNilContBB, nilMergeBB); + + Builder.emitBlock(isNotNilContBB); + } } // If the source value is a metatype, either do a metatype-to-metatype @@ -882,11 +925,14 @@ void irgen::emitScalarCheckedCast(IRGenFunction &IGF, } if (targetType.isExistentialType()) { + Explosion outRes; emitScalarExistentialDowncast(IGF, instance, sourceType, targetType, - mode, /*not a metatype*/ None, out); + mode, /*not a metatype*/ None, outRes); + returnNilCheckedResult(IGF.Builder, outRes); return; } + Explosion outRes; llvm::Value *result = emitClassDowncast(IGF, instance, targetType, mode); out.add(result); } diff --git a/lib/IRGen/GenClangType.cpp b/lib/IRGen/GenClangType.cpp index 7ebdb9ce5e6cd..c70b3ac5baa7b 100644 --- a/lib/IRGen/GenClangType.cpp +++ b/lib/IRGen/GenClangType.cpp @@ -441,7 +441,7 @@ GenClangType::visitBoundGenericType(CanBoundGenericType type) { SILType::getPrimitiveObjectType(type).getOptionalObjectType()) { // The underlying type could be a bridged type, which makes any // sort of casual assertion here difficult. - return Converter.convert(IGM, underlyingTy.getSwiftRValueType()); + return Converter.convert(IGM, underlyingTy.getASTType()); } auto swiftStructDecl = type->getDecl(); @@ -466,7 +466,7 @@ GenClangType::visitBoundGenericType(CanBoundGenericType type) { auto args = type.getGenericArgs(); assert(args.size() == 1 && "should have a single generic argument!"); - auto loweredArgTy = IGM.getLoweredType(args[0]).getSwiftRValueType(); + auto loweredArgTy = IGM.getLoweredType(args[0]).getASTType(); switch (kind) { case StructKind::Invalid: @@ -756,7 +756,7 @@ clang::CanQualType IRGenModule::getClangType(CanType type) { } clang::CanQualType IRGenModule::getClangType(SILType type) { - return getClangType(type.getSwiftRValueType()); + return getClangType(type.getASTType()); } clang::CanQualType IRGenModule::getClangType(SILParameterInfo params) { diff --git a/lib/IRGen/GenClass.cpp b/lib/IRGen/GenClass.cpp index 45c8546dfbc2f..d217fc952e741 100644 --- a/lib/IRGen/GenClass.cpp +++ b/lib/IRGen/GenClass.cpp @@ -141,6 +141,10 @@ namespace { unsigned NumInherited = 0; + // If the class has @objc ancestry, we lay out resiliently-typed fields + // as if they were fragile. + bool CompletelyFragileLayout = false; + // Does the class metadata require dynamic initialization above and // beyond what the runtime can automatically achieve? // @@ -199,7 +203,7 @@ namespace { Elements.push_back(Elt); if (!addField(Elements.back(), LayoutStrategy::Universal)) { // For empty tail allocated elements we still add 1 padding byte. - assert(cast(Elt.getType()).getFixedStride() == Size(1) && + assert(cast(Elt.getTypeForLayout()).getFixedStride() == Size(1) && "empty elements should have stride 1"); StructFields.push_back(llvm::ArrayType::get(IGM.Int8Ty, 1)); CurSize += Size(1); @@ -245,6 +249,10 @@ namespace { } if (superclass->hasClangNode()) { + // Perform fragile layout if the class has @objc ancestry. + if (!IGM.IRGen.Opts.EnableClassResilience) + CompletelyFragileLayout = true; + // If the superclass was imported from Objective-C, its size is // not known at compile time. However, since the field offset // vector only stores offsets of stored properties defined in @@ -321,9 +329,22 @@ namespace { const ClassLayout *abstractLayout) { for (VarDecl *var : theClass->getStoredProperties()) { SILType type = classType.getFieldType(var, IGM.getSILModule()); - auto &eltType = IGM.getTypeInfo(type); + auto &eltTypeForAccess = IGM.getTypeInfo(type); + + // For now, the Objective-C runtime cannot support dynamic layout of + // classes that contain resilient value types, so we just look through + // the resilience boundary and assume fragile layout for class ivars + // instead. + Optional generateStaticLayoutRAII; + + if (CompletelyFragileLayout && + !isa(eltTypeForAccess)) { + generateStaticLayoutRAII.emplace(IGM); + } + + auto &eltTypeForLayout = IGM.getTypeInfo(type); - if (!eltType.isFixedSize()) { + if (!eltTypeForLayout.isFixedSize()) { ClassMetadataRequiresDynamicInitialization = true; ClassHasFixedSize = false; @@ -335,7 +356,8 @@ namespace { assert(!abstractLayout || abstractLayout->getFieldIndex(var) == fieldIndex); - Elements.push_back(ElementLayout::getIncomplete(eltType)); + Elements.push_back(ElementLayout::getIncomplete(eltTypeForLayout, + eltTypeForAccess)); AllStoredProperties.push_back(var); AllFieldAccesses.push_back(getFieldAccess(abstractLayout, fieldIndex)); } @@ -399,7 +421,7 @@ void ClassTypeInfo::generateLayout(IRGenModule &IGM, SILType classType) const { builder.setAsBodyOfStruct(classTy); // Record the layout. - Layout = new StructLayout(builder, classType.getSwiftRValueType(), classTy, + Layout = new StructLayout(builder, classType.getASTType(), classTy, builder.getElements()); FieldLayout = builder.getClassLayout(); } @@ -414,7 +436,7 @@ ClassTypeInfo::createLayoutWithTailElems(IRGenModule &IGM, // Add the tail elements. for (SILType TailTy : tailTypes) { const TypeInfo &tailTI = IGM.getTypeInfo(TailTy); - builder.addTailElement(ElementLayout::getIncomplete(tailTI)); + builder.addTailElement(ElementLayout::getIncomplete(tailTI, tailTI)); } // Create a name for the new llvm type. @@ -431,7 +453,7 @@ ClassTypeInfo::createLayoutWithTailElems(IRGenModule &IGM, // Create the StructLayout, which is transfered to the caller (the caller is // responsible for deleting it). - return new StructLayout(builder, classType.getSwiftRValueType(), ResultTy, + return new StructLayout(builder, classType.getASTType(), ResultTy, builder.getElements()); } @@ -461,7 +483,7 @@ llvm::Value *IRGenFunction::emitByteOffsetGEP(llvm::Value *base, llvm::Value *offset, llvm::Type *objectType, const llvm::Twine &name) { - assert(offset->getType() == IGM.SizeTy); + assert(offset->getType() == IGM.SizeTy || offset->getType() == IGM.Int32Ty); auto addr = Builder.CreateBitCast(base, IGM.Int8PtrTy); addr = Builder.CreateInBoundsGEP(addr, offset); return Builder.CreateBitCast(addr, objectType->getPointerTo(), name); @@ -749,7 +771,7 @@ llvm::Value *irgen::emitClassAllocation(IRGenFunction &IGF, SILType selfType, bool objc, int &StackAllocSize, TailArraysRef TailArrays) { auto &classTI = IGF.getTypeInfo(selfType).as(); - auto classType = selfType.getSwiftRValueType(); + auto classType = selfType.getASTType(); // If we need to use Objective-C allocation, do so. // If the root class isn't known to use the Swift allocator, we need @@ -898,7 +920,7 @@ static void getInstanceSizeAndAlignMask(IRGenFunction &IGF, llvm::Value *&alignMask) { // Use the magic __getInstanceSizeAndAlignMask method if we can // see a declaration of it - if (getInstanceSizeByMethod(IGF, selfType.getSwiftRValueType(), + if (getInstanceSizeByMethod(IGF, selfType.getASTType(), selfClass, selfValue, size, alignMask)) return; @@ -1315,7 +1337,7 @@ namespace { if (getClass()->hasClangNode()) fields.add(IGM.getAddrOfObjCClass(getClass(), NotForDefinition)); else { - auto type = getSelfType(getClass()).getSwiftRValueType(); + auto type = getSelfType(getClass()).getASTType(); llvm::Constant *metadata = tryEmitConstantHeapMetadataRef(IGM, type, /*allowUninit*/ true); assert(metadata && @@ -2433,7 +2455,7 @@ FunctionPointer irgen::emitVirtualMethodValue(IRGenFunction &IGF, llvm::Value *metadata; if (useSuperVTable) { auto instanceTy = baseType; - if (auto metaTy = dyn_cast(baseType.getSwiftRValueType())) + if (auto metaTy = dyn_cast(baseType.getASTType())) instanceTy = SILType::getPrimitiveObjectType(metaTy.getInstanceType()); if (IGF.IGM.isResilient(instanceTy.getClassOrBoundGenericClass(), @@ -2443,7 +2465,7 @@ FunctionPointer irgen::emitVirtualMethodValue(IRGenFunction &IGF, // resilience domain. So, we need to load the superclass metadata // dynamically. - metadata = emitClassHeapMetadataRef(IGF, instanceTy.getSwiftRValueType(), + metadata = emitClassHeapMetadataRef(IGF, instanceTy.getASTType(), MetadataValueType::TypeMetadata, MetadataState::Complete); auto superField = emitAddressOfSuperclassRefInClassMetadata(IGF, metadata); @@ -2452,7 +2474,7 @@ FunctionPointer irgen::emitVirtualMethodValue(IRGenFunction &IGF, // Otherwise, we can directly load the statically known superclass's // metadata. auto superTy = instanceTy.getSuperclass(); - metadata = emitClassHeapMetadataRef(IGF, superTy.getSwiftRValueType(), + metadata = emitClassHeapMetadataRef(IGF, superTy.getASTType(), MetadataValueType::TypeMetadata, MetadataState::Complete); } diff --git a/lib/IRGen/GenCoverage.cpp b/lib/IRGen/GenCoverage.cpp index 2a021d4005f7a..f90bdde71d726 100644 --- a/lib/IRGen/GenCoverage.cpp +++ b/lib/IRGen/GenCoverage.cpp @@ -39,9 +39,9 @@ static std::string getCoverageSection(IRGenModule &IGM) { void IRGenModule::emitCoverageMapping() { std::vector Mappings; - for (const auto &M : getSILModule().getCoverageMapList()) - if (M.hasSymtabEntry()) - Mappings.push_back(&M); + for (const auto &M : getSILModule().getCoverageMaps()) + if (M.second->hasSymtabEntry()) + Mappings.push_back(M.second); // If there aren't any coverage maps, there's nothing to emit. if (Mappings.empty()) diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index 109d1469d3a53..6f2520e4032c8 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -79,6 +79,8 @@ bool IRGenerator::tryEnableLazyTypeMetadata(NominalTypeDecl *Nominal) { // is not used by the program itself. if (!Opts.shouldOptimize()) return false; + if (Opts.UseJIT) + return false; switch (Nominal->getKind()) { case DeclKind::Enum: @@ -483,14 +485,8 @@ void IRGenModule::emitSourceFile(SourceFile &SF, unsigned StartElem) { for (auto *localDecl : SF.LocalTypeDecls) emitGlobalDecl(localDecl); - SF.forAllVisibleModules([&](swift::ModuleDecl::ImportedModule import) { - swift::ModuleDecl *next = import.second; - if (next->getName() == SF.getParentModule()->getName()) - return; - - next->collectLinkLibraries([this](LinkLibrary linkLib) { + SF.collectLinkLibraries([this](LinkLibrary linkLib) { this->addLinkLibrary(linkLib); - }); }); if (ObjCInterop) @@ -554,6 +550,7 @@ emitGlobalList(IRGenModule &IGM, ArrayRef handles, init, name); var->setSection(section); var->setAlignment(alignment.getValue()); + disableAddressSanitizer(IGM, var); // Mark the variable as used if doesn't have external linkage. // (Note that we'd specifically like to not put @llvm.used in itself.) @@ -1048,7 +1045,7 @@ static bool hasCodeCoverageInstrumentation(SILFunction &f, SILModule &m) { return f.getProfiler() && m.getOptions().EmitProfileCoverageMapping; } -void IRGenerator::emitGlobalTopLevel() { +void IRGenerator::emitGlobalTopLevel(bool emitForParallelEmission) { // Generate order numbers for the functions in the SIL module that // correspond to definitions in the LLVM module. unsigned nextOrderNumber = 0; @@ -1058,12 +1055,18 @@ void IRGenerator::emitGlobalTopLevel() { FunctionOrder.insert(std::make_pair(&silFn, nextOrderNumber++)); } + // Ensure that relative symbols are collocated in the same LLVM module. + for (SILWitnessTable &wt : PrimaryIGM->getSILModule().getWitnessTableList()) { + CurrentIGMPtr IGM = getGenModule(wt.getConformance()->getDeclContext()); + if (emitForParallelEmission) + IGM->ensureRelativeSymbolCollocation(wt); + } + for (SILGlobalVariable &v : PrimaryIGM->getSILModule().getSILGlobals()) { Decl *decl = v.getDecl(); CurrentIGMPtr IGM = getGenModule(decl ? decl->getDeclContext() : nullptr); IGM->emitSILGlobalVariable(&v); } - PrimaryIGM->emitCoverageMapping(); // Emit SIL functions. for (SILFunction &f : PrimaryIGM->getSILModule()) { @@ -1097,6 +1100,9 @@ void IRGenerator::emitGlobalTopLevel() { IGM->emitSILProperty(&prop); } + // Emit code coverage mapping data. + PrimaryIGM->emitCoverageMapping(); + for (auto Iter : *this) { IRGenModule *IGM = Iter.second; IGM->finishEmitAfterTopLevel(); @@ -1836,7 +1842,8 @@ void irgen::updateLinkageForDefinition(IRGenModule &IGM, LinkInfo LinkInfo::get(IRGenModule &IGM, const LinkEntity &entity, ForDefinition_t isDefinition) { - return LinkInfo::get(IGM, IGM.getSwiftModule(), entity, isDefinition); + return LinkInfo::get(UniversalLinkageInfo(IGM), IGM.getSwiftModule(), entity, + isDefinition); } LinkInfo LinkInfo::get(const UniversalLinkageInfo &linkInfo, @@ -1845,27 +1852,21 @@ LinkInfo LinkInfo::get(const UniversalLinkageInfo &linkInfo, LinkInfo result; entity.mangle(result.Name); - std::tie(result.Linkage, result.Visibility, result.DLLStorageClass) = - getIRLinkage(linkInfo, entity.getLinkage(isDefinition), - isDefinition, entity.isWeakImported(swiftModule)); - + getIRLinkage(linkInfo, entity.getLinkage(isDefinition), isDefinition, + entity.isWeakImported(swiftModule)); result.ForDefinition = isDefinition; - return result; } -LinkInfo LinkInfo::get(const UniversalLinkageInfo &linkInfo, - StringRef name, - SILLinkage linkage, - ForDefinition_t isDefinition, +LinkInfo LinkInfo::get(const UniversalLinkageInfo &linkInfo, StringRef name, + SILLinkage linkage, ForDefinition_t isDefinition, bool isWeakImported) { LinkInfo result; result.Name += name; std::tie(result.Linkage, result.Visibility, result.DLLStorageClass) = - getIRLinkage(linkInfo, linkage, - isDefinition, isWeakImported); + getIRLinkage(linkInfo, linkage, isDefinition, isWeakImported); result.ForDefinition = isDefinition; return result; } @@ -1946,7 +1947,7 @@ bool LinkInfo::isUsed(llvm::GlobalValue::LinkageTypes Linkage, llvm::GlobalVariable *swift::irgen::createVariable( IRGenModule &IGM, LinkInfo &linkInfo, llvm::Type *storageType, Alignment alignment, DebugTypeInfo DbgTy, Optional DebugLoc, - StringRef DebugName) { + StringRef DebugName, bool inFixedBuffer) { auto name = linkInfo.getName(); llvm::GlobalValue *existingValue = IGM.Module.getNamedGlobal(name); if (existingValue) { @@ -1978,11 +1979,36 @@ llvm::GlobalVariable *swift::irgen::createVariable( if (IGM.DebugInfo && !DbgTy.isNull() && linkInfo.isForDefinition()) IGM.DebugInfo->emitGlobalVariableDeclaration( var, DebugName.empty() ? name : DebugName, name, DbgTy, - var->hasInternalLinkage(), DebugLoc); + var->hasInternalLinkage(), inFixedBuffer, DebugLoc); return var; } +void swift::irgen::disableAddressSanitizer(IRGenModule &IGM, llvm::GlobalVariable *var) { + // Add an operand to llvm.asan.globals blacklisting this global variable. + llvm::Metadata *metadata[] = { + // The global variable to blacklist. + llvm::ConstantAsMetadata::get(var), + + // Source location. Optional, unnecessary here. + nullptr, + + // Name. Optional, unnecessary here. + nullptr, + + // Whether the global is dynamically initialized. + llvm::ConstantAsMetadata::get(llvm::ConstantInt::get( + llvm::Type::getInt1Ty(IGM.Module.getContext()), false)), + + // Whether the global is blacklisted. + llvm::ConstantAsMetadata::get(llvm::ConstantInt::get( + llvm::Type::getInt1Ty(IGM.Module.getContext()), true))}; + + auto *globalNode = llvm::MDNode::get(IGM.Module.getContext(), metadata); + auto *asanMetadata = IGM.Module.getOrInsertNamedMetadata("llvm.asan.globals"); + asanMetadata->addOperand(globalNode); +} + /// Emit a global declaration. void IRGenModule::emitGlobalDecl(Decl *D) { switch (D->getKind()) { @@ -2089,6 +2115,7 @@ Address IRGenModule::getAddrOfSILGlobalVariable(SILGlobalVariable *var, llvm::Type *storageType; Size fixedSize; Alignment fixedAlignment; + bool inFixedBuffer = false; if (var->isInitializedObject()) { assert(ti.isFixedSize(expansion)); @@ -2118,6 +2145,7 @@ Address IRGenModule::getAddrOfSILGlobalVariable(SILGlobalVariable *var, } else { // Allocate a fixed-size buffer and possibly heap-allocate a payload at // runtime if the runtime size of the type does not fit in the buffer. + inFixedBuffer = true; storageType = getFixedBufferTy(); fixedSize = Size(DataLayout.getTypeAllocSize(storageType)); fixedAlignment = Alignment(DataLayout.getABITypeAlignment(storageType)); @@ -2148,20 +2176,21 @@ Address IRGenModule::getAddrOfSILGlobalVariable(SILGlobalVariable *var, gvar = createVariable(*this, link, storageTypeWithContainer, fixedAlignment); } else { - auto DbgTy = DebugTypeInfo::getGlobal(var, storageTypeWithContainer, - fixedSize, fixedAlignment); + StringRef name; + Optional loc; if (var->getDecl()) { - // If we have the VarDecl, use it for more accurate debugging information. - gvar = createVariable(*this, link, storageTypeWithContainer, - fixedAlignment, DbgTy, SILLocation(var->getDecl()), - var->getDecl()->getName().str()); + // Use the VarDecl for more accurate debugging information. + loc = var->getDecl(); + name = var->getDecl()->getName().str(); } else { - Optional loc; if (var->hasLocation()) loc = var->getLocation(); - gvar = createVariable(*this, link, storageTypeWithContainer, - fixedAlignment, DbgTy, loc, var->getName()); + name = var->getName(); } + auto DbgTy = DebugTypeInfo::getGlobal(var, storageTypeWithContainer, + fixedSize, fixedAlignment); + gvar = createVariable(*this, link, storageTypeWithContainer, + fixedAlignment, DbgTy, loc, name, inFixedBuffer); } /// Add a zero initializer. if (forDefinition) @@ -2283,11 +2312,19 @@ llvm::Function *IRGenModule::getAddrOfSILFunction(SILFunction *f, LinkInfo link = LinkInfo::get(*this, entity, forDefinition); - if (f->getInlineStrategy() == NoInline) { + switch (f->getInlineStrategy()) { + case NoInline: attrs = attrs.addAttribute(signature.getType()->getContext(), llvm::AttributeList::FunctionIndex, llvm::Attribute::NoInline); + break; + case AlwaysInline: + // FIXME: We do not currently transfer AlwaysInline since doing so results + // in test failures, which must be investigated first. + case InlineDefault: + break; } + if (isReadOnlyFunction(f)) { attrs = attrs.addAttribute(signature.getType()->getContext(), llvm::AttributeList::FunctionIndex, @@ -2714,10 +2751,10 @@ llvm::Constant *IRGenModule::emitSwiftProtocols() { StringRef sectionName; switch (TargetInfo.OutputObjectFormat) { case llvm::Triple::MachO: - sectionName = "__TEXT, __swift4_protos, regular, no_dead_strip"; + sectionName = "__TEXT, __swift5_protos, regular, no_dead_strip"; break; case llvm::Triple::ELF: - sectionName = "swift4_protocols"; + sectionName = "swift5_protocols"; break; case llvm::Triple::COFF: sectionName = ".sw5prt$B"; @@ -2728,6 +2765,9 @@ llvm::Constant *IRGenModule::emitSwiftProtocols() { } var->setSection(sectionName); + + disableAddressSanitizer(*this, var); + addUsedGlobal(var); return var; } @@ -2783,7 +2823,8 @@ namespace { llvm::Constant *witnessTableVar; if (Conformance->getConditionalRequirements().empty()) { - if (!isDependentConformance(Conformance)) { + if (!isDependentConformance(Conformance) && + !Conformance->isSynthesizedNonUnique()) { Flags = Flags.withConformanceKind(ConformanceKind::WitnessTable); witnessTableVar = IGM.getAddrOfWitnessTable(Conformance); } else { @@ -2900,10 +2941,10 @@ llvm::Constant *IRGenModule::emitProtocolConformances() { StringRef sectionName; switch (TargetInfo.OutputObjectFormat) { case llvm::Triple::MachO: - sectionName = "__TEXT, __swift4_proto, regular, no_dead_strip"; + sectionName = "__TEXT, __swift5_proto, regular, no_dead_strip"; break; case llvm::Triple::ELF: - sectionName = "swift4_protocol_conformances"; + sectionName = "swift5_protocol_conformances"; break; case llvm::Triple::COFF: sectionName = ".sw5prtc$B"; @@ -2914,8 +2955,10 @@ llvm::Constant *IRGenModule::emitProtocolConformances() { } var->setSection(sectionName); - addUsedGlobal(var); + + disableAddressSanitizer(*this, var); + addUsedGlobal(var); return var; } @@ -2925,10 +2968,10 @@ llvm::Constant *IRGenModule::emitTypeMetadataRecords() { std::string sectionName; switch (TargetInfo.OutputObjectFormat) { case llvm::Triple::MachO: - sectionName = "__TEXT, __swift4_types, regular, no_dead_strip"; + sectionName = "__TEXT, __swift5_types, regular, no_dead_strip"; break; case llvm::Triple::ELF: - sectionName = "swift4_type_metadata"; + sectionName = "swift5_type_metadata"; break; case llvm::Triple::COFF: sectionName = ".sw5tymd$B"; @@ -2981,8 +3024,10 @@ llvm::Constant *IRGenModule::emitTypeMetadataRecords() { var->setInitializer(initializer); var->setSection(sectionName); var->setAlignment(4); - addUsedGlobal(var); + + disableAddressSanitizer(*this, var); + addUsedGlobal(var); return var; } @@ -2990,13 +3035,13 @@ llvm::Constant *IRGenModule::emitFieldDescriptors() { std::string sectionName; switch (TargetInfo.OutputObjectFormat) { case llvm::Triple::MachO: - sectionName = "__TEXT, __swift4_fieldmd, regular, no_dead_strip"; + sectionName = "__TEXT, __swift5_fieldmd, regular, no_dead_strip"; break; case llvm::Triple::ELF: - sectionName = "swift4_fieldmd"; + sectionName = "swift5_fieldmd"; break; case llvm::Triple::COFF: - sectionName = ".swift4_fieldmd"; + sectionName = ".swift5_fieldmd"; break; default: llvm_unreachable("Don't know how to emit field records table for " @@ -3028,6 +3073,9 @@ llvm::Constant *IRGenModule::emitFieldDescriptors() { var->setInitializer(llvm::ConstantArray::get(arrayTy, elts)); var->setSection(sectionName); var->setAlignment(4); + + disableAddressSanitizer(*this, var); + addUsedGlobal(var); return var; } @@ -4019,6 +4067,10 @@ IRGenModule::getAddrOfGlobalUTF16ConstantString(StringRef utf8) { /// - For classes, the superclass might change the size or number /// of stored properties bool IRGenModule::isResilient(NominalTypeDecl *D, ResilienceExpansion expansion) { + if (expansion == ResilienceExpansion::Maximal && + Types.isCompletelyFragile()) { + return false; + } return D->isResilient(getSwiftModule(), expansion); } diff --git a/lib/IRGen/GenDecl.h b/lib/IRGen/GenDecl.h index f06269ae37f22..22ac1129ce391 100644 --- a/lib/IRGen/GenDecl.h +++ b/lib/IRGen/GenDecl.h @@ -46,14 +46,13 @@ namespace irgen { OptimizationMode FuncOptMode = OptimizationMode::NotSet); - llvm::GlobalVariable *createVariable(IRGenModule &IGM, - LinkInfo &linkInfo, - llvm::Type *objectType, - Alignment alignment, - DebugTypeInfo DebugType=DebugTypeInfo(), - Optional DebugLoc = None, - StringRef DebugName = StringRef()); + llvm::GlobalVariable * + createVariable(IRGenModule &IGM, LinkInfo &linkInfo, llvm::Type *objectType, + Alignment alignment, DebugTypeInfo DebugType = DebugTypeInfo(), + Optional DebugLoc = None, + StringRef DebugName = StringRef(), bool heapAllocated = false); + void disableAddressSanitizer(IRGenModule &IGM, llvm::GlobalVariable *var); } } diff --git a/lib/IRGen/GenEnum.cpp b/lib/IRGen/GenEnum.cpp index 6df76182c7daa..ed99fda5f0657 100644 --- a/lib/IRGen/GenEnum.cpp +++ b/lib/IRGen/GenEnum.cpp @@ -142,7 +142,8 @@ static llvm::Constant *emitEnumLayoutFlags(IRGenModule &IGM, bool isVWTMutable){ return IGM.getSize(Size(uintptr_t(flags))); } -SpareBitVector getBitVectorFromAPInt(const APInt &bits, unsigned startBit = 0) { +static SpareBitVector +getBitVectorFromAPInt(const APInt &bits, unsigned startBit = 0) { if (startBit == 0) { return SpareBitVector::fromAPInt(bits); } @@ -152,6 +153,28 @@ SpareBitVector getBitVectorFromAPInt(const APInt &bits, unsigned startBit = 0) { return result; } +static IsABIAccessible_t +areElementsABIAccessible(ArrayRef elts) { + for (auto &elt : elts) { + if (!elt.ti->isABIAccessible()) + return IsNotABIAccessible; + } + return IsABIAccessible; +} + +EnumImplStrategy::EnumImplStrategy(IRGenModule &IGM, + TypeInfoKind tik, + IsFixedSize_t alwaysFixedSize, + unsigned NumElements, + std::vector &&eltsWithPayload, + std::vector &&eltsWithNoPayload) + : ElementsWithPayload(std::move(eltsWithPayload)), + ElementsWithNoPayload(std::move(eltsWithNoPayload)), + IGM(IGM), TIK(tik), AlwaysFixedSize(alwaysFixedSize), + ElementsAreABIAccessible(areElementsABIAccessible(ElementsWithPayload)), + NumElements(NumElements) { +} + void irgen::EnumImplStrategy::initializeFromParams(IRGenFunction &IGF, Explosion ¶ms, Address dest, SILType T, @@ -465,7 +488,9 @@ namespace { void assignWithCopy(IRGenFunction &IGF, Address dest, Address src, SILType T, bool isOutlined) const override { if (!getSingleton()) return; - if (isOutlined || T.hasOpenedExistential()) { + if (!ElementsAreABIAccessible) { + emitAssignWithCopyCall(IGF, T, dest, src); + } else if (isOutlined || T.hasOpenedExistential()) { dest = getSingletonAddress(IGF, dest); src = getSingletonAddress(IGF, src); getSingleton()->assignWithCopy( @@ -478,7 +503,9 @@ namespace { void assignWithTake(IRGenFunction &IGF, Address dest, Address src, SILType T, bool isOutlined) const override { if (!getSingleton()) return; - if (isOutlined || T.hasOpenedExistential()) { + if (!ElementsAreABIAccessible) { + emitAssignWithTakeCall(IGF, T, dest, src); + } else if (isOutlined || T.hasOpenedExistential()) { dest = getSingletonAddress(IGF, dest); src = getSingletonAddress(IGF, src); getSingleton()->assignWithTake( @@ -498,7 +525,9 @@ namespace { void initializeWithCopy(IRGenFunction &IGF, Address dest, Address src, SILType T, bool isOutlined) const override { if (!getSingleton()) return; - if (isOutlined || T.hasOpenedExistential()) { + if (!ElementsAreABIAccessible) { + emitInitializeWithCopyCall(IGF, T, dest, src); + } else if (isOutlined || T.hasOpenedExistential()) { dest = getSingletonAddress(IGF, dest); src = getSingletonAddress(IGF, src); getSingleton()->initializeWithCopy( @@ -511,7 +540,9 @@ namespace { void initializeWithTake(IRGenFunction &IGF, Address dest, Address src, SILType T, bool isOutlined) const override { if (!getSingleton()) return; - if (isOutlined || T.hasOpenedExistential()) { + if (!ElementsAreABIAccessible) { + emitInitializeWithTakeCall(IGF, T, dest, src); + } else if (isOutlined || T.hasOpenedExistential()) { dest = getSingletonAddress(IGF, dest); src = getSingletonAddress(IGF, src); getSingleton()->initializeWithTake( @@ -527,6 +558,7 @@ namespace { return; getSingleton()->collectMetadataForOutlining(collector, getSingletonType(collector.IGF.IGM, T)); + collector.collectTypeMetadataForLayout(T); } void reexplode(IRGenFunction &IGF, Explosion &src, Explosion &dest) @@ -554,7 +586,9 @@ namespace { bool isOutlined) const override { if (getSingleton() && !getSingleton()->isPOD(ResilienceExpansion::Maximal)) { - if (isOutlined || T.hasOpenedExistential()) { + if (!ElementsAreABIAccessible) { + emitDestroyCall(IGF, T, addr); + } else if (isOutlined || T.hasOpenedExistential()) { getSingleton()->destroy(IGF, getSingletonAddress(IGF, addr), getSingletonType(IGF.IGM, T), isOutlined); } else { @@ -1477,6 +1511,8 @@ namespace { Normal, /// The payload is POD, so copying is bitwise, and destruction is a noop. POD, + /// The payload type is ABI-inaccessible, so we can't recurse. + ABIInaccessible, /// The payload is a single reference-counted value, and we have /// a single no-payload case which uses the null extra inhabitant, so /// copy and destroy can pass through to retain and release entry @@ -1594,7 +1630,9 @@ namespace { // If the payload is POD, then we can use POD value semantics. auto &payloadTI = *ElementsWithPayload[0].ti; - if (payloadTI.isPOD(ResilienceExpansion::Maximal)) { + if (!payloadTI.isABIAccessible()) { + CopyDestroyKind = ABIInaccessible; + } else if (payloadTI.isPOD(ResilienceExpansion::Maximal)) { CopyDestroyKind = POD; // If the payload is a single refcounted pointer and we have a single // empty case, then the layout will be a nullable pointer, and we can @@ -2216,6 +2254,7 @@ namespace { return IGM.getReferenceType(Refcounting); case POD: case Normal: + case ABIInaccessible: llvm_unreachable("not a refcounted payload"); } @@ -2230,6 +2269,7 @@ namespace { return; case POD: case Normal: + case ABIInaccessible: llvm_unreachable("not a refcounted payload"); } } @@ -2242,6 +2282,7 @@ namespace { return; case POD: case Normal: + case ABIInaccessible: llvm_unreachable("not a refcounted payload"); } } @@ -2254,6 +2295,7 @@ namespace { return; case POD: case Normal: + case ABIInaccessible: llvm_unreachable("not a refcounted payload"); } } @@ -2280,6 +2322,9 @@ namespace { reexplode(IGF, src, dest); return; + case ABIInaccessible: + llvm_unreachable("ABI-inaccessible type cannot be loadable"); + case Normal: { if (!copyEnumFunction) copyEnumFunction = emitCopyEnumFunction(IGF.IGM, loweredType); @@ -2314,6 +2359,9 @@ namespace { (void)src.claim(getExplosionSize()); return; + case ABIInaccessible: + llvm_unreachable("ABI-inaccessible type cannot be loadable"); + case Normal: { if (!consumeEnumFunction) consumeEnumFunction = emitConsumeEnumFunction(IGF.IGM, loweredType); @@ -2344,6 +2392,9 @@ namespace { (void)src.claim(getExplosionSize()); return; + case ABIInaccessible: + llvm_unreachable("ABI-inaccessible type cannot be loadable"); + case Normal: { // Check that we have a payload. EnumPayload payload; llvm::Value *extraTag; @@ -2384,11 +2435,16 @@ namespace { if (CopyDestroyKind == POD) { return; } - if (isOutlined || T.hasOpenedExistential()) { + if (!ElementsAreABIAccessible) { + return emitDestroyCall(IGF, T, addr); + } else if (isOutlined || T.hasOpenedExistential()) { switch (CopyDestroyKind) { case POD: return; + case ABIInaccessible: + llvm_unreachable("should already have been handled"); + case Normal: { // Check that there is a payload at the address. llvm::BasicBlock *endBB = testEnumContainsPayload(IGF, addr, T); @@ -2474,6 +2530,9 @@ namespace { case POD: return emitPrimitiveCopy(IGF, dest, src, T); + case ABIInaccessible: + llvm_unreachable("shouldn't get here"); + case Normal: { llvm::BasicBlock *endBB = llvm::BasicBlock::Create(C); @@ -2581,6 +2640,9 @@ namespace { case POD: return emitPrimitiveCopy(IGF, dest, src, T); + case ABIInaccessible: + llvm_unreachable("shouldn't get here"); + case Normal: { llvm::BasicBlock *endBB = llvm::BasicBlock::Create(C); @@ -2637,7 +2699,9 @@ namespace { public: void assignWithCopy(IRGenFunction &IGF, Address dest, Address src, SILType T, bool isOutlined) const override { - if (isOutlined || T.hasOpenedExistential()) { + if (!ElementsAreABIAccessible) { + emitAssignWithCopyCall(IGF, T, dest, src); + } else if (isOutlined || T.hasOpenedExistential()) { emitIndirectAssign(IGF, dest, src, T, IsNotTake, isOutlined); } else { callOutlinedCopy(IGF, dest, src, T, IsNotInitialization, IsNotTake); @@ -2646,7 +2710,9 @@ namespace { void assignWithTake(IRGenFunction &IGF, Address dest, Address src, SILType T, bool isOutlined) const override { - if (isOutlined || T.hasOpenedExistential()) { + if (!ElementsAreABIAccessible) { + emitAssignWithTakeCall(IGF, T, dest, src); + } else if (isOutlined || T.hasOpenedExistential()) { emitIndirectAssign(IGF, dest, src, T, IsTake, isOutlined); } else { callOutlinedCopy(IGF, dest, src, T, IsNotInitialization, IsTake); @@ -2655,7 +2721,9 @@ namespace { void initializeWithCopy(IRGenFunction &IGF, Address dest, Address src, SILType T, bool isOutlined) const override { - if (isOutlined || T.hasOpenedExistential()) { + if (!ElementsAreABIAccessible) { + emitInitializeWithCopyCall(IGF, T, dest, src); + } else if (isOutlined || T.hasOpenedExistential()) { emitIndirectInitialize(IGF, dest, src, T, IsNotTake, isOutlined); } else { callOutlinedCopy(IGF, dest, src, T, IsInitialization, IsNotTake); @@ -2664,7 +2732,9 @@ namespace { void initializeWithTake(IRGenFunction &IGF, Address dest, Address src, SILType T, bool isOutlined) const override { - if (isOutlined || T.hasOpenedExistential()) { + if (!ElementsAreABIAccessible) { + emitInitializeWithTakeCall(IGF, T, dest, src); + } else if (isOutlined || T.hasOpenedExistential()) { emitIndirectInitialize(IGF, dest, src, T, IsTake, isOutlined); } else { callOutlinedCopy(IGF, dest, src, T, IsInitialization, IsTake); @@ -2931,6 +3001,8 @@ namespace { /// The payloads are all POD, so copying is bitwise, and destruction is a /// noop. POD, + /// One or more of the payloads is ABI-inaccessible, so we cannot recurse. + ABIInaccessible, /// The payloads are all bitwise-takable, but have no other special /// shared layout. BitwiseTakable, @@ -3072,7 +3144,9 @@ namespace { } } - if (allPOD) { + if (!ElementsAreABIAccessible) { + CopyDestroyKind = ABIInaccessible; + } else if (allPOD) { assert(!allSingleRefcount && "pod *and* refcounted?!"); CopyDestroyKind = POD; // FIXME: Memory corruption issues arise when enabling this for mixed @@ -3167,6 +3241,7 @@ namespace { case POD: case BitwiseTakable: case Normal: + case ABIInaccessible: llvm_unreachable("not a refcounted payload"); } @@ -3182,6 +3257,7 @@ namespace { case POD: case BitwiseTakable: case Normal: + case ABIInaccessible: llvm_unreachable("not a refcounted payload"); } } @@ -3195,6 +3271,7 @@ namespace { case POD: case BitwiseTakable: case Normal: + case ABIInaccessible: llvm_unreachable("not a refcounted payload"); } } @@ -3208,6 +3285,7 @@ namespace { case POD: case BitwiseTakable: case Normal: + case ABIInaccessible: llvm_unreachable("not a refcounted payload"); } } @@ -3292,7 +3370,7 @@ namespace { llvm::Value * loadDynamicTag(IRGenFunction &IGF, Address addr, SILType T) const { addr = IGF.Builder.CreateBitCast(addr, IGF.IGM.OpaquePtrTy); - auto metadata = IGF.emitTypeMetadataRef(T.getSwiftRValueType()); + auto metadata = IGF.emitTypeMetadataRef(T.getASTType()); auto call = IGF.Builder.CreateCall(IGF.IGM.getGetEnumCaseMultiPayloadFn(), {addr.getAddress(), metadata}); call->setDoesNotThrow(); @@ -3895,7 +3973,7 @@ namespace { } void forNontrivialPayloads(IRGenFunction &IGF, llvm::Value *tag, - std::function f) + llvm::function_ref f) const { auto *endBB = llvm::BasicBlock::Create(IGF.IGM.getLLVMContext()); @@ -3988,6 +4066,9 @@ namespace { reexplode(IGF, src, dest); return; + case ABIInaccessible: + llvm_unreachable("ABI-accessible type cannot be loadable"); + case BitwiseTakable: case Normal: { if (!copyEnumFunction) @@ -4030,6 +4111,9 @@ namespace { (void)src.claim(getExplosionSize()); return; + case ABIInaccessible: + llvm_unreachable("ABI-accessible type cannot be loadable"); + case BitwiseTakable: case Normal: { if (!consumeEnumFunction) @@ -4063,6 +4147,9 @@ namespace { (void)src.claim(getExplosionSize()); return; + case ABIInaccessible: + llvm_unreachable("ABI-accessible type cannot be loadable"); + case BitwiseTakable: case Normal: { auto parts = destructureAndTagLoadableEnum(IGF, src); @@ -4102,6 +4189,9 @@ namespace { case POD: return emitPrimitiveCopy(IGF, dest, src, T); + case ABIInaccessible: + llvm_unreachable("shouldn't get here"); + case BitwiseTakable: case TaggedRefcounted: case Normal: { @@ -4152,6 +4242,9 @@ namespace { case POD: return emitPrimitiveCopy(IGF, dest, src, T); + case ABIInaccessible: + llvm_unreachable("shouldn't get here"); + case BitwiseTakable: case TaggedRefcounted: // Takes can be done by primitive copy in these case. @@ -4276,7 +4369,9 @@ namespace { public: void assignWithCopy(IRGenFunction &IGF, Address dest, Address src, SILType T, bool isOutlined) const override { - if (isOutlined || T.hasOpenedExistential()) { + if (!ElementsAreABIAccessible) { + emitAssignWithCopyCall(IGF, T, dest, src); + } else if (isOutlined || T.hasOpenedExistential()) { emitIndirectAssign(IGF, dest, src, T, IsNotTake, isOutlined); } else { callOutlinedCopy(IGF, dest, src, T, IsNotInitialization, IsNotTake); @@ -4285,7 +4380,9 @@ namespace { void assignWithTake(IRGenFunction &IGF, Address dest, Address src, SILType T, bool isOutlined) const override { - if (isOutlined || T.hasOpenedExistential()) { + if (!ElementsAreABIAccessible) { + emitAssignWithTakeCall(IGF, T, dest, src); + } else if (isOutlined || T.hasOpenedExistential()) { emitIndirectAssign(IGF, dest, src, T, IsTake, isOutlined); } else { callOutlinedCopy(IGF, dest, src, T, IsNotInitialization, IsTake); @@ -4294,7 +4391,9 @@ namespace { void initializeWithCopy(IRGenFunction &IGF, Address dest, Address src, SILType T, bool isOutlined) const override { - if (isOutlined || T.hasOpenedExistential()) { + if (!ElementsAreABIAccessible) { + emitInitializeWithCopyCall(IGF, T, dest, src); + } else if (isOutlined || T.hasOpenedExistential()) { emitIndirectInitialize(IGF, dest, src, T, IsNotTake, isOutlined); } else { callOutlinedCopy(IGF, dest, src, T, IsInitialization, IsNotTake); @@ -4303,7 +4402,9 @@ namespace { void initializeWithTake(IRGenFunction &IGF, Address dest, Address src, SILType T, bool isOutlined) const override { - if (isOutlined || T.hasOpenedExistential()) { + if (!ElementsAreABIAccessible) { + emitInitializeWithTakeCall(IGF, T, dest, src); + } else if (isOutlined || T.hasOpenedExistential()) { emitIndirectInitialize(IGF, dest, src, T, IsTake, isOutlined); } else { callOutlinedCopy(IGF, dest, src, T, IsInitialization, IsTake); @@ -4331,11 +4432,16 @@ namespace { if (CopyDestroyKind == POD) { return; } - if (isOutlined || T.hasOpenedExistential()) { + if (!ElementsAreABIAccessible) { + emitDestroyCall(IGF, T, addr); + } else if (isOutlined || T.hasOpenedExistential()) { switch (CopyDestroyKind) { case POD: return; + case ABIInaccessible: + llvm_unreachable("shouldn't get here"); + case BitwiseTakable: case Normal: case TaggedRefcounted: { @@ -4489,7 +4595,7 @@ namespace { // Invoke the runtime to store the tag. enumAddr = IGF.Builder.CreateBitCast(enumAddr, IGF.IGM.OpaquePtrTy); - auto metadata = IGF.emitTypeMetadataRef(T.getSwiftRValueType()); + auto metadata = IGF.emitTypeMetadataRef(T.getASTType()); auto call = IGF.Builder.CreateCall( IGF.IGM.getStoreEnumTagMultiPayloadFn(), @@ -5108,9 +5214,7 @@ EnumImplStrategy::get(TypeConverter &TC, SILType type, EnumDecl *theEnum) { origArgType = theEnum->mapTypeIntoContext(origArgType); auto origArgLoweredTy = TC.IGM.getLoweredType(origArgType); - const TypeInfo *origArgTI - = TC.tryGetCompleteTypeInfo(origArgLoweredTy.getSwiftRValueType()); - assert(origArgTI && "didn't complete type info?!"); + auto *origArgTI = &TC.getCompleteTypeInfo(origArgLoweredTy.getASTType()); // If the unsubstituted argument contains a generic parameter type, or // is not fixed-size in all resilience domains that have knowledge of @@ -5146,7 +5250,7 @@ EnumImplStrategy::get(TypeConverter &TC, SILType type, EnumDecl *theEnum) { // can reproduce. if (!substArgTI->isFixedSize(layoutScope)) { alwaysFixedSize = IsNotFixedSize; - allowFixedLayoutOptimizations = false; + assert(!allowFixedLayoutOptimizations); } } } @@ -5394,8 +5498,9 @@ namespace { llvm::Type *irTy, Alignment align, IsPOD_t pod, - IsBitwiseTakable_t bt) - : EnumTypeInfoBase(strategy, irTy, align, pod, bt) {} + IsBitwiseTakable_t bt, + IsABIAccessible_t abiAccessible) + : EnumTypeInfoBase(strategy, irTy, align, pod, bt, abiAccessible) {} }; /// TypeInfo for dynamically-sized enum types. @@ -5404,8 +5509,8 @@ namespace { { public: ResilientEnumTypeInfo(EnumImplStrategy &strategy, - llvm::Type *irTy) - : EnumTypeInfoBase(strategy, irTy) {} + llvm::Type *irTy, IsABIAccessible_t abiAccessible) + : EnumTypeInfoBase(strategy, irTy, abiAccessible) {} }; } // end anonymous namespace @@ -5456,8 +5561,7 @@ SingletonEnumImplStrategy::completeEnumTypeLayout(TypeConverter &TC, if (ElementsWithPayload.empty()) { enumTy->setBody(ArrayRef{}, /*isPacked*/ true); Alignment alignment(1); - applyLayoutAttributes(TC.IGM, Type.getSwiftRValueType(), /*fixed*/true, - alignment); + applyLayoutAttributes(TC.IGM, Type.getASTType(), /*fixed*/true, alignment); return registerEnumTypeInfo(new LoadableEnumTypeInfo(*this, enumTy, Size(0), {}, alignment, @@ -5476,16 +5580,19 @@ SingletonEnumImplStrategy::completeEnumTypeLayout(TypeConverter &TC, if (TIK <= Opaque) { auto alignment = eltTI.getBestKnownAlignment(); - applyLayoutAttributes(TC.IGM, Type.getSwiftRValueType(), /*fixed*/false, + applyLayoutAttributes(TC.IGM, Type.getASTType(), /*fixed*/false, alignment); + auto enumAccessible = + IsABIAccessible_t(TC.IGM.getSILModule().isTypeABIAccessible(Type)); return registerEnumTypeInfo(new NonFixedEnumTypeInfo(*this, enumTy, alignment, eltTI.isPOD(ResilienceExpansion::Maximal), - eltTI.isBitwiseTakable(ResilienceExpansion::Maximal))); + eltTI.isBitwiseTakable(ResilienceExpansion::Maximal), + enumAccessible)); } else { auto &fixedEltTI = cast(eltTI); auto alignment = fixedEltTI.getFixedAlignment(); - applyLayoutAttributes(TC.IGM, Type.getSwiftRValueType(), /*fixed*/true, + applyLayoutAttributes(TC.IGM, Type.getASTType(), /*fixed*/true, alignment); return getFixedEnumTypeInfo(enumTy, @@ -5522,8 +5629,7 @@ NoPayloadEnumImplStrategy::completeEnumTypeLayout(TypeConverter &TC, spareBits.extendWithSetBits(tagSize.getValueInBits()); Alignment alignment(tagSize.getValue()); - applyLayoutAttributes(TC.IGM, Type.getSwiftRValueType(), /*fixed*/true, - alignment); + applyLayoutAttributes(TC.IGM, Type.getASTType(), /*fixed*/true, alignment); return registerEnumTypeInfo(new LoadableEnumTypeInfo(*this, enumTy, tagSize, std::move(spareBits), @@ -5563,8 +5669,7 @@ CCompatibleEnumImplStrategy::completeEnumTypeLayout(TypeConverter &TC, enumTy->setBody(body, /*isPacked*/ false); auto alignment = rawFixedTI.getFixedAlignment(); - applyLayoutAttributes(TC.IGM, Type.getSwiftRValueType(), /*fixed*/true, - alignment); + applyLayoutAttributes(TC.IGM, Type.getASTType(), /*fixed*/true, alignment); assert(!TC.IGM.isResilient(theEnum, ResilienceExpansion::Minimal) && "C-compatible enums cannot be resilient"); @@ -5635,8 +5740,7 @@ TypeInfo *SinglePayloadEnumImplStrategy::completeFixedLayout( } auto alignment = payloadTI.getFixedAlignment(); - applyLayoutAttributes(TC.IGM, Type.getSwiftRValueType(), /*fixed*/true, - alignment); + applyLayoutAttributes(TC.IGM, Type.getASTType(), /*fixed*/true, alignment); getFixedEnumTypeInfo( enumTy, Size(sizeWithTag), std::move(spareBits), alignment, @@ -5664,13 +5768,17 @@ TypeInfo *SinglePayloadEnumImplStrategy::completeDynamicLayout( auto &payloadTI = getPayloadTypeInfo(); auto alignment = payloadTI.getBestKnownAlignment(); - applyLayoutAttributes(TC.IGM, Type.getSwiftRValueType(), /*fixed*/false, + applyLayoutAttributes(TC.IGM, Type.getASTType(), /*fixed*/false, alignment); + auto enumAccessible = + IsABIAccessible_t(TC.IGM.getSILModule().isTypeABIAccessible(Type)); + return registerEnumTypeInfo(new NonFixedEnumTypeInfo(*this, enumTy, alignment, payloadTI.isPOD(ResilienceExpansion::Maximal), - payloadTI.isBitwiseTakable(ResilienceExpansion::Maximal))); + payloadTI.isBitwiseTakable(ResilienceExpansion::Maximal), + enumAccessible)); } TypeInfo * @@ -5826,7 +5934,7 @@ MultiPayloadEnumImplStrategy::completeFixedLayout(TypeConverter &TC, assert(PayloadTagBits.count() == numTagBits); } - applyLayoutAttributes(TC.IGM, Type.getSwiftRValueType(), /*fixed*/ true, + applyLayoutAttributes(TC.IGM, Type.getASTType(), /*fixed*/ true, worstAlignment); getFixedEnumTypeInfo(enumTy, Size(sizeWithTag), std::move(spareBits), @@ -5863,11 +5971,15 @@ TypeInfo *MultiPayloadEnumImplStrategy::completeDynamicLayout( bt &= payloadTI.isBitwiseTakable(ResilienceExpansion::Maximal); } - applyLayoutAttributes(TC.IGM, Type.getSwiftRValueType(), /*fixed*/false, + applyLayoutAttributes(TC.IGM, Type.getASTType(), /*fixed*/false, alignment); + + auto enumAccessible = + IsABIAccessible_t(TC.IGM.getSILModule().isTypeABIAccessible(Type)); return registerEnumTypeInfo(new NonFixedEnumTypeInfo(*this, enumTy, - alignment, pod, bt)); + alignment, pod, bt, + enumAccessible)); } TypeInfo * @@ -5886,7 +5998,10 @@ ResilientEnumImplStrategy::completeEnumTypeLayout(TypeConverter &TC, SILType Type, EnumDecl *theEnum, llvm::StructType *enumTy) { - return registerEnumTypeInfo(new ResilientEnumTypeInfo(*this, enumTy)); + auto abiAccessible = + IsABIAccessible_t(TC.IGM.getSILModule().isTypeABIAccessible(Type)); + return registerEnumTypeInfo( + new ResilientEnumTypeInfo(*this, enumTy, abiAccessible)); } const TypeInfo *TypeConverter::convertEnumType(TypeBase *key, CanType type, diff --git a/lib/IRGen/GenEnum.h b/lib/IRGen/GenEnum.h index 8531f2b9a811a..c8c29aa6d0f84 100644 --- a/lib/IRGen/GenEnum.h +++ b/lib/IRGen/GenEnum.h @@ -138,6 +138,7 @@ class EnumImplStrategy { const TypeInfo *TI = nullptr; TypeInfoKind TIK; IsFixedSize_t AlwaysFixedSize; + IsABIAccessible_t ElementsAreABIAccessible; unsigned NumElements; EnumImplStrategy(IRGenModule &IGM, @@ -145,12 +146,7 @@ class EnumImplStrategy { IsFixedSize_t alwaysFixedSize, unsigned NumElements, std::vector &&ElementsWithPayload, - std::vector &&ElementsWithNoPayload) - : ElementsWithPayload(std::move(ElementsWithPayload)), - ElementsWithNoPayload(std::move(ElementsWithNoPayload)), - IGM(IGM), TIK(tik), AlwaysFixedSize(alwaysFixedSize), - NumElements(NumElements) - {} + std::vector &&ElementsWithNoPayload); /// Save the TypeInfo created for the enum. TypeInfo *registerEnumTypeInfo(TypeInfo *mutableTI) { diff --git a/lib/IRGen/GenExistential.cpp b/lib/IRGen/GenExistential.cpp index 18f50233f19b3..e7e5fb5c40acf 100644 --- a/lib/IRGen/GenExistential.cpp +++ b/lib/IRGen/GenExistential.cpp @@ -280,7 +280,7 @@ class OpaqueExistentialTypeInfo final : llvm::Type *ty, Size size, Alignment align) : super(protocols, ty, size, SpareBitVector::getConstant(size.getValueInBits(), false), align, - IsNotPOD, IsNotBitwiseTakable, IsFixedSize) {} + IsNotPOD, IsBitwiseTakable, IsFixedSize) {} public: OpaqueExistentialLayout getLayout() const { @@ -343,16 +343,11 @@ class OpaqueExistentialTypeInfo final : void initializeWithTake(IRGenFunction &IGF, Address dest, Address src, SILType T, bool isOutlined) const override { if (isOutlined) { - llvm::Value *metadata = copyType(IGF, dest, src); - - auto layout = getLayout(); - - // Project down to the buffers and ask the witnesses to do a - // take-initialize. - Address srcBuffer = layout.projectExistentialBuffer(IGF, src); - Address destBuffer = layout.projectExistentialBuffer(IGF, dest); - emitInitializeBufferWithTakeOfBufferCall(IGF, metadata, destBuffer, - srcBuffer); + // memcpy the existential container. This is safe because: either the + // value is stored inline and is therefore by convention bitwise takable + // or the value is stored in a reference counted heap buffer, in which + // case a memcpy of the reference is also correct. + IGF.emitMemCpy(dest, src, getLayout().getSize(IGF.IGM)); } else { // Create an outlined function to avoid explosion OutliningMetadataCollector collector(IGF); @@ -1163,7 +1158,7 @@ class ClassExistentialTypeInfo final Explosion &out) const override { Address valueAddr = projectValue(IGF, existential); out.add(IGF.emitUnownedLoadStrong(valueAddr, - IGF.IGM.getReferenceType(Refcounting), + getPayloadType(), Refcounting)); emitLoadOfTables(IGF, existential, out); } @@ -1172,7 +1167,7 @@ class ClassExistentialTypeInfo final Explosion &out) const override { Address valueAddr = projectValue(IGF, existential); out.add(IGF.emitUnownedTakeStrong(valueAddr, - IGF.IGM.getReferenceType(Refcounting), + getPayloadType(), Refcounting)); emitLoadOfTables(IGF, existential, out); } @@ -1545,12 +1540,11 @@ TypeConverter::convertExistentialMetatypeType(ExistentialMetatypeType *T) { /// Emit protocol witness table pointers for the given protocol conformances, /// passing each emitted witness table index into the given function body. -static void forEachProtocolWitnessTable(IRGenFunction &IGF, - CanType srcType, llvm::Value **srcMetadataCache, - CanType destType, - ArrayRef protocols, - ArrayRef conformances, - std::function body) { +static void forEachProtocolWitnessTable( + IRGenFunction &IGF, CanType srcType, llvm::Value **srcMetadataCache, + CanType destType, ArrayRef protocols, + ArrayRef conformances, + llvm::function_ref body) { // Collect the conformances that need witness tables. auto layout = destType.getExistentialLayout(); auto destProtocols = layout.getProtocols(); @@ -1661,7 +1655,7 @@ OwnedAddress irgen::emitBoxedExistentialContainerAllocation(IRGenFunction &IGF, auto box = IGF.Builder.CreateExtractValue(result, 0); auto addr = IGF.Builder.CreateExtractValue(result, 1); - auto archetype = ArchetypeType::getOpened(destType.getSwiftRValueType()); + auto archetype = ArchetypeType::getOpened(destType.getASTType()); auto &srcTI = IGF.getTypeInfoForUnlowered(AbstractionPattern(archetype), formalSrcType); addr = IGF.Builder.CreateBitCast(addr, @@ -1696,8 +1690,8 @@ void irgen::emitClassExistentialContainer(IRGenFunction &IGF, ArrayRef conformances) { // As a special case, an Error existential can be represented as a // reference to an already existing NSError or CFError instance. - if (outType.getSwiftRValueType().isExistentialType()) { - auto layout = outType.getSwiftRValueType().getExistentialLayout(); + if (outType.isExistentialType()) { + auto layout = outType.getASTType().getExistentialLayout(); if (layout.isErrorExistential()) { // Bitcast the incoming class reference to Error. out.add(IGF.Builder.CreateBitCast(instance, IGF.IGM.ErrorPtrTy)); @@ -1718,7 +1712,7 @@ void irgen::emitClassExistentialContainer(IRGenFunction &IGF, // Emit the witness table pointers. llvm::Value *instanceMetadata = nullptr; forEachProtocolWitnessTable(IGF, instanceFormalType, &instanceMetadata, - outType.getSwiftRValueType(), + outType.getASTType(), destTI.getStoredProtocols(), conformances, [&](unsigned i, llvm::Value *ptable) { @@ -1748,7 +1742,7 @@ Address irgen::emitOpaqueExistentialContainerInit(IRGenFunction &IGF, // Next, write the protocol witness tables. forEachProtocolWitnessTable(IGF, formalSrcType, &metadata, - destType.getSwiftRValueType(), + destType.getASTType(), destTI.getStoredProtocols(), conformances, [&](unsigned i, llvm::Value *ptable) { Address ptableSlot = destLayout.projectWitnessTable(IGF, dest, i); @@ -1881,15 +1875,8 @@ void irgen::emitMetatypeOfClassExistential(IRGenFunction &IGF, Explosion &value, assert((IGF.IGM.ObjCInterop || repr != MetatypeRepresentation::ObjC) && "Class metatypes should not have ObjC representation without runtime"); - if (repr == MetatypeRepresentation::Thick) { - auto dynamicType = emitDynamicTypeOfOpaqueHeapObject(IGF, instance); - out.add(dynamicType); - } else if (repr == MetatypeRepresentation::ObjC) { - auto dynamicType = emitHeapMetadataRefForUnknownHeapObject(IGF, instance); - out.add(dynamicType); - } else { - llvm_unreachable("Unknown metatype representation"); - } + auto dynamicType = emitDynamicTypeOfOpaqueHeapObject(IGF, instance, repr); + out.add(dynamicType); // Get the witness tables. out.add(tablesAndValue.first); @@ -1926,7 +1913,8 @@ irgen::emitClassExistentialProjection(IRGenFunction &IGF, ArrayRef wtables; llvm::Value *value; std::tie(wtables, value) = baseTI.getWitnessTablesAndValue(base); - auto metadata = emitDynamicTypeOfOpaqueHeapObject(IGF, value); + auto metadata = emitDynamicTypeOfOpaqueHeapObject(IGF, value, + MetatypeRepresentation::Thick); IGF.bindArchetype(openedArchetype, metadata, MetadataState::Complete, wtables); @@ -2265,7 +2253,8 @@ Address irgen::emitOpaqueBoxedExistentialProjection( IGF.getTypeInfo(existentialTy).as(); auto valueAddr = baseTI.projectValue(IGF, base); auto value = IGF.Builder.CreateLoad(valueAddr); - auto metadata = emitDynamicTypeOfOpaqueHeapObject(IGF, value); + auto metadata = emitDynamicTypeOfOpaqueHeapObject(IGF, value, + MetatypeRepresentation::Thick); // If we are projecting into an opened archetype, capture the // witness tables. diff --git a/lib/IRGen/GenFunc.cpp b/lib/IRGen/GenFunc.cpp index 2c8e66d8b1fa4..eeb67352dfd0a 100644 --- a/lib/IRGen/GenFunc.cpp +++ b/lib/IRGen/GenFunc.cpp @@ -717,8 +717,7 @@ static bool isABIIgnoredParameterWithoutStorage(IRGenModule &IGM, unsigned paramIdx) { auto param = substType->getParameters()[paramIdx]; SILType argType = IGM.silConv.getSILType(param); - auto argLoweringTy = - getArgumentLoweringType(argType.getSwiftRValueType(), param); + auto argLoweringTy = getArgumentLoweringType(argType.getASTType(), param); auto &ti = IGF.getTypeInfoForLowered(argLoweringTy); // Empty values don't matter. return ti.getSchema().empty() && !param.isFormalIndirect(); @@ -758,7 +757,7 @@ static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM, CanSILFunctionType origType, CanSILFunctionType substType, CanSILFunctionType outType, - SubstitutionList subs, + SubstitutionMap subs, HeapLayout const *layout, ArrayRef conventions) { auto outSig = IGM.getSignature(outType); @@ -948,8 +947,9 @@ static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM, Explosion polyArgs; // Emit the polymorphic arguments. - assert((subs.empty() != hasPolymorphicParameters(origType) || - (subs.empty() && origType->getRepresentation() == + assert((subs.hasAnySubstitutableParams() + == hasPolymorphicParameters(origType) || + (!subs.hasAnySubstitutableParams() && origType->getRepresentation() == SILFunctionTypeRepresentation::WitnessMethod)) && "should have substitutions iff original function is generic"); WitnessMetadata witnessMetadata; @@ -985,10 +985,7 @@ static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM, (void)param.claimAll(); } - SubstitutionMap subMap; - if (auto genericSig = origType->getGenericSignature()) - subMap = genericSig->getSubstitutionMap(subs); - emitPolymorphicArguments(subIGF, origType, subMap, + emitPolymorphicArguments(subIGF, origType, subs, &witnessMetadata, polyArgs); } @@ -1063,6 +1060,7 @@ static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM, if (RetainableValue->getType() != subIGF.IGM.RefCountedPtrTy) RetainableValue = subIGF.Builder.CreateBitCast( RetainableValue, subIGF.IGM.RefCountedPtrTy); + needsAllocas = true; auto temporary = subIGF.createAlloca(RetainableValue->getType(), subIGF.IGM.getPointerAlignment(), "partial-apply.context"); @@ -1092,7 +1090,7 @@ static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM, auto &fieldTy = layout->getElementTypes()[nextCapturedField]; auto fieldConvention = conventions[nextCapturedField]; Address fieldAddr = fieldLayout.project(subIGF, data, offsets); - auto &fieldTI = fieldLayout.getType(); + auto &fieldTI = fieldLayout.getTypeForAccess(); auto fieldSchema = fieldTI.getSchema(); Explosion param; @@ -1184,10 +1182,7 @@ static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM, // Now that we have bound generic parameters from the captured arguments // emit the polymorphic arguments. if (hasPolymorphicParameters(origType)) { - SubstitutionMap subMap; - if (auto genericSig = origType->getGenericSignature()) - subMap = genericSig->getSubstitutionMap(subs); - emitPolymorphicArguments(subIGF, origType, subMap, + emitPolymorphicArguments(subIGF, origType, subs, &witnessMetadata, polyArgs); } } @@ -1333,7 +1328,7 @@ static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM, void irgen::emitFunctionPartialApplication( IRGenFunction &IGF, SILFunction &SILFn, const FunctionPointer &fn, llvm::Value *fnContext, Explosion &args, ArrayRef params, - SubstitutionList subs, CanSILFunctionType origType, + SubstitutionMap subs, CanSILFunctionType origType, CanSILFunctionType substType, CanSILFunctionType outType, Explosion &out, bool isOutlined) { // If we have a single Swift-refcounted context value, we can adopt it @@ -1349,11 +1344,8 @@ void irgen::emitFunctionPartialApplication( assert(!outType->isNoEscape()); // Reserve space for polymorphic bindings. - SubstitutionMap subMap; - if (auto genericSig = origType->getGenericSignature()) - subMap = genericSig->getSubstitutionMap(subs); auto bindings = NecessaryBindings::forFunctionInvocations(IGF.IGM, - origType, subMap); + origType, subs); if (!bindings.empty()) { hasSingleSwiftRefcountedContext = No; auto bindingsSize = bindings.getBufferSize(IGF.IGM); @@ -1368,8 +1360,7 @@ void irgen::emitFunctionPartialApplication( for (auto param : params) { SILType argType = IGF.IGM.silConv.getSILType(param); - auto argLoweringTy = - getArgumentLoweringType(argType.getSwiftRValueType(), param); + auto argLoweringTy = getArgumentLoweringType(argType.getASTType(), param); auto &ti = IGF.getTypeInfoForLowered(argLoweringTy); @@ -1574,9 +1565,9 @@ void irgen::emitFunctionPartialApplication( case ParameterConvention::Indirect_In: case ParameterConvention::Indirect_In_Constant: case ParameterConvention::Indirect_In_Guaranteed: { - auto addr = fieldLayout.getType().getAddressForPointer(args.claimNext()); - fieldLayout.getType().initializeWithTake(IGF, fieldAddr, addr, fieldTy, - isOutlined); + auto addr = fieldLayout.getTypeForAccess().getAddressForPointer(args.claimNext()); + fieldLayout.getTypeForAccess().initializeWithTake(IGF, fieldAddr, addr, fieldTy, + isOutlined); break; } // Take direct value arguments and inout pointers by value. @@ -1585,7 +1576,7 @@ void irgen::emitFunctionPartialApplication( case ParameterConvention::Direct_Guaranteed: case ParameterConvention::Indirect_Inout: case ParameterConvention::Indirect_InoutAliasable: - cast(fieldLayout.getType()) + cast(fieldLayout.getTypeForAccess()) .initialize(IGF, args, fieldAddr, isOutlined); break; } diff --git a/lib/IRGen/GenFunc.h b/lib/IRGen/GenFunc.h index 5b98bd90134d7..45cb6a81fff5b 100644 --- a/lib/IRGen/GenFunc.h +++ b/lib/IRGen/GenFunc.h @@ -50,7 +50,7 @@ namespace irgen { void emitFunctionPartialApplication( IRGenFunction &IGF, SILFunction &SILFn, const FunctionPointer &fnPtr, llvm::Value *fnContext, Explosion &args, - ArrayRef argTypes, SubstitutionList subs, + ArrayRef argTypes, SubstitutionMap subs, CanSILFunctionType origType, CanSILFunctionType substType, CanSILFunctionType outType, Explosion &out, bool isOutlined); diff --git a/lib/IRGen/GenHeap.cpp b/lib/IRGen/GenHeap.cpp index 960680a922ae4..ee5a70a23426c 100644 --- a/lib/IRGen/GenHeap.cpp +++ b/lib/IRGen/GenHeap.cpp @@ -81,6 +81,34 @@ HeapLayout::HeapLayout(IRGenModule &IGM, LayoutStrategy strategy, #endif } +static llvm::Value *calcInitOffset(swift::irgen::IRGenFunction &IGF, + unsigned int i, + const swift::irgen::HeapLayout &layout) { + llvm::Value *offset = nullptr; + if (i == 0) { + auto startoffset = layout.getSize(); + offset = llvm::ConstantInt::get(IGF.IGM.SizeTy, startoffset.getValue()); + return offset; + } + auto &prevElt = layout.getElement(i - 1); + auto prevType = layout.getElementTypes()[i - 1]; + // Start calculating offsets from the last fixed-offset field. + Size lastFixedOffset = layout.getElement(i - 1).getByteOffset(); + if (auto *fixedType = dyn_cast(&prevElt.getTypeForLayout())) { + // If the last fixed-offset field is also fixed-size, we can + // statically compute the end of the fixed-offset fields. + auto fixedEnd = lastFixedOffset + fixedType->getFixedSize(); + offset = llvm::ConstantInt::get(IGF.IGM.SizeTy, fixedEnd.getValue()); + } else { + // Otherwise, we need to add the dynamic size to the fixed start + // offset. + offset = llvm::ConstantInt::get(IGF.IGM.SizeTy, lastFixedOffset.getValue()); + offset = IGF.Builder.CreateAdd( + offset, prevElt.getTypeForLayout().getSize(IGF, prevType)); + } + return offset; +} + HeapNonFixedOffsets::HeapNonFixedOffsets(IRGenFunction &IGF, const HeapLayout &layout) { if (!layout.isFixedLayout()) { @@ -96,7 +124,7 @@ HeapNonFixedOffsets::HeapNonFixedOffsets(IRGenFunction &IGF, case ElementLayout::Kind::InitialNonFixedSize: // Factor the non-fixed-size field's alignment into the total alignment. totalAlign = IGF.Builder.CreateOr(totalAlign, - elt.getType().getAlignmentMask(IGF, eltTy)); + elt.getTypeForLayout().getAlignmentMask(IGF, eltTy)); LLVM_FALLTHROUGH; case ElementLayout::Kind::Empty: case ElementLayout::Kind::Fixed: @@ -107,38 +135,12 @@ HeapNonFixedOffsets::HeapNonFixedOffsets(IRGenFunction &IGF, case ElementLayout::Kind::NonFixed: // Start calculating non-fixed offsets from the end of the first fixed // field. - if (i == 0) { - totalAlign = elt.getType().getAlignmentMask(IGF, eltTy); - offset = totalAlign; - Offsets.push_back(totalAlign); - break; - } - - assert(i > 0 && "shouldn't begin with a non-fixed field"); - auto &prevElt = layout.getElement(i-1); - auto prevType = layout.getElementTypes()[i-1]; - // Start calculating offsets from the last fixed-offset field. if (!offset) { - Size lastFixedOffset = layout.getElement(i-1).getByteOffset(); - if (auto *fixedType = dyn_cast(&prevElt.getType())) { - // If the last fixed-offset field is also fixed-size, we can - // statically compute the end of the fixed-offset fields. - auto fixedEnd = lastFixedOffset + fixedType->getFixedSize(); - offset - = llvm::ConstantInt::get(IGF.IGM.SizeTy, fixedEnd.getValue()); - } else { - // Otherwise, we need to add the dynamic size to the fixed start - // offset. - offset - = llvm::ConstantInt::get(IGF.IGM.SizeTy, - lastFixedOffset.getValue()); - offset = IGF.Builder.CreateAdd(offset, - prevElt.getType().getSize(IGF, prevType)); - } + offset = calcInitOffset(IGF, i, layout); } // Round up to alignment to get the offset. - auto alignMask = elt.getType().getAlignmentMask(IGF, eltTy); + auto alignMask = elt.getTypeForLayout().getAlignmentMask(IGF, eltTy); auto notAlignMask = IGF.Builder.CreateNot(alignMask); offset = IGF.Builder.CreateAdd(offset, alignMask); offset = IGF.Builder.CreateAnd(offset, notAlignMask); @@ -147,7 +149,7 @@ HeapNonFixedOffsets::HeapNonFixedOffsets(IRGenFunction &IGF, // Advance by the field's size to start the next field. offset = IGF.Builder.CreateAdd(offset, - elt.getType().getSize(IGF, eltTy)); + elt.getTypeForLayout().getSize(IGF, eltTy)); totalAlign = IGF.Builder.CreateOr(totalAlign, alignMask); break; @@ -237,7 +239,7 @@ static llvm::Function *createDtorFn(IRGenModule &IGM, if (field.isPOD()) continue; - field.getType().destroy( + field.getTypeForAccess().destroy( IGF, field.project(IGF, structAddr, offsets), fieldTy, true /*Called from metadata constructors: must be outlined*/); } @@ -1304,7 +1306,8 @@ llvm::Constant *IRGenModule::getFixLifetimeFn() { /// optimizer not to touch this value. void IRGenFunction::emitFixLifetime(llvm::Value *value) { // If we aren't running the LLVM ARC optimizer, we don't need to emit this. - if (!IGM.IRGen.Opts.shouldOptimize() || IGM.IRGen.Opts.DisableLLVMARCOpts) + if (!IGM.IRGen.Opts.shouldOptimize() || + IGM.IRGen.Opts.DisableSwiftSpecificLLVMOptzns) return; if (doesNotRequireRefCounting(value)) return; emitUnaryRefCountCall(*this, IGM.getFixLifetimeFn(), value); @@ -1433,16 +1436,18 @@ emitIsUniqueCall(llvm::Value *value, SourceLoc loc, bool isNonNull, return call; } -llvm::Value *IRGenFunction::emitIsEscapingClosureCall(llvm::Value *value, - SourceLoc sourceLoc) { +llvm::Value *IRGenFunction::emitIsEscapingClosureCall( + llvm::Value *value, SourceLoc sourceLoc, unsigned verificationType) { auto loc = SILLocation::decode(sourceLoc, IGM.Context.SourceMgr); auto line = llvm::ConstantInt::get(IGM.Int32Ty, loc.Line); + auto col = llvm::ConstantInt::get(IGM.Int32Ty, loc.Column); auto filename = IGM.getAddrOfGlobalString(loc.Filename); auto filenameLength = llvm::ConstantInt::get(IGM.Int32Ty, loc.Filename.size()); + auto type = llvm::ConstantInt::get(IGM.Int32Ty, verificationType); llvm::CallInst *call = Builder.CreateCall(IGM.getIsEscapingClosureAtFileLocationFn(), - {value, filename, filenameLength, line}); + {value, filename, filenameLength, line, col, type}); call->setDoesNotThrow(); return call; } @@ -1554,13 +1559,13 @@ class FixedBoxTypeInfoBase : public BoxTypeInfo { auto boxedInterfaceType = boxedType; if (env) { boxedInterfaceType = SILType::getPrimitiveType( - boxedType.getSwiftRValueType()->mapTypeOutOfContext() + boxedType.getASTType()->mapTypeOutOfContext() ->getCanonicalType(), boxedType.getCategory()); } auto boxDescriptor = IGF.IGM.getAddrOfBoxDescriptor( - boxedInterfaceType.getSwiftRValueType()); + boxedInterfaceType.getASTType()); llvm::Value *allocation = IGF.emitUnmanagedAlloc(layout, name, boxDescriptor); Address rawAddr = project(IGF, allocation, boxedType); @@ -1715,7 +1720,7 @@ Address irgen::emitAllocateExistentialBoxInBuffer( IRGenFunction &IGF, SILType boxedType, Address destBuffer, GenericEnvironment *env, const llvm::Twine &name, bool isOutlined) { // Get a box for the boxed value. - auto boxType = SILBoxType::get(boxedType.getSwiftRValueType()); + auto boxType = SILBoxType::get(boxedType.getASTType()); auto &boxTI = IGF.getTypeInfoForLowered(boxType).as(); OwnedAddress owned = boxTI.allocate(IGF, boxedType, env, name); Explosion box; @@ -1796,7 +1801,8 @@ llvm::Value *IRGenFunction::getLocalSelfMetadata() { case ObjCMetatype: return emitObjCMetadataRefForMetadata(*this, LocalSelf); case ObjectReference: - return emitDynamicTypeOfOpaqueHeapObject(*this, LocalSelf); + return emitDynamicTypeOfOpaqueHeapObject(*this, LocalSelf, + MetatypeRepresentation::Thick); } llvm_unreachable("Not a valid LocalSelfKind."); @@ -1909,18 +1915,33 @@ llvm::Value *irgen::emitHeapMetadataRefForHeapObject(IRGenFunction &IGF, SILType objectType, bool suppressCast) { return emitHeapMetadataRefForHeapObject(IGF, object, - objectType.getSwiftRValueType(), + objectType.getASTType(), suppressCast); } /// Given an opaque class instance pointer, produce the type metadata reference /// as a %type*. llvm::Value *irgen::emitDynamicTypeOfOpaqueHeapObject(IRGenFunction &IGF, - llvm::Value *object) { + llvm::Value *object, + MetatypeRepresentation repr) { object = IGF.Builder.CreateBitCast(object, IGF.IGM.ObjCPtrTy); - auto metadata = IGF.Builder.CreateCall(IGF.IGM.getGetObjectTypeFn(), - object, - object->getName() + ".Type"); + llvm::CallInst *metadata; + + switch (repr) { + case MetatypeRepresentation::ObjC: + metadata = IGF.Builder.CreateCall(IGF.IGM.getGetObjCClassFromObjectFn(), + object, + object->getName() + ".Type"); + break; + case MetatypeRepresentation::Thick: + metadata = IGF.Builder.CreateCall(IGF.IGM.getGetObjectTypeFn(), + object, + object->getName() + ".Type"); + break; + case MetatypeRepresentation::Thin: + llvm_unreachable("class metadata can't be thin"); + } + metadata->setDoesNotThrow(); metadata->setOnlyReadsMemory(); return metadata; @@ -1944,18 +1965,18 @@ emitHeapMetadataRefForUnknownHeapObject(IRGenFunction &IGF, /// as a %type*. llvm::Value *irgen::emitDynamicTypeOfHeapObject(IRGenFunction &IGF, llvm::Value *object, + MetatypeRepresentation repr, SILType objectType, bool suppressCast) { - // If it is known to have swift metadata, just load. - if (hasKnownSwiftMetadata(IGF.IGM, objectType.getSwiftRValueType())) { + // If it is known to have swift metadata, just load. A swift class is both + // heap metadata and type metadata. + if (hasKnownSwiftMetadata(IGF.IGM, objectType.getASTType())) { return emitLoadOfHeapMetadataRef(IGF, object, - getIsaEncodingForType(IGF.IGM, objectType.getSwiftRValueType()), + getIsaEncodingForType(IGF.IGM, objectType.getASTType()), suppressCast); } - // Okay, ask the runtime for the type metadata of this - // potentially-ObjC object. - return emitDynamicTypeOfOpaqueHeapObject(IGF, object); + return emitDynamicTypeOfOpaqueHeapObject(IGF, object, repr); } static ClassDecl *getRootClass(ClassDecl *theClass) { diff --git a/lib/IRGen/GenHeap.h b/lib/IRGen/GenHeap.h index acf214d5f60e1..2862a61ec9486 100644 --- a/lib/IRGen/GenHeap.h +++ b/lib/IRGen/GenHeap.h @@ -143,12 +143,14 @@ emitAllocateExistentialBoxInBuffer(IRGenFunction &IGF, SILType boxedType, /// Given an opaque class instance pointer, produce the type /// metadata reference as a %type*. llvm::Value *emitDynamicTypeOfOpaqueHeapObject(IRGenFunction &IGF, - llvm::Value *object); + llvm::Value *object, + MetatypeRepresentation rep); /// Given a heap-object instance, with some heap-object type, /// produce a reference to its type metadata. llvm::Value *emitDynamicTypeOfHeapObject(IRGenFunction &IGF, llvm::Value *object, + MetatypeRepresentation rep, SILType objectType, bool suppressCast = false); diff --git a/lib/IRGen/GenInit.cpp b/lib/IRGen/GenInit.cpp index 9d7ca4dc10ed3..1c90a69f9dd2b 100644 --- a/lib/IRGen/GenInit.cpp +++ b/lib/IRGen/GenInit.cpp @@ -45,7 +45,8 @@ void IRGenModule::emitSILGlobalVariable(SILGlobalVariable *var) { auto DbgTy = DebugTypeInfo::getGlobal(var, Int8Ty, Size(0), Alignment(1)); DebugInfo->emitGlobalVariableDeclaration( nullptr, var->getDecl()->getName().str(), "", DbgTy, - var->getLinkage() != SILLinkage::Public, SILLocation(var->getDecl())); + var->getLinkage() != SILLinkage::Public, + IRGenDebugInfo::NotHeapAllocated, SILLocation(var->getDecl())); } return; } diff --git a/lib/IRGen/GenKeyPath.cpp b/lib/IRGen/GenKeyPath.cpp index 7f3b5cdfcbf31..a482ae423a8cc 100644 --- a/lib/IRGen/GenKeyPath.cpp +++ b/lib/IRGen/GenKeyPath.cpp @@ -230,8 +230,7 @@ getAccessorForComputedComponent(IRGenModule &IGM, // Use the bound generic metadata to form a call to the original generic // accessor. WitnessMetadata ignoreWitnessMetadata; - auto forwardingSubs = genericEnv->getGenericSignature()->getSubstitutionMap( - genericEnv->getForwardingSubstitutions()); + auto forwardingSubs = genericEnv->getForwardingSubstitutionMap(); emitPolymorphicArguments(IGF, accessor->getLoweredFunctionType(), forwardingSubs, &ignoreWitnessMetadata, @@ -338,12 +337,12 @@ getWitnessTableForComputedComponent(IRGenModule &IGM, if (auto existing = IGM.Module.getNamedGlobal("swift_keyPathGenericWitnessTable")) return existing; - - auto linkInfo = LinkInfo::get(IGM, "swift_keyPathGenericWitnessTable", - SILLinkage::PublicExternal, - NotForDefinition, + + auto linkInfo = LinkInfo::get(UniversalLinkageInfo(IGM), + "swift_keyPathGenericWitnessTable", + SILLinkage::PublicExternal, NotForDefinition, /*weak imported*/ false); - + return createVariable(IGM, linkInfo, IGM.Int8PtrTy, IGM.getPointerAlignment()); } @@ -753,8 +752,7 @@ emitKeyPathComponent(IRGenModule &IGM, SmallVector descriptorArgs; auto componentSig = component.getExternalDecl()->getInnermostDeclContext() ->getGenericSignatureOfContext(); - auto subs = componentSig->getSubstitutionMap( - component.getExternalSubstitutions()); + auto subs = component.getExternalSubstitutions(); enumerateGenericSignatureRequirements( componentSig->getCanonicalSignature(), [&](GenericRequirement reqt) { diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index e18c7f57947ca..0763a57857c92 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -101,6 +101,8 @@ static Address createPointerSizedGEP(IRGenFunction &IGF, } void IRGenModule::setTrueConstGlobal(llvm::GlobalVariable *var) { + disableAddressSanitizer(*this, var); + switch (TargetInfo.OutputObjectFormat) { case llvm::Triple::UnknownObjectFormat: llvm_unreachable("unknown object format"); @@ -223,10 +225,10 @@ namespace { for (auto param : canSig->getGenericParams()) { // Currently, there are only type parameters. The parameter is a key - // argument if it hasn't been grounded by a same-type constraint. + // argument if it's canonical in its generic context. asImpl().addGenericParameter(GenericParamKind::Type, - /*key argument*/ !canSig->isConcreteType(param), - /*extra argument*/ false); + /*key argument*/ canSig->isCanonicalTypeInContext(param), + /*extra argument*/ false); } // Pad the structure up to four bytes for the following requirements. @@ -363,8 +365,8 @@ namespace { } void addExtendedContext() { - auto string = getTypeRef(IGM, - E->getSelfInterfaceType()->getCanonicalType()); + auto string = IGM.getTypeRef( + E->getSelfInterfaceType()->getCanonicalType()); B.addRelativeAddress(string); } @@ -2422,6 +2424,8 @@ void irgen::emitClassMetadata(IRGenModule &IGM, ClassDecl *classDecl, IGM.addObjCClass(var, classDecl->getAttrs().hasAttribute()); } + + IGM.IRGen.noteUseOfAnyParentTypeMetadata(classDecl); } llvm::Value *IRGenFunction::emitInvariantLoad(Address address, @@ -2629,10 +2633,14 @@ namespace { B.add(offset); } else { asImpl().flagUnfilledFieldOffset(); - B.addInt(IGM.IntPtrTy, 0); + B.addInt(IGM.Int32Ty, 0); } } + void noteEndOfFieldOffsets() { + B.addAlignmentPadding(super::IGM.getPointerAlignment()); + } + void addGenericArgument(CanType type) { B.addNullPointer(IGM.TypeMetadataPtrTy); } @@ -2727,7 +2735,7 @@ namespace { /// Fill in a constant field offset vector if possible. PartialPattern buildExtraDataPattern() { ConstantInitBuilder builder(IGM); - auto init = builder.beginArray(IGM.SizeTy); + auto init = builder.beginArray(IGM.Int32Ty); struct Scanner : StructMetadataScanner { SILType Type; @@ -2744,7 +2752,11 @@ namespace { } assert(IGM.isKnownEmpty(Type.getFieldType(field, IGM.getSILModule()), ResilienceExpansion::Maximal)); - B.addInt(IGM.SizeTy, 0); + B.addInt32(0); + } + + void noteEndOfFieldOffsets() { + B.addAlignmentPadding(IGM.getPointerAlignment()); } }; Scanner(IGM, Target, getLoweredType(), init).layout(); @@ -2810,6 +2822,19 @@ void irgen::emitStructMetadata(IRGenModule &IGM, StructDecl *structDecl) { IGM.defineTypeMetadata(declaredType, isIndirect, isPattern, canBeConstant, init.finishAndCreateFuture()); + + IGM.IRGen.noteUseOfAnyParentTypeMetadata(structDecl); +} + +void IRGenerator::noteUseOfAnyParentTypeMetadata(NominalTypeDecl *type) { + // If this is a nested type we also potentially might need the outer types. + auto *declCtxt = type->getDeclContext(); + auto *parentNominalDecl = + declCtxt->getAsNominalTypeOrNominalTypeExtensionContext(); + if (!parentNominalDecl) + return; + + noteUseOfTypeMetadata(parentNominalDecl); } // Enums @@ -3014,6 +3039,8 @@ void irgen::emitEnumMetadata(IRGenModule &IGM, EnumDecl *theEnum) { IGM.defineTypeMetadata(declaredType, isIndirect, isPattern, canBeConstant, init.finishAndCreateFuture()); + + IGM.IRGen.noteUseOfAnyParentTypeMetadata(theEnum); } llvm::Value *IRGenFunction::emitObjCSelectorRefLoad(StringRef selector) { @@ -3340,7 +3367,7 @@ IRGenModule::getAddrOfForeignTypeMetadataCandidate(CanType type) { if (auto classType = dyn_cast(type)) { assert(!classType.getParent()); auto classDecl = classType->getDecl(); - assert(classDecl->isForeign()); + assert(classDecl->getForeignClassKind() == ClassDecl::ForeignKind::CFType); ForeignClassMetadataBuilder builder(*this, classDecl, init); builder.layout(); @@ -3381,7 +3408,8 @@ IRGenModule::getAddrOfForeignTypeMetadataCandidate(CanType type) { if (auto enclosing = type->getNominalParent()) { auto canonicalEnclosing = enclosing->getCanonicalType(); if (requiresForeignTypeMetadata(canonicalEnclosing)) { - getAddrOfForeignTypeMetadataCandidate(canonicalEnclosing); + (void)getTypeMetadataAccessFunction(*this, canonicalEnclosing, + ForDefinition); } } @@ -3634,7 +3662,7 @@ namespace { // Look up the dispatch thunk if the protocol is resilient. llvm::Constant *thunk = nullptr; - if (Protocol->isResilient()) + if (IGM.isResilient(Protocol, ResilienceExpansion::Minimal)) thunk = IGM.getAddrOfDispatchThunk(func, NotForDefinition); // Classify the function. @@ -3699,7 +3727,7 @@ void IRGenModule::emitProtocolDecl(ProtocolDecl *protocol) { } SILDefaultWitnessTable *defaultWitnesses = nullptr; - if (protocol->isResilient()) + if (isResilient(protocol, ResilienceExpansion::Minimal)) defaultWitnesses = getSILModule().lookUpDefaultWitnessTable(protocol); ConstantInitBuilder initBuilder(*this); @@ -3710,6 +3738,7 @@ void IRGenModule::emitProtocolDecl(ProtocolDecl *protocol) { auto var = cast( getAddrOfProtocolDescriptor(protocol, init.finishAndCreateFuture())); var->setConstant(true); + disableAddressSanitizer(*this, var); // Note that we emitted this protocol. SwiftProtocols.push_back(protocol); @@ -3804,15 +3833,13 @@ GenericRequirementsMetadata irgen::addGenericRequirements( break; case RequirementKind::Conformance: { - // ABI TODO: We also need a *key* argument that uniquely identifies - // the conformance for conformance requirements as well. auto protocol = requirement.getSecondType()->castTo() ->getDecl(); bool needsWitnessTable = Lowering::TypeConverter::protocolRequiresWitnessTable(protocol); auto flags = GenericRequirementFlags(GenericRequirementKind::Protocol, - /*TODO key argument*/ false, - needsWitnessTable); + /*key argument*/needsWitnessTable, + /*extra argument*/false); auto descriptorRef = IGM.getConstantReferenceForProtocolDescriptor(protocol); addGenericRequirement(IGM, B, metadata, sig, flags, @@ -3829,7 +3856,7 @@ GenericRequirementsMetadata irgen::addGenericRequirements( auto flags = GenericRequirementFlags(abiKind, false, false); auto typeName = - getTypeRef(IGM, requirement.getSecondType()->getCanonicalType()); + IGM.getTypeRef(requirement.getSecondType()->getCanonicalType()); addGenericRequirement(IGM, B, metadata, sig, flags, requirement.getFirstType(), diff --git a/lib/IRGen/GenObjC.cpp b/lib/IRGen/GenObjC.cpp index 250041f912bf7..c87c61802335e 100644 --- a/lib/IRGen/GenObjC.cpp +++ b/lib/IRGen/GenObjC.cpp @@ -687,7 +687,7 @@ Callee irgen::getObjCMethodCallee(IRGenFunction &IGF, if (auto searchType = methodInfo.getSearchType()) { receiverValue = emitSuperArgument(IGF, isInstanceMethod, selfValue, - searchType.getSwiftRValueType()); + searchType.getASTType()); } else { receiverValue = selfValue; } @@ -934,8 +934,8 @@ void irgen::emitObjCPartialApplication(IRGenFunction &IGF, Address fieldAddr = fieldLayout.project(IGF, dataAddr, offsets); Explosion selfParams; selfParams.add(self); - fieldLayout.getType().initializeFromParams(IGF, selfParams, fieldAddr, - fieldType, false); + fieldLayout.getTypeForAccess().initializeFromParams(IGF, selfParams, fieldAddr, + fieldType, false); // Create the forwarding stub. llvm::Function *forwarder = emitObjCPartialApplicationForwarder(IGF.IGM, @@ -1064,7 +1064,7 @@ static clang::CanQualType getObjCPropertyType(IRGenModule &IGM, assert(getter); CanSILFunctionType methodTy = getObjCMethodType(IGM, getter); return IGM.getClangType( - methodTy->getFormalCSemanticResult().getSwiftRValueType()); + methodTy->getFormalCSemanticResult().getASTType()); } void irgen::getObjCEncodingForPropertyType(IRGenModule &IGM, @@ -1096,7 +1096,7 @@ static llvm::Constant *getObjCEncodingForTypes(IRGenModule &IGM, // Return type. { - auto clangType = IGM.getClangType(resultType.getSwiftRValueType()); + auto clangType = IGM.getClangType(resultType.getASTType()); if (clangType.isNull()) return llvm::ConstantPointerNull::get(IGM.Int8PtrTy); HelperGetObjCEncodingForType(clangASTContext, clangType, encodingString, diff --git a/lib/IRGen/GenObjC.h b/lib/IRGen/GenObjC.h index 4102210895339..b5f8e8f9ee485 100644 --- a/lib/IRGen/GenObjC.h +++ b/lib/IRGen/GenObjC.h @@ -29,7 +29,6 @@ namespace swift { struct SILDeclRef; class SILFunction; class SILType; - class Substitution; namespace irgen { class Callee; diff --git a/lib/IRGen/GenOpaque.cpp b/lib/IRGen/GenOpaque.cpp index a336b9408cf4f..0c074298144bc 100644 --- a/lib/IRGen/GenOpaque.cpp +++ b/lib/IRGen/GenOpaque.cpp @@ -66,9 +66,7 @@ static llvm::Type *createWitnessType(IRGenModule &IGM, ValueWitness index) { } // T *(*initializeBufferWithCopyOfBuffer)(B *dest, B *src, M *self); - // T *(*initializeBufferWithTakeOfBuffer)(B *dest, B *src, M *self); - case ValueWitness::InitializeBufferWithCopyOfBuffer: - case ValueWitness::InitializeBufferWithTakeOfBuffer: { + case ValueWitness::InitializeBufferWithCopyOfBuffer: { llvm::Type *bufPtrTy = IGM.getFixedBufferTy()->getPointerTo(0); llvm::Type *args[] = { bufPtrTy, bufPtrTy, IGM.TypeMetadataPtrTy }; return llvm::FunctionType::get(IGM.OpaquePtrTy, args, /*isVarArg*/ false); @@ -212,7 +210,6 @@ static llvm::AttributeList getValueWitnessAttrs(IRGenModule &IGM, // These have two arguments and they don't alias each other. case ValueWitness::AssignWithTake: case ValueWitness::InitializeBufferWithCopyOfBuffer: - case ValueWitness::InitializeBufferWithTakeOfBuffer: case ValueWitness::InitializeWithCopy: case ValueWitness::InitializeWithTake: return attrs.addAttribute(ctx, 1, llvm::Attribute::NoAlias) @@ -259,8 +256,6 @@ static StringRef getValueWitnessLabel(ValueWitness index) { return "initializeWithCopy"; case ValueWitness::InitializeWithTake: return "initializeWithTake"; - case ValueWitness::InitializeBufferWithTakeOfBuffer: - return "initializeBufferWithTakeOfBuffer"; case ValueWitness::Size: return "size"; case ValueWitness::Flags: @@ -388,6 +383,11 @@ IRGenFunction::emitValueWitnessFunctionRef(SILType type, return witness; } +static llvm::Value *emitCastToOpaquePtr(IRGenFunction &IGF, + Address object) { + return IGF.Builder.CreateBitCast(object.getAddress(), IGF.IGM.OpaquePtrTy); +} + llvm::Value *irgen::emitInitializeBufferWithCopyOfBufferCall(IRGenFunction &IGF, SILType T, Address destBuffer, @@ -412,31 +412,6 @@ irgen::emitInitializeBufferWithCopyOfBufferCall(IRGenFunction &IGF, return call; } -llvm::Value * -irgen::emitInitializeBufferWithTakeOfBufferCall(IRGenFunction &IGF, - SILType T, - Address destBuffer, - Address srcBuffer) { - auto metadata = IGF.emitTypeMetadataRefForLayout(T); - return emitInitializeBufferWithTakeOfBufferCall(IGF, metadata, - destBuffer, srcBuffer); -} - -/// Emit a call to do an 'initializeBufferWithTakeOfBuffer' operation. -llvm::Value * -irgen::emitInitializeBufferWithTakeOfBufferCall(IRGenFunction &IGF, - llvm::Value *metadata, - Address destBuffer, - Address srcBuffer) { - auto copyFn = emitLoadOfValueWitnessFunctionFromMetadata(IGF, metadata, - ValueWitness::InitializeBufferWithTakeOfBuffer); - llvm::CallInst *call = - IGF.Builder.CreateCall(copyFn, - {destBuffer.getAddress(), srcBuffer.getAddress(), metadata}); - - return call; -} - /// Emit a dynamic alloca call to allocate enough memory to hold an object of /// type 'T' and an optional llvm.stackrestore point if 'isInEntryBlock' is /// false. @@ -529,10 +504,8 @@ void irgen::emitInitializeArrayWithCopyCall(IRGenFunction &IGF, Address srcObject, llvm::Value *count) { auto metadata = IGF.emitTypeMetadataRefForLayout(T); - auto dest = - IGF.Builder.CreateBitCast(destObject.getAddress(), IGF.IGM.OpaquePtrTy); - auto src = - IGF.Builder.CreateBitCast(srcObject.getAddress(), IGF.IGM.OpaquePtrTy); + auto dest = emitCastToOpaquePtr(IGF, destObject); + auto src = emitCastToOpaquePtr(IGF, srcObject); IGF.Builder.CreateCall(IGF.IGM.getArrayInitWithCopyFn(), {dest, src, count, metadata}); } @@ -544,10 +517,8 @@ void irgen::emitInitializeArrayWithTakeNoAliasCall(IRGenFunction &IGF, Address srcObject, llvm::Value *count) { auto metadata = IGF.emitTypeMetadataRefForLayout(T); - auto dest = - IGF.Builder.CreateBitCast(destObject.getAddress(), IGF.IGM.OpaquePtrTy); - auto src = - IGF.Builder.CreateBitCast(srcObject.getAddress(), IGF.IGM.OpaquePtrTy); + auto dest = emitCastToOpaquePtr(IGF, destObject); + auto src = emitCastToOpaquePtr(IGF, srcObject); IGF.Builder.CreateCall(IGF.IGM.getArrayInitWithTakeNoAliasFn(), {dest, src, count, metadata}); } @@ -559,10 +530,8 @@ void irgen::emitInitializeArrayWithTakeFrontToBackCall(IRGenFunction &IGF, Address srcObject, llvm::Value *count) { auto metadata = IGF.emitTypeMetadataRefForLayout(T); - auto dest = - IGF.Builder.CreateBitCast(destObject.getAddress(), IGF.IGM.OpaquePtrTy); - auto src = - IGF.Builder.CreateBitCast(srcObject.getAddress(), IGF.IGM.OpaquePtrTy); + auto dest = emitCastToOpaquePtr(IGF, destObject); + auto src = emitCastToOpaquePtr(IGF, srcObject); IGF.Builder.CreateCall(IGF.IGM.getArrayInitWithTakeFrontToBackFn(), {dest, src, count, metadata}); } @@ -574,10 +543,8 @@ void irgen::emitInitializeArrayWithTakeBackToFrontCall(IRGenFunction &IGF, Address srcObject, llvm::Value *count) { auto metadata = IGF.emitTypeMetadataRefForLayout(T); - auto dest = - IGF.Builder.CreateBitCast(destObject.getAddress(), IGF.IGM.OpaquePtrTy); - auto src = - IGF.Builder.CreateBitCast(srcObject.getAddress(), IGF.IGM.OpaquePtrTy); + auto dest = emitCastToOpaquePtr(IGF, destObject); + auto src = emitCastToOpaquePtr(IGF, srcObject); IGF.Builder.CreateCall(IGF.IGM.getArrayInitWithTakeBackToFrontFn(), {dest, src, count, metadata}); } @@ -590,8 +557,9 @@ void irgen::emitAssignWithCopyCall(IRGenFunction &IGF, llvm::Value *metadata; auto copyFn = IGF.emitValueWitnessFunctionRef(T, metadata, ValueWitness::AssignWithCopy); - IGF.Builder.CreateCall(copyFn, - {destObject.getAddress(), srcObject.getAddress(), metadata}); + auto dest = emitCastToOpaquePtr(IGF, destObject); + auto src = emitCastToOpaquePtr(IGF, srcObject); + IGF.Builder.CreateCall(copyFn, {dest, src, metadata}); } /// Emit a call to do an 'assignWithCopy' operation. @@ -601,8 +569,9 @@ void irgen::emitAssignWithCopyCall(IRGenFunction &IGF, Address srcObject) { auto copyFn = emitLoadOfValueWitnessFunctionFromMetadata(IGF, metadata, ValueWitness::AssignWithCopy); - IGF.Builder.CreateCall(copyFn, - {destObject.getAddress(), srcObject.getAddress(), metadata}); + auto dest = emitCastToOpaquePtr(IGF, destObject); + auto src = emitCastToOpaquePtr(IGF, srcObject); + IGF.Builder.CreateCall(copyFn, {dest, src, metadata}); } /// Emit a call to do an 'arrayAssignWithCopyNoAlias' operation. @@ -610,10 +579,8 @@ void irgen::emitAssignArrayWithCopyNoAliasCall(IRGenFunction &IGF, SILType T, Address destObject, Address srcObject, llvm::Value *count) { auto metadata = IGF.emitTypeMetadataRefForLayout(T); - auto dest = - IGF.Builder.CreateBitCast(destObject.getAddress(), IGF.IGM.OpaquePtrTy); - auto src = - IGF.Builder.CreateBitCast(srcObject.getAddress(), IGF.IGM.OpaquePtrTy); + auto dest = emitCastToOpaquePtr(IGF, destObject); + auto src = emitCastToOpaquePtr(IGF, srcObject); IGF.Builder.CreateCall(IGF.IGM.getArrayAssignWithCopyNoAliasFn(), {dest, src, count, metadata}); } @@ -625,10 +592,8 @@ void irgen::emitAssignArrayWithCopyFrontToBackCall(IRGenFunction &IGF, Address srcObject, llvm::Value *count) { auto metadata = IGF.emitTypeMetadataRefForLayout(T); - auto dest = - IGF.Builder.CreateBitCast(destObject.getAddress(), IGF.IGM.OpaquePtrTy); - auto src = - IGF.Builder.CreateBitCast(srcObject.getAddress(), IGF.IGM.OpaquePtrTy); + auto dest = emitCastToOpaquePtr(IGF, destObject); + auto src = emitCastToOpaquePtr(IGF, srcObject); IGF.Builder.CreateCall(IGF.IGM.getArrayAssignWithCopyFrontToBackFn(), {dest, src, count, metadata}); } @@ -640,10 +605,8 @@ void irgen::emitAssignArrayWithCopyBackToFrontCall(IRGenFunction &IGF, Address srcObject, llvm::Value *count) { auto metadata = IGF.emitTypeMetadataRefForLayout(T); - auto dest = - IGF.Builder.CreateBitCast(destObject.getAddress(), IGF.IGM.OpaquePtrTy); - auto src = - IGF.Builder.CreateBitCast(srcObject.getAddress(), IGF.IGM.OpaquePtrTy); + auto dest = emitCastToOpaquePtr(IGF, destObject); + auto src = emitCastToOpaquePtr(IGF, srcObject); IGF.Builder.CreateCall(IGF.IGM.getArrayAssignWithCopyBackToFrontFn(), {dest, src, count, metadata}); } @@ -656,8 +619,9 @@ void irgen::emitAssignWithTakeCall(IRGenFunction &IGF, llvm::Value *metadata; auto copyFn = IGF.emitValueWitnessFunctionRef(T, metadata, ValueWitness::AssignWithTake); - IGF.Builder.CreateCall(copyFn, - {destObject.getAddress(), srcObject.getAddress(), metadata}); + auto dest = emitCastToOpaquePtr(IGF, destObject); + auto src = emitCastToOpaquePtr(IGF, srcObject); + IGF.Builder.CreateCall(copyFn, {dest, src, metadata}); } /// Emit a call to do an 'arrayAssignWithTake' operation. @@ -665,10 +629,8 @@ void irgen::emitAssignArrayWithTakeCall(IRGenFunction &IGF, SILType T, Address destObject, Address srcObject, llvm::Value *count) { auto metadata = IGF.emitTypeMetadataRefForLayout(T); - auto dest = - IGF.Builder.CreateBitCast(destObject.getAddress(), IGF.IGM.OpaquePtrTy); - auto src = - IGF.Builder.CreateBitCast(srcObject.getAddress(), IGF.IGM.OpaquePtrTy); + auto dest = emitCastToOpaquePtr(IGF, destObject); + auto src = emitCastToOpaquePtr(IGF, srcObject); IGF.Builder.CreateCall(IGF.IGM.getArrayAssignWithTakeFn(), {dest, src, count, metadata}); } @@ -683,8 +645,7 @@ void irgen::emitDestroyArrayCall(IRGenFunction &IGF, return; auto metadata = IGF.emitTypeMetadataRefForLayout(T); - auto obj = - IGF.Builder.CreateBitCast(object.getAddress(), IGF.IGM.OpaquePtrTy); + auto obj = emitCastToOpaquePtr(IGF, object); IGF.Builder.CreateCall(IGF.IGM.getArrayDestroyFn(), {obj, count, metadata}); } @@ -696,9 +657,10 @@ llvm::Value *irgen::emitGetExtraInhabitantIndexCall(IRGenFunction &IGF, llvm::Value *metadata; auto fn = IGF.emitValueWitnessFunctionRef(T, metadata, ValueWitness::GetExtraInhabitantIndex); - + + auto src = emitCastToOpaquePtr(IGF, srcObject); llvm::CallInst *call = - IGF.Builder.CreateCall(fn, {srcObject.getAddress(), metadata}); + IGF.Builder.CreateCall(fn, {src, metadata}); return call; } @@ -711,8 +673,9 @@ llvm::Value *irgen::emitStoreExtraInhabitantCall(IRGenFunction &IGF, llvm::Value *metadata; auto fn = IGF.emitValueWitnessFunctionRef(T, metadata, ValueWitness::StoreExtraInhabitant); + auto dest = emitCastToOpaquePtr(IGF, destObject); llvm::CallInst *call = - IGF.Builder.CreateCall(fn, {destObject.getAddress(), index, metadata}); + IGF.Builder.CreateCall(fn, {dest, index, metadata}); return call; } @@ -783,16 +746,15 @@ llvm::Value *irgen::emitGetEnumTagSinglePayloadCall(IRGenFunction &IGF, llvm::Value *metadata; auto fn = IGF.emitValueWitnessFunctionRef( T, metadata, ValueWitness::GetEnumTagSinglePayload); + auto dest = emitCastToOpaquePtr(IGF, destObject); llvm::CallInst *call = IGF.Builder.CreateCall( - fn, {destObject.getAddress(), numEmptyCases, metadata}); + fn, {dest, numEmptyCases, metadata}); return call; } auto *metadata = IGF.emitTypeMetadataRefForLayout(T); auto *func = getGetEnumTagSinglePayloadTrampolineFn(IGF.IGM); - auto *result = IGF.Builder.CreateCall( - func, - {IGF.Builder.CreateBitCast(destObject.getAddress(), IGF.IGM.OpaquePtrTy), - numEmptyCases, metadata}); + auto dest = emitCastToOpaquePtr(IGF, destObject); + auto *result = IGF.Builder.CreateCall(func, {dest, numEmptyCases, metadata}); return result; } @@ -803,17 +765,17 @@ llvm::Value *irgen::emitStoreEnumTagSinglePayloadCall( llvm::Value *metadata; auto fn = IGF.emitValueWitnessFunctionRef( T, metadata, ValueWitness::StoreEnumTagSinglePayload); + auto dest = emitCastToOpaquePtr(IGF, destObject); llvm::CallInst *call = IGF.Builder.CreateCall( - fn, {destObject.getAddress(), whichCase, numEmptyCases, metadata}); + fn, {dest, whichCase, numEmptyCases, metadata}); return call; } auto *metadata = IGF.emitTypeMetadataRefForLayout(T); auto *func = getStoreEnumTagSinglePayloadTrampolineFn(IGF.IGM); - auto *result = IGF.Builder.CreateCall( - func, - {IGF.Builder.CreateBitCast(destObject.getAddress(), IGF.IGM.OpaquePtrTy), - whichCase, numEmptyCases, metadata}); + auto dest = emitCastToOpaquePtr(IGF, destObject); + auto *result = IGF.Builder.CreateCall(func, + {dest, whichCase, numEmptyCases, metadata}); return result; } @@ -825,8 +787,9 @@ llvm::Value *irgen::emitGetEnumTagCall(IRGenFunction &IGF, auto fn = IGF.emitValueWitnessFunctionRef(T, metadata, ValueWitness::GetEnumTag); + auto src = emitCastToOpaquePtr(IGF, srcObject); llvm::CallInst *call = - IGF.Builder.CreateCall(fn, {srcObject.getAddress(), metadata}); + IGF.Builder.CreateCall(fn, {src, metadata}); return call; } @@ -838,7 +801,8 @@ void irgen::emitDestructiveProjectEnumDataCall(IRGenFunction &IGF, llvm::Value *metadata; auto fn = IGF.emitValueWitnessFunctionRef(T, metadata, ValueWitness::DestructiveProjectEnumData); - IGF.Builder.CreateCall(fn, {srcObject.getAddress(), metadata}); + auto src = emitCastToOpaquePtr(IGF, srcObject); + IGF.Builder.CreateCall(fn, {src, metadata}); } /// Emit a call to the 'destructiveInjectEnumTag' operation. @@ -850,7 +814,8 @@ void irgen::emitDestructiveInjectEnumTagCall(IRGenFunction &IGF, llvm::Value *metadata; auto fn = IGF.emitValueWitnessFunctionRef(T, metadata, ValueWitness::DestructiveInjectEnumTag); - IGF.Builder.CreateCall(fn, {srcObject.getAddress(), tagValue, metadata}); + auto src = emitCastToOpaquePtr(IGF, srcObject); + IGF.Builder.CreateCall(fn, {src, tagValue, metadata}); } /// Load the 'size' value witness from the given table as a size_t. @@ -950,7 +915,9 @@ void irgen::emitInitializeWithCopyCall(IRGenFunction &IGF, llvm::Value *metadata; auto fn = IGF.emitValueWitnessFunctionRef(T, metadata, ValueWitness::InitializeWithCopy); - IGF.Builder.CreateCall(fn, {dest.getAddress(), src.getAddress(), metadata}); + auto destPtr = emitCastToOpaquePtr(IGF, dest); + auto srcPtr = emitCastToOpaquePtr(IGF, src); + IGF.Builder.CreateCall(fn, {destPtr, srcPtr, metadata}); } llvm::Value *irgen::emitInitializeWithCopyCall(IRGenFunction &IGF, @@ -958,8 +925,10 @@ llvm::Value *irgen::emitInitializeWithCopyCall(IRGenFunction &IGF, Address dest, Address src) { auto copyFn = emitLoadOfValueWitnessFunctionFromMetadata( IGF, metadata, ValueWitness::InitializeWithCopy); + auto destPtr = emitCastToOpaquePtr(IGF, dest); + auto srcPtr = emitCastToOpaquePtr(IGF, src); llvm::CallInst *call = IGF.Builder.CreateCall( - copyFn, {dest.getAddress(), src.getAddress(), metadata}); + copyFn, {destPtr, srcPtr, metadata}); return call; } @@ -972,7 +941,9 @@ void irgen::emitInitializeWithTakeCall(IRGenFunction &IGF, llvm::Value *metadata; auto fn = IGF.emitValueWitnessFunctionRef(T, metadata, ValueWitness::InitializeWithTake); - IGF.Builder.CreateCall(fn, {dest.getAddress(), src.getAddress(), metadata}); + auto destPtr = emitCastToOpaquePtr(IGF, dest); + auto srcPtr = emitCastToOpaquePtr(IGF, src); + IGF.Builder.CreateCall(fn, {destPtr, srcPtr, metadata}); } llvm::Value *irgen::emitInitializeWithTakeCall(IRGenFunction &IGF, @@ -980,8 +951,10 @@ llvm::Value *irgen::emitInitializeWithTakeCall(IRGenFunction &IGF, Address dest, Address src) { auto copyFn = emitLoadOfValueWitnessFunctionFromMetadata( IGF, metadata, ValueWitness::InitializeWithTake); - llvm::CallInst *call = IGF.Builder.CreateCall( - copyFn, {dest.getAddress(), src.getAddress(), metadata}); + auto destPtr = emitCastToOpaquePtr(IGF, dest); + auto srcPtr = emitCastToOpaquePtr(IGF, src); + llvm::CallInst *call = + IGF.Builder.CreateCall(copyFn, {destPtr, srcPtr, metadata}); return call; } @@ -996,14 +969,16 @@ void irgen::emitDestroyCall(IRGenFunction &IGF, llvm::Value *metadata; auto fn = IGF.emitValueWitnessFunctionRef(T, metadata, ValueWitness::Destroy); - IGF.Builder.CreateCall(fn, {object.getAddress(), metadata}); + auto objectPtr = emitCastToOpaquePtr(IGF, object); + IGF.Builder.CreateCall(fn, {objectPtr, metadata}); } void irgen::emitDestroyCall(IRGenFunction &IGF, llvm::Value *metadata, Address object) { auto fn = emitLoadOfValueWitnessFunctionFromMetadata(IGF, metadata, ValueWitness::Destroy); - IGF.Builder.CreateCall(fn, {object.getAddress(), metadata}); + auto objectPtr = emitCastToOpaquePtr(IGF, object); + IGF.Builder.CreateCall(fn, {objectPtr, metadata}); } static llvm::Constant *getAllocateValueBufferFunction(IRGenModule &IGM) { diff --git a/lib/IRGen/GenOpaque.h b/lib/IRGen/GenOpaque.h index 0afcc4a2ef6e7..622bc59af2856 100644 --- a/lib/IRGen/GenOpaque.h +++ b/lib/IRGen/GenOpaque.h @@ -58,18 +58,6 @@ namespace irgen { Address destBuffer, Address srcBuffer); - /// Emit a call to do an 'initializeBufferWithTakeOfBuffer' operation. - llvm::Value *emitInitializeBufferWithTakeOfBufferCall(IRGenFunction &IGF, - llvm::Value *metadata, - Address destBuffer, - Address srcBuffer); - - /// Emit a call to do an 'initializeBufferWithTakeOfBuffer' operation. - llvm::Value *emitInitializeBufferWithTakeOfBufferCall(IRGenFunction &IGF, - SILType T, - Address destBuffer, - Address srcBuffer); - /// Emit a call to do an 'initializeWithCopy' operation. void emitInitializeWithCopyCall(IRGenFunction &IGF, SILType T, diff --git a/lib/IRGen/GenPoly.cpp b/lib/IRGen/GenPoly.cpp index 15cdb51086f86..870e3b4b956b6 100644 --- a/lib/IRGen/GenPoly.cpp +++ b/lib/IRGen/GenPoly.cpp @@ -43,8 +43,7 @@ static SILType applyContextArchetypes(IRGenFunction &IGF, } auto substType = - IGF.IGM.getGenericEnvironment()->mapTypeIntoContext( - type.getSwiftRValueType()) + IGF.IGM.getGenericEnvironment()->mapTypeIntoContext(type.getASTType()) ->getCanonicalType(); return SILType::getPrimitiveType(substType, type.getCategory()); } diff --git a/lib/IRGen/GenPoly.h b/lib/IRGen/GenPoly.h index 0805fbd902c46..478b61132d216 100644 --- a/lib/IRGen/GenPoly.h +++ b/lib/IRGen/GenPoly.h @@ -25,7 +25,6 @@ namespace llvm { namespace swift { class CanType; - class Substitution; namespace irgen { class Explosion; diff --git a/lib/IRGen/GenProto.cpp b/lib/IRGen/GenProto.cpp index f44f9dd1fb0aa..496e8a776b0d9 100644 --- a/lib/IRGen/GenProto.cpp +++ b/lib/IRGen/GenProto.cpp @@ -627,7 +627,9 @@ bindParameterSource(SILParameterInfo param, unsigned paramIndex, llvm::Value *instanceRef = getParameter(paramIndex); SILType instanceType = SILType::getPrimitiveObjectType(paramType); llvm::Value *metadata = - emitDynamicTypeOfHeapObject(IGF, instanceRef, instanceType); + emitDynamicTypeOfHeapObject(IGF, instanceRef, + MetatypeRepresentation::Thick, + instanceType); IGF.bindLocalTypeDataFromTypeMetadata(paramType, IsInexact, metadata, MetadataState::Complete); return; @@ -686,7 +688,9 @@ void BindPolymorphicParameter::emit(Explosion &nativeParam, unsigned paramIndex) llvm::Value *instanceRef = nativeParam.getAll()[0]; SILType instanceType = SILType::getPrimitiveObjectType(paramType); llvm::Value *metadata = - emitDynamicTypeOfHeapObject(IGF, instanceRef, instanceType); + emitDynamicTypeOfHeapObject(IGF, instanceRef, + MetatypeRepresentation::Thick, + instanceType); IGF.bindLocalTypeDataFromTypeMetadata(paramType, IsInexact, metadata, MetadataState::Complete); } @@ -1162,6 +1166,21 @@ class AccessorConformanceInfo : public ConformanceInfo { } }; +/// Emit a reference to the witness table for a foreign type. +llvm::Value *uniqueForeignWitnessTableRef(IRGenFunction &IGF, + llvm::Value *candidate, + llvm::Value *typeDescriptorRef, + llvm::Value *protocolDescriptor) { + auto call = IGF.Builder.CreateCall( + IGF.IGM.getGetForeignWitnessTableFn(), + {candidate, typeDescriptorRef, protocolDescriptor}); + call->addAttribute(llvm::AttributeList::FunctionIndex, + llvm::Attribute::NoUnwind); + call->addAttribute(llvm::AttributeList::FunctionIndex, + llvm::Attribute::ReadNone); + return call; +} + /// A class which lays out a specific conformance to a protocol. class WitnessTableBuilder : public SILWitnessVisitor { IRGenModule &IGM; @@ -1832,10 +1851,6 @@ static llvm::Constant *emitResilientWitnessTable(IRGenModule &IGM, SILFunction *Func = entry.getMethodWitness().Witness; llvm::Constant *witness; if (Func) { - // Force the thunk to be emitted in the current translation unit - // when in multi-threaded mode. - IGM.IRGen.forceLocalEmitOfLazyFunction(Func); - witness = IGM.getAddrOfSILFunction(Func, NotForDefinition); } else { // The method is removed by dead method elimination. @@ -1866,15 +1881,31 @@ void WitnessTableBuilder::buildAccessFunction(llvm::Constant *wtable) { wtable = llvm::ConstantExpr::getBitCast(wtable, IGM.WitnessTablePtrTy); + auto conformingType = Conformance.getType()->getCanonicalType(); + bool needsUniquing = Conformance.isSynthesizedNonUnique(); + + // We will unique the witness table if it is for a non-unique foreign type. + auto uniqueWitnessTableIfIsForeignType = + [&](llvm::Value *witness) -> llvm::Value * { + if (!needsUniquing) + return witness; + auto *nominal = conformingType->getAnyNominal(); + auto *proto = Conformance.getProtocol(); + return uniqueForeignWitnessTableRef( + IGF, witness, + IGM.getAddrOfTypeContextDescriptor(nominal, DontRequireMetadata), + IGM.getAddrOfProtocolDescriptor(proto)); + }; + // If specialization isn't required, just return immediately. // TODO: allow dynamic specialization? if (!RequiresSpecialization) { - IGF.Builder.CreateRet(wtable); + auto res = uniqueWitnessTableIfIsForeignType(wtable); + IGF.Builder.CreateRet(res); return; } - // The target metadata is the first argument. - assert(isDependentConformance(&Conformance)); + assert(isDependentConformance(&Conformance) || needsUniquing); Explosion params = IGF.collectParameters(); llvm::Value *metadata; @@ -1977,7 +2008,8 @@ void WitnessTableBuilder::buildAccessFunction(llvm::Constant *wtable) { call->setDoesNotThrow(); - IGF.Builder.CreateRet(call); + // Possibly unique the witness table if this is a foreign type. + IGF.Builder.CreateRet(uniqueWitnessTableIfIsForeignType(call)); } llvm::Constant *WitnessTableBuilder::buildInstantiationFunction() { @@ -2046,6 +2078,21 @@ llvm::Constant *WitnessTableBuilder::buildInstantiationFunction() { return fn; } +void IRGenModule::ensureRelativeSymbolCollocation(SILWitnessTable &wt) { + // Only resilient conformances use relative pointers for witness methods. + if (wt.isDeclaration() || isAvailableExternally(wt.getLinkage()) || + !isResilientConformance(wt.getConformance())) + return; + + for (auto &entry : wt.getEntries()) { + if (entry.getKind() != SILWitnessTable::Method) + continue; + auto *witness = entry.getMethodWitness().Witness; + if (witness) + IRGen.forceLocalEmitOfLazyFunction(witness); + } +} + /// Do a memoized witness-table layout for a protocol. const ProtocolInfo &IRGenModule::getProtocolInfo(ProtocolDecl *protocol) { return Types.getProtocolInfo(protocol); @@ -2114,7 +2161,10 @@ ProtocolInfo::getConformance(IRGenModule &IGM, ProtocolDecl *protocol, // If the conformance is dependent in any way, we need to unique it. // TODO: maybe this should apply whenever it's out of the module? // TODO: actually enable this - if (isDependentConformance(normalConformance)) { + if (isDependentConformance(normalConformance) || + // Foreign types need to go through the accessor to unique the witness + // table. + normalConformance->isSynthesizedNonUnique()) { info = new AccessorConformanceInfo(conformance); Conformances.insert({conformance, info}); } else { @@ -2172,8 +2222,21 @@ void IRGenModule::emitSILWitnessTable(SILWitnessTable *wt) { // Trigger the lazy emission of the foreign type metadata. CanType conformingType = conf->getType()->getCanonicalType(); - if (requiresForeignTypeMetadata(conformingType)) - (void)getAddrOfForeignTypeMetadataCandidate(conformingType); + if (requiresForeignTypeMetadata(conformingType)) { + // Make sure we emit the metadata access function. + (void)getTypeMetadataAccessFunction(*this, conformingType, + ForDefinition); + + // Make sure we emit the nominal type descriptor. + auto *nominal = conformingType->getAnyNominal(); + auto entity = LinkEntity::forNominalTypeDescriptor(nominal); + if (auto entry = GlobalVars[entity]) { + if (!cast(entry)->isDeclaration()) + return; + } + + emitLazyTypeContextDescriptor(*this, nominal, RequireMetadata); + } } /// True if a function's signature in LLVM carries polymorphic parameters. @@ -2791,34 +2854,8 @@ llvm::Value *irgen::emitWitnessTableRef(IRGenFunction &IGF, return wtable; } -/// Emit the witness table references required for the given type -/// substitution. -void irgen::emitWitnessTableRefs(IRGenFunction &IGF, - const Substitution &sub, - llvm::Value **metadataCache, - SmallVectorImpl &out) { - auto conformances = sub.getConformances(); - - // We don't need to do anything if we have no protocols to conform to. - if (conformances.empty()) return; - - // Look at the replacement type. - CanType replType = sub.getReplacement()->getCanonicalType(); - - for (auto &conformance : conformances) { - auto *proto = conformance.getRequirement(); - if (!Lowering::TypeConverter::protocolRequiresWitnessTable(proto)) - continue; - - auto wtable = emitWitnessTableRef(IGF, replType, metadataCache, - conformance); - - out.push_back(wtable); - } -} - static CanType getSubstSelfType(CanSILFunctionType origFnType, - const SubstitutionMap &subs) { + SubstitutionMap subs) { // Grab the apparent 'self' type. If there isn't a 'self' type, // we're not going to try to access this anyway. assert(!origFnType->getParameters().empty()); @@ -2859,11 +2896,11 @@ namespace { CanSILFunctionType polyFn) : PolymorphicConvention(IGF.IGM, polyFn), IGF(IGF) {} - void emit(const SubstitutionMap &subs, + void emit(SubstitutionMap subs, WitnessMetadata *witnessMetadata, Explosion &out); private: - void emitEarlySources(const SubstitutionMap &subs, Explosion &out) { + void emitEarlySources(SubstitutionMap subs, Explosion &out) { for (auto &source : getSources()) { switch (source.getKind()) { // Already accounted for in the parameters. @@ -2892,13 +2929,13 @@ namespace { /// Pass all the arguments necessary for the given function. void irgen::emitPolymorphicArguments(IRGenFunction &IGF, CanSILFunctionType origFnType, - const SubstitutionMap &subs, + SubstitutionMap subs, WitnessMetadata *witnessMetadata, Explosion &out) { EmitPolymorphicArguments(IGF, origFnType).emit(subs, witnessMetadata, out); } -void EmitPolymorphicArguments::emit(const SubstitutionMap &subs, +void EmitPolymorphicArguments::emit(SubstitutionMap subs, WitnessMetadata *witnessMetadata, Explosion &out) { // Add all the early sources. @@ -2943,7 +2980,7 @@ void EmitPolymorphicArguments::emit(const SubstitutionMap &subs, NecessaryBindings NecessaryBindings::forFunctionInvocations(IRGenModule &IGM, CanSILFunctionType origType, - const SubstitutionMap &subs) { + SubstitutionMap subs) { NecessaryBindings bindings; // Bail out early if we don't have polymorphic parameters. @@ -3029,7 +3066,7 @@ GenericTypeRequirements::GenericTypeRequirements(IRGenModule &IGM, void GenericTypeRequirements::enumerateFulfillments(IRGenModule &IGM, - const SubstitutionMap &subs, + SubstitutionMap subs, FulfillmentCallback callback) { if (empty()) return; @@ -3047,7 +3084,7 @@ GenericTypeRequirements::enumerateFulfillments(IRGenModule &IGM, } void GenericTypeRequirements::emitInitOfBuffer(IRGenFunction &IGF, - const SubstitutionMap &subs, + SubstitutionMap subs, Address buffer) { if (Requirements.empty()) return; @@ -3091,7 +3128,7 @@ irgen::emitGenericRequirementFromSubstitutions(IRGenFunction &IGF, CanGenericSignature generics, ModuleDecl &module, GenericRequirement requirement, - const SubstitutionMap &subs) { + SubstitutionMap subs) { CanType depTy = requirement.TypeParameter; CanType argType = depTy.subst(subs)->getCanonicalType(); @@ -3295,7 +3332,8 @@ irgen::emitAssociatedTypeMetadataRef(IRGenFunction &IGF, FunctionPointer witnessFnPtr(witness, sig); // Call the accessor. - assert((!IGF.IGM.DebugInfo || IGF.Builder.getCurrentDebugLocation()) && + assert((!IGF.IGM.DebugInfo || IGF.Builder.getCurrentDebugLocation() || + !IGF.CurFn->getSubprogram()) && "creating a function call without a debug location"); auto call = IGF.Builder.CreateCall(witnessFnPtr, { request.get(IGF), diff --git a/lib/IRGen/GenProto.h b/lib/IRGen/GenProto.h index 94331bdc4f4ee..69dfee86c8acd 100644 --- a/lib/IRGen/GenProto.h +++ b/lib/IRGen/GenProto.h @@ -139,7 +139,7 @@ namespace irgen { /// generics clause. void emitPolymorphicArguments(IRGenFunction &IGF, CanSILFunctionType origType, - const SubstitutionMap &subs, + SubstitutionMap subs, WitnessMetadata *witnessMetadata, Explosion &args); @@ -149,12 +149,6 @@ namespace irgen { CanSILFunctionType &SubstFnType, Explosion &nativeParam, unsigned paramIndex); - /// Emit references to the witness tables for the substituted type - /// in the given substitution. - void emitWitnessTableRefs(IRGenFunction &IGF, const Substitution &sub, - llvm::Value **metadataCache, - SmallVectorImpl &out); - /// \brief Load a reference to the protocol descriptor for the given protocol. /// /// For Swift protocols, this is a constant reference to the protocol diff --git a/lib/IRGen/GenRecord.h b/lib/IRGen/GenRecord.h index b18012a237ec0..66b0e1472f5e2 100644 --- a/lib/IRGen/GenRecord.h +++ b/lib/IRGen/GenRecord.h @@ -22,6 +22,7 @@ #include "IRGenModule.h" #include "Explosion.h" #include "GenEnum.h" +#include "GenOpaque.h" #include "LoadableTypeInfo.h" #include "Outlining.h" #include "TypeInfo.h" @@ -45,7 +46,7 @@ template class RecordField { protected: explicit RecordField(const TypeInfo &elementTI) - : Layout(ElementLayout::getIncomplete(elementTI)) {} + : Layout(ElementLayout::getIncomplete(elementTI, elementTI)) {} explicit RecordField(const ElementLayout &layout, unsigned begin, unsigned end) @@ -55,7 +56,7 @@ template class RecordField { return static_cast(this); } public: - const TypeInfo &getTypeInfo() const { return Layout.getType(); } + const TypeInfo &getTypeInfo() const { return Layout.getTypeForLayout(); } void completeFrom(const ElementLayout &layout) { Layout.completeFrom(layout); @@ -69,6 +70,10 @@ template class RecordField { return Layout.isPOD(); } + IsABIAccessible_t isABIAccessible() const { + return Layout.getTypeForLayout().isABIAccessible(); + } + Address projectAddress(IRGenFunction &IGF, Address seq, NonFixedOffsets offsets) const { return Layout.project(IGF, seq, offsets, "." + asImpl()->getFieldName()); @@ -93,6 +98,11 @@ template class RecordField { } }; +enum FieldsAreABIAccessible_t : bool { + FieldsAreNotABIAccessible = false, + FieldsAreABIAccessible = true, +}; + /// A metaprogrammed TypeInfo implementation for record types. template ::value> @@ -105,13 +115,18 @@ class RecordTypeInfoImpl : public Base, private: const unsigned NumFields; + const unsigned AreFieldsABIAccessible : 1; protected: const Impl &asImpl() const { return *static_cast(this); } template - RecordTypeInfoImpl(ArrayRef fields, As&&...args) - : Base(std::forward(args)...), NumFields(fields.size()) { + RecordTypeInfoImpl(ArrayRef fields, + FieldsAreABIAccessible_t fieldsABIAccessible, + As&&...args) + : Base(std::forward(args)...), + NumFields(fields.size()), + AreFieldsABIAccessible(fieldsABIAccessible) { std::uninitialized_copy(fields.begin(), fields.end(), this->template getTrailingObjects()); } @@ -138,6 +153,11 @@ class RecordTypeInfoImpl : public Base, void assignWithCopy(IRGenFunction &IGF, Address dest, Address src, SILType T, bool isOutlined) const override { + // If the fields are not ABI-accessible, use the value witness table. + if (!AreFieldsABIAccessible) { + return emitAssignWithCopyCall(IGF, T, dest, src); + } + if (isOutlined || T.hasOpenedExistential()) { auto offsets = asImpl().getNonFixedOffsets(IGF, T); for (auto &field : getFields()) { @@ -156,6 +176,11 @@ class RecordTypeInfoImpl : public Base, void assignWithTake(IRGenFunction &IGF, Address dest, Address src, SILType T, bool isOutlined) const override { + // If the fields are not ABI-accessible, use the value witness table. + if (!AreFieldsABIAccessible) { + return emitAssignWithTakeCall(IGF, T, dest, src); + } + if (isOutlined || T.hasOpenedExistential()) { auto offsets = asImpl().getNonFixedOffsets(IGF, T); for (auto &field : getFields()) { @@ -181,6 +206,11 @@ class RecordTypeInfoImpl : public Base, IGF, dest, src, T, isOutlined); } + // If the fields are not ABI-accessible, use the value witness table. + if (!AreFieldsABIAccessible) { + return emitInitializeWithCopyCall(IGF, T, dest, src); + } + if (isOutlined || T.hasOpenedExistential()) { auto offsets = asImpl().getNonFixedOffsets(IGF, T); for (auto &field : getFields()) { @@ -207,6 +237,11 @@ class RecordTypeInfoImpl : public Base, return; } + // If the fields are not ABI-accessible, use the value witness table. + if (!AreFieldsABIAccessible) { + return emitInitializeWithTakeCall(IGF, T, dest, src); + } + if (isOutlined || T.hasOpenedExistential()) { auto offsets = asImpl().getNonFixedOffsets(IGF, T); for (auto &field : getFields()) { @@ -225,6 +260,11 @@ class RecordTypeInfoImpl : public Base, void destroy(IRGenFunction &IGF, Address addr, SILType T, bool isOutlined) const override { + // If the fields are not ABI-accessible, use the value witness table. + if (!AreFieldsABIAccessible) { + return emitDestroyCall(IGF, T, addr); + } + if (isOutlined || T.hasOpenedExistential()) { auto offsets = asImpl().getNonFixedOffsets(IGF, T); for (auto &field : getFields()) { @@ -283,22 +323,6 @@ class RecordTypeInfogetTypeInfo(); - Address fieldResult = - fieldTI.initializeBufferWithTakeOfBuffer(IGF, destBuffer, srcBuffer, - field->getType(IGF.IGM, type)); - return IGF.Builder.CreateElementBitCast(fieldResult, getStorageType()); - } else { - return super::initializeBufferWithTakeOfBuffer(IGF, destBuffer, - srcBuffer, type); - } - } - Address initializeBufferWithCopyOfBuffer(IRGenFunction &IGF, Address destBuffer, Address srcBuffer, @@ -323,6 +347,9 @@ class RecordTypeInfo - RecordTypeInfo(As&&...args) : super(std::forward(args)...) {} + RecordTypeInfo(ArrayRef fields, As &&...args) + : super(fields, FieldsAreABIAccessible, std::forward(args)...) {} }; /// An implementation of RecordTypeInfo for loadable types. @@ -367,9 +395,10 @@ class RecordTypeInfo - RecordTypeInfo(ArrayRef fields, unsigned explosionSize, + RecordTypeInfo(ArrayRef fields, + unsigned explosionSize, As &&...args) - : super(fields, std::forward(args)...), + : super(fields, FieldsAreABIAccessible, std::forward(args)...), ExplosionSize(explosionSize) {} private: @@ -518,15 +547,18 @@ class RecordTypeBuilder { fieldTypesForLayout.reserve(astFields.size()); bool loadable = true; + auto fieldsABIAccessible = FieldsAreABIAccessible; unsigned explosionSize = 0; for (unsigned i : indices(astFields)) { auto &astField = astFields[i]; // Compute the field's type info. auto &fieldTI = IGM.getTypeInfo(asImpl()->getType(astField)); - assert(fieldTI.isComplete()); fieldTypesForLayout.push_back(&fieldTI); + if (!fieldTI.isABIAccessible()) + fieldsABIAccessible = FieldsAreNotABIAccessible; + fields.push_back(FieldImpl(asImpl()->getFieldInfo(i, astField, fieldTI))); auto loadableFieldTI = dyn_cast(&fieldTI); @@ -550,11 +582,14 @@ class RecordTypeBuilder { // Create the type info. if (loadable) { assert(layout.isFixedLayout()); + assert(fieldsABIAccessible); return asImpl()->createLoadable(fields, std::move(layout), explosionSize); } else if (layout.isFixedLayout()) { + assert(fieldsABIAccessible); return asImpl()->createFixed(fields, std::move(layout)); } else { - return asImpl()->createNonFixed(fields, std::move(layout)); + return asImpl()->createNonFixed(fields, fieldsABIAccessible, + std::move(layout)); } } }; diff --git a/lib/IRGen/GenReflection.cpp b/lib/IRGen/GenReflection.cpp index 57d9cb7a8d6de..876ddb03a091b 100644 --- a/lib/IRGen/GenReflection.cpp +++ b/lib/IRGen/GenReflection.cpp @@ -164,6 +164,12 @@ class PrintMetadataSource } }; +llvm::Constant *IRGenModule::getTypeRef(CanType type) { + IRGenMangler Mangler; + auto SymbolicName = Mangler.mangleTypeForReflection(*this, type); + return getAddrOfStringForTypeRef(SymbolicName); +} + class ReflectionMetadataBuilder { protected: IRGenModule &IGM; @@ -203,46 +209,23 @@ class ReflectionMetadataBuilder { /// Add a 32-bit relative offset to a mangled typeref string /// in the typeref reflection section. - void addTypeRef(ModuleDecl *ModuleContext, CanType type, - CanGenericSignature Context = {}) { - assert(type); - - // Generic parameters should be written in terms of interface types - // for the purposes of reflection metadata - assert(!type->hasArchetype() && "Forgot to map typeref out of context"); - - // TODO: As a compatibility hack, mangle single-field boxes with the legacy - // mangling in reflection metadata. - bool isSingleFieldOfBox = false; - auto boxTy = dyn_cast(type); - if (boxTy && boxTy->getLayout()->getFields().size() == 1) { - GenericContextScope scope(IGM, Context); - type = boxTy->getFieldLoweredType(IGM.getSILModule(), 0); - isSingleFieldOfBox = true; - } - IRGenMangler mangler; - auto MangledStr = mangler.mangleTypeForReflection(IGM, type, - ModuleContext, - isSingleFieldOfBox); - auto mangledName = IGM.getAddrOfStringForTypeRef(MangledStr); - B.addRelativeAddress(mangledName); + void addTypeRef(CanType type) { + B.addRelativeAddress(IGM.getTypeRef(type)); } /// Add a 32-bit relative offset to a mangled nominal type string /// in the typeref reflection section. void addNominalRef(const NominalTypeDecl *nominal) { - IRGenMangler mangler; - SymbolicMangling mangledStr; if (auto proto = dyn_cast(nominal)) { + IRGenMangler mangler; + SymbolicMangling mangledStr; mangledStr.String = mangler.mangleBareProtocol(proto); + auto mangledName = IGM.getAddrOfStringForTypeRef(mangledStr); + B.addRelativeAddress(mangledName); } else { CanType type = nominal->getDeclaredType()->getCanonicalType(); - mangledStr = - mangler.mangleTypeForReflection(IGM, type, nominal->getModuleContext(), - /*isSingleFieldOfBox=*/false); + B.addRelativeAddress(IGM.getTypeRef(type)); } - auto mangledName = IGM.getAddrOfStringForTypeRef(mangledStr); - B.addRelativeAddress(mangledName); } llvm::GlobalVariable *emit(Optional entity, @@ -264,7 +247,7 @@ class ReflectionMetadataBuilder { // Others, such as capture descriptors, do not have a name. } else { - var = B.finishAndCreateGlobal("\x01l__swift4_reflection_descriptor", + var = B.finishAndCreateGlobal("\x01l__swift5_reflection_descriptor", Alignment(4), /*isConstant*/ true, llvm::GlobalValue::PrivateLinkage); } @@ -273,6 +256,8 @@ class ReflectionMetadataBuilder { IGM.addUsedGlobal(var); + disableAddressSanitizer(IGM, var); + return var; } @@ -294,9 +279,7 @@ class AssociatedTypeMetadataBuilder : public ReflectionMetadataBuilder { PrettyStackTraceDecl DebugStack("emitting associated type metadata", Nominal); - auto *M = IGM.getSILModule().getSwiftModule(); - - addTypeRef(M, Nominal->getDeclaredType()->getCanonicalType()); + addTypeRef(Nominal->getDeclaredType()->getCanonicalType()); addNominalRef(Conformance->getProtocol()); B.addInt32(AssociatedTypes.size()); @@ -306,7 +289,7 @@ class AssociatedTypeMetadataBuilder : public ReflectionMetadataBuilder { auto NameGlobal = IGM.getAddrOfFieldName(AssocTy.first); B.addRelativeAddress(NameGlobal); addBuiltinTypeRefs(AssocTy.second); - addTypeRef(M, AssocTy.second); + addTypeRef(AssocTy.second); } } @@ -340,7 +323,7 @@ class FieldTypeMetadataBuilder : public ReflectionMetadataBuilder { if (!type) { B.addInt32(0); } else { - addTypeRef(value->getModuleContext(), type); + addTypeRef(type); addBuiltinTypeRefs(type); } @@ -446,8 +429,7 @@ class FieldTypeMetadataBuilder : public ReflectionMetadataBuilder { auto *CD = dyn_cast(NTD); if (CD && CD->getSuperclass()) { - addTypeRef(NTD->getModuleContext(), - CD->getSuperclass()->getCanonicalType()); + addTypeRef(CD->getSuperclass()->getCanonicalType()); } else { B.addInt32(0); } @@ -509,7 +491,7 @@ class FixedTypeMetadataBuilder : public ReflectionMetadataBuilder { } void layout() override { - addTypeRef(module, type); + addTypeRef(type); B.addInt32(ti->getFixedSize().getValue()); B.addInt32(ti->getFixedAlignment().getValue()); @@ -559,7 +541,7 @@ class BoxDescriptorBuilder : public ReflectionMetadataBuilder { B.addInt32(0); // Number of sources B.addInt32(0); // Number of generic bindings - addTypeRef(IGM.getSILModule().getSwiftModule(), BoxedType); + addTypeRef(BoxedType); addBuiltinTypeRefs(BoxedType); } @@ -576,14 +558,14 @@ class CaptureDescriptorBuilder : public ReflectionMetadataBuilder { swift::reflection::MetadataSourceBuilder SourceBuilder; CanSILFunctionType OrigCalleeType; CanSILFunctionType SubstCalleeType; - SubstitutionList Subs; + SubstitutionMap Subs; const HeapLayout &Layout; public: CaptureDescriptorBuilder(IRGenModule &IGM, CanSILFunctionType OrigCalleeType, CanSILFunctionType SubstCalleeType, - SubstitutionList Subs, + SubstitutionMap Subs, const HeapLayout &Layout) : ReflectionMetadataBuilder(IGM), OrigCalleeType(OrigCalleeType), @@ -628,7 +610,7 @@ class CaptureDescriptorBuilder : public ReflectionMetadataBuilder { auto ElementTypes = Layout.getElementTypes().slice( Layout.hasBindings() ? 1 : 0); for (auto ElementType : ElementTypes) { - auto SwiftType = ElementType.getSwiftRValueType(); + auto SwiftType = ElementType.getASTType(); if (SwiftType->hasOpenedExistential()) return true; } @@ -674,9 +656,6 @@ class CaptureDescriptorBuilder : public ReflectionMetadataBuilder { // Check if any requirements were fulfilled by metadata stored inside a // captured value. - auto SubstMap = - OrigCalleeType->getGenericSignature()->getSubstitutionMap(Subs); - enumerateGenericParamFulfillments(IGM, OrigCalleeType, [&](CanType GenericParam, const irgen::MetadataSource &Source, @@ -708,7 +687,7 @@ class CaptureDescriptorBuilder : public ReflectionMetadataBuilder { // parameters. auto Src = Path.getMetadataSource(SourceBuilder, Root); - auto SubstType = GenericParam.subst(SubstMap); + auto SubstType = GenericParam.subst(Subs); auto InterfaceType = SubstType->mapTypeOutOfContext(); SourceMap.push_back({InterfaceType->getCanonicalType(), Src}); }); @@ -722,7 +701,7 @@ class CaptureDescriptorBuilder : public ReflectionMetadataBuilder { std::vector CaptureTypes; for (auto ElementType : getElementTypes()) { - auto SwiftType = ElementType.getSwiftRValueType(); + auto SwiftType = ElementType.getASTType(); // Erase pseudogeneric captures down to AnyObject. if (OrigCalleeType->isPseudogeneric()) { @@ -752,8 +731,7 @@ class CaptureDescriptorBuilder : public ReflectionMetadataBuilder { // Now add typerefs of all of the captures. for (auto CaptureType : CaptureTypes) { - addTypeRef(IGM.getSILModule().getSwiftModule(), CaptureType, - OrigCalleeType->getGenericSignature()); + addTypeRef(CaptureType); addBuiltinTypeRefs(CaptureType); } @@ -763,7 +741,7 @@ class CaptureDescriptorBuilder : public ReflectionMetadataBuilder { auto GenericParam = GenericAndSource.first->getCanonicalType(); auto Source = GenericAndSource.second; - addTypeRef(nullptr, GenericParam); + addTypeRef(GenericParam); addMetadataSource(Source); } } @@ -788,12 +766,12 @@ static std::string getReflectionSectionName(IRGenModule &IGM, OS << ".sw5" << FourCC << "$B"; break; case llvm::Triple::ELF: - OS << "swift4_" << LongName; + OS << "swift5_" << LongName; break; case llvm::Triple::MachO: assert(LongName.size() <= 7 && "Mach-O section name length must be <= 16 characters"); - OS << "__TEXT,__swift4_" << LongName << ", regular, no_dead_strip"; + OS << "__TEXT,__swift5_" << LongName << ", regular, no_dead_strip"; break; case llvm::Triple::Wasm: llvm_unreachable("web assembly object format is not supported."); @@ -845,6 +823,7 @@ llvm::Constant *IRGenModule::getAddrOfFieldName(StringRef Name) { entry = createStringConstant(Name, /*willBeRelativelyAddressed*/ true, getReflectionStringsSectionName()); + disableAddressSanitizer(*this, entry.first); return entry.second; } @@ -863,7 +842,7 @@ llvm::Constant * IRGenModule::getAddrOfCaptureDescriptor(SILFunction &Caller, CanSILFunctionType OrigCalleeType, CanSILFunctionType SubstCalleeType, - SubstitutionList Subs, + SubstitutionMap Subs, const HeapLayout &Layout) { if (!IRGen.Opts.EnableReflectionMetadata) return llvm::Constant::getNullValue(CaptureDescriptorPtrTy); diff --git a/lib/IRGen/GenStruct.cpp b/lib/IRGen/GenStruct.cpp index 48b36cf2628ef..2174148d8f6c9 100644 --- a/lib/IRGen/GenStruct.cpp +++ b/lib/IRGen/GenStruct.cpp @@ -158,16 +158,16 @@ namespace { auto offsets = asImpl().getNonFixedOffsets(IGF, T); return fieldInfo.projectAddress(IGF, addr, offsets); } - - /// Return the constant offset of a field as a SizeTy, or nullptr if the + + /// Return the constant offset of a field as a Int32Ty, or nullptr if the /// field is not at a fixed offset. llvm::Constant *getConstantFieldOffset(IRGenModule &IGM, VarDecl *field) const { auto &fieldInfo = getFieldInfo(field); if (fieldInfo.getKind() == ElementLayout::Kind::Fixed || fieldInfo.getKind() == ElementLayout::Kind::Empty) { - return llvm::ConstantInt::get(IGM.SizeTy, - fieldInfo.getFixedByteOffset().getValue()); + return llvm::ConstantInt::get( + IGM.Int32Ty, fieldInfo.getFixedByteOffset().getValue()); } return nullptr; } @@ -494,11 +494,15 @@ namespace { WitnessSizedTypeInfo> { public: - NonFixedStructTypeInfo(ArrayRef fields, llvm::Type *T, + NonFixedStructTypeInfo(ArrayRef fields, + FieldsAreABIAccessible_t fieldsAccessible, + llvm::Type *T, Alignment align, - IsPOD_t isPOD, IsBitwiseTakable_t isBT) + IsPOD_t isPOD, IsBitwiseTakable_t isBT, + IsABIAccessible_t structAccessible) : StructTypeInfoBase(StructTypeInfoKind::NonFixedStructTypeInfo, - fields, T, align, isPOD, isBT) { + fields, fieldsAccessible, + T, align, isPOD, isBT, structAccessible) { } // We have an indirect schema. @@ -556,11 +560,16 @@ namespace { } NonFixedStructTypeInfo *createNonFixed(ArrayRef fields, + FieldsAreABIAccessible_t fieldsAccessible, StructLayout &&layout) { - return NonFixedStructTypeInfo::create(fields, layout.getType(), + auto structAccessible = IsABIAccessible_t( + IGM.getSILModule().isTypeMetadataAccessible(TheStruct)); + return NonFixedStructTypeInfo::create(fields, fieldsAccessible, + layout.getType(), layout.getAlignment(), layout.isPOD(), - layout.isBitwiseTakable()); + layout.isBitwiseTakable(), + structAccessible); } StructFieldInfo getFieldInfo(unsigned index, @@ -766,7 +775,7 @@ class ClangRecordLowering { NextExplosionIndex += explosionSize; unsigned explosionEnd = NextExplosionIndex; - ElementLayout layout = ElementLayout::getIncomplete(fieldType); + ElementLayout layout = ElementLayout::getIncomplete(fieldType, fieldType); auto isEmpty = fieldType.isKnownEmpty(ResilienceExpansion::Maximal); if (isEmpty) layout.completeEmpty(fieldType.isPOD(ResilienceExpansion::Maximal), @@ -871,24 +880,29 @@ namespace { : public ResilientTypeInfo { public: - ResilientStructTypeInfo(llvm::Type *T) - : ResilientTypeInfo(T) { + ResilientStructTypeInfo(llvm::Type *T, IsABIAccessible_t abiAccessible) + : ResilientTypeInfo(T, abiAccessible) { setSubclassKind((unsigned) StructTypeInfoKind::ResilientStructTypeInfo); } }; } // end anonymous namespace -const TypeInfo *TypeConverter::convertResilientStruct() { +const TypeInfo * +TypeConverter::convertResilientStruct(IsABIAccessible_t abiAccessible) { llvm::Type *storageType = IGM.OpaquePtrTy->getElementType(); - return new ResilientStructTypeInfo(storageType); + return new ResilientStructTypeInfo(storageType, abiAccessible); } const TypeInfo *TypeConverter::convertStructType(TypeBase *key, CanType type, StructDecl *D) { // All resilient structs have the same opaque lowering, since they are - // indistinguishable as values. - if (IGM.isResilient(D, ResilienceExpansion::Maximal)) - return &getResilientStructTypeInfo(); + // indistinguishable as values --- except that we have to track + // ABI-accessibility. + if (IGM.isResilient(D, ResilienceExpansion::Maximal)) { + auto structAccessible = + IsABIAccessible_t(IGM.getSILModule().isTypeMetadataAccessible(type)); + return &getResilientStructTypeInfo(structAccessible); + } // Create the struct type. auto ty = IGM.createNominalType(type); diff --git a/lib/IRGen/GenTuple.cpp b/lib/IRGen/GenTuple.cpp index 1193fab0bd190..c4242391b7117 100644 --- a/lib/IRGen/GenTuple.cpp +++ b/lib/IRGen/GenTuple.cpp @@ -23,6 +23,7 @@ #include "swift/AST/Types.h" #include "swift/AST/Decl.h" #include "swift/AST/Pattern.h" +#include "swift/SIL/SILModule.h" #include "swift/SIL/SILType.h" #include "llvm/IR/DerivedTypes.h" @@ -331,10 +332,14 @@ namespace { WitnessSizedTypeInfo> { public: - NonFixedTupleTypeInfo(ArrayRef fields, llvm::Type *T, + NonFixedTupleTypeInfo(ArrayRef fields, + FieldsAreABIAccessible_t fieldsABIAccessible, + llvm::Type *T, Alignment minAlign, IsPOD_t isPOD, - IsBitwiseTakable_t isBT) - : TupleTypeInfoBase(fields, T, minAlign, isPOD, isBT) {} + IsBitwiseTakable_t isBT, + IsABIAccessible_t tupleAccessible) + : TupleTypeInfoBase(fields, fieldsABIAccessible, + T, minAlign, isPOD, isBT, tupleAccessible) {} TupleNonFixedOffsets getNonFixedOffsets(IRGenFunction &IGF, SILType T) const { @@ -374,11 +379,16 @@ namespace { } NonFixedTupleTypeInfo *createNonFixed(ArrayRef fields, + FieldsAreABIAccessible_t fieldsAccessible, StructLayout &&layout) { - return NonFixedTupleTypeInfo::create(fields, layout.getType(), + auto tupleAccessible = IsABIAccessible_t( + IGM.getSILModule().isTypeABIAccessible(TheTuple)); + return NonFixedTupleTypeInfo::create(fields, fieldsAccessible, + layout.getType(), layout.getAlignment(), layout.isPOD(), - layout.isBitwiseTakable()); + layout.isBitwiseTakable(), + tupleAccessible); } TupleFieldInfo getFieldInfo(unsigned index, diff --git a/lib/IRGen/GenType.cpp b/lib/IRGen/GenType.cpp index 3832b06a93630..ffdadb8fca3f0 100644 --- a/lib/IRGen/GenType.cpp +++ b/lib/IRGen/GenType.cpp @@ -52,8 +52,16 @@ using namespace swift; using namespace irgen; llvm::DenseMap & -TypeConverter::Types_t::getCacheFor(TypeBase *t) { - return t->hasTypeParameter() ? DependentCache : IndependentCache; +TypeConverter::Types_t::getCacheFor(bool isDependent, bool completelyFragile) { + if (completelyFragile) { + return (isDependent + ? FragileDependentCache + : FragileIndependentCache); + } + + return (isDependent + ? DependentCache + : IndependentCache); } void TypeInfo::assign(IRGenFunction &IGF, Address dest, Address src, @@ -94,12 +102,12 @@ TypeInfo::~TypeInfo() { Address TypeInfo::getAddressForPointer(llvm::Value *ptr) const { assert(ptr->getType()->getPointerElementType() == StorageType); - return Address(ptr, StorageAlignment); + return Address(ptr, getBestKnownAlignment()); } Address TypeInfo::getUndefAddress() const { return Address(llvm::UndefValue::get(getStorageType()->getPointerTo(0)), - StorageAlignment); + getBestKnownAlignment()); } /// Whether this type is known to be empty. @@ -231,12 +239,12 @@ unsigned FixedTypeInfo::getSpareBitExtraInhabitantCount() const { return 0; // The runtime supports a max of 0x7FFFFFFF extra inhabitants, which ought // to be enough for anybody. - if (StorageSize.getValue() >= 4) + if (getFixedSize().getValue() >= 4) return 0x7FFFFFFF; unsigned spareBitCount = SpareBits.count(); - assert(spareBitCount <= StorageSize.getValueInBits() + assert(spareBitCount <= getFixedSize().getValueInBits() && "more spare bits than storage bits?!"); - unsigned inhabitedBitCount = StorageSize.getValueInBits() - spareBitCount; + unsigned inhabitedBitCount = getFixedSize().getValueInBits() - spareBitCount; return ((1U << spareBitCount) - 1U) << inhabitedBitCount; } @@ -296,7 +304,7 @@ FixedTypeInfo::getSpareBitExtraInhabitantIndex(IRGenFunction &IGF, auto &C = IGF.IGM.getLLVMContext(); // Load the value. - auto payloadTy = llvm::IntegerType::get(C, StorageSize.getValueInBits()); + auto payloadTy = llvm::IntegerType::get(C, getFixedSize().getValueInBits()); src = IGF.Builder.CreateBitCast(src, payloadTy->getPointerTo()); auto val = IGF.Builder.CreateLoad(src); @@ -323,7 +331,7 @@ FixedTypeInfo::getSpareBitExtraInhabitantIndex(IRGenFunction &IGF, // See if spare bits fit into the 31 bits of the index. unsigned numSpareBits = SpareBits.count(); - unsigned numOccupiedBits = StorageSize.getValueInBits() - numSpareBits; + unsigned numOccupiedBits = getFixedSize().getValueInBits() - numSpareBits; if (numOccupiedBits < 31) { // Gather the spare bits. llvm::Value *spareIdx @@ -748,7 +756,7 @@ FixedTypeInfo::storeSpareBitExtraInhabitant(IRGenFunction &IGF, auto &C = IGF.IGM.getLLVMContext(); - auto payloadTy = llvm::IntegerType::get(C, StorageSize.getValueInBits()); + auto payloadTy = llvm::IntegerType::get(C, getFixedSize().getValueInBits()); unsigned spareBitCount = SpareBits.count(); unsigned occupiedBitCount = SpareBits.size() - spareBitCount; @@ -1056,7 +1064,15 @@ static ProtocolInfo *invalidProtocolInfo() { return (ProtocolInfo*) 1; } TypeConverter::TypeConverter(IRGenModule &IGM) : IGM(IGM), FirstType(invalidTypeInfo()), - FirstProtocol(invalidProtocolInfo()) {} + FirstProtocol(invalidProtocolInfo()) { + // FIXME: In LLDB, everything is completely fragile, so that IRGen can query + // the size of resilient types. Of course this is not the right long term + // solution, because it won't work once the swiftmodule file is not in + // sync with the binary module. Once LLDB can calculate type layouts at + // runtime (using remote mirrors or some other mechanism), we can remove this. + if (IGM.IRGen.Opts.EnableResilienceBypass) + CompletelyFragile = true; +} TypeConverter::~TypeConverter() { // Delete all the converted type infos. @@ -1083,7 +1099,8 @@ void TypeConverter::pushGenericContext(CanGenericSignature signature) { // Clear the dependent type info cache since we have a new active signature // now. - Types.DependentCache.clear(); + Types.getCacheFor(/*isDependent*/ true, /*isFragile*/ false).clear(); + Types.getCacheFor(/*isDependent*/ true, /*isFragile*/ true).clear(); } void TypeConverter::popGenericContext(CanGenericSignature signature) { @@ -1093,7 +1110,8 @@ void TypeConverter::popGenericContext(CanGenericSignature signature) { // Pop the SIL TypeConverter's generic context too. IGM.getSILTypes().popGenericContext(signature); - Types.DependentCache.clear(); + Types.getCacheFor(/*isDependent*/ true, /*isFragile*/ false).clear(); + Types.getCacheFor(/*isDependent*/ true, /*isFragile*/ true).clear(); } GenericEnvironment *TypeConverter::getGenericEnvironment() { @@ -1110,8 +1128,9 @@ GenericEnvironment *IRGenModule::getGenericEnvironment() { void TypeConverter::addForwardDecl(TypeBase *key, llvm::Type *type) { assert(key->isCanonical()); assert(!key->hasTypeParameter()); - assert(!Types.IndependentCache.count(key) && "entry already exists for type!"); - Types.IndependentCache.insert(std::make_pair(key, type)); + auto &Cache = Types.getCacheFor(/*isDependent*/ false, CompletelyFragile); + assert(!Cache.count(key) && "entry already exists for type!"); + Cache.insert(std::make_pair(key, type)); } const TypeInfo &IRGenModule::getWitnessTablePtrTypeInfo() { @@ -1224,12 +1243,15 @@ const LoadableTypeInfo &TypeConverter::getEmptyTypeInfo() { return *EmptyTI; } -const TypeInfo &TypeConverter::getResilientStructTypeInfo() { - if (ResilientStructTI) return *ResilientStructTI; - ResilientStructTI = convertResilientStruct(); - ResilientStructTI->NextConverted = FirstType; - FirstType = ResilientStructTI; - return *ResilientStructTI; +const TypeInfo & +TypeConverter::getResilientStructTypeInfo(IsABIAccessible_t isAccessible) { + auto &cache = isAccessible ? AccessibleResilientStructTI + : InaccessibleResilientStructTI; + if (cache) return *cache; + cache = convertResilientStruct(isAccessible); + cache->NextConverted = FirstType; + FirstType = cache; + return *cache; } /// Get the fragile type information for the given type, which may not @@ -1279,7 +1301,7 @@ SILType IRGenModule::getLoweredType(Type subst) { /// unlike fetching the type info and asking it for the storage type, /// this operation will succeed for forward-declarations. llvm::PointerType *IRGenModule::getStoragePointerType(SILType T) { - return getStoragePointerTypeForLowered(T.getSwiftRValueType()); + return getStoragePointerTypeForLowered(T.getASTType()); } llvm::PointerType *IRGenModule::getStoragePointerTypeForUnlowered(Type T) { return getStorageTypeForUnlowered(T)->getPointerTo(); @@ -1293,7 +1315,7 @@ llvm::Type *IRGenModule::getStorageTypeForUnlowered(Type subst) { } llvm::Type *IRGenModule::getStorageType(SILType T) { - return getStorageTypeForLowered(T.getSwiftRValueType()); + return getStorageTypeForLowered(T.getASTType()); } /// Get the storage type for the given type. Note that, unlike @@ -1334,7 +1356,7 @@ IRGenModule::getTypeInfoForUnlowered(AbstractionPattern orig, CanType subst) { /// to have undergone SIL type lowering (or be one of the types for /// which that lowering is the identity function). const TypeInfo &IRGenModule::getTypeInfo(SILType T) { - return getTypeInfoForLowered(T.getSwiftRValueType()); + return getTypeInfoForLowered(T.getASTType()); } /// Get the fragile type information for the given type. @@ -1346,17 +1368,7 @@ const TypeInfo &IRGenModule::getTypeInfoForLowered(CanType T) { const TypeInfo &TypeConverter::getCompleteTypeInfo(CanType T) { auto entry = getTypeEntry(T); assert(entry.is() && "getting TypeInfo recursively!"); - auto &ti = *entry.get(); - assert(ti.isComplete()); - return ti; -} - -const TypeInfo *TypeConverter::tryGetCompleteTypeInfo(CanType T) { - auto entry = getTypeEntry(T); - if (!entry.is()) return nullptr; - auto &ti = *entry.get(); - if (!ti.isComplete()) return nullptr; - return &ti; + return *entry.get(); } ArchetypeType *TypeConverter::getExemplarArchetype(ArchetypeType *t) { @@ -1402,9 +1414,20 @@ CanType TypeConverter::getExemplarType(CanType contextTy) { } } +void TypeConverter::pushCompletelyFragile() { + assert(!CompletelyFragile); + CompletelyFragile = true; +} + +void TypeConverter::popCompletelyFragile() { + assert(CompletelyFragile); + CompletelyFragile = false; +} + TypeCacheEntry TypeConverter::getTypeEntry(CanType canonicalTy) { // Cache this entry in the dependent or independent cache appropriate to it. - auto &Cache = Types.getCacheFor(canonicalTy.getPointer()); + auto &Cache = Types.getCacheFor(canonicalTy->hasTypeParameter(), + CompletelyFragile); { auto it = Cache.find(canonicalTy.getPointer()); @@ -1419,8 +1442,7 @@ TypeCacheEntry TypeConverter::getTypeEntry(CanType canonicalTy) { // The type we got should be lowered, so lower it like a SILType. contextTy = getGenericEnvironment()->mapTypeIntoContext( IGM.getSILModule(), - SILType::getPrimitiveAddressType(contextTy)) - .getSwiftRValueType(); + SILType::getPrimitiveAddressType(contextTy)).getASTType(); } // Fold archetypes to unique exemplars. Any archetype with the same @@ -1430,8 +1452,9 @@ TypeCacheEntry TypeConverter::getTypeEntry(CanType canonicalTy) { // See whether we lowered a type equivalent to this one. if (exemplarTy != canonicalTy) { - auto it = Types.IndependentCache.find(exemplarTy.getPointer()); - if (it != Types.IndependentCache.end()) { + auto &Cache = Types.getCacheFor(/*isDependent*/ false, CompletelyFragile); + auto it = Cache.find(exemplarTy.getPointer()); + if (it != Cache.end()) { // Record the object under the original type. auto result = it->second; Cache[canonicalTy.getPointer()] = result; @@ -1459,8 +1482,11 @@ TypeCacheEntry TypeConverter::getTypeEntry(CanType canonicalTy) { entry = convertedTI; }; insertEntry(Cache[canonicalTy.getPointer()]); - if (canonicalTy != exemplarTy) - insertEntry(Types.IndependentCache[exemplarTy.getPointer()]); + if (canonicalTy != exemplarTy) { + auto &IndependentCache = Types.getCacheFor(/*isDependent*/ false, + CompletelyFragile); + insertEntry(IndependentCache[exemplarTy.getPointer()]); + } // If the type info hasn't been added to the list of types, do so. if (!convertedTI->NextConverted) { @@ -1855,7 +1881,7 @@ TypeCacheEntry TypeConverter::convertAnyNominalType(CanType type, assert(decl->getDeclaredType()->isCanonical()); assert(decl->getDeclaredType()->hasUnboundGenericType()); TypeBase *key = decl->getDeclaredType().getPointer(); - auto &Cache = Types.IndependentCache; + auto &Cache = Types.getCacheFor(/*isDependent*/ false, CompletelyFragile); auto entry = Cache.find(key); if (entry != Cache.end()) return entry->second; @@ -2086,18 +2112,6 @@ void IRGenFunction::setLocalSelfMetadata(llvm::Value *value, } #ifndef NDEBUG -CanType TypeConverter::getTypeThatLoweredTo(llvm::Type *t) const { - for (auto &mapping : Types.IndependentCache) { - if (auto fwd = mapping.second.dyn_cast()) - if (fwd == t) - return CanType(mapping.first); - if (auto *ti = mapping.second.dyn_cast()) - if (ti->getStorageType() == t) - return CanType(mapping.first); - } - return CanType(); -} - bool TypeConverter::isExemplarArchetype(ArchetypeType *arch) const { auto genericEnv = arch->getGenericEnvironment(); if (!genericEnv) return true; diff --git a/lib/IRGen/GenType.h b/lib/IRGen/GenType.h index c568e2c431e23..3b4f8d3615e81 100644 --- a/lib/IRGen/GenType.h +++ b/lib/IRGen/GenType.h @@ -67,6 +67,8 @@ class TypeConverter { public: IRGenModule &IGM; private: + bool CompletelyFragile = false; + llvm::DenseMap Protocols; const TypeInfo *FirstType; @@ -80,7 +82,8 @@ class TypeConverter { const LoadableTypeInfo *ObjCClassPtrTI = nullptr; const LoadableTypeInfo *EmptyTI = nullptr; - const TypeInfo *ResilientStructTI = nullptr; + const TypeInfo *AccessibleResilientStructTI = nullptr; + const TypeInfo *InaccessibleResilientStructTI = nullptr; llvm::DenseMap, const LoadableTypeInfo *> OpaqueStorageTypes; @@ -121,7 +124,7 @@ class TypeConverter { const LoadableTypeInfo *convertBuiltinNativeObject(); const LoadableTypeInfo *convertBuiltinUnknownObject(); const LoadableTypeInfo *convertBuiltinBridgeObject(); - const TypeInfo *convertResilientStruct(); + const TypeInfo *convertResilientStruct(IsABIAccessible_t abiAccessible); const TypeInfo *convertUnmanagedStorageType(UnmanagedStorageType *T); const TypeInfo *convertUnownedStorageType(UnownedStorageType *T); const TypeInfo *convertWeakStorageType(WeakStorageType *T); @@ -130,9 +133,12 @@ class TypeConverter { TypeConverter(IRGenModule &IGM); ~TypeConverter(); + bool isCompletelyFragile() const { + return CompletelyFragile; + } + TypeCacheEntry getTypeEntry(CanType type); const TypeInfo &getCompleteTypeInfo(CanType type); - const TypeInfo *tryGetCompleteTypeInfo(CanType type); const LoadableTypeInfo &getNativeObjectTypeInfo(); const LoadableTypeInfo &getUnknownObjectTypeInfo(); const LoadableTypeInfo &getBridgeObjectTypeInfo(); @@ -141,7 +147,7 @@ class TypeConverter { const LoadableTypeInfo &getObjCClassPtrTypeInfo(); const LoadableTypeInfo &getWitnessTablePtrTypeInfo(); const LoadableTypeInfo &getEmptyTypeInfo(); - const TypeInfo &getResilientStructTypeInfo(); + const TypeInfo &getResilientStructTypeInfo(IsABIAccessible_t abiAccessible); const ProtocolInfo &getProtocolInfo(ProtocolDecl *P); const LoadableTypeInfo &getOpaqueStorageTypeInfo(Size storageSize, Alignment storageAlign); @@ -160,6 +166,12 @@ class TypeConverter { /// Exit a generic context. void popGenericContext(CanGenericSignature signature); + /// Enter a scope where all types are lowered bypassing resilience. + void pushCompletelyFragile(); + + /// Exit a completely fragile scope. + void popCompletelyFragile(); + /// Retrieve the generic environment for the current generic context. /// /// Fails if there is no generic context. @@ -169,10 +181,6 @@ class TypeConverter { // Debugging aids. #ifndef NDEBUG bool isExemplarArchetype(ArchetypeType *arch) const; - - LLVM_ATTRIBUTE_DEPRECATED( - CanType getTypeThatLoweredTo(llvm::Type *t) const LLVM_ATTRIBUTE_USED, - "only for use within the debugger"); #endif ArchetypeType *getExemplarArchetype(ArchetypeType *t); @@ -181,20 +189,12 @@ class TypeConverter { class Types_t { llvm::DenseMap IndependentCache; llvm::DenseMap DependentCache; - llvm::DenseMap &getCacheFor(TypeBase *t); + llvm::DenseMap FragileIndependentCache; + llvm::DenseMap FragileDependentCache; - friend TypeCacheEntry TypeConverter::getTypeEntry(CanType T); - friend TypeCacheEntry TypeConverter::convertAnyNominalType(CanType Type, - NominalTypeDecl *D); - friend void TypeConverter::addForwardDecl(TypeBase*, llvm::Type*); - friend ArchetypeType *TypeConverter::getExemplarArchetype(ArchetypeType *t); - friend void TypeConverter::pushGenericContext(CanGenericSignature signature); - friend void TypeConverter::popGenericContext(CanGenericSignature signature); - -#ifndef NDEBUG - friend CanType TypeConverter::getTypeThatLoweredTo(llvm::Type *t) const; - friend bool TypeConverter::isExemplarArchetype(ArchetypeType *arch) const; -#endif + public: + llvm::DenseMap &getCacheFor(bool isDependent, + bool completelyFragile); }; Types_t Types; }; @@ -220,6 +220,26 @@ class GenericContextScope { } }; +/// An RAII interface for forcing types to be lowered bypassing resilience. +class CompletelyFragileScope { + bool State; + TypeConverter &TC; +public: + explicit CompletelyFragileScope(TypeConverter &TC) : TC(TC) { + State = TC.isCompletelyFragile(); + if (!State) + TC.pushCompletelyFragile(); + } + + CompletelyFragileScope(IRGenModule &IGM) + : CompletelyFragileScope(IGM.Types) {} + + ~CompletelyFragileScope() { + if (!State) + TC.popCompletelyFragile(); + } +}; + /// If a type is visibly a singleton aggregate (a tuple with one element, a /// struct with one field, or an enum with a single payload case), return the /// type of its field, which it is guaranteed to have identical layout to. diff --git a/lib/IRGen/GenValueWitness.cpp b/lib/IRGen/GenValueWitness.cpp index de11743fce749..16c8fd8f184e1 100644 --- a/lib/IRGen/GenValueWitness.cpp +++ b/lib/IRGen/GenValueWitness.cpp @@ -56,7 +56,6 @@ const char *irgen::getValueWitnessName(ValueWitness witness) { CASE(InitializeBufferWithCopyOfBuffer) CASE(InitializeWithCopy) CASE(InitializeWithTake) - CASE(InitializeBufferWithTakeOfBuffer) CASE(StoreExtraInhabitant) CASE(GetExtraInhabitantIndex) CASE(GetEnumTag) @@ -331,47 +330,6 @@ static Address emitDefaultInitializeBufferWithCopyOfBuffer( } } -/// Emit an 'initializeBufferWithTakeOfBuffer' operation. -/// Returns the address of the destination object. -static Address -emitDefaultInitializeBufferWithTakeOfBuffer(IRGenFunction &IGF, - Address destBuffer, - Address srcBuffer, - SILType T, - const TypeInfo &type, - FixedPacking packing) { - switch (packing) { - - case FixedPacking::Dynamic: - // Special-case dynamic packing in order to thread the jumps. - return emitForDynamicPacking(IGF, - &emitDefaultInitializeBufferWithTakeOfBuffer, - T, type, destBuffer, srcBuffer); - - case FixedPacking::OffsetZero: { - // Both of these allocations/projections should be no-ops. - Address destObject = - emitDefaultAllocateBuffer(IGF, destBuffer, T, type, packing); - Address srcObject = - emitDefaultProjectBuffer(IGF, srcBuffer, T, type, packing); - type.initializeWithTake(IGF, destObject, srcObject, T, true); - return destObject; - } - - case FixedPacking::Allocate: { - // Just copy the out-of-line storage pointers. - srcBuffer = IGF.Builder.CreateBitCast( - srcBuffer, IGF.IGM.RefCountedPtrTy->getPointerTo()); - llvm::Value *addr = IGF.Builder.CreateLoad(srcBuffer); - destBuffer = IGF.Builder.CreateBitCast( - destBuffer, IGF.IGM.RefCountedPtrTy->getPointerTo()); - IGF.Builder.CreateStore(addr, destBuffer); - return emitDefaultProjectBuffer(IGF, destBuffer, T, type, packing); - } - } - llvm_unreachable("bad fixed packing"); -} - // Metaprogram some of the common boilerplate here: // - the default implementation in TypeInfo // - the value-witness emitter which tries to avoid some dynamic @@ -392,8 +350,6 @@ static Address emit##TITLE(IRGenFunction &IGF, Address dest, Address src, \ } DEFINE_BINARY_BUFFER_OP(initializeBufferWithCopyOfBuffer, InitializeBufferWithCopyOfBuffer) -DEFINE_BINARY_BUFFER_OP(initializeBufferWithTakeOfBuffer, - InitializeBufferWithTakeOfBuffer) #undef DEFINE_BINARY_BUFFER_OP @@ -532,19 +488,6 @@ static void buildValueWitnessFunction(IRGenModule &IGM, return; } - case ValueWitness::InitializeBufferWithTakeOfBuffer: { - Address dest = getArgAsBuffer(IGF, argv, "dest"); - Address src = getArgAsBuffer(IGF, argv, "src"); - getArgAsLocalSelfTypeMetadata(IGF, argv, abstractType); - - Address result = - emitInitializeBufferWithTakeOfBuffer(IGF, dest, src, concreteType, - type, packing); - result = IGF.Builder.CreateBitCast(result, IGF.IGM.OpaquePtrTy); - IGF.Builder.CreateRet(result.getAddress()); - return; - } - case ValueWitness::InitializeWithCopy: { Address dest = getArgAs(IGF, argv, type, "dest"); Address src = getArgAs(IGF, argv, type, "src"); @@ -805,38 +748,6 @@ static llvm::Constant *getMemCpyFunction(IRGenModule &IGM, }); } -/// Return a function which takes two buffer arguments, copies -/// a pointer from the second to the first, and returns the pointer. -static llvm::Constant * -getCopyOutOfLineBoxPointerFunction(IRGenModule &IGM, - const FixedTypeInfo &fixedTI) { - llvm::Type *argTys[] = { IGM.Int8PtrPtrTy, IGM.Int8PtrPtrTy, - IGM.TypeMetadataPtrTy }; - llvm::SmallString<40> name; - { - llvm::raw_svector_ostream nameStream(name); - nameStream << "__swift_copy_outline_existential_box_pointer"; - nameStream << fixedTI.getFixedAlignment().getValue(); - } - return IGM.getOrCreateHelperFunction( - name, IGM.Int8PtrTy, argTys, [&](IRGenFunction &IGF) { - auto it = IGF.CurFn->arg_begin(); - Address dest(&*it++, IGM.getPointerAlignment()); - Address src(&*it++, IGM.getPointerAlignment()); - auto *ptr = IGF.Builder.CreateLoad(src); - IGF.Builder.CreateStore(ptr, dest); - auto *alignmentMask = fixedTI.getStaticAlignmentMask(IGM); - auto *heapHeaderSize = llvm::ConstantInt::get( - IGM.SizeTy, IGM.RefCountedStructSize.getValue()); - auto *startOffset = IGF.Builder.CreateAnd( - IGF.Builder.CreateAdd(heapHeaderSize, alignmentMask), - IGF.Builder.CreateNot(alignmentMask)); - auto *objectAddr = - IGF.emitByteOffsetGEP(ptr, startOffset, IGM.Int8Ty); - IGF.Builder.CreateRet(objectAddr); - }); -} - /// Find a witness to the fact that a type is a value type. /// Always adds an i8*. static void addValueWitness(IRGenModule &IGM, @@ -870,17 +781,6 @@ static void addValueWitness(IRGenModule &IGM, } goto standard; - case ValueWitness::InitializeBufferWithTakeOfBuffer: - if (packing == FixedPacking::Allocate) { - return addFunction(getCopyOutOfLineBoxPointerFunction( - IGM, cast(concreteTI))); - } else - if (packing == FixedPacking::OffsetZero && - concreteTI.isBitwiseTakable(ResilienceExpansion::Maximal)) { - return addFunction(getMemCpyFunction(IGM, concreteTI)); - } - goto standard; - case ValueWitness::InitializeWithTake: if (concreteTI.isBitwiseTakable(ResilienceExpansion::Maximal)) { return addFunction(getMemCpyFunction(IGM, concreteTI)); @@ -927,13 +827,16 @@ static void addValueWitness(IRGenModule &IGM, if (auto *fixedTI = dyn_cast(&concreteTI)) { assert(packing == FixedPacking::OffsetZero || packing == FixedPacking::Allocate); + bool isInline = packing == FixedPacking::OffsetZero; + bool isBitwiseTakable = + fixedTI->isBitwiseTakable(ResilienceExpansion::Maximal); + assert(isBitwiseTakable || !isInline); flags = flags.withAlignment(fixedTI->getFixedAlignment().getValue()) - .withPOD(fixedTI->isPOD(ResilienceExpansion::Maximal)) - .withInlineStorage(packing == FixedPacking::OffsetZero) - .withExtraInhabitants( + .withPOD(fixedTI->isPOD(ResilienceExpansion::Maximal)) + .withInlineStorage(isInline) + .withExtraInhabitants( fixedTI->getFixedExtraInhabitantCount(IGM) > 0) - .withBitwiseTakable( - fixedTI->isBitwiseTakable(ResilienceExpansion::Maximal)); + .withBitwiseTakable(isBitwiseTakable); } else { flags = flags.withIncomplete(true); } @@ -1288,6 +1191,10 @@ FixedPacking TypeInfo::getFixedPacking(IRGenModule &IGM) const { if (!fixedTI) return FixedPacking::Dynamic; + // By convention we only store bitwise takable values inline. + if (!fixedTI->isBitwiseTakable(ResilienceExpansion::Maximal)) + return FixedPacking::Allocate; + Size bufferSize = getFixedBufferSize(IGM); Size requiredSize = fixedTI->getFixedSize(); @@ -1491,6 +1398,6 @@ void TypeInfo::assignArrayWithTake(IRGenFunction &IGF, Address dest, void TypeInfo::collectMetadataForOutlining(OutliningMetadataCollector &c, SILType T) const { - auto canType = T.getSwiftRValueType(); + auto canType = T.getASTType(); assert(!canType->is() && "Did not expect an ArchetypeType"); } diff --git a/lib/IRGen/GenericRequirement.h b/lib/IRGen/GenericRequirement.h index 42c8ed415d449..68057942bd449 100644 --- a/lib/IRGen/GenericRequirement.h +++ b/lib/IRGen/GenericRequirement.h @@ -62,7 +62,7 @@ emitGenericRequirementFromSubstitutions(IRGenFunction &IGF, CanGenericSignature signature, ModuleDecl &module, GenericRequirement requirement, - const SubstitutionMap &subs); + SubstitutionMap subs); using EmitGenericRequirementFn = llvm::function_ref; @@ -141,10 +141,10 @@ class GenericTypeRequirements { llvm::function_ref conf)>; - void enumerateFulfillments(IRGenModule &IGM, const SubstitutionMap &subs, + void enumerateFulfillments(IRGenModule &IGM, SubstitutionMap subs, FulfillmentCallback callback); - void emitInitOfBuffer(IRGenFunction &IGF, const SubstitutionMap &subs, + void emitInitOfBuffer(IRGenFunction &IGF, SubstitutionMap subs, Address buffer); void bindFromBuffer(IRGenFunction &IGF, Address buffer, MetadataState state, diff --git a/lib/IRGen/IRGen.cpp b/lib/IRGen/IRGen.cpp index 40d3a5f7f148e..1ae1a2d9d45e4 100644 --- a/lib/IRGen/IRGen.cpp +++ b/lib/IRGen/IRGen.cpp @@ -19,6 +19,7 @@ #include "swift/AST/DiagnosticsIRGen.h" #include "swift/AST/IRGenOptions.h" #include "swift/AST/LinkLibrary.h" +#include "swift/AST/ProtocolConformance.h" #include "swift/Basic/Defer.h" #include "swift/Basic/Dwarf.h" #include "swift/Basic/Platform.h" @@ -26,6 +27,7 @@ #include "swift/Basic/Timer.h" #include "swift/Basic/Version.h" #include "swift/ClangImporter/ClangImporter.h" +#include "swift/ClangImporter/ClangModule.h" #include "swift/IRGen/IRGenPublic.h" #include "swift/IRGen/IRGenSILPasses.h" #include "swift/LLVMPasses/Passes.h" @@ -174,9 +176,12 @@ void swift::performLLVMOptimizations(IRGenOptions &Opts, llvm::Module *Module, llvm::createAlwaysInlinerLegacyPass(/*insertlifetime*/false); } + bool RunSwiftSpecificLLVMOptzns = + !Opts.DisableSwiftSpecificLLVMOptzns && !Opts.DisableLLVMOptzns; + // If the optimizer is enabled, we run the ARCOpt pass in the scalar optimizer // and the Contract pass as late as possible. - if (!Opts.DisableLLVMARCOpts) { + if (RunSwiftSpecificLLVMOptzns) { PMBuilder.addExtension(PassManagerBuilder::EP_ScalarOptimizerLate, addSwiftARCOptPass); PMBuilder.addExtension(PassManagerBuilder::EP_OptimizerLast, @@ -206,11 +211,12 @@ void swift::performLLVMOptimizations(IRGenOptions &Opts, llvm::Module *Module, addSanitizerCoveragePass); } - if (!Opts.DisableLLVMOptzns) + if (RunSwiftSpecificLLVMOptzns) addCoroutinePassesToExtensionPoints(PMBuilder); - PMBuilder.addExtension(PassManagerBuilder::EP_OptimizerLast, - addSwiftMergeFunctionsPass); + if (RunSwiftSpecificLLVMOptzns) + PMBuilder.addExtension(PassManagerBuilder::EP_OptimizerLast, + addSwiftMergeFunctionsPass); // Configure the function passes. legacy::FunctionPassManager FunctionPasses(Module); @@ -222,7 +228,7 @@ void swift::performLLVMOptimizations(IRGenOptions &Opts, llvm::Module *Module, // The PMBuilder only knows about LLVM AA passes. We should explicitly add // the swift AA pass after the other ones. - if (!Opts.DisableLLVMARCOpts) { + if (RunSwiftSpecificLLVMOptzns) { FunctionPasses.add(createSwiftAAWrapperPass()); FunctionPasses.add(createExternalAAWrapperPass([](Pass &P, Function &, AAResults &AAR) { @@ -251,7 +257,7 @@ void swift::performLLVMOptimizations(IRGenOptions &Opts, llvm::Module *Module, // The PMBuilder only knows about LLVM AA passes. We should explicitly add // the swift AA pass after the other ones. - if (!Opts.DisableLLVMARCOpts) { + if (RunSwiftSpecificLLVMOptzns) { ModulePasses.add(createSwiftAAWrapperPass()); ModulePasses.add(createExternalAAWrapperPass([](Pass &P, Function &, AAResults &AAR) { @@ -791,22 +797,6 @@ static std::unique_ptr performIRGeneration(IRGenOptions &Opts, IGM.addLinkLibrary(linkLib); }); - // Hack to handle thunks eagerly synthesized by the Clang importer. - swift::ModuleDecl *prev = nullptr; - for (auto external : Ctx.ExternalDefinitions) { - swift::ModuleDecl *next = external->getModuleContext(); - if (next == prev) - continue; - prev = next; - - if (next->getName() == M->getName()) - continue; - - next->collectLinkLibraries([&](LinkLibrary linkLib) { - IGM.addLinkLibrary(linkLib); - }); - } - if (!IGM.finalize()) return nullptr; @@ -933,8 +923,8 @@ static void performParallelIRGeneration( } // Emit the module contents. - irgen.emitGlobalTopLevel(); - + irgen.emitGlobalTopLevel(true /*emitForParallelEmission*/); + for (auto *File : M->getFiles()) { if (auto *SF = dyn_cast(File)) { IRGenModule *IGM = irgen.getGenModule(SF); @@ -976,22 +966,6 @@ static void performParallelIRGeneration( PrimaryGM->addLinkLibrary(linkLib); }); - // Hack to handle thunks eagerly synthesized by the Clang importer. - swift::ModuleDecl *prev = nullptr; - for (auto external : Ctx.ExternalDefinitions) { - swift::ModuleDecl *next = external->getModuleContext(); - if (next == prev) - continue; - prev = next; - - if (next->getName() == M->getName()) - continue; - - next->collectLinkLibraries([&](LinkLibrary linkLib) { - PrimaryGM->addLinkLibrary(linkLib); - }); - } - llvm::StringSet<> referencedGlobals; for (auto it = irgen.begin(); it != irgen.end(); ++it) { diff --git a/lib/IRGen/IRGen.h b/lib/IRGen/IRGen.h index a02e9584c4311..9f5b48534b10b 100644 --- a/lib/IRGen/IRGen.h +++ b/lib/IRGen/IRGen.h @@ -33,7 +33,7 @@ namespace swift { class CanType; class ClusteredBitVector; enum ForDefinition_t : bool; - + namespace irgen { using Lowering::AbstractionPattern; using clang::CodeGen::ConstantInitFuture; @@ -77,6 +77,11 @@ inline IsBitwiseTakable_t &operator&=(IsBitwiseTakable_t &l, IsBitwiseTakable_t return (l = (l & r)); } +enum IsABIAccessible_t : bool { + IsNotABIAccessible = false, + IsABIAccessible = true +}; + /// The kind of reference counting implementation a heap object uses. enum class ReferenceCounting : uint8_t { /// The object uses native Swift reference counting. diff --git a/lib/IRGen/IRGenDebugInfo.cpp b/lib/IRGen/IRGenDebugInfo.cpp index 3a9ba33c5a65f..f413d1c13087b 100644 --- a/lib/IRGen/IRGenDebugInfo.cpp +++ b/lib/IRGen/IRGenDebugInfo.cpp @@ -125,7 +125,7 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { void finalize(); void setCurrentLoc(IRBuilder &Builder, const SILDebugScope *DS, - Optional Loc = None); + SILLocation Loc); void clearLoc(IRBuilder &Builder); void pushLoc(); void popLoc(); @@ -154,7 +154,7 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { void emitGlobalVariableDeclaration(llvm::GlobalVariable *Storage, StringRef Name, StringRef LinkageName, DebugTypeInfo DebugType, - bool IsLocalToUnit, + bool IsLocalToUnit, bool InFixedBuffer, Optional Loc); void emitTypeMetadata(IRGenFunction &IGF, llvm::Value *Metadata, StringRef Name); @@ -286,6 +286,9 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { auto L = decodeDebugLoc(CS->Loc); auto Scope = getOrCreateScope(CS->Parent.dyn_cast()); + // Pretend transparent functions don't exist. + if (!Scope) + return createInlinedAt(CS); auto InlinedAt = llvm::DebugLoc::get(L.Line, L.Column, Scope, createInlinedAt(CS)); InlinedAtCache.insert( @@ -468,7 +471,7 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { void createParameterType(llvm::SmallVectorImpl &Parameters, SILType type, DeclContext *DeclCtx, GenericEnvironment *GE) { - auto RealType = type.getSwiftRValueType(); + auto RealType = type.getASTType(); if (type.isAddress()) RealType = CanInOutType::get(RealType); auto DbgTy = DebugTypeInfo::getFromTypeInfo(DeclCtx, GE, RealType, @@ -1182,7 +1185,7 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { SmallVector Protocols; for (auto *ProtocolDecl : Archetype->getConformsTo()) { auto PTy = IGM.getLoweredType(ProtocolDecl->getInterfaceType()) - .getSwiftRValueType(); + .getASTType(); auto PDbgTy = DebugTypeInfo::getFromTypeInfo( DbgTy.getDeclContext(), DbgTy.getGenericEnvironment(), ProtocolDecl->getInterfaceType(), IGM.getTypeInfoForLowered(PTy)); @@ -1555,7 +1558,7 @@ void IRGenDebugInfoImpl::finalize() { void IRGenDebugInfoImpl::setCurrentLoc(IRBuilder &Builder, const SILDebugScope *DS, - Optional Loc) { + SILLocation Loc) { assert(DS && "empty scope"); auto *Scope = getOrCreateScope(DS); if (!Scope) @@ -1563,9 +1566,9 @@ void IRGenDebugInfoImpl::setCurrentLoc(IRBuilder &Builder, SILLocation::DebugLoc L; SILFunction *Fn = DS->getInlinedFunction(); - if (Fn && Fn->isThunk()) { + if (Fn && (Fn->isThunk() || Fn->isTransparent())) { L = SILLocation::getCompilerGeneratedDebugLoc(); - } else if (DS == LastScope && Loc && Loc->isAutoGenerated()) { + } else if (DS == LastScope && Loc.isAutoGenerated()) { // Reuse the last source location if we are still in the same // scope to get a more contiguous line table. L = LastDebugLoc; @@ -1574,7 +1577,7 @@ void IRGenDebugInfoImpl::setCurrentLoc(IRBuilder &Builder, L = getDebugLocation(Loc); // Otherwise use a line 0 artificial location, but the file from the // location. - if (Loc && Loc->isAutoGenerated()) { + if (Loc.isAutoGenerated()) { L.Line = 0; L.Column = 0; } @@ -1735,6 +1738,7 @@ IRGenDebugInfoImpl::emitFunction(const SILDebugScope *DS, llvm::Function *Fn, // Some IRGen-generated helper functions don't have a corresponding // SIL function, hence the dyn_cast. auto *SILFn = DS ? DS->Parent.dyn_cast() : nullptr; + StringRef LinkageName; if (Fn) LinkageName = Fn->getName(); @@ -1754,17 +1758,18 @@ IRGenDebugInfoImpl::emitFunction(const SILDebugScope *DS, llvm::Function *Fn, /// The source line used for the function prologue. unsigned ScopeLine = 0; SILLocation::DebugLoc L; - if (DS && (!SILFn || (!SILFn->isBare() && !SILFn->isThunk()))) { + if (!DS || (SILFn && (SILFn->isBare() || SILFn->isThunk() || + SILFn->isTransparent()))) { // Bare functions and thunks should not have any line numbers. This // is especially important for shared functions like reabstraction // thunk helpers, where DS->Loc is an arbitrary location of whichever use // was emitted first. + L = SILLocation::getCompilerGeneratedDebugLoc(); + } else { L = decodeDebugLoc(DS->Loc); ScopeLine = L.Line; if (!DS->Loc.isDebugInfoLoc()) L = decodeSourceLoc(DS->Loc.getSourceLoc()); - } else { - L = SILLocation::getCompilerGeneratedDebugLoc(); } auto Line = L.Line; @@ -1882,7 +1887,7 @@ void IRGenDebugInfoImpl::emitVariableDeclaration( if (!DbgTy.size) DbgTy.size = getStorageSize(IGM.DataLayout, Storage); - auto *Scope = dyn_cast(getOrCreateScope(DS)); + auto *Scope = dyn_cast_or_null(getOrCreateScope(DS)); assert(Scope && "variable has no local scope"); auto Loc = getDebugLoc(*this, VarDecl); @@ -1989,7 +1994,8 @@ void IRGenDebugInfoImpl::emitDbgIntrinsic( void IRGenDebugInfoImpl::emitGlobalVariableDeclaration( llvm::GlobalVariable *Var, StringRef Name, StringRef LinkageName, - DebugTypeInfo DbgTy, bool IsLocalToUnit, Optional Loc) { + DebugTypeInfo DbgTy, bool IsLocalToUnit, bool InFixedBuffer, + Optional Loc) { if (Opts.DebugInfoKind <= IRGenDebugInfoKind::LineTables) return; @@ -2005,7 +2011,16 @@ void IRGenDebugInfoImpl::emitGlobalVariableDeclaration( auto File = getOrCreateFile(L.Filename); // Emit it as global variable of the current module. - auto *Expr = Var ? nullptr : DBuilder.createConstantValueExpression(0); + llvm::DIExpression *Expr = nullptr; + if (!Var) + Expr = DBuilder.createConstantValueExpression(0); + else if (InFixedBuffer) + // FIXME: This is *not* generally correct, but LLDB at the moment cannot + // poke to runtime to figure out whether a resilient value has inline + // storage, so this is assuming that it doesn't to get the majority of + // resilient Foundation types. + Expr = + DBuilder.createExpression(ArrayRef(llvm::dwarf::DW_OP_deref)); auto *GV = DBuilder.createGlobalVariableExpression( MainModule, Name, LinkageName, File, L.Line, Ty, IsLocalToUnit, Expr); if (Var) @@ -2018,13 +2033,17 @@ void IRGenDebugInfoImpl::emitTypeMetadata(IRGenFunction &IGF, if (Opts.DebugInfoKind <= IRGenDebugInfoKind::LineTables) return; + // Don't emit debug info in transparent functions. + auto *DS = IGF.getDebugScope(); + if (!DS || DS->getInlinedFunction()->isTransparent()) + return; + auto TName = BumpAllocatedString(("$swift.type." + Name).str()); auto DbgTy = DebugTypeInfo::getMetadata( getMetadataType()->getDeclaredInterfaceType().getPointer(), Metadata->getType(), Size(CI.getTargetInfo().getPointerWidth(0)), Alignment(CI.getTargetInfo().getPointerAlign(0))); - emitVariableDeclaration(IGF.Builder, Metadata, DbgTy, IGF.getDebugScope(), - nullptr, TName, 0, + emitVariableDeclaration(IGF.Builder, Metadata, DbgTy, DS, nullptr, TName, 0, // swift.type is already a pointer type, // having a shadow copy doesn't add another // layer of indirection. @@ -2056,7 +2075,7 @@ void IRGenDebugInfo::finalize() { } void IRGenDebugInfo::setCurrentLoc(IRBuilder &Builder, const SILDebugScope *DS, - Optional Loc) { + SILLocation Loc) { static_cast(this)->setCurrentLoc(Builder, DS, Loc); } @@ -2129,9 +2148,10 @@ void IRGenDebugInfo::emitDbgIntrinsic(IRBuilder &Builder, llvm::Value *Storage, void IRGenDebugInfo::emitGlobalVariableDeclaration( llvm::GlobalVariable *Storage, StringRef Name, StringRef LinkageName, - DebugTypeInfo DebugType, bool IsLocalToUnit, Optional Loc) { + DebugTypeInfo DebugType, bool IsLocalToUnit, bool InFixedBuffer, + Optional Loc) { static_cast(this)->emitGlobalVariableDeclaration( - Storage, Name, LinkageName, DebugType, IsLocalToUnit, Loc); + Storage, Name, LinkageName, DebugType, IsLocalToUnit, InFixedBuffer, Loc); } void IRGenDebugInfo::emitTypeMetadata(IRGenFunction &IGF, llvm::Value *Metadata, diff --git a/lib/IRGen/IRGenDebugInfo.h b/lib/IRGen/IRGenDebugInfo.h index 1066fd655781f..a82217489ebcc 100644 --- a/lib/IRGen/IRGenDebugInfo.h +++ b/lib/IRGen/IRGenDebugInfo.h @@ -57,7 +57,7 @@ class IRGenDebugInfo { /// Update the IRBuilder's current debug location to the location /// Loc and the lexical scope DS. void setCurrentLoc(IRBuilder &Builder, const SILDebugScope *DS, - Optional Loc = None); + SILLocation Loc); void clearLoc(IRBuilder &Builder); @@ -136,11 +136,13 @@ class IRGenDebugInfo { unsigned Line, unsigned Col, llvm::DILocalScope *Scope, const SILDebugScope *DS); + enum { NotHeapAllocated = false }; + /// Create debug metadata for a global variable. void emitGlobalVariableDeclaration(llvm::GlobalVariable *Storage, StringRef Name, StringRef LinkageName, DebugTypeInfo DebugType, - bool IsLocalToUnit, + bool IsLocalToUnit, bool InFixedBuffer, Optional Loc); /// Emit debug metadata for type metadata (for generic types). So meta. diff --git a/lib/IRGen/IRGenFunction.h b/lib/IRGen/IRGenFunction.h index 958549b8dffff..7324308bc70f1 100644 --- a/lib/IRGen/IRGenFunction.h +++ b/lib/IRGen/IRGenFunction.h @@ -445,9 +445,11 @@ class IRGenFunction { llvm::Value *emitIsUniqueCall(llvm::Value *value, SourceLoc loc, bool isNonNull, bool checkPinned); - llvm::Value *emitIsEscapingClosureCall(llvm::Value *value, SourceLoc loc); + llvm::Value *emitIsEscapingClosureCall(llvm::Value *value, SourceLoc loc, + unsigned verificationType); -//--- Expression emission ------------------------------------------------------ + //--- Expression emission + //------------------------------------------------------ public: void emitFakeExplosion(const TypeInfo &type, Explosion &explosion); diff --git a/lib/IRGen/IRGenMangler.cpp b/lib/IRGen/IRGenMangler.cpp index d9fcc0c59ec8b..7c74e2bdddf83 100644 --- a/lib/IRGen/IRGenMangler.cpp +++ b/lib/IRGen/IRGenMangler.cpp @@ -45,7 +45,6 @@ std::string IRGenMangler::mangleValueWitness(Type type, ValueWitness witness) { GET_MANGLING(AssignWithCopy) \ GET_MANGLING(InitializeWithTake) \ GET_MANGLING(AssignWithTake) \ - GET_MANGLING(InitializeBufferWithTakeOfBuffer) \ GET_MANGLING(GetEnumTagSinglePayload) \ GET_MANGLING(StoreEnumTagSinglePayload) \ GET_MANGLING(StoreExtraInhabitant) \ @@ -81,10 +80,8 @@ std::string IRGenMangler::manglePartialApplyForwarder(StringRef FuncName) { SymbolicMangling IRGenMangler::mangleTypeForReflection(IRGenModule &IGM, - Type Ty, - ModuleDecl *Module, - bool isSingleFieldOfBox) { - Mod = Module; + Type Ty) { + Mod = IGM.getSwiftModule(); OptimizeProtocolNames = false; llvm::SaveAndRestore> @@ -109,8 +106,6 @@ IRGenMangler::mangleTypeForReflection(IRGenModule &IGM, SymbolicReferences.clear(); appendType(Ty); - if (isSingleFieldOfBox) - appendOperator("Xb"); return {finalize(), std::move(SymbolicReferences)}; } diff --git a/lib/IRGen/IRGenMangler.h b/lib/IRGen/IRGenMangler.h index a0b25a4b45e35..9d342e1c5246f 100644 --- a/lib/IRGen/IRGenMangler.h +++ b/lib/IRGen/IRGenMangler.h @@ -354,9 +354,7 @@ class IRGenMangler : public Mangle::ASTMangler { } SymbolicMangling mangleTypeForReflection(IRGenModule &IGM, - Type Ty, - ModuleDecl *Module, - bool isSingleFieldOfBox); + Type Ty); std::string mangleTypeForLLVMTypeName(CanType Ty); diff --git a/lib/IRGen/IRGenModule.cpp b/lib/IRGen/IRGenModule.cpp index 21ed904c3ba0f..3bc600046ecd6 100644 --- a/lib/IRGen/IRGenModule.cpp +++ b/lib/IRGen/IRGenModule.cpp @@ -793,8 +793,9 @@ static void appendEncodedName(llvm::SmallVectorImpl &buf, appendEncodedName(os, name); } -static StringRef encodeForceLoadSymbolName(llvm::SmallVectorImpl &buf, - StringRef name) { +StringRef +swift::irgen::encodeForceLoadSymbolName(llvm::SmallVectorImpl &buf, + StringRef name) { llvm::raw_svector_ostream os{buf}; os << "_swift_FORCE_LOAD_$"; appendEncodedName(os, name); @@ -1047,9 +1048,11 @@ bool IRGenModule::finalize() { ModuleHash->setSection("__LLVM,__swift_modhash"); break; case llvm::Triple::ELF: - case llvm::Triple::COFF: ModuleHash->setSection(".swift_modhash"); break; + case llvm::Triple::COFF: + ModuleHash->setSection(".sw5hash"); + break; default: llvm_unreachable("Don't know how to emit the module hash for the selected" "object format."); diff --git a/lib/IRGen/IRGenModule.h b/lib/IRGen/IRGenModule.h index 39303b8a88779..c102655002e61 100644 --- a/lib/IRGen/IRGenModule.h +++ b/lib/IRGen/IRGenModule.h @@ -309,7 +309,11 @@ class IRGenerator { /// Emit functions, variables and tables which are needed anyway, e.g. because /// they are externally visible. - void emitGlobalTopLevel(); + /// If \p emitForParallelEmission is true ensures that symbols that are + /// expressed as relative pointers are collocated in the same output module + /// with their base symbol. For example, witness methods need to be collocated + /// with the witness table in the same LLVM module. + void emitGlobalTopLevel(bool emitForParallelEmission = false); /// Emit references to each of the protocol descriptors defined in this /// IR module. @@ -353,6 +357,8 @@ class IRGenerator { noteUseOfTypeGlobals(type, false, requireMetadata); } + void noteUseOfAnyParentTypeMetadata(NominalTypeDecl *type); + private: void noteUseOfTypeGlobals(NominalTypeDecl *type, bool isUseOfMetadata, @@ -760,6 +766,7 @@ class IRGenModule { void destroyMetadataLayoutMap(); friend class GenericContextScope; + friend class CompletelyFragileScope; //--- Globals --------------------------------------------------------------- public: @@ -965,13 +972,14 @@ class IRGenModule { /// reflection metadata. llvm::SetVector ImportedStructs; + llvm::Constant *getTypeRef(CanType type); llvm::Constant *getAddrOfStringForTypeRef(StringRef mangling); llvm::Constant *getAddrOfStringForTypeRef(const SymbolicMangling &mangling); llvm::Constant *getAddrOfFieldName(StringRef Name); llvm::Constant *getAddrOfCaptureDescriptor(SILFunction &caller, CanSILFunctionType origCalleeType, CanSILFunctionType substCalleeType, - SubstitutionList subs, + SubstitutionMap subs, const HeapLayout &layout); llvm::Constant *getAddrOfBoxDescriptor(CanType boxedType); @@ -1257,6 +1265,8 @@ private: \ void emitSharedContextDescriptor(DeclContext *dc); + void ensureRelativeSymbolCollocation(SILWitnessTable &wt); + private: llvm::Constant * getAddrOfSharedContextDescriptor(LinkEntity entity, diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index d38dd96e81590..9cbb9c167ea16 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -719,6 +719,32 @@ class IRGenSILFunction : } } + /// To make it unambiguous whether a `var` binding has been initialized, + /// zero-initialize the first pointer-sized field. LLDB uses this to + /// recognize to detect uninitizialized variables. This can be removed once + /// swiftc switches to @llvm.dbg.addr() intrinsics. + void zeroInit(llvm::AllocaInst *AI) { + if (!AI) + return; + + // Only do this at -Onone. + if (IGM.IRGen.Opts.shouldOptimize()) + return; + + auto &DL = IGM.DataLayout; + if (DL.getTypeSizeInBits(AI->getAllocatedType()) < + DL.getPointerSizeInBits()) + return; + + llvm::IRBuilder<> ZeroInitBuilder(AI->getNextNode()); + ZeroInitBuilder.SetCurrentDebugLocation(nullptr); + auto *BC = + ZeroInitBuilder.CreateBitCast(AI, IGM.OpaquePtrTy->getPointerTo()); + ZeroInitBuilder.CreateAlignedStore( + llvm::ConstantPointerNull::get(IGM.OpaquePtrTy), BC, + IGM.getPointerAlignment().getValue()); + } + /// Account for bugs in LLVM. /// /// - The LLVM type legalizer currently doesn't update debug @@ -742,6 +768,7 @@ class IRGenSILFunction : auto &Alloca = ShadowStackSlots[{ArgNo, {Scope, Name}}]; if (!Alloca.isValid()) Alloca = createAlloca(Storage->getType(), Align, Name+".addr"); + zeroInit(dyn_cast(Alloca.getAddress())); ArtificialLocation AutoRestore(Scope, IGM.DebugInfo, Builder); Builder.CreateStore(Storage, Alloca.getAddress(), Align); @@ -1039,6 +1066,9 @@ class IRGenSILFunction : } void visitMarkDependenceInst(MarkDependenceInst *i); void visitCopyBlockInst(CopyBlockInst *i); + void visitCopyBlockWithoutEscapingInst(CopyBlockWithoutEscapingInst *i) { + llvm_unreachable("not valid in canonical SIL"); + } void visitStrongPinInst(StrongPinInst *i); void visitStrongUnpinInst(StrongUnpinInst *i); void visitStrongRetainInst(StrongRetainInst *i); @@ -1547,7 +1577,7 @@ static void emitLocalSelfMetadata(IRGenSILFunction &IGF) { const SILArgument *selfArg = IGF.CurSILFn->getSelfMetadataArgument(); CanMetatypeType metaTy = - dyn_cast(selfArg->getType().getSwiftRValueType()); + dyn_cast(selfArg->getType().getASTType()); IRGenFunction::LocalSelfKind selfKind; if (!metaTy) selfKind = IRGenFunction::ObjectReference; @@ -1741,8 +1771,8 @@ void IRGenSILFunction::visitSILBasicBlock(SILBasicBlock *BB) { for (auto &I : *BB) { if (IGM.DebugInfo) { // Set the debug info location for I, if applicable. - SILLocation ILoc = I.getLoc(); auto DS = I.getDebugScope(); + SILLocation ILoc = I.getLoc(); // Handle cleanup locations. if (ILoc.is()) { // Cleanup locations point to the decl of the value that is @@ -1789,17 +1819,19 @@ void IRGenSILFunction::visitSILBasicBlock(SILBasicBlock *BB) { auto Prev = --I.getIterator(); if (Prev != BB->end()) DS = Prev->getDebugScope(); - // Use an artificial (line 0) location. - IGM.DebugInfo->setCurrentLoc(Builder, DS); + + // Use an artificial (line 0) location, to indicate we'd like to + // reuse the last debug loc. + IGM.DebugInfo->setCurrentLoc( + Builder, DS, RegularLocation::getAutoGeneratedLocation()); } if (isa(&I)) emitDebugVariableRangeExtension(BB); } visit(&I); - } - + assert(Builder.hasPostTerminatorIP() && "SIL bb did not terminate block?!"); } @@ -1883,7 +1915,7 @@ void IRGenSILFunction::visitGlobalValueInst(GlobalValueInst *i) { llvm::Value *Ref = IGM.getAddrOfSILGlobalVariable(var, ti, NotForDefinition).getAddress(); - CanType ClassType = loweredTy.getSwiftRValueType(); + auto ClassType = loweredTy.getASTType(); llvm::Value *Metadata = emitClassHeapMetadataRef(*this, ClassType, MetadataValueType::TypeMetadata, MetadataState::Complete); @@ -1914,24 +1946,6 @@ static llvm::Value *getClassBaseValue(IRGenSILFunction &IGF, return e.claimNext(); } -static llvm::Value *getClassMetatype(IRGenFunction &IGF, - llvm::Value *baseValue, - MetatypeRepresentation repr, - SILType instanceType) { - switch (repr) { - case MetatypeRepresentation::Thin: - llvm_unreachable("Class metatypes are never thin"); - - case MetatypeRepresentation::Thick: - return emitDynamicTypeOfHeapObject(IGF, baseValue, instanceType); - - case MetatypeRepresentation::ObjC: - return emitHeapMetadataRefForHeapObject(IGF, baseValue, instanceType); - } - - llvm_unreachable("Not a valid MetatypeRepresentation."); -} - void IRGenSILFunction::visitValueMetatypeInst(swift::ValueMetatypeInst *i) { SILType instanceTy = i->getOperand()->getType(); auto metaTy = i->getType().castTo(); @@ -1945,12 +1959,12 @@ void IRGenSILFunction::visitValueMetatypeInst(swift::ValueMetatypeInst *i) { Explosion e; if (instanceTy.getClassOrBoundGenericClass()) { - e.add(getClassMetatype(*this, + e.add(emitDynamicTypeOfHeapObject(*this, getClassBaseValue(*this, i->getOperand()), metaTy->getRepresentation(), instanceTy)); } else if (auto arch = instanceTy.getAs()) { if (arch->requiresClass()) { - e.add(getClassMetatype(*this, + e.add(emitDynamicTypeOfHeapObject(*this, getClassBaseValue(*this, i->getOperand()), metaTy->getRepresentation(), instanceTy)); } else { @@ -2063,7 +2077,7 @@ static llvm::Value *getObjCClassForValue(IRGenFunction &IGF, static llvm::Value *emitWitnessTableForLoweredCallee(IRGenSILFunction &IGF, CanSILFunctionType origCalleeType, - SubstitutionList subs) { + SubstitutionMap subs) { llvm::Value *wtable; if (auto *proto = origCalleeType->getDefaultWitnessMethodProtocol()) { @@ -2072,11 +2086,9 @@ static llvm::Value *emitWitnessTableForLoweredCallee(IRGenSILFunction &IGF, // // We recover the witness table from the substitution that was used to // produce the substituted callee type. - auto subMap = origCalleeType->getGenericSignature() - ->getSubstitutionMap(subs); auto origSelfType = proto->getSelfInterfaceType()->getCanonicalType(); - auto substSelfType = origSelfType.subst(subMap)->getCanonicalType(); - auto conformance = *subMap.lookupConformance(origSelfType, proto); + auto substSelfType = origSelfType.subst(subs)->getCanonicalType(); + auto conformance = *subs.lookupConformance(origSelfType, proto); llvm::Value *argMetadata = IGF.emitTypeMetadataRef(substSelfType); wtable = emitWitnessTableRef(IGF, substSelfType, &argMetadata, @@ -2178,7 +2190,7 @@ static CallEmission getCallEmissionForLoweredValue(IRGenSILFunction &IGF, CanSILFunctionType substCalleeType, const LoweredValue &lv, llvm::Value *selfValue, - const SubstitutionList &substitutions, + SubstitutionMap substitutions, WitnessMetadata *witnessMetadata, Explosion &args) { Callee callee = lv.getCallee(IGF, selfValue, @@ -2267,7 +2279,8 @@ void IRGenSILFunction::visitFullApplySite(FullApplySite site) { WitnessMetadata witnessMetadata; CallEmission emission = getCallEmissionForLoweredValue(*this, origCalleeType, substCalleeType, - calleeLV, selfValue, site.getSubstitutions(), + calleeLV, selfValue, + site.getSubstitutionMap(), &witnessMetadata, llArgs); // Lower the arguments and return value in the callee's generic context. @@ -2301,9 +2314,7 @@ void IRGenSILFunction::visitFullApplySite(FullApplySite site) { // Pass the generic arguments. if (hasPolymorphicParameters(origCalleeType)) { - SubstitutionMap subMap; - if (auto genericSig = origCalleeType->getGenericSignature()) - subMap = genericSig->getSubstitutionMap(site.getSubstitutions()); + SubstitutionMap subMap = site.getSubstitutionMap(); emitPolymorphicArguments(*this, origCalleeType, subMap, &witnessMetadata, llArgs); } @@ -2385,7 +2396,7 @@ void IRGenSILFunction::visitFullApplySite(FullApplySite site) { /// applying to 'v'. static std::tuple getPartialApplicationFunction(IRGenSILFunction &IGF, SILValue v, - SubstitutionList subs) { + SubstitutionMap subs) { LoweredValue &lv = IGF.getLoweredValue(v); auto fnType = v->getType().castTo(); @@ -2494,7 +2505,7 @@ void IRGenSILFunction::visitPartialApplyInst(swift::PartialApplyInst *i) { // Get the function value. auto result = getPartialApplicationFunction(*this, i->getCallee(), - i->getSubstitutions()); + i->getSubstitutionMap()); FunctionPointer calleeFn = std::get<0>(result); llvm::Value *innerContext = std::get<1>(result); CanSILFunctionType origCalleeTy = std::get<2>(result); @@ -2503,7 +2514,7 @@ void IRGenSILFunction::visitPartialApplyInst(swift::PartialApplyInst *i) { Explosion function; emitFunctionPartialApplication( *this, *CurSILFn, calleeFn, innerContext, llArgs, params, - i->getSubstitutions(), origCalleeTy, i->getSubstCalleeType(), + i->getSubstitutionMap(), origCalleeTy, i->getSubstCalleeType(), i->getType().castTo(), function, false); setLoweredExplosion(v, function); } @@ -3635,6 +3646,9 @@ void IRGenSILFunction::visitDebugValueInst(DebugValueInst *i) { if (!IGM.DebugInfo) return; + if (i->getDebugScope()->getInlinedFunction()->isTransparent()) + return; + auto VarInfo = i->getVarInfo(); assert(VarInfo && "debug_value without debug info"); auto SILVal = i->getOperand(); @@ -3652,7 +3666,7 @@ void IRGenSILFunction::visitDebugValueInst(DebugValueInst *i) { StringRef Name = getVarName(i, IsAnonymous); DebugTypeInfo DbgTy; SILType SILTy = SILVal->getType(); - auto RealTy = SILVal->getType().getSwiftRValueType(); + auto RealTy = SILVal->getType().getASTType(); if (VarDecl *Decl = i->getDecl()) { DbgTy = DebugTypeInfo::getLocalVariable( CurSILFn->getDeclContext(), CurSILFn->getGenericEnvironment(), Decl, @@ -3677,6 +3691,10 @@ void IRGenSILFunction::visitDebugValueInst(DebugValueInst *i) { void IRGenSILFunction::visitDebugValueAddrInst(DebugValueAddrInst *i) { if (!IGM.DebugInfo) return; + + if (i->getDebugScope()->getInlinedFunction()->isTransparent()) + return; + VarDecl *Decl = i->getDecl(); if (!Decl) return; @@ -3692,9 +3710,7 @@ void IRGenSILFunction::visitDebugValueAddrInst(DebugValueAddrInst *i) { StringRef Name = getVarName(i, IsAnonymous); auto Addr = getLoweredAddress(SILVal).getAddress(); SILType SILTy = SILVal->getType(); - auto RealType = SILTy.getSwiftRValueType(); - if (SILTy.isAddress() && !IsLoadablyByAddress) - RealType = CanInOutType::get(RealType); + auto RealType = SILTy.getASTType(); // Unwrap implicitly indirect types and types that are passed by // reference only at the SIL level and below. // @@ -3867,7 +3883,7 @@ void IRGenSILFunction::visitStoreUnownedInst(swift::StoreUnownedInst *i) { static bool hasReferenceSemantics(IRGenSILFunction &IGF, SILType silType) { - auto operType = silType.getSwiftRValueType(); + auto operType = silType.getASTType(); auto valueType = operType->getOptionalObjectType(); auto objType = valueType ? valueType : operType; return (objType->mayHaveSuperclass() @@ -3911,20 +3927,30 @@ visitIsUniqueOrPinnedInst(swift::IsUniqueOrPinnedInst *i) { void IRGenSILFunction::visitIsEscapingClosureInst( swift::IsEscapingClosureInst *i) { - assert(i->getOperand()->getType().is() && - i->getOperand() - ->getType() - .getAs() - ->getExtInfo() - .hasContext() && - "Must have a closure operand"); + // The closure operand is allowed to be an optional closure. + auto operandType = i->getOperand()->getType(); + if (operandType.getOptionalObjectType()) + operandType = operandType.getOptionalObjectType(); + + auto fnType = operandType.getAs(); + assert(fnType->getExtInfo().hasContext() && "Must have a closure operand"); + + // This code relies on that an optional<()->()>'s tag fits in the function + // pointer. + auto &TI = cast(getTypeInfo(operandType)); + assert(TI.mayHaveExtraInhabitants(IGM) && + "Must have extra inhabitants to be able to handle the optional " + "closure case"); Explosion closure = getLoweredExplosion(i->getOperand()); auto func = closure.claimNext(); (void)func; auto context = closure.claimNext(); - - auto result = emitIsEscapingClosureCall(context, i->getLoc().getSourceLoc()); + assert(closure.empty()); + if (context->getType()->isIntegerTy()) + context = Builder.CreateIntToPtr(context, IGM.RefCountedPtrTy); + auto result = emitIsEscapingClosureCall(context, i->getLoc().getSourceLoc(), + i->getVerificationType()); Explosion out; out.add(result); setLoweredExplosion(i, out); @@ -3948,6 +3974,9 @@ void IRGenSILFunction::emitDebugInfoForAllocStack(AllocStackInst *i, if (!DS) return; + if (i->getDebugScope()->getInlinedFunction()->isTransparent()) + return; + bool IsAnonymous = false; StringRef Name = getVarName(i, IsAnonymous); @@ -3971,7 +4000,7 @@ void IRGenSILFunction::emitDebugInfoForAllocStack(AllocStackInst *i, return; SILType SILTy = i->getType(); - auto RealType = SILTy.getSwiftRValueType(); + auto RealType = SILTy.getASTType(); auto DbgTy = DebugTypeInfo::getLocalVariable( CurSILFn->getDeclContext(), CurSILFn->getGenericEnvironment(), Decl, RealType, type, false); @@ -4003,26 +4032,12 @@ void IRGenSILFunction::visitAllocStackInst(swift::AllocStackInst *i) { // Generate Debug Info. if (!Decl) return; - emitDebugInfoForAllocStack(i, type, addr.getAddress().getAddress()); - - // To make it unambiguous whether a `var` binding has been initialized, - // zero-initialize the first pointer-sized field. LLDB uses this to - // recognize to detect uninitizialized variables. This can be removed once - // swiftc switches to @llvm.dbg.addr() intrinsics. This dead store will get - // optimized away when optimizations are enabled. - if (!Decl->getType()->getClassOrBoundGenericClass()) - return; - auto *AI = dyn_cast(addr.getAddress().getAddress()); - if (!AI) - return; - - auto &DL = IGM.DataLayout; - if (DL.getTypeSizeInBits(AI->getAllocatedType()) < DL.getPointerSize()) - return; - auto *BC = Builder.CreateBitCast(AI, IGM.OpaquePtrTy->getPointerTo()); - Builder.CreateStore(llvm::ConstantPointerNull::get(IGM.OpaquePtrTy), BC, - IGM.getPointerAlignment()); + Type Desugared = Decl->getType()->getDesugaredType(); + if (Desugared->getClassOrBoundGenericClass() || + Desugared->getStructOrBoundGenericStruct()) + zeroInit(dyn_cast(addr.getAddress().getAddress())); + emitDebugInfoForAllocStack(i, type, addr.getAddress().getAddress()); } static void @@ -4165,6 +4180,9 @@ void IRGenSILFunction::visitAllocBoxInst(swift::AllocBoxInst *i) { DbgName); setLoweredBox(i, boxWithAddr); + if (i->getDebugScope()->getInlinedFunction()->isTransparent()) + return; + if (IGM.DebugInfo && Decl) { // FIXME: This is a workaround to not produce local variables for // capture list arguments like "[weak self]". The better solution @@ -4177,9 +4195,7 @@ void IRGenSILFunction::visitAllocBoxInst(swift::AllocBoxInst *i) { assert(i->getBoxType()->getLayout()->getFields().size() == 1 && "box for a local variable should only have one field"); auto SILTy = i->getBoxType()->getFieldType(IGM.getSILModule(), 0); - auto RealType = SILTy.getSwiftRValueType(); - if (SILTy.isAddress()) - RealType = CanInOutType::get(RealType); + auto RealType = SILTy.getASTType(); auto DbgTy = DebugTypeInfo::getLocalVariable( CurSILFn->getDeclContext(), CurSILFn->getGenericEnvironment(), Decl, RealType, type, /*Unwrap=*/false); @@ -4227,11 +4243,12 @@ static ExclusivityFlags getExclusivityAction(SILAccessKind kind) { static ExclusivityFlags getExclusivityFlags(SILModule &M, SILAccessKind kind, - bool noNestedConflict) { + bool noNestedConflict, + bool fromBuiltin) { auto flags = getExclusivityAction(kind); // In old Swift compatibility modes, downgrade this to a warning. - if (M.getASTContext().LangOpts.isSwiftVersion3()) + if (!fromBuiltin && M.getASTContext().LangOpts.isSwiftVersion3()) flags |= ExclusivityFlags::WarningOnly; if (!noNestedConflict) @@ -4263,7 +4280,7 @@ static SILAccessEnforcement getEffectiveEnforcement(IRGenFunction &IGF, template static ExclusivityFlags getExclusivityFlags(BeginAccessInst *i) { return getExclusivityFlags(i->getModule(), i->getAccessKind(), - i->hasNoNestedConflict()); + i->hasNoNestedConflict(), i->isFromBuiltin()); } void IRGenSILFunction::visitBeginAccessInst(BeginAccessInst *access) { @@ -4984,9 +5001,7 @@ void IRGenSILFunction::visitKeyPathInst(swift::KeyPathInst *I) { llvm::Value *args; if (!I->getSubstitutions().empty() || !I->getAllOperands().empty()) { auto sig = I->getPattern()->getGenericSignature(); - SubstitutionMap subs; - if (sig) - subs = sig->getSubstitutionMap(I->getSubstitutions()); + SubstitutionMap subs = I->getSubstitutions(); SmallVector requirements; enumerateGenericSignatureRequirements(sig, @@ -5221,8 +5236,7 @@ void IRGenSILFunction::visitOpenExistentialAddrInst(OpenExistentialAddrInst *i) SILType baseTy = i->getOperand()->getType(); Address base = getLoweredAddress(i->getOperand()); - auto openedArchetype = cast( - i->getType().getSwiftRValueType()); + auto openedArchetype = i->getType().castTo(); // Insert a copy of the boxed value for COW semantics if necessary. auto accessKind = i->getAccessKind(); @@ -5236,8 +5250,7 @@ void IRGenSILFunction::visitOpenExistentialRefInst(OpenExistentialRefInst *i) { SILType baseTy = i->getOperand()->getType(); Explosion base = getLoweredExplosion(i->getOperand()); - auto openedArchetype = cast( - i->getType().getSwiftRValueType()); + auto openedArchetype = i->getType().castTo(); Explosion result; llvm::Value *instance @@ -5251,7 +5264,7 @@ void IRGenSILFunction::visitOpenExistentialMetatypeInst( OpenExistentialMetatypeInst *i) { SILType baseTy = i->getOperand()->getType(); Explosion base = getLoweredExplosion(i->getOperand()); - auto openedTy = i->getType().getSwiftRValueType(); + auto openedTy = i->getType().getASTType(); llvm::Value *metatype = emitExistentialMetatypeProjection(*this, base, baseTy, openedTy); @@ -5325,7 +5338,7 @@ void IRGenSILFunction::visitDeallocExistentialBoxInst( void IRGenSILFunction::visitOpenExistentialBoxInst(OpenExistentialBoxInst *i) { Explosion box = getLoweredExplosion(i->getOperand()); - auto openedArchetype = cast(i->getType().getSwiftRValueType()); + auto openedArchetype = i->getType().castTo(); auto addr = emitOpenExistentialBox(*this, box, i->getOperand()->getType(), openedArchetype); @@ -5348,7 +5361,7 @@ IRGenSILFunction::visitProjectExistentialBoxInst(ProjectExistentialBoxInst *i) { Explosion box = getLoweredExplosion(i->getOperand()); auto caddr = emitBoxedExistentialProjection(*this, box, i->getOperand()->getType(), - i->getType().getSwiftRValueType()); + i->getType().getASTType()); setLoweredAddress(i, caddr.getAddress()); } } diff --git a/lib/IRGen/LoadableByAddress.cpp b/lib/IRGen/LoadableByAddress.cpp index 5c44c4f1934d6..2f3ebae0f698f 100644 --- a/lib/IRGen/LoadableByAddress.cpp +++ b/lib/IRGen/LoadableByAddress.cpp @@ -40,6 +40,54 @@ static GenericEnvironment *getGenericEnvironment(CanSILFunctionType loweredTy) { return loweredTy->getGenericSignature().getGenericEnvironment(); } +class LargeSILTypeMapper { +public: + LargeSILTypeMapper() {} + +public: + SILType getNewSILType(GenericEnvironment *GenericEnv, SILType storageType, + irgen::IRGenModule &Mod); + bool shouldTransformResults(GenericEnvironment *env, + CanSILFunctionType fnType, + irgen::IRGenModule &IGM); + bool shouldTransformFunctionType(GenericEnvironment *env, + CanSILFunctionType fnType, + irgen::IRGenModule &IGM); + SILParameterInfo getNewParameter(GenericEnvironment *env, + SILParameterInfo param, + irgen::IRGenModule &IGM); + bool shouldTransformParameter(GenericEnvironment *env, SILParameterInfo param, + irgen::IRGenModule &IGM); + SmallVector getNewParameters(GenericEnvironment *env, + CanSILFunctionType fnType, + irgen::IRGenModule &IGM); + SmallVector getNewYields(GenericEnvironment *env, + CanSILFunctionType fnType, + irgen::IRGenModule &IGM); + SmallVector getNewResults(GenericEnvironment *GenericEnv, + CanSILFunctionType fnType, + irgen::IRGenModule &Mod); + CanSILFunctionType getNewSILFunctionType(GenericEnvironment *env, + CanSILFunctionType fnType, + irgen::IRGenModule &IGM); + SILType getNewOptionalFunctionType(GenericEnvironment *GenericEnv, + SILType storageType, + irgen::IRGenModule &Mod); + SILType getNewTupleType(GenericEnvironment *GenericEnv, + irgen::IRGenModule &Mod, + const SILType &nonOptionalType, + const SILType &storageType); + bool newResultsDiffer(GenericEnvironment *GenericEnv, + ArrayRef origResults, + irgen::IRGenModule &Mod); + bool shouldConvertBBArg(SILArgument *arg, irgen::IRGenModule &Mod); + +private: + // Cache of already computed type transforms + llvm::MapVector, SILType> + oldToNewTypeMap; +}; + /// Utility to determine if this is a large loadable type static bool isLargeLoadableType(GenericEnvironment *GenericEnv, SILType t, irgen::IRGenModule &Mod) { @@ -47,7 +95,7 @@ static bool isLargeLoadableType(GenericEnvironment *GenericEnv, SILType t, return false; } - CanType canType = t.getSwiftRValueType(); + auto canType = t.getASTType(); if (canType->hasTypeParameter()) { assert(GenericEnv && "Expected a GenericEnv"); canType = GenericEnv->mapTypeIntoContext(canType)->getCanonicalType(); @@ -71,29 +119,24 @@ static bool modifiableFunction(CanSILFunctionType funcType) { return true; } -static bool shouldTransformResults(GenericEnvironment *env, - CanSILFunctionType fnType, - irgen::IRGenModule &IGM); - -static bool shouldTransformFunctionType(GenericEnvironment *env, - CanSILFunctionType fnType, - irgen::IRGenModule &IGM); - -static SILParameterInfo getNewParameter(GenericEnvironment *env, - SILParameterInfo param, - irgen::IRGenModule &IGM); - -static bool shouldTransformParameter(GenericEnvironment *env, - SILParameterInfo param, - irgen::IRGenModule &IGM) { - +bool LargeSILTypeMapper::shouldTransformParameter(GenericEnvironment *env, + SILParameterInfo param, + irgen::IRGenModule &IGM) { auto newParam = getNewParameter(env, param, IGM); return (param != newParam); } -static bool shouldTransformFunctionType(GenericEnvironment *env, - CanSILFunctionType fnType, - irgen::IRGenModule &IGM) { +static bool isFuncOrOptionalFuncType(SILType Ty) { + SILType nonOptionalType = Ty; + if (auto optType = Ty.getOptionalObjectType()) { + nonOptionalType = optType; + } + return nonOptionalType.is(); +} + +bool LargeSILTypeMapper::shouldTransformFunctionType(GenericEnvironment *env, + CanSILFunctionType fnType, + irgen::IRGenModule &IGM) { if (shouldTransformResults(env, fnType, IGM)) return true; @@ -131,19 +174,9 @@ static bool containsFunctionSignature(GenericEnvironment *genEnv, return false; } -// Forward declarations - functions depend on each other -static SmallVector -getNewParameters(GenericEnvironment *env, CanSILFunctionType fnType, - irgen::IRGenModule &IGM); -static SmallVector -getNewYields(GenericEnvironment *env, CanSILFunctionType fnType, - irgen::IRGenModule &IGM); -static SILType getNewSILType(GenericEnvironment *GenericEnv, - SILType storageType, irgen::IRGenModule &Mod); - -static bool newResultsDiffer(GenericEnvironment *GenericEnv, - ArrayRef origResults, - irgen::IRGenModule &Mod) { +bool LargeSILTypeMapper::newResultsDiffer(GenericEnvironment *GenericEnv, + ArrayRef origResults, + irgen::IRGenModule &Mod) { SmallVector newResults; for (auto result : origResults) { SILType currResultTy = result.getSILStorageType(); @@ -173,9 +206,10 @@ static bool modNonFuncTypeResultType(GenericEnvironment *genEnv, return false; } -static SmallVector -getNewResults(GenericEnvironment *GenericEnv, - CanSILFunctionType fnType, irgen::IRGenModule &Mod) { +SmallVector +LargeSILTypeMapper::getNewResults(GenericEnvironment *GenericEnv, + CanSILFunctionType fnType, + irgen::IRGenModule &Mod) { // Get new SIL Function results - same as old results UNLESS: // 1) Function type results might have a different signature // 2) Large loadables are replaced by @out version @@ -187,12 +221,11 @@ getNewResults(GenericEnvironment *GenericEnv, // We (currently) only care about function signatures if (containsFunctionSignature(GenericEnv, Mod, currResultTy, newSILType)) { // Case (1) Above - SILResultInfo newResult(newSILType.getSwiftRValueType(), - result.getConvention()); + SILResultInfo newResult(newSILType.getASTType(), result.getConvention()); newResults.push_back(newResult); } else if (modNonFuncTypeResultType(GenericEnv, fnType, Mod)) { // Case (2) Above - SILResultInfo newSILResultInfo(newSILType.getSwiftRValueType(), + SILResultInfo newSILResultInfo(newSILType.getASTType(), ResultConvention::Indirect); newResults.push_back(newSILResultInfo); } else { @@ -202,10 +235,10 @@ getNewResults(GenericEnvironment *GenericEnv, return newResults; } -static CanSILFunctionType -getNewSILFunctionType(GenericEnvironment *env, - CanSILFunctionType fnType, - irgen::IRGenModule &IGM) { +CanSILFunctionType +LargeSILTypeMapper::getNewSILFunctionType(GenericEnvironment *env, + CanSILFunctionType fnType, + irgen::IRGenModule &IGM) { if (!modifiableFunction(fnType)) { return fnType; } @@ -239,9 +272,10 @@ static CanSILFunctionType getInnerFunctionType(SILType storageType) { return CanSILFunctionType(); } -static SILType getNewOptionalFunctionType(GenericEnvironment *GenericEnv, - SILType storageType, - irgen::IRGenModule &Mod) { +SILType +LargeSILTypeMapper::getNewOptionalFunctionType(GenericEnvironment *GenericEnv, + SILType storageType, + irgen::IRGenModule &Mod) { SILType newSILType = storageType; if (auto objectType = storageType.getOptionalObjectType()) { if (auto fnType = objectType.getAs()) { @@ -256,9 +290,9 @@ static SILType getNewOptionalFunctionType(GenericEnvironment *GenericEnv, return newSILType; } -static bool shouldTransformResults(GenericEnvironment *genEnv, - CanSILFunctionType loweredTy, - irgen::IRGenModule &Mod) { +bool LargeSILTypeMapper::shouldTransformResults(GenericEnvironment *genEnv, + CanSILFunctionType loweredTy, + irgen::IRGenModule &Mod) { if (!modifiableFunction(loweredTy)) { return false; } @@ -274,43 +308,47 @@ static bool shouldTransformResults(GenericEnvironment *genEnv, return modNonFuncTypeResultType(genEnv, loweredTy, Mod); } -static bool modResultType(SILFunction *F, irgen::IRGenModule &Mod) { +static bool modResultType(SILFunction *F, irgen::IRGenModule &Mod, + LargeSILTypeMapper &Mapper) { GenericEnvironment *genEnv = F->getGenericEnvironment(); auto loweredTy = F->getLoweredFunctionType(); - return shouldTransformResults(genEnv, loweredTy, Mod); + return Mapper.shouldTransformResults(genEnv, loweredTy, Mod); } static bool shouldTransformYields(GenericEnvironment *genEnv, CanSILFunctionType loweredTy, - irgen::IRGenModule &Mod) { + irgen::IRGenModule &Mod, + LargeSILTypeMapper &Mapper) { if (!modifiableFunction(loweredTy)) { return false; } for (auto &yield : loweredTy->getYields()) { auto yieldStorageType = yield.getSILStorageType(); - auto newYieldStorageType = getNewSILType(genEnv, yieldStorageType, Mod); + auto newYieldStorageType = + Mapper.getNewSILType(genEnv, yieldStorageType, Mod); if (yieldStorageType != newYieldStorageType) return true; } return false; } -static bool modYieldType(SILFunction *F, irgen::IRGenModule &Mod) { +static bool modYieldType(SILFunction *F, irgen::IRGenModule &Mod, + LargeSILTypeMapper &Mapper) { GenericEnvironment *genEnv = F->getGenericEnvironment(); auto loweredTy = F->getLoweredFunctionType(); - return shouldTransformYields(genEnv, loweredTy, Mod); + return shouldTransformYields(genEnv, loweredTy, Mod, Mapper); } -static SILParameterInfo -getNewParameter(GenericEnvironment *env, SILParameterInfo param, - irgen::IRGenModule &IGM) { +SILParameterInfo LargeSILTypeMapper::getNewParameter(GenericEnvironment *env, + SILParameterInfo param, + irgen::IRGenModule &IGM) { SILType storageType = param.getSILStorageType(); SILType newOptFuncType = getNewOptionalFunctionType(env, storageType, IGM); if (newOptFuncType != storageType) { - return param.getWithType(newOptFuncType.getSwiftRValueType()); + return param.getWithType(newOptFuncType.getASTType()); } if (auto paramFnType = storageType.getAs()) { @@ -322,21 +360,22 @@ getNewParameter(GenericEnvironment *env, SILParameterInfo param, } } else if (isLargeLoadableType(env, storageType, IGM)) { if (param.getConvention() == ParameterConvention::Direct_Guaranteed) - return SILParameterInfo(storageType.getSwiftRValueType(), + return SILParameterInfo(storageType.getASTType(), ParameterConvention::Indirect_In_Guaranteed); else - return SILParameterInfo(storageType.getSwiftRValueType(), + return SILParameterInfo(storageType.getASTType(), ParameterConvention::Indirect_In_Constant); } else { auto newType = getNewSILType(env, storageType, IGM); - return SILParameterInfo(newType.getSwiftRValueType(), + return SILParameterInfo(newType.getASTType(), param.getConvention()); } } -static SmallVector -getNewParameters(GenericEnvironment *env, CanSILFunctionType fnType, - irgen::IRGenModule &IGM) { +SmallVector +LargeSILTypeMapper::getNewParameters(GenericEnvironment *env, + CanSILFunctionType fnType, + irgen::IRGenModule &IGM) { SmallVector newParams; for (SILParameterInfo param : fnType->getParameters()) { auto newParam = getNewParameter(env, param, IGM); @@ -345,9 +384,10 @@ getNewParameters(GenericEnvironment *env, CanSILFunctionType fnType, return newParams; } -static SmallVector -getNewYields(GenericEnvironment *env, CanSILFunctionType fnType, - irgen::IRGenModule &IGM) { +SmallVector +LargeSILTypeMapper::getNewYields(GenericEnvironment *env, + CanSILFunctionType fnType, + irgen::IRGenModule &IGM) { SmallVector newYields; for (auto oldYield : fnType->getYields()) { auto newYieldAsParam = getNewParameter(env, oldYield, IGM); @@ -357,10 +397,10 @@ getNewYields(GenericEnvironment *env, CanSILFunctionType fnType, return newYields; } -static SILType getNewTupleType(GenericEnvironment *GenericEnv, - irgen::IRGenModule &Mod, - const SILType &nonOptionalType, - const SILType &storageType) { +SILType LargeSILTypeMapper::getNewTupleType(GenericEnvironment *GenericEnv, + irgen::IRGenModule &Mod, + const SILType &nonOptionalType, + const SILType &storageType) { auto origType = nonOptionalType.getAs(); assert(origType && "Expected a tuple type"); SmallVector newElems; @@ -369,7 +409,7 @@ static SILType getNewTupleType(GenericEnvironment *GenericEnv, auto elem = SILType::getPrimitiveObjectType(origCanType); auto newElem = getNewSILType(GenericEnv, elem, Mod); auto newTupleType = - TupleTypeElt(newElem.getSwiftRValueType(), canElem.getName(), + TupleTypeElt(newElem.getASTType(), canElem.getName(), canElem.getParameterFlags()); newElems.push_back(newTupleType); } @@ -388,8 +428,15 @@ static SILType getNewTupleType(GenericEnvironment *GenericEnv, return newSILType; } -static SILType getNewSILType(GenericEnvironment *GenericEnv, - SILType storageType, irgen::IRGenModule &Mod) { +SILType LargeSILTypeMapper::getNewSILType(GenericEnvironment *GenericEnv, + SILType storageType, + irgen::IRGenModule &Mod) { + // See if the type is already in the cache: + auto typePair = std::make_pair(GenericEnv, storageType); + if (oldToNewTypeMap.find(typePair) != oldToNewTypeMap.end()) { + return oldToNewTypeMap[typePair]; + } + SILType nonOptionalType = storageType; if (auto optType = storageType.getOptionalObjectType()) { nonOptionalType = optType; @@ -397,9 +444,11 @@ static SILType getNewSILType(GenericEnvironment *GenericEnv, if (nonOptionalType.getAs()) { SILType newSILType = getNewTupleType(GenericEnv, Mod, nonOptionalType, storageType); - return isLargeLoadableType(GenericEnv, newSILType, Mod) - ? newSILType.getAddressType() - : newSILType; + auto typeToRet = isLargeLoadableType(GenericEnv, newSILType, Mod) + ? newSILType.getAddressType() + : newSILType; + oldToNewTypeMap[typePair] = typeToRet; + return typeToRet; } SILType newSILType = getNewOptionalFunctionType(GenericEnv, storageType, Mod); if (newSILType != storageType) { @@ -414,6 +463,7 @@ static SILType getNewSILType(GenericEnvironment *GenericEnv, } else if (isLargeLoadableType(GenericEnv, storageType, Mod)) { newSILType = storageType.getAddressType(); } + oldToNewTypeMap[typePair] = newSILType; return newSILType; } @@ -425,6 +475,7 @@ namespace { struct StructLoweringState { SILFunction *F; irgen::IRGenModule &Mod; + LargeSILTypeMapper &Mapper; // All large loadable function arguments that we modified SmallVector largeLoadableArgs; @@ -470,8 +521,9 @@ struct StructLoweringState { // to be modified *only if* the operands are used in "real" instructions SmallVector debugInstsToMod; - StructLoweringState(SILFunction *F, irgen::IRGenModule &Mod) - : F(F), Mod(Mod) {} + StructLoweringState(SILFunction *F, irgen::IRGenModule &Mod, + LargeSILTypeMapper &Mapper) + : F(F), Mod(Mod), Mapper(Mapper) {} }; } // end anonymous namespace @@ -627,6 +679,10 @@ static bool modifiableApply(ApplySite applySite, irgen::IRGenModule &Mod) { if (applySite.getSubstCalleeType()->getLanguage() == SILFunctionLanguage::C) { return false; } + SILValue callee = applySite.getCallee(); + if (auto site = ApplySite::isa(callee)) { + return modifiableApply(site, Mod); + } return true; } @@ -638,7 +694,7 @@ void LargeValueVisitor::visitApply(ApplySite applySite) { for (Operand &operand : applySite.getArgumentOperands()) { SILValue currOperand = operand.get(); SILType silType = currOperand->getType(); - SILType newSilType = getNewSILType(genEnv, silType, pass.Mod); + SILType newSilType = pass.Mapper.getNewSILType(genEnv, silType, pass.Mod); if (silType != newSilType || std::find(pass.largeLoadableArgs.begin(), pass.largeLoadableArgs.end(), currOperand) != pass.largeLoadableArgs.end() || @@ -654,7 +710,8 @@ void LargeValueVisitor::visitApply(ApplySite applySite) { if (auto beginApply = dyn_cast(applySite)) { for (auto yield : beginApply->getYieldedValues()) { auto oldYieldType = yield->getType(); - auto newYieldType = getNewSILType(genEnv, oldYieldType, pass.Mod); + auto newYieldType = + pass.Mapper.getNewSILType(genEnv, oldYieldType, pass.Mod); if (oldYieldType != newYieldType) { pass.applies.push_back(applySite.getInstruction()); return; @@ -664,7 +721,7 @@ void LargeValueVisitor::visitApply(ApplySite applySite) { } SILType currType = applySite.getType(); - SILType newType = getNewSILType(genEnv, currType, pass.Mod); + SILType newType = pass.Mapper.getNewSILType(genEnv, currType, pass.Mod); // We only care about function type results if (!isLargeLoadableType(genEnv, currType, pass.Mod) && (currType != newType)) { @@ -674,8 +731,8 @@ void LargeValueVisitor::visitApply(ApplySite applySite) { // Check callee - need new generic env: CanSILFunctionType origSILFunctionType = applySite.getSubstCalleeType(); GenericEnvironment *genEnvCallee = nullptr; - auto newSILFunctionType = - getNewSILFunctionType(genEnvCallee, origSILFunctionType, pass.Mod); + auto newSILFunctionType = pass.Mapper.getNewSILFunctionType( + genEnvCallee, origSILFunctionType, pass.Mod); if (origSILFunctionType != newSILFunctionType) { pass.applies.push_back(applySite.getInstruction()); } @@ -707,11 +764,11 @@ void LargeValueVisitor::visitMethodInst(MethodInst *instr) { if (fnType->isPolymorphic()) { genEnv = getGenericEnvironment(fnType); } - if (shouldTransformFunctionType(genEnv, fnType, pass.Mod)) { + if (pass.Mapper.shouldTransformFunctionType(genEnv, fnType, pass.Mod)) { pass.methodInstsToMod.push_back(instr); return; } - if (newResultsDiffer(genEnv, fnType->getResults(), pass.Mod)) { + if (pass.Mapper.newResultsDiffer(genEnv, fnType->getResults(), pass.Mod)) { pass.methodInstsToMod.push_back(instr); } } @@ -724,11 +781,12 @@ void LargeValueVisitor::visitStoreInst(StoreInst *instr) { } } -static bool shouldConvertBBArg(SILArgument *arg, irgen::IRGenModule &Mod) { +bool LargeSILTypeMapper::shouldConvertBBArg(SILArgument *arg, + irgen::IRGenModule &Mod) { auto *F = arg->getFunction(); SILType storageType = arg->getType(); GenericEnvironment *genEnv = F->getGenericEnvironment(); - CanType currCanType = storageType.getSwiftRValueType(); + auto currCanType = storageType.getASTType(); if (auto funcType = dyn_cast(currCanType)) { if (funcType->isPolymorphic()) { genEnv = getGenericEnvironment(funcType); @@ -757,10 +815,11 @@ void LargeValueVisitor::visitSwitchEnumInst(SwitchEnumInst *instr) { auto currCase = instr->getCase(i); auto *currBB = currCase.second; for (SILArgument *arg : currBB->getArguments()) { - if (shouldConvertBBArg(arg, pass.Mod)) { + if (pass.Mapper.shouldConvertBBArg(arg, pass.Mod)) { SILType storageType = arg->getType(); auto *genEnv = instr->getFunction()->getGenericEnvironment(); - SILType newSILType = getNewSILType(genEnv, storageType, pass.Mod); + SILType newSILType = + pass.Mapper.getNewSILType(genEnv, storageType, pass.Mod); if (newSILType.isAddress()) { pass.switchEnumInstsToMod.push_back(instr); return; @@ -819,7 +878,7 @@ void LargeValueVisitor::visitDestroyValueInst(DestroyValueInst *instr) { void LargeValueVisitor::visitResultTyInst(SingleValueInstruction *instr) { GenericEnvironment *genEnv = instr->getFunction()->getGenericEnvironment(); SILType currSILType = instr->getType().getObjectType(); - SILType newSILType = getNewSILType(genEnv, currSILType, pass.Mod); + SILType newSILType = pass.Mapper.getNewSILType(genEnv, currSILType, pass.Mod); if (currSILType != newSILType) { pass.resultTyInstsToMod.insert(instr); } @@ -838,7 +897,8 @@ void LargeValueVisitor::visitTupleInst(SingleValueInstruction *instr) { if (!genEnv && funcType->isPolymorphic()) { genEnv = getGenericEnvironment(funcType); } - auto newSILFunctionType = getNewSILFunctionType(genEnv, funcType, pass.Mod); + auto newSILFunctionType = + pass.Mapper.getNewSILFunctionType(genEnv, funcType, pass.Mod); if (funcType != newSILFunctionType) { pass.tupleInstsToMod.push_back(instr); } @@ -867,7 +927,7 @@ static bool modNonFuncTypeResultType(SILFunction *F, irgen::IRGenModule &Mod) { } void LargeValueVisitor::visitReturnInst(ReturnInst *instr) { - if (!modResultType(pass.F, pass.Mod)) { + if (!modResultType(pass.F, pass.Mod, pass.Mapper)) { visitInstr(instr); } else if (modNonFuncTypeResultType(pass.F, pass.Mod)) { pass.modReturnInsts.push_back(instr); @@ -875,7 +935,7 @@ void LargeValueVisitor::visitReturnInst(ReturnInst *instr) { } void LargeValueVisitor::visitYieldInst(YieldInst *instr) { - if (!modYieldType(pass.F, pass.Mod)) { + if (!modYieldType(pass.F, pass.Mod, pass.Mapper)) { visitInstr(instr); } // else: function signature return instructions remain as-is } @@ -1033,7 +1093,8 @@ void LoadableStorageAllocation::replaceLoadWithCopyAddr( } static bool usesContainApplies(LoadInst *unoptimizableLoad, - irgen::IRGenModule &Mod) { + irgen::IRGenModule &Mod, + LargeSILTypeMapper &Mapper) { for (auto *user : unoptimizableLoad->getUses()) { SILInstruction *userIns = user->getUser(); switch (userIns->getKind()) { @@ -1049,7 +1110,7 @@ static bool usesContainApplies(LoadInst *unoptimizableLoad, SILType currType = unoptimizableLoad->getType().getObjectType(); GenericEnvironment *genEnv = unoptimizableLoad->getFunction()->getGenericEnvironment(); - SILType newSILType = getNewSILType(genEnv, currType, Mod); + SILType newSILType = Mapper.getNewSILType(genEnv, currType, Mod); if (currType == newSILType) { break; } @@ -1064,7 +1125,7 @@ static bool usesContainApplies(LoadInst *unoptimizableLoad, void LoadableStorageAllocation::replaceLoadWithCopyAddrForModifiable( LoadInst *unoptimizableLoad) { - if (!usesContainApplies(unoptimizableLoad, pass.Mod)) { + if (!usesContainApplies(unoptimizableLoad, pass.Mod, pass.Mapper)) { return; } SILValue value = unoptimizableLoad->getOperand(); @@ -1104,7 +1165,8 @@ void LoadableStorageAllocation::replaceLoadWithCopyAddrForModifiable( SILType currType = unoptimizableLoad->getType().getObjectType(); GenericEnvironment *genEnv = userIns->getFunction()->getGenericEnvironment(); - SILType newSILType = getNewSILType(genEnv, currType, pass.Mod); + SILType newSILType = + pass.Mapper.getNewSILType(genEnv, currType, pass.Mod); if (currType == newSILType) { break; } @@ -1225,7 +1287,7 @@ void LoadableStorageAllocation::insertIndirectReturnArgs() { auto loweredTy = pass.F->getLoweredFunctionType(); auto singleResult = loweredTy->getSingleResult(); SILType resultStorageType = singleResult.getSILStorageType(); - auto canType = resultStorageType.getSwiftRValueType(); + auto canType = resultStorageType.getASTType(); if (canType->hasTypeParameter()) { assert(genEnv && "Expected a GenericEnv"); canType = genEnv->mapTypeIntoContext(canType)->getCanonicalType(); @@ -1237,7 +1299,7 @@ void LoadableStorageAllocation::insertIndirectReturnArgs() { VarDecl::Specifier::InOut, SourceLoc(), SourceLoc(), ctx.getIdentifier("$return_value"), SourceLoc(), ctx.getIdentifier("$return_value"), - resultStorageType.getSwiftRValueType(), pass.F->getDeclContext()); + resultStorageType.getASTType(), pass.F->getDeclContext()); pass.F->begin()->insertFunctionArgument(0, resultStorageType.getAddressType(), ValueOwnershipKind::Trivial, var); } @@ -1250,7 +1312,8 @@ void LoadableStorageAllocation::convertIndirectFunctionArgs() { for (SILArgument *arg : entry->getArguments()) { SILType storageType = arg->getType(); - SILType newSILType = getNewSILType(genEnv, storageType, pass.Mod); + SILType newSILType = + pass.Mapper.getNewSILType(genEnv, storageType, pass.Mod); if (newSILType != storageType) { ValueOwnershipKind ownership = arg->getOwnershipKind(); arg = replaceArgType(argBuilder, arg, newSILType); @@ -1299,7 +1362,8 @@ void LoadableStorageAllocation::convertApplyResults() { CanSILFunctionType origSILFunctionType = applySite.getSubstCalleeType(); GenericEnvironment *genEnv = nullptr; - if (!shouldTransformResults(genEnv, origSILFunctionType, pass.Mod)) { + if (!pass.Mapper.shouldTransformResults(genEnv, origSILFunctionType, + pass.Mod)) { continue; } auto singleResult = origSILFunctionType->getSingleResult(); @@ -1316,7 +1380,8 @@ void LoadableStorageAllocation::convertApplyResults() { } continue; } - auto newSILType = getNewSILType(genEnv, resultStorageType, pass.Mod); + auto newSILType = + pass.Mapper.getNewSILType(genEnv, resultStorageType, pass.Mod); auto *newVal = allocateForApply(currIns, newSILType.getObjectType()); if (auto apply = dyn_cast(currIns)) { apply->replaceAllUsesWith(newVal); @@ -1344,7 +1409,8 @@ void LoadableStorageAllocation:: for (SILArgument *arg : entry->getArguments()) { SILType storageType = arg->getType(); GenericEnvironment *genEnv = pass.F->getGenericEnvironment(); - SILType newSILType = getNewSILType(genEnv, storageType, pass.Mod); + SILType newSILType = + pass.Mapper.getNewSILType(genEnv, storageType, pass.Mod); if (containsFunctionSignature(genEnv, pass.Mod, storageType, newSILType)) { auto *castInstr = argBuilder.createUncheckedBitCast( RegularLocation(const_cast(arg->getDecl())), arg, @@ -1365,11 +1431,12 @@ void LoadableStorageAllocation::convertIndirectBasicBlockArgs() { } SILBuilderWithScope argBuilder(BB.begin()); for (SILArgument *arg : BB.getArguments()) { - if (!shouldConvertBBArg(arg, pass.Mod)) { + if (!pass.Mapper.shouldConvertBBArg(arg, pass.Mod)) { continue; } SILType storageType = arg->getType(); - SILType newSILType = getNewSILType(genEnv, storageType, pass.Mod); + SILType newSILType = + pass.Mapper.getNewSILType(genEnv, storageType, pass.Mod); convertBBArgType(argBuilder, newSILType, arg); } } @@ -1486,6 +1553,7 @@ class LoadableByAddress : public SILModuleTransform { llvm::SetVector storeToBlockStorageInstrs; llvm::SetVector modApplies; llvm::MapVector allApplyRetToAllocMap; + LargeSILTypeMapper MapperCache; }; } // end anonymous namespace @@ -1591,7 +1659,8 @@ static void allocateAndSetForArgumentOperand(StructLoweringState &pass, } static bool allUsesAreReplaceable(SingleValueInstruction *instr, - irgen::IRGenModule &Mod) { + irgen::IRGenModule &Mod, + LargeSILTypeMapper &Mapper) { bool allUsesAreReplaceable = true; for (auto *user : instr->getUses()) { SILInstruction *userIns = user->getUser(); @@ -1619,7 +1688,7 @@ static bool allUsesAreReplaceable(SingleValueInstruction *instr, SILType currType = instr->getType().getObjectType(); GenericEnvironment *genEnv = instr->getFunction()->getGenericEnvironment(); - SILType newSILType = getNewSILType(genEnv, currType, Mod); + SILType newSILType = Mapper.getNewSILType(genEnv, currType, Mod); if (currType == newSILType) { allUsesAreReplaceable = false; } @@ -1636,7 +1705,8 @@ static bool allUsesAreReplaceable(SingleValueInstruction *instr, return allUsesAreReplaceable; } -static void castTupleInstr(SingleValueInstruction *instr, IRGenModule &Mod) { +static void castTupleInstr(SingleValueInstruction *instr, IRGenModule &Mod, + LargeSILTypeMapper &Mapper) { SILType currSILType = instr->getType(); auto funcType = getInnerFunctionType(currSILType); assert(funcType && "Expected a function Type"); @@ -1644,7 +1714,7 @@ static void castTupleInstr(SingleValueInstruction *instr, IRGenModule &Mod) { if (!genEnv && funcType->isPolymorphic()) { genEnv = getGenericEnvironment(funcType); } - SILType newSILType = getNewSILType(genEnv, currSILType, Mod); + SILType newSILType = Mapper.getNewSILType(genEnv, currSILType, Mod); if (currSILType == newSILType) { return; } @@ -1738,11 +1808,11 @@ static void createResultTyInstrAndLoad(LoadableStorageAllocation &allocator, instr->getParent()->erase(instr); // If the load is of a function type - do not replace it. - if (loadArg->getType().is()) { + if (isFuncOrOptionalFuncType(loadArg->getType())) { return; } - if (allUsesAreReplaceable(loadArg, pass.Mod)) { + if (allUsesAreReplaceable(loadArg, pass.Mod, pass.Mapper)) { allocator.replaceLoadWithCopyAddr(loadArg); } else { allocator.replaceLoadWithCopyAddrForModifiable(loadArg); @@ -1776,7 +1846,8 @@ static void rewriteFunction(StructLoweringState &pass, EnumElementDecl *decl = currCase.first; for (SILArgument *arg : currBB->getArguments()) { SILType storageType = arg->getType(); - SILType newSILType = getNewSILType(genEnv, storageType, pass.Mod); + SILType newSILType = + pass.Mapper.getNewSILType(genEnv, storageType, pass.Mod); if (storageType == newSILType) { newSILType = newSILType.getAddressType(); } @@ -1799,11 +1870,11 @@ static void rewriteFunction(StructLoweringState &pass, loadArg->setOperand(newArg); // If the load is of a function type - do not replace it. - if (loadArg->getType().is()) { + if (isFuncOrOptionalFuncType(loadArg->getType())) { continue; } - if (allUsesAreReplaceable(loadArg, pass.Mod)) { + if (allUsesAreReplaceable(loadArg, pass.Mod, pass.Mapper)) { allocator.replaceLoadWithCopyAddr(loadArg); } else { allocator.replaceLoadWithCopyAddrForModifiable(loadArg); @@ -1839,11 +1910,11 @@ static void rewriteFunction(StructLoweringState &pass, allocateAndSetForArgumentOperand(pass, currOperand, applyInst); } else if (auto *load = dyn_cast(currOperandInstr)) { // If the load is of a function type - do not replace it. - if (load->getType().is()) { + if (isFuncOrOptionalFuncType(load->getType())) { continue; } - if (allUsesAreReplaceable(load, pass.Mod)) { + if (allUsesAreReplaceable(load, pass.Mod, pass.Mapper)) { allocator.replaceLoadWithCopyAddr(load); } else { allocator.replaceLoadWithCopyAddrForModifiable(load); @@ -1877,14 +1948,15 @@ static void rewriteFunction(StructLoweringState &pass, } for (SingleValueInstruction *instr : pass.tupleInstsToMod) { - castTupleInstr(instr, pass.Mod); + castTupleInstr(instr, pass.Mod, pass.Mapper); } while (!pass.allocStackInstsToMod.empty()) { auto *instr = pass.allocStackInstsToMod.pop_back_val(); SILBuilderWithScope allocBuilder(instr); SILType currSILType = instr->getType(); - SILType newSILType = getNewSILType(genEnv, currSILType, pass.Mod); + SILType newSILType = + pass.Mapper.getNewSILType(genEnv, currSILType, pass.Mod); auto *newInstr = allocBuilder.createAllocStack(instr->getLoc(), newSILType, instr->getVarInfo()); instr->replaceAllUsesWith(newInstr); @@ -1895,7 +1967,8 @@ static void rewriteFunction(StructLoweringState &pass, auto *instr = pass.pointerToAddrkInstsToMod.pop_back_val(); SILBuilderWithScope pointerBuilder(instr); SILType currSILType = instr->getType(); - SILType newSILType = getNewSILType(genEnv, currSILType, pass.Mod); + SILType newSILType = + pass.Mapper.getNewSILType(genEnv, currSILType, pass.Mod); auto *newInstr = pointerBuilder.createPointerToAddress( instr->getLoc(), instr->getOperand(), newSILType.getAddressType(), instr->isStrict()); @@ -1969,7 +2042,8 @@ static void rewriteFunction(StructLoweringState &pass, // Update the return type of these instrs // Note: The operand was already updated! SILType currSILType = instr->getType().getObjectType(); - SILType newSILType = getNewSILType(genEnv, currSILType, pass.Mod); + SILType newSILType = + pass.Mapper.getNewSILType(genEnv, currSILType, pass.Mod); SILBuilderWithScope resultTyBuilder(instr); SILLocation Loc = instr->getLoc(); SingleValueInstruction *newInstr = nullptr; @@ -2012,7 +2086,8 @@ static void rewriteFunction(StructLoweringState &pass, auto *convInstr = cast(instr); newInstr = resultTyBuilder.createBeginAccess( Loc, convInstr->getOperand(), convInstr->getAccessKind(), - convInstr->getEnforcement(), convInstr->hasNoNestedConflict()); + convInstr->getEnforcement(), convInstr->hasNoNestedConflict(), + convInstr->isFromBuiltin()); break; } case SILInstructionKind::EnumInst: { @@ -2037,8 +2112,9 @@ static void rewriteFunction(StructLoweringState &pass, if (currSILFunctionType->isPolymorphic()) { genEnvForMethod = getGenericEnvironment(currSILFunctionType); } - SILType newSILType = SILType::getPrimitiveObjectType( - getNewSILFunctionType(genEnvForMethod, currSILFunctionType, pass.Mod)); + SILType newSILType = + SILType::getPrimitiveObjectType(pass.Mapper.getNewSILFunctionType( + genEnvForMethod, currSILFunctionType, pass.Mod)); auto member = instr->getMember(); auto loc = instr->getLoc(); SILBuilderWithScope methodBuilder(instr); @@ -2126,7 +2202,7 @@ static bool rewriteFunctionReturn(StructLoweringState &pass) { auto loweredTy = pass.F->getLoweredFunctionType(); SILFunction *F = pass.F; SILType resultTy = loweredTy->getAllResultsType(); - SILType newSILType = getNewSILType(genEnv, resultTy, pass.Mod); + SILType newSILType = pass.Mapper.getNewSILType(genEnv, resultTy, pass.Mod); // We (currently) only care about function signatures if (isLargeLoadableType(genEnv, resultTy, pass.Mod)) { return true; @@ -2135,7 +2211,7 @@ static bool rewriteFunctionReturn(StructLoweringState &pass) { (resultTy != newSILType)) { assert(loweredTy->getNumResults() == 1 && "Expected a single result"); SILResultInfo origResultInfo = loweredTy->getSingleResult(); - SILResultInfo newSILResultInfo(newSILType.getSwiftRValueType(), + SILResultInfo newSILResultInfo(newSILType.getASTType(), origResultInfo.getConvention()); auto NewTy = SILFunctionType::get( loweredTy->getGenericSignature(), loweredTy->getExtInfo(), @@ -2165,14 +2241,14 @@ void LoadableByAddress::runOnFunction(SILFunction *F) { if (!genEnv && loweredTy->isPolymorphic()) { genEnv = getGenericEnvironment(loweredTy); } - if (shouldTransformFunctionType(genEnv, loweredTy, - *currIRMod)) { + if (MapperCache.shouldTransformFunctionType(genEnv, loweredTy, + *currIRMod)) { modFuncs.insert(F); } return; } - StructLoweringState pass(F, *currIRMod); + StructLoweringState pass(F, *currIRMod, MapperCache); // Rewrite function args and insert allocs. LoadableStorageAllocation allocator(pass); @@ -2209,18 +2285,31 @@ void LoadableByAddress::runOnFunction(SILFunction *F) { static SILValue getOperandTypeWithCastIfNecessary(SILInstruction *containingInstr, SILValue op, - IRGenModule &Mod, SILBuilder &builder) { + IRGenModule &Mod, SILBuilder &builder, + LargeSILTypeMapper &Mapper) { SILType currSILType = op->getType(); - if (auto funcType = currSILType.getAs()) { + SILType nonOptionalType = currSILType; + if (auto optType = currSILType.getOptionalObjectType()) { + nonOptionalType = optType; + } + if (auto funcType = nonOptionalType.getAs()) { GenericEnvironment *genEnv = containingInstr->getFunction()->getGenericEnvironment(); if (!genEnv && funcType->isPolymorphic()) { genEnv = getGenericEnvironment(funcType); } - auto newFnType = getNewSILFunctionType(genEnv, funcType, Mod); + auto newFnType = Mapper.getNewSILFunctionType(genEnv, funcType, Mod); SILType newSILType = SILType::getPrimitiveObjectType(newFnType); + if (nonOptionalType.isAddress()) { + newSILType = newSILType.getAddressType(); + } + if (nonOptionalType != currSILType) { + newSILType = SILType::getOptionalType(newSILType); + } + if (currSILType.isAddress()) { + newSILType = newSILType.getAddressType(); + } if (currSILType.isAddress()) { - newSILType = newSILType.getAddressType(); // we need address for loads if (newSILType != currSILType) { auto castInstr = builder.createUncheckedAddrCast( containingInstr->getLoc(), op, newSILType); @@ -2255,8 +2344,8 @@ void LoadableByAddress::recreateSingleApply(SILInstruction *applyInst) { } CanSILFunctionType origSILFunctionType = applySite.getSubstCalleeType(); GenericEnvironment *genEnv = nullptr; - CanSILFunctionType newSILFunctionType = - getNewSILFunctionType(genEnv, origSILFunctionType, *currIRMod); + CanSILFunctionType newSILFunctionType = MapperCache.getNewSILFunctionType( + genEnv, origSILFunctionType, *currIRMod); SILFunctionConventions newSILFunctionConventions(newSILFunctionType, *getModule()); SmallVector callArgs; @@ -2276,8 +2365,8 @@ void LoadableByAddress::recreateSingleApply(SILInstruction *applyInst) { // Collect arg operands for (Operand &operand : applySite.getArgumentOperands()) { SILValue currOperand = operand.get(); - currOperand = getOperandTypeWithCastIfNecessary(applyInst, currOperand, - *currIRMod, applyBuilder); + currOperand = getOperandTypeWithCastIfNecessary( + applyInst, currOperand, *currIRMod, applyBuilder, MapperCache); callArgs.push_back(currOperand); } // Recreate apply with new operands due to substitution-type cache @@ -2286,7 +2375,7 @@ void LoadableByAddress::recreateSingleApply(SILInstruction *applyInst) { auto *castedApply = cast(applyInst); SILValue newApply = applyBuilder.createApply(castedApply->getLoc(), callee, - applySite.getSubstitutions(), + applySite.getSubstitutionMap(), callArgs, castedApply->isNonThrowing()); castedApply->replaceAllUsesWith(newApply); break; @@ -2295,7 +2384,7 @@ void LoadableByAddress::recreateSingleApply(SILInstruction *applyInst) { auto *castedApply = cast(applyInst); applyBuilder.createTryApply( castedApply->getLoc(), callee, - applySite.getSubstitutions(), callArgs, + applySite.getSubstitutionMap(), callArgs, castedApply->getNormalBB(), castedApply->getErrorBB()); break; } @@ -2303,7 +2392,7 @@ void LoadableByAddress::recreateSingleApply(SILInstruction *applyInst) { auto oldApply = cast(applyInst); auto newApply = applyBuilder.createBeginApply(oldApply->getLoc(), callee, - applySite.getSubstitutions(), callArgs, + applySite.getSubstitutionMap(), callArgs, oldApply->isNonThrowing()); // Use the new token result. @@ -2349,7 +2438,7 @@ void LoadableByAddress::recreateSingleApply(SILInstruction *applyInst) { auto newApply = applyBuilder.createPartialApply(castedApply->getLoc(), callee, - applySite.getSubstitutions(), callArgs, + applySite.getSubstitutionMap(), callArgs, partialApplyConvention); castedApply->replaceAllUsesWith(newApply); break; @@ -2373,8 +2462,8 @@ void LoadableByAddress::recreateLoadInstrs() { // If this is a load of a function for which we changed the return type: // add UncheckedBitCast before the load auto loadOp = loadInstr->getOperand(); - loadOp = getOperandTypeWithCastIfNecessary(loadInstr, loadOp, - *getIRGenModule(), loadBuilder); + loadOp = getOperandTypeWithCastIfNecessary( + loadInstr, loadOp, *getIRGenModule(), loadBuilder, MapperCache); auto *newInstr = loadBuilder.createLoad(loadInstr->getLoc(), loadOp, loadInstr->getOwnershipQualifier()); loadInstr->replaceAllUsesWith(newInstr); @@ -2389,7 +2478,7 @@ void LoadableByAddress::recreateUncheckedEnumDataInstrs() { IRGenModule *currIRMod = getIRGenModule()->IRGen.getGenModule(F); SILType origType = enumInstr->getType(); GenericEnvironment *genEnv = F->getGenericEnvironment(); - SILType newType = getNewSILType(genEnv, origType, *currIRMod); + SILType newType = MapperCache.getNewSILType(genEnv, origType, *currIRMod); auto caseTy = enumInstr->getOperand()->getType().getEnumElementType( enumInstr->getElement(), F->getModule()); SingleValueInstruction *newInstr = nullptr; @@ -2419,7 +2508,7 @@ void LoadableByAddress::recreateUncheckedTakeEnumDataAddrInst() { IRGenModule *currIRMod = getIRGenModule()->IRGen.getGenModule(F); SILType origType = enumInstr->getType(); GenericEnvironment *genEnv = F->getGenericEnvironment(); - SILType newType = getNewSILType(genEnv, origType, *currIRMod); + SILType newType = MapperCache.getNewSILType(genEnv, origType, *currIRMod); auto caseTy = enumInstr->getOperand()->getType().getEnumElementType( enumInstr->getElement(), F->getModule()); SingleValueInstruction *newInstr = nullptr; @@ -2467,8 +2556,8 @@ void LoadableByAddress::recreateConvInstrs() { auto currSILFunctionType = currSILType.castTo(); GenericEnvironment *genEnv = convInstr->getFunction()->getGenericEnvironment(); - CanSILFunctionType newFnType = - getNewSILFunctionType(genEnv, currSILFunctionType, *currIRMod); + CanSILFunctionType newFnType = MapperCache.getNewSILFunctionType( + genEnv, currSILFunctionType, *currIRMod); SILType newType = SILType::getPrimitiveObjectType(newFnType); SILBuilderWithScope convBuilder(convInstr); SingleValueInstruction *newInstr = nullptr; @@ -2481,7 +2570,8 @@ void LoadableByAddress::recreateConvInstrs() { } case SILInstructionKind::ThinFunctionToPointerInst: { auto instr = cast(convInstr); - newType = getNewSILType(genEnv, instr->getType(), *getIRGenModule()); + newType = MapperCache.getNewSILType(genEnv, instr->getType(), + *getIRGenModule()); newInstr = convBuilder.createThinFunctionToPointer( instr->getLoc(), instr->getOperand(), newType); break; @@ -2496,7 +2586,7 @@ void LoadableByAddress::recreateConvInstrs() { auto instr = cast(convInstr); newInstr = convBuilder.createConvertEscapeToNoEscape( instr->getLoc(), instr->getOperand(), newType, - instr->isLifetimeGuaranteed()); + instr->isEscapedByUser(), instr->isLifetimeGuaranteed()); break; } case SILInstructionKind::MarkDependenceInst: { @@ -2520,7 +2610,7 @@ void LoadableByAddress::recreateBuiltinInstrs() { auto *F = builtinInstr->getFunction(); GenericEnvironment *genEnv = F->getGenericEnvironment(); auto resultTy = builtinInstr->getType(); - auto newResultTy = getNewSILType(genEnv, resultTy, *currIRMod); + auto newResultTy = MapperCache.getNewSILType(genEnv, resultTy, *currIRMod); llvm::SmallVector newArgs; for (auto oldArg : builtinInstr->getArguments()) { @@ -2544,7 +2634,8 @@ void LoadableByAddress::updateLoweredTypes(SILFunction *F) { if (!genEnv && funcType->isPolymorphic()) { genEnv = getGenericEnvironment(funcType); } - auto newFuncTy = getNewSILFunctionType(genEnv, funcType, *currIRMod); + auto newFuncTy = + MapperCache.getNewSILFunctionType(genEnv, funcType, *currIRMod); F->rewriteLoweredTypeUnsafe(newFuncTy); } diff --git a/lib/IRGen/LoadableTypeInfo.h b/lib/IRGen/LoadableTypeInfo.h index fdec52c947913..ff022d106b6e6 100644 --- a/lib/IRGen/LoadableTypeInfo.h +++ b/lib/IRGen/LoadableTypeInfo.h @@ -57,7 +57,7 @@ class LoadableTypeInfo : public FixedTypeInfo { const SpareBitVector &spareBits, Alignment align, IsPOD_t pod, IsFixedSize_t alwaysFixedSize, - SpecialTypeInfoKind stik = STIK_Loadable) + SpecialTypeInfoKind stik = SpecialTypeInfoKind::Loadable) : FixedTypeInfo(type, size, spareBits, align, pod, // All currently implemented loadable types are bitwise-takable. IsBitwiseTakable, alwaysFixedSize, stik) { @@ -68,7 +68,7 @@ class LoadableTypeInfo : public FixedTypeInfo { SpareBitVector &&spareBits, Alignment align, IsPOD_t pod, IsFixedSize_t alwaysFixedSize, - SpecialTypeInfoKind stik = STIK_Loadable) + SpecialTypeInfoKind stik = SpecialTypeInfoKind::Loadable) : FixedTypeInfo(type, size, std::move(spareBits), align, pod, // All currently implemented loadable types are bitwise-takable. IsBitwiseTakable, alwaysFixedSize, stik) { diff --git a/lib/IRGen/LocalTypeData.cpp b/lib/IRGen/LocalTypeData.cpp index a7d34c3c61df9..a6bcdb63e7c3a 100644 --- a/lib/IRGen/LocalTypeData.cpp +++ b/lib/IRGen/LocalTypeData.cpp @@ -150,7 +150,7 @@ bool LocalTypeDataCache::AbstractCacheEntry::immediatelySatisfies( MetadataResponse IRGenFunction::tryGetLocalTypeMetadataForLayout(SILType layoutType, DynamicMetadataRequest request){ - auto type = layoutType.getSwiftRValueType(); + auto type = layoutType.getASTType(); // Check under the formal type first. if (type->isLegalFormalType()) { @@ -192,7 +192,7 @@ IRGenFunction::tryGetConcreteLocalTypeData(LocalTypeDataKey key, llvm::Value *IRGenFunction::tryGetLocalTypeDataForLayout(SILType type, LocalTypeDataKind kind) { - return tryGetLocalTypeData(LocalTypeDataKey(type.getSwiftRValueType(), kind)); + return tryGetLocalTypeData(LocalTypeDataKey(type.getASTType(), kind)); } llvm::Value *IRGenFunction::tryGetLocalTypeData(CanType type, @@ -346,7 +346,7 @@ static void maybeEmitDebugInfoForLocalTypeData(IRGenFunction &IGF, void IRGenFunction::setScopedLocalTypeMetadataForLayout(SILType type, MetadataResponse response) { - auto key = LocalTypeDataKey(type.getSwiftRValueType(), + auto key = LocalTypeDataKey(type.getASTType(), LocalTypeDataKind::forRepresentationTypeMetadata()); setScopedLocalTypeData(key, response); } @@ -369,7 +369,7 @@ void IRGenFunction::setScopedLocalTypeDataForLayout(SILType type, LocalTypeDataKind kind, llvm::Value *data) { assert(!kind.isAnyTypeMetadata()); - setScopedLocalTypeData(LocalTypeDataKey(type.getSwiftRValueType(), kind), + setScopedLocalTypeData(LocalTypeDataKey(type.getASTType(), kind), MetadataResponse::forComplete(data)); } diff --git a/lib/IRGen/MetadataLayout.cpp b/lib/IRGen/MetadataLayout.cpp index faf627564415e..bb8eeb8093f13 100644 --- a/lib/IRGen/MetadataLayout.cpp +++ b/lib/IRGen/MetadataLayout.cpp @@ -111,7 +111,7 @@ MetadataLayout &IRGenModule::getMetadataLayout(NominalTypeDecl *decl) { auto &entry = MetadataLayouts[decl]; if (!entry) { if (auto theClass = dyn_cast(decl)) { - if (theClass->isForeign()) + if (theClass->getForeignClassKind() == ClassDecl::ForeignKind::CFType) entry = new ForeignClassMetadataLayout(*this, theClass); else entry = new ClassMetadataLayout(*this, theClass); @@ -248,7 +248,11 @@ Address irgen::emitAddressOfFieldOffsetVector(IRGenFunction &IGF, } }(); - return IGF.emitAddressAtOffset(metadata, offset, IGF.IGM.SizeTy, + auto *elementSize = IGF.IGM.SizeTy; + if (isa(decl)) + elementSize = IGF.IGM.Int32Ty; + + return IGF.emitAddressAtOffset(metadata, offset, elementSize, IGF.IGM.getPointerAlignment()); } @@ -452,7 +456,7 @@ ClassMetadataLayout::getFieldOffsetVectorOffset(IRGenFunction &IGF) const { Size irgen::getClassFieldOffsetOffset(IRGenModule &IGM, ClassDecl *theClass, VarDecl *field) { - if (theClass->isForeign()) + if (theClass->getForeignClassKind() == ClassDecl::ForeignKind::CFType) return Size(); return IGM.getClassMetadataLayout(theClass).getStaticFieldOffset(field); @@ -564,6 +568,10 @@ StructMetadataLayout::StructMetadataLayout(IRGenModule &IGM, StructDecl *decl) super::addFieldOffset(field); } + void noteEndOfFieldOffsets() { + super::noteEndOfFieldOffsets(); + } + void layout() { super::layout(); Layout.TheSize = getMetadataSize(); @@ -594,7 +602,8 @@ StructMetadataLayout::getFieldOffsetVectorOffset() const { ForeignClassMetadataLayout::ForeignClassMetadataLayout(IRGenModule &IGM, ClassDecl *theClass) : MetadataLayout(Kind::ForeignClass), Class(theClass) { - assert(theClass->isForeign() && "Not a foreign class"); + assert(theClass->getForeignClassKind() == ClassDecl::ForeignKind::CFType && + "Not a foreign class"); struct Scanner : LayoutScanner { using super = LayoutScanner; diff --git a/lib/IRGen/MetadataRequest.cpp b/lib/IRGen/MetadataRequest.cpp index aa993fc966d89..dd4410ac91de5 100644 --- a/lib/IRGen/MetadataRequest.cpp +++ b/lib/IRGen/MetadataRequest.cpp @@ -297,18 +297,6 @@ llvm::Constant *IRGenModule::getAddrOfStringForTypeRef( return addr; } -// FIXME: willBeRelativelyAddressed is only needed to work around an ld64 bug -// resolving relative references to coalesceable symbols. -// It should be removed when fixed. rdar://problem/22674524 -llvm::Constant *irgen::getTypeRef(IRGenModule &IGM, CanType type) { - IRGenMangler Mangler; - auto SymbolicName = Mangler.mangleTypeForReflection(IGM, type, - IGM.getSwiftModule(), - /*single-field box*/ false); - - return IGM.getAddrOfStringForTypeRef(SymbolicName); -} - llvm::Value *irgen::emitObjCMetadataRefForMetadata(IRGenFunction &IGF, llvm::Value *classPtr) { assert(IGF.IGM.Context.LangOpts.EnableObjCInterop); @@ -1908,6 +1896,9 @@ namespace { /// not to cache the result as if it were the metadata for a formal type /// unless the type actually cannot possibly be a formal type, e.g. because /// it is one of the special lowered type kinds like SILFunctionType. + /// + /// NOTE: If you modify the special cases in this, you should update + /// isTypeMetadataForLayoutAccessible in SIL.cpp. class EmitTypeMetadataRefForLayout : public CanTypeVisitor { @@ -2058,7 +2049,7 @@ llvm::Value * IRGenFunction::emitTypeMetadataRefForLayout(SILType type, DynamicMetadataRequest request) { assert(request.canResponseStatusBeIgnored()); - return EmitTypeMetadataRefForLayout(*this).visit(type.getSwiftRValueType(), + return EmitTypeMetadataRefForLayout(*this).visit(type.getASTType(), request); } @@ -2157,7 +2148,7 @@ namespace { // to the aggregate's. if (SILType singletonFieldTy = getSingletonAggregateFieldType(IGF.IGM, silTy, ResilienceExpansion::Maximal)) - return visit(singletonFieldTy.getSwiftRValueType(), request); + return visit(singletonFieldTy.getASTType(), request); // If the type is fixed-layout, emit a copy of its layout. if (auto fixed = dyn_cast(&ti)) @@ -2339,7 +2330,7 @@ llvm::Value *irgen::emitTypeLayoutRef(IRGenFunction &IGF, SILType type, DynamicMetadataRequest::getNonBlocking(MetadataState::LayoutComplete, collector); assert(request.canResponseStatusBeIgnored()); - return EmitTypeLayoutRef(IGF).visit(type.getSwiftRValueType(), request); + return EmitTypeLayoutRef(IGF).visit(type.getASTType(), request); } /// Given a class metatype, produce the necessary heap metadata diff --git a/lib/IRGen/MetadataRequest.h b/lib/IRGen/MetadataRequest.h index 86ad7a3d10986..ec1beba276d01 100644 --- a/lib/IRGen/MetadataRequest.h +++ b/lib/IRGen/MetadataRequest.h @@ -503,8 +503,6 @@ emitInPlaceTypeMetadataAccessFunctionBody(IRGenFunction &IGF, llvm::Value *uniqueForeignTypeMetadataRef(IRGenFunction &IGF, llvm::Value *candidate); -llvm::Constant *getTypeRef(IRGenModule &IGM, CanType type); - using LazyCacheEmitter = llvm::function_ref; diff --git a/lib/IRGen/NecessaryBindings.h b/lib/IRGen/NecessaryBindings.h index d1c9e3c867dd7..a7a65ff1c0cbf 100644 --- a/lib/IRGen/NecessaryBindings.h +++ b/lib/IRGen/NecessaryBindings.h @@ -49,7 +49,7 @@ class NecessaryBindings { /// signature. static NecessaryBindings forFunctionInvocations(IRGenModule &IGM, CanSILFunctionType origType, - const SubstitutionMap &subs); + SubstitutionMap subs); /// Add whatever information is necessary to reconstruct type metadata /// for the given type. diff --git a/lib/IRGen/NonFixedTypeInfo.h b/lib/IRGen/NonFixedTypeInfo.h index 1a01a963043b8..43e4e574ab8ea 100644 --- a/lib/IRGen/NonFixedTypeInfo.h +++ b/lib/IRGen/NonFixedTypeInfo.h @@ -52,8 +52,9 @@ class WitnessSizedTypeInfo : public IndirectTypeInfo { const Impl &asImpl() const { return static_cast(*this); } WitnessSizedTypeInfo(llvm::Type *type, Alignment align, IsPOD_t pod, - IsBitwiseTakable_t bt) - : super(type, align, pod, bt, IsNotFixedSize, TypeInfo::STIK_None) {} + IsBitwiseTakable_t bt, IsABIAccessible_t abi) + : super(type, align, pod, bt, IsNotFixedSize, abi, + SpecialTypeInfoKind::None) {} private: /// Bit-cast the given pointer to the right type and assume it as an diff --git a/lib/IRGen/Outlining.cpp b/lib/IRGen/Outlining.cpp index cae28be03c47a..1cb12f71f5411 100644 --- a/lib/IRGen/Outlining.cpp +++ b/lib/IRGen/Outlining.cpp @@ -35,15 +35,19 @@ void OutliningMetadataCollector::collectTypeMetadataForLayout(SILType type) { return; } + auto formalType = type.getASTType(); + if (isa(IGF.IGM.getTypeInfoForLowered(formalType))) { + return; + } + // If the type is a legal formal type, add it as a formal type. // FIXME: does this force us to emit a more expensive metadata than we need // to? - CanType formalType = type.getSwiftRValueType(); if (formalType->isLegalFormalType()) { return collectFormalTypeMetadata(formalType); } - auto key = LocalTypeDataKey(type.getSwiftRValueType(), + auto key = LocalTypeDataKey(type.getASTType(), LocalTypeDataKind::forRepresentationTypeMetadata()); if (Values.count(key)) return; @@ -53,9 +57,7 @@ void OutliningMetadataCollector::collectTypeMetadataForLayout(SILType type) { void OutliningMetadataCollector::collectFormalTypeMetadata(CanType type) { // If the type has no archetypes, we can emit it from scratch in the callee. - if (!type->hasArchetype()) { - return; - } + assert(type->hasArchetype()); auto key = LocalTypeDataKey(type, LocalTypeDataKind::forFormalTypeMetadata()); if (Values.count(key)) return; @@ -98,7 +100,7 @@ void OutliningMetadataCollector::bindMetadataParameters(IRGenFunction &IGF, std::pair irgen::getTypeAndGenericSignatureForManglingOutlineFunction(SILType type) { - auto loweredType = type.getSwiftRValueType(); + auto loweredType = type.getASTType(); if (loweredType->hasArchetype()) { GenericEnvironment *env = nullptr; loweredType.findIf([&env](Type t) -> bool { diff --git a/lib/IRGen/ReferenceTypeInfo.h b/lib/IRGen/ReferenceTypeInfo.h index 816a24096d74c..bf04267b7e094 100644 --- a/lib/IRGen/ReferenceTypeInfo.h +++ b/lib/IRGen/ReferenceTypeInfo.h @@ -35,7 +35,7 @@ class ReferenceTypeInfo : public LoadableTypeInfo { ReferenceTypeInfo(llvm::Type *type, Size size, SpareBitVector spareBits, Alignment align, IsPOD_t pod = IsNotPOD) : LoadableTypeInfo(type, size, spareBits, align, pod, - IsFixedSize, STIK_Reference) + IsFixedSize, SpecialTypeInfoKind::Reference) {} public: @@ -102,7 +102,7 @@ class ReferenceTypeInfo : public LoadableTypeInfo { static bool classof(const ReferenceTypeInfo *type) { return true; } static bool classof(const TypeInfo *type) { - return type->getSpecialTypeInfoKind() == STIK_Reference; + return type->getSpecialTypeInfoKind() == SpecialTypeInfoKind::Reference; } }; diff --git a/lib/IRGen/ResilientTypeInfo.h b/lib/IRGen/ResilientTypeInfo.h index 0be4944f83589..0e495030bc342 100644 --- a/lib/IRGen/ResilientTypeInfo.h +++ b/lib/IRGen/ResilientTypeInfo.h @@ -42,9 +42,10 @@ namespace irgen { template class ResilientTypeInfo : public WitnessSizedTypeInfo { protected: - ResilientTypeInfo(llvm::Type *type) + ResilientTypeInfo(llvm::Type *type, IsABIAccessible_t abiAccessible) : WitnessSizedTypeInfo(type, Alignment(1), - IsNotPOD, IsNotBitwiseTakable) {} + IsNotPOD, IsNotBitwiseTakable, + abiAccessible) {} public: void assignWithCopy(IRGenFunction &IGF, Address dest, Address src, SILType T, @@ -87,13 +88,6 @@ class ResilientTypeInfo : public WitnessSizedTypeInfo { return this->getAddressForPointer(addr); } - Address initializeBufferWithTakeOfBuffer(IRGenFunction &IGF, - Address dest, Address src, - SILType T) const override { - auto addr = emitInitializeBufferWithTakeOfBufferCall(IGF, T, dest, src); - return this->getAddressForPointer(addr); - } - void initializeWithCopy(IRGenFunction &IGF, Address dest, Address src, SILType T, bool isOutlined) const override { emitInitializeWithCopyCall(IGF, T, dest, src); diff --git a/lib/IRGen/StructLayout.cpp b/lib/IRGen/StructLayout.cpp index a71bd64a7b761..de18d75d42627 100644 --- a/lib/IRGen/StructLayout.cpp +++ b/lib/IRGen/StructLayout.cpp @@ -49,7 +49,7 @@ StructLayout::StructLayout(IRGenModule &IGM, CanType astTy, // Fill in the Elements array. for (auto type : types) - Elements.push_back(ElementLayout::getIncomplete(*type)); + Elements.push_back(ElementLayout::getIncomplete(*type, *type)); assert(typeToFill == nullptr || typeToFill->isOpaque()); @@ -114,18 +114,18 @@ void irgen::applyLayoutAttributes(IRGenModule &IGM, return; if (auto alignment = decl->getAttrs().getAttribute()) { - assert(alignment->Value != 0 - && ((alignment->Value - 1) & alignment->Value) == 0 + auto value = alignment->getValue(); + assert(value != 0 && ((value - 1) & value) == 0 && "alignment not a power of two!"); if (!IsFixedLayout) Diags.diagnose(alignment->getLocation(), diag::alignment_dynamic_type_layout_unsupported); - else if (alignment->Value < MinimumAlign.getValue()) + else if (value < MinimumAlign.getValue()) Diags.diagnose(alignment->getLocation(), diag::alignment_less_than_natural, MinimumAlign.getValue()); else - MinimumAlign = Alignment(alignment->Value); + MinimumAlign = Alignment(value); } } @@ -153,7 +153,7 @@ Address ElementLayout::project(IRGenFunction &IGF, Address baseAddr, const llvm::Twine &suffix) const { switch (getKind()) { case Kind::Empty: - return getType().getUndefAddress(); + return getTypeForAccess().getUndefAddress(); case Kind::Fixed: return IGF.Builder.CreateStructGEP(baseAddr, @@ -165,13 +165,13 @@ Address ElementLayout::project(IRGenFunction &IGF, Address baseAddr, assert(offsets.hasValue()); llvm::Value *offset = offsets.getValue()->getOffsetForIndex(IGF, getNonFixedElementIndex()); - return IGF.emitByteOffsetGEP(baseAddr.getAddress(), offset, getType(), + return IGF.emitByteOffsetGEP(baseAddr.getAddress(), offset, getTypeForAccess(), baseAddr.getAddress()->getName() + suffix); } case Kind::InitialNonFixedSize: return IGF.Builder.CreateBitCast(baseAddr, - getType().getStorageType()->getPointerTo(), + getTypeForAccess().getStorageType()->getPointerTo(), baseAddr.getAddress()->getName() + suffix); } llvm_unreachable("bad element layout kind"); @@ -207,7 +207,7 @@ bool StructLayoutBuilder::addFields(llvm::MutableArrayRef elts, bool StructLayoutBuilder::addField(ElementLayout &elt, LayoutStrategy strategy) { - auto &eltTI = elt.getType(); + auto &eltTI = elt.getTypeForLayout(); IsKnownPOD &= eltTI.isPOD(ResilienceExpansion::Maximal); IsKnownBitwiseTakable &= eltTI.isBitwiseTakable(ResilienceExpansion::Maximal); IsKnownAlwaysFixedSize &= eltTI.isFixedSize(ResilienceExpansion::Minimal); @@ -235,7 +235,7 @@ bool StructLayoutBuilder::addField(ElementLayout &elt, } void StructLayoutBuilder::addFixedSizeElement(ElementLayout &elt) { - auto &eltTI = cast(elt.getType()); + auto &eltTI = cast(elt.getTypeForLayout()); // Note that, even in the presence of elements with non-fixed // size, we continue to compute the minimum size and alignment @@ -302,7 +302,7 @@ void StructLayoutBuilder::addNonFixedSizeElement(ElementLayout &elt) { /// Add an empty element to the aggregate. void StructLayoutBuilder::addEmptyElement(ElementLayout &elt) { - elt.completeEmpty(elt.getType().isPOD(ResilienceExpansion::Maximal), + elt.completeEmpty(elt.getTypeForLayout().isPOD(ResilienceExpansion::Maximal), CurSize); } @@ -310,11 +310,11 @@ void StructLayoutBuilder::addEmptyElement(ElementLayout &elt) { /// aggregate. void StructLayoutBuilder::addElementAtFixedOffset(ElementLayout &elt) { assert(isFixedLayout()); - auto &eltTI = cast(elt.getType()); + auto &eltTI = cast(elt.getTypeForLayout()); - elt.completeFixed(elt.getType().isPOD(ResilienceExpansion::Maximal), + elt.completeFixed(elt.getTypeForLayout().isPOD(ResilienceExpansion::Maximal), CurSize, StructFields.size()); - StructFields.push_back(elt.getType().getStorageType()); + StructFields.push_back(elt.getTypeForLayout().getStorageType()); // Carry over the spare bits from the element. CurSpareBits.append(eltTI.getSpareBits()); @@ -323,7 +323,7 @@ void StructLayoutBuilder::addElementAtFixedOffset(ElementLayout &elt) { /// Add an element at a non-fixed offset to the aggregate. void StructLayoutBuilder::addElementAtNonFixedOffset(ElementLayout &elt) { assert(!isFixedLayout()); - elt.completeNonFixed(elt.getType().isPOD(ResilienceExpansion::Maximal), + elt.completeNonFixed(elt.getTypeForLayout().isPOD(ResilienceExpansion::Maximal), NextNonFixedOffsetIndex); CurSpareBits.clear(); } @@ -331,9 +331,9 @@ void StructLayoutBuilder::addElementAtNonFixedOffset(ElementLayout &elt) { /// Add a non-fixed-size element to the aggregate at offset zero. void StructLayoutBuilder::addNonFixedSizeElementAtOffsetZero(ElementLayout &elt) { assert(isFixedLayout()); - assert(!isa(elt.getType())); + assert(!isa(elt.getTypeForLayout())); assert(CurSize.isZero()); - elt.completeInitialNonFixedSize(elt.getType().isPOD(ResilienceExpansion::Maximal)); + elt.completeInitialNonFixedSize(elt.getTypeForLayout().isPOD(ResilienceExpansion::Maximal)); CurSpareBits.clear(); } diff --git a/lib/IRGen/StructLayout.h b/lib/IRGen/StructLayout.h index 047a932645594..e6544ae8fd5a6 100644 --- a/lib/IRGen/StructLayout.h +++ b/lib/IRGen/StructLayout.h @@ -100,8 +100,16 @@ class ElementLayout { private: enum : unsigned { IncompleteKind = 4 }; - /// The swift type information for this element. - const TypeInfo *Type; + /// The swift type information for this element's layout. + const TypeInfo *TypeLayout; + + /// The swift type information for this element's access. + /// + /// Almost always the same as the layout type info, except for classes + /// we have a workaround where we must perform layout as if the type + /// was completely fragile, since the Objective-C runtime does not + /// support classes with an unknown instance size. + const TypeInfo *TypeAccess; /// The offset in bytes from the start of the struct. unsigned ByteOffset; @@ -117,16 +125,18 @@ class ElementLayout { /// The kind of layout performed for this element. unsigned TheKind : 3; - explicit ElementLayout(const TypeInfo &type) - : Type(&type), TheKind(IncompleteKind) {} + explicit ElementLayout(const TypeInfo &typeLayout, + const TypeInfo &typeAccess) + : TypeLayout(&typeLayout), TypeAccess(&typeAccess), TheKind(IncompleteKind) {} bool isCompleted() const { return (TheKind != IncompleteKind); } public: - static ElementLayout getIncomplete(const TypeInfo &type) { - return ElementLayout(type); + static ElementLayout getIncomplete(const TypeInfo &typeLayout, + const TypeInfo &typeAccess) { + return ElementLayout(typeLayout, typeAccess); } void completeFrom(const ElementLayout &other) { @@ -171,7 +181,9 @@ class ElementLayout { Index = nonFixedElementIndex; } - const TypeInfo &getType() const { return *Type; } + const TypeInfo &getTypeForLayout() const { return *TypeLayout; } + + const TypeInfo &getTypeForAccess() const { return *TypeAccess; } Kind getKind() const { assert(isCompleted()); diff --git a/lib/IRGen/StructMetadataVisitor.h b/lib/IRGen/StructMetadataVisitor.h index 16b8123c59df1..3a4fda75c4941 100644 --- a/lib/IRGen/StructMetadataVisitor.h +++ b/lib/IRGen/StructMetadataVisitor.h @@ -59,10 +59,15 @@ template class StructMetadataVisitor asImpl().noteStartOfFieldOffsets(); for (VarDecl *prop : Target->getStoredProperties()) asImpl().addFieldOffset(prop); + + asImpl().noteEndOfFieldOffsets(); } // Note the start of the field offset vector. void noteStartOfFieldOffsets() {} + + // Note the end of the field offset vector. + void noteEndOfFieldOffsets() {} }; /// An "implementation" of StructMetadataVisitor that just scans through @@ -81,17 +86,22 @@ class StructMetadataScanner : public StructMetadataVisitor { void addMetadataFlags() { addPointer(); } void addValueWitnessTable() { addPointer(); } void addNominalTypeDescriptor() { addPointer(); } - void addFieldOffset(VarDecl*) { addPointer(); } + void addFieldOffset(VarDecl *) { addInt32(); } void addGenericArgument(CanType argument) { addPointer(); } void addGenericWitnessTable(CanType argument, ProtocolConformanceRef conf) { addPointer(); } void noteStartOfTypeSpecificMembers() {} + void noteEndOfFieldOffsets() { + NextOffset = NextOffset.roundUpToAlignment(super::IGM.getPointerAlignment()); + } + private: void addPointer() { NextOffset += super::IGM.getPointerSize(); } + void addInt32() { NextOffset += Size(4); } }; } // end namespace irgen diff --git a/lib/IRGen/TypeInfo.h b/lib/IRGen/TypeInfo.h index 36a8d7f008244..abf1b932ee6cf 100644 --- a/lib/IRGen/TypeInfo.h +++ b/lib/IRGen/TypeInfo.h @@ -66,6 +66,24 @@ enum class FixedPacking { Dynamic }; +enum class SpecialTypeInfoKind : uint8_t { + Unimplemented, + + None, + + /// Everything after this is statically fixed-size. + Fixed, + Weak, + + /// Everything after this is loadable. + Loadable, + Reference, + + Last_Kind = Reference +}; +enum : unsigned { NumSpecialTypeInfoKindBits = + countBitsUsed(static_cast(SpecialTypeInfoKind::Last_Kind)) }; + /// Information about the IR representation and generation of the /// given type. class TypeInfo { @@ -73,102 +91,130 @@ class TypeInfo { TypeInfo &operator=(const TypeInfo &) = delete; friend class TypeConverter; - mutable const TypeInfo *NextConverted; protected: - enum SpecialTypeInfoKind { - STIK_Unimplemented, - - STIK_None, - - /// Everything after this is statically fixed-size. - STIK_Fixed, - STIK_Weak, - - /// Everything after this is loadable. - STIK_Loadable, - STIK_Reference, - }; + union { + uint64_t OpaqueBits; + + SWIFT_INLINE_BITFIELD_BASE(TypeInfo, + bitmax(NumSpecialTypeInfoKindBits,8)+6+1+1+3+1+1, + /// The kind of supplemental API this type has, if any. + Kind : bitmax(NumSpecialTypeInfoKindBits,8), + + /// The storage alignment of this type in log2 bytes. + AlignmentShift : 6, + + /// Whether this type is known to be POD. + POD : 1, + + /// Whether this type is known to be bitwise-takable. + BitwiseTakable : 1, + + /// An arbitrary discriminator for the subclass. This is useful for e.g. + /// distinguishing between different TypeInfos that all implement the same + /// kind of type. + /// FIXME -- Create TypeInfoNodes.def and get rid of this field. + SubclassKind : 3, + + /// Whether this type can be assumed to have a fixed size from all + /// resilience domains. + AlwaysFixedSize : 1, + + /// Whether this type is ABI-accessible from this SILModule. + ABIAccessible : 1 + ); + + /// FixedTypeInfo will use the remaining bits for the size. + /// + /// NOTE: Until one can define statically sized inline arrays in the + /// language, defining an extremely large object is quite impractical. + /// For now: "4 GiB should be more than good enough." + SWIFT_INLINE_BITFIELD_FULL(FixedTypeInfo, TypeInfo, 32, + : NumPadBits, + + /// The storage size of this type in bytes. This may be zero even + /// for well-formed and complete types, such as a trivial enum or + /// tuple. + Size : 32 + ); + } Bits; + enum { InvalidSubclassKind = 0x7 }; TypeInfo(llvm::Type *Type, Alignment A, IsPOD_t pod, IsBitwiseTakable_t bitwiseTakable, IsFixedSize_t alwaysFixedSize, - SpecialTypeInfoKind stik) - : NextConverted(0), StorageType(Type), nativeReturnSchema(nullptr), - nativeParameterSchema(nullptr), StorageAlignment(A), - POD(pod), BitwiseTakable(bitwiseTakable), - AlwaysFixedSize(alwaysFixedSize), STIK(stik), - SubclassKind(InvalidSubclassKind) { - assert(STIK >= STIK_Fixed || !AlwaysFixedSize); + IsABIAccessible_t abiAccessible, + SpecialTypeInfoKind stik) : StorageType(Type) { + assert(stik >= SpecialTypeInfoKind::Fixed || !alwaysFixedSize); + assert(!A.isZero() && "Invalid alignment"); + Bits.OpaqueBits = 0; + Bits.TypeInfo.Kind = unsigned(stik); + Bits.TypeInfo.AlignmentShift = llvm::Log2_32(A.getValue()); + Bits.TypeInfo.POD = pod; + Bits.TypeInfo.BitwiseTakable = bitwiseTakable; + Bits.TypeInfo.SubclassKind = InvalidSubclassKind; + Bits.TypeInfo.AlwaysFixedSize = alwaysFixedSize; + Bits.TypeInfo.ABIAccessible = abiAccessible; } /// Change the minimum alignment of a stored value of this type. void setStorageAlignment(Alignment alignment) { - StorageAlignment = alignment; + auto Prev = Bits.TypeInfo.AlignmentShift; + auto Next = llvm::Log2_32(alignment.getValue()); + assert(Next >= Prev && "Alignment can only increase"); + (void)Prev; + Bits.TypeInfo.AlignmentShift = Next; } -public: - virtual ~TypeInfo(); - - /// Unsafely cast this to the given subtype. - template const T &as() const { - // FIXME: maybe do an assert somehow if we have RTTI enabled. - return static_cast(*this); + void setSubclassKind(unsigned kind) { + assert(kind != InvalidSubclassKind); + Bits.TypeInfo.SubclassKind = kind; + assert(Bits.TypeInfo.SubclassKind == kind && "kind was truncated?"); } private: + mutable const TypeInfo *NextConverted = nullptr; + /// The LLVM representation of a stored value of this type. For /// non-fixed types, this is really useful only for forming pointers to it. llvm::Type *StorageType; - mutable NativeConventionSchema *nativeReturnSchema; - mutable NativeConventionSchema *nativeParameterSchema; - - /// The storage alignment of this type in bytes. This is never zero - /// for a completely-converted type. - Alignment StorageAlignment; - - /// Whether this type is known to be POD. - unsigned POD : 1; - - /// Whether this type is known to be bitwise-takable. - unsigned BitwiseTakable : 1; - - /// Whether this type can be assumed to have a fixed size from all - /// resilience domains. - unsigned AlwaysFixedSize : 1; - - /// The kind of supplemental API this type has, if any. - unsigned STIK : 3; + mutable NativeConventionSchema *nativeReturnSchema = nullptr; + mutable NativeConventionSchema *nativeParameterSchema = nullptr; - /// An arbitrary discriminator for the subclass. This is useful for - /// e.g. distinguishing between different TypeInfos that all - /// implement the same kind of type. - unsigned SubclassKind : 3; - enum { InvalidSubclassKind = 0x7 }; +public: + virtual ~TypeInfo(); -protected: - void setSubclassKind(unsigned kind) { - assert(kind != InvalidSubclassKind); - SubclassKind = kind; - assert(SubclassKind == kind && "kind was truncated?"); + /// Unsafely cast this to the given subtype. + template const T &as() const { + // FIXME: maybe do an assert somehow if we have RTTI enabled. + return static_cast(*this); } -public: - /// Whether this type info has been completely converted. - bool isComplete() const { return !StorageAlignment.isZero(); } - /// Whether this type is known to be empty. bool isKnownEmpty(ResilienceExpansion expansion) const; + /// Whether this type is known to be ABI-accessible, i.e. whether it's + /// actually possible to do ABI operations on it from this current SILModule. + /// See SILModule::isTypeABIAccessible. + /// + /// All fixed-size types are currently ABI-accessible, although this would + /// not be difficult to change (e.g. if we had an archetype size constraint + /// that didn't say anything about triviality). + IsABIAccessible_t isABIAccessible() const { + return IsABIAccessible_t(Bits.TypeInfo.ABIAccessible); + } + /// Whether this type is known to be POD, i.e. to not require any /// particular action on copy or destroy. - IsPOD_t isPOD(ResilienceExpansion expansion) const { return IsPOD_t(POD); } + IsPOD_t isPOD(ResilienceExpansion expansion) const { + return IsPOD_t(Bits.TypeInfo.POD); + } /// Whether this type is known to be bitwise-takable, i.e. "initializeWithTake" /// is equivalent to a memcpy. IsBitwiseTakable_t isBitwiseTakable(ResilienceExpansion expansion) const { - return IsBitwiseTakable_t(BitwiseTakable); + return IsBitwiseTakable_t(Bits.TypeInfo.BitwiseTakable); } /// Returns the type of special interface followed by this TypeInfo. @@ -178,7 +224,7 @@ class TypeInfo { /// properties on their parameter types, but then the program /// can rely on them. SpecialTypeInfoKind getSpecialTypeInfoKind() const { - return SpecialTypeInfoKind(STIK); + return SpecialTypeInfoKind(Bits.TypeInfo.Kind); } /// Returns whatever arbitrary data has been stash in the subclass @@ -186,16 +232,16 @@ class TypeInfo { /// distinguishing between TypeInfos, which is useful when multiple /// TypeInfo subclasses are used to implement the same kind of type. unsigned getSubclassKind() const { - assert(SubclassKind != InvalidSubclassKind && + assert(Bits.TypeInfo.SubclassKind != InvalidSubclassKind && "subclass kind has not been initialized!"); - return SubclassKind; + return Bits.TypeInfo.SubclassKind; } /// Whether this type is known to be fixed-size in the local /// resilience domain. If true, this TypeInfo can be cast to /// FixedTypeInfo. IsFixedSize_t isFixedSize() const { - return IsFixedSize_t(STIK >= STIK_Fixed); + return IsFixedSize_t(getSpecialTypeInfoKind() >= SpecialTypeInfoKind::Fixed); } /// Whether this type is known to be fixed-size in the given @@ -207,9 +253,9 @@ class TypeInfo { case ResilienceExpansion::Minimal: // We can't be universally fixed size if we're not locally // fixed size. - assert((isFixedSize() || AlwaysFixedSize == IsNotFixedSize) && + assert((isFixedSize() || Bits.TypeInfo.AlwaysFixedSize == IsNotFixedSize) && "IsFixedSize vs IsAlwaysFixedSize mismatch"); - return IsFixedSize_t(AlwaysFixedSize); + return IsFixedSize_t(Bits.TypeInfo.AlwaysFixedSize); } llvm_unreachable("Not a valid ResilienceExpansion."); @@ -219,13 +265,14 @@ class TypeInfo { /// resilience domain. If true, this TypeInfo can be cast to /// LoadableTypeInfo. IsLoadable_t isLoadable() const { - return IsLoadable_t(STIK >= STIK_Loadable); + return IsLoadable_t(getSpecialTypeInfoKind() >= SpecialTypeInfoKind::Loadable); } llvm::Type *getStorageType() const { return StorageType; } Alignment getBestKnownAlignment() const { - return StorageAlignment; + auto Shift = Bits.TypeInfo.AlignmentShift; + return Alignment(1ull << Shift); } /// Given a generic pointer to this type, produce an Address for it. @@ -323,23 +370,6 @@ class TypeInfo { Address srcBuffer, SILType T) const; - /// Perform a take-initialization from the given fixed-size buffer - /// into an uninitialized fixed-size buffer, allocating the buffer if - /// necessary and deallocating the destination buffer. Returns the - /// address of the value inside the destination buffer. - /// - /// This is equivalent to: - /// auto srcAddress = projectBuffer(IGF, srcBuffer, T); - /// initializeBufferWithTake(IGF, destBuffer, srcAddress, T); - /// deallocateBuffer(IGF, srcBuffer, T); - /// but may be able to re-use the buffer from the source buffer, and may - /// be more efficient for dynamic types, since it uses a single - /// value witness call. - virtual Address initializeBufferWithTakeOfBuffer(IRGenFunction &IGF, - Address destBuffer, - Address srcBuffer, - SILType T) const; - /// Take-initialize an address from a parameter explosion. virtual void initializeFromParams(IRGenFunction &IGF, Explosion ¶ms, Address src, SILType T, @@ -405,6 +435,9 @@ class TypeInfo { SILType T) const = 0; /// Compute the packing of values of this type into a fixed-size buffer. + /// A value might not be stored in the fixed-size buffer because it does not + /// fit or because it is not bit-wise takable. Non bit-wise takable values are + /// not stored inline by convention. FixedPacking getFixedPacking(IRGenModule &IGM) const; /// Index into an array of objects of this type. diff --git a/lib/IRGen/WeakTypeInfo.h b/lib/IRGen/WeakTypeInfo.h index d7eef1edc5d3d..906f264267d65 100644 --- a/lib/IRGen/WeakTypeInfo.h +++ b/lib/IRGen/WeakTypeInfo.h @@ -30,12 +30,14 @@ class WeakTypeInfo : public FixedTypeInfo { WeakTypeInfo(llvm::Type *type, Size size, Alignment align, const SpareBitVector &spareBits) : FixedTypeInfo(type, size, spareBits, align, IsNotPOD, - IsNotBitwiseTakable, IsFixedSize, STIK_Weak) {} + IsNotBitwiseTakable, IsFixedSize, + SpecialTypeInfoKind::Weak) {} WeakTypeInfo(llvm::Type *type, Size size, Alignment align, SpareBitVector &&spareBits) : FixedTypeInfo(type, size, std::move(spareBits), align, IsNotPOD, - IsNotBitwiseTakable, IsFixedSize, STIK_Weak) {} + IsNotBitwiseTakable, IsFixedSize, + SpecialTypeInfoKind::Weak) {} public: virtual void weakLoadStrong(IRGenFunction &IGF, Address addr, @@ -49,7 +51,7 @@ class WeakTypeInfo : public FixedTypeInfo { static bool classof(const WeakTypeInfo *type) { return true; } static bool classof(const TypeInfo *type) { - return type->getSpecialTypeInfoKind() == STIK_Weak; + return type->getSpecialTypeInfoKind() == SpecialTypeInfoKind::Weak; } }; diff --git a/lib/Immediate/Immediate.cpp b/lib/Immediate/Immediate.cpp index 2fdf64c7806ff..751e059102b44 100644 --- a/lib/Immediate/Immediate.cpp +++ b/lib/Immediate/Immediate.cpp @@ -27,6 +27,7 @@ #include "swift/Basic/LLVM.h" #include "swift/Basic/LLVMContext.h" #include "swift/Frontend/Frontend.h" +#include "swift/IRGen/IRGenPublic.h" #include "swift/SILOptimizer/PassManager/Passes.h" #include "llvm/ADT/SmallString.h" #include "llvm/Config/config.h" @@ -204,11 +205,9 @@ bool swift::immediate::linkLLVMModules(llvm::Module *Module, } bool swift::immediate::IRGenImportedModules( - CompilerInstance &CI, - llvm::Module &Module, - llvm::SmallPtrSet &ImportedModules, - SmallVectorImpl &InitFns, - IRGenOptions &IRGenOpts, + CompilerInstance &CI, llvm::Module &Module, + llvm::SmallPtrSetImpl &ImportedModules, + SmallVectorImpl &InitFns, IRGenOptions &IRGenOpts, const SILOptions &SILOpts) { swift::ModuleDecl *M = CI.getMainModule(); @@ -218,20 +217,7 @@ bool swift::immediate::IRGenImportedModules( AllLinkLibraries.push_back(linkLib); }; - M->forAllVisibleModules({}, /*includePrivateTopLevelImports=*/true, - [&](ModuleDecl::ImportedModule import) { - import.second->collectLinkLibraries(addLinkLibrary); - }); - - // Hack to handle thunks eagerly synthesized by the Clang importer. - swift::ModuleDecl *prev = nullptr; - for (auto external : CI.getASTContext().ExternalDefinitions) { - swift::ModuleDecl *next = external->getModuleContext(); - if (next == prev) - continue; - next->collectLinkLibraries(addLinkLibrary); - prev = next; - } + M->collectLinkLibraries(addLinkLibrary); tryLoadLibraries(AllLinkLibraries, CI.getASTContext().SearchPathOpts, CI.getDiags()); @@ -253,7 +239,6 @@ bool swift::immediate::IRGenImportedModules( std::unique_ptr SILMod = performSILGeneration(import, CI.getSILOptions()); - performSILLinking(SILMod.get()); if (runSILDiagnosticPasses(*SILMod)) { hadError = true; break; diff --git a/lib/Immediate/ImmediateImpl.h b/lib/Immediate/ImmediateImpl.h index 6fe51be38c5ad..9ca786fafe1ee 100644 --- a/lib/Immediate/ImmediateImpl.h +++ b/lib/Immediate/ImmediateImpl.h @@ -45,11 +45,9 @@ bool tryLoadLibraries(ArrayRef LinkLibraries, bool linkLLVMModules(llvm::Module *Module, std::unique_ptr SubModule); bool IRGenImportedModules( - CompilerInstance &CI, - llvm::Module &Module, - llvm::SmallPtrSet &ImportedModules, - SmallVectorImpl &InitFns, - IRGenOptions &IRGenOpts, + CompilerInstance &CI, llvm::Module &Module, + llvm::SmallPtrSetImpl &ImportedModules, + SmallVectorImpl &InitFns, IRGenOptions &IRGenOpts, const SILOptions &SILOpts); } // end namespace immediate diff --git a/lib/Immediate/REPL.cpp b/lib/Immediate/REPL.cpp index 401baf511b425..3f0abd0650712 100644 --- a/lib/Immediate/REPL.cpp +++ b/lib/Immediate/REPL.cpp @@ -869,7 +869,6 @@ class REPLEnvironment { if (!CI.getASTContext().hadError()) { sil = performSILGeneration(REPLInputFile, CI.getSILOptions(), RC.CurIRGenElem); - performSILLinking(sil.get()); runSILDiagnosticPasses(*sil); runSILLoweringPasses(*sil); } diff --git a/lib/Index/Index.cpp b/lib/Index/Index.cpp index 44b2391a53c96..9ae73e2e20d56 100644 --- a/lib/Index/Index.cpp +++ b/lib/Index/Index.cpp @@ -245,7 +245,7 @@ class IndexSwiftASTWalker : public SourceEntityWalker { private: bool visitImports(SourceFileOrModule Mod, - llvm::SmallPtrSet &Visited); + llvm::SmallPtrSetImpl &Visited); bool handleSourceOrModuleFile(SourceFileOrModule SFOrMod, StringRef KnownHash, bool &HashIsKnown); @@ -464,8 +464,9 @@ class IndexSwiftASTWalker : public SourceEntityWalker { SourceFileOrModule SFOrMod); void getRecursiveModuleImports(ModuleDecl &Mod, SmallVectorImpl &Imports); - void collectRecursiveModuleImports(ModuleDecl &Mod, - llvm::SmallPtrSet &Visited); + void + collectRecursiveModuleImports(ModuleDecl &Mod, + llvm::SmallPtrSetImpl &Visited); template void warn(F log) { @@ -540,7 +541,7 @@ bool IndexSwiftASTWalker::handleSourceOrModuleFile(SourceFileOrModule SFOrMod, } bool IndexSwiftASTWalker::visitImports( - SourceFileOrModule TopMod, llvm::SmallPtrSet &Visited) { + SourceFileOrModule TopMod, llvm::SmallPtrSetImpl &Visited) { // Dependencies of the stdlib module (like SwiftShims module) are // implementation details. if (TopMod.getModule().isStdlibModule()) @@ -1329,7 +1330,7 @@ void IndexSwiftASTWalker::getRecursiveModuleImports( } void IndexSwiftASTWalker::collectRecursiveModuleImports( - ModuleDecl &TopMod, llvm::SmallPtrSet &Visited) { + ModuleDecl &TopMod, llvm::SmallPtrSetImpl &Visited) { bool IsNew = Visited.insert(&TopMod).second; if (!IsNew) diff --git a/lib/Markup/LineList.cpp b/lib/Markup/LineList.cpp index 00b5de99b9fa6..0afeb5eec995d 100644 --- a/lib/Markup/LineList.cpp +++ b/lib/Markup/LineList.cpp @@ -25,7 +25,7 @@ std::string LineList::str() const { return ""; auto FirstLine = Lines.begin(); - while (FirstLine->Text.empty() && FirstLine != Lines.end()) + while (FirstLine != Lines.end() && FirstLine->Text.empty()) ++FirstLine; if (FirstLine == Lines.end()) diff --git a/lib/Migrator/APIDiffMigratorPass.cpp b/lib/Migrator/APIDiffMigratorPass.cpp index e886b906106ca..957ee92ef3d94 100644 --- a/lib/Migrator/APIDiffMigratorPass.cpp +++ b/lib/Migrator/APIDiffMigratorPass.cpp @@ -28,6 +28,7 @@ #include "clang/Rewrite/Core/RewriteBuffer.h" #include "llvm/Support/FileSystem.h" #include "swift/IDE/APIDigesterData.h" +#include "swift/Basic/Defer.h" using namespace swift; using namespace swift::migrator; @@ -47,13 +48,14 @@ struct FoundResult { class ChildIndexFinder : public TypeReprVisitor { ArrayRef ChildIndices; bool ParentIsOptional; + bool IsFunctionTypeArgument; public: - ChildIndexFinder(ArrayRef ChildIndices) : - ChildIndices(ChildIndices) {} + ChildIndexFinder(ArrayRef ChildIndices) + : ChildIndices(ChildIndices), ParentIsOptional(false), + IsFunctionTypeArgument(false) {} FoundResult findChild(AbstractFunctionDecl *Parent) { - ParentIsOptional = false; auto NextIndex = consumeNext(); if (!NextIndex) { if (auto Func = dyn_cast(Parent)) @@ -118,12 +120,16 @@ class ChildIndexFinder : public TypeReprVisitor { Parent->getSourceRange(), Optional, Suffixable, /*Suffixed=*/ParentIsOptional }; + auto NextIndex = consumeNext(); if (isUserTypeAlias(Parent)) return {SourceRange(), false, false, false}; + assert(NextIndex < Children.size()); TypeRepr *Child = Children[NextIndex]; ParentIsOptional = Optional; + IsFunctionTypeArgument = NextIndex == 1 && isa(Parent); + return visit(Child); } @@ -173,12 +179,14 @@ class ChildIndexFinder : public TypeReprVisitor { } FoundResult visitTupleTypeRepr(TupleTypeRepr *T) { - // Single element TupleTypeReprs may be arbitrarily nested so don't count - // as their own index level - if (T->getNumElements() == 1) { + // Paren TupleTypeReprs may be arbitrarily nested so don't count as their + // own index level except in the case of function type argument parens + if (T->isParenType() && !IsFunctionTypeArgument) { ParentIsOptional = false; return visit(T->getElementType(0)); } + + IsFunctionTypeArgument = false; llvm::SmallVector Children; T->getElementTypes(Children); return handleParent(T, ArrayRef(Children)); @@ -227,10 +235,31 @@ class ChildIndexFinder : public TypeReprVisitor { } }; +static ValueDecl* getReferencedDecl(Expr *E) { + // Get the syntactic expression out of an implicit expression. + if (auto *ICE = dyn_cast(E)) + E = ICE->getSyntacticSubExpr(); + if (auto *DRE = dyn_cast(E)) { + return DRE->getDecl(); + } else if (auto *MRE = dyn_cast(E)) { + return MRE->getMember().getDecl(); + } else if (auto OtherCtorE = dyn_cast(E)) { + return OtherCtorE->getDecl(); + } else { + return nullptr; + } +} + struct APIDiffMigratorPass : public ASTMigratorPass, public SourceEntityWalker { APIDiffItemStore DiffStore; + bool isNilExpr(Expr *E) { + auto Range = E->getSourceRange(); + return Range.isValid() && Lexer::getCharSourceRangeFromSourceRange( + SF->getASTContext().SourceMgr, Range).str() == "nil"; + } + std::vector getRelatedDiffItems(ValueDecl *VD) { std::vector results; auto addDiffItems = [&](ValueDecl *VD) { @@ -250,22 +279,30 @@ struct APIDiffMigratorPass : public ASTMigratorPass, public SourceEntityWalker { return results; } - DeclNameViewer getFuncRename(ValueDecl *VD, bool &IgnoreBase) { + DeclNameViewer getFuncRename(ValueDecl *VD, llvm::SmallString<32> &Buffer, + bool &IgnoreBase) { for (auto *Item: getRelatedDiffItems(VD)) { if (auto *CI = dyn_cast(Item)) { if (CI->isRename()) { IgnoreBase = true; switch(CI->NodeKind) { - case SDKNodeKind::Function: + case SDKNodeKind::DeclFunction: IgnoreBase = false; LLVM_FALLTHROUGH; - case SDKNodeKind::Constructor: + case SDKNodeKind::DeclConstructor: return DeclNameViewer(CI->getNewName()); default: return DeclNameViewer(); } } } + if (auto *MI = dyn_cast(Item)) { + if (MI->Subkind == TypeMemberDiffItemSubKind::FuncRename) { + llvm::raw_svector_ostream OS(Buffer); + OS << MI->newTypeName << "." << MI->newPrintedName; + return DeclNameViewer(OS.str()); + } + } } return DeclNameViewer(); } @@ -282,7 +319,8 @@ struct APIDiffMigratorPass : public ASTMigratorPass, public SourceEntityWalker { // Simple rename. if (auto CI = dyn_cast(Item)) { - if (CI->NodeKind == SDKNodeKind::Var && CI->isRename()) { + if (CI->isRename() && (CI->NodeKind == SDKNodeKind::DeclVar || + CI->NodeKind == SDKNodeKind::DeclType)) { Text = CI->getNewName(); return true; } @@ -290,9 +328,20 @@ struct APIDiffMigratorPass : public ASTMigratorPass, public SourceEntityWalker { return false; } + std::set InsertedFunctions; + SourceLoc FileEndLoc; APIDiffMigratorPass(EditorAdapter &Editor, SourceFile *SF, - const MigratorOptions &Opts) - : ASTMigratorPass(Editor, SF, Opts) {} + const MigratorOptions &Opts): + ASTMigratorPass(Editor, SF, Opts), + FileEndLoc(SM.getRangeForBuffer(BufferID).getEnd()) { + SmallVector TopDecls; + SF->getTopLevelDecls(TopDecls); + for (auto *D: TopDecls) { + if (auto *FD = dyn_cast(D)) { + InsertedFunctions.insert(FD->getBaseName().getIdentifier().str()); + } + } + } void run() { if (Opts.APIDigesterDataStorePaths.empty()) @@ -303,10 +352,28 @@ struct APIDiffMigratorPass : public ASTMigratorPass, public SourceEntityWalker { walk(SF); } + bool updateStringRepresentableDeclRef(APIDiffItem *Diff, + CharSourceRange Range) { + auto *CD = dyn_cast(Diff); + if (!CD) + return false; + if (CD->NodeKind != SDKNodeKind::DeclVar) + return false; + if (!CD->isStringRepresentableChange()) + return false; + switch(CD->DiffKind) { + case NodeAnnotation::SimpleStringRepresentableUpdate: + Editor.insert(Range.getEnd(), ".rawValue"); + return true; + default: + return false; + } + } + bool visitDeclReference(ValueDecl *D, CharSourceRange Range, TypeDecl *CtorTyRef, ExtensionDecl *ExtTyRef, Type T, ReferenceMetaData Data) override { - for (auto *Item: getRelatedDiffItems(D)) { + for (auto *Item: getRelatedDiffItems(CtorTyRef ? CtorTyRef: D)) { std::string RepText; if (isSimpleReplacement(Item, RepText)) { Editor.replace(Range, RepText); @@ -367,7 +434,8 @@ struct APIDiffMigratorPass : public ASTMigratorPass, public SourceEntityWalker { void handleFuncRename(ValueDecl *FD, Expr* FuncRefContainer, Expr *Arg) { bool IgnoreBase = false; - if (auto View = getFuncRename(FD, IgnoreBase)) { + llvm::SmallString<32> Buffer; + if (auto View = getFuncRename(FD, Buffer, IgnoreBase)) { if (!IgnoreBase) { ReferenceCollector Walker(FD); Walker.walk(FuncRefContainer); @@ -529,7 +597,8 @@ struct APIDiffMigratorPass : public ASTMigratorPass, public SourceEntityWalker { if (!Item) return false; if (Item->Subkind == TypeMemberDiffItemSubKind::SimpleReplacement || - Item->Subkind == TypeMemberDiffItemSubKind::QualifiedReplacement) + Item->Subkind == TypeMemberDiffItemSubKind::QualifiedReplacement || + Item->Subkind == TypeMemberDiffItemSubKind::FuncRename) return false; if (Item->Subkind == TypeMemberDiffItemSubKind::GlobalFuncToStaticProperty) { @@ -578,6 +647,7 @@ struct APIDiffMigratorPass : public ASTMigratorPass, public SourceEntityWalker { } switch (Item->Subkind) { + case TypeMemberDiffItemSubKind::FuncRename: case TypeMemberDiffItemSubKind::GlobalFuncToStaticProperty: case TypeMemberDiffItemSubKind::SimpleReplacement: case TypeMemberDiffItemSubKind::QualifiedReplacement: @@ -640,9 +710,195 @@ struct APIDiffMigratorPass : public ASTMigratorPass, public SourceEntityWalker { } } + void replaceExpr(Expr* E, StringRef Text) { + Editor.replace(CharSourceRange(SM, E->getStartLoc(), + Lexer::getLocForEndOfToken(SM, E->getEndLoc())), Text); + } + + bool wrapAttributeReference(Expr* Reference, Expr* WrapperTarget, + bool FromString) { + auto *RD = getReferencedDecl(Reference); + if (!RD) + return false; + std::string Rename; + Optional Kind; + StringRef LeftComment; + StringRef RightComment; + for (auto *Item: getRelatedDiffItems(RD)) { + if (isSimpleReplacement(Item, Rename)) { + } else if (auto *CI = dyn_cast(Item)) { + if (CI->isStringRepresentableChange() && + CI->NodeKind == SDKNodeKind::DeclVar) { + Kind = CI->DiffKind; + LeftComment = CI->LeftComment; + RightComment = CI->RightComment; + } + } + } + if (!Kind.hasValue()) + return false; + if (Kind && !isNilExpr(WrapperTarget)) { + SmallString<256> Buffer; + auto Func = insertHelperFunction(*Kind, LeftComment, RightComment, Buffer, + FromString); + Editor.insert(WrapperTarget->getStartLoc(), (Twine(Func) + "(").str()); + Editor.insertAfterToken(WrapperTarget->getEndLoc(), ")"); + } + if (!Rename.empty()) { + replaceExpr(Reference, Rename); + } + return true; + } + + bool handleAssignDestMigration(Expr *E) { + Editor.disableCache(); + SWIFT_DEFER { Editor.enableCache(); }; + auto *ASE = dyn_cast(E); + if (!ASE || !ASE->getDest() || !ASE->getSrc()) + return false; + auto *Dest = ASE->getDest(); + auto Src = ASE->getSrc(); + if (wrapAttributeReference(Dest, Src, true)) { + // We should handle the assignment source here since we won't visit + // the children with present changes. + handleAttributeReference(Src); + return true; + } + return false; + } + + bool handleAttributeReference(Expr *E) { + return wrapAttributeReference(E, E, false); + } + + StringRef insertHelperFunction(NodeAnnotation Anno, StringRef RawType, + StringRef NewType, + SmallString<256> &Buffer, bool FromString) { + llvm::raw_svector_ostream OS(Buffer); + OS << "\n"; + OS << "// Helper function inserted by Swift 4.2 migrator.\n"; + OS << "fileprivate func "; + unsigned FuncNameStart = Buffer.size(); + OS << (FromString ? "convertTo" : "convertFrom"); + SmallVector Segs; + StringRef guard = "\tguard let input = input else { return nil }\n"; + switch(Anno) { + case NodeAnnotation::OptionalArrayMemberUpdate: + Segs = {"Optional", "Array", (Twine("[") + RawType + "]?").str()}; + Segs.push_back((Twine("[") + NewType +"]?").str()); + Segs.push_back((Twine(guard) + "\treturn input.map { key in " + NewType +"(key) }").str()); + Segs.push_back((Twine(guard) + "\treturn input.map { key in key.rawValue }").str()); + break; + case NodeAnnotation::OptionalDictionaryKeyUpdate: + Segs = {"Optional", "Dictionary", (Twine("[") + RawType + ": Any]?").str()}; + Segs.push_back((Twine("[") + NewType +": Any]?").str()); + Segs.push_back((Twine(guard) + + "\treturn Dictionary(uniqueKeysWithValues: input.map" + " { key, value in (" + NewType + "(rawValue: key), value)})").str()); + Segs.push_back((Twine(guard) + + "\treturn Dictionary(uniqueKeysWithValues: input.map" + " {key, value in (key.rawValue, value)})").str()); + break; + case NodeAnnotation::ArrayMemberUpdate: + Segs = {"", "Array", (Twine("[") + RawType + "]").str()}; + Segs.push_back((Twine("[") + NewType +"]").str()); + Segs.push_back((Twine("\treturn input.map { key in ") + NewType +"(key) }").str()); + Segs.push_back("\treturn input.map { key in key.rawValue }"); + break; + case NodeAnnotation::DictionaryKeyUpdate: + Segs = {"", "Dictionary", (Twine("[") + RawType + ": Any]").str()}; + Segs.push_back((Twine("[") + NewType +": Any]").str()); + Segs.push_back((Twine("\treturn Dictionary(uniqueKeysWithValues: input.map" + " { key, value in (") + NewType + "(rawValue: key), value)})").str()); + Segs.push_back("\treturn Dictionary(uniqueKeysWithValues: input.map" + " {key, value in (key.rawValue, value)})"); + break; + case NodeAnnotation::SimpleStringRepresentableUpdate: + Segs = {"", "", RawType}; + Segs.push_back(NewType); + Segs.push_back((Twine("\treturn ") + NewType + "(rawValue: input)").str()); + Segs.push_back("\treturn input.rawValue"); + break; + case NodeAnnotation::SimpleOptionalStringRepresentableUpdate: + Segs = {"Optional", "", (Twine(RawType) +"?").str()}; + Segs.push_back((Twine(NewType) +"?").str()); + Segs.push_back((Twine(guard) + "\treturn " + NewType + "(rawValue: input)").str()); + Segs.push_back((Twine(guard) + "\treturn input.rawValue").str()); + break; + default: + llvm_unreachable("shouldn't handle this key."); + } + assert(Segs.size() == 6); + OS << Segs[0]; + SmallVector Parts; + NewType.split(Parts, '.'); + for (auto P: Parts) + OS << P; + OS << Segs[1]; + auto FuncName = Buffer.str().substr(FuncNameStart); + if (!InsertedFunctions.count(FuncName)) { + if (FromString) { + OS << "(_ input: " << Segs[2] << ") -> " << Segs[3] << " {\n"; + OS << Segs[4] << "\n}\n"; + } else { + OS << "(_ input: " << Segs[3] << ") -> " << Segs[2] << " {\n"; + OS << Segs[5] << "\n}\n"; + } + Editor.insert(FileEndLoc, OS.str()); + InsertedFunctions.insert(FuncName); + } + return FuncName; + } + + void handleStringRepresentableArg(ValueDecl *FD, Expr *Arg, Expr *Call) { + Editor.disableCache(); + SWIFT_DEFER { Editor.enableCache(); }; + NodeAnnotation Kind; + StringRef RawType; + StringRef NewAttributeType; + uint8_t ArgIdx; + for (auto Item: getRelatedDiffItems(FD)) { + if (auto *CI = dyn_cast(Item)) { + if (CI->isStringRepresentableChange()) { + Kind = CI->DiffKind; + RawType = CI->LeftComment; + NewAttributeType = CI->RightComment; + assert(CI->getChildIndices().size() == 1); + ArgIdx = CI->getChildIndices().front(); + break; + } + } + } + if (NewAttributeType.empty()) + return; + Expr *WrapTarget = Call; + bool FromString = false; + if (ArgIdx) { + ArgIdx --; + FromString = true; + auto AllArgs = getCallArgInfo(SM, Arg, LabelRangeEndAt::LabelNameOnly); + if (AllArgs.size() <= ArgIdx) + return; + WrapTarget = AllArgs[ArgIdx].ArgExp; + // Avoid wrapping nil literal. + if (isNilExpr(WrapTarget)) + return; + } + assert(WrapTarget); + SmallString<256> Buffer; + auto FuncName = insertHelperFunction(Kind, RawType, NewAttributeType, Buffer, + FromString); + Editor.insert(WrapTarget->getStartLoc(), (Twine(FuncName) + "(").str()); + Editor.insertAfterToken(WrapTarget->getEndLoc(), ")"); + } + bool walkToExprPre(Expr *E) override { if (handleQualifiedReplacement(E)) return false; + if (handleAssignDestMigration(E)) + return false; + if (handleAttributeReference(E)) + return false; if (auto *CE = dyn_cast(E)) { auto Fn = CE->getFn(); auto Args = CE->getArg(); @@ -652,6 +908,7 @@ struct APIDiffMigratorPass : public ASTMigratorPass, public SourceEntityWalker { handleFuncRename(FD, Fn, Args); handleTypeHoist(FD, CE, Args); handleSpecialCases(FD, CE, Args); + handleStringRepresentableArg(FD, Args, CE); } break; } @@ -661,13 +918,17 @@ struct APIDiffMigratorPass : public ASTMigratorPass, public SourceEntityWalker { handleFuncRename(FD, DSC->getFn(), Args); handleFunctionCallToPropertyChange(FD, DSC->getFn(), Args); handleSpecialCases(FD, CE, Args); + handleStringRepresentableArg(FD, Args, CE); } break; } case ExprKind::ConstructorRefCall: { auto CCE = cast(Fn); - if (auto FD = CCE->getFn()->getReferencedDecl().getDecl()) - handleFuncRename(FD, CCE->getFn(), Args); + if (auto FD = CCE->getFn()->getReferencedDecl().getDecl()) { + auto *CE = CCE->getFn(); + handleFuncRename(FD, CE, Args); + handleStringRepresentableArg(FD, Args, CE); + } break; } default: @@ -680,7 +941,8 @@ struct APIDiffMigratorPass : public ASTMigratorPass, public SourceEntityWalker { void handleFuncDeclRename(AbstractFunctionDecl *AFD, CharSourceRange NameRange) { bool IgnoreBase = false; - if (auto View = getFuncRename(AFD, IgnoreBase)) { + llvm::SmallString<32> Buffer; + if (auto View = getFuncRename(AFD, Buffer, IgnoreBase)) { if (!IgnoreBase) Editor.replace(NameRange, View.base()); unsigned Index = 0; diff --git a/lib/Migrator/EditorAdapter.cpp b/lib/Migrator/EditorAdapter.cpp index 6b9971157a41b..ee77d52ecd39b 100644 --- a/lib/Migrator/EditorAdapter.cpp +++ b/lib/Migrator/EditorAdapter.cpp @@ -29,6 +29,8 @@ EditorAdapter::getLocInfo(swift::SourceLoc Loc) const { bool EditorAdapter::cacheReplacement(CharSourceRange Range, StringRef Text) const { + if (!CacheEnabled) + return false; unsigned SwiftBufferID, Offset; std::tie(SwiftBufferID, Offset) = getLocInfo(Range.getStart()); Replacement R { Offset, Range.getByteLength(), Text }; diff --git a/lib/Migrator/FixitApplyDiagnosticConsumer.cpp b/lib/Migrator/FixitApplyDiagnosticConsumer.cpp index 95903bd21f928..348d78f5b5ed3 100644 --- a/lib/Migrator/FixitApplyDiagnosticConsumer.cpp +++ b/lib/Migrator/FixitApplyDiagnosticConsumer.cpp @@ -55,15 +55,27 @@ handleDiagnostic(SourceManager &SM, SourceLoc Loc, auto Offset = SM.getLocOffsetInBuffer(Fixit.getRange().getStart(), ThisBufferID); auto Length = Fixit.getRange().getByteLength(); + auto Text = Fixit.getText(); - Replacement R { Offset, Length, Fixit.getText() }; + // Ignore meaningless Fix-its. + if (Length == 0 && Text.size() == 0) + return; + + // Ignore pre-applied equivalents. + Replacement R { Offset, Length, Text }; if (Replacements.count(R)) { return; } else { Replacements.insert(R); } - RewriteBuf.ReplaceText(Offset, Length, Fixit.getText()); + if (Length == 0) { + RewriteBuf.InsertText(Offset, Text); + } else if (Text.size() == 0) { + RewriteBuf.RemoveText(Offset, Length); + } else { + RewriteBuf.ReplaceText(Offset, Length, Text); + } ++NumFixitsApplied; } } diff --git a/lib/Migrator/Migrator.cpp b/lib/Migrator/Migrator.cpp index 610ab63d5feb3..435a31955a8a3 100644 --- a/lib/Migrator/Migrator.cpp +++ b/lib/Migrator/Migrator.cpp @@ -37,14 +37,15 @@ bool migrator::updateCodeAndEmitRemapIfNeeded( llvm::sys::fs::remove(Invocation.getMigratorOptions().EmitRemapFilePath); Migrator M { Instance, Invocation }; // Provide inputs and configuration + auto EffectiveVersion = Invocation.getLangOptions().EffectiveLanguageVersion; + auto CurrentVersion = version::Version::getCurrentLanguageVersion(); // Phase 1: Pre Fix-it passes // These uses the initial frontend invocation to apply any obvious fix-its // to see if we can get an error-free AST to get to Phase 2. std::unique_ptr PreFixItInstance; if (Instance->getASTContext().hadError()) { - PreFixItInstance = M.repeatFixitMigrations(2, - Invocation.getLangOptions().EffectiveLanguageVersion); + PreFixItInstance = M.repeatFixitMigrations(2, EffectiveVersion); // If we still couldn't fix all of the errors, give up. if (PreFixItInstance == nullptr || @@ -56,10 +57,8 @@ bool migrator::updateCodeAndEmitRemapIfNeeded( } // Phase 2: Syntactic Transformations - // Don't run these passes if we're already in Swift 4.2 - auto Opts = Invocation.getLangOptions().EffectiveLanguageVersion; - bool isFourTwo = Opts.size() == 2 && Opts[0] == 4 && Opts[1] == 2; - if (!isFourTwo) { + // Don't run these passes if we're already in newest Swift version. + if (EffectiveVersion != CurrentVersion) { auto FailedSyntacticPasses = M.performSyntacticPasses(); if (FailedSyntacticPasses) { return true; @@ -75,7 +74,7 @@ bool migrator::updateCodeAndEmitRemapIfNeeded( if (M.getMigratorOptions().EnableMigratorFixits) { M.repeatFixitMigrations(Migrator::MaxCompilerFixitPassIterations, - {4, 0, 0}); + CurrentVersion); } // OK, we have a final resulting text. Now we compare against the input @@ -133,20 +132,22 @@ Migrator::performAFixItMigration(version::Version SwiftLanguageVersion) { LLVMArgs.erase(aarch64_use_tbi); } - // SE-0160: When migrating, always use the Swift 3 @objc inference rules, - // which drives warnings with the "@objc" Fix-Its. - Invocation.getLangOptions().EnableSwift3ObjCInference = true; - - // The default behavior of the migrator, referred to as "minimal" migration - // in SE-0160, only adds @objc Fix-Its to those cases where the Objective-C - // entry point is explicitly used somewhere in the source code. The user - // may also select a workflow that adds @objc for every declaration that - // would infer @objc under the Swift 3 rules but would no longer infer - // @objc in Swift 4. - Invocation.getLangOptions().WarnSwift3ObjCInference = - getMigratorOptions().KeepObjcVisibility - ? Swift3ObjCInferenceWarnings::Complete - : Swift3ObjCInferenceWarnings::Minimal; + if (StartInvocation.getLangOptions().EffectiveLanguageVersion.isVersion3()) { + // SE-0160: When migrating, always use the Swift 3 @objc inference rules, + // which drives warnings with the "@objc" Fix-Its. + Invocation.getLangOptions().EnableSwift3ObjCInference = true; + + // The default behavior of the migrator, referred to as "minimal" migration + // in SE-0160, only adds @objc Fix-Its to those cases where the Objective-C + // entry point is explicitly used somewhere in the source code. The user + // may also select a workflow that adds @objc for every declaration that + // would infer @objc under the Swift 3 rules but would no longer infer + // @objc in Swift 4. + Invocation.getLangOptions().WarnSwift3ObjCInference = + getMigratorOptions().KeepObjcVisibility + ? Swift3ObjCInferenceWarnings::Complete + : Swift3ObjCInferenceWarnings::Minimal; + } const auto &OrigFrontendOpts = StartInvocation.getFrontendOptions(); diff --git a/lib/Migrator/TypeOfMigratorPass.cpp b/lib/Migrator/TypeOfMigratorPass.cpp index 3697b9ca72602..965f5b3224c79 100644 --- a/lib/Migrator/TypeOfMigratorPass.cpp +++ b/lib/Migrator/TypeOfMigratorPass.cpp @@ -44,7 +44,6 @@ class TypeOfMigratorPass: public ASTMigratorPass, { SF->getASTContext().getIdentifier("type") }, ContextStack.empty() ? SF->getModuleScopeContext() : ContextStack.back(), /*TypeResolver=*/SF->getASTContext().getLazyResolver(), - /*IsKnownPrivate=*/false, DTE->getLoc() }; auto isShadowing = [&]() -> bool { diff --git a/lib/Option/SanitizerOptions.cpp b/lib/Option/SanitizerOptions.cpp index 62016b328362f..287ab90c81d6f 100644 --- a/lib/Option/SanitizerOptions.cpp +++ b/lib/Option/SanitizerOptions.cpp @@ -160,8 +160,8 @@ OptionSet swift::parseSanitizerArgValues( } } - // Sanitizers are only supported on Linux or Darwin. - if (!(Triple.isOSDarwin() || Triple.isOSLinux())) { + // Check that we're one of the known supported targets for sanitizers. + if (!(Triple.isOSDarwin() || Triple.isOSLinux() || Triple.isOSWindows())) { SmallString<128> b; Diags.diagnose(SourceLoc(), diag::error_unsupported_opt_for_target, (A->getOption().getPrefixedName() + diff --git a/lib/Parse/CMakeLists.txt b/lib/Parse/CMakeLists.txt index f0ed84b99894c..37b3c65e21b52 100644 --- a/lib/Parse/CMakeLists.txt +++ b/lib/Parse/CMakeLists.txt @@ -11,6 +11,7 @@ add_swift_library(swiftParse STATIC ParseType.cpp PersistentParserState.cpp Scope.cpp + SyntaxParsingCache.cpp SyntaxParsingContext.cpp LINK_LIBRARIES swiftAST diff --git a/lib/Parse/Lexer.cpp b/lib/Parse/Lexer.cpp index 3c504dcd9e80b..21ed3c6892bd5 100644 --- a/lib/Parse/Lexer.cpp +++ b/lib/Parse/Lexer.cpp @@ -682,6 +682,12 @@ static bool isLeftBound(const char *tokBegin, const char *bufferBegin) { else return true; + case '\xA0': + if (tokBegin - 1 != bufferBegin && tokBegin[-2] == '\xC2') + return false; // Non-breaking whitespace (U+00A0) + else + return true; + default: return true; } @@ -716,6 +722,12 @@ static bool isRightBound(const char *tokEnd, bool isLeftBound, else return true; + case '\xC2': + if (tokEnd[1] == '\xA0') + return false; // Non-breaking whitespace (U+00A0) + else + return true; + default: return true; } @@ -1894,6 +1906,17 @@ bool Lexer::lexUnknown(bool EmitDiagnosticsIfToken) { .fixItReplaceChars(getSourceLoc(CurPtr - 1), getSourceLoc(Tmp), " "); CurPtr = Tmp; return false; // Skip presumed whitespace. + } else if (Codepoint == 0x000000A0) { + // Non-breaking whitespace (U+00A0) + while (Tmp[0] == '\xC2' && Tmp[1] == '\xA0') + Tmp += 2; + SmallString<8> Spaces; + Spaces.assign((Tmp - CurPtr + 1) / 2, ' '); + diagnose(CurPtr - 1, diag::lex_nonbreaking_space) + .fixItReplaceChars(getSourceLoc(CurPtr - 1), getSourceLoc(Tmp), + Spaces); + CurPtr = Tmp; + return false; } else if (Codepoint == 0x0000201D) { // If this is an end curly quote, just diagnose it with a fixit hint. if (EmitDiagnosticsIfToken) { @@ -2621,7 +2644,17 @@ SourceLoc Lexer::getLocForEndOfLine(SourceManager &SM, SourceLoc Loc) { return getSourceLoc(L.CurPtr); } -StringRef Lexer::getIndentationForLine(SourceManager &SM, SourceLoc Loc) { +StringRef Lexer::getIndentationForLine(SourceManager &SM, SourceLoc Loc, + StringRef *ExtraIndentation) { + // FIXME: do something more intelligent here. + // + // Four spaces is the typical indentation in Swift code, so for now just use + // that directly here, but if someone was to do something better, updating + // here will update everyone. + + if (ExtraIndentation) + *ExtraIndentation = " "; + // Don't try to do anything with an invalid location. if (Loc.isInvalid()) return ""; diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index ce0346075eac8..723ba5242b3fa 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -300,18 +300,257 @@ getStringLiteralIfNotInterpolated(Parser &P, SourceLoc Loc, const Token &Tok, Segments.front().Length)); } -bool Parser::parseSpecializeAttribute(swift::tok ClosingBrace, SourceLoc AtLoc, - SourceLoc Loc, SpecializeAttr *&Attr) { - assert(ClosingBrace == tok::r_paren || ClosingBrace == tok::r_square); - SourceLoc lParenLoc = consumeToken(); - bool DiscardAttribute = false; - StringRef AttrName = "_specialize"; +ParserResult Parser::parseExtendedAvailabilitySpecList( + SourceLoc AtLoc, SourceLoc AttrLoc, StringRef AttrName) { + // Check 'Tok', return false if ':' or '=' cannot be found. + // Complain if '=' is found and suggest replacing it with ": ". + auto findAttrValueDelimiter = [&]() -> bool { + if (!Tok.is(tok::colon)) { + if (!Tok.is(tok::equal)) + return false; - Optional exported; - Optional kind; + diagnose(Tok.getLoc(), diag::replace_equal_with_colon_for_value) + .fixItReplace(Tok.getLoc(), ": "); + } + return true; + }; + + StringRef Platform = Tok.getText(); + + StringRef Message, Renamed; + clang::VersionTuple Introduced, Deprecated, Obsoleted; + SourceRange IntroducedRange, DeprecatedRange, ObsoletedRange; + auto PlatformAgnostic = PlatformAgnosticAvailabilityKind::None; + + SyntaxParsingContext AvailabilitySpecContext( + SyntaxContext, SyntaxKind::AvailabilitySpecList); + + bool HasUpcomingEntry = false; + + { + SyntaxParsingContext EntryContext(SyntaxContext, + SyntaxKind::AvailabilityArgument); + consumeToken(); + if (consumeIf(tok::comma)) { + HasUpcomingEntry = true; + } + } + + bool AnyAnnotations = false; + bool AnyArgumentInvalid = false; + int ParamIndex = 0; + + while (HasUpcomingEntry) { + SyntaxParsingContext EntryContext(SyntaxContext, + SyntaxKind::AvailabilityArgument); + AnyAnnotations = true; + StringRef ArgumentKindStr = Tok.getText(); + ParamIndex++; + + enum { + IsMessage, IsRenamed, + IsIntroduced, IsDeprecated, IsObsoleted, + IsUnavailable, + IsInvalid + } ArgumentKind = IsInvalid; + + if (Tok.is(tok::identifier)) { + ArgumentKind = + llvm::StringSwitch(ArgumentKindStr) + .Case("message", IsMessage) + .Case("renamed", IsRenamed) + .Case("introduced", IsIntroduced) + .Case("deprecated", IsDeprecated) + .Case("obsoleted", IsObsoleted) + .Case("unavailable", IsUnavailable) + .Default(IsInvalid); + } + + if (ArgumentKind == IsInvalid) { + diagnose(Tok.getLoc(), diag::attr_availability_expected_option, AttrName) + .highlight(SourceRange(Tok.getLoc())); + if (Tok.is(tok::code_complete) && CodeCompletion) { + CodeCompletion->completeDeclAttrParam(DAK_Available, ParamIndex); + consumeToken(tok::code_complete); + } else { + consumeIf(tok::identifier); + } + return nullptr; + } + + consumeToken(); + + switch (ArgumentKind) { + case IsMessage: + case IsRenamed: { + // Items with string arguments. + if (findAttrValueDelimiter()) { + consumeToken(); + } else { + diagnose(Tok, diag::attr_availability_expected_equal, AttrName, + ArgumentKindStr); + AnyArgumentInvalid = true; + if (peekToken().isAny(tok::r_paren, tok::comma)) + consumeToken(); + break; + } + + if (!Tok.is(tok::string_literal)) { + diagnose(AttrLoc, diag::attr_expected_string_literal, AttrName); + AnyArgumentInvalid = true; + if (peekToken().isAny(tok::r_paren, tok::comma)) + consumeToken(); + break; + } + + auto Value = getStringLiteralIfNotInterpolated(*this, AttrLoc, Tok, + ArgumentKindStr); + consumeToken(); + if (!Value) { + AnyArgumentInvalid = true; + break; + } + + if (ArgumentKind == IsMessage) { + Message = Value.getValue(); + } else { + ParsedDeclName parsedName = parseDeclName(Value.getValue()); + if (!parsedName) { + diagnose(AttrLoc, diag::attr_availability_invalid_renamed, AttrName); + AnyArgumentInvalid = true; + break; + } + Renamed = Value.getValue(); + } + + SyntaxContext->createNodeInPlace(SyntaxKind::AvailabilityLabeledArgument); + + break; + } + + case IsDeprecated: + if (!findAttrValueDelimiter()) { + if (PlatformAgnostic != PlatformAgnosticAvailabilityKind::None) { + diagnose(Tok, diag::attr_availability_unavailable_deprecated, + AttrName); + } + + PlatformAgnostic = PlatformAgnosticAvailabilityKind::Deprecated; + break; + } + LLVM_FALLTHROUGH; + + case IsIntroduced: + case IsObsoleted: { + // Items with version arguments. + if (findAttrValueDelimiter()) { + consumeToken(); + } else { + diagnose(Tok, diag::attr_availability_expected_equal, AttrName, + ArgumentKindStr); + AnyArgumentInvalid = true; + if (peekToken().isAny(tok::r_paren, tok::comma)) + consumeToken(); + break; + } + auto &VersionArg = + (ArgumentKind == IsIntroduced) + ? Introduced + : (ArgumentKind == IsDeprecated) ? Deprecated : Obsoleted; + + auto &VersionRange = (ArgumentKind == IsIntroduced) + ? IntroducedRange + : (ArgumentKind == IsDeprecated) + ? DeprecatedRange + : ObsoletedRange; + + if (parseVersionTuple( + VersionArg, VersionRange, + Diagnostic(diag::attr_availability_expected_version, AttrName))) { + AnyArgumentInvalid = true; + if (peekToken().isAny(tok::r_paren, tok::comma)) + consumeToken(); + } + + SyntaxContext->createNodeInPlace(SyntaxKind::AvailabilityLabeledArgument); + + break; + } + + case IsUnavailable: + if (PlatformAgnostic != PlatformAgnosticAvailabilityKind::None) { + diagnose(Tok, diag::attr_availability_unavailable_deprecated, AttrName); + } + + PlatformAgnostic = PlatformAgnosticAvailabilityKind::Unavailable; + break; + + case IsInvalid: + llvm_unreachable("handled above"); + } + + // Parse the trailing comma + if (consumeIf(tok::comma)) { + HasUpcomingEntry = true; + } else { + HasUpcomingEntry = false; + } + } + + if (!AnyAnnotations) { + diagnose(Tok.getLoc(), diag::attr_expected_comma, AttrName, + /*isDeclModifier*/ false); + } + + auto PlatformKind = platformFromString(Platform); + + // Treat 'swift' as a valid version-qualifying token, when + // at least some versions were mentioned and no other + // platform-agnostic availability spec has been provided. + bool SomeVersion = (!Introduced.empty() || + !Deprecated.empty() || + !Obsoleted.empty()); + if (!PlatformKind.hasValue() && + Platform == "swift" && + SomeVersion && + PlatformAgnostic == PlatformAgnosticAvailabilityKind::None) { + PlatformKind = PlatformKind::none; + PlatformAgnostic = PlatformAgnosticAvailabilityKind::SwiftVersionSpecific; + } + + + if (AnyArgumentInvalid) + return nullptr; + if (!PlatformKind.hasValue()) { + diagnose(AttrLoc, diag::attr_availability_unknown_platform, + Platform, AttrName); + return nullptr; + } + + auto Attr = new (Context) + AvailableAttr(AtLoc, SourceRange(AttrLoc, Tok.getLoc()), + PlatformKind.getValue(), + Message, Renamed, + Introduced, IntroducedRange, + Deprecated, DeprecatedRange, + Obsoleted, ObsoletedRange, + PlatformAgnostic, + /*Implicit=*/false); + return makeParserResult(Attr); + +} + +bool Parser::parseSpecializeAttributeArguments( + swift::tok ClosingBrace, bool &DiscardAttribute, Optional &Exported, + Optional &Kind, + swift::TrailingWhereClause *&TrailingWhereClause) { + SyntaxParsingContext ContentContext(SyntaxContext, + SyntaxKind::SpecializeAttributeSpecList); // Parse optional "exported" and "kind" labeled parameters. while (!Tok.is(tok::kw_where)) { + SyntaxParsingContext ArgumentContext(SyntaxContext, + SyntaxKind::LabeledSpecializeEntry); if (Tok.is(tok::identifier)) { auto ParamLabel = Tok.getText(); if (ParamLabel != "exported" && ParamLabel != "kind") { @@ -334,8 +573,8 @@ bool Parser::parseSpecializeAttribute(swift::tok ClosingBrace, SourceLoc AtLoc, DiscardAttribute = true; return false; } - if ((ParamLabel == "exported" && exported.hasValue()) || - (ParamLabel == "kind" && kind.hasValue())) { + if ((ParamLabel == "exported" && Exported.hasValue()) || + (ParamLabel == "kind" && Kind.hasValue())) { diagnose(Tok.getLoc(), diag::attr_specialize_parameter_already_defined, ParamLabel); } @@ -358,16 +597,16 @@ bool Parser::parseSpecializeAttribute(swift::tok ClosingBrace, SourceLoc AtLoc, return false; } if (ParamLabel == "exported") { - exported = isTrue ? true : false; + Exported = isTrue ? true : false; } } if (ParamLabel == "kind") { SourceLoc paramValueLoc; if (Tok.is(tok::identifier)) { if (Tok.getText() == "partial") { - kind = SpecializeAttr::SpecializationKind::Partial; + Kind = SpecializeAttr::SpecializationKind::Partial; } else if (Tok.getText() == "full") { - kind = SpecializeAttr::SpecializationKind::Full; + Kind = SpecializeAttr::SpecializationKind::Full; } else { diagnose(Tok.getLoc(), diag::attr_specialize_expected_partial_or_full); @@ -403,15 +642,34 @@ bool Parser::parseSpecializeAttribute(swift::tok ClosingBrace, SourceLoc AtLoc, }; // Parse the where clause. - TrailingWhereClause *trailingWhereClause = nullptr; if (Tok.is(tok::kw_where)) { SourceLoc whereLoc; SmallVector requirements; bool firstTypeInComplete; parseGenericWhereClause(whereLoc, requirements, firstTypeInComplete, /* AllowLayoutConstraints */ true); - trailingWhereClause = - TrailingWhereClause::create(Context, whereLoc, requirements); + TrailingWhereClause = + TrailingWhereClause::create(Context, whereLoc, requirements); + } + return true; +} + +bool Parser::parseSpecializeAttribute(swift::tok ClosingBrace, SourceLoc AtLoc, + SourceLoc Loc, SpecializeAttr *&Attr) { + assert(ClosingBrace == tok::r_paren || ClosingBrace == tok::r_square); + + SourceLoc lParenLoc = consumeToken(); + bool DiscardAttribute = false; + StringRef AttrName = "_specialize"; + + Optional exported; + Optional kind; + + TrailingWhereClause *trailingWhereClause = nullptr; + + if (!parseSpecializeAttributeArguments(ClosingBrace, DiscardAttribute, + exported, kind, trailingWhereClause)) { + return false; } // Parse the closing ')' or ']'. @@ -457,26 +715,31 @@ Parser::parseImplementsAttribute(SourceLoc AtLoc, SourceLoc Loc) { SourceLoc lParenLoc = consumeToken(); - ParserResult ProtocolType = parseType(); - Status |= ProtocolType; - - if (!(Status.shouldStopParsing() || consumeIf(tok::comma))) { - diagnose(Tok.getLoc(), diag::attr_expected_comma, AttrName, - /*DeclModifier=*/false); - Status.setIsParseError(); - } - DeclNameLoc MemberNameLoc; DeclName MemberName; - if (!Status.shouldStopParsing()) { - MemberName = - parseUnqualifiedDeclName(/*afterDot=*/false, MemberNameLoc, - diag::attr_implements_expected_member_name, - /*allowOperators=*/true, - /*allowZeroArgCompoundNames=*/true); - if (!MemberName) { + ParserResult ProtocolType; + { + SyntaxParsingContext ContentContext( + SyntaxContext, SyntaxKind::ImplementsAttributeArguments); + ProtocolType = parseType(); + Status |= ProtocolType; + + if (!(Status.shouldStopParsing() || consumeIf(tok::comma))) { + diagnose(Tok.getLoc(), diag::attr_expected_comma, AttrName, + /*DeclModifier=*/false); Status.setIsParseError(); } + + if (!Status.shouldStopParsing()) { + MemberName = + parseUnqualifiedDeclName(/*afterDot=*/false, MemberNameLoc, + diag::attr_implements_expected_member_name, + /*allowOperators=*/true, + /*allowZeroArgCompoundNames=*/true); + if (!MemberName) { + Status.setIsParseError(); + } + } } if (Status.isError()) { @@ -499,16 +762,74 @@ Parser::parseImplementsAttribute(SourceLoc AtLoc, SourceLoc Loc) { ProtocolType.get(), MemberName, MemberNameLoc)); } +void Parser::parseObjCSelector(SmallVector &Names, + SmallVector &NameLocs, + bool &IsNullarySelector) { + IsNullarySelector = true; + SyntaxParsingContext SelectorContext(SyntaxContext, SyntaxKind::ObjCSelector); + while (true) { + SyntaxParsingContext SelectorPieceContext(SyntaxContext, + SyntaxKind::ObjCSelectorPiece); + // Empty selector piece. + if (Tok.is(tok::colon)) { + Names.push_back(Identifier()); + NameLocs.push_back(Tok.getLoc()); + IsNullarySelector = false; + consumeToken(); + continue; + } + + // Name. + if (Tok.is(tok::identifier) || Tok.isKeyword()) { + Names.push_back(Context.getIdentifier(Tok.getText())); + NameLocs.push_back(Tok.getLoc()); + consumeToken(); + + // If we have a colon, consume it. + if (Tok.is(tok::colon)) { + consumeToken(); + IsNullarySelector = false; + continue; + } + + // If we see a closing parentheses, we're done. + if (Tok.is(tok::r_paren)) { + // If we saw more than one identifier, there's a ':' + // missing here. Complain and pretend we saw it. + if (Names.size() > 1) { + diagnose(Tok, diag::attr_objc_missing_colon) + .fixItInsertAfter(NameLocs.back(), ":"); + IsNullarySelector = false; + } + + break; + } + + // If we see another identifier or keyword, complain about + // the missing colon and keep going. + if (Tok.is(tok::identifier) || Tok.isKeyword()) { + diagnose(Tok, diag::attr_objc_missing_colon) + .fixItInsertAfter(NameLocs.back(), ":"); + IsNullarySelector = false; + continue; + } + + // We don't know what happened. Break out. + break; + } + + // We didn't parse anything, don't create a ObjCSelectorPiece + SelectorPieceContext.setTransparent(); + break; + } +} + bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, DeclAttrKind DK) { // Ok, it is a valid attribute, eat it, and then process it. StringRef AttrName = Tok.getText(); SourceLoc Loc = consumeToken(); - // We can only make this attribute a token list intead of an Attribute node - // because the attribute node may include '@' - SyntaxParsingContext TokListContext(SyntaxContext, SyntaxKind::TokenList); - bool DiscardAttribute = false; // Diagnose duplicated attributes. @@ -530,20 +851,6 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, // diagnostic this can be used for better error presentation. SourceRange AttrRange; - - // Check 'Tok', return false if ':' or '=' cannot be found. - // Complain if '=' is found and suggest replacing it with ": ". - auto findAttrValueDelimiter = [&]() -> bool { - if (!Tok.is(tok::colon)) { - if (!Tok.is(tok::equal)) - return false; - - diagnose(Tok.getLoc(), diag::replace_equal_with_colon_for_value) - .fixItReplace(Tok.getLoc(), ": "); - } - return true; - }; - switch (DK) { case DAK_Count: llvm_unreachable("DAK_Count should not appear in parsing switch"); @@ -1026,167 +1333,9 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, break; } - consumeToken(); - - StringRef Message, Renamed; - clang::VersionTuple Introduced, Deprecated, Obsoleted; - SourceRange IntroducedRange, DeprecatedRange, ObsoletedRange; - auto PlatformAgnostic = PlatformAgnosticAvailabilityKind::None; - bool AnyAnnotations = false; - int ParamIndex = 0; - - while (consumeIf(tok::comma)) { - AnyAnnotations = true; - StringRef ArgumentKindStr = Tok.getText(); - ParamIndex ++; - - enum { - IsMessage, IsRenamed, - IsIntroduced, IsDeprecated, IsObsoleted, - IsUnavailable, - IsInvalid - } ArgumentKind = IsInvalid; - - if (Tok.is(tok::identifier)) { - ArgumentKind = - llvm::StringSwitch(ArgumentKindStr) - .Case("message", IsMessage) - .Case("renamed", IsRenamed) - .Case("introduced", IsIntroduced) - .Case("deprecated", IsDeprecated) - .Case("obsoleted", IsObsoleted) - .Case("unavailable", IsUnavailable) - .Default(IsInvalid); - } - - if (ArgumentKind == IsInvalid) { - DiscardAttribute = true; - diagnose(Tok.getLoc(), diag::attr_availability_expected_option, - AttrName) - .highlight(SourceRange(Tok.getLoc())); - if (Tok.is(tok::code_complete) && CodeCompletion) { - CodeCompletion->completeDeclAttrParam(DAK_Available, ParamIndex); - consumeToken(tok::code_complete); - } else { - consumeIf(tok::identifier); - } - break; - } - - consumeToken(); - - switch (ArgumentKind) { - case IsMessage: - case IsRenamed: { - // Items with string arguments. - if (findAttrValueDelimiter()) { - consumeToken(); - } else { - diagnose(Tok, diag::attr_availability_expected_equal, - AttrName, ArgumentKindStr); - DiscardAttribute = true; - if (peekToken().isAny(tok::r_paren, tok::comma)) - consumeToken(); - continue; - } - - if (!Tok.is(tok::string_literal)) { - diagnose(Loc, diag::attr_expected_string_literal, AttrName); - DiscardAttribute = true; - if (peekToken().isAny(tok::r_paren, tok::comma)) - consumeToken(); - continue; - } - - auto Value = - getStringLiteralIfNotInterpolated(*this, Loc, Tok, ArgumentKindStr); - consumeToken(); - if (!Value) { - DiscardAttribute = true; - continue; - } - - if (ArgumentKind == IsMessage) { - Message = Value.getValue(); - } else { - ParsedDeclName parsedName = parseDeclName(Value.getValue()); - if (!parsedName) { - diagnose(Loc, diag::attr_availability_invalid_renamed, AttrName); - DiscardAttribute = true; - continue; - } - Renamed = Value.getValue(); - } - break; - } - - case IsDeprecated: - if (!findAttrValueDelimiter()) { - if (PlatformAgnostic != PlatformAgnosticAvailabilityKind::None) { - diagnose(Tok, diag::attr_availability_unavailable_deprecated, - AttrName); - } - - PlatformAgnostic = PlatformAgnosticAvailabilityKind::Deprecated; - break; - } - LLVM_FALLTHROUGH; - - case IsIntroduced: - case IsObsoleted: { - // Items with version arguments. - if (findAttrValueDelimiter()) { - consumeToken(); - } else { - diagnose(Tok, diag::attr_availability_expected_equal, - AttrName, ArgumentKindStr); - DiscardAttribute = true; - if (peekToken().isAny(tok::r_paren, tok::comma)) - consumeToken(); - continue; - } - - auto &VersionArg = (ArgumentKind == IsIntroduced) ? Introduced : - (ArgumentKind == IsDeprecated) ? Deprecated : - Obsoleted; - - auto &VersionRange = (ArgumentKind == IsIntroduced) ? IntroducedRange : - (ArgumentKind == IsDeprecated) ? DeprecatedRange : - ObsoletedRange; - - if (parseVersionTuple( - VersionArg, VersionRange, - Diagnostic(diag::attr_availability_expected_version, - AttrName))) { - DiscardAttribute = true; - if (peekToken().isAny(tok::r_paren, tok::comma)) - consumeToken(); - } - - break; - } - - case IsUnavailable: - if (PlatformAgnostic != PlatformAgnosticAvailabilityKind::None) { - diagnose(Tok, diag::attr_availability_unavailable_deprecated, - AttrName); - } - - PlatformAgnostic = PlatformAgnosticAvailabilityKind::Unavailable; - break; - - case IsInvalid: - llvm_unreachable("handled above"); - } - } - - if (!AnyAnnotations) { - diagnose(Tok.getLoc(), diag::attr_expected_comma, AttrName, - DeclAttribute::isDeclModifier(DK)); - DiscardAttribute = true; - } - - AttrRange = SourceRange(Loc, Tok.getLoc()); + auto AvailabilityAttr = parseExtendedAvailabilitySpecList(AtLoc, Loc, + AttrName); + DiscardAttribute |= AvailabilityAttr.isParseError(); if (!consumeIf(tok::r_paren)) { if (!DiscardAttribute) { @@ -1197,39 +1346,9 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, } if (!DiscardAttribute) { - auto PlatformKind = platformFromString(Platform); - - // Treat 'swift' as a valid version-qualifying token, when - // at least some versions were mentioned and no other - // platform-agnostic availability spec has been provided. - bool SomeVersion = (!Introduced.empty() || - !Deprecated.empty() || - !Obsoleted.empty()); - if (!PlatformKind.hasValue() && - Platform == "swift" && - SomeVersion && - PlatformAgnostic == PlatformAgnosticAvailabilityKind::None) { - PlatformKind = PlatformKind::none; - PlatformAgnostic = - PlatformAgnosticAvailabilityKind::SwiftVersionSpecific; - } - - if (PlatformKind.hasValue()) { - Attributes.add(new (Context) - AvailableAttr(AtLoc, AttrRange, - PlatformKind.getValue(), - Message, Renamed, - Introduced, IntroducedRange, - Deprecated, DeprecatedRange, - Obsoleted, ObsoletedRange, - PlatformAgnostic, - /*Implicit=*/false)); - } else { - // Not a known platform. Just drop the attribute. - diagnose(Loc, diag::attr_availability_unknown_platform, - Platform, AttrName); - return false; - } + Attributes.add(AvailabilityAttr.get()); + } else { + return false; } break; } @@ -1245,62 +1364,13 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, // Parse the leading '('. SourceLoc LParenLoc = consumeToken(tok::l_paren); - // Parse the names, with trailing colons (if there are present). + // Parse the names, with trailing colons (if there are present) and populate + // the inout parameters SmallVector Names; SmallVector NameLocs; - bool sawColon = false; - while (true) { - // Empty selector piece. - if (Tok.is(tok::colon)) { - Names.push_back(Identifier()); - NameLocs.push_back(Tok.getLoc()); - sawColon = true; - consumeToken(); - continue; - } - - // Name. - if (Tok.is(tok::identifier) || Tok.isKeyword()) { - Names.push_back(Context.getIdentifier(Tok.getText())); - NameLocs.push_back(Tok.getLoc()); - consumeToken(); - - // If we have a colon, consume it. - if (Tok.is(tok::colon)) { - consumeToken(); - sawColon = true; - continue; - } - - // If we see a closing parentheses, we're done. - if (Tok.is(tok::r_paren)) { - // If we saw more than one identifier, there's a ':' - // missing here. Complain and pretend we saw it. - if (Names.size() > 1) { - diagnose(Tok, diag::attr_objc_missing_colon) - .fixItInsertAfter(NameLocs.back(), ":"); - sawColon = true; - } + bool NullarySelector = true; + parseObjCSelector(Names, NameLocs, NullarySelector); - break; - } - - // If we see another identifier or keyword, complain about - // the missing colon and keep going. - if (Tok.is(tok::identifier) || Tok.isKeyword()) { - diagnose(Tok, diag::attr_objc_missing_colon) - .fixItInsertAfter(NameLocs.back(), ":"); - sawColon = true; - continue; - } - - // We don't know what happened. Break out. - break; - } - - break; - } - // Parse the matching ')'. SourceLoc RParenLoc; bool Invalid = parseMatchingToken(tok::r_paren, RParenLoc, @@ -1313,7 +1383,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, if (!Invalid) diagnose(LParenLoc, diag::attr_objc_empty_name); attr = ObjCAttr::createUnnamed(Context, AtLoc, Loc); - } else if (!sawColon) { + } else if (NullarySelector) { // When we didn't see a colon, this is a nullary name. assert(Names.size() == 1 && "Forgot to set sawColon?"); attr = ObjCAttr::createNullary(Context, AtLoc, Loc, LParenLoc, @@ -1371,6 +1441,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, bool Parser::parseVersionTuple(clang::VersionTuple &Version, SourceRange &Range, const Diagnostic &D) { + SyntaxParsingContext VersionContext(SyntaxContext, SyntaxKind::VersionTuple); // A version number is either an integer (8), a float (8.1), or a // float followed by a dot and an integer (8.1.0). if (!Tok.isAny(tok::integer_literal, tok::floating_literal)) { @@ -1465,35 +1536,34 @@ bool Parser::parseDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc) { // If the attribute follows the new representation, switch // over to the alternate parsing path. DeclAttrKind DK = DeclAttribute::getAttrKindFromString(Tok.getText()); - - auto checkRenamedAttr = [&](StringRef oldName, StringRef newName, - DeclAttrKind kind, bool warning) { - if (DK == DAK_Count && Tok.getText() == oldName) { + + auto checkInvalidAttrName = [&](StringRef invalidName, StringRef correctName, + DeclAttrKind kind, Diag diag) { + if (DK == DAK_Count && Tok.getText() == invalidName) { // We renamed @availability to @available, so if we see the former, // treat it as the latter and emit a Fix-It. DK = kind; - if (warning) { - diagnose(Tok, diag::attr_renamed_warning, oldName, newName) - .fixItReplace(Tok.getLoc(), newName); - } else { - diagnose(Tok, diag::attr_renamed, oldName, newName) - .fixItReplace(Tok.getLoc(), newName); - } + + diagnose(Tok, diag, invalidName, correctName) + .fixItReplace(Tok.getLoc(), correctName); } }; // FIXME: This renaming happened before Swift 3, we can probably remove // the specific fallback path at some point. - checkRenamedAttr("availability", "available", DAK_Available, false); + checkInvalidAttrName("availability", "available", DAK_Available, diag::attr_renamed); + + // Check if attr is inlineable, and suggest inlinable instead + checkInvalidAttrName("inlineable", "inlinable", DAK_Inlinable, diag::attr_name_close_match); // In Swift 5 and above, these become hard errors. Otherwise, emit a // warning for compatibility. if (!Context.isSwiftVersionAtLeast(5)) { - checkRenamedAttr("_versioned", "usableFromInline", DAK_UsableFromInline, true); - checkRenamedAttr("_inlineable", "inlinable", DAK_Inlinable, true); + checkInvalidAttrName("_versioned", "usableFromInline", DAK_UsableFromInline, diag::attr_renamed_warning); + checkInvalidAttrName("_inlineable", "inlinable", DAK_Inlinable, diag::attr_renamed_warning); } else { - checkRenamedAttr("_versioned", "usableFromInline", DAK_UsableFromInline, false); - checkRenamedAttr("_inlineable", "inlinable", DAK_Inlinable, false); + checkInvalidAttrName("_versioned", "usableFromInline", DAK_UsableFromInline, diag::attr_renamed); + checkInvalidAttrName("_inlineable", "inlinable", DAK_Inlinable, diag::attr_renamed); } if (DK == DAK_Count && Tok.getText() == "warn_unused_result") { @@ -1655,9 +1725,6 @@ bool Parser::parseTypeAttribute(TypeAttributes &Attributes, bool justChecking) { StringRef Text = Tok.getText(); SourceLoc Loc = consumeToken(); - // Accumulate attribute argument '( ... )' as a token list. - SyntaxParsingContext TokListContext(SyntaxContext, SyntaxKind::TokenList); - bool isAutoclosureEscaping = false; SourceRange autoclosureEscapingParenRange; StringRef conventionName; @@ -1905,6 +1972,143 @@ bool Parser::parseDeclAttributeList(DeclAttributes &Attributes, return error; } +/// \verbatim +/// modifier-list +/// /* empty */ +// modifier modifier-list +// modifier +// 'private' +// 'private' '(' 'set' ')' +// 'fileprivate' +// 'fileprivate' '(' 'set' )' +// 'internal' +// 'internal' '(' 'set' ')' +// 'public' +// 'open' +// 'weak' +// 'unowned' +// 'unowned' '(' 'safe' ')' +// 'unowned' '(' 'unsafe' ')' +// 'optional' +// 'required' +// 'lazy' +// 'final' +// 'dynamic' +// 'prefix' +// 'postfix' +// 'infix' +// 'override' +// 'mutating +// 'nonmutating' +// '__consuming' +// 'convenience' +bool Parser::parseDeclModifierList(DeclAttributes &Attributes, + SourceLoc &StaticLoc, + StaticSpellingKind &StaticSpelling) { + SyntaxParsingContext ListContext(SyntaxContext, SyntaxKind::ModifierList); + bool isError = false; + bool hasModifier = false; + while (true) { + switch (Tok.getKind()) { + + case tok::kw_private: + case tok::kw_fileprivate: + case tok::kw_internal: + case tok::kw_public: { + SyntaxParsingContext ModContext(SyntaxContext, SyntaxKind::DeclModifier); + // We still model these specifiers as attributes. + isError |= + parseNewDeclAttribute(Attributes, /*AtLoc=*/{}, DAK_AccessControl); + hasModifier = true; + continue; + } + + // Context sensitive keywords. + case tok::identifier: { + if (Tok.isEscapedIdentifier()) + break; + + DeclAttrKind Kind = llvm::StringSwitch(Tok.getText()) +#define CONTEXTUAL_CASE(KW, CLASS) .Case(#KW, DAK_##CLASS) +#define CONTEXTUAL_DECL_ATTR(KW, CLASS, ...) CONTEXTUAL_CASE(KW, CLASS) +#define CONTEXTUAL_DECL_ATTR_ALIAS(KW, CLASS) CONTEXTUAL_CASE(KW, CLASS) +#define CONTEXTUAL_SIMPLE_DECL_ATTR(KW, CLASS, ...) CONTEXTUAL_CASE(KW, CLASS) +#include +#undef CONTEXTUAL_CASE + .Default(DAK_Count); + + if (Kind == DAK_Count) + break; + + SyntaxParsingContext ModContext(SyntaxContext, + SyntaxKind::DeclModifier); + isError |= parseNewDeclAttribute(Attributes, /*AtLoc=*/{}, Kind); + hasModifier = true; + continue; + } + + case tok::kw_static: { + // 'static' is not handled as an attribute in AST. + if (StaticLoc.isValid()) { + diagnose(Tok, diag::decl_already_static, + StaticSpellingKind::KeywordStatic) + .highlight(StaticLoc) + .fixItRemove(Tok.getLoc()); + } else { + StaticLoc = Tok.getLoc(); + StaticSpelling = StaticSpellingKind::KeywordStatic; + } + SyntaxParsingContext ModContext(SyntaxContext, SyntaxKind::DeclModifier); + consumeToken(tok::kw_static); + hasModifier = true; + continue; + } + + case tok::kw_class: { + // If 'class' is a modifier on another decl kind, like var or func, + // then treat it as a modifier. + { + BacktrackingScope Scope(*this); + consumeToken(tok::kw_class); + if (!isStartOfDecl()) + // This 'class' is a real ClassDecl introducer. + break; + } + if (StaticLoc.isValid()) { + diagnose(Tok, diag::decl_already_static, + StaticSpellingKind::KeywordClass) + .highlight(StaticLoc) + .fixItRemove(Tok.getLoc()); + } else { + StaticLoc = Tok.getLoc(); + StaticSpelling = StaticSpellingKind::KeywordClass; + } + SyntaxParsingContext ModContext(SyntaxContext, SyntaxKind::DeclModifier); + consumeToken(tok::kw_class); + hasModifier = true; + continue; + } + + case tok::unknown: + // Eat an invalid token in decl modifier context. Error tokens are + // diagnosed by the lexer, so we don't need to emit another diagnostic. + consumeToken(tok::unknown); + hasModifier = true; + continue; + + default: + break; + } + + // If we don't have any modifiers, don't bother to construct an empty list. + if (!hasModifier) + ListContext.setTransparent(); + + // If we 'break' out of the switch, modifier list has ended. + return isError; + } +} + /// \brief This is the internal implementation of \c parseTypeAttributeList, /// which we expect to be inlined to handle the common case of an absent /// attribute list. @@ -2081,7 +2285,13 @@ bool Parser::isStartOfDecl() { if (Tok.is(tok::kw_init)) { return !isa(CurDeclContext); } - + + // Similarly, when 'case' appears inside a function, it's probably a switch + // case, not an enum case declaration. + if (Tok.is(tok::kw_case)) { + return !isa(CurDeclContext); + } + // The protocol keyword needs more checking to reject "protocol". if (Tok.is(tok::kw_protocol)) { const Token &Tok2 = peekToken(); @@ -2198,6 +2408,14 @@ void Parser::setLocalDiscriminator(ValueDecl *D) { D->setLocalDiscriminator(discriminator); } +void Parser::setLocalDiscriminatorToParamList(ParameterList *PL) { + for (auto P : *PL) { + if (!P->hasName() || P->isImplicit()) + continue; + setLocalDiscriminator(P); + } +} + void Parser::delayParseFromBeginningToHere(ParserPosition BeginParserPosition, ParseDeclOptions Flags) { auto CurLoc = Tok.getLoc(); @@ -2233,9 +2451,6 @@ void Parser::delayParseFromBeginningToHere(ParserPosition BeginParserPosition, ParserResult Parser::parseDecl(ParseDeclOptions Flags, llvm::function_ref Handler) { - SyntaxParsingContext DeclParsingContext(SyntaxContext, - SyntaxContextKind::Decl); - if (Tok.is(tok::pound_if)) { auto IfConfigResult = parseIfConfig( [&](SmallVectorImpl &Decls, bool IsActive) { @@ -2246,6 +2461,8 @@ Parser::parseDecl(ParseDeclOptions Flags, ParserStatus Status; bool PreviousHadSemi = true; + SyntaxParsingContext DeclListCtx(SyntaxContext, + SyntaxKind::MemberDeclList); while (Tok.isNot(tok::pound_else, tok::pound_endif, tok::pound_elseif, tok::eof)) { if (Tok.is(tok::r_brace)) { @@ -2275,325 +2492,212 @@ Parser::parseDecl(ParseDeclOptions Flags, } return IfConfigResult; } + if (Tok.isAny(tok::pound_warning, tok::pound_error)) { + auto Result = parseDeclPoundDiagnostic(); + if (Result.isNonNull()) + Handler(Result.get()); + return Result; + } + + SyntaxParsingContext DeclParsingContext(SyntaxContext, + SyntaxContextKind::Decl); ParserPosition BeginParserPosition; if (isCodeCompletionFirstPass()) BeginParserPosition = getParserPosition(); - SourceLoc tryLoc; - (void)consumeIf(tok::kw_try, tryLoc); - // Note that we're parsing a declaration. StructureMarkerRAII ParsingDecl(*this, Tok.getLoc(), StructureMarkerKind::Declaration); + // Parse attributes. DeclAttributes Attributes; if (Tok.hasComment()) Attributes.add(new (Context) RawDocCommentAttr(Tok.getCommentRange())); bool FoundCCTokenInAttr; parseDeclAttributeList(Attributes, FoundCCTokenInAttr); + // Parse modifiers. // Keep track of where and whether we see a contextual keyword on the decl. SourceLoc StaticLoc; StaticSpellingKind StaticSpelling = StaticSpellingKind::None; - ParserResult DeclResult; + parseDeclModifierList(Attributes, StaticLoc, StaticSpelling); - while (1) { - // Save the original token, in case code-completion needs it. - auto OrigTok = Tok; - bool MayNeedOverrideCompletion = false; + // We emit diagnostics for 'try let ...' in parseDeclVar(). + SourceLoc tryLoc; + if (Tok.is(tok::kw_try) && peekToken().isAny(tok::kw_let, tok::kw_var)) + tryLoc = consumeToken(tok::kw_try); - switch (Tok.getKind()) { - // Modifiers - case tok::kw_static: { - if (StaticLoc.isValid()) { - diagnose(Tok, diag::decl_already_static, - StaticSpellingKind::KeywordStatic) - .highlight(StaticLoc) - .fixItRemove(Tok.getLoc()); - } else { - StaticLoc = Tok.getLoc(); - StaticSpelling = StaticSpellingKind::KeywordStatic; - } - SyntaxParsingContext ModContext(SyntaxContext, SyntaxKind::DeclModifier); - consumeToken(tok::kw_static); - // Static modifier doesn't have more details. - SyntaxParsingContext DetailContext(SyntaxContext, SyntaxKind::TokenList); - continue; - } - // 'class' is a modifier on func, but is also a top-level decl. - case tok::kw_class: { - SourceLoc ClassLoc; - bool AsModifier; - { - BacktrackingScope Scope(*this); - ClassLoc = consumeToken(tok::kw_class); - AsModifier = isStartOfDecl(); - } - // If 'class' is a modifier on another decl kind, like var or func, - // then treat it as a modifier. - if (AsModifier) { - SyntaxParsingContext ModContext(SyntaxContext, SyntaxKind::DeclModifier); - consumeToken(tok::kw_class); - SyntaxParsingContext DetailContext(SyntaxContext, SyntaxKind::TokenList); - if (StaticLoc.isValid()) { - diagnose(Tok, diag::decl_already_static, - StaticSpellingKind::KeywordClass) - .highlight(StaticLoc).fixItRemove(ClassLoc); - } else { - StaticLoc = ClassLoc; - StaticSpelling = StaticSpellingKind::KeywordClass; - } - continue; - } + ParserResult DeclResult; - consumeToken(tok::kw_class); - // Otherwise this is the start of a class declaration. - DeclParsingContext.setCreateSyntax(SyntaxKind::ClassDecl); - DeclResult = parseDeclClass(ClassLoc, Flags, Attributes); - break; - } + // Save the original token, in case code-completion needs it. + auto OrigTok = Tok; + bool MayNeedOverrideCompletion = false; - case tok::kw_private: - case tok::kw_fileprivate: - case tok::kw_internal: - case tok::kw_public: { - SyntaxParsingContext ModContext(SyntaxContext, SyntaxKind::DeclModifier); - // We still model these specifiers as attributes. - parseNewDeclAttribute(Attributes, /*AtLoc=*/{}, DAK_AccessControl); - continue; + switch (Tok.getKind()) { + case tok::kw_import: + DeclParsingContext.setCreateSyntax(SyntaxKind::ImportDecl); + DeclResult = parseDeclImport(Flags, Attributes); + break; + case tok::kw_extension: + DeclParsingContext.setCreateSyntax(SyntaxKind::ExtensionDecl); + DeclResult = parseDeclExtension(Flags, Attributes); + break; + case tok::kw_let: + case tok::kw_var: { + // Collect all modifiers into a modifier list. + DeclParsingContext.setCreateSyntax(SyntaxKind::VariableDecl); + llvm::SmallVector Entries; + DeclResult = parseDeclVar(Flags, Attributes, Entries, StaticLoc, + StaticSpelling, tryLoc); + StaticLoc = SourceLoc(); // we handled static if present. + MayNeedOverrideCompletion = true; + std::for_each(Entries.begin(), Entries.end(), Handler); + if (auto *D = DeclResult.getPtrOrNull()) + markWasHandled(D); + break; + } + case tok::kw_typealias: + DeclParsingContext.setCreateSyntax(SyntaxKind::TypealiasDecl); + DeclResult = parseDeclTypeAlias(Flags, Attributes); + MayNeedOverrideCompletion = true; + break; + case tok::kw_associatedtype: + DeclParsingContext.setCreateSyntax(SyntaxKind::AssociatedtypeDecl); + DeclResult = parseDeclAssociatedType(Flags, Attributes); + break; + case tok::kw_enum: + DeclParsingContext.setCreateSyntax(SyntaxKind::EnumDecl); + DeclResult = parseDeclEnum(Flags, Attributes); + break; + case tok::kw_case: { + llvm::SmallVector Entries; + DeclParsingContext.setCreateSyntax(SyntaxKind::EnumCaseDecl); + DeclResult = parseDeclEnumCase(Flags, Attributes, Entries); + std::for_each(Entries.begin(), Entries.end(), Handler); + if (auto *D = DeclResult.getPtrOrNull()) + markWasHandled(D); + break; + } + case tok::kw_class: + DeclParsingContext.setCreateSyntax(SyntaxKind::ClassDecl); + DeclResult = parseDeclClass(Flags, Attributes); + break; + case tok::kw_struct: + DeclParsingContext.setCreateSyntax(SyntaxKind::StructDecl); + DeclResult = parseDeclStruct(Flags, Attributes); + break; + case tok::kw_init: + DeclParsingContext.setCreateSyntax(SyntaxKind::InitializerDecl); + DeclResult = parseDeclInit(Flags, Attributes); + break; + case tok::kw_deinit: + DeclParsingContext.setCreateSyntax(SyntaxKind::DeinitializerDecl); + DeclResult = parseDeclDeinit(Flags, Attributes); + break; + case tok::kw_operator: + DeclParsingContext.setCreateSyntax(SyntaxKind::OperatorDecl); + DeclResult = parseDeclOperator(Flags, Attributes); + break; + case tok::kw_precedencegroup: + DeclParsingContext.setCreateSyntax(SyntaxKind::PrecedenceGroupDecl); + DeclResult = parseDeclPrecedenceGroup(Flags, Attributes); + break; + case tok::kw_protocol: + DeclParsingContext.setCreateSyntax(SyntaxKind::ProtocolDecl); + DeclResult = parseDeclProtocol(Flags, Attributes); + break; + case tok::kw_func: + // Collect all modifiers into a modifier list. + DeclParsingContext.setCreateSyntax(SyntaxKind::FunctionDecl); + DeclResult = parseDeclFunc(StaticLoc, StaticSpelling, Flags, Attributes); + StaticLoc = SourceLoc(); // we handled static if present. + MayNeedOverrideCompletion = true; + break; + case tok::kw_subscript: { + DeclParsingContext.setCreateSyntax(SyntaxKind::SubscriptDecl); + if (StaticLoc.isValid()) { + diagnose(Tok, diag::subscript_static, StaticSpelling) + .fixItRemove(SourceRange(StaticLoc)); + StaticLoc = SourceLoc(); } + llvm::SmallVector Entries; + DeclResult = parseDeclSubscript(Flags, Attributes, Entries); + std::for_each(Entries.begin(), Entries.end(), Handler); + MayNeedOverrideCompletion = true; + if (auto *D = DeclResult.getPtrOrNull()) + markWasHandled(D); + break; + } - case tok::pound_warning: - DeclParsingContext.setCreateSyntax(SyntaxKind::PoundWarningDecl); - DeclResult = parseDeclPoundDiagnostic(); - break; - case tok::pound_error: - DeclParsingContext.setCreateSyntax(SyntaxKind::PoundErrorDecl); - DeclResult = parseDeclPoundDiagnostic(); - break; + case tok::code_complete: + MayNeedOverrideCompletion = true; + DeclResult = makeParserError(); + // Handled below. + break; - // Context sensitive keywords. - case tok::identifier: { - Optional Kind; - // FIXME: This is ridiculous, this all needs to be sucked into the - // declparsing goop. - if (Tok.isContextualKeyword("open")) { - Kind = DAK_AccessControl; - } else if (Tok.isContextualKeyword("weak") || - Tok.isContextualKeyword("unowned")) { - Kind = DAK_ReferenceOwnership; - } else if (Tok.isContextualKeyword("optional")) { - Kind = DAK_Optional; - } else if (Tok.isContextualKeyword("required")) { - Kind = DAK_Required; - } else if (Tok.isContextualKeyword("lazy")) { - Kind = DAK_Lazy; - } else if (Tok.isContextualKeyword("final")) { - Kind = DAK_Final; - } else if (Tok.isContextualKeyword("dynamic")) { - Kind = DAK_Dynamic; - } else if (Tok.isContextualKeyword("prefix")) { - Kind = DAK_Prefix; - } else if (Tok.isContextualKeyword("postfix")) { - Kind = DAK_Postfix; - } else if (Tok.isContextualKeyword("indirect")) { - Kind = DAK_Indirect; - } else if (Tok.isContextualKeyword("infix")) { - Kind = DAK_Infix; - } else if (Tok.isContextualKeyword("override")) { - Kind = DAK_Override; - } else if (Tok.isContextualKeyword("mutating")) { - Kind = DAK_Mutating; - } else if (Tok.isContextualKeyword("nonmutating")) { - Kind = DAK_NonMutating; - } else if (Tok.isContextualKeyword("__consuming")) { - Kind = DAK_Consuming; - } else if (Tok.isContextualKeyword("convenience")) { - Kind = DAK_Convenience; - } - if (Kind) { - SyntaxParsingContext ModContext(SyntaxContext, SyntaxKind::DeclModifier); - parseNewDeclAttribute(Attributes, SourceLoc(), *Kind); - continue; - } + case tok::pound_if: + case tok::pound_sourceLocation: + case tok::pound_line: + case tok::pound_warning: + case tok::pound_error: + // We see some attributes right before these pounds. + // TODO: Emit dedicated errors for them. + LLVM_FALLTHROUGH; - // Otherwise this is not a context-sensitive keyword. - LLVM_FALLTHROUGH; + // Obvious nonsense. + default: + if (FoundCCTokenInAttr) { + if (!CodeCompletion) { + delayParseFromBeginningToHere(BeginParserPosition, Flags); + } else { + CodeCompletion->completeDeclAttrKeyword(nullptr, isInSILMode(), false); + } } - case tok::pound_if: - case tok::pound_sourceLocation: - case tok::pound_line: - // We see some attributes right before these pounds. - // TODO: Emit dedicated errors for them. - LLVM_FALLTHROUGH; - - // Obvious nonsense. - default: - if (FoundCCTokenInAttr) { - if (!CodeCompletion) { - delayParseFromBeginningToHere(BeginParserPosition, Flags); - } else { - CodeCompletion->completeDeclAttrKeyword(nullptr, isInSILMode(), - false); - } - } + diagnose(Tok, diag::expected_decl); - diagnose(Tok, diag::expected_decl); - - if (CurDeclContext) { - if (auto nominal = dyn_cast(CurDeclContext)) { - diagnose(nominal->getLoc(), diag::note_in_decl_extension, false, - nominal->getName()); - } else if (auto extension = dyn_cast(CurDeclContext)) { - if (auto repr = extension->getExtendedTypeLoc().getTypeRepr()) { - if (auto idRepr = dyn_cast(repr)) { - diagnose(extension->getLoc(), diag::note_in_decl_extension, true, - idRepr->getComponentRange().front()->getIdentifier()); - } + if (CurDeclContext) { + if (auto nominal = dyn_cast(CurDeclContext)) { + diagnose(nominal->getLoc(), diag::note_in_decl_extension, false, + nominal->getName()); + } else if (auto extension = dyn_cast(CurDeclContext)) { + if (auto repr = extension->getExtendedTypeLoc().getTypeRepr()) { + if (auto idRepr = dyn_cast(repr)) { + diagnose(extension->getLoc(), diag::note_in_decl_extension, true, + idRepr->getComponentRange().front()->getIdentifier()); } } } - return makeParserErrorResult(); - - case tok::unknown: - consumeToken(tok::unknown); - continue; - - // Unambiguous top level decls. - case tok::kw_import: - DeclParsingContext.setCreateSyntax(SyntaxKind::ImportDecl); - DeclResult = parseDeclImport(Flags, Attributes); - break; - case tok::kw_extension: { - DeclParsingContext.setCreateSyntax(SyntaxKind::ExtensionDecl); - DeclResult = parseDeclExtension(Flags, Attributes); - break; - } - case tok::kw_let: - case tok::kw_var: { - // Collect all modifiers into a modifier list. - DeclParsingContext.collectNodesInPlace(SyntaxKind::ModifierList); - DeclParsingContext.setCreateSyntax(SyntaxKind::VariableDecl); - llvm::SmallVector Entries; - DeclResult = parseDeclVar(Flags, Attributes, Entries, StaticLoc, - StaticSpelling, tryLoc); - StaticLoc = SourceLoc(); // we handled static if present. - MayNeedOverrideCompletion = true; - std::for_each(Entries.begin(), Entries.end(), Handler); - if (auto *D = DeclResult.getPtrOrNull()) - markWasHandled(D); - break; - } - case tok::kw_typealias: - DeclParsingContext.setCreateSyntax(SyntaxKind::TypealiasDecl); - DeclResult = parseDeclTypeAlias(Flags, Attributes); - MayNeedOverrideCompletion = true; - break; - case tok::kw_associatedtype: - DeclParsingContext.setCreateSyntax(SyntaxKind::AssociatedtypeDecl); - DeclResult = parseDeclAssociatedType(Flags, Attributes); - break; - case tok::kw_enum: - DeclResult = parseDeclEnum(Flags, Attributes); - break; - case tok::kw_case: { - llvm::SmallVector Entries; - DeclResult = parseDeclEnumCase(Flags, Attributes, Entries); - std::for_each(Entries.begin(), Entries.end(), Handler); - if (auto *D = DeclResult.getPtrOrNull()) - markWasHandled(D); - break; - } - case tok::kw_struct: { - DeclParsingContext.setCreateSyntax(SyntaxKind::StructDecl); - DeclResult = parseDeclStruct(Flags, Attributes); - break; } - case tok::kw_init: - DeclParsingContext.collectNodesInPlace(SyntaxKind::ModifierList); - DeclParsingContext.setCreateSyntax(SyntaxKind::InitializerDecl); - DeclResult = parseDeclInit(Flags, Attributes); - break; - case tok::kw_deinit: - DeclParsingContext.collectNodesInPlace(SyntaxKind::ModifierList); - DeclParsingContext.setCreateSyntax(SyntaxKind::DeinitializerDecl); - DeclResult = parseDeclDeinit(Flags, Attributes); - break; - case tok::kw_operator: - DeclResult = parseDeclOperator(Flags, Attributes); - break; - case tok::kw_precedencegroup: - DeclResult = parseDeclPrecedenceGroup(Flags, Attributes); - break; - case tok::kw_protocol: { - DeclParsingContext.setCreateSyntax(SyntaxKind::ProtocolDecl); - DeclResult = parseDeclProtocol(Flags, Attributes); - break; - } - case tok::kw_func: - // Collect all modifiers into a modifier list. - DeclParsingContext.collectNodesInPlace(SyntaxKind::ModifierList); - DeclParsingContext.setCreateSyntax(SyntaxKind::FunctionDecl); - DeclResult = parseDeclFunc(StaticLoc, StaticSpelling, Flags, Attributes); - StaticLoc = SourceLoc(); // we handled static if present. - MayNeedOverrideCompletion = true; - break; + return makeParserErrorResult(); + } - case tok::kw_subscript: { - DeclParsingContext.collectNodesInPlace(SyntaxKind::ModifierList); - DeclParsingContext.setCreateSyntax(SyntaxKind::SubscriptDecl); - if (StaticLoc.isValid()) { - diagnose(Tok, diag::subscript_static, StaticSpelling) - .fixItRemove(SourceRange(StaticLoc)); - StaticLoc = SourceLoc(); + if (DeclResult.isParseError() && MayNeedOverrideCompletion && + Tok.is(tok::code_complete)) { + DeclResult = makeParserCodeCompletionStatus(); + if (CodeCompletion) { + // If we need to complete an override, collect the keywords already + // specified so that we do not duplicate them in code completion + // strings. + SmallVector Keywords; + switch (OrigTok.getKind()) { + case tok::kw_func: + case tok::kw_subscript: + case tok::kw_var: + case tok::kw_let: + case tok::kw_typealias: + Keywords.push_back(OrigTok.getText()); + break; + default: + // Other tokens are already accounted for. + break; } - llvm::SmallVector Entries; - DeclResult = parseDeclSubscript(Flags, Attributes, Entries); - std::for_each(Entries.begin(), Entries.end(), Handler); - MayNeedOverrideCompletion = true; - if (auto *D = DeclResult.getPtrOrNull()) - markWasHandled(D); - break; - } - - case tok::code_complete: - MayNeedOverrideCompletion = true; - DeclResult = makeParserError(); - // Handled below. - break; - } - - if (DeclResult.isParseError() && MayNeedOverrideCompletion && - Tok.is(tok::code_complete)) { - DeclResult = makeParserCodeCompletionStatus(); - if (CodeCompletion) { - // If we need to complete an override, collect the keywords already - // specified so that we do not duplicate them in code completion - // strings. - SmallVector Keywords; - switch (OrigTok.getKind()) { - case tok::kw_func: - case tok::kw_subscript: - case tok::kw_var: - case tok::kw_let: - case tok::kw_typealias: - Keywords.push_back(OrigTok.getText()); - break; - default: - // Other tokens are already accounted for. - break; - } - for (auto attr : Attributes) { - Keywords.push_back(attr->getAttrName()); - } - CodeCompletion->completeNominalMemberBeginning(Keywords); + for (auto attr : Attributes) { + Keywords.push_back(attr->getAttrName()); } + CodeCompletion->completeNominalMemberBeginning(Keywords); } - - // If we 'break' out of the switch, break out of the loop too. - break; } if (auto SF = CurDeclContext->getParentSourceFile()) { @@ -2821,9 +2925,8 @@ ParserStatus Parser::parseInheritance(SmallVectorImpl &Inherited, }; // Parse the 'class' keyword for a class requirement. if (Tok.is(tok::kw_class)) { - // FIXME: class requirement will turn to an unknown type in libSyntax tree. SyntaxParsingContext ClassTypeContext(SyntaxContext, - SyntaxContextKind::Type); + SyntaxKind::ClassRestrictionType); // If we aren't allowed to have a class requirement here, complain. auto classLoc = consumeToken(); if (!allowClassRequirement) { @@ -3008,7 +3111,6 @@ void Parser::diagnoseConsecutiveIDs(StringRef First, SourceLoc FirstLoc, ParserStatus Parser::parseDeclItem(bool &PreviousHadSemi, Parser::ParseDeclOptions Options, llvm::function_ref handler) { - SyntaxParsingContext DeclContext(SyntaxContext, SyntaxContextKind::Decl); if (Tok.is(tok::semi)) { // Consume ';' without preceding decl. diagnose(Tok, diag::unexpected_separator, ";") @@ -3033,7 +3135,13 @@ ParserStatus Parser::parseDeclItem(bool &PreviousHadSemi, return LineDirectiveStatus; } - auto Result = parseDecl(Options, handler); + ParserResult Result; + SyntaxParsingContext DeclContext(SyntaxContext, + SyntaxKind::MemberDeclListItem); + if (loadCurrentSyntaxNodeFromCache()) { + return ParserStatus(); + } + Result = parseDecl(Options, handler); if (Result.isParseError()) skipUntilDeclRBrace(tok::semi, tok::pound_endif); SourceLoc SemiLoc; @@ -3054,7 +3162,7 @@ bool Parser::parseDeclList(SourceLoc LBLoc, SourceLoc &RBLoc, ParserStatus Status; bool PreviousHadSemi = true; { - SyntaxParsingContext ListContext(SyntaxContext, SyntaxKind::DeclList); + SyntaxParsingContext ListContext(SyntaxContext, SyntaxKind::MemberDeclList); while (Tok.isNot(tok::r_brace)) { Status |= parseDeclItem(PreviousHadSemi, Options, handler); if (Tok.isAny(tok::eof, tok::pound_endif, tok::pound_else, @@ -3244,6 +3352,8 @@ ParserResult Parser::parseDeclPoundDiagnostic() { } ParserStatus Parser::parseLineDirective(bool isLine) { + SyntaxParsingContext PoundSourceLocation(SyntaxContext, + SyntaxKind::PoundSourceLocation); SourceLoc Loc = consumeToken(); if (isLine) { diagnose(Loc, diag::line_directive_style_deprecated) @@ -3273,40 +3383,47 @@ ParserStatus Parser::parseLineDirective(bool isLine) { } return makeParserSuccess(); } - - if (parseSpecificIdentifier("file", diag::sourceLocation_expected,"file:")|| - parseToken(tok::colon, diag::sourceLocation_expected, ":")) - return makeParserError(); - if (Tok.isNot(tok::string_literal)) { - diagnose(Tok, diag::expected_line_directive_name); - return makeParserError(); - } - - Filename = getStringLiteralIfNotInterpolated(*this, Loc, Tok, - "#sourceLocation"); - if (!Filename.hasValue()) - return makeParserError(); - consumeToken(tok::string_literal); - - if (parseToken(tok::comma, diag::sourceLocation_expected, ",") || - parseSpecificIdentifier("line", diag::sourceLocation_expected,"line:")|| - parseToken(tok::colon, diag::sourceLocation_expected, ":")) - return makeParserError(); - - if (Tok.isNot(tok::integer_literal)) { - diagnose(Tok, diag::expected_line_directive_number); - return makeParserError(); - } - if (Tok.getText().getAsInteger(0, StartLine)) { - diagnose(Tok, diag::expected_line_directive_number); - return makeParserError(); - } - if (StartLine == 0) { - diagnose(Tok, diag::line_directive_line_zero); - return makeParserError(); + { + SyntaxParsingContext Args(SyntaxContext, + SyntaxKind::PoundSourceLocationArgs); + + if (parseSpecificIdentifier("file", diag::sourceLocation_expected, + "file:") || + parseToken(tok::colon, diag::sourceLocation_expected, ":")) + return makeParserError(); + + if (Tok.isNot(tok::string_literal)) { + diagnose(Tok, diag::expected_line_directive_name); + return makeParserError(); + } + + Filename = + getStringLiteralIfNotInterpolated(*this, Loc, Tok, "#sourceLocation"); + if (!Filename.hasValue()) + return makeParserError(); + consumeToken(tok::string_literal); + + if (parseToken(tok::comma, diag::sourceLocation_expected, ",") || + parseSpecificIdentifier("line", diag::sourceLocation_expected, + "line:") || + parseToken(tok::colon, diag::sourceLocation_expected, ":")) + return makeParserError(); + + if (Tok.isNot(tok::integer_literal)) { + diagnose(Tok, diag::expected_line_directive_number); + return makeParserError(); + } + if (Tok.getText().getAsInteger(0, StartLine)) { + diagnose(Tok, diag::expected_line_directive_number); + return makeParserError(); + } + if (StartLine == 0) { + diagnose(Tok, diag::line_directive_line_zero); + return makeParserError(); + } + consumeToken(tok::integer_literal); } - consumeToken(tok::integer_literal); LastTokTextEnd = Tok.getText().end(); if (parseToken(tok::r_paren, diag::sourceLocation_expected, ")")) @@ -3431,10 +3548,16 @@ parseDeclTypeAlias(Parser::ParseDeclOptions Flags, DeclAttributes &Attributes) { return parseDeclAssociatedType(Flags, Attributes); } TmpCtxt.reset(); - + + auto *TAD = new (Context) TypeAliasDecl(TypeAliasLoc, EqualLoc, Id, IdLoc, + /*genericParams*/nullptr, + CurDeclContext); + ParserResult UnderlyingTy; if (Tok.is(tok::colon) || Tok.is(tok::equal)) { + ContextChange CC(*this, TAD); + SyntaxParsingContext InitCtx(SyntaxContext, SyntaxKind::TypeInitializerClause); if (Tok.is(tok::colon)) { @@ -3442,20 +3565,15 @@ parseDeclTypeAlias(Parser::ParseDeclOptions Flags, DeclAttributes &Attributes) { // Recognize this and produce a fixit. diagnose(Tok, diag::expected_equal_in_typealias) .fixItReplace(Tok.getLoc(), " = "); - consumeToken(tok::colon); + EqualLoc = consumeToken(tok::colon); } else { EqualLoc = consumeToken(tok::equal); } UnderlyingTy = parseType(diag::expected_type_in_typealias); Status |= UnderlyingTy; - if (UnderlyingTy.isNull()) - return Status; } - auto *TAD = new (Context) TypeAliasDecl(TypeAliasLoc, EqualLoc, Id, IdLoc, - /*genericParams*/nullptr, - CurDeclContext); TAD->getUnderlyingTypeLoc() = UnderlyingTy.getPtrOrNull(); TAD->getAttrs() = Attributes; @@ -3469,8 +3587,13 @@ parseDeclTypeAlias(Parser::ParseDeclOptions Flags, DeclAttributes &Attributes) { TAD->setGenericParams(genericParams); if (UnderlyingTy.isNull()) { - diagnose(Tok, diag::expected_equal_in_typealias); - Status.setIsParseError(); + // If there is an attempt to do code completion + // inside of typealias type, let's just return + // because we've seen required '=' token. + if (EqualLoc.isInvalid()) { + diagnose(Tok, diag::expected_equal_in_typealias); + Status.setIsParseError(); + } return Status; } @@ -3772,7 +3895,13 @@ static ParameterList *parseOptionalAccessorArgument(SourceLoc SpecifierLoc, return ParameterList::create(P.Context, StartLoc, param, EndLoc); } -static unsigned skipUntilMatchingRBrace(Parser &P) { +static unsigned skipUntilMatchingRBrace(Parser &P, + SyntaxParsingContext *&SyntaxContext) { + SyntaxParsingContext BlockItemListContext(SyntaxContext, + SyntaxKind::CodeBlockItemList); + SyntaxParsingContext BlockItemContext(SyntaxContext, + SyntaxKind::CodeBlockItem); + SyntaxParsingContext BodyContext(SyntaxContext, SyntaxKind::TokenList); unsigned OpenBraces = 1; while (OpenBraces != 0 && P.Tok.isNot(tok::eof)) { if (P.consumeIf(tok::l_brace)) { @@ -3790,9 +3919,11 @@ static unsigned skipUntilMatchingRBrace(Parser &P) { return OpenBraces; } -static unsigned skipBracedBlock(Parser &P) { +static unsigned skipBracedBlock(Parser &P, + SyntaxParsingContext *&SyntaxContext) { + SyntaxParsingContext CodeBlockContext(SyntaxContext, SyntaxKind::CodeBlock); P.consumeToken(tok::l_brace); - unsigned OpenBraces = skipUntilMatchingRBrace(P); + unsigned OpenBraces = skipUntilMatchingRBrace(P, SyntaxContext); if (P.consumeIf(tok::r_brace)) OpenBraces--; return OpenBraces; @@ -3806,7 +3937,7 @@ void Parser::consumeGetSetBody(AbstractFunctionDecl *AFD, BodyRange.Start = Tok.getLoc(); // Skip until the next '}' at the correct nesting level. - unsigned OpenBraces = skipUntilMatchingRBrace(*this); + unsigned OpenBraces = skipUntilMatchingRBrace(*this, SyntaxContext); if (OpenBraces != 1) { // FIXME: implement some error recovery? @@ -4146,6 +4277,7 @@ bool Parser::parseGetSetImpl(ParseDeclOptions Flags, return true; } ExternalAsmName = true; + BlockCtx.setTransparent(); } // Set up a function declaration. @@ -4169,6 +4301,8 @@ bool Parser::parseGetSetImpl(ParseDeclOptions Flags, // Establish the new context. ParseFunctionBody CC(*this, TheDecl); + for (auto PL : TheDecl->getParameterLists()) + setLocalDiscriminatorToParamList(PL); // Parse the body. SmallVector Entries; @@ -4260,6 +4394,8 @@ void Parser::parseAccessorBodyDelayed(AbstractFunctionDecl *AFD) { // Re-enter the lexical scope. Scope S(this, AccessorParserState->takeScope()); ParseFunctionBody CC(*this, AFD); + for (auto PL : AFD->getParameterLists()) + setLocalDiscriminatorToParamList(PL); SmallVector Entries; parseBraceItems(Entries); @@ -4357,8 +4493,6 @@ VarDecl *Parser::parseDeclVarGetSet(Pattern *pattern, if (!PrimaryVar || !primaryVarIsWellFormed) { diagnose(pattern->getLoc(), diag::getset_nontrivial_pattern); Invalid = true; - } else { - setLocalDiscriminator(PrimaryVar); } TypeLoc TyLoc; @@ -4768,6 +4902,7 @@ Parser::parseDeclVar(ParseDeclOptions Flags, pattern->forEachVariable([&](VarDecl *VD) { VD->setStatic(StaticLoc.isValid()); VD->getAttrs() = Attributes; + setLocalDiscriminator(VD); Decls.push_back(VD); }); @@ -5004,7 +5139,7 @@ void Parser::consumeAbstractFunctionBody(AbstractFunctionDecl *AFD, BodyRange.Start = Tok.getLoc(); // Consume the '{', and find the matching '}'. - unsigned OpenBraces = skipBracedBlock(*this); + unsigned OpenBraces = skipBracedBlock(*this, SyntaxContext); if (OpenBraces != 0 && Tok.isNot(tok::code_complete)) { assert(Tok.is(tok::eof)); // We hit EOF, and not every brace has a pair. Recover by searching @@ -5213,6 +5348,8 @@ Parser::parseDeclFunc(SourceLoc StaticLoc, StaticSpellingKind StaticSpelling, // Establish the new context. ParseFunctionBody CC(*this, FD); + for (auto PL : FD->getParameterLists()) + setLocalDiscriminatorToParamList(PL); // Check to see if we have a "{" to start a brace statement. if (Tok.is(tok::l_brace)) { @@ -5283,6 +5420,8 @@ bool Parser::parseAbstractFunctionBodyDelayed(AbstractFunctionDecl *AFD) { // Re-enter the lexical scope. Scope S(this, FunctionParserState->takeScope()); ParseFunctionBody CC(*this, AFD); + for (auto PL : AFD->getParameterLists()) + setLocalDiscriminatorToParamList(PL); ParserResult Body = parseBraceItemList(diag::func_decl_without_brace); @@ -5362,6 +5501,7 @@ ParserResult Parser::parseDeclEnum(ParseDeclOptions Flags, ED->setGenericParams(GenericParams); + SyntaxParsingContext BlockContext(SyntaxContext, SyntaxKind::MemberDeclBlock); SourceLoc LBLoc, RBLoc; if (parseToken(tok::l_brace, LBLoc, diag::expected_lbrace_enum)) { LBLoc = PreviousLoc; @@ -5402,6 +5542,8 @@ Parser::parseDeclEnumCase(ParseDeclOptions Flags, SourceLoc CommaLoc; for (;;) { + SyntaxParsingContext ElementContext(SyntaxContext, + SyntaxKind::EnumCaseElement); Identifier Name; SourceLoc NameLoc; @@ -5470,6 +5612,9 @@ Parser::parseDeclEnumCase(ParseDeclOptions Flags, ParserResult RawValueExpr; LiteralExpr *LiteralRawValueExpr = nullptr; if (Tok.is(tok::equal)) { + SyntaxParsingContext InitContext(SyntaxContext, + SyntaxKind::InitializerClause); + EqualsLoc = consumeToken(); { CodeCompletionCallbacks::InEnumElementRawValueRAII @@ -5540,6 +5685,7 @@ Parser::parseDeclEnumCase(ParseDeclOptions Flags, break; CommaLoc = consumeToken(tok::comma); } + SyntaxContext->collectNodesInPlace(SyntaxKind::EnumCaseElementList); if (!(Flags & PD_AllowEnumElement)) { diagnose(CaseLoc, diag::disallowed_enum_element); @@ -5658,9 +5804,10 @@ ParserResult Parser::parseDeclStruct(ParseDeclOptions Flags, /// decl-class-body: /// decl* /// \endverbatim -ParserResult Parser::parseDeclClass(SourceLoc ClassLoc, - ParseDeclOptions Flags, +ParserResult Parser::parseDeclClass(ParseDeclOptions Flags, DeclAttributes &Attributes) { + SourceLoc ClassLoc = consumeToken(tok::kw_class); + Identifier ClassName; SourceLoc ClassNameLoc; ParserStatus Status; @@ -5700,7 +5847,30 @@ ParserResult Parser::parseDeclClass(SourceLoc ClassLoc, /*allowClassRequirement=*/false, /*allowAnyObject=*/false); CD->setInherited(Context.AllocateCopy(Inherited)); - } + + // Parse python style inheritance clause and replace parentheses with a colon + } else if (Tok.is(tok::l_paren)) { + bool isParenStyleInheritance = false; + { + BacktrackingScope backtrack(*this); + consumeToken(tok::l_paren); + isParenStyleInheritance = canParseType() && + Tok.isAny(tok::r_paren, tok::kw_where, tok::l_brace, tok::eof); + } + if(isParenStyleInheritance) { + SourceLoc LParenLoc = consumeToken(tok::l_paren); + auto TypeResult = parseType(); + if (TypeResult.isNull()) { + Status.setIsParseError(); + return Status; + } + SourceLoc RParenLoc; + consumeIf(tok::r_paren, RParenLoc); + diagnose(LParenLoc, diag::expected_colon_class) + .fixItReplace(LParenLoc, ": ") + .fixItRemove(RParenLoc); + } + } diagnoseWhereClauseInGenericParamList(GenericParams); @@ -5852,6 +6022,14 @@ Parser::parseDeclSubscript(ParseDeclOptions Flags, ParserStatus Status; SourceLoc SubscriptLoc = consumeToken(tok::kw_subscript); + // Diagnose 'subscript' with name. + if (Tok.is(tok::identifier) && + (peekToken().is(tok::l_paren) || startsWithLess(peekToken()))) { + diagnose(Tok, diag::subscript_has_name) + .fixItRemove(Tok.getLoc()); + consumeToken(tok::identifier); + } + // Parse the generic-params, if present. Optional GenericsScope; GenericsScope.emplace(this, ScopeKind::Generics); @@ -6003,6 +6181,14 @@ Parser::parseDeclInit(ParseDeclOptions Flags, DeclAttributes &Attributes) { FailabilityLoc = consumeToken(); } + // Reject named 'init'. e.g. 'init withString(string: str)'. + if (Tok.is(tok::identifier) && + (peekToken().is(tok::l_paren) || startsWithLess(peekToken()))) { + diagnose(Tok, diag::initializer_has_name) + .fixItRemove(Tok.getLoc()); + consumeToken(tok::identifier); + } + // Parse the generic-params, if present. Scope S(this, ScopeKind::Generics); auto GPResult = maybeParseGenericParams(); @@ -6098,6 +6284,8 @@ Parser::parseDeclInit(ParseDeclOptions Flags, DeclAttributes &Attributes) { } else { // Parse the body. ParseFunctionBody CC(*this, CD); + for (auto PL : CD->getParameterLists()) + setLocalDiscriminatorToParamList(PL); if (!isDelayedParsingEnabled()) { if (Context.Stats) @@ -6254,7 +6442,9 @@ Parser::parseDeclOperatorImpl(SourceLoc OperatorLoc, Identifier Name, SourceLoc colonLoc; Identifier precedenceGroupName; SourceLoc precedenceGroupNameLoc; - if (consumeIf(tok::colon, colonLoc)) { + if (Tok.is(tok::colon)) { + SyntaxParsingContext GroupCtxt(SyntaxContext, SyntaxKind::InfixOperatorGroup); + colonLoc = consumeToken(); if (Tok.is(tok::identifier)) { precedenceGroupName = Context.getIdentifier(Tok.getText()); precedenceGroupNameLoc = consumeToken(tok::identifier); @@ -6334,7 +6524,7 @@ Parser::parseDeclPrecedenceGroup(ParseDeclOptions flags, (void) consumeIf(tok::r_brace); } else if (Tok.isNot(tok::eof) && peekToken().is(tok::l_brace)) { consumeToken(); - skipBracedBlock(*this); + skipBracedBlock(*this, SyntaxContext); } return nullptr; } @@ -6381,6 +6571,14 @@ Parser::parseDeclPrecedenceGroup(ParseDeclOptions flags, diagnose(Tok, diag::expected_precedencegroup_lbrace); return createInvalid(); } + // Empty body. + if (Tok.is(tok::r_brace)) { + // Create empty attribute list. + SyntaxParsingContext(SyntaxContext, + SyntaxKind::PrecedenceGroupAttributeList); + rbraceLoc = consumeToken(tok::r_brace); + return makeParserResult(create()); + } auto abortBody = [&] { skipUntilDeclRBrace(); @@ -6402,8 +6600,9 @@ Parser::parseDeclPrecedenceGroup(ParseDeclOptions flags, } }; + // Parse the attributes in the body. - while (!consumeIf(tok::r_brace, rbraceLoc)) { + while (!Tok.is(tok::r_brace)) { if (!Tok.is(tok::identifier)) { diagnose(Tok, diag::expected_precedencegroup_attribute); return abortBody(); @@ -6412,6 +6611,8 @@ Parser::parseDeclPrecedenceGroup(ParseDeclOptions flags, auto attrName = Tok.getText(); if (attrName == "associativity") { + SyntaxParsingContext AttrCtxt(SyntaxContext, + SyntaxKind::PrecedenceGroupAssociativity); // "associativity" is considered as a contextual keyword. TokReceiver->registerTokenKindChange(Tok.getLoc(), tok::contextual_keyword); @@ -6438,6 +6639,8 @@ Parser::parseDeclPrecedenceGroup(ParseDeclOptions flags, } if (attrName == "assignment") { + SyntaxParsingContext AttrCtxt(SyntaxContext, + SyntaxKind::PrecedenceGroupAssignment); parseAttributePrefix(assignmentKeywordLoc); // "assignment" is considered as a contextual keyword. @@ -6457,6 +6660,8 @@ Parser::parseDeclPrecedenceGroup(ParseDeclOptions flags, bool isLowerThan = false; if (attrName == "higherThan" || (isLowerThan = (attrName == "lowerThan"))) { + SyntaxParsingContext AttrCtxt(SyntaxContext, + SyntaxKind::PrecedenceGroupRelation); // "lowerThan" and "higherThan" are contextual keywords. TokReceiver->registerTokenKindChange(Tok.getLoc(), tok::contextual_keyword); @@ -6465,6 +6670,8 @@ Parser::parseDeclPrecedenceGroup(ParseDeclOptions flags, auto &relations = (isLowerThan ? lowerThan : higherThan); do { + SyntaxParsingContext NameCtxt(SyntaxContext, + SyntaxKind::PrecedenceGroupNameElement); if (!Tok.is(tok::identifier)) { diagnose(Tok, diag::expected_precedencegroup_relation, attrName); return abortBody(); @@ -6472,13 +6679,19 @@ Parser::parseDeclPrecedenceGroup(ParseDeclOptions flags, auto name = Context.getIdentifier(Tok.getText()); auto loc = consumeToken(); relations.push_back({loc, name, nullptr}); - } while (consumeIf(tok::comma)); + if (!consumeIf(tok::comma)) + break; + } while (true); + SyntaxContext->collectNodesInPlace(SyntaxKind::PrecedenceGroupNameList); continue; } diagnose(Tok, diag::unknown_precedencegroup_attribute, attrName); return abortBody(); } + SyntaxContext->collectNodesInPlace(SyntaxKind::PrecedenceGroupAttributeList); + rbraceLoc = consumeToken(tok::r_brace); + auto result = create(); if (invalid) result->setInvalid(); diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index b250a813dc391..d8c47bc496ced 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -582,7 +582,6 @@ ParserResult Parser::parseExprKeyPath() { // Consume '\'. SourceLoc backslashLoc = consumeToken(tok::backslash); llvm::SaveAndRestore slashLoc(SwiftKeyPathSlashLoc, backslashLoc); - SyntaxParsingContext ExprCtx(SyntaxContext, SyntaxContextKind::Expr); // FIXME: diagnostics ParserResult rootResult, pathResult; @@ -598,14 +597,19 @@ ParserResult Parser::parseExprKeyPath() { if (startsWithSymbol(Tok, '.')) { llvm::SaveAndRestore S(SwiftKeyPathRoot, rootResult.getPtrOrNull()); + SyntaxParsingContext ExprContext(SyntaxContext, SyntaxContextKind::Expr); + auto dotLoc = Tok.getLoc(); // For uniformity, \.foo is parsed as if it were MAGIC.foo, so we need to // make sure the . is there, but parsing the ? in \.? as .? doesn't make // sense. This is all made more complicated by .?. being considered an // operator token, and a single one at that (which means // peekToken().is(tok::identifier) is incorrect: it is true for .?.foo). - if (Tok.getLength() != 1 || !peekToken().is(tok::identifier)) + if (Tok.getLength() != 1 || !peekToken().is(tok::identifier)) { + SyntaxParsingContext KeyPathBaseContext(SyntaxContext, + SyntaxKind::KeyPathBaseExpr); consumeStartingCharacterOfCurrentToken(tok::period); + } auto inner = makeParserResult(new (Context) KeyPathDotExpr(dotLoc)); bool unusedHasBindOptional = false; @@ -727,6 +731,7 @@ ParserResult Parser::parseExprKeyPathObjC() { /// '#selector' '(' 'setter' ':' expr ')' /// ParserResult Parser::parseExprSelector() { + SyntaxParsingContext ExprCtxt(SyntaxContext, SyntaxKind::ObjcSelectorExpr); // Consume '#selector'. SourceLoc keywordLoc = consumeToken(tok::pound_selector); @@ -750,7 +755,8 @@ ParserResult Parser::parseExprSelector() { else selectorKind = ObjCSelectorExpr::Setter; - modifierLoc = consumeToken(tok::identifier); + Tok.setKind(tok::contextual_keyword); + modifierLoc = consumeToken(); (void)consumeToken(tok::colon); } else { selectorKind = ObjCSelectorExpr::Method; @@ -1905,6 +1911,19 @@ parseStringSegments(SmallVectorImpl &Segments, tok::string_interpolation_anchor); ParserResult E = parseExprList(tok::l_paren, tok::r_paren, SyntaxKind::Unknown); + + // If we stopped parsing the expression before the expression segment is + // over, eat the remaining tokens into a token list + if (Segment.getEndLoc() != + L->getLocForEndOfToken(SourceMgr, Tok.getLoc())) { + SyntaxParsingContext RemainingTokens(SyntaxContext, + SyntaxKind::NonEmptyTokenList); + do { + consumeToken(); + } while (Segment.getEndLoc() != + L->getLocForEndOfToken(SourceMgr, Tok.getLoc())); + } + Status |= E; if (E.isNonNull()) { Exprs.push_back(E.get()); @@ -2017,12 +2036,7 @@ void Parser::parseOptionalArgumentLabel(Identifier &name, SourceLoc &loc) { .fixItRemoveChars(end.getAdvancedLoc(-1), end); } - auto unescapedUnderscore = underscore && !escaped; - if (!unescapedUnderscore) - name = Context.getIdentifier(text); - if (!underscore) - Tok.setKind(tok::identifier); - loc = consumeToken(); + loc = consumeArgumentLabel(name); consumeToken(tok::colon); } } @@ -2725,6 +2739,7 @@ ParserResult Parser::parseExprClosure() { if (params) { // Add the parameters into scope. addParametersToScope(params); + setLocalDiscriminatorToParamList(params); } else { // There are no parameters; allow anonymous closure variables. // FIXME: We could do this all the time, and then provide Fix-Its @@ -2964,7 +2979,7 @@ ParserStatus Parser::parseExprList(tok leftTok, tok rightTok, Expr *SubExpr = nullptr; if (Tok.isBinaryOperator() && peekToken().isAny(rightTok, tok::comma)) { SyntaxParsingContext operatorContext(SyntaxContext, - SyntaxContextKind::Expr); + SyntaxKind::IdentifierExpr); SourceLoc Loc; Identifier OperName; if (parseAnyIdentifier(OperName, Loc, diag::expected_operator_ref)) { @@ -3473,6 +3488,8 @@ ParserResult Parser::parseAvailabilitySpec() { /// "swift" version-tuple ParserResult Parser::parseLanguageVersionConstraintSpec() { + SyntaxParsingContext VersionRestrictionContext( + SyntaxContext, SyntaxKind::AvailabilityVersionRestriction); SourceLoc SwiftLoc; clang::VersionTuple Version; SourceRange VersionRange; @@ -3496,6 +3513,9 @@ Parser::parseLanguageVersionConstraintSpec() { /// identifier version-comparison version-tuple ParserResult Parser::parsePlatformVersionConstraintSpec() { + SyntaxParsingContext VersionRestrictionContext( + SyntaxContext, SyntaxKind::AvailabilityVersionRestriction); + Identifier PlatformIdentifier; SourceLoc PlatformLoc; if (Tok.is(tok::code_complete)) { @@ -3552,8 +3572,13 @@ ParserResult Parser::parseExprTypeOf() { // DynamicTypeExpr. SyntaxParsingContext CallCtxt(SyntaxContext, SyntaxKind::FunctionCallExpr); - // Consume 'type' - SourceLoc keywordLoc = consumeToken(); + SourceLoc keywordLoc; + { + SyntaxParsingContext IdentifierExprContext(SyntaxContext, + SyntaxKind::IdentifierExpr); + // Consume 'type' + keywordLoc = consumeToken(); + } // Parse the leading '('. SourceLoc lParenLoc = consumeToken(tok::l_paren); diff --git a/lib/Parse/ParsePattern.cpp b/lib/Parse/ParsePattern.cpp index 636727a343f4a..95fea250a0e2f 100644 --- a/lib/Parse/ParsePattern.cpp +++ b/lib/Parse/ParsePattern.cpp @@ -57,7 +57,7 @@ static DefaultArgumentKind getDefaultArgKind(Expr *init) { } void Parser::DefaultArgumentInfo::setFunctionContext( - DeclContext *DC, MutableArrayRef paramList){ + DeclContext *DC, ArrayRef paramList){ for (auto context : ParsedContexts) { context->changeFunction(DC, paramList); } @@ -240,23 +240,11 @@ Parser::parseParameterClause(SourceLoc &leftParenLoc, if (startsParameterName(*this, isClosure)) { // identifier-or-none for the first name - if (Tok.is(tok::kw__)) { - param.FirstNameLoc = consumeToken(); - } else { - assert(Tok.canBeArgumentLabel() && "startsParameterName() lied"); - Tok.setKind(tok::identifier); - param.FirstName = Context.getIdentifier(Tok.getText()); - param.FirstNameLoc = consumeToken(); - } + param.FirstNameLoc = consumeArgumentLabel(param.FirstName); // identifier-or-none? for the second name - if (Tok.canBeArgumentLabel()) { - if (!Tok.is(tok::kw__)) { - param.SecondName = Context.getIdentifier(Tok.getText()); - Tok.setKind(tok::identifier); - } - param.SecondNameLoc = consumeToken(); - } + if (Tok.canBeArgumentLabel()) + param.SecondNameLoc = consumeArgumentLabel(param.SecondName); // Operators, closures, and enum elements cannot have API names. if ((paramContext == ParameterContextKind::Operator || @@ -612,27 +600,18 @@ Parser::parseSingleParameterClause(ParameterContextKind paramContext, if (!Tok.is(tok::l_paren)) { // If we don't have the leading '(', complain. Diag<> diagID; - bool skipIdentifier = false; switch (paramContext) { case ParameterContextKind::Function: case ParameterContextKind::Operator: diagID = diag::func_decl_without_paren; break; - case ParameterContextKind::EnumElement: - diagID = diag::enum_element_decl_without_paren; - break; case ParameterContextKind::Subscript: - skipIdentifier = Tok.is(tok::identifier) && - peekToken().is(tok::l_paren); - diagID = skipIdentifier ? diag::subscript_has_name - : diag::expected_lparen_subscript; + diagID = diag::expected_lparen_subscript; break; case ParameterContextKind::Initializer: - skipIdentifier = Tok.is(tok::identifier) && - peekToken().is(tok::l_paren); - diagID = skipIdentifier ? diag::initializer_has_name - : diag::expected_lparen_initializer; + diagID = diag::expected_lparen_initializer; break; + case ParameterContextKind::EnumElement: case ParameterContextKind::Closure: case ParameterContextKind::Curried: llvm_unreachable("should never be here"); @@ -642,17 +621,8 @@ Parser::parseSingleParameterClause(ParameterContextKind paramContext, auto diag = diagnose(Tok, diagID); if (Tok.isAny(tok::l_brace, tok::arrow, tok::kw_throws, tok::kw_rethrows)) diag.fixItInsertAfter(PreviousLoc, "()"); - - if (skipIdentifier) - diag.fixItRemove(Tok.getLoc()); } - // We might diagnose again down here, so make sure 'diag' is out of scope. - if (skipIdentifier) { - consumeToken(); - skipSingle(); - } - // Create an empty parameter list to recover. return makeParserErrorResult( ParameterList::createEmpty(Context, PreviousLoc, PreviousLoc)); diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index 83cfc106deb28..255e0e5a3ab94 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -137,25 +137,51 @@ ParserStatus Parser::parseExprOrStmt(ASTNode &Result) { return ResultExpr; } +/// Returns whether the parser's current position is the start of a switch case, +/// given that we're in the middle of a switch already. +static bool isAtStartOfSwitchCase(Parser &parser, + bool needsToBacktrack = true) { + Optional backtrack; + + // Check for and consume attributes. The only valid attribute is `@unknown` + // but that's a semantic restriction. + while (parser.Tok.is(tok::at_sign)) { + if (!parser.peekToken().is(tok::identifier)) + return false; + + if (needsToBacktrack && !backtrack) + backtrack.emplace(parser); + + parser.consumeToken(tok::at_sign); + parser.consumeIdentifier(); + if (parser.Tok.is(tok::l_paren)) + parser.skipSingle(); + } + + return parser.Tok.isAny(tok::kw_case, tok::kw_default); +} + bool Parser::isTerminatorForBraceItemListKind(BraceItemListKind Kind, ArrayRef ParsedDecls) { switch (Kind) { case BraceItemListKind::Brace: return false; - case BraceItemListKind::Case: + case BraceItemListKind::Case: { if (Tok.is(tok::pound_if)) { + // Backtracking scopes are expensive, so avoid setting one up if possible. + Parser::BacktrackingScope Backtrack(*this); // '#if' here could be to guard 'case:' or statements in cases. // If the next non-directive line starts with 'case' or 'default', it is // for 'case's. - Parser::BacktrackingScope Backtrack(*this); do { consumeToken(); while (!Tok.isAtStartOfLine() && Tok.isNot(tok::eof)) skipSingle(); } while (Tok.isAny(tok::pound_if, tok::pound_elseif, tok::pound_else)); - return Tok.isAny(tok::kw_case, tok::kw_default); + return isAtStartOfSwitchCase(*this, /*needsToBacktrack*/false); } - return Tok.isAny(tok::kw_case, tok::kw_default); + return isAtStartOfSwitchCase(*this); + } case BraceItemListKind::TopLevelCode: // When parsing the top level executable code for a module, if we parsed // some executable code, then we're done. We want to process (name bind, @@ -269,6 +295,9 @@ ParserStatus Parser::parseBraceItems(SmallVectorImpl &Entries, !isTerminatorForBraceItemListKind(Kind, Entries))) { SyntaxParsingContext NodeContext(SyntaxContext, SyntaxKind::CodeBlockItem); + if (loadCurrentSyntaxNodeFromCache()) { + continue; + } if (Tok.is(tok::r_brace)) { SyntaxParsingContext ErrContext(SyntaxContext, SyntaxContextKind::Stmt); @@ -419,11 +448,8 @@ ParserStatus Parser::parseBraceItems(SmallVectorImpl &Entries, } if (NeedParseErrorRecovery) { - SyntaxContext->createNodeInPlace(SyntaxKind::CodeBlockItem); - SyntaxContext->setTransparent(); - - SyntaxParsingContext ItemCtxt(SyntaxContext, SyntaxKind::CodeBlockItem); - SyntaxParsingContext StmtCtxt(SyntaxContext, SyntaxContextKind::Stmt); + SyntaxParsingContext TokenListCtxt(SyntaxContext, + SyntaxKind::NonEmptyTokenList); // If we had a parse error, skip to the start of the next stmt, decl or // '{'. // @@ -611,8 +637,11 @@ ParserResult Parser::parseBraceItemList(Diag<> ID) { ParserStatus Status = parseBraceItems(Entries, BraceItemListKind::Brace, BraceItemListKind::Brace); - parseMatchingToken(tok::r_brace, RBLoc, - diag::expected_rbrace_in_brace_stmt, LBLoc); + if (parseMatchingToken(tok::r_brace, RBLoc, + diag::expected_rbrace_in_brace_stmt, LBLoc)) { + // Synthesize a r-brace if the source doesn't have any. + LocalContext.synthesize(tok::r_brace); + } return makeParserResult(Status, BraceStmt::create(Context, LBLoc, Entries, RBLoc)); @@ -946,6 +975,7 @@ static void parseGuardedPattern(Parser &P, GuardedPattern &result, // represents tuples and var patterns as tupleexprs and // unresolved_pattern_expr nodes, instead of as proper pattern nodes. patternResult.get()->forEachVariable([&](VarDecl *VD) { + P.setLocalDiscriminator(VD); if (VD->hasName()) P.addToScope(VD); boundDecls.push_back(VD); }); @@ -967,6 +997,8 @@ static void parseGuardedPattern(Parser &P, GuardedPattern &result, for (auto previous : boundDecls) { if (previous->hasName() && previous->getName() == VD->getName()) { found = true; + // Use the same local discriminator. + VD->setLocalDiscriminator(previous->getLocalDiscriminator()); break; } } @@ -1124,11 +1156,15 @@ ParserResult Parser::parseStmtConditionPoundAvailable() { ParserStatus Parser::parseAvailabilitySpecList(SmallVectorImpl &Specs) { + SyntaxParsingContext AvailabilitySpecContext( + SyntaxContext, SyntaxKind::AvailabilitySpecList); ParserStatus Status = makeParserSuccess(); // We don't use parseList() because we want to provide more specific // diagnostics disallowing operators in version specs. while (1) { + SyntaxParsingContext AvailabilityEntryContext( + SyntaxContext, SyntaxKind::AvailabilityArgument); auto SpecResult = parseAvailabilitySpec(); if (auto *Spec = SpecResult.getPtrOrNull()) { Specs.push_back(Spec); @@ -1364,6 +1400,7 @@ Parser::parseStmtConditionElement(SmallVectorImpl &result, // Add variable bindings from the pattern to our current scope and mark // them as being having a non-pattern-binding initializer. ThePattern.get()->forEachVariable([&](VarDecl *VD) { + setLocalDiscriminator(VD); if (VD->hasName()) addToScope(VD); VD->setHasNonPatternBindingInit(); @@ -1579,7 +1616,7 @@ ParserResult Parser::parseStmtGuard() { for (auto &elt : Condition) if (auto pattern = elt.getPatternOrNull()) pattern->collectVariables(Vars); - + Vars.append(DisabledVars.begin(), DisabledVars.end()); llvm::SaveAndRestore RestoreCurVars(DisabledVars, Vars); @@ -2025,7 +2062,7 @@ Parser::parseStmtCases(SmallVectorImpl &cases, bool IsActive) { ParserStatus Status; while (Tok.isNot(tok::r_brace, tok::eof, tok::pound_endif, tok::pound_elseif, tok::pound_else)) { - if (Tok.isAny(tok::kw_case, tok::kw_default)) { + if (isAtStartOfSwitchCase(*this)) { ParserResult Case = parseStmtCase(IsActive); Status |= Case; if (Case.isNonNull()) @@ -2094,12 +2131,11 @@ static ParserStatus parseStmtCase(Parser &P, SourceLoc &CaseLoc, parseGuardedPattern(P, PatternResult, Status, BoundDecls, GuardedPatternContext::Case, isFirst); LabelItems.push_back( - CaseLabelItem(/*IsDefault=*/false, PatternResult.ThePattern, - PatternResult.WhereLoc, PatternResult.Guard)); + CaseLabelItem(PatternResult.ThePattern, PatternResult.WhereLoc, + PatternResult.Guard)); isFirst = false; - if (P.consumeIf(tok::comma)) - continue; - break; + if (!P.consumeIf(tok::comma)) + break; } } @@ -2144,7 +2180,7 @@ parseStmtCaseDefault(Parser &P, SourceLoc &CaseLoc, // Create an implicit AnyPattern to represent the default match. auto Any = new (P.Context) AnyPattern(CaseLoc); LabelItems.push_back( - CaseLabelItem(/*IsDefault=*/true, Any, WhereLoc, Guard.getPtrOrNull())); + CaseLabelItem::getDefault(Any, WhereLoc, Guard.getPtrOrNull())); return Status; } @@ -2159,13 +2195,45 @@ ParserResult Parser::parseStmtCase(bool IsActive) { SmallVector CaseLabelItems; SmallVector BoundDecls; + SourceLoc UnknownAttrLoc; + while (Tok.is(tok::at_sign)) { + SyntaxParsingContext AttrCtx(SyntaxContext, SyntaxKind::Attribute); + + if (peekToken().isContextualKeyword("unknown")) { + if (!UnknownAttrLoc.isValid()) { + UnknownAttrLoc = consumeToken(tok::at_sign); + } else { + diagnose(Tok, diag::duplicate_attribute, false); + diagnose(UnknownAttrLoc, diag::previous_attribute, false); + consumeToken(tok::at_sign); + } + consumeIdentifier(); + + SyntaxParsingContext Args(SyntaxContext, SyntaxKind::TokenList); + if (Tok.is(tok::l_paren)) { + diagnose(Tok, diag::unexpected_lparen_in_attribute, "unknown"); + skipSingle(); + } + } else { + consumeToken(tok::at_sign); + diagnose(Tok, diag::unknown_attribute, Tok.getText()); + consumeIdentifier(); + + SyntaxParsingContext Args(SyntaxContext, SyntaxKind::TokenList); + if (Tok.is(tok::l_paren)) + skipSingle(); + } + } + SourceLoc CaseLoc; SourceLoc ColonLoc; if (Tok.is(tok::kw_case)) { Status |= ::parseStmtCase(*this, CaseLoc, CaseLabelItems, BoundDecls, ColonLoc); - } else { + } else if (Tok.is(tok::kw_default)) { Status |= parseStmtCaseDefault(*this, CaseLoc, CaseLabelItems, ColonLoc); + } else { + llvm_unreachable("isAtStartOfSwitchCase() lied."); } assert(!CaseLabelItems.empty() && "did not parse any labels?!"); @@ -2173,8 +2241,7 @@ ParserResult Parser::parseStmtCase(bool IsActive) { SmallVector BodyItems; SourceLoc StartOfBody = Tok.getLoc(); - if (Tok.isNot(tok::kw_case) && Tok.isNot(tok::kw_default) && - Tok.isNot(tok::r_brace)) { + if (Tok.isNot(tok::r_brace) && !isAtStartOfSwitchCase(*this)) { Status |= parseBraceItems(BodyItems, BraceItemListKind::Case); } else if (Status.isSuccess()) { diagnose(CaseLoc, diag::case_stmt_without_body, @@ -2193,5 +2260,6 @@ ParserResult Parser::parseStmtCase(bool IsActive) { return makeParserResult( Status, CaseStmt::create(Context, CaseLoc, CaseLabelItems, - !BoundDecls.empty(), ColonLoc, Body)); + !BoundDecls.empty(), UnknownAttrLoc, ColonLoc, + Body)); } diff --git a/lib/Parse/ParseType.cpp b/lib/Parse/ParseType.cpp index 350b17dc70feb..ad69d3ff1b08f 100644 --- a/lib/Parse/ParseType.cpp +++ b/lib/Parse/ParseType.cpp @@ -878,16 +878,11 @@ ParserResult Parser::parseTypeTupleBody() { Backtracking->cancelBacktrack(); // Consume a name. - if (!Tok.is(tok::kw__)) - element.Name = Context.getIdentifier(Tok.getText()); - element.NameLoc = consumeToken(); + element.NameLoc = consumeArgumentLabel(element.Name); // If there is a second name, consume it as well. - if (Tok.canBeArgumentLabel()) { - if (!Tok.is(tok::kw__)) - element.SecondName = Context.getIdentifier(Tok.getText()); - element.SecondNameLoc = consumeToken(); - } + if (Tok.canBeArgumentLabel()) + element.SecondNameLoc = consumeArgumentLabel(element.SecondName); // Consume the ':'. if (!consumeIf(tok::colon, element.ColonLoc)) diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index 355586474cb46..5446f6156a122 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -18,6 +18,7 @@ #include "swift/Subsystems.h" #include "swift/AST/ASTWalker.h" #include "swift/AST/DiagnosticsParse.h" +#include "swift/AST/Module.h" #include "swift/AST/PrettyStackTrace.h" #include "swift/Basic/Defer.h" #include "swift/Basic/SourceManager.h" @@ -500,6 +501,10 @@ Parser::~Parser() { delete SyntaxContext; } +bool Parser::allowTopLevelCode() const { + return SF.isScriptMode(); +} + const Token &Parser::peekToken() { return L->peekNextToken(); } @@ -559,7 +564,6 @@ void Parser::markSplitToken(tok Kind, StringRef Txt) { SplitTokens.back().setToken(Kind, Txt); Trivia EmptyTrivia; SyntaxContext->addToken(SplitTokens.back(), LeadingTrivia, EmptyTrivia); - LeadingTrivia.empty(); TokReceiver->receive(SplitTokens.back()); } @@ -705,6 +709,23 @@ void Parser::skipUntilConditionalBlockClose() { } } +bool Parser::loadCurrentSyntaxNodeFromCache() { + // Don't do a cache lookup when not building a syntax tree since otherwise + // the corresponding AST nodes do not get created + if (!SF.shouldBuildSyntaxTree()) { + return false; + } + unsigned LexerOffset = + SourceMgr.getLocOffsetInBuffer(Tok.getLoc(), L->getBufferID()); + unsigned LeadingTriviaOffset = LexerOffset - LeadingTrivia.getTextLength(); + if (auto TextLength = SyntaxContext->loadFromCache(LeadingTriviaOffset)) { + L->resetToOffset(LeadingTriviaOffset + TextLength); + L->lex(Tok, LeadingTrivia, TrailingTrivia); + return true; + } + return false; +} + bool Parser::parseEndIfDirective(SourceLoc &Loc) { Loc = Tok.getLoc(); if (parseToken(tok::pound_endif, diag::expected_close_to_if_directive)) { @@ -880,7 +901,7 @@ static SyntaxKind getListElementKind(SyntaxKind ListKind) { ParserStatus Parser::parseList(tok RightK, SourceLoc LeftLoc, SourceLoc &RightLoc, bool AllowSepAfterLast, Diag<> ErrorDiag, SyntaxKind Kind, - std::function callback) { + llvm::function_ref callback) { llvm::Optional ListContext; ListContext.emplace(SyntaxContext, Kind); if (Kind == SyntaxKind::Unknown) @@ -966,16 +987,15 @@ Parser::parseList(tok RightK, SourceLoc LeftLoc, SourceLoc &RightLoc, void Parser::diagnoseRedefinition(ValueDecl *Prev, ValueDecl *New) { assert(New != Prev && "Cannot conflict with self"); - diagnose(New->getLoc(), diag::decl_redefinition, New->isDefinition()); - diagnose(Prev->getLoc(), diag::previous_decldef, Prev->isDefinition(), - Prev->getBaseName()); + diagnose(New->getLoc(), diag::decl_redefinition); + diagnose(Prev->getLoc(), diag::previous_decldef, Prev->getBaseName()); } struct ParserUnit::Implementation { LangOptions LangOpts; SearchPathOptions SearchPathOpts; DiagnosticEngine Diags; - ASTContext Ctx; + ASTContext &Ctx; SourceFile *SF; std::unique_ptr TheParser; @@ -983,7 +1003,7 @@ struct ParserUnit::Implementation { const LangOptions &Opts, StringRef ModuleName) : LangOpts(Opts), Diags(SM), - Ctx(LangOpts, SearchPathOpts, SM, Diags), + Ctx(*ASTContext::get(LangOpts, SearchPathOpts, SM, Diags)), SF(new (Ctx) SourceFile( *ModuleDecl::create(Ctx.getIdentifier(ModuleName), Ctx), SourceFileKind::Main, BufferID, diff --git a/lib/Parse/SyntaxParsingCache.cpp b/lib/Parse/SyntaxParsingCache.cpp new file mode 100644 index 0000000000000..9d4eb2e23e927 --- /dev/null +++ b/lib/Parse/SyntaxParsingCache.cpp @@ -0,0 +1,92 @@ +//===--- SyntaxParsingCache.cpp - Incremental syntax parsing lookup--------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "swift/Parse/SyntaxParsingCache.h" + +using namespace swift; +using namespace swift::syntax; + +bool SyntaxParsingCache::nodeCanBeReused(const Syntax &Node, size_t Position, + SyntaxKind Kind) const { + auto NodeStart = Node.getAbsolutePositionWithLeadingTrivia().getOffset(); + if (NodeStart != Position) + return false; + if (Node.getKind() != Kind) + return false; + + // Node can also not be reused if an edit has been made in the next token's + // text, e.g. because `private struct Foo {}` parses as a CodeBlockItem with a + // StructDecl inside and `private struc Foo {}` parses as two CodeBlockItems + // one for `private` and one for `struc Foo {}` + size_t NextLeafNodeLength = 0; + if (auto NextNode = Node.getData().getNextNode()) { + auto NextLeafNode = NextNode->getFirstToken(); + auto NextRawNode = NextLeafNode->getRaw(); + NextLeafNodeLength += NextRawNode->getTokenText().size(); + for (auto TriviaPiece : NextRawNode->getLeadingTrivia()) { + NextLeafNodeLength += TriviaPiece.getTextLength(); + } + } + + auto NodeEnd = NodeStart + Node.getTextLength(); + for (auto Edit : Edits) { + // Check if this node or the trivia of the next node has been edited. If it + // has, we cannot reuse it. + if (Edit.intersectsOrTouchesRange(NodeStart, NodeEnd + NextLeafNodeLength)) + return false; + } + + return true; +} + +llvm::Optional SyntaxParsingCache::lookUpFrom(const Syntax &Node, + size_t Position, + SyntaxKind Kind) { + if (nodeCanBeReused(Node, Position, Kind)) { + return Node; + } + + for (size_t I = 0, E = Node.getNumChildren(); I < E; ++I) { + llvm::Optional Child = Node.getChild(I); + if (!Child.hasValue()) { + continue; + } + auto ChildStart = Child->getAbsolutePositionWithLeadingTrivia().getOffset(); + auto ChildEnd = ChildStart + Child->getTextLength(); + if (ChildStart <= Position && Position < ChildEnd) { + return lookUpFrom(Child.getValue(), Position, Kind); + } + } + return llvm::None; +} + +llvm::Optional SyntaxParsingCache::lookUp(size_t NewPosition, + SyntaxKind Kind) { + // Undo the edits in reverse order + size_t OldPosition = NewPosition; + for (auto I = Edits.rbegin(), E = Edits.rend(); I != E; ++I) { + auto Edit = *I; + if (Edit.End <= OldPosition) { + OldPosition = + OldPosition - Edit.ReplacementLength + Edit.originalLength(); + } + } + + auto Node = lookUpFrom(OldSyntaxTree, OldPosition, Kind); + if (Node.hasValue()) { + if (RecordReuseInformation) { + ReusedRanges.push_back( + {NewPosition, NewPosition + Node->getTextLength()}); + } + } + return Node; +} diff --git a/lib/Parse/SyntaxParsingContext.cpp b/lib/Parse/SyntaxParsingContext.cpp index 263e496402c99..8a5214ede09fc 100644 --- a/lib/Parse/SyntaxParsingContext.cpp +++ b/lib/Parse/SyntaxParsingContext.cpp @@ -34,20 +34,41 @@ using RootContextData = SyntaxParsingContext::RootContextData; SyntaxParsingContext::SyntaxParsingContext(SyntaxParsingContext *&CtxtHolder, SourceFile &SF, unsigned BufferID) - : RootDataOrParent(new RootContextData(SF, SF.getASTContext().Diags, - SF.getASTContext().SourceMgr, - BufferID)), - CtxtHolder(CtxtHolder), Arena(SF.getASTContext().getSyntaxArena()), - Storage(getRootData().Storage), Offset(0), Mode(AccumulationMode::Root), - Enabled(SF.shouldBuildSyntaxTree()) { + : RootDataOrParent(new RootContextData( + SF, SF.getASTContext().Diags, SF.getASTContext().SourceMgr, BufferID, + SF.getASTContext().getSyntaxArena(), SF.SyntaxParsingCache)), + CtxtHolder(CtxtHolder), + RootData(RootDataOrParent.get()), Offset(0), + Mode(AccumulationMode::Root), Enabled(SF.shouldBuildSyntaxTree()) { CtxtHolder = this; - Storage.reserve(128); + getStorage().reserve(128); +} + +size_t SyntaxParsingContext::loadFromCache(size_t LexerOffset) { + assert(getStorage().size() == Offset && + "Cannot load from cache if nodes have " + "already been gathered"); + assert(Mode == AccumulationMode::CreateSyntax && + "Loading from cache is only supported for mode CreateSyntax"); + if (!getSyntaxParsingCache()) { + // We don't have a cache, so there's nothing to look up + return 0; + } + auto CacheLookup = getSyntaxParsingCache()->lookUp(LexerOffset, SynKind); + if (!CacheLookup) { + return 0; + } + Mode = AccumulationMode::LoadedFromCache; + RC RawLookup = CacheLookup->getRaw().get(); + getStorage().push_back(RawLookup); + return RawLookup->getTextLength(); } RC SyntaxParsingContext::makeUnknownSyntax(SyntaxKind Kind, ArrayRef> Parts) { assert(isUnknownKind(Kind)); + SyntaxArena &Arena = getArena(); return RawSyntax::make(Kind, Parts, SourcePresence::Present, &Arena); } @@ -55,6 +76,7 @@ RC SyntaxParsingContext::createSyntaxAs(SyntaxKind Kind, ArrayRef> Parts) { // Try to create the node of the given syntax. + SyntaxArena &Arena = getArena(); if (auto Node = SyntaxFactory::createRaw(Kind, Parts, &Arena)) return Node; @@ -93,6 +115,9 @@ RC SyntaxParsingContext::bridgeAs(SyntaxContextKind Kind, break; } return RawNode; + } else if (Parts.empty()) { + // Just omit the unknown node if it does not have any children + return nullptr; } else { SyntaxKind UnknownKind; switch (Kind) { @@ -121,10 +146,10 @@ RC SyntaxParsingContext::bridgeAs(SyntaxContextKind Kind, /// Add RawSyntax to the parts. void SyntaxParsingContext::addRawSyntax(RC Raw) { - Storage.emplace_back(Raw); + getStorage().emplace_back(Raw); } -SyntaxParsingContext *SyntaxParsingContext::getRoot() { +const SyntaxParsingContext *SyntaxParsingContext::getRoot() const { auto Curr = this; while (!Curr->isRoot()) Curr = Curr->getParent(); @@ -137,6 +162,7 @@ void SyntaxParsingContext::addToken(Token &Tok, Trivia &LeadingTrivia, if (!Enabled) return; + auto &Arena = getArena(); addRawSyntax(RawSyntax::getToken(Arena, Tok.getKind(), Tok.getText(), LeadingTrivia.Pieces, TrailingTrivia.Pieces)); @@ -151,16 +177,17 @@ void SyntaxParsingContext::addSyntax(Syntax Node) { void SyntaxParsingContext::createNodeInPlace(SyntaxKind Kind, size_t N) { if (N == 0) { - Storage.push_back(createSyntaxAs(Kind, {})); + if (!parserShallOmitWhenNoChildren(Kind)) + getStorage().push_back(createSyntaxAs(Kind, {})); return; } - auto I = Storage.end() - N; + auto I = getStorage().end() - N; *I = createSyntaxAs(Kind, getParts().take_back(N)); // Remove consumed parts. if (N != 1) - Storage.erase(I + 1, Storage.end()); + getStorage().erase(I + 1, getStorage().end()); } void SyntaxParsingContext::createNodeInPlace(SyntaxKind Kind) { @@ -173,7 +200,8 @@ void SyntaxParsingContext::createNodeInPlace(SyntaxKind Kind) { case SyntaxKind::OptionalChainingExpr: case SyntaxKind::ForcedValueExpr: case SyntaxKind::PostfixUnaryExpr: - case SyntaxKind::TernaryExpr: { + case SyntaxKind::TernaryExpr: + case SyntaxKind::AvailabilityLabeledArgument: { auto Pair = SyntaxFactory::countChildren(Kind); assert(Pair.first == Pair.second); createNodeInPlace(Kind, Pair.first); @@ -234,7 +262,21 @@ class SyntaxVerifier: public SyntaxVisitor { "expression"); visitChildren(Node); } - + void visit(UnknownStmtSyntax Node) override { + RootData.Diags.diagnose(getSourceLoc(Node), diag::unknown_syntax_entity, + "statement"); + visitChildren(Node); + } + void visit(UnknownTypeSyntax Node) override { + RootData.Diags.diagnose(getSourceLoc(Node), diag::unknown_syntax_entity, + "type"); + visitChildren(Node); + } + void visit(UnknownPatternSyntax Node) override { + RootData.Diags.diagnose(getSourceLoc(Node), diag::unknown_syntax_entity, + "pattern"); + visitChildren(Node); + } void verify(Syntax Node) { Node.accept(*this); } @@ -282,8 +324,10 @@ void finalizeSourceFile(RootContextData &RootData, assert(newRaw); SF.setSyntaxRoot(make(newRaw)); - if (SF.getASTContext().LangOpts.VerifySyntaxTree) { - // Verify the added nodes if specified. + // Verify the tree if specified. + // Do this only when we see the real EOF token because parseIntoSourceFile() + // can get called multiple times for single source file. + if (EOFToken->isPresent() && SF.getASTContext().LangOpts.VerifySyntaxTree) { SyntaxVerifier Verifier(RootData); Verifier.verify(SF.getSyntaxRoot()); } @@ -296,11 +340,33 @@ void SyntaxParsingContext::finalizeRoot() { assert(isTopOfContextStack() && "some sub-contexts are not destructed"); assert(isRoot() && "only root context can finalize the tree"); assert(Mode == AccumulationMode::Root); - finalizeSourceFile(getRootData(), getParts()); + finalizeSourceFile(*getRootData(), getParts()); // Clear the parts because we will call this function again when destroying // the root context. - getRootData().Storage.clear(); + getStorage().clear(); +} + +void SyntaxParsingContext::synthesize(tok Kind, StringRef Text) { + if (!Enabled) + return; + if (Text.empty()) + Text = getTokenText(Kind); + getStorage().push_back(RawSyntax::missing(Kind, Text)); +} + +void SyntaxParsingContext::synthesize(SyntaxKind Kind) { + if (!Enabled) + return; + getStorage().push_back(RawSyntax::missing(Kind)); +} + +void SyntaxParsingContext::dumpStorage() const { + llvm::errs() << "======================\n"; + for (auto Node : getStorage()) { + Node->dump(); + llvm::errs() << "\n--------------\n"; + } } SyntaxParsingContext::~SyntaxParsingContext() { @@ -317,6 +383,8 @@ SyntaxParsingContext::~SyntaxParsingContext() { if (!Enabled) return; + auto &Storage = getStorage(); + switch (Mode) { // Create specified Syntax node from the parts and add it to the parent. case AccumulationMode::CreateSyntax: @@ -328,7 +396,9 @@ SyntaxParsingContext::~SyntaxParsingContext() { case AccumulationMode::CoerceKind: { assert(!isRoot()); if (Storage.size() == Offset) { - Storage.push_back(bridgeAs(CtxtKind, {})); + if (auto BridgedNode = bridgeAs(CtxtKind, {})) { + Storage.push_back(BridgedNode); + } } else { auto I = Storage.begin() + Offset; *I = bridgeAs(CtxtKind, getParts()); @@ -349,6 +419,9 @@ SyntaxParsingContext::~SyntaxParsingContext() { Storage.resize(Offset); break; + case AccumulationMode::LoadedFromCache: + break; + // Accumulate parsed toplevel syntax onto the SourceFile. case AccumulationMode::Root: finalizeRoot(); diff --git a/lib/ParseSIL/ParseSIL.cpp b/lib/ParseSIL/ParseSIL.cpp index 8345ae54e814f..368ade5926a95 100644 --- a/lib/ParseSIL/ParseSIL.cpp +++ b/lib/ParseSIL/ParseSIL.cpp @@ -662,7 +662,7 @@ SILValue SILParser::getLocalValue(UnresolvedValueName Name, SILType Type, if (EntryTy != Type) { HadError = true; P.diagnose(Name.NameLoc, diag::sil_value_use_type_mismatch, Name.Name, - EntryTy.getSwiftRValueType(), Type.getSwiftRValueType()); + EntryTy.getASTType(), Type.getASTType()); // Make sure to return something of the requested type. return new (SILMod) GlobalAddrInst(getDebugLoc(B, Loc), Type); } @@ -696,8 +696,8 @@ void SILParser::setLocalValue(ValueBase *Value, StringRef Name, // If the forward reference was of the wrong type, diagnose this now. if (Entry->getType() != Value->getType()) { P.diagnose(NameLoc, diag::sil_value_def_type_mismatch, Name, - Entry->getType().getSwiftRValueType(), - Value->getType().getSwiftRValueType()); + Entry->getType().getASTType(), + Value->getType().getASTType()); HadError = true; } else { // Forward references only live here if they have a single result. @@ -1566,57 +1566,57 @@ static bool getConformancesForSubstitution(Parser &P, return false; } -/// Reconstruct AST substitutions from parsed substitutions using archetypes -/// from a SILFunctionType. -bool getApplySubstitutionsFromParsed( +/// Reconstruct an AST substitution map from parsed substitutions. +SubstitutionMap getApplySubstitutionsFromParsed( SILParser &SP, GenericEnvironment *env, - ArrayRef parses, - SmallVectorImpl &subs) { + ArrayRef parses) { if (parses.empty()) { assert(!env); - return false; + return SubstitutionMap(); } assert(env); auto loc = parses[0].loc; - // The replacement is for the corresponding dependent type by ordering. - auto result = env->getGenericSignature()->enumeratePairedRequirements( - [&](Type depTy, ArrayRef reqts) -> bool { - if (parses.empty()) { - SP.P.diagnose(loc, diag::sil_missing_substitutions); - return true; - } - auto parsed = parses.front(); - parses = parses.slice(1); - - SmallVector conformances; - SmallVector protocols; - for (auto reqt : reqts) - protocols.push_back(reqt.getSecondType()); - - if (getConformancesForSubstitution(SP.P, - ExistentialLayout::ProtocolTypeArrayRef(protocols), - parsed.replacement, - parsed.loc, conformances)) - return true; - - subs.push_back({parsed.replacement, - SP.P.Context.AllocateCopy(conformances)}); - return false; + // Ensure that we have the right number of type arguments. + auto genericSig = env->getGenericSignature(); + if (parses.size() != genericSig->getGenericParams().size()) { + bool hasTooFew = parses.size() < genericSig->getGenericParams().size(); + SP.P.diagnose(loc, + hasTooFew ? diag::sil_missing_substitutions + : diag::sil_too_many_substitutions); + return SubstitutionMap(); + } + + bool failed = false; + SubstitutionMap subMap = genericSig->getSubstitutionMap( + [&](SubstitutableType *type) -> Type { + auto genericParam = dyn_cast(type); + if (!genericParam) return nullptr; + + auto index = genericSig->getGenericParamOrdinal(genericParam); + assert(index < genericSig->getGenericParams().size()); + assert(index < parses.size()); + + // Provide the replacement type. + return parses[index].replacement; + }, + [&](CanType dependentType, Type replacementType, + ProtocolDecl *proto) ->Optional { + auto M = SP.P.SF.getParentModule(); + auto conformance = M->lookupConformance(replacementType, proto); + if (conformance) return conformance; + + SP.P.diagnose(loc, diag::sil_substitution_mismatch, replacementType, + proto->getDeclaredType()); + failed = true; + + return ProtocolConformanceRef(proto); }); - if (result) - return true; - - if (!parses.empty()) { - SP.P.diagnose(loc, diag::sil_too_many_substitutions); - return true; - } - - return false; + return failed ? SubstitutionMap() : subMap; } static ArrayRef @@ -1879,7 +1879,7 @@ SILParser::parseKeyPathPatternComponent(KeyPathPatternComponent &component, if (patternEnv) loweredTy = SILType::getPrimitiveType( - loweredTy.getSwiftRValueType()->mapTypeOutOfContext() + loweredTy.getASTType()->mapTypeOutOfContext() ->getCanonicalType(), loweredTy.getCategory()); @@ -1906,8 +1906,8 @@ SILParser::parseKeyPathPatternComponent(KeyPathPatternComponent &component, P.diagnose(loweredTyLoc, diag::sil_keypath_index_operand_type_conflict, index, - operandTypes[index].getSwiftRValueType(), - loweredTy.getSwiftRValueType()); + operandTypes[index].getASTType(), + loweredTy.getASTType()); return true; } operandTypes[index] = loweredTy; @@ -1936,7 +1936,6 @@ SILParser::parseKeyPathPatternComponent(KeyPathPatternComponent &component, } else if (componentKind.str() == "external") { ValueDecl *externalDecl; SmallVector parsedSubs; - SmallVector subs; SmallVector indexes; CanType ty; @@ -1984,6 +1983,7 @@ SILParser::parseKeyPathPatternComponent(KeyPathPatternComponent &component, return true; } + SubstitutionMap subsMap; if (!parsedSubs.empty()) { auto genericEnv = externalDecl->getInnermostDeclContext() ->getGenericEnvironmentOfContext(); @@ -1992,29 +1992,19 @@ SILParser::parseKeyPathPatternComponent(KeyPathPatternComponent &component, diag::sil_substitutions_on_non_polymorphic_type); return true; } - if (getApplySubstitutionsFromParsed(*this, genericEnv, - parsedSubs, subs)) - return true; - + subsMap = getApplySubstitutionsFromParsed(*this, genericEnv, parsedSubs); + if (!subsMap) return true; + // Map the substitutions out of the pattern context so that they // use interface types. - auto subsMap = genericEnv->getGenericSignature() - ->getSubstitutionMap(subs); - subsMap = subsMap.mapReplacementTypesOutOfContext(); - subs.clear(); - genericEnv->getGenericSignature()->getSubstitutions(subsMap, subs); - - for (auto &sub : subs) - sub = sub.getCanonicalSubstitution(); + subsMap = subsMap.mapReplacementTypesOutOfContext().getCanonical(); } - auto indexesCopy = P.Context.AllocateCopy(indexes); - auto subsCopy = P.Context.AllocateCopy(subs); - + component = KeyPathPatternComponent::forExternal( cast(externalDecl), - subsCopy, indexesCopy, equals, hash, ty); + subsMap, indexesCopy, equals, hash, ty); return false; } else if (componentKind.str() == "gettable_property" || componentKind.str() == "settable_property") { @@ -2559,7 +2549,7 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { GenericEnvironment *genericEnv = builtinFunc->getGenericEnvironment(); SmallVector parsedSubs; - SmallVector subs; + SubstitutionMap subMap; if (parseSubstitutions(parsedSubs)) return true; @@ -2568,7 +2558,8 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { P.diagnose(P.Tok, diag::sil_substitutions_on_non_polymorphic_type); return true; } - if (getApplySubstitutionsFromParsed(*this, genericEnv, parsedSubs, subs)) + subMap = getApplySubstitutionsFromParsed(*this, genericEnv, parsedSubs); + if (!subMap) return true; } @@ -2607,7 +2598,7 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { if (parseSILDebugLocation(InstLoc, B)) return true; - ResultVal = B.createBuiltin(InstLoc, Id, ResultTy, subs, Args); + ResultVal = B.createBuiltin(InstLoc, Id, ResultTy, subMap, Args); break; } case SILInstructionKind::OpenExistentialAddrInst: @@ -2687,7 +2678,6 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { UNARY_INSTRUCTION(CopyBlock) UNARY_INSTRUCTION(IsUnique) UNARY_INSTRUCTION(IsUniqueOrPinned) - UNARY_INSTRUCTION(IsEscapingClosure) UNARY_INSTRUCTION(DestroyAddr) UNARY_INSTRUCTION(CopyValue) UNARY_INSTRUCTION(CopyUnownedValue) @@ -2715,6 +2705,20 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { #undef UNARY_INSTRUCTION #undef REFCOUNTING_INSTRUCTION + case SILInstructionKind::IsEscapingClosureInst: { + bool IsObjcVerifcationType = false; + if (parseSILOptional(IsObjcVerifcationType, *this, "objc")) + return true; + if (parseTypedValueRef(Val, B) || + parseSILDebugLocation(InstLoc, B)) + return true; + ResultVal = B.createIsEscapingClosure( + InstLoc, Val, + IsObjcVerifcationType ? IsEscapingClosureInst::ObjCEscaping + : IsEscapingClosureInst::WithoutActuallyEscaping); + break; + } + case SILInstructionKind::DebugValueInst: case SILInstructionKind::DebugValueAddrInst: { SILDebugVariable VarInfo; @@ -2811,6 +2815,18 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { break; } + case SILInstructionKind::CopyBlockWithoutEscapingInst: { + SILValue Closure; + if (parseTypedValueRef(Val, B) || + parseVerbatim("withoutEscaping") || + parseTypedValueRef(Closure, B) || + parseSILDebugLocation(InstLoc, B)) + return true; + + ResultVal = B.createCopyBlockWithoutEscaping(InstLoc, Val, Closure); + break; + } + case SILInstructionKind::MarkDependenceInst: { SILValue Base; if (parseTypedValueRef(Val, B) || @@ -2890,24 +2906,22 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { P.diagnose(InstLoc.getSourceLoc(), diag::sil_keypath_no_root); SmallVector parsedSubs; - SmallVector subs; if (parseSubstitutions(parsedSubs, ContextGenericEnv)) return true; + SubstitutionMap subMap; if (!parsedSubs.empty()) { if (!patternEnv) { P.diagnose(InstLoc.getSourceLoc(), diag::sil_substitutions_on_non_polymorphic_type); return true; } - if (getApplySubstitutionsFromParsed(*this, patternEnv, parsedSubs, subs)) + + subMap = getApplySubstitutionsFromParsed(*this, patternEnv, parsedSubs); + if (!subMap) return true; } - SubstitutionMap subMap; - if (patternEnv && patternEnv->getGenericSignature()) - subMap = patternEnv->getGenericSignature()->getSubstitutionMap(subs); - SmallVector operands; if (P.consumeIf(tok::l_paren)) { @@ -2942,13 +2956,14 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { return true; CanGenericSignature canSig = nullptr; - if (patternEnv && patternEnv->getGenericSignature()) + if (patternEnv && patternEnv->getGenericSignature()) { canSig = patternEnv->getGenericSignature()->getCanonicalSignature(); + } auto pattern = KeyPathPattern::get(B.getModule(), canSig, rootType, components.back().getComponentType(), components, objcString); - ResultVal = B.createKeyPath(InstLoc, pattern, subs, operands, Ty); + ResultVal = B.createKeyPath(InstLoc, pattern, subMap, operands, Ty); break; } @@ -2979,10 +2994,21 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { SILType Ty; Identifier ToToken; SourceLoc ToLoc; - bool guaranteed = true; - if (Opcode == SILInstructionKind::ConvertEscapeToNoEscapeInst) - if(parseSILOptional(guaranteed, *this, "not_guaranteed")) + bool not_guaranteed = false; + bool escaped = false; + if (Opcode == SILInstructionKind::ConvertEscapeToNoEscapeInst) { + StringRef attrName; + if (parseSILOptional(attrName, *this)) { + if (attrName.equals("escaped")) + escaped = true; + else if (attrName.equals("not_guaranteed")) + not_guaranteed = true; + else + return true; + } + if (parseSILOptional(escaped, *this, "escaped")) return true; + } if (parseTypedValueRef(Val, B) || parseSILIdentifier(ToToken, ToLoc, diag::expected_tok_in_sil_instr, "to") || @@ -3016,7 +3042,8 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { ResultVal = B.createConvertFunction(InstLoc, Val, Ty); break; case SILInstructionKind::ConvertEscapeToNoEscapeInst: - ResultVal = B.createConvertEscapeToNoEscape(InstLoc, Val, Ty, guaranteed); + ResultVal = B.createConvertEscapeToNoEscape(InstLoc, Val, Ty, escaped, + !not_guaranteed); break; case SILInstructionKind::AddressToPointerInst: ResultVal = B.createAddressToPointer(InstLoc, Val, Ty); @@ -3273,14 +3300,25 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { SILValue InitStorageFunc = getLocalValue(InitStorageFuncName, InitStorageTy, InstLoc, B); SILValue SetterFunc = getLocalValue(SetterFuncName, SetterTy, InstLoc, B); - - SmallVector InitStorageSubs, SetterSubs; - if (getApplySubstitutionsFromParsed(*this, InitStorageEnv, - ParsedInitStorageSubs, InitStorageSubs) - || getApplySubstitutionsFromParsed(*this, SetterEnv, - ParsedSetterSubs, SetterSubs)) - return true; - + + SubstitutionMap InitStorageSubs, SetterSubs; + { + if (InitStorageEnv) { + InitStorageSubs = + getApplySubstitutionsFromParsed(*this, InitStorageEnv, + ParsedInitStorageSubs); + if (!InitStorageSubs) + return true; + } + + if (SetterEnv) { + SetterSubs = getApplySubstitutionsFromParsed(*this, SetterEnv, + ParsedSetterSubs); + if (!SetterSubs) + return true; + } + } + auto SubstInitStorageTy = InitStorageTy.castTo() ->substGenericArgs(B.getModule(), InitStorageSubs); auto SubstSetterTy = SetterTy.castTo() @@ -3397,6 +3435,7 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { ParsedEnum enforcement; ParsedEnum aborting; ParsedEnum noNestedConflict; + ParsedEnum fromBuiltin; bool isBeginAccess = (Opcode == SILInstructionKind::BeginAccessInst || Opcode == SILInstructionKind::BeginUnpairedAccessInst); @@ -3428,6 +3467,10 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { auto setNoNestedConflict = [&](bool value) { maybeSetEnum(isBeginAccess, noNestedConflict, value, attr, identLoc); }; + auto setFromBuiltin = [&](bool value) { + maybeSetEnum(Opcode != SILInstructionKind::EndAccessInst, fromBuiltin, + value, attr, identLoc); + }; if (attr == "unknown") { setEnforcement(SILAccessEnforcement::Unknown); @@ -3449,6 +3492,8 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { setAborting(true); } else if (attr == "no_nested_conflict") { setNoNestedConflict(true); + } else if (attr == "builtin") { + setFromBuiltin(true); } else { P.diagnose(identLoc, diag::unknown_attribute, attr); } @@ -3473,6 +3518,9 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { if (isBeginAccess && !noNestedConflict.isSet()) noNestedConflict.Value = false; + if (!fromBuiltin.isSet()) + fromBuiltin.Value = false; + SILValue addrVal; SourceLoc addrLoc; if (parseTypedValueRef(addrVal, addrLoc, B)) @@ -3497,16 +3545,16 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { if (Opcode == SILInstructionKind::BeginAccessInst) { ResultVal = B.createBeginAccess(InstLoc, addrVal, *kind, *enforcement, - *noNestedConflict); + *noNestedConflict, *fromBuiltin); } else if (Opcode == SILInstructionKind::EndAccessInst) { ResultVal = B.createEndAccess(InstLoc, addrVal, *aborting); } else if (Opcode == SILInstructionKind::BeginUnpairedAccessInst) { ResultVal = B.createBeginUnpairedAccess(InstLoc, addrVal, bufferVal, *kind, *enforcement, - *noNestedConflict); + *noNestedConflict, *fromBuiltin); } else { - ResultVal = B.createEndUnpairedAccess(InstLoc, addrVal, - *enforcement, *aborting); + ResultVal = B.createEndUnpairedAccess(InstLoc, addrVal, *enforcement, + *aborting, *fromBuiltin); } break; } @@ -3757,7 +3805,7 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { do { if (parseTypedValueRef(Val, B)) return true; OpList.push_back(Val); - TypeElts.push_back(Val->getType().getSwiftRValueType()); + TypeElts.push_back(Val->getType().getASTType()); } while (P.consumeIf(tok::comma)); } HadError |= P.parseToken(tok::r_paren, @@ -3797,7 +3845,7 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { RegularLocation(P.Tok.getLoc()), B)) return true; OpList.push_back(Val); - TypeElts.push_back(Val->getType().getSwiftRValueType()); + TypeElts.push_back(Val->getType().getASTType()); } while (P.consumeIf(tok::comma)); } HadError |= P.parseToken(tok::r_paren, @@ -4084,17 +4132,13 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { P.diagnose(TyLoc, diag::sil_witness_method_not_protocol); return true; } - ProtocolConformanceRef Conformance(proto); - if (!isa(LookupTy)) { - auto lookup = P.SF.getParentModule()->lookupConformance(LookupTy, proto); - if (!lookup) { - P.diagnose(TyLoc, diag::sil_witness_method_type_does_not_conform); - return true; - } - Conformance = ProtocolConformanceRef(*lookup); + auto conformance = P.SF.getParentModule()->lookupConformance(LookupTy, proto); + if (!conformance) { + P.diagnose(TyLoc, diag::sil_witness_method_type_does_not_conform); + return true; } - - ResultVal = B.createWitnessMethod(InstLoc, LookupTy, Conformance, Member, + + ResultVal = B.createWitnessMethod(InstLoc, LookupTy, *conformance, Member, MethodTy); break; } @@ -4336,8 +4380,8 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { global->getLoweredType()); if (expectedType != Ty) { P.diagnose(IdLoc, diag::sil_value_use_type_mismatch, GlobalName.str(), - global->getLoweredType().getSwiftRValueType(), - Ty.getSwiftRValueType()); + global->getLoweredType().getASTType(), + Ty.getASTType()); return true; } @@ -4623,7 +4667,7 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { // Lower the type at the abstraction level of the existential. auto archetype - = ArchetypeType::getOpened(Val->getType().getSwiftRValueType()) + = ArchetypeType::getOpened(Val->getType().getASTType()) ->getCanonicalType(); SILType LoweredTy = SILMod.Types.getLoweredType( @@ -4633,7 +4677,7 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { // Collect conformances for the type. ArrayRef conformances = collectExistentialConformances(P, Ty, TyLoc, - Val->getType().getSwiftRValueType()); + Val->getType().getASTType()); ResultVal = B.createInitExistentialAddr(InstLoc, Val, Ty, LoweredTy, conformances); @@ -4654,7 +4698,7 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { ArrayRef conformances = collectExistentialConformances(P, FormalConcreteTy, TyLoc, - ExistentialTy.getSwiftRValueType()); + ExistentialTy.getASTType()); ResultVal = B.createInitExistentialValue( InstLoc, ExistentialTy, FormalConcreteTy, Val, conformances); @@ -4675,7 +4719,7 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { // Collect conformances for the type. ArrayRef conformances = collectExistentialConformances(P, ConcreteFormalTy, TyLoc, - ExistentialTy.getSwiftRValueType()); + ExistentialTy.getASTType()); ResultVal = B.createAllocExistentialBox(InstLoc, ExistentialTy, ConcreteFormalTy, conformances); @@ -4698,7 +4742,7 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { ArrayRef conformances = collectExistentialConformances(P, FormalConcreteTy, TyLoc, - ExistentialTy.getSwiftRValueType()); + ExistentialTy.getASTType()); // FIXME: Conformances in InitExistentialRefInst is currently not included // in SIL.rst. @@ -4716,8 +4760,8 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { parseSILDebugLocation(InstLoc, B)) return true; - auto baseExType = ExistentialTy.getSwiftRValueType(); - auto formalConcreteType = Val->getType().getSwiftRValueType(); + auto baseExType = ExistentialTy.getASTType(); + auto formalConcreteType = Val->getType().getASTType(); while (auto instExType = dyn_cast(baseExType)) { baseExType = instExType.getInstanceType(); formalConcreteType = @@ -4798,21 +4842,22 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { } auto invokeVal = getLocalValue(invokeName, invokeTy, InstLoc, B); - - SmallVector subs; + + SubstitutionMap subMap; if (!parsedSubs.empty()) { if (!invokeGenericEnv) { P.diagnose(typeLoc, diag::sil_substitutions_on_non_polymorphic_type); return true; } - if (getApplySubstitutionsFromParsed(*this, - invokeGenericEnv, - parsedSubs, subs)) + + subMap = getApplySubstitutionsFromParsed(*this, invokeGenericEnv, + parsedSubs); + if (!subMap) return true; } ResultVal = B.createInitBlockStorageHeader(InstLoc, Val, invokeVal, - blockType, subs); + blockType, subMap); break; } } @@ -4883,13 +4928,14 @@ bool SILParser::parseCallInstruction(SILLocation InstLoc, return true; } - SmallVector subs; + SubstitutionMap subs; if (!parsedSubs.empty()) { if (!GenericEnv) { P.diagnose(TypeLoc, diag::sil_substitutions_on_non_polymorphic_type); return true; } - if (getApplySubstitutionsFromParsed(*this, GenericEnv, parsedSubs, subs)) + subs = getApplySubstitutionsFromParsed(*this, GenericEnv, parsedSubs); + if (!subs) return true; } @@ -5747,13 +5793,13 @@ ProtocolConformance *SILParser::parseProtocolConformanceHelper( if (P.parseToken(tok::r_paren, diag::expected_sil_witness_rparen)) return nullptr; - SmallVector subs; - if (getApplySubstitutionsFromParsed(*this, specializedEnv, parsedSubs, - subs)) + SubstitutionMap subMap = + getApplySubstitutionsFromParsed(*this, specializedEnv, parsedSubs); + if (!subMap) return nullptr; auto result = P.Context.getSpecializedConformance( - ConformingTy, genericConform, subs); + ConformingTy, genericConform, subMap); return result; } @@ -6165,11 +6211,12 @@ bool SILParserTUState::parseSILCoverageMap(Parser &P) { return true; // Parse the covered name. - Identifier FuncName; - SourceLoc FuncLoc; - if (State.parseSILIdentifier(FuncName, FuncLoc, - diag::expected_sil_value_name)) + if (!P.Tok.is(tok::string_literal)) { + P.diagnose(P.Tok, diag::sil_coverage_expected_quote); return true; + } + StringRef FuncName = P.Tok.getText().drop_front().drop_back(); + P.consumeToken(); // Parse the PGO func name. if (!P.Tok.is(tok::string_literal)) { @@ -6179,12 +6226,6 @@ bool SILParserTUState::parseSILCoverageMap(Parser &P) { StringRef PGOFuncName = P.Tok.getText().drop_front().drop_back(); P.consumeToken(); - SILFunction *Func = M.lookUpFunction(FuncName.str()); - if (!Func) { - P.diagnose(FuncLoc, diag::sil_coverage_func_not_found, FuncName); - return true; - } - uint64_t Hash; if (State.parseInteger(Hash, diag::sil_coverage_invalid_hash)) return true; diff --git a/lib/PrintAsObjC/PrintAsObjC.cpp b/lib/PrintAsObjC/PrintAsObjC.cpp index 323802290335f..7871a87e4b930 100644 --- a/lib/PrintAsObjC/PrintAsObjC.cpp +++ b/lib/PrintAsObjC/PrintAsObjC.cpp @@ -2064,22 +2064,6 @@ class ReferencedTypeFinder : public TypeVisitor { } }; -/// A generalization of llvm::SmallSetVector that allows a custom comparator. -template > -using SmallSetVector = - llvm::SetVector, llvm::SmallSet>; - -/// A comparator for types with PointerLikeTypeTraits that sorts by opaque -/// void pointer representation. -template -struct PointerLikeComparator { - using Traits = llvm::PointerLikeTypeTraits; - bool operator()(T lhs, T rhs) const { - return std::less()(Traits::getAsVoidPointer(lhs), - Traits::getAsVoidPointer(rhs)); - } -}; - class ModuleWriter { enum class EmissionState { NotYetDefined = 0, @@ -2092,8 +2076,7 @@ class ModuleWriter { DelayedMemberSet delayedMembers; using ImportModuleTy = PointerUnion; - SmallSetVector> imports; + SmallPtrSet imports; std::string bodyBuffer; llvm::raw_string_ostream os{bodyBuffer}; @@ -2175,7 +2158,7 @@ class ModuleWriter { } void forwardDeclare(const NominalTypeDecl *NTD, - std::function Printer) { + llvm::function_ref Printer) { if (NTD->getModuleContext()->isStdlibModule()) return; auto &state = seenTypes[NTD]; @@ -2634,13 +2617,73 @@ class ModuleWriter { return import == importer->getImportedHeaderModule(); } + static void getClangSubmoduleReversePath(SmallVectorImpl &path, + const clang::Module *module) { + // FIXME: This should be an API on clang::Module. + do { + path.push_back(module->Name); + module = module->Parent; + } while (module); + } + + static int compareImportModulesByName(const ImportModuleTy *left, + const ImportModuleTy *right) { + auto *leftSwiftModule = left->dyn_cast(); + auto *rightSwiftModule = right->dyn_cast(); + + if (leftSwiftModule && !rightSwiftModule) + return -compareImportModulesByName(right, left); + + if (leftSwiftModule && rightSwiftModule) + return leftSwiftModule->getName().compare(rightSwiftModule->getName()); + + auto *leftClangModule = left->get(); + assert(leftClangModule->isSubModule() && + "top-level modules should use a normal swift::ModuleDecl"); + if (rightSwiftModule) { + // Because the Clang module is a submodule, its full name will never be + // equal to a Swift module's name, even if the top-level name is the same; + // it will always come before or after. + if (leftClangModule->getTopLevelModuleName() < + rightSwiftModule->getName().str()) { + return -1; + } + return 1; + } + + auto *rightClangModule = right->get(); + assert(rightClangModule->isSubModule() && + "top-level modules should use a normal swift::ModuleDecl"); + + SmallVector leftReversePath; + SmallVector rightReversePath; + getClangSubmoduleReversePath(leftReversePath, leftClangModule); + getClangSubmoduleReversePath(rightReversePath, rightClangModule); + + assert(leftReversePath != rightReversePath && + "distinct Clang modules should not have the same full name"); + if (std::lexicographical_compare(leftReversePath.rbegin(), + leftReversePath.rend(), + rightReversePath.rbegin(), + rightReversePath.rend())) { + return -1; + } + return 1; + } + void writeImports(raw_ostream &out) { out << "#if __has_feature(modules)\n"; + // Sort alphabetically for determinism and consistency. + SmallVector sortedImports{imports.begin(), + imports.end()}; + llvm::array_pod_sort(sortedImports.begin(), sortedImports.end(), + &compareImportModulesByName); + // Track printed names to handle overlay modules. llvm::SmallPtrSet seenImports; bool includeUnderlying = false; - for (auto import : imports) { + for (auto import : sortedImports) { if (auto *swiftModule = import.dyn_cast()) { auto Name = swiftModule->getName(); if (isUnderlyingModule(swiftModule)) { @@ -2651,13 +2694,11 @@ class ModuleWriter { out << "@import " << Name.str() << ";\n"; } else { const auto *clangModule = import.get(); + assert(clangModule->isSubModule() && + "top-level modules should use a normal swift::ModuleDecl"); out << "@import "; - // FIXME: This should be an API on clang::Module. - SmallVector submoduleNames; - do { - submoduleNames.push_back(clangModule->Name); - clangModule = clangModule->Parent; - } while (clangModule); + SmallVector submoduleNames; + getClangSubmoduleReversePath(submoduleNames, clangModule); interleave(submoduleNames.rbegin(), submoduleNames.rend(), [&out](StringRef next) { out << next; }, [&out] { out << "."; }); diff --git a/lib/RemoteAST/RemoteAST.cpp b/lib/RemoteAST/RemoteAST.cpp index 8b7452a7d088c..cdeda2521e099 100644 --- a/lib/RemoteAST/RemoteAST.cpp +++ b/lib/RemoteAST/RemoteAST.cpp @@ -65,12 +65,19 @@ struct IRGenContext { private: IRGenContext(ASTContext &ctx, ModuleDecl *module) - : SILMod(SILModule::createEmptyModule(module, SILOpts)), + : IROpts(createIRGenOptions()), + SILMod(SILModule::createEmptyModule(module, SILOpts)), IRGen(IROpts, *SILMod), IGM(IRGen, IRGen.createTargetMachine(), /*SourceFile*/ nullptr, LLVMContext, "", "", "") {} + static IRGenOptions createIRGenOptions() { + IRGenOptions IROpts; + IROpts.EnableResilienceBypass = true; + return IROpts; + } + public: static std::unique_ptr create(ASTContext &ctx, DeclContext *nominalDC) { diff --git a/lib/SIL/Bridging.cpp b/lib/SIL/Bridging.cpp index b74aa41a79408..0835c886c63e3 100644 --- a/lib/SIL/Bridging.cpp +++ b/lib/SIL/Bridging.cpp @@ -21,6 +21,7 @@ #include "swift/AST/Decl.h" #include "swift/AST/DiagnosticsSIL.h" #include "swift/AST/ProtocolConformance.h" +#include "swift/AST/Module.h" #include "clang/AST/DeclObjC.h" #include "llvm/Support/Debug.h" #include "llvm/Support/ErrorHandling.h" diff --git a/lib/SIL/CMakeLists.txt b/lib/SIL/CMakeLists.txt index 6a42551f9562d..bd153628582b3 100644 --- a/lib/SIL/CMakeLists.txt +++ b/lib/SIL/CMakeLists.txt @@ -2,10 +2,13 @@ add_swift_library(swiftSIL STATIC AbstractionPattern.cpp BasicBlockUtils.cpp Bridging.cpp + DebugUtils.cpp Dominance.cpp DynamicCasts.cpp InstructionUtils.cpp + MemAccessUtils.cpp Linker.cpp + LinearLifetimeChecker.cpp LoopInfo.cpp OptimizationRemark.cpp PrettyStackTrace.cpp diff --git a/lib/SIL/DebugUtils.cpp b/lib/SIL/DebugUtils.cpp new file mode 100644 index 0000000000000..cd08fe71ac95b --- /dev/null +++ b/lib/SIL/DebugUtils.cpp @@ -0,0 +1,66 @@ +//===--- DebugUtils.cpp ---------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "swift/SIL/DebugUtils.h" +#include "swift/Basic/STLExtras.h" +#include "swift/SIL/SILArgument.h" +#include "swift/SIL/SILInstruction.h" +#include "llvm/ADT/PointerIntPair.h" +#include "llvm/ADT/SmallPtrSet.h" + +using namespace swift; + +bool swift::hasNonTrivialNonDebugTransitiveUsers( + PointerUnion V) { + llvm::SmallVector Worklist; + llvm::SmallPtrSet SeenInsts; + + // Initialize our worklist. + if (auto *A = V.dyn_cast()) { + for (Operand *Op : getNonDebugUses(SILValue(A))) { + auto *User = Op->getUser(); + if (!SeenInsts.insert(User).second) + continue; + Worklist.push_back(User); + } + } else { + auto *I = V.get(); + SeenInsts.insert(I); + Worklist.push_back(I); + } + + while (!Worklist.empty()) { + SILInstruction *U = Worklist.pop_back_val(); + assert(SeenInsts.count(U) && + "Worklist should only contain seen instructions?!"); + + // If U is a terminator inst, return false. + if (isa(U)) + return true; + + // If U has side effects... + if (U->mayHaveSideEffects()) + return true; + + // Otherwise add all non-debug uses of I that we have not seen yet to the + // worklist. + for (SILValue Result : U->getResults()) { + for (Operand *I : getNonDebugUses(Result)) { + auto *User = I->getUser(); + if (!SeenInsts.insert(User).second) + continue; + Worklist.push_back(User); + } + } + } + return false; +} diff --git a/lib/SIL/DynamicCasts.cpp b/lib/SIL/DynamicCasts.cpp index 692bd3aba1ea5..1604f8dda5360 100644 --- a/lib/SIL/DynamicCasts.cpp +++ b/lib/SIL/DynamicCasts.cpp @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +#include "swift/AST/Module.h" #include "swift/AST/Types.h" #include "swift/SIL/SILArgument.h" #include "swift/SIL/SILBuilder.h" @@ -284,10 +285,10 @@ CanType swift::getNSBridgedClassOfCFClass(ModuleDecl *M, CanType type) { static bool isCFBridgingConversion(ModuleDecl *M, SILType sourceType, SILType targetType) { - return (sourceType.getSwiftRValueType() == - getNSBridgedClassOfCFClass(M, targetType.getSwiftRValueType()) || - targetType.getSwiftRValueType() == - getNSBridgedClassOfCFClass(M, sourceType.getSwiftRValueType())); + return (sourceType.getASTType() == + getNSBridgedClassOfCFClass(M, targetType.getASTType()) || + targetType.getASTType() == + getNSBridgedClassOfCFClass(M, sourceType.getASTType())); } /// Try to classify the dynamic-cast relationship between two types. @@ -322,10 +323,15 @@ swift::classifyDynamicCast(ModuleDecl *M, // Casting to a less-optional type can always fail. } else if (sourceObject) { - return atBest(classifyDynamicCast(M, sourceObject, target, - /* isSourceTypeExact */ false, - isWholeModuleOpts), - DynamicCastFeasibility::MaySucceed); + auto result = atBest(classifyDynamicCast(M, sourceObject, target, + /* isSourceTypeExact */ false, + isWholeModuleOpts), + DynamicCastFeasibility::MaySucceed); + if (target.isExistentialType()) { + result = atWorst(result, classifyDynamicCastToProtocol( + M, source, target, isWholeModuleOpts)); + } + return result; } assert(!sourceObject && !targetObject); diff --git a/lib/SIL/InstructionUtils.cpp b/lib/SIL/InstructionUtils.cpp index 06e1bd5b0ba81..35ff6ec928b12 100644 --- a/lib/SIL/InstructionUtils.cpp +++ b/lib/SIL/InstructionUtils.cpp @@ -256,6 +256,7 @@ SingleValueInstruction *swift::getSingleValueCopyOrCast(SILInstruction *I) { return nullptr; case SILInstructionKind::CopyValueInst: case SILInstructionKind::CopyBlockInst: + case SILInstructionKind::CopyBlockWithoutEscapingInst: case SILInstructionKind::BeginBorrowInst: case SILInstructionKind::BeginAccessInst: return cast(I); @@ -275,8 +276,8 @@ bool swift::isEndOfScopeMarker(SILInstruction *user) { } bool swift::isIncidentalUse(SILInstruction *user) { - return isEndOfScopeMarker(user) || isDebugInst(user) - || isa(user); + return isEndOfScopeMarker(user) || user->isDebugInstruction() || + isa(user); } bool swift::onlyAffectsRefCount(SILInstruction *user) { @@ -313,171 +314,6 @@ SILValue swift::stripConvertFunctions(SILValue V) { return V; } -// Return true if the given address is a 'let' lvalue. -static bool isLetAccess(SILValue address) { - switch (address->getKind()) { - default: - return false; - - case ValueKind::AllocStackInst: { - VarDecl *decl = cast(address)->getDecl(); - return decl && decl->isLet(); - } - case ValueKind::AllocBoxInst: { - VarDecl *decl = cast(address)->getDecl(); - return decl && decl->isLet(); - } - case ValueKind::RefElementAddrInst: { - VarDecl *decl = cast(address)->getField(); - return decl && decl->isLet(); - } - case ValueKind::GlobalAddrInst: { - SILGlobalVariable *global = - cast(address)->getReferencedGlobal(); - return global && global->isLet(); - } - }; -} - -// An address base is a block argument. Verify that it is actually a box -// projected from a switch_enum. -static void checkSwitchEnumBlockArg(SILPHIArgument *arg) { - assert(!arg->getType().isAddress()); - SILBasicBlock *Pred = arg->getParent()->getSinglePredecessorBlock(); - if (!Pred || !isa(Pred->getTerminator())) { - arg->dump(); - llvm_unreachable("unexpected box source."); - } -} - -SILValue swift::findAccessedAddressBase(SILValue sourceAddr) { - SILValue address = sourceAddr; - while (true) { - switch (address->getKind()) { - default: - address->dump(); - llvm_unreachable("unexpected address source."); - - // Base cases: these are always the base of a formal access. - case ValueKind::GlobalAddrInst: - case ValueKind::RefElementAddrInst: - // An AllocBox is a fully identified memory location. - case ValueKind::AllocBoxInst: - // An AllocStack is a fully identified memory location, which may occur - // after inlining code already subjected to stack promotion. - case ValueKind::AllocStackInst: - // View the outer begin_access as a separate location because nested - // accesses do not conflict with each other. - case ValueKind::BeginAccessInst: - // A function argument is effectively a nested access, enforced - // independently in the caller and callee. - case ValueKind::SILFunctionArgument: - // An addressor provides access to a global or class property via a - // RawPointer. Calling the addressor casts that raw pointer to an address. - case ValueKind::PointerToAddressInst: - return address; - - // A block argument may be a box value projected out of - // switch_enum. Address-type block arguments are not allowed. - case ValueKind::SILPHIArgument: - checkSwitchEnumBlockArg(cast(address)); - return address; - - // 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. - case ValueKind::LoadInst: { - assert(address->getType().is()); - address = cast(address)->getOperand(); - assert(isa(address)); - continue; - } - // Inductive 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: - address = cast(address)->getOperand(0); - continue; - - // Subobject projections. - case ValueKind::StructElementAddrInst: - case ValueKind::TupleElementAddrInst: - case ValueKind::UncheckedTakeEnumDataAddrInst: - case ValueKind::RefTailAddrInst: - case ValueKind::TailAddrInst: - case ValueKind::IndexAddrInst: - address = cast(address)->getOperand(0); - continue; - - // Value to address conversions: the operand is the non-address source - // value. These allow local mutation of the value but should never be used - // for formal access of an lvalue. - case ValueKind::OpenExistentialBoxInst: - case ValueKind::ProjectExistentialBoxInst: - case ValueKind::ProjectValueBufferInst: - return SILValue(); - - // Local initialization: these cases are skipped. - case ValueKind::InitEnumDataAddrInst: - case ValueKind::InitExistentialAddrInst: - case ValueKind::AllocExistentialBoxInst: - case ValueKind::AllocValueBufferInst: - case ValueKind::SILUndef: - return SILValue(); - } - } -} - -bool swift::isPossibleFormalAccessBase(SILValue baseAddress) { - // 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. - while (auto *beginAccess = dyn_cast(baseAddress)) - baseAddress = beginAccess->getOperand(); - - // Function arguments are accessed by the caller. - if (isa(baseAddress)) - return false; - - if (isa(baseAddress)) { - checkSwitchEnumBlockArg(cast(baseAddress)); - return false; - } - - // Pointer-to-address exclusivity cannot be enforced. `baseAddress` may be - // pointing anywhere within an object. - if (isa(baseAddress)) - return false; - - // Immutable values are only accessed for initialization. - if (isLetAccess(baseAddress)) - return false; - - // Special case unsafe value buffer access. - if (isa( - baseAddress->getType().getSwiftRValueType())) { - return false; - } - return true; -} SILValue swift::isPartialApplyOfReabstractionThunk(PartialApplyInst *PAI) { if (PAI->getNumArguments() != 1) @@ -512,9 +348,13 @@ static SILValue findClosureStoredIntoBlock(SILValue V) { // pattern match to find the noescape closure that invoking the block // will call: // %noescape_closure = ... + // %wae_Thunk = function_ref @$withoutActuallyEscapingThunk + // %sentinel = + // partial_apply [callee_guaranteed] %wae_thunk(%noescape_closure) + // %noescaped_wrapped = mark_dependence %sentinel on %noescape_closure // %storage = alloc_stack // %storage_address = project_block_storage %storage - // store %noescape_closure to [init] %storage_address + // store %noescaped_wrapped to [init] %storage_address // %block = init_block_storage_header %storage invoke %thunk // %arg = copy_block %block @@ -527,6 +367,11 @@ static SILValue findClosureStoredIntoBlock(SILValue V) { continue; } + if (auto *CBI = dyn_cast(V)) { + V = CBI->getBlock(); + continue; + } + IBSHI = dyn_cast(V); break; } @@ -541,7 +386,19 @@ static SILValue findClosureStoredIntoBlock(SILValue V) { auto *SI = PBSI->getSingleUserOfType(); assert(SI && "Couldn't find single store of function into block storage"); - return SI->getSrc(); + auto *CV = dyn_cast(SI->getSrc()); + if (!CV) + return nullptr; + auto *WrappedNoEscape = dyn_cast(CV->getOperand()); + if (!WrappedNoEscape) + return nullptr; + auto Sentinel = dyn_cast(WrappedNoEscape->getValue()); + if (!Sentinel) + return nullptr; + auto NoEscapeClosure = isPartialApplyOfReabstractionThunk(Sentinel); + if (WrappedNoEscape->getBase() != NoEscapeClosure) + return nullptr; + return NoEscapeClosure; } /// Look through a value passed as a function argument to determine whether @@ -582,221 +439,6 @@ FindClosureResult swift::findClosureForAppliedArg(SILValue V) { return FindClosureResult(PAI, false); } -/// Helper for visitApplyAccesses that visits address-type call arguments, -/// including arguments to @noescape functions that are passed as closures to -/// the current call. -static void visitApplyAccesses(ApplySite apply, - std::function visitor) { - for (Operand &oper : apply.getArgumentOperands()) { - // Consider any address-type operand an access. Whether it is read or modify - // depends on the argument convention. - if (oper.get()->getType().isAddress()) { - visitor(&oper); - continue; - } - auto fnType = oper.get()->getType().getAs(); - if (!fnType || !fnType->isNoEscape()) - continue; - - // When @noescape function closures are passed as arguments, their - // arguments are considered accessed at the call site. - FindClosureResult result = findClosureForAppliedArg(oper.get()); - if (!result.PAI) - continue; - - // Recursively visit @noescape function closure arguments. - visitApplyAccesses(result.PAI, visitor); - } -} - -void swift::visitAccessedAddress(SILInstruction *I, - std::function visitor) { - assert(I->mayReadOrWriteMemory()); - - // Reference counting instructions do not access user visible memory. - if (isa(I)) - return; - - if (isa(I)) - return; - - if (auto apply = FullApplySite::isa(I)) { - visitApplyAccesses(apply, visitor); - return; - } - - if (auto builtin = dyn_cast(I)) { - if (auto Kind = builtin->getBuiltinKind()) { - switch (Kind.getValue()) { - default: - I->dump(); - llvm_unreachable("unexpected bulitin memory access."); - - // Buitins that affect memory but can't be formal accesses. - case BuiltinValueKind::UnexpectedError: - case BuiltinValueKind::ErrorInMain: - case BuiltinValueKind::IsOptionalType: - case BuiltinValueKind::AllocRaw: - case BuiltinValueKind::DeallocRaw: - case BuiltinValueKind::Fence: - case BuiltinValueKind::StaticReport: - case BuiltinValueKind::Once: - case BuiltinValueKind::OnceWithContext: - case BuiltinValueKind::Unreachable: - case BuiltinValueKind::CondUnreachable: - case BuiltinValueKind::DestroyArray: - case BuiltinValueKind::UnsafeGuaranteed: - case BuiltinValueKind::UnsafeGuaranteedEnd: - case BuiltinValueKind::Swift3ImplicitObjCEntrypoint: - case BuiltinValueKind::TSanInoutAccess: - return; - - // General memory access to a pointer in first operand position. - case BuiltinValueKind::CmpXChg: - case BuiltinValueKind::AtomicLoad: - case BuiltinValueKind::AtomicStore: - case BuiltinValueKind::AtomicRMW: - // Currently ignored because the access is on a RawPointer, not a - // SIL address. - // visitor(&builtin->getAllOperands()[0]); - return; - - // Arrays: (T.Type, Builtin.RawPointer, Builtin.RawPointer, - // Builtin.Word) - case BuiltinValueKind::CopyArray: - case BuiltinValueKind::TakeArrayNoAlias: - case BuiltinValueKind::TakeArrayFrontToBack: - case BuiltinValueKind::TakeArrayBackToFront: - case BuiltinValueKind::AssignCopyArrayNoAlias: - case BuiltinValueKind::AssignCopyArrayFrontToBack: - case BuiltinValueKind::AssignCopyArrayBackToFront: - case BuiltinValueKind::AssignTakeArray: - // Currently ignored because the access is on a RawPointer. - // visitor(&builtin->getAllOperands()[1]); - // visitor(&builtin->getAllOperands()[2]); - return; - } - } - if (auto ID = builtin->getIntrinsicID()) { - switch (ID.getValue()) { - // Exhaustively verifying all LLVM instrinsics that access memory is - // impractical. Instead, we call out the few common cases and return in - // the default case. - default: - return; - case llvm::Intrinsic::memcpy: - case llvm::Intrinsic::memmove: - // Currently ignored because the access is on a RawPointer. - // visitor(&builtin->getAllOperands()[0]); - // visitor(&builtin->getAllOperands()[1]); - return; - case llvm::Intrinsic::memset: - // Currently ignored because the access is on a RawPointer. - // visitor(&builtin->getAllOperands()[0]); - return; - } - } - llvm_unreachable("Must be either a builtin or intrinsic."); - } - - switch (I->getKind()) { - default: - I->dump(); - llvm_unreachable("unexpected memory access."); - - case SILInstructionKind::AssignInst: - visitor(&I->getAllOperands()[AssignInst::Dest]); - return; - - case SILInstructionKind::CheckedCastAddrBranchInst: - visitor(&I->getAllOperands()[CheckedCastAddrBranchInst::Src]); - visitor(&I->getAllOperands()[CheckedCastAddrBranchInst::Dest]); - return; - - case SILInstructionKind::CopyAddrInst: - visitor(&I->getAllOperands()[CopyAddrInst::Src]); - visitor(&I->getAllOperands()[CopyAddrInst::Dest]); - return; - - case SILInstructionKind::StoreInst: - case SILInstructionKind::StoreBorrowInst: - case SILInstructionKind::StoreUnownedInst: - case SILInstructionKind::StoreWeakInst: - visitor(&I->getAllOperands()[StoreInst::Dest]); - return; - - case SILInstructionKind::SelectEnumAddrInst: - visitor(&I->getAllOperands()[0]); - return; - - case SILInstructionKind::InitExistentialAddrInst: - case SILInstructionKind::InjectEnumAddrInst: - case SILInstructionKind::LoadInst: - case SILInstructionKind::LoadBorrowInst: - case SILInstructionKind::LoadWeakInst: - case SILInstructionKind::LoadUnownedInst: - case SILInstructionKind::OpenExistentialAddrInst: - case SILInstructionKind::SwitchEnumAddrInst: - case SILInstructionKind::UncheckedTakeEnumDataAddrInst: - case SILInstructionKind::UnconditionalCheckedCastInst: { - assert(I->getNumOperands() - I->getNumTypeDependentOperands() == 1); - Operand *singleOperand = &I->getAllOperands()[0]; - if (singleOperand->get()->getType().isAddress()) - visitor(singleOperand); - return; - } - // Non-access cases: these are marked with memory side effects, but, by - // themselves, do not access formal memory. - case SILInstructionKind::AbortApplyInst: - case SILInstructionKind::AllocBoxInst: - case SILInstructionKind::AllocExistentialBoxInst: - case SILInstructionKind::AllocGlobalInst: - case SILInstructionKind::BeginAccessInst: - case SILInstructionKind::BeginApplyInst: - case SILInstructionKind::BeginBorrowInst: - case SILInstructionKind::BeginUnpairedAccessInst: - case SILInstructionKind::BindMemoryInst: - case SILInstructionKind::CheckedCastValueBranchInst: - case SILInstructionKind::CondFailInst: - case SILInstructionKind::CopyBlockInst: - case SILInstructionKind::CopyValueInst: - case SILInstructionKind::CopyUnownedValueInst: - case SILInstructionKind::DeinitExistentialAddrInst: - case SILInstructionKind::DeinitExistentialValueInst: - case SILInstructionKind::DestroyAddrInst: - case SILInstructionKind::DestroyValueInst: - case SILInstructionKind::EndAccessInst: - case SILInstructionKind::EndApplyInst: - case SILInstructionKind::EndBorrowArgumentInst: - case SILInstructionKind::EndBorrowInst: - case SILInstructionKind::EndUnpairedAccessInst: - case SILInstructionKind::EndLifetimeInst: - case SILInstructionKind::ExistentialMetatypeInst: - case SILInstructionKind::FixLifetimeInst: - case SILInstructionKind::InitExistentialValueInst: - case SILInstructionKind::IsUniqueInst: - case SILInstructionKind::IsEscapingClosureInst: - case SILInstructionKind::IsUniqueOrPinnedInst: - case SILInstructionKind::KeyPathInst: - case SILInstructionKind::OpenExistentialBoxInst: - case SILInstructionKind::OpenExistentialBoxValueInst: - case SILInstructionKind::OpenExistentialValueInst: - case SILInstructionKind::PartialApplyInst: - case SILInstructionKind::ProjectValueBufferInst: - case SILInstructionKind::StrongPinInst: - case SILInstructionKind::YieldInst: - case SILInstructionKind::UnwindInst: - case SILInstructionKind::UncheckedOwnershipConversionInst: - case SILInstructionKind::UncheckedRefCastAddrInst: - case SILInstructionKind::UnconditionalCheckedCastAddrInst: - case SILInstructionKind::UnconditionalCheckedCastValueInst: - case SILInstructionKind::UnownedReleaseInst: - case SILInstructionKind::UnownedRetainInst: - case SILInstructionKind::ValueMetatypeInst: - return; - } -} - namespace { enum class OwnershipQualifiedKind { diff --git a/lib/SIL/LinearLifetimeChecker.cpp b/lib/SIL/LinearLifetimeChecker.cpp new file mode 100644 index 0000000000000..dfbd0ade542d9 --- /dev/null +++ b/lib/SIL/LinearLifetimeChecker.cpp @@ -0,0 +1,516 @@ +//===--- LinearLifetimeChecker.cpp ----------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// +/// This file defines a linear lifetime checker for SIL. A value with a linear +/// lifetime is defined as a value that is guaranteed to be consuming exactly +/// once along any path through the program and has a guarantee that all +/// non-consuming uses and the initial value are joint-postdominated by the set +/// of consuming uses. +/// +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "sil-linear-lifetime-checker" +#include "swift/SIL/BasicBlockUtils.h" +#include "swift/SIL/BranchPropagatedUser.h" +#include "swift/SIL/OwnershipUtils.h" +#include "swift/SIL/SILBasicBlock.h" +#include "swift/SIL/SILFunction.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/Debug.h" + +using namespace swift; +using namespace swift::ownership; + +//===----------------------------------------------------------------------===// +// Declarations +//===----------------------------------------------------------------------===// + +namespace { + +using BrPropUserAndBlockPair = std::pair; + +struct State { + /// The value that we are checking. + SILValue value; + + /// The behavior of the checker when we detect an error. Can either be + /// returning false, returning false with a message emitted to stderr, or an + /// assert. + ErrorBehaviorKind errorBehavior; + + /// The blocks that we have already visited. + SmallPtrSetImpl &visitedBlocks; + + /// The set of blocks with consuming uses. + SmallPtrSet blocksWithConsumingUses; + + /// The set of blocks with non-consuming uses and the associated + /// non-consuming use SILInstruction. + llvm::SmallDenseMap + blocksWithNonConsumingUses; + + /// The worklist that we use when performing our block dataflow. + SmallVector worklist; + + /// A list of successor blocks that we must visit by the time the algorithm + /// terminates. + SmallPtrSet successorBlocksThatMustBeVisited; + + State(SILValue value, SmallPtrSetImpl &visitedBlocks, + ErrorBehaviorKind errorBehavior) + : value(value), errorBehavior(errorBehavior), + visitedBlocks(visitedBlocks) {} + + void initializeAllNonConsumingUses( + ArrayRef nonConsumingUsers); + bool initializeAllConsumingUses( + ArrayRef consumingUsers, + SmallVectorImpl &predsToAddToWorklist); + + /// Initializes state for a consuming use. Returns true if we have not yet + /// seen a consuming use in the same block yet. Returns false if detect such a + /// condition so users know that a use-after-free was detected. + bool initializeConsumingUse(BranchPropagatedUser consumingUser, + SILBasicBlock *userBlock); + + /// Returns true if the given block contains a non-consuming use that is + /// strictly later in the block than a consuming use. If all + /// non-consuming uses are before the consuming use, the block is + /// removed from the blocksWithNonConsumingUses map to show that the uses + /// were found to properly be post-dominated by a consuming use. + bool checkForSameBlockUseAfterFree(BranchPropagatedUser consumingUser, + SILBasicBlock *userBlock); + + /// Once we have marked all of our producing blocks. + bool checkPredsForDoubleConsume(BranchPropagatedUser consumingUser, + SILBasicBlock *userBlock); + bool checkPredsForDoubleConsume(SILBasicBlock *userBlock); + + /// Once we have setup all of our consuming/non-consuming blocks and have + /// validated that all intra-block dataflow is safe, perform the inter-block + /// dataflow. + bool performDataflow(DeadEndBlocks &deBlocks); + + /// After we have performed the dataflow, check the end state of our dataflow + /// for validity. If this is a linear typed value, return true. Return false + /// otherwise. + bool checkDataflowEndState(DeadEndBlocks &deBlocks); + + /// Depending on our initialization, either return false or call Func and + /// throw an error. + bool handleError(llvm::function_ref &&messagePrinterFunc) const { + if (errorBehavior.shouldPrintMessage()) { + messagePrinterFunc(); + } + + if (errorBehavior.shouldReturnFalse()) { + return false; + } + + assert(errorBehavior.shouldAssert() && "At this point, we should assert"); + llvm_unreachable("triggering standard assertion failure routine"); + } +}; + +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// Non Consuming Use Initialization +//===----------------------------------------------------------------------===// + +void State::initializeAllNonConsumingUses( + ArrayRef nonConsumingUsers) { + for (BranchPropagatedUser user : nonConsumingUsers) { + auto *userBlock = user.getParent(); + // First try to associate User with User->getParent(). + auto result = + blocksWithNonConsumingUses.insert(std::make_pair(userBlock, user)); + + // If the insertion succeeds, then we know that there is no more work to + // be done, so process the next use. + if (result.second) + continue; + + // If the insertion fails, then we have at least two non-consuming + // uses in the same block. Since we are performing a liveness type of + // dataflow, we only need the last non-consuming use to show that all + // consuming uses post dominate both. + // + // We begin by checking if the first use is a cond_br use from the previous + // block. In such a case, we always use the already stored value and + // continue. + if (user.isCondBranchUser()) { + continue; + } + + // Then, we check if Use is after Result.first->second in the use list. If + // Use is not later, then we wish to keep the already mapped value, not use, + // so continue. + if (std::find_if(result.first->second.getIterator(), userBlock->end(), + [&user](const SILInstruction &i) -> bool { + return user == &i; + }) == userBlock->end()) { + continue; + } + + // At this point, we know that user is later in the Block than + // result.first->second, so store user instead. + result.first->second = user; + } +} + +//===----------------------------------------------------------------------===// +// Consuming Use Initialization +//===----------------------------------------------------------------------===// + +bool State::initializeAllConsumingUses( + ArrayRef consumingUses, + SmallVectorImpl> + &predsToAddToWorklist) { + for (BranchPropagatedUser user : consumingUses) { + SILBasicBlock *userBlock = user.getParent(); + + // First initialize our state for the consuming user. This returns false if + // we found another consuming instruction associated with userBlock and true + // if we successfully associated user with userBlock. + if (!initializeConsumingUse(user, userBlock)) { + // We already handled the error. + return handleError([] {}); + } + + // Then check if the given block has a use after free. + if (checkForSameBlockUseAfterFree(user, userBlock)) { + // We already handled the error. + return handleError([] {}); + } + + // If this user is in the same block as the value, do not visit + // predecessors. We must be extra tolerant here since we allow for + // unreachable code. + if (userBlock == value->getParentBlock()) + continue; + + // Then for each predecessor of this block... + for (auto *pred : userBlock->getPredecessorBlocks()) { + // If this block is not a block that we have already put on the list, add + // it to the worklist. + predsToAddToWorklist.push_back({user, pred}); + } + } + + return true; +} + +bool State::initializeConsumingUse(BranchPropagatedUser consumingUser, + SILBasicBlock *userBlock) { + // Map this user to the block. If we already have a value for the block, then + // we have a double consume and need to fail. + if (blocksWithConsumingUses.insert(userBlock).second) + return true; + + return handleError([&] { + llvm::errs() << "Function: '" << value->getFunction()->getName() << "'\n" + << "Found over consume?!\n" + << "Value: " << *value << "User: " << *consumingUser + << "Block: bb" << userBlock->getDebugID() << "\n\n"; + }); +} + +bool State::checkForSameBlockUseAfterFree(BranchPropagatedUser consumingUser, + SILBasicBlock *userBlock) { + // If we do not have any consuming uses in the same block as our + // consuming user, then we can not have a same block use-after-free. + auto iter = blocksWithNonConsumingUses.find(userBlock); + if (iter == blocksWithNonConsumingUses.end()) + return false; + + BranchPropagatedUser nonConsumingUser = iter->second; + + // Make sure that the non-consuming use is before the consuming + // use. Otherwise, we have a use after free. + + // First check if our consuming user is a cond_br. In such a case, we + // always consider the non-consuming use to be a use after free since + // the cond branch user is in a previous block. So just bail early. + if (consumingUser.isCondBranchUser()) { + return !handleError([&]() { + llvm::errs() << "Function: '" << value->getFunction()->getName() << "'\n" + << "Found use after free?!\n" + << "Value: " << *value + << "Consuming User: " << *consumingUser + << "Non Consuming User: " << *iter->second << "Block: bb" + << userBlock->getDebugID() << "\n\n"; + }); + } + + // Ok. At this point, we know that our consuming user is not a cond branch + // user. Check if our non-consuming user is. In such a case, we know that our + // non-consuming user is properly post-dominated so we can ignore the + // consuming use. and continue. + if (nonConsumingUser.isCondBranchUser()) { + blocksWithNonConsumingUses.erase(iter); + return false; + } + + // Otherwise, we know that both of our users are non-cond branch users and + // thus must be instructions in the given block. Make sure that the non + // consuming user is strictly before the consuming user. + if (std::find_if(consumingUser.getIterator(), userBlock->end(), + [&nonConsumingUser](const SILInstruction &i) -> bool { + return nonConsumingUser == &i; + }) != userBlock->end()) { + return !handleError([&] { + llvm::errs() << "Function: '" << value->getFunction()->getName() << "'\n" + << "Found use after free?!\n" + << "Value: " << *value + << "Consuming User: " << *consumingUser + << "Non Consuming User: " << *iter->second << "Block: bb" + << userBlock->getDebugID() << "\n\n"; + }); + } + + // Erase the use since we know that it is properly joint post-dominated. + blocksWithNonConsumingUses.erase(iter); + return false; +} + +bool State::checkPredsForDoubleConsume(BranchPropagatedUser consumingUser, + SILBasicBlock *userBlock) { + if (!blocksWithConsumingUses.count(userBlock)) + return false; + + return !handleError([&] { + llvm::errs() << "Function: '" << value->getFunction()->getName() << "'\n" + << "Found over consume?!\n" + << "Value: " << *value << "User: " << *consumingUser + << "Block: bb" << userBlock->getDebugID() << "\n\n"; + }); +} + +bool State::checkPredsForDoubleConsume(SILBasicBlock *userBlock) { + if (!blocksWithConsumingUses.count(userBlock)) + return false; + + return !handleError([&] { + llvm::errs() << "Function: '" << value->getFunction()->getName() << "'\n" + << "Found over consume?!\n" + << "Value: " << *value << "Block: bb" + << userBlock->getDebugID() << "\n\n"; + }); +} + +//===----------------------------------------------------------------------===// +// Dataflow +//===----------------------------------------------------------------------===// + +bool State::performDataflow(DeadEndBlocks &deBlocks) { + DEBUG(llvm::dbgs() << " Beginning to check dataflow constraints\n"); + // Until the worklist is empty... + while (!worklist.empty()) { + // Grab the next block to visit. + SILBasicBlock *block = worklist.pop_back_val(); + DEBUG(llvm::dbgs() << " Visiting Block: bb" << block->getDebugID() + << '\n'); + + // Since the block is on our worklist, we know already that it is not a + // block with lifetime ending uses, due to the invariants of our loop. + + // First remove BB from the SuccessorBlocksThatMustBeVisited list. This + // ensures that when the algorithm terminates, we know that BB was not the + // beginning of a non-covered path to the exit. + successorBlocksThatMustBeVisited.erase(block); + + // Then remove BB from BlocksWithNonLifetimeEndingUses so we know that + // this block was properly joint post-dominated by our lifetime ending + // users. + blocksWithNonConsumingUses.erase(block); + + // Ok, now we know that we do not have an overconsume. If this block does + // not end in a no return function, we need to update our state for our + // successors to make sure by the end of the traversal we visit them. + // + // We must consider such no-return blocks since we may be running during + // SILGen before NoReturn folding has run. + for (auto *succBlock : block->getSuccessorBlocks()) { + // If we already visited the successor, there is nothing to do since we + // already visited the successor. + if (visitedBlocks.count(succBlock)) + continue; + + // Then check if the successor is a transitively unreachable block. In + // such a case, we ignore it since we are going to leak along that path. + if (deBlocks.isDeadEnd(succBlock)) + continue; + + // Otherwise, add the successor to our SuccessorBlocksThatMustBeVisited + // set to ensure that we assert if we do not visit it by the end of the + // algorithm. + successorBlocksThatMustBeVisited.insert(succBlock); + } + + // If we are at the dominating block of our walk, continue. There is nothing + // further to do since we do not want to visit the predecessors of our + // dominating block. On the other hand, we do want to add its successors to + // the successorBlocksThatMustBeVisited set. + if (block == value->getParentBlock()) + continue; + + // Then for each predecessor of this block: + // + // 1. If we have visited the predecessor already, then it is not a block + // with lifetime ending uses. If it is a block with uses, then we have a + // double release... so assert. If not, we continue. + // + // 2. We add the predecessor to the worklist if we have not visited it yet. + for (auto *predBlock : block->getPredecessorBlocks()) { + if (checkPredsForDoubleConsume(predBlock)) { + return handleError([] {}); + } + + if (visitedBlocks.count(predBlock)) { + continue; + } + + visitedBlocks.insert(predBlock); + worklist.push_back(predBlock); + } + } + + return true; +} + +bool State::checkDataflowEndState(DeadEndBlocks &deBlocks) { + // Make sure that we visited all successor blocks that we needed to visit to + // make sure we didn't leak. + if (!successorBlocksThatMustBeVisited.empty()) { + return handleError([&] { + llvm::errs() + << "Function: '" << value->getFunction()->getName() << "'\n" + << "Error! Found a leak due to a consuming post-dominance failure!\n" + << " Value: " << *value << " Post Dominating Failure Blocks:\n"; + for (auto *succBlock : successorBlocksThatMustBeVisited) { + llvm::errs() << " bb" << succBlock->getDebugID(); + } + llvm::errs() << '\n'; + }); + } + + // Make sure that we do not have any lifetime ending uses left to visit that + // are not transitively unreachable blocks.... so return early. + if (blocksWithNonConsumingUses.empty()) { + return true; + } + + // If we do have remaining blocks, then these non lifetime ending uses must be + // outside of our "alive" blocks implying a use-after free. + for (auto &pair : blocksWithNonConsumingUses) { + if (deBlocks.isDeadEnd(pair.first)) { + continue; + } + + return handleError([&] { + llvm::errs() << "Function: '" << value->getFunction()->getName() << "'\n" + << "Found use after free due to unvisited non lifetime " + "ending uses?!\n" + << "Value: " << *value << " Remaining Users:\n"; + for (auto &pair : blocksWithNonConsumingUses) { + llvm::errs() << "User:" << *pair.second << "Block: bb" + << pair.first->getDebugID() << "\n"; + } + llvm::errs() << "\n"; + }); + } + + // If all of our remaining blocks were dead uses, then return true. We are + // good. + return true; +} + +//===----------------------------------------------------------------------===// +// Top Level Entrypoints +//===----------------------------------------------------------------------===// + +bool swift::valueHasLinearLifetime( + SILValue value, ArrayRef consumingUses, + ArrayRef nonConsumingUses, + SmallPtrSetImpl &visitedBlocks, DeadEndBlocks &deBlocks, + ErrorBehaviorKind errorBehavior) { + assert(!consumingUses.empty() && "Must have at least one consuming user?!"); + + State state(value, visitedBlocks, errorBehavior); + + // First add our non-consuming uses and their blocks to the + // blocksWithNonConsumingUses map. While we do this, if we have multiple uses + // in the same block, we only accept the last use since from a liveness + // perspective that is all we care about. + state.initializeAllNonConsumingUses(nonConsumingUses); + + // Then, we go through each one of our consuming users performing the + // following operation: + // + // 1. Verifying that no two consuming users are in the same block. This + // is accomplished by adding the user blocks to the blocksWithConsumingUsers + // list. This avoids double consumes. + // + // 2. Verifying that no predecessor is a block with a consuming use. The + // reason why this is necessary is because we wish to not add elements to the + // worklist twice. Thus we want to check if we have already visited a + // predecessor. + SmallVector predsToAddToWorklist; + state.initializeAllConsumingUses(consumingUses, predsToAddToWorklist); + + // If we have a singular consuming use and it is in the same block as value's + // def, we bail early. Any use-after-frees due to non-consuming uses would + // have been detected by initializing our consuming uses. So we are done. + if (consumingUses.size() == 1 && + consumingUses[0].getParent() == value->getParentBlock()) { + return true; + } + + // Ok, we may have multiple consuming uses. Add the user block of each of our + // consuming users to the visited list since we do not want them to be added + // to the successors to visit set. + for (const auto &i : consumingUses) { + state.visitedBlocks.insert(i.getParent()); + } + + // Now that we have marked all of our producing blocks, we go through our + // predsToAddToWorklist list and add our preds, making sure that none of these + // preds are in blocksWithConsumingUses. This is important so that we do not + // need to re-process. + for (auto pair : predsToAddToWorklist) { + BranchPropagatedUser user = pair.first; + SILBasicBlock *predBlock = pair.second; + + // Make sure that the predecessor is not in our blocksWithConsumingUses + // list. + if (state.checkPredsForDoubleConsume(user, predBlock)) { + return state.handleError([] {}); + } + + if (!state.visitedBlocks.insert(predBlock).second) + continue; + state.worklist.push_back(predBlock); + } + + // Now that our algorithm is completely prepared, run the + // dataflow... If we find a failure, return false. + if (!state.performDataflow(deBlocks)) + return false; + + // ...and then check that the end state shows that we have a valid linear + // typed value. + return state.checkDataflowEndState(deBlocks); +} diff --git a/lib/SIL/Linker.cpp b/lib/SIL/Linker.cpp index dd18be3d5c73c..9d51f758264b6 100644 --- a/lib/SIL/Linker.cpp +++ b/lib/SIL/Linker.cpp @@ -10,6 +10,45 @@ // //===----------------------------------------------------------------------===// +/// \file The SIL linker walks the call graph beginning at a starting function, +/// deserializing functions, vtables and witness tables. +/// +/// The behavior of the linker is controlled by a LinkMode value. The LinkMode +/// has three possible values: +/// +/// - LinkNone: The linker does not deserialize anything. This is only used for +/// debugging and testing purposes, and never during normal operation. +/// +/// - LinkNormal: The linker deserializes bodies for declarations that must be +/// emitted into the client because they do not have definitions available +/// externally. This includes: +/// +/// - witness tables for imported conformances +/// +/// - functions with shared linkage +/// +/// - LinkAll: All reachable functions (including public functions) are +/// deserialized, including public functions. +/// +/// The primary entry point into the linker is the SILModule::linkFunction() +/// function, which recursively walks the call graph starting from the given +/// function. +/// +/// In the mandatory pipeline (-Onone), the linker is invoked from the mandatory +/// SIL linker pass, which pulls in just enough to allow us to emit code, using +/// LinkNormal mode. +/// +/// In the performance pipeline, after guaranteed optimizations but before +/// performance optimizations, the 'performance SILLinker' pass links +/// transitively all reachable functions, to uncover optimization opportunities +/// that might be missed from deserializing late. The performance pipeline uses +/// LinkAll mode. +/// +/// *NOTE*: In LinkAll mode, we deserialize all vtables and witness tables, +/// even those with public linkage. This is not strictly necessary, since the +/// devirtualizer deserializes vtables and witness tables as needed. However, +/// doing so early creates more opportunities for optimization. + #define DEBUG_TYPE "sil-linker" #include "Linker.h" #include "llvm/ADT/Statistic.h" @@ -18,6 +57,8 @@ #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/Debug.h" #include "swift/AST/ProtocolConformance.h" +#include "swift/AST/SubstitutionMap.h" +#include "swift/ClangImporter/ClangModule.h" #include "swift/SIL/FormalLinkage.h" #include @@ -30,139 +71,143 @@ STATISTIC(NumFuncLinked, "Number of SIL functions linked"); // Linker Helpers //===----------------------------------------------------------------------===// -/// Process F, recursively deserializing any thing F may reference. -bool SILLinkerVisitor::processFunction(SILFunction *F) { - if (Mode == LinkingMode::LinkNone) - return false; - - // If F is a declaration, first deserialize it. - if (F->isExternalDeclaration()) { - auto *NewFn = Loader->lookupSILFunction(F); +void SILLinkerVisitor::addFunctionToWorklist(SILFunction *F) { + assert(F->isExternalDeclaration()); - if (!NewFn || NewFn->isExternalDeclaration()) - return false; + DEBUG(llvm::dbgs() << "Imported function: " + << F->getName() << "\n"); + if (Mod.loadFunction(F)) { + if (F->isExternalDeclaration()) + return; - F = NewFn; + F->setBare(IsBare); + F->verify(); + Worklist.push_back(F); + Changed = true; + ++NumFuncLinked; } - - ++NumFuncLinked; - - // Try to transitively deserialize everything referenced by this - // function. - Worklist.push_back(F); - process(); - - // Since we successfully processed at least one function, return true. - return true; } -/// Deserialize the VTable mapped to C if it exists and all SIL the VTable -/// transitively references. -/// -/// This method assumes that the caller made sure that no vtable existed in -/// Mod. -SILVTable *SILLinkerVisitor::processClassDecl(const ClassDecl *C) { - // If we are not linking anything, bail. - if (Mode == LinkingMode::LinkNone) - return nullptr; - - // Attempt to load the VTable from the SerializedSILLoader. If we - // fail... bail... - SILVTable *Vtbl = Loader->lookupVTable(C); - if (!Vtbl) - return nullptr; +/// Deserialize a function and add it to the worklist for processing. +void SILLinkerVisitor::maybeAddFunctionToWorklist(SILFunction *F) { + // Don't need to do anything if the function already has a body. + if (!F->isExternalDeclaration()) + return; + + // In the performance pipeline, we deserialize all reachable functions. + if (isLinkAll()) + return addFunctionToWorklist(F); + + // Otherwise, make sure to deserialize shared functions; we need to + // emit them into the client binary since they're not available + // externally. + if (hasSharedVisibility(F->getLinkage())) + return addFunctionToWorklist(F); + + // Functions with PublicNonABI linkage are deserialized as having + // HiddenExternal linkage when they are declarations, then they + // become SharedExternal after the body has been deserialized. + // So try deserializing HiddenExternal functions too. + if (F->getLinkage() == SILLinkage::HiddenExternal) + return addFunctionToWorklist(F); +} - // Otherwise, add all the vtable functions in Vtbl to the function - // processing list... - for (auto &E : Vtbl->getEntries()) - Worklist.push_back(E.Implementation); +/// Process F, recursively deserializing any thing F may reference. +bool SILLinkerVisitor::processFunction(SILFunction *F) { + // If F is a declaration, first deserialize it. + if (F->isExternalDeclaration()) { + maybeAddFunctionToWorklist(F); + } else { + Worklist.push_back(F); + } - // And then transitively deserialize all SIL referenced by those functions. process(); - - // Return the deserialized Vtbl. - return Vtbl; + return Changed; } -bool SILLinkerVisitor::linkInVTable(ClassDecl *D) { +/// Deserialize the given VTable all SIL the VTable transitively references. +void SILLinkerVisitor::linkInVTable(ClassDecl *D) { + // Devirtualization already deserializes vtables as needed in both the + // mandatory and performance pipelines, and we don't support specialized + // vtables that might have shared linkage yet, so this is only needed in + // the performance pipeline to deserialize more functions early, and expose + // optimization opportunities. + assert(isLinkAll()); + // Attempt to lookup the Vtbl from the SILModule. SILVTable *Vtbl = Mod.lookUpVTable(D); - - // If the SILModule does not have the VTable, attempt to deserialize the - // VTable. If we fail to do that as well, bail. - if (!Vtbl || !(Vtbl = Loader->lookupVTable(D->getName()))) - return false; + if (!Vtbl) + return; // Ok we found our VTable. Visit each function referenced by the VTable. If // any of the functions are external declarations, add them to the worklist // for processing. - bool Result = false; for (auto P : Vtbl->getEntries()) { - if (P.Implementation->isExternalDeclaration()) { - Result = true; - addFunctionToWorklist(P.Implementation); - } + // Deserialize and recursively walk any vtable entries that do not have + // bodies yet. + maybeAddFunctionToWorklist(P.Implementation); } - return Result; } //===----------------------------------------------------------------------===// // Visitors //===----------------------------------------------------------------------===// -bool SILLinkerVisitor::visitApplyInst(ApplyInst *AI) { - // Ok we have a function ref inst, grab the callee. - SILFunction *Callee = AI->getReferencedFunction(); - if (!Callee) - return false; - - if (isLinkAll() || - hasSharedVisibility(Callee->getLinkage())) { - addFunctionToWorklist(Callee); - return true; +void SILLinkerVisitor::visitApplyInst(ApplyInst *AI) { + if (auto sig = AI->getCallee()->getType().castTo() + ->getGenericSignature()) { + visitApplySubstitutions(AI->getSubstitutionMap()); } - - return false; } -bool SILLinkerVisitor::visitPartialApplyInst(PartialApplyInst *PAI) { - SILFunction *Callee = PAI->getReferencedFunction(); - if (!Callee) - return false; - - if (isLinkAll() || - hasSharedVisibility(Callee->getLinkage())) { - addFunctionToWorklist(Callee); - return true; +void SILLinkerVisitor::visitTryApplyInst(TryApplyInst *TAI) { + if (auto sig = TAI->getCallee()->getType().castTo() + ->getGenericSignature()) { + visitApplySubstitutions(TAI->getSubstitutionMap()); } - - return false; } -bool SILLinkerVisitor::visitFunctionRefInst(FunctionRefInst *FRI) { - // Needed to handle closures which are no longer applied, but are left - // behind as dead code. This shouldn't happen, but if it does don't get into - // an inconsistent state. - SILFunction *Callee = FRI->getReferencedFunction(); - - if (isLinkAll() || - hasSharedVisibility(Callee->getLinkage())) { - addFunctionToWorklist(FRI->getReferencedFunction()); - return true; +void SILLinkerVisitor::visitPartialApplyInst(PartialApplyInst *PAI) { + if (auto sig = PAI->getCallee()->getType().castTo() + ->getGenericSignature()) { + visitApplySubstitutions(PAI->getSubstitutionMap()); } +} + +void SILLinkerVisitor::visitFunctionRefInst(FunctionRefInst *FRI) { + maybeAddFunctionToWorklist(FRI->getReferencedFunction()); +} - return false; +// Eagerly visiting all used conformances leads to a large blowup +// in the amount of SIL we read in. For optimization purposes we can defer +// reading in most conformances until we need them for devirtualization. +// However, we *must* pull in shared clang-importer-derived conformances +// we potentially use, since we may not otherwise have a local definition. +static bool mustDeserializeProtocolConformance(SILModule &M, + ProtocolConformanceRef c) { + if (!c.isConcrete()) + return false; + auto conformance = c.getConcrete()->getRootNormalConformance(); + return M.Types.protocolRequiresWitnessTable(conformance->getProtocol()) + && isa(conformance->getDeclContext() + ->getModuleScopeContext()); } -bool SILLinkerVisitor::visitProtocolConformance( +void SILLinkerVisitor::visitProtocolConformance( ProtocolConformanceRef ref, const Optional &Member) { // If an abstract protocol conformance was passed in, just return false. if (ref.isAbstract()) - return false; + return; + + bool mustDeserialize = mustDeserializeProtocolConformance(Mod, ref); // Otherwise try and lookup a witness table for C. auto C = ref.getConcrete(); - SILWitnessTable *WT = Mod.lookUpWitnessTable(C); + + if (!VisitedConformances.insert(C).second) + return; + + SILWitnessTable *WT = Mod.lookUpWitnessTable(C, true); // If we don't find any witness table for the conformance, bail and return // false. @@ -170,19 +215,39 @@ bool SILLinkerVisitor::visitProtocolConformance( Mod.createWitnessTableDeclaration( C, getLinkageForProtocolConformance( C->getRootNormalConformance(), NotForDefinition)); - return false; + + // Adding the declaration may allow us to now deserialize the body. + // Force the body if we must deserialize this witness table. + if (mustDeserialize) { + WT = Mod.lookUpWitnessTable(C, true); + assert(WT && WT->isDefinition() + && "unable to deserialize witness table when we must?!"); + } else { + return; + } } // If the looked up witness table is a declaration, there is nothing we can // do here. Just bail and return false. if (WT->isDeclaration()) - return false; - - bool performFuncDeserialization = false; + return; + + auto maybeVisitRelatedConformance = [&](ProtocolConformanceRef c) { + // Formally all conformances referenced by a used conformance are used. + // However, eagerly visiting them all at this point leads to a large blowup + // in the amount of SIL we read in. For optimization purposes we can defer + // reading in most conformances until we need them for devirtualization. + // However, we *must* pull in shared clang-importer-derived conformances + // we potentially use, since we may not otherwise have a local definition. + if (mustDeserializeProtocolConformance(Mod, c)) + visitProtocolConformance(c, None); + }; + // For each entry in the witness table... for (auto &E : WT->getEntries()) { + switch (E.getKind()) { // If the entry is a witness method... - if (E.getKind() == SILWitnessTable::WitnessKind::Method) { + case SILWitnessTable::WitnessKind::Method: { // And we are only interested in deserializing a specific requirement // and don't have that requirement, don't deserialize this method. if (Member.hasValue() && E.getMethodWitness().Requirement != *Member) @@ -192,18 +257,62 @@ bool SILLinkerVisitor::visitProtocolConformance( if (!E.getMethodWitness().Witness) continue; - // Otherwise if it is the requirement we are looking for or we just want - // to deserialize everything, add the function to the list of functions - // to deserialize. - performFuncDeserialization = true; - addFunctionToWorklist(E.getMethodWitness().Witness); + // Otherwise, deserialize the witness if it has shared linkage, or if + // we were asked to deserialize everything. + maybeAddFunctionToWorklist(E.getMethodWitness().Witness); + break; + } + + // If the entry is a related witness table, see whether we need to + // eagerly deserialize it. + case SILWitnessTable::WitnessKind::BaseProtocol: { + auto baseConformance = E.getBaseProtocolWitness().Witness; + maybeVisitRelatedConformance(ProtocolConformanceRef(baseConformance)); + break; + } + case SILWitnessTable::WitnessKind::AssociatedTypeProtocol: { + auto assocConformance = E.getAssociatedTypeProtocolWitness().Witness; + maybeVisitRelatedConformance(assocConformance); + break; + } + + case SILWitnessTable::WitnessKind::AssociatedType: + case SILWitnessTable::WitnessKind::Invalid: + break; } } +} - return performFuncDeserialization; +void SILLinkerVisitor::visitApplySubstitutions(SubstitutionMap subs) { + for (auto &reqt : subs.getGenericSignature()->getRequirements()) { + switch (reqt.getKind()) { + case RequirementKind::Conformance: { + auto conformance = subs.lookupConformance( + reqt.getFirstType()->getCanonicalType(), + cast(reqt.getSecondType()->getAnyNominal())) + .getValue(); + + // Formally all conformances referenced in a function application are + // used. However, eagerly visiting them all at this point leads to a + // large blowup in the amount of SIL we read in, and we aren't very + // systematic about laziness. For optimization purposes we can defer + // reading in most conformances until we need them for devirtualization. + // However, we *must* pull in shared clang-importer-derived conformances + // we potentially use, since we may not otherwise have a local definition. + if (mustDeserializeProtocolConformance(Mod, conformance)) { + visitProtocolConformance(conformance, None); + } + break; + } + case RequirementKind::Layout: + case RequirementKind::SameType: + case RequirementKind::Superclass: + break; + } + } } -bool SILLinkerVisitor::visitInitExistentialAddrInst( +void SILLinkerVisitor::visitInitExistentialAddrInst( InitExistentialAddrInst *IEI) { // Link in all protocol conformances that this touches. // @@ -213,15 +322,12 @@ bool SILLinkerVisitor::visitInitExistentialAddrInst( // not going to be smart about this to enable avoiding any issues with // visiting the open_existential_addr/witness_method before the // init_existential_inst. - bool performFuncDeserialization = false; for (ProtocolConformanceRef C : IEI->getConformances()) { - performFuncDeserialization |= - visitProtocolConformance(C, Optional()); + visitProtocolConformance(C, Optional()); } - return performFuncDeserialization; } -bool SILLinkerVisitor::visitInitExistentialRefInst( +void SILLinkerVisitor::visitInitExistentialRefInst( InitExistentialRefInst *IERI) { // Link in all protocol conformances that this touches. // @@ -230,30 +336,33 @@ bool SILLinkerVisitor::visitInitExistentialRefInst( // protocol method inst causes the actual deserialization. For now we are // not going to be smart about this to enable avoiding any issues with // visiting the protocol_method before the init_existential_inst. - bool performFuncDeserialization = false; for (ProtocolConformanceRef C : IERI->getConformances()) { - performFuncDeserialization |= - visitProtocolConformance(C, Optional()); + visitProtocolConformance(C, Optional()); } - return performFuncDeserialization; } -bool SILLinkerVisitor::visitAllocRefInst(AllocRefInst *ARI) { +void SILLinkerVisitor::visitAllocRefInst(AllocRefInst *ARI) { + if (!isLinkAll()) + return; + // Grab the class decl from the alloc ref inst. ClassDecl *D = ARI->getType().getClassOrBoundGenericClass(); if (!D) - return false; + return; - return linkInVTable(D); + linkInVTable(D); } -bool SILLinkerVisitor::visitMetatypeInst(MetatypeInst *MI) { +void SILLinkerVisitor::visitMetatypeInst(MetatypeInst *MI) { + if (!isLinkAll()) + return; + CanType instTy = MI->getType().castTo().getInstanceType(); ClassDecl *C = instTy.getClassOrBoundGenericClass(); if (!C) - return false; + return; - return linkInVTable(C); + linkInVTable(C); } //===----------------------------------------------------------------------===// @@ -261,10 +370,9 @@ bool SILLinkerVisitor::visitMetatypeInst(MetatypeInst *MI) { //===----------------------------------------------------------------------===// // Main loop of the visitor. Called by one of the other *visit* methods. -bool SILLinkerVisitor::process() { +void SILLinkerVisitor::process() { // Process everything transitively referenced by one of the functions in the // worklist. - bool Result = false; while (!Worklist.empty()) { auto *Fn = Worklist.pop_back_val(); @@ -280,37 +388,8 @@ bool SILLinkerVisitor::process() { for (auto &BB : *Fn) { for (auto &I : BB) { - // Should we try linking? - if (visit(&I)) { - for (auto *F : FunctionDeserializationWorklist) { - - DEBUG(llvm::dbgs() << "Imported function: " - << F->getName() << "\n"); - F->setBare(IsBare); - - if (F->isExternalDeclaration()) { - if (auto *NewFn = Loader->lookupSILFunction(F)) { - if (NewFn->isExternalDeclaration()) - continue; - - NewFn->verify(); - Worklist.push_back(NewFn); - Result = true; - - ++NumFuncLinked; - } - } - } - FunctionDeserializationWorklist.clear(); - } else { - assert(FunctionDeserializationWorklist.empty() && - "Worklist should " - "always be empty if visit does not return true."); - } + visit(&I); } } } - - // If we return true, we deserialized at least one function. - return Result; } diff --git a/lib/SIL/Linker.h b/lib/SIL/Linker.h index cca58397a97aa..093578e3a2761 100644 --- a/lib/SIL/Linker.h +++ b/lib/SIL/Linker.h @@ -16,20 +16,19 @@ #include "swift/SIL/SILDebugScope.h" #include "swift/SIL/SILVisitor.h" #include "swift/SIL/SILModule.h" -#include "swift/Serialization/SerializedSILLoader.h" #include namespace swift { /// Visitor that knows how to link in dependencies of SILInstructions. -class SILLinkerVisitor : public SILInstructionVisitor { +class SILLinkerVisitor : public SILInstructionVisitor { using LinkingMode = SILModule::LinkingMode; /// The SILModule that we are loading from. SILModule &Mod; - /// The SILLoader that this visitor is using to link. - SerializedSILLoader *Loader; + /// Break cycles visiting recursive protocol conformances. + llvm::DenseSet VisitedConformances; /// Worklist of SILFunctions we are processing. llvm::SmallVector Worklist; @@ -41,13 +40,16 @@ class SILLinkerVisitor : public SILInstructionVisitor { /// The current linking mode. LinkingMode Mode; + /// Whether any functions were deserialized. + bool Changed; + public: - SILLinkerVisitor(SILModule &M, SerializedSILLoader *L, - SILModule::LinkingMode LinkingMode) - : Mod(M), Loader(L), Worklist(), FunctionDeserializationWorklist(), - Mode(LinkingMode) {} + SILLinkerVisitor(SILModule &M, SILModule::LinkingMode LinkingMode) + : Mod(M), Worklist(), FunctionDeserializationWorklist(), + Mode(LinkingMode), Changed(false) {} /// Process F, recursively deserializing any thing F may reference. + /// Returns true if any deserialization was performed. bool processFunction(SILFunction *F); /// Deserialize the VTable mapped to C if it exists and all SIL the VTable @@ -58,35 +60,40 @@ class SILLinkerVisitor : public SILInstructionVisitor { SILVTable *processClassDecl(const ClassDecl *C); /// We do not want to visit callee functions if we just have a value base. - bool visitSILInstruction(SILInstruction *I) { return false; } + void visitSILInstruction(SILInstruction *I) { } - bool visitApplyInst(ApplyInst *AI); - bool visitPartialApplyInst(PartialApplyInst *PAI); - bool visitFunctionRefInst(FunctionRefInst *FRI); - bool visitProtocolConformance(ProtocolConformanceRef C, + void visitApplyInst(ApplyInst *AI); + void visitTryApplyInst(TryApplyInst *TAI); + void visitPartialApplyInst(PartialApplyInst *PAI); + void visitFunctionRefInst(FunctionRefInst *FRI); + void visitProtocolConformance(ProtocolConformanceRef C, const Optional &Member); - bool visitWitnessMethodInst(WitnessMethodInst *WMI) { - return visitProtocolConformance(WMI->getConformance(), WMI->getMember()); + void visitApplySubstitutions(SubstitutionMap subs); + void visitWitnessMethodInst(WitnessMethodInst *WMI) { + visitProtocolConformance(WMI->getConformance(), WMI->getMember()); } - bool visitInitExistentialAddrInst(InitExistentialAddrInst *IEI); - bool visitInitExistentialRefInst(InitExistentialRefInst *IERI); - bool visitAllocRefInst(AllocRefInst *ARI); - bool visitMetatypeInst(MetatypeInst *MI); + void visitInitExistentialAddrInst(InitExistentialAddrInst *IEI); + void visitInitExistentialRefInst(InitExistentialRefInst *IERI); + void visitAllocRefInst(AllocRefInst *ARI); + void visitMetatypeInst(MetatypeInst *MI); private: - /// Add a function to our function worklist for processing. - void addFunctionToWorklist(SILFunction *F) { - FunctionDeserializationWorklist.push_back(F); - } + /// Cause a function to be deserialized, and visit all other functions + /// referenced from this function according to the linking mode. + void addFunctionToWorklist(SILFunction *F); + + /// Consider a function for deserialization if the current linking mode + /// requires it. + void maybeAddFunctionToWorklist(SILFunction *F); /// Is the current mode link all? Link all implies we should try and link /// everything, not just transparent/shared functions. bool isLinkAll() const { return Mode == LinkingMode::LinkAll; } - bool linkInVTable(ClassDecl *D); + void linkInVTable(ClassDecl *D); // Main loop of the visitor. Called by one of the other *visit* methods. - bool process(); + void process(); }; } // end namespace swift diff --git a/lib/SIL/MemAccessUtils.cpp b/lib/SIL/MemAccessUtils.cpp new file mode 100644 index 0000000000000..cb588441f0bfa --- /dev/null +++ b/lib/SIL/MemAccessUtils.cpp @@ -0,0 +1,617 @@ +//===--- MemAccessUtils.cpp - Utilities for SIL memory access. ------------===// +// +// 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-access-utils" + +#include "swift/SIL/MemAccessUtils.h" +#include "swift/SIL/SILUndef.h" + +using namespace swift; + +AccessedStorage::Kind AccessedStorage::classify(SILValue base) { + switch (base->getKind()) { + // An AllocBox is a fully identified memory location. + case ValueKind::AllocBoxInst: + return Box; + // An AllocStack is a fully identified memory location, which may occur + // after inlining code already subjected to stack promotion. + case ValueKind::AllocStackInst: + return Stack; + case ValueKind::GlobalAddrInst: + return Global; + case ValueKind::RefElementAddrInst: + return Class; + // A function argument is effectively a nested access, enforced + // independently in the caller and callee. + case ValueKind::SILFunctionArgument: + return Argument; + // View the outer begin_access as a separate location because nested + // accesses do not conflict with each other. + case ValueKind::BeginAccessInst: + return Nested; + default: + return Unidentified; + } +} + +AccessedStorage::AccessedStorage(SILValue base, Kind kind) { + assert(base && "invalid storage base"); + initKind(kind); + + switch (kind) { + case Box: + assert(isa(base)); + value = base; + break; + case Stack: + assert(isa(base)); + value = base; + break; + case Nested: + assert(isa(base)); + value = base; + break; + case Unidentified: + value = base; + break; + case Argument: + paramIndex = cast(base)->getIndex(); + break; + case Global: + global = cast(base)->getReferencedGlobal(); + break; + case Class: { + // Do a best-effort to find the identity of the object being projected + // from. It is OK to be unsound here (i.e. miss when two ref_element_addrs + // actually refer the same address) because these will be dynamically + // checked. + auto *REA = cast(base); + SILValue Object = stripBorrow(REA->getOperand()); + objProj = ObjectProjection(Object, Projection(REA)); + } + } +} + +const ValueDecl *AccessedStorage::getDecl(SILFunction *F) const { + switch (getKind()) { + case Box: + return cast(value)->getLoc().getAsASTNode(); + + case Stack: + return cast(value)->getDecl(); + + case Global: + return global->getDecl(); + + case Class: + return objProj.getProjection().getVarDecl(objProj.getObject()->getType()); + + case Argument: + return getArgument(F)->getDecl(); + + case Nested: + return nullptr; + + case Unidentified: + return nullptr; + } +} + +const char *AccessedStorage::getKindName(AccessedStorage::Kind k) { + switch (k) { + case Box: + return "Box"; + case Stack: + return "Stack"; + case Nested: + return "Nested"; + case Unidentified: + return "Unidentified"; + case Argument: + return "Argument"; + case Global: + return "Global"; + case Class: + return "Class"; + } +} + +void AccessedStorage::print(raw_ostream &os) const { + os << getKindName(getKind()) << " "; + switch (getKind()) { + case Box: + case Stack: + case Nested: + case Unidentified: + os << value; + break; + case Argument: + os << "index: " << paramIndex << "\n"; + break; + case Global: + os << *global; + break; + case Class: + os << objProj.getObject() << " "; + objProj.getProjection().print(os, objProj.getObject()->getType()); + os << "\n"; + } +} + +void AccessedStorage::dump() const { print(llvm::dbgs()); } + +// Given an address base is a block argument, verify that it is actually a box +// projected from a switch_enum. This is a valid pattern at any SIL stage +// resulting in a block-type phi. In later SIL stages, the optimizer may form +// address-type phis, causing this assert if called on those cases. +static void checkSwitchEnumBlockArg(SILPHIArgument *arg) { + assert(!arg->getType().isAddress()); + SILBasicBlock *Pred = arg->getParent()->getSinglePredecessorBlock(); + if (!Pred || !isa(Pred->getTerminator())) { + arg->dump(); + llvm_unreachable("unexpected box source."); + } +} + +/// Return true if the given address value is produced by a special address +/// producer that is only used for local initialization, not formal access. +static bool isAddressForLocalInitOnly(SILValue sourceAddr) { + switch (sourceAddr->getKind()) { + default: + return false; + + // Value to address conversions: the operand is the non-address source + // value. These allow local mutation of the value but should never be used + // for formal access of an lvalue. + case ValueKind::OpenExistentialBoxInst: + case ValueKind::ProjectExistentialBoxInst: + return true; + + // Self-evident local initialization. + case ValueKind::InitEnumDataAddrInst: + case ValueKind::InitExistentialAddrInst: + case ValueKind::AllocExistentialBoxInst: + case ValueKind::AllocValueBufferInst: + case ValueKind::ProjectValueBufferInst: + return true; + } +} + +AccessedStorage swift::findAccessedStorage(SILValue sourceAddr) { + SILValue address = sourceAddr; + while (true) { + AccessedStorage::Kind kind = AccessedStorage::classify(address); + // First handle identified cases: these are always valid as the base of a + // formal access. + if (kind != AccessedStorage::Unidentified) + return AccessedStorage(address, kind); + + // Handle other unidentified address sources. + switch (address->getKind()) { + default: + if (isAddressForLocalInitOnly(address)) + return AccessedStorage(address, AccessedStorage::Unidentified); + return AccessedStorage(); + + case ValueKind::PointerToAddressInst: + case ValueKind::SILUndef: + return AccessedStorage(address, AccessedStorage::Unidentified); + + // A block argument may be a box value projected out of + // switch_enum. Address-type block arguments are not allowed. + case ValueKind::SILPHIArgument: + if (address->getType().isAddress()) + return AccessedStorage(); + + checkSwitchEnumBlockArg(cast(address)); + return AccessedStorage(address, AccessedStorage::Unidentified); + + // 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. + case ValueKind::LoadInst: { + assert(address->getType().is()); + address = cast(address)->getOperand(); + assert(isa(address)); + continue; + } + // Inductive 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: + address = cast(address)->getOperand(0); + continue; + + // Subobject projections. + case ValueKind::StructElementAddrInst: + case ValueKind::TupleElementAddrInst: + case ValueKind::UncheckedTakeEnumDataAddrInst: + case ValueKind::RefTailAddrInst: + case ValueKind::TailAddrInst: + case ValueKind::IndexAddrInst: + address = cast(address)->getOperand(0); + continue; + } + } +} + +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(); + } +} + +// Return true if the given access is on a 'let' lvalue. +static bool isLetAccess(const AccessedStorage &storage, SILFunction *F) { + if (auto *decl = dyn_cast_or_null(storage.getDecl(F))) + return decl->isLet(); + + // It's unclear whether a global will ever be missing it's varDecl, but + // technically we only preserve it for debug info. So if we don't have a decl, + // check the flag on SILGlobalVariable, which is guaranteed valid, + if (storage.getKind() == AccessedStorage::Global) + return storage.getGlobal()->isLet(); + + return false; +} + +static bool isScratchBuffer(SILValue value) { + // Special case unsafe value buffer access. + return value->getType().is(); +} + +bool swift::memInstMustInitialize(Operand *memOper) { + SILValue address = memOper->get(); + SILInstruction *memInst = memOper->getUser(); + + switch (memInst->getKind()) { + default: + return false; + + case SILInstructionKind::CopyAddrInst: { + auto *CAI = cast(memInst); + return CAI->getDest() == address && CAI->isInitializationOfDest(); + } + case SILInstructionKind::InitExistentialAddrInst: + case SILInstructionKind::InitEnumDataAddrInst: + case SILInstructionKind::InjectEnumAddrInst: + return true; + + case SILInstructionKind::StoreInst: + return cast(memInst)->getOwnershipQualifier() + == StoreOwnershipQualifier::Init; + + case SILInstructionKind::StoreWeakInst: + return cast(memInst)->isInitializationOfDest(); + + case SILInstructionKind::StoreUnownedInst: + return cast(memInst)->isInitializationOfDest(); + } +} + +bool swift::isPossibleFormalAccessBase(const AccessedStorage &storage, + SILFunction *F) { + switch (storage.getKind()) { + case AccessedStorage::Box: + case AccessedStorage::Stack: + if (isScratchBuffer(storage.getValue())) + return false; + break; + case AccessedStorage::Global: + break; + case AccessedStorage::Class: + break; + 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; + + if (isa(storage.getValue())) { + checkSwitchEnumBlockArg(cast(storage.getValue())); + return false; + } + // Pointer-to-address exclusivity cannot be enforced. `baseAddress` may be + // pointing anywhere within an object. + if (isa(storage.getValue())) + return false; + + if (isa(storage.getValue())) + return false; + + if (isScratchBuffer(storage.getValue())) + return false; + } + // Additional checks that apply to anything that may fall through. + + // Immutable values are only accessed for initialization. + if (isLetAccess(storage, F)) + return false; + + return true; +} + +/// Helper for visitApplyAccesses that visits address-type call arguments, +/// including arguments to @noescape functions that are passed as closures to +/// the current call. +static void visitApplyAccesses(ApplySite apply, + llvm::function_ref visitor) { + for (Operand &oper : apply.getArgumentOperands()) { + // Consider any address-type operand an access. Whether it is read or modify + // depends on the argument convention. + if (oper.get()->getType().isAddress()) { + visitor(&oper); + continue; + } + auto fnType = oper.get()->getType().getAs(); + if (!fnType || !fnType->isNoEscape()) + continue; + + // When @noescape function closures are passed as arguments, their + // arguments are considered accessed at the call site. + FindClosureResult result = findClosureForAppliedArg(oper.get()); + if (!result.PAI) + continue; + + // Recursively visit @noescape function closure arguments. + visitApplyAccesses(result.PAI, visitor); + } +} + +static void visitBuiltinAddress(BuiltinInst *builtin, + llvm::function_ref visitor) { + if (auto kind = builtin->getBuiltinKind()) { + switch (kind.getValue()) { + default: + builtin->dump(); + llvm_unreachable("unexpected bulitin memory access."); + + // Buitins that affect memory but can't be formal accesses. + case BuiltinValueKind::UnexpectedError: + case BuiltinValueKind::ErrorInMain: + case BuiltinValueKind::IsOptionalType: + case BuiltinValueKind::AllocRaw: + case BuiltinValueKind::DeallocRaw: + case BuiltinValueKind::Fence: + case BuiltinValueKind::StaticReport: + case BuiltinValueKind::Once: + case BuiltinValueKind::OnceWithContext: + case BuiltinValueKind::Unreachable: + case BuiltinValueKind::CondUnreachable: + case BuiltinValueKind::DestroyArray: + case BuiltinValueKind::UnsafeGuaranteed: + case BuiltinValueKind::UnsafeGuaranteedEnd: + case BuiltinValueKind::Swift3ImplicitObjCEntrypoint: + case BuiltinValueKind::TSanInoutAccess: + return; + + // General memory access to a pointer in first operand position. + case BuiltinValueKind::CmpXChg: + case BuiltinValueKind::AtomicLoad: + case BuiltinValueKind::AtomicStore: + case BuiltinValueKind::AtomicRMW: + // Currently ignored because the access is on a RawPointer, not a + // SIL address. + // visitor(&builtin->getAllOperands()[0]); + return; + + // Arrays: (T.Type, Builtin.RawPointer, Builtin.RawPointer, + // Builtin.Word) + case BuiltinValueKind::CopyArray: + case BuiltinValueKind::TakeArrayNoAlias: + case BuiltinValueKind::TakeArrayFrontToBack: + case BuiltinValueKind::TakeArrayBackToFront: + case BuiltinValueKind::AssignCopyArrayNoAlias: + case BuiltinValueKind::AssignCopyArrayFrontToBack: + case BuiltinValueKind::AssignCopyArrayBackToFront: + case BuiltinValueKind::AssignTakeArray: + // Currently ignored because the access is on a RawPointer. + // visitor(&builtin->getAllOperands()[1]); + // visitor(&builtin->getAllOperands()[2]); + return; + } + } + if (auto ID = builtin->getIntrinsicID()) { + switch (ID.getValue()) { + // Exhaustively verifying all LLVM instrinsics that access memory is + // impractical. Instead, we call out the few common cases and return in + // the default case. + default: + return; + case llvm::Intrinsic::memcpy: + case llvm::Intrinsic::memmove: + // Currently ignored because the access is on a RawPointer. + // visitor(&builtin->getAllOperands()[0]); + // visitor(&builtin->getAllOperands()[1]); + return; + case llvm::Intrinsic::memset: + // Currently ignored because the access is on a RawPointer. + // visitor(&builtin->getAllOperands()[0]); + return; + } + } + llvm_unreachable("Must be either a builtin or intrinsic."); +} + +void swift::visitAccessedAddress(SILInstruction *I, + llvm::function_ref visitor) { + assert(I->mayReadOrWriteMemory()); + + // Reference counting instructions do not access user visible memory. + if (isa(I)) + return; + + if (isa(I)) + return; + + if (auto apply = FullApplySite::isa(I)) { + visitApplyAccesses(apply, visitor); + return; + } + + if (auto builtin = dyn_cast(I)) { + visitBuiltinAddress(builtin, visitor); + return; + } + + switch (I->getKind()) { + default: + I->dump(); + llvm_unreachable("unexpected memory access."); + + case SILInstructionKind::AssignInst: + visitor(&I->getAllOperands()[AssignInst::Dest]); + return; + + case SILInstructionKind::CheckedCastAddrBranchInst: + visitor(&I->getAllOperands()[CheckedCastAddrBranchInst::Src]); + visitor(&I->getAllOperands()[CheckedCastAddrBranchInst::Dest]); + return; + + case SILInstructionKind::CopyAddrInst: + visitor(&I->getAllOperands()[CopyAddrInst::Src]); + visitor(&I->getAllOperands()[CopyAddrInst::Dest]); + return; + + case SILInstructionKind::StoreInst: + case SILInstructionKind::StoreBorrowInst: + case SILInstructionKind::StoreUnownedInst: + case SILInstructionKind::StoreWeakInst: + visitor(&I->getAllOperands()[StoreInst::Dest]); + return; + + case SILInstructionKind::SelectEnumAddrInst: + visitor(&I->getAllOperands()[0]); + return; + + case SILInstructionKind::InitExistentialAddrInst: + case SILInstructionKind::InjectEnumAddrInst: + case SILInstructionKind::LoadInst: + case SILInstructionKind::LoadBorrowInst: + case SILInstructionKind::LoadWeakInst: + case SILInstructionKind::LoadUnownedInst: + case SILInstructionKind::OpenExistentialAddrInst: + case SILInstructionKind::SwitchEnumAddrInst: + case SILInstructionKind::UncheckedTakeEnumDataAddrInst: + case SILInstructionKind::UnconditionalCheckedCastInst: { + // Assuming all the above have only a single address operand. + assert(I->getNumOperands() - I->getNumTypeDependentOperands() == 1); + Operand *singleOperand = &I->getAllOperands()[0]; + // Check the operand type because UnconditionalCheckedCastInst may operate + // on a non-address. + if (singleOperand->get()->getType().isAddress()) + visitor(singleOperand); + return; + } + // Non-access cases: these are marked with memory side effects, but, by + // themselves, do not access formal memory. + case SILInstructionKind::AbortApplyInst: + case SILInstructionKind::AllocBoxInst: + case SILInstructionKind::AllocExistentialBoxInst: + case SILInstructionKind::AllocGlobalInst: + case SILInstructionKind::BeginAccessInst: + case SILInstructionKind::BeginApplyInst: + case SILInstructionKind::BeginBorrowInst: + case SILInstructionKind::BeginUnpairedAccessInst: + case SILInstructionKind::BindMemoryInst: + case SILInstructionKind::CheckedCastValueBranchInst: + case SILInstructionKind::CondFailInst: + case SILInstructionKind::CopyBlockInst: + case SILInstructionKind::CopyBlockWithoutEscapingInst: + case SILInstructionKind::CopyValueInst: + case SILInstructionKind::CopyUnownedValueInst: + case SILInstructionKind::DeinitExistentialAddrInst: + case SILInstructionKind::DeinitExistentialValueInst: + case SILInstructionKind::DestroyAddrInst: + case SILInstructionKind::DestroyValueInst: + case SILInstructionKind::EndAccessInst: + case SILInstructionKind::EndApplyInst: + case SILInstructionKind::EndBorrowArgumentInst: + case SILInstructionKind::EndBorrowInst: + case SILInstructionKind::EndUnpairedAccessInst: + case SILInstructionKind::EndLifetimeInst: + case SILInstructionKind::ExistentialMetatypeInst: + case SILInstructionKind::FixLifetimeInst: + case SILInstructionKind::InitExistentialValueInst: + case SILInstructionKind::IsUniqueInst: + case SILInstructionKind::IsEscapingClosureInst: + case SILInstructionKind::IsUniqueOrPinnedInst: + case SILInstructionKind::KeyPathInst: + case SILInstructionKind::OpenExistentialBoxInst: + case SILInstructionKind::OpenExistentialBoxValueInst: + case SILInstructionKind::OpenExistentialValueInst: + case SILInstructionKind::PartialApplyInst: + case SILInstructionKind::ProjectValueBufferInst: + case SILInstructionKind::StrongPinInst: + case SILInstructionKind::YieldInst: + case SILInstructionKind::UnwindInst: + case SILInstructionKind::UncheckedOwnershipConversionInst: + case SILInstructionKind::UncheckedRefCastAddrInst: + case SILInstructionKind::UnconditionalCheckedCastAddrInst: + case SILInstructionKind::UnconditionalCheckedCastValueInst: + case SILInstructionKind::UnownedReleaseInst: + case SILInstructionKind::UnownedRetainInst: + case SILInstructionKind::ValueMetatypeInst: + return; + } +} + +SILBasicBlock::iterator swift::removeBeginAccess(BeginAccessInst *beginAccess) { + while (!beginAccess->use_empty()) { + Operand *op = *beginAccess->use_begin(); + + // Delete any associated end_access instructions. + if (auto endAccess = dyn_cast(op->getUser())) { + endAccess->eraseFromParent(); + + // Forward all other uses to the original address. + } else { + op->set(beginAccess->getSource()); + } + } + return beginAccess->getParent()->erase(beginAccess); +} diff --git a/lib/SIL/Projection.cpp b/lib/SIL/Projection.cpp index fe0371b729f61..ea4c1e0d15a5d 100644 --- a/lib/SIL/Projection.cpp +++ b/lib/SIL/Projection.cpp @@ -91,7 +91,7 @@ Projection::Projection(SingleValueInstruction *I) : Value() { } case SILInstructionKind::RefTailAddrInst: { auto *RTAI = cast(I); - auto *Ty = RTAI->getTailType().getSwiftRValueType().getPointer(); + auto *Ty = RTAI->getTailType().getASTType().getPointer(); Value = ValueTy(ProjectionKind::TailElems, Ty); assert(getKind() == ProjectionKind::TailElems); break; @@ -148,14 +148,14 @@ Projection::Projection(SingleValueInstruction *I) : Value() { break; } case SILInstructionKind::UpcastInst: { - auto *Ty = I->getType().getSwiftRValueType().getPointer(); + auto *Ty = I->getType().getASTType().getPointer(); assert(Ty->isCanonical()); Value = ValueTy(ProjectionKind::Upcast, Ty); assert(getKind() == ProjectionKind::Upcast); break; } case SILInstructionKind::UncheckedRefCastInst: { - auto *Ty = I->getType().getSwiftRValueType().getPointer(); + auto *Ty = I->getType().getASTType().getPointer(); assert(Ty->isCanonical()); Value = ValueTy(ProjectionKind::RefCast, Ty); assert(getKind() == ProjectionKind::RefCast); @@ -163,7 +163,7 @@ Projection::Projection(SingleValueInstruction *I) : Value() { } case SILInstructionKind::UncheckedBitwiseCastInst: case SILInstructionKind::UncheckedAddrCastInst: { - auto *Ty = I->getType().getSwiftRValueType().getPointer(); + auto *Ty = I->getType().getASTType().getPointer(); assert(Ty->isCanonical()); Value = ValueTy(ProjectionKind::BitwiseCast, Ty); assert(getKind() == ProjectionKind::BitwiseCast); @@ -508,7 +508,46 @@ ProjectionPath::removePrefix(const ProjectionPath &Path, return P; } -raw_ostream &ProjectionPath::print(raw_ostream &os, SILModule &M) { +void Projection::print(raw_ostream &os, SILType baseType) const { + if (isNominalKind()) { + auto *Decl = getVarDecl(baseType); + os << "Field: "; + Decl->print(os); + return; + } + + if (getKind() == ProjectionKind::Tuple) { + os << "Index: " << getIndex(); + return; + } + if (getKind() == ProjectionKind::BitwiseCast) { + os << "BitwiseCast"; + return; + } + if (getKind() == ProjectionKind::Index) { + os << "Index: " << getIndex(); + return; + } + if (getKind() == ProjectionKind::Upcast) { + os << "UpCast"; + return; + } + if (getKind() == ProjectionKind::RefCast) { + os << "RefCast"; + return; + } + if (getKind() == ProjectionKind::Box) { + os << " Box over"; + return; + } + if (getKind() == ProjectionKind::TailElems) { + os << " TailElems"; + return; + } + os << ""; +} + +raw_ostream &ProjectionPath::print(raw_ostream &os, SILModule &M) const { os << "Projection Path ["; SILType IterType = getBaseType(); for (const Projection &IterProj : Path) { @@ -517,50 +556,14 @@ raw_ostream &ProjectionPath::print(raw_ostream &os, SILModule &M) { os << BaseType.getAddressType() << "\n "; - if (IterProj.isNominalKind()) { - auto *Decl = IterProj.getVarDecl(BaseType); - os << "Field: "; - Decl->print(os); - os << " of: "; - continue; - } - - if (IterProj.getKind() == ProjectionKind::Tuple) { - os << "Index: " << IterProj.getIndex() << " into: "; - continue; - } - - if (IterProj.getKind() == ProjectionKind::BitwiseCast) { - os << "BitwiseCast to: "; - continue; - } - if (IterProj.getKind() == ProjectionKind::Index) { - os << "Index: " << IterProj.getIndex() << " into: "; - continue; - } - if (IterProj.getKind() == ProjectionKind::Upcast) { - os << "UpCast to: "; - continue; - } - if (IterProj.getKind() == ProjectionKind::RefCast) { - os << "RefCast to: "; - continue; - } - if (IterProj.getKind() == ProjectionKind::Box) { - os << " Box over: "; - continue; - } - if (IterProj.getKind() == ProjectionKind::TailElems) { - os << " TailElems of: "; - continue; - } - os << " into: "; + IterProj.print(os, BaseType); + os << " in: "; } os << IterType.getAddressType() << "]\n"; return os; } -void ProjectionPath::dump(SILModule &M) { +void ProjectionPath::dump(SILModule &M) const { print(llvm::dbgs(), M); } @@ -1102,7 +1105,8 @@ class NewAggregateBuilderMap { ProjectionTree:: ProjectionTree(SILModule &Mod, SILType BaseTy) : Mod(Mod) { - DEBUG(llvm::dbgs() << "Constructing Projection Tree For : " << BaseTy); + DEBUG(llvm::dbgs() << "Constructing Projection Tree For : " << BaseTy + << "\n"); // Create the root node of the tree with our base type. createRoot(BaseTy); @@ -1240,7 +1244,7 @@ computeUsesAndLiveness(SILValue Base) { #ifndef NDEBUG DEBUG(llvm::dbgs() << "Final Leafs: \n"); llvm::SmallVector LeafTypes; - getLeafTypes(LeafTypes); + getLiveLeafTypes(LeafTypes); for (SILType Leafs : LeafTypes) { DEBUG(llvm::dbgs() << " " << Leafs << "\n"); } diff --git a/lib/SIL/SIL.cpp b/lib/SIL/SIL.cpp index ecbe6acdb3e13..31bd13b43b589 100644 --- a/lib/SIL/SIL.cpp +++ b/lib/SIL/SIL.cpp @@ -105,3 +105,106 @@ swift::getLinkageForProtocolConformance(const NormalProtocolConformance *C, return (definition ? SILLinkage::Public : SILLinkage::PublicExternal); } } + +bool SILModule::isTypeMetadataAccessible(CanType type) { + // SILModules built for the debugger have special powers to access metadata + // for types in other files/modules. + if (getASTContext().LangOpts.DebuggerSupport) + return true; + + assert(type->isLegalFormalType()); + + return !type.findIf([&](CanType type) { + // Note that this function returns true if the type is *illegal* to use. + + // Ignore non-nominal types. + auto decl = type.getNominalOrBoundGenericNominal(); + if (!decl) + return false; + + // Check whether the declaration is inaccessible from the current context. + switch (getDeclLinkage(decl)) { + + // Public declarations are accessible from everywhere. + case FormalLinkage::PublicUnique: + case FormalLinkage::PublicNonUnique: + return false; + + // Hidden declarations are inaccessible from different modules. + case FormalLinkage::HiddenUnique: + return (decl->getModuleContext() != getSwiftModule()); + + // Private declarations are inaccessible from different files unless + // this is WMO and we're in the same module. + case FormalLinkage::Private: { + // The only time we don't have an associated DC is in the + // integrated REPL, where we also don't have a concept of other + // source files within the current module. + if (!AssociatedDeclContext) + return (decl->getModuleContext() != getSwiftModule()); + + // The associated DC should be either a SourceFile or, in WMO mode, + // a ModuleDecl. In the WMO modes, IRGen will ensure that private + // declarations are usable throughout the module. Therefore, in + // either case we just need to make sure that the declaration comes + // from within the associated DC. + auto declDC = decl->getDeclContext(); + return !(declDC == AssociatedDeclContext || + declDC->isChildContextOf(AssociatedDeclContext)); + } + } + llvm_unreachable("bad linkage"); + }); +} + +/// Answer whether IRGen's emitTypeMetadataForLayout can fetch metadata for +/// a type, which is the necessary condition for being able to do value +/// operations on the type using dynamic metadata. +static bool isTypeMetadataForLayoutAccessible(SILModule &M, SILType type) { + // Look through types that aren't necessarily legal formal types: + + // - tuples + if (auto tupleType = type.getAs()) { + for (auto index : indices(tupleType.getElementTypes())) { + if (!isTypeMetadataForLayoutAccessible(M, type.getTupleElementType(index))) + return false; + } + return true; + } + + // - optionals + if (auto objType = type.getOptionalObjectType()) { + return isTypeMetadataForLayoutAccessible(M, objType); + } + + // - function types + if (type.is()) + return true; + + // - metatypes + if (type.is()) + return true; + + // Otherwise, check that we can fetch the type metadata. + return M.isTypeMetadataAccessible(type.getASTType()); + +} + +/// Can we perform value operations on the given type? We have no way +/// of doing value operations on resilient-layout types from other modules +/// that are ABI-private to their defining module. But if the type is not +/// ABI-private, we can always at least fetch its metadata and use the +/// value witness table stored there. +bool SILModule::isTypeABIAccessible(SILType type) { + // Fixed-ABI types can have value operations done without metadata. + if (Types.getTypeLowering(type).isFixedABI()) + return true; + + assert(!type.is() && + !type.is() && + !type.is() && + "unexpected SIL lowered-only type with non-fixed layout"); + + // Otherwise, we need to be able to fetch layout-metadata for the type. + return isTypeMetadataForLayoutAccessible(*this, type); +} diff --git a/lib/SIL/SILBasicBlock.cpp b/lib/SIL/SILBasicBlock.cpp index 09c9a0bcc9dde..c33d4dd88af41 100644 --- a/lib/SIL/SILBasicBlock.cpp +++ b/lib/SIL/SILBasicBlock.cpp @@ -336,20 +336,18 @@ bool SILBasicBlock::isEntry() const { } SILBasicBlock::PHIArgumentArrayRefTy SILBasicBlock::getPHIArguments() const { - using FuncTy = std::function; - FuncTy F = [](SILArgument *A) -> SILPHIArgument * { + return PHIArgumentArrayRefTy(getArguments(), + [](SILArgument *A) -> SILPHIArgument * { return cast(A); - }; - return makeTransformArrayRef(getArguments(), F); + }); } SILBasicBlock::FunctionArgumentArrayRefTy SILBasicBlock::getFunctionArguments() const { - using FuncTy = std::function; - FuncTy F = [](SILArgument *A) -> SILFunctionArgument * { + return FunctionArgumentArrayRefTy(getArguments(), + [](SILArgument *A) -> SILFunctionArgument* { return cast(A); - }; - return makeTransformArrayRef(getArguments(), F); + }); } /// Returns true if this block ends in an unreachable or an apply of a diff --git a/lib/SIL/SILBuilder.cpp b/lib/SIL/SILBuilder.cpp index f28c923d35e83..4409ab11dc52c 100644 --- a/lib/SIL/SILBuilder.cpp +++ b/lib/SIL/SILBuilder.cpp @@ -41,7 +41,7 @@ TupleInst *SILBuilder::createTuple(SILLocation loc, ArrayRef elts) { // Derive the tuple type from the elements. SmallVector eltTypes; for (auto elt : elts) - eltTypes.push_back(elt->getType().getSwiftRValueType()); + eltTypes.push_back(elt->getType().getASTType()); auto tupleType = SILType::getPrimitiveObjectType( CanType(TupleType::get(eltTypes, getASTContext()))); @@ -50,7 +50,7 @@ TupleInst *SILBuilder::createTuple(SILLocation loc, ArrayRef elts) { SILType SILBuilder::getPartialApplyResultType(SILType origTy, unsigned argCount, SILModule &M, - SubstitutionList subs, + SubstitutionMap subs, ParameterConvention calleeConvention) { CanSILFunctionType FTI = origTy.castTo(); if (!subs.empty()) diff --git a/lib/SIL/SILCoverageMap.cpp b/lib/SIL/SILCoverageMap.cpp index 22cee3396d9a2..a215a6b34d3a8 100644 --- a/lib/SIL/SILCoverageMap.cpp +++ b/lib/SIL/SILCoverageMap.cpp @@ -15,6 +15,7 @@ // //===----------------------------------------------------------------------===// +#include "llvm/ADT/STLExtras.h" #include "swift/SIL/SILCoverageMap.h" #include "swift/SIL/SILModule.h" @@ -40,7 +41,11 @@ SILCoverageMap::create(SILModule &M, StringRef Filename, StringRef Name, CM->MappedRegions = M.allocateCopy(MappedRegions); CM->Expressions = M.allocateCopy(Expressions); - M.coverageMaps.push_back(CM); + auto result = M.coverageMaps.insert({CM->PGOFuncName, CM}); + + // Assert that this coverage map is unique. + assert(result.second && "Duplicate coverage mapping for function"); + return CM; } diff --git a/lib/SIL/SILDeclRef.cpp b/lib/SIL/SILDeclRef.cpp index 0fa0497dc3041..671dbe1db4019 100644 --- a/lib/SIL/SILDeclRef.cpp +++ b/lib/SIL/SILDeclRef.cpp @@ -456,8 +456,9 @@ IsSerialized_t SILDeclRef::isSerialized() const { // marked as @_fixed_layout. if (isStoredPropertyInitializer()) { auto *nominal = cast(d->getDeclContext()); - auto scope = nominal->getFormalAccessScope(/*useDC=*/nullptr, - /*respectVersionedAttr=*/true); + auto scope = + nominal->getFormalAccessScope(/*useDC=*/nullptr, + /*treatUsableFromInlineAsPublic=*/true); if (!scope.isPublic()) return IsNotSerialized; if (nominal->isFormallyResilient()) diff --git a/lib/SIL/SILFunction.cpp b/lib/SIL/SILFunction.cpp index 5ee94aed443fd..4fa3942f161d3 100644 --- a/lib/SIL/SILFunction.cpp +++ b/lib/SIL/SILFunction.cpp @@ -10,11 +10,12 @@ // //===----------------------------------------------------------------------===// -#include "swift/SIL/SILModule.h" -#include "swift/SIL/SILFunction.h" +#include "swift/SIL/SILArgument.h" #include "swift/SIL/SILBasicBlock.h" +#include "swift/SIL/SILFunction.h" #include "swift/SIL/SILInstruction.h" -#include "swift/SIL/SILArgument.h" +#include "swift/SIL/SILModule.h" +#include "swift/SIL/SILProfiler.h" #include "swift/SIL/CFG.h" #include "swift/SIL/PrettyStackTrace.h" #include "swift/AST/GenericEnvironment.h" @@ -139,6 +140,11 @@ SILFunction::~SILFunction() { "Function cannot be deleted while function_ref's still exist"); } +void SILFunction::createProfiler(ASTNode Root, ForDefinition_t forDefinition) { + assert(!Profiler && "Function already has a profiler"); + Profiler = SILProfiler::create(Module, forDefinition, Root); +} + bool SILFunction::hasForeignBody() const { if (!hasClangNode()) return false; return SILDeclRef::isClangGenerated(getClangNode()); @@ -418,7 +424,7 @@ bool SILFunction::hasSelfMetadataParam() const { if (!silTy.isObject()) return false; - auto selfTy = silTy.getSwiftRValueType(); + auto selfTy = silTy.getASTType(); if (auto metaTy = dyn_cast(selfTy)) { selfTy = metaTy.getInstanceType(); @@ -476,16 +482,14 @@ void SILFunction::convertToDeclaration() { getBlocks().clear(); } -SubstitutionList SILFunction::getForwardingSubstitutions() { - if (ForwardingSubs) - return *ForwardingSubs; +SubstitutionMap SILFunction::getForwardingSubstitutionMap() { + if (ForwardingSubMap) + return ForwardingSubMap; - auto *env = getGenericEnvironment(); - if (!env) - return {}; + if (auto *env = getGenericEnvironment()) + ForwardingSubMap = env->getForwardingSubstitutionMap(); - ForwardingSubs = env->getForwardingSubstitutions(); - return *ForwardingSubs; + return ForwardingSubMap; } bool SILFunction::shouldVerifyOwnership() const { diff --git a/lib/SIL/SILFunctionType.cpp b/lib/SIL/SILFunctionType.cpp index 69e4bcd27a076..75d7819696208 100644 --- a/lib/SIL/SILFunctionType.cpp +++ b/lib/SIL/SILFunctionType.cpp @@ -22,8 +22,8 @@ #include "swift/AST/DiagnosticsSIL.h" #include "swift/AST/ForeignInfo.h" #include "swift/AST/GenericEnvironment.h" +#include "swift/AST/Module.h" #include "swift/AST/ProtocolConformance.h" -#include "swift/Basic/StringExtras.h" #include "swift/SIL/SILModule.h" #include "swift/SIL/SILType.h" #include "clang/AST/Attr.h" @@ -358,7 +358,7 @@ class DestructureResults { } } - SILResultInfo result(substResultTL.getLoweredType().getSwiftRValueType(), + SILResultInfo result(substResultTL.getLoweredType().getASTType(), convention); Results.push_back(result); } @@ -705,7 +705,7 @@ class DestructureInputs { substTL); assert(!isIndirectFormalParameter(convention)); } - auto loweredType = substTL.getLoweredType().getSwiftRValueType(); + auto loweredType = substTL.getLoweredType().getASTType(); Inputs.push_back(SILParameterInfo(loweredType, convention)); @@ -730,7 +730,7 @@ class DestructureInputs { M.Types.getLoweredType(Foreign.Error->getErrorParameterType()); // Assume the error parameter doesn't have interesting lowering. - Inputs.push_back(SILParameterInfo(foreignErrorTy.getSwiftRValueType(), + Inputs.push_back(SILParameterInfo(foreignErrorTy.getASTType(), ParameterConvention::Direct_Unowned)); NextOrigParamIndex++; return true; @@ -863,14 +863,14 @@ lowerCaptureContextParameters(SILModule &M, AnyFunctionRef function, } else { convention = ParameterConvention::Direct_Guaranteed; } - SILParameterInfo param(loweredTy.getSwiftRValueType(), convention); + SILParameterInfo param(loweredTy.getASTType(), convention); inputs.push_back(param); break; } case CaptureKind::Box: { // Lvalues are captured as a box that owns the captured value. auto boxTy = Types.getInterfaceBoxTypeForCapture( - VD, loweredTy.getSwiftRValueType(), + VD, loweredTy.getASTType(), /*mutable*/ true); auto convention = ParameterConvention::Direct_Guaranteed; auto param = SILParameterInfo(boxTy, convention); @@ -881,7 +881,7 @@ lowerCaptureContextParameters(SILModule &M, AnyFunctionRef function, // Non-escaping lvalues are captured as the address of the value. SILType ty = loweredTy.getAddressType(); auto param = - SILParameterInfo(ty.getSwiftRValueType(), + SILParameterInfo(ty.getASTType(), ParameterConvention::Indirect_InoutAliasable); inputs.push_back(param); break; @@ -959,7 +959,7 @@ static CanSILFunctionType getSILFunctionType( "using native Swift error convention for foreign type!"); SILType exnType = SILType::getExceptionType(M.getASTContext()); assert(exnType.isObject()); - errorResult = SILResultInfo(exnType.getSwiftRValueType(), + errorResult = SILResultInfo(exnType.getASTType(), ResultConvention::Owned); } @@ -1455,6 +1455,11 @@ class ObjCMethodConventions : public Conventions { if (tl.isTrivial()) { if (Method->hasAttr()) return ResultConvention::UnownedInnerPointer; + + auto type = tl.getLoweredType(); + if (type.unwrapOptionalType().getStructOrBoundGenericStruct() + == type.getASTContext().getUnmanagedDecl()) + return ResultConvention::UnownedInnerPointer; return ResultConvention::Unowned; } @@ -1736,12 +1741,23 @@ static SelectorFamily getSelectorFamily(Identifier name) { StringRef text = name.get(); while (!text.empty() && text[0] == '_') text = text.substr(1); - StringRef firstWord = camel_case::getFirstWord(text); + // Does the given selector start with the given string as a prefix, in the + // sense of the selector naming conventions? + // This implementation matches the one used by + // clang::Selector::getMethodFamily, to make sure we behave the same as Clang + // ARC. We're not just calling that method here because it means allocating a + // clang::IdentifierInfo, which requires a Clang ASTContext. + auto hasPrefix = [](StringRef text, StringRef prefix) { + if (!text.startswith(prefix)) return false; + if (text.size() == prefix.size()) return true; + assert(text.size() > prefix.size()); + return !clang::isLowercase(text[prefix.size()]); + }; auto result = SelectorFamily::None; if (false) /*for #define purposes*/; #define CHECK_PREFIX(LABEL, PREFIX) \ - else if (firstWord == PREFIX) result = SelectorFamily::LABEL; + else if (hasPrefix(text, PREFIX)) result = SelectorFamily::LABEL; FOREACH_FAMILY(CHECK_PREFIX) #undef CHECK_PREFIX @@ -1838,7 +1854,7 @@ class SelectorFamilyConventions : public Conventions { break; } - auto type = tl.getLoweredType().getSwiftRValueType(); + auto type = tl.getLoweredType().getASTType(); if (type->hasRetainablePointerRepresentation() || (type->getSwiftNewtypeUnderlyingType() && !tl.isTrivial())) return ResultConvention::Autoreleased; @@ -2359,7 +2375,7 @@ class SILTypeSubstituter : } SILType subst(SILType type) { - return SILType::getPrimitiveType(visit(type.getSwiftRValueType()), + return SILType::getPrimitiveType(visit(type.getASTType()), type.getCategory()); } @@ -2424,7 +2440,7 @@ class SILTypeSubstituter : AbstractionPattern abstraction(Sig, origType); return TheSILModule.Types.getLoweredType(abstraction, substType) - .getSwiftRValueType(); + .getASTType(); } }; @@ -2444,7 +2460,7 @@ SILType SILType::subst(SILModule &silModule, return STST.subst(*this); } -SILType SILType::subst(SILModule &silModule, const SubstitutionMap &subs) const{ +SILType SILType::subst(SILModule &silModule, SubstitutionMap subs) const{ return subst(silModule, QuerySubstitutionMap{subs}, LookUpConformanceInSubstitutionMap(subs)); @@ -2455,24 +2471,7 @@ SILType SILType::subst(SILModule &silModule, const SubstitutionMap &subs) const{ /// type, except using the original conventions. CanSILFunctionType SILFunctionType::substGenericArgs(SILModule &silModule, - SubstitutionList subs) { - if (subs.empty()) { - assert( - (!isPolymorphic() || getGenericSignature()->areAllParamsConcrete()) && - "no args for non-concrete polymorphic substitution"); - return CanSILFunctionType(this); - } - - auto subMap = GenericSig->getSubstitutionMap(subs); - return substGenericArgs(silModule, subMap); -} - -/// Apply a substitution to this polymorphic SILFunctionType so that -/// it has the form of the normal SILFunctionType for the substituted -/// type, except using the original conventions. -CanSILFunctionType -SILFunctionType::substGenericArgs(SILModule &silModule, - const SubstitutionMap &subs) { + SubstitutionMap subs) { if (!isPolymorphic()) { return CanSILFunctionType(this); } @@ -2743,13 +2742,13 @@ static bool areABICompatibleParamsOrReturns(SILType a, SILType b) { auto types = tup.getElementTypes(); aElements.append(types.begin(), types.end()); } else { - aElements.push_back(a.getSwiftRValueType()); + aElements.push_back(a.getASTType()); } if (auto tup = b.getAs()) { auto types = tup.getElementTypes(); bElements.append(types.begin(), types.end()); } else { - bElements.push_back(b.getSwiftRValueType()); + bElements.push_back(b.getASTType()); } if (aElements.size() != bElements.size()) diff --git a/lib/SIL/SILInstruction.cpp b/lib/SIL/SILInstruction.cpp index 75732df6f60e2..2f32f28a61e82 100644 --- a/lib/SIL/SILInstruction.cpp +++ b/lib/SIL/SILInstruction.cpp @@ -388,7 +388,8 @@ namespace { auto left = cast(LHS); return left->getAccessKind() == right->getAccessKind() && left->getEnforcement() == right->getEnforcement() - && left->hasNoNestedConflict() == right->hasNoNestedConflict(); + && left->hasNoNestedConflict() == right->hasNoNestedConflict() + && left->isFromBuiltin() == right->isFromBuiltin(); } bool visitEndAccessInst(const EndAccessInst *right) { @@ -400,13 +401,15 @@ namespace { auto left = cast(LHS); return left->getAccessKind() == right->getAccessKind() && left->getEnforcement() == right->getEnforcement() - && left->hasNoNestedConflict() == right->hasNoNestedConflict(); + && left->hasNoNestedConflict() == right->hasNoNestedConflict() + && left->isFromBuiltin() == right->isFromBuiltin(); } bool visitEndUnpairedAccessInst(const EndUnpairedAccessInst *right) { auto left = cast(LHS); return left->getEnforcement() == right->getEnforcement() - && left->isAborting() == right->isAborting(); + && left->isAborting() == right->isAborting() + && left->isFromBuiltin() == right->isFromBuiltin(); } bool visitStrongReleaseInst(const StrongReleaseInst *RHS) { @@ -606,7 +609,7 @@ namespace { bool visitApplyInst(ApplyInst *RHS) { auto *X = cast(LHS); - return X->getSubstitutions() == RHS->getSubstitutions(); + return X->getSubstitutionMap() == RHS->getSubstitutionMap(); } bool visitBuiltinInst(BuiltinInst *RHS) { @@ -1304,7 +1307,7 @@ SILInstructionResultArray::SILInstructionResultArray( auto TRangeBegin = TypedRange.begin(); auto TRangeIter = TRangeBegin; auto TRangeEnd = TypedRange.end(); - assert(MVResults.size() == unsigned(std::distance(VRangeBegin, VRangeEnd))); + assert(MVResults.size() == unsigned(std::distance(TRangeBegin, TRangeEnd))); for (unsigned i : indices(MVResults)) { assert(SILValue(&MVResults[i]) == (*this)[i]); assert(SILValue(&MVResults[i])->getType() == (*this)[i]->getType()); @@ -1354,18 +1357,18 @@ operator==(const SILInstructionResultArray &other) { SILInstructionResultArray::type_range SILInstructionResultArray::getTypes() const { - std::function F = [](SILValue V) -> SILType { + SILType (*F)(SILValue) = [](SILValue V) -> SILType { return V->getType(); }; return {llvm::map_iterator(begin(), F), llvm::map_iterator(end(), F)}; } SILInstructionResultArray::iterator SILInstructionResultArray::begin() const { - return iterator(*this, getStartOffset()); + return iterator(*this, 0); } SILInstructionResultArray::iterator SILInstructionResultArray::end() const { - return iterator(*this, getEndOffset()); + return iterator(*this, size()); } SILInstructionResultArray::reverse_iterator diff --git a/lib/SIL/SILInstructions.cpp b/lib/SIL/SILInstructions.cpp index dec0573fbc51f..372470333ddb3 100644 --- a/lib/SIL/SILInstructions.cpp +++ b/lib/SIL/SILInstructions.cpp @@ -79,7 +79,7 @@ static void buildTypeDependentOperands( for (auto archetype : OpenedArchetypes) { auto Def = OpenedArchetypesState.getOpenedArchetypeDef(archetype); assert(Def); - assert(getOpenedArchetypeOf(Def->getType().getSwiftRValueType()) && + assert(getOpenedArchetypeOf(Def->getType().getASTType()) && "Opened archetype operands should be of an opened existential type"); TypeDependentOperands.push_back(Def); } @@ -98,13 +98,13 @@ static void collectTypeDependentOperands( SILOpenedArchetypesState &OpenedArchetypesState, SILFunction &F, CanType Ty, - SubstitutionList subs = SubstitutionList()) { + SubstitutionMap subs = { }) { SmallVector openedArchetypes; bool hasDynamicSelf = false; collectDependentTypeInfo(Ty, openedArchetypes, hasDynamicSelf); - for (auto sub : subs) { + for (Type replacement : subs.getReplacementTypes()) { // Substitutions in SIL should really be canonical. - auto ReplTy = sub.getReplacement()->getCanonicalType(); + auto ReplTy = replacement->getCanonicalType(); collectDependentTypeInfo(ReplTy, openedArchetypes, hasDynamicSelf); } buildTypeDependentOperands(openedArchetypes, hasDynamicSelf, @@ -128,21 +128,23 @@ static void *allocateDebugVarCarryingInst(SILModule &M, TailAllocatedDebugVariable::TailAllocatedDebugVariable( Optional Var, char *buf) { if (!Var) { - RawValue = 0; + Bits.RawValue = 0; return; } - Data.HasValue = true; - Data.Constant = Var->Constant; - Data.ArgNo = Var->ArgNo; - Data.NameLength = Var->Name.size(); - assert(Data.ArgNo == Var->ArgNo && "Truncation"); - assert(Data.NameLength == Var->Name.size() && "Truncation"); - memcpy(buf, Var->Name.data(), Data.NameLength); + Bits.Data.HasValue = true; + Bits.Data.Constant = Var->Constant; + Bits.Data.ArgNo = Var->ArgNo; + Bits.Data.NameLength = Var->Name.size(); + assert(Bits.Data.ArgNo == Var->ArgNo && "Truncation"); + assert(Bits.Data.NameLength == Var->Name.size() && "Truncation"); + memcpy(buf, Var->Name.data(), Bits.Data.NameLength); } StringRef TailAllocatedDebugVariable::getName(const char *buf) const { - return Data.NameLength ? StringRef(buf, Data.NameLength) : StringRef(); + if (Bits.Data.NameLength) + return StringRef(buf, Bits.Data.NameLength); + return StringRef(); } AllocStackInst::AllocStackInst(SILDebugLocation Loc, SILType elementType, @@ -167,7 +169,7 @@ AllocStackInst::create(SILDebugLocation Loc, Optional Var) { SmallVector TypeDependentOperands; collectTypeDependentOperands(TypeDependentOperands, OpenedArchetypes, F, - elementType.getSwiftRValueType()); + elementType.getASTType()); void *Buffer = allocateDebugVarCarryingInst( F.getModule(), Var, TypeDependentOperands); return ::new (Buffer) @@ -178,6 +180,21 @@ VarDecl *AllocStackInst::getDecl() const { return getLoc().getAsASTNode(); } +DeallocStackInst *AllocStackInst::getSingleDeallocStack() const { + DeallocStackInst *Dealloc = nullptr; + for (auto *U : getUses()) { + if (auto DS = dyn_cast(U->getUser())) { + if (Dealloc == nullptr) { + Dealloc = DS; + continue; + } + // Already saw a dealloc_stack. + return nullptr; + } + } + return Dealloc; +} + AllocRefInstBase::AllocRefInstBase(SILInstructionKind Kind, SILDebugLocation Loc, SILType ObjectType, @@ -204,10 +221,10 @@ AllocRefInst *AllocRefInst::create(SILDebugLocation Loc, SILFunction &F, ElementCountOperands.end()); for (SILType ElemType : ElementTypes) { collectTypeDependentOperands(AllOperands, OpenedArchetypes, F, - ElemType.getSwiftRValueType()); + ElemType.getASTType()); } collectTypeDependentOperands(AllOperands, OpenedArchetypes, F, - ObjectType.getSwiftRValueType()); + ObjectType.getASTType()); auto Size = totalSizeToAlloc(AllOperands.size(), ElementTypes.size()); auto Buffer = F.getModule().allocateInst(Size, alignof(AllocRefInst)); @@ -225,10 +242,10 @@ AllocRefDynamicInst::create(SILDebugLocation DebugLoc, SILFunction &F, ElementCountOperands.end()); AllOperands.push_back(metatypeOperand); collectTypeDependentOperands(AllOperands, OpenedArchetypes, F, - ty.getSwiftRValueType()); + ty.getASTType()); for (SILType ElemType : ElementTypes) { collectTypeDependentOperands(AllOperands, OpenedArchetypes, F, - ElemType.getSwiftRValueType()); + ElemType.getASTType()); } auto Size = totalSizeToAlloc(AllOperands.size(), ElementTypes.size()); @@ -339,7 +356,7 @@ AllocValueBufferInst::create(SILDebugLocation DebugLoc, SILType valueType, SILOpenedArchetypesState &OpenedArchetypes) { SmallVector TypeDependentOperands; collectTypeDependentOperands(TypeDependentOperands, OpenedArchetypes, F, - valueType.getSwiftRValueType()); + valueType.getASTType()); void *Buffer = F.getModule().allocateInst( sizeof(AllocValueBufferInst) + sizeof(Operand) * (TypeDependentOperands.size() + 1), @@ -350,34 +367,29 @@ AllocValueBufferInst::create(SILDebugLocation DebugLoc, SILType valueType, BuiltinInst *BuiltinInst::create(SILDebugLocation Loc, Identifier Name, SILType ReturnType, - SubstitutionList Substitutions, + SubstitutionMap Substitutions, ArrayRef Args, SILModule &M) { - auto Size = totalSizeToAlloc(Args.size(), - Substitutions.size()); + auto Size = totalSizeToAlloc(Args.size()); auto Buffer = M.allocateInst(Size, alignof(BuiltinInst)); return ::new (Buffer) BuiltinInst(Loc, Name, ReturnType, Substitutions, Args); } BuiltinInst::BuiltinInst(SILDebugLocation Loc, Identifier Name, - SILType ReturnType, SubstitutionList Subs, + SILType ReturnType, SubstitutionMap Subs, ArrayRef Args) - : InstructionBaseWithTrailingOperands(Args, Loc, ReturnType), Name(Name) { - SILInstruction::Bits.BuiltinInst.NumSubstitutions = Subs.size(); - assert(SILInstruction::Bits.BuiltinInst.NumSubstitutions == Subs.size() && - "Truncation"); - std::uninitialized_copy(Subs.begin(), Subs.end(), - getTrailingObjects()); + : InstructionBaseWithTrailingOperands(Args, Loc, ReturnType), Name(Name), + Substitutions(Subs) { } InitBlockStorageHeaderInst * InitBlockStorageHeaderInst::create(SILFunction &F, SILDebugLocation DebugLoc, SILValue BlockStorage, SILValue InvokeFunction, SILType BlockType, - SubstitutionList Subs) { + SubstitutionMap Subs) { void *Buffer = F.getModule().allocateInst( - sizeof(InitBlockStorageHeaderInst) + sizeof(Substitution) * Subs.size(), + sizeof(InitBlockStorageHeaderInst), alignof(InitBlockStorageHeaderInst)); return ::new (Buffer) InitBlockStorageHeaderInst(DebugLoc, BlockStorage, @@ -387,8 +399,9 @@ InitBlockStorageHeaderInst::create(SILFunction &F, ApplyInst::ApplyInst(SILDebugLocation Loc, SILValue Callee, SILType SubstCalleeTy, SILType Result, - SubstitutionList Subs, - ArrayRef Args, ArrayRef TypeDependentOperands, + SubstitutionMap Subs, + ArrayRef Args, + ArrayRef TypeDependentOperands, bool isNonThrowing, const GenericSpecializationInformation *SpecializationInfo) : InstructionBase(Loc, Callee, SubstCalleeTy, Subs, Args, @@ -398,7 +411,7 @@ ApplyInst::ApplyInst(SILDebugLocation Loc, SILValue Callee, } ApplyInst * -ApplyInst::create(SILDebugLocation Loc, SILValue Callee, SubstitutionList Subs, +ApplyInst::create(SILDebugLocation Loc, SILValue Callee, SubstitutionMap Subs, ArrayRef Args, bool isNonThrowing, Optional ModuleConventions, SILFunction &F, SILOpenedArchetypesState &OpenedArchetypes, @@ -414,10 +427,10 @@ ApplyInst::create(SILDebugLocation Loc, SILValue Callee, SubstitutionList Subs, SmallVector TypeDependentOperands; collectTypeDependentOperands(TypeDependentOperands, OpenedArchetypes, F, - SubstCalleeSILTy.getSwiftRValueType(), Subs); + SubstCalleeSILTy.getASTType(), Subs); void *Buffer = - allocateTrailingInst( - F, getNumAllOperands(Args, TypeDependentOperands), Subs.size()); + allocateTrailingInst( + F, getNumAllOperands(Args, TypeDependentOperands)); return ::new(Buffer) ApplyInst(Loc, Callee, SubstCalleeSILTy, Result, Subs, Args, TypeDependentOperands, isNonThrowing, @@ -428,7 +441,7 @@ BeginApplyInst::BeginApplyInst(SILDebugLocation loc, SILValue callee, SILType substCalleeTy, ArrayRef allResultTypes, ArrayRef allResultOwnerships, - SubstitutionList subs, + SubstitutionMap subs, ArrayRef args, ArrayRef typeDependentOperands, bool isNonThrowing, @@ -443,7 +456,7 @@ BeginApplyInst::BeginApplyInst(SILDebugLocation loc, SILValue callee, BeginApplyInst * BeginApplyInst::create(SILDebugLocation loc, SILValue callee, - SubstitutionList subs, ArrayRef args, + SubstitutionMap subs, ArrayRef args, bool isNonThrowing, Optional moduleConventions, SILFunction &F, @@ -476,9 +489,9 @@ BeginApplyInst::create(SILDebugLocation loc, SILValue callee, collectTypeDependentOperands(typeDependentOperands, openedArchetypes, F, substCalleeType, subs); void *buffer = - allocateTrailingInst( - F, getNumAllOperands(args, typeDependentOperands), subs.size(), + F, getNumAllOperands(args, typeDependentOperands), 1, resultTypes.size()); return ::new(buffer) BeginApplyInst(loc, callee, substCalleeSILType, resultTypes, resultOwnerships, subs, @@ -495,7 +508,7 @@ bool swift::doesApplyCalleeHaveSemantics(SILValue callee, StringRef semantics) { PartialApplyInst::PartialApplyInst( SILDebugLocation Loc, SILValue Callee, SILType SubstCalleeTy, - SubstitutionList Subs, ArrayRef Args, + SubstitutionMap Subs, ArrayRef Args, ArrayRef TypeDependentOperands, SILType ClosureType, const GenericSpecializationInformation *SpecializationInfo) // FIXME: the callee should have a lowered SIL function type, and @@ -508,7 +521,7 @@ PartialApplyInst::PartialApplyInst( PartialApplyInst *PartialApplyInst::create( SILDebugLocation Loc, SILValue Callee, ArrayRef Args, - SubstitutionList Subs, ParameterConvention CalleeConvention, SILFunction &F, + SubstitutionMap Subs, ParameterConvention CalleeConvention, SILFunction &F, SILOpenedArchetypesState &OpenedArchetypes, const GenericSpecializationInformation *SpecializationInfo) { SILType SubstCalleeTy = @@ -518,10 +531,10 @@ PartialApplyInst *PartialApplyInst::create( SmallVector TypeDependentOperands; collectTypeDependentOperands(TypeDependentOperands, OpenedArchetypes, F, - SubstCalleeTy.getSwiftRValueType(), Subs); + SubstCalleeTy.getASTType(), Subs); void *Buffer = - allocateTrailingInst( - F, getNumAllOperands(Args, TypeDependentOperands), Subs.size()); + allocateTrailingInst( + F, getNumAllOperands(Args, TypeDependentOperands)); return ::new(Buffer) PartialApplyInst(Loc, Callee, SubstCalleeTy, Subs, Args, TypeDependentOperands, ClosureType, @@ -536,7 +549,7 @@ TryApplyInstBase::TryApplyInstBase(SILInstructionKind kind, TryApplyInst::TryApplyInst( SILDebugLocation Loc, SILValue callee, SILType substCalleeTy, - SubstitutionList subs, ArrayRef args, + SubstitutionMap subs, ArrayRef args, ArrayRef TypeDependentOperands, SILBasicBlock *normalBB, SILBasicBlock *errorBB, const GenericSpecializationInformation *SpecializationInfo) @@ -545,7 +558,7 @@ TryApplyInst::TryApplyInst( errorBB) {} TryApplyInst *TryApplyInst::create( - SILDebugLocation loc, SILValue callee, SubstitutionList subs, + SILDebugLocation loc, SILValue callee, SubstitutionMap subs, ArrayRef args, SILBasicBlock *normalBB, SILBasicBlock *errorBB, SILFunction &F, SILOpenedArchetypesState &openedArchetypes, const GenericSpecializationInformation *specializationInfo) { @@ -554,10 +567,11 @@ TryApplyInst *TryApplyInst::create( SmallVector typeDependentOperands; collectTypeDependentOperands(typeDependentOperands, openedArchetypes, F, - substCalleeTy.getSwiftRValueType(), subs); + substCalleeTy.getASTType(), + subs); void *buffer = - allocateTrailingInst( - F, getNumAllOperands(args, typeDependentOperands), subs.size()); + allocateTrailingInst( + F, getNumAllOperands(args, typeDependentOperands)); return ::new (buffer) TryApplyInst(loc, callee, substCalleeTy, subs, args, typeDependentOperands, normalBB, errorBB, specializationInfo); @@ -818,7 +832,7 @@ MarkFunctionEscapeInst::create(SILDebugLocation Loc, static SILType getPinResultType(SILType operandType) { return SILType::getPrimitiveObjectType( - OptionalType::get(operandType.getSwiftRValueType())->getCanonicalType()); + OptionalType::get(operandType.getASTType())->getCanonicalType()); } StrongPinInst::StrongPinInst(SILDebugLocation Loc, SILValue operand, @@ -842,7 +856,7 @@ BindMemoryInst::create(SILDebugLocation Loc, SILValue Base, SILValue Index, SILOpenedArchetypesState &OpenedArchetypes) { SmallVector TypeDependentOperands; collectTypeDependentOperands(TypeDependentOperands, OpenedArchetypes, F, - BoundType.getSwiftRValueType()); + BoundType.getASTType()); auto Size = totalSizeToAlloc(TypeDependentOperands.size() + NumFixedOpers); auto Buffer = F.getModule().allocateInst(Size, alignof(BindMemoryInst)); @@ -1584,7 +1598,7 @@ ObjCMethodInst::create(SILDebugLocation DebugLoc, SILValue Operand, SILModule &Mod = F->getModule(); SmallVector TypeDependentOperands; collectTypeDependentOperands(TypeDependentOperands, OpenedArchetypes, *F, - Ty.getSwiftRValueType()); + Ty.getASTType()); unsigned size = totalSizeToAlloc(1 + TypeDependentOperands.size()); @@ -1679,7 +1693,7 @@ InitExistentialMetatypeInst *InitExistentialMetatypeInst::create( SILModule &M = F->getModule(); SmallVector TypeDependentOperands; collectTypeDependentOperands(TypeDependentOperands, OpenedArchetypes, *F, - existentialMetatypeType.getSwiftRValueType()); + existentialMetatypeType.getASTType()); unsigned size = totalSizeToAlloc( 1 + TypeDependentOperands.size(), conformances.size()); @@ -1702,15 +1716,13 @@ MarkUninitializedBehaviorInst * MarkUninitializedBehaviorInst::create(SILModule &M, SILDebugLocation DebugLoc, SILValue InitStorage, - SubstitutionList InitStorageSubs, + SubstitutionMap InitStorageSubs, SILValue Storage, SILValue Setter, - SubstitutionList SetterSubs, + SubstitutionMap SetterSubs, SILValue Self, SILType Ty) { - auto totalSubs = InitStorageSubs.size() + SetterSubs.size(); - auto mem = M.allocateInst(sizeof(MarkUninitializedBehaviorInst) - + additionalSizeToAlloc(totalSubs), + auto mem = M.allocateInst(sizeof(MarkUninitializedBehaviorInst), alignof(MarkUninitializedBehaviorInst)); return ::new (mem) MarkUninitializedBehaviorInst(DebugLoc, InitStorage, InitStorageSubs, @@ -1723,24 +1735,17 @@ MarkUninitializedBehaviorInst::create(SILModule &M, MarkUninitializedBehaviorInst::MarkUninitializedBehaviorInst( SILDebugLocation DebugLoc, SILValue InitStorage, - SubstitutionList InitStorageSubs, + SubstitutionMap InitStorageSubs, SILValue Storage, SILValue Setter, - SubstitutionList SetterSubs, + SubstitutionMap SetterSubs, SILValue Self, SILType Ty) : InstructionBase(DebugLoc, Ty), Operands(this, InitStorage, Storage, Setter, Self), - NumInitStorageSubstitutions(InitStorageSubs.size()), - NumSetterSubstitutions(SetterSubs.size()) + InitStorageSubstitutions(InitStorageSubs), + SetterSubstitutions(SetterSubs) { - auto *trailing = getTrailingObjects(); - for (unsigned i = 0; i < InitStorageSubs.size(); ++i) { - ::new ((void*)trailing++) Substitution(InitStorageSubs[i]); - } - for (unsigned i = 0; i < SetterSubs.size(); ++i) { - ::new ((void*)trailing++) Substitution(SetterSubs[i]); - } } OpenedExistentialAccess swift::getOpenedExistentialAccessFor(AccessKind access) { @@ -1793,7 +1798,7 @@ UncheckedRefCastInst::create(SILDebugLocation DebugLoc, SILValue Operand, SILModule &Mod = F.getModule(); SmallVector TypeDependentOperands; collectTypeDependentOperands(TypeDependentOperands, OpenedArchetypes, F, - Ty.getSwiftRValueType()); + Ty.getASTType()); unsigned size = totalSizeToAlloc(1 + TypeDependentOperands.size()); void *Buffer = Mod.allocateInst(size, alignof(UncheckedRefCastInst)); @@ -1808,7 +1813,7 @@ UncheckedAddrCastInst::create(SILDebugLocation DebugLoc, SILValue Operand, SILModule &Mod = F.getModule(); SmallVector TypeDependentOperands; collectTypeDependentOperands(TypeDependentOperands, OpenedArchetypes, F, - Ty.getSwiftRValueType()); + Ty.getASTType()); unsigned size = totalSizeToAlloc(1 + TypeDependentOperands.size()); void *Buffer = Mod.allocateInst(size, alignof(UncheckedAddrCastInst)); @@ -1823,7 +1828,7 @@ UncheckedTrivialBitCastInst::create(SILDebugLocation DebugLoc, SILValue Operand, SILModule &Mod = F.getModule(); SmallVector TypeDependentOperands; collectTypeDependentOperands(TypeDependentOperands, OpenedArchetypes, F, - Ty.getSwiftRValueType()); + Ty.getASTType()); unsigned size = totalSizeToAlloc(1 + TypeDependentOperands.size()); void *Buffer = Mod.allocateInst(size, alignof(UncheckedTrivialBitCastInst)); @@ -1839,7 +1844,7 @@ UncheckedBitwiseCastInst::create(SILDebugLocation DebugLoc, SILValue Operand, SILModule &Mod = F.getModule(); SmallVector TypeDependentOperands; collectTypeDependentOperands(TypeDependentOperands, OpenedArchetypes, F, - Ty.getSwiftRValueType()); + Ty.getASTType()); unsigned size = totalSizeToAlloc(1 + TypeDependentOperands.size()); void *Buffer = Mod.allocateInst(size, alignof(UncheckedBitwiseCastInst)); @@ -1853,7 +1858,7 @@ UnconditionalCheckedCastInst *UnconditionalCheckedCastInst::create( SILModule &Mod = F.getModule(); SmallVector TypeDependentOperands; collectTypeDependentOperands(TypeDependentOperands, OpenedArchetypes, F, - DestTy.getSwiftRValueType()); + DestTy.getASTType()); unsigned size = totalSizeToAlloc(1 + TypeDependentOperands.size()); void *Buffer = Mod.allocateInst(size, alignof(UnconditionalCheckedCastInst)); @@ -1867,7 +1872,7 @@ UnconditionalCheckedCastValueInst *UnconditionalCheckedCastValueInst::create( SILModule &Mod = F.getModule(); SmallVector TypeDependentOperands; collectTypeDependentOperands(TypeDependentOperands, OpenedArchetypes, F, - DestTy.getSwiftRValueType()); + DestTy.getASTType()); unsigned size = totalSizeToAlloc(1 + TypeDependentOperands.size()); void *Buffer = @@ -1884,7 +1889,7 @@ CheckedCastBranchInst *CheckedCastBranchInst::create( SILModule &Mod = F.getModule(); SmallVector TypeDependentOperands; collectTypeDependentOperands(TypeDependentOperands, OpenedArchetypes, F, - DestTy.getSwiftRValueType()); + DestTy.getASTType()); unsigned size = totalSizeToAlloc(1 + TypeDependentOperands.size()); void *Buffer = Mod.allocateInst(size, alignof(CheckedCastBranchInst)); @@ -1901,7 +1906,7 @@ CheckedCastValueBranchInst::create(SILDebugLocation DebugLoc, SILValue Operand, SILModule &Mod = F.getModule(); SmallVector TypeDependentOperands; collectTypeDependentOperands(TypeDependentOperands, OpenedArchetypes, F, - DestTy.getSwiftRValueType()); + DestTy.getASTType()); unsigned size = totalSizeToAlloc(1 + TypeDependentOperands.size()); void *Buffer = Mod.allocateInst(size, alignof(CheckedCastValueBranchInst)); @@ -1927,7 +1932,7 @@ UpcastInst *UpcastInst::create(SILDebugLocation DebugLoc, SILValue Operand, SILModule &Mod = F.getModule(); SmallVector TypeDependentOperands; collectTypeDependentOperands(TypeDependentOperands, OpenedArchetypes, F, - Ty.getSwiftRValueType()); + Ty.getASTType()); unsigned size = totalSizeToAlloc(1 + TypeDependentOperands.size()); void *Buffer = Mod.allocateInst(size, alignof(UpcastInst)); @@ -1942,7 +1947,7 @@ ThinToThickFunctionInst::create(SILDebugLocation DebugLoc, SILValue Operand, SILModule &Mod = F.getModule(); SmallVector TypeDependentOperands; collectTypeDependentOperands(TypeDependentOperands, OpenedArchetypes, F, - Ty.getSwiftRValueType()); + Ty.getASTType()); unsigned size = totalSizeToAlloc(1 + TypeDependentOperands.size()); void *Buffer = Mod.allocateInst(size, alignof(ThinToThickFunctionInst)); @@ -1957,7 +1962,7 @@ PointerToThinFunctionInst::create(SILDebugLocation DebugLoc, SILValue Operand, SILModule &Mod = F.getModule(); SmallVector TypeDependentOperands; collectTypeDependentOperands(TypeDependentOperands, OpenedArchetypes, F, - Ty.getSwiftRValueType()); + Ty.getASTType()); unsigned size = totalSizeToAlloc(1 + TypeDependentOperands.size()); void *Buffer = Mod.allocateInst(size, alignof(PointerToThinFunctionInst)); @@ -1972,7 +1977,7 @@ ConvertFunctionInst::create(SILDebugLocation DebugLoc, SILValue Operand, SILModule &Mod = F.getModule(); SmallVector TypeDependentOperands; collectTypeDependentOperands(TypeDependentOperands, OpenedArchetypes, F, - Ty.getSwiftRValueType()); + Ty.getASTType()); unsigned size = totalSizeToAlloc(1 + TypeDependentOperands.size()); void *Buffer = Mod.allocateInst(size, alignof(ConvertFunctionInst)); @@ -1998,16 +2003,18 @@ ConvertFunctionInst::create(SILDebugLocation DebugLoc, SILValue Operand, ConvertEscapeToNoEscapeInst *ConvertEscapeToNoEscapeInst::create( SILDebugLocation DebugLoc, SILValue Operand, SILType Ty, SILFunction &F, - SILOpenedArchetypesState &OpenedArchetypes, bool isLifetimeGuaranteed) { + SILOpenedArchetypesState &OpenedArchetypes, bool isEscapedByUser, + bool isLifetimeGuaranteed) { SILModule &Mod = F.getModule(); SmallVector TypeDependentOperands; collectTypeDependentOperands(TypeDependentOperands, OpenedArchetypes, F, - Ty.getSwiftRValueType()); + Ty.getASTType()); unsigned size = totalSizeToAlloc(1 + TypeDependentOperands.size()); void *Buffer = Mod.allocateInst(size, alignof(ConvertEscapeToNoEscapeInst)); - auto *CFI = ::new (Buffer) ConvertEscapeToNoEscapeInst( - DebugLoc, Operand, TypeDependentOperands, Ty, isLifetimeGuaranteed); + auto *CFI = ::new (Buffer) + ConvertEscapeToNoEscapeInst(DebugLoc, Operand, TypeDependentOperands, Ty, + isEscapedByUser, isLifetimeGuaranteed); // If we do not have lowered SIL, make sure that are not performing // ABI-incompatible conversions. // @@ -2192,7 +2199,7 @@ void KeyPathPattern::Profile(llvm::FoldingSetNodeID &ID, case KeyPathPatternComponent::Kind::External: { ID.AddPointer(component.getExternalDecl()); - profileSubstitutionList(ID, component.getExternalSubstitutions()); + component.getExternalSubstitutions().profile(ID); profileIndices(component.getSubscriptIndices()); break; } @@ -2234,31 +2241,28 @@ void KeyPathPattern::Profile(llvm::FoldingSetNodeID &ID, KeyPathInst * KeyPathInst::create(SILDebugLocation Loc, KeyPathPattern *Pattern, - SubstitutionList Subs, + SubstitutionMap Subs, ArrayRef Args, SILType Ty, SILFunction &F) { assert(Args.size() == Pattern->getNumOperands() && "number of key path args doesn't match pattern"); - auto totalSize = totalSizeToAlloc - (Subs.size(), Args.size()); - void *mem = F.getModule().allocateInst(totalSize, alignof(Substitution)); + auto totalSize = totalSizeToAlloc(Args.size()); + void *mem = F.getModule().allocateInst(totalSize, alignof(KeyPathInst)); return ::new (mem) KeyPathInst(Loc, Pattern, Subs, Args, Ty); } KeyPathInst::KeyPathInst(SILDebugLocation Loc, KeyPathPattern *Pattern, - SubstitutionList Subs, + SubstitutionMap Subs, ArrayRef Args, SILType Ty) : InstructionBase(Loc, Ty), - Pattern(Pattern), NumSubstitutions(Subs.size()), - NumOperands(Pattern->getNumOperands()) + Pattern(Pattern), + NumOperands(Pattern->getNumOperands()), + Substitutions(Subs) { - auto *subsBuf = getTrailingObjects(); - std::uninitialized_copy(Subs.begin(), Subs.end(), subsBuf); - auto *operandsBuf = getTrailingObjects(); for (unsigned i = 0; i < Args.size(); ++i) { ::new ((void*)&operandsBuf[i]) Operand(this, Args[i]); @@ -2270,11 +2274,6 @@ KeyPathInst::KeyPathInst(SILDebugLocation Loc, } } -MutableArrayRef -KeyPathInst::getSubstitutions() { - return {getTrailingObjects(), NumSubstitutions}; -} - MutableArrayRef KeyPathInst::getAllOperands() { return {getTrailingObjects(), NumOperands}; @@ -2306,18 +2305,17 @@ void KeyPathInst::dropReferencedPattern() { } GenericSpecializationInformation::GenericSpecializationInformation( - SILFunction *Caller, SILFunction *Parent, SubstitutionList Subs) + SILFunction *Caller, SILFunction *Parent, SubstitutionMap Subs) : Caller(Caller), Parent(Parent), Subs(Subs) {} const GenericSpecializationInformation * GenericSpecializationInformation::create(SILFunction *Caller, SILFunction *Parent, - SubstitutionList Subs) { + SubstitutionMap Subs) { auto &M = Parent->getModule(); void *Buf = M.allocate(sizeof(GenericSpecializationInformation), alignof(GenericSpecializationInformation)); - auto NewSubs = M.allocateCopy(Subs); - return new (Buf) GenericSpecializationInformation(Caller, Parent, NewSubs); + return new (Buf) GenericSpecializationInformation(Caller, Parent, Subs); } const GenericSpecializationInformation * @@ -2393,7 +2391,7 @@ DestructureStructInst *DestructureStructInst::create(SILModule &M, DestructureTupleInst *DestructureTupleInst::create(SILModule &M, SILDebugLocation Loc, SILValue Operand) { - assert(Operand->getType().getSwiftRValueType()->is() && + assert(Operand->getType().is() && "Expected a tuple typed operand?!"); llvm::SmallVector Types; diff --git a/lib/SIL/SILModule.cpp b/lib/SIL/SILModule.cpp index 40adaf93bc5e5..dd2bfc448bb75 100644 --- a/lib/SIL/SILModule.cpp +++ b/lib/SIL/SILModule.cpp @@ -14,7 +14,6 @@ #include "swift/Serialization/SerializedSILLoader.h" #include "swift/AST/GenericEnvironment.h" #include "swift/AST/ProtocolConformance.h" -#include "swift/AST/Substitution.h" #include "swift/SIL/FormalLinkage.h" #include "swift/SIL/SILDebugScope.h" #include "swift/SIL/SILModule.h" @@ -22,6 +21,7 @@ #include "Linker.h" #include "swift/SIL/SILVisitor.h" #include "swift/SIL/SILValue.h" +#include "swift/ClangImporter/ClangModule.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringSwitch.h" @@ -112,6 +112,17 @@ SILModule::~SILModule() { F.dropAllReferences(); } +std::unique_ptr +SILModule::createEmptyModule(ModuleDecl *M, SILOptions &Options, + bool WholeModule) { + return std::unique_ptr( + new SILModule(M, Options, M, WholeModule)); +} + +ASTContext &SILModule::getASTContext() const { + return TheSwiftModule->getASTContext(); +} + void *SILModule::allocate(unsigned Size, unsigned Align) const { if (getASTContext().LangOpts.UseMalloc) return AlignedAlloc(Size, Align); @@ -185,6 +196,18 @@ SILModule::lookUpWitnessTable(const ProtocolConformance *C, if (wtable->isDefinition()) return wtable; + // If the module is at or past the Lowered stage, then we can't do any + // further deserialization, since pre-IRGen SIL lowering changes the types + // of definitions to make them incompatible with canonical serialized SIL. + switch (getStage()) { + case SILStage::Canonical: + case SILStage::Raw: + break; + + case SILStage::Lowered: + return wtable; + } + // Otherwise try to deserialize it. If we succeed return the deserialized // function. // @@ -472,8 +495,17 @@ SILFunction *SILModule::lookUpFunction(SILDeclRef fnRef) { return lookUpFunction(name); } -bool SILModule::linkFunction(SILFunction *Fun, SILModule::LinkingMode Mode) { - return SILLinkerVisitor(*this, getSILLoader(), Mode).processFunction(Fun); +bool SILModule::loadFunction(SILFunction *F) { + SILFunction *NewF = getSILLoader()->lookupSILFunction(F); + if (!NewF) + return false; + + assert(F == NewF); + return true; +} + +bool SILModule::linkFunction(SILFunction *F, SILModule::LinkingMode Mode) { + return SILLinkerVisitor(*this, Mode).processFunction(F); } SILFunction *SILModule::findFunction(StringRef Name, SILLinkage Linkage) { @@ -548,14 +580,6 @@ void SILModule::linkAllFromCurrentModule() { /*PrimaryFile=*/nullptr); } -void SILModule::linkAllWitnessTables() { - getSILLoader()->getAllWitnessTables(); -} - -void SILModule::linkAllVTables() { - getSILLoader()->getAllVTables(); -} - void SILModule::invalidateSILLoaderCaches() { getSILLoader()->invalidateCaches(); } @@ -609,9 +633,7 @@ SILVTable *SILModule::lookUpVTable(const ClassDecl *C) { return R->second; // If that fails, try to deserialize it. If that fails, return nullptr. - SILVTable *Vtbl = - SILLinkerVisitor(*this, getSILLoader(), SILModule::LinkingMode::LinkAll) - .processClassDecl(C); + SILVTable *Vtbl = getSILLoader()->lookupVTable(C); if (!Vtbl) return nullptr; @@ -774,6 +796,23 @@ bool SILModule::isNoReturnBuiltinOrIntrinsic(Identifier Name) { } } +bool SILModule:: +shouldSerializeEntitiesAssociatedWithDeclContext(const DeclContext *DC) const { + // Serialize entities associated with this module's associated context. + if (DC->isChildContextOf(getAssociatedContext())) { + return true; + } + + // Serialize entities associated with clang modules, since other entities + // may depend on them, and someone who deserializes those entities may not + // have their own copy. + if (isa(DC->getModuleScopeContext())) { + return true; + } + + return false; +} + /// Returns true if it is the OnoneSupport module. bool SILModule::isOnoneSupportModule() const { return getSwiftModule()->getName().str() == SWIFT_ONONE_SUPPORT; diff --git a/lib/SIL/SILOpenedArchetypesTracker.cpp b/lib/SIL/SILOpenedArchetypesTracker.cpp index 960cf8b36ba3d..1b2180dbb469e 100644 --- a/lib/SIL/SILOpenedArchetypesTracker.cpp +++ b/lib/SIL/SILOpenedArchetypesTracker.cpp @@ -152,7 +152,7 @@ CanArchetypeType swift::getOpenedArchetypeOf(const SILInstruction *I) { isa(I) || isa(I) || isa(I) || isa(I)) { auto SVI = cast(I); - auto Ty = getOpenedArchetypeOf(SVI->getType().getSwiftRValueType()); + auto Ty = getOpenedArchetypeOf(SVI->getType().getASTType()); assert(Ty && Ty->isOpenedExistential() && "Type should be an opened archetype"); return Ty; diff --git a/lib/SIL/SILOwnershipVerifier.cpp b/lib/SIL/SILOwnershipVerifier.cpp index bd49d6cdc833a..259e7d7444312 100644 --- a/lib/SIL/SILOwnershipVerifier.cpp +++ b/lib/SIL/SILOwnershipVerifier.cpp @@ -24,9 +24,10 @@ #include "swift/Basic/TransformArrayRef.h" #include "swift/ClangImporter/ClangModule.h" #include "swift/SIL/BasicBlockUtils.h" +#include "swift/SIL/BranchPropagatedUser.h" #include "swift/SIL/Dominance.h" #include "swift/SIL/DynamicCasts.h" -#include "swift/SIL/OwnershipChecker.h" +#include "swift/SIL/OwnershipUtils.h" #include "swift/SIL/PrettyStackTrace.h" #include "swift/SIL/Projection.h" #include "swift/SIL/SILBuiltinVisitor.h" @@ -45,6 +46,7 @@ #include using namespace swift; +using namespace swift::ownership; // This is an option to put the SILOwnershipVerifier in testing mode. This // causes the following: @@ -68,110 +70,6 @@ llvm::cl::opt IsSILOwnershipVerifierTestingEnabled( static llvm::cl::opt DisableOwnershipVerification("disable-sil-ownership-verification"); -//===----------------------------------------------------------------------===// -// Generalized User -//===----------------------------------------------------------------------===// - -namespace { - -/// This is a class that models normal users and also cond_br users that are -/// associated with the block in the target block. This is safe to do since in -/// Semantic SIL, cond_br with non-trivial arguments are not allowed to have -/// critical edges. -class GeneralizedUser { - using InnerTy = llvm::PointerIntPair; - InnerTy User; - -public: - GeneralizedUser(SILInstruction *I) : User(I) { - assert(!isa(I)); - } - - GeneralizedUser(CondBranchInst *I) : User(I) {} - - GeneralizedUser(CondBranchInst *I, unsigned SuccessorIndex) - : User(I, SuccessorIndex) { - assert(SuccessorIndex == CondBranchInst::TrueIdx || - SuccessorIndex == CondBranchInst::FalseIdx); - } - - GeneralizedUser(const GeneralizedUser &Other) : User(Other.User) {} - GeneralizedUser &operator=(const GeneralizedUser &Other) { - User = Other.User; - return *this; - } - - operator SILInstruction *() { return User.getPointer(); } - operator const SILInstruction *() const { return User.getPointer(); } - - SILInstruction *getInst() const { return User.getPointer(); } - - SILBasicBlock *getParent() const; - - bool isCondBranchUser() const { - return isa(User.getPointer()); - } - - unsigned getCondBranchSuccessorID() const { - assert(isCondBranchUser()); - return User.getInt(); - } - - SILBasicBlock::iterator getIterator() const { - return User.getPointer()->getIterator(); - } - - void *getAsOpaqueValue() const { - return llvm::PointerLikeTypeTraits::getAsVoidPointer(User); - } - - static GeneralizedUser getFromOpaqueValue(void *p) { - InnerTy TmpUser = - llvm::PointerLikeTypeTraits::getFromVoidPointer(p); - if (auto *CBI = dyn_cast(TmpUser.getPointer())) { - return GeneralizedUser(CBI, TmpUser.getInt()); - } - return GeneralizedUser(TmpUser.getPointer()); - } - - enum { - NumLowBitsAvailable = - llvm::PointerLikeTypeTraits::NumLowBitsAvailable - }; -}; - -} // end anonymous namespace - -SILBasicBlock *GeneralizedUser::getParent() const { - if (!isCondBranchUser()) { - return getInst()->getParent(); - } - - auto *CBI = cast(getInst()); - unsigned Number = getCondBranchSuccessorID(); - if (Number == CondBranchInst::TrueIdx) - return CBI->getTrueBB(); - return CBI->getFalseBB(); -} - -namespace llvm { - -template <> struct PointerLikeTypeTraits { - -public: - static void *getAsVoidPointer(GeneralizedUser v) { - return v.getAsOpaqueValue(); - } - - static GeneralizedUser getFromVoidPointer(void *p) { - return GeneralizedUser::getFromOpaqueValue(p); - } - - enum { NumLowBitsAvailable = GeneralizedUser::NumLowBitsAvailable }; -}; - -} // namespace llvm - //===----------------------------------------------------------------------===// // Utility //===----------------------------------------------------------------------===// @@ -241,35 +139,6 @@ static bool isOwnershipForwardingInst(SILInstruction *I) { namespace { -struct ErrorBehaviorKind { - enum inner_t { - Invalid = 0, - ReturnFalse = 1, - PrintMessage = 2, - Assert = 4, - PrintMessageAndReturnFalse = PrintMessage | ReturnFalse, - PrintMessageAndAssert = PrintMessage | Assert, - } Value; - - ErrorBehaviorKind() : Value(Invalid) {} - ErrorBehaviorKind(inner_t Inner) : Value(Inner) { assert(Value != Invalid); } - - bool shouldAssert() const { - assert(Value != Invalid); - return Value & Assert; - } - - bool shouldPrintMessage() const { - assert(Value != Invalid); - return Value & PrintMessage; - } - - bool shouldReturnFalse() const { - assert(Value != Invalid); - return Value & ReturnFalse; - } -}; - struct OwnershipUseCheckerResult { bool HasCompatibleOwnership; bool ShouldCheckForDataflowViolations; @@ -1181,6 +1050,21 @@ OwnershipCompatibilityUseChecker::visitStoreInst(StoreInst *I) { return {true, UseLifetimeConstraint::MustBeLive}; } +OwnershipUseCheckerResult +OwnershipCompatibilityUseChecker::visitCopyBlockWithoutEscapingInst( + CopyBlockWithoutEscapingInst *I) { + // Consumes the closure parameter. + if (getValue() == I->getClosure()) { + return {compatibleWithOwnership(ValueOwnershipKind::Owned), + UseLifetimeConstraint::MustBeInvalidated}; + } + bool compatible = hasExactOwnership(ValueOwnershipKind::Any) || + !compatibleWithOwnership(ValueOwnershipKind::Trivial); + + return { compatible, UseLifetimeConstraint::MustBeLive }; +} + + OwnershipUseCheckerResult OwnershipCompatibilityUseChecker::visitMarkDependenceInst( MarkDependenceInst *MDI) { @@ -1428,9 +1312,13 @@ BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(BridgeFromRawPointer) BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(CastReference) BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(ReinterpretCast) BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(AddressOf) +BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(AddressOfBorrow) BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(GepRaw) BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(Gep) BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(GetTailAddr) +BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(PerformInstantaneousReadAccess) +BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(BeginUnpairedModifyAccess) +BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(EndUnpairedAccess) BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(CondFail) BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(FixLifetime) BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(IsUnique) @@ -1472,40 +1360,24 @@ class SILValueOwnershipChecker { /// The action that the checker should perform on detecting an error. ErrorBehaviorKind ErrorBehavior; - /// The worklist that we will use for our iterative reachability query. - llvm::SmallVector Worklist; - - /// The set of blocks with lifetime ending uses. - llvm::SmallPtrSet BlocksWithLifetimeEndingUses; - - /// The set of blocks with non-lifetime ending uses and the associated - /// non-lifetime ending use SILInstruction. - llvm::SmallDenseMap - BlocksWithNonLifetimeEndingUses; - - /// The blocks that we have already visited. - llvm::SmallPtrSet &VisitedBlocks; - - /// A list of successor blocks that we must visit by the time the algorithm - /// terminates. - llvm::SmallPtrSet SuccessorBlocksThatMustBeVisited; - /// The list of lifetime ending users that we found. Only valid if check is /// successful. - llvm::SmallVector LifetimeEndingUsers; + llvm::SmallVector LifetimeEndingUsers; /// The list of non lifetime ending users that we found. Only valid if check /// is successful. - llvm::SmallVector RegularUsers; + llvm::SmallVector RegularUsers; + + /// The set of blocks that we have visited. + llvm::SmallPtrSetImpl &VisitedBlocks; public: SILValueOwnershipChecker( SILModule &M, DeadEndBlocks &DEBlocks, SILValue V, ErrorBehaviorKind ErrorBehavior, - llvm::SmallPtrSet &VisitedBlocks) + llvm::SmallPtrSetImpl &VisitedBlocks) : Result(), Mod(M), DEBlocks(DEBlocks), Value(V), - ErrorBehavior(ErrorBehavior), - VisitedBlocks(VisitedBlocks) { + ErrorBehavior(ErrorBehavior), VisitedBlocks(VisitedBlocks) { assert(Value && "Can not initialize a checker with an empty SILValue"); } @@ -1518,12 +1390,18 @@ class SILValueOwnershipChecker { return Result.getValue(); DEBUG(llvm::dbgs() << "Verifying ownership of: " << *Value); - Result = checkUses() && checkDataflow(); + Result = checkUses(); + if (!Result.getValue()) + return false; + + Result = valueHasLinearLifetime(Value, LifetimeEndingUsers, RegularUsers, + VisitedBlocks, DEBlocks, ErrorBehavior); return Result.getValue(); } - using user_array_transform = std::function; + using user_array_transform = + std::function; using user_array = TransformArrayRef; /// A function that returns a range of lifetime ending users found for the @@ -1533,10 +1411,10 @@ class SILValueOwnershipChecker { assert(Result.getValue() && "Can not call if check() returned false"); user_array_transform Transform( - [](GeneralizedUser User) -> SILInstruction * { + [](BranchPropagatedUser User) -> SILInstruction * { return User.getInst(); }); - return user_array(ArrayRef(LifetimeEndingUsers), + return user_array(ArrayRef(LifetimeEndingUsers), Transform); } @@ -1547,37 +1425,17 @@ class SILValueOwnershipChecker { assert(Result.getValue() && "Can not call if check() returned false"); user_array_transform Transform( - [](GeneralizedUser User) -> SILInstruction * { + [](BranchPropagatedUser User) -> SILInstruction * { return User.getInst(); }); - return user_array(ArrayRef(RegularUsers), Transform); + return user_array(ArrayRef(RegularUsers), Transform); } private: bool checkUses(); - bool checkDataflow(); - void checkDataflowEndConditions(); - void - gatherUsers(llvm::SmallVectorImpl &LifetimeEndingUsers, - llvm::SmallVectorImpl &NonLifetimeEndingUsers); - void uniqueNonLifetimeEndingUsers( - ArrayRef NonLifetimeEndingUsers); - - /// Returns true if the given block is in the BlocksWithLifetimeEndingUses - /// set. This is a helper to extract out large logging messages so that the - /// main logic is easy to read. - bool doesBlockDoubleConsume( - SILBasicBlock *UserBlock, - llvm::Optional LifetimeEndingUser = None, - bool ShouldInsert = false); - - /// Returns true if the given block contains a non-lifetime ending use that is - /// strictly later in the block than a lifetime ending use. If all - /// non-lifetime ending uses are before the lifetime ending use, the block is - /// removed from the BlocksWithNonLifetimeEndingUses map to show that the uses - /// were found to properly be post-dominated by a lifetime ending use. - bool doesBlockContainUseAfterFree(GeneralizedUser LifetimeEndingUser, - SILBasicBlock *UserBlock); + void gatherUsers( + llvm::SmallVectorImpl &LifetimeEndingUsers, + llvm::SmallVectorImpl &NonLifetimeEndingUsers); bool checkValueWithoutLifetimeEndingUses(); @@ -1585,10 +1443,12 @@ class SILValueOwnershipChecker { bool isGuaranteedFunctionArgWithLifetimeEndingUses( SILFunctionArgument *Arg, - const llvm::SmallVectorImpl &LifetimeEndingUsers) const; + const llvm::SmallVectorImpl &LifetimeEndingUsers) + const; bool isSubobjectProjectionWithLifetimeEndingUses( SILValue Value, - const llvm::SmallVectorImpl &LifetimeEndingUsers) const; + const llvm::SmallVectorImpl &LifetimeEndingUsers) + const; /// Depending on our initialization, either return false or call Func and /// throw an error. @@ -1608,81 +1468,9 @@ class SILValueOwnershipChecker { } // end anonymous namespace -bool SILValueOwnershipChecker::doesBlockContainUseAfterFree( - GeneralizedUser LifetimeEndingUser, SILBasicBlock *UserBlock) { - auto Iter = BlocksWithNonLifetimeEndingUses.find(UserBlock); - if (Iter == BlocksWithNonLifetimeEndingUses.end()) - return false; - - GeneralizedUser NonLifetimeEndingUser = Iter->second; - - // Make sure that the non-lifetime ending use is before the lifetime ending - // use. Otherwise, we have a use after free. - - // First check if our lifetime ending user is a cond_br. In such a case, we - // always consider the non-lifetime ending use to be a use after free. - if (LifetimeEndingUser.isCondBranchUser()) { - return !handleError([&]() { - llvm::errs() << "Function: '" << Value->getFunction()->getName() << "'\n" - << "Found use after free?!\n" - << "Value: " << *Value - << "Consuming User: " << *LifetimeEndingUser - << "Non Consuming User: " << *Iter->second << "Block: bb" - << UserBlock->getDebugID() << "\n\n"; - }); - } - - // Ok. At this point, we know that our lifetime ending user is not a cond - // branch user. Check if our non-lifetime ending use is. In such a case, we - // know that our non lifetime ending user is properly post-dominated so we can - // erase the non lifetime ending use and continue. - if (NonLifetimeEndingUser.isCondBranchUser()) { - BlocksWithNonLifetimeEndingUses.erase(Iter); - return false; - } - - // Otherwise, we know that both of our users are non-cond branch users and - // thus must be instructions in the given block. Make sure that the non - // lifetime ending user is strictly before the lifetime ending user. - if (std::find_if(LifetimeEndingUser.getIterator(), UserBlock->end(), - [&NonLifetimeEndingUser](const SILInstruction &I) -> bool { - return NonLifetimeEndingUser == &I; - }) != UserBlock->end()) { - return !handleError([&] { - llvm::errs() << "Function: '" << Value->getFunction()->getName() << "'\n" - << "Found use after free?!\n" - << "Value: " << *Value - << "Consuming User: " << *LifetimeEndingUser - << "Non Consuming User: " << *Iter->second << "Block: bb" - << UserBlock->getDebugID() << "\n\n"; - }); - } - - // Erase the use since we know that it is properly joint post-dominated. - BlocksWithNonLifetimeEndingUses.erase(Iter); - return false; -} - -bool SILValueOwnershipChecker::doesBlockDoubleConsume( - SILBasicBlock *UserBlock, - llvm::Optional LifetimeEndingUser, bool ShouldInsert) { - if ((ShouldInsert && BlocksWithLifetimeEndingUses.insert(UserBlock).second) || - !BlocksWithLifetimeEndingUses.count(UserBlock)) - return false; - - return !handleError([&] { - llvm::errs() << "Function: '" << Value->getFunction()->getName() << "'\n" - << "Found over consume?!\n" - << "Value: " << *Value; - if (LifetimeEndingUser.hasValue()) - llvm::errs() << "User: " << *LifetimeEndingUser.getValue(); - llvm::errs() << "Block: bb" << UserBlock->getDebugID() << "\n\n"; - }); -} - void SILValueOwnershipChecker::gatherUsers( - llvm::SmallVectorImpl &LifetimeEndingUsers, - llvm::SmallVectorImpl &NonLifetimeEndingUsers) { + llvm::SmallVectorImpl &LifetimeEndingUsers, + llvm::SmallVectorImpl &NonLifetimeEndingUsers) { // See if Value is guaranteed. If we are guaranteed and not forwarding, then // we need to look through subobject uses for more uses. Otherwise, if we are @@ -1698,17 +1486,18 @@ void SILValueOwnershipChecker::gatherUsers( llvm::SmallVector Users; std::copy(Value->use_begin(), Value->use_end(), std::back_inserter(Users)); - auto addCondBranchToList = [](llvm::SmallVectorImpl &List, - CondBranchInst *CBI, unsigned OperandIndex) { - if (CBI->isConditionOperandIndex(OperandIndex)) { - List.emplace_back(CBI); - return; - } + auto addCondBranchToList = + [](llvm::SmallVectorImpl &List, CondBranchInst *CBI, + unsigned OperandIndex) { + if (CBI->isConditionOperandIndex(OperandIndex)) { + List.emplace_back(CBI); + return; + } - bool isTrueOperand = CBI->isTrueOperandIndex(OperandIndex); - List.emplace_back(CBI, isTrueOperand ? CondBranchInst::TrueIdx - : CondBranchInst::FalseIdx); - }; + bool isTrueOperand = CBI->isTrueOperandIndex(OperandIndex); + List.emplace_back(CBI, isTrueOperand ? CondBranchInst::TrueIdx + : CondBranchInst::FalseIdx); + }; while (!Users.empty()) { Operand *Op = Users.pop_back_val(); @@ -1814,49 +1603,6 @@ void SILValueOwnershipChecker::gatherUsers( } } -// Unique our non lifetime ending user list by only selecting the last user in -// each block. -void SILValueOwnershipChecker::uniqueNonLifetimeEndingUsers( - ArrayRef NonLifetimeEndingUsers) { - for (GeneralizedUser User : NonLifetimeEndingUsers) { - auto *UserBlock = User.getParent(); - // First try to associate User with User->getParent(). - auto Result = - BlocksWithNonLifetimeEndingUses.insert(std::make_pair(UserBlock, User)); - - // If the insertion succeeds, then we know that there is no more work to - // be done, so process the next use. - if (Result.second) - continue; - - // If the insertion fails, then we have at least two non-lifetime ending - // uses in the same block. Since we are performing a liveness type of - // dataflow, we only need the last non-lifetime ending use to show that all - // lifetime ending uses post dominate both. - // - // We begin by checking if the first use is a cond_br use from the previous - // block. In such a case, we always use the already stored value and - // continue. - if (User.isCondBranchUser()) { - continue; - } - - // Then, we check if Use is after Result.first->second in the use list. If - // Use is not later, then we wish to keep the already mapped value, not use, - // so continue. - if (std::find_if(Result.first->second.getIterator(), UserBlock->end(), - [&User](const SILInstruction &I) -> bool { - return User == &I; - }) == UserBlock->end()) { - continue; - } - - // At this point, we know that User is later in the Block than - // Result.first->second, so store Use instead. - Result.first->second = User; - } -} - bool SILValueOwnershipChecker::checkFunctionArgWithoutLifetimeEndingUses( SILFunctionArgument *Arg) { switch (Arg->getOwnershipKind()) { @@ -1931,7 +1677,8 @@ bool SILValueOwnershipChecker::checkValueWithoutLifetimeEndingUses() { bool SILValueOwnershipChecker::isGuaranteedFunctionArgWithLifetimeEndingUses( SILFunctionArgument *Arg, - const llvm::SmallVectorImpl &LifetimeEndingUsers) const { + const llvm::SmallVectorImpl &LifetimeEndingUsers) + const { if (Arg->getOwnershipKind() != ValueOwnershipKind::Guaranteed) return true; @@ -1948,7 +1695,8 @@ bool SILValueOwnershipChecker::isGuaranteedFunctionArgWithLifetimeEndingUses( bool SILValueOwnershipChecker::isSubobjectProjectionWithLifetimeEndingUses( SILValue Value, - const llvm::SmallVectorImpl &LifetimeEndingUsers) const { + const llvm::SmallVectorImpl &LifetimeEndingUsers) + const { return handleError([&] { llvm::errs() << " Function: '" << Value->getFunction()->getName() << "'\n" @@ -2011,198 +1759,6 @@ bool SILValueOwnershipChecker::checkUses() { } } - // Then add our non lifetime ending users and their blocks to the - // BlocksWithNonLifetimeEndingUses map. While we do this, if we have multiple - // uses in the same block, we only accept the last use since from a liveness - // perspective that is all we care about. - uniqueNonLifetimeEndingUsers(RegularUsers); - - // Finally, we go through each one of our lifetime ending users performing the - // following operation: - // - // 1. Verifying that no two lifetime ending users are in the same block. This - // is accomplished by adding the user blocks to the - // BlocksWithLifetimeEndingUses list. This avoids double consumes. - // - // 2. Verifying that no predecessor is a block with a lifetime ending use. The - // reason why this is necessary is because we wish to not add elements to the - // worklist twice. Thus we want to check if we have already visited a - // predecessor. - llvm::SmallVector, 32> - PredsToAddToWorklist; - for (GeneralizedUser User : LifetimeEndingUsers) { - SILBasicBlock *UserBlock = User.getParent(); - // If the block does over consume, we either assert or return false. We only - // return false when debugging. - if (doesBlockDoubleConsume(UserBlock, User, true)) { - return handleError([] {}); - } - - // Then check if the given block has a use after free. - if (doesBlockContainUseAfterFree(User, UserBlock)) { - return handleError([] {}); - } - - // If this user is in the same block as the value, do not visit - // predecessors. We must be extra tolerant here since we allow for - // unreachable code. - if (UserBlock == Value->getParentBlock()) - continue; - - // Then for each predecessor of this block... - for (auto *Pred : UserBlock->getPredecessorBlocks()) { - // If this block is not a block that we have already put on the list, add - // it to the worklist. - PredsToAddToWorklist.push_back({User, Pred}); - } - } - - for (const auto &I : LifetimeEndingUsers) { - // Finally add the user block to the visited list so we do not try to add it - // to our must visit successor list. - VisitedBlocks.insert(I.getParent()); - } - - // Make sure not to add predecessors to our worklist if we only have 1 - // lifetime ending user and it is in the same block as our def. - if (LifetimeEndingUsers.size() == 1 && - LifetimeEndingUsers[0].getParent() == Value->getParentBlock()) { - return true; - } - - // Now that we have marked all of our producing blocks, we go through our - // PredsToAddToWorklist list and add our preds, making sure that none of these - // preds are in BlocksWithLifetimeEndingUses. - for (auto Pair : PredsToAddToWorklist) { - GeneralizedUser User = Pair.first; - SILBasicBlock *PredBlock = Pair.second; - - // Make sure that the predecessor is not in our - // BlocksWithLifetimeEndingUses list. - if (doesBlockDoubleConsume(PredBlock, User)) { - return handleError([] {}); - } - - if (!VisitedBlocks.insert(PredBlock).second) - continue; - Worklist.push_back(PredBlock); - } - - return true; -} - -bool SILValueOwnershipChecker::checkDataflow() { - DEBUG(llvm::dbgs() << " Beginning to check dataflow constraints\n"); - // Until the worklist is empty... - while (!Worklist.empty()) { - // Grab the next block to visit. - SILBasicBlock *BB = Worklist.pop_back_val(); - DEBUG(llvm::dbgs() << " Visiting Block: bb" << BB->getDebugID() << '\n'); - - // Since the block is on our worklist, we know already that it is not a - // block with lifetime ending uses, due to the invariants of our loop. - - // First remove BB from the SuccessorBlocksThatMustBeVisited list. This - // ensures that when the algorithm terminates, we know that BB was not the - // beginning of a non-covered path to the exit. - SuccessorBlocksThatMustBeVisited.erase(BB); - - // Then remove BB from BlocksWithNonLifetimeEndingUses so we know that - // this block was properly joint post-dominated by our lifetime ending - // users. - BlocksWithNonLifetimeEndingUses.erase(BB); - - // Ok, now we know that we do not have an overconsume. If this block does - // not end in a no return function, we need to update our state for our - // successors to make sure by the end of the traversal we visit them. - // - // We must consider such no-return blocks since we may be running during - // SILGen before NoReturn folding has run. - for (SILBasicBlock *SuccBlock : BB->getSuccessorBlocks()) { - // If we already visited the successor, there is nothing to do since we - // already visited the successor. - if (VisitedBlocks.count(SuccBlock)) - continue; - - // Then check if the successor is a transitively unreachable block. In - // such a case, we ignore it since we are going to leak along that path. - if (DEBlocks.isDeadEnd(SuccBlock)) - continue; - - // Otherwise, add the successor to our SuccessorBlocksThatMustBeVisited - // set to ensure that we assert if we do not visit it by the end of the - // algorithm. - SuccessorBlocksThatMustBeVisited.insert(SuccBlock); - } - - // If we are at the dominating block of our walk, continue. There is nothing - // further to do since we do not want to visit the predecessors of our - // dominating block. On the other hand, we do want to add its successors to - // the SuccessorBlocksThatMustBeVisited set. - if (BB == Value->getParentBlock()) - continue; - - // Then for each predecessor of this block: - // - // 1. If we have visited the predecessor already, that it is not a block - // with lifetime ending uses. If it is a block with uses, then we have a - // double release... so assert. If not, we continue. - // - // 2. We add the predecessor to the worklist if we have not visited it yet. - for (auto *PredBlock : BB->getPredecessorBlocks()) { - if (doesBlockDoubleConsume(PredBlock)) { - return handleError([] {}); - } - - if (VisitedBlocks.count(PredBlock)) { - continue; - } - - VisitedBlocks.insert(PredBlock); - Worklist.push_back(PredBlock); - } - } - - // Make sure that we visited all successor blocks that we needed to visit to - // make sure we didn't leak. - if (!SuccessorBlocksThatMustBeVisited.empty()) { - return handleError([&] { - llvm::errs() - << "Function: '" << Value->getFunction()->getName() << "'\n" - << "Error! Found a leak due to a consuming post-dominance failure!\n" - << " Value: " << *Value << " Post Dominating Failure Blocks:\n"; - for (auto *BB : SuccessorBlocksThatMustBeVisited) { - llvm::errs() << " bb" << BB->getDebugID(); - } - llvm::errs() << '\n'; - }); - } - - // Make sure that we do not have any lifetime ending uses left to visit that - // are not transitively unreachable blocks. If we do, then these non lifetime - // ending uses must be outside of our "alive" blocks implying a use-after - // free. - if (!BlocksWithNonLifetimeEndingUses.empty()) { - for (auto &Pair : BlocksWithNonLifetimeEndingUses) { - if (DEBlocks.isDeadEnd(Pair.first)) { - continue; - } - - return handleError([&] { - llvm::errs() << "Function: '" << Value->getFunction()->getName() - << "'\n" - << "Found use after free due to unvisited non lifetime " - "ending uses?!\n" - << "Value: " << *Value << " Remaining Users:\n"; - for (auto &Pair : BlocksWithNonLifetimeEndingUses) { - llvm::errs() << "User:" << *Pair.second << "Block: bb" - << Pair.first->getDebugID() << "\n"; - } - llvm::errs() << "\n"; - }); - } - } - return true; } diff --git a/lib/SIL/SILPrinter.cpp b/lib/SIL/SILPrinter.cpp index 86e9e45b8b569..d76f16c4ec37f 100644 --- a/lib/SIL/SILPrinter.cpp +++ b/lib/SIL/SILPrinter.cpp @@ -372,14 +372,14 @@ void SILDeclRef::dump() const { static void printGenericSpecializationInfo( raw_ostream &OS, StringRef Kind, StringRef Name, const GenericSpecializationInformation *SpecializationInfo, - SubstitutionList Subs = SubstitutionList()) { + SubstitutionMap Subs = { }) { if (!SpecializationInfo) return; - auto PrintSubstitutions = [&](SubstitutionList Subs) { + auto PrintSubstitutions = [&](SubstitutionMap Subs) { OS << '<'; - interleave(Subs, - [&](const Substitution &s) { OS << s.getReplacement(); }, + interleave(Subs.getReplacementTypes(), + [&](Type type) { OS << type; }, [&] { OS << ", "; }); OS << '>'; }; @@ -436,7 +436,7 @@ void SILType::print(raw_ostream &OS) const { // Print other types as their Swift representation. PrintOptions SubPrinter = PrintOptions::printSIL(); - getSwiftRValueType().print(OS, SubPrinter); + getASTType().print(OS, SubPrinter); } void SILType::dump() const { @@ -496,7 +496,7 @@ class SILPrinter : public SILInstructionVisitor { SILPrinter &operator<<(SILType t) { printSILTypeColorAndSigil(PrintState.OS, t); - t.getSwiftRValueType().print(PrintState.OS, PrintState.ASTOptions); + t.getASTType().print(PrintState.OS, PrintState.ASTOptions); return *this; } @@ -638,7 +638,7 @@ class SILPrinter : public SILInstructionVisitor { if (AI.getSpecializationInfo() && AI.getCalleeFunction()) printGenericSpecializationInfo( PrintState.OS, "call-site", AI.getCalleeFunction()->getName(), - AI.getSpecializationInfo(), AI.getSubstitutions()); + AI.getSpecializationInfo(), AI.getSubstitutionMap()); } print(&I); } @@ -1065,21 +1065,30 @@ class SILPrinter : public SILInstructionVisitor { printDebugVar(ABI->getVarInfo()); } - void printSubstitutions(SubstitutionList Subs) { - if (Subs.empty()) - return; - + void printSubstitutions(SubstitutionMap Subs, + GenericSignature *Sig = nullptr) { + if (!Subs.hasAnySubstitutableParams()) return; + + // FIXME: This is a hack to cope with cases where the substitution map uses + // a generic signature that's close-to-but-not-the-same-as expected. + auto genericSig = Sig ? Sig : Subs.getGenericSignature(); + *this << '<'; - interleave(Subs, - [&](const Substitution &s) { *this << s.getReplacement(); }, - [&] { *this << ", "; }); + bool first = true; + for (auto gp : genericSig->getGenericParams()) { + if (first) first = false; + else *this << ", "; + + *this << Type(gp).subst(Subs); + } *this << '>'; } template void visitApplyInstBase(Inst *AI) { *this << Ctx.getID(AI->getCallee()); - printSubstitutions(AI->getSubstitutions()); + printSubstitutions(AI->getSubstitutionMap(), + AI->getOrigCalleeType()->getGenericSignature()); *this << '('; interleave(AI->getArguments(), [&](const SILValue &arg) { *this << Ctx.getID(arg); }, @@ -1436,6 +1445,7 @@ class SILPrinter : public SILInstructionVisitor { } void visitConvertEscapeToNoEscapeInst(ConvertEscapeToNoEscapeInst *CI) { *this << (CI->isLifetimeGuaranteed() ? "" : "[not_guaranteed] ") + << (CI->isEscapedByUser() ? "[escaped] " : "") << getIDAndType(CI->getOperand()) << " to " << CI->getType(); } void visitThinFunctionToPointerInst(ThinFunctionToPointerInst *CI) { @@ -1780,6 +1790,10 @@ class SILPrinter : public SILInstructionVisitor { void visitCopyBlockInst(CopyBlockInst *RI) { *this << getIDAndType(RI->getOperand()); } + void visitCopyBlockWithoutEscapingInst(CopyBlockWithoutEscapingInst *RI) { + *this << getIDAndType(RI->getBlock()) << " withoutEscaping " + << getIDAndType(RI->getClosure()); + } void visitRefCountingInst(RefCountingInst *I) { if (I->isNonAtomic()) *this << "[nonatomic] "; @@ -1797,6 +1811,8 @@ class SILPrinter : public SILInstructionVisitor { *this << getIDAndType(CUI->getOperand()); } void visitIsEscapingClosureInst(IsEscapingClosureInst *CUI) { + if (CUI->getVerificationType()) + *this << "[objc] "; *this << getIDAndType(CUI->getOperand()); } void visitDeallocStackInst(DeallocStackInst *DI) { @@ -1835,6 +1851,7 @@ class SILPrinter : public SILInstructionVisitor { *this << '[' << getSILAccessKindName(BAI->getAccessKind()) << "] [" << getSILAccessEnforcementName(BAI->getEnforcement()) << "] " << (BAI->hasNoNestedConflict() ? "[no_nested_conflict] " : "") + << (BAI->isFromBuiltin() ? "[builtin] " : "") << getIDAndType(BAI->getOperand()); } void visitEndAccessInst(EndAccessInst *EAI) { @@ -1845,12 +1862,14 @@ class SILPrinter : public SILInstructionVisitor { *this << '[' << getSILAccessKindName(BAI->getAccessKind()) << "] [" << getSILAccessEnforcementName(BAI->getEnforcement()) << "] " << (BAI->hasNoNestedConflict() ? "[no_nested_conflict] " : "") + << (BAI->isFromBuiltin() ? "[builtin] " : "") << getIDAndType(BAI->getSource()) << ", " << getIDAndType(BAI->getBuffer()); } void visitEndUnpairedAccessInst(EndUnpairedAccessInst *EAI) { - *this << (EAI->isAborting() ? "[abort] " : "") - << '[' << getSILAccessEnforcementName(EAI->getEnforcement()) << "] " + *this << (EAI->isAborting() ? "[abort] " : "") << '[' + << getSILAccessEnforcementName(EAI->getEnforcement()) << "] " + << (EAI->isFromBuiltin() ? "[builtin] " : "") << getIDAndType(EAI->getOperand()); } @@ -2215,6 +2234,14 @@ void SILBasicBlock::dump() const { /// Pretty-print the SILBasicBlock to the designated stream. void SILBasicBlock::print(raw_ostream &OS) const { SILPrintContext Ctx(OS); + + // Print the debug scope (and compute if we didn't do it already). + auto &SM = this->getParent()->getModule().getASTContext().SourceMgr; + for (auto &I : *this) { + SILPrinter P(Ctx); + P.printDebugScope(I.getDebugScope(), SM); + } + SILPrinter(Ctx).print(this); } @@ -2589,17 +2616,17 @@ printSILDefaultWitnessTables(SILPrintContext &Ctx, static void printSILCoverageMaps(SILPrintContext &Ctx, - const SILModule::CoverageMapListType &CoverageMaps) { + const SILModule::CoverageMapCollectionType &CoverageMaps) { if (!Ctx.sortSIL()) { - for (const SILCoverageMap &M : CoverageMaps) - M.print(Ctx); + for (const auto &M : CoverageMaps) + M.second->print(Ctx); return; } std::vector Maps; Maps.reserve(CoverageMaps.size()); - for (const SILCoverageMap &M : CoverageMaps) - Maps.push_back(&M); + for (const auto &M : CoverageMaps) + Maps.push_back(M.second); std::sort(Maps.begin(), Maps.end(), [](const SILCoverageMap *LHS, const SILCoverageMap *RHS) -> bool { return LHS->getName().compare(RHS->getName()) == -1; @@ -2681,7 +2708,7 @@ void SILModule::print(SILPrintContext &PrintCtx, ModuleDecl *M, if (!WholeModuleMode && !(D->getDeclContext() == AssociatedDeclContext)) continue; if ((isa(D) || isa(D) || - isa(D)) && + isa(D) || isa(D)) && !D->isImplicit()) { if (isa(D)) continue; @@ -2696,7 +2723,7 @@ void SILModule::print(SILPrintContext &PrintCtx, ModuleDecl *M, printSILVTables(PrintCtx, getVTableList()); printSILWitnessTables(PrintCtx, getWitnessTableList()); printSILDefaultWitnessTables(PrintCtx, getDefaultWitnessTableList()); - printSILCoverageMaps(PrintCtx, getCoverageMapList()); + printSILCoverageMaps(PrintCtx, getCoverageMaps()); printSILProperties(PrintCtx, getPropertyList()); OS << "\n\n"; @@ -2923,9 +2950,9 @@ void SILDefaultWitnessTable::dump() const { void SILCoverageMap::print(SILPrintContext &PrintCtx) const { llvm::raw_ostream &OS = PrintCtx.OS(); - OS << "sil_coverage_map " << QuotedString(getFile()) << " " << getName() - << " " << QuotedString(getPGOFuncName()) << " " << getHash() << " {\t// " - << demangleSymbol(getName()) << "\n"; + OS << "sil_coverage_map " << QuotedString(getFile()) << " " + << QuotedString(getName()) << " " << QuotedString(getPGOFuncName()) << " " + << getHash() << " {\t// " << demangleSymbol(getName()) << "\n"; if (PrintCtx.sortSIL()) std::sort(MappedRegions.begin(), MappedRegions.end(), [](const MappedRegion &LHS, const MappedRegion &RHS) { @@ -2990,9 +3017,12 @@ void SILSpecializeAttr::print(llvm::raw_ostream &OS) const { SILFunction *F = getFunction(); assert(F); auto GenericEnv = F->getGenericEnvironment(); - assert(GenericEnv); interleave(getRequirements(), [&](Requirement req) { + if (!GenericEnv) { + req.print(OS, SubPrinter); + return; + } // Use GenericEnvironment to produce user-friendly // names instead of something like t_0_0. auto FirstTy = GenericEnv->getSugaredType(req.getFirstType()); diff --git a/lib/SIL/SILProfiler.cpp b/lib/SIL/SILProfiler.cpp index 4193a58db3ce3..1509b15a1e035 100644 --- a/lib/SIL/SILProfiler.cpp +++ b/lib/SIL/SILProfiler.cpp @@ -13,6 +13,9 @@ #include "swift/SIL/SILProfiler.h" #include "swift/AST/ASTWalker.h" #include "swift/AST/Decl.h" +#include "swift/AST/Expr.h" +#include "swift/AST/Module.h" +#include "swift/AST/Stmt.h" #include "swift/Parse/Lexer.h" #include "swift/SIL/FormalLinkage.h" #include "swift/SIL/SILModule.h" @@ -24,96 +27,106 @@ #include +#define DEBUG_TYPE "SILProfiler" + using namespace swift; +/// Check if a closure has a body. +static bool doesClosureHaveBody(AbstractClosureExpr *ACE) { + if (auto *CE = dyn_cast(ACE)) + return CE->getBody(); + if (auto *autoCE = dyn_cast(ACE)) + return autoCE->getBody(); + return false; +} + +/// Check whether a root AST node is unmapped, i.e not profiled. static bool isUnmapped(ASTNode N) { if (auto *E = N.dyn_cast()) { - auto *CE = dyn_cast(E); - return !CE || CE->isImplicit() || !CE->getBody(); + auto *CE = dyn_cast(E); + + // Only map closure expressions with bodies. + if (!CE || !doesClosureHaveBody(CE)) + return true; + + // Don't map implicit closures, unless they're autoclosures. + if (!isa(CE) && CE->isImplicit()) + return true; + + return false; } auto *D = N.get(); if (auto *AFD = dyn_cast(D)) { + // Don't map functions without bodies. if (!AFD->getBody()) return true; + // Map all *structors, even if they are implicit. + if (isa(D) || isa(D)) + return false; + + // Map implicit getters. if (auto *accessor = dyn_cast(AFD)) if (accessor->isImplicit() && accessor->isGetter()) return false; } - if (isa(D) || isa(D)) - return false; + // Skip any remaining implicit, or otherwise unsupported decls. + if (D->isImplicit() || isa(D)) + return true; - return D->isImplicit() || isa(D); + return false; } -/// A simple heuristic to determine whether \p E contains a definition of a -/// closure. This is not complete, but it suffices to cheaply filter away some -/// redundant coverage mappings. -static bool containsClosure(Expr *E) { - Expr *candidateExpr = E; - if (auto *ce = dyn_cast(E)) - candidateExpr = ce->getDirectCallee(); - return dyn_cast_or_null(candidateExpr); +namespace swift { +bool doesASTRequireProfiling(SILModule &M, ASTNode N) { + return M.getOptions().GenerateProfile && !isUnmapped(N); } - -/// Walk the non-static initializers in \p PBD. -static void walkPatternForProfiling(PatternBindingDecl *PBD, ASTWalker &Walker, - bool AllowClosures = true) { - if (PBD && !PBD->isStatic()) - for (auto E : PBD->getPatternList()) - if (E.getInit()) - if (AllowClosures || !containsClosure(E.getInit())) - E.getInit()->walk(Walker); +} // namespace swift + +/// Check that the input AST has at least been type-checked. +static bool hasASTBeenTypeChecked(ASTNode N) { + DeclContext *DC = N.getAsDeclContext(); + assert(DC && "Invalid AST node for profiling"); + SourceFile *SF = DC->getParentSourceFile(); + return !SF || SF->ASTStage >= SourceFile::TypeChecked; } -/// Walk the AST of \c Root and related nodes that are relevant for profiling. -static void walkFunctionForProfiling(AbstractFunctionDecl *Root, - ASTWalker &Walker) { - Root->walk(Walker); - - // We treat non-closure member initializers as part of the constructor for - // profiling. - if (auto *CD = dyn_cast(Root)) { - auto *NominalType = - CD->getDeclContext()->getAsNominalTypeOrNominalTypeExtensionContext(); - for (auto *Member : NominalType->getMembers()) { - // Find pattern binding declarations that have initializers. - if (auto *PBD = dyn_cast(Member)) - walkPatternForProfiling(PBD, Walker, /*AllowClosures=*/false); - } - } -} +/// Check whether a mapped AST node requires a new profiler. +static bool canCreateProfilerForAST(ASTNode N) { + assert(hasASTBeenTypeChecked(N) && "Cannot use this AST for profiling"); -/// Walk \p D for profiling. -static void walkForProfiling(ASTNode N, ASTWalker &Walker) { if (auto *D = N.dyn_cast()) { if (auto *AFD = dyn_cast(D)) - walkFunctionForProfiling(AFD, Walker); - else if (auto *PBD = dyn_cast(D)) - walkPatternForProfiling(PBD, Walker); - else if (auto *TLCD = dyn_cast(D)) - TLCD->walk(Walker); - } else if (auto *E = N.dyn_cast()) { - cast(E)->walk(Walker); - } -} + return true; -SILProfiler *SILProfiler::create(SILModule &M, ASTNode N) { - if (auto *D = N.dyn_cast()) { - assert(isa(D) || - isa(D) && "Cannot create profiler"); - } else if (auto *E = N.dyn_cast()) { - assert(isa(E) && "Cannot create profiler"); + if (isa(D)) + return true; + + if (isa(D)) + return true; } else { - llvm_unreachable("Invalid AST node for profiling"); + auto *E = N.get(); + if (isa(E)) + return true; } + return false; +} + +SILProfiler *SILProfiler::create(SILModule &M, ForDefinition_t forDefinition, + ASTNode N) { + // Avoid generating profiling state for declarations. + if (!forDefinition) + return nullptr; const auto &Opts = M.getOptions(); - if ((!Opts.GenerateProfile && Opts.UseProfile.empty()) || isUnmapped(N)) + if (!doesASTRequireProfiling(M, N) && Opts.UseProfile.empty()) return nullptr; + if (!canCreateProfilerForAST(N)) + llvm_unreachable("Invalid AST node for profiling"); + auto *Buf = M.allocate(1); auto *SP = ::new (Buf) SILProfiler(M, N, Opts.EmitProfileCoverageMapping); SP->assignRegionCounters(); @@ -122,56 +135,129 @@ SILProfiler *SILProfiler::create(SILModule &M, ASTNode N) { namespace { +/// Walk the non-static initializers in \p PBD. +static void walkPatternForProfiling(PatternBindingDecl *PBD, + ASTWalker &Walker) { + if (PBD && !PBD->isStatic()) + for (auto E : PBD->getPatternList()) + if (E.getInit()) + E.getInit()->walk(Walker); +} + +/// Special logic for handling closure visitation. +/// +/// To prevent a closure from being mapped twice, avoid recursively walking +/// into one unless the closure's function definition is being profiled. +/// +/// Apply \p Func if the closure can be visited. +template +std::pair visitClosureExpr(ASTWalker &Walker, + AbstractClosureExpr *CE, F Func) { + if (!Walker.Parent.isNull()) + return {false, CE}; + Func(); + return {true, CE}; +} + +/// Special logic for handling function visitation. +/// +/// To avoid creating duplicate mappings, a function decl is only profiled if +/// it hasn't been reached via recursive walk, or if it's a constructor for a +/// nominal type (these are profiled in a group). +/// +/// Apply \p Func is the function can be visited. +template +bool visitFunctionDecl(ASTWalker &Walker, AbstractFunctionDecl *AFD, F Func) { + bool continueWalk = Walker.Parent.isNull() || isa(AFD); + if (continueWalk) + Func(); + return continueWalk; +} + +/// Special logic for handling nominal type visitation. +/// +/// Apply \p Func if the nominal type can be visited (i.e it has not been +/// reached via recursive walk). +template +bool visitNominalTypeDecl(ASTWalker &Walker, NominalTypeDecl *NTD, F Func) { + bool continueWalk = Walker.Parent.isNull(); + if (continueWalk) + Func(); + return continueWalk; +} + /// An ASTWalker that maps ASTNodes to profiling counters. struct MapRegionCounters : public ASTWalker { /// The next counter value to assign. - unsigned NextCounter; + unsigned NextCounter = 0; /// The map of statements to counters. llvm::DenseMap &CounterMap; + /// A flag indicating whether we're walking a nominal type. + bool WithinNominalType = false; + MapRegionCounters(llvm::DenseMap &CounterMap) - : NextCounter(0), CounterMap(CounterMap) {} + : CounterMap(CounterMap) {} + + void mapRegion(ASTNode N) { + CounterMap[N] = NextCounter; + + DEBUG({ + llvm::dbgs() << "Assigned counter #" << NextCounter << " to: "; + auto *E = N.dyn_cast(); + if (E) + llvm::dbgs() << Expr::getKindName(E->getKind()) << "\n"; + auto *S = N.dyn_cast(); + if (S) + llvm::dbgs() << Stmt::getKindName(S->getKind()) << "\n"; + }); + + ++NextCounter; + } bool walkToDeclPre(Decl *D) override { if (isUnmapped(D)) return false; - if (auto *AFD = dyn_cast(D)) - CounterMap[AFD->getBody()] = NextCounter++; - if (auto *TLCD = dyn_cast(D)) - CounterMap[TLCD->getBody()] = NextCounter++; + + if (auto *AFD = dyn_cast(D)) { + return visitFunctionDecl(*this, AFD, [&] { mapRegion(AFD->getBody()); }); + } else if (auto *TLCD = dyn_cast(D)) { + mapRegion(TLCD->getBody()); + } else if (auto *NTD = dyn_cast(D)) { + return visitNominalTypeDecl(*this, NTD, + [&] { WithinNominalType = true; }); + } return true; } std::pair walkToStmtPre(Stmt *S) override { if (auto *IS = dyn_cast(S)) { - CounterMap[IS->getThenStmt()] = NextCounter++; + mapRegion(IS->getThenStmt()); } else if (auto *US = dyn_cast(S)) { - CounterMap[US->getBody()] = NextCounter++; + mapRegion(US->getBody()); } else if (auto *WS = dyn_cast(S)) { - CounterMap[WS->getBody()] = NextCounter++; + mapRegion(WS->getBody()); } else if (auto *RWS = dyn_cast(S)) { - CounterMap[RWS->getBody()] = NextCounter++; + mapRegion(RWS->getBody()); } else if (auto *FES = dyn_cast(S)) { - CounterMap[FES->getBody()] = NextCounter++; + mapRegion(FES->getBody()); walkPatternForProfiling(FES->getIterator(), *this); } else if (auto *SS = dyn_cast(S)) { - CounterMap[SS] = NextCounter++; + mapRegion(SS); } else if (auto *CS = dyn_cast(S)) { - CounterMap[CS] = NextCounter++; - } else if (auto *DCS = dyn_cast(S)) { - CounterMap[DCS] = NextCounter++; + mapRegion(CS); } else if (auto *CS = dyn_cast(S)) { - CounterMap[CS->getBody()] = NextCounter++; + mapRegion(CS->getBody()); } return {true, S}; } std::pair walkToExprPre(Expr *E) override { if (auto *IE = dyn_cast(E)) { - CounterMap[IE->getThenExpr()] = NextCounter++; - } else if (isa(E) || isa(E)) { - CounterMap[E] = NextCounter++; + mapRegion(IE->getThenExpr()); + } else if (auto *ACE = dyn_cast(E)) { + return visitClosureExpr(*this, ACE, [&] { mapRegion(ACE); }); } return {true, E}; } @@ -255,6 +341,33 @@ class CounterExpr { llvm_unreachable("Unhandled Kind in switch."); } + + void print(raw_ostream &OS) const { + switch (K) { + case Kind::Zero: + OS << "zero"; + return; + case Kind::Node: + OS << "node(" << Node.getOpaqueValue() << ")"; + return; + case Kind::Add: + case Kind::Sub: + LHS->print(OS); + OS << ' ' << ((K == Kind::Add) ? '+' : '-') << ' '; + RHS->print(OS); + return; + case Kind::Ref: + OS << "ref("; + LHS->print(OS); + OS << ")"; + return; + } + llvm_unreachable("Unhandled Kind in switch."); + } + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + LLVM_DUMP_METHOD void dump() const { print(llvm::errs()); } +#endif }; /// \brief A region of source code that can be mapped to a counter. @@ -361,10 +474,12 @@ struct PGOMapping : public ASTWalker { if (isUnmapped(D)) return false; if (auto *AFD = dyn_cast(D)) { - auto node = AFD->getBody(); - CounterMap[node] = NextCounter++; - auto count = loadExecutionCount(node); - LoadedCounterMap[node] = count; + return visitFunctionDecl(*this, AFD, [&] { + auto node = AFD->getBody(); + CounterMap[node] = NextCounter++; + auto count = loadExecutionCount(node); + LoadedCounterMap[node] = count; + }); } if (auto *TLCD = dyn_cast(D)) { auto node = TLCD->getBody(); @@ -372,6 +487,9 @@ struct PGOMapping : public ASTWalker { auto count = loadExecutionCount(node); LoadedCounterMap[node] = count; } + if (auto *NTD = dyn_cast(D)) { + return visitNominalTypeDecl(*this, NTD, [&] {}); + } return true; } @@ -445,10 +563,6 @@ struct PGOMapping : public ASTWalker { CounterMap[CS] = NextCounter++; auto csCount = loadExecutionCount(CS); LoadedCounterMap[CS] = csCount; - } else if (auto *DCS = dyn_cast(S)) { - CounterMap[DCS] = NextCounter++; - auto dcsCount = loadExecutionCount(DCS); - LoadedCounterMap[DCS] = dcsCount; } else if (auto *CS = dyn_cast(S)) { auto csBody = CS->getBody(); CounterMap[csBody] = NextCounter++; @@ -465,25 +579,27 @@ struct PGOMapping : public ASTWalker { CounterMap[thenExpr] = NextCounter++; auto thenCount = loadExecutionCount(thenExpr); LoadedCounterMap[thenExpr] = thenCount; - if (auto elseExpr = IE->getElseExpr()) { - CounterMap[elseExpr] = parent; - auto count = loadExecutionCount(elseExpr); - if (!parent) { - auto thenVal = thenCount.getValue(); - for (auto pCount = NextCounter - 1; pCount > 0; --pCount) { - auto cCount = LoadedCounts->Counts[pCount]; - if (cCount > thenVal) { - count = cCount; - break; - } + auto elseExpr = IE->getElseExpr(); + assert(elseExpr && "An if-expr must have an else subexpression"); + CounterMap[elseExpr] = parent; + auto count = loadExecutionCount(elseExpr); + if (!parent) { + auto thenVal = thenCount.getValue(); + for (auto pCount = NextCounter - 1; pCount > 0; --pCount) { + auto cCount = LoadedCounts->Counts[pCount]; + if (cCount > thenVal) { + count = cCount; + break; } } - LoadedCounterMap[elseExpr] = subtract(count, thenCount); } - } else if (isa(E) || isa(E)) { - CounterMap[E] = NextCounter++; - auto eCount = loadExecutionCount(E); - LoadedCounterMap[E] = eCount; + LoadedCounterMap[elseExpr] = subtract(count, thenCount); + } else if (auto *ACE = dyn_cast(E)) { + return visitClosureExpr(*this, ACE, [&] { + CounterMap[E] = NextCounter++; + auto eCount = loadExecutionCount(E); + LoadedCounterMap[E] = eCount; + }); } return {true, E}; } @@ -508,7 +624,14 @@ struct CoverageMapping : public ASTWalker { /// \brief A stack of active repeat-while loops. std::vector RepeatWhileStack; - CounterExpr *ExitCounter; + /// \brief A stack of active do-catch statements. + std::vector DoCatchStack; + + CounterExpr *ExitCounter = nullptr; + + Stmt *ImplicitTopLevelBody = nullptr; + + NominalTypeDecl *ParentNominalType = nullptr; /// \brief Return true if \c Node has an associated counter. bool hasCounter(ASTNode Node) { return CounterMap.count(Node); } @@ -710,15 +833,35 @@ struct CoverageMapping : public ASTWalker { bool walkToDeclPre(Decl *D) override { if (isUnmapped(D)) return false; - if (auto *AFD = dyn_cast(D)) - assignCounter(AFD->getBody()); - else if (auto *TLCD = dyn_cast(D)) + + if (auto *AFD = dyn_cast(D)) { + return visitFunctionDecl(*this, AFD, [&] { + CounterExpr &funcCounter = assignCounter(AFD->getBody()); + + if (ParentNominalType && isa(AFD)) + addToCounter(ParentNominalType, funcCounter); + }); + } else if (auto *TLCD = dyn_cast(D)) { assignCounter(TLCD->getBody()); + ImplicitTopLevelBody = TLCD->getBody(); + } else if (auto *NTD = dyn_cast(D)) { + return visitNominalTypeDecl(*this, NTD, [&] { + ParentNominalType = NTD; + assignCounter(NTD, CounterExpr::Zero()); + pushRegion(NTD); + }); + } + return true; + } + + bool walkToDeclPost(Decl *D) override { + if (isa(D)) + ImplicitTopLevelBody = nullptr; return true; } std::pair walkToStmtPre(Stmt *S) override { - if (S->isImplicit()) + if (S->isImplicit() && S != ImplicitTopLevelBody) return {true, S}; if (!RegionStack.empty()) @@ -769,17 +912,23 @@ struct CoverageMapping : public ASTWalker { assignCounter(DS); } else if (auto *DCS = dyn_cast(S)) { + // The do-catch body is visited the same number of times as its parent. assignCounter(DCS->getBody(), CounterExpr::Ref(getCurrentCounter())); - assignCounter(DCS); + + // Initialize the exit count of the do-catch to the entry count, then + // subtract off non-local exits as they are visited. + assignCounter(DCS, CounterExpr::Ref(getCurrentCounter())); + DoCatchStack.push_back(DCS); } else if (auto *CS = dyn_cast(S)) { + assert(DoCatchStack.size() && "catch stmt with no parent"); assignCounter(CS->getBody()); } return {true, S}; } Stmt *walkToStmtPost(Stmt *S) override { - if (S->isImplicit()) + if (S->isImplicit() && S != ImplicitTopLevelBody) return S; if (isa(S)) { @@ -811,11 +960,17 @@ struct CoverageMapping : public ASTWalker { } else if (auto *BS = dyn_cast(S)) { // When we break from a loop, we need to adjust the exit count. - if (auto *RWS = dyn_cast(BS->getTarget())) { + Stmt *BreakTarget = BS->getTarget(); + if (auto *RWS = dyn_cast(BreakTarget)) { subtractFromCounter(RWS->getCond(), getCurrentCounter()); - } else if (!isa(BS->getTarget())) { + } else if (!isa(BreakTarget)) { addToCounter(BS->getTarget(), getCurrentCounter()); } + + // The break also affects the exit counts of active do-catch statements. + for (auto *DCS : DoCatchStack) + subtractFromCounter(DCS, getCurrentCounter()); + terminateRegion(S); } else if (auto *FS = dyn_cast(S)) { @@ -828,13 +983,23 @@ struct CoverageMapping : public ASTWalker { } else if (isa(S)) { popRegions(S); - } else if (isa(S)) { + } else if (auto *DCS = dyn_cast(S)) { + assert(DoCatchStack.back() == DCS && "Malformed do-catch stack"); + DoCatchStack.pop_back(); replaceCount(CounterExpr::Ref(getCounter(S)), getEndLoc(S)); + } else if (isa(S)) { + assert(DoCatchStack.size() && "catch stmt with no parent"); + } else if (isa(S) || isa(S) || isa(S)) { - // When we return, we may need to adjust some loop condition counts. - for (auto *RWS : RepeatWhileStack) - subtractFromCounter(RWS->getCond(), getCurrentCounter()); + // When we return, adjust loop condition counts and do-catch exit counts + // to reflect the early exit. + if (isa(S) || isa(S)) { + for (auto *RWS : RepeatWhileStack) + subtractFromCounter(RWS->getCond(), getCurrentCounter()); + for (auto *DCS : DoCatchStack) + subtractFromCounter(DCS, getCurrentCounter()); + } terminateRegion(S); } @@ -845,23 +1010,14 @@ struct CoverageMapping : public ASTWalker { if (!RegionStack.empty()) extendRegion(E); - if (isa(E)) { - // Autoclosures look strange if there isn't a region, since it looks like - // control flow starts partway through an expression. For now we skip - // these so we don't get odd behavior in default arguments and the like, - // but in the future we should consider creating appropriate regions for - // those expressions. - if (!RegionStack.empty()) - assignCounter(E); - } else if (isa(E)) { - assignCounter(E); + if (auto *ACE = dyn_cast(E)) { + auto Result = visitClosureExpr(*this, ACE, [&] { assignCounter(ACE); }); + if (!Result.first) + return Result; } else if (auto *IE = dyn_cast(E)) { CounterExpr &ThenCounter = assignCounter(IE->getThenExpr()); - if (RegionStack.empty()) - assignCounter(IE->getElseExpr()); - else - assignCounter(IE->getElseExpr(), - CounterExpr::Sub(getCurrentCounter(), ThenCounter)); + assignCounter(IE->getElseExpr(), + CounterExpr::Sub(getCurrentCounter(), ThenCounter)); } if (hasCounter(E)) @@ -920,23 +1076,24 @@ void SILProfiler::assignRegionCounters() { TLCD->getStartLoc().printLineAndColumn(OS, SM); CurrentFuncLinkage = FormalLinkage::HiddenUnique; } else { - llvm_unreachable("Unsupported decl"); - } - } else { - auto *E = Root.get(); - if (auto *CE = dyn_cast(E)) { - CurrentFuncName = SILDeclRef(CE).mangle(); + auto *NTD = cast(D); + llvm::raw_string_ostream OS{CurrentFuncName}; + OS << "__ntd_" << NTD->getNameStr() << "_"; + NTD->getStartLoc().printLineAndColumn(OS, SM); CurrentFuncLinkage = FormalLinkage::HiddenUnique; - } else { - llvm_unreachable("Unsupported expr"); } + } else { + auto *CE = cast(Root.get()); + CurrentFuncName = SILDeclRef(CE).mangle(); + CurrentFuncLinkage = FormalLinkage::HiddenUnique; } PGOFuncName = llvm::getPGOFuncName( CurrentFuncName, getEquivalentPGOLinkage(CurrentFuncLinkage), CurrentFileName); - walkForProfiling(Root, Mapper); + DEBUG(llvm::dbgs() << "Assigning counters to: " << CurrentFuncName << "\n"); + Root.walk(Mapper); NumRegionCounters = Mapper.NextCounter; // TODO: Mapper needs to calculate a function hash as it goes. @@ -944,7 +1101,7 @@ void SILProfiler::assignRegionCounters() { if (EmitCoverageMapping) { CoverageMapping Coverage(SM); - walkForProfiling(Root, Coverage); + Root.walk(Coverage); CovMap = Coverage.emitSourceRegions(M, CurrentFuncName, PGOFuncName, PGOFuncHash, RegionCounterMap, CurrentFileName); @@ -962,7 +1119,7 @@ void SILProfiler::assignRegionCounters() { } PGOMapping pgoMapper(RegionLoadedCounterMap, LoadedCounts, RegionCondToParentMap); - walkForProfiling(Root, pgoMapper); + Root.walk(pgoMapper); } } @@ -987,10 +1144,3 @@ Optional SILProfiler::getPGOParent(ASTNode Node) { } return it->getSecond(); } - -void SILProfiler::recordCounterUpdate() { - // If a counter update is recorded, the profile symbol table is guaranteed - // to have name data needed by the coverage mapping. - if (CovMap) - CovMap->setSymtabEntryGuaranteed(); -} diff --git a/lib/SIL/SILType.cpp b/lib/SIL/SILType.cpp index 5ee4f7a5632bf..1a9cf831d0486 100644 --- a/lib/SIL/SILType.cpp +++ b/lib/SIL/SILType.cpp @@ -66,9 +66,9 @@ SILType SILType::getBuiltinWordType(const ASTContext &C) { } SILType SILType::getOptionalType(SILType type) { - auto &ctx = type.getSwiftRValueType()->getASTContext(); + auto &ctx = type.getASTContext(); auto optType = BoundGenericEnumType::get(ctx.getOptionalDecl(), Type(), - { type.getSwiftRValueType() }); + { type.getASTType() }); return getPrimitiveType(CanType(optType), type.getCategory()); } @@ -85,7 +85,7 @@ bool SILType::isReferenceCounted(SILModule &M) const { } bool SILType::isNoReturnFunction() const { - if (auto funcTy = dyn_cast(getSwiftRValueType())) + if (auto funcTy = dyn_cast(getASTType())) return funcTy->isNoReturnFunction(); return false; @@ -101,216 +101,29 @@ std::string SILType::getAsString() const { bool SILType::isPointerSizeAndAligned() { auto &C = getASTContext(); if (isHeapObjectReferenceType() - || getSwiftRValueType()->isEqual(C.TheRawPointerType)) { + || getASTType()->isEqual(C.TheRawPointerType)) { return true; } - if (auto intTy = dyn_cast(getSwiftRValueType())) + if (auto intTy = dyn_cast(getASTType())) return intTy->getWidth().isPointerWidth(); return false; } -// Allow casting a struct by value when all elements in toType correspond to -// an element of the same size or larger laid out in the same order in -// fromType. The assumption is that if fromType has larger elements, or -// additional elements, their presence cannot induce a more compact layout of -// the overlapping elements. -// -// struct {A, B} -> A is castable -// struct {A, B, C} -> struct {A, B} is castable -// struct { struct {A, B}, C} -> struct {A, B} is castable -// struct { A, B, C} -> struct { struct {A, B}, C} is NOT castable -// -// FIXME: This is unnecessarily conservative given the current ABI -// (TypeLayout.rst). It would be simpler to flatten both `from` and `to` types, -// exploding all structs and tuples, then trivially check if `to` is a prefix. -static bool canUnsafeCastStruct(SILType fromType, StructDecl *fromStruct, - SILType toType, SILModule &M) { - auto fromRange = fromStruct->getStoredProperties(); - if (fromRange.begin() == fromRange.end()) - return false; - - // Can the first element of fromStruct be cast by value into toType? - SILType fromEltTy = fromType.getFieldType(*fromRange.begin(), M); - if (SILType::canPerformABICompatibleUnsafeCastValue(fromEltTy, toType, M)) - return true; - - // Otherwise, flatten one level of struct elements on each side. - StructDecl *toStruct = toType.getStructOrBoundGenericStruct(); - if (!toStruct) - return false; - - auto toRange = toStruct->getStoredProperties(); - for (auto toI = toRange.begin(), toE = toRange.end(), - fromI = fromRange.begin(), fromE = fromRange.end(); - toI != toE; ++toI, ++fromI) { - - if (fromI == fromE) - return false; // fromType is a struct with fewer elements. - - SILType fromEltTy = fromType.getFieldType(*fromI, M); - SILType toEltTy = toType.getFieldType(*toI, M); - if (!SILType::canPerformABICompatibleUnsafeCastValue(fromEltTy, toEltTy, M)) - return false; - } - // fromType's overlapping elements are compatible. - return true; -} - -// Allow casting a tuple by value when all elements in toType correspond to an -// element of the same size or larger in fromType in the same order. -static bool canUnsafeCastTuple(SILType fromType, CanTupleType fromTupleTy, - SILType toType, SILModule &M) { - unsigned numFromElts = fromTupleTy->getNumElements(); - // Can the first element of fromTupleTy be cast by value into toType? - if (numFromElts != 0 - && SILType::canPerformABICompatibleUnsafeCastValue( - fromType.getTupleElementType(0), toType, M)) { - return true; - } - // Otherwise, flatten one level of tuple elements on each side. - auto toTupleTy = dyn_cast(toType.getSwiftRValueType()); - if (!toTupleTy) - return false; - - unsigned numToElts = toTupleTy->getNumElements(); - if (numFromElts < numToElts) - return false; - - for (unsigned i = 0; i != numToElts; ++i) { - if (!SILType::canPerformABICompatibleUnsafeCastValue( - fromType.getTupleElementType(i), toType.getTupleElementType(i), - M)) { - return false; - } - } - return true; -} - -// Allow casting an enum by value when toType is an enum and each elements is -// individually castable to toType. An enum cannot be smaller than its payload. -static bool canUnsafeCastEnum(SILType fromType, EnumDecl *fromEnum, - SILType toType, SILModule &M) { - unsigned numToElements = 0; - SILType toElementTy; - if (EnumDecl *toEnum = toType.getEnumOrBoundGenericEnum()) { - for (auto toElement : toEnum->getAllElements()) { - ++numToElements; - if (!toElement->hasAssociatedValues()) - continue; - // Bail on multiple payloads. - if (!toElementTy.isNull()) - return false; - toElementTy = toType.getEnumElementType(toElement, M); - } - } else { - // If toType is not an enum, handle it like a singleton - numToElements = 1; - toElementTy = toType; - } - // If toType has more elements, it may be larger. - auto fromElements = fromEnum->getAllElements(); - if (static_cast(numToElements) > - std::distance(fromElements.begin(), fromElements.end())) - return false; - - if (toElementTy.isNull()) - return true; - - // If any of the fromElements can be cast by value to the singleton toElement, - // then the overall enum can be cast by value. - for (auto fromElement : fromElements) { - if (!fromElement->hasAssociatedValues()) - continue; - - auto fromElementTy = fromType.getEnumElementType(fromElement, M); - if (SILType::canPerformABICompatibleUnsafeCastValue(fromElementTy, - toElementTy, M)) - return true; - } - return false; -} - -static bool canUnsafeCastScalars(SILType fromType, SILType toType, - SILModule &M) { - CanType fromCanTy = fromType.getSwiftRValueType(); - bool isToPointer = toType.isPointerSizeAndAligned(); - - unsigned LeastFromWidth = 0; - // Like UnsafeRefBitCast, allow class existentials to be truncated to - // single-pointer references. Unlike UnsafeRefBitCast, this also supports raw - // pointers and words. - if (fromType.isPointerSizeAndAligned() - || fromCanTy.isAnyClassReferenceType()) { - - // Allow casting from a value that contains an aligned pointer into another - // pointer value regardless of the fixed width. - if (isToPointer) - return true; - - LeastFromWidth = BuiltinIntegerWidth::pointer().getLeastWidth(); - - } else if (auto fromIntTy = dyn_cast(fromCanTy)) { - if (fromIntTy->isFixedWidth()) - LeastFromWidth = fromIntTy->getFixedWidth(); - } - - unsigned GreatestToWidth = UINT_MAX; - if (isToPointer) { - GreatestToWidth = BuiltinIntegerWidth::pointer().getGreatestWidth(); - - } else if (auto toIntTy = dyn_cast( - toType.getSwiftRValueType())) { - if (toIntTy->isFixedWidth()) - GreatestToWidth = toIntTy->getFixedWidth(); - } - return LeastFromWidth >= GreatestToWidth; -} - -bool SILType::canPerformABICompatibleUnsafeCastValue(SILType fromType, - SILType toType, - SILModule &M) { - if (fromType == toType) - return true; - - // Unwrap single element structs. - if (StructDecl *toStruct = toType.getStructOrBoundGenericStruct()) { - auto toRange = toStruct->getStoredProperties(); - if (toRange.begin() != toRange.end() - && std::next(toRange.begin()) == toRange.end()) { - toType = toType.getFieldType(*toRange.begin(), M); - } - } - if (canUnsafeCastScalars(fromType, toType, M)) - return true; - - if (StructDecl *fromStruct = fromType.getStructOrBoundGenericStruct()) - return canUnsafeCastStruct(fromType, fromStruct, toType, M); - - if (CanTupleType fromTupleTy = - dyn_cast(fromType.getSwiftRValueType())) { - return canUnsafeCastTuple(fromType, fromTupleTy, toType, M); - } - if (EnumDecl *fromEnum = fromType.getEnumOrBoundGenericEnum()) - return canUnsafeCastEnum(fromType, fromEnum, toType, M); - - return false; -} - // Reference cast from representations with single pointer low bits. // Only reference cast to simple single pointer representations. // // TODO: handle casting to a loadable existential by generating // init_existential_ref. Until then, only promote to a heap object dest. bool SILType::canRefCast(SILType operTy, SILType resultTy, SILModule &M) { - auto fromTy = operTy.unwrapAnyOptionalType(); - auto toTy = resultTy.unwrapAnyOptionalType(); + auto fromTy = operTy.unwrapOptionalType(); + auto toTy = resultTy.unwrapOptionalType(); return (fromTy.isHeapObjectReferenceType() || fromTy.isClassExistentialType()) && toTy.isHeapObjectReferenceType(); } SILType SILType::getFieldType(VarDecl *field, SILModule &M) const { - auto baseTy = getSwiftRValueType(); + auto baseTy = getASTType(); AbstractionPattern origFieldTy = M.Types.getAbstractionPattern(field); CanType substFieldTy; @@ -333,7 +146,7 @@ SILType SILType::getEnumElementType(EnumElementDecl *elt, SILModule &M) const { assert(elt->getDeclContext() == getEnumOrBoundGenericEnum()); assert(elt->hasAssociatedValues()); - if (auto objectType = getSwiftRValueType().getOptionalObjectType()) { + if (auto objectType = getASTType().getOptionalObjectType()) { assert(elt == M.getASTContext().getOptionalSomeDecl()); return SILType(objectType, getCategory()); } @@ -341,17 +154,17 @@ SILType SILType::getEnumElementType(EnumElementDecl *elt, SILModule &M) const { // If the case is indirect, then the payload is boxed. if (elt->isIndirect() || elt->getParentEnum()->isIndirect()) { auto box = M.Types.getBoxTypeForEnumElement(*this, elt); - return SILType(SILType::getPrimitiveObjectType(box).getSwiftRValueType(), + return SILType(SILType::getPrimitiveObjectType(box).getASTType(), getCategory()); } auto substEltTy = - getSwiftRValueType()->getTypeOfMember(M.getSwiftModule(), elt, + getASTType()->getTypeOfMember(M.getSwiftModule(), elt, elt->getArgumentInterfaceType()); auto loweredTy = M.Types.getLoweredType(M.Types.getAbstractionPattern(elt), substEltTy); - return SILType(loweredTy.getSwiftRValueType(), getCategory()); + return SILType(loweredTy.getASTType(), getCategory()); } bool SILType::isLoadableOrOpaque(SILModule &M) const { @@ -366,14 +179,7 @@ bool SILType::isAddressOnly(SILModule &M) const { } SILType SILType::substGenericArgs(SILModule &M, - SubstitutionList Subs) const { - auto fnTy = castTo(); - auto canFnTy = CanSILFunctionType(fnTy->substGenericArgs(M, Subs)); - return SILType::getPrimitiveObjectType(canFnTy); -} - -SILType SILType::substGenericArgs(SILModule &M, - const SubstitutionMap &SubMap) const { + SubstitutionMap SubMap) const { auto fnTy = castTo(); auto canFnTy = CanSILFunctionType(fnTy->substGenericArgs(M, SubMap)); return SILType::getPrimitiveObjectType(canFnTy); @@ -381,13 +187,14 @@ SILType SILType::substGenericArgs(SILModule &M, bool SILType::isHeapObjectReferenceType() const { auto &C = getASTContext(); - if (getSwiftRValueType()->isBridgeableObjectType()) + auto Ty = getASTType(); + if (Ty->isBridgeableObjectType()) return true; - if (getSwiftRValueType()->isEqual(C.TheNativeObjectType)) + if (Ty->isEqual(C.TheNativeObjectType)) return true; - if (getSwiftRValueType()->isEqual(C.TheBridgeObjectType)) + if (Ty->isEqual(C.TheBridgeObjectType)) return true; - if (getSwiftRValueType()->isEqual(C.TheUnknownObjectType)) + if (Ty->isEqual(C.TheUnknownObjectType)) return true; if (is()) return true; @@ -395,7 +202,7 @@ bool SILType::isHeapObjectReferenceType() const { } SILType SILType::getMetatypeInstanceType(SILModule &M) const { - CanType MetatypeType = getSwiftRValueType(); + CanType MetatypeType = getASTType(); assert(MetatypeType->is() && "This method should only be called on SILTypes with an underlying " "metatype type."); @@ -462,14 +269,14 @@ bool SILType::aggregateHasUnreferenceableStorage() const { } SILType SILType::getOptionalObjectType() const { - if (auto objectTy = getSwiftRValueType().getOptionalObjectType()) { + if (auto objectTy = getASTType().getOptionalObjectType()) { return SILType(objectTy, getCategory()); } return SILType(); } -SILType SILType::unwrapAnyOptionalType() const { +SILType SILType::unwrapOptionalType() const { if (auto objectTy = getOptionalObjectType()) { return objectTy; } @@ -512,7 +319,7 @@ SILType::getPreferredExistentialRepresentation(SILModule &M, if (!isExistentialType()) return ExistentialRepresentation::None; - auto layout = getSwiftRValueType().getExistentialLayout(); + auto layout = getASTType().getExistentialLayout(); if (layout.isErrorExistential()) { // NSError or CFError references can be adopted directly as Error @@ -544,10 +351,10 @@ SILType::canUseExistentialRepresentation(SILModule &M, case ExistentialRepresentation::Class: case ExistentialRepresentation::Boxed: { // Look at the protocols to see what representation is appropriate. - if (!getSwiftRValueType().isExistentialType()) + if (!isExistentialType()) return false; - auto layout = getSwiftRValueType().getExistentialLayout(); + auto layout = getASTType().getExistentialLayout(); // The (uncomposed) Error existential uses a special boxed // representation. It can also adopt class references of bridged error types @@ -572,8 +379,7 @@ SILType::canUseExistentialRepresentation(SILModule &M, } SILType SILType::getReferentType(SILModule &M) const { - ReferenceStorageType *Ty = - getSwiftRValueType()->castTo(); + auto Ty = castTo(); return M.Types.getLoweredType(Ty->getReferentType()->getCanonicalType()); } @@ -582,15 +388,14 @@ SILBoxType::getFieldLoweredType(SILModule &M, unsigned index) const { auto fieldTy = getLayout()->getFields()[index].getLoweredType(); // Apply generic arguments if the layout is generic. - if (!getGenericArgs().empty()) { + if (auto subMap = getSubstitutions()) { auto sig = getLayout()->getGenericSignature(); - auto subs = sig->getSubstitutionMap(getGenericArgs()); return SILType::getPrimitiveObjectType(fieldTy) .subst(M, - QuerySubstitutionMap{subs}, - LookUpConformanceInSubstitutionMap(subs), + QuerySubstitutionMap{subMap}, + LookUpConformanceInSubstitutionMap(subMap), sig) - .getSwiftRValueType(); + .getASTType(); } return fieldTy; } @@ -638,17 +443,7 @@ bool SILModuleConventions::isPassedIndirectlyInSIL(SILType type, SILModule &M) { } bool SILFunctionType::isNoReturnFunction() { - return getDirectFormalResultsType().getSwiftRValueType()->isUninhabited(); -} - -SILType SILType::wrapAnyOptionalType(SILFunction &F) const { - SILModule &M = F.getModule(); - EnumDecl *OptionalDecl = M.getASTContext().getOptionalDecl(); - BoundGenericType *BoundEnumDecl = - BoundGenericType::get(OptionalDecl, Type(), {getSwiftRValueType()}); - AbstractionPattern Pattern(F.getLoweredFunctionType()->getGenericSignature(), - BoundEnumDecl->getCanonicalType()); - return M.Types.getLoweredType(Pattern, BoundEnumDecl); + return getDirectFormalResultsType().getASTType()->isUninhabited(); } #ifndef NDEBUG @@ -717,8 +512,8 @@ static bool areOnlyAbstractionDifferent(CanType type1, CanType type2) { /// check whether they have an abstraction difference. bool SILType::hasAbstractionDifference(SILFunctionTypeRepresentation rep, SILType type2) { - CanType ct1 = getSwiftRValueType(); - CanType ct2 = type2.getSwiftRValueType(); + CanType ct1 = getASTType(); + CanType ct2 = type2.getASTType(); assert(getSILFunctionLanguage(rep) == SILFunctionLanguage::C || areOnlyAbstractionDifferent(ct1, ct2)); (void)ct1; @@ -783,5 +578,5 @@ bool SILType::isLoweringOf(SILModule &Mod, CanType formalType) { formalType = dynamicSelf.getSelfType(); // Other types are preserved through lowering. - return loweredType.getSwiftRValueType() == formalType; + return loweredType.getASTType() == formalType; } diff --git a/lib/SIL/SILVerifier.cpp b/lib/SIL/SILVerifier.cpp index 1a3b2344706a0..e8219fcfb9c95 100644 --- a/lib/SIL/SILVerifier.cpp +++ b/lib/SIL/SILVerifier.cpp @@ -25,6 +25,7 @@ #include "swift/SIL/DebugUtils.h" #include "swift/SIL/Dominance.h" #include "swift/SIL/DynamicCasts.h" +#include "swift/SIL/MemAccessUtils.h" #include "swift/SIL/PostOrder.h" #include "swift/SIL/PrettyStackTrace.h" #include "swift/SIL/SILDebugScope.h" @@ -62,6 +63,9 @@ static llvm::cl::opt VerifyDIHoles( "verify-di-holes", llvm::cl::init(true)); +static llvm::cl::opt SkipConvertEscapeToNoescapeAttributes( + "verify-skip-convert-escape-to-noescape-attributes", llvm::cl::init(false)); + // The verifier is basically all assertions, so don't compile it with NDEBUG to // prevent release builds from triggering spurious unused variable warnings. @@ -120,15 +124,11 @@ void verifyKeyPathComponent(SILModule &M, const KeyPathPatternComponent &component, ArrayRef operands, CanGenericSignature patternSig, - SubstitutionList patternSubList, + SubstitutionMap patternSubs, bool forPropertyDescriptor, bool hasIndices) { auto &C = M.getASTContext(); - SubstitutionMap patternSubs; - if (patternSig) - patternSubs = patternSig->getSubstitutionMap(patternSubList); - auto loweredBaseTy = M.Types.getLoweredType(AbstractionPattern::getOpaque(), baseTy); auto componentTy = component.getComponentType().subst(patternSubs) @@ -146,7 +146,7 @@ void verifyKeyPathComponent(SILModule &M, "operator"); auto substEqualsType = equals->getLoweredFunctionType() - ->substGenericArgs(M, patternSubList); + ->substGenericArgs(M, patternSubs); require(substEqualsType->getParameters().size() == 2, "must have two arguments"); @@ -178,7 +178,7 @@ void verifyKeyPathComponent(SILModule &M, "operator"); auto substHashType = hash->getLoweredFunctionType() - ->substGenericArgs(M, patternSubList); + ->substGenericArgs(M, patternSubs); require(substHashType->getParameters().size() == 1, "must have two arguments"); @@ -259,7 +259,7 @@ void verifyKeyPathComponent(SILModule &M, { auto getter = component.getComputedPropertyGetter(); auto substGetterType = getter->getLoweredFunctionType() - ->substGenericArgs(M, patternSubList); + ->substGenericArgs(M, patternSubs); require(substGetterType->getRepresentation() == SILFunctionTypeRepresentation::Thin, "getter should be a thin function"); @@ -269,7 +269,7 @@ void verifyKeyPathComponent(SILModule &M, auto baseParam = substGetterType->getParameters()[0]; require(baseParam.getConvention() == normalArgConvention, "getter base parameter should have normal arg convention"); - require(baseParam.getType() == loweredBaseTy.getSwiftRValueType(), + require(baseParam.getType() == loweredBaseTy.getASTType(), "getter base parameter should match base of component"); if (hasIndices) { @@ -287,7 +287,7 @@ void verifyKeyPathComponent(SILModule &M, auto result = substGetterType->getResults()[0]; require(result.getConvention() == ResultConvention::Indirect, "getter result should be @out"); - require(result.getType() == loweredComponentTy.getSwiftRValueType(), + require(result.getType() == loweredComponentTy.getASTType(), "getter result should match the maximal abstraction of the " "formal component type"); } @@ -298,7 +298,7 @@ void verifyKeyPathComponent(SILModule &M, auto setter = component.getComputedPropertySetter(); auto substSetterType = setter->getLoweredFunctionType() - ->substGenericArgs(M, patternSubList); + ->substGenericArgs(M, patternSubs); require(substSetterType->getRepresentation() == SILFunctionTypeRepresentation::Thin, @@ -331,7 +331,7 @@ void verifyKeyPathComponent(SILModule &M, } require(newValueParam.getType() == - loweredComponentTy.getSwiftRValueType(), + loweredComponentTy.getASTType(), "setter value should match the maximal abstraction of the " "formal component type"); @@ -366,7 +366,7 @@ void verifyKeyPathComponent(SILModule &M, CanGenericSignature canSig = nullptr; if (sig) { canSig = sig->getCanonicalSignature(); - subs = sig->getSubstitutionMap(component.getExternalSubstitutions()); + subs = component.getExternalSubstitutions(); } auto substType = component.getExternalDecl()->getStorageInterfaceType() .subst(subs); @@ -438,6 +438,10 @@ class SILVerifier : public SILVerifierBase { SmallVector DebugVars; const SILInstruction *CurInstruction = nullptr; DominanceInfo *Dominance = nullptr; + + // Used for dominance checking within a basic block. + llvm::DenseMap InstNumbers; + DeadEndBlocks DEBlocks; bool SingleFunction = true; @@ -542,7 +546,7 @@ class SILVerifier : public SILVerifierBase { const Twine &valueDescription) { require(value->getType().isObject(), valueDescription +" must be an object"); - auto objectTy = value->getType().unwrapAnyOptionalType(); + auto objectTy = value->getType().unwrapOptionalType(); require(objectTy.isReferenceCounted(F.getModule()), valueDescription + " must have reference semantics"); @@ -616,20 +620,32 @@ class SILVerifier : public SILVerifierBase { } } + static unsigned numInstsInFunction(const SILFunction &F) { + unsigned numInsts = 0; + for (auto &BB : F) { + numInsts += std::distance(BB.begin(), BB.end()); + } + return numInsts; + } + SILVerifier(const SILFunction &F, bool SingleFunction = true) : M(F.getModule().getSwiftModule()), F(F), fnConv(F.getLoweredFunctionType(), F.getModule()), TC(F.getModule().Types), OpenedArchetypes(&F), Dominance(nullptr), + InstNumbers(numInstsInFunction(F)), DEBlocks(&F), SingleFunction(SingleFunction) { if (F.isExternalDeclaration()) return; // Check to make sure that all blocks are well formed. If not, the // SILVerifier object will explode trying to compute dominance info. + unsigned InstIdx = 0; for (auto &BB : F) { require(!BB.empty(), "Basic blocks cannot be empty"); require(isa(BB.back()), "Basic blocks must end with a terminator instruction"); + for (auto &I : BB) + InstNumbers[&I] = InstIdx++; } Dominance = new DominanceInfo(const_cast(&F)); @@ -645,6 +661,20 @@ class SILVerifier : public SILVerifierBase { delete Dominance; } + // Checks dominance between two instructions. + // This does not use DominanceInfo.properlyDominates, because for large basic + // blocks it would result in quadratic behavior. + bool properlyDominates(SILInstruction *a, SILInstruction *b) { + auto aBlock = a->getParent(), bBlock = b->getParent(); + + // If the blocks are different, it's as easy as whether A's block + // dominates B's block. + if (aBlock != bBlock) + return Dominance->properlyDominates(aBlock, bBlock); + + return InstNumbers[a] < InstNumbers[b]; + } + // FIXME: For sanity, address-type block args should be prohibited at all SIL // stages. However, the optimizer currently breaks the invariant in three // places: @@ -776,7 +806,7 @@ class SILVerifier : public SILVerifierBase { } else { require(valueI->getFunction() == &F, "instruction uses value of instruction from another function"); - require(Dominance->properlyDominates(valueI, I), + require(properlyDominates(valueI, I), "instruction isn't dominated by its operand"); } } @@ -898,7 +928,7 @@ class SILVerifier : public SILVerifierBase { /// Check that the given type is a legal SIL value type. void checkLegalType(SILFunction *F, SILType type, SILInstruction *I) { - checkLegalSILType(F, type.getSwiftRValueType(), I); + checkLegalSILType(F, type.getASTType(), I); } /// Check that the given type is a legal SIL value type. @@ -940,7 +970,7 @@ class SILVerifier : public SILVerifierBase { auto Def = OpenedArchetypes.getOpenedArchetypeDef(OpenedA); require (Def, "Opened archetype should be registered in SILFunction"); require(I == nullptr || Def == I || - Dominance->properlyDominates(cast(Def), I), + properlyDominates(cast(Def), I), "Use of an opened archetype should be dominated by a " "definition of this opened archetype"); } @@ -971,7 +1001,7 @@ class SILVerifier : public SILVerifierBase { require(AI->getType().isAddress(), "result of alloc_stack must be an address type"); - verifyOpenedArchetype(AI, AI->getElementType().getSwiftRValueType()); + verifyOpenedArchetype(AI, AI->getElementType().getASTType()); // There used to be a check if all uses of ASI are inside the alloc-dealloc // range. But apparently it can be the case that ASI has uses after the @@ -982,7 +1012,7 @@ class SILVerifier : public SILVerifierBase { void checkAllocRefBase(AllocRefInstBase *ARI) { requireReferenceValue(ARI, "Result of alloc_ref"); - verifyOpenedArchetype(ARI, ARI->getType().getSwiftRValueType()); + verifyOpenedArchetype(ARI, ARI->getType().getASTType()); auto Types = ARI->getTailAllocatedTypes(); auto Counts = ARI->getTailAllocatedCounts(); unsigned NumTypes = Types.size(); @@ -990,7 +1020,7 @@ class SILVerifier : public SILVerifierBase { require(NumTypes == 0 || !ARI->isObjC(), "Can't tail allocate with ObjC class"); for (unsigned Idx = 0; Idx < NumTypes; ++Idx) { - verifyOpenedArchetype(ARI, Types[Idx].getSwiftRValueType()); + verifyOpenedArchetype(ARI, Types[Idx].getASTType()); require(Counts[Idx].get()->getType().is(), "count needs integer type"); } @@ -1020,12 +1050,12 @@ class SILVerifier : public SILVerifierBase { } /// Check the substitutions passed to an apply or partial_apply. - CanSILFunctionType checkApplySubstitutions(SubstitutionList subs, + CanSILFunctionType checkApplySubstitutions(SubstitutionMap subs, SILType calleeTy) { auto fnTy = requireObjectType(SILFunctionType, calleeTy, "callee operand"); // If there are substitutions, verify them and apply them to the callee. - if (subs.empty()) { + if (!subs.hasAnySubstitutableParams()) { require(!fnTy->isPolymorphic(), "callee of apply without substitutions must not be polymorphic"); return fnTy; @@ -1035,8 +1065,8 @@ class SILVerifier : public SILVerifierBase { // Each archetype occurring in the substitutions list should belong to the // current function. - for (auto sub : subs) { - sub.getReplacement()->getCanonicalType().visit([&](CanType t) { + for (auto replacementType : subs.getReplacementTypes()) { + replacementType->getCanonicalType().visit([&](CanType t) { auto A = dyn_cast(t); if (!A) return; @@ -1072,7 +1102,7 @@ class SILVerifier : public SILVerifierBase { auto Def = OpenedArchetypes.getOpenedArchetypeDef(A); require(Def, "Opened archetype should be registered in SILFunction"); require(Def == AI || - Dominance->properlyDominates(cast(Def), AI), + properlyDominates(cast(Def), AI), "Use of an opened archetype should be dominated by a " "definition of this opened archetype"); } @@ -1082,8 +1112,8 @@ class SILVerifier : public SILVerifierBase { }; // Search for opened archetypes and dynamic self. - for (auto &Sub : AS.getSubstitutions()) { - Sub.getReplacement()->getCanonicalType().visit(HandleType); + for (auto Replacement : AS.getSubstitutionMap().getReplacementTypes()) { + Replacement->getCanonicalType().visit(HandleType); } AS.getSubstCalleeType().visit(HandleType); @@ -1119,7 +1149,7 @@ class SILVerifier : public SILVerifierBase { // Then make sure that we have a type that can be substituted for the // callee. - auto substTy = checkApplySubstitutions(site.getSubstitutions(), + auto substTy = checkApplySubstitutions(site.getSubstitutionMap(), site.getCallee()->getType()); require(site.getOrigCalleeType()->getRepresentation() == site.getSubstCalleeType()->getRepresentation(), @@ -1275,7 +1305,7 @@ class SILVerifier : public SILVerifierBase { checkApplyTypeDependentArguments(PAI); - auto substTy = checkApplySubstitutions(PAI->getSubstitutions(), + auto substTy = checkApplySubstitutions(PAI->getSubstitutionMap(), PAI->getCallee()->getType()); require(!PAI->getSubstCalleeType()->isPolymorphic(), @@ -1387,7 +1417,7 @@ class SILVerifier : public SILVerifierBase { auto isSwiftRefcounted = [](SILType t) -> bool { if (t.is()) return true; - if (t.getSwiftRValueType() == t.getASTContext().TheNativeObjectType) + if (t.getASTType() == t.getASTContext().TheNativeObjectType) return true; if (auto clas = t.getClassOrBoundGenericClass()) // Must be a class defined in Swift. @@ -1418,14 +1448,17 @@ class SILVerifier : public SILVerifierBase { SILFunction *RefF = FRI->getReferencedFunction(); - // A direct reference to a shared_external declaration is an error; we - // should have deserialized a body. - if (RefF->isExternalDeclaration()) { - require(SingleFunction || - !hasSharedVisibility(RefF->getLinkage()) || - RefF->hasForeignBody(), - "external declarations of SILFunctions with shared visibility is " - "not allowed"); + // In canonical SIL, direct reference to a shared_external declaration + // is an error; we should have deserialized a body. In raw SIL, we may + // not have deserialized the body yet. + if (F.getModule().getStage() >= SILStage::Canonical) { + if (RefF->isExternalDeclaration()) { + require(SingleFunction || + !hasSharedVisibility(RefF->getLinkage()) || + RefF->hasForeignBody(), + "external declarations of SILFunctions with shared visibility is " + "not allowed"); + } } // A direct reference to a non-public or shared but not fragile function @@ -1549,8 +1582,8 @@ class SILVerifier : public SILVerifierBase { } void checkBeginAccessInst(BeginAccessInst *BAI) { - auto op = BAI->getOperand(); - requireSameType(BAI->getType(), op->getType(), + auto sourceOper = BAI->getOperand(); + requireSameType(BAI->getType(), sourceOper->getType(), "result must be same type as operand"); require(BAI->getType().isAddress(), "begin_access operand must have address type"); @@ -1571,6 +1604,11 @@ class SILVerifier : public SILVerifierBase { case SILAccessKind::Modify: break; } + + // For dynamic Read/Modify access, AccessEnforcementWMO assumes a valid + // AccessedStorage and runs very late in the optimizer pipeline. + // TODO: eventually, make this true for any kind of access. + findAccessedStorage(sourceOper); } void checkEndAccessInst(EndAccessInst *EAI) { @@ -1657,12 +1695,12 @@ class SILVerifier : public SILVerifierBase { void checkLoadUnownedInst(LoadUnownedInst *LUI) { require(LUI->getType().isObject(), "Result of load must be an object"); auto PointerType = LUI->getOperand()->getType(); - auto PointerRVType = PointerType.getSwiftRValueType(); + auto PointerRVType = PointerType.getASTType(); require(PointerType.isAddress() && PointerRVType->is(), "load_unowned operand must be an unowned address"); require(PointerRVType->getReferenceStorageReferent()->getCanonicalType() == - LUI->getType().getSwiftRValueType(), + LUI->getType().getASTType(), "Load operand type and result type mismatch"); } @@ -1670,12 +1708,12 @@ class SILVerifier : public SILVerifierBase { require(SUI->getSrc()->getType().isObject(), "Can't store from an address source"); auto PointerType = SUI->getDest()->getType(); - auto PointerRVType = PointerType.getSwiftRValueType(); + auto PointerRVType = PointerType.getASTType(); require(PointerType.isAddress() && PointerRVType->is(), "store_unowned address operand must be an unowned address"); require(PointerRVType->getReferenceStorageReferent()->getCanonicalType() == - SUI->getSrc()->getType().getSwiftRValueType(), + SUI->getSrc()->getType().getASTType(), "Store operand type and dest type mismatch"); } @@ -1684,12 +1722,12 @@ class SILVerifier : public SILVerifierBase { require(LWI->getType().getOptionalObjectType(), "Result of weak load must be an optional"); auto PointerType = LWI->getOperand()->getType(); - auto PointerRVType = PointerType.getSwiftRValueType(); + auto PointerRVType = PointerType.getASTType(); require(PointerType.isAddress() && PointerRVType->is(), "load_weak operand must be a weak address"); require(PointerRVType->getReferenceStorageReferent()->getCanonicalType() == - LWI->getType().getSwiftRValueType(), + LWI->getType().getASTType(), "Load operand type and result type mismatch"); } @@ -1699,12 +1737,12 @@ class SILVerifier : public SILVerifierBase { require(SWI->getSrc()->getType().getOptionalObjectType(), "store_weak must be of an optional value"); auto PointerType = SWI->getDest()->getType(); - auto PointerRVType = PointerType.getSwiftRValueType(); + auto PointerRVType = PointerType.getASTType(); require(PointerType.isAddress() && PointerRVType->is(), "store_weak address operand must be a weak address"); require(PointerRVType->getReferenceStorageReferent()->getCanonicalType() == - SWI->getSrc()->getType().getSwiftRValueType(), + SWI->getSrc()->getType().getASTType(), "Store operand type and dest type mismatch"); } @@ -1713,9 +1751,7 @@ class SILVerifier : public SILVerifierBase { require(MU->getModule().getStage() == SILStage::Raw, "mark_uninitialized instruction can only exist in raw SIL"); require(Src->getType().isAddress() || - Src->getType() - .getSwiftRValueType() - ->getClassOrBoundGenericClass() || + Src->getType().getClassOrBoundGenericClass() || Src->getType().getAs(), "mark_uninitialized must be an address, class, or box type"); require(Src->getType() == MU->getType(),"operand and result type mismatch"); @@ -1783,6 +1819,8 @@ class SILVerifier : public SILVerifierBase { "Dest address should be lvalue"); require(SI->getDest()->getType() == SI->getSrc()->getType(), "Store operand type and dest type mismatch"); + require(F.getModule().isTypeABIAccessible(SI->getDest()->getType()), + "cannot directly copy type with inaccessible ABI"); } void checkRetainValueInst(RetainValueInst *I) { @@ -1860,13 +1898,23 @@ class SILVerifier : public SILVerifierBase { require(I->getOperand()->getType() == I->getType(), "result of copy_block should be same type as operand"); } + void checkCopyBlockInst(CopyBlockWithoutEscapingInst *I) { + require(I->getBlock()->getType().isBlockPointerCompatible(), + "operand of copy_block should be a block"); + require(I->getBlock()->getType() == I->getType(), + "result of copy_block should be same type as operand"); + auto FnTy = requireObjectType(SILFunctionType, I->getClosure(), + "copy_block_without_escaping operand"); + require(!FnTy->isNoEscape(), + "closure parameter must not be a @noescape closure"); + } void checkAllocValueBufferInst(AllocValueBufferInst *I) { require(I->getOperand()->getType().isAddress(), "Operand value should be an address"); require(I->getOperand()->getType().is(), "Operand value should be a Builtin.UnsafeValueBuffer"); - verifyOpenedArchetype(I, I->getValueType().getSwiftRValueType()); + verifyOpenedArchetype(I, I->getValueType().getASTType()); } void checkProjectValueBufferInst(ProjectValueBufferInst *I) { @@ -1912,7 +1960,7 @@ class SILVerifier : public SILVerifierBase { if (auto *AEBI = dyn_cast(PEBI->getOperand())) { // The lowered type must be the properly-abstracted form of the AST type. SILType exType = AEBI->getExistentialType(); - auto archetype = ArchetypeType::getOpened(exType.getSwiftRValueType()); + auto archetype = ArchetypeType::getOpened(exType.getASTType()); auto loweredTy = F.getModule().Types.getLoweredType( Lowering::AbstractionPattern(archetype), @@ -1994,8 +2042,12 @@ class SILVerifier : public SILVerifierBase { SILType caseTy = UI->getOperand()->getType().getEnumElementType(UI->getElement(), F.getModule()); - requireSameType(caseTy, UI->getType(), - "InitEnumDataAddrInst result does not match type of enum case"); + + if (UI->getModule().getStage() != SILStage::Lowered) { + requireSameType( + caseTy, UI->getType(), + "InitEnumDataAddrInst result does not match type of enum case"); + } } void checkUncheckedEnumDataInst(UncheckedEnumDataInst *UI) { @@ -2013,8 +2065,11 @@ class SILVerifier : public SILVerifierBase { SILType caseTy = UI->getOperand()->getType().getEnumElementType(UI->getElement(), F.getModule()); - require(caseTy == UI->getType(), - "UncheckedEnumData result does not match type of enum case"); + + if (UI->getModule().getStage() != SILStage::Lowered) { + require(caseTy == UI->getType(), + "UncheckedEnumData result does not match type of enum case"); + } } void checkUncheckedTakeEnumDataAddrInst(UncheckedTakeEnumDataAddrInst *UI) { @@ -2032,8 +2087,11 @@ class SILVerifier : public SILVerifierBase { SILType caseTy = UI->getOperand()->getType().getEnumElementType(UI->getElement(), F.getModule()); - require(caseTy == UI->getType(), - "UncheckedTakeEnumDataAddrInst result does not match type of enum case"); + + if (UI->getModule().getStage() != SILStage::Lowered) { + require(caseTy == UI->getType(), "UncheckedTakeEnumDataAddrInst result " + "does not match type of enum case"); + } } void checkInjectEnumAddrInst(InjectEnumAddrInst *IUAI) { @@ -2055,7 +2113,7 @@ class SILVerifier : public SILVerifierBase { if (TI->getModule().getStage() != SILStage::Lowered) { for (size_t i = 0, size = TI->getElements().size(); i < size; ++i) { - require(TI->getElement(i)->getType().getSwiftRValueType() == + require(TI->getElement(i)->getType().getASTType() == ResTy.getElementType(i), "Tuple element arguments do not match tuple type!"); } @@ -2195,6 +2253,8 @@ class SILVerifier : public SILVerifierBase { void checkDestroyAddrInst(DestroyAddrInst *DI) { require(DI->getOperand()->getType().isAddress(), "Operand of destroy_addr must be address"); + require(F.getModule().isTypeABIAccessible(DI->getOperand()->getType()), + "cannot directly destroy type with inaccessible ABI"); } void checkBindMemoryInst(BindMemoryInst *BI) { @@ -2238,7 +2298,7 @@ class SILVerifier : public SILVerifierBase { require(EI->getFieldNo() < operandTy->getNumElements(), "invalid field index for tuple_extract instruction"); if (EI->getModule().getStage() != SILStage::Lowered) { - require(EI->getType().getSwiftRValueType() == + require(EI->getType().getASTType() == operandTy.getElementType(EI->getFieldNo()), "type of tuple_extract does not match type of element"); } @@ -2281,7 +2341,7 @@ class SILVerifier : public SILVerifierBase { require(EI->getFieldNo() < fields.size(), "invalid field index for element_addr instruction"); if (EI->getModule().getStage() != SILStage::Lowered) { - require(EI->getType().getSwiftRValueType() == + require(EI->getType().getASTType() == CanType(fields[EI->getFieldNo()].getType()), "type of tuple_element_addr does not match type of element"); } @@ -2396,10 +2456,7 @@ class SILVerifier : public SILVerifierBase { require(AMI->getTypeDependentOperands().empty(), "Should not have an operand for the opened existential"); } - if (isa(lookupType) || lookupType->isAnyExistentialType()) { - require(AMI->getConformance().isAbstract(), - "archetype or existential lookup should have abstract conformance"); - } else { + if (!isa(lookupType)) { require(AMI->getConformance().isConcrete(), "concrete type lookup requires concrete conformance"); auto conformance = AMI->getConformance().getConcrete(); @@ -2423,7 +2480,7 @@ class SILVerifier : public SILVerifierBase { // Map interface types to archetypes. if (auto *env = constantInfo.GenericEnv) { - auto subs = env->getForwardingSubstitutions(); + auto subs = env->getForwardingSubstitutionMap(); methodTy = methodTy->substGenericArgs(F.getModule(), subs); } assert(!methodTy->isPolymorphic()); @@ -2432,7 +2489,7 @@ class SILVerifier : public SILVerifierBase { auto params = methodTy->getParameters(); SmallVector dynParams(params.begin(), params.end() - 1); - dynParams.push_back(SILParameterInfo(selfType.getSwiftRValueType(), + dynParams.push_back(SILParameterInfo(selfType.getASTType(), params.back().getConvention())); auto results = methodTy->getResults(); @@ -2531,7 +2588,7 @@ class SILVerifier : public SILVerifierBase { "wrong function type representation"); auto operandType = OMI->getOperand()->getType(); - auto operandInstanceType = operandType.getSwiftRValueType(); + auto operandInstanceType = operandType.getASTType(); if (auto metatypeType = dyn_cast(operandInstanceType)) operandInstanceType = metatypeType.getInstanceType(); @@ -2544,7 +2601,7 @@ class SILVerifier : public SILVerifierBase { require(isa(operandInstanceType) || operandInstanceType->isObjCExistentialType(), "operand type must be an archetype or self-conforming existential"); - verifyOpenedArchetype(OMI, OMI->getType().getSwiftRValueType()); + verifyOpenedArchetype(OMI, OMI->getType().getASTType()); } // TODO: We should enforce that ObjC methods are dispatched on ObjC @@ -2598,7 +2655,7 @@ class SILVerifier : public SILVerifierBase { require(OEI->getType().isAddress(), "open_existential_addr result must be an address"); - auto archetype = getOpenedArchetypeOf(OEI->getType().getSwiftRValueType()); + auto archetype = getOpenedArchetypeOf(OEI->getType().getASTType()); require(archetype, "open_existential_addr result must be an opened existential archetype"); require(OpenedArchetypes.getOpenedArchetypeDef(archetype) == OEI, @@ -2738,7 +2795,7 @@ class SILVerifier : public SILVerifierBase { ExistentialRepresentation::Class), "open_existential_ref operand must be class existential"); - CanType resultInstanceTy = OEI->getType().getSwiftRValueType(); + CanType resultInstanceTy = OEI->getType().getASTType(); require(OEI->getType().isObject(), "open_existential_ref result must be an address"); @@ -2760,7 +2817,7 @@ class SILVerifier : public SILVerifierBase { ExistentialRepresentation::Boxed), "open_existential_box operand must be boxed existential"); - CanType resultInstanceTy = OEI->getType().getSwiftRValueType(); + CanType resultInstanceTy = OEI->getType().getASTType(); require(OEI->getType().isAddress(), "open_existential_box result must be an address"); @@ -2782,7 +2839,7 @@ class SILVerifier : public SILVerifierBase { ExistentialRepresentation::Boxed), "open_existential_box operand must be boxed existential"); - CanType resultInstanceTy = OEI->getType().getSwiftRValueType(); + CanType resultInstanceTy = OEI->getType().getASTType(); require(!OEI->getType().isAddress(), "open_existential_box_value result must not be an address"); @@ -2852,7 +2909,7 @@ class SILVerifier : public SILVerifierBase { require(!OEI->getType().isAddress(), "open_existential_value result must not be an address"); - auto archetype = getOpenedArchetypeOf(OEI->getType().getSwiftRValueType()); + auto archetype = getOpenedArchetypeOf(OEI->getType().getASTType()); require(archetype, "open_existential_value result must be an opened " "existential archetype"); require(OpenedArchetypes.getOpenedArchetypeDef(archetype) == OEI, @@ -2887,7 +2944,7 @@ class SILVerifier : public SILVerifierBase { "existential type"); // The lowered type must be the properly-abstracted form of the AST type. - auto archetype = ArchetypeType::getOpened(exType.getSwiftRValueType()); + auto archetype = ArchetypeType::getOpened(exType.getASTType()); auto loweredTy = F.getModule().Types.getLoweredType( Lowering::AbstractionPattern(archetype), @@ -2917,7 +2974,7 @@ class SILVerifier : public SILVerifierBase { "init_existential_value result must not be an address"); // The operand must be at the right abstraction level for the existential. SILType exType = IEI->getType(); - auto archetype = ArchetypeType::getOpened(exType.getSwiftRValueType()); + auto archetype = ArchetypeType::getOpened(exType.getASTType()); auto loweredTy = F.getModule().Types.getLoweredType( Lowering::AbstractionPattern(archetype), IEI->getFormalConcreteType()); requireSameType( @@ -2938,7 +2995,7 @@ class SILVerifier : public SILVerifierBase { void checkInitExistentialRefInst(InitExistentialRefInst *IEI) { SILType concreteType = IEI->getOperand()->getType(); - require(concreteType.getSwiftRValueType()->isBridgeableObjectType(), + require(concreteType.getASTType()->isBridgeableObjectType(), "init_existential_ref operand must be a class instance"); require(IEI->getType().canUseExistentialRepresentation(F.getModule(), ExistentialRepresentation::Class, @@ -2949,7 +3006,7 @@ class SILVerifier : public SILVerifierBase { // The operand must be at the right abstraction level for the existential. SILType exType = IEI->getType(); - auto archetype = ArchetypeType::getOpened(exType.getSwiftRValueType()); + auto archetype = ArchetypeType::getOpened(exType.getASTType()); auto loweredTy = F.getModule().Types.getLoweredType( Lowering::AbstractionPattern(archetype), IEI->getFormalConcreteType()); @@ -3025,7 +3082,7 @@ class SILVerifier : public SILVerifierBase { } checkExistentialProtocolConformances(resultType, - operandType.getSwiftRValueType(), + operandType.getASTType(), I->getConformances()); verifyOpenedArchetype(I, MetaTy.getInstanceType()); } @@ -3033,7 +3090,7 @@ class SILVerifier : public SILVerifierBase { void checkExistentialProtocolConformances(SILType resultType, CanType concreteType, ArrayRef conformances) { - auto layout = resultType.getSwiftRValueType().getExistentialLayout(); + auto layout = resultType.getASTType().getExistentialLayout(); auto protocols = layout.getProtocols(); require(conformances.size() == protocols.size(), @@ -3072,8 +3129,8 @@ class SILVerifier : public SILVerifierBase { require(fromTy.isObject() && toTy.isObject(), "value checked cast src and dest must be objects"); - auto fromCanTy = fromTy.getSwiftRValueType(); - auto toCanTy = toTy.getSwiftRValueType(); + auto fromCanTy = fromTy.getASTType(); + auto toCanTy = toTy.getASTType(); require(isOpaque || canUseScalarCheckedCastInstructions(F.getModule(), fromCanTy, toCanTy), @@ -3119,14 +3176,14 @@ class SILVerifier : public SILVerifierBase { verifyCheckedCast(/*exact*/ false, CI->getOperand()->getType(), CI->getType()); - verifyOpenedArchetype(CI, CI->getType().getSwiftRValueType()); + verifyOpenedArchetype(CI, CI->getType().getASTType()); } void checkUnconditionalCheckedCastValueInst( UnconditionalCheckedCastValueInst *CI) { verifyCheckedCast(/*exact*/ false, CI->getOperand()->getType(), CI->getType(), true); - verifyOpenedArchetype(CI, CI->getType().getSwiftRValueType()); + verifyOpenedArchetype(CI, CI->getType().getASTType()); } /// Verify if a given type is or contains an opened archetype or dynamic self. @@ -3166,7 +3223,7 @@ class SILVerifier : public SILVerifierBase { verifyCheckedCast(CBI->isExact(), CBI->getOperand()->getType(), CBI->getCastType()); - verifyOpenedArchetype(CBI, CBI->getCastType().getSwiftRValueType()); + verifyOpenedArchetype(CBI, CBI->getCastType().getASTType()); require(CBI->getSuccessBB()->args_size() == 1, "success dest of checked_cast_br must take one argument"); @@ -3193,7 +3250,7 @@ class SILVerifier : public SILVerifierBase { void checkCheckedCastValueBranchInst(CheckedCastValueBranchInst *CBI) { verifyCheckedCast(false, CBI->getOperand()->getType(), CBI->getCastType(), true); - verifyOpenedArchetype(CBI, CBI->getCastType().getSwiftRValueType()); + verifyOpenedArchetype(CBI, CBI->getCastType().getASTType()); require(CBI->getSuccessBB()->args_size() == 1, "success dest of checked_cast_value_br must take one argument"); @@ -3283,7 +3340,7 @@ class SILVerifier : public SILVerifierBase { void checkRefToUnownedInst(RefToUnownedInst *I) { requireReferenceStorageCapableValue(I->getOperand(), "Operand of ref_to_unowned"); - auto operandType = I->getOperand()->getType().getSwiftRValueType(); + auto operandType = I->getOperand()->getType().getASTType(); auto resultType = requireObjectType(UnownedStorageType, I, "Result of ref_to_unowned"); require(resultType->isLoadable(ResilienceExpansion::Maximal), @@ -3300,7 +3357,7 @@ class SILVerifier : public SILVerifierBase { require(operandType->isLoadable(ResilienceExpansion::Maximal), "unowned_to_ref requires unowned type to be loadable"); requireReferenceStorageCapableValue(I, "Result of unowned_to_ref"); - auto resultType = I->getType().getSwiftRValueType(); + auto resultType = I->getType().getASTType(); require(operandType.getReferentType() == resultType, "Operand of unowned_to_ref does not have the " "operand's type as its referent type"); @@ -3309,7 +3366,7 @@ class SILVerifier : public SILVerifierBase { void checkRefToUnmanagedInst(RefToUnmanagedInst *I) { requireReferenceStorageCapableValue(I->getOperand(), "Operand of ref_to_unmanaged"); - auto operandType = I->getOperand()->getType().getSwiftRValueType(); + auto operandType = I->getOperand()->getType().getASTType(); auto resultType = requireObjectType(UnmanagedStorageType, I, "Result of ref_to_unmanaged"); require(resultType.getReferentType() == operandType, @@ -3322,7 +3379,7 @@ class SILVerifier : public SILVerifierBase { I->getOperand(), "Operand of unmanaged_to_ref"); requireReferenceStorageCapableValue(I, "Result of unmanaged_to_ref"); - auto resultType = I->getType().getSwiftRValueType(); + auto resultType = I->getType().getASTType(); require(operandType.getReferentType() == resultType, "Operand of unmanaged_to_ref does not have the " "operand's type as its referent type"); @@ -3363,12 +3420,12 @@ class SILVerifier : public SILVerifierBase { // Upcast from Optional to Optional is legal as long as B is a // subclass of A. - if (ToTy.getSwiftRValueType().getOptionalObjectType() && - FromTy.getSwiftRValueType().getOptionalObjectType()) { + if (ToTy.getASTType().getOptionalObjectType() && + FromTy.getASTType().getOptionalObjectType()) { ToTy = SILType::getPrimitiveObjectType( - ToTy.getSwiftRValueType().getOptionalObjectType()); + ToTy.getASTType().getOptionalObjectType()); FromTy = SILType::getPrimitiveObjectType( - FromTy.getSwiftRValueType().getOptionalObjectType()); + FromTy.getASTType().getOptionalObjectType()); } auto ToClass = ToTy.getClassOrBoundGenericClass(); @@ -3376,7 +3433,7 @@ class SILVerifier : public SILVerifierBase { "upcast must convert a class instance to a class type"); if (ToClass->usesObjCGenericsModel()) { require(ToClass->getDeclaredTypeInContext() - ->isBindableToSuperclassOf(FromTy.getSwiftRValueType()), + ->isBindableToSuperclassOf(FromTy.getASTType()), "upcast must cast to a superclass or an existential metatype"); } else { require(ToTy.isExactSuperclassOf(FromTy), @@ -3387,13 +3444,13 @@ class SILVerifier : public SILVerifierBase { void checkAddressToPointerInst(AddressToPointerInst *AI) { require(AI->getOperand()->getType().isAddress(), "address-to-pointer operand must be an address"); - require(AI->getType().getSwiftRValueType()->isEqual( + require(AI->getType().getASTType()->isEqual( AI->getType().getASTContext().TheRawPointerType), "address-to-pointer result type must be RawPointer"); } void checkUncheckedRefCastInst(UncheckedRefCastInst *AI) { - verifyOpenedArchetype(AI, AI->getType().getSwiftRValueType()); + verifyOpenedArchetype(AI, AI->getType().getASTType()); require(AI->getOperand()->getType().isObject(), "unchecked_ref_cast operand must be a value"); require(AI->getType().isObject(), @@ -3417,7 +3474,7 @@ class SILVerifier : public SILVerifierBase { } void checkUncheckedAddrCastInst(UncheckedAddrCastInst *AI) { - verifyOpenedArchetype(AI, AI->getType().getSwiftRValueType()); + verifyOpenedArchetype(AI, AI->getType().getASTType()); require(AI->getOperand()->getType().isAddress(), "unchecked_addr_cast operand must be an address"); @@ -3426,7 +3483,7 @@ class SILVerifier : public SILVerifierBase { } void checkUncheckedTrivialBitCastInst(UncheckedTrivialBitCastInst *BI) { - verifyOpenedArchetype(BI, BI->getType().getSwiftRValueType()); + verifyOpenedArchetype(BI, BI->getType().getASTType()); require(BI->getOperand()->getType().isObject(), "unchecked_trivial_bit_cast must operate on a value"); require(BI->getType().isObject(), @@ -3436,7 +3493,7 @@ class SILVerifier : public SILVerifierBase { } void checkUncheckedBitwiseCastInst(UncheckedBitwiseCastInst *BI) { - verifyOpenedArchetype(BI, BI->getType().getSwiftRValueType()); + verifyOpenedArchetype(BI, BI->getType().getASTType()); require(BI->getOperand()->getType().isObject(), "unchecked_bitwise_cast must operate on a value"); require(BI->getType().isObject(), @@ -3444,23 +3501,22 @@ class SILVerifier : public SILVerifierBase { } void checkRefToRawPointerInst(RefToRawPointerInst *AI) { - require(AI->getOperand()->getType().getSwiftRValueType() - ->isAnyClassReferenceType(), + require(AI->getOperand()->getType().isAnyClassReferenceType(), "ref-to-raw-pointer operand must be a class reference or" " NativeObject"); - require(AI->getType().getSwiftRValueType()->isEqual( + require(AI->getType().getASTType()->isEqual( AI->getType().getASTContext().TheRawPointerType), "ref-to-raw-pointer result must be RawPointer"); } void checkRawPointerToRefInst(RawPointerToRefInst *AI) { - verifyOpenedArchetype(AI, AI->getType().getSwiftRValueType()); + verifyOpenedArchetype(AI, AI->getType().getASTType()); require(AI->getType() - .getSwiftRValueType()->isBridgeableObjectType() - || AI->getType().getSwiftRValueType()->isEqual( + .getASTType()->isBridgeableObjectType() + || AI->getType().getASTType()->isEqual( AI->getType().getASTContext().TheNativeObjectType), "raw-pointer-to-ref result must be a class reference or NativeObject"); - require(AI->getOperand()->getType().getSwiftRValueType()->isEqual( + require(AI->getOperand()->getType().getASTType()->isEqual( AI->getType().getASTContext().TheRawPointerType), "raw-pointer-to-ref operand must be NativeObject"); } @@ -3468,7 +3524,7 @@ class SILVerifier : public SILVerifierBase { void checkRefToBridgeObjectInst(RefToBridgeObjectInst *RI) { require(RI->getConverted()->getType().isObject(), "ref_to_bridge_object must convert from a value"); - require(RI->getConverted()->getType().getSwiftRValueType() + require(RI->getConverted()->getType().getASTType() ->isBridgeableObjectType(), "ref_to_bridge_object must convert from a heap object ref"); require(RI->getBitsOperand()->getType() @@ -3479,13 +3535,13 @@ class SILVerifier : public SILVerifierBase { } void checkBridgeObjectToRefInst(BridgeObjectToRefInst *RI) { - verifyOpenedArchetype(RI, RI->getType().getSwiftRValueType()); + verifyOpenedArchetype(RI, RI->getType().getASTType()); require(RI->getConverted()->getType() == SILType::getBridgeObjectType(F.getASTContext()), "bridge_object_to_ref must take a BridgeObject"); require(RI->getType().isObject(), "bridge_object_to_ref must produce a value"); - require(RI->getType().getSwiftRValueType()->isBridgeableObjectType(), + require(RI->getType().getASTType()->isBridgeableObjectType(), "bridge_object_to_ref must produce a heap object reference"); } void checkBridgeObjectToWordInst(BridgeObjectToWordInst *RI) { @@ -3523,6 +3579,18 @@ class SILVerifier : public SILVerifierBase { requireABICompatibleFunctionTypes( opTI, resTI->getWithExtInfo(resTI->getExtInfo().withNoEscape(false)), "convert_escape_to_noescape cannot change function ABI"); + + // After mandatory passes convert_escape_to_noescape should not have the + // '[not_guaranteed]' or '[escaped]' attributes. + if (!SkipConvertEscapeToNoescapeAttributes && + F.getModule().getStage() != SILStage::Raw) { + require(!ICI->isEscapedByUser(), + "convert_escape_to_noescape [escaped] not " + "allowed after mandatory passes"); + require(ICI->isLifetimeGuaranteed(), + "convert_escape_to_noescape [not_guaranteed] not " + "allowed after mandatory passes"); + } } void checkThinFunctionToPointerInst(ThinFunctionToPointerInst *CI) { @@ -3941,9 +4009,9 @@ class SILVerifier : public SILVerifierBase { require(DMBI->getMember().getDecl()->isObjC(), "method must be @objc"); if (!DMBI->getMember().getDecl()->isInstanceMember()) { - require(operandType.getSwiftRValueType()->is(), + require(operandType.is(), "operand must have metatype type"); - require(operandType.getSwiftRValueType()->castTo() + require(operandType.castTo() ->getInstanceType()->mayHaveSuperclass(), "operand must have metatype of class or class-bound type"); } @@ -3954,8 +4022,8 @@ class SILVerifier : public SILVerifierBase { auto bbArgTy = DMBI->getHasMethodBB()->args_begin()[0]->getType(); require(getDynamicMethodType(operandType, DMBI->getMember()) - .getSwiftRValueType() - ->isBindableTo(bbArgTy.getSwiftRValueType()), + .getASTType() + ->isBindableTo(bbArgTy.getASTType()), "bb argument for dynamic_method_br must be of the method's type"); } @@ -3967,7 +4035,7 @@ class SILVerifier : public SILVerifierBase { require(PBSI->getType().isAddress(), "result must be an address"); - auto captureTy = PBSI->getType().getSwiftRValueType(); + auto captureTy = PBSI->getType().getASTType(); require(storageTy->getCaptureType() == captureTy, "result must be the capture type of the @block_storage type"); } @@ -4067,7 +4135,7 @@ class SILVerifier : public SILVerifierBase { "objc_metatype_to_object must take an @objc metatype value"); require(OMOI->getType().isObject(), "objc_metatype_to_object must produce a value"); - require(OMOI->getType().getSwiftRValueType()->isAnyObject(), + require(OMOI->getType().getASTType()->isAnyObject(), "objc_metatype_to_object must produce an AnyObject value"); } @@ -4082,7 +4150,7 @@ class SILVerifier : public SILVerifierBase { "objc_metatype_to_object must take an @objc existential metatype value"); require(OMOI->getType().isObject(), "objc_metatype_to_object must produce a value"); - require(OMOI->getType().getSwiftRValueType()->isAnyObject(), + require(OMOI->getType().getASTType()->isAnyObject(), "objc_metatype_to_object must produce an AnyObject value"); } @@ -4101,10 +4169,7 @@ class SILVerifier : public SILVerifierBase { auto baseTy = CanType(kpBGT->getGenericArgs()[0]); auto pattern = KPI->getPattern(); - SubstitutionMap patternSubs; - if (pattern->getGenericSignature()) - patternSubs = pattern->getGenericSignature() - ->getSubstitutionMap(KPI->getSubstitutions()); + SubstitutionMap patternSubs = KPI->getSubstitutions(); require(baseTy == pattern->getRootType().subst(patternSubs)->getCanonicalType(), "keypath root type should match root type of keypath pattern"); @@ -4157,13 +4222,22 @@ class SILVerifier : public SILVerifierBase { } void checkIsEscapingClosureInst(IsEscapingClosureInst *IEC) { - auto fnType = IEC->getOperand()->getType().getAs(); + // The closure operand is allowed to be an optional closure. + auto operandType = IEC->getOperand()->getType(); + if (operandType.getOptionalObjectType()) + operandType = operandType.getOptionalObjectType(); + + auto fnType = operandType.getAs(); require(fnType && fnType->getExtInfo().hasContext() && !fnType->isNoEscape() && fnType->getExtInfo().getRepresentation() == SILFunctionTypeRepresentation::Thick, "is_escaping_closure must have a thick " "function operand"); + require(IEC->getVerificationType() == IsEscapingClosureInst::ObjCEscaping || + IEC->getVerificationType() == + IsEscapingClosureInst::WithoutActuallyEscaping, + "unknown verfication type"); } // This verifies that the entry block of a SIL function doesn't have @@ -4266,9 +4340,8 @@ class SILVerifier : public SILVerifierBase { } } - bool - isUnreachableAlongAllPathsStartingAt(SILBasicBlock *StartBlock, - SmallPtrSet &Visited) { + bool isUnreachableAlongAllPathsStartingAt( + SILBasicBlock *StartBlock, SmallPtrSetImpl &Visited) { if (isa(StartBlock->getTerminator())) return true; else if (isa(StartBlock->getTerminator())) @@ -4739,10 +4812,10 @@ void SILProperty::verify(const SILModule &M) const { ->getCanonicalType(sig); auto leafTy = decl->getStorageInterfaceType()->getReferenceStorageReferent() ->getCanonicalType(sig); - SubstitutionList subs; + SubstitutionMap subs; if (sig) { auto env = dc->getGenericEnvironmentOfContext(); - subs = env->getForwardingSubstitutions(); + subs = env->getForwardingSubstitutionMap(); baseTy = env->mapTypeIntoContext(baseTy)->getCanonicalType(); leafTy = env->mapTypeIntoContext(leafTy)->getCanonicalType(); } diff --git a/lib/SIL/TypeLowering.cpp b/lib/SIL/TypeLowering.cpp index fd15e4772a744..fe78eed41f346 100644 --- a/lib/SIL/TypeLowering.cpp +++ b/lib/SIL/TypeLowering.cpp @@ -153,26 +153,11 @@ CaptureKind TypeConverter::getDeclCaptureKind(CapturedValue capture) { llvm_unreachable("function-like captures should have been lowered away"); } -enum class LoweredTypeKind { - /// Trivial and loadable. - Trivial, +using RecursiveProperties = TypeLowering::RecursiveProperties; - /// A reference type. - Reference, - - /// An aggregate type that contains references (potentially recursively). - AggWithReference, - - /// Non-trivial and not loadable. - AddressOnly, - - /// Trivial and not loadable. - TrivialAddressOnly -}; - -static LoweredTypeKind classifyType(CanType type, SILModule &M, - CanGenericSignature sig, - ResilienceExpansion expansion); +static RecursiveProperties +classifyType(CanType type, SILModule &M, CanGenericSignature sig, + ResilienceExpansion expansion); namespace { /// A CRTP helper class for doing things that depends on type @@ -190,13 +175,36 @@ namespace { public: // The subclass should implement: - // RetTy handleAddressOnly(CanType); - // RetTy handleReference(CanType); + // // Trivial, fixed-layout, and non-address-only. // RetTy handleTrivial(CanType); - // RetTy handleTrivialAddressOnly(CanType); - // In addition, if it does not override visitTupleType - // and visitAnyStructType, it should also implement: - // RetTy handleAggWithReference(CanType); + // // A reference type. + // RetTy handleReference(CanType); + // // Non-trivial and address-only. + // RetTy handleAddressOnly(CanType, RecursiveProperties properties); + // and, if it doesn't override handleTupleType, + // // An aggregate type that's non-trivial. + // RetTy handleNonTrivialAggregate(CanType, IsFixedABI_t fixed); + // + // Alternatively, it can just implement: + // RetTy handle(CanType, RecursiveProperties properties); + + /// Handle a trivial, fixed-size, loadable type. + RetTy handleTrivial(CanType type) { + return asImpl().handle(type, RecursiveProperties()); + } + + RetTy handleReference(CanType type) { + return asImpl().handle(type, RecursiveProperties::forReference()); + } + + RetTy handleAddressOnly(CanType type, RecursiveProperties properties) { + return asImpl().handle(type, properties); + } + + RetTy handleNonTrivialAggregate(CanType type, + RecursiveProperties properties) { + return asImpl().handle(type, properties); + } #define IMPL(TYPE, LOWERING) \ RetTy visit##TYPE##Type(Can##TYPE##Type type) { \ @@ -209,14 +217,21 @@ namespace { IMPL(BuiltinNativeObject, Reference) IMPL(BuiltinBridgeObject, Reference) IMPL(BuiltinUnknownObject, Reference) - IMPL(BuiltinUnsafeValueBuffer, AddressOnly) IMPL(BuiltinVector, Trivial) IMPL(SILToken, Trivial) IMPL(Class, Reference) IMPL(BoundGenericClass, Reference) IMPL(AnyMetatype, Trivial) IMPL(Module, Trivial) - + +#undef IMPL + + RetTy visitBuiltinUnsafeValueBufferType( + CanBuiltinUnsafeValueBufferType type) { + return asImpl().handleAddressOnly(type, {IsNotTrivial, IsFixedABI, + IsAddressOnly}); + } + RetTy visitAnyFunctionType(CanAnyFunctionType type) { switch (type->getRepresentation()) { case AnyFunctionType::Representation::Swift: @@ -240,8 +255,6 @@ namespace { return asImpl().handleTrivial(type); } -#undef IMPL - RetTy visitLValueType(CanLValueType type) { llvm_unreachable("shouldn't get an l-value type here"); } @@ -268,7 +281,8 @@ namespace { return asImpl().visit(genericSig->getConcreteType(type) ->getCanonicalType()); } else { - return asImpl().handleAddressOnly(type); + return asImpl().handleAddressOnly(type, + RecursiveProperties::forOpaque()); } } llvm_unreachable("should have substituted dependent type into context"); @@ -330,11 +344,15 @@ namespace { } RetTy visitAddressOnlyUnownedStorageType(CanUnownedStorageType type) { - return asImpl().handleAddressOnly(type); + return asImpl().handleAddressOnly(type, {IsNotTrivial, + IsFixedABI, + IsAddressOnly}); } RetTy visitWeakStorageType(CanWeakStorageType type) { - return asImpl().handleAddressOnly(type); + return asImpl().handleAddressOnly(type, {IsNotTrivial, + IsFixedABI, + IsAddressOnly}); } RetTy visitArchetypeType(CanArchetypeType type) { @@ -349,13 +367,14 @@ namespace { } if (LayoutInfo->isAddressOnlyTrivial()) { - return asImpl().handleTrivialAddressOnly(type); + return asImpl().handleAddressOnly(type, + {IsTrivial, IsNotFixedABI, IsAddressOnly}); } if (LayoutInfo->isRefCounted()) return asImpl().handleReference(type); } - return asImpl().handleAddressOnly(type); + return asImpl().handleAddressOnly(type, RecursiveProperties::forOpaque()); } RetTy visitExistentialType(CanType type) { @@ -365,7 +384,9 @@ namespace { llvm_unreachable("not an existential type?!"); // Opaque existentials are address-only. case ExistentialRepresentation::Opaque: - return asImpl().handleAddressOnly(type); + return asImpl().handleAddressOnly(type, {IsNotTrivial, + IsFixedABI, + IsAddressOnly}); // Class-constrained and boxed existentials are refcounted. case ExistentialRepresentation::Class: case ExistentialRepresentation::Boxed: @@ -402,26 +423,11 @@ namespace { // Tuples depend on their elements. RetTy visitTupleType(CanTupleType type) { - bool hasReference = false; + RecursiveProperties props; for (auto eltType : type.getElementTypes()) { - switch (classifyType(eltType, M, Sig, Expansion)) { - case LoweredTypeKind::Trivial: - continue; - case LoweredTypeKind::TrivialAddressOnly: - return asImpl().handleTrivialAddressOnly(type); - case LoweredTypeKind::AddressOnly: - return asImpl().handleAddressOnly(type); - case LoweredTypeKind::Reference: - case LoweredTypeKind::AggWithReference: - hasReference = true; - continue; - } - llvm_unreachable("bad type classification"); + props.addSubobject(classifyType(eltType, M, Sig, Expansion)); } - - if (hasReference) - return asImpl().handleAggWithReference(type); - return asImpl().handleTrivial(type); + return asImpl().handleAggregateByProperties(type, props); } RetTy visitDynamicSelfType(CanDynamicSelfType type) { @@ -430,43 +436,39 @@ namespace { RetTy visitSILBlockStorageType(CanSILBlockStorageType type) { // Should not be loaded. - return asImpl().handleAddressOnly(type); + return asImpl().handleAddressOnly(type, {IsNotTrivial, IsFixedABI, + IsAddressOnly}); } RetTy visitSILBoxType(CanSILBoxType type) { // Should not be loaded. return asImpl().handleReference(type); } + + RetTy handleAggregateByProperties(CanType type, RecursiveProperties props) { + if (props.isAddressOnly()) { + return asImpl().handleAddressOnly(type, props); + } + assert(props.isFixedABI() && "unsupported combination for now"); + if (props.isTrivial()) { + return asImpl().handleTrivial(type); + } + return asImpl().handleNonTrivialAggregate(type, props); + } }; class TypeClassifier : - public TypeClassifierBase { + public TypeClassifierBase { public: TypeClassifier(SILModule &M, CanGenericSignature Sig, ResilienceExpansion Expansion) : TypeClassifierBase(M, Sig, Expansion) {} - LoweredTypeKind handleReference(CanType type) { - return LoweredTypeKind::Reference; - } - LoweredTypeKind handleAggWithReference(CanType type) { - return LoweredTypeKind::AggWithReference; - } - LoweredTypeKind - handleTrivial(CanType type) { - return LoweredTypeKind::Trivial; - } - - LoweredTypeKind - handleTrivialAddressOnly(CanType type) { - return LoweredTypeKind::TrivialAddressOnly; + RecursiveProperties handle(CanType type, RecursiveProperties properties) { + return properties; } - LoweredTypeKind handleAddressOnly(CanType type) { - return LoweredTypeKind::AddressOnly; - } - - LoweredTypeKind visitAnyEnumType(CanType type, EnumDecl *D) { + RecursiveProperties visitAnyEnumType(CanType type, EnumDecl *D) { // We have to look through optionals here without grabbing the // type lowering because the way that optionals are reabstracted // can trip recursion checks if we try to build a lowered type. @@ -480,7 +482,7 @@ namespace { return handleClassificationFromLowering(type, lowering); } - LoweredTypeKind visitAnyStructType(CanType type, StructDecl *D) { + RecursiveProperties visitAnyStructType(CanType type, StructDecl *D) { // Consult the type lowering. type = getSubstitutedTypeForTypeLowering(type); auto &lowering = M.Types.getTypeLowering(type); @@ -504,20 +506,16 @@ namespace { return type; } - LoweredTypeKind handleClassificationFromLowering(CanType type, + RecursiveProperties handleClassificationFromLowering(CanType type, const TypeLowering &lowering) { - if (lowering.isAddressOnly()) - return handleAddressOnly(type); - if (lowering.isTrivial()) - return handleTrivial(type); - return handleAggWithReference(type); + return handle(type, lowering.getRecursiveProperties()); } }; } // end anonymous namespace -static LoweredTypeKind classifyType(CanType type, SILModule &M, - CanGenericSignature sig, - ResilienceExpansion expansion) { +static RecursiveProperties classifyType(CanType type, SILModule &M, + CanGenericSignature sig, + ResilienceExpansion expansion) { assert(!type->hasError() && "Error types should not appear in type-checked AST"); @@ -530,8 +528,7 @@ static LoweredTypeKind classifyType(CanType type, SILModule &M, bool SILType::isAddressOnly(CanType type, SILModule &M, CanGenericSignature sig, ResilienceExpansion expansion) { - return classifyType(type, M, sig, expansion) - == LoweredTypeKind::AddressOnly; + return classifyType(type, M, sig, expansion).isAddressOnly(); } namespace { @@ -540,10 +537,9 @@ namespace { /// opaque values are passed by value. class LoadableTypeLowering : public TypeLowering { protected: - LoadableTypeLowering(SILType type, IsTrivial_t isTrivial, - IsAddressOnly_t isAddressOnly, + LoadableTypeLowering(SILType type, RecursiveProperties properties, IsReferenceCounted_t isRefCounted) - : TypeLowering(type, isTrivial, isAddressOnly, isRefCounted) {} + : TypeLowering(type, properties, isRefCounted) {} public: void emitDestroyAddress(SILBuilder &B, SILLocation loc, @@ -565,11 +561,11 @@ namespace { } }; - /// A class for trivial, loadable types. + /// A class for trivial, fixed-layout, loadable types. class TrivialTypeLowering final : public LoadableTypeLowering { public: TrivialTypeLowering(SILType type) - : LoadableTypeLowering(type, IsTrivial, IsNotAddressOnly, + : LoadableTypeLowering(type, {IsTrivial, IsFixedABI, IsNotAddressOnly}, IsNotReferenceCounted) {} SILValue emitLoadOfCopy(SILBuilder &B, SILLocation loc, SILValue addr, @@ -632,9 +628,18 @@ namespace { class NonTrivialLoadableTypeLowering : public LoadableTypeLowering { public: NonTrivialLoadableTypeLowering(SILType type, - IsAddressOnly_t isAddressOnly, IsReferenceCounted_t isRefCounted) - : LoadableTypeLowering(type, IsNotTrivial, isAddressOnly, isRefCounted) {} + : NonTrivialLoadableTypeLowering(type, + {IsNotTrivial, IsFixedABI, IsNotAddressOnly}, + isRefCounted) {} + + /// This constructor is necessary because of opaque-values. + NonTrivialLoadableTypeLowering(SILType type, + RecursiveProperties properties, + IsReferenceCounted_t isRefCounted) + : LoadableTypeLowering(type, properties, isRefCounted) { + assert(!properties.isTrivial()); + } SILValue emitLoadOfCopy(SILBuilder &B, SILLocation loc, SILValue addr, IsTake_t isTake) const override { @@ -725,7 +730,6 @@ namespace { public: LoadableAggTypeLowering(CanType type) : NonTrivialLoadableTypeLowering(SILType::getPrimitiveObjectType(type), - IsNotAddressOnly, IsNotReferenceCounted) { } @@ -910,7 +914,6 @@ namespace { public: LoadableEnumTypeLowering(CanType type) : NonTrivialLoadableTypeLowering(SILType::getPrimitiveObjectType(type), - IsNotAddressOnly, IsNotReferenceCounted) {} SILValue emitCopyValue(SILBuilder &B, SILLocation loc, @@ -948,10 +951,9 @@ namespace { class LeafLoadableTypeLowering : public NonTrivialLoadableTypeLowering { public: - LeafLoadableTypeLowering(SILType type, - IsAddressOnly_t isAddressOnly, + LeafLoadableTypeLowering(SILType type, RecursiveProperties properties, IsReferenceCounted_t isRefCounted) - : NonTrivialLoadableTypeLowering(type, isAddressOnly, isRefCounted) {} + : NonTrivialLoadableTypeLowering(type, properties, isRefCounted) {} SILValue emitLoweredCopyValue(SILBuilder &B, SILLocation loc, SILValue value, @@ -970,7 +972,8 @@ namespace { class ReferenceTypeLowering : public LeafLoadableTypeLowering { public: ReferenceTypeLowering(SILType type) - : LeafLoadableTypeLowering(type, IsNotAddressOnly, IsReferenceCounted) {} + : LeafLoadableTypeLowering(type, RecursiveProperties::forReference(), + IsReferenceCounted) {} SILValue emitCopyValue(SILBuilder &B, SILLocation loc, SILValue value) const override { @@ -998,7 +1001,8 @@ namespace { class LoadableUnownedTypeLowering final : public LeafLoadableTypeLowering { public: LoadableUnownedTypeLowering(SILType type) - : LeafLoadableTypeLowering(type, IsNotAddressOnly, IsReferenceCounted) {} + : LeafLoadableTypeLowering(type, RecursiveProperties::forReference(), + IsReferenceCounted) {} SILValue emitCopyValue(SILBuilder &B, SILLocation loc, SILValue value) const override { @@ -1022,8 +1026,8 @@ namespace { /// A class for non-trivial, address-only types. class AddressOnlyTypeLowering : public TypeLowering { public: - AddressOnlyTypeLowering(SILType type) - : TypeLowering(type, IsNotTrivial, IsAddressOnly, IsNotReferenceCounted) + AddressOnlyTypeLowering(SILType type, RecursiveProperties properties) + : TypeLowering(type, properties, IsNotReferenceCounted) {} void emitCopyInto(SILBuilder &B, SILLocation loc, @@ -1055,12 +1059,14 @@ namespace { void emitDestroyAddress(SILBuilder &B, SILLocation loc, SILValue addr) const override { - B.emitDestroyAddrAndFold(loc, addr); + if (!isTrivial()) + B.emitDestroyAddrAndFold(loc, addr); } void emitDestroyRValue(SILBuilder &B, SILLocation loc, SILValue value) const override { - B.emitDestroyAddrAndFold(loc, value); + if (!isTrivial()) + B.emitDestroyAddrAndFold(loc, value); } SILValue emitCopyValue(SILBuilder &B, SILLocation loc, @@ -1090,7 +1096,8 @@ namespace { class UnsafeValueBufferTypeLowering : public AddressOnlyTypeLowering { public: UnsafeValueBufferTypeLowering(SILType type) - : AddressOnlyTypeLowering(type) {} + : AddressOnlyTypeLowering(type, + {IsNotTrivial, IsFixedABI, IsAddressOnly}) {} void emitCopyInto(SILBuilder &B, SILLocation loc, SILValue src, SILValue dest, IsTake_t isTake, @@ -1116,8 +1123,8 @@ namespace { /// FIXME: When you remove an unreachable, just delete the method. class OpaqueValueTypeLowering : public LeafLoadableTypeLowering { public: - OpaqueValueTypeLowering(SILType type) - : LeafLoadableTypeLowering(type, IsAddressOnly, IsNotReferenceCounted) {} + OpaqueValueTypeLowering(SILType type, RecursiveProperties properties) + : LeafLoadableTypeLowering(type, properties, IsNotReferenceCounted) {} void emitCopyInto(SILBuilder &B, SILLocation loc, SILValue src, SILValue dest, IsTake_t isTake, @@ -1149,71 +1156,6 @@ namespace { } }; - /// A class for trivial, address-only types. - class AddressOnlyTrivialTypeLowering : public TypeLowering { - public: - AddressOnlyTrivialTypeLowering(SILType type) - : TypeLowering(type, IsTrivial, IsAddressOnly, IsNotReferenceCounted) - {} - - void emitCopyInto(SILBuilder &B, SILLocation loc, - SILValue src, SILValue dest, IsTake_t isTake, - IsInitialization_t isInit) const override { - B.createCopyAddr(loc, src, dest, isTake, isInit); - } - - SILValue emitLoadOfCopy(SILBuilder &B, SILLocation loc, - SILValue addr, IsTake_t isTake) const override { - llvm_unreachable("calling emitLoadOfCopy on non-loadable type"); - } - - void emitStoreOfCopy(SILBuilder &B, SILLocation loc, - SILValue newValue, SILValue addr, - IsInitialization_t isInit) const override { - llvm_unreachable("calling emitStoreOfCopy on non-loadable type"); - } - - void emitStore(SILBuilder &B, SILLocation loc, SILValue value, - SILValue addr, StoreOwnershipQualifier qual) const override { - llvm_unreachable("calling emitStore on non-loadable type"); - } - - SILValue emitLoad(SILBuilder &B, SILLocation loc, SILValue addr, - LoadOwnershipQualifier qual) const override { - llvm_unreachable("calling emitLoad on non-loadable type"); - } - - void emitDestroyAddress(SILBuilder &B, SILLocation loc, - SILValue addr) const override { - } - - void emitDestroyRValue(SILBuilder &B, SILLocation loc, - SILValue value) const override { - } - - SILValue emitCopyValue(SILBuilder &B, SILLocation loc, - SILValue value) const override { - llvm_unreachable("type is not loadable!"); - } - - SILValue emitLoweredCopyValue(SILBuilder &B, SILLocation loc, - SILValue value, - TypeExpansionKind style) const override { - llvm_unreachable("type is not loadable!"); - } - - void emitDestroyValue(SILBuilder &B, SILLocation loc, - SILValue value) const override { - llvm_unreachable("type is not loadable!"); - } - - void emitLoweredDestroyValue(SILBuilder &B, SILLocation loc, SILValue value, - TypeExpansionKind style) const override { - llvm_unreachable("type is not loadable!"); - } - }; - - /// Build the appropriate TypeLowering subclass for the given type, /// which is assumed to already have been lowered. class LowerType @@ -1233,24 +1175,19 @@ namespace { return new (TC, Dependent) TrivialTypeLowering(silType); } - const TypeLowering * - handleTrivialAddressOnly(CanType type) { - auto silType = SILType::getPrimitiveObjectType(type); - return new (TC, Dependent) AddressOnlyTrivialTypeLowering(silType); - } - const TypeLowering *handleReference(CanType type) { auto silType = SILType::getPrimitiveObjectType(type); return new (TC, Dependent) ReferenceTypeLowering(silType); } - const TypeLowering *handleAddressOnly(CanType type) { + const TypeLowering *handleAddressOnly(CanType type, + RecursiveProperties properties) { if (SILModuleConventions(M).useLoweredAddresses()) { auto silType = SILType::getPrimitiveAddressType(type); - return new (TC, Dependent) AddressOnlyTypeLowering(silType); + return new (TC, Dependent) AddressOnlyTypeLowering(silType, properties); } auto silType = SILType::getPrimitiveObjectType(type); - return new (TC, Dependent) OpaqueValueTypeLowering(silType); + return new (TC, Dependent) OpaqueValueTypeLowering(silType, properties); } const TypeLowering * @@ -1266,19 +1203,14 @@ namespace { } const TypeLowering *visitTupleType(CanTupleType tupleType) { - bool hasOnlyTrivialChildren = true; - + RecursiveProperties properties; for (auto eltType : tupleType.getElementTypes()) { auto &lowering = TC.getTypeLowering(eltType); - if (lowering.isAddressOnly()) - return handleAddressOnly(tupleType); - hasOnlyTrivialChildren &= lowering.isTrivial(); + properties.addSubobject(lowering.getRecursiveProperties()); } - if (hasOnlyTrivialChildren) - return handleTrivial(tupleType); - - return new (TC, Dependent) LoadableTupleTypeLowering(tupleType); + return handleAggregateByProperties(tupleType, + properties); } const TypeLowering *visitAnyStructType(CanType structType, StructDecl *D) { @@ -1286,37 +1218,27 @@ namespace { // For now, if the type does not have a fixed layout in all resilience // domains, we will treat it as address-only in SIL. if (D->isResilient(M.getSwiftModule(), Expansion)) - return handleAddressOnly(structType); + return handleAddressOnly(structType, + RecursiveProperties::forOpaque()); // Classify the type according to its stored properties. - bool trivial = true; + RecursiveProperties properties; for (auto field : D->getStoredProperties()) { auto substFieldType = structType->getTypeOfMember(D->getModuleContext(), field, nullptr); - switch (classifyType(substFieldType->getCanonicalType(), - M, Sig, Expansion)) { - case LoweredTypeKind::TrivialAddressOnly: - case LoweredTypeKind::AddressOnly: - return handleAddressOnly(structType); - case LoweredTypeKind::AggWithReference: - case LoweredTypeKind::Reference: - trivial = false; - break; - case LoweredTypeKind::Trivial: - break; - } + properties.addSubobject(classifyType(substFieldType->getCanonicalType(), + M, Sig, Expansion)); } - if (trivial) - return handleTrivial(structType); - return new (TC, Dependent) LoadableStructTypeLowering(structType); + return handleAggregateByProperties(structType, + properties); } const TypeLowering *visitAnyEnumType(CanType enumType, EnumDecl *D) { // For now, if the type does not have a fixed layout in all resilience // domains, we will treat it as address-only in SIL. if (D->isResilient(M.getSwiftModule(), Expansion)) - return handleAddressOnly(enumType); + return handleAddressOnly(enumType, RecursiveProperties::forOpaque()); // If the whole enum is indirect, we lower it as if all payload // cases were indirect. This means a fixed-layout indirect enum @@ -1327,18 +1249,16 @@ namespace { return new (TC, Dependent) LoadableEnumTypeLowering(enumType); } - // If any of the enum elements have address-only data, the enum is - // address-only. - bool trivial = true; + // Accumulate the properties of all direct payloads. + RecursiveProperties properties; for (auto elt : D->getAllElements()) { - // No-payload elements do not affect address-only-ness. + // No-payload elements do not affect any recursive properties. if (!elt->hasAssociatedValues()) continue; - // Indirect elements make the type nontrivial, but don't affect - // address-only-ness. + // Indirect elements only make the type nontrivial. if (elt->isIndirect()) { - trivial = false; + properties.setNonTrivial(); continue; } @@ -1347,22 +1267,24 @@ namespace { elt->getArgumentInterfaceType()) ->getCanonicalType(); - switch (classifyType(substEltType, M, Sig, Expansion)) { - case LoweredTypeKind::TrivialAddressOnly: - case LoweredTypeKind::AddressOnly: - return handleAddressOnly(enumType); - case LoweredTypeKind::AggWithReference: - case LoweredTypeKind::Reference: - trivial = false; - break; - case LoweredTypeKind::Trivial: - break; - } - + properties.addSubobject(classifyType(substEltType, M, Sig, Expansion)); + } + + return handleAggregateByProperties(enumType, + properties); + } + + template + const TypeLowering *handleAggregateByProperties(CanType type, + RecursiveProperties props) { + if (props.isAddressOnly()) { + return handleAddressOnly(type, props); + } + assert(props.isFixedABI()); + if (props.isTrivial()) { + return handleTrivial(type); } - if (trivial) - return handleTrivial(enumType); - return new (TC, Dependent) LoadableEnumTypeLowering(enumType); + return new (TC, Dependent) LoadableLoweringClass(type); } }; } // end anonymous namespace @@ -1378,7 +1300,7 @@ TypeConverter::~TypeConverter() { // Destroy only the unique entries. CanType srcType = ti.first.OrigType; if (!srcType) continue; - CanType mappedType = ti.second->getLoweredType().getSwiftRValueType(); + CanType mappedType = ti.second->getLoweredType().getASTType(); if (srcType == mappedType || isa(srcType)) ti.second->~TypeLowering(); } @@ -1454,7 +1376,7 @@ static CanTupleType getLoweredTupleType(TypeConverter &tc, // the original type of the element --- the actual archetype // doesn't matter, just the abstraction pattern. SILType silType = tc.getLoweredType(origEltType, substEltType); - CanType loweredSubstEltType = silType.getSwiftRValueType(); + CanType loweredSubstEltType = silType.getASTType(); changed = (changed || substEltType != loweredSubstEltType); @@ -1477,7 +1399,7 @@ static CanType getLoweredOptionalType(TypeConverter &tc, CanType loweredObjectType = tc.getLoweredType(origType.getOptionalObjectType(), substObjectType) - .getSwiftRValueType(); + .getASTType(); // If the object type didn't change, we don't have to rebuild anything. if (loweredObjectType == substObjectType) { @@ -1494,7 +1416,7 @@ static CanType getLoweredReferenceStorageType(TypeConverter &tc, CanType loweredReferentType = tc.getLoweredType(origType.getReferenceStorageReferentType(), substType.getReferentType()) - .getSwiftRValueType(); + .getASTType(); if (loweredReferentType == substType.getReferentType()) return substType; @@ -1664,7 +1586,7 @@ CanType TypeConverter::getLoweredRValueType(AbstractionPattern origType, } const TypeLowering &TypeConverter::getTypeLowering(SILType type) { - auto loweredType = type.getSwiftRValueType(); + auto loweredType = type.getASTType(); auto key = getTypeKey(AbstractionPattern(getCurGenericContext(), loweredType), loweredType); @@ -2000,7 +1922,7 @@ SILType TypeConverter::getSubstitutedStorageType(AbstractStorageDecl *value, } SILType silSubstType = getLoweredType(origType, substType).getAddressType(); - substType = silSubstType.getSwiftRValueType(); + substType = silSubstType.getASTType(); // Type substitution preserves structural type structure, and the // type-of-reference is only different in the outermost structural @@ -2044,7 +1966,7 @@ void TypeConverter::popGenericContext(CanGenericSignature sig) { // Destroy only the unique entries. CanType srcType = ti.first.OrigType; if (!srcType) continue; - CanType mappedType = ti.second->getLoweredType().getSwiftRValueType(); + CanType mappedType = ti.second->getLoweredType().getASTType(); if (srcType == mappedType) ti.second->~TypeLowering(); } @@ -2085,7 +2007,7 @@ CanSILFunctionType TypeConverter::getMaterializeForSetCallbackType( // selfMetatypeType. if (auto metatype = canSelfType->getAs()) { if (!metatype->hasRepresentation()) - canSelfType = getLoweredType(metatype).getSwiftRValueType(); + canSelfType = getLoweredType(metatype).getASTType(); } } @@ -2294,8 +2216,8 @@ TypeConverter::checkForABIDifferences(SILType type1, SILType type2, // Classes, class-constrained archetypes, and pure-ObjC existential types // all have single retainable pointer representation; optionality change // is allowed. - if (type1.getSwiftRValueType()->satisfiesClassConstraint() && - type2.getSwiftRValueType()->satisfiesClassConstraint()) + if (type1.getASTType()->satisfiesClassConstraint() && + type2.getASTType()->satisfiesClassConstraint()) return ABIDifference::Trivial; // Function parameters are ABI compatible if their differences are @@ -2468,14 +2390,12 @@ TypeConverter::getInterfaceBoxTypeForCapture(ValueDecl *captured, [&](SubstitutableType *type) -> Type { return signature->getCanonicalTypeInContext(type); }, - [](Type depTy, Type replacementTy, ProtocolType *conformedTy) + [](Type depTy, Type replacementTy, ProtocolDecl *proto) -> ProtocolConformanceRef { - return ProtocolConformanceRef(conformedTy->getDecl()); + return ProtocolConformanceRef(proto); }); - SmallVector genericArgs; - signature->getSubstitutions(subMap, genericArgs); - auto boxTy = SILBoxType::get(C, layout, genericArgs); + auto boxTy = SILBoxType::get(C, layout, subMap); #ifndef NDEBUG // FIXME: Map the box type out of context when asserting the field so // we don't need to push a GenericContextScope (which really ought to die). @@ -2490,7 +2410,7 @@ TypeConverter::getInterfaceBoxTypeForCapture(ValueDecl *captured, ->getCanonicalType()); } assert(contextBoxTy->getLayout()->getFields().size() == 1 - && contextBoxTy->getFieldType(M, 0).getSwiftRValueType() + && contextBoxTy->getFieldType(M, 0).getASTType() == loweredContextType && "box field type doesn't match capture!"); #endif @@ -2536,28 +2456,26 @@ CanSILBoxType TypeConverter::getBoxTypeForEnumElement(SILType enumType, if (boxSignature == CanGenericSignature()) { auto eltIntfTy = elt->getArgumentInterfaceType(); - auto boxVarTy = getLoweredType(eltIntfTy).getSwiftRValueType(); + auto boxVarTy = getLoweredType(eltIntfTy).getASTType(); auto layout = SILLayout::get(C, nullptr, SILField(boxVarTy, true)); return SILBoxType::get(C, layout, {}); } // Use the enum's signature for the box type. - auto boundEnum = enumType.getSwiftRValueType(); + auto boundEnum = enumType.getASTType(); // Lower the enum element's argument in the box's context. auto eltIntfTy = elt->getArgumentInterfaceType(); GenericContextScope scope(*this, boxSignature); auto boxVarTy = getLoweredType(getAbstractionPattern(elt), eltIntfTy) - .getSwiftRValueType(); + .getASTType(); auto layout = SILLayout::get(C, boxSignature, SILField(boxVarTy, true)); // Instantiate the layout with enum's substitution list. auto subMap = boundEnum->getContextSubstitutionMap( M.getSwiftModule(), enumDecl, enumDecl->getGenericEnvironment()); - SmallVector genericArgs; - boxSignature->getSubstitutions(subMap, genericArgs); - auto boxTy = SILBoxType::get(C, layout, genericArgs); + auto boxTy = SILBoxType::get(C, layout, subMap); return boxTy; } diff --git a/lib/SIL/ValueOwnershipKindClassifier.cpp b/lib/SIL/ValueOwnershipKindClassifier.cpp index d201e3f66bdee..40b25da50ad3b 100644 --- a/lib/SIL/ValueOwnershipKindClassifier.cpp +++ b/lib/SIL/ValueOwnershipKindClassifier.cpp @@ -35,6 +35,7 @@ CONSTANT_OWNERSHIP_INST(Owned, AllocRef) CONSTANT_OWNERSHIP_INST(Owned, AllocRefDynamic) CONSTANT_OWNERSHIP_INST(Trivial, AllocValueBuffer) CONSTANT_OWNERSHIP_INST(Owned, CopyBlock) +CONSTANT_OWNERSHIP_INST(Owned, CopyBlockWithoutEscaping) CONSTANT_OWNERSHIP_INST(Owned, CopyValue) CONSTANT_OWNERSHIP_INST(Owned, CopyUnownedValue) CONSTANT_OWNERSHIP_INST(Owned, LoadUnowned) @@ -472,9 +473,13 @@ CONSTANT_OWNERSHIP_BUILTIN(Trivial, BridgeToRawPointer) CONSTANT_OWNERSHIP_BUILTIN(Unowned, BridgeFromRawPointer) CONSTANT_OWNERSHIP_BUILTIN(Unowned, CastReference) CONSTANT_OWNERSHIP_BUILTIN(Trivial, AddressOf) +CONSTANT_OWNERSHIP_BUILTIN(Trivial, AddressOfBorrow) CONSTANT_OWNERSHIP_BUILTIN(Trivial, GepRaw) CONSTANT_OWNERSHIP_BUILTIN(Trivial, Gep) CONSTANT_OWNERSHIP_BUILTIN(Trivial, GetTailAddr) +CONSTANT_OWNERSHIP_BUILTIN(Trivial, PerformInstantaneousReadAccess) +CONSTANT_OWNERSHIP_BUILTIN(Trivial, BeginUnpairedModifyAccess) +CONSTANT_OWNERSHIP_BUILTIN(Trivial, EndUnpairedAccess) CONSTANT_OWNERSHIP_BUILTIN(Trivial, OnFastPath) CONSTANT_OWNERSHIP_BUILTIN(Trivial, IsUnique) CONSTANT_OWNERSHIP_BUILTIN(Trivial, IsUniqueOrPinned) diff --git a/lib/SILGen/ArgumentSource.cpp b/lib/SILGen/ArgumentSource.cpp index 765a145e08be9..808da5bbb4591 100644 --- a/lib/SILGen/ArgumentSource.cpp +++ b/lib/SILGen/ArgumentSource.cpp @@ -356,7 +356,7 @@ void ArgumentSource::forwardInto(SILGenFunction &SGF, // RValue about the formal type (by using the lowered type) because // we're emitting into an abstracted value, which RValue doesn't // really handle. - auto substLoweredType = destTL.getLoweredType().getSwiftRValueType(); + auto substLoweredType = destTL.getLoweredType().getASTType(); RValue(SGF, loc, substLoweredType, outputValue).forwardInto(SGF, loc, dest); } diff --git a/lib/SILGen/Cleanup.h b/lib/SILGen/Cleanup.h index 6d66c182a4c90..28dc2130d1587 100644 --- a/lib/SILGen/Cleanup.h +++ b/lib/SILGen/Cleanup.h @@ -129,7 +129,6 @@ class LLVM_LIBRARY_VISIBILITY CleanupManager { friend class CleanupStateRestorationScope; friend class SharedBorrowFormalEvaluation; friend class FormalEvaluationScope; - friend class PostponedCleanup; public: CleanupManager(SILGenFunction &SGF) diff --git a/lib/SILGen/LValue.h b/lib/SILGen/LValue.h index dd6e015f8334d..2a7c99923293e 100644 --- a/lib/SILGen/LValue.h +++ b/lib/SILGen/LValue.h @@ -431,7 +431,7 @@ class LValue { /// Add a member component to the access path of this lvalue. void addMemberComponent(SILGenFunction &SGF, SILLocation loc, AbstractStorageDecl *storage, - SubstitutionList subs, + SubstitutionMap subs, LValueOptions options, bool isSuper, AccessKind accessKind, @@ -442,7 +442,7 @@ class LValue { void addMemberVarComponent(SILGenFunction &SGF, SILLocation loc, VarDecl *var, - SubstitutionList subs, + SubstitutionMap subs, LValueOptions options, bool isSuper, AccessKind accessKind, @@ -452,7 +452,7 @@ class LValue { void addMemberSubscriptComponent(SILGenFunction &SGF, SILLocation loc, SubscriptDecl *subscript, - SubstitutionList subs, + SubstitutionMap subs, LValueOptions options, bool isSuper, AccessKind accessKind, diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index e17aca47b24a6..4c5e080d7aa45 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -29,6 +29,7 @@ #include "swift/SIL/PrettyStackTrace.h" #include "swift/SIL/SILArgument.h" #include "swift/SIL/SILDebugScope.h" +#include "swift/SIL/SILProfiler.h" #include "swift/Subsystems.h" #include "llvm/ProfileData/InstrProfReader.h" #include "llvm/Support/Debug.h" @@ -483,6 +484,34 @@ static bool hasSILBody(FuncDecl *fd) { return fd->getBody(/*canSynthesize=*/false); } +static bool haveProfiledAssociatedFunction(SILDeclRef constant) { + return constant.isDefaultArgGenerator() || constant.isForeign || + constant.isCurried; +} + +SILProfiler * +SILGenModule::getOrCreateProfilerForConstructors(DeclContext *ctx, + ConstructorDecl *cd) { + const auto &Opts = M.getOptions(); + if (!Opts.GenerateProfile && Opts.UseProfile.empty()) + return nullptr; + + // Profile nominal types and extensions separately, as they may live in + // distinct files. For extensions, just pass in the constructor, because + // there are no stored property initializers to visit. + Decl *decl = nullptr; + if (ctx->isExtensionContext()) + decl = cd; + else + decl = ctx->getAsNominalTypeOrNominalTypeExtensionContext(); + assert(decl && "No decl available for profiling in this context"); + + SILProfiler *&profiler = constructorProfilers[decl]; + if (!profiler) + profiler = SILProfiler::create(M, ForDefinition, decl); + return profiler; +} + SILFunction *SILGenModule::getFunction(SILDeclRef constant, ForDefinition_t forDefinition) { // If we already emitted the function, return it (potentially preparing it @@ -495,38 +524,24 @@ SILFunction *SILGenModule::getFunction(SILDeclRef constant, : (Decl *)nullptr, constant, forDefinition); + // Set up the function for profiling instrumentation. ASTNode profiledNode; - if (constant.hasDecl()) { - if (auto *fd = constant.getFuncDecl()) { - if (hasSILBody(fd)) { - // Set up the function for profiling instrumentation. - F->createProfiler(fd); - profiledNode = fd->getBody(/*canSynthesize=*/false); - } - } - } else if (auto *ace = constant.getAbstractClosureExpr()) { - // Closures inherit profiling metadata and counters from their parent, if - // one exists. If not, they receive a fresh profiler. - if (auto *parentDecl = dyn_cast_or_null( - ace->getInnermostDeclarationDeclContext())) { - SILDeclRef parentConstant(parentDecl, SILDeclRef::Kind::Func); - auto parentIt = emittedFunctions.find(parentConstant); - if (parentIt != emittedFunctions.end()) { - F->setProfiler(parentIt->second->getProfiler()); - profiledNode = ace; - } - } - - if (!profiledNode) { - if (auto *ce = dyn_cast(ace)) { - F->createProfiler(ce); - profiledNode = ce; + if (!haveProfiledAssociatedFunction(constant)) { + if (constant.hasDecl()) { + if (auto *fd = constant.getFuncDecl()) { + if (hasSILBody(fd)) { + F->createProfiler(fd, forDefinition); + profiledNode = fd->getBody(/*canSynthesize=*/false); + } } + } else if (auto *ace = constant.getAbstractClosureExpr()) { + F->createProfiler(ace, forDefinition); + profiledNode = ace; } + // Set the function entry count for PGO. + if (SILProfiler *SP = F->getProfiler()) + F->setEntryCount(SP->getExecutionCount(profiledNode)); } - // Set the function entry count for PGO. - if (SILProfiler *SP = F->getProfiler()) - F->setEntryCount(SP->getExecutionCount(profiledNode)); assert(F && "SILFunction should have been defined"); @@ -709,7 +724,7 @@ void SILGenModule::emitFunction(FuncDecl *fd) { SILDeclRef constant(decl); - bool ForCoverageMapping = M.getOptions().EmitProfileCoverageMapping; + bool ForCoverageMapping = doesASTRequireProfiling(M, fd); emitOrDelayFunction(*this, constant, [this,constant,fd](SILFunction *f){ preEmitFunction(constant, fd, f, fd); @@ -746,42 +761,48 @@ void SILGenModule::emitConstructor(ConstructorDecl *decl) { return; SILDeclRef constant(decl); + DeclContext *declCtx = decl->getDeclContext(); - bool ForCoverageMapping = M.getOptions().EmitProfileCoverageMapping; + bool ForCoverageMapping = doesASTRequireProfiling(M, decl); - if (decl->getDeclContext()->getAsClassOrClassExtensionContext()) { + if (declCtx->getAsClassOrClassExtensionContext()) { // Class constructors have separate entry points for allocation and // initialization. - emitOrDelayFunction(*this, constant, [this,constant,decl](SILFunction *f){ - preEmitFunction(constant, decl, f, decl); - PrettyStackTraceSILFunction X("silgen emitConstructor", f); - f->createProfiler(decl); - SILGenFunction(*this, *f) - .emitClassConstructorAllocator(decl); - postEmitFunction(constant, f); - }, /*forceEmission=*/ForCoverageMapping); + emitOrDelayFunction( + *this, constant, [this, constant, decl](SILFunction *f) { + preEmitFunction(constant, decl, f, decl); + PrettyStackTraceSILFunction X("silgen emitConstructor", f); + SILGenFunction(*this, *f).emitClassConstructorAllocator(decl); + postEmitFunction(constant, f); + }); // If this constructor was imported, we don't need the initializing // constructor to be emitted. if (!decl->hasClangNode()) { SILDeclRef initConstant(decl, SILDeclRef::Kind::Initializer); - emitOrDelayFunction(*this, initConstant, - [this,initConstant,decl](SILFunction *initF){ - preEmitFunction(initConstant, decl, initF, decl); - PrettyStackTraceSILFunction X("silgen constructor initializer", initF); - SILGenFunction(*this, *initF).emitClassConstructorInitializer(decl); - postEmitFunction(initConstant, initF); - }); + emitOrDelayFunction( + *this, initConstant, + [this, initConstant, decl, declCtx](SILFunction *initF) { + preEmitFunction(initConstant, decl, initF, decl); + PrettyStackTraceSILFunction X("silgen constructor initializer", + initF); + initF->setProfiler( + getOrCreateProfilerForConstructors(declCtx, decl)); + SILGenFunction(*this, *initF).emitClassConstructorInitializer(decl); + postEmitFunction(initConstant, initF); + }, + /*forceEmission=*/ForCoverageMapping); } } else { // Struct and enum constructors do everything in a single function. - emitOrDelayFunction(*this, constant, [this,constant,decl](SILFunction *f) { - preEmitFunction(constant, decl, f, decl); - PrettyStackTraceSILFunction X("silgen emitConstructor", f); - f->createProfiler(decl); - SILGenFunction(*this, *f).emitValueConstructor(decl); - postEmitFunction(constant, f); - }, /*forceEmission=*/ForCoverageMapping); + emitOrDelayFunction( + *this, constant, [this, constant, decl, declCtx](SILFunction *f) { + preEmitFunction(constant, decl, f, decl); + PrettyStackTraceSILFunction X("silgen emitConstructor", f); + f->setProfiler(getOrCreateProfilerForConstructors(declCtx, decl)); + SILGenFunction(*this, *f).emitValueConstructor(decl); + postEmitFunction(constant, f); + }); } } @@ -865,7 +886,7 @@ void SILGenModule::emitObjCAllocatorDestructor(ClassDecl *cd, SILFunction *f = getFunction(dealloc, ForDefinition); preEmitFunction(dealloc, dd, f, dd); PrettyStackTraceSILFunction X("silgen emitDestructor -dealloc", f); - f->createProfiler(dd); + f->createProfiler(dd, ForDefinition); SILGenFunction(*this, *f).emitObjCDestructor(dealloc); postEmitFunction(dealloc, f); } @@ -935,7 +956,7 @@ void SILGenModule::emitDestructor(ClassDecl *cd, DestructorDecl *dd) { SILFunction *f = getFunction(deallocator, ForDefinition); preEmitFunction(deallocator, dd, f, dd); PrettyStackTraceSILFunction X("silgen emitDeallocatingDestructor", f); - f->createProfiler(dd); + f->createProfiler(dd, ForDefinition); SILGenFunction(*this, *f).emitDeallocatingDestructor(dd); f->setDebugScope(new (M) SILDebugScope(dd, f)); postEmitFunction(deallocator, f); @@ -981,9 +1002,14 @@ emitStoredPropertyInitialization(PatternBindingDecl *pbd, unsigned i) { auto *init = pbdEntry.getInit(); SILDeclRef constant(var, SILDeclRef::Kind::StoredPropertyInitializer); - emitOrDelayFunction(*this, constant, [this,constant,init](SILFunction *f) { + emitOrDelayFunction(*this, constant, [this,constant,init,var](SILFunction *f) { preEmitFunction(constant, init, f, init); PrettyStackTraceSILFunction X("silgen emitStoredPropertyInitialization", f); + + // Inherit a profiler instance from the constructor. + f->setProfiler( + getOrCreateProfilerForConstructors(var->getDeclContext(), nullptr)); + SILGenFunction(*this, *f).emitGeneratorFunction(constant, init); postEmitFunction(constant, f); }); @@ -1238,9 +1264,9 @@ void SILGenModule::tryEmitPropertyDescriptor(AbstractStorageDecl *decl) { return; } - SubstitutionList subs = {}; + SubstitutionMap subs; if (genericEnv) - subs = genericEnv->getForwardingSubstitutions(); + subs = genericEnv->getForwardingSubstitutionMap(); if (auto sub = dyn_cast(decl)) { for (auto *index : *sub->getIndices()) { @@ -1303,7 +1329,7 @@ void SILGenModule::visitTopLevelCodeDecl(TopLevelCodeDecl *td) { // A single SILFunction may be used to lower multiple top-level decls. When // this happens, fresh profile counters must be assigned to the new decl. TopLevelSGF->F.discardProfiler(); - TopLevelSGF->F.createProfiler(td); + TopLevelSGF->F.createProfiler(td, ForDefinition); TopLevelSGF->emitProfilerIncrement(td->getBody()); @@ -1357,12 +1383,10 @@ void SILGenModule::useConformance(ProtocolConformanceRef conformanceRef) { usedConformances.insert(root); } -void -SILGenModule::useConformancesFromSubstitutions(SubstitutionList subs) { - for (auto &sub : subs) { - for (auto conformance : sub.getConformances()) - useConformance(conformance); - } +void SILGenModule::useConformancesFromSubstitutions( + const SubstitutionMap subs) { + for (auto conf : subs.getConformances()) + useConformance(conf); } namespace { diff --git a/lib/SILGen/SILGen.h b/lib/SILGen/SILGen.h index c6c1c3ac908b6..6cd07f4794751 100644 --- a/lib/SILGen/SILGen.h +++ b/lib/SILGen/SILGen.h @@ -96,6 +96,11 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor { /// The most recent conformance... NormalProtocolConformance *lastEmittedConformance = nullptr; + /// Profiler instances for constructors, grouped by associated decl. + /// Each profiler is shared by all member initializers for a nominal type. + /// Constructors within extensions are profiled separately. + llvm::DenseMap constructorProfilers; + SILFunction *emitTopLevelFunction(SILLocation Loc); size_t anonymousSymbolCounter = 0; @@ -339,7 +344,7 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor { GenericEnvironment *genericEnv, unsigned &baseOperand, bool &needsGenericContext, - SubstitutionList subs, + SubstitutionMap subs, AbstractStorageDecl *storage, ArrayRef indexHashables, CanType baseTy, @@ -421,7 +426,7 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor { void useConformance(ProtocolConformanceRef conformance); /// Mark protocol conformances from the given set of substitutions as used. - void useConformancesFromSubstitutions(SubstitutionList subs); + void useConformancesFromSubstitutions(SubstitutionMap subs); /// Emit a `mark_function_escape` instruction for top-level code when a /// function or closure at top level refers to script globals. @@ -430,12 +435,18 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor { /// Get the substitutions necessary to invoke a non-member (global or local) /// property. - SubstitutionList + SubstitutionMap getNonMemberVarDeclSubstitutions(VarDecl *var); /// Emit a property descriptor for the given storage decl if it needs one. void tryEmitPropertyDescriptor(AbstractStorageDecl *decl); - + + /// Get or create the shared profiler instance for a type's constructors. + /// This takes care to create separate profilers for extensions, which may + /// reside in a different file than the one where the base type is defined. + SILProfiler *getOrCreateProfilerForConstructors(DeclContext *ctx, + ConstructorDecl *cd); + private: /// Emit the deallocator for a class that uses the objc allocator. void emitObjCAllocatorDestructor(ClassDecl *cd, DestructorDecl *dd); diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index 9ee676941b602..039fd166be922 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -178,7 +178,7 @@ static ManagedValue borrowedCastToOriginalSelfType(SILGenFunction &SGF, // If we have a metatype, then we just return the original self value since // metatypes are trivial, so we can avoid ownership concerns. - if (originalSelfType.getSwiftRValueType()->is()) { + if (originalSelfType.is()) { assert(originalSelfType.isTrivial(SGF.getModule()) && "Metatypes should always be trivial"); return ManagedValue::forUnmanaged(originalSelf); @@ -259,6 +259,8 @@ class Callee { const Kind kind; + bool isMaterializeForSet = false; + // Move, don't copy. Callee(const Callee &) = delete; Callee &operator=(const Callee &) = delete; @@ -287,7 +289,7 @@ class Callee { /// The substitutions applied to OrigFormalInterfaceType to produce /// SubstFormalInterfaceType. - SubstitutionList Substitutions; + SubstitutionMap Substitutions; /// The list of values captured by our callee. Optional> Captures; @@ -297,7 +299,7 @@ class Callee { static CanFunctionType getSubstFormalInterfaceType(CanAnyFunctionType substFormalType, - SubstitutionList subs) { + SubstitutionMap subs) { if (auto *gft = substFormalType->getAs()) { return cast( gft->substGenericArgs(subs) @@ -323,7 +325,7 @@ class Callee { Callee(SILGenFunction &SGF, SILDeclRef standaloneFunction, AbstractionPattern origFormalType, CanAnyFunctionType substFormalType, - SubstitutionList subs, SILLocation l) + SubstitutionMap subs, SILLocation l) : kind(Kind::StandaloneFunction), Constant(standaloneFunction), OrigFormalInterfaceType(origFormalType), SubstFormalInterfaceType(getSubstFormalInterfaceType(substFormalType, @@ -337,7 +339,7 @@ class Callee { /// forIndirect. Callee(Kind methodKind, SILGenFunction &SGF, SILDeclRef methodName, AbstractionPattern origFormalType, CanAnyFunctionType substFormalType, - SubstitutionList subs, SILLocation l) + SubstitutionMap subs, SILLocation l) : kind(methodKind), Constant(methodName), OrigFormalInterfaceType(origFormalType), SubstFormalInterfaceType( @@ -353,13 +355,13 @@ class Callee { return Callee(indirectValue, origFormalType, substFormalType, l); } static Callee forDirect(SILGenFunction &SGF, SILDeclRef c, - SubstitutionList subs, + SubstitutionMap subs, SILLocation l) { auto &ci = SGF.getConstantInfo(c); return Callee(SGF, c, ci.FormalPattern, ci.FormalType, subs, l); } static Callee forEnumElement(SILGenFunction &SGF, SILDeclRef c, - SubstitutionList subs, + SubstitutionMap subs, SILLocation l) { assert(isa(c.getDecl())); auto &ci = SGF.getConstantInfo(c); @@ -367,7 +369,7 @@ class Callee { ci.FormalType, subs, l); } static Callee forClassMethod(SILGenFunction &SGF, - SILDeclRef c, SubstitutionList subs, + SILDeclRef c, SubstitutionMap subs, SILLocation l) { auto base = c.getOverriddenVTableEntry(); auto &baseCI = SGF.getConstantInfo(base); @@ -376,7 +378,7 @@ class Callee { baseCI.FormalPattern, derivedCI.FormalType, subs, l); } static Callee forSuperMethod(SILGenFunction &SGF, - SILDeclRef c, SubstitutionList subs, + SILDeclRef c, SubstitutionMap subs, SILLocation l) { auto &ci = SGF.getConstantInfo(c); return Callee(Kind::SuperMethod, SGF, c, @@ -385,16 +387,16 @@ class Callee { static Callee forWitnessMethod(SILGenFunction &SGF, CanType protocolSelfType, SILDeclRef c, - SubstitutionList subs, + SubstitutionMap subs, SILLocation l) { auto &ci = SGF.getConstantInfo(c); return Callee(Kind::WitnessMethod, SGF, c, ci.FormalPattern, ci.FormalType, subs, l); } static Callee forDynamic(SILGenFunction &SGF, - SILDeclRef c, const SubstitutionList &constantSubs, + SILDeclRef c, SubstitutionMap constantSubs, CanAnyFunctionType substFormalType, - SubstitutionList subs, SILLocation l) { + SubstitutionMap subs, SILLocation l) { auto &ci = SGF.getConstantInfo(c); AbstractionPattern origFormalType = ci.FormalPattern; @@ -590,18 +592,17 @@ class Callee { case Kind::WitnessMethod: { auto constantInfo = SGF.getConstantInfo(*constant); - auto proto = cast( - Constant.getDecl()->getDeclContext()); - auto lookupType = getSubstFormalType() - .getInput() - ->getRValueInstanceType() - ->getCanonicalType(); + auto proto = cast(Constant.getDecl()->getDeclContext()); + auto selfType = proto->getSelfInterfaceType()->getCanonicalType(); + auto lookupType = selfType.subst(Substitutions)->getCanonicalType(); + auto conformance = *Substitutions.lookupConformance(selfType, proto); SILValue fn; if (!constant->isForeign) { + fn = SGF.B.createWitnessMethod( - Loc, lookupType, ProtocolConformanceRef(proto), *constant, + Loc, lookupType, conformance, *constant, constantInfo.getSILType()); } else { fn = SGF.B.createObjCMethod(Loc, borrowedSelf->getValue(), @@ -671,7 +672,7 @@ class Callee { } } - SubstitutionList getSubstitutions() const { + SubstitutionMap getSubstitutions() const { return Substitutions; } @@ -855,7 +856,7 @@ class SILGenApply : public Lowering::ExprVisitor { ArgumentSource selfValue = thisCallSite->getArg(); - SubstitutionList subs = e->getDeclRef().getSubstitutions(); + auto subs = e->getDeclRef().getSubstitutions(); SILDeclRef::Kind kind = SILDeclRef::Kind::Func; if (isa(afd)) { @@ -868,7 +869,7 @@ class SILGenApply : public Lowering::ExprVisitor { auto metatype = std::move(selfValue).getAsSingleValue(SGF); auto allocated = allocateObjCObject(metatype, loc); - auto allocatedType = allocated.getType().getSwiftRValueType(); + auto allocatedType = allocated.getType().getASTType(); selfValue = ArgumentSource(loc, RValue(SGF, loc, allocatedType, allocated)); } else { @@ -941,7 +942,7 @@ class SILGenApply : public Lowering::ExprVisitor { RValue selfMetatype = SGF.emitRValue(thisCallSite->getArg()); auto selfValue = allocateObjCObject(std::move(selfMetatype).getAsSingleValue(SGF, loc), loc); - RValue self = RValue(SGF, loc, selfValue.getType().getSwiftRValueType(), + RValue self = RValue(SGF, loc, selfValue.getType().getASTType(), selfValue); ArgumentSource selfArgSource(thisCallSite->getArg(), std::move(self)); setSelfParam(std::move(selfArgSource), thisCallSite); @@ -961,7 +962,7 @@ class SILGenApply : public Lowering::ExprVisitor { // Known callees. // void visitDeclRefExpr(DeclRefExpr *e) { - SubstitutionList subs = e->getDeclRef().getSubstitutions(); + auto subs = e->getDeclRef().getSubstitutions(); // If this is a direct reference to a vardecl, just emit its value directly. // Recursive references to callable declarations are allowed. @@ -999,7 +1000,7 @@ class SILGenApply : public Lowering::ExprVisitor { auto captureInfo = SGF.SGM.Types.getLoweredLocalCaptures(afd); if (afd->getDeclContext()->isLocalContext() && !captureInfo.hasGenericParamCaptures()) - subs = SubstitutionList(); + subs = SubstitutionMap(); setCallee(Callee::forDirect(SGF, constant, subs, e)); @@ -1027,9 +1028,9 @@ class SILGenApply : public Lowering::ExprVisitor { // really producing a closure object. SILDeclRef constant(e); - SubstitutionList subs; + SubstitutionMap subs; if (e->getCaptureInfo().hasGenericParamCaptures()) - subs = SGF.getForwardingSubstitutions(); + subs = SGF.getForwardingSubstitutionMap(); setCallee(Callee::forDirect(SGF, constant, subs, e)); @@ -1050,7 +1051,8 @@ class SILGenApply : public Lowering::ExprVisitor { // constructors imported from Clang (which won't have a direct entry point) // or to delegate to a designated initializer. setCallee(Callee::forDirect(SGF, - SILDeclRef(e->getDecl(), SILDeclRef::Kind::Initializer), subs, e)); + SILDeclRef(e->getDecl(), SILDeclRef::Kind::Initializer), + subs, e)); } void visitDotSyntaxBaseIgnoredExpr(DotSyntaxBaseIgnoredExpr *e) { @@ -1090,7 +1092,7 @@ class SILGenApply : public Lowering::ExprVisitor { // The callee for a super call has to be either a method or constructor. Expr *fn = apply->getFn(); - SubstitutionList substitutions; + SubstitutionMap substitutions; SILDeclRef constant; if (auto *ctorRef = dyn_cast(fn)) { constant = SILDeclRef(ctorRef->getDecl(), SILDeclRef::Kind::Initializer) @@ -1304,7 +1306,7 @@ class SILGenApply : public Lowering::ExprVisitor { if (isa(ctorRef->getDecl()->getDeclContext())) { // Look up the witness for the constructor. setCallee(Callee::forWitnessMethod( - SGF, self.getType().getSwiftRValueType(), + SGF, self.getType().getASTType(), constant, subs, expr)); } else if (getMethodDispatch(ctorRef->getDecl()) == MethodDispatch::Class) { @@ -1314,8 +1316,7 @@ class SILGenApply : public Lowering::ExprVisitor { SGF, constant, subs, fn)); } else { // Directly call the peer constructor. - setCallee( - Callee::forDirect(SGF, constant, subs, fn)); + setCallee(Callee::forDirect(SGF, constant, subs, fn)); } setSelfParam(std::move(selfArgSource), expr); @@ -1410,7 +1411,8 @@ class SILGenApply : public Lowering::ExprVisitor { dynamicMemberRef->getBase()->getType()->getCanonicalType(), substFormalType, AnyFunctionType::ExtInfo()); - setCallee(Callee::forDynamic(SGF, member, memberRef.getSubstitutions(), + setCallee(Callee::forDynamic(SGF, member, + memberRef.getSubstitutions(), substFormalType, {}, e)); setSelfParam(std::move(baseArgSource), dynamicMemberRef); }; @@ -1469,7 +1471,7 @@ static RValue emitStringLiteral(SILGenFunction &SGF, Expr *E, StringRef Str, SILValue UnicodeScalarValue = SGF.B.createIntegerLiteral(E, Int32Ty, unicode::extractFirstUnicodeScalar(Str)); - return RValue(SGF, E, Int32Ty.getSwiftRValueType(), + return RValue(SGF, E, Int32Ty.getASTType(), ManagedValue::forUnmanaged(UnicodeScalarValue)); } } @@ -1478,7 +1480,7 @@ static RValue emitStringLiteral(SILGenFunction &SGF, Expr *E, StringRef Str, if (useConstantStringBuiltin) { auto *string = SGF.B.createConstStringLiteral(E, Str, constInstEncoding); ManagedValue Elts[] = {ManagedValue::forUnmanaged(string)}; - TupleTypeElt TypeElts[] = {Elts[0].getType().getSwiftRValueType()}; + TupleTypeElt TypeElts[] = {Elts[0].getType().getASTType()}; CanType ty = TupleType::get(TypeElts, SGF.getASTContext())->getCanonicalType(); return RValue(SGF, Elts, ty); @@ -1502,9 +1504,9 @@ static RValue emitStringLiteral(SILGenFunction &SGF, Expr *E, StringRef Str, }; TupleTypeElt TypeEltsArray[] = { - EltsArray[0].getType().getSwiftRValueType(), - EltsArray[1].getType().getSwiftRValueType(), - EltsArray[2].getType().getSwiftRValueType() + EltsArray[0].getType().getASTType(), + EltsArray[1].getType().getASTType(), + EltsArray[2].getType().getASTType() }; ArrayRef Elts; @@ -1534,7 +1536,7 @@ static RValue emitStringLiteral(SILGenFunction &SGF, Expr *E, StringRef Str, static SILValue emitRawApply(SILGenFunction &SGF, SILLocation loc, ManagedValue fn, - SubstitutionList subs, + SubstitutionMap subs, ArrayRef args, CanSILFunctionType substFnType, ApplyOptions options, @@ -1675,6 +1677,164 @@ class OriginalArgument { Expr *getExpr() const { return ExprAndIsIndirect.getPointer(); } bool isIndirect() const { return ExprAndIsIndirect.getInt(); } }; + +/// A possibly-discontiguous slice of function parameters claimed by a +/// function application. +class ClaimedParamsRef { +public: + static constexpr const unsigned NoSkip = (unsigned)-1; +private: + ArrayRef Params; + + // The index of the param excluded from this range, if any, or ~0. + unsigned SkipParamIndex; + + friend struct ParamLowering; + explicit ClaimedParamsRef(ArrayRef params, + unsigned skip) + : Params(params), SkipParamIndex(skip) + { + // Eagerly chop a skipped parameter off either end. + if (SkipParamIndex == 0) { + Params = Params.slice(1); + SkipParamIndex = NoSkip; + } + assert(!hasSkip() || SkipParamIndex < Params.size()); + } + + bool hasSkip() const { + return SkipParamIndex != (unsigned)NoSkip; + } +public: + ClaimedParamsRef() : Params({}), SkipParamIndex(-1) {} + explicit ClaimedParamsRef(ArrayRef params) + : Params(params), SkipParamIndex(NoSkip) + {} + + struct iterator : public std::iterator + { + const SILParameterInfo *Base; + unsigned I, SkipParamIndex; + + iterator(const SILParameterInfo *Base, + unsigned I, unsigned SkipParamIndex) + : Base(Base), I(I), SkipParamIndex(SkipParamIndex) + {} + + iterator &operator++() { + ++I; + if (I == SkipParamIndex) + ++I; + return *this; + } + iterator operator++(int) { + iterator old(*this); + ++*this; + return old; + } + iterator &operator--() { + --I; + if (I == SkipParamIndex) + --I; + return *this; + } + iterator operator--(int) { + iterator old(*this); + --*this; + return old; + } + + const SILParameterInfo &operator*() const { + return Base[I]; + } + const SILParameterInfo *operator->() const { + return Base + I; + } + + bool operator==(iterator other) const { + return Base == other.Base && I == other.I + && SkipParamIndex == other.SkipParamIndex; + } + + bool operator!=(iterator other) const { + return !(*this == other); + } + + iterator operator+(std::ptrdiff_t distance) const { + if (distance > 0) + return goForward(distance); + if (distance < 0) + return goBackward(distance); + return *this; + } + iterator operator-(std::ptrdiff_t distance) const { + if (distance > 0) + return goBackward(distance); + if (distance < 0) + return goForward(distance); + return *this; + } + std::ptrdiff_t operator-(iterator other) const { + assert(Base == other.Base && SkipParamIndex == other.SkipParamIndex); + auto baseDistance = (std::ptrdiff_t)I - (std::ptrdiff_t)other.I; + if (std::min(I, other.I) < SkipParamIndex && + std::max(I, other.I) > SkipParamIndex) + return baseDistance - 1; + return baseDistance; + } + + iterator goBackward(unsigned distance) const { + auto result = *this; + if (I > SkipParamIndex && I <= SkipParamIndex + distance) + result.I -= (distance + 1); + result.I -= distance; + return result; + } + + iterator goForward(unsigned distance) const { + auto result = *this; + if (I < SkipParamIndex && I + distance >= SkipParamIndex) + result.I += distance + 1; + result.I += distance; + return result; + } + }; + + iterator begin() const { + return iterator{Params.data(), 0, SkipParamIndex}; + } + + iterator end() const { + return iterator{Params.data(), (unsigned)Params.size(), SkipParamIndex}; + } + + unsigned size() const { + return Params.size() - (hasSkip() ? 1 : 0); + } + + bool empty() const { return size() == 0; } + + SILParameterInfo front() const { return *begin(); } + + ClaimedParamsRef slice(unsigned start) const { + if (start >= SkipParamIndex) + return ClaimedParamsRef(Params.slice(start + 1), NoSkip); + return ClaimedParamsRef(Params.slice(start), + hasSkip() ? SkipParamIndex - start : NoSkip); + } + ClaimedParamsRef slice(unsigned start, unsigned count) const { + if (start >= SkipParamIndex) + return ClaimedParamsRef(Params.slice(start + 1, count), NoSkip); + unsigned newSkip = SkipParamIndex; + if (hasSkip()) + newSkip -= start; + + if (newSkip < count) + return ClaimedParamsRef(Params.slice(start, count+1), newSkip); + return ClaimedParamsRef(Params.slice(start, count), NoSkip); + } +}; /// A delayed argument. Call arguments are evaluated in two phases: /// a formal evaluation phase and a formal access phase. The primary @@ -1691,13 +1851,7 @@ class DelayedArgument { /// This is a true inout argument. InOut, - /// This is a borrowed direct argument. - BorrowDirect, - - /// This is a borrowed indirect argument. - BorrowIndirect, - - LastLVKindWithoutExtra = BorrowIndirect, + LastLVKindWithoutExtra = InOut, /// The l-value needs to be converted to a pointer type. LValueToPointer, @@ -1712,6 +1866,11 @@ class DelayedArgument { /// A string r-value needs to be converted to a pointer type. RValueStringToPointer, + + LastRVKind = RValueStringToPointer, + + /// A default argument that needs to be evaluated. + DefaultArgument, }; private: @@ -1728,11 +1887,36 @@ class DelayedArgument { RValueStorage(ManagedValue rv) : RV(rv) {} }; + struct DefaultArgumentStorage { + SILLocation loc; + ConcreteDeclRef defaultArgsOwner; + unsigned destIndex; + CanType resultType; + AbstractionPattern origResultType; + ClaimedParamsRef paramsToEmit; + SILFunctionTypeRepresentation functionRepresentation; + + DefaultArgumentStorage(SILLocation loc, + ConcreteDeclRef defaultArgsOwner, + unsigned destIndex, + CanType resultType, + AbstractionPattern origResultType, + ClaimedParamsRef paramsToEmit, + SILFunctionTypeRepresentation functionRepresentation) + : loc(loc), defaultArgsOwner(defaultArgsOwner), destIndex(destIndex), + resultType(resultType), origResultType(origResultType), + paramsToEmit(paramsToEmit), + functionRepresentation(functionRepresentation) + {} + }; - using ValueMembers = ExternalUnionMembers; + using ValueMembers = + ExternalUnionMembers; static ValueMembers::Index getValueMemberIndexForKind(KindTy kind) { - return (kind <= LastLVKind ? ValueMembers::indexOf() - : ValueMembers::indexOf()); + return (kind <= LastLVKind ? ValueMembers::indexOf() : + kind <= LastRVKind ? ValueMembers::indexOf() + : ValueMembers::indexOf()); } /// Storage for either the l-value or the r-value. @@ -1749,9 +1933,11 @@ class DelayedArgument { using PointerAccessInfo = SILGenFunction::PointerAccessInfo; using ArrayAccessInfo = SILGenFunction::ArrayAccessInfo; - + using ExtraMembers = - ExternalUnionMembers; + ExternalUnionMembers; static ExtraMembers::Index getExtraMemberIndexForKind(KindTy kind) { switch (kind) { case LValueToPointer: @@ -1800,6 +1986,21 @@ class DelayedArgument { Value.emplace(Kind, rv); Extra.emplace(Kind, arrayInfo); } + + DelayedArgument(SILLocation loc, + ConcreteDeclRef defaultArgsOwner, + unsigned destIndex, + CanType resultType, + AbstractionPattern origResultType, + ClaimedParamsRef params, + SILFunctionTypeRepresentation functionTypeRepresentation) + : Kind(DefaultArgument) { + Value.emplace(Kind, loc, defaultArgsOwner, + destIndex, + resultType, + origResultType, params, + functionTypeRepresentation); + } DelayedArgument(DelayedArgument &&other) : Kind(other.Kind), Original(other.Original) { @@ -1826,19 +2027,23 @@ class DelayedArgument { return LV().Loc; } - ManagedValue emit(SILGenFunction &SGF) { + void emit(SILGenFunction &SGF, + SmallVectorImpl &args, + int &argIndex) { switch (Kind) { case InOut: - return emitInOut(SGF); - case BorrowDirect: - return emitBorrowDirect(SGF); - case BorrowIndirect: - return emitBorrowIndirect(SGF); + args[argIndex] = emitInOut(SGF); + return; case LValueToPointer: case LValueArrayToPointer: case RValueArrayToPointer: case RValueStringToPointer: - return finishOriginalArgument(SGF); + args[argIndex] = finishOriginalArgument(SGF); + return; + case DefaultArgument: + emitDefaultArgument(SGF, Value.get(Kind), + args, argIndex); + return; } llvm_unreachable("bad kind"); } @@ -1875,6 +2080,11 @@ class DelayedArgument { return value; } + + void emitDefaultArgument(SILGenFunction &SGF, + const DefaultArgumentStorage &info, + SmallVectorImpl &args, + int &argIndex); // (value, owner) std::pair @@ -1915,8 +2125,7 @@ class DelayedArgument { switch (Kind) { case InOut: - case BorrowDirect: - case BorrowIndirect: + case DefaultArgument: llvm_unreachable("no original expr to finish in these cases"); case LValueToPointer: @@ -2016,27 +2225,29 @@ static void emitDelayedArguments(SILGenFunction &SGF, SmallVector, 4> emittedInoutArgs; auto delayedNext = delayedArgs.begin(); - // The assumption we make is that 'args' and 'inoutArgs' were built + // The assumption we make is that 'args' and 'delayedArgs' were built // up in parallel, with empty spots being dropped into 'args' - // wherever there's an inout argument to insert. + // wherever there's a delayed argument to insert. // // Note that this also begins the formal accesses in evaluation order. for (auto &siteArgs : args) { - for (ManagedValue &siteArg : siteArgs) { + // NB: siteArgs.size() may change during iteration + for (int i = 0; i < (int)siteArgs.size(); ++i) { + auto &siteArg = siteArgs[i]; + if (siteArg) continue; assert(delayedNext != delayedArgs.end()); auto &delayedArg = *delayedNext; // Emit the delayed argument and replace it in the arguments array. - auto value = delayedArg.emit(SGF); - siteArg = value; + delayedArg.emit(SGF, siteArgs, i); // Remember all the simple inouts we emitted so we can perform // a basic inout-aliasing analysis. // This should be completely obviated by static enforcement. if (delayedArg.isSimpleInOut()) { - emittedInoutArgs.push_back({value.getValue(), + emittedInoutArgs.push_back({siteArg.getValue(), delayedArg.getInOutLocation()}); } @@ -2146,164 +2357,6 @@ struct ArgSpecialDest { } }; -/// A possibly-discontiguous slice of function parameters claimed by a -/// function application. -class ClaimedParamsRef { -public: - static constexpr const unsigned NoSkip = (unsigned)-1; -private: - ArrayRef Params; - - // The index of the param excluded from this range, if any, or ~0. - unsigned SkipParamIndex; - - friend struct ParamLowering; - explicit ClaimedParamsRef(ArrayRef params, - unsigned skip) - : Params(params), SkipParamIndex(skip) - { - // Eagerly chop a skipped parameter off either end. - if (SkipParamIndex == 0) { - Params = Params.slice(1); - SkipParamIndex = NoSkip; - } - assert(!hasSkip() || SkipParamIndex < Params.size()); - } - - bool hasSkip() const { - return SkipParamIndex != (unsigned)NoSkip; - } -public: - ClaimedParamsRef() : Params({}), SkipParamIndex(-1) {} - explicit ClaimedParamsRef(ArrayRef params) - : Params(params), SkipParamIndex(NoSkip) - {} - - struct iterator : public std::iterator - { - const SILParameterInfo *Base; - unsigned I, SkipParamIndex; - - iterator(const SILParameterInfo *Base, - unsigned I, unsigned SkipParamIndex) - : Base(Base), I(I), SkipParamIndex(SkipParamIndex) - {} - - iterator &operator++() { - ++I; - if (I == SkipParamIndex) - ++I; - return *this; - } - iterator operator++(int) { - iterator old(*this); - ++*this; - return old; - } - iterator &operator--() { - --I; - if (I == SkipParamIndex) - --I; - return *this; - } - iterator operator--(int) { - iterator old(*this); - --*this; - return old; - } - - const SILParameterInfo &operator*() const { - return Base[I]; - } - const SILParameterInfo *operator->() const { - return Base + I; - } - - bool operator==(iterator other) const { - return Base == other.Base && I == other.I - && SkipParamIndex == other.SkipParamIndex; - } - - bool operator!=(iterator other) const { - return !(*this == other); - } - - iterator operator+(std::ptrdiff_t distance) const { - if (distance > 0) - return goForward(distance); - if (distance < 0) - return goBackward(distance); - return *this; - } - iterator operator-(std::ptrdiff_t distance) const { - if (distance > 0) - return goBackward(distance); - if (distance < 0) - return goForward(distance); - return *this; - } - std::ptrdiff_t operator-(iterator other) const { - assert(Base == other.Base && SkipParamIndex == other.SkipParamIndex); - auto baseDistance = (std::ptrdiff_t)I - (std::ptrdiff_t)other.I; - if (std::min(I, other.I) < SkipParamIndex && - std::max(I, other.I) > SkipParamIndex) - return baseDistance - 1; - return baseDistance; - } - - iterator goBackward(unsigned distance) const { - auto result = *this; - if (I > SkipParamIndex && I <= SkipParamIndex + distance) - result.I -= (distance + 1); - result.I -= distance; - return result; - } - - iterator goForward(unsigned distance) const { - auto result = *this; - if (I < SkipParamIndex && I + distance >= SkipParamIndex) - result.I += distance + 1; - result.I += distance; - return result; - } - }; - - iterator begin() const { - return iterator{Params.data(), 0, SkipParamIndex}; - } - - iterator end() const { - return iterator{Params.data(), (unsigned)Params.size(), SkipParamIndex}; - } - - unsigned size() const { - return Params.size() - (hasSkip() ? 1 : 0); - } - - bool empty() const { return size() == 0; } - - SILParameterInfo front() const { return *begin(); } - - ClaimedParamsRef slice(unsigned start) const { - if (start >= SkipParamIndex) - return ClaimedParamsRef(Params.slice(start + 1), NoSkip); - return ClaimedParamsRef(Params.slice(start), - hasSkip() ? SkipParamIndex - start : NoSkip); - } - ClaimedParamsRef slice(unsigned start, unsigned count) const { - if (start >= SkipParamIndex) - return ClaimedParamsRef(Params.slice(start + 1, count), NoSkip); - unsigned newSkip = SkipParamIndex; - if (hasSkip()) - newSkip -= start; - - if (newSkip < count) - return ClaimedParamsRef(Params.slice(start, count+1), newSkip); - return ClaimedParamsRef(Params.slice(start, count), NoSkip); - } -}; - using ArgSpecialDestArray = MutableArrayRef; class TupleShuffleArgEmitter; @@ -2814,7 +2867,7 @@ class ArgEmitter { static EmissionContexts getRValueEmissionContexts(SILType loweredArgType, SILParameterInfo param) { bool requiresReabstraction = - loweredArgType.getSwiftRValueType() != param.getType(); + loweredArgType.getASTType() != param.getType(); // If the parameter is consumed, we have to emit at +1. if (param.isConsumed()) { return {SGFContext(), requiresReabstraction}; @@ -2830,6 +2883,40 @@ class ArgEmitter { } }; +void DelayedArgument::emitDefaultArgument(SILGenFunction &SGF, + const DefaultArgumentStorage &info, + SmallVectorImpl &args, + int &argIndex) { + auto value = SGF.emitApplyOfDefaultArgGenerator(info.loc, + info.defaultArgsOwner, + info.destIndex, + info.resultType, + info.origResultType); + + SmallVector loweredArgs; + SmallVector delayedArgs; + Optional errorConvention = None; + auto emitter = ArgEmitter(SGF, info.functionRepresentation, + info.paramsToEmit, + loweredArgs, delayedArgs, + errorConvention, ImportAsMemberStatus()); + + emitter.emitTopLevel(ArgumentSource(info.loc, std::move(value)), + info.origResultType); + assert(delayedArgs.empty()); + assert(!errorConvention); + + // Splice the emitted default argument into the argument list. + if (loweredArgs.size() == 1) { + args[argIndex] = loweredArgs.front(); + } else { + args.erase(args.begin() + argIndex); + args.insert(args.begin() + argIndex, + loweredArgs.begin(), loweredArgs.end()); + argIndex += loweredArgs.size() - 1; + } +} + struct ElementExtent { /// The parameters which go into this tuple element. /// This is set in the first pass. @@ -3007,7 +3094,7 @@ void TupleShuffleArgEmitter::constructInnerTupleTypeInfo(ArgEmitter &parent) { // tuple. variadicParamInfo = SILParameterInfo(varargsInfo->getBaseTypeLowering() - .getLoweredType().getSwiftRValueType(), + .getLoweredType().getASTType(), ParameterConvention::Indirect_In); unsigned i = 0; @@ -3114,16 +3201,22 @@ void TupleShuffleArgEmitter::emitDefaultArgsAndFinalize(ArgEmitter &parent) { continue; } - // If this is default initialization, call the default argument - // generator. + // If this is default initialization, prepare to emit the default argument + // generator later. if (innerIndex == TupleShuffleExpr::DefaultInitialize) { // Otherwise, emit the default initializer, then map that as a // default argument. CanType eltType = outerElements[outerIndex].getType()->getCanonicalType(); auto origType = getOutputOrigElementType(outerIndex); - RValue value = parent.SGF.emitApplyOfDefaultArgGenerator( - outer, defaultArgsOwner, outerIndex, eltType, origType); - parent.emit(ArgumentSource(outer, std::move(value)), origType); + + auto numParams = getFlattenedValueCount(origType, eltType, + ImportAsMemberStatus()); + parent.DelayedArguments.emplace_back(outer, defaultArgsOwner, + outerIndex, eltType, origType, + parent.ParamInfos.slice(0, numParams), + parent.Rep); + parent.ParamInfos = parent.ParamInfos.slice(numParams); + parent.Args.push_back(ManagedValue()); continue; } @@ -3672,7 +3765,7 @@ CallEmission::applyNormalCall(SGFContext C) { SGF, calleeTypeInfo, uncurriedSites.back().Loc, uncurriedContext); ArgumentScope argScope(SGF, uncurriedSites.back().Loc); - PostponedCleanup postpone(SGF); + // Emit the arguments. SmallVector uncurriedArgs; Optional uncurriedLoc; @@ -3693,11 +3786,20 @@ CallEmission::applyNormalCall(SGFContext C) { auto mv = callee.getFnValue(SGF, isCurried, borrowedSelf); + // Materialize for set could temporarily escape its arguments. + if (callee.isMaterializeForSet) { + auto indices = ArrayRef(uncurriedArgs).slice(2); + for (auto index : indices) { + auto *toNoEscape = dyn_cast(index.getValue()); + if (!toNoEscape) continue; + toNoEscape->setEscapedByUser(); + } + } // Emit the uncurried call. firstLevelResult.value = SGF.emitApply( std::move(resultPlan), std::move(argScope), uncurriedLoc.getValue(), mv, callee.getSubstitutions(), uncurriedArgs, calleeTypeInfo, options, - uncurriedContext, postpone); + uncurriedContext); firstLevelResult.foreignSelf = calleeTypeInfo.foreignSelf; return firstLevelResult; } @@ -3814,7 +3916,8 @@ CallEmission::applyPartiallyAppliedSuperMethod(SGFContext C) { partialApplyTy = partialApplyTy.substGenericArgs(module, subs); ManagedValue pa = SGF.B.createPartialApply(loc, superMethod, partialApplyTy, - subs, {upcastedSelf}, closureTy); + subs, {upcastedSelf}, + closureTy); assert(!closureTy.castTo()->isNoEscape()); firstLevelResult.value = RValue(SGF, loc, formalApplyType.getResult(), pa); return firstLevelResult; @@ -3857,8 +3960,8 @@ CallEmission::applySpecializedEmitter(SpecializedEmitter &specializedEmitter, // always still expressions. Expr *argument = std::move(uncurriedSites[0]).forward().asKnownExpr(); ManagedValue resultMV = - emitter(SGF, uncurriedLoc, callee.getSubstitutions(), argument, - uncurriedContext); + emitter(SGF, uncurriedLoc, callee.getSubstitutions(), + argument, uncurriedContext); firstLevelResult.value = RValue(SGF, uncurriedLoc, formalResultType, resultMV); return firstLevelResult; @@ -3876,7 +3979,8 @@ CallEmission::applySpecializedEmitter(SpecializedEmitter &specializedEmitter, // If we have a late emitter, just delegate to that emitter and return. if (specializedEmitter.isLateEmitter()) { auto emitter = specializedEmitter.getLateEmitter(); - ManagedValue mv = emitter(SGF, *uncurriedLoc, callee.getSubstitutions(), + ManagedValue mv = emitter(SGF, *uncurriedLoc, + callee.getSubstitutions(), uncurriedArgs, uncurriedContext); firstLevelResult.value = RValue(SGF, *uncurriedLoc, formalApplyType.getResult(), mv); @@ -3895,7 +3999,8 @@ CallEmission::applySpecializedEmitter(SpecializedEmitter &specializedEmitter, SILFunctionConventions substConv(substFnType, SGF.SGM.M); auto resultVal = SGF.B.createBuiltin(uncurriedLoc.getValue(), builtinName, substConv.getSILResultType(), - callee.getSubstitutions(), consumedArgs); + callee.getSubstitutions(), + consumedArgs); firstLevelResult.value = RValue(SGF, *uncurriedLoc, formalApplyType.getResult(), SGF.emitManagedRValueWithCleanup(resultVal)); @@ -4022,7 +4127,6 @@ RValue CallEmission::applyRemainingCallSites(RValue &&result, ResultPlanPtr resultPtr = ResultPlanBuilder::computeResultPlan(SGF, calleeTypeInfo, loc, context); ArgumentScope argScope(SGF, loc); - PostponedCleanup postpone(SGF); std::move(extraSites[i]) .emit(SGF, origParamType, paramLowering, siteArgs, delayedArgs, @@ -4033,7 +4137,7 @@ RValue CallEmission::applyRemainingCallSites(RValue &&result, result = SGF.emitApply(std::move(resultPtr), std::move(argScope), loc, functionMV, {}, siteArgs, calleeTypeInfo, - ApplyOptions::None, context, postpone); + ApplyOptions::None, context); } return std::move(result); @@ -4083,11 +4187,10 @@ CallEmission CallEmission::forApplyExpr(SILGenFunction &SGF, Expr *e) { /// formal type. RValue SILGenFunction::emitApply(ResultPlanPtr &&resultPlan, ArgumentScope &&argScope, SILLocation loc, - ManagedValue fn, SubstitutionList subs, + ManagedValue fn, SubstitutionMap subs, ArrayRef args, const CalleeTypeInfo &calleeTypeInfo, - ApplyOptions options, SGFContext evalContext, - PostponedCleanup &postponedCleanup) { + ApplyOptions options, SGFContext evalContext) { auto substFnType = calleeTypeInfo.substFnType; auto substResultType = calleeTypeInfo.substResultType; @@ -4147,8 +4250,12 @@ RValue SILGenFunction::emitApply(ResultPlanPtr &&resultPlan, } // Emit the raw application. - loc.decodeDebugLoc(SGM.M.getASTContext().SourceMgr); - postponedCleanup.end(); + auto genericSig = + fn.getType().castTo()->getGenericSignature(); + if (genericSig != subs.getGenericSignature()) { + subs = genericSig->getSubstitutionMap(subs); + } + SILValue rawDirectResult = emitRawApply( *this, loc, fn, subs, args, substFnType, options, indirectResultAddrs); @@ -4269,16 +4376,15 @@ RValue SILGenFunction::emitMonomorphicApply( ResultPlanPtr resultPlan = ResultPlanBuilder::computeResultPlan( *this, calleeTypeInfo, loc, evalContext); ArgumentScope argScope(*this, loc); - PostponedCleanup postpone(*this); return emitApply(std::move(resultPlan), std::move(argScope), loc, fn, {}, - args, calleeTypeInfo, options, evalContext, postpone); + args, calleeTypeInfo, options, evalContext); } /// Emit either an 'apply' or a 'try_apply', with the error branch of /// the 'try_apply' simply branching out of all cleanups and throwing. SILValue SILGenFunction::emitApplyWithRethrow(SILLocation loc, SILValue fn, SILType substFnType, - SubstitutionList subs, + SubstitutionMap subs, ArrayRef args) { CanSILFunctionType silFnType = substFnType.castTo(); SILFunctionConventions fnConv(silFnType, SGM.M); @@ -4417,23 +4523,10 @@ RValue SILGenFunction::emitApplyExpr(Expr *e, SGFContext c) { RValue SILGenFunction::emitApplyOfLibraryIntrinsic(SILLocation loc, FuncDecl *fn, - const SubstitutionMap &subMap, - ArrayRef args, - SGFContext ctx) { - SmallVector subs; - if (auto *genericSig = fn->getGenericSignature()) - genericSig->getSubstitutions(subMap, subs); - - return emitApplyOfLibraryIntrinsic(loc, fn, subs, args, ctx); -} - -RValue -SILGenFunction::emitApplyOfLibraryIntrinsic(SILLocation loc, - FuncDecl *fn, - const SubstitutionList &subs, + SubstitutionMap subMap, ArrayRef args, SGFContext ctx) { - auto callee = Callee::forDirect(*this, SILDeclRef(fn), subs, loc); + auto callee = Callee::forDirect(*this, SILDeclRef(fn), subMap, loc); auto origFormalType = callee.getOrigFormalType(); auto substFormalType = callee.getSubstFormalType(); @@ -4456,15 +4549,14 @@ SILGenFunction::emitApplyOfLibraryIntrinsic(SILLocation loc, SILFunctionConventions silConv(calleeTypeInfo.substFnType, getModule()); llvm::SmallVector finalArgs; - convertOwnershipConventionsGivenParamInfos(*this, silConv.getParameters(), args, loc, finalArgs); + convertOwnershipConventionsGivenParamInfos(*this, silConv.getParameters(), + args, loc, finalArgs); ResultPlanPtr resultPlan = - ResultPlanBuilder::computeResultPlan(*this, calleeTypeInfo, loc, ctx); + ResultPlanBuilder::computeResultPlan(*this, calleeTypeInfo, loc, ctx); ArgumentScope argScope(*this, loc); - PostponedCleanup postpone(*this); - return emitApply(std::move(resultPlan), std::move(argScope), loc, mv, subs, - finalArgs, calleeTypeInfo, ApplyOptions::None, ctx, - postpone); + return emitApply(std::move(resultPlan), std::move(argScope), loc, mv, subMap, + finalArgs, calleeTypeInfo, ApplyOptions::None, ctx); } static StringRef @@ -4532,8 +4624,10 @@ static RValue emitApplyAllocatingInitializer(SILGenFunction &SGF, Optional callee; if (isa(ctor->getDeclContext())) { callee.emplace(Callee::forWitnessMethod( - SGF, selfMetaVal.getType().getSwiftRValueType(), + SGF, selfMetaVal.getType().getASTType(), initRef, subs, loc)); + } else if (getMethodDispatch(ctor) == MethodDispatch::Class) { + callee.emplace(Callee::forClassMethod(SGF, initRef, subs, loc)); } else { callee.emplace(Callee::forDirect(SGF, initRef, subs, loc)); } @@ -4543,7 +4637,7 @@ static RValue emitApplyAllocatingInitializer(SILGenFunction &SGF, // For an inheritable initializer, determine whether we'll need to adjust the // result type. bool requiresDowncast = false; - if (ctor->isInheritable() && overriddenSelfType) { + if (ctor->isRequired() && overriddenSelfType) { CanType substResultType = substFormalType; for (unsigned i : range(ctor->getNumParameterLists())) { (void)i; @@ -4562,7 +4656,7 @@ static RValue emitApplyAllocatingInitializer(SILGenFunction &SGF, ArgumentSource(loc, RValue(SGF, loc, selfMetaVal.getType() - .getSwiftRValueType(), + .getASTType(), std::move(selfMetaVal))), substFormalType); @@ -4694,7 +4788,7 @@ void SILGenFunction::emitUninitializedArrayDeallocation(SILLocation loc, auto &Ctx = getASTContext(); auto deallocate = Ctx.getDeallocateUninitializedArray(nullptr); - CanType arrayTy = array->getType().getSwiftRValueType(); + CanType arrayTy = array->getType().getASTType(); // Invoke the intrinsic. auto subMap = arrayTy->getContextSubstitutionMap(SGM.M.getSwiftModule(), @@ -4738,7 +4832,7 @@ static Callee getBaseAccessorFunctionRef(SILGenFunction &SGF, ArgumentSource &selfValue, bool isSuper, bool isDirectUse, - SubstitutionList subs) { + SubstitutionMap subs) { auto *decl = cast(constant.getDecl()); // The accessor might be a local function that does not capture any @@ -4747,7 +4841,7 @@ static Callee getBaseAccessorFunctionRef(SILGenFunction &SGF, auto captureInfo = SGF.SGM.Types.getLoweredLocalCaptures(decl); if (decl->getDeclContext()->isLocalContext() && !captureInfo.hasGenericParamCaptures()) { - subs = SubstitutionList(); + subs = SubstitutionMap(); } // If this is a method in a protocol, generate it as a protocol call. @@ -4793,7 +4887,7 @@ static Callee emitSpecializedAccessorFunctionRef(SILGenFunction &SGF, SILLocation loc, SILDeclRef constant, - SubstitutionList substitutions, + SubstitutionMap substitutions, ArgumentSource &selfValue, bool isSuper, bool isDirectUse) @@ -5004,7 +5098,7 @@ SILDeclRef SILGenModule::getGetterDeclRef(AbstractStorageDecl *storage) { /// Emit a call to a getter. RValue SILGenFunction:: emitGetAccessor(SILLocation loc, SILDeclRef get, - SubstitutionList substitutions, + SubstitutionMap substitutions, ArgumentSource &&selfValue, bool isSuper, bool isDirectUse, RValue &&subscripts, SGFContext c) { @@ -5041,7 +5135,7 @@ SILDeclRef SILGenModule::getSetterDeclRef(AbstractStorageDecl *storage) { } void SILGenFunction::emitSetAccessor(SILLocation loc, SILDeclRef set, - SubstitutionList substitutions, + SubstitutionMap substitutions, ArgumentSource &&selfValue, bool isSuper, bool isDirectUse, RValue &&subscripts, @@ -5066,7 +5160,7 @@ void SILGenFunction::emitSetAccessor(SILLocation loc, SILDeclRef set, if (!subscripts.isNull()) { // If we have a value and index list, create a new rvalue to represent the // both of them together. - auto inputTupleType = cast(accessType.getInput()); + auto inputTupleType = dyn_cast(accessType.getInput()); SmallVector eltSources; @@ -5079,18 +5173,25 @@ void SILGenFunction::emitSetAccessor(SILLocation loc, SILDeclRef set, // TODO: we should really take an array of RValues. if (accessType->getNumParams() != 2) { auto subscriptsTupleType = cast(subscripts.getType()); - assert(inputTupleType->getNumElements() + assert(accessType.getParams().size() == 1 + subscriptsTupleType->getNumElements()); SmallVector eltRVs; std::move(subscripts).extractElements(eltRVs); for (auto &elt : eltRVs) eltSources.emplace_back(loc, std::move(elt)); } else { + assert(inputTupleType && "Must have an input tuple here"); subscripts.rewriteType(inputTupleType.getElementType(1)); eltSources.emplace_back(loc, std::move(subscripts)); } - setValue = ArgumentSource(loc, inputTupleType, eltSources); + if (eltSources.size() == 1) { + setValue = std::move(eltSources.front()); + } else { + assert(inputTupleType); + setValue = ArgumentSource(loc, inputTupleType, eltSources); + } + } else { setValue.rewriteType(accessType.getInput()); } @@ -5107,7 +5208,7 @@ SILGenModule::getMaterializeForSetDeclRef(AbstractStorageDecl *storage) { MaterializedLValue SILGenFunction:: emitMaterializeForSetAccessor(SILLocation loc, SILDeclRef materializeForSet, - SubstitutionList substitutions, + SubstitutionMap substitutions, ArgumentSource &&selfValue, bool isSuper, bool isDirectUse, RValue &&subscripts, SILValue buffer, @@ -5119,6 +5220,8 @@ emitMaterializeForSetAccessor(SILLocation loc, SILDeclRef materializeForSet, materializeForSet, substitutions, selfValue, isSuper, isDirectUse); + callee.isMaterializeForSet = true; + bool hasSelf = (bool)selfValue; auto accessType = callee.getSubstFormalType(); @@ -5190,7 +5293,7 @@ SILDeclRef SILGenModule::getAddressorDeclRef(AbstractStorageDecl *storage, /// pointer, if applicable. std::pair SILGenFunction:: emitAddressorAccessor(SILLocation loc, SILDeclRef addressor, - SubstitutionList substitutions, + SubstitutionMap substitutions, ArgumentSource &&selfValue, bool isSuper, bool isDirectUse, RValue &&subscripts, SILType addressType) { @@ -5305,7 +5408,7 @@ static ManagedValue emitDynamicPartialApply(SILGenFunction &SGF, auto nativeTy = SGF.getLoweredLoadableType(nativeFormalType).castTo(); - if (nativeTy != partialApplyTy.getSwiftRValueType()) { + if (nativeTy != partialApplyTy.getASTType()) { result = SGF.emitBlockToFunc(loc, result, foreignFormalType, nativeFormalType, nativeTy); } @@ -5382,7 +5485,7 @@ RValue SILGenFunction::emitDynamicMemberRefExpr(DynamicMemberRefExpr *e, getPartialApplyOfDynamicMethodFormalType(SGM, member, e->getMember()); auto memberFnTy = CanFunctionType::get( - operand->getType().getSwiftRValueType(), + operand->getType().getASTType(), memberMethodTy->getCanonicalType()); auto loweredMethodTy = getDynamicMethodLoweredType(SGM.M, member, @@ -5479,7 +5582,7 @@ RValue SILGenFunction::emitDynamicSubscriptExpr(DynamicSubscriptExpr *e, auto foreignMethodTy = getPartialApplyOfDynamicMethodFormalType(SGM, member, e->getMember()); - auto functionTy = CanFunctionType::get(base->getType().getSwiftRValueType(), + auto functionTy = CanFunctionType::get(base->getType().getASTType(), methodTy); auto loweredMethodTy = getDynamicMethodLoweredType(SGM.M, member, functionTy); diff --git a/lib/SILGen/SILGenBridging.cpp b/lib/SILGen/SILGenBridging.cpp index 8466b2210ae8f..1a6c65dbd6f16 100644 --- a/lib/SILGen/SILGenBridging.cpp +++ b/lib/SILGen/SILGenBridging.cpp @@ -120,7 +120,6 @@ emitBridgeNativeToObjectiveC(SILGenFunction &SGF, "Generic witnesses not supported"); auto *dc = cast(witness)->getDeclContext(); - auto *genericSig = dc->getGenericSignatureOfContext(); auto typeSubMap = swiftValueType->getContextSubstitutionMap( SGF.SGM.SwiftModule, dc); @@ -152,14 +151,10 @@ emitBridgeNativeToObjectiveC(SILGenFunction &SGF, swiftValue = ManagedValue::forUnmanaged(tmp); } - SmallVector subs; - if (genericSig) - genericSig->getSubstitutions(typeSubMap, subs); - // Call the witness. SILType resultTy = SGF.getLoweredType(objcType); SILValue bridgedValue = - SGF.B.createApply(loc, witnessRef, witnessFnTy, resultTy, subs, + SGF.B.createApply(loc, witnessRef, witnessFnTy, resultTy, typeSubMap, swiftValue.borrow(SGF, loc).getValue()); auto bridgedMV = SGF.emitManagedRValueWithCleanup(bridgedValue); @@ -206,9 +201,7 @@ emitBridgeObjectiveCToNative(SILGenFunction &SGF, CanType swiftValueType = conformance->getType()->getCanonicalType(); auto genericSig = witnessFnTy->getGenericSignature(); - SubstitutionMap typeSubMap; - if (genericSig) - typeSubMap = genericSig->getSubstitutionMap(witness.getSubstitutions()); + SubstitutionMap typeSubMap = witness.getSubstitutions(); // Substitute into the witness function type. witnessFnTy = witnessFnTy->substGenericArgs(SGF.SGM.M, typeSubMap); @@ -241,12 +234,11 @@ emitBridgeObjectiveCToNative(SILGenFunction &SGF, ResultPlanPtr resultPlan = ResultPlanBuilder::computeResultPlan(SGF, calleeTypeInfo, loc, context); ArgumentScope argScope(SGF, loc); - PostponedCleanup postpone(SGF); RValue result = SGF.emitApply(std::move(resultPlan), std::move(argScope), loc, ManagedValue::forUnmanaged(witnessRef), subs, {objcValue, ManagedValue::forUnmanaged(metatypeValue)}, - calleeTypeInfo, ApplyOptions::None, context, postpone); + calleeTypeInfo, ApplyOptions::None, context); return std::move(result).getAsSingleValue(SGF, loc); } @@ -508,6 +500,26 @@ ManagedValue SILGenFunction::emitFuncToBlock(SILLocation loc, CanSILFunctionType loweredBlockTy){ auto loweredFuncTy = fn.getType().castTo(); + // If we store a @noescape closure in a block verify that the block has not + // escaped by storing a withoutActuallyEscaping closure in the block and after + // the block is ultimately destroyed checking that the closure is uniquely + // referenced. + bool useWithoutEscapingVerifcation = false; + ManagedValue escaping; + if (loweredFuncTy->isNoEscape()) { + auto escapingTy = loweredFuncTy->getWithExtInfo( + loweredFuncTy->getExtInfo().withNoEscape(false)); + + escaping = createWithoutActuallyEscapingClosure( + loc, fn, SILType::getPrimitiveObjectType(escapingTy)); + loweredFuncTy = escapingTy; + auto escapingAnyTy = + funcType.withExtInfo(funcType->getExtInfo().withNoEscape(false)); + funcType = escapingAnyTy; + fn = B.createCopyValue(loc, escaping); + useWithoutEscapingVerifcation = true; + } + // Build the invoke function signature. The block will capture the original // function value. auto fnInterfaceTy = cast( @@ -534,12 +546,12 @@ ManagedValue SILGenFunction::emitFuncToBlock(SILLocation loc, CanGenericSignature genericSig; GenericEnvironment *genericEnv = nullptr; - SubstitutionList subs; + SubstitutionMap subs; if (funcType->hasArchetype() || blockType->hasArchetype()) { genericSig = F.getLoweredFunctionType()->getGenericSignature(); genericEnv = F.getGenericEnvironment(); - subs = F.getForwardingSubstitutions(); + subs = F.getForwardingSubstitutionMap(); // The block invoke function must be pseudogeneric. This should be OK for now // since a bridgeable function's parameters and returns should all be @@ -587,7 +599,15 @@ ManagedValue SILGenFunction::emitFuncToBlock(SILLocation loc, subs); // Copy the block so we have an independent heap object we can hand off. - auto heapBlock = B.createCopyBlock(loc, stackBlock); + + // If withoutActuallyEscaping verification is requested we emit a + // copy_block_without_escaping %block withoutEscaping %closure instruction. + // A mandatory SIL pass will replace this instruction by the required + // verification instruction sequence. + auto heapBlock = useWithoutEscapingVerifcation + ? SILValue(B.createCopyBlockWithoutEscaping( + loc, stackBlock, escaping.forward(*this))) + : SILValue(B.createCopyBlock(loc, stackBlock)); return emitManagedRValueWithCleanup(heapBlock); } @@ -920,9 +940,7 @@ SILGenFunction::emitBlockToFunc(SILLocation loc, CanSILFunctionType substFnTy = thunkTy; - SmallVector subs; if (auto genericSig = thunkTy->getGenericSignature()) { - genericSig->getSubstitutions(interfaceSubs, subs); substFnTy = thunkTy->substGenericArgs(F.getModule(), interfaceSubs); } @@ -930,8 +948,8 @@ SILGenFunction::emitBlockToFunc(SILLocation loc, // Create it in the current function. auto thunkValue = B.createFunctionRef(loc, thunk); ManagedValue thunkedFn = B.createPartialApply( - loc, thunkValue, SILType::getPrimitiveObjectType(substFnTy), subs, - block, + loc, thunkValue, SILType::getPrimitiveObjectType(substFnTy), + interfaceSubs, block, SILType::getPrimitiveObjectType(loweredFuncTyWithoutNoEscape)); if (!loweredFuncTy->isNoEscape()) { @@ -940,8 +958,8 @@ SILGenFunction::emitBlockToFunc(SILLocation loc, // Handle the escaping to noescape conversion. assert(loweredFuncTy->isNoEscape()); - return B.createConvertEscapeToNoEscape(loc, thunkedFn, - SILType::getPrimitiveObjectType(loweredFuncTy)); + return B.createConvertEscapeToNoEscape( + loc, thunkedFn, SILType::getPrimitiveObjectType(loweredFuncTy), false); } static ManagedValue emitCBridgedToNativeValue(SILGenFunction &SGF, @@ -1094,7 +1112,7 @@ ManagedValue SILGenFunction::emitBridgedToNativeValue(SILLocation loc, ManagedValue SILGenFunction::emitBridgedToNativeError(SILLocation loc, ManagedValue bridgedError) { // If the incoming error is non-optional, just do an existential erasure. - CanType bridgedErrorTy = bridgedError.getType().getSwiftRValueType(); + auto bridgedErrorTy = bridgedError.getType().getASTType(); if (!bridgedErrorTy.getOptionalObjectType()) { auto nativeErrorTy = SILType::getExceptionType(getASTContext()); @@ -1226,7 +1244,7 @@ static SILValue emitObjCUnconsumedArgument(SILGenFunction &SGF, } static CanAnyFunctionType substGenericArgs(CanAnyFunctionType fnType, - const SubstitutionList &subs) { + SubstitutionMap subs) { if (auto genericFnType = dyn_cast(fnType)) { return cast(genericFnType->substGenericArgs(subs) ->getCanonicalType()); @@ -1245,7 +1263,7 @@ static SILFunctionType *emitObjCThunkArguments(SILGenFunction &SGF, CanType &bridgedFormalResultTy) { SILDeclRef native = thunk.asForeign(false); - auto subs = SGF.F.getForwardingSubstitutions(); + auto subs = SGF.F.getForwardingSubstitutionMap(); auto objcInfo = SGF.SGM.Types.getConstantInfo(thunk); auto objcFnTy = objcInfo.SILFnType->substGenericArgs(SGF.SGM.M, subs); @@ -1366,7 +1384,7 @@ void SILGenFunction::emitNativeToForeignThunk(SILDeclRef thunk) { assert(thunk.isForeign); SILDeclRef native = thunk.asForeign(false); auto nativeInfo = getConstantInfo(native); - auto subs = F.getForwardingSubstitutions(); + auto subs = F.getForwardingSubstitutionMap(); auto substTy = nativeInfo.SILFnType->substGenericArgs(SGM.M, subs); SILType substSILTy = SILType::getPrimitiveObjectType(substTy); SILFunctionConventions substConv(substTy, SGM.M); @@ -1531,7 +1549,6 @@ getThunkedForeignFunctionRef(SILGenFunction &SGF, SILLocation loc, SILDeclRef foreign, ArrayRef args, - SubstitutionList subs, const SILConstantInfo &foreignCI) { assert(!foreign.isCurried && "should not thunk calling convention when curried"); @@ -1733,8 +1750,8 @@ void SILGenFunction::emitForeignToNativeThunk(SILDeclRef thunk) { maybeAddForeignErrorArg(); // Call the original. - auto subs = getForwardingSubstitutions(); - auto fn = getThunkedForeignFunctionRef(*this, fd, foreignDeclRef, args, subs, + auto subs = getForwardingSubstitutionMap(); + auto fn = getThunkedForeignFunctionRef(*this, fd, foreignDeclRef, args, foreignCI); auto fnType = fn->getType().castTo(); @@ -1760,11 +1777,10 @@ void SILGenFunction::emitForeignToNativeThunk(SILDeclRef thunk) { ResultPlanPtr resultPlan = ResultPlanBuilder::computeResultPlan( *this, calleeTypeInfo, fd, context); ArgumentScope argScope(*this, fd); - PostponedCleanup postpone(*this); ManagedValue resultMV = emitApply(std::move(resultPlan), std::move(argScope), fd, - ManagedValue::forUnmanaged(fn), subs, args, calleeTypeInfo, - ApplyOptions::None, context, postpone) + ManagedValue::forUnmanaged(fn), subs, args, + calleeTypeInfo, ApplyOptions::None, context) .getAsSingleValue(*this, fd); if (indirectResult) { diff --git a/lib/SILGen/SILGenBuilder.cpp b/lib/SILGen/SILGenBuilder.cpp index e69658ad4cb5d..eb6b25e37369b 100644 --- a/lib/SILGen/SILGenBuilder.cpp +++ b/lib/SILGen/SILGenBuilder.cpp @@ -76,9 +76,7 @@ MetatypeInst *SILGenBuilder::createMetatype(SILLocation loc, SILType metatype) { auto subMap = t->getContextSubstitutionMap(getSILGenModule().SwiftModule, decl); - SmallVector subs; - genericSig->getSubstitutions(subMap, subs); - getSILGenModule().useConformancesFromSubstitutions(subs); + getSILGenModule().useConformancesFromSubstitutions(subMap); return false; }); @@ -91,7 +89,7 @@ MetatypeInst *SILGenBuilder::createMetatype(SILLocation loc, SILType metatype) { ApplyInst *SILGenBuilder::createApply(SILLocation loc, SILValue fn, SILType substFnTy, SILType result, - SubstitutionList subs, + SubstitutionMap subs, ArrayRef args) { getSILGenModule().useConformancesFromSubstitutions(subs); return SILBuilder::createApply(loc, fn, subs, args, false); @@ -99,7 +97,7 @@ ApplyInst *SILGenBuilder::createApply(SILLocation loc, SILValue fn, TryApplyInst * SILGenBuilder::createTryApply(SILLocation loc, SILValue fn, SILType substFnTy, - SubstitutionList subs, ArrayRef args, + SubstitutionMap subs, ArrayRef args, SILBasicBlock *normalBB, SILBasicBlock *errorBB) { getSILGenModule().useConformancesFromSubstitutions(subs); return SILBuilder::createTryApply(loc, fn, subs, args, normalBB, errorBB); @@ -107,7 +105,7 @@ SILGenBuilder::createTryApply(SILLocation loc, SILValue fn, SILType substFnTy, PartialApplyInst * SILGenBuilder::createPartialApply(SILLocation loc, SILValue fn, - SILType substFnTy, SubstitutionList subs, + SILType substFnTy, SubstitutionMap subs, ArrayRef args, SILType closureTy) { getSILGenModule().useConformancesFromSubstitutions(subs); return SILBuilder::createPartialApply( @@ -118,7 +116,7 @@ SILGenBuilder::createPartialApply(SILLocation loc, SILValue fn, BuiltinInst *SILGenBuilder::createBuiltin(SILLocation loc, Identifier name, SILType resultTy, - SubstitutionList subs, + SubstitutionMap subs, ArrayRef args) { getSILGenModule().useConformancesFromSubstitutions(subs); return SILBuilder::createBuiltin(loc, name, resultTy, subs, args); @@ -185,7 +183,7 @@ AllocExistentialBoxInst *SILGenBuilder::createAllocExistentialBox( ManagedValue SILGenBuilder::createPartialApply(SILLocation loc, SILValue fn, SILType substFnTy, - SubstitutionList subs, + SubstitutionMap subs, ArrayRef args, SILType closureTy) { llvm::SmallVector values; @@ -209,7 +207,7 @@ ManagedValue SILGenBuilder::createConvertFunction(SILLocation loc, ManagedValue SILGenBuilder::createConvertEscapeToNoEscape( SILLocation loc, ManagedValue fn, SILType resultTy, - bool postponeToNoEscapeCleanup) { + bool isEscapedByUser) { auto fnType = fn.getType().castTo(); auto resultFnType = resultTy.castTo(); @@ -221,14 +219,9 @@ ManagedValue SILGenBuilder::createConvertEscapeToNoEscape( SILFunctionTypeRepresentation::Thick && !fnType->isNoEscape() && resultFnType->isNoEscape() && "Expect a escaping to noescape conversion"); - - SILValue fnValue = postponeToNoEscapeCleanup - ? fn.ensurePlusOne(SGF, loc).forward(SGF) - : fn.getValue(); - SILValue result = createConvertEscapeToNoEscape(loc, fnValue, resultTy, - postponeToNoEscapeCleanup); - if (postponeToNoEscapeCleanup) - getSILGenFunction().enterPostponedCleanup(fnValue); + SILValue fnValue = fn.getValue(); + SILValue result = createConvertEscapeToNoEscape( + loc, fnValue, resultTy, isEscapedByUser, false); return ManagedValue::forTrivialObjectRValue(result); } @@ -436,9 +429,10 @@ ManagedValue SILGenBuilder::createFormalAccessCopyAddr( return SGF.emitFormalAccessManagedBufferWithCleanup(loc, newAddr); } -ManagedValue SILGenBuilder::bufferForExpr( - SILLocation loc, SILType ty, const TypeLowering &lowering, - SGFContext context, std::function rvalueEmitter) { +ManagedValue +SILGenBuilder::bufferForExpr(SILLocation loc, SILType ty, + const TypeLowering &lowering, SGFContext context, + llvm::function_ref rvalueEmitter) { // If we have a single-buffer "emit into" initialization, use that for the // result. SILValue address = context.getAddressForInPlaceInitialization(SGF, loc); @@ -464,10 +458,9 @@ ManagedValue SILGenBuilder::bufferForExpr( return SGF.emitManagedBufferWithCleanup(address); } - ManagedValue SILGenBuilder::formalAccessBufferForExpr( SILLocation loc, SILType ty, const TypeLowering &lowering, - SGFContext context, std::function rvalueEmitter) { + SGFContext context, llvm::function_ref rvalueEmitter) { // If we have a single-buffer "emit into" initialization, use that for the // result. SILValue address = context.getAddressForInPlaceInitialization(SGF, loc); @@ -665,7 +658,7 @@ ManagedValue SILGenBuilder::createOptionalSome(SILLocation loc, ManagedValue arg) { CleanupCloner cloner(*this, arg); auto &argTL = SGF.getTypeLowering(arg.getType()); - SILType optionalType = arg.getType().wrapAnyOptionalType(getFunction()); + SILType optionalType = SILType::getOptionalType(arg.getType()); if (argTL.isLoadable() || !SGF.silConv.useLoweredAddresses()) { SILValue someValue = createOptionalSome(loc, arg.forward(SGF), optionalType); @@ -673,7 +666,7 @@ ManagedValue SILGenBuilder::createOptionalSome(SILLocation loc, } SILValue tempResult = SGF.emitTemporaryAllocation(loc, optionalType); - RValue rvalue(SGF, loc, arg.getType().getSwiftRValueType(), arg); + RValue rvalue(SGF, loc, arg.getType().getASTType(), arg); ArgumentSource argValue(loc, std::move(rvalue)); SGF.emitInjectOptionalValueInto( loc, std::move(argValue), tempResult, diff --git a/lib/SILGen/SILGenBuilder.h b/lib/SILGen/SILGenBuilder.h index a59d8fec8f435..133fed642aabb 100644 --- a/lib/SILGen/SILGenBuilder.h +++ b/lib/SILGen/SILGenBuilder.h @@ -76,24 +76,24 @@ class SILGenBuilder : public SILBuilder { using SILBuilder::createApply; ApplyInst *createApply(SILLocation loc, SILValue fn, SILType SubstFnTy, - SILType result, SubstitutionList subs, + SILType result, SubstitutionMap subs, ArrayRef args); TryApplyInst *createTryApply(SILLocation loc, SILValue fn, SILType substFnTy, - SubstitutionList subs, + SubstitutionMap subs, ArrayRef args, SILBasicBlock *normalBB, SILBasicBlock *errorBB); PartialApplyInst *createPartialApply(SILLocation loc, SILValue fn, - SILType substFnTy, SubstitutionList subs, + SILType substFnTy, SubstitutionMap subs, ArrayRef args, SILType closureTy); ManagedValue createPartialApply(SILLocation loc, SILValue fn, - SILType substFnTy, SubstitutionList subs, + SILType substFnTy, SubstitutionMap subs, ArrayRef args, SILType closureTy); ManagedValue createPartialApply(SILLocation loc, ManagedValue fn, - SILType substFnTy, SubstitutionList subs, + SILType substFnTy, SubstitutionMap subs, ArrayRef args, SILType closureTy) { return createPartialApply(loc, fn.getValue(), substFnTy, subs, args, @@ -101,7 +101,7 @@ class SILGenBuilder : public SILBuilder { } BuiltinInst *createBuiltin(SILLocation loc, Identifier name, SILType resultTy, - SubstitutionList subs, ArrayRef args); + SubstitutionMap subs, ArrayRef args); // Existential containers use the conformances needed by the existential // box. @@ -236,9 +236,8 @@ class SILGenBuilder : public SILBuilder { /// /// \return an empty value if the buffer was taken from the context. ManagedValue bufferForExpr(SILLocation loc, SILType ty, - const TypeLowering &lowering, - SGFContext context, - std::function rvalueEmitter); + const TypeLowering &lowering, SGFContext context, + llvm::function_ref rvalueEmitter); using SILBuilder::createUncheckedEnumData; ManagedValue createUncheckedEnumData(SILLocation loc, ManagedValue operand, @@ -276,9 +275,10 @@ class SILGenBuilder : public SILBuilder { ManagedValue createSemanticLoadBorrow(SILLocation loc, ManagedValue addr); - ManagedValue formalAccessBufferForExpr( - SILLocation loc, SILType ty, const TypeLowering &lowering, - SGFContext context, std::function rvalueEmitter); + ManagedValue + formalAccessBufferForExpr(SILLocation loc, SILType ty, + const TypeLowering &lowering, SGFContext context, + llvm::function_ref rvalueEmitter); using SILBuilder::createUnconditionalCheckedCastValue; ManagedValue @@ -364,7 +364,7 @@ class SILGenBuilder : public SILBuilder { ManagedValue createConvertEscapeToNoEscape(SILLocation loc, ManagedValue fn, SILType resultTy, - bool postponeToNoEscapeCleanup = true); + bool isEscapedByUser); using SILBuilder::createStore; /// Forward \p value into \p address. diff --git a/lib/SILGen/SILGenBuiltin.cpp b/lib/SILGen/SILGenBuiltin.cpp index f8953fed98b4f..4a7d52a406318 100644 --- a/lib/SILGen/SILGenBuiltin.cpp +++ b/lib/SILGen/SILGenBuiltin.cpp @@ -65,7 +65,7 @@ static ArrayRef decomposeArguments(SILGenFunction &SGF, static ManagedValue emitBuiltinRetain(SILGenFunction &SGF, SILLocation loc, - SubstitutionList substitutions, + SubstitutionMap substitutions, ArrayRef args, SGFContext C) { // The value was produced at +1; we can produce an unbalanced retain simply by @@ -78,7 +78,7 @@ static ManagedValue emitBuiltinRetain(SILGenFunction &SGF, static ManagedValue emitBuiltinRelease(SILGenFunction &SGF, SILLocation loc, - SubstitutionList substitutions, + SubstitutionMap substitutions, ArrayRef args, SGFContext C) { // The value was produced at +1, so to produce an unbalanced @@ -91,7 +91,7 @@ static ManagedValue emitBuiltinRelease(SILGenFunction &SGF, static ManagedValue emitBuiltinAutorelease(SILGenFunction &SGF, SILLocation loc, - SubstitutionList substitutions, + SubstitutionMap substitutions, ArrayRef args, SGFContext C) { SGF.B.createUnmanagedAutoreleaseValue(loc, args[0].getValue(), @@ -113,13 +113,14 @@ static bool requireIsOptionalNativeObject(SILGenFunction &SGF, static ManagedValue emitBuiltinTryPin(SILGenFunction &SGF, SILLocation loc, - SubstitutionList subs, + SubstitutionMap subs, ArrayRef args, SGFContext C) { assert(args.size() == 1); - if (!requireIsOptionalNativeObject(SGF, loc, subs[0].getReplacement())) { - return SGF.emitUndef(loc, subs[0].getReplacement()); + auto argTy = subs.getReplacementTypes()[0]; + if (!requireIsOptionalNativeObject(SGF, loc, argTy)) { + return SGF.emitUndef(loc, argTy); } // The value was produced at +1, but pinning is only a conditional @@ -134,12 +135,13 @@ static ManagedValue emitBuiltinTryPin(SILGenFunction &SGF, static ManagedValue emitBuiltinUnpin(SILGenFunction &SGF, SILLocation loc, - SubstitutionList subs, + SubstitutionMap subs, ArrayRef args, SGFContext C) { assert(args.size() == 1); - if (requireIsOptionalNativeObject(SGF, loc, subs[0].getReplacement())) { + auto argTy = subs.getReplacementTypes()[0]; + if (requireIsOptionalNativeObject(SGF, loc, argTy)) { // Unpinning takes responsibility for the +1 handle. SGF.B.createStrongUnpin(loc, args[0].ensurePlusOne(SGF, loc).forward(SGF), SGF.B.getDefaultAtomicity()); @@ -151,19 +153,20 @@ static ManagedValue emitBuiltinUnpin(SILGenFunction &SGF, /// Specialized emitter for Builtin.load and Builtin.take. static ManagedValue emitBuiltinLoadOrTake(SILGenFunction &SGF, SILLocation loc, - SubstitutionList substitutions, + SubstitutionMap substitutions, ArrayRef args, SGFContext C, IsTake_t isTake, bool isStrict, bool isInvariant) { - assert(substitutions.size() == 1 && "load should have single substitution"); + assert(substitutions.getReplacementTypes().size() == 1 && + "load should have single substitution"); assert(args.size() == 1 && "load should have a single argument"); // The substitution gives the type of the load. This is always a // first-class type; there is no way to e.g. produce a @weak load // with this builtin. - auto &rvalueTL = SGF.getTypeLowering(substitutions[0].getReplacement()); + auto &rvalueTL = SGF.getTypeLowering(substitutions.getReplacementTypes()[0]); SILType loadedType = rvalueTL.getLoweredType(); // Convert the pointer argument to a SIL address. @@ -176,7 +179,7 @@ static ManagedValue emitBuiltinLoadOrTake(SILGenFunction &SGF, static ManagedValue emitBuiltinLoad(SILGenFunction &SGF, SILLocation loc, - SubstitutionList substitutions, + SubstitutionMap substitutions, ArrayRef args, SGFContext C) { return emitBuiltinLoadOrTake(SGF, loc, substitutions, args, @@ -186,7 +189,7 @@ static ManagedValue emitBuiltinLoad(SILGenFunction &SGF, static ManagedValue emitBuiltinLoadRaw(SILGenFunction &SGF, SILLocation loc, - SubstitutionList substitutions, + SubstitutionMap substitutions, ArrayRef args, SGFContext C) { return emitBuiltinLoadOrTake(SGF, loc, substitutions, args, @@ -196,7 +199,7 @@ static ManagedValue emitBuiltinLoadRaw(SILGenFunction &SGF, static ManagedValue emitBuiltinLoadInvariant(SILGenFunction &SGF, SILLocation loc, - SubstitutionList substitutions, + SubstitutionMap substitutions, ArrayRef args, SGFContext C) { return emitBuiltinLoadOrTake(SGF, loc, substitutions, args, @@ -206,7 +209,7 @@ static ManagedValue emitBuiltinLoadInvariant(SILGenFunction &SGF, static ManagedValue emitBuiltinTake(SILGenFunction &SGF, SILLocation loc, - SubstitutionList substitutions, + SubstitutionMap substitutions, ArrayRef args, SGFContext C) { return emitBuiltinLoadOrTake(SGF, loc, substitutions, args, @@ -217,14 +220,14 @@ static ManagedValue emitBuiltinTake(SILGenFunction &SGF, /// Specialized emitter for Builtin.destroy. static ManagedValue emitBuiltinDestroy(SILGenFunction &SGF, SILLocation loc, - SubstitutionList substitutions, + SubstitutionMap substitutions, ArrayRef args, SGFContext C) { assert(args.size() == 2 && "destroy should have two arguments"); - assert(substitutions.size() == 1 && + assert(substitutions.getReplacementTypes().size() == 1 && "destroy should have a single substitution"); // The substitution determines the type of the thing we're destroying. - auto &ti = SGF.getTypeLowering(substitutions[0].getReplacement()); + auto &ti = SGF.getTypeLowering(substitutions.getReplacementTypes()[0]); // Destroy is a no-op for trivial types. if (ti.isTrivial()) @@ -248,15 +251,16 @@ static ManagedValue emitBuiltinDestroy(SILGenFunction &SGF, static ManagedValue emitBuiltinAssign(SILGenFunction &SGF, SILLocation loc, - SubstitutionList substitutions, + SubstitutionMap substitutions, ArrayRef args, SGFContext C) { assert(args.size() >= 2 && "assign should have two arguments"); - assert(substitutions.size() == 1 && + assert(substitutions.getReplacementTypes().size() == 1 && "assign should have a single substitution"); // The substitution determines the type of the thing we're destroying. - CanType assignFormalType = substitutions[0].getReplacement()->getCanonicalType(); + CanType assignFormalType = + substitutions.getReplacementTypes()[0]->getCanonicalType(); SILType assignType = SGF.getLoweredType(assignFormalType); // Convert the destination pointer argument to a SIL address. @@ -278,12 +282,13 @@ static ManagedValue emitBuiltinAssign(SILGenFunction &SGF, /// the address. static ManagedValue emitBuiltinInit(SILGenFunction &SGF, SILLocation loc, - SubstitutionList substitutions, + SubstitutionMap substitutions, Expr *tuple, SGFContext C) { auto args = decomposeArguments(SGF, tuple, 2); - CanType formalType = substitutions[0].getReplacement()->getCanonicalType(); + CanType formalType = + substitutions.getReplacementTypes()[0]->getCanonicalType(); auto &formalTL = SGF.getTypeLowering(formalType); SILValue addr = SGF.emitRValueAsSingleValue(args[1]).getUnmanagedValue(); @@ -301,7 +306,7 @@ static ManagedValue emitBuiltinInit(SILGenFunction &SGF, /// Specialized emitter for Builtin.fixLifetime. static ManagedValue emitBuiltinFixLifetime(SILGenFunction &SGF, SILLocation loc, - SubstitutionList substitutions, + SubstitutionMap substitutions, ArrayRef args, SGFContext C) { for (auto arg : args) { @@ -312,16 +317,17 @@ static ManagedValue emitBuiltinFixLifetime(SILGenFunction &SGF, static ManagedValue emitCastToReferenceType(SILGenFunction &SGF, SILLocation loc, - SubstitutionList substitutions, + SubstitutionMap substitutions, ArrayRef args, SGFContext C, SILType objPointerType) { assert(args.size() == 1 && "cast should have a single argument"); - assert(substitutions.size() == 1 && "cast should have a type substitution"); + assert(substitutions.getReplacementTypes().size() == 1 && + "cast should have a type substitution"); // Bail if the source type is not a class reference of some kind. - if (!substitutions[0].getReplacement()->mayHaveSuperclass() && - !substitutions[0].getReplacement()->isClassExistentialType()) { + Type argTy = substitutions.getReplacementTypes()[0]; + if (!argTy->mayHaveSuperclass() && !argTy->isClassExistentialType()) { SGF.SGM.diagnose(loc, diag::invalid_sil_builtin, "castToNativeObject source must be a class"); SILValue undef = SILUndef::get(objPointerType, SGF.SGM.M); @@ -332,9 +338,8 @@ static ManagedValue emitCastToReferenceType(SILGenFunction &SGF, ManagedValue arg = args[0]; // If the argument is existential, open it. - if (substitutions[0].getReplacement()->isClassExistentialType()) { - auto openedTy - = ArchetypeType::getOpened(substitutions[0].getReplacement()); + if (argTy->isClassExistentialType()) { + auto openedTy = ArchetypeType::getOpened(argTy); SILType loweredOpenedTy = SGF.getLoweredLoadableType(openedTy); arg = SGF.B.createOpenExistentialRef(loc, arg, loweredOpenedTy); } @@ -346,7 +351,7 @@ static ManagedValue emitCastToReferenceType(SILGenFunction &SGF, /// Specialized emitter for Builtin.unsafeCastToNativeObject. static ManagedValue emitBuiltinUnsafeCastToNativeObject(SILGenFunction &SGF, SILLocation loc, - SubstitutionList substitutions, + SubstitutionMap substitutions, ArrayRef args, SGFContext C) { return emitCastToReferenceType(SGF, loc, substitutions, args, C, @@ -356,10 +361,10 @@ static ManagedValue emitBuiltinUnsafeCastToNativeObject(SILGenFunction &SGF, /// Specialized emitter for Builtin.castToNativeObject. static ManagedValue emitBuiltinCastToNativeObject(SILGenFunction &SGF, SILLocation loc, - SubstitutionList substitutions, + SubstitutionMap substitutions, ArrayRef args, SGFContext C) { - CanType ty = args[0].getType().getSwiftRValueType(); + auto ty = args[0].getType().getASTType(); (void)ty; assert(ty->usesNativeReferenceCounting(ResilienceExpansion::Maximal) && "Can only cast types that use native reference counting to native " @@ -371,18 +376,19 @@ static ManagedValue emitBuiltinCastToNativeObject(SILGenFunction &SGF, static ManagedValue emitCastFromReferenceType(SILGenFunction &SGF, SILLocation loc, - SubstitutionList substitutions, + SubstitutionMap substitutions, ArrayRef args, SGFContext C) { assert(args.size() == 1 && "cast should have a single argument"); - assert(substitutions.size() == 1 && + assert(substitutions.getReplacementTypes().size() == 1 && "cast should have a single substitution"); // The substitution determines the destination type. - SILType destType = SGF.getLoweredType(substitutions[0].getReplacement()); + SILType destType = + SGF.getLoweredType(substitutions.getReplacementTypes()[0]); // Bail if the source type is not a class reference of some kind. - if (!substitutions[0].getReplacement()->isBridgeableObjectType() + if (!substitutions.getReplacementTypes()[0]->isBridgeableObjectType() || !destType.isObject()) { SGF.SGM.diagnose(loc, diag::invalid_sil_builtin, "castFromNativeObject dest must be an object type"); @@ -397,7 +403,7 @@ static ManagedValue emitCastFromReferenceType(SILGenFunction &SGF, /// Specialized emitter for Builtin.castFromNativeObject. static ManagedValue emitBuiltinCastFromNativeObject(SILGenFunction &SGF, SILLocation loc, - SubstitutionList substitutions, + SubstitutionMap substitutions, ArrayRef args, SGFContext C) { return emitCastFromReferenceType(SGF, loc, substitutions, args, C); @@ -406,7 +412,7 @@ static ManagedValue emitBuiltinCastFromNativeObject(SILGenFunction &SGF, /// Specialized emitter for Builtin.bridgeToRawPointer. static ManagedValue emitBuiltinBridgeToRawPointer(SILGenFunction &SGF, SILLocation loc, - SubstitutionList substitutions, + SubstitutionMap substitutions, ArrayRef args, SGFContext C) { assert(args.size() == 1 && "bridge should have a single argument"); @@ -423,16 +429,17 @@ static ManagedValue emitBuiltinBridgeToRawPointer(SILGenFunction &SGF, /// Specialized emitter for Builtin.bridgeFromRawPointer. static ManagedValue emitBuiltinBridgeFromRawPointer(SILGenFunction &SGF, SILLocation loc, - SubstitutionList substitutions, + SubstitutionMap substitutions, ArrayRef args, SGFContext C) { - assert(substitutions.size() == 1 && + assert(substitutions.getReplacementTypes().size() == 1 && "bridge should have a single substitution"); assert(args.size() == 1 && "bridge should have a single argument"); // The substitution determines the destination type. // FIXME: Archetype destination type? - auto &destLowering = SGF.getTypeLowering(substitutions[0].getReplacement()); + auto &destLowering = + SGF.getTypeLowering(substitutions.getReplacementTypes()[0]); assert(destLowering.isLoadable()); SILType destType = destLowering.getLoweredType(); @@ -446,15 +453,52 @@ static ManagedValue emitBuiltinBridgeFromRawPointer(SILGenFunction &SGF, /// Specialized emitter for Builtin.addressof. static ManagedValue emitBuiltinAddressOf(SILGenFunction &SGF, SILLocation loc, - SubstitutionList substitutions, - ArrayRef args, + SubstitutionMap substitutions, + Expr *argument, SGFContext C) { - assert(args.size() == 1 && "addressof should have a single argument"); + auto rawPointerTy = SILType::getRawPointerType(SGF.getASTContext()); + // If the argument is inout, try forming its lvalue. This builtin only works + // if it's trivially physically projectable. + auto inout = cast(argument->getSemanticsProvidingExpr()); + auto lv = SGF.emitLValue(inout->getSubExpr(), AccessKind::ReadWrite); + if (!lv.isPhysical() || !lv.isLoadingPure()) { + SGF.SGM.diagnose(argument->getLoc(), diag::non_physical_addressof); + return ManagedValue::forUnmanaged(SILUndef::get(rawPointerTy, &SGF.SGM.M)); + } + + auto addr = SGF.emitAddressOfLValue(argument, std::move(lv), + AccessKind::ReadWrite) + .getValue(); // Take the address argument and cast it to RawPointer. SILType rawPointerType = SILType::getRawPointerType(SGF.F.getASTContext()); - SILValue result = SGF.B.createAddressToPointer(loc, - args[0].getUnmanagedValue(), + SILValue result = SGF.B.createAddressToPointer(loc, addr, + rawPointerType); + return ManagedValue::forUnmanaged(result); +} + +/// Specialized emitter for Builtin.addressOfBorrow. +static ManagedValue emitBuiltinAddressOfBorrow(SILGenFunction &SGF, + SILLocation loc, + SubstitutionMap substitutions, + Expr *argument, + SGFContext C) { + auto rawPointerTy = SILType::getRawPointerType(SGF.getASTContext()); + SILValue addr; + // Try to borrow the argument at +0. We only support if it's + // naturally emitted borrowed in memory. + auto borrow = SGF.emitRValue(argument, SGFContext::AllowGuaranteedPlusZero) + .getAsSingleValue(SGF, argument); + if (!borrow.isPlusZero() || !borrow.getType().isAddress()) { + SGF.SGM.diagnose(argument->getLoc(), diag::non_borrowed_indirect_addressof); + return ManagedValue::forUnmanaged(SILUndef::get(rawPointerTy, &SGF.SGM.M)); + } + + addr = borrow.getValue(); + + // Take the address argument and cast it to RawPointer. + SILType rawPointerType = SILType::getRawPointerType(SGF.F.getASTContext()); + SILValue result = SGF.B.createAddressToPointer(loc, addr, rawPointerType); return ManagedValue::forUnmanaged(result); } @@ -462,7 +506,7 @@ static ManagedValue emitBuiltinAddressOf(SILGenFunction &SGF, /// Specialized emitter for Builtin.gepRaw. static ManagedValue emitBuiltinGepRaw(SILGenFunction &SGF, SILLocation loc, - SubstitutionList substitutions, + SubstitutionMap substitutions, ArrayRef args, SGFContext C) { assert(args.size() == 2 && "gepRaw should be given two arguments"); @@ -476,15 +520,17 @@ static ManagedValue emitBuiltinGepRaw(SILGenFunction &SGF, /// Specialized emitter for Builtin.gep. static ManagedValue emitBuiltinGep(SILGenFunction &SGF, SILLocation loc, - SubstitutionList substitutions, + SubstitutionMap substitutions, ArrayRef args, SGFContext C) { - assert(substitutions.size() == 1 && "gep should have two substitutions"); + assert(substitutions.getReplacementTypes().size() == 1 && + "gep should have two substitutions"); assert(args.size() == 3 && "gep should be given three arguments"); - SILType ElemTy = SGF.getLoweredType(substitutions[0].getReplacement()); + SILType ElemTy = SGF.getLoweredType(substitutions.getReplacementTypes()[0]); SILType RawPtrType = args[0].getUnmanagedValue()->getType(); - SILValue addr = SGF.B.createPointerToAddress(loc, args[0].getUnmanagedValue(), + SILValue addr = SGF.B.createPointerToAddress(loc, + args[0].getUnmanagedValue(), ElemTy.getAddressType(), /*strict*/ true, /*invariant*/ false); @@ -497,16 +543,18 @@ static ManagedValue emitBuiltinGep(SILGenFunction &SGF, /// Specialized emitter for Builtin.getTailAddr. static ManagedValue emitBuiltinGetTailAddr(SILGenFunction &SGF, SILLocation loc, - SubstitutionList substitutions, + SubstitutionMap substitutions, ArrayRef args, SGFContext C) { - assert(substitutions.size() == 2 && "getTailAddr should have two substitutions"); + assert(substitutions.getReplacementTypes().size() == 2 && + "getTailAddr should have two substitutions"); assert(args.size() == 4 && "gep should be given four arguments"); - SILType ElemTy = SGF.getLoweredType(substitutions[0].getReplacement()); - SILType TailTy = SGF.getLoweredType(substitutions[1].getReplacement()); + SILType ElemTy = SGF.getLoweredType(substitutions.getReplacementTypes()[0]); + SILType TailTy = SGF.getLoweredType(substitutions.getReplacementTypes()[1]); SILType RawPtrType = args[0].getUnmanagedValue()->getType(); - SILValue addr = SGF.B.createPointerToAddress(loc, args[0].getUnmanagedValue(), + SILValue addr = SGF.B.createPointerToAddress(loc, + args[0].getUnmanagedValue(), ElemTy.getAddressType(), /*strict*/ true, /*invariant*/ false); @@ -517,10 +565,108 @@ static ManagedValue emitBuiltinGetTailAddr(SILGenFunction &SGF, return ManagedValue::forUnmanaged(addr); } +/// Specialized emitter for Builtin.beginUnpairedModifyAccess. +static ManagedValue emitBuiltinBeginUnpairedModifyAccess(SILGenFunction &SGF, + SILLocation loc, + SubstitutionMap substitutions, + ArrayRef args, + SGFContext C) { + assert(substitutions.getReplacementTypes().size() == 1 && + "Builtin.beginUnpairedModifyAccess should have one substitution"); + assert(args.size() == 3 && + "beginUnpairedModifyAccess should be given three arguments"); + + SILType elemTy = SGF.getLoweredType(substitutions.getReplacementTypes()[0]); + SILValue addr = SGF.B.createPointerToAddress(loc, + args[0].getUnmanagedValue(), + elemTy.getAddressType(), + /*strict*/ true, + /*invariant*/ false); + + SILType valueBufferTy = + SGF.getLoweredType(SGF.getASTContext().TheUnsafeValueBufferType); + + SILValue buffer = + SGF.B.createPointerToAddress(loc, args[1].getUnmanagedValue(), + valueBufferTy.getAddressType(), + /*strict*/ true, + /*invariant*/ false); + SGF.B.createBeginUnpairedAccess(loc, addr, buffer, SILAccessKind::Modify, + SILAccessEnforcement::Dynamic, + /*noNestedConflict*/ false, + /*fromBuiltin*/ true); + + return ManagedValue::forUnmanaged(SGF.emitEmptyTuple(loc)); +} + +/// Specialized emitter for Builtin.performInstantaneousReadAccess +static ManagedValue emitBuiltinPerformInstantaneousReadAccess( + SILGenFunction &SGF, SILLocation loc, SubstitutionMap substitutions, + ArrayRef args, SGFContext C) { + + assert(substitutions.getReplacementTypes().size() == 1 && + "Builtin.performInstantaneousReadAccess should have one substitution"); + assert(args.size() == 2 && + "Builtin.performInstantaneousReadAccess should be given " + "two arguments"); + + SILType elemTy = SGF.getLoweredType(substitutions.getReplacementTypes()[0]); + SILValue addr = SGF.B.createPointerToAddress(loc, + args[0].getUnmanagedValue(), + elemTy.getAddressType(), + /*strict*/ true, + /*invariant*/ false); + + SILType valueBufferTy = + SGF.getLoweredType(SGF.getASTContext().TheUnsafeValueBufferType); + SILValue unusedBuffer = SGF.emitTemporaryAllocation(loc, valueBufferTy); + + // Begin an "unscoped" read access. No nested conflict is possible because + // the compiler should generate the actual read for the KeyPath expression + // immediately after the call to this builtin, which forms the address of + // that real access. When noNestedConflict=true, no EndUnpairedAccess should + // be emitted. + // + // Unpaired access is necessary because a BeginAccess/EndAccess pair with no + // use will be trivially optimized away. + SGF.B.createBeginUnpairedAccess(loc, addr, unusedBuffer, SILAccessKind::Read, + SILAccessEnforcement::Dynamic, + /*noNestedConflict*/ true, + /*fromBuiltin*/ true); + + return ManagedValue::forUnmanaged(SGF.emitEmptyTuple(loc)); +} + +/// Specialized emitter for Builtin.endUnpairedAccessModifyAccess. +static ManagedValue emitBuiltinEndUnpairedAccess(SILGenFunction &SGF, + SILLocation loc, + SubstitutionMap substitutions, + ArrayRef args, + SGFContext C) { + assert(substitutions.empty() && + "Builtin.endUnpairedAccess should have no substitutions"); + assert(args.size() == 1 && + "endUnpairedAccess should be given one argument"); + + SILType valueBufferTy = + SGF.getLoweredType(SGF.getASTContext().TheUnsafeValueBufferType); + + SILValue buffer = SGF.B.createPointerToAddress(loc, + args[0].getUnmanagedValue(), + valueBufferTy.getAddressType(), + /*strict*/ true, + /*invariant*/ false); + SGF.B.createEndUnpairedAccess(loc, buffer, SILAccessEnforcement::Dynamic, + /*aborted*/ false, + /*fromBuiltin*/ true); + + return ManagedValue::forUnmanaged(SGF.emitEmptyTuple(loc)); +} + /// Specialized emitter for Builtin.condfail. static ManagedValue emitBuiltinCondFail(SILGenFunction &SGF, SILLocation loc, - SubstitutionList substitutions, + SubstitutionMap substitutions, ArrayRef args, SGFContext C) { assert(args.size() == 1 && "condfail should be given one argument"); @@ -533,14 +679,15 @@ static ManagedValue emitBuiltinCondFail(SILGenFunction &SGF, static ManagedValue emitBuiltinCastReference(SILGenFunction &SGF, SILLocation loc, - SubstitutionList substitutions, + SubstitutionMap substitutions, ArrayRef args, SGFContext C) { assert(args.size() == 1 && "castReference should be given one argument"); - assert(substitutions.size() == 2 && "castReference should have two subs"); + assert(substitutions.getReplacementTypes().size() == 2 && + "castReference should have two subs"); - auto fromTy = substitutions[0].getReplacement(); - auto toTy = substitutions[1].getReplacement(); + auto fromTy = substitutions.getReplacementTypes()[0]; + auto toTy = substitutions.getReplacementTypes()[1]; auto &fromTL = SGF.getTypeLowering(fromTy); auto &toTL = SGF.getTypeLowering(toTy); assert(!fromTL.isTrivial() && !toTL.isTrivial() && "expected ref type"); @@ -596,14 +743,15 @@ emitBuiltinCastReference(SILGenFunction &SGF, /// Specialized emitter for Builtin.reinterpretCast. static ManagedValue emitBuiltinReinterpretCast(SILGenFunction &SGF, SILLocation loc, - SubstitutionList substitutions, + SubstitutionMap substitutions, ArrayRef args, SGFContext C) { assert(args.size() == 1 && "reinterpretCast should be given one argument"); - assert(substitutions.size() == 2 && "reinterpretCast should have two subs"); + assert(substitutions.getReplacementTypes().size() == 2 && + "reinterpretCast should have two subs"); - auto &fromTL = SGF.getTypeLowering(substitutions[0].getReplacement()); - auto &toTL = SGF.getTypeLowering(substitutions[1].getReplacement()); + auto &fromTL = SGF.getTypeLowering(substitutions.getReplacementTypes()[0]); + auto &toTL = SGF.getTypeLowering(substitutions.getReplacementTypes()[1]); // If casting between address types, cast the address. if (fromTL.isAddress() || toTL.isAddress()) { @@ -656,18 +804,20 @@ static ManagedValue emitBuiltinReinterpretCast(SILGenFunction &SGF, /// Specialized emitter for Builtin.castToBridgeObject. static ManagedValue emitBuiltinCastToBridgeObject(SILGenFunction &SGF, SILLocation loc, - SubstitutionList subs, + SubstitutionMap subs, ArrayRef args, SGFContext C) { assert(args.size() == 2 && "cast should have two arguments"); - assert(subs.size() == 1 && "cast should have a type substitution"); + assert(subs.getReplacementTypes().size() == 1 && + "cast should have a type substitution"); // Take the reference type argument and cast it to BridgeObject. SILType objPointerType = SILType::getBridgeObjectType(SGF.F.getASTContext()); // Bail if the source type is not a class reference of some kind. - if (!subs[0].getReplacement()->mayHaveSuperclass() && - !subs[0].getReplacement()->isClassExistentialType()) { + auto sourceType = subs.getReplacementTypes()[0]; + if (!sourceType->mayHaveSuperclass() && + !sourceType->isClassExistentialType()) { SGF.SGM.diagnose(loc, diag::invalid_sil_builtin, "castToBridgeObject source must be a class"); SILValue undef = SILUndef::get(objPointerType, SGF.SGM.M); @@ -678,9 +828,8 @@ static ManagedValue emitBuiltinCastToBridgeObject(SILGenFunction &SGF, SILValue bits = args[1].getUnmanagedValue(); // If the argument is existential, open it. - if (subs[0].getReplacement()->isClassExistentialType()) { - auto openedTy - = ArchetypeType::getOpened(subs[0].getReplacement()); + if (sourceType->isClassExistentialType()) { + auto openedTy = ArchetypeType::getOpened(sourceType); SILType loweredOpenedTy = SGF.getLoweredLoadableType(openedTy); ref = SGF.B.createOpenExistentialRef(loc, ref, loweredOpenedTy); } @@ -692,18 +841,19 @@ static ManagedValue emitBuiltinCastToBridgeObject(SILGenFunction &SGF, static ManagedValue emitBuiltinCastReferenceFromBridgeObject( SILGenFunction &SGF, SILLocation loc, - SubstitutionList subs, + SubstitutionMap subs, ArrayRef args, SGFContext C) { assert(args.size() == 1 && "cast should have one argument"); - assert(subs.size() == 1 && "cast should have a type substitution"); + assert(subs.getReplacementTypes().size() == 1 && + "cast should have a type substitution"); // The substitution determines the destination type. - SILType destType = SGF.getLoweredType(subs[0].getReplacement()); + auto destTy = subs.getReplacementTypes()[0]; + SILType destType = SGF.getLoweredType(destTy); // Bail if the source type is not a class reference of some kind. - if (!subs[0].getReplacement()->isBridgeableObjectType() - || !destType.isObject()) { + if (!destTy->isBridgeableObjectType() || !destType.isObject()) { SGF.SGM.diagnose(loc, diag::invalid_sil_builtin, "castReferenceFromBridgeObject dest must be an object type"); // Recover by propagating an undef result. @@ -717,7 +867,7 @@ static ManagedValue emitBuiltinCastReferenceFromBridgeObject( static ManagedValue emitBuiltinCastBitPatternFromBridgeObject( SILGenFunction &SGF, SILLocation loc, - SubstitutionList subs, + SubstitutionMap subs, ArrayRef args, SGFContext C) { assert(args.size() == 1 && "cast should have one argument"); @@ -731,7 +881,7 @@ static ManagedValue emitBuiltinCastBitPatternFromBridgeObject( static ManagedValue emitBuiltinClassifyBridgeObject(SILGenFunction &SGF, SILLocation loc, - SubstitutionList subs, + SubstitutionMap subs, ArrayRef args, SGFContext C) { assert(args.size() == 1 && "classify should have one argument"); @@ -743,12 +893,13 @@ static ManagedValue emitBuiltinClassifyBridgeObject(SILGenFunction &SGF, static ManagedValue emitBuiltinValueToBridgeObject(SILGenFunction &SGF, SILLocation loc, - SubstitutionList subs, + SubstitutionMap subs, ArrayRef args, SGFContext C) { assert(args.size() == 1 && "ValueToBridgeObject should have one argument"); - assert(subs.size() == 1 && "ValueToBridgeObject should have one sub"); - auto &fromTL = SGF.getTypeLowering(subs[0].getReplacement()); + assert(subs.getReplacementTypes().size() == 1 && + "ValueToBridgeObject should have one sub"); + auto &fromTL = SGF.getTypeLowering(subs.getReplacementTypes()[0]); assert(fromTL.isTrivial() && "Expected a trivial type"); SILValue result = SGF.B.createValueToBridgeObject(loc, args[0].getValue()); @@ -761,11 +912,12 @@ static ManagedValue emitBuiltinValueToBridgeObject(SILGenFunction &SGF, // generic wrapper (we can only type check after specialization). static ManagedValue emitBuiltinIsUnique(SILGenFunction &SGF, SILLocation loc, - SubstitutionList subs, + SubstitutionMap subs, ArrayRef args, SGFContext C) { - assert(subs.size() == 1 && "isUnique should have a single substitution"); + assert(subs.getReplacementTypes().size() == 1 && + "isUnique should have a single substitution"); assert(args.size() == 1 && "isUnique should have a single argument"); assert((args[0].getType().isAddress() && !args[0].hasCleanup()) && "Builtin.isUnique takes an address."); @@ -777,10 +929,11 @@ static ManagedValue emitBuiltinIsUnique(SILGenFunction &SGF, static ManagedValue emitBuiltinIsUniqueOrPinned(SILGenFunction &SGF, SILLocation loc, - SubstitutionList subs, + SubstitutionMap subs, ArrayRef args, SGFContext C) { - assert(subs.size() == 1 && "isUnique should have a single substitution"); + assert(subs.getReplacementTypes().size() == 1 && + "isUnique should have a single substitution"); assert(args.size() == 1 && "isUnique should have a single argument"); assert((args[0].getType().isAddress() && !args[0].hasCleanup()) && "Builtin.isUnique takes an address."); @@ -795,11 +948,12 @@ emitBuiltinIsUniqueOrPinned(SILGenFunction &SGF, static ManagedValue emitBuiltinIsUnique_native(SILGenFunction &SGF, SILLocation loc, - SubstitutionList subs, + SubstitutionMap subs, ArrayRef args, SGFContext C) { - assert(subs.size() == 1 && "isUnique_native should have one sub."); + assert(subs.getReplacementTypes().size() == 1 && + "isUnique_native should have one sub."); assert(args.size() == 1 && "isUnique_native should have one arg."); auto ToType = @@ -812,11 +966,12 @@ emitBuiltinIsUnique_native(SILGenFunction &SGF, static ManagedValue emitBuiltinIsUniqueOrPinned_native(SILGenFunction &SGF, SILLocation loc, - SubstitutionList subs, + SubstitutionMap subs, ArrayRef args, SGFContext C) { - assert(subs.size() == 1 && "isUniqueOrPinned_native should have one sub."); + assert(subs.getReplacementTypes().size() == 1 && + "isUniqueOrPinned_native should have one sub."); assert(args.size() == 1 && "isUniqueOrPinned_native should have one arg."); auto ToType = @@ -828,14 +983,14 @@ emitBuiltinIsUniqueOrPinned_native(SILGenFunction &SGF, static ManagedValue emitBuiltinBindMemory(SILGenFunction &SGF, SILLocation loc, - SubstitutionList subs, + SubstitutionMap subs, ArrayRef args, SGFContext C) { - assert(subs.size() == 1 && "bindMemory should have a single substitution"); + assert(subs.getReplacementTypes().size() == 1 && "bindMemory should have a single substitution"); assert(args.size() == 3 && "bindMemory should have three argument"); // The substitution determines the element type for bound memory. - CanType boundFormalType = subs[0].getReplacement()->getCanonicalType(); + CanType boundFormalType = subs.getReplacementTypes()[0]->getCanonicalType(); SILType boundType = SGF.getLoweredType(boundFormalType); SGF.B.createBindMemory(loc, args[0].getValue(), @@ -846,23 +1001,24 @@ static ManagedValue emitBuiltinBindMemory(SILGenFunction &SGF, static ManagedValue emitBuiltinAllocWithTailElems(SILGenFunction &SGF, SILLocation loc, - SubstitutionList subs, + SubstitutionMap subs, ArrayRef args, SGFContext C) { - unsigned NumTailTypes = subs.size() - 1; + unsigned NumTailTypes = subs.getReplacementTypes().size() - 1; assert(args.size() == NumTailTypes * 2 + 1 && "wrong number of substitutions for allocWithTailElems"); // The substitution determines the element type for bound memory. - SILType RefType = SGF.getLoweredType(subs[0].getReplacement()-> + auto replacementTypes = subs.getReplacementTypes(); + SILType RefType = SGF.getLoweredType(replacementTypes[0]-> getCanonicalType()).getObjectType(); SmallVector Counts; SmallVector ElemTypes; for (unsigned Idx = 0; Idx < NumTailTypes; ++Idx) { Counts.push_back(args[Idx * 2 + 1]); - ElemTypes.push_back(SGF.getLoweredType(subs[Idx+1].getReplacement()-> - getCanonicalType()).getObjectType()); + ElemTypes.push_back(SGF.getLoweredType(replacementTypes[Idx+1]-> + getCanonicalType()).getObjectType()); } ManagedValue Metatype = args[0]; if (isa(Metatype)) { @@ -878,16 +1034,16 @@ static ManagedValue emitBuiltinAllocWithTailElems(SILGenFunction &SGF, static ManagedValue emitBuiltinProjectTailElems(SILGenFunction &SGF, SILLocation loc, - SubstitutionList subs, + SubstitutionMap subs, ArrayRef args, SGFContext C) { - assert(subs.size() == 2 && + assert(subs.getReplacementTypes().size() == 2 && "allocWithTailElems should have two substitutions"); assert(args.size() == 2 && "allocWithTailElems should have three arguments"); // The substitution determines the element type for bound memory. - SILType ElemType = SGF.getLoweredType(subs[1].getReplacement()-> + SILType ElemType = SGF.getLoweredType(subs.getReplacementTypes()[1]-> getCanonicalType()).getObjectType(); SILValue result = SGF.B.createRefTailAddr(loc, args[0].getValue(), @@ -902,17 +1058,17 @@ template static ManagedValue emitBuiltinTypeTrait(SILGenFunction &SGF, SILLocation loc, - SubstitutionList substitutions, + SubstitutionMap substitutions, ArrayRef args, SGFContext C) { - assert(substitutions.size() == 1 + assert(substitutions.getReplacementTypes().size() == 1 && "type trait should take a single type parameter"); assert(args.size() == 1 && "type trait should take a single argument"); unsigned result; - auto traitTy = substitutions[0].getReplacement()->getCanonicalType(); + auto traitTy = substitutions.getReplacementTypes()[0]->getCanonicalType(); switch ((traitTy.getPointer()->*Trait)()) { // If the type obviously has or lacks the trait, emit a constant result. diff --git a/lib/SILGen/SILGenConstructor.cpp b/lib/SILGen/SILGenConstructor.cpp index 4507dfc7b3c63..e5283e40c4d3e 100644 --- a/lib/SILGen/SILGenConstructor.cpp +++ b/lib/SILGen/SILGenConstructor.cpp @@ -516,7 +516,6 @@ void SILGenFunction::emitClassConstructorAllocator(ConstructorDecl *ctor) { // Call the initializer. SubstitutionMap subMap; - SmallVector subs; if (auto *genericEnv = ctor->getGenericEnvironmentOfContext()) { auto *genericSig = genericEnv->getGenericSignature(); subMap = genericSig->getSubstitutionMap( @@ -525,14 +524,13 @@ void SILGenFunction::emitClassConstructorAllocator(ConstructorDecl *ctor) { t->castTo()); }, MakeAbstractConformanceForGenericType()); - genericSig->getSubstitutions(subMap, subs); } std::tie(initVal, initTy) = emitSiblingMethodRef(Loc, selfValue, initConstant, subMap); SILValue initedSelfValue = emitApplyWithRethrow(Loc, initVal.forward(*this), - initTy, subs, args); + initTy, subMap, args); emitProfilerIncrement(ctor->getBody()); @@ -886,10 +884,10 @@ static SILValue getBehaviorInitStorageFn(SILGenFunction &SGF, auto initConstantTy = initFn->getLoweredType().castTo(); - auto param = SILParameterInfo(initTy.getSwiftRValueType(), + auto param = SILParameterInfo(initTy.getASTType(), initTy.isAddress() ? ParameterConvention::Indirect_In : ParameterConvention::Direct_Owned); - auto result = SILResultInfo(storageTy.getSwiftRValueType(), + auto result = SILResultInfo(storageTy.getASTType(), storageTy.isAddress() ? ResultConvention::Indirect : ResultConvention::Owned); @@ -953,7 +951,7 @@ void SILGenFunction::emitMemberInitializers(DeclContext *dc, // signature of the type, with replacement archetypes from the // constructor's context (which might be in an extension of // the type, which adds additional generic requirements). - SubstitutionList subs; + SubstitutionMap subs; auto *genericEnv = dc->getGenericEnvironmentOfContext(); auto typeGenericSig = nominal->getGenericSignatureOfContext(); @@ -961,7 +959,7 @@ void SILGenFunction::emitMemberInitializers(DeclContext *dc, // Generate a set of substitutions for the initialization function, // whose generic signature is that of the type context, and whose // replacement types are the archetypes of the initializer itself. - auto subMap = typeGenericSig->getSubstitutionMap( + subs = typeGenericSig->getSubstitutionMap( [&](SubstitutableType *type) { if (auto gp = type->getAs()) { return genericEnv->mapTypeIntoContext(gp); @@ -971,13 +969,9 @@ void SILGenFunction::emitMemberInitializers(DeclContext *dc, }, [](CanType dependentType, Type conformingReplacementType, - ProtocolType *conformedProtocol) { - return ProtocolConformanceRef( - conformedProtocol->getDecl()); + ProtocolDecl *conformedProtocol) { + return ProtocolConformanceRef(conformedProtocol); }); - SmallVector subsVec; - typeGenericSig->getSubstitutions(subMap, subsVec); - subs = SGM.getASTContext().AllocateCopy(subsVec); } // Get the type of the initialization result, in terms @@ -1021,8 +1015,9 @@ void SILGenFunction::emitMemberInitializers(DeclContext *dc, auto self = emitSelfForMemberInit(*this, var, selfDecl); auto mark = B.createMarkUninitializedBehavior(var, - initFn, init.getSubstitutions(), storageAddr.getValue(), - setterFn, getForwardingSubstitutions(), self.getValue(), + initFn, init.getSubstitutions(), + storageAddr.getValue(), + setterFn, getForwardingSubstitutionMap(), self.getValue(), getLoweredType(var->getType()).getAddressType()); // The mark instruction stands in for the behavior property. diff --git a/lib/SILGen/SILGenConvert.cpp b/lib/SILGen/SILGenConvert.cpp index 07c64988c5087..c02c37b4c85cc 100644 --- a/lib/SILGen/SILGenConvert.cpp +++ b/lib/SILGen/SILGenConvert.cpp @@ -126,7 +126,7 @@ getOptionalSomeValue(SILLocation loc, ManagedValue value, assert((optTL.isLoadable() || !silConv.useLoweredAddresses()) && "Address-only optionals cannot use this"); SILType optType = optTL.getLoweredType(); - CanType formalOptType = optType.getSwiftRValueType(); + auto formalOptType = optType.getASTType(); (void)formalOptType; assert(formalOptType.getOptionalObjectType()); @@ -435,7 +435,7 @@ SILGenFunction::emitOptionalToOptional(SILLocation loc, return scope.exitAndBranch(loc, some); } - RValue R(*this, loc, noOptResultTy.getSwiftRValueType(), result); + RValue R(*this, loc, noOptResultTy.getASTType(), result); ArgumentSource resultValueRV(loc, std::move(R)); emitInjectOptionalValueInto(loc, std::move(resultValueRV), finalResult.getValue(), resultTL); @@ -572,7 +572,7 @@ ManagedValue SILGenFunction::emitExistentialErasure( auto nsErrorVar = SGM.getNSErrorRequirement(loc); if (!nsErrorVar) return emitUndef(loc, existentialTL.getLoweredType()); - SubstitutionList nsErrorVarSubstitutions; + SubstitutionMap nsErrorVarSubstitutions; // Devirtualize. Maybe this should be done implicitly by // emitPropertyLValue? @@ -592,7 +592,8 @@ ManagedValue SILGenFunction::emitExistentialErasure( ManagedValue nsError = emitRValueForStorageLoad( loc, nativeError, concreteFormalType, - /*super*/ false, nsErrorVar, RValue(), nsErrorVarSubstitutions, + /*super*/ false, nsErrorVar, RValue(), + nsErrorVarSubstitutions, AccessSemantics::Ordinary, nsErrorType, SGFContext()) .getAsSingleValue(*this, loc); diff --git a/lib/SILGen/SILGenDecl.cpp b/lib/SILGen/SILGenDecl.cpp index 21c297ff01dfe..60451892f7752 100644 --- a/lib/SILGen/SILGenDecl.cpp +++ b/lib/SILGen/SILGenDecl.cpp @@ -144,8 +144,8 @@ namespace { }; } // end anonymous namespace -SubstitutionList SILGenFunction::getForwardingSubstitutions() { - return F.getForwardingSubstitutions(); +SubstitutionMap SILGenFunction::getForwardingSubstitutionMap() { + return F.getForwardingSubstitutionMap(); } void SILGenFunction::visitFuncDecl(FuncDecl *fd) { @@ -394,7 +394,7 @@ class LocalVariableInitialization : public SingleBufferInitialization { auto boxType = SGF.SGM.Types .getContextBoxTypeForCapture(decl, - SGF.getLoweredType(decl->getType()).getSwiftRValueType(), + SGF.getLoweredType(decl->getType()).getASTType(), SGF.F.getGenericEnvironment(), /*mutable*/ true); @@ -861,7 +861,7 @@ void EnumElementPatternInitialization::emitEnumMatch( // Reabstract to the substituted type, if needed. CanType substEltTy = value.getType() - .getSwiftRValueType() + .getASTType() ->getTypeOfMember(SGF.SGM.M.getSwiftModule(), eltDecl, eltDecl->getArgumentInterfaceType()) ->getCanonicalType(); @@ -1289,51 +1289,6 @@ CleanupHandle SILGenFunction::enterDestroyCleanup(SILValue valueOrAddr) { return Cleanups.getTopCleanup(); } -PostponedCleanup::PostponedCleanup(SILGenFunction &sgf, bool recursive) - : depth(sgf.Cleanups.innermostScope), SGF(sgf), - previouslyActiveCleanup(sgf.CurrentlyActivePostponedCleanup), - active(true), applyRecursively(recursive) { - SGF.CurrentlyActivePostponedCleanup = this; -} - -PostponedCleanup::PostponedCleanup(SILGenFunction &sgf) - : depth(sgf.Cleanups.innermostScope), SGF(sgf), - previouslyActiveCleanup(sgf.CurrentlyActivePostponedCleanup), - active(true), - applyRecursively(previouslyActiveCleanup - ? previouslyActiveCleanup->applyRecursively - : false) { - SGF.CurrentlyActivePostponedCleanup = this; -} - -PostponedCleanup::~PostponedCleanup() { - if (active) { - end(); - } -} - -void PostponedCleanup::end() { - if (previouslyActiveCleanup && applyRecursively && - previouslyActiveCleanup->applyRecursively) { - previouslyActiveCleanup->deferredCleanups.append(deferredCleanups.begin(), - deferredCleanups.end()); - } - - SGF.CurrentlyActivePostponedCleanup = previouslyActiveCleanup; - active = false; -} - -void PostponedCleanup::postponeCleanup(CleanupHandle cleanup, - SILValue forValue) { - deferredCleanups.push_back(std::make_pair(cleanup, forValue)); -} - -void SILGenFunction::enterPostponedCleanup(SILValue forValue) { - auto handle = enterDestroyCleanup(forValue); - if (CurrentlyActivePostponedCleanup) - CurrentlyActivePostponedCleanup->postponeCleanup(handle, forValue); -} - namespace { /// A cleanup that deinitializes an opaque existential container /// before a value has been stored into it, or after its value was taken. diff --git a/lib/SILGen/SILGenDestructor.cpp b/lib/SILGen/SILGenDestructor.cpp index b1d50ca52c6a1..3213facbba607 100644 --- a/lib/SILGen/SILGenDestructor.cpp +++ b/lib/SILGen/SILGenDestructor.cpp @@ -68,11 +68,8 @@ void SILGenFunction::emitDestroyingDestructor(DestructorDecl *dd) { std::tie(dtorValue, dtorTy) = emitSiblingMethodRef(cleanupLoc, baseSelf, dtorConstant, subMap); - SmallVector subs; - if (auto *genericSig = superclass->getGenericSignature()) - genericSig->getSubstitutions(subMap, subs); resultSelfValue = B.createApply(cleanupLoc, dtorValue.forward(*this), - dtorTy, objectPtrTy, subs, baseSelf); + dtorTy, objectPtrTy, subMap, baseSelf); } else { resultSelfValue = selfValue; } @@ -117,19 +114,15 @@ void SILGenFunction::emitDeallocatingDestructor(DestructorDecl *dd) { // Form a reference to the destroying destructor. SILDeclRef dtorConstant(dd, SILDeclRef::Kind::Destroyer); auto classTy = initialSelfValue->getType(); - auto classDecl = classTy.getSwiftRValueType()->getAnyNominal(); + auto classDecl = classTy.getASTType()->getAnyNominal(); ManagedValue dtorValue; SILType dtorTy; - auto subMap = classTy.getSwiftRValueType() + auto subMap = classTy.getASTType() ->getContextSubstitutionMap(SGM.M.getSwiftModule(), classDecl); std::tie(dtorValue, dtorTy) = emitSiblingMethodRef(loc, initialSelfValue, dtorConstant, subMap); - SmallVector subs; - if (auto *genericSig = classDecl->getGenericSignature()) - genericSig->getSubstitutions(subMap, subs); - // Call the destroying destructor. SILValue selfForDealloc; { @@ -137,7 +130,7 @@ void SILGenFunction::emitDeallocatingDestructor(DestructorDecl *dd) { ManagedValue borrowedSelf = emitManagedBeginBorrow(loc, initialSelfValue); SILType objectPtrTy = SILType::getNativeObjectType(F.getASTContext()); selfForDealloc = B.createApply(loc, dtorValue.forward(*this), - dtorTy, objectPtrTy, subs, + dtorTy, objectPtrTy, subMap, borrowedSelf.getUnmanagedValue()); } @@ -262,12 +255,8 @@ void SILGenFunction::emitObjCDestructor(SILDeclRef dtor) { ParameterConvention::Direct_Unowned && "Objective C deinitializing destructor takes self as unowned"); - SmallVector subs; - if (auto *genericSig = superclass->getGenericSignature()) - genericSig->getSubstitutions(subMap, subs); - B.createApply(cleanupLoc, superclassDtorValue, substDtorType, - dtorConv.getSILResultType(), subs, superSelf); + dtorConv.getSILResultType(), subMap, superSelf); // We know that the givne value came in at +1, but we pass the relevant value // as unowned to the destructor. Create a fake balance for the verifier to be diff --git a/lib/SILGen/SILGenDynamicCast.cpp b/lib/SILGen/SILGenDynamicCast.cpp index c2ea60a198f1e..2fe3cd3aab516 100644 --- a/lib/SILGen/SILGenDynamicCast.cpp +++ b/lib/SILGen/SILGenDynamicCast.cpp @@ -116,8 +116,8 @@ namespace { /// Emit a conditional cast. void emitConditional( ManagedValue operand, CastConsumptionKind consumption, SGFContext ctx, - const std::function &handleTrue, - const std::function)> &handleFalse, + llvm::function_ref handleTrue, + llvm::function_ref)> handleFalse, ProfileCounter TrueCount = ProfileCounter(), ProfileCounter FalseCount = ProfileCounter()) { // The cast instructions don't know how to work with anything @@ -286,8 +286,8 @@ namespace { void SILGenFunction::emitCheckedCastBranch( SILLocation loc, Expr *source, Type targetType, SGFContext ctx, - std::function handleTrue, - std::function)> handleFalse, + llvm::function_ref handleTrue, + llvm::function_ref)> handleFalse, ProfileCounter TrueCount, ProfileCounter FalseCount) { CheckedCastEmitter emitter(*this, loc, source->getType(), targetType); ManagedValue operand = emitter.emitOperand(source); @@ -298,8 +298,8 @@ void SILGenFunction::emitCheckedCastBranch( void SILGenFunction::emitCheckedCastBranch( SILLocation loc, ConsumableManagedValue src, Type sourceType, CanType targetType, SGFContext ctx, - std::function handleTrue, - std::function)> handleFalse, + llvm::function_ref handleTrue, + llvm::function_ref)> handleFalse, ProfileCounter TrueCount, ProfileCounter FalseCount) { CheckedCastEmitter emitter(*this, loc, sourceType, targetType); emitter.emitConditional(src.getFinalManagedValue(), src.getFinalConsumption(), @@ -397,8 +397,8 @@ namespace { /// Emit a conditional cast. void emitConditional(ManagedValue operand, CastConsumptionKind consumption, SGFContext ctx, - const std::function &handleTrue, - const std::function &handleFalse, + llvm::function_ref handleTrue, + llvm::function_ref handleFalse, ProfileCounter TrueCount = ProfileCounter(), ProfileCounter FalseCount = ProfileCounter()) { // The cast instructions don't know how to work with anything @@ -544,8 +544,8 @@ namespace { void SILGenFunction::emitCheckedCastBranchOld( SILLocation loc, Expr *source, Type targetType, SGFContext ctx, - std::function handleTrue, - std::function handleFalse, ProfileCounter TrueCount, + llvm::function_ref handleTrue, + llvm::function_ref handleFalse, ProfileCounter TrueCount, ProfileCounter FalseCount) { CheckedCastEmitterOld emitter(*this, loc, source->getType(), targetType); ManagedValue operand = emitter.emitOperand(source); @@ -556,8 +556,8 @@ void SILGenFunction::emitCheckedCastBranchOld( void SILGenFunction::emitCheckedCastBranchOld( SILLocation loc, ConsumableManagedValue src, Type sourceType, CanType targetType, SGFContext ctx, - std::function handleTrue, - std::function handleFalse, ProfileCounter TrueCount, + llvm::function_ref handleTrue, + llvm::function_ref handleFalse, ProfileCounter TrueCount, ProfileCounter FalseCount) { CheckedCastEmitterOld emitter(*this, loc, sourceType, targetType); emitter.emitConditional(src.getFinalManagedValue(), src.getFinalConsumption(), diff --git a/lib/SILGen/SILGenEpilog.cpp b/lib/SILGen/SILGenEpilog.cpp index fc06a3ea6ed4d..a65363bc78bf6 100644 --- a/lib/SILGen/SILGenEpilog.cpp +++ b/lib/SILGen/SILGenEpilog.cpp @@ -61,7 +61,7 @@ static SILValue buildReturnValue(SILGenFunction &SGF, SILLocation loc, SmallVector eltTypes; for (auto elt : directResults) - eltTypes.push_back(elt->getType().getSwiftRValueType()); + eltTypes.push_back(elt->getType().getASTType()); auto resultType = SILType::getPrimitiveObjectType( CanType(TupleType::get(eltTypes, SGF.getASTContext()))); return SGF.B.createTuple(loc, resultType, directResults); diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index fbb86e7e87fa0..b40c3f25f4f1b 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -368,7 +368,6 @@ void SILGenFunction::emitExprInto(Expr *E, Initialization *I, // Handle the special case of copying an lvalue. if (auto load = dyn_cast(E)) { FormalEvaluationScope writeback(*this); - PostponedCleanup postpone(*this); auto lv = emitLValue(load->getSubExpr(), AccessKind::Read); emitCopyLValueInto(E, std::move(lv), I); return; @@ -920,7 +919,6 @@ emitRValueForDecl(SILLocation loc, ConcreteDeclRef declRef, Type ncRefType, // Any writebacks for this access are tightly scoped. FormalEvaluationScope scope(*this); - PostponedCleanup postpone(*this); // If this is a decl that we have an lvalue for, produce and return it. ValueDecl *decl = declRef.getDecl(); @@ -1131,7 +1129,7 @@ static SILDeclRef getRValueAccessorDeclRef(SILGenFunction &SGF, static RValue emitRValueWithAccessor(SILGenFunction &SGF, SILLocation loc, AbstractStorageDecl *storage, - SubstitutionList substitutions, + SubstitutionMap substitutions, ArgumentSource &&baseRV, RValue &&subscriptRV, bool isSuper, AccessStrategy strategy, SILDeclRef accessor, @@ -1206,7 +1204,7 @@ emitRValueWithAccessor(SILGenFunction &SGF, SILLocation loc, RValue SILGenFunction::emitRValueForStorageLoad( SILLocation loc, ManagedValue base, CanType baseFormalType, bool isSuper, AbstractStorageDecl *storage, RValue indexes, - SubstitutionList substitutions, + SubstitutionMap substitutions, AccessSemantics semantics, Type propTy, SGFContext C, bool isBaseGuaranteed) { AccessStrategy strategy = @@ -1254,7 +1252,7 @@ RValue SILGenFunction::emitRValueForStorageLoad( // rvalue MemberRefExprs are produced in two cases: when accessing a 'let' // decl member, and when the base is a (non-lvalue) struct. assert(baseFormalType->getAnyNominal() && - base.getType().getSwiftRValueType()->getAnyNominal() && + base.getType().getASTType()->getAnyNominal() && "The base of an rvalue MemberRefExpr should be an rvalue value"); // If the accessed field is stored, emit a StructExtract on the base. @@ -1400,7 +1398,6 @@ RValue RValueEmitter::visitStringLiteralExpr(StringLiteralExpr *E, RValue RValueEmitter::visitLoadExpr(LoadExpr *E, SGFContext C) { // Any writebacks here are tightly scoped. FormalEvaluationScope writeback(SGF); - PostponedCleanup postpone(SGF); LValue lv = SGF.emitLValue(E->getSubExpr(), AccessKind::Read); // We can't load at immediate +0 from the lvalue without deeper analysis, // since the access will be immediately ended and might invalidate the value @@ -1763,7 +1760,8 @@ RValueEmitter::visitConditionalBridgeFromObjCExpr( auto conversion = cast(conversionRef.getDecl()); auto subs = conversionRef.getSubstitutions(); - auto nativeType = subs[0].getReplacement(); + auto nativeType = + Type(GenericTypeParamType::get(0, 0, SGF.getASTContext())).subst(subs); auto metatypeType = SGF.getLoweredType(MetatypeType::get(nativeType)); auto metatype = @@ -2091,9 +2089,7 @@ RValue RValueEmitter::visitFunctionConversionExpr(FunctionConversionExpr *e, result = convertFunctionRepresentation(SGF, e, result, srcRepTy, srcTy); if (srcTy != destTy) { - bool postponeToNoEscapeCleanup = !isa(e->getSubExpr()); - result = SGF.emitTransformedValue(e, result, srcTy, destTy, SGFContext(), - postponeToNoEscapeCleanup); + result = SGF.emitTransformedValue(e, result, srcTy, destTy, SGFContext()); } if (destTy != destRepTy) @@ -2577,7 +2573,6 @@ RValue RValueEmitter::visitMemberRefExpr(MemberRefExpr *E, SGFContext C) { // Any writebacks for this access are tightly scoped. FormalEvaluationScope scope(SGF); - PostponedCleanup postpone(SGF); LValue lv = SGF.emitLValue(E, AccessKind::Read); // We can't load at +0 without further analysis, since the formal access into @@ -2599,7 +2594,6 @@ visitDotSyntaxBaseIgnoredExpr(DotSyntaxBaseIgnoredExpr *E, SGFContext C) { RValue RValueEmitter::visitSubscriptExpr(SubscriptExpr *E, SGFContext C) { // Any writebacks for this access are tightly scoped. FormalEvaluationScope scope(SGF); - PostponedCleanup postpone(SGF); LValue lv = SGF.emitLValue(E, AccessKind::Read); // We can't load at +0 without further analysis, since the formal access into @@ -2629,11 +2623,11 @@ RValue RValueEmitter::visitTupleElementExpr(TupleElementExpr *E, RValue SILGenFunction::emitApplyOfDefaultArgGenerator(SILLocation loc, - ConcreteDeclRef defaultArgsOwner, - unsigned destIndex, - CanType resultType, + ConcreteDeclRef defaultArgsOwner, + unsigned destIndex, + CanType resultType, AbstractionPattern origResultType, - SGFContext C) { + SGFContext C) { SILDeclRef generator = SILDeclRef::getDefaultArgGenerator(defaultArgsOwner.getDecl(), destIndex); @@ -2644,7 +2638,7 @@ SILGenFunction::emitApplyOfDefaultArgGenerator(SILLocation loc, auto fnRef = ManagedValue::forUnmanaged(emitGlobalFunctionRef(loc,generator)); auto fnType = fnRef.getType().castTo(); - SubstitutionList subs; + SubstitutionMap subs; if (fnType->isPolymorphic()) subs = defaultArgsOwner.getSubstitutions(); @@ -2654,15 +2648,14 @@ SILGenFunction::emitApplyOfDefaultArgGenerator(SILLocation loc, ResultPlanPtr resultPtr = ResultPlanBuilder::computeResultPlan(*this, calleeTypeInfo, loc, C); ArgumentScope argScope(*this, loc); - PostponedCleanup postpone(*this); - return emitApply(std::move(resultPtr), std::move(argScope), loc, fnRef, subs, - {}, calleeTypeInfo, ApplyOptions::None, C, postpone); + return emitApply(std::move(resultPtr), std::move(argScope), loc, fnRef, + subs, {}, calleeTypeInfo, ApplyOptions::None, C); } RValue SILGenFunction::emitApplyOfStoredPropertyInitializer( SILLocation loc, const PatternBindingEntry &entry, - SubstitutionList subs, + SubstitutionMap subs, CanType resultType, AbstractionPattern origResultType, SGFContext C) { @@ -2678,9 +2671,8 @@ RValue SILGenFunction::emitApplyOfStoredPropertyInitializer( ResultPlanPtr resultPlan = ResultPlanBuilder::computeResultPlan(*this, calleeTypeInfo, loc, C); ArgumentScope argScope(*this, loc); - PostponedCleanup postpone(*this); - return emitApply(std::move(resultPlan), std::move(argScope), loc, fnRef, subs, - {}, calleeTypeInfo, ApplyOptions::None, C, postpone); + return emitApply(std::move(resultPlan), std::move(argScope), loc, fnRef, + subs, {}, calleeTypeInfo, ApplyOptions::None, C); } static void emitTupleShuffleExprInto(RValueEmitter &emitter, @@ -2929,9 +2921,9 @@ RValue RValueEmitter::visitAbstractClosureExpr(AbstractClosureExpr *e, // Emit the closure body. SGF.SGM.emitClosure(e); - SubstitutionList subs; + SubstitutionMap subs; if (e->getCaptureInfo().hasGenericParamCaptures()) - subs = SGF.getForwardingSubstitutions(); + subs = SGF.getForwardingSubstitutionMap(); // Generate the closure value (if any) for the closure expr's function // reference. @@ -3005,8 +2997,7 @@ emitKeyPathRValueBase(SILGenFunction &subSGF, SILLocation loc, SILValue paramArg, CanType &baseType, - SubstitutionList &subs, - SmallVectorImpl &subsBuf) { + SubstitutionMap subs) { // If the storage is at global scope, then the base value () is a formality. // There no real argument to pass to the underlying accessors. if (!storage->getDeclContext()->isTypeContext()) @@ -3032,7 +3023,7 @@ emitKeyPathRValueBase(SILGenFunction &subSGF, if (storage->getDeclContext()->getAsClassOrClassExtensionContext()) { opened = ArchetypeType::getOpened(baseType); } else { - opened = subs[0].getReplacement()->castTo(); + opened = subs.getReplacementTypes()[0]->castTo(); } assert(opened->isOpenedExistential()); @@ -3110,7 +3101,7 @@ static RValue loadIndexValuesForKeyPathComponent(SILGenFunction &SGF, static SILFunction *getOrCreateKeyPathGetter(SILGenModule &SGM, SILLocation loc, AbstractStorageDecl *property, - SubstitutionList subs, + SubstitutionMap subs, AccessStrategy strategy, GenericEnvironment *genericEnv, ArrayRef indexes, @@ -3137,7 +3128,7 @@ static SILFunction *getOrCreateKeyPathGetter(SILGenModule &SGM, paramConvention = ParameterConvention::Indirect_In; SmallVector params; - params.push_back({loweredBaseTy.getSwiftRValueType(), + params.push_back({loweredBaseTy.getASTType(), paramConvention}); auto &C = SGM.getASTContext(); if (!indexes.empty()) @@ -3145,7 +3136,7 @@ static SILFunction *getOrCreateKeyPathGetter(SILGenModule &SGM, ->getCanonicalType(), ParameterConvention::Direct_Unowned}); - SILResultInfo result(loweredPropTy.getSwiftRValueType(), + SILResultInfo result(loweredPropTy.getASTType(), ResultConvention::Indirect); auto signature = SILFunctionType::get(genericSig, @@ -3158,10 +3149,9 @@ static SILFunction *getOrCreateKeyPathGetter(SILGenModule &SGM, // Find the function and see if we already created it. SmallVector interfaceSubs; - for (auto &sub : subs) { + for (auto replacement : subs.getReplacementTypes()) { interfaceSubs.push_back( - sub.getReplacement()->mapTypeOutOfContext() - ->getCanonicalType()); + replacement->mapTypeOutOfContext()->getCanonicalType()); } auto name = Mangle::ASTMangler() .mangleKeyPathGetterThunkHelper(property, genericSig, baseType, @@ -3199,11 +3189,9 @@ static SILFunction *getOrCreateKeyPathGetter(SILGenModule &SGM, Scope scope(subSGF, loc); - SmallVector subsBuf; - auto baseSubstValue = emitKeyPathRValueBase(subSGF, property, loc, baseArg, - baseType, subs, subsBuf); + baseType, subs); RValue indexValue = loadIndexValuesForKeyPathComponent(subSGF, loc, indexes, @@ -3231,7 +3219,7 @@ static SILFunction *getOrCreateKeyPathGetter(SILGenModule &SGM, SILFunction *getOrCreateKeyPathSetter(SILGenModule &SGM, SILLocation loc, AbstractStorageDecl *property, - SubstitutionList subs, + SubstitutionMap subs, AccessStrategy strategy, GenericEnvironment *genericEnv, ArrayRef indexes, @@ -3261,10 +3249,10 @@ SILFunction *getOrCreateKeyPathSetter(SILGenModule &SGM, SmallVector params; // property value - params.push_back({loweredPropTy.getSwiftRValueType(), + params.push_back({loweredPropTy.getASTType(), paramConvention}); // base - params.push_back({loweredBaseTy.getSwiftRValueType(), + params.push_back({loweredBaseTy.getASTType(), property->isSetterMutating() ? ParameterConvention::Indirect_Inout : paramConvention}); @@ -3286,10 +3274,9 @@ SILFunction *getOrCreateKeyPathSetter(SILGenModule &SGM, SmallString<64> nameBuf; SmallVector interfaceSubs; - for (auto &sub : subs) { + for (Type replacement : subs.getReplacementTypes()) { interfaceSubs.push_back( - sub.getReplacement()->mapTypeOutOfContext() - ->getCanonicalType()); + replacement->mapTypeOutOfContext()->getCanonicalType()); } auto name = Mangle::ASTMangler().mangleKeyPathSetterThunkHelper(property, genericSig, @@ -3344,12 +3331,11 @@ SILFunction *getOrCreateKeyPathSetter(SILGenModule &SGM, propertyType); LValue lv; - SmallVector subsBuf; if (!property->isSetterMutating()) { auto baseSubst = emitKeyPathRValueBase(subSGF, property, loc, baseArg, - baseType, subs, subsBuf); + baseType, subs); lv = LValue::forValue(baseSubst, baseType); } else { @@ -3360,7 +3346,7 @@ SILFunction *getOrCreateKeyPathSetter(SILGenModule &SGM, // Open an existential lvalue, if necessary. if (baseType->isAnyExistentialType()) { - auto opened = subs[0].getReplacement()->castTo(); + auto opened = subs.getReplacementTypes()[0]->castTo(); assert(opened->isOpenedExistential()); baseType = opened->getCanonicalType(); lv = subSGF.emitOpenExistentialLValue(loc, std::move(lv), @@ -3561,17 +3547,12 @@ getOrCreateKeyPathEqualsAndHash(SILGenModule &SGM, auto equalsResultPlan = ResultPlanBuilder::computeResultPlan(subSGF, equalsInfo, loc, SGFContext()); ArgumentScope argScope(subSGF, loc); - PostponedCleanup postpone(subSGF); - SmallVector equatableSubListBuf; - equatableProtocol->getGenericSignature() - ->getSubstitutions(equatableSub, equatableSubListBuf); isEqual = subSGF .emitApply(std::move(equalsResultPlan), std::move(argScope), loc, ManagedValue::forUnmanaged(equalsWitness), - C.AllocateCopy(equatableSubListBuf), + equatableSub, {lhsArg, rhsArg, metatyValue}, - equalsInfo, ApplyOptions::None, SGFContext(), - postpone) + equalsInfo, ApplyOptions::None, SGFContext()) .getUnmanagedSingleValue(subSGF, loc); } @@ -3651,7 +3632,7 @@ getOrCreateKeyPathEqualsAndHash(SILGenModule &SGM, SILValue hashCode; - // TODO: Combine hashes of the indexes using an inout _Hasher + // TODO: Combine hashes of the indexes using an inout Hasher { auto &index = indexes[0]; @@ -3664,23 +3645,31 @@ getOrCreateKeyPathEqualsAndHash(SILGenModule &SGM, auto formalTy = index.FormalType; auto hashable = index.Hashable; + SubstitutionMap hashableSubsMap; if (genericEnv) { formalTy = genericEnv->mapTypeIntoContext(formalTy)->getCanonicalType(); hashable = hashable.subst(index.FormalType, [&](Type t) -> Type { return genericEnv->mapTypeIntoContext(t); }, LookUpConformanceInSignature(*genericSig)); } - - // Get the Equatable conformance from the Hashable conformance - auto hashableSub = Substitution(formalTy, - C.AllocateCopy(ArrayRef(hashable))); + + if (auto genericSig = + hashTy.castTo()->getGenericSignature()) { + hashableSubsMap = + genericSig->getSubstitutionMap( + [&](SubstitutableType *type) -> Type { return formalTy; }, + [&](CanType dependentType, Type replacementType, + ProtocolDecl *proto)->Optional { + return hashable; + }); + } auto hashWitness = subSGF.B.createWitnessMethod(loc, formalTy, hashable, hashRef, hashTy); - + auto hashSubstTy = hashTy.castTo() - ->substGenericArgs(SGM.M, hashableSub); + ->substGenericArgs(SGM.M, hashableSubsMap); auto hashInfo = CalleeTypeInfo(hashSubstTy, AbstractionPattern(intTy), intTy, None, @@ -3700,13 +3689,12 @@ getOrCreateKeyPathEqualsAndHash(SILGenModule &SGM, auto hashResultPlan = ResultPlanBuilder::computeResultPlan(subSGF, hashInfo, loc, SGFContext()); ArgumentScope argScope(subSGF, loc); - PostponedCleanup postpone(subSGF); hashCode = subSGF .emitApply(std::move(hashResultPlan), std::move(argScope), loc, - ManagedValue::forUnmanaged(hashWitness), hashableSub, - {arg}, hashInfo, ApplyOptions::None, SGFContext(), - postpone) + ManagedValue::forUnmanaged(hashWitness), + hashableSubsMap, + {arg}, hashInfo, ApplyOptions::None, SGFContext()) .getUnmanagedSingleValue(subSGF, loc); } } @@ -3751,7 +3739,7 @@ lowerKeyPathSubscriptIndexTypes( SILGenModule &SGM, SmallVectorImpl &indexPatterns, SubscriptDecl *subscript, - SubstitutionList subscriptSubs, + SubstitutionMap subscriptSubs, bool &needsGenericContext) { // Capturing an index value dependent on the generic context means we // need the generic context captured in the key path. @@ -3759,21 +3747,20 @@ lowerKeyPathSubscriptIndexTypes( SubstitutionMap subMap; auto sig = subscript->getGenericSignature(); if (sig) { - subMap = sig->getSubstitutionMap(subscriptSubs); - subscriptSubstTy = subscriptSubstTy.subst(subMap); + subscriptSubstTy = subscriptSubstTy.subst(subscriptSubs); } needsGenericContext |= subscriptSubstTy->hasArchetype(); for (auto *index : *subscript->getIndices()) { auto indexTy = index->getInterfaceType(); if (sig) { - indexTy = indexTy.subst(subMap); + indexTy = indexTy.subst(subscriptSubs); } auto indexLoweredTy = SGM.Types.getLoweredType( AbstractionPattern::getOpaque(), indexTy); indexLoweredTy = SILType::getPrimitiveType( - indexLoweredTy.getSwiftRValueType()->mapTypeOutOfContext() + indexLoweredTy.getASTType()->mapTypeOutOfContext() ->getCanonicalType(), indexLoweredTy.getCategory()); indexPatterns.push_back({indexTy->mapTypeOutOfContext() @@ -3806,7 +3793,7 @@ SILGenModule::emitKeyPathComponentForDecl(SILLocation loc, GenericEnvironment *genericEnv, unsigned &baseOperand, bool &needsGenericContext, - SubstitutionList subs, + SubstitutionMap subs, AbstractStorageDecl *storage, ArrayRef indexHashables, CanType baseTy, @@ -3833,8 +3820,7 @@ SILGenModule::emitKeyPathComponentForDecl(SILLocation loc, auto componentObjTy = componentTy->getWithoutSpecifierType(); if (genericEnv) componentObjTy = genericEnv->mapTypeIntoContext(componentObjTy); - auto storageTy = Types.getSubstitutedStorageType(var, - componentObjTy); + auto storageTy = Types.getSubstitutedStorageType(var, componentObjTy); auto opaqueTy = Types .getLoweredType(AbstractionPattern::getOpaque(), componentObjTy); @@ -3996,26 +3982,17 @@ RValue RValueEmitter::visitKeyPathExpr(KeyPathExpr *E, SGFContext C) { auto makeExternalKeyPathComponent = [&](const KeyPathExpr::Component &component) -> KeyPathPatternComponent { SmallVector indices; - SubstitutionList subs = component.getDeclRef().getSubstitutions(); + SubstitutionMap subMap = component.getDeclRef().getSubstitutions(); auto decl = cast(component.getDeclRef().getDecl()); auto ty = decl->getStorageInterfaceType(); // Map the substitutions out of context. - if (!subs.empty()) { - auto sig = decl->getInnermostDeclContext() - ->getGenericSignatureOfContext(); - auto subMap = sig->getSubstitutionMap(subs); + if (!subMap.empty()) { // If any of the substitutions involve local archetypes, then the // key path pattern needs to capture the generic context, and we need // to map the pattern substitutions out of this context. - if (std::any_of(subs.begin(), subs.end(), - [](const Substitution &s) -> bool { - return s.getReplacement()->hasArchetype(); - })) { + if (subMap.hasArchetypes()) { needsGenericContext = true; subMap = subMap.mapReplacementTypesOutOfContext(); - SmallVector subsBuf; - sig->getSubstitutions(subMap, subsBuf); - subs = SGF.getASTContext().AllocateCopy(subsBuf); } ty = ty.subst(subMap); } @@ -4025,9 +4002,10 @@ RValue RValueEmitter::visitKeyPathExpr(KeyPathExpr *E, SGFContext C) { unsigned numOperands = operands.size(); SmallVector indexTypes; auto sub = cast(component.getDeclRef().getDecl()); - lowerKeyPathSubscriptIndexTypes(SGF.SGM, indexTypes, - sub, component.getDeclRef().getSubstitutions(), - needsGenericContext); + lowerKeyPathSubscriptIndexTypes( + SGF.SGM, indexTypes, sub, + component.getDeclRef().getSubstitutions(), + needsGenericContext); lowerKeyPathSubscriptIndexPatterns(indices, indexTypes, component.getSubscriptIndexHashableConformances(), numOperands); @@ -4042,7 +4020,7 @@ RValue RValueEmitter::visitKeyPathExpr(KeyPathExpr *E, SGFContext C) { indexEquals, indexHash); } return KeyPathPatternComponent::forExternal( - decl, subs, SGF.getASTContext().AllocateCopy(indices), + decl, subMap, SGF.getASTContext().AllocateCopy(indices), indexEquals, indexHash, ty->getCanonicalType()); }; @@ -4124,8 +4102,8 @@ RValue RValueEmitter::visitKeyPathExpr(KeyPathExpr *E, SGFContext C) { objcString); auto keyPath = SGF.B.createKeyPath(SILLocation(E), pattern, needsGenericContext - ? SGF.F.getForwardingSubstitutions() - : SubstitutionList(), + ? SGF.F.getForwardingSubstitutionMap() + : SubstitutionMap(), operands, loweredTy); auto value = SGF.emitManagedRValueWithCleanup(keyPath); @@ -4142,25 +4120,25 @@ visitKeyPathApplicationExpr(KeyPathApplicationExpr *E, SGFContext C) { auto keyPathDecl = E->getKeyPath()->getType()->getAnyNominal(); FuncDecl *projectFn; - SmallVector subs; - + + SmallVector replacementTypes; if (keyPathDecl == SGF.getASTContext().getAnyKeyPathDecl()) { // Invoke projectKeyPathAny with the type of the base value. // The result is always `Any?`. projectFn = SGF.getASTContext().getProjectKeyPathAny(nullptr); - subs.push_back(Substitution(E->getBase()->getType(), {})); + replacementTypes.push_back(E->getBase()->getType()); } else { auto keyPathTy = E->getKeyPath()->getType()->castTo(); if (keyPathDecl == SGF.getASTContext().getPartialKeyPathDecl()) { // Invoke projectKeyPathPartial with the type of the base value. // The result is always `Any`. projectFn = SGF.getASTContext().getProjectKeyPathPartial(nullptr); - subs.push_back(Substitution(keyPathTy->getGenericArgs()[0], {})); + replacementTypes.push_back(keyPathTy->getGenericArgs()[0]); } else { projectFn = SGF.getASTContext().getProjectKeyPathReadOnly(nullptr); // Get the root and leaf type from the key path type. - subs.push_back(Substitution(keyPathTy->getGenericArgs()[0], {})); - subs.push_back(Substitution(keyPathTy->getGenericArgs()[1], {})); + replacementTypes.push_back(keyPathTy->getGenericArgs()[0]); + replacementTypes.push_back(keyPathTy->getGenericArgs()[1]); // Upcast the keypath to KeyPath if it isn't already. if (keyPathTy->getDecl() != SGF.getASTContext().getKeyPathDecl()) { @@ -4175,8 +4153,15 @@ visitKeyPathApplicationExpr(KeyPathApplicationExpr *E, SGFContext C) { } } - auto genericArgsMap = - projectFn->getGenericSignature()->getSubstitutionMap(subs); + auto projectionGenericSig = projectFn->getGenericSignature(); + auto genericArgsMap = projectionGenericSig->getSubstitutionMap( + [&](SubstitutableType *type) -> Type { + auto genericParam = cast(type); + auto index = + projectionGenericSig->getGenericParamOrdinal(genericParam); + return replacementTypes[index]; + }, + LookUpConformanceInSignature(*projectionGenericSig)); return SGF.emitApplyOfLibraryIntrinsic(SILLocation(E), projectFn, genericArgsMap, {root, keyPath}, C); @@ -4254,7 +4239,7 @@ static ManagedValue flattenOptional(SILGenFunction &SGF, SILLocation loc, SILType resultTy = optVal.getType().getOptionalObjectType(); auto &resultTL = SGF.getTypeLowering(resultTy); - assert(resultTy.getSwiftRValueType().getOptionalObjectType() && + assert(resultTy.getASTType().getOptionalObjectType() && "input was not a nested optional value"); SILValue contBBArg; @@ -4313,7 +4298,6 @@ computeNewSelfForRebindSelfInConstructorExpr(SILGenFunction &SGF, // Get newSelf, forward the cleanup for newSelf and clean everything else // up. FormalEvaluationScope Scope(SGF); - PostponedCleanup postpone(SGF); ManagedValue newSelfWithCleanup = SGF.emitRValueAsSingleValue(E->getSubExpr()); @@ -4583,30 +4567,12 @@ static bool mayLieAboutNonOptionalReturn(SILModule &M, Expr *expr) { return mayLieAboutNonOptionalReturn(M, load->getSubExpr()); } - // A reference to a member property. - if (auto member = dyn_cast(expr)) { + // A reference to a potentially dynamic member/subscript property. + if (auto member = dyn_cast(expr)) { return isVerbatimNullableTypeInC(M, member->getType()) && mayLieAboutNonOptionalReturn(M, member->getMember().getDecl()); } - // A reference to a subscript. - if (auto subscript = dyn_cast(expr)) { - return isVerbatimNullableTypeInC(M, subscript->getType()) && - mayLieAboutNonOptionalReturn(M, subscript->getDecl().getDecl()); - } - - // A reference to a member property found via dynamic lookup. - if (auto member = dyn_cast(expr)) { - return isVerbatimNullableTypeInC(M, member->getType()) && - mayLieAboutNonOptionalReturn(M, member->getMember().getDecl()); - } - - // A reference to a subscript found via dynamic lookup. - if (auto subscript = dyn_cast(expr)) { - return isVerbatimNullableTypeInC(M, subscript->getType()) && - mayLieAboutNonOptionalReturn(M, subscript->getMember().getDecl()); - } - return false; } @@ -4867,7 +4833,6 @@ static void emitSimpleAssignment(SILGenFunction &SGF, SILLocation loc, if (dest->getType()->isEqual(srcLoad->getSubExpr()->getType())) { assert(!dest->getType()->is()); FormalEvaluationScope writeback(SGF); - PostponedCleanup postpone(SGF); auto destLV = SGF.emitLValue(dest, AccessKind::Write); auto srcLV = SGF.emitLValue(srcLoad->getSubExpr(), AccessKind::Read); SGF.emitAssignLValueToLValue(loc, std::move(srcLV), std::move(destLV)); @@ -4889,14 +4854,12 @@ static void emitSimpleAssignment(SILGenFunction &SGF, SILLocation loc, } FormalEvaluationScope writeback(SGF); - PostponedCleanup postpone(SGF); LValue destLV = SGF.emitLValue(dest, AccessKind::Write); SGF.emitAssignToLValue(loc, src, std::move(destLV)); return; } FormalEvaluationScope writeback(SGF); - PostponedCleanup postpone(SGF); // Produce a flattened queue of LValues. SmallVector, 4> destLVs; @@ -5003,7 +4966,7 @@ RValue RValueEmitter::visitBindOptionalExpr(BindOptionalExpr *E, SGFContext C) { // Emit the operand into the temporary. SGF.emitExprInto(E->getSubExpr(), temp.get()); - // And then greab the managed address. + // And then grab the managed address. optValue = temp->getManagedAddress(); } @@ -5408,34 +5371,15 @@ RValue RValueEmitter::emitForceValue(ForceValueExpr *loc, Expr *E, void SILGenFunction::emitOpenExistentialExprImpl( OpenExistentialExpr *E, llvm::function_ref emitSubExpr) { + Optional writebackScope; + // Emit the existential value. if (E->getExistentialValue()->getType()->is()) { - // Open the existential container right away. We need the dynamic type - // to be opened in order to evaluate the subexpression. - AccessKind accessKind; - if (E->hasLValueAccessKind()) - accessKind = E->getLValueAccessKind(); - else - accessKind = E->getExistentialValue()->getLValueAccessKind(); - auto lv = emitLValue(E->getExistentialValue(), accessKind); - auto formalOpenedType = E->getOpaqueValue()->getType() - ->getWithoutSpecifierType() - ->getCanonicalType(); - lv = emitOpenExistentialLValue(E, std::move(lv), - CanArchetypeType(E->getOpenedArchetype()), - formalOpenedType, - accessKind); - - auto addr = emitAddressOfLValue(E, std::move(lv), accessKind); - bool inserted = OpaqueLValues.insert({E->getOpaqueValue(), - {addr.getValue(), formalOpenedType}}) - .second; + bool inserted = OpaqueValueExprs.insert({E->getOpaqueValue(), E}).second; (void)inserted; assert(inserted && "already have this opened existential?"); emitSubExpr(E->getSubExpr()); - assert(OpaqueLValues.count(E->getOpaqueValue()) == 0 - && "opened existential not removed?"); return; } @@ -5462,12 +5406,6 @@ RValue RValueEmitter::visitOpenExistentialExpr(OpenExistentialExpr *E, return RValue(SGF, E, *result); } - Optional scope; - // Begin an evaluation scope for an lvalue existential opened into an - // rvalue expression. - if (E->getExistentialValue()->getType()->is()) - scope.emplace(SGF); - return SGF.emitOpenExistentialExpr(E, [&](Expr *subExpr) -> RValue { return visit(subExpr, C); @@ -5516,8 +5454,9 @@ RValue RValueEmitter::visitMakeTemporarilyEscapableExpr( // Now create the verification of the withoutActuallyEscaping operand. // Either we fail the uniquenes check (which means the closure has escaped) // and abort or we continue and destroy the ultimate reference. - auto isEscaping = - SGF.B.createIsEscapingClosure(loc, borrowedClosure.getValue()); + auto isEscaping = SGF.B.createIsEscapingClosure( + loc, borrowedClosure.getValue(), + IsEscapingClosureInst::WithoutActuallyEscaping); SGF.B.createCondFail(loc, isEscaping); return rvalue; } @@ -5582,7 +5521,7 @@ class AutoreleasingWritebackComponent : public LogicalPathComponent { ManagedValue loadedBase = SGF.B.createLoadBorrow(loc, base); // Convert it to unowned. - auto refType = loadedBase.getType().getSwiftRValueType(); + auto refType = loadedBase.getType().getASTType(); auto unownedType = SILType::getPrimitiveObjectType( CanUnmanagedStorageType::get(refType)); SILValue unowned = SGF.B.createRefToUnmanaged( @@ -5644,8 +5583,7 @@ ManagedValue SILGenFunction::emitLValueToPointer(SILLocation loc, LValue &&lv, // Unsafe*Pointer. Reabstract it if necessary. auto opaqueTy = AbstractionPattern::getOpaque(); auto loweredTy = getLoweredType(opaqueTy, lv.getSubstFormalType()); - if (lv.getTypeOfRValue().getSwiftRValueType() - != loweredTy.getSwiftRValueType()) { + if (lv.getTypeOfRValue().getASTType() != loweredTy.getASTType()) { lv.addSubstToOrigComponent(opaqueTy, loweredTy); } switch (pointerInfo.PointerKind) { @@ -5660,7 +5598,7 @@ ManagedValue SILGenFunction::emitLValueToPointer(SILLocation loc, LValue &&lv, // Set up a writeback through a +0 buffer. LValueTypeData typeData = lv.getTypeData(); SILType rvalueType = SILType::getPrimitiveObjectType( - CanUnmanagedStorageType::get(typeData.TypeOfRValue.getSwiftRValueType())); + CanUnmanagedStorageType::get(typeData.TypeOfRValue.getASTType())); LValueTypeData unownedTypeData( AbstractionPattern( @@ -5700,7 +5638,6 @@ ManagedValue SILGenFunction::emitLValueToPointer(SILLocation loc, LValue &&lv, RValue RValueEmitter::visitArrayToPointerExpr(ArrayToPointerExpr *E, SGFContext C) { FormalEvaluationScope writeback(SGF); - PostponedCleanup postpone(SGF); auto subExpr = E->getSubExpr(); auto accessInfo = SGF.getArrayAccessInfo(E->getType(), @@ -5885,7 +5822,6 @@ void SILGenFunction::emitIgnoredExpr(Expr *E) { if (E->getType()->hasLValueType()) { // Emit the l-value, but don't perform an access. FormalEvaluationScope scope(*this); - PostponedCleanup postpone(*this); emitLValue(E, AccessKind::Read); return; } @@ -5894,7 +5830,6 @@ void SILGenFunction::emitIgnoredExpr(Expr *E) { // (which could materialize a potentially expensive value with cleanups). if (auto *LE = dyn_cast(E)) { FormalEvaluationScope scope(*this); - PostponedCleanup postpone(*this); LValue lv = emitLValue(LE->getSubExpr(), AccessKind::Read); // If loading from the lvalue is guaranteed to have no side effects, we diff --git a/lib/SILGen/SILGenForeignError.cpp b/lib/SILGen/SILGenForeignError.cpp index 9e5ce6b4368da..65b332d1fe899 100644 --- a/lib/SILGen/SILGenForeignError.cpp +++ b/lib/SILGen/SILGenForeignError.cpp @@ -74,8 +74,7 @@ static void emitStoreToForeignErrorSlot(SILGenFunction &SGF, // Okay, break down the components of SomePointer. // TODO: this should really be an unlowered AST type? - CanType bridgedErrorPtrType = - foreignErrorSlot->getType().getSwiftRValueType(); + auto bridgedErrorPtrType = foreignErrorSlot->getType().getASTType(); PointerTypeKind ptrKind; CanType bridgedErrorProto = @@ -132,7 +131,7 @@ namespace { SILValue emitBridged(SILGenFunction &SGF, SILLocation loc, CanType bridgedErrorProto) const override { - auto nativeErrorType = NativeError->getType().getSwiftRValueType(); + auto nativeErrorType = NativeError->getType().getASTType(); assert(nativeErrorType == SGF.SGM.getASTContext().getExceptionType()); SILValue bridgedError = SGF.emitNativeToBridgedError(loc, @@ -314,7 +313,7 @@ emitResultIsZeroErrorCheck(SILGenFunction &SGF, SILLocation loc, SILValue resultValue = emitUnwrapIntegerResult(SGF, loc, result.getUnmanagedValue()); - CanType resultType = resultValue->getType().getSwiftRValueType(); + auto resultType = resultValue->getType().getASTType(); if (!resultType->isBuiltinIntegerType(1)) { SILValue zero = @@ -394,7 +393,7 @@ emitErrorIsNonNilErrorCheck(SILGenFunction &SGF, SILLocation loc, // Switch on the optional error. SILBasicBlock *errorBB = SGF.createBasicBlock(FunctionSection::Postmatter); - errorBB->createPHIArgument(optionalError->getType().unwrapAnyOptionalType(), + errorBB->createPHIArgument(optionalError->getType().unwrapOptionalType(), ValueOwnershipKind::Owned); SILBasicBlock *contBB = SGF.createBasicBlock(); SGF.B.createSwitchEnum(loc, optionalError, /*default*/ nullptr, diff --git a/lib/SILGen/SILGenFunction.cpp b/lib/SILGen/SILGenFunction.cpp index 0eae4b4de0631..35902627b5117 100644 --- a/lib/SILGen/SILGenFunction.cpp +++ b/lib/SILGen/SILGenFunction.cpp @@ -20,6 +20,7 @@ #include "Scope.h" #include "swift/AST/Initializer.h" #include "swift/SIL/SILArgument.h" +#include "swift/SIL/SILProfiler.h" #include "swift/SIL/SILUndef.h" using namespace swift; @@ -129,7 +130,7 @@ std::tuple SILGenFunction::emitSiblingMethodRef(SILLocation loc, SILValue selfValue, SILDeclRef methodConstant, - const SubstitutionMap &subMap) { + SubstitutionMap subMap) { SILValue methodValue; // If the method is dynamic, access it through runtime-hookable virtual @@ -272,7 +273,7 @@ void SILGenFunction::emitCaptures(SILLocation loc, // in-place. // TODO: Use immutable box for immutable captures. auto boxTy = SGM.Types.getContextBoxTypeForCapture(vd, - vl.value->getType().getSwiftRValueType(), + vl.value->getType().getASTType(), F.getGenericEnvironment(), /*mutable*/ true); @@ -302,7 +303,7 @@ void SILGenFunction::emitCaptures(SILLocation loc, ManagedValue SILGenFunction::emitClosureValue(SILLocation loc, SILDeclRef constant, CanType expectedType, - SubstitutionList subs) { + SubstitutionMap subs) { auto closure = *constant.getAnyFunctionRef(); auto captureInfo = closure.getCaptureInfo(); auto loweredCaptureInfo = SGM.Types.getLoweredLocalCaptures(closure); @@ -325,7 +326,7 @@ SILGenFunction::emitClosureValue(SILLocation loc, SILDeclRef constant, // If we have a closure expression in generic context, Sema won't give // us substitutions, so we just use the forwarding substitutions from // context. - subs = getForwardingSubstitutions(); + subs = getForwardingSubstitutionMap(); } bool wasSpecialized = false; @@ -406,14 +407,13 @@ void SILGenFunction::emitClosure(AbstractClosureExpr *ace) { ace->isBodyThrowing()); prepareEpilog(ace->getResultType(), ace->isBodyThrowing(), CleanupLocation(ace)); + emitProfilerIncrement(ace); if (auto *ce = dyn_cast(ace)) { - emitProfilerIncrement(ce); emitStmt(ce->getBody()); } else { auto *autoclosure = cast(ace); // Closure expressions implicitly return the result of their body // expression. - emitProfilerIncrement(autoclosure); emitReturnExpr(ImplicitReturnLocation(ace), autoclosure->getSingleExpressionBody()); } @@ -506,7 +506,7 @@ void SILGenFunction::emitArtificialTopLevel(ClassDecl *mainClass) { auto argvTy = fnConv.getSILArgumentType(1); SILType unwrappedTy = argvTy; - if (Type innerTy = argvTy.getSwiftRValueType()->getOptionalObjectType()) { + if (Type innerTy = argvTy.getASTType()->getOptionalObjectType()) { auto canInnerTy = innerTy->getCanonicalType(); unwrappedTy = SILType::getPrimitiveObjectType(canInnerTy); } @@ -516,8 +516,8 @@ void SILGenFunction::emitArtificialTopLevel(ClassDecl *mainClass) { if (unwrappedTy != argv->getType()) { auto converted = emitPointerToPointer(mainClass, managedArgv, - argv->getType().getSwiftRValueType(), - unwrappedTy.getSwiftRValueType()); + argv->getType().getASTType(), + unwrappedTy.getASTType()); managedArgv = std::move(converted).getAsSingleValue(*this, mainClass); } @@ -550,9 +550,9 @@ void SILGenFunction::emitArtificialTopLevel(ClassDecl *mainClass) { // return NSApplicationMain(C_ARGC, C_ARGV); SILParameterInfo argTypes[] = { - SILParameterInfo(argc->getType().getSwiftRValueType(), + SILParameterInfo(argc->getType().getASTType(), ParameterConvention::Direct_Unowned), - SILParameterInfo(argv->getType().getSwiftRValueType(), + SILParameterInfo(argv->getType().getASTType(), ParameterConvention::Direct_Unowned), }; auto NSApplicationMainType = SILFunctionType::get(nullptr, @@ -564,7 +564,7 @@ void SILGenFunction::emitArtificialTopLevel(ClassDecl *mainClass) { ParameterConvention::Direct_Unowned, argTypes, /*yields*/ {}, - SILResultInfo(argc->getType().getSwiftRValueType(), + SILResultInfo(argc->getType().getASTType(), ResultConvention::Unowned), /*error result*/ None, getASTContext()); @@ -658,7 +658,6 @@ void SILGenFunction::emitProfilerIncrement(ASTNode N) { B.createIntegerLiteral(Loc, Int32Ty, CounterIt->second)}; B.createBuiltin(Loc, C.getIdentifier("int_instrprof_increment"), SGM.Types.getEmptyTupleType(), {}, Args); - SP->recordCounterUpdate(); } ProfileCounter SILGenFunction::loadProfilerCount(ASTNode Node) const { diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h index fb77e77789cad..24edce0235620 100644 --- a/lib/SILGen/SILGenFunction.h +++ b/lib/SILGen/SILGenFunction.h @@ -43,7 +43,6 @@ class CalleeTypeInfo; class ResultPlan; using ResultPlanPtr = std::unique_ptr; class ArgumentScope; -class PostponedCleanup; class Scope; enum class ApplyOptions : unsigned { @@ -248,10 +247,6 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction /// \brief The current context where formal evaluation cleanups are managed. FormalEvaluationContext FormalEvalContext; - /// Currently active postponed cleanups. - PostponedCleanup *CurrentlyActivePostponedCleanup = nullptr; - void enterPostponedCleanup(SILValue forValue); - /// \brief Values to end dynamic access enforcement on. A hack for /// materializeForSet. struct UnpairedAccesses { @@ -555,7 +550,7 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction CanAnyFunctionType reqtSubstTy, SILDeclRef requirement, SILDeclRef witness, - SubstitutionList witnessSubs, + SubstitutionMap witnessSubs, IsFreeFunctionWitness_t isFree); /// Convert a block to a native function with a thunk. @@ -1088,7 +1083,7 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction CanType baseFormalType, bool isSuper, AbstractStorageDecl *storage, RValue indexes, - SubstitutionList substitutions, + SubstitutionMap substitutions, AccessSemantics semantics, Type propTy, SGFContext C, bool isBaseGuaranteed = false); @@ -1104,20 +1099,20 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction ManagedValue emitClosureValue(SILLocation loc, SILDeclRef function, CanType expectedType, - SubstitutionList subs); + SubstitutionMap subs); ArgumentSource prepareAccessorBaseArg(SILLocation loc, ManagedValue base, CanType baseFormalType, SILDeclRef accessor); RValue emitGetAccessor(SILLocation loc, SILDeclRef getter, - SubstitutionList substitutions, + SubstitutionMap substitutions, ArgumentSource &&optionalSelfValue, bool isSuper, bool isDirectAccessorUse, RValue &&optionalSubscripts, SGFContext C); void emitSetAccessor(SILLocation loc, SILDeclRef setter, - SubstitutionList substitutions, + SubstitutionMap substitutions, ArgumentSource &&optionalSelfValue, bool isSuper, bool isDirectAccessorUse, RValue &&optionalSubscripts, @@ -1125,7 +1120,7 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction MaterializedLValue emitMaterializeForSetAccessor(SILLocation loc, SILDeclRef materializeForSet, - SubstitutionList substitutions, + SubstitutionMap substitutions, ArgumentSource &&optionalSelfValue, bool isSuper, bool isDirectAccessorUse, RValue &&optionalSubscripts, @@ -1136,12 +1131,12 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction GenericEnvironment *genericEnv, AccessorDecl *requirement, AccessorDecl *witness, - SubstitutionList witnessSubs); + SubstitutionMap witnessSubs); void emitMaterializeForSet(AccessorDecl *decl); std::pair emitAddressorAccessor(SILLocation loc, SILDeclRef addressor, - SubstitutionList substitutions, + SubstitutionMap substitutions, ArgumentSource &&optionalSelfValue, bool isSuper, bool isDirectAccessorUse, RValue &&optionalSubscripts, @@ -1275,7 +1270,7 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction emitSiblingMethodRef(SILLocation loc, SILValue selfValue, SILDeclRef methodConstant, - const SubstitutionMap &subMap); + SubstitutionMap subMap); SILValue emitMetatypeOfValue(SILLocation loc, Expr *baseExpr); @@ -1302,10 +1297,10 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction /// result does need to be turned back into something matching a /// formal type. RValue emitApply(ResultPlanPtr &&resultPlan, ArgumentScope &&argScope, - SILLocation loc, ManagedValue fn, SubstitutionList subs, + SILLocation loc, ManagedValue fn, SubstitutionMap subs, ArrayRef args, const CalleeTypeInfo &calleeTypeInfo, ApplyOptions options, - SGFContext evalContext, PostponedCleanup &cleanup); + SGFContext evalContext); RValue emitApplyOfDefaultArgGenerator(SILLocation loc, ConcreteDeclRef defaultArgsOwner, @@ -1317,7 +1312,7 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction RValue emitApplyOfStoredPropertyInitializer( SILLocation loc, const PatternBindingEntry &entry, - SubstitutionList subs, + SubstitutionMap subs, CanType resultType, AbstractionPattern origResultType, SGFContext C); @@ -1336,19 +1331,13 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction RValue emitApplyOfLibraryIntrinsic(SILLocation loc, FuncDecl *fn, - const SubstitutionMap &subMap, - ArrayRef args, - SGFContext ctx); - - RValue emitApplyOfLibraryIntrinsic(SILLocation loc, - FuncDecl *fn, - const SubstitutionList &subs, + SubstitutionMap subMap, ArrayRef args, SGFContext ctx); SILValue emitApplyWithRethrow(SILLocation loc, SILValue fn, SILType substFnType, - SubstitutionList subs, + SubstitutionMap subs, ArrayRef args); /// Emit a literal that applies the various initializers. @@ -1408,10 +1397,10 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction llvm::SmallDenseMap OpaqueValues; - /// A mapping from opaque value lvalue expressions to the address of the - /// opened value in memory. - llvm::SmallDenseMap> - OpaqueLValues; + /// A mapping from opaque value expressions to the open-existential + /// expression that determines them, used while lowering lvalues. + llvm::SmallDenseMap + OpaqueValueExprs; /// RAII object that introduces a temporary binding for an opaque value. /// @@ -1451,13 +1440,13 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction /// terminated. /// \param handleFalse A callback to invoke in the failure path. The /// current BB should be terminated. - void - emitCheckedCastBranch(SILLocation loc, ConsumableManagedValue src, - Type sourceType, CanType targetType, SGFContext C, - std::function handleTrue, - std::function)> handleFalse, - ProfileCounter TrueCount = ProfileCounter(), - ProfileCounter FalseCount = ProfileCounter()); + void emitCheckedCastBranch( + SILLocation loc, ConsumableManagedValue src, Type sourceType, + CanType targetType, SGFContext C, + llvm::function_ref handleTrue, + llvm::function_ref)> handleFalse, + ProfileCounter TrueCount = ProfileCounter(), + ProfileCounter FalseCount = ProfileCounter()); /// A form of checked cast branch that uses the old non-ownership preserving /// semantics. @@ -1465,12 +1454,13 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction /// The main difference is that this code does not pass the old argument as a /// block argument in the failure case. This causes values to be double /// consumed. - void emitCheckedCastBranchOld(SILLocation loc, Expr *source, Type targetType, - SGFContext ctx, - std::function handleTrue, - std::function handleFalse, - ProfileCounter TrueCount = ProfileCounter(), - ProfileCounter FalseCount = ProfileCounter()); + void + emitCheckedCastBranchOld(SILLocation loc, Expr *source, Type targetType, + SGFContext ctx, + llvm::function_ref handleTrue, + llvm::function_ref handleFalse, + ProfileCounter TrueCount = ProfileCounter(), + ProfileCounter FalseCount = ProfileCounter()); /// \brief Emit a conditional checked cast branch, starting from an /// expression. Terminates the current BB. @@ -1484,13 +1474,12 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction /// terminated. /// \param handleFalse A callback to invoke in the failure path. The /// current BB should be terminated. - void - emitCheckedCastBranch(SILLocation loc, Expr *src, Type targetType, - SGFContext C, - std::function handleTrue, - std::function)> handleFalse, - ProfileCounter TrueCount = ProfileCounter(), - ProfileCounter FalseCount = ProfileCounter()); + void emitCheckedCastBranch( + SILLocation loc, Expr *src, Type targetType, SGFContext C, + llvm::function_ref handleTrue, + llvm::function_ref)> handleFalse, + ProfileCounter TrueCount = ProfileCounter(), + ProfileCounter FalseCount = ProfileCounter()); /// A form of checked cast branch that uses the old non-ownership preserving /// semantics. @@ -1498,13 +1487,13 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction /// The main difference is that this code does not pass the old argument as a /// block argument in the failure case. This causes values to be double /// consumed. - void emitCheckedCastBranchOld(SILLocation loc, ConsumableManagedValue src, - Type sourceType, CanType targetType, - SGFContext ctx, - std::function handleTrue, - std::function handleFalse, - ProfileCounter TrueCount = ProfileCounter(), - ProfileCounter FalseCount = ProfileCounter()); + void + emitCheckedCastBranchOld(SILLocation loc, ConsumableManagedValue src, + Type sourceType, CanType targetType, SGFContext ctx, + llvm::function_ref handleTrue, + llvm::function_ref handleFalse, + ProfileCounter TrueCount = ProfileCounter(), + ProfileCounter FalseCount = ProfileCounter()); /// Emit the control flow for an optional 'bind' operation, branching to the /// active failure destination if the optional value addressed by optionalAddr @@ -1617,8 +1606,7 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction ManagedValue emitTransformedValue(SILLocation loc, ManagedValue input, CanType inputType, CanType outputType, - SGFContext ctx = SGFContext(), - bool postponeToNoEscapeCleanup = true); + SGFContext ctx = SGFContext()); /// Most general form of the above. ManagedValue emitTransformedValue(SILLocation loc, ManagedValue input, @@ -1626,8 +1614,7 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction CanType inputSubstType, AbstractionPattern outputOrigType, CanType outputSubstType, - SGFContext ctx = SGFContext(), - bool postponeToNoEscapeCleanup = true); + SGFContext ctx = SGFContext()); RValue emitTransformedValue(SILLocation loc, RValue &&input, AbstractionPattern inputOrigType, CanType inputSubstType, @@ -1828,8 +1815,8 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction /// Return forwarding substitutions for the archetypes in the current /// function. - SubstitutionList getForwardingSubstitutions(); - + SubstitutionMap getForwardingSubstitutionMap(); + /// Get the _Pointer protocol used for pointer argument operations. ProtocolDecl *getPointerProtocol(); }; @@ -1869,37 +1856,6 @@ class SILGenSavedInsertionPoint { } }; -/// Utility class to facilitate posponment of cleanup of @noescape -/// partial_apply arguments into the 'right' scope. -/// -/// If a Postponed cleanup is active at the end of a scope. The scope will -/// actively push the cleanup into its surrounding scope. -class PostponedCleanup { - friend SILGenFunction; - friend Scope; - - SmallVector, 16> deferredCleanups; - CleanupsDepth depth; - SILGenFunction &SGF; - PostponedCleanup *previouslyActiveCleanup; - bool active; - bool applyRecursively; - - void postponeCleanup(CleanupHandle cleanup, SILValue forValue); -public: - PostponedCleanup(SILGenFunction &SGF); - PostponedCleanup(SILGenFunction &SGF, bool applyRecursively); - ~PostponedCleanup(); - - void end(); - - PostponedCleanup() = delete; - PostponedCleanup(const PostponedCleanup &) = delete; - PostponedCleanup &operator=(const PostponedCleanup &) = delete; - PostponedCleanup &operator=(PostponedCleanup &&other) = delete; - PostponedCleanup(PostponedCleanup &&) = delete; -}; - } // end namespace Lowering } // end namespace swift diff --git a/lib/SILGen/SILGenLValue.cpp b/lib/SILGen/SILGenLValue.cpp index 4eccfba2e508c..8414287be5451 100644 --- a/lib/SILGen/SILGenLValue.cpp +++ b/lib/SILGen/SILGenLValue.cpp @@ -14,23 +14,24 @@ // //===----------------------------------------------------------------------===// -#include "SILGen.h" +#include "ASTVisitor.h" #include "ArgumentSource.h" #include "Conversion.h" +#include "Initialization.h" #include "LValue.h" #include "RValue.h" +#include "SILGen.h" #include "Scope.h" -#include "Initialization.h" -#include "swift/AST/DiagnosticsSIL.h" #include "swift/AST/DiagnosticsCommon.h" +#include "swift/AST/DiagnosticsSIL.h" #include "swift/AST/GenericEnvironment.h" #include "swift/SIL/InstructionUtils.h" +#include "swift/SIL/MemAccessUtils.h" #include "swift/SIL/PrettyStackTrace.h" #include "swift/SIL/SILArgument.h" #include "swift/SIL/SILUndef.h" #include "swift/SIL/TypeLowering.h" #include "llvm/Support/raw_ostream.h" -#include "ASTVisitor.h" using namespace swift; using namespace Lowering; @@ -228,7 +229,7 @@ ManagedValue LogicalPathComponent::materializeIntoTemporary( // If the RValue type has an openedExistential, then the RValue must be // materialized before allocating a temporary for the RValue type. In that // case, the RValue cannot be emitted directly into the temporary. - if (getTypeOfRValue().getSwiftRValueType()->hasOpenedExistential()) { + if (getTypeOfRValue().hasOpenedExistential()) { // Emit a 'get'. rvalue = std::move(*this).get(SGF, loc, base, SGFContext()); @@ -465,7 +466,8 @@ static SILValue enterAccessScope(SILGenFunction &SGF, SILLocation loc, if (enforcement == SILAccessEnforcement::Dynamic) { SGF.B.createBeginUnpairedAccess(loc, addr, unpairedAccesses->Buffer, silAccessKind, enforcement, - /*hasNoNestedConflict=*/false); + /*hasNoNestedConflict=*/false, + /*fromBuiltin=*/false); unpairedAccesses->NumAccesses++; } return addr; @@ -473,7 +475,8 @@ static SILValue enterAccessScope(SILGenFunction &SGF, SILLocation loc, // Enter the access. addr = SGF.B.createBeginAccess(loc, addr, silAccessKind, enforcement, - /*hasNoNestedConflict=*/false); + /*hasNoNestedConflict=*/false, + /*fromBuiltin=*/false); // Push a writeback to end it. auto accessedMV = ManagedValue::forLValue(addr); @@ -497,13 +500,18 @@ SILValue UnenforcedAccess::beginAccess(SILGenFunction &SGF, SILLocation loc, if (!SGF.getOptions().VerifyExclusivity) return address; - SILValue base = findAccessedAddressBase(address); - if (!base || !isPossibleFormalAccessBase(base)) + const AccessedStorage &storage = findAccessedStorage(address); + if (!storage) { + llvm::dbgs() << "Bad memory access source: " << address; + llvm_unreachable("Unexpected access source."); + } + if (!isPossibleFormalAccessBase(storage, &SGF.F)) return address; auto BAI = SGF.B.createBeginAccess(loc, address, kind, SILAccessEnforcement::Unsafe, - /*hasNoNestedConflict=*/false); + /*hasNoNestedConflict=*/false, + /*fromBuiltin=*/false); beginAccessPtr = BeginAccessPtr(BAI, DeleterCheck()); return BAI; @@ -989,7 +997,7 @@ namespace { AbstractStorageDecl *decl; bool IsSuper; bool IsDirectAccessorUse; - std::vector substitutions; + SubstitutionMap substitutions; /// The subscript index expression. Useless Expr *subscriptIndexExpr; @@ -1024,14 +1032,14 @@ namespace { AccessorBasedComponent(PathComponent::KindTy kind, AbstractStorageDecl *decl, bool isSuper, bool isDirectAccessorUse, - SubstitutionList substitutions, + SubstitutionMap substitutions, CanType baseFormalType, LValueTypeData typeData, Expr *subscriptIndexExpr, RValue *optSubscripts) : Base(typeData, kind), decl(decl), IsSuper(isSuper), IsDirectAccessorUse(isDirectAccessorUse), - substitutions(substitutions.begin(), substitutions.end()), + substitutions(substitutions), subscriptIndexExpr(subscriptIndexExpr), baseFormalType(baseFormalType) { @@ -1051,18 +1059,21 @@ namespace { subscripts(copied.subscripts.copy(SGF, loc)) , baseFormalType(copied.baseFormalType) {} - virtual SILDeclRef getAccessor(SILGenFunction &SGF, - AccessKind kind) const = 0; + virtual bool doesAccessMutateSelf(SILGenFunction &SGF, + AccessKind kind) const = 0; + bool doesAccessorMutateSelf(SILGenFunction &SGF, + SILDeclRef accessor) const { + auto accessorSelf = SGF.SGM.Types.getConstantSelfParameter(accessor); + return accessorSelf.getType() && accessorSelf.isIndirectMutating(); + } + AccessKind getBaseAccessKind(SILGenFunction &SGF, AccessKind kind) const override { - SILDeclRef accessor = getAccessor(SGF, kind); - auto accessorSelf = SGF.SGM.Types.getConstantSelfParameter(accessor); - if (accessorSelf.getType() && accessorSelf.isIndirectMutating()) { + if (doesAccessMutateSelf(SGF, kind)) return AccessKind::ReadWrite; - } else { + else return AccessKind::Read; - } } void printBase(raw_ostream &OS, unsigned indent, StringRef name) const { @@ -1083,7 +1094,7 @@ namespace { GetterSetterComponent(AbstractStorageDecl *decl, bool isSuper, bool isDirectAccessorUse, - SubstitutionList substitutions, + SubstitutionMap substitutions, CanType baseFormalType, LValueTypeData typeData, Expr *subscriptIndexExpr = nullptr, @@ -1102,13 +1113,25 @@ namespace { { } - SILDeclRef getAccessor(SILGenFunction &SGF, - AccessKind accessKind) const override { - if (accessKind == AccessKind::Read) { - return SGF.SGM.getGetterDeclRef(decl); - } else { - return SGF.SGM.getSetterDeclRef(decl); + bool doesAccessMutateSelf(SILGenFunction &SGF, + AccessKind accessKind) const override { + switch (accessKind) { + case AccessKind::Read: { + auto getter = SGF.SGM.getGetterDeclRef(decl); + return doesAccessorMutateSelf(SGF, getter); + } + case AccessKind::Write: { + auto setter = SGF.SGM.getSetterDeclRef(decl); + return doesAccessorMutateSelf(SGF, setter); } + case AccessKind::ReadWrite: { + auto getter = SGF.SGM.getGetterDeclRef(decl); + auto setter = SGF.SGM.getSetterDeclRef(decl); + return doesAccessorMutateSelf(SGF, getter) + || doesAccessorMutateSelf(SGF, setter); + } + } + llvm_unreachable("unknown access kind"); } void emitAssignWithSetter(SILGenFunction &SGF, SILLocation loc, @@ -1127,15 +1150,15 @@ namespace { dest.dropLastComponent(*this); return emitAssignWithSetter(SGF, loc, std::move(dest), baseFormalType, - isSuper, setter, isDirectAccessorUse, subs, - std::move(indices), std::move(value)); + isSuper, setter, isDirectAccessorUse, + subs, std::move(indices), std::move(value)); } static void emitAssignWithSetter(SILGenFunction &SGF, SILLocation loc, LValue &&baseLV, CanType baseFormalType, bool isSuper, SILDeclRef setter, bool isDirectAccessorUse, - SubstitutionList subs, + SubstitutionMap subs, RValue &&indices, ArgumentSource &&value) { ArgumentSource self = [&] { if (!baseLV.isValid()) { @@ -1149,8 +1172,8 @@ namespace { } }(); - return SGF.emitSetAccessor(loc, setter, subs, std::move(self), isSuper, - isDirectAccessorUse, + return SGF.emitSetAccessor(loc, setter, subs, std::move(self), + isSuper, isDirectAccessorUse, std::move(indices), std::move(value)); } @@ -1238,9 +1261,6 @@ namespace { SILValue buffer = SGF.emitTemporaryAllocation(loc, getTypeOfRValue()); - // Postpone cleanup for noescape closures. - PostponedCleanup postpone(SGF, true); - // Clone the component without cloning the indices. We don't actually // consume them in writeback(). std::unique_ptr clonedComponent( @@ -1279,7 +1299,8 @@ namespace { auto args = std::move(*this).prepareAccessorArgs(SGF, loc, borrowedBase, materializeForSet); materialized = SGF.emitMaterializeForSetAccessor( - loc, materializeForSet, substitutions, std::move(args.base), + loc, materializeForSet, substitutions, + std::move(args.base), IsSuper, IsDirectAccessorUse, std::move(args.subscripts), buffer, callbackStorage); @@ -1298,7 +1319,6 @@ namespace { // access for stored properties with didSet. pushWriteback(SGF, loc, std::move(clonedComponent), base, materialized); - postpone.end(); return ManagedValue::forLValue(materialized.temporary.getValue()); } @@ -1568,7 +1588,7 @@ namespace { public: AddressorComponent(AbstractStorageDecl *decl, bool isSuper, bool isDirectAccessorUse, - SubstitutionList substitutions, + SubstitutionMap substitutions, CanType baseFormalType, LValueTypeData typeData, SILType substFieldType, Expr *subscriptIndexExpr = nullptr, @@ -1581,9 +1601,10 @@ namespace { { } - SILDeclRef getAccessor(SILGenFunction &SGF, - AccessKind accessKind) const override { - return SGF.SGM.getAddressorDeclRef(decl, accessKind); + bool doesAccessMutateSelf(SILGenFunction &SGF, + AccessKind kind) const override { + auto addressor = SGF.SGM.getAddressorDeclRef(decl, kind); + return doesAccessorMutateSelf(SGF, addressor); } ManagedValue offset(SILGenFunction &SGF, SILLocation loc, ManagedValue base, @@ -1599,7 +1620,8 @@ namespace { auto args = std::move(*this).prepareAccessorArgs(SGF, loc, base, addressor); result = SGF.emitAddressorAccessor( - loc, addressor, substitutions, std::move(args.base), IsSuper, + loc, addressor, substitutions, std::move(args.base), + IsSuper, IsDirectAccessorUse, std::move(args.subscripts), SubstFieldType); } switch (cast(addressor.getDecl())->getAddressorKind()) { @@ -1699,14 +1721,16 @@ namespace { llvm_unreachable("not a writable key path type?!"); } - Substitution args[] = { - Substitution(keyPathTy->getGenericArgs()[0], {}), - Substitution(keyPathTy->getGenericArgs()[1], {}), - }; - - auto subMap = projectionFunction->getGenericSignature() - ->getSubstitutionMap(args); - + auto projectionGenericSig = projectionFunction->getGenericSignature(); + auto subMap = projectionGenericSig->getSubstitutionMap( + [&](SubstitutableType *type) -> Type { + auto genericParam = cast(type); + auto index = + projectionGenericSig->getGenericParamOrdinal(genericParam); + return keyPathTy->getGenericArgs()[index]; + }, + LookUpConformanceInSignature(*projectionGenericSig)); + // The projection function behaves like an owning addressor, returning // a pointer to the projected value and an owner reference that keeps // it alive. @@ -1755,13 +1779,17 @@ namespace { SILType::getPrimitiveObjectType(keyPathTy->getCanonicalType())); } - Substitution args[] = { - Substitution(keyPathTy->getGenericArgs()[0], {}), - Substitution(keyPathTy->getGenericArgs()[1], {}), - }; auto projectFn = C.getProjectKeyPathReadOnly(nullptr); - auto subMap = projectFn->getGenericSignature() - ->getSubstitutionMap(args); + + auto projectionGenericSig = projectFn->getGenericSignature(); + auto subMap = projectionGenericSig->getSubstitutionMap( + [&](SubstitutableType *type) -> Type { + auto genericParam = cast(type); + auto index = + projectionGenericSig->getGenericParamOrdinal(genericParam); + return keyPathTy->getGenericArgs()[index]; + }, + LookUpConformanceInSignature(*projectionGenericSig)); // Allocate a temporary to own the projected value. auto &resultTL = SGF.getTypeLowering(keyPathTy->getGenericArgs()[1]); @@ -1997,7 +2025,7 @@ LValue LValue::forAddress(ManagedValue address, void LValue::addMemberComponent(SILGenFunction &SGF, SILLocation loc, AbstractStorageDecl *storage, - SubstitutionList subs, + SubstitutionMap subs, LValueOptions options, bool isSuper, AccessKind accessKind, @@ -2208,13 +2236,13 @@ LValue SILGenLValue::visitExpr(Expr *e, AccessKind accessKind, llvm_unreachable("unimplemented lvalue expr"); } -SubstitutionList +SubstitutionMap SILGenModule::getNonMemberVarDeclSubstitutions(VarDecl *var) { - SubstitutionList substitutions; auto *dc = var->getDeclContext(); if (auto *genericEnv = dc->getGenericEnvironmentOfContext()) - substitutions = genericEnv->getForwardingSubstitutions(); - return substitutions; + return genericEnv->getForwardingSubstitutionMap(); + + return SubstitutionMap(); } // For now, we don't need either an AccessKind or an @@ -2228,7 +2256,7 @@ addNonMemberVarDeclAddressorComponent(SILGenModule &SGM, VarDecl *var, auto typeData = getPhysicalStorageTypeData(SGM, var, formalRValueType); SILType storageType = SGM.Types.getLoweredType(var->getType()).getAddressType(); lvalue.add(var, /*isSuper=*/ false, /*direct*/ true, - SGM.getNonMemberVarDeclSubstitutions(var), + SGM.getNonMemberVarDeclSubstitutions(var), CanType(), typeData, storageType); } @@ -2338,18 +2366,20 @@ LValue SILGenLValue::visitOpaqueValueExpr(OpaqueValueExpr *e, AccessKind accessKind, LValueOptions options) { // Handle an opaque lvalue that refers to an opened existential. - auto known = SGF.OpaqueLValues.find(e); - if (known != SGF.OpaqueLValues.end()) { - // Dig the opened address out of the list. - SILValue opened = known->second.first; - CanType openedType = known->second.second; - SGF.OpaqueLValues.erase(known); - - // Continue formal evaluation of the lvalue from this address. - return LValue::forAddress(ManagedValue::forLValue(opened), - None, - AbstractionPattern(openedType), - openedType); + auto known = SGF.OpaqueValueExprs.find(e); + if (known != SGF.OpaqueValueExprs.end()) { + // Dig the open-existential expression out of the list. + OpenExistentialExpr *opened = known->second; + SGF.OpaqueValueExprs.erase(known); + + // Do formal evaluation of the underlying existential lvalue. + auto lv = visitRec(opened->getExistentialValue(), accessKind, options); + lv = SGF.emitOpenExistentialLValue( + opened, std::move(lv), + CanArchetypeType(opened->getOpenedArchetype()), + e->getType()->getWithoutSpecifierType()->getCanonicalType(), + accessKind); + return lv; } assert(SGF.OpaqueValues.count(e) && "Didn't bind OpaqueValueExpr"); @@ -2426,7 +2456,8 @@ LValue SILGenLValue::visitMemberRefExpr(MemberRefExpr *e, assert(lv.isValid()); CanType substFormalRValueType = getSubstFormalRValueType(e); - lv.addMemberVarComponent(SGF, e, var, e->getMember().getSubstitutions(), + lv.addMemberVarComponent(SGF, e, var, + e->getMember().getSubstitutions(), options, e->isSuper(), accessKind, e->getAccessSemantics(), strategy, substFormalRValueType); @@ -2435,7 +2466,7 @@ LValue SILGenLValue::visitMemberRefExpr(MemberRefExpr *e, void LValue::addMemberVarComponent(SILGenFunction &SGF, SILLocation loc, VarDecl *var, - SubstitutionList subs, + SubstitutionMap subs, LValueOptions options, bool isSuper, AccessKind accessKind, @@ -2525,7 +2556,8 @@ LValue SILGenLValue::visitSubscriptExpr(SubscriptExpr *e, RValue index = SGF.emitRValue(indexExpr); CanType formalRValueType = getSubstFormalRValueType(e); - lv.addMemberSubscriptComponent(SGF, e, decl, e->getDecl().getSubstitutions(), + lv.addMemberSubscriptComponent(SGF, e, decl, + e->getDecl().getSubstitutions(), options, e->isSuper(), accessKind, accessSemantics, strategy, formalRValueType, std::move(index), @@ -2578,7 +2610,7 @@ LValue SILGenLValue::visitKeyPathApplicationExpr(KeyPathApplicationExpr *e, void LValue::addMemberSubscriptComponent(SILGenFunction &SGF, SILLocation loc, SubscriptDecl *decl, - SubstitutionList subs, + SubstitutionMap subs, LValueOptions options, bool isSuper, AccessKind accessKind, @@ -2641,12 +2673,29 @@ LValue SILGenLValue::visitTupleElementExpr(TupleElementExpr *e, LValue SILGenLValue::visitOpenExistentialExpr(OpenExistentialExpr *e, AccessKind accessKind, LValueOptions options) { - return SGF.emitOpenExistentialExpr(e, - [&](Expr *subExpr) -> LValue { - return visitRec(subExpr, - accessKind, - options); - }); + // If the opaque value is not an lvalue, open the existential immediately. + if (!e->getOpaqueValue()->getType()->is()) { + return SGF.emitOpenExistentialExpr(e, + [&](Expr *subExpr) -> LValue { + return visitRec(subExpr, + accessKind, + options); + }); + } + + // Record the fact that we're opening this existential. The actual + // opening operation will occur when we see the OpaqueValueExpr. + bool inserted = SGF.OpaqueValueExprs.insert({e->getOpaqueValue(), e}).second; + (void)inserted; + assert(inserted && "already have this opened existential?"); + + // Visit the subexpression. + LValue lv = visitRec(e->getSubExpr(), accessKind, options); + + // Sanity check that we did see the OpaqueValueExpr. + assert(SGF.OpaqueValueExprs.count(e->getOpaqueValue()) == 0 && + "opened existential not removed?"); + return lv; } static LValueTypeData @@ -2716,14 +2765,10 @@ LValue SILGenFunction::emitPropertyLValue(SILLocation loc, ManagedValue base, SILGenLValue sgl(*this); LValue lv; - auto baseType = base.getType().getSwiftRValueType(); + auto baseType = base.getType().getASTType(); auto subMap = baseType->getContextSubstitutionMap( SGM.M.getSwiftModule(), ivar->getDeclContext()); - SmallVector subs; - if (auto *genericSig = ivar->getDeclContext()->getGenericSignatureOfContext()) - genericSig->getSubstitutions(subMap, subs); - LValueTypeData baseTypeData = getValueTypeData(baseFormalType, base.getValue()); @@ -2745,7 +2790,7 @@ LValue SILGenFunction::emitPropertyLValue(SILLocation loc, ManagedValue base, auto typeData = getLogicalStorageTypeData(SGM, substFormalType); lv.add(ivar, /*super*/ false, strategy == AccessStrategy::DirectToAccessor, - subs, baseFormalType, typeData); + subMap, baseFormalType, typeData); return lv; } @@ -2760,7 +2805,7 @@ LValue SILGenFunction::emitPropertyLValue(SILLocation loc, ManagedValue base, if (strategy == AccessStrategy::Addressor) { lv.add(ivar, /*super*/ false, /*direct*/ true, - subs, baseFormalType, typeData, varStorageType); + subMap, baseFormalType, typeData, varStorageType); } else if (baseFormalType->hasReferenceSemantics()) { lv.add(ivar, options, varStorageType, typeData); } else { @@ -3441,12 +3486,18 @@ void SILGenFunction::emitCopyLValueInto(SILLocation loc, LValue &&src, if (!dest->canPerformInPlaceInitialization()) return skipPeephole(); auto destAddr = dest->getAddressForInPlaceInitialization(*this, loc); - assert(src.getTypeOfRValue().getSwiftRValueType() - == destAddr->getType().getSwiftRValueType()); - + assert(src.getTypeOfRValue().getASTType() + == destAddr->getType().getASTType()); + auto srcAddr = emitAddressOfLValue(loc, std::move(src), AccessKind::Read) .getUnmanagedValue(); - B.createCopyAddr(loc, srcAddr, destAddr, IsNotTake, IsInitialization); + + UnenforcedAccess access; + SILValue accessAddress = + access.beginAccess(*this, loc, destAddr, SILAccessKind::Modify); + B.createCopyAddr(loc, srcAddr, accessAddress, IsNotTake, IsInitialization); + access.endAccess(*this); + dest->finishInitialization(*this); } diff --git a/lib/SILGen/SILGenMaterializeForSet.cpp b/lib/SILGen/SILGenMaterializeForSet.cpp index b3c4cb5f8aa28..0f3f90b87b254 100644 --- a/lib/SILGen/SILGenMaterializeForSet.cpp +++ b/lib/SILGen/SILGenMaterializeForSet.cpp @@ -235,7 +235,7 @@ struct MaterializeForSetEmitter { AccessorDecl *Witness; AbstractStorageDecl *WitnessStorage; AbstractionPattern WitnessStoragePattern; - SubstitutionList WitnessSubs; + SubstitutionMap WitnessSubs; CanGenericSignature GenericSig; GenericEnvironment *GenericEnv; @@ -261,7 +261,7 @@ struct MaterializeForSetEmitter { private: MaterializeForSetEmitter( SILGenModule &SGM, SILLinkage linkage, AccessorDecl *witness, - SubstitutionList subs, GenericEnvironment *genericEnv, + SubstitutionMap subs, GenericEnvironment *genericEnv, Type selfInterfaceType, Type selfType, SILFunctionTypeRepresentation callbackRepresentation, Optional witnessMethodConformance) @@ -306,7 +306,7 @@ struct MaterializeForSetEmitter { forWitnessThunk(SILGenModule &SGM, ProtocolConformanceRef conformance, SILLinkage linkage, Type selfInterfaceType, Type selfType, GenericEnvironment *genericEnv, AccessorDecl *requirement, - AccessorDecl *witness, SubstitutionList witnessSubs) { + AccessorDecl *witness, SubstitutionMap witnessSubs) { MaterializeForSetEmitter emitter( SGM, linkage, witness, witnessSubs, genericEnv, selfInterfaceType, selfType, SILFunctionTypeRepresentation::WitnessMethod, conformance); @@ -330,7 +330,7 @@ struct MaterializeForSetEmitter { static MaterializeForSetEmitter forConcreteImplementation(SILGenModule &SGM, AccessorDecl *witness, - SubstitutionList witnessSubs) { + SubstitutionMap witnessSubs) { auto *dc = witness->getDeclContext(); Type selfInterfaceType = dc->getSelfInterfaceType(); Type selfType = witness->mapTypeIntoContext(selfInterfaceType); @@ -370,12 +370,36 @@ struct MaterializeForSetEmitter { return true; // We also need to open-code if the witness is defined in a - // protocol context because IRGen won't know how to reconstruct - // the type parameters. (In principle, this can be done in the - // callback storage if we need to.) + // context that isn't ABI-compatible with the protocol witness, + // because IRGen won't know how to reconstruct + // the type parameters. (In principle, this could be done in the + // callback storage.) + + // This can happen if the witness is in a protocol extension... if (Witness->getDeclContext()->getAsProtocolOrProtocolExtensionContext()) return true; + // ...if the witness is in a constrained extension that adds protocol + // requirements... + if (auto ext = dyn_cast(Witness->getDeclContext())) { + if (ext->isConstrainedExtension()) { + // TODO: We could perhaps avoid open coding if the extension only adds + // same type or superclass constraints, which don't require any + // additional generic arguments. + return true; + } + } + + // ...or if the witness is a generic subscript with more general + // subscript-level constraints than the requirement. + if (auto witnessSub = dyn_cast(Witness->getStorage())) { + // TODO: We only really need to open-code if the witness has more general + // subscript-level constraints than the requirement. Our generic signature + // representation makes testing this difficult, unfortunately. + if (witnessSub->isGeneric()) + return true; + } + return false; } @@ -506,8 +530,8 @@ struct MaterializeForSetEmitter { /// substitution according to the witness substitutions. CanType getSubstWitnessInterfaceType(CanType type) { if (auto *witnessSig = Witness->getGenericSignature()) { - auto subMap = witnessSig->getSubstitutionMap(WitnessSubs); - return type.subst(subMap, SubstFlags::UseErrorType)->getCanonicalType(); + return type.subst(WitnessSubs, SubstFlags::UseErrorType) + ->getCanonicalType(); } return type; @@ -770,7 +794,8 @@ MaterializeForSetEmitter::createEndUnpairedAccessesCallback(SILFunction &F, "multiple unpaired accesses not supported"); SGF.B.createEndUnpairedAccess(loc, callbackStorage, SILAccessEnforcement::Dynamic, - /*aborting*/ false); + /*aborting*/ false, + /*fromBuiltin*/ false); }); } @@ -1004,7 +1029,7 @@ bool SILGenFunction::maybeEmitMaterializeForSetThunk( ProtocolConformanceRef conformance, SILLinkage linkage, Type selfInterfaceType, Type selfType, GenericEnvironment *genericEnv, AccessorDecl *requirement, AccessorDecl *witness, - SubstitutionList witnessSubs) { + SubstitutionMap witnessSubs) { MaterializeForSetEmitter emitter = MaterializeForSetEmitter::forWitnessThunk( @@ -1026,6 +1051,6 @@ void SILGenFunction::emitMaterializeForSet(AccessorDecl *decl) { MaterializeForSetEmitter emitter = MaterializeForSetEmitter::forConcreteImplementation( - SGM, decl, getForwardingSubstitutions()); + SGM, decl, getForwardingSubstitutionMap()); emitter.emit(*this); } diff --git a/lib/SILGen/SILGenPattern.cpp b/lib/SILGen/SILGenPattern.cpp index 0c9cce4530698..c728d5efaaf8e 100644 --- a/lib/SILGen/SILGenPattern.cpp +++ b/lib/SILGen/SILGenPattern.cpp @@ -22,7 +22,9 @@ #include "swift/AST/DiagnosticsSIL.h" #include "swift/AST/Pattern.h" #include "swift/AST/SILOptions.h" +#include "swift/AST/SubstitutionMap.h" #include "swift/AST/Types.h" +#include "swift/Basic/Defer.h" #include "swift/Basic/ProfileCounter.h" #include "swift/Basic/STLExtras.h" #include "swift/SIL/DynamicCasts.h" @@ -1449,7 +1451,7 @@ emitReabstractedSubobject(SILGenFunction &SGF, SILLocation loc, CanType substFormalType) { // Return if there's no abstraction. (The first condition is just // a fast path.) - if (value.getType().getSwiftRValueType() == substFormalType || + if (value.getType().getASTType() == substFormalType || value.getType() == SGF.getLoweredType(substFormalType)) return value; @@ -1863,7 +1865,7 @@ void PatternMatchEmission::emitEnumElementDispatchWithOwnership( bool hasElt = false; if (elt->hasAssociatedValues()) { eltTy = src.getType().getEnumElementType(elt, SGF.SGM.M); - hasElt = !eltTy.getSwiftRValueType()->isVoid(); + hasElt = !eltTy.getASTType()->isVoid(); } ConsumableManagedValue eltCMV, origCMV; @@ -2024,7 +2026,7 @@ void PatternMatchEmission::emitEnumElementDispatch( bool hasElt = false; if (elt->hasAssociatedValues()) { eltTy = src.getType().getEnumElementType(elt, SGF.SGM.M); - hasElt = !eltTy.getSwiftRValueType()->isVoid(); + hasElt = !eltTy.getASTType()->isVoid(); } ConsumableManagedValue eltCMV, origCMV; @@ -2555,19 +2557,93 @@ void SILGenFunction::usingImplicitVariablesForPattern(Pattern *pattern, CaseStmt variableSwapper(); } +static void emitDiagnoseOfUnexpectedEnumCaseValue(SILGenFunction &SGF, + SILLocation loc, + ManagedValue value, + const EnumDecl *enumDecl) { + ASTContext &ctx = SGF.getASTContext(); + auto diagnoseFailure = ctx.getDiagnoseUnexpectedEnumCaseValue(nullptr); + if (!diagnoseFailure) { + SGF.B.createBuiltinTrap(loc); + return; + } + + assert(enumDecl->isObjC()); + assert(enumDecl->hasRawType()); + assert(value.getType().isTrivial(SGF.getModule())); + + // Get the enum type as an Any.Type value. + CanType switchedValueSwiftType = value.getType().getASTType(); + SILType metatypeType = SGF.getLoweredType( + CanMetatypeType::get(switchedValueSwiftType, + MetatypeRepresentation::Thick)); + SILValue metatype = SGF.B.createMetatype(loc, metatypeType); + + // Bitcast the enum value to its raw type. (This is only safe for @objc + // enums.) + SILType loweredRawType = SGF.getLoweredType(enumDecl->getRawType()); + assert(loweredRawType.isTrivial(SGF.getModule())); + assert(loweredRawType.isObject()); + auto rawValue = SGF.B.createUncheckedTrivialBitCast(loc, value, + loweredRawType); + auto materializedRawValue = rawValue.materialize(SGF, loc); + + auto genericSig = diagnoseFailure->getGenericSignature(); + auto subs = genericSig->getSubstitutionMap( + [&](SubstitutableType *type) -> Type { + auto genericParam = cast(type); + assert(genericParam->getDepth() == 0); + assert(genericParam->getIndex() < 2); + switch (genericParam->getIndex()) { + case 0: + return switchedValueSwiftType; + + case 1: + return enumDecl->getRawType(); + + default: + llvm_unreachable("wrong generic signature for expected case value"); + } + }, + LookUpConformanceInSignature(*genericSig)); + + SGF.emitApplyOfLibraryIntrinsic(loc, diagnoseFailure, subs, + {ManagedValue::forUnmanaged(metatype), + materializedRawValue}, + SGFContext()); +} + +static void emitDiagnoseOfUnexpectedEnumCase(SILGenFunction &SGF, + SILLocation loc, + ManagedValue value) { + ASTContext &ctx = SGF.getASTContext(); + auto diagnoseFailure = ctx.getDiagnoseUnexpectedEnumCase(nullptr); + if (!diagnoseFailure) { + SGF.B.createBuiltinTrap(loc); + return; + } + + // Get the switched-upon value's type. + CanType switchedValueSwiftType = value.getType().getASTType(); + SILType metatypeType = SGF.getLoweredType( + CanMetatypeType::get(switchedValueSwiftType, + MetatypeRepresentation::Thick)); + ManagedValue metatype = SGF.B.createValueMetatype(loc, metatypeType, value); + + auto diagnoseSignature = diagnoseFailure->getGenericSignature(); + auto genericArgsMap = diagnoseSignature->getSubstitutionMap( + [&](SubstitutableType *type) -> Type { return switchedValueSwiftType; }, + LookUpConformanceInSignature(*diagnoseSignature)); + + SGF.emitApplyOfLibraryIntrinsic(loc, diagnoseFailure, genericArgsMap, + metatype, + SGFContext()); +} + void SILGenFunction::emitSwitchStmt(SwitchStmt *S) { DEBUG(llvm::dbgs() << "emitting switch stmt\n"; S->print(llvm::dbgs()); llvm::dbgs() << '\n'); - auto failure = [this](SILLocation location) { - // If we fail to match anything, we trap. This can happen with a switch - // over an @objc enum, which may contain any value of its underlying type. - // FIXME: Even if we can't say what the invalid value was, we should at - // least mention that this was because of a non-exhaustive enum. - B.createBuiltinTrap(location); - B.createUnreachable(location); - }; - // If the subject expression is uninhabited, we're already dead. // Emit an unreachable in place of the switch statement. if (S->getSubjectExpr()->getType()->isStructurallyUninhabited()) { @@ -2651,10 +2727,7 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) { PatternMatchEmission emission(*this, S, completionHandler); // Add a row for each label of each case. - // - // We use std::vector because it supports emplace_back; moving a ClauseRow is - // expensive. - std::vector clauseRows; + SmallVector clauseRows; clauseRows.reserve(S->getRawCases().size()); bool hasFallthrough = false; for (auto caseBlock : S->getCases()) { @@ -2695,6 +2768,27 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) { ManagedValue subjectMV = emitRValueAsSingleValue(S->getSubjectExpr()); auto subject = ConsumableManagedValue::forOwned(subjectMV); + auto failure = [&](SILLocation location) { + // If we fail to match anything, we trap. This can happen with a switch + // over an @objc enum, which may contain any value of its underlying type, + // or a switch over a non-frozen Swift enum when the user hasn't written a + // catch-all case. + SWIFT_DEFER { B.createUnreachable(location); }; + + // Special case: if it's a single @objc enum, we can print the raw value. + CanType ty = S->getSubjectExpr()->getType()->getCanonicalType(); + if (auto *singleEnumDecl = ty->getEnumOrBoundGenericEnum()) { + if (singleEnumDecl->isObjC()) { + emitDiagnoseOfUnexpectedEnumCaseValue(*this, location, + subject.getFinalManagedValue(), + singleEnumDecl); + return; + } + } + emitDiagnoseOfUnexpectedEnumCase(*this, location, + subject.getFinalManagedValue()); + }; + // Set up an initial clause matrix. ClauseMatrix clauses(clauseRows); diff --git a/lib/SILGen/SILGenPoly.cpp b/lib/SILGen/SILGenPoly.cpp index bbc0ea596d3b5..2eb357a16eaef 100644 --- a/lib/SILGen/SILGenPoly.cpp +++ b/lib/SILGen/SILGenPoly.cpp @@ -136,8 +136,7 @@ namespace { CanType inputSubstType, AbstractionPattern outputOrigType, CanType outputSubstType, - SGFContext ctxt, - bool postponeToNoEscapeCleanup = true); + SGFContext ctxt); /// Transform a metatype value. ManagedValue transformMetatype(ManagedValue fn, @@ -160,8 +159,7 @@ namespace { CanAnyFunctionType inputSubstType, AbstractionPattern outputOrigType, CanAnyFunctionType outputSubstType, - const TypeLowering &expectedTL, - bool postponeToNoEscapeCleanup = true); + const TypeLowering &expectedTL); }; } // end anonymous namespace ; @@ -358,8 +356,7 @@ ManagedValue Transform::transform(ManagedValue v, CanType inputSubstType, AbstractionPattern outputOrigType, CanType outputSubstType, - SGFContext ctxt, - bool postponeToNoEscapeCleanup) { + SGFContext ctxt) { // Look through inout types. if (isa(inputSubstType)) inputSubstType = CanType(inputSubstType->getInOutObjectType()); @@ -443,8 +440,7 @@ ManagedValue Transform::transform(ManagedValue v, return transformFunction(v, inputOrigType, inputFnType, outputOrigType, outputFnType, - expectedTL, - postponeToNoEscapeCleanup); + expectedTL); } // - tuples of transformable values @@ -2081,7 +2077,7 @@ ResultPlanner::planTupleIntoDirectResult(AbstractionPattern innerOrigType, auto someDecl = SGF.getASTContext().getOptionalSomeDecl(); SILType outerObjectType = SGF.getSILType(outerResult).getOptionalObjectType(); - SILResultInfo outerObjectResult(outerObjectType.getSwiftRValueType(), + SILResultInfo outerObjectResult(outerObjectType.getASTType(), outerResult.getConvention()); // Plan to leave the tuple elements as a single direct outer result. @@ -2125,7 +2121,7 @@ ResultPlanner::planTupleIntoDirectResult(AbstractionPattern innerOrigType, for (auto eltIndex : indices(innerSubstType.getElementTypes())) { auto outerEltType = SGF.getSILType(outerResult).getTupleElementType(eltIndex); - SILResultInfo outerEltResult(outerEltType.getSwiftRValueType(), + SILResultInfo outerEltResult(outerEltType.getASTType(), outerResult.getConvention()); planIntoDirectResult(innerOrigType.getTupleElementType(eltIndex), @@ -2189,7 +2185,7 @@ ResultPlanner::planScalarIntoIndirectResult(AbstractionPattern innerOrigType, assert(!outerOrigType.isTuple()); bool hasAbstractionDifference = - (innerResult.getType() != outerResultAddr->getType().getSwiftRValueType()); + (innerResult.getType() != outerResultAddr->getType().getASTType()); // If the inner result is indirect, we need some memory to emit it into. if (SGF.silConv.isSILIndirect(innerResult)) { @@ -2326,7 +2322,7 @@ ResultPlanner::planScalarFromIndirectResult(AbstractionPattern innerOrigType, assert(SGF.silConv.isSILIndirect(outerResult) == bool(optOuterResultAddr)); bool hasAbstractionDifference = - (innerResultAddr->getType().getSwiftRValueType() != outerResult.getType()); + (innerResultAddr->getType().getASTType() != outerResult.getType()); // The outer result can be indirect, and it doesn't necessarily have an // abstraction difference. Note that we should only end up in this path @@ -2659,8 +2655,7 @@ buildThunkSignature(SILGenFunction &SGF, if (openedExistential == nullptr) { auto genericSig = SGF.F.getLoweredFunctionType()->getGenericSignature(); genericEnv = SGF.F.getGenericEnvironment(); - auto subsArray = SGF.F.getForwardingSubstitutions(); - interfaceSubs = genericSig->getSubstitutionMap(subsArray); + interfaceSubs = SGF.F.getForwardingSubstitutionMap(); contextSubs = interfaceSubs; return genericSig; } @@ -2916,9 +2911,7 @@ static ManagedValue createThunk(SILGenFunction &SGF, CanSILFunctionType substFnType = thunkType; - SmallVector subs; if (auto genericSig = thunkType->getGenericSignature()) { - genericSig->getSubstitutions(interfaceSubs, subs); substFnType = thunkType->substGenericArgs(SGF.F.getModule(), interfaceSubs); } @@ -2928,7 +2921,7 @@ static ManagedValue createThunk(SILGenFunction &SGF, ManagedValue thunkedFn = SGF.B.createPartialApply(loc, thunkValue, SILType::getPrimitiveObjectType(substFnType), - subs, fn.ensurePlusOne(SGF, loc), + interfaceSubs, fn.ensurePlusOne(SGF, loc), SILType::getPrimitiveObjectType(toType)); if (!expectedType->isNoEscape()) { @@ -2937,8 +2930,8 @@ static ManagedValue createThunk(SILGenFunction &SGF, // Handle the escaping to noescape conversion. assert(expectedType->isNoEscape()); - return SGF.B.createConvertEscapeToNoEscape(loc, thunkedFn, - SILType::getPrimitiveObjectType(expectedType)); + return SGF.B.createConvertEscapeToNoEscape( + loc, thunkedFn, SILType::getPrimitiveObjectType(expectedType), false); } static CanSILFunctionType buildWithoutActuallyEscapingThunkType( @@ -3021,7 +3014,7 @@ SILGenFunction::createWithoutActuallyEscapingClosure( } CanSILFunctionType substFnType = thunkType->substGenericArgs( - F.getModule(), thunk->getForwardingSubstitutions()); + F.getModule(), thunk->getForwardingSubstitutionMap()); // Create it in our current function. auto thunkValue = B.createFunctionRef(loc, thunk); @@ -3030,7 +3023,7 @@ SILGenFunction::createWithoutActuallyEscapingClosure( SingleValueInstruction *thunkedFn = B.createPartialApply( loc, thunkValue, SILType::getPrimitiveObjectType(substFnType), - thunk->getForwardingSubstitutions(), + thunk->getForwardingSubstitutionMap(), noEscapeValue, SILType::getPrimitiveObjectType(escapingFnTy)); // We need to ensure the 'lifetime' of the trivial values context captures. As @@ -3045,8 +3038,7 @@ ManagedValue Transform::transformFunction(ManagedValue fn, CanAnyFunctionType inputSubstType, AbstractionPattern outputOrigType, CanAnyFunctionType outputSubstType, - const TypeLowering &expectedTL, - bool postponeToNoEscapeCleanup) { + const TypeLowering &expectedTL) { assert(fn.getType().isObject() && "expected input to emitTransformedFunctionValue to be loaded"); @@ -3105,8 +3097,7 @@ ManagedValue Transform::transformFunction(ManagedValue fn, } else if (newFnType != expectedFnType) { // Escaping to noescape conversion. SILType resTy = SILType::getPrimitiveObjectType(expectedFnType); - fn = SGF.B.createConvertEscapeToNoEscape(Loc, fn, resTy, - postponeToNoEscapeCleanup); + fn = SGF.B.createConvertEscapeToNoEscape(Loc, fn, resTy, false); } return fn; @@ -3202,12 +3193,11 @@ ManagedValue SILGenFunction::emitTransformedValue(SILLocation loc, ManagedValue v, CanType inputType, CanType outputType, - SGFContext ctxt, - bool postponeToNoEscapeCleanup) { + SGFContext ctxt) { return emitTransformedValue(loc, v, AbstractionPattern(inputType), inputType, AbstractionPattern(outputType), outputType, - ctxt, postponeToNoEscapeCleanup); + ctxt); } ManagedValue @@ -3216,14 +3206,12 @@ SILGenFunction::emitTransformedValue(SILLocation loc, ManagedValue v, CanType inputSubstType, AbstractionPattern outputOrigType, CanType outputSubstType, - SGFContext ctxt, - bool postponeToNoEscapeCleanup) { + SGFContext ctxt) { return Transform(*this, loc).transform(v, inputOrigType, inputSubstType, outputOrigType, - outputSubstType, ctxt, - postponeToNoEscapeCleanup); + outputSubstType, ctxt); } RValue @@ -3260,10 +3248,10 @@ SILGenFunction::emitVTableThunk(SILDeclRef derived, auto fTy = implFn->getLoweredFunctionType(); - SubstitutionList subs; + SubstitutionMap subs; if (auto *genericEnv = fd->getGenericEnvironment()) { F.setGenericEnvironment(genericEnv); - subs = getForwardingSubstitutions(); + subs = getForwardingSubstitutionMap(); fTy = fTy->substGenericArgs(SGM.M, subs); inputSubstType = cast( @@ -3409,7 +3397,7 @@ void SILGenFunction::emitProtocolWitness(AbstractionPattern reqtOrigTy, CanAnyFunctionType reqtSubstTy, SILDeclRef requirement, SILDeclRef witness, - SubstitutionList witnessSubs, + SubstitutionMap witnessSubs, IsFreeFunctionWitness_t isFree) { // FIXME: Disable checks that the protocol witness carries debug info. // Should we carry debug info for witnesses? @@ -3428,11 +3416,10 @@ void SILGenFunction::emitProtocolWitness(AbstractionPattern reqtOrigTy, // Get the type of the witness. auto witnessInfo = getConstantInfo(witness); CanAnyFunctionType witnessSubstTy = witnessInfo.LoweredType; - if (!witnessSubs.empty()) { - witnessSubstTy = cast( - cast(witnessSubstTy) - ->substGenericArgs(witnessSubs) - ->getCanonicalType()); + if (auto genericFnType = dyn_cast(witnessSubstTy)) { + witnessSubstTy = cast(genericFnType + ->substGenericArgs(witnessSubs) + ->getCanonicalType()); } CanType reqtSubstInputTy = F.mapTypeIntoContext(reqtSubstTy.getInput()) ->getCanonicalType(); @@ -3622,16 +3609,15 @@ SILGenFunction::emitCanonicalFunctionThunk(SILLocation loc, ManagedValue fn, CanSILFunctionType substFnTy = thunkTy; - SmallVector subs; if (auto genericSig = thunkTy->getGenericSignature()) { - genericSig->getSubstitutions(interfaceSubs, subs); substFnTy = thunkTy->substGenericArgs(F.getModule(), interfaceSubs); } // Create it in the current function. auto thunkValue = B.createFunctionRef(loc, thunk); ManagedValue thunkedFn = B.createPartialApply( - loc, thunkValue, SILType::getPrimitiveObjectType(substFnTy), subs, {fn}, + loc, thunkValue, SILType::getPrimitiveObjectType(substFnTy), + interfaceSubs, {fn}, SILType::getPrimitiveObjectType(canonicalTy)); if (canonicalTy->isNoEscape()) { auto &funcTL = getTypeLowering(canonicalTy); diff --git a/lib/SILGen/SILGenProlog.cpp b/lib/SILGen/SILGenProlog.cpp index 84f0a244ecd64..30d8d9c9faf08 100644 --- a/lib/SILGen/SILGenProlog.cpp +++ b/lib/SILGen/SILGenProlog.cpp @@ -397,7 +397,7 @@ static void emitCaptureArguments(SILGenFunction &SGF, // the captured value. auto type = getVarTypeInCaptureContext(); auto boxTy = SGF.SGM.Types.getContextBoxTypeForCapture(VD, - SGF.getLoweredType(type).getSwiftRValueType(), + SGF.getLoweredType(type).getASTType(), SGF.F.getGenericEnvironment(), /*mutable*/ true); SILValue box = SGF.F.begin()->createFunctionArgument( SILType::getPrimitiveObjectType(boxTy), VD); diff --git a/lib/SILGen/SILGenStmt.cpp b/lib/SILGen/SILGenStmt.cpp index a24236bfbd906..c9256d764d0f2 100644 --- a/lib/SILGen/SILGenStmt.cpp +++ b/lib/SILGen/SILGenStmt.cpp @@ -680,9 +680,6 @@ void StmtEmitter::visitDoCatchStmt(DoCatchStmt *S) { llvm::SaveAndRestore savedThrowDest(SGF.ThrowDest, throwDest); visit(S->getBody()); - // We emit the counter for exiting the do-block here, as we may not have a - // valid insertion point when falling out. - SGF.emitProfilerIncrement(S); } // Emit the catch clauses, but only if the body of the function diff --git a/lib/SILGen/SILGenThunk.cpp b/lib/SILGen/SILGenThunk.cpp index af23b762b75c7..955d60a775648 100644 --- a/lib/SILGen/SILGenThunk.cpp +++ b/lib/SILGen/SILGenThunk.cpp @@ -86,7 +86,7 @@ SILGenFunction::emitDynamicMethodRef(SILLocation loc, SILDeclRef constant, static ManagedValue getNextUncurryLevelRef(SILGenFunction &SGF, SILLocation loc, SILDeclRef thunk, ManagedValue selfArg, - SubstitutionList curriedSubs) { + SubstitutionMap curriedSubs) { auto *vd = thunk.getDecl(); // Reference the next uncurrying level of the function. @@ -122,11 +122,9 @@ static ManagedValue getNextUncurryLevelRef(SILGenFunction &SGF, SILLocation loc, == SILFunctionTypeRepresentation::WitnessMethod) { auto protocol = func->getDeclContext()->getAsProtocolOrProtocolExtensionContext(); - auto subMap = func->getGenericSignature() - ->getSubstitutionMap(curriedSubs); auto origSelfType = protocol->getSelfInterfaceType()->getCanonicalType(); - auto substSelfType = origSelfType.subst(subMap)->getCanonicalType(); - auto conformance = subMap.lookupConformance(origSelfType, protocol); + auto substSelfType = origSelfType.subst(curriedSubs)->getCanonicalType(); + auto conformance = curriedSubs.lookupConformance(origSelfType, protocol); auto result = SGF.B.createWitnessMethod(loc, substSelfType, *conformance, next, constantInfo.getSILType()); return ManagedValue::forUnmanaged(result); @@ -157,7 +155,7 @@ void SILGenFunction::emitCurryThunk(SILDeclRef thunk) { B.createInputFunctionArgument(getLoweredType(selfTy), SILLocation(vd)); // Forward substitutions. - auto subs = F.getForwardingSubstitutions(); + auto subs = F.getForwardingSubstitutionMap(); ManagedValue toFn = getNextUncurryLevelRef(*this, vd, thunk, selfArg, subs); diff --git a/lib/SILGen/SILGenType.cpp b/lib/SILGen/SILGenType.cpp index 38908c5b76198..f865c9c37343e 100644 --- a/lib/SILGen/SILGenType.cpp +++ b/lib/SILGen/SILGenType.cpp @@ -604,8 +604,7 @@ SILFunction *SILGenModule::emitProtocolWitness( // Mapping from the requirement's generic signature to the witness // thunk's generic signature. - auto reqtSubs = witness.getRequirementToSyntheticSubs(); - auto reqtSubMap = reqtOrigTy->getGenericSignature()->getSubstitutionMap(reqtSubs); + auto reqtSubMap = witness.getRequirementToSyntheticSubs(); // The generic environment for the witness thunk. auto *genericEnv = witness.getSyntheticEnvironment(); @@ -614,6 +613,20 @@ SILFunction *SILGenModule::emitProtocolWitness( auto input = reqtOrigTy->getInput().subst(reqtSubMap)->getCanonicalType(); auto result = reqtOrigTy->getResult().subst(reqtSubMap)->getCanonicalType(); + // If there's something to map to for the witness thunk, the conformance + // should be phrased in the same terms. This particularly applies to classes + // where a thunk for a method in a conformance like `extension Class: P where + // T: Q` will go from its native signature of `<τ_0_0 where τ_0_0: Q>` (with T + // canonicalised to τ_0_0), to `<τ_0_0, τ_1_0 where τ_0_0: Class<τ_1_0>, + // τ_1_0: Q>` (with T now represented by τ_1_0). Find the right conformance by + // looking for the conformance of 'Self'. + if (reqtSubMap) { + auto requirement = conformance.getRequirement(); + auto self = requirement->getProtocolSelfType()->getCanonicalType(); + + conformance = *reqtSubMap.lookupConformance(self, requirement); + } + CanAnyFunctionType reqtSubstTy; if (genericEnv) { auto *genericSig = genericEnv->getGenericSignature(); diff --git a/lib/SILGen/Scope.cpp b/lib/SILGen/Scope.cpp index 1fc5c9d1e5d2a..614e6a9e2770d 100644 --- a/lib/SILGen/Scope.cpp +++ b/lib/SILGen/Scope.cpp @@ -56,7 +56,7 @@ static void lifetimeExtendAddressOnlyRValueSubValues( assert(v->getType().isAddressOnly(SGF.getModule()) && "RValue invariants imply that all RValue subtypes that are " "addresses must be address only."); - auto boxTy = SILBoxType::get(v->getType().getSwiftRValueType()); + auto boxTy = SILBoxType::get(v->getType().getASTType()); SILValue box = SGF.B.createAllocBox(loc, boxTy); SILValue addr = SGF.B.createProjectBox(loc, box, 0); SGF.B.createCopyAddr(loc, v, addr, IsTake, IsInitialization); @@ -122,26 +122,6 @@ RValue Scope::popPreservingValue(RValue &&rv) { void Scope::popImpl() { SmallVector cleanupsToPropagateToOuterScope; - // Deactivate any postpone cleanups in the current scope. - if (currentlyActivePostponedCleanup) { - bool inScope; - do { - inScope = false; - auto &deferredCleanups = - currentlyActivePostponedCleanup->deferredCleanups; - if (deferredCleanups.empty()) - break; - auto &top = deferredCleanups.back(); - auto topCleanupDepth = top.first.getDepth(); - if (topCleanupDepth > depth.getDepth()) { - cleanups.forwardCleanup(top.first); - cleanupsToPropagateToOuterScope.push_back(top.second); - deferredCleanups.pop_back(); - inScope = true; - } - } while (inScope); - } - cleanups.stack.checkIterator(depth); cleanups.stack.checkIterator(cleanups.innermostScope); assert(cleanups.innermostScope == depth && "popping scopes out of order"); @@ -150,14 +130,4 @@ void Scope::popImpl() { cleanups.endScope(depth, loc); cleanups.stack.checkIterator(cleanups.innermostScope); cleanups.popTopDeadCleanups(cleanups.innermostScope); - - // Propagate the cleanup to the current parent scope. - for (auto valueToCleanup : cleanupsToPropagateToOuterScope) { - auto handle = cleanups.SGF.enterDestroyCleanup(valueToCleanup); - // Propagate cleanup to parent Scope. - if (currentlyActivePostponedCleanup->depth.getDepth() < depth.getDepth()) { - currentlyActivePostponedCleanup->deferredCleanups.push_back( - std::make_pair(handle, valueToCleanup)); - } - } } diff --git a/lib/SILGen/Scope.h b/lib/SILGen/Scope.h index 8b67e71b45119..89ce3c822430f 100644 --- a/lib/SILGen/Scope.h +++ b/lib/SILGen/Scope.h @@ -31,14 +31,11 @@ class LLVM_LIBRARY_VISIBILITY Scope { CleanupsDepth depth; CleanupsDepth savedInnermostScope; CleanupLocation loc; - PostponedCleanup *currentlyActivePostponedCleanup; public: explicit Scope(CleanupManager &cleanups, CleanupLocation loc) : cleanups(cleanups), depth(cleanups.getCleanupsDepth()), - savedInnermostScope(cleanups.innermostScope), loc(loc), - currentlyActivePostponedCleanup( - cleanups.SGF.CurrentlyActivePostponedCleanup) { + savedInnermostScope(cleanups.innermostScope), loc(loc) { assert(depth.isValid()); cleanups.stack.checkIterator(cleanups.innermostScope); cleanups.innermostScope = depth; diff --git a/lib/SILGen/SpecializedEmitter.h b/lib/SILGen/SpecializedEmitter.h index c6ceabe58d3d2..c249b6a0aab76 100644 --- a/lib/SILGen/SpecializedEmitter.h +++ b/lib/SILGen/SpecializedEmitter.h @@ -26,7 +26,6 @@ class Expr; struct SILDeclRef; class SILLocation; class SILModule; -class Substitution; namespace Lowering { class ManagedValue; @@ -41,7 +40,7 @@ class SpecializedEmitter { /// have already been emitted. using EarlyEmitter = ManagedValue (SILGenFunction &, SILLocation, - SubstitutionList, + SubstitutionMap, Expr *argument, SGFContext); @@ -49,7 +48,7 @@ class SpecializedEmitter { /// have already been emitted. using LateEmitter = ManagedValue (SILGenFunction &, SILLocation, - SubstitutionList, + SubstitutionMap, ArrayRef, SGFContext); diff --git a/lib/SILOptimizer/ARC/RCStateTransition.cpp b/lib/SILOptimizer/ARC/RCStateTransition.cpp index 23a2c1d9a7287..334628dbd5d57 100644 --- a/lib/SILOptimizer/ARC/RCStateTransition.cpp +++ b/lib/SILOptimizer/ARC/RCStateTransition.cpp @@ -78,14 +78,15 @@ RCStateTransitionKind swift::getRCStateTransitionKind(SILNode *N) { return RCStateTransitionKind::Unknown; } + // Alloc* are always allocating new classes so they are introducing new + // values at +1. case SILNodeKind::AllocRefInst: case SILNodeKind::AllocRefDynamicInst: - // AllocRef* are always allocating new classes so they are introducing new - // values at +1. + case SILNodeKind::AllocBoxInst: return RCStateTransitionKind::StrongEntrance; - case SILNodeKind::AllocBoxInst: - // AllocBox introduce their container result at +1. + case SILNodeKind::PartialApplyInst: + // Partial apply boxes are introduced at +1. return RCStateTransitionKind::StrongEntrance; default: diff --git a/lib/SILOptimizer/ARC/RCStateTransitionVisitors.cpp b/lib/SILOptimizer/ARC/RCStateTransitionVisitors.cpp index ee79d35ceb956..29e3685f8d35d 100644 --- a/lib/SILOptimizer/ARC/RCStateTransitionVisitors.cpp +++ b/lib/SILOptimizer/ARC/RCStateTransitionVisitors.cpp @@ -305,6 +305,21 @@ visitStrongEntranceApply(ApplyInst *AI) { return DataflowResult(AI); } +template +typename TopDownDataflowRCStateVisitor::DataflowResult +TopDownDataflowRCStateVisitor::visitStrongEntrancePartialApply( + PartialApplyInst *PAI) { + DEBUG(llvm::dbgs() << "VISITING ENTRANCE PARTIAL APPLY: " << *PAI); + + // Rreturn a dataflow result containing a +1. + DEBUG(llvm::dbgs() << " Initializing state.\n"); + + auto &State = DataflowState.getTopDownRefCountState(PAI); + State.initWithEntranceInst(SetFactory.get(PAI), PAI); + + return DataflowResult(PAI); +} + template typename TopDownDataflowRCStateVisitor::DataflowResult TopDownDataflowRCStateVisitor:: @@ -356,6 +371,9 @@ visitStrongEntrance(SILNode *N) { if (auto *ABI = dyn_cast(N)) return visitStrongAllocBox(ABI); + if (auto *PAI = dyn_cast(N)) + return visitStrongEntrancePartialApply(PAI); + return DataflowResult(); } diff --git a/lib/SILOptimizer/ARC/RCStateTransitionVisitors.h b/lib/SILOptimizer/ARC/RCStateTransitionVisitors.h index f51f518441d71..8daa9cda8055c 100644 --- a/lib/SILOptimizer/ARC/RCStateTransitionVisitors.h +++ b/lib/SILOptimizer/ARC/RCStateTransitionVisitors.h @@ -170,6 +170,7 @@ class TopDownDataflowRCStateVisitor private: DataflowResult visitStrongEntranceApply(ApplyInst *AI); + DataflowResult visitStrongEntrancePartialApply(PartialApplyInst *PAI); DataflowResult visitStrongEntranceArgument(SILFunctionArgument *Arg); DataflowResult visitStrongEntranceAllocRef(AllocRefInst *ARI); DataflowResult visitStrongEntranceAllocRefDynamic(AllocRefDynamicInst *ARI); diff --git a/lib/SILOptimizer/Analysis/ARCAnalysis.cpp b/lib/SILOptimizer/Analysis/ARCAnalysis.cpp index 0a984848357ca..c9a3ea2810a85 100644 --- a/lib/SILOptimizer/Analysis/ARCAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/ARCAnalysis.cpp @@ -36,12 +36,14 @@ using BasicBlockRetainValue = std::pair; //===----------------------------------------------------------------------===// bool swift::isRetainInstruction(SILInstruction *I) { - return isa(I) || isa(I); + return isa(I) || isa(I) || + isa(I); } bool swift::isReleaseInstruction(SILInstruction *I) { - return isa(I) || isa(I); + return isa(I) || isa(I) || + isa(I); } //===----------------------------------------------------------------------===// diff --git a/lib/SILOptimizer/Analysis/AccessSummaryAnalysis.cpp b/lib/SILOptimizer/Analysis/AccessSummaryAnalysis.cpp index d5d6a6411c46d..5e46d55bda515 100644 --- a/lib/SILOptimizer/Analysis/AccessSummaryAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/AccessSummaryAnalysis.cpp @@ -15,6 +15,7 @@ #include "swift/SILOptimizer/Analysis/AccessSummaryAnalysis.h" #include "swift/SILOptimizer/Analysis/FunctionOrder.h" #include "swift/SILOptimizer/PassManager/PassManager.h" +#include "swift/SIL/DebugUtils.h" using namespace swift; @@ -150,6 +151,37 @@ static bool hasExpectedUsesOfNoEscapePartialApply(Operand *partialApplyUse) { return llvm::all_of(cast(user)->getUses(), hasExpectedUsesOfNoEscapePartialApply); + // Look through mark_dependence. + case SILInstructionKind::MarkDependenceInst: + return llvm::all_of(cast(user)->getUses(), + hasExpectedUsesOfNoEscapePartialApply); + + case SILInstructionKind::CopyBlockWithoutEscapingInst: + return partialApplyUse->getOperandNumber() == + CopyBlockWithoutEscapingInst::Closure; + + // A copy_value that is only used by the store to a block storage is fine. + // It is part of the pattern we emit for verifying that a noescape closure + // passed to objc has not escaped. + // %4 = convert_escape_to_noescape [not_guaranteed] %3 : + // $@callee_guaranteed () -> () to $@noescape @callee_guaranteed () -> () + // %5 = function_ref @withoutEscapingThunk + // %6 = partial_apply [callee_guaranteed] %5(%4) : + // $@convention(thin) (@noescape @callee_guaranteed () -> ()) -> () + // %7 = mark_dependence %6 : $@callee_guaranteed () -> () on %4 : + // $@noescape @callee_guaranteed () -> () + // %8 = copy_value %7 : $@callee_guaranteed () -> () + // %9 = alloc_stack $@block_storage @callee_guaranteed () -> () + // %10 = project_block_storage %9 : + // $*@block_storage @callee_guaranteed () -> () + // store %8 to [init] %10 : $*@callee_guaranteed () -> () + // %13 = init_block_storage_header %9 : + // $*@block_storage @callee_guaranteed () -> (), + // invoke %12 + // %14 = copy_block_without_escaping %13 : $() -> () withoutEscaping %7 + case SILInstructionKind::CopyValueInst: + return isa(getSingleNonDebugUser(cast(user))); + // End borrow is always ok. case SILInstructionKind::EndBorrowInst: return true; diff --git a/lib/SILOptimizer/Analysis/AccessedStorageAnalysis.cpp b/lib/SILOptimizer/Analysis/AccessedStorageAnalysis.cpp new file mode 100644 index 0000000000000..494828893fbff --- /dev/null +++ b/lib/SILOptimizer/Analysis/AccessedStorageAnalysis.cpp @@ -0,0 +1,350 @@ +//===--- AccessedStorageAnalysis.cpp - Accessed Storage Analysis ---------===// +// +// 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-sea" + +#include "swift/SILOptimizer/Analysis/AccessedStorageAnalysis.h" +#include "swift/SILOptimizer/Analysis/BasicCalleeAnalysis.h" +#include "swift/SILOptimizer/Analysis/FunctionOrder.h" +#include "swift/SILOptimizer/PassManager/PassManager.h" + +using namespace swift; + +// ----------------------------------------------------------------------------- +// MARK: Accessing the results. +// ----------------------------------------------------------------------------- + +bool FunctionAccessedStorage::hasNoNestedConflict( + const AccessedStorage &otherStorage) const { + assert(otherStorage.isUniquelyIdentified()); + assert(!hasUnidentifiedAccess()); + + return getStorageAccessInfo(otherStorage).hasNoNestedConflict(); +} + +bool FunctionAccessedStorage::mayConflictWith( + SILAccessKind otherAccessKind, const AccessedStorage &otherStorage) const { + if (hasUnidentifiedAccess() + && accessKindMayConflict(otherAccessKind, + unidentifiedAccess.getValue())) { + return true; + } + for (auto &storageAccess : storageAccessSet) { + assert(storageAccess && "FunctionAccessedStorage mapped invalid storage."); + + if (!accessKindMayConflict(otherAccessKind, storageAccess.getAccessKind())) + continue; + + if (!otherStorage.isDistinctFrom(storageAccess)) + return true; + } + return false; +} + +StorageAccessInfo FunctionAccessedStorage::getStorageAccessInfo( + const AccessedStorage &otherStorage) const { + // Construct a fake StorageAccessInfo to do a hash lookup for the real + // StorageAccessInfo. The DenseSet key is limited to the AccessedStorage base + // class members. + StorageAccessInfo storageKey(otherStorage, SILAccessKind::Read, false); + auto iter = storageAccessSet.find(storageKey); + assert(iter != storageAccessSet.end()); + return *iter; +} + +// ----------------------------------------------------------------------------- +// MARK: Constructing the results. +// ----------------------------------------------------------------------------- + +static bool updateAccessKind(SILAccessKind &LHS, SILAccessKind RHS) { + bool changed = false; + // Assume we don't track Init/Deinit. + if (LHS == SILAccessKind::Read && RHS == SILAccessKind::Modify) { + LHS = RHS; + changed = true; + } + return changed; +} + +static bool updateOptionalAccessKind(Optional &LHS, + Optional RHS) { + if (RHS == None) + return false; + + if (LHS == None) { + LHS = RHS; + return true; + } + return updateAccessKind(LHS.getValue(), RHS.getValue()); +} + +bool StorageAccessInfo::mergeFrom(const StorageAccessInfo &RHS) { + bool changed = false; + SILAccessKind accessKind = getAccessKind(); + assert(accessKind == SILAccessKind::Read + || accessKind == SILAccessKind::Modify && "uninitialized info"); + if (updateAccessKind(accessKind, RHS.getAccessKind())) { + setAccessKind(accessKind); + changed = true; + } + if (hasNoNestedConflict() && !RHS.hasNoNestedConflict()) { + setNoNestedConflict(false); + changed = true; + } + return changed; +} + +bool FunctionAccessedStorage::summarizeFunction(SILFunction *F) { + assert(storageAccessSet.empty() && "expected uninitialized results."); + + if (F->isDefinition()) + return false; + + // If the function definition is unavailable, set unidentifiedAccess to a + // conservative value, since analyzeInstruction will never be called. + // + // If FunctionSideEffects can be summarized, use that information. + FunctionSideEffects functionSideEffects; + if (!functionSideEffects.summarizeFunction(F)) { + setWorstEffects(); + // May as well consider this a successful summary since there are no + // instructions to visit anyway. + return true; + } + bool mayRead = functionSideEffects.getGlobalEffects().mayRead(); + bool mayWrite = functionSideEffects.getGlobalEffects().mayWrite(); + for (auto ¶mEffects : functionSideEffects.getParameterEffects()) { + mayRead |= paramEffects.mayRead(); + mayWrite |= paramEffects.mayWrite(); + } + if (mayWrite) + unidentifiedAccess = SILAccessKind::Modify; + else if (mayRead) + unidentifiedAccess = SILAccessKind::Read; + + // If function side effects is "readnone" then this result will have an empty + // storageAccessSet and unidentifiedAccess == None. + return true; +} + +bool FunctionAccessedStorage::updateUnidentifiedAccess( + SILAccessKind accessKind) { + if (unidentifiedAccess == None) { + unidentifiedAccess = accessKind; + return true; + } + return updateAccessKind(unidentifiedAccess.getValue(), accessKind); +} + +// Merge the given FunctionAccessedStorage in `other` into this +// FunctionAccessedStorage. Use the given `transformStorage` to map `other` +// AccessedStorage into this context. If `other` is from a callee, argument +// substitution will be performed if possible. However, there's no guarantee +// that the merged access values will belong to this function. +// +// Note that we may have `this` == `other` for self-recursion. We still need to +// propagate and merge in that case in case arguments are recursively dependent. +bool FunctionAccessedStorage::mergeAccesses( + const FunctionAccessedStorage &other, + std::function + transformStorage) { + + // Insertion in DenseMap invalidates the iterator in the rare case of + // self-recursion (`this` == `other`) that passes accessed storage though an + // argument. Rather than complicate the code, make a temporary copy of the + // AccessedStorage. + // + // Also note that the storageAccessIndex from otherStorage is relative to its + // original context and should not be copied into this context. + SmallVector otherStorageAccesses; + otherStorageAccesses.reserve(other.storageAccessSet.size()); + otherStorageAccesses.append(other.storageAccessSet.begin(), + other.storageAccessSet.end()); + + bool changed = false; + for (auto &rawStorageInfo : otherStorageAccesses) { + const StorageAccessInfo &otherStorageInfo = + transformStorage(rawStorageInfo); + // transformStorage() returns invalid storage object for local storage + // that should not be merged with the caller. + if (!otherStorageInfo) + continue; + + if (otherStorageInfo.getKind() == AccessedStorage::Unidentified) { + changed |= updateUnidentifiedAccess(otherStorageInfo.getAccessKind()); + continue; + } + // Attempt to add identified AccessedStorage to this map. + auto result = insertStorageAccess(otherStorageInfo); + if (result.second) { + // A new AccessedStorage key was added to this map. + changed = true; + continue; + } + // Merge StorageAccessInfo into already-mapped AccessedStorage. + changed |= result.first->mergeFrom(otherStorageInfo); + } + if (other.unidentifiedAccess != None) + changed |= updateUnidentifiedAccess(other.unidentifiedAccess.getValue()); + + return changed; +} + +bool FunctionAccessedStorage::mergeFrom(const FunctionAccessedStorage &other) { + // Merge accesses from other. Both `this` and `other` are either from the same + // function or are both callees of the same call site, so their parameters + // indices coincide. transformStorage is the identity function. + return mergeAccesses(other, [](const StorageAccessInfo &s) { return s; }); +} + +/// Returns the argument of the full apply or partial apply corresponding to the +/// callee's parameter index, or returns an invalid SILValue if the applied +/// closure cannot be found. This walks up the apply chain starting at the given +/// `fullApply` to find the applied argument. +static SILValue getCallerArg(FullApplySite fullApply, unsigned paramIndex) { + if (paramIndex < fullApply.getNumArguments()) + return fullApply.getArgument(paramIndex); + + SILValue callee = fullApply.getCalleeOrigin(); + auto *PAI = dyn_cast(callee); + if (!PAI) + return SILValue(); + + unsigned appliedIndex = + paramIndex - ApplySite(PAI).getCalleeArgIndexOfFirstAppliedArg(); + if (appliedIndex < PAI->getNumArguments()) + return PAI->getArgument(appliedIndex); + + // This must be a chain of partial_applies. We don't expect this in practice, + // so handle it conservatively. + return SILValue(); +} + +/// Transform AccessedStorage from a callee into the caller context. If this is +/// uniquely identified local storage, then return an invalid storage object. +/// +/// For correctness, AccessEnforcementOpts relies on all Argument access to +/// either be mapped into the caller's context or marked as an unidentified +/// access at the call site. +/// +/// Note: This does *not* map the storage index into the caller function's index +/// range. (When the storage value doesn't need to be remapped, it returns the +/// original storage value.) It's simpler to set the storage index later when it +/// is actually added to the function's storageAccessSet. +static StorageAccessInfo +transformCalleeStorage(const StorageAccessInfo &storage, + FullApplySite fullApply) { + switch (storage.getKind()) { + case AccessedStorage::Box: + case AccessedStorage::Stack: + // Do not merge local storage. + return StorageAccessInfo(AccessedStorage(), storage); + case AccessedStorage::Global: + // Global accesses is universal. + return storage; + case AccessedStorage::Class: { + // If the object's value is an argument, translate it into a value on the + // caller side. + SILValue obj = storage.getObjectProjection().getObject(); + if (auto *arg = dyn_cast(obj)) { + SILValue argVal = getCallerArg(fullApply, arg->getIndex()); + if (argVal) { + auto &proj = storage.getObjectProjection().getProjection(); + // Remap the argument source value and inherit the old storage info. + return StorageAccessInfo(AccessedStorage(argVal, proj), storage); + } + } + // Otherwise, continue to reference the value in the callee because we don't + // have any better placeholder for a callee-defined object. + return storage; + } + case AccessedStorage::Argument: { + // Transitively search for the storage base in the caller. + SILValue argVal = getCallerArg(fullApply, storage.getParamIndex()); + if (argVal) { + // Remap the argument source value and inherit the old storage info. + return StorageAccessInfo(findAccessedStorageNonNested(argVal), storage); + } + // If the argument can't be transformed, demote it to an unidentified + // access. + return StorageAccessInfo( + AccessedStorage(storage.getValue(), AccessedStorage::Unidentified), + storage); + } + case AccessedStorage::Nested: + llvm_unreachable("Unexpected nested access"); + case AccessedStorage::Unidentified: + // For unidentified storage, continue to reference the value in the callee + // because we don't have any better placeholder for a callee-defined object. + return storage; + } +} + +bool FunctionAccessedStorage::mergeFromApply( + const FunctionAccessedStorage &calleeAccess, FullApplySite fullApply) { + // Merge accesses from calleeAccess. Transform any Argument type + // AccessedStorage into the caller context to be added to `this` storage map. + return mergeAccesses(calleeAccess, [&fullApply](const StorageAccessInfo &s) { + return transformCalleeStorage(s, fullApply); + }); +} + +template +void FunctionAccessedStorage::visitBeginAccess(B *beginAccess) { + if (beginAccess->getEnforcement() != SILAccessEnforcement::Dynamic) + return; + + const AccessedStorage &storage = + findAccessedStorageNonNested(beginAccess->getSource()); + + if (storage.getKind() == AccessedStorage::Unidentified) { + // This also catches invalid storage. + updateOptionalAccessKind(unidentifiedAccess, beginAccess->getAccessKind()); + return; + } + StorageAccessInfo storageAccess(storage, beginAccess); + auto result = insertStorageAccess(storageAccess); + if (!result.second) + result.first->mergeFrom(storageAccess); +} + +void FunctionAccessedStorage::analyzeInstruction(SILInstruction *I) { + if (auto *BAI = dyn_cast(I)) + visitBeginAccess(BAI); + else if (auto *BUAI = dyn_cast(I)) + visitBeginAccess(BUAI); +} + +void StorageAccessInfo::print(raw_ostream &os) const { + os << " [" << getSILAccessKindName(getAccessKind()) << "] "; + if (hasNoNestedConflict()) + os << "[no_nested_conflict] "; + AccessedStorage::print(os); +} + +void StorageAccessInfo::dump() const { print(llvm::dbgs()); } + +void FunctionAccessedStorage::print(raw_ostream &os) const { + for (auto &storageAccess : storageAccessSet) + storageAccess.print(os); + + if (unidentifiedAccess != None) { + os << " unidentified accesses: " + << getSILAccessKindName(unidentifiedAccess.getValue()) << "\n"; + } +} + +void FunctionAccessedStorage::dump() const { print(llvm::dbgs()); } + +SILAnalysis *swift::createAccessedStorageAnalysis(SILModule *) { + return new AccessedStorageAnalysis(); +} diff --git a/lib/SILOptimizer/Analysis/AliasAnalysis.cpp b/lib/SILOptimizer/Analysis/AliasAnalysis.cpp index 5b2d77fce6bb9..385b0162db593 100644 --- a/lib/SILOptimizer/Analysis/AliasAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/AliasAnalysis.cpp @@ -652,8 +652,8 @@ bool AliasAnalysis::canApplyDecrementRefCount(FullApplySite FAS, SILValue Ptr) { if (!EA->canEscapeTo(Ptr, FAS)) return false; - SideEffectAnalysis::FunctionEffects ApplyEffects; - SEA->getEffects(ApplyEffects, FAS); + FunctionSideEffects ApplyEffects; + SEA->getCalleeEffects(ApplyEffects, FAS); auto &GlobalEffects = ApplyEffects.getGlobalEffects(); if (ApplyEffects.mayReadRC() || GlobalEffects.mayRelease()) diff --git a/lib/SILOptimizer/Analysis/ArraySemantic.cpp b/lib/SILOptimizer/Analysis/ArraySemantic.cpp index 518cec273b146..a8c0b13105c70 100644 --- a/lib/SILOptimizer/Analysis/ArraySemantic.cpp +++ b/lib/SILOptimizer/Analysis/ArraySemantic.cpp @@ -717,7 +717,7 @@ bool swift::ArraySemanticsCall::replaceByValue(SILValue V) { bool swift::ArraySemanticsCall::replaceByAppendingValues( SILModule &M, SILFunction *AppendFn, SILFunction *ReserveFn, - const SmallVectorImpl &Vals, ArrayRef Subs) { + const SmallVectorImpl &Vals, SubstitutionMap Subs) { assert(getKind() == ArrayCallKind::kAppendContentsOf && "Must be an append_contentsOf call"); assert(AppendFn && "Must provide an append SILFunction"); diff --git a/lib/SILOptimizer/Analysis/BasicCalleeAnalysis.cpp b/lib/SILOptimizer/Analysis/BasicCalleeAnalysis.cpp index c1af007d51407..78402fb32c95b 100644 --- a/lib/SILOptimizer/Analysis/BasicCalleeAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/BasicCalleeAnalysis.cpp @@ -238,10 +238,6 @@ CalleeList CalleeCache::getCalleeListForCalleeKind(SILValue Callee) const { "Unhandled method instruction in callee determination!"); return CalleeList(); - case ValueKind::ThinToThickFunctionInst: - return getCalleeListForCalleeKind( - cast(Callee)->getOperand()); - case ValueKind::FunctionRefInst: return CalleeList(cast(Callee)->getReferencedFunction()); @@ -265,7 +261,7 @@ CalleeList CalleeCache::getCalleeListForCalleeKind(SILValue Callee) const { // Return the list of functions that can be called via the given apply // site. CalleeList CalleeCache::getCalleeList(FullApplySite FAS) const { - return getCalleeListForCalleeKind(FAS.getCallee()); + return getCalleeListForCalleeKind(FAS.getCalleeOrigin()); } // Return the list of functions that can be called via the given instruction. diff --git a/lib/SILOptimizer/Analysis/CMakeLists.txt b/lib/SILOptimizer/Analysis/CMakeLists.txt index b6f4fc42caa8d..825bc0d679969 100644 --- a/lib/SILOptimizer/Analysis/CMakeLists.txt +++ b/lib/SILOptimizer/Analysis/CMakeLists.txt @@ -1,6 +1,7 @@ set(ANALYSIS_SOURCES Analysis/ARCAnalysis.cpp Analysis/AccessSummaryAnalysis.cpp + Analysis/AccessedStorageAnalysis.cpp Analysis/AliasAnalysis.cpp Analysis/Analysis.cpp Analysis/ArraySemantic.cpp diff --git a/lib/SILOptimizer/Analysis/ClosureScope.cpp b/lib/SILOptimizer/Analysis/ClosureScope.cpp index 9d33e48091351..562b9a911c046 100644 --- a/lib/SILOptimizer/Analysis/ClosureScope.cpp +++ b/lib/SILOptimizer/Analysis/ClosureScope.cpp @@ -167,7 +167,7 @@ SILAnalysis *createClosureScopeAnalysis(SILModule *M) { } void TopDownClosureFunctionOrder::visitFunctions( - std::function visitor) { + llvm::function_ref visitor) { auto markVisited = [&](SILFunction *F) { bool visitOnce = visited.insert(F).second; assert(visitOnce); diff --git a/lib/SILOptimizer/Analysis/DestructorAnalysis.cpp b/lib/SILOptimizer/Analysis/DestructorAnalysis.cpp index 0033f5a676aea..dd074e69254dc 100644 --- a/lib/SILOptimizer/Analysis/DestructorAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/DestructorAnalysis.cpp @@ -27,10 +27,10 @@ using namespace swift; /// * is a value type that implements the _DestructorSafeContainer protocol and /// whose type parameters are safe types T1...Tn. bool DestructorAnalysis::mayStoreToMemoryOnDestruction(SILType T) { - bool IsSafe = isSafeType(T.getSwiftRValueType()); + bool IsSafe = isSafeType(T.getASTType()); DEBUG(llvm::dbgs() << " DestructorAnalysis::mayStoreToMemoryOnDestruction is" << (IsSafe ? " false: " : " true: ")); - DEBUG(T.getSwiftRValueType()->print(llvm::errs())); + DEBUG(T.getASTType()->print(llvm::errs())); DEBUG(llvm::errs() << "\n"); return !IsSafe; } diff --git a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp index 15bf1eceab5fa..14b48be47b298 100644 --- a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp @@ -1035,7 +1035,7 @@ static bool mayContainReference(SILType Ty, SILModule *Mod) { if (Ty.hasReferenceSemantics()) return true; - if (Ty.getSwiftRValueType() == Mod->getASTContext().TheRawPointerType) + if (Ty.getASTType() == Mod->getASTContext().TheRawPointerType) return true; if (auto *Str = Ty.getStructOrBoundGenericStruct()) { @@ -1824,7 +1824,29 @@ bool EscapeAnalysis::canEscapeToUsePoint(SILValue V, SILNode *UsePoint, return true; // No hidden escapes: check if the Node is reachable from the UsePoint. - return ConGraph->isUsePoint(UsePoint, Node); + // Check if the object itself can escape to the called function. + if (ConGraph->isUsePoint(UsePoint, Node)) + return true; + + assert(isPointer(V) && "should not have a node for a non-pointer"); + + // Check if the object "content" can escape to the called function. + // This will catch cases where V is a reference and a pointer to a stored + // property escapes. + // It's also important in case of a pointer assignment, e.g. + // V = V1 + // apply(V1) + // In this case the apply is only a use-point for V1 and V1's content node. + // As V1's content node is the same as V's content node, we also make the + // check for the content node. + CGNode *ContentNode = ConGraph->getContentNode(Node); + if (ContentNode->escapesInsideFunction(false)) + return true; + + if (ConGraph->isUsePoint(UsePoint, ContentNode)) + return true; + + return false; } bool EscapeAnalysis::canEscapeTo(SILValue V, FullApplySite FAS) { @@ -1840,46 +1862,6 @@ static bool hasReferenceSemantics(SILType T) { return T.isObject() && T.hasReferenceSemantics(); } -bool EscapeAnalysis::canObjectOrContentEscapeTo(SILValue V, FullApplySite FAS) { - // If it's not a local object we don't know anything about the value. - if (!pointsToLocalObject(V)) - return true; - - auto *ConGraph = getConnectionGraph(FAS.getFunction()); - CGNode *Node = ConGraph->getNodeOrNull(V, this); - if (!Node) - return true; - - // First check if there are escape paths which we don't explicitly see - // in the graph. - if (Node->escapesInsideFunction(isNotAliasingArgument(V))) - return true; - - // Check if the object itself can escape to the called function. - SILInstruction *UsePoint = FAS.getInstruction(); - if (ConGraph->isUsePoint(UsePoint, Node)) - return true; - - if (isPointer(V)) { - // Check if the object "content" can escape to the called function. - // This will catch cases where V is a reference and a pointer to a stored - // property escapes. - // It's also important in case of a pointer assignment, e.g. - // V = V1 - // apply(V1) - // In this case the apply is only a use-point for V1 and V1's content node. - // As V1's content node is the same as V's content node, we also make the - // check for the content node. - CGNode *ContentNode = ConGraph->getContentNode(Node); - if (ContentNode->escapesInsideFunction(false)) - return true; - - if (ConGraph->isUsePoint(UsePoint, ContentNode)) - return true; - } - return false; -} - bool EscapeAnalysis::canEscapeTo(SILValue V, RefCountingInst *RI) { // If it's not a local object we don't know anything about the value. if (!pointsToLocalObject(V)) diff --git a/lib/SILOptimizer/Analysis/MemoryBehavior.cpp b/lib/SILOptimizer/Analysis/MemoryBehavior.cpp index 527fb9a1dc881..0b255a699818b 100644 --- a/lib/SILOptimizer/Analysis/MemoryBehavior.cpp +++ b/lib/SILOptimizer/Analysis/MemoryBehavior.cpp @@ -227,7 +227,7 @@ MemBehavior MemoryBehaviorVisitor::visitBuiltinInst(BuiltinInst *BI) { MemBehavior MemoryBehaviorVisitor::visitTryApplyInst(TryApplyInst *AI) { MemBehavior Behavior = MemBehavior::MayHaveSideEffects; // Ask escape analysis. - if (!EA->canObjectOrContentEscapeTo(V, AI)) + if (!EA->canEscapeTo(V, AI)) Behavior = MemBehavior::None; // Otherwise be conservative and return that we may have side effects. @@ -237,8 +237,8 @@ MemBehavior MemoryBehaviorVisitor::visitTryApplyInst(TryApplyInst *AI) { MemBehavior MemoryBehaviorVisitor::visitApplyInst(ApplyInst *AI) { - SideEffectAnalysis::FunctionEffects ApplyEffects; - SEA->getEffects(ApplyEffects, AI); + FunctionSideEffects ApplyEffects; + SEA->getCalleeEffects(ApplyEffects, AI); MemBehavior Behavior = MemBehavior::None; @@ -290,7 +290,7 @@ MemBehavior MemoryBehaviorVisitor::visitApplyInst(ApplyInst *AI) { Behavior = MemBehavior::MayRead; // Ask escape analysis. - if (!EA->canObjectOrContentEscapeTo(V, AI)) + if (!EA->canEscapeTo(V, AI)) Behavior = MemBehavior::None; } DEBUG(llvm::dbgs() << " Found apply, returning " << Behavior << '\n'); diff --git a/lib/SILOptimizer/Analysis/RCIdentityAnalysis.cpp b/lib/SILOptimizer/Analysis/RCIdentityAnalysis.cpp index 978d163ca2dfc..a6b3b06cd3c52 100644 --- a/lib/SILOptimizer/Analysis/RCIdentityAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/RCIdentityAnalysis.cpp @@ -497,19 +497,37 @@ static bool isNonOverlappingTrivialAccess(SILValue value) { return false; } +void RCIdentityFunctionInfo::getRCUsers( + SILValue V, llvm::SmallVectorImpl &Users) { + // We assume that Users is empty. + assert(Users.empty() && "Expected an empty out variable."); + + // First grab our RC uses. + llvm::SmallVector TmpUsers; + getRCUses(V, TmpUsers); + + // Then map our operands out of TmpUsers into Users. + transform(TmpUsers, std::back_inserter(Users), + [](Operand *Op) { return Op->getUser(); }); + + // Finally sort/unique our users array. + sortUnique(Users); +} + /// Return all recursive users of V, looking through users which propagate /// RCIdentity. *NOTE* This ignores obvious ARC escapes where the a potential /// user of the RC is not managed by ARC. /// /// We only use the instruction analysis here. -void RCIdentityFunctionInfo::getRCUsers( - SILValue InputValue, llvm::SmallVectorImpl &Users) { +void RCIdentityFunctionInfo::getRCUses(SILValue InputValue, + llvm::SmallVectorImpl &Uses) { + // Add V to the worklist. llvm::SmallVector Worklist; Worklist.push_back(InputValue); - // A set used to ensure we only visit users once. - llvm::SmallPtrSet VisitedInsts; + // A set used to ensure we only visit uses once. + llvm::SmallPtrSet VisitedOps; // Then until we finish the worklist... while (!Worklist.empty()) { @@ -518,34 +536,34 @@ void RCIdentityFunctionInfo::getRCUsers( // For each user of V... for (auto *Op : V->getUses()) { - SILInstruction *User = Op->getUser(); - // If we have already visited this user, continue. - if (!VisitedInsts.insert(User).second) + if (!VisitedOps.insert(Op).second) continue; - for (auto value : User->getResults()) { + auto *User = Op->getUser(); + + if (auto *SVI = dyn_cast(User)) { // Otherwise attempt to strip off one layer of RC identical instructions // from User. - SILValue StrippedRCID = stripRCIdentityPreservingInsts(value); - - // If StrippedRCID is not V, then we know that User's result is - // conservatively not RCIdentical to V. - if (StrippedRCID != V) { - // If the user is extracting a trivial field of an aggregate structure - // that does not overlap with the ref counted part of the aggregate, we - // can ignore it. - if (isNonOverlappingTrivialAccess(value)) - continue; - - // Otherwise, it is an RC user that our user wants. - Users.push_back(User); + SILValue StrippedRCID = stripRCIdentityPreservingInsts(SVI); + + // If the User's result has the same RC identity as its operand, V, then + // it must still be RC identical to InputValue, so transitively search + // for more users. + if (StrippedRCID == V) { + Worklist.push_back(SILValue(SVI)); continue; } - // Otherwise, add the result to our list to continue searching. - Worklist.push_back(value); + // If the user is extracting a trivial field of an aggregate structure + // that does not overlap with the ref counted part of the aggregate, we + // can ignore it. + if (isNonOverlappingTrivialAccess(SVI)) + continue; } + + // Otherwise, stop searching and report this RC operand. + Uses.push_back(Op); } } } diff --git a/lib/SILOptimizer/Analysis/SideEffectAnalysis.cpp b/lib/SILOptimizer/Analysis/SideEffectAnalysis.cpp index 28fb7d0094180..1952a4c79986f 100644 --- a/lib/SILOptimizer/Analysis/SideEffectAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/SideEffectAnalysis.cpp @@ -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 - 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 @@ -12,19 +12,192 @@ #define DEBUG_TYPE "sil-sea" #include "swift/SILOptimizer/Analysis/SideEffectAnalysis.h" +#include "swift/SIL/SILArgument.h" +#include "swift/SILOptimizer/Analysis/AccessedStorageAnalysis.h" #include "swift/SILOptimizer/Analysis/BasicCalleeAnalysis.h" #include "swift/SILOptimizer/Analysis/FunctionOrder.h" #include "swift/SILOptimizer/PassManager/PassManager.h" -#include "swift/SIL/SILArgument.h" using namespace swift; -using FunctionEffects = SideEffectAnalysis::FunctionEffects; -using Effects = SideEffectAnalysis::Effects; +// ----------------------------------------------------------------------------- +// GenericFunctionEffectAnalysis +// ----------------------------------------------------------------------------- + +template +void GenericFunctionEffectAnalysis::initialize( + SILPassManager *PM) { + BCA = PM->getAnalysis(); +} + +template +void GenericFunctionEffectAnalysis::invalidate() { + functionInfoMap.clear(); + allocator.DestroyAll(); + DEBUG(llvm::dbgs() << "invalidate all\n"); +} + +template +void GenericFunctionEffectAnalysis::invalidate( + SILFunction *F, InvalidationKind K) { + if (FunctionInfo *FInfo = functionInfoMap.lookup(F)) { + DEBUG(llvm::dbgs() << " invalidate " << FInfo->F->getName() << '\n'); + invalidateIncludingAllCallers(FInfo); + } +} + +template +void GenericFunctionEffectAnalysis::getCalleeEffects( + FunctionEffects &calleeEffects, FullApplySite fullApply) { + if (calleeEffects.summarizeCall(fullApply)) + return; + + auto callees = BCA->getCalleeList(fullApply); + if (!callees.allCalleesVisible() || + // @callee_owned function calls implicitly release the context, which + // may call deinits of boxed values. + // TODO: be less conservative about what destructors might be called. + fullApply.getOrigCalleeType()->isCalleeConsumed()) { + calleeEffects.setWorstEffects(); + return; + } + + // We can see all the callees, so merge the effects from all of them. + for (auto *callee : callees) + calleeEffects.mergeFrom(getEffects(callee)); +} + +template +void GenericFunctionEffectAnalysis::analyzeFunction( + FunctionInfo *functionInfo, FunctionOrder &bottomUpOrder, + int recursionDepth) { + functionInfo->needUpdateCallers = true; + + if (bottomUpOrder.prepareForVisiting(functionInfo)) + return; + + auto *F = functionInfo->F; + if (functionInfo->functionEffects.summarizeFunction(F)) + return; + + DEBUG(llvm::dbgs() << " >> analyze " << F->getName() << '\n'); + + // Check all instructions of the function + for (auto &BB : *F) { + for (auto &I : BB) { + if (auto fullApply = FullApplySite::isa(&I)) + analyzeCall(functionInfo, fullApply, bottomUpOrder, recursionDepth); + else + functionInfo->functionEffects.analyzeInstruction(&I); + } + } + DEBUG(llvm::dbgs() << " << finished " << F->getName() << '\n'); +} + +template +void GenericFunctionEffectAnalysis::analyzeCall( + FunctionInfo *functionInfo, FullApplySite fullApply, + FunctionOrder &bottomUpOrder, int recursionDepth) { + + FunctionEffects applyEffects; + if (applyEffects.summarizeCall(fullApply)) { + functionInfo->functionEffects.mergeFromApply(applyEffects, fullApply); + return; + } + + if (recursionDepth >= MaxRecursionDepth) { + functionInfo->functionEffects.setWorstEffects(); + return; + } + CalleeList callees = BCA->getCalleeList(fullApply); + if (!callees.allCalleesVisible() || + // @callee_owned function calls implicitly release the context, which + // may call deinits of boxed values. + // TODO: be less conservative about what destructors might be called. + fullApply.getOrigCalleeType()->isCalleeConsumed()) { + functionInfo->functionEffects.setWorstEffects(); + return; + } + // Derive the effects of the apply from the known callees. + // Defer merging callee effects until the callee is scheduled + for (SILFunction *callee : callees) { + FunctionInfo *calleeInfo = getFunctionInfo(callee); + calleeInfo->addCaller(functionInfo, fullApply); + if (!calleeInfo->isVisited()) { + // Recursively visit the called function. + analyzeFunction(calleeInfo, bottomUpOrder, recursionDepth + 1); + bottomUpOrder.tryToSchedule(calleeInfo); + } + } +} + +template +void GenericFunctionEffectAnalysis::recompute( + FunctionInfo *initialInfo) { + allocNewUpdateID(); + + DEBUG(llvm::dbgs() << "recompute function-effect analysis with UpdateID " + << getCurrentUpdateID() << '\n'); + + // Collect and analyze all functions to recompute, starting at initialInfo. + FunctionOrder bottomUpOrder(getCurrentUpdateID()); + analyzeFunction(initialInfo, bottomUpOrder, 0); + + // Build the bottom-up order. + bottomUpOrder.tryToSchedule(initialInfo); + bottomUpOrder.finishScheduling(); + + // Second step: propagate the side-effect information up the call-graph until + // it stabilizes. + bool needAnotherIteration; + do { + DEBUG(llvm::dbgs() << "new iteration\n"); + needAnotherIteration = false; + + for (FunctionInfo *functionInfo : bottomUpOrder) { + if (!functionInfo->needUpdateCallers) + continue; + + DEBUG(llvm::dbgs() << " update callers of " << functionInfo->F->getName() + << '\n'); + functionInfo->needUpdateCallers = false; + + // Propagate the function effects to all callers. + for (const auto &E : functionInfo->getCallers()) { + assert(E.isValid()); + + // Only include callers which we are actually recomputing. + if (!bottomUpOrder.wasRecomputedWithCurrentUpdateID(E.Caller)) + continue; + + DEBUG(llvm::dbgs() << " merge into caller " << E.Caller->F->getName() + << '\n'); + + if (E.Caller->functionEffects.mergeFromApply( + functionInfo->functionEffects, FullApplySite(E.FAS))) { + E.Caller->needUpdateCallers = true; + if (!E.Caller->isScheduledAfter(functionInfo)) { + // This happens if we have a cycle in the call-graph. + needAnotherIteration = true; + } + } + } + } + } while (needAnotherIteration); +} + +// Instantiate template members. +template class swift::GenericFunctionEffectAnalysis; +template class swift::GenericFunctionEffectAnalysis; + +// ----------------------------------------------------------------------------- +// FunctionSideEffects +// ----------------------------------------------------------------------------- + using MemoryBehavior = SILInstruction::MemoryBehavior; MemoryBehavior -FunctionEffects::getMemBehavior(RetainObserveKind ScanKind) const { +FunctionSideEffects::getMemBehavior(RetainObserveKind ScanKind) const { bool Observe = (ScanKind == RetainObserveKind::ObserveRetains); if ((Observe && mayAllocObjects()) || mayReadRC()) @@ -46,7 +219,7 @@ FunctionEffects::getMemBehavior(RetainObserveKind ScanKind) const { return Behavior; } -bool FunctionEffects::mergeFrom(const FunctionEffects &RHS) { +bool FunctionSideEffects::mergeFrom(const FunctionSideEffects &RHS) { bool Changed = mergeFlags(RHS); Changed |= GlobalEffects.mergeFrom(RHS.GlobalEffects); Changed |= LocalEffects.mergeFrom(RHS.LocalEffects); @@ -64,33 +237,26 @@ bool FunctionEffects::mergeFrom(const FunctionEffects &RHS) { return Changed; } -bool FunctionEffects::mergeFromApply( - const FunctionEffects &ApplyEffects, FullApplySite FAS) { - return mergeFromApply(ApplyEffects, FAS.getInstruction()); -} - -bool FunctionEffects::mergeFromApply( - const FunctionEffects &ApplyEffects, SILInstruction *AS) { +bool FunctionSideEffects::mergeFromApply( + const FunctionSideEffects &ApplyEffects, FullApplySite FAS) { bool Changed = mergeFlags(ApplyEffects); Changed |= GlobalEffects.mergeFrom(ApplyEffects.GlobalEffects); - auto FAS = FullApplySite::isa(AS); - unsigned numCallerArgs = FAS ? FAS.getNumArguments() : 1; + unsigned numCallerArgs = FAS.getNumArguments(); unsigned numCalleeArgs = ApplyEffects.ParamEffects.size(); assert(numCalleeArgs >= numCallerArgs); for (unsigned Idx = 0; Idx < numCalleeArgs; Idx++) { // Map the callee argument effects to parameters of this function. // If there are more callee parameters than arguments it means that the // callee is the result of a partial_apply. - Effects *E = (Idx < numCallerArgs ? getEffectsOn(FAS ? FAS.getArgument(Idx) : AS->getOperand(Idx)) : - &GlobalEffects); + FunctionSideEffectFlags *E = (Idx < numCallerArgs + ? getEffectsOn(FAS.getArgument(Idx)) + : &GlobalEffects); Changed |= E->mergeFrom(ApplyEffects.ParamEffects[Idx]); } return Changed; } -void FunctionEffects::dump() const { - llvm::errs() << *this << '\n'; -} +void FunctionSideEffects::dump() const { llvm::errs() << *this << '\n'; } static SILValue skipAddrProjections(SILValue V) { for (;;) { @@ -130,7 +296,7 @@ static SILValue skipValueProjections(SILValue V) { llvm_unreachable("there is no escape from an infinite loop"); } -Effects *FunctionEffects::getEffectsOn(SILValue Addr) { +FunctionSideEffectFlags *FunctionSideEffects::getEffectsOn(SILValue Addr) { SILValue BaseAddr = skipValueProjections(skipAddrProjections(Addr)); switch (BaseAddr->getKind()) { case swift::ValueKind::SILFunctionArgument: { @@ -152,17 +318,18 @@ Effects *FunctionEffects::getEffectsOn(SILValue Addr) { return &GlobalEffects; } -bool SideEffectAnalysis::getDefinedEffects(FunctionEffects &Effects, - SILFunction *F) { +// Return true if the given function has defined effects that were successfully +// recorded in this FunctionSideEffects object. +bool FunctionSideEffects::setDefinedEffects(SILFunction *F) { if (F->hasSemanticsAttr("arc.programtermination_point")) { - Effects.Traps = true; + Traps = true; return true; } switch (F->getEffectsKind()) { case EffectsKind::ReleaseNone: - Effects.GlobalEffects.Reads = true; - Effects.GlobalEffects.Writes = true; - Effects.GlobalEffects.Releases = false; + GlobalEffects.Reads = true; + GlobalEffects.Writes = true; + GlobalEffects.Releases = false; return true; case EffectsKind::ReadNone: return true; @@ -171,7 +338,7 @@ bool SideEffectAnalysis::getDefinedEffects(FunctionEffects &Effects, // the release inside the callee may call a deinit, which itself can do // anything. if (!F->hasOwnedParameters()) { - Effects.GlobalEffects.Reads = true; + GlobalEffects.Reads = true; return true; } break; @@ -182,10 +349,34 @@ bool SideEffectAnalysis::getDefinedEffects(FunctionEffects &Effects, return false; } -bool SideEffectAnalysis::getSemanticEffects(FunctionEffects &FE, - ArraySemanticsCall ASC) { +// Return true if this function's effects have been fully summarized in this +// FunctionSideEffects object without visiting its body. +bool FunctionSideEffects::summarizeFunction(SILFunction *F) { + assert(ParamEffects.empty() && "Expect uninitialized effects."); + if (!F->empty()) + ParamEffects.resize(F->getArguments().size()); + + // Handle @effects attributes + if (setDefinedEffects(F)) { + DEBUG(llvm::dbgs() << " -- has defined effects " << F->getName() << '\n'); + return true; + } + + if (!F->isDefinition()) { + // We can't assume anything about external functions. + DEBUG(llvm::dbgs() << " -- is external " << F->getName() << '\n'); + setWorstEffects(); + return true; + } + return false; +} + +// Return true if the side effects of this semantic call are fully known without +// visiting the callee and have been recorded in this FunctionSideEffects +// object. +bool FunctionSideEffects::setSemanticEffects(ArraySemanticsCall ASC) { assert(ASC.hasSelf()); - auto &SelfEffects = FE.ParamEffects[FE.ParamEffects.size() - 1]; + auto &SelfEffects = ParamEffects[ParamEffects.size() - 1]; // Currently we only handle array semantics. // TODO: also handle other semantic functions. @@ -205,7 +396,7 @@ bool SideEffectAnalysis::getSemanticEffects(FunctionEffects &FE, if (!ASC.mayHaveBridgedObjectElementType()) { SelfEffects.Reads = true; SelfEffects.Releases |= !ASC.hasGuaranteedSelf(); - FE.Traps = true; + Traps = true; return true; } return false; @@ -218,7 +409,7 @@ bool SideEffectAnalysis::getSemanticEffects(FunctionEffects &FE, ->getOrigCalleeConv() .getNumIndirectSILResults())) { assert(!ASC.hasGetElementDirectResult()); - FE.ParamEffects[i].Writes = true; + ParamEffects[i].Writes = true; } return true; } @@ -240,9 +431,9 @@ bool SideEffectAnalysis::getSemanticEffects(FunctionEffects &FE, case ArrayCallKind::kMakeMutable: if (!ASC.mayHaveBridgedObjectElementType()) { SelfEffects.Writes = true; - FE.GlobalEffects.Releases = true; - FE.AllocsObjects = true; - FE.ReadsRC = true; + GlobalEffects.Releases = true; + AllocsObjects = true; + ReadsRC = true; return true; } return false; @@ -252,290 +443,134 @@ bool SideEffectAnalysis::getSemanticEffects(FunctionEffects &FE, } } -void SideEffectAnalysis::analyzeFunction(FunctionInfo *FInfo, - FunctionOrder &BottomUpOrder, - int RecursionDepth) { - FInfo->NeedUpdateCallers = true; - - if (BottomUpOrder.prepareForVisiting(FInfo)) - return; +// Summarize the callee side effects of a call instruction using this +// FunctionSideEffects object without analyzing the callee function bodies or +// scheduling the callees for bottom-up propagation. +// +// Return true if this call-site's effects are summarized without visiting the +// callee. +bool FunctionSideEffects::summarizeCall(FullApplySite fullApply) { + assert(ParamEffects.empty() && "Expect uninitialized effects."); + ParamEffects.resize(fullApply.getNumArguments()); - // Handle @effects attributes - if (getDefinedEffects(FInfo->FE, FInfo->F)) { - DEBUG(llvm::dbgs() << " -- has defined effects " << - FInfo->F->getName() << '\n'); - return; - } - - if (!FInfo->F->isDefinition()) { - // We can't assume anything about external functions. - DEBUG(llvm::dbgs() << " -- is external " << FInfo->F->getName() << '\n'); - FInfo->FE.setWorstEffects(); - return; + // Is this a call to a semantics function? + if (auto apply = dyn_cast(fullApply.getInstruction())) { + ArraySemanticsCall ASC(apply); + if (ASC && ASC.hasSelf()) { + if (setSemanticEffects(ASC)) + return true; + } } - - DEBUG(llvm::dbgs() << " >> analyze " << FInfo->F->getName() << '\n'); - // Check all instructions of the function - for (auto &BB : *FInfo->F) { - for (auto &I : BB) { - analyzeInstruction(FInfo, &I, BottomUpOrder, RecursionDepth); - } + if (SILFunction *SingleCallee = fullApply.getReferencedFunction()) { + // Does the function have any @effects? + if (setDefinedEffects(SingleCallee)) + return true; } - DEBUG(llvm::dbgs() << " << finished " << FInfo->F->getName() << '\n'); + return false; } -void SideEffectAnalysis::analyzeInstruction(FunctionInfo *FInfo, - SILInstruction *I, - FunctionOrder &BottomUpOrder, - int RecursionDepth) { - if (FullApplySite FAS = FullApplySite::isa(I)) { - // Is this a call to a semantics function? - if (auto apply = dyn_cast(FAS.getInstruction())) { - ArraySemanticsCall ASC(apply); - if (ASC && ASC.hasSelf()) { - FunctionEffects ApplyEffects(FAS.getNumArguments()); - if (getSemanticEffects(ApplyEffects, ASC)) { - FInfo->FE.mergeFromApply(ApplyEffects, FAS); - return; - } - } - } - - if (SILFunction *SingleCallee = FAS.getReferencedFunction()) { - // Does the function have any @effects? - if (getDefinedEffects(FInfo->FE, SingleCallee)) - return; - } - - if (RecursionDepth < MaxRecursionDepth) { - CalleeList Callees = BCA->getCalleeList(FAS); - if (Callees.allCalleesVisible() && - // @callee_owned function calls implicitly release the context, which - // may call deinits of boxed values. - // TODO: be less conservative about what destructors might be called. - !FAS.getOrigCalleeType()->isCalleeConsumed()) { - // Derive the effects of the apply from the known callees. - for (SILFunction *Callee : Callees) { - FunctionInfo *CalleeInfo = getFunctionInfo(Callee); - CalleeInfo->addCaller(FInfo, FAS); - if (!CalleeInfo->isVisited()) { - // Recursively visit the called function. - analyzeFunction(CalleeInfo, BottomUpOrder, RecursionDepth + 1); - BottomUpOrder.tryToSchedule(CalleeInfo); - } - } - return; - } - } - // Be conservative for everything else. - FInfo->FE.setWorstEffects(); - return; - } +void FunctionSideEffects::analyzeInstruction(SILInstruction *I) { // Handle some kind of instructions specially. switch (I->getKind()) { - case SILInstructionKind::FixLifetimeInst: - // A fix_lifetime instruction acts like a read on the operand. Retains can move after it - // but the last release can't move before it. - FInfo->FE.getEffectsOn(I->getOperand(0))->Reads = true; - return; - case SILInstructionKind::AllocStackInst: - case SILInstructionKind::DeallocStackInst: - return; - case SILInstructionKind::StrongRetainInst: - case SILInstructionKind::StrongRetainUnownedInst: - case SILInstructionKind::RetainValueInst: - case SILInstructionKind::UnownedRetainInst: - FInfo->FE.getEffectsOn(I->getOperand(0))->Retains = true; - return; - case SILInstructionKind::StrongReleaseInst: - case SILInstructionKind::ReleaseValueInst: - case SILInstructionKind::UnownedReleaseInst: - FInfo->FE.getEffectsOn(I->getOperand(0))->Releases = true; - return; - case SILInstructionKind::UnconditionalCheckedCastInst: - FInfo->FE.getEffectsOn(cast(I)->getOperand())->Reads = true; - FInfo->FE.Traps = true; - return; - case SILInstructionKind::LoadInst: - FInfo->FE.getEffectsOn(cast(I)->getOperand())->Reads = true; - return; - case SILInstructionKind::StoreInst: - FInfo->FE.getEffectsOn(cast(I)->getDest())->Writes = true; - return; - case SILInstructionKind::CondFailInst: - FInfo->FE.Traps = true; - return; - case SILInstructionKind::PartialApplyInst: { - FInfo->FE.AllocsObjects = true; - auto *PAI = cast(I); - auto Args = PAI->getArguments(); - auto Params = PAI->getSubstCalleeType()->getParameters(); - Params = Params.slice(Params.size() - Args.size(), Args.size()); - for (unsigned Idx : indices(Args)) { - if (isIndirectFormalParameter(Params[Idx].getConvention())) - FInfo->FE.getEffectsOn(Args[Idx])->Reads = true; - } - return; + case SILInstructionKind::FixLifetimeInst: + // A fix_lifetime instruction acts like a read on the operand. Retains can + // move after it but the last release can't move before it. + getEffectsOn(I->getOperand(0))->Reads = true; + return; + case SILInstructionKind::AllocStackInst: + case SILInstructionKind::DeallocStackInst: + return; + case SILInstructionKind::StrongRetainInst: + case SILInstructionKind::StrongRetainUnownedInst: + case SILInstructionKind::RetainValueInst: + case SILInstructionKind::UnownedRetainInst: + getEffectsOn(I->getOperand(0))->Retains = true; + return; + case SILInstructionKind::StrongReleaseInst: + case SILInstructionKind::ReleaseValueInst: + case SILInstructionKind::UnownedReleaseInst: + getEffectsOn(I->getOperand(0))->Releases = true; + return; + case SILInstructionKind::UnconditionalCheckedCastInst: + getEffectsOn(cast(I)->getOperand())->Reads = + true; + Traps = true; + return; + case SILInstructionKind::LoadInst: + getEffectsOn(cast(I)->getOperand())->Reads = true; + return; + case SILInstructionKind::StoreInst: + getEffectsOn(cast(I)->getDest())->Writes = true; + return; + case SILInstructionKind::CondFailInst: + Traps = true; + return; + case SILInstructionKind::PartialApplyInst: { + AllocsObjects = true; + auto *PAI = cast(I); + auto Args = PAI->getArguments(); + auto Params = PAI->getSubstCalleeType()->getParameters(); + Params = Params.slice(Params.size() - Args.size(), Args.size()); + for (unsigned Idx : indices(Args)) { + if (isIndirectFormalParameter(Params[Idx].getConvention())) + getEffectsOn(Args[Idx])->Reads = true; } - case SILInstructionKind::BuiltinInst: { - auto *BInst = cast(I); - auto &BI = BInst->getBuiltinInfo(); - switch (BI.ID) { - case BuiltinValueKind::IsUnique: - // TODO: derive this information in a more general way, e.g. add it - // to Builtins.def - FInfo->FE.ReadsRC = true; - break; - case BuiltinValueKind::CondUnreachable: - FInfo->FE.Traps = true; - return; - default: - break; - } - const IntrinsicInfo &IInfo = BInst->getIntrinsicInfo(); - if (IInfo.ID == llvm::Intrinsic::trap) { - FInfo->FE.Traps = true; - return; - } - // Detailed memory effects of builtins are handled below by checking the - // memory behavior of the instruction. + return; + } + case SILInstructionKind::BuiltinInst: { + auto *BInst = cast(I); + auto &BI = BInst->getBuiltinInfo(); + switch (BI.ID) { + case BuiltinValueKind::IsUnique: + // TODO: derive this information in a more general way, e.g. add it + // to Builtins.def + ReadsRC = true; break; - } + case BuiltinValueKind::CondUnreachable: + Traps = true; + return; default: break; + } + const IntrinsicInfo &IInfo = BInst->getIntrinsicInfo(); + if (IInfo.ID == llvm::Intrinsic::trap) { + Traps = true; + return; + } + // Detailed memory effects of builtins are handled below by checking the + // memory behavior of the instruction. + break; + } + default: + break; } if (isa(I)) { // Excluding AllocStackInst (which is handled above). - FInfo->FE.AllocsObjects = true; + AllocsObjects = true; } // Check the general memory behavior for instructions we didn't handle above. switch (I->getMemoryBehavior()) { - case MemoryBehavior::None: - break; - case MemoryBehavior::MayRead: - FInfo->FE.GlobalEffects.Reads = true; - break; - case MemoryBehavior::MayWrite: - FInfo->FE.GlobalEffects.Writes = true; - break; - case MemoryBehavior::MayReadWrite: - FInfo->FE.GlobalEffects.Reads = true; - FInfo->FE.GlobalEffects.Writes = true; - break; - case MemoryBehavior::MayHaveSideEffects: - FInfo->FE.setWorstEffects(); - break; + case MemoryBehavior::None: + break; + case MemoryBehavior::MayRead: + GlobalEffects.Reads = true; + break; + case MemoryBehavior::MayWrite: + GlobalEffects.Writes = true; + break; + case MemoryBehavior::MayReadWrite: + GlobalEffects.Reads = true; + GlobalEffects.Writes = true; + break; + case MemoryBehavior::MayHaveSideEffects: + setWorstEffects(); + break; } if (I->mayTrap()) - FInfo->FE.Traps = true; -} - -void SideEffectAnalysis::initialize(SILPassManager *PM) { - BCA = PM->getAnalysis(); -} - -void SideEffectAnalysis::recompute(FunctionInfo *Initial) { - allocNewUpdateID(); - - DEBUG(llvm::dbgs() << "recompute side-effect analysis with UpdateID " << - getCurrentUpdateID() << '\n'); - - // Collect and analyze all functions to recompute, starting at Initial. - FunctionOrder BottomUpOrder(getCurrentUpdateID()); - analyzeFunction(Initial, BottomUpOrder, 0); - - // Build the bottom-up order. - BottomUpOrder.tryToSchedule(Initial); - BottomUpOrder.finishScheduling(); - - // Second step: propagate the side-effect information up the call-graph until - // it stabilizes. - bool NeedAnotherIteration; - do { - DEBUG(llvm::dbgs() << "new iteration\n"); - NeedAnotherIteration = false; - - for (FunctionInfo *FInfo : BottomUpOrder) { - if (FInfo->NeedUpdateCallers) { - DEBUG(llvm::dbgs() << " update callers of " << FInfo->F->getName() << - '\n'); - FInfo->NeedUpdateCallers = false; - - // Propagate the side-effects to all callers. - for (const auto &E : FInfo->getCallers()) { - assert(E.isValid()); - - // Only include callers which we are actually recomputing. - if (BottomUpOrder.wasRecomputedWithCurrentUpdateID(E.Caller)) { - DEBUG(llvm::dbgs() << " merge into caller " << - E.Caller->F->getName() << '\n'); - - if (E.Caller->FE.mergeFromApply(FInfo->FE, E.FAS)) { - E.Caller->NeedUpdateCallers = true; - if (!E.Caller->isScheduledAfter(FInfo)) { - // This happens if we have a cycle in the call-graph. - NeedAnotherIteration = true; - } - } - } - } - } - } - } while (NeedAnotherIteration); -} - -void SideEffectAnalysis::getEffects(FunctionEffects &ApplyEffects, FullApplySite FAS) { - assert(ApplyEffects.ParamEffects.empty() && - "Not using a new ApplyEffects?"); - ApplyEffects.ParamEffects.resize(FAS.getNumArguments()); - - // Is this a call to a semantics function? - if (auto apply = dyn_cast(FAS.getInstruction())) { - ArraySemanticsCall ASC(apply); - if (ASC && ASC.hasSelf()) { - if (getSemanticEffects(ApplyEffects, ASC)) - return; - } - } - - if (SILFunction *SingleCallee = FAS.getReferencedFunction()) { - // Does the function have any @effects? - if (getDefinedEffects(ApplyEffects, SingleCallee)) - return; - } - - auto Callees = BCA->getCalleeList(FAS); - if (!Callees.allCalleesVisible() || - // @callee_owned function calls implicitly release the context, which - // may call deinits of boxed values. - // TODO: be less conservative about what destructors might be called. - FAS.getOrigCalleeType()->isCalleeConsumed()) { - ApplyEffects.setWorstEffects(); - return; - } - - // We can see all the callees. So we just merge the effects from all of - // them. - for (auto *Callee : Callees) { - const FunctionEffects &CalleeFE = getEffects(Callee); - ApplyEffects.mergeFrom(CalleeFE); - } -} - -void SideEffectAnalysis::invalidate() { - Function2Info.clear(); - Allocator.DestroyAll(); - DEBUG(llvm::dbgs() << "invalidate all\n"); -} - -void SideEffectAnalysis::invalidate(SILFunction *F, InvalidationKind K) { - if (FunctionInfo *FInfo = Function2Info.lookup(F)) { - DEBUG(llvm::dbgs() << " invalidate " << FInfo->F->getName() << '\n'); - invalidateIncludingAllCallers(FInfo); - } + Traps = true; } SILAnalysis *swift::createSideEffectAnalysis(SILModule *M) { diff --git a/lib/SILOptimizer/IPO/CapturePromotion.cpp b/lib/SILOptimizer/IPO/CapturePromotion.cpp index 16fa827b1db39..b1170f9c80450 100644 --- a/lib/SILOptimizer/IPO/CapturePromotion.cpp +++ b/lib/SILOptimizer/IPO/CapturePromotion.cpp @@ -371,7 +371,7 @@ computeNewArgInterfaceTypes(SILFunction *F, convention = param.isGuaranteed() ? ParameterConvention::Direct_Guaranteed : ParameterConvention::Direct_Owned; } - OutTys.push_back(SILParameterInfo(paramBoxedTy.getSwiftRValueType(), + OutTys.push_back(SILParameterInfo(paramBoxedTy.getASTType(), convention)); } } @@ -1221,7 +1221,7 @@ processPartialApplyInst(PartialApplyInst *PAI, IndicesSet &PromotableIndices, auto SubstCalleeFunctionTy = CalleeFunctionTy; if (PAI->hasSubstitutions()) SubstCalleeFunctionTy = - CalleeFunctionTy->substGenericArgs(M, PAI->getSubstitutions()); + CalleeFunctionTy->substGenericArgs(M, PAI->getSubstitutionMap()); SILFunctionConventions calleeConv(SubstCalleeFunctionTy, M); auto CalleePInfo = SubstCalleeFunctionTy->getParameters(); SILFunctionConventions paConv(PAI->getType().castTo(), M); @@ -1261,7 +1261,7 @@ processPartialApplyInst(PartialApplyInst *PAI, IndicesSet &PromotableIndices, // Create a new partial apply with the new arguments. auto *NewPAI = B.createPartialApply( - PAI->getLoc(), FnVal, PAI->getSubstitutions(), Args, + PAI->getLoc(), FnVal, PAI->getSubstitutionMap(), Args, PAI->getType().getAs()->getCalleeConvention()); PAI->replaceAllUsesWith(NewPAI); PAI->eraseFromParent(); diff --git a/lib/SILOptimizer/IPO/CapturePropagation.cpp b/lib/SILOptimizer/IPO/CapturePropagation.cpp index 7db61c4984d73..c6041ee663c92 100644 --- a/lib/SILOptimizer/IPO/CapturePropagation.cpp +++ b/lib/SILOptimizer/IPO/CapturePropagation.cpp @@ -101,7 +101,7 @@ class CapturePropagationCloner bool IsCloningConstant; public: CapturePropagationCloner(SILFunction *OrigF, SILFunction *NewF, - SubstitutionList Subs) + SubstitutionMap Subs) : SuperTy(*NewF, *OrigF, Subs), OrigF(OrigF), IsCloningConstant(false) {} void cloneBlocks(OperandValueArrayRef Args); @@ -218,7 +218,8 @@ CanSILFunctionType getPartialApplyInterfaceResultType(PartialApplyInst *PAI) { // expressed as literals. So its callee signature will be the same as its // return signature. auto FTy = PAI->getType().castTo(); - assert(!PAI->hasSubstitutions() || !hasArchetypes(PAI->getSubstitutions())); + assert(!PAI->hasSubstitutions() || + !PAI->getSubstitutionMap().hasArchetypes()); FTy = cast( FTy->mapTypeOutOfContext()->getCanonicalType()); auto NewFTy = FTy; @@ -271,7 +272,7 @@ SILFunction *CapturePropagation::specializeConstClosure(PartialApplyInst *PAI, llvm::dbgs() << "CapturePropagation of generic partial_apply:\n"; PAI->dumpInContext(); }); - CapturePropagationCloner cloner(OrigF, NewF, PAI->getSubstitutions()); + CapturePropagationCloner cloner(OrigF, NewF, PAI->getSubstitutionMap()); cloner.cloneBlocks(PAI->getArguments()); assert(OrigF->getDebugScope()->Parent != NewF->getDebugScope()->Parent); return NewF; @@ -410,11 +411,13 @@ static SILFunction *getSpecializedWithDeadParams( return nullptr; // Perform a generic specialization of the Specialized function. - ReabstractionInfo ReInfo(ApplySite(), Specialized, PAI->getSubstitutions(), + ReabstractionInfo ReInfo(ApplySite(), Specialized, + PAI->getSubstitutionMap(), /* ConvertIndirectToDirect */ false); - GenericFuncSpecializer FuncSpecializer(Specialized, - ReInfo.getClonerParamSubstitutions(), - Specialized->isSerialized(), ReInfo); + GenericFuncSpecializer FuncSpecializer( + Specialized, + ReInfo.getClonerParamSubstitutionMap(), + Specialized->isSerialized(), ReInfo); SILFunction *GenericSpecializedFunc = FuncSpecializer.trySpecialization(); if (!GenericSpecializedFunc) @@ -432,7 +435,7 @@ bool CapturePropagation::optimizePartialApply(PartialApplyInst *PAI) { if (SubstF->isExternalDeclaration()) return false; - if (PAI->hasSubstitutions() && hasArchetypes(PAI->getSubstitutions())) { + if (PAI->hasSubstitutions() && PAI->getSubstitutionMap().hasArchetypes()) { DEBUG(llvm::dbgs() << "CapturePropagation: cannot handle partial specialization " "of partial_apply:\n"; diff --git a/lib/SILOptimizer/IPO/ClosureSpecializer.cpp b/lib/SILOptimizer/IPO/ClosureSpecializer.cpp index 6beb3d7878a3f..9788b31fc2fc7 100644 --- a/lib/SILOptimizer/IPO/ClosureSpecializer.cpp +++ b/lib/SILOptimizer/IPO/ClosureSpecializer.cpp @@ -57,6 +57,7 @@ #define DEBUG_TYPE "closure-specialization" #include "swift/Basic/Range.h" +#include "swift/SIL/InstructionUtils.h" #include "swift/SIL/SILCloner.h" #include "swift/SIL/SILFunction.h" #include "swift/SIL/SILInstruction.h" @@ -123,8 +124,10 @@ class ClosureSpecCloner : public SILClonerWithScopes { void populateCloned(); - SILValue cloneCalleeConversion(SILValue calleeValue, SILValue NewClosure, - SILBuilder &Builder); + SILValue + cloneCalleeConversion(SILValue calleeValue, SILValue NewClosure, + SILBuilder &Builder, + SmallVectorImpl &NeedsRelease); SILFunction *getCloned() { return &getBuilder().getFunction(); } static SILFunction *cloneFunction(const CallSiteDescriptor &CallSiteDesc, @@ -274,7 +277,7 @@ struct ClosureInfo { ValueLifetimeAnalysis::Frontier LifetimeFrontier; llvm::SmallVector CallSites; - ClosureInfo(SingleValueInstruction *Closure): Closure(Closure) {} + ClosureInfo(SingleValueInstruction *Closure) : Closure(Closure) {} ClosureInfo(ClosureInfo &&) =default; ClosureInfo &operator=(ClosureInfo &&) =default; @@ -405,7 +408,7 @@ static void rewriteApplyInst(const CallSiteDescriptor &CSDesc, FullApplySite NewAI; if (auto *TAI = dyn_cast(AI)) { NewAI = Builder.createTryApply(AI.getLoc(), FRI, - SubstitutionList(), NewArgs, + SubstitutionMap(), NewArgs, TAI->getNormalBB(), TAI->getErrorBB()); // If we passed in the original closure as @owned, then insert a release // right after NewAI. This is to balance the +1 from being an @owned @@ -420,7 +423,7 @@ static void rewriteApplyInst(const CallSiteDescriptor &CSDesc, } else { auto oldApply = cast(AI); auto newApply = Builder.createApply(oldApply->getLoc(), FRI, - SubstitutionList(), + SubstitutionMap(), NewArgs, oldApply->isNonThrowing()); // If we passed in the original closure as @owned, then insert a release // right after NewAI. This is to balance the +1 from being an @owned @@ -648,22 +651,41 @@ ClosureSpecCloner::initCloned(const CallSiteDescriptor &CallSiteDesc, } // Clone a chain of ConvertFunctionInsts. -SILValue ClosureSpecCloner::cloneCalleeConversion(SILValue calleeValue, - SILValue NewClosure, - SILBuilder &Builder) { +SILValue ClosureSpecCloner::cloneCalleeConversion( + SILValue calleeValue, SILValue NewClosure, SILBuilder &Builder, + SmallVectorImpl &NeedsRelease) { if (calleeValue == CallSiteDesc.getClosure()) return NewClosure; if (auto *CFI = dyn_cast(calleeValue)) { - calleeValue = cloneCalleeConversion(CFI->getOperand(), NewClosure, Builder); + calleeValue = cloneCalleeConversion(CFI->getOperand(), NewClosure, Builder, + NeedsRelease); return Builder.createConvertFunction(CallSiteDesc.getLoc(), calleeValue, CFI->getType()); } + if (auto *PAI = dyn_cast(calleeValue)) { + assert(isPartialApplyOfReabstractionThunk(PAI) && isSupportedClosure(PAI) && + PAI->getArgument(0) + ->getType() + .getAs() + ->isTrivialNoEscape()); + calleeValue = cloneCalleeConversion(PAI->getArgument(0), NewClosure, + Builder, NeedsRelease); + auto FunRef = Builder.createFunctionRef(CallSiteDesc.getLoc(), + PAI->getReferencedFunction()); + auto NewPA = Builder.createPartialApply( + CallSiteDesc.getLoc(), FunRef, {}, {calleeValue}, + PAI->getType().getAs()->getCalleeConvention()); + NeedsRelease.push_back(NewPA); + return NewPA; + } + auto *Cvt = cast(calleeValue); - calleeValue = cloneCalleeConversion(Cvt->getOperand(), NewClosure, Builder); + calleeValue = cloneCalleeConversion(Cvt->getOperand(), NewClosure, Builder, + NeedsRelease); return Builder.createConvertEscapeToNoEscape( - CallSiteDesc.getLoc(), calleeValue, Cvt->getType(), true); + CallSiteDesc.getLoc(), calleeValue, Cvt->getType(), false, true); } /// \brief Populate the body of the cloned closure, modifying instructions as @@ -718,9 +740,17 @@ void ClosureSpecCloner::populateCloned() { Builder.createFunctionRef(CallSiteDesc.getLoc(), ClosedOverFun); auto *NewClosure = CallSiteDesc.createNewClosure(Builder, FnVal, NewPAIArgs); - // Clone a chain of ConvertFunctionInsts. + // Clone a chain of ConvertFunctionInsts. This can create further + // reabstraction partial_apply instructions. + SmallVector NeedsRelease; SILValue ConvertedCallee = cloneCalleeConversion( - CallSiteDesc.getClosureCallerArg(), NewClosure, Builder); + CallSiteDesc.getClosureCallerArg(), NewClosure, Builder, NeedsRelease); + + // Make sure that we actually emit the releases for reabstraction thunks. We + // have guaranteed earlier that we only allow reabstraction thunks if the + // closure was passed trivial. + assert(NeedsRelease.empty() || CallSiteDesc.isTrivialNoEscapeParameter()); + ValueMap.insert(std::make_pair(ClosureArg, ConvertedCallee)); BBMap.insert(std::make_pair(ClosureUserEntryBB, ClonedEntryBB)); @@ -736,10 +766,11 @@ void ClosureSpecCloner::populateCloned() { // Then insert a release in all non failure exit BBs if our partial apply was // guaranteed. This is b/c it was passed at +0 originally and we need to - // balance the initial increment of the newly created closure. + // balance the initial increment of the newly created closure(s). + bool ClosureHasRefSemantics = CallSiteDesc.closureHasRefSemanticContext(); if ((CallSiteDesc.isClosureGuaranteed() || CallSiteDesc.isTrivialNoEscapeParameter()) && - CallSiteDesc.closureHasRefSemanticContext()) { + (ClosureHasRefSemantics || !NeedsRelease.empty())) { for (SILBasicBlock *BB : CallSiteDesc.getNonFailureExitBBs()) { SILBasicBlock *OpBB = BBMap[BB]; @@ -750,13 +781,21 @@ void ClosureSpecCloner::populateCloned() { // that it will be executed at the end of the epilogue. if (isa(TI)) { Builder.setInsertionPoint(TI); - Builder.createReleaseValue(Loc, SILValue(NewClosure), - Builder.getDefaultAtomicity()); + if (ClosureHasRefSemantics) + Builder.createReleaseValue(Loc, SILValue(NewClosure), + Builder.getDefaultAtomicity()); + for (auto PAI : NeedsRelease) + Builder.createReleaseValue(Loc, SILValue(PAI), + Builder.getDefaultAtomicity()); continue; } else if (isa(TI)) { Builder.setInsertionPoint(TI); - Builder.createReleaseValue(Loc, SILValue(NewClosure), - Builder.getDefaultAtomicity()); + if (ClosureHasRefSemantics) + Builder.createReleaseValue(Loc, SILValue(NewClosure), + Builder.getDefaultAtomicity()); + for (auto PAI : NeedsRelease) + Builder.createReleaseValue(Loc, SILValue(PAI), + Builder.getDefaultAtomicity()); continue; } @@ -772,7 +811,12 @@ void ClosureSpecCloner::populateCloned() { // value, we will retain the partial apply before we release it and // potentially eliminate it. Builder.setInsertionPoint(NoReturnApply.getInstruction()); - Builder.createReleaseValue(Loc, SILValue(NewClosure), Builder.getDefaultAtomicity()); + if (ClosureHasRefSemantics) + Builder.createReleaseValue(Loc, SILValue(NewClosure), + Builder.getDefaultAtomicity()); + for (auto PAI : NeedsRelease) + Builder.createReleaseValue(Loc, SILValue(PAI), + Builder.getDefaultAtomicity()); } } } @@ -839,6 +883,27 @@ void SILClosureSpecializerTransform::run() { invalidateAnalysis(SILAnalysis::InvalidationKind::Everything); } +static void markReabstractionPartialApplyAsUsed( + SILValue FirstClosure, SILValue Current, + llvm::DenseSet &UsedReabstractionClosure) { + if (Current == FirstClosure) + return; + if (auto PA = dyn_cast(Current)) { + UsedReabstractionClosure.insert(PA); + return markReabstractionPartialApplyAsUsed(FirstClosure, PA->getArgument(0), + UsedReabstractionClosure); + } + if (auto Cvt = dyn_cast(Current)) { + return markReabstractionPartialApplyAsUsed(FirstClosure, Cvt->getOperand(), + UsedReabstractionClosure); + } + if (auto Cvt = dyn_cast(Current)) { + return markReabstractionPartialApplyAsUsed(FirstClosure, Cvt->getOperand(), + UsedReabstractionClosure); + } + llvm_unreachable("Unexpect instruction"); +} + void SILClosureSpecializerTransform::gatherCallSites( SILFunction *Caller, llvm::SmallVectorImpl &ClosureCandidates, @@ -848,6 +913,10 @@ void SILClosureSpecializerTransform::gatherCallSites( // make sure that we do not handle call sites with multiple closure arguments. llvm::DenseSet VisitedAI; + // We should not look at reabstraction closure twice who we ultimately ended + // up using as an argument that we specialize on. + llvm::DenseSet UsedReabstractionClosure; + // For each basic block BB in Caller... for (auto &BB : *Caller) { @@ -857,6 +926,8 @@ void SILClosureSpecializerTransform::gatherCallSites( if (!isSupportedClosure(&II)) continue; auto ClosureInst = cast(&II); + if (UsedReabstractionClosure.count(ClosureInst)) + continue; ClosureInfo *CInfo = nullptr; @@ -868,6 +939,7 @@ void SILClosureSpecializerTransform::gatherCallSites( // Live range end points. SmallVector UsePoints; + bool HaveUsedReabstraction = false; // Uses may grow in this loop. for (size_t UseIndex = 0; UseIndex < Uses.size(); ++UseIndex) { auto *Use = Uses[UseIndex]; @@ -883,6 +955,26 @@ void SILClosureSpecializerTransform::gatherCallSites( Uses.append(Cvt->getUses().begin(), Cvt->getUses().end()); continue; } + + // Look through reabstraction thunks. + if (auto *PA = dyn_cast(Use->getUser())) { + // Reabstraction can cause series of partial_apply to be emitted. It + // is okay to treat these like conversion instructions. Current + // restriction: if the partial_apply does not take ownership of its + // argument we don't need to analyze which partial_apply to emit + // release for (its all of them). + if (isPartialApplyOfReabstractionThunk(PA) && + isSupportedClosure(PA) && + PA->getArgument(0) + ->getType() + .getAs() + ->isTrivialNoEscape()) { + Uses.append(PA->getUses().begin(), PA->getUses().end()); + HaveUsedReabstraction = true; + } + continue; + } + // If this use is not an apply inst or an apply inst with // substitutions, there is nothing interesting for us to do, so // continue... @@ -961,6 +1053,14 @@ void SILClosureSpecializerTransform::gatherCallSites( auto ParamInfo = AI.getSubstCalleeType()->getParameters(); SILParameterInfo ClosureParamInfo = ParamInfo[ClosureParamIndex]; + // We currently only support copying intermediate reabastraction + // closures if the closure is ultimately passed trivially. + bool IsClosurePassedTrivially = ClosureParamInfo.getType() + ->castTo() + ->isTrivialNoEscape(); + if (HaveUsedReabstraction && !IsClosurePassedTrivially) + continue; + // Get all non-failure exit BBs in the Apply Callee if our partial apply // is guaranteed. If we do not understand one of the exit BBs, bail. // @@ -969,11 +1069,12 @@ void SILClosureSpecializerTransform::gatherCallSites( // // However, thin_to_thick_function closures don't have a context and // don't need to be released. + bool OnlyHaveThinToThickClosure = + isa(ClosureInst) && !HaveUsedReabstraction; + llvm::TinyPtrVector NonFailureExitBBs; - if ((ClosureParamInfo.isGuaranteed() || ClosureParamInfo.getType() - ->castTo() - ->isTrivialNoEscape()) && - !isa(ClosureInst) && + if ((ClosureParamInfo.isGuaranteed() || IsClosurePassedTrivially) && + !OnlyHaveThinToThickClosure && !findAllNonFailureExitBBs(ApplyCallee, NonFailureExitBBs)) { continue; } @@ -983,6 +1084,10 @@ void SILClosureSpecializerTransform::gatherCallSites( if (!CInfo) CInfo = new ClosureInfo(ClosureInst); + // Mark the reabstraction closures as used. + if (HaveUsedReabstraction) + markReabstractionPartialApplyAsUsed(ClosureInst, Use->get(), + UsedReabstractionClosure); // Now we know that CSDesc is profitable to specialize. Add it to our // call site list. CInfo->CallSites.push_back( diff --git a/lib/SILOptimizer/IPO/EagerSpecializer.cpp b/lib/SILOptimizer/IPO/EagerSpecializer.cpp index a50fd5e07ad1b..fb60a653ec16a 100644 --- a/lib/SILOptimizer/IPO/EagerSpecializer.cpp +++ b/lib/SILOptimizer/IPO/EagerSpecializer.cpp @@ -153,7 +153,7 @@ emitApplyWithRethrow(SILBuilder &Builder, SILLocation Loc, SILValue FuncRef, CanSILFunctionType CanSILFuncTy, - SubstitutionList Subs, + SubstitutionMap Subs, ArrayRef CallArgs, void (*EmitCleanup)(SILBuilder&, SILLocation)) { @@ -174,7 +174,7 @@ emitApplyWithRethrow(SILBuilder &Builder, Builder.createBuiltin(Loc, Builder.getASTContext().getIdentifier("willThrow"), Builder.getModule().Types.getEmptyTupleType(), - SubstitutionList(), + SubstitutionMap(), {Error}); EmitCleanup(Builder, Loc); @@ -203,7 +203,7 @@ emitInvocation(SILBuilder &Builder, auto *FuncRefInst = Builder.createFunctionRef(Loc, CalleeFunc); auto CanSILFuncTy = CalleeFunc->getLoweredFunctionType(); auto CalleeSubstFnTy = CanSILFuncTy; - SubstitutionList Subs; + SubstitutionMap Subs; if (CanSILFuncTy->isPolymorphic()) { // Create a substituted callee type. assert(CanSILFuncTy == ReInfo.getSpecializedType() && @@ -228,9 +228,9 @@ emitInvocation(SILBuilder &Builder, // where introduces a new archetype with the given // constraints. if (ReInfo.getSpecializedType()->isPolymorphic()) { - Subs = ReInfo.getCallerParamSubstitutions(); + Subs = ReInfo.getCallerParamSubstitutionMap(); CalleeSubstFnTy = CanSILFuncTy->substGenericArgs( - Builder.getModule(), ReInfo.getCallerParamSubstitutions()); + Builder.getModule(), ReInfo.getCallerParamSubstitutionMap()); assert(!CalleeSubstFnTy->isPolymorphic() && "Substituted callee type should not be polymorphic"); assert(!CalleeSubstFnTy->hasTypeParameter() && @@ -345,8 +345,7 @@ void EagerDispatch::emitDispatchTo(SILFunction *NewFunc) { // SubstitutableTypes, skipping DependentTypes. auto GenericSig = GenericFunc->getLoweredFunctionType()->getGenericSignature(); - auto SubMap = GenericSig->getSubstitutionMap( - ReInfo.getClonerParamSubstitutions()); + auto SubMap = ReInfo.getClonerParamSubstitutionMap(); for (auto ParamTy : GenericSig->getSubstitutableParams()) { auto Replacement = Type(ParamTy).subst(SubMap); assert(!Replacement->hasTypeParameter()); @@ -458,6 +457,13 @@ emitTypeCheck(SILBasicBlock *FailedTypeCheckBB, SubstitutableType *ParamTy, Builder.emitBlock(SuccessBB); } +static SubstitutionMap getSingleSubstititutionMap(SILFunction *F, + Type Ty) { + return F->getGenericEnvironment()->getGenericSignature()-> + getSubstitutionMap([&](SubstitutableType *type) { return Ty; }, + MakeAbstractConformanceForGenericType()); +} + void EagerDispatch::emitIsTrivialCheck(SILBasicBlock *FailedTypeCheckBB, SubstitutableType *ParamTy, Type SubTy, LayoutConstraint Layout) { @@ -467,11 +473,11 @@ void EagerDispatch::emitIsTrivialCheck(SILBasicBlock *FailedTypeCheckBB, auto GenericMT = Builder.createMetatype( Loc, getThickMetatypeType(ContextTy->getCanonicalType())); auto BoolTy = SILType::getBuiltinIntegerType(1, Ctx); - Substitution Sub(ContextTy, {}); + SubstitutionMap SubMap = getSingleSubstititutionMap(GenericFunc, ContextTy); // Emit a check that it is a pod object. auto IsPOD = Builder.createBuiltin(Loc, Ctx.getIdentifier("ispod"), BoolTy, - Sub, {GenericMT}); + SubMap, {GenericMT}); auto *SuccessBB = Builder.getFunction().createBasicBlock(); Builder.createCondBranch(Loc, IsPOD, SuccessBB, FailedTypeCheckBB); Builder.emitBlock(SuccessBB); @@ -493,9 +499,9 @@ void EagerDispatch::emitTrivialAndSizeCheck(SILBasicBlock *FailedTypeCheckBB, auto WordTy = SILType::getBuiltinWordType(Ctx); auto BoolTy = SILType::getBuiltinIntegerType(1, Ctx); - Substitution Sub(ContextTy, {}); + SubstitutionMap SubMap = getSingleSubstititutionMap(GenericFunc, ContextTy); auto ParamSize = Builder.createBuiltin(Loc, Ctx.getIdentifier("sizeof"), - WordTy, Sub, { GenericMT }); + WordTy, SubMap, { GenericMT }); auto LayoutSize = Builder.createIntegerLiteral(Loc, WordTy, Layout->getTrivialSizeInBytes()); const char *CmpOpName = Layout->isFixedSizeTrivial() ? "cmp_eq" : "cmp_le"; @@ -510,7 +516,7 @@ void EagerDispatch::emitTrivialAndSizeCheck(SILBasicBlock *FailedTypeCheckBB, // Emit a check that it is a pod object. // TODO: Perform this check before all the fixed size checks! auto IsPOD = Builder.createBuiltin(Loc, Ctx.getIdentifier("ispod"), - BoolTy, Sub, { GenericMT }); + BoolTy, SubMap, { GenericMT }); auto *SuccessBB2 = Builder.getFunction().createBasicBlock(); Builder.createCondBranch(Loc, IsPOD, SuccessBB2, FailedTypeCheckBB); Builder.emitBlock(SuccessBB2); @@ -528,13 +534,13 @@ void EagerDispatch::emitRefCountedObjectCheck(SILBasicBlock *FailedTypeCheckBB, auto Int8Ty = SILType::getBuiltinIntegerType(8, Ctx); auto BoolTy = SILType::getBuiltinIntegerType(1, Ctx); - Substitution Sub(ContextTy, {}); + SubstitutionMap SubMap = getSingleSubstititutionMap(GenericFunc, ContextTy); // Emit a check that it is a reference-counted object. // TODO: Perform this check before all fixed size checks. // FIXME: What builtin do we use to check it???? auto CanBeClass = Builder.createBuiltin( - Loc, Ctx.getIdentifier("canBeClass"), Int8Ty, Sub, {GenericMT}); + Loc, Ctx.getIdentifier("canBeClass"), Int8Ty, SubMap, {GenericMT}); auto ClassConst = Builder.createIntegerLiteral(Loc, Int8Ty, 1); auto Cmp1 = @@ -563,7 +569,8 @@ void EagerDispatch::emitRefCountedObjectCheck(SILBasicBlock *FailedTypeCheckBB, Builder.emitBlock(IsClassCheckBB); auto *FRI = Builder.createFunctionRef(Loc, IsClassF); - auto IsClassRuntimeCheck = Builder.createApply(Loc, FRI, {Sub}, {GenericMT}, + auto IsClassRuntimeCheck = Builder.createApply(Loc, FRI, SubMap, + {GenericMT}, /* isNonThrowing */ false); // Extract the i1 from the Bool struct. StructDecl *BoolStruct = cast(Ctx.getBoolDecl()); @@ -616,7 +623,7 @@ emitArgumentConversion(SmallVectorImpl &CallArgs) { auto CalleeSubstFnTy = CanSILFuncTy; if (CanSILFuncTy->isPolymorphic()) { CalleeSubstFnTy = CanSILFuncTy->substGenericArgs( - Builder.getModule(), ReInfo.getCallerParamSubstitutions()); + Builder.getModule(), ReInfo.getCallerParamSubstitutionMap()); assert(!CalleeSubstFnTy->isPolymorphic() && "Substituted callee type should not be polymorphic"); assert(!CalleeSubstFnTy->hasTypeParameter() && @@ -711,7 +718,7 @@ static SILFunction *eagerSpecialize(SILFunction *GenericFunc, Serialized = IsSerializable; GenericFuncSpecializer - FuncSpecializer(GenericFunc, ReInfo.getClonerParamSubstitutions(), + FuncSpecializer(GenericFunc, ReInfo.getClonerParamSubstitutionMap(), Serialized, ReInfo); SILFunction *NewFunc = FuncSpecializer.trySpecialization(); diff --git a/lib/SILOptimizer/IPO/GlobalOpt.cpp b/lib/SILOptimizer/IPO/GlobalOpt.cpp index 6c2c004e7df27..31fafa08e0f60 100644 --- a/lib/SILOptimizer/IPO/GlobalOpt.cpp +++ b/lib/SILOptimizer/IPO/GlobalOpt.cpp @@ -261,7 +261,7 @@ static SILFunction *getGlobalGetterFunction(SILModule &M, auto refType = M.Types.getLoweredType(varDecl->getInterfaceType()); // Function takes no arguments and returns refType - SILResultInfo Results[] = { SILResultInfo(refType.getSwiftRValueType(), + SILResultInfo Results[] = { SILResultInfo(refType.getASTType(), ResultConvention::Owned) }; SILFunctionType::ExtInfo EInfo; EInfo = EInfo.withRepresentation(SILFunctionType::Representation::Thin); diff --git a/lib/SILOptimizer/IPO/GlobalPropertyOpt.cpp b/lib/SILOptimizer/IPO/GlobalPropertyOpt.cpp index 6b86ad1349fac..9dc68e1a31d20 100644 --- a/lib/SILOptimizer/IPO/GlobalPropertyOpt.cpp +++ b/lib/SILOptimizer/IPO/GlobalPropertyOpt.cpp @@ -346,7 +346,7 @@ void GlobalPropertyOpt::scanInstruction(swift::SILInstruction *Inst) { return; } } else if (auto *TI = dyn_cast(Inst)) { - if (isTupleWithArray(TI->getType().getSwiftRValueType())) { + if (isTupleWithArray(TI->getType().getASTType())) { // Add dependencies from array elements to the tuple itself. for (Operand &Op : TI->getAllOperands()) { SILValue V = Op.get(); @@ -379,7 +379,7 @@ void GlobalPropertyOpt::scanInstruction(swift::SILInstruction *Inst) { // the instruction value to false. for (auto result : Inst->getResults()) { SILType Type = result->getType(); - if (isArrayType(Type) || isTupleWithArray(Type.getSwiftRValueType())) { + if (isArrayType(Type) || isTupleWithArray(Type.getASTType())) { DEBUG(llvm::dbgs() << " value could be non-native array: " << *result); setNotNative(getValueEntry(result)); @@ -400,7 +400,7 @@ void GlobalPropertyOpt::scanInstructions() { for (auto *BBArg : BB.getArguments()) { bool hasPreds = false; SILType Type = BBArg->getType(); - if (isArrayType(Type) || isTupleWithArray(Type.getSwiftRValueType())) { + if (isArrayType(Type) || isTupleWithArray(Type.getASTType())) { for (auto *Pred : BB.getPredecessorBlocks()) { hasPreds = true; auto *Term = Pred->getTerminator(); diff --git a/lib/SILOptimizer/IPO/UsePrespecialized.cpp b/lib/SILOptimizer/IPO/UsePrespecialized.cpp index c3cc418e671eb..2265ecd589546 100644 --- a/lib/SILOptimizer/IPO/UsePrespecialized.cpp +++ b/lib/SILOptimizer/IPO/UsePrespecialized.cpp @@ -77,15 +77,15 @@ bool UsePrespecialized::replaceByPrespecialized(SILFunction &F) { // If this is the case, check if there is a specialization // available for it already and use this specialization // instead of the generic version. - - SubstitutionList Subs = AI.getSubstitutions(); - if (Subs.empty()) + if (!AI.hasSubstitutions()) continue; + SubstitutionMap Subs = AI.getSubstitutionMap(); + // Bail if any generic type parameters are unbound. // TODO: Remove this limitation once public partial specializations // are supported and can be provided by other modules. - if (hasArchetypes(Subs)) + if (Subs.hasArchetypes()) continue; ReabstractionInfo ReInfo(AI, ReferencedF, Subs); diff --git a/lib/SILOptimizer/LoopTransforms/COWArrayOpt.cpp b/lib/SILOptimizer/LoopTransforms/COWArrayOpt.cpp index 6e5c5670ed243..69c9a26b74a07 100644 --- a/lib/SILOptimizer/LoopTransforms/COWArrayOpt.cpp +++ b/lib/SILOptimizer/LoopTransforms/COWArrayOpt.cpp @@ -579,7 +579,7 @@ bool COWArrayOpt::checkSafeArrayAddressUses(UserList &AddressUsers) { for (auto *UseInst : AddressUsers) { - if (isDebugInst(UseInst)) + if (UseInst->isDebugInstruction()) continue; if (auto *AI = dyn_cast(UseInst)) { @@ -698,7 +698,7 @@ bool COWArrayOpt::checkSafeArrayValueUses(UserList &ArrayValueUsers) { if (isa(UseInst)) continue; - if (isDebugInst(UseInst)) + if (UseInst->isDebugInstruction()) continue; // Found an unsafe or unknown user. The Array may escape here. @@ -761,7 +761,7 @@ bool COWArrayOpt::checkSafeArrayElementUse(SILInstruction *UseInst, if (isa(UseInst)) return true; - if (isDebugInst(UseInst)) + if (UseInst->isDebugInstruction()) return true; // If this is an instruction which is a safe array element use if and only if @@ -1394,11 +1394,11 @@ bool COWArrayOpt::hasLoopOnlyDestructorSafeArrayOperations() { // Checking // that all types are the same make guarantees that this cannot happen. if (SameTy.isNull()) { - SameTy = Sem.getSelf()->getType().getSwiftRValueType(); + SameTy = Sem.getSelf()->getType().getASTType(); continue; } - if (Sem.getSelf()->getType().getSwiftRValueType() != SameTy) { + if (Sem.getSelf()->getType().getASTType() != SameTy) { DEBUG(llvm::dbgs() << " (NO) mismatching array types\n"); return ReturnWithCleanup(false); } @@ -1764,7 +1764,7 @@ class ArrayPropertiesAnalysis { bool checkSafeArrayAddressUses(UserList &AddressUsers) { for (auto *UseInst : AddressUsers) { - if (isDebugInst(UseInst)) + if (UseInst->isDebugInstruction()) continue; if (isa(UseInst)) { diff --git a/lib/SILOptimizer/LoopTransforms/LICM.cpp b/lib/SILOptimizer/LoopTransforms/LICM.cpp index 00ad5340fb7f4..e0ec8465f3572 100644 --- a/lib/SILOptimizer/LoopTransforms/LICM.cpp +++ b/lib/SILOptimizer/LoopTransforms/LICM.cpp @@ -59,8 +59,8 @@ static bool mayWriteTo(AliasAnalysis *AA, WriteSet &MayWrites, LoadInst *LI) { /// alias with any memory which is read by \p AI. static bool mayWriteTo(AliasAnalysis *AA, SideEffectAnalysis *SEA, WriteSet &MayWrites, ApplyInst *AI) { - SideEffectAnalysis::FunctionEffects E; - SEA->getEffects(E, AI); + FunctionSideEffects E; + SEA->getCalleeEffects(E, AI); assert(E.getMemBehavior(RetainObserveKind::IgnoreRetains) <= SILInstruction::MemoryBehavior::MayRead && "apply should only read from memory"); @@ -495,8 +495,8 @@ void LoopTreeOptimization::analyzeCurrentLoop( if (auto *AI = dyn_cast(&Inst)) { // In contrast to load instructions, we first collect all read-only // function calls and add them later to SafeReads. - SideEffectAnalysis::FunctionEffects E; - SEA->getEffects(E, AI); + FunctionSideEffects E; + SEA->getCalleeEffects(E, AI); auto MB = E.getMemBehavior(RetainObserveKind::ObserveRetains); if (MB <= SILInstruction::MemoryBehavior::MayRead) diff --git a/lib/SILOptimizer/Mandatory/AccessMarkerElimination.cpp b/lib/SILOptimizer/Mandatory/AccessMarkerElimination.cpp index b6bc09a5d1bda..7f1c121a327c9 100644 --- a/lib/SILOptimizer/Mandatory/AccessMarkerElimination.cpp +++ b/lib/SILOptimizer/Mandatory/AccessMarkerElimination.cpp @@ -19,10 +19,17 @@ /// test cases through the pipeline and exercising SIL verification before all /// passes support access markers. /// +/// This must only run before inlining _semantic calls. If we inline and drop +/// the @_semantics("optimize.sil.preserve_exclusivity") attribute, the inlined +/// markers will be eliminated, but the noninlined markers will not. This would +/// result in inconsistent begin/end_unpaired_access resulting in unpredictable, +/// potentially catastrophic runtime behavior. +/// //===----------------------------------------------------------------------===// #define DEBUG_TYPE "access-marker-elim" #include "swift/Basic/Range.h" +#include "swift/SIL/MemAccessUtils.h" #include "swift/SIL/SILFunction.h" #include "swift/SILOptimizer/PassManager/Transforms.h" #include "llvm/Support/CommandLine.h" @@ -50,42 +57,27 @@ struct AccessMarkerElimination { AccessMarkerElimination(SILFunction *F) : Mod(&F->getModule()), F(F) {} - SILBasicBlock::iterator eraseInst(SILInstruction *inst) { + void notifyErased(SILInstruction *inst) { DEBUG(llvm::dbgs() << "Erasing access marker: " << *inst); removedAny = true; + } + + SILBasicBlock::iterator eraseInst(SILInstruction *inst) { + notifyErased(inst); return inst->getParent()->erase(inst); }; - void replaceBeginAccessUsers(BeginAccessInst *beginAccess); - bool shouldPreserveAccess(SILAccessEnforcement enforcement); // Check if the instruction is a marker that should be eliminated. If so, // updated the SIL, short of erasing the marker itself, and return true. - bool checkAndEliminateMarker(SILInstruction *inst); + SILBasicBlock::iterator checkAndEliminateMarker(SILInstruction *inst); // Entry point called either by the pass by the same name // or as a utility (e.g. during deserialization). bool stripMarkers(); }; -void AccessMarkerElimination::replaceBeginAccessUsers( - BeginAccessInst *beginAccess) { - // Handle all the uses: - while (!beginAccess->use_empty()) { - Operand *op = *beginAccess->use_begin(); - - // Delete any associated end_access instructions. - if (auto endAccess = dyn_cast(op->getUser())) { - endAccess->eraseFromParent(); - - // Forward all other uses to the original address. - } else { - op->set(beginAccess->getSource()); - } - } -} - bool AccessMarkerElimination::shouldPreserveAccess( SILAccessEnforcement enforcement) { if (!EnableOptimizedAccessMarkers) @@ -101,16 +93,26 @@ bool AccessMarkerElimination::shouldPreserveAccess( } } -// Check if the instruction is a marker that should be eliminated. If so, -// updated the SIL, short of erasing the marker itself, and return true. -bool AccessMarkerElimination::checkAndEliminateMarker(SILInstruction *inst) { +// Check if the instruction is a marker that should be eliminated. If so, delete +// the begin_access along with all associated end_access and a valid instruction +// iterator pointing to the first remaining instruction following the +// begin_access. If the marker is not eliminated, return an iterator pointing to +// the marker. +SILBasicBlock::iterator +AccessMarkerElimination::checkAndEliminateMarker(SILInstruction *inst) { if (auto beginAccess = dyn_cast(inst)) { + // Builtins used by the standard library must emit markers regardless of the + // current compiler options so that any user code that initiates access via + // the standard library is fully enforced. + if (beginAccess->isFromBuiltin()) + return inst->getIterator(); + // Leave dynamic accesses in place, but delete all others. if (shouldPreserveAccess(beginAccess->getEnforcement())) - return false; + return inst->getIterator(); - replaceBeginAccessUsers(beginAccess); - return true; + notifyErased(beginAccess); + return removeBeginAccess(beginAccess); } // end_access instructions will be handled when we process the @@ -119,20 +121,30 @@ bool AccessMarkerElimination::checkAndEliminateMarker(SILInstruction *inst) { // begin_unpaired_access instructions will be directly removed and // simply replaced with their operand. if (auto BUA = dyn_cast(inst)) { + // Builtins used by the standard library must emit markers regardless of the + // current compiler options. + if (BUA->isFromBuiltin()) + return inst->getIterator(); + if (shouldPreserveAccess(BUA->getEnforcement())) - return false; + return inst->getIterator(); - return true; + return eraseInst(BUA); } // end_unpaired_access instructions will be directly removed and // simply replaced with their operand. if (auto EUA = dyn_cast(inst)) { + // Builtins used by the standard library must emit markers regardless of the + // current compiler options. + if (EUA->isFromBuiltin()) + return inst->getIterator(); + if (shouldPreserveAccess(EUA->getEnforcement())) - return false; + return inst->getIterator(); - return true; + return eraseInst(EUA); } - return false; + return inst->getIterator(); } // Top-level per-function entry-point. @@ -144,8 +156,9 @@ bool AccessMarkerElimination::stripMarkers() { // Don't cache the begin iterator since we're reverse iterating. for (auto II = BB.end(); II != BB.begin();) { SILInstruction *inst = &*(--II); - if (checkAndEliminateMarker(inst)) - II = eraseInst(inst); + // checkAndEliminateMarker returns the next non-deleted instruction. The + // following iteration moves the iterator backward. + II = checkAndEliminateMarker(inst); } } return removedAny; diff --git a/lib/SILOptimizer/Mandatory/AddressLowering.cpp b/lib/SILOptimizer/Mandatory/AddressLowering.cpp index 351da23ecf0fe..4e25a9f626988 100644 --- a/lib/SILOptimizer/Mandatory/AddressLowering.cpp +++ b/lib/SILOptimizer/Mandatory/AddressLowering.cpp @@ -463,7 +463,7 @@ unsigned OpaqueStorageAllocation::insertIndirectReturnArgs() { ParamDecl(VarDecl::Specifier::InOut, SourceLoc(), SourceLoc(), ctx.getIdentifier("$return_value"), SourceLoc(), ctx.getIdentifier("$return_value"), - bodyResultTy.getSwiftRValueType(), pass.F->getDeclContext()); + bodyResultTy.getASTType(), pass.F->getDeclContext()); pass.F->begin()->insertFunctionArgument(argIdx, bodyResultTy.getAddressType(), @@ -981,7 +981,7 @@ void ApplyRewriter::convertApplyWithIndirectResults() { switch (origCallInst->getKind()) { case SILInstructionKind::ApplyInst: newCallInst = callBuilder.createApply( - loc, apply.getCallee(), apply.getSubstitutions(), newCallArgs, + loc, apply.getCallee(), apply.getSubstitutionMap(), newCallArgs, cast(origCallInst)->isNonThrowing(), nullptr); break; case SILInstructionKind::TryApplyInst: diff --git a/lib/SILOptimizer/Mandatory/CMakeLists.txt b/lib/SILOptimizer/Mandatory/CMakeLists.txt index 6eeef3a1469b0..8819fe1f683f6 100644 --- a/lib/SILOptimizer/Mandatory/CMakeLists.txt +++ b/lib/SILOptimizer/Mandatory/CMakeLists.txt @@ -2,6 +2,7 @@ set(MANDATORY_SOURCES Mandatory/AccessEnforcementSelection.cpp Mandatory/AccessMarkerElimination.cpp Mandatory/AddressLowering.cpp + Mandatory/ConstantPropagation.cpp Mandatory/DefiniteInitialization.cpp Mandatory/DIMemoryUseCollector.cpp Mandatory/DIMemoryUseCollectorOwnership.cpp @@ -10,8 +11,11 @@ set(MANDATORY_SOURCES Mandatory/DiagnoseStaticExclusivity.cpp Mandatory/DiagnoseUnreachable.cpp Mandatory/GuaranteedARCOpts.cpp + Mandatory/IRGenPrepare.cpp Mandatory/MandatoryInlining.cpp Mandatory/PredictableMemOpt.cpp - Mandatory/ConstantPropagation.cpp Mandatory/SemanticARCOpts.cpp + Mandatory/ClosureLifetimeFixup.cpp + Mandatory/RawSILInstLowering.cpp + Mandatory/MandatoryOptUtils.cpp PARENT_SCOPE) diff --git a/lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp b/lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp new file mode 100644 index 0000000000000..3bc4861c419f8 --- /dev/null +++ b/lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp @@ -0,0 +1,540 @@ +//===--- ClosureLifetimeFixup.cpp - Fixup the lifetime of closures --------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "closure-lifetime-fixup" + +#include "swift/SIL/DebugUtils.h" +#include "swift/SIL/InstructionUtils.h" +#include "swift/SIL/SILArgument.h" +#include "swift/SIL/SILBuilder.h" +#include "swift/SIL/SILInstruction.h" +#include "swift/SILOptimizer/PassManager/Passes.h" +#include "swift/SILOptimizer/PassManager/Transforms.h" +#include "swift/SILOptimizer/Utils/CFG.h" +#include "swift/SILOptimizer/Utils/Local.h" + +#include "llvm/Support/CommandLine.h" + +llvm::cl::opt DisableConvertEscapeToNoEscapeSwitchEnumPeephole( + "sil-disable-convert-escape-to-noescape-switch-peephole", + llvm::cl::init(false), + llvm::cl::desc( + "Disable the convert_escape_to_noescape switch enum peephole. "), + llvm::cl::Hidden); + +using namespace swift; + +static SILBasicBlock *getOptionalDiamondSuccessor(SwitchEnumInst *SEI) { + auto numSuccs = SEI->getNumSuccessors(); + if (numSuccs != 2) + return nullptr; + auto *SuccSome = SEI->getCase(0).second; + auto *SuccNone = SEI->getCase(1).second; + if (SuccSome->args_size() != 1) + std::swap(SuccSome, SuccNone); + + if (SuccSome->args_size() != 1 || SuccNone->args_size() != 0) + return nullptr; + + auto *Succ = SuccSome->getSingleSuccessorBlock(); + if (!Succ) + return nullptr; + + if (SuccNone == Succ) + return Succ; + + SuccNone = SuccNone->getSingleSuccessorBlock(); + if (SuccNone == Succ) + return Succ; + + if (SuccNone == nullptr) + return nullptr; + + SuccNone = SuccNone->getSingleSuccessorBlock(); + if (SuccNone == Succ) + return Succ; + + return nullptr; +} + +/// Extend the lifetime of the convert_escape_to_noescape's operand to the end +/// of the function. +static void extendLifetimeToEndOfFunction(SILFunction &Fn, + ConvertEscapeToNoEscapeInst *Cvt) { + auto EscapingClosure = Cvt->getOperand(); + auto EscapingClosureTy = EscapingClosure->getType(); + auto OptionalEscapingClosureTy = SILType::getOptionalType(EscapingClosureTy); + auto loc = RegularLocation::getAutoGeneratedLocation(); + + SILBuilderWithScope B(Cvt); + auto NewCvt = B.createConvertEscapeToNoEscape( + Cvt->getLoc(), Cvt->getOperand(), Cvt->getType(), false, true); + Cvt->replaceAllUsesWith(NewCvt); + Cvt->eraseFromParent(); + Cvt = NewCvt; + + // Create an alloc_stack Optional<() -> ()> at the beginning of the function. + AllocStackInst *Slot; + auto &Context = Cvt->getModule().getASTContext(); + { + SILBuilderWithScope B(Fn.getEntryBlock()->begin()); + Slot = B.createAllocStack(loc, OptionalEscapingClosureTy); + auto *NoneDecl = Context.getOptionalNoneDecl(); + // Store None to it. + B.createStore( + loc, B.createEnum(loc, SILValue(), NoneDecl, OptionalEscapingClosureTy), + Slot, StoreOwnershipQualifier::Init); + } + // Insert a copy before the convert_escape_to_noescape and store it to the + // alloc_stack location. + { + SILBuilderWithScope B(Cvt); + auto *SomeDecl = Context.getOptionalSomeDecl(); + B.createDestroyAddr(loc, Slot); + auto ClosureCopy = B.createCopyValue(loc, EscapingClosure); + B.createStore( + loc, + B.createEnum(loc, ClosureCopy, SomeDecl, OptionalEscapingClosureTy), + Slot, StoreOwnershipQualifier::Init); + } + // Insert destroys at the function exits. + SmallVector ExitingBlocks; + Fn.findExitingBlocks(ExitingBlocks); + for (auto *Exit : ExitingBlocks) { + auto *Term = Exit->getTerminator(); + SILBuilderWithScope B(Term); + B.setInsertionPoint(Term); + B.createDestroyAddr(loc, Slot); + B.createDeallocStack(loc, Slot); + } +} + +static SILInstruction *lookThroughRebastractionUsers( + SILInstruction *Inst, + llvm::DenseMap &Memoized) { + + if (Inst == nullptr) + return nullptr; + + // Try a cached lookup. + auto Res = Memoized.find(Inst); + if (Res != Memoized.end()) + return Res->second; + + // Cache recursive results. + auto memoizeResult = [&] (SILInstruction *from, SILInstruction *toResult) { + Memoized[from] = toResult; + return toResult; + }; + + // If we have a convert_function, just look at its user. + if (auto *Cvt = dyn_cast(Inst)) + return memoizeResult(Inst, lookThroughRebastractionUsers( + getSingleNonDebugUser(Cvt), Memoized)); + if (auto *Cvt = dyn_cast(Inst)) + return memoizeResult(Inst, lookThroughRebastractionUsers( + getSingleNonDebugUser(Cvt), Memoized)); + + // If we have a partial_apply user look at its single (non release) user. + auto *PA = dyn_cast(Inst); + if (!PA) return Inst; + + SILInstruction *SingleNonDebugNonRefCountUser = nullptr; + for (auto *Usr : getNonDebugUses(PA)) { + auto *I = Usr->getUser(); + if (onlyAffectsRefCount(I)) + continue; + if (SingleNonDebugNonRefCountUser) { + SingleNonDebugNonRefCountUser = nullptr; + break; + } + SingleNonDebugNonRefCountUser = I; + } + + return memoizeResult(Inst, lookThroughRebastractionUsers( + SingleNonDebugNonRefCountUser, Memoized)); +} + +static bool tryExtendLifetimeToLastUse( + ConvertEscapeToNoEscapeInst *Cvt, + llvm::DenseMap &Memoized) { + // Don't optimize converts that might have been escaped by the function call + // (materializeForSet 'escapes' its arguments into the writeback buffer). + if (Cvt->isEscapedByUser()) + return false; + + // If there is a single user that is an apply this is simple: extend the + // lifetime of the operand until after the apply. + auto SingleUser = lookThroughRebastractionUsers(Cvt, Memoized); + if (!SingleUser) + return false; + + // Handle an apply. + if (auto SingleApplyUser = FullApplySite::isa(SingleUser)) { + // FIXME: Don't know how-to handle begin_apply/end_apply yet. + if (auto *Begin = + dyn_cast(SingleApplyUser.getInstruction())) { + return false; + } + + auto loc = RegularLocation::getAutoGeneratedLocation(); + + // Insert a copy at the convert_escape_to_noescape [not_guaranteed] and + // change the instruction to the guaranteed form. + auto EscapingClosure = Cvt->getOperand(); + { + SILBuilderWithScope B(Cvt); + auto NewCvt = B.createConvertEscapeToNoEscape( + Cvt->getLoc(), Cvt->getOperand(), Cvt->getType(), false, true); + Cvt->replaceAllUsesWith(NewCvt); + Cvt->eraseFromParent(); + Cvt = NewCvt; + } + + SILBuilderWithScope B2(Cvt); + auto ClosureCopy = B2.createCopyValue(loc, EscapingClosure); + + // Insert a destroy after the apply. + if (auto *Apply = dyn_cast(SingleApplyUser.getInstruction())) { + auto InsertPt = std::next(SILBasicBlock::iterator(Apply)); + SILBuilderWithScope B3(InsertPt); + B3.createDestroyValue(loc, ClosureCopy); + + } else if (auto *Try = + dyn_cast(SingleApplyUser.getInstruction())) { + for (auto *SuccBB : Try->getSuccessorBlocks()) { + SILBuilderWithScope B3(SuccBB->begin()); + B3.createDestroyValue(loc, ClosureCopy); + } + } else { + llvm_unreachable("Unknown FullApplySite instruction kind"); + } + return true; + } + return false; +} + +/// Ensure the lifetime of the closure accross an +/// +/// optional<@escaping () -> ()> to +/// optional<@noescape @convention(block) () -> ()> +/// +/// conversion and its use. +/// +/// The pattern this is looking for +/// switch_enum %closure +/// / \ +/// convert_escape_to_noescape nil +/// switch_enum +/// / \ +/// convertToBlock nil +/// \ / +/// (%convertOptionalBlock :) +/// We will insert a copy_value of the original %closure before the two +/// diamonds. And a destroy of %closure at the last destroy of +/// %convertOptionalBlock. +static bool trySwitchEnumPeephole(ConvertEscapeToNoEscapeInst *Cvt) { + // Don't optimize converts that might have been escaped by the function call + // (materializeForSet 'escapes' its arguments into the writeback buffer). + if (Cvt->isEscapedByUser()) + return false; + + auto *blockArg = dyn_cast(Cvt->getOperand()); + if (!blockArg) + return false; + auto *PredBB = Cvt->getParent()->getSinglePredecessorBlock(); + if (!PredBB) + return false; + auto *ConvertSuccessorBlock = Cvt->getParent()->getSingleSuccessorBlock(); + if (!ConvertSuccessorBlock) + return false; + auto *SwitchEnum1 = dyn_cast(PredBB->getTerminator()); + if (!SwitchEnum1) + return false; + auto *DiamondSucc = getOptionalDiamondSuccessor(SwitchEnum1); + if (!DiamondSucc) + return false; + auto *SwitchEnum2 = dyn_cast(DiamondSucc->getTerminator()); + if (!SwitchEnum2) + return false; + auto *DiamondSucc2 = getOptionalDiamondSuccessor(SwitchEnum2); + if (!DiamondSucc2) + return false; + if (DiamondSucc2->getNumArguments() != 1) + return false; + + // Look for the last and only destroy. + SILInstruction *onlyDestroy = [&]() -> SILInstruction * { + SILInstruction *lastDestroy = nullptr; + for (auto *Use : DiamondSucc2->getArgument(0)->getUses()) { + SILInstruction *Usr = Use->getUser(); + if (isa(Usr) || isa(Usr) || + isa(Usr)) { + if (lastDestroy) + return nullptr; + lastDestroy = Usr; + } + } + return lastDestroy; + }(); + if (!onlyDestroy) + return false; + + // Replace the convert_escape_to_noescape instruction. + { + SILBuilderWithScope B(Cvt); + auto NewCvt = B.createConvertEscapeToNoEscape( + Cvt->getLoc(), Cvt->getOperand(), Cvt->getType(), false, true); + Cvt->replaceAllUsesWith(NewCvt); + Cvt->eraseFromParent(); + Cvt = NewCvt; + } + + // Extend the lifetime. + SILBuilderWithScope B(SwitchEnum1); + auto loc = RegularLocation::getAutoGeneratedLocation(); + auto copy = + B.createCopyValue(loc, SwitchEnum1->getOperand()); + B.setInsertionPoint(onlyDestroy); + B.createDestroyValue(loc, copy); + return true; +} + +/// Look for a single destroy user and possibly unowned apply uses. +static SILInstruction *getOnlyDestroy(CopyBlockWithoutEscapingInst *CB) { + SILInstruction *onlyDestroy = nullptr; + + for (auto *Use : getNonDebugUses(CB)) { + SILInstruction *Inst = Use->getUser(); + + // If this an apply use, only handle unowned parameters. + if (auto Apply = FullApplySite::isa(Inst)) { + SILArgumentConvention Conv = + Apply.getArgumentConvention(Apply.getCalleeArgIndex(*Use)); + if (Conv != SILArgumentConvention::Direct_Unowned) + return nullptr; + continue; + } + + // We have already seen one destroy. + if (onlyDestroy) + return nullptr; + + if (isa(Inst) || isa(Inst) || + isa(Inst)) { + onlyDestroy = Inst; + continue; + } + + // Some other instruction. + return nullptr; + } + + if (!onlyDestroy) + return nullptr; + + // Now look at whether the dealloc_stack or the destroy postdominates and + // return the post dominator. + auto *BlockInit = dyn_cast(CB->getBlock()); + if (!BlockInit) + return nullptr; + + auto *AS = dyn_cast(BlockInit->getBlockStorage()); + if (!AS) + return nullptr; + auto *Dealloc = AS->getSingleDeallocStack(); + if (!Dealloc || Dealloc->getParent() != onlyDestroy->getParent()) + return nullptr; + + // Return the later instruction. + for (auto It = SILBasicBlock::iterator(onlyDestroy), + E = Dealloc->getParent()->end(); + It != E; ++It) { + if (&*It == Dealloc) + return Dealloc; + } + return onlyDestroy; +} + +/// Lower a copy_block_without_escaping instruction. +/// +/// This involves replacing: +/// +/// %copy = copy_block_without_escaping %block withoutEscaping %closure +/// +/// ... +/// destroy_value %copy +/// +/// by (roughly) the instruction sequence: +/// +/// %copy = copy_block %block +/// +/// ... +/// destroy_value %copy +/// %e = is_escaping %closure +/// cond_fail %e +/// destroy_value %closure +static bool fixupCopyBlockWithoutEscaping(CopyBlockWithoutEscapingInst *CB) { + SmallVector LifetimeEndPoints; + + // Find the end of the lifetime of the copy_block_without_escaping + // instruction. + auto &Fn = *CB->getFunction(); + auto *SingleDestroy = getOnlyDestroy(CB); + SmallVector ExitingBlocks; + Fn.findExitingBlocks(ExitingBlocks); + if (SingleDestroy) { + LifetimeEndPoints.push_back( + &*std::next(SILBasicBlock::iterator(SingleDestroy))); + } else { + // Otherwise, conservatively insert verification at the end of the function. + for (auto *Exit : ExitingBlocks) + LifetimeEndPoints.push_back(Exit->getTerminator()); + } + + auto SentinelClosure = CB->getClosure(); + auto Loc = CB->getLoc(); + + SILBuilderWithScope B(CB); + auto *NewCB = B.createCopyBlock(Loc, CB->getBlock()); + CB->replaceAllUsesWith(NewCB); + CB->eraseFromParent(); + + // Create an stack slot for the closure sentinel and store the sentinel (or + // none on other paths. + auto generatedLoc = RegularLocation::getAutoGeneratedLocation(); + AllocStackInst *Slot; + auto &Context = NewCB->getModule().getASTContext(); + auto OptionalEscapingClosureTy = + SILType::getOptionalType(SentinelClosure->getType()); + auto *NoneDecl = Context.getOptionalNoneDecl(); + { + SILBuilderWithScope B(Fn.getEntryBlock()->begin()); + Slot = B.createAllocStack(generatedLoc, OptionalEscapingClosureTy); + // Store None to it. + B.createStore(generatedLoc, + B.createEnum(generatedLoc, SILValue(), NoneDecl, + OptionalEscapingClosureTy), + Slot, StoreOwnershipQualifier::Init); + } + { + SILBuilderWithScope B(NewCB); + // Store the closure sentinel (the copy_block_without_escaping closure + // operand consumed at +1, so we don't need a copy) to it. + B.createDestroyAddr(generatedLoc, Slot); // We could be in a loop. + auto *SomeDecl = Context.getOptionalSomeDecl(); + B.createStore(generatedLoc, + B.createEnum(generatedLoc, SentinelClosure, SomeDecl, + OptionalEscapingClosureTy), + Slot, StoreOwnershipQualifier::Init); + } + + for (auto LifetimeEndPoint : LifetimeEndPoints) { + SILBuilderWithScope B(LifetimeEndPoint); + auto IsEscaping = + B.createIsEscapingClosure(Loc, B.createLoadBorrow(generatedLoc, Slot), + IsEscapingClosureInst::ObjCEscaping); + B.createCondFail(Loc, IsEscaping); + B.createDestroyAddr(generatedLoc, Slot); + // Store None to it. + B.createStore(generatedLoc, + B.createEnum(generatedLoc, SILValue(), NoneDecl, + OptionalEscapingClosureTy), + Slot, StoreOwnershipQualifier::Init); + } + + // Insert the dealloc_stack in all exiting blocks. + for (auto *ExitBlock: ExitingBlocks) { + auto *Terminator = ExitBlock->getTerminator(); + SILBuilderWithScope B(Terminator); + B.createDeallocStack(generatedLoc, Slot); + } + + return true; +} + +static bool fixupClosureLifetimes(SILFunction &Fn) { + bool Changed = false; + + // tryExtendLifetimeToLastUse uses a cache of recursive instruction use + // queries. + llvm::DenseMap MemoizedQueries; + + for (auto &BB : Fn) { + auto I = BB.begin(); + while (I != BB.end()) { + SILInstruction *Inst = &*I; + ++I; + + // Handle, copy_block_without_escaping instructions. + if (auto *CB = dyn_cast(Inst)) { + Changed |= fixupCopyBlockWithoutEscaping(CB); + continue; + } + + // Otherwise, look at convert_escape_to_noescape [not_guaranteed] + // instructions. + auto *Cvt = dyn_cast(Inst); + if (!Cvt || Cvt->isLifetimeGuaranteed()) + continue; + + // First try to peephole a known pattern. + if (!DisableConvertEscapeToNoEscapeSwitchEnumPeephole && + trySwitchEnumPeephole(Cvt)) { + Changed |= true; + continue; + } + + if (tryExtendLifetimeToLastUse(Cvt, MemoizedQueries)) { + Changed |= true; + continue; + } + + // Otherwise, extend the lifetime of the operand to the end of the + // function. + extendLifetimeToEndOfFunction(Fn, Cvt); + Changed |= true; + } + } + return Changed; +} + +/// Fix-up the lifetime of the escaping closure argument of +/// convert_escape_to_noescape [not_guaranteed] instructions. +/// +/// convert_escape_to_noescape [not_guaranteed] assume that someone guarantees +/// the lifetime of the operand for the duration of the trivial closure result. +/// SILGen does not guarantee this for '[not_guaranteed]' instructions so we +/// ensure it here. +namespace { +class ClosureLifetimeFixup : public SILFunctionTransform { + + /// The entry point to the transformation. + void run() override { + // Don't rerun diagnostics on deserialized functions. + if (getFunction()->wasDeserializedCanonical()) + return; + + // Fixup convert_escape_to_noescape [not_guaranteed] and + // copy_block_without_escaping instructions. + if (fixupClosureLifetimes(*getFunction())) + invalidateAnalysis(SILAnalysis::InvalidationKind::FunctionBody); + DEBUG(getFunction()->verify()); + + } + +}; +} // end anonymous namespace + +SILTransform *swift::createClosureLifetimeFixup() { + return new ClosureLifetimeFixup(); +} diff --git a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp index 6df2adb3b61fe..3bf4636d568b6 100644 --- a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp +++ b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp @@ -247,36 +247,46 @@ namespace { /// This is the main entry point for the use walker. It collects uses from /// the address and the refcount result of the allocation. - void collectFrom() { - if (auto *ABI = TheMemory.getContainer()) - collectContainerUses(ABI); - else - collectUses(TheMemory.getAddress(), 0); - - // Collect information about the retain count result as well. - for (auto UI : TheMemory.MemoryInst->getUses()) { - auto *User = UI->getUser(); - - // If this is a release or dealloc_stack, then remember it as such. - if (isa(User) || isa(User) || - isa(User)) { - Releases.push_back(User); - } - } - } + LLVM_NODISCARD bool collectFrom(); private: - void collectUses(SILValue Pointer, unsigned BaseEltNo); - void collectContainerUses(AllocBoxInst *ABI); + LLVM_NODISCARD bool collectUses(SILValue Pointer, unsigned BaseEltNo); + LLVM_NODISCARD bool collectContainerUses(AllocBoxInst *ABI); void addElementUses(unsigned BaseEltNo, SILType UseTy, SILInstruction *User, DIUseKind Kind); - void collectTupleElementUses(TupleElementAddrInst *TEAI, - unsigned BaseEltNo); - void collectStructElementUses(StructElementAddrInst *SEAI, - unsigned BaseEltNo); + LLVM_NODISCARD bool collectTupleElementUses(TupleElementAddrInst *TEAI, + unsigned BaseEltNo); + LLVM_NODISCARD bool collectStructElementUses(StructElementAddrInst *SEAI, + unsigned BaseEltNo); }; } // end anonymous namespace +bool ElementUseCollector::collectFrom() { + bool shouldOptimize = false; + + if (auto *ABI = TheMemory.getContainer()) { + shouldOptimize = collectContainerUses(ABI); + } else { + shouldOptimize = collectUses(TheMemory.getAddress(), 0); + } + + if (!shouldOptimize) + return false; + + // Collect information about the retain count result as well. + for (auto UI : TheMemory.MemoryInst->getUses()) { + auto *User = UI->getUser(); + + // If this is a release or dealloc_stack, then remember it as such. + if (isa(User) || isa(User) || + isa(User)) { + Releases.push_back(User); + } + } + + return true; +} + /// addElementUses - An operation (e.g. load, store, inout use, etc) on a value /// acts on all of the aggregate elements in that value. For example, a load /// of $*(Int,Int) is a use of both Int elements of the tuple. This is a helper @@ -295,8 +305,8 @@ void ElementUseCollector::addElementUses(unsigned BaseEltNo, SILType UseTy, /// Given a tuple_element_addr or struct_element_addr, compute the new /// BaseEltNo implicit in the selected member, and recursively add uses of /// the instruction. -void ElementUseCollector:: -collectTupleElementUses(TupleElementAddrInst *TEAI, unsigned BaseEltNo) { +bool ElementUseCollector::collectTupleElementUses(TupleElementAddrInst *TEAI, + unsigned BaseEltNo) { // If we're walking into a tuple within a struct or enum, don't adjust the // BaseElt. The uses hanging off the tuple_element_addr are going to be @@ -314,20 +324,20 @@ collectTupleElementUses(TupleElementAddrInst *TEAI, unsigned BaseEltNo) { BaseEltNo += getElementCountRec(Module, EltTy); } } - - collectUses(TEAI, BaseEltNo); + + return collectUses(TEAI, BaseEltNo); } -void ElementUseCollector::collectStructElementUses(StructElementAddrInst *SEAI, +bool ElementUseCollector::collectStructElementUses(StructElementAddrInst *SEAI, unsigned BaseEltNo) { // Generally, we set the "InStructSubElement" flag and recursively process // the uses so that we know that we're looking at something within the // current element. llvm::SaveAndRestore X(InStructSubElement, true); - collectUses(SEAI, BaseEltNo); + return collectUses(SEAI, BaseEltNo); } -void ElementUseCollector::collectContainerUses(AllocBoxInst *ABI) { +bool ElementUseCollector::collectContainerUses(AllocBoxInst *ABI) { for (Operand *UI : ABI->getUses()) { auto *User = UI->getUser(); @@ -340,16 +350,21 @@ void ElementUseCollector::collectContainerUses(AllocBoxInst *ABI) { continue; if (auto project = dyn_cast(User)) { - collectUses(project, project->getFieldIndex()); + if (!collectUses(project, project->getFieldIndex())) + return false; continue; } // Other uses of the container are considered escapes of the values. - for (unsigned field : indices(ABI->getBoxType()->getLayout()->getFields())) + for (unsigned field : + indices(ABI->getBoxType()->getLayout()->getFields())) { addElementUses(field, ABI->getBoxType()->getFieldType(ABI->getModule(), field), User, DIUseKind::Escape); + } } + + return true; } // Returns true when the instruction represents added instrumentation for @@ -367,7 +382,7 @@ static bool isSanitizerInstrumentation(SILInstruction *Instruction, return false; } -void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { +bool ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { assert(Pointer->getType().isAddress() && "Walked through the pointer to the value?"); SILType PointeeType = Pointer->getType().getObjectType(); @@ -383,19 +398,22 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { // struct_element_addr P, #field indexes into the current element. if (auto *SEAI = dyn_cast(User)) { - collectStructElementUses(SEAI, BaseEltNo); + if (!collectStructElementUses(SEAI, BaseEltNo)) + return false; continue; } // Instructions that compute a subelement are handled by a helper. if (auto *TEAI = dyn_cast(User)) { - collectTupleElementUses(TEAI, BaseEltNo); + if (!collectTupleElementUses(TEAI, BaseEltNo)) + return false; continue; } // Look through begin_access. if (auto I = dyn_cast(User)) { - collectUses(I, BaseEltNo); + if (!collectUses(I, BaseEltNo)) + return false; continue; } @@ -439,7 +457,7 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { continue; } - if (auto *SWI = dyn_cast(User)) + if (auto *SWI = dyn_cast(User)) { if (UI->getOperandNumber() == 1) { DIUseKind Kind; if (InStructSubElement) @@ -451,8 +469,9 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { Uses.push_back(DIMemoryUse(User, Kind, BaseEltNo, 1)); continue; } + } - if (auto *SUI = dyn_cast(User)) + if (auto *SUI = dyn_cast(User)) { if (UI->getOperandNumber() == 1) { DIUseKind Kind; if (InStructSubElement) @@ -464,6 +483,7 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { Uses.push_back(DIMemoryUse(User, Kind, BaseEltNo, 1)); continue; } + } if (auto *CAI = dyn_cast(User)) { // If this is a copy of a tuple, we should scalarize it so that we don't @@ -505,7 +525,13 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { // If this is an out-parameter, it is like a store. unsigned NumIndirectResults = substConv.getNumIndirectSILResults(); if (ArgumentNumber < NumIndirectResults) { - assert(!InStructSubElement && "We're initializing sub-members?"); + // We do not support initializing sub members. This is an old + // restriction from when this code was used by Definite + // Initialization. With proper code review, we can remove this, but for + // now, lets be conservative. + if (InStructSubElement) { + return false; + } addElementUses(BaseEltNo, PointeeType, User, DIUseKind::Initialization); continue; @@ -550,20 +576,27 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { // be explicitly initialized by a copy_addr or some other use of the // projected address). if (auto I = dyn_cast(User)) { - assert(!InStructSubElement && - "init_enum_data_addr shouldn't apply to struct subelements"); + // If we are in a struct already, bail. With proper analysis, we should be + // able to do this optimization. + if (InStructSubElement) { + return false; + } + // Keep track of the fact that we're inside of an enum. This informs our // recursion that tuple stores are not scalarized outside, and that stores // should not be treated as partial stores. llvm::SaveAndRestore X(InEnumSubElement, true); - collectUses(I, BaseEltNo); + if (!collectUses(I, BaseEltNo)) + return false; continue; } // init_existential_addr is modeled as an initialization store. if (isa(User)) { - assert(!InStructSubElement && - "init_existential_addr should not apply to struct subelements"); + // init_existential_addr should not apply to struct subelements. + if (InStructSubElement) { + return false; + } Uses.push_back(DIMemoryUse(User, DIUseKind::Initialization, BaseEltNo, 1)); continue; @@ -571,8 +604,10 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { // inject_enum_addr is modeled as an initialization store. if (isa(User)) { - assert(!InStructSubElement && - "inject_enum_addr the subelement of a struct unless in a ctor"); + // inject_enum_addr the subelement of a struct unless in a ctor. + if (InStructSubElement) { + return false; + } Uses.push_back(DIMemoryUse(User, DIUseKind::Initialization, BaseEltNo, 1)); continue; @@ -667,16 +702,22 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { // Now that we've scalarized some stuff, recurse down into the newly created // element address computations to recursively process it. This can cause // further scalarization. - for (auto EltPtr : ElementAddrs) - collectTupleElementUses(cast(EltPtr), BaseEltNo); + if (llvm::any_of(ElementAddrs, [&](SILValue V) { + return !collectTupleElementUses(cast(V), + BaseEltNo); + })) { + return false; + } } + + return true; } /// collectDIElementUsesFrom - Analyze all uses of the specified allocation /// instruction (alloc_box, alloc_stack or mark_uninitialized), classifying them /// and storing the information found into the Uses and Releases lists. -void swift::collectDIElementUsesFrom( +bool swift::collectDIElementUsesFrom( const DIMemoryObjectInfo &MemoryInfo, SmallVectorImpl &Uses, SmallVectorImpl &Releases) { - ElementUseCollector(MemoryInfo, Uses, Releases).collectFrom(); + return ElementUseCollector(MemoryInfo, Uses, Releases).collectFrom(); } diff --git a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h index 1a3021a0f093b..56f236fa239c3 100644 --- a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h +++ b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h @@ -21,12 +21,14 @@ #define SWIFT_SILOPTIMIZER_MANDATORY_DIMEMORYUSECOLLECTOR_H #include "swift/Basic/LLVM.h" -#include "llvm/ADT/APInt.h" #include "swift/SIL/SILInstruction.h" #include "swift/SIL/SILType.h" +#include "llvm/ADT/APInt.h" +#include "llvm/Support/Compiler.h" namespace swift { - class SILBuilder; + +class SILBuilder; /// DIMemoryObjectInfo - This struct holds information about the memory object /// being analyzed that is required to correctly break it down into elements. @@ -73,7 +75,7 @@ class DIMemoryObjectInfo { SILInstruction *getFunctionEntryPoint() const; CanType getType() const { - return MemorySILType.getSwiftRValueType(); + return MemorySILType.getASTType(); } SingleValueInstruction *getAddress() const { @@ -187,9 +189,10 @@ struct DIMemoryUse { /// collectDIElementUsesFrom - Analyze all uses of the specified allocation /// instruction (alloc_box, alloc_stack or mark_uninitialized), classifying them /// and storing the information found into the Uses and Releases lists. -void collectDIElementUsesFrom(const DIMemoryObjectInfo &MemoryInfo, - SmallVectorImpl &Uses, - SmallVectorImpl &Releases); +LLVM_NODISCARD bool +collectDIElementUsesFrom(const DIMemoryObjectInfo &MemoryInfo, + SmallVectorImpl &Uses, + SmallVectorImpl &Releases); } // end namespace swift diff --git a/lib/SILOptimizer/Mandatory/DIMemoryUseCollectorOwnership.cpp b/lib/SILOptimizer/Mandatory/DIMemoryUseCollectorOwnership.cpp index 2df2d15164755..c8e83b9dc3e24 100644 --- a/lib/SILOptimizer/Mandatory/DIMemoryUseCollectorOwnership.cpp +++ b/lib/SILOptimizer/Mandatory/DIMemoryUseCollectorOwnership.cpp @@ -1459,24 +1459,79 @@ void ElementUseCollector::collectClassSelfUses( } //===----------------------------------------------------------------------===// -// DelegatingInitElementUseCollector +// DelegatingValueTypeInitUseCollector +//===----------------------------------------------------------------------===// + +static void +collectValueTypeDelegatingInitUses(const DIMemoryObjectInfo &TheMemory, + DIElementUseInfo &UseInfo, + SingleValueInstruction *I) { + for (auto *Op : I->getUses()) { + auto *User = Op->getUser(); + + // destroy_addr is a release of the entire value. This can result from an + // early release due to a conditional initializer. + if (isa(User)) { + UseInfo.trackDestroy(User); + continue; + } + + // For delegating initializers, we only track calls to self.init with + // specialized code. All other uses are modeled as escapes. + // + // *NOTE* This intentionally ignores all stores, which (if they got emitted + // as copyaddr or assigns) will eventually get rewritten as assignments (not + // initializations), which is the right thing to do. + DIUseKind Kind = DIUseKind::Escape; + + // Stores *to* the allocation are writes. If the value being stored is a + // call to self.init()... then we have a self.init call. + if (auto *AI = dyn_cast(User)) { + if (AI->getDest() == I) { + UseInfo.trackStoreToSelf(AI); + Kind = DIUseKind::InitOrAssign; + } + } + + if (auto *CAI = dyn_cast(User)) { + if (CAI->getDest() == I) { + UseInfo.trackStoreToSelf(CAI); + Kind = DIUseKind::InitOrAssign; + } + } + + // Look through begin_access + if (auto *BAI = dyn_cast(User)) { + collectValueTypeDelegatingInitUses(TheMemory, UseInfo, BAI); + continue; + } + + // Ignore end_access + if (isa(User)) + continue; + + // We can safely handle anything else as an escape. They should all happen + // after self.init is invoked. + UseInfo.trackUse(DIMemoryUse(User, Kind, 0, 1)); + } +} + +//===----------------------------------------------------------------------===// +// DelegatingClassInitElementUseCollector //===----------------------------------------------------------------------===// namespace { -class DelegatingInitElementUseCollector { +class DelegatingClassInitElementUseCollector { const DIMemoryObjectInfo &TheMemory; DIElementUseInfo &UseInfo; - void collectValueTypeInitSelfUses(SingleValueInstruction *I); - public: - DelegatingInitElementUseCollector(const DIMemoryObjectInfo &TheMemory, - DIElementUseInfo &UseInfo) + DelegatingClassInitElementUseCollector(const DIMemoryObjectInfo &TheMemory, + DIElementUseInfo &UseInfo) : TheMemory(TheMemory), UseInfo(UseInfo) {} void collectClassInitSelfUses(); - void collectValueTypeInitSelfUses(); // *NOTE* Even though this takes a SILInstruction it actually only accepts // load_borrow and load instructions. This is enforced via an assert. @@ -1488,7 +1543,7 @@ class DelegatingInitElementUseCollector { /// collectDelegatingClassInitSelfUses - Collect uses of the self argument in a /// delegating-constructor-for-a-class case. -void DelegatingInitElementUseCollector::collectClassInitSelfUses() { +void DelegatingClassInitElementUseCollector::collectClassInitSelfUses() { // When we're analyzing a delegating constructor, we aren't field sensitive at // all. Just treat all members of self as uses of the single // non-field-sensitive value. @@ -1631,70 +1686,9 @@ void DelegatingInitElementUseCollector::collectClassInitSelfUses() { } } -void DelegatingInitElementUseCollector::collectValueTypeInitSelfUses( - SingleValueInstruction *I) { - for (auto Op : I->getUses()) { - auto *User = Op->getUser(); - - // destroy_addr is a release of the entire value. This can result from an - // early release due to a conditional initializer. - if (isa(User)) { - UseInfo.trackDestroy(User); - continue; - } - - // For delegating initializers, we only track calls to self.init with - // specialized code. All other uses are modeled as escapes. - // - // *NOTE* This intentionally ignores all stores, which (if they got emitted - // as copyaddr or assigns) will eventually get rewritten as assignments (not - // initializations), which is the right thing to do. - DIUseKind Kind = DIUseKind::Escape; - - // Stores *to* the allocation are writes. If the value being stored is a - // call to self.init()... then we have a self.init call. - if (auto *AI = dyn_cast(User)) { - if (AI->getDest() == I) { - UseInfo.trackStoreToSelf(AI); - Kind = DIUseKind::InitOrAssign; - } - } - - if (auto *CAI = dyn_cast(User)) { - if (CAI->getDest() == I) { - UseInfo.trackStoreToSelf(CAI); - Kind = DIUseKind::InitOrAssign; - } - } - - // Look through begin_access - if (auto *BAI = dyn_cast(User)) { - collectValueTypeInitSelfUses(BAI); - continue; - } - - // Ignore end_access - if (isa(User)) - continue; - - // We can safely handle anything else as an escape. They should all happen - // after self.init is invoked. - UseInfo.trackUse(DIMemoryUse(User, Kind, 0, 1)); - } -} - -void DelegatingInitElementUseCollector::collectValueTypeInitSelfUses() { - // When we're analyzing a delegating constructor, we aren't field sensitive at - // all. Just treat all members of self as uses of the single - // non-field-sensitive value. - assert(TheMemory.NumElements == 1 && "delegating inits only have 1 bit"); - - auto *MUI = cast(TheMemory.MemoryInst); - collectValueTypeInitSelfUses(MUI); -} - -void DelegatingInitElementUseCollector::collectDelegatingClassInitSelfLoadUses( - MarkUninitializedInst *MUI, SingleValueInstruction *LI) { +void DelegatingClassInitElementUseCollector:: +collectDelegatingClassInitSelfLoadUses(MarkUninitializedInst *MUI, + SingleValueInstruction *LI) { assert(isa(LI) || isa(LI)); // If we have a load, then this is a use of the box. Look at the uses of @@ -1775,31 +1769,38 @@ void DelegatingInitElementUseCollector::collectDelegatingClassInitSelfLoadUses( // Top Level Entrypoint //===----------------------------------------------------------------------===// +static bool shouldPerformClassInitSelf(const DIMemoryObjectInfo &MemoryInfo) { + if (MemoryInfo.isDelegatingInit()) { + assert(MemoryInfo.isClassInitSelf()); + return true; + } + + return MemoryInfo.isNonDelegatingInit() && + MemoryInfo.getType()->getClassOrBoundGenericClass() != nullptr && + MemoryInfo.isDerivedClassSelfOnly(); +} + /// collectDIElementUsesFrom - Analyze all uses of the specified allocation /// instruction (alloc_box, alloc_stack or mark_uninitialized), classifying them /// and storing the information found into the Uses and Releases lists. void swift::ownership::collectDIElementUsesFrom( const DIMemoryObjectInfo &MemoryInfo, DIElementUseInfo &UseInfo, bool isDIFinished, bool TreatAddressToPointerAsInout) { - // If we have a delegating init, use the delegating init element use - // collector. - if (MemoryInfo.isDelegatingInit()) { - DelegatingInitElementUseCollector UseCollector(MemoryInfo, UseInfo); - if (MemoryInfo.isClassInitSelf()) { - UseCollector.collectClassInitSelfUses(); - } else { - UseCollector.collectValueTypeInitSelfUses(); - } + if (MemoryInfo.isDelegatingInit() && !MemoryInfo.isClassInitSelf()) { + // When we're analyzing a delegating constructor, we aren't field sensitive + // at all. Just treat all members of self as uses of the single + // non-field-sensitive value. + assert(MemoryInfo.NumElements == 1 && "delegating inits only have 1 bit"); + auto *MUI = cast(MemoryInfo.MemoryInst); + collectValueTypeDelegatingInitUses(MemoryInfo, UseInfo, MUI); MemoryInfo.collectRetainCountInfo(UseInfo); return; } - if (MemoryInfo.isNonDelegatingInit() && - MemoryInfo.getType()->getClassOrBoundGenericClass() != nullptr && - MemoryInfo.isDerivedClassSelfOnly()) { - DelegatingInitElementUseCollector(MemoryInfo, UseInfo) - .collectClassInitSelfUses(); + if (shouldPerformClassInitSelf(MemoryInfo)) { + DelegatingClassInitElementUseCollector UseCollector(MemoryInfo, UseInfo); + UseCollector.collectClassInitSelfUses(); MemoryInfo.collectRetainCountInfo(UseInfo); return; } diff --git a/lib/SILOptimizer/Mandatory/DIMemoryUseCollectorOwnership.h b/lib/SILOptimizer/Mandatory/DIMemoryUseCollectorOwnership.h index dd8b41bc113fa..d4f3d950e967b 100644 --- a/lib/SILOptimizer/Mandatory/DIMemoryUseCollectorOwnership.h +++ b/lib/SILOptimizer/Mandatory/DIMemoryUseCollectorOwnership.h @@ -82,7 +82,7 @@ class DIMemoryObjectInfo { /// Return the first instruction of the function containing the memory object. SILInstruction *getFunctionEntryPoint() const; - CanType getType() const { return MemorySILType.getSwiftRValueType(); } + CanType getType() const { return MemorySILType.getASTType(); } SingleValueInstruction *getAddress() const { if (isa(MemoryInst) || diff --git a/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp b/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp index bc5b5a469e95e..0ba46bb0e1a7c 100644 --- a/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp +++ b/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp @@ -12,6 +12,7 @@ #define DEBUG_TYPE "definite-init" #include "DIMemoryUseCollectorOwnership.h" +#include "MandatoryOptUtils.h" #include "swift/AST/DiagnosticEngine.h" #include "swift/AST/DiagnosticsSIL.h" #include "swift/AST/Expr.h" @@ -44,8 +45,6 @@ llvm::cl::opt TriggerUnreachableOnFailure( "Meant for debugging ONLY!"), llvm::cl::Hidden); -STATISTIC(NumAssignRewritten, "Number of assigns rewritten"); - template static InFlightDiagnostic diagnose(SILModule &M, SILLocation loc, ArgTypes... args) { @@ -56,83 +55,6 @@ static InFlightDiagnostic diagnose(SILModule &M, SILLocation loc, return diag; } -enum class PartialInitializationKind { - /// The box contains a fully-initialized value. - IsNotInitialization, - - /// The box contains a class instance that we own, but the instance has - /// not been initialized and should be freed with a special SIL - /// instruction made for this purpose. - IsReinitialization, - - /// The box contains an undefined value that should be ignored. - IsInitialization, -}; - -/// Emit the sequence that an assign instruction lowers to once we know -/// if it is an initialization or an assignment. If it is an assignment, -/// a live-in value can be provided to optimize out the reload. -static void LowerAssignInstruction(SILBuilderWithScope &B, AssignInst *Inst, - PartialInitializationKind isInitialization) { - DEBUG(llvm::dbgs() << " *** Lowering [isInit=" << unsigned(isInitialization) - << "]: " << *Inst << "\n"); - - ++NumAssignRewritten; - - SILValue Src = Inst->getSrc(); - SILLocation Loc = Inst->getLoc(); - - if (isInitialization == PartialInitializationKind::IsInitialization || - Inst->getDest()->getType().isTrivial(Inst->getModule())) { - - // If this is an initialization, or the storage type is trivial, we - // can just replace the assignment with a store. - assert(isInitialization != PartialInitializationKind::IsReinitialization); - B.createTrivialStoreOr(Loc, Src, Inst->getDest(), - StoreOwnershipQualifier::Init); - Inst->eraseFromParent(); - return; - } - - if (isInitialization == PartialInitializationKind::IsReinitialization) { - // We have a case where a convenience initializer on a class - // delegates to a factory initializer from a protocol extension. - // Factory initializers give us a whole new instance, so the existing - // instance, which has not been initialized and never will be, must be - // freed using dealloc_partial_ref. - SILValue Pointer = - B.createLoad(Loc, Inst->getDest(), LoadOwnershipQualifier::Take); - B.createStore(Loc, Src, Inst->getDest(), StoreOwnershipQualifier::Init); - - auto MetatypeTy = CanMetatypeType::get( - Inst->getDest()->getType().getSwiftRValueType(), - MetatypeRepresentation::Thick); - auto SILMetatypeTy = SILType::getPrimitiveObjectType(MetatypeTy); - SILValue Metatype = B.createValueMetatype(Loc, SILMetatypeTy, Pointer); - - B.createDeallocPartialRef(Loc, Pointer, Metatype); - Inst->eraseFromParent(); - return; - } - - assert(isInitialization == PartialInitializationKind::IsNotInitialization); - // Otherwise, we need to replace the assignment with the full - // load/store/release dance. Note that the new value is already - // considered to be retained (by the semantics of the storage type), - // and we're transferring that ownership count into the destination. - - // This is basically TypeLowering::emitStoreOfCopy, except that if we have - // a known incoming value, we can avoid the load. - SILValue IncomingVal = - B.createLoad(Loc, Inst->getDest(), LoadOwnershipQualifier::Take); - B.createStore(Inst->getLoc(), Src, Inst->getDest(), - StoreOwnershipQualifier::Init); - - B.emitDestroyValueOperation(Loc, IncomingVal); - Inst->eraseFromParent(); -} - - /// Insert a CFG diamond at the position specified by the SILBuilder, with a /// conditional branch based on "Cond". /// @@ -1912,7 +1834,7 @@ void LifetimeChecker::updateInstructionForInitState(DIMemoryUse &Use) { SmallVector InsertedInsts; SILBuilderWithScope B(Inst, &InsertedInsts); - LowerAssignInstruction(B, AI, PartialInitKind); + lowerAssignInstruction(B, AI, PartialInitKind); // If lowering of the assign introduced any new loads or stores, keep track // of them. @@ -1962,7 +1884,7 @@ void LifetimeChecker::processUninitializedRelease(SILInstruction *Release, Pointer = B.createLoad(Loc, Pointer, LoadOwnershipQualifier::Take); auto MetatypeTy = CanMetatypeType::get( - TheMemory.MemorySILType.getSwiftRValueType(), + TheMemory.MemorySILType.getASTType(), MetatypeRepresentation::Thick); auto SILMetatypeTy = SILType::getPrimitiveObjectType(MetatypeTy); SILValue Metatype; @@ -2899,268 +2821,35 @@ static bool checkDefiniteInitialization(SILFunction &Fn) { DEBUG(llvm::dbgs() << "*** Definite Init visiting function: " << Fn.getName() << "\n"); bool Changed = false; - for (auto &BB : Fn) { - for (auto I = BB.begin(), E = BB.end(); I != E; ++I) { - SILInstruction *Inst = &*I; - if (auto *MUI = dyn_cast(Inst)) - Changed |= processMemoryObject(MUI); - } - } - return Changed; -} -/// lowerRawSILOperations - There are a variety of raw-sil instructions like -/// 'assign' that are only used by this pass. Now that definite initialization -/// checking is done, remove them. -static bool lowerRawSILOperations(SILFunction &Fn) { - bool Changed = false; for (auto &BB : Fn) { - auto I = BB.begin(), E = BB.end(); - while (I != E) { + for (auto I = BB.begin(), E = BB.end(); I != E;) { SILInstruction *Inst = &*I; - ++I; - - // Unprocessed assigns just lower into assignments, not initializations. - if (auto *AI = dyn_cast(Inst)) { - SILBuilderWithScope B(AI); - LowerAssignInstruction(B, AI, - PartialInitializationKind::IsNotInitialization); - // Assign lowering may split the block. If it did, - // reset our iteration range to the block after the insertion. - if (B.getInsertionBB() != &BB) - I = E; - Changed = true; - continue; - } - // mark_uninitialized just becomes a noop, resolving to its operand. - if (auto *MUI = dyn_cast(Inst)) { - MUI->replaceAllUsesWith(MUI->getOperand()); - MUI->eraseFromParent(); - Changed = true; - continue; - } - - // mark_function_escape just gets zapped. - if (isa(Inst)) { - Inst->eraseFromParent(); - Changed = true; + auto *MUI = dyn_cast(Inst); + if (!MUI || !processMemoryObject(MUI)) { + ++I; continue; } - } - } - return Changed; -} - -static SILBasicBlock *getOptionalDiamondSuccessor(SwitchEnumInst *SEI) { - auto numSuccs = SEI->getNumSuccessors(); - if (numSuccs != 2) - return nullptr; - auto *SuccSome = SEI->getCase(0).second; - auto *SuccNone = SEI->getCase(1).second; - if (SuccSome->args_size() != 1) - std::swap(SuccSome, SuccNone); - - if (SuccSome->args_size() != 1 || SuccNone->args_size() != 0) - return nullptr; - - auto *Succ = SuccSome->getSingleSuccessorBlock(); - if (!Succ) - return nullptr; - - if (SuccNone == Succ) - return Succ; - - SuccNone = SuccNone->getSingleSuccessorBlock(); - if (SuccNone == Succ) - return Succ; - - SuccNone = SuccNone->getSingleSuccessorBlock(); - if (SuccNone == Succ) - return Succ; - - return nullptr; -} -/// Extend the lifetime of the convert_escape_to_noescape's operand to the end -/// of the function. -void extendLifetimeToEndOfFunction(SILFunction &Fn, - ConvertEscapeToNoEscapeInst *Cvt) { - auto EscapingClosure = Cvt->getOperand(); - auto EscapingClosureTy = EscapingClosure->getType(); - auto OptionalEscapingClosureTy = SILType::getOptionalType(EscapingClosureTy); - auto loc = RegularLocation::getAutoGeneratedLocation(); - - SILBuilderWithScope B(Cvt); - auto NewCvt = B.createConvertEscapeToNoEscape( - Cvt->getLoc(), Cvt->getOperand(), Cvt->getType(), true); - Cvt->replaceAllUsesWith(NewCvt); - Cvt->eraseFromParent(); - Cvt = NewCvt; - - // Create an alloc_stack Optional<() -> ()> at the beginning of the function. - AllocStackInst *Slot; - auto &Context = Cvt->getModule().getASTContext(); - { - SILBuilderWithScope B(Fn.getEntryBlock()->begin()); - Slot = B.createAllocStack(loc, OptionalEscapingClosureTy); - auto *NoneDecl = Context.getOptionalNoneDecl(); - // Store None to it. - B.createStore( - loc, B.createEnum(loc, SILValue(), NoneDecl, OptionalEscapingClosureTy), - Slot, StoreOwnershipQualifier::Init); - } - // Insert a copy before the convert_escape_to_noescape and store it to the - // alloc_stack location. - { - SILBuilderWithScope B(Cvt); - auto *SomeDecl = Context.getOptionalSomeDecl(); - B.createDestroyAddr(loc, Slot); - auto ClosureCopy = B.createCopyValue(loc, EscapingClosure); - B.createStore( - loc, - B.createEnum(loc, ClosureCopy, SomeDecl, OptionalEscapingClosureTy), - Slot, StoreOwnershipQualifier::Init); - } - // Insert destroys at the function exits. - SmallVector ExitingBlocks; - Fn.findExitingBlocks(ExitingBlocks); - for (auto *Exit : ExitingBlocks) { - auto *Term = Exit->getTerminator(); - SILBuilderWithScope B(Term); - B.setInsertionPoint(Term); - B.createDestroyAddr(loc, Slot); - B.createDeallocStack(loc, Slot); - } -} - -/// Ensure the lifetime of the closure accross an -/// -/// optional<@escaping () -> ()> to -/// optional<@noescape @convention(block) () -> ()> -/// -/// conversion and its use. -/// -/// The pattern this is looking for -/// switch_enum %closure -/// / \ -/// convert_escape_to_noescape nil -/// switch_enum -/// / \ -/// convertToBlock nil -/// \ / -/// (%convertOptionalBlock :) -/// We will insert a copy_value of the original %closure before the two -/// diamonds. And a destroy of %closure at the last destroy of -/// %convertOptionalBlock. -static bool trySwitchEnumPeephole(ConvertEscapeToNoEscapeInst *Cvt) { - auto *blockArg = dyn_cast(Cvt->getOperand()); - if (!blockArg) - return false; - auto *PredBB = Cvt->getParent()->getSinglePredecessorBlock(); - if (!PredBB) - return false; - auto *ConvertSuccessorBlock = Cvt->getParent()->getSingleSuccessorBlock(); - if (!ConvertSuccessorBlock) - return false; - auto *SwitchEnum1 = dyn_cast(PredBB->getTerminator()); - if (!SwitchEnum1) - return false; - auto *DiamondSucc = getOptionalDiamondSuccessor(SwitchEnum1); - if (!DiamondSucc) - return false; - auto *SwitchEnum2 = dyn_cast(DiamondSucc->getTerminator()); - if (!SwitchEnum2) - return false; - auto *DiamondSucc2 = getOptionalDiamondSuccessor(SwitchEnum2); - if (!DiamondSucc2) - return false; - if (DiamondSucc2->getNumArguments() != 1) - return false; - - // Look for the last and only destroy. - SILInstruction *onlyDestroy = [&]() -> SILInstruction * { - SILInstruction *lastDestroy = nullptr; - for (auto *Use : DiamondSucc2->getArgument(0)->getUses()) { - SILInstruction *Usr = Use->getUser(); - if (isa(Usr) || isa(Usr) || - isa(Usr)) { - if (lastDestroy) - return nullptr; - lastDestroy = Usr; - } - } - return lastDestroy; - }(); - if (!onlyDestroy) - return false; - - // Replace the convert_escape_to_noescape instruction. - { - SILBuilderWithScope B(Cvt); - auto NewCvt = B.createConvertEscapeToNoEscape( - Cvt->getLoc(), Cvt->getOperand(), Cvt->getType(), true); - Cvt->replaceAllUsesWith(NewCvt); - Cvt->eraseFromParent(); - Cvt = NewCvt; - } - - // Extend the lifetime. - SILBuilderWithScope B(SwitchEnum1); - auto loc = RegularLocation::getAutoGeneratedLocation(); - auto copy = - B.createCopyValue(loc, SwitchEnum1->getOperand()); - B.setInsertionPoint(onlyDestroy); - B.createDestroyValue(loc, copy); - return true; -} - -llvm::cl::opt DIDisableConvertEscapeToNoEscapeSwitchEnumPeephole( - "sil-di-disable-convert-escape-to-noescape-switch-peephole", - llvm::cl::init(false), - llvm::cl::desc( - "Disable the convert_escape_to_noescape switch enum peephole. "), - llvm::cl::Hidden); - -/// Fix-up the lifetime of the escaping closure argument of -/// convert_escape_to_noescape [not_guaranteed] instructions. -/// -/// convert_escape_to_noescape [not_guaranteed] assume that someone guarantees -/// the lifetime of the operand for the duration of the trivial closure result. -/// SILGen does not guarantee this for '[not_guaranteed]' instructions so we -/// ensure it here. -static bool fixupConvertEscapeToNoEscapeLifetime(SILFunction &Fn) { - bool Changed = false; - for (auto &BB : Fn) { - auto I = BB.begin(); - while (I != BB.end()) { - SILInstruction *Inst = &*I; + // Move off of the MUI only after we have processed memory objects. The + // lifetime checker may rewrite instructions, so it is important to not + // move onto the next element until after it runs. ++I; - auto *Cvt = dyn_cast(Inst); - if (!Cvt || Cvt->isLifetimeGuaranteed()) - continue; - - // First try to peephole a known pattern. - if (!DIDisableConvertEscapeToNoEscapeSwitchEnumPeephole && - trySwitchEnumPeephole(Cvt)) { - Changed |= true; - continue; - } - - // Otherwise, extend the lifetime of the operand to the end of the - // function. - extendLifetimeToEndOfFunction(Fn, Cvt); - Changed |= true; + MUI->replaceAllUsesWith(MUI->getOperand()); + MUI->eraseFromParent(); + Changed = true; } } + return Changed; } namespace { + /// Perform definitive initialization analysis and promote alloc_box uses into /// SSA registers for later SSA-based dataflow passes. class DefiniteInitialization : public SILFunctionTransform { - /// The entry point to the transformation. void run() override { // Don't rerun diagnostics on deserialized functions. @@ -3171,20 +2860,9 @@ class DefiniteInitialization : public SILFunctionTransform { if (checkDefiniteInitialization(*getFunction())) { invalidateAnalysis(SILAnalysis::InvalidationKind::FunctionBody); } - - DEBUG(getFunction()->verify()); - - // Lower raw-sil only instructions used by this pass, like "assign". - if (lowerRawSILOperations(*getFunction())) - invalidateAnalysis(SILAnalysis::InvalidationKind::FunctionBody); - - // Fixup lifetimes of optional convertEscapeToNoEscape. - if (fixupConvertEscapeToNoEscapeLifetime(*getFunction())) - invalidateAnalysis(SILAnalysis::InvalidationKind::FunctionBody); - } - }; + } // end anonymous namespace SILTransform *swift::createDefiniteInitialization() { diff --git a/lib/SILOptimizer/Mandatory/DiagnoseStaticExclusivity.cpp b/lib/SILOptimizer/Mandatory/DiagnoseStaticExclusivity.cpp index be1e2c7cbd727..6474fe199a75d 100644 --- a/lib/SILOptimizer/Mandatory/DiagnoseStaticExclusivity.cpp +++ b/lib/SILOptimizer/Mandatory/DiagnoseStaticExclusivity.cpp @@ -33,6 +33,7 @@ #include "swift/Parse/Lexer.h" #include "swift/SIL/CFG.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" @@ -54,118 +55,6 @@ static InFlightDiagnostic diagnose(ASTContext &Context, SourceLoc loc, namespace { -enum class AccessedStorageKind : unsigned { - /// The access is to a location represented by a SIL value - /// (for example, an 'alloc_box' instruction for a local variable). - /// Two accesses accessing the exact same SILValue are considered to be - /// accessing the same storage location. - Value, - - /// The access is to a global variable. - GlobalVar, - - /// The access is to a stored class property. - ClassProperty -}; - -/// Represents the identity of a stored class property as a combination -/// of a base and a single projection. Eventually the goal is to make this -/// more precise and consider, casts, etc. -class ObjectProjection { -public: - ObjectProjection(SILValue Object, const Projection &Proj) - : Object(Object), Proj(Proj) { - assert(Object->getType().isObject()); - } - - SILValue getObject() const { return Object; } - const Projection &getProjection() const { return Proj; } - - bool operator==(const ObjectProjection &Other) const { - return Object == Other.Object && Proj == Other.Proj; - } - - bool operator!=(const ObjectProjection &Other) const { - return Object != Other.Object || Proj != Other.Proj; - } - -private: - SILValue Object; - Projection Proj; -}; - -/// Represents the identity of a storage location being accessed. -/// This is used to determine when two 'begin_access' instructions -/// definitely access the same underlying location. -/// -/// The key invariant that this class must maintain is that if it says -/// two storage locations are the same then they must be the same at run time. -/// It is allowed to err on the other side: it may imprecisely fail to -/// recognize that two storage locations that represent the same run-time -/// location are in fact the same. -class AccessedStorage { - -private: - AccessedStorageKind Kind; - - union { - SILValue Value; - SILGlobalVariable *Global; - ObjectProjection ObjProj; - }; - -public: - AccessedStorage(SILValue V) - : Kind(AccessedStorageKind::Value), Value(V) { } - - AccessedStorage(SILGlobalVariable *Global) - : Kind(AccessedStorageKind::GlobalVar), Global(Global) {} - - AccessedStorage(AccessedStorageKind Kind, - const ObjectProjection &ObjProj) - : Kind(Kind), ObjProj(ObjProj) {} - - AccessedStorageKind getKind() const { return Kind; } - - SILValue getValue() const { - assert(Kind == AccessedStorageKind::Value); - return Value; - } - - SILGlobalVariable *getGlobal() const { - assert(Kind == AccessedStorageKind::GlobalVar); - return Global; - } - - const ObjectProjection &getObjectProjection() const { - assert(Kind == AccessedStorageKind::ClassProperty); - return ObjProj; - } - - /// Returns the ValueDecl for the underlying storage, if it can be - /// determined. Otherwise returns null. For diagnostic purposes. - const ValueDecl *getStorageDecl() const { - switch(Kind) { - case AccessedStorageKind::GlobalVar: - return getGlobal()->getDecl(); - case AccessedStorageKind::Value: - if (auto *Box = dyn_cast(getValue())) { - return Box->getLoc().getAsASTNode(); - } - if (auto *Arg = dyn_cast(getValue())) { - return Arg->getDecl(); - } - break; - case AccessedStorageKind::ClassProperty: { - const ObjectProjection &OP = getObjectProjection(); - const Projection &P = OP.getProjection(); - return P.getVarDecl(OP.getObject()->getType()); - } - } - return nullptr; - } -}; - enum class RecordedAccessKind { /// The access was for a 'begin_access' instruction in the current function /// being checked. @@ -410,13 +299,6 @@ using StorageMap = llvm::SmallDenseMap; /// Represents two accesses that conflict and their underlying storage. struct ConflictingAccess { -private: - - /// If true, always diagnose this conflict as a warning. This is useful for - /// staging in fixes for false negatives without affecting source - /// compatibility. - bool AlwaysDiagnoseAsWarning = false; -public: /// Create a conflict for two begin_access instructions in the same function. ConflictingAccess(const AccessedStorage &Storage, const RecordedAccess &First, const RecordedAccess &Second) @@ -425,60 +307,10 @@ struct ConflictingAccess { const AccessedStorage Storage; const RecordedAccess FirstAccess; const RecordedAccess SecondAccess; - - bool getAlwaysDiagnoseAsWarning() const { return AlwaysDiagnoseAsWarning; } - void setAlwaysDiagnoseAsWarning(bool AlwaysDiagnoseAsWarning) { - this->AlwaysDiagnoseAsWarning = AlwaysDiagnoseAsWarning; - } }; } // end anonymous namespace -namespace llvm { -/// Enable using AccessedStorage as a key in DenseMap. -template <> struct DenseMapInfo { - static AccessedStorage getEmptyKey() { - return AccessedStorage(swift::SILValue::getFromOpaqueValue( - llvm::DenseMapInfo::getEmptyKey())); - } - - static AccessedStorage getTombstoneKey() { - return AccessedStorage(swift::SILValue::getFromOpaqueValue( - llvm::DenseMapInfo::getTombstoneKey())); - } - - static unsigned getHashValue(AccessedStorage Storage) { - switch (Storage.getKind()) { - case AccessedStorageKind::Value: - return DenseMapInfo::getHashValue(Storage.getValue()); - case AccessedStorageKind::GlobalVar: - return DenseMapInfo::getHashValue(Storage.getGlobal()); - case AccessedStorageKind::ClassProperty: { - const ObjectProjection &P = Storage.getObjectProjection(); - return llvm::hash_combine(P.getObject(), P.getProjection()); - } - } - llvm_unreachable("Unhandled AccessedStorageKind"); - } - - static bool isEqual(AccessedStorage LHS, AccessedStorage RHS) { - if (LHS.getKind() != RHS.getKind()) - return false; - - switch (LHS.getKind()) { - case AccessedStorageKind::Value: - return LHS.getValue() == RHS.getValue(); - case AccessedStorageKind::GlobalVar: - return LHS.getGlobal() == RHS.getGlobal(); - case AccessedStorageKind::ClassProperty: - return LHS.getObjectProjection() == RHS.getObjectProjection(); - } - llvm_unreachable("Unhandled AccessedStorageKind"); - } -}; - -} // end namespace llvm - /// Returns whether an access of the given kind requires exclusive or shared /// access to its storage. static ExclusiveOrShared_t getRequiredAccess(SILAccessKind Kind) { @@ -686,11 +518,11 @@ static void diagnoseExclusivityViolation(const ConflictingAccess &Violation, const AccessedStorage &Storage = Violation.Storage; const RecordedAccess &FirstAccess = Violation.FirstAccess; const RecordedAccess &SecondAccess = Violation.SecondAccess; + SILFunction *F = FirstAccess.getInstruction()->getFunction(); DEBUG(llvm::dbgs() << "Conflict on " << *FirstAccess.getInstruction() << "\n vs " << *SecondAccess.getInstruction() - << "\n in function " - << *FirstAccess.getInstruction()->getFunction()); + << "\n in function " << *F); // Can't have a conflict if both accesses are reads. assert(!(FirstAccess.getAccessKind() == SILAccessKind::Read && @@ -710,10 +542,9 @@ static void diagnoseExclusivityViolation(const ConflictingAccess &Violation, // For now, all exclusivity violations are warning in Swift 3 mode. // Also treat some violations as warnings to allow them to be staged in. - bool DiagnoseAsWarning = Violation.getAlwaysDiagnoseAsWarning() || - Ctx.LangOpts.isSwiftVersion3(); + bool DiagnoseAsWarning = Ctx.LangOpts.isSwiftVersion3(); - if (const ValueDecl *VD = Storage.getStorageDecl()) { + if (const ValueDecl *VD = Storage.getDecl(F)) { // We have a declaration, so mention the identifier in the diagnostic. auto DiagnosticID = (DiagnoseAsWarning ? diag::exclusivity_access_required_warn : @@ -756,48 +587,14 @@ static void diagnoseExclusivityViolation(const ConflictingAccess &Violation, .highlight(NoteAccess.getAccessLoc().getSourceRange()); } -/// Make a best effort to find the underlying object for the purpose -/// of identifying the base of a 'ref_element_addr'. -static SILValue findUnderlyingObject(SILValue Value) { - assert(Value->getType().isObject()); - SILValue Iter = Value; - - while (true) { - // For now just look through begin_borrow instructions; we can likely - // make this more precise in the future. - if (auto *BBI = dyn_cast(Iter)) { - Iter = BBI->getOperand(); - continue; - } - break; - } - - assert(Iter->getType().isObject()); - return Iter; -} - /// Look through a value to find the underlying storage accessed. -static AccessedStorage findAccessedStorage(SILValue Source) { - SILValue BaseAddress = findAccessedAddressBase(Source); - if (!BaseAddress) { +static AccessedStorage findValidAccessedStorage(SILValue Source) { + const AccessedStorage &Storage = findAccessedStorage(Source); + if (!Storage) { llvm::dbgs() << "Bad memory access source: " << Source; llvm_unreachable("Unexpected access source."); } - // Base case for globals: make sure ultimate source is recognized. - if (auto *GAI = dyn_cast(BaseAddress)) { - return AccessedStorage(GAI->getReferencedGlobal()); - } - // Base case for class objects. - if (auto *REA = dyn_cast(BaseAddress)) { - // Do a best-effort to find the identity of the object being projected - // from. It is OK to be unsound here (i.e. miss when two ref_element_addrs - // actually refer the same address) because these will be dynamically - // checked. - SILValue Object = findUnderlyingObject(REA->getOperand()); - const ObjectProjection &OP = ObjectProjection(Object, Projection(REA)); - return AccessedStorage(AccessedStorageKind::ClassProperty, OP); - } - return AccessedStorage(BaseAddress); + return Storage; } /// Returns true when the apply calls the Standard Library swap(). @@ -879,25 +676,51 @@ findConflictingArgumentAccess(const AccessSummaryAnalysis::ArgumentSummary &AS, *BestArgAccess); } -/// Use the summary analysis to check whether a call to the given -/// function would conflict with any in progress accesses. The starting -/// index indicates what index into the the callee's parameters the -/// arguments array starts at -- this is useful for partial_apply functions, -/// which pass only a suffix of the callee's arguments at the apply site. -static void checkForViolationWithCall( - const StorageMap &Accesses, SILFunction *Callee, unsigned StartingAtIndex, - OperandValueArrayRef Arguments, AccessSummaryAnalysis *ASA, - bool DiagnoseAsWarning, - llvm::SmallVectorImpl &ConflictingAccesses) { +// ============================================================================= +// The data flow algorithm that drives diagnostics. + +// Forward declare verification entry points. +#ifndef NDEBUG +static void checkNoEscapePartialApply(PartialApplyInst *PAI); +#endif +static void checkAccessedAddress(Operand *memOper, StorageMap &Accesses); + +namespace { +/// Track the current state of formal accesses, including exclusivity +/// violations, and function summaries at a particular point in the program. +struct AccessState { + AccessSummaryAnalysis *ASA; + + // Stores the accesses that have been found to conflict. Used to defer + // emitting diagnostics until we can determine whether they should + // be suppressed. + llvm::SmallVector ConflictingAccesses; + + // Collects calls the Standard Library swap() for Fix-Its. + llvm::SmallVector CallsToSwap; + + StorageMap *Accesses = nullptr; + + AccessState(AccessSummaryAnalysis *ASA) : ASA(ASA) {} +}; +} // namespace + +/// For each argument in the range of the callee arguments being applied at the +/// given apply site, use the summary analysis to determine whether the +/// arguments will be accessed in a way that conflicts with any currently in +/// progress accesses. If so, diagnose. +static void checkCaptureAccess(ApplySite Apply, AccessState &State) { + SILFunction *Callee = Apply.getCalleeFunction(); + if (!Callee || Callee->empty()) + return; + const AccessSummaryAnalysis::FunctionSummary &FS = - ASA->getOrCreateSummary(Callee); + State.ASA->getOrCreateSummary(Callee); - // For each argument in the suffix of the callee arguments being passed - // at this call site, determine whether the arguments will be accessed - // in a way that conflicts with any currently in progress accesses. - // If so, diagnose. - for (unsigned ArgumentIndex : indices(Arguments)) { - unsigned CalleeIndex = StartingAtIndex + ArgumentIndex; + for (unsigned ArgumentIndex : range(Apply.getNumArguments())) { + + unsigned CalleeIndex = + Apply.getCalleeArgIndexOfFirstAppliedArg() + ArgumentIndex; const AccessSummaryAnalysis::ArgumentSummary &AS = FS.getAccessForArgument(CalleeIndex); @@ -908,21 +731,19 @@ static void checkForViolationWithCall( if (SubAccesses.empty()) continue; - SILValue Argument = Arguments[ArgumentIndex]; + SILValue Argument = Apply.getArgument(ArgumentIndex); assert(Argument->getType().isAddress()); - const AccessedStorage &Storage = findAccessedStorage(Argument); - auto AccessIt = Accesses.find(Storage); + const AccessedStorage &Storage = findValidAccessedStorage(Argument); + auto AccessIt = State.Accesses->find(Storage); // Are there any accesses in progress at the time of the call? - if (AccessIt == Accesses.end()) + if (AccessIt == State.Accesses->end()) continue; const AccessInfo &Info = AccessIt->getSecond(); - if (auto Conflict = findConflictingArgumentAccess(AS, Storage, Info)) { - Conflict->setAlwaysDiagnoseAsWarning(DiagnoseAsWarning); - ConflictingAccesses.push_back(*Conflict); - } + if (auto Conflict = findConflictingArgumentAccess(AS, Storage, Info)) + State.ConflictingAccesses.push_back(*Conflict); } } @@ -937,84 +758,195 @@ static CanSILFunctionType getSILFunctionTypeForValue(SILValue arg) { return argTy.getAs(); } -/// Checks whether any of the arguments to the apply are closures and diagnoses -/// if any of the @inout_aliasable captures passed to those closures have -/// in-progress accesses that would conflict with any access the summary -/// says the closure would perform. -static void checkForViolationsInNoEscapeClosureArguments( - const StorageMap &Accesses, ApplySite AS, AccessSummaryAnalysis *ASA, - llvm::SmallVectorImpl &ConflictingAccesses, - bool DiagnoseAsWarning) { - - // Check for violation with closures passed as arguments - for (SILValue Argument : AS.getArguments()) { - auto fnType = getSILFunctionTypeForValue(Argument); - if (!fnType || !fnType->isNoEscape()) +/// Recursively check for conflicts with in-progress accesses at the given +/// apply. +/// +/// Any captured variable accessed by a noescape closure is considered to be +/// accessed at the point that the closure is fully applied. This includes +/// variables captured by address by the noescape closure being applied or by +/// any other noescape closure that is itself passed as an argument to that +/// closure. +/// +/// (1) Use AccessSummaryAnalysis to check each argument for statically +/// enforced accesses nested within the callee. +/// +/// (2) If an applied argument is itself a function type, recursively check for +/// violations on the closure being passed as an argument. +/// +/// (3) Walk up the chain of partial applies to recursively visit all arguments. +/// +/// Note: This handles closures that are called immediately: +/// var i = 7 +/// ({ (p: inout Int) in i = 8})(&i) // Overlapping access to 'i' +/// +/// Note: This handles chains of partial applies: +/// pa1 = partial_apply f(c) : $(a, b, c) +/// pa2 = partial_apply pa1(b) : $(a, b) +/// apply pa2(a) +static void checkForViolationAtApply(ApplySite Apply, AccessState &State) { + // First, check access to variables immediately captured at this apply site. + checkCaptureAccess(Apply, State); + + // Next, recursively check any noescape closures passed as arguments at this + // apply site. + for (SILValue Argument : Apply.getArguments()) { + auto FnType = getSILFunctionTypeForValue(Argument); + if (!FnType || !FnType->isNoEscape()) continue; FindClosureResult result = findClosureForAppliedArg(Argument); if (!result.PAI) continue; - SILFunction *Callee = result.PAI->getCalleeFunction(); - if (!Callee || Callee->empty()) - continue; + checkForViolationAtApply(ApplySite(result.PAI), State); + } + // Finally, continue recursively walking up the chain of applies. + if (auto *PAI = dyn_cast(Apply.getCalleeOrigin())) + checkForViolationAtApply(ApplySite(PAI), State); +} + +// Apply transfer function to the AccessState. Beginning an access +// increments the read or write count for the storage location; +// Ending one decrements the count. +static void checkForViolationsAtInstruction(SILInstruction &I, + AccessState &State) { + if (auto *BAI = dyn_cast(&I)) { + SILAccessKind Kind = BAI->getAccessKind(); + const AccessedStorage &Storage = + findValidAccessedStorage(BAI->getSource()); + AccessInfo &Info = (*State.Accesses)[Storage]; + const IndexTrieNode *SubPath = State.ASA->findSubPathAccessed(BAI); + if (auto Conflict = shouldReportAccess(Info, Kind, SubPath)) { + State.ConflictingAccesses.emplace_back(Storage, *Conflict, + RecordedAccess(BAI, SubPath)); + } - // For source compatibility reasons, treat conflicts found by - // looking through reabstraction thunks as warnings. A future compiler - // will upgrade these to errors; - DiagnoseAsWarning |= result.isReabstructionThunk; - - // For source compatibility reasons, treat conflicts found by - // looking through noescape blocks as warnings. A future compiler - // will upgrade these to errors. - DiagnoseAsWarning |= - (getSILFunctionTypeForValue(Argument)->getRepresentation() - == SILFunctionTypeRepresentation::Block); - - // Check the closure's captures, which are a suffix of the closure's - // parameters. - unsigned StartIndex = - Callee->getArguments().size() - result.PAI->getNumArguments(); - checkForViolationWithCall(Accesses, Callee, StartIndex, - result.PAI->getArguments(), ASA, - DiagnoseAsWarning, ConflictingAccesses); + Info.beginAccess(BAI, SubPath); + return; } + + if (auto *EAI = dyn_cast(&I)) { + auto It = + State.Accesses->find(findValidAccessedStorage(EAI->getSource())); + AccessInfo &Info = It->getSecond(); + + BeginAccessInst *BAI = EAI->getBeginAccess(); + const IndexTrieNode *SubPath = State.ASA->findSubPathAccessed(BAI); + Info.endAccess(EAI, SubPath); + + // If the storage location has no more in-progress accesses, remove + // it to keep the StorageMap lean. + if (!Info.hasAccessesInProgress()) + State.Accesses->erase(It); + return; + } + + if (I.getModule().getOptions().VerifyExclusivity && I.mayReadOrWriteMemory()) { + visitAccessedAddress(&I, [&State](Operand *memOper) { + checkAccessedAddress(memOper, *State.Accesses); + }); + } + + if (auto *AI = dyn_cast(&I)) { + // Record calls to swap() for potential Fix-Its. + if (isCallToStandardLibrarySwap(AI, I.getFunction()->getASTContext())) + State.CallsToSwap.push_back(AI); + else + checkForViolationAtApply(AI, State); + return; + } + + if (auto *TAI = dyn_cast(&I)) { + checkForViolationAtApply(TAI, State); + return; + } +#ifndef NDEBUG + // FIXME: Once AllocBoxToStack is fixed to correctly set noescape + // closure types, move this PartialApply verification into the + // SILVerifier to better pinpoint the offending pass. + if (auto *PAI = dyn_cast(&I)) { + ApplySite apply(PAI); + if (llvm::any_of(range(apply.getNumArguments()), + [apply](unsigned argIdx) { + return apply.getArgumentConvention(argIdx) + == SILArgumentConvention::Indirect_InoutAliasable; + })) { + checkNoEscapePartialApply(PAI); + } + } +#endif + // Sanity check to make sure entries are properly removed. + assert((!isa(&I) || State.Accesses->empty()) + && "Entries were not properly removed?!"); } -/// Given a full apply site, diagnose if the apply either calls a closure -/// directly that conflicts with an in-progress access or takes a noescape -/// argument that, when called, would conflict with an in-progress access. -static void checkForViolationsInNoEscapeClosures( - const StorageMap &Accesses, FullApplySite FAS, AccessSummaryAnalysis *ASA, - llvm::SmallVectorImpl &ConflictingAccesses) { - // Check to make sure that calling a closure immediately will not result in - // a conflict. This diagnoses in cases where there is a conflict between an - // argument passed inout to the closure and an access inside the closure to a - // captured variable: - // - // var i = 7 - // ({ (p: inout Int) in i = 8})(&i) // Overlapping access to 'i' - // - SILFunction *Callee = FAS.getCalleeFunction(); - if (Callee && !Callee->empty()) { - // Check for violation with directly called closure - checkForViolationWithCall(Accesses, Callee, 0, FAS.getArguments(), ASA, - /*DiagnoseAsWarning=*/false, ConflictingAccesses); +static void checkStaticExclusivity(SILFunction &Fn, PostOrderFunctionInfo *PO, + AccessSummaryAnalysis *ASA) { + // The implementation relies on the following SIL invariants: + // - All incoming edges to a block must have the same in-progress + // accesses. This enables the analysis to not perform a data flow merge + // on incoming edges. + // - Further, for a given address each of the in-progress + // accesses must have begun in the same order on all edges. This ensures + // consistent diagnostics across changes to the exploration of the CFG. + // - On return from a function there are no in-progress accesses. This + // enables a sanity check for lean analysis state at function exit. + // - Each end_access instruction corresponds to exactly one begin access + // instruction. (This is encoded in the EndAccessInst itself) + // - begin_access arguments cannot be basic block arguments. + // This enables the analysis to look back to find the *single* storage + // storage location accessed. + + if (Fn.empty()) + return; + + AccessState State(ASA); + + // For each basic block, track the stack of current accesses on + // exit from that block. + llvm::SmallDenseMap, 32> + BlockOutAccesses; + + BlockOutAccesses[Fn.getEntryBlock()] = StorageMap(); + + for (auto *BB : PO->getReversePostOrder()) { + Optional &BBState = BlockOutAccesses[BB]; + + // Because we use a reverse post-order traversal, unless this is the entry + // at least one of its predecessors must have been reached. Use the out + // state for that predecessor as our in state. The SIL verifier guarantees + // that all incoming edges must have the same current accesses. + for (auto *Pred : BB->getPredecessorBlocks()) { + auto it = BlockOutAccesses.find(Pred); + if (it == BlockOutAccesses.end()) + continue; + + const Optional &PredAccesses = it->getSecond(); + if (PredAccesses) { + BBState = PredAccesses; + break; + } + } + + // The in-progress accesses for the current program point, represented + // as map from storage locations to the accesses in progress for the + // location. + State.Accesses = BBState.getPointer(); + for (auto &I : *BB) + checkForViolationsAtInstruction(I, State); } - // Check to make sure that any arguments to the apply are not themselves - // noescape closures that -- when called -- might conflict with an in-progress - // access. For example, this will diagnose on the following: - // - // var i = 7 - // takesInoutAndClosure(&i) { i = 8 } // Overlapping access to 'i' - // - checkForViolationsInNoEscapeClosureArguments(Accesses, FAS, ASA, - ConflictingAccesses, - /*DiagnoseAsWarning=*/false); + // Now that we've collected violations and suppressed calls, emit + // diagnostics. + for (auto &Violation : State.ConflictingAccesses) { + diagnoseExclusivityViolation(Violation, State.CallsToSwap, + Fn.getASTContext()); + } } +// ============================================================================= +// Verification + #ifndef NDEBUG // If a partial apply has @inout_aliasable arguments, it may only be used as // a @noescape function type in a way that is recognized by @@ -1028,6 +960,13 @@ static void checkNoEscapePartialApply(PartialApplyInst *PAI) { if (isIncidentalUse(user) || onlyAffectsRefCount(user)) continue; + if (auto *MD = dyn_cast(user)) { + if (oper->getOperandNumber() == MarkDependenceInst::Base) + continue; + uses.append(MD->getUses().begin(), MD->getUses().end()); + continue; + } + if (SingleValueInstruction *copy = getSingleValueCopyOrCast(user)) { uses.append(copy->getUses().begin(), copy->getUses().end()); continue; @@ -1076,31 +1015,6 @@ static void checkNoEscapePartialApply(PartialApplyInst *PAI) { } #endif -// Check if the given memory instruction obviously initializes the memory -// object at `root`. Initialization does not require exclusivity enforcement -// because uninitialized variables can't be captured. -static bool isSafeInitialization(SILInstruction *memInst, SILValue root) { - // Local variable initialization is already filtered out by the check for - // temporary buffer access. Handle globals. - if (auto *globalAddr = dyn_cast(root)) { - // There doesn't seem to be a better way to check for initialization than - // scanning for the preceding alloc_global. - SILGlobalVariable *var = globalAddr->getReferencedGlobal(); - auto I = globalAddr->getIterator(); - auto beginI = globalAddr->getParent()->begin(); - while (I != beginI) { - --I; - if (auto *allocGlobal = dyn_cast(I)) { - if (allocGlobal->getReferencedGlobal() == var) - return true; - } - if (I->mayReadOrWriteMemory()) - break; - } - } - return false; -} - // Check that the given address-type operand is guarded by begin/end access // markers. static void checkAccessedAddress(Operand *memOper, StorageMap &Accesses) { @@ -1116,6 +1030,11 @@ static void checkAccessedAddress(Operand *memOper, StorageMap &Accesses) { abort(); }; + // If the memory instruction is only used for initialization, it doesn't need + // an access marker. + if (memInstMustInitialize(memOper)) + return; + if (auto apply = ApplySite::isa(memInst)) { SILArgumentConvention conv = apply.getArgumentConvention(apply.getCalleeArgIndex(*memOper)); @@ -1139,25 +1058,29 @@ static void checkAccessedAddress(Operand *memOper, StorageMap &Accesses) { } // Strip off address projections, but not ref_element_addr. - SILValue base = findAccessedAddressBase(address); - // `base` is null for address producers that are only used for local - // initialization. - if (!base || !isPossibleFormalAccessBase(base)) + 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 + // 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 + // findAccessedStorage. + if (!storage || !isPossibleFormalAccessBase(storage, memInst->getFunction())) return; - // Skip local non-lvalue accesses. There are many local initialization - // patterns that don't require access markers. + // A box or stack variable may represent lvalues, but they can only conflict + // with call sites in the same scope. Some initialization patters (stores to + // the local value) aren't protected by markers, so we need this check. if (!isa(memInst) - && (isa(base) || isa(base))) { + && (storage.getKind() == AccessedStorage::Box + || storage.getKind() == AccessedStorage::Stack)) { return; } - if (isSafeInitialization(memInst, base)) - return; - // Otherwise, the address base should be an in-scope begin_access. - if (auto *BAI = dyn_cast(base)) { - const AccessedStorage &Storage = findAccessedStorage(BAI->getSource()); + if (storage.getKind() == AccessedStorage::Nested) { + auto *BAI = cast(storage.getValue()); + const AccessedStorage &Storage = findValidAccessedStorage(BAI->getSource()); AccessInfo &Info = Accesses[Storage]; if (!Info.hasAccessesInProgress()) error(); @@ -1166,148 +1089,8 @@ static void checkAccessedAddress(Operand *memOper, StorageMap &Accesses) { error(); } -static void checkStaticExclusivity(SILFunction &Fn, PostOrderFunctionInfo *PO, - AccessSummaryAnalysis *ASA) { - // The implementation relies on the following SIL invariants: - // - All incoming edges to a block must have the same in-progress - // accesses. This enables the analysis to not perform a data flow merge - // on incoming edges. - // - Further, for a given address each of the in-progress - // accesses must have begun in the same order on all edges. This ensures - // consistent diagnostics across changes to the exploration of the CFG. - // - On return from a function there are no in-progress accesses. This - // enables a sanity check for lean analysis state at function exit. - // - Each end_access instruction corresponds to exactly one begin access - // instruction. (This is encoded in the EndAccessInst itself) - // - begin_access arguments cannot be basic block arguments. - // This enables the analysis to look back to find the *single* storage - // storage location accessed. - - if (Fn.empty()) - return; - - bool VerifyMemOps = Fn.getModule().getOptions().VerifyExclusivity; - - // Collects calls the Standard Library swap() for Fix-Its. - llvm::SmallVector CallsToSwap; - - // Stores the accesses that have been found to conflict. Used to defer - // emitting diagnostics until we can determine whether they should - // be suppressed. - llvm::SmallVector ConflictingAccesses; - - // For each basic block, track the stack of current accesses on - // exit from that block. - llvm::SmallDenseMap, 32> - BlockOutAccesses; - - BlockOutAccesses[Fn.getEntryBlock()] = StorageMap(); - - for (auto *BB : PO->getReversePostOrder()) { - Optional &BBState = BlockOutAccesses[BB]; - - // Because we use a reverse post-order traversal, unless this is the entry - // at least one of its predecessors must have been reached. Use the out - // state for that predecessor as our in state. The SIL verifier guarantees - // that all incoming edges must have the same current accesses. - for (auto *Pred : BB->getPredecessorBlocks()) { - auto it = BlockOutAccesses.find(Pred); - if (it == BlockOutAccesses.end()) - continue; - - const Optional &PredAccesses = it->getSecond(); - if (PredAccesses) { - BBState = PredAccesses; - break; - } - } - - // The in-progress accesses for the current program point, represented - // as map from storage locations to the accesses in progress for the - // location. - StorageMap &Accesses = *BBState; - - for (auto &I : *BB) { - // Apply transfer functions. Beginning an access - // increments the read or write count for the storage location; - // Ending onr decrements the count. - if (auto *BAI = dyn_cast(&I)) { - SILAccessKind Kind = BAI->getAccessKind(); - const AccessedStorage &Storage = findAccessedStorage(BAI->getSource()); - AccessInfo &Info = Accesses[Storage]; - const IndexTrieNode *SubPath = ASA->findSubPathAccessed(BAI); - if (auto Conflict = shouldReportAccess(Info, Kind, SubPath)) { - ConflictingAccesses.emplace_back(Storage, *Conflict, - RecordedAccess(BAI, SubPath)); - } - - Info.beginAccess(BAI, SubPath); - continue; - } - - if (auto *EAI = dyn_cast(&I)) { - auto It = Accesses.find(findAccessedStorage(EAI->getSource())); - AccessInfo &Info = It->getSecond(); - - BeginAccessInst *BAI = EAI->getBeginAccess(); - const IndexTrieNode *SubPath = ASA->findSubPathAccessed(BAI); - Info.endAccess(EAI, SubPath); - - // If the storage location has no more in-progress accesses, remove - // it to keep the StorageMap lean. - if (!Info.hasAccessesInProgress()) - Accesses.erase(It); - continue; - } - - if (VerifyMemOps && I.mayReadOrWriteMemory()) { - visitAccessedAddress(&I, [&Accesses](Operand *memOper) { - checkAccessedAddress(memOper, Accesses); - }); - } - - if (auto *AI = dyn_cast(&I)) { - // Record calls to swap() for potential Fix-Its. - if (isCallToStandardLibrarySwap(AI, Fn.getASTContext())) - CallsToSwap.push_back(AI); - else - checkForViolationsInNoEscapeClosures(Accesses, AI, ASA, - ConflictingAccesses); - continue; - } - - if (auto *TAI = dyn_cast(&I)) { - checkForViolationsInNoEscapeClosures(Accesses, TAI, ASA, - ConflictingAccesses); - continue; - } -#ifndef NDEBUG - // FIXME: Once AllocBoxToStack is fixed to correctly set noescape - // closure types, move this PartialApply verification into the - // SILVerifier to better pinpoint the offending pass. - if (auto *PAI = dyn_cast(&I)) { - ApplySite apply(PAI); - if (llvm::any_of(range(apply.getNumArguments()), - [apply](unsigned argIdx) { - return apply.getArgumentConvention(argIdx) - == SILArgumentConvention::Indirect_InoutAliasable; - })) { - checkNoEscapePartialApply(PAI); - } - } -#endif - // Sanity check to make sure entries are properly removed. - assert((!isa(&I) || Accesses.empty()) && - "Entries were not properly removed?!"); - } - } - - // Now that we've collected violations and suppressed calls, emit - // diagnostics. - for (auto &Violation : ConflictingAccesses) { - diagnoseExclusivityViolation(Violation, CallsToSwap, Fn.getASTContext()); - } -} +// ============================================================================= +// Function Pass Driver namespace { diff --git a/lib/SILOptimizer/Mandatory/IRGenPrepare.cpp b/lib/SILOptimizer/Mandatory/IRGenPrepare.cpp new file mode 100644 index 0000000000000..da159518b7548 --- /dev/null +++ b/lib/SILOptimizer/Mandatory/IRGenPrepare.cpp @@ -0,0 +1,87 @@ +//===--- IRGenPrepare.cpp -------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// +/// Cleanup SIL to make it suitable for IRGen. +/// +/// We perform the following canonicalizations: +/// +/// 1. We remove calls to Builtin.staticReport(), which are not needed post SIL. +/// +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "sil-cleanup" +#include "swift/SIL/SILFunction.h" +#include "swift/SIL/SILInstruction.h" +#include "swift/SIL/SILModule.h" +#include "swift/SILOptimizer/PassManager/Passes.h" +#include "swift/SILOptimizer/PassManager/Transforms.h" +#include "swift/SILOptimizer/Utils/Local.h" +#include "swift/Strings.h" + +using namespace swift; + +static bool cleanFunction(SILFunction &fn) { + bool madeChange = false; + + for (auto &bb : fn) { + for (auto i = bb.begin(), e = bb.end(); i != e;) { + // Make sure there is no iterator invalidation if the inspected + // instruction gets removed from the block. + SILInstruction *inst = &*i; + ++i; + + // Remove calls to Builtin.staticReport(). + auto *bi = dyn_cast(inst); + if (!bi) { + continue; + } + + const BuiltinInfo &bInfo = bi->getBuiltinInfo(); + if (bInfo.ID != BuiltinValueKind::StaticReport) { + continue; + } + + // The call to the builtin should get removed before we reach + // IRGen. + recursivelyDeleteTriviallyDeadInstructions(bi, /* Force */ true); + madeChange = true; + } + } + + return madeChange; +} + +//===----------------------------------------------------------------------===// +// Top Level Entrypoint +//===----------------------------------------------------------------------===// + +namespace { + +class IRGenPrepare : public SILFunctionTransform { + void run() override { + SILFunction *F = getFunction(); + + bool shouldInvalidate = cleanFunction(*F); + + if (shouldInvalidate) + invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions); + } +}; + +} // end anonymous namespace + + +SILTransform *swift::createIRGenPrepare() { + return new IRGenPrepare(); +} diff --git a/lib/SILOptimizer/Mandatory/MandatoryInlining.cpp b/lib/SILOptimizer/Mandatory/MandatoryInlining.cpp index 8536f24a91260..3f89d510d8ee7 100644 --- a/lib/SILOptimizer/Mandatory/MandatoryInlining.cpp +++ b/lib/SILOptimizer/Mandatory/MandatoryInlining.cpp @@ -259,8 +259,7 @@ static void collectPartiallyAppliedArguments( static SILFunction *getCalleeFunction( SILFunction *F, FullApplySite AI, bool &IsThick, SmallVectorImpl> &CaptureArgs, - SmallVectorImpl &FullArgs, PartialApplyInst *&PartialApply, - SILModule::LinkingMode Mode) { + SmallVectorImpl &FullArgs, PartialApplyInst *&PartialApply) { IsThick = false; PartialApply = nullptr; CaptureArgs.clear(); @@ -404,17 +403,19 @@ static SILFunction *getCalleeFunction( return nullptr; } - // If CalleeFunction is a declaration, see if we can load it. If we fail to - // load it, bail. - if (CalleeFunction->empty() - && !AI.getModule().linkFunction(CalleeFunction, Mode)) - return nullptr; - // If the CalleeFunction is a not-transparent definition, we can not process // it. if (CalleeFunction->isTransparent() == IsNotTransparent) return nullptr; + // If CalleeFunction is a declaration, see if we can load it. + if (CalleeFunction->empty()) + AI.getModule().loadFunction(CalleeFunction); + + // If we fail to load it, bail. + if (CalleeFunction->empty()) + return nullptr; + if (F->isSerialized() && !CalleeFunction->hasValidLinkageForFragileInline()) { if (!CalleeFunction->hasValidLinkageForFragileRef()) { @@ -433,22 +434,17 @@ static std::tuple tryDevirtualizeApplyHelper(FullApplySite InnerAI, SILBasicBlock::iterator I, ClassHierarchyAnalysis *CHA) { auto NewInstPair = tryDevirtualizeApply(InnerAI, CHA); - auto *NewInst = NewInstPair.first; - if (!NewInst) + if (!NewInstPair.second) { return std::make_tuple(InnerAI, I); + } - replaceDeadApply(InnerAI, NewInst); + replaceDeadApply(InnerAI, NewInstPair.first); auto newApplyAI = NewInstPair.second.getInstruction(); assert(newApplyAI && "devirtualized but removed apply site?"); - I = newApplyAI->getIterator(); - auto NewAI = FullApplySite::isa(newApplyAI); - // *NOTE*, it is important that we return I here since we may have - // devirtualized but not have a full apply site anymore. - if (!NewAI) - return std::make_tuple(FullApplySite(), I); - - return std::make_tuple(NewAI, I); + + return std::make_tuple(FullApplySite::isa(newApplyAI), + newApplyAI->getIterator()); } /// \brief Inlines all mandatory inlined functions into the body of a function, @@ -467,7 +463,6 @@ tryDevirtualizeApplyHelper(FullApplySite InnerAI, SILBasicBlock::iterator I, /// \returns true if successful, false if failed due to circular inlining. static bool runOnFunctionRecursively(SILFunction *F, FullApplySite AI, - SILModule::LinkingMode Mode, DenseFunctionSet &FullyInlinedSet, ImmutableFunctionSet::Factory &SetFactory, ImmutableFunctionSet CurrentInliningSet, @@ -515,13 +510,13 @@ runOnFunctionRecursively(SILFunction *F, FullApplySite AI, bool IsThick; PartialApplyInst *PAI; SILFunction *CalleeFunction = getCalleeFunction( - F, InnerAI, IsThick, CaptureArgs, FullArgs, PAI, Mode); + F, InnerAI, IsThick, CaptureArgs, FullArgs, PAI); if (!CalleeFunction) continue; // Then recursively process it first before trying to inline it. - if (!runOnFunctionRecursively(CalleeFunction, InnerAI, Mode, + if (!runOnFunctionRecursively(CalleeFunction, InnerAI, FullyInlinedSet, SetFactory, CurrentInliningSet, CHA)) { // If we failed due to circular inlining, then emit some notes to @@ -538,15 +533,10 @@ runOnFunctionRecursively(SILFunction *F, FullApplySite AI, return false; } - // Create our initial list of substitutions. - llvm::SmallVector ApplySubs(InnerAI.subs_begin(), - InnerAI.subs_end()); - - // Then if we have a partial_apply, add any additional subsitutions that - // we may require to the end of the list. - if (PAI) { - copy(PAI->getSubstitutions(), std::back_inserter(ApplySubs)); - } + // Get our list of substitutions. + auto Subs = (PAI + ? PAI->getSubstitutionMap() + : InnerAI.getSubstitutionMap()); SILOpenedArchetypesTracker OpenedArchetypesTracker(F); F->getModule().registerDeleteNotificationHandler( @@ -560,7 +550,7 @@ runOnFunctionRecursively(SILFunction *F, FullApplySite AI, } SILInliner Inliner(*F, *CalleeFunction, - SILInliner::InlineKind::MandatoryInline, ApplySubs, + SILInliner::InlineKind::MandatoryInline, Subs, OpenedArchetypesTracker); if (!Inliner.canInlineFunction(InnerAI)) { // See comment above about casting when devirtualizing and how this @@ -629,25 +619,12 @@ runOnFunctionRecursively(SILFunction *F, FullApplySite AI, //===----------------------------------------------------------------------===// namespace { -/// MandatoryInlining reruns on deserialized functions for two reasons, both -/// unrelated to mandatory inlining: -/// -/// 1. It recursively visits the entire call tree rooted at transparent -/// functions. This has the effect of linking all reachable functions. If they -/// aren't linked until the explicit SILLinker pass, then they don't benefit -/// from rerunning optimizations like PredictableMemOps. Ideally we wouldn't -/// need to rerun PredictableMemOps and wouldn't need to eagerly link anything -/// in the mandatory pipeline. -/// -/// 2. It may devirtualize non-transparent methods. It's not clear whether we -/// really need to devirtualize this early without actually inlining, but it can -/// unblock other optimizations in the mandatory pipeline. + class MandatoryInlining : public SILModuleTransform { /// The entry point to the transformation. void run() override { ClassHierarchyAnalysis *CHA = getAnalysis(); SILModule *M = getModule(); - SILModule::LinkingMode Mode = getOptions().LinkMode; bool ShouldCleanup = !getOptions().DebugSerialization; DenseFunctionSet FullyInlinedSet; ImmutableFunctionSet::Factory SetFactory; @@ -657,21 +634,15 @@ class MandatoryInlining : public SILModuleTransform { if (F.isThunk()) continue; + // Skip deserialized functions. + if (F.wasDeserializedCanonical()) + continue; + runOnFunctionRecursively(&F, - FullApplySite(static_cast(nullptr)), - Mode, FullyInlinedSet, - SetFactory, SetFactory.getEmptySet(), CHA); + FullApplySite(), FullyInlinedSet, SetFactory, + SetFactory.getEmptySet(), CHA); } - // Make sure that we de-serialize all transparent functions, - // even if we didn't inline them for some reason. - // Transparent functions are not available externally, so we - // have to generate code for them. - for (auto &F : *M) { - if (F.isTransparent()) - M->linkFunction(&F, Mode); - } - if (!ShouldCleanup) return; diff --git a/lib/SILOptimizer/Mandatory/MandatoryOptUtils.cpp b/lib/SILOptimizer/Mandatory/MandatoryOptUtils.cpp new file mode 100644 index 0000000000000..fb7716d1fcef5 --- /dev/null +++ b/lib/SILOptimizer/Mandatory/MandatoryOptUtils.cpp @@ -0,0 +1,98 @@ +//===--- MandatoryOptUtils.cpp --------------------------------------------===// +// +// 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-mandatory-utils" +#include "MandatoryOptUtils.h" +#include "swift/AST/DiagnosticEngine.h" +#include "swift/AST/DiagnosticsSIL.h" +#include "swift/AST/Expr.h" +#include "swift/ClangImporter/ClangModule.h" +#include "swift/SIL/InstructionUtils.h" +#include "swift/SIL/SILArgument.h" +#include "swift/SIL/SILBuilder.h" +#include "swift/SILOptimizer/PassManager/Passes.h" +#include "swift/SILOptimizer/PassManager/Transforms.h" +#include "swift/SILOptimizer/Utils/CFG.h" +#include "swift/SILOptimizer/Utils/Local.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallBitVector.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/Debug.h" + +STATISTIC(NumAssignRewritten, "Number of assigns rewritten"); + +using namespace swift; + +/// Emit the sequence that an assign instruction lowers to once we know +/// if it is an initialization or an assignment. If it is an assignment, +/// a live-in value can be provided to optimize out the reload. +void swift::lowerAssignInstruction(SILBuilderWithScope &B, AssignInst *Inst, + PartialInitializationKind isInitialization) { + DEBUG(llvm::dbgs() << " *** Lowering [isInit=" << unsigned(isInitialization) + << "]: " << *Inst << "\n"); + + ++NumAssignRewritten; + + SILValue Src = Inst->getSrc(); + SILLocation Loc = Inst->getLoc(); + + if (isInitialization == PartialInitializationKind::IsInitialization || + Inst->getDest()->getType().isTrivial(Inst->getModule())) { + + // If this is an initialization, or the storage type is trivial, we + // can just replace the assignment with a store. + assert(isInitialization != PartialInitializationKind::IsReinitialization); + B.createTrivialStoreOr(Loc, Src, Inst->getDest(), + StoreOwnershipQualifier::Init); + Inst->eraseFromParent(); + return; + } + + if (isInitialization == PartialInitializationKind::IsReinitialization) { + // We have a case where a convenience initializer on a class + // delegates to a factory initializer from a protocol extension. + // Factory initializers give us a whole new instance, so the existing + // instance, which has not been initialized and never will be, must be + // freed using dealloc_partial_ref. + SILValue Pointer = + B.createLoad(Loc, Inst->getDest(), LoadOwnershipQualifier::Take); + B.createStore(Loc, Src, Inst->getDest(), StoreOwnershipQualifier::Init); + + auto MetatypeTy = CanMetatypeType::get( + Inst->getDest()->getType().getASTType(), MetatypeRepresentation::Thick); + auto SILMetatypeTy = SILType::getPrimitiveObjectType(MetatypeTy); + SILValue Metatype = B.createValueMetatype(Loc, SILMetatypeTy, Pointer); + + B.createDeallocPartialRef(Loc, Pointer, Metatype); + Inst->eraseFromParent(); + return; + } + + assert(isInitialization == PartialInitializationKind::IsNotInitialization); + // Otherwise, we need to replace the assignment with the full + // load/store/release dance. Note that the new value is already + // considered to be retained (by the semantics of the storage type), + // and we're transferring that ownership count into the destination. + + // This is basically TypeLowering::emitStoreOfCopy, except that if we have + // a known incoming value, we can avoid the load. + SILValue IncomingVal = + B.createLoad(Loc, Inst->getDest(), LoadOwnershipQualifier::Take); + B.createStore(Inst->getLoc(), Src, Inst->getDest(), + StoreOwnershipQualifier::Init); + + B.emitDestroyValueOperation(Loc, IncomingVal); + Inst->eraseFromParent(); +} diff --git a/lib/SILOptimizer/Mandatory/MandatoryOptUtils.h b/lib/SILOptimizer/Mandatory/MandatoryOptUtils.h new file mode 100644 index 0000000000000..f9c02f4680af3 --- /dev/null +++ b/lib/SILOptimizer/Mandatory/MandatoryOptUtils.h @@ -0,0 +1,46 @@ +//===--- MandatoryOptUtils.h ----------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// Contains utility operations used by various mandatory passes. +/// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SILOPTIMIZER_MANDATORY_MANDATORYOOPTUTILS_H +#define SWIFT_SILOPTIMIZER_MANDATORY_MANDATORYOOPTUTILS_H + +#include "llvm/Support/Compiler.h" + +namespace swift { + +class SILBuilderWithScope; +class AssignInst; + +enum class PartialInitializationKind { + /// The box contains a fully-initialized value. + IsNotInitialization, + + /// The box contains a class instance that we own, but the instance has + /// not been initialized and should be freed with a special SIL + /// instruction made for this purpose. + IsReinitialization, + + /// The box contains an undefined value that should be ignored. + IsInitialization, +}; + +void lowerAssignInstruction(SILBuilderWithScope &B, AssignInst *Inst, + PartialInitializationKind isInitialization) + LLVM_LIBRARY_VISIBILITY; + +} // namespace swift + +#endif diff --git a/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp b/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp index 6689c4574d89d..5245e2ccf0df4 100644 --- a/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp +++ b/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp @@ -1372,9 +1372,14 @@ static bool optimizeMemoryAllocations(SILFunction &Fn) { // Set up the datastructure used to collect the uses of the allocation. SmallVector Uses; SmallVector Releases; - - // Walk the use list of the pointer, collecting them. - collectDIElementUsesFrom(MemInfo, Uses, Releases); + + // Walk the use list of the pointer, collecting them. If we are not able + // to optimize, skip this value. *NOTE* We may still scalarize values + // inside the value. + if (!collectDIElementUsesFrom(MemInfo, Uses, Releases)) { + ++I; + continue; + } Changed |= AllocOptimize(Alloc, Uses, Releases).doIt(); diff --git a/lib/SILOptimizer/Mandatory/RawSILInstLowering.cpp b/lib/SILOptimizer/Mandatory/RawSILInstLowering.cpp new file mode 100644 index 0000000000000..235b50428bc64 --- /dev/null +++ b/lib/SILOptimizer/Mandatory/RawSILInstLowering.cpp @@ -0,0 +1,89 @@ +//===--- RawSILInstLowering.cpp -------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "MandatoryOptUtils.h" +#include "swift/SIL/SILBuilder.h" +#include "swift/SIL/SILFunction.h" +#include "swift/SIL/SILInstruction.h" +#include "swift/SILOptimizer/PassManager/Passes.h" +#include "swift/SILOptimizer/PassManager/Transforms.h" + +using namespace swift; + +/// lowerRawSILOperations - There are a variety of raw-sil instructions like +/// 'assign' that are only used by this pass. Now that definite initialization +/// checking is done, remove them. +static bool lowerRawSILOperations(SILFunction &Fn) { + bool Changed = false; + for (auto &BB : Fn) { + auto I = BB.begin(), E = BB.end(); + while (I != E) { + SILInstruction *Inst = &*I; + ++I; + + // Unprocessed assigns just lower into assignments, not initializations. + if (auto *AI = dyn_cast(Inst)) { + SILBuilderWithScope B(AI); + lowerAssignInstruction(B, AI, + PartialInitializationKind::IsNotInitialization); + // Assign lowering may split the block. If it did, + // reset our iteration range to the block after the insertion. + if (B.getInsertionBB() != &BB) + I = E; + Changed = true; + continue; + } + + // mark_uninitialized just becomes a noop, resolving to its operand. + if (auto *MUI = dyn_cast(Inst)) { + MUI->replaceAllUsesWith(MUI->getOperand()); + MUI->eraseFromParent(); + Changed = true; + continue; + } + + // mark_function_escape just gets zapped. + if (isa(Inst)) { + Inst->eraseFromParent(); + Changed = true; + continue; + } + } + } + return Changed; +} + +//===----------------------------------------------------------------------===// +// Top Level Entrypoint +//===----------------------------------------------------------------------===// + +namespace { + +class RawSILInstLowering : public SILFunctionTransform { + void run() override { + // Do not try to relower raw instructions in canonical SIL. There won't be + // any there. + if (getFunction()->wasDeserializedCanonical()) { + return; + } + + // Lower raw-sil only instructions used by this pass, like "assign". + if (lowerRawSILOperations(*getFunction())) + invalidateAnalysis(SILAnalysis::InvalidationKind::FunctionBody); + } +}; + +} // end anonymous namespace + +SILTransform *swift::createRawSILInstLowering() { + return new RawSILInstLowering(); +} diff --git a/lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp b/lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp index 6d55006efefb0..f9e05c6290d23 100644 --- a/lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp +++ b/lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp @@ -11,10 +11,10 @@ //===----------------------------------------------------------------------===// #define DEBUG_TYPE "sil-semantic-arc-opts" -#include "swift/SIL/OwnershipChecker.h" +#include "swift/SIL/BasicBlockUtils.h" +#include "swift/SIL/OwnershipUtils.h" #include "swift/SIL/SILArgument.h" #include "swift/SIL/SILInstruction.h" -#include "swift/SIL/BasicBlockUtils.h" #include "swift/SILOptimizer/Analysis/PostOrderAnalysis.h" #include "swift/SILOptimizer/PassManager/Passes.h" #include "swift/SILOptimizer/PassManager/Transforms.h" diff --git a/lib/SILOptimizer/PassManager/PassPipeline.cpp b/lib/SILOptimizer/PassManager/PassPipeline.cpp index bd46d849f7200..3e6a28032a23a 100644 --- a/lib/SILOptimizer/PassManager/PassPipeline.cpp +++ b/lib/SILOptimizer/PassManager/PassPipeline.cpp @@ -71,6 +71,14 @@ static void addOwnershipModelEliminatorPipeline(SILPassPipelinePlan &P) { P.addOwnershipModelEliminator(); } +/// Passes for performing definite initialization. Must be run together in this +/// order. +static void addDefiniteInitialization(SILPassPipelinePlan &P) { + P.addMarkUninitializedFixup(); + P.addDefiniteInitialization(); + P.addRawSILInstLowering(); +} + static void addMandatoryOptPipeline(SILPassPipelinePlan &P, const SILOptions &Options) { P.startPipeline("Guaranteed Passes"); @@ -86,10 +94,11 @@ static void addMandatoryOptPipeline(SILPassPipelinePlan &P, P.addAllocBoxToStack(); P.addNoReturnFolding(); - P.addMarkUninitializedFixup(); - P.addDefiniteInitialization(); + addDefiniteInitialization(P); + P.addClosureLifetimeFixup(); P.addOwnershipModelEliminator(); P.addMandatoryInlining(); + P.addMandatorySILLinker(); P.addPredictableMemoryOptimizations(); // Diagnostic ConstantPropagation must be rerun on deserialized functions @@ -222,6 +231,10 @@ void addSSAPasses(SILPassPipelinePlan &P, OptimizationLevelKind OpLevel) { // Split up operations on stack-allocated aggregates (struct, tuple). P.addSROA(); + // Re-run predictable memory optimizations, since previous optimization + // passes sometimes expose oppotunities here. + P.addPredictableMemoryOptimizations(); + // Promote stack allocations to values. P.addMem2Reg(); @@ -314,7 +327,7 @@ void addSSAPasses(SILPassPipelinePlan &P, OptimizationLevelKind OpLevel) { static void addPerfDebugSerializationPipeline(SILPassPipelinePlan &P) { P.startPipeline("Performance Debug Serialization"); - P.addSILLinker(); + P.addPerformanceSILLinker(); } static void addPerfEarlyModulePassPipeline(SILPassPipelinePlan &P) { @@ -324,7 +337,7 @@ static void addPerfEarlyModulePassPipeline(SILPassPipelinePlan &P) { // we do not spend time optimizing them. P.addDeadFunctionElimination(); // Start by cloning functions from stdlib. - P.addSILLinker(); + P.addPerformanceSILLinker(); // Cleanup after SILGen: remove trivial copies to temporaries. P.addTempRValueOpt(); @@ -344,7 +357,7 @@ static void addHighLevelEarlyLoopOptPipeline(SILPassPipelinePlan &P) { static void addMidModulePassesStackPromotePassPipeline(SILPassPipelinePlan &P) { P.startPipeline("MidModulePasses+StackPromote"); P.addDeadFunctionElimination(); - P.addSILLinker(); + P.addPerformanceSILLinker(); P.addDeadObjectElimination(); P.addGlobalPropertyOpt(); @@ -440,8 +453,17 @@ static void addLateLoopOptPassPipeline(SILPassPipelinePlan &P) { // Try to hoist all releases, including epilogue releases. This should be // after FSO. P.addLateReleaseHoisting(); +} - // Has only an effect if the -assume-single-thread option is specified. +// Run passes that +// - should only run after all general SIL transformations. +// - have no reason to run before any other SIL optimizations. +// - don't require IRGen information. +static void addLastChanceOptPassPipeline(SILPassPipelinePlan &P) { + // Optimize access markers for improved IRGen after all other optimizations. + P.addAccessEnforcementOpts(); + + // Only has an effect if the -assume-single-thread option is specified. P.addAssumeSingleThreaded(); } @@ -456,7 +478,7 @@ SILPassPipelinePlan SILPassPipelinePlan::getLoweringPassPipeline() { SILPassPipelinePlan P; P.startPipeline("Address Lowering"); - P.addSILCleanup(); + P.addIRGenPrepare(); P.addAddressLowering(); return P; @@ -521,6 +543,8 @@ SILPassPipelinePlan::getPerformancePassPipeline(const SILOptions &Options) { addLateLoopOptPassPipeline(P); + addLastChanceOptPassPipeline(P); + // Has only an effect if the -gsil option is specified. addSILDebugInfoGeneratorPipeline(P); diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp index 393ab1f731eb6..5b34f2c1a1d42 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp @@ -308,7 +308,7 @@ bool PartialApplyCombiner::processSingleApply(FullApplySite AI) { } auto Callee = PAI->getCallee(); - SubstitutionList Subs = PAI->getSubstitutions(); + SubstitutionMap Subs = PAI->getSubstitutionMap(); // The partial_apply might be substituting in an open existential type. Builder.addOpenedArchetypeOperands(PAI); @@ -489,7 +489,7 @@ SILCombiner::optimizeApplyOfConvertFunctionInst(FullApplySite AI, assert(NewOpType.isAddress() && "Addresses should map to addresses."); auto UAC = Builder.createUncheckedAddrCast(AI.getLoc(), Op, NewOpType); Args.push_back(UAC); - } else if (OldOpType.getSwiftRValueType() != NewOpType.getSwiftRValueType()) { + } else if (OldOpType.getASTType() != NewOpType.getASTType()) { auto URC = Builder.createUncheckedBitCast(AI.getLoc(), Op, NewOpType); Args.push_back(URC); } else { @@ -501,10 +501,10 @@ SILCombiner::optimizeApplyOfConvertFunctionInst(FullApplySite AI, SILInstruction *NAI; if (auto *TAI = dyn_cast(AI)) NAI = Builder.createTryApply(AI.getLoc(), FRI, - SubstitutionList(), Args, + SubstitutionMap(), Args, TAI->getNormalBB(), TAI->getErrorBB()); else { - NAI = Builder.createApply(AI.getLoc(), FRI, SubstitutionList(), Args, + NAI = Builder.createApply(AI.getLoc(), FRI, SubstitutionMap(), Args, cast(AI)->isNonThrowing()); assert(FullApplySite::isa(NAI).getSubstCalleeType()->getAllResultsType() == AI.getSubstCalleeType()->getAllResultsType() && @@ -600,6 +600,51 @@ SILCombiner::optimizeConcatenationOfStringLiterals(ApplyInst *AI) { return tryToConcatenateStrings(AI, Builder); } +/// Determine the pattern for global_addr. +/// %3 = global_addr @$P : $*SomeP +/// %4 = init_existential_addr %3 : $*SomeP, $SomeC +/// %5 = alloc_ref $SomeC +/// store %5 to %4 : $*SomeC +/// %8 = alloc_stack $SomeP +/// copy_addr %3 to [initialization] %8 : $*SomeP +/// %9 = open_existential_addr immutable_access %8 : $*SomeP to $*@opened SomeP +static SILValue findInitExistentialFromGlobalAddr(GlobalAddrInst *GAI, + CopyAddrInst *CAI) { + assert(CAI->getSrc() == SILValue(GAI) && + "Broken Assumption! Global Addr is not the source of the passed in " + "copy_addr?!"); + + /// Check for a single InitExistential usage for GAI and + /// a simple dominance check: both InitExistential and CAI are in + /// the same basic block and only one InitExistential + /// occurs between GAI and CAI. + llvm::SmallPtrSet IEUses; + for (auto *Use : GAI->getUses()) { + if (auto *InitExistential = + dyn_cast(Use->getUser())) { + IEUses.insert(InitExistential); + } + } + + /// No InitExistential found in the basic block. + if (IEUses.empty()) + return SILValue(); + + /// Walk backwards from CAI instruction till the begining of the basic block + /// looking for InitExistential. + SILValue SingleIE; + for (auto II = CAI->getIterator().getReverse(), IE = CAI->getParent()->rend(); + II != IE; ++II) { + if (!IEUses.count(&*II)) + continue; + if (SingleIE) + return SILValue(); + + SingleIE = cast(&*II); + } + return SingleIE; +} + /// Returns the address of an object with which the stack location \p ASI is /// initialized. This is either a init_existential_addr or the destination of a /// copy_addr. Returns a null value if the address does not dominate the @@ -666,6 +711,10 @@ static SILValue getAddressOfStackInit(AllocStackInst *ASI, SILValue CAISrc = CAI->getSrc(); if (auto *ASI = dyn_cast(CAISrc)) return getAddressOfStackInit(ASI, CAI, isCopied); + // Check if the CAISrc is a global_addr. + if (auto *GAI = dyn_cast(CAISrc)) { + return findInitExistentialFromGlobalAddr(GAI, CAI); + } return CAISrc; } return cast(SingleWrite); @@ -719,7 +768,7 @@ static SILInstruction *findInitExistential(FullApplySite AI, SILValue Self, if (auto *Open = dyn_cast(Self)) { if (auto *IE = dyn_cast(Open->getOperand())) { - auto Ty = Open->getType().getSwiftRValueType(); + auto Ty = Open->getType().getASTType(); while (auto Metatype = dyn_cast(Ty)) Ty = Metatype.getInstanceType(); OpenedArchetype = cast(Ty); @@ -754,25 +803,24 @@ SILCombiner::createApplyWithConcreteType(FullApplySite AI, // Form a new set of substitutions where Self is // replaced by a concrete type. - SmallVector Substitutions; + SubstitutionMap Substitutions; if (FnTy->isPolymorphic()) { - auto FnSubsMap = - FnTy->getGenericSignature()->getSubstitutionMap(AI.getSubstitutions()); - auto FinalSubsMap = FnSubsMap.subst( + auto FnSubsMap = AI.getSubstitutionMap(); + Substitutions = FnSubsMap.subst( [&](SubstitutableType *type) -> Type { if (type == OpenedArchetype) return ConcreteType; return type; }, [&](CanType origTy, Type substTy, - ProtocolType *proto) -> Optional { + ProtocolDecl *proto) -> Optional { if (substTy->isEqual(ConcreteType)) { - assert(proto->getDecl() == Conformance.getRequirement()); + assert(proto == Conformance.getRequirement()); return Conformance; } - return ProtocolConformanceRef(proto->getDecl()); + return ProtocolConformanceRef(proto); }); - FnTy->getGenericSignature()->getSubstitutions(FinalSubsMap, Substitutions); + // Handle polymorphic functions by properly substituting // their parameter types. CanSILFunctionType SFT = FnTy->substGenericArgs( @@ -854,17 +902,17 @@ ConformanceAndConcreteType::ConformanceAndConcreteType( Conformances = IE->getConformances(); ConcreteType = IE->getFormalConcreteType(); NewSelf = IE; - ExistentialType = IE->getOperand()->getType().getSwiftRValueType(); + ExistentialType = IE->getOperand()->getType().getASTType(); } else if (auto IER = dyn_cast(InitExistential)) { Conformances = IER->getConformances(); ConcreteType = IER->getFormalConcreteType(); NewSelf = IER->getOperand(); - ExistentialType = IER->getType().getSwiftRValueType(); + ExistentialType = IER->getType().getASTType(); } else if (auto IEM = dyn_cast(InitExistential)){ Conformances = IEM->getConformances(); NewSelf = IEM->getOperand(); - ConcreteType = NewSelf->getType().getSwiftRValueType(); - ExistentialType = IEM->getType().getSwiftRValueType(); + ConcreteType = NewSelf->getType().getASTType(); + ExistentialType = IEM->getType().getASTType(); while (auto InstanceType = dyn_cast(ExistentialType)) { ExistentialType = InstanceType.getInstanceType(); ConcreteType = cast(ConcreteType).getInstanceType(); @@ -879,8 +927,8 @@ ConformanceAndConcreteType::ConformanceAndConcreteType( auto ExistentialSig = Ctx.getExistentialSignature(ExistentialType, AI.getModule().getSwiftModule()); - Substitution ConcreteSub(ConcreteType, Conformances); - auto SubMap = ExistentialSig->getSubstitutionMap({&ConcreteSub, 1}); + auto SubMap = SubstitutionMap::get(ExistentialSig, { ConcreteType }, + Conformances); // If the requirement is in a base protocol that is refined by the // conforming protocol, fish out the exact conformance for the base @@ -934,10 +982,10 @@ static bool isAddressInitializedAtCall(SILValue addr, SILInstruction *AI, if (!DT->properlyDominates(AI, user)) return false; } else { - assert(isa(user) || isa(user) - || isa(user) - || isa(user) - || isDebugInst(user) && "Unexpected instruction"); + assert(isa(user) || isa(user) || + isa(user) || + isa(user) || + user->isDebugInstruction() && "Unexpected instruction"); } } return true; @@ -1056,14 +1104,14 @@ SILCombiner::propagateConcreteTypeOfInitExistential(FullApplySite AI, // Notice that it is sufficient to compare the return type to the // substituted type because types that depend on the Self type are // not allowed (for example [Self] is not allowed). - if (AI.getType().getSwiftRValueType() == WMI->getLookupType()) + if (AI.getType().getASTType() == WMI->getLookupType()) return nullptr; // We need to handle the Self return type. // In we find arguments that are not the 'self' argument and if // they are of the Self type then we abort the optimization. for (auto Arg : AI.getArgumentsWithoutSelf()) { - if (Arg->getType().getSwiftRValueType() == WMI->getLookupType()) + if (Arg->getType().getASTType() == WMI->getLookupType()) return nullptr; } @@ -1138,8 +1186,8 @@ SILCombiner::propagateConcreteTypeOfInitExistential(FullApplySite AI) { // In we find arguments that are not the 'self' argument and if // they are of the Self type then we abort the optimization. for (auto Arg : AI.getArgumentsWithoutSelf()) { - if (Arg->getType().getSwiftRValueType() == - AI.getArguments().back()->getType().getSwiftRValueType()) + if (Arg->getType().getASTType() == + AI.getArguments().back()->getType().getASTType()) return nullptr; } @@ -1317,11 +1365,11 @@ FullApplySite SILCombiner::rewriteApplyCallee(FullApplySite apply, Builder.addOpenedArchetypeOperands(apply.getInstruction()); if (auto *TAI = dyn_cast(apply)) { return Builder.createTryApply(TAI->getLoc(), callee, - TAI->getSubstitutions(), arguments, + TAI->getSubstitutionMap(), arguments, TAI->getNormalBB(), TAI->getErrorBB()); } else { - return Builder.createApply(apply.getLoc(), callee, apply.getSubstitutions(), - arguments, + return Builder.createApply(apply.getLoc(), callee, + apply.getSubstitutionMap(), arguments, cast(apply)->isNonThrowing()); } } diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerBuiltinVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerBuiltinVisitors.cpp index 81f24922bcd5f..c52d371752841 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerBuiltinVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerBuiltinVisitors.cpp @@ -84,10 +84,10 @@ SILInstruction *SILCombiner::optimizeBuiltinCanBeObjCClass(BuiltinInst *BI) { assert(BI->hasSubstitutions() && "Expected substitutions for canBeClass"); auto const &Subs = BI->getSubstitutions(); - assert((Subs.size() == 1) && + assert((Subs.getReplacementTypes().size() == 1) && "Expected one substitution in call to canBeClass"); - auto Ty = Subs[0].getReplacement()->getCanonicalType(); + auto Ty = Subs.getReplacementTypes()[0]->getCanonicalType(); switch (Ty->canBeClass()) { case TypeTraitResult::IsNot: return Builder.createIntegerLiteral(BI->getLoc(), BI->getType(), @@ -514,11 +514,10 @@ SILInstruction *SILCombiner::visitBuiltinInst(BuiltinInst *I) { [](const APInt &i) -> bool { return false; } /* isZero */, Builder, this); case BuiltinValueKind::DestroyArray: { - SubstitutionList Substs = I->getSubstitutions(); + SubstitutionMap Substs = I->getSubstitutions(); // Check if the element type is a trivial type. - if (Substs.size() == 1) { - Substitution Subst = Substs[0]; - Type ElemType = Subst.getReplacement(); + if (Substs.getReplacementTypes().size() == 1) { + Type ElemType = Substs.getReplacementTypes()[0]; auto &SILElemTy = I->getModule().Types.getTypeLowering(ElemType); // Destroying an array of trivial types is a no-op. if (SILElemTy.isTrivial()) diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerCastVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerCastVisitors.cpp index b2c65d665408a..64d21410b2179 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerCastVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerCastVisitors.cpp @@ -192,7 +192,6 @@ visitPointerToAddressInst(PointerToAddressInst *PTAI) { SILInstruction * SILCombiner::visitUncheckedAddrCastInst(UncheckedAddrCastInst *UADCI) { Builder.setCurrentDebugScope(UADCI->getDebugScope()); - SILModule &Mod = UADCI->getModule(); // (unchecked-addr-cast (unchecked-addr-cast x X->Y) Y->Z) // -> @@ -208,68 +207,7 @@ SILCombiner::visitUncheckedAddrCastInst(UncheckedAddrCastInst *UADCI) { return Builder.createUpcast(UADCI->getLoc(), UADCI->getOperand(), UADCI->getType()); - // See if we have all loads from this unchecked_addr_cast. If we do, load the - // original type and create the appropriate bitcast. - - // First if our UADCI has not users, bail. This will be eliminated by DCE. - if (UADCI->use_empty()) - return nullptr; - - SILType InputTy = UADCI->getOperand()->getType(); - SILType OutputTy = UADCI->getType(); - - // If either type is address only, do not do anything here. - if (InputTy.isAddressOnly(Mod) || OutputTy.isAddressOnly(Mod)) - return nullptr; - - bool InputIsTrivial = InputTy.isTrivial(Mod); - bool OutputIsTrivial = OutputTy.isTrivial(Mod); - - // If our input is trivial and our output type is not, do not do - // anything. This is to ensure that we do not change any types reference - // semantics from trivial -> reference counted. - if (InputIsTrivial && !OutputIsTrivial) - return nullptr; - - // Check that the input type can be value cast to the output type. It is - // possible to cast the address of a smaller InputType to the address of a - // larger OutputType (the actual memory object must be large enough to hold - // both types). However, such address casts cannot be converted to value - // casts. - if (!SILType::canPerformABICompatibleUnsafeCastValue(InputTy, OutputTy, - UADCI->getModule())) { - return nullptr; - } - // For each user U of the unchecked_addr_cast... - for (auto U : getNonDebugUses(UADCI)) - // Check if it is load. If it is not a load, bail... - if (!isa(U->getUser())) - return nullptr; - - SILValue Op = UADCI->getOperand(); - SILLocation Loc = UADCI->getLoc(); - - // Ok, we have all loads. Lets simplify this. Go back through the loads a - // second time, rewriting them into a load + bitcast from our source. - auto UsesRange = getNonDebugUses(UADCI); - for (auto UI = UsesRange.begin(), E = UsesRange.end(); UI != E;) { - // Grab the original load. - LoadInst *L = cast(UI->getUser()); - UI++; - - // Insert a new load from our source and bitcast that as appropriate. - LoadInst *NewLoad = - Builder.createLoad(Loc, Op, LoadOwnershipQualifier::Unqualified); - auto *BitCast = Builder.createUncheckedBitCast(Loc, NewLoad, - OutputTy.getObjectType()); - // Replace all uses of the old load with the new bitcasted result and erase - // the old load. - replaceInstUsesWith(*L, BitCast); - eraseInstFromFunction(*L); - } - - // Delete the old cast. - return eraseInstFromFunction(*UADCI); + return nullptr; } SILInstruction * diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp index 0cae8fed5150c..f3be21d91206c 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp @@ -415,7 +415,7 @@ SILInstruction *SILCombiner::visitAllocStackInst(AllocStackInst *AS) { if (auto *OEAI = dyn_cast(Op->getUser())) { for (auto *Op : OEAI->getUses()) { assert(isa(Op->getUser()) || - isDebugInst(Op->getUser()) && "Unexpected instruction"); + Op->getUser()->isDebugInstruction() && "Unexpected instruction"); ToDelete.insert(Op->getUser()); } } @@ -425,7 +425,7 @@ SILInstruction *SILCombiner::visitAllocStackInst(AllocStackInst *AS) { isa(Op->getUser()) || isa(Op->getUser()) || isa(Op->getUser()) || - isDebugInst(Op->getUser()) && "Unexpected instruction"); + Op->getUser()->isDebugInstruction() && "Unexpected instruction"); ToDelete.insert(Op->getUser()); } @@ -920,7 +920,7 @@ SILCombiner::visitInjectEnumAddrInst(InjectEnumAddrInst *IEAI) { // we don't care about the dealloc stack instructions continue; } - if (isDebugInst(CurrUser) || isa(CurrUser)) { + if (CurrUser->isDebugInstruction() || isa(CurrUser)) { // These Instructions are a non-risky use we can ignore continue; } @@ -1496,7 +1496,7 @@ visitClassifyBridgeObjectInst(ClassifyBridgeObjectInst *CBOI) { if (!URC) return nullptr; - auto type = URC->getOperand()->getType().getSwiftRValueType(); + auto type = URC->getOperand()->getType().getASTType(); if (ClassDecl *cd = type->getClassOrBoundGenericClass()) { if (!cd->isObjC()) { auto int1Ty = SILType::getBuiltinIntegerType(1, Builder.getASTContext()); diff --git a/lib/SILOptimizer/Transforms/AccessEnforcementOpts.cpp b/lib/SILOptimizer/Transforms/AccessEnforcementOpts.cpp new file mode 100644 index 0000000000000..02502e01679e8 --- /dev/null +++ b/lib/SILOptimizer/Transforms/AccessEnforcementOpts.cpp @@ -0,0 +1,636 @@ +//===------ AccessEnforcementOpts.cpp - Optimize access enforcement -------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// This pass optimizes access enforcement as follows: +/// +/// Access marker folding: Find begin/end access scopes that are uninterrupted +/// by a potential conflicting access. Flag those as [nontracking] access. +/// +/// Folding must prove that no dynamic conflicts occur inside of an access +/// scope. That is, a scope has no "nested inner conflicts". The access itself +/// may still conflict with an outer scope. If successful, folding simply sets +/// the [no_nested_conflict] attribute on the begin_[unpaired_]access +/// instruction and removes all corresponding end_[unpaired_]access +/// instructions. +/// +/// This analysis is conceptually similar to DiagnoseStaticExclusivity. The +/// difference is that it conservatively considers any dynamic access that may +/// alias, as opposed to only the obviously aliasing accesses (it is the +/// complement of the static diagnostic pass in that respect). This makes a +/// considerable difference in the implementation. For example, +/// DiagnoseStaticExclusivity must be able to fully analyze all @inout_aliasable +/// parameters because they aren't dynamically enforced. This optimization +/// completely ignores @inout_aliasable paramters because it only cares about +/// dynamic enforcement. This optimization also does not attempt to +/// differentiate accesses on disjoint subaccess paths, because it should not +/// weaken enforcement in any way--a program that traps at -Onone should also +/// trap at -O. +/// +/// Access folding is a forward data flow analysis that tracks open accesses. If +/// any path to an access' end of scope has a potentially conflicting access, +/// then that access is marked as a nested conflict. +/// +/// Pass order dependencies: +/// - Will benefit from running after AccessEnforcementSelection. +/// +/// TODO: Perform another run of AccessEnforcementSelection immediately before +/// this pass. Currently, that pass only works well when run before +/// AllocBox2Stack. Ideally all such closure analysis passes are combined into a +/// shared analysis with a set of associated optimizations that can be rerun at +/// any point in the pipeline. Until then, we could settle for a partially +/// working AccessEnforcementSelection, or expand it somewhat to handle +/// alloc_stack. +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "access-enforcement-opts" + +#include "swift/SIL/DebugUtils.h" +#include "swift/SIL/MemAccessUtils.h" +#include "swift/SIL/SILFunction.h" +#include "swift/SILOptimizer/Analysis/AccessedStorageAnalysis.h" +#include "swift/SILOptimizer/PassManager/Transforms.h" +#include "swift/SILOptimizer/Utils/Local.h" +#include "llvm/ADT/SmallBitVector.h" + +using namespace swift; + +namespace swift { +/// Represents the identity of a storage location being accessed. +/// +/// A value-based subclass of AccessedStorage with identical layout. This +/// provides access to pass-specific data in reserved bits. +/// +/// The fully descriptive class name allows forward declaration in order to +/// define bitfields in AccessedStorage. +/// +/// Aliased to AccessInfo in this file. +class AccessEnforcementOptsInfo : public AccessedStorage { +public: + AccessEnforcementOptsInfo(const AccessedStorage &storage) + : AccessedStorage(storage) { + Bits.AccessEnforcementOptsInfo.beginAccessIndex = 0; + Bits.AccessEnforcementOptsInfo.seenNestedConflict = false; + } + + /// Get a unique index for this access within its function. + unsigned getAccessIndex() const { + return Bits.AccessEnforcementOptsInfo.beginAccessIndex; + } + + void setAccessIndex(unsigned index) { + Bits.AccessEnforcementOptsInfo.beginAccessIndex = index; + assert(unsigned(Bits.AccessEnforcementOptsInfo.beginAccessIndex) == index); + } + + /// Has the analysis seen a conflicting nested access on any path within this + /// access' scope. + bool seenNestedConflict() const { + return Bits.AccessEnforcementOptsInfo.seenNestedConflict; + } + + void setSeenNestedConflict() { + Bits.AccessEnforcementOptsInfo.seenNestedConflict = 1; + } + + void dump() const { + AccessedStorage::dump(); + llvm::dbgs() << " access index: " << getAccessIndex() << " <" + << (seenNestedConflict() ? "" : "no ") << "conflict>\n"; + } +}; +using AccessInfo = AccessEnforcementOptsInfo; +} // namespace swift + +namespace { +// Sparse access sets are used temporarily for fast operations by local +// reachability analysis. We don't care if they take more space. +class SparseAccessSet; + +/// A dense set of begin_access instructions as a compact vector. Reachability +/// results are stored here because very few accesses are typically in-progress +/// at a particular program point, particularly at block boundaries. +using DenseAccessSet = SmallVector; + +/// Analyze a function's formal access to determine nested conflicts. +/// +/// Maps each begin access instruction to its AccessInfo, which: +/// - identifies the accessed memory for conflict detection +/// - contains a pass-specific reachability set index +/// - contains a pass-specific flag that indicates the presence of a conflict +/// on any path. +/// +/// If, after computing reachability, an access' conflict flag is still not set, +/// then all paths in its scope are conflict free. Reachability begins at a +/// begin_access instruction and ends either at a potential conflict +/// or at the end_access instruction that is associated with the +/// begin_access. +/// +/// Forward data flow computes `blockOutAccess` for each block. At a control +/// flow merge, this analysis forms an intersection of reachable accesses on +/// each path. Only visited predecessors are merged (unvisited paths +/// optimistically assume reachability). Before a block is visited, it has no +/// map entry in blockOutAccess. Blocks are processed in RPO order, and a single +/// begin_access dominates all associated end_access instructions. Consequently, +/// when a block is first visited, blockOutAccess contains the maximal +/// reachability set. Further iteration would only reduce this set. +/// +/// The only result of this analysis is the seenNestedConflict flags in +/// AccessInfo. Since reducing a reachability set cannot further detect +/// conflicts, there is no need to iterate to a reachability fix point. +class AccessConflictAnalysis { +public: + using AccessMap = llvm::SmallDenseMap; + // This result of this analysis is a map from all BeginAccessInst in this + // function to AccessInfo. + struct Result { + /// Map each begin access to its AccessInfo with index, data, and flags. + /// Iterating over this map is nondeterministic. If it is necessary to order + /// the accesses, then AccessInfo::getAccessIndex() can be used. + AccessMap accessMap; + + /// Convenience. + /// + /// Note: If AccessInfo has already been retrieved, get the index directly + /// from it instead of calling this to avoid additional hash lookup. + unsigned getAccessIndex(BeginAccessInst *beginAccess) const { + return getAccessInfo(beginAccess).getAccessIndex(); + } + + /// Get the AccessInfo for a BeginAccessInst within this function. All + /// accesses are mapped by identifyBeginAccesses(). + AccessInfo &getAccessInfo(BeginAccessInst *beginAccess) { + auto iter = accessMap.find(beginAccess); + assert(iter != accessMap.end()); + return iter->second; + } + const AccessInfo &getAccessInfo(BeginAccessInst *beginAccess) const { + return const_cast(*this).getAccessInfo(beginAccess); + } + }; + +private: + SILFunction *F; + PostOrderFunctionInfo *PO; + AccessedStorageAnalysis *ASA; + + /// Tracks the in-scope accesses at the end of each block, for the purpose of + /// finding nested conflicts. (Out-of-scope accesses are currently only + /// tracked locally for the purpose of merging access scopes.) + llvm::SmallDenseMap blockOutAccess; + + Result result; + +public: + AccessConflictAnalysis(SILFunction *F, PostOrderFunctionInfo *PO, + AccessedStorageAnalysis *ASA) + : F(F), PO(PO), ASA(ASA) {} + + Result &&analyze() &&; + +protected: + void identifyBeginAccesses(); + + void visitBeginAccess(BeginAccessInst *beginAccess, + SparseAccessSet &accessMap); + + void visitEndAccess(EndAccessInst *endAccess, SparseAccessSet &accessMap); + + void visitFullApply(FullApplySite fullApply, SparseAccessSet &accessMap); + + void mergePredAccesses(SILBasicBlock *succBB, + SparseAccessSet &mergedAccesses); + + void visitBlock(SILBasicBlock *BB); +}; + +/// A sparse set of in-flight accesses. +/// +/// Explodes a DenseAccessSet into a temporary sparse set for comparison +/// and membership. +/// +/// The AccessConflictAnalysis result provides the instruction to index +/// mapping. +class SparseAccessSet { + AccessConflictAnalysis::Result &result; + + // Mark the in-scope accesses. + // (Most functions have < 64 accesses.) + llvm::SmallBitVector inScopeBitmask; + // Mark a potential conflicts on each access since the last begin/end marker. + llvm::SmallBitVector conflictBitmask; + DenseAccessSet denseVec; // Hold all local accesses seen thus far. + +public: + /// Iterate over in-scope, conflict free access. + class NoNestedConflictIterator { + const SparseAccessSet &sparseSet; + DenseAccessSet::const_iterator denseIter; + + public: + NoNestedConflictIterator(const SparseAccessSet &set) + : sparseSet(set), denseIter(set.denseVec.begin()) {} + + BeginAccessInst *next() { + auto end = sparseSet.denseVec.end(); + while (denseIter != end) { + BeginAccessInst *beginAccess = *denseIter; + ++denseIter; + unsigned sparseIndex = sparseSet.result.getAccessIndex(beginAccess); + if (sparseSet.inScopeBitmask[sparseIndex] + && !sparseSet.conflictBitmask[sparseIndex]) { + return beginAccess; + } + } + return nullptr; + } + }; + + SparseAccessSet(AccessConflictAnalysis::Result &result) + : result(result), inScopeBitmask(result.accessMap.size()), + conflictBitmask(result.accessMap.size()) {} + + // All accessed in the given denseVec are presumed to be in-scope and conflict + // free. + SparseAccessSet(const DenseAccessSet &denseVec, + AccessConflictAnalysis::Result &result) + : result(result), inScopeBitmask(result.accessMap.size()), + conflictBitmask(result.accessMap.size()), denseVec(denseVec) { + for (BeginAccessInst *beginAccess : denseVec) + inScopeBitmask.set(result.getAccessIndex(beginAccess)); + } + bool hasConflictFreeAccess() const { + NoNestedConflictIterator iterator(*this); + return iterator.next() == nullptr; + } + + bool hasInScopeAccess() const { + return llvm::any_of(denseVec, [this](BeginAccessInst *beginAccess) { + unsigned sparseIndex = result.getAccessIndex(beginAccess); + return inScopeBitmask[sparseIndex]; + }); + } + + bool isInScope(unsigned index) const { return inScopeBitmask[index]; } + + // Insert the given BeginAccessInst with its corresponding reachability index. + // Set the in-scope bit and reset the conflict bit. + bool enterScope(BeginAccessInst *beginAccess, unsigned index) { + assert(!inScopeBitmask[index] + && "nested access should not be dynamically enforced."); + inScopeBitmask.set(index); + conflictBitmask.reset(index); + denseVec.push_back(beginAccess); + return true; + } + + /// End the scope of the given access by marking it in-scope and clearing the + /// conflict bit. (The conflict bit only marks conflicts since the last begin + /// *or* end access). + void exitScope(unsigned index) { inScopeBitmask.reset(index); } + + bool seenConflict(unsigned index) const { return conflictBitmask[index]; } + + void setConflict(unsigned index) { conflictBitmask.set(index); } + + // Only merge accesses that are present on the `other` map. i.e. erase + // all accesses in this map that are not present in `other`. + void merge(const SparseAccessSet &other) { + inScopeBitmask &= other.inScopeBitmask; + // Currently only conflict free accesses are preserved across blocks by this + // analysis. Otherwise, taking the union of conflict bits would be valid. + assert(other.conflictBitmask.none()); + } + + void copyNoNestedConflictInto(DenseAccessSet &other) { + other.clear(); + NoNestedConflictIterator iterator(*this); + while (BeginAccessInst *beginAccess = iterator.next()) + other.push_back(beginAccess); + } + + // Dump only the accesses with no conflict up to this point. + void dump() const { + for (BeginAccessInst *beginAccess : denseVec) { + unsigned sparseIndex = result.getAccessIndex(beginAccess); + if (conflictBitmask[sparseIndex]) + continue; + + llvm::dbgs() << *beginAccess << " "; + if (!inScopeBitmask[sparseIndex]) + llvm::dbgs() << " [noscope]"; + result.getAccessInfo(beginAccess).dump(); + } + } +}; +} // namespace + +// Top-level driver for AccessConflictAnalysis. +AccessConflictAnalysis::Result &&AccessConflictAnalysis::analyze() && { + // Populate beginAccessMap. + identifyBeginAccesses(); + + // Perform data flow and set the conflict flags on AccessInfo. + for (auto *BB : PO->getReversePostOrder()) + visitBlock(BB); + + return std::move(result); +} + +// Find all begin access operations in this function. Map each access to +// AccessInfo, which includes its identified memory location, identifying +// index, and analysis result flags. +// +// TODO: begin_unpaired_access is not tracked. Even though begin_unpaired_access +// isn't explicitly paired, it may be possible after devirtualization and +// inlining to find all uses of the scratch buffer. However, this doesn't +// currently happen in practice (rdar://40033735). +void AccessConflictAnalysis::identifyBeginAccesses() { + for (auto &BB : *F) { + for (auto &I : BB) { + auto *beginAccess = dyn_cast(&I); + if (!beginAccess) + continue; + + if (beginAccess->getEnforcement() != SILAccessEnforcement::Dynamic) + continue; + + // The accessed base is expected to be valid for begin_access, but for + // 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. + const AccessedStorage &storage = + findAccessedStorageNonNested(beginAccess->getSource()); + if (!storage) + continue; + + auto iterAndSuccess = result.accessMap.try_emplace( + beginAccess, static_cast(storage)); + (void)iterAndSuccess; + assert(iterAndSuccess.second); + + // Add a pass-specific access index to the mapped storage object. + AccessInfo &info = iterAndSuccess.first->second; + info.setAccessIndex(result.accessMap.size() - 1); + assert(!info.seenNestedConflict()); + } + } +} + +/// When a conflict is detected, flag the access so it can't be folded, and +/// remove its index from the current access set so we stop checking for +/// conflicts. Erasing from SparseAccessSet does not invalidate any iterators. +static void recordConflict(AccessInfo &info, SparseAccessSet &accessSet) { + info.setSeenNestedConflict(); + accessSet.setConflict(info.getAccessIndex()); +} + +// Given an "inner" access, check for potential conflicts with any outer access. +// Allow these overlapping accesses: +// - read/read +// - different bases, both valid, and at least one is local +// +// Remove any outer access that may conflict from the accessSet. +// and flag the conflict in beginAccessMap. +// +// Record the inner access in the accessSet. +// +void AccessConflictAnalysis::visitBeginAccess(BeginAccessInst *innerBeginAccess, + SparseAccessSet &accessSet) { + if (innerBeginAccess->getEnforcement() != SILAccessEnforcement::Dynamic) + return; + + const AccessInfo &innerAccess = result.getAccessInfo(innerBeginAccess); + SILAccessKind innerAccessKind = innerBeginAccess->getAccessKind(); + + SparseAccessSet::NoNestedConflictIterator accessIter(accessSet); + while (BeginAccessInst *outerBeginAccess = accessIter.next()) { + // If both are reads, keep the mapped access. + if (!accessKindMayConflict(innerAccessKind, + outerBeginAccess->getAccessKind())) { + continue; + } + AccessInfo &outerAccess = result.getAccessInfo(outerBeginAccess); + + // If there is no potential conflict, leave the outer access mapped. + if (!outerAccess.isDistinctFrom(innerAccess)) + continue; + + DEBUG(innerAccess.dump(); llvm::dbgs() << " may conflict with:\n"; + outerAccess.dump()); + + recordConflict(outerAccess, accessSet); + } + DEBUG(llvm::dbgs() << "Recording access: " << *innerBeginAccess; + llvm::dbgs() << " at: "; innerAccess.dump()); + + // Record the current access in the map. It can potentially be folded + // regardless of whether it may conflict with an outer access. + bool inserted = + accessSet.enterScope(innerBeginAccess, innerAccess.getAccessIndex()); + (void)inserted; + assert(inserted && "the same begin_access cannot be seen twice."); +} + +void AccessConflictAnalysis::visitEndAccess(EndAccessInst *endAccess, + SparseAccessSet &accessSet) { + auto *beginAccess = endAccess->getBeginAccess(); + if (beginAccess->getEnforcement() != SILAccessEnforcement::Dynamic) + return; + + unsigned index = result.getAccessIndex(beginAccess); + DEBUG(if (accessSet.seenConflict(index)) llvm::dbgs() + << "No conflict on one path from " << *beginAccess << " to " + << *endAccess); + + // Erase this access from the sparse set. We only want to detect conflicts + // within the access scope. + accessSet.exitScope(index); +} + +void AccessConflictAnalysis::visitFullApply(FullApplySite fullApply, + SparseAccessSet &accessSet) { + FunctionAccessedStorage callSiteAccesses; + ASA->getCallSiteEffects(callSiteAccesses, fullApply); + + SparseAccessSet::NoNestedConflictIterator accessIter(accessSet); + while (BeginAccessInst *outerBeginAccess = accessIter.next()) { + + // If there is no potential conflict, leave the outer access mapped. + SILAccessKind accessKind = outerBeginAccess->getAccessKind(); + AccessInfo &outerAccess = result.getAccessInfo(outerBeginAccess); + if (!callSiteAccesses.mayConflictWith(accessKind, outerAccess)) + continue; + + DEBUG(llvm::dbgs() << *fullApply.getInstruction() << " call site access: "; + callSiteAccesses.dump(); llvm::dbgs() << " may conflict with:\n"; + outerAccess.dump()); + + recordConflict(outerAccess, accessSet); + } +} + +// Merge all predecessor accesses into the local acces set. Only propagate +// accesses that are still present in all predecessors. The absence of a begin +// access from a visited predecessor indicates the presence of a conflict. A +// block has been visited if it has a map entry in blockOutAccess. +void AccessConflictAnalysis::mergePredAccesses(SILBasicBlock *succBB, + SparseAccessSet &mergedAccesses) { + for (SILBasicBlock *predBB : succBB->getPredecessorBlocks()) { + auto mapI = blockOutAccess.find(predBB); + if (mapI == blockOutAccess.end()) + continue; + + const DenseAccessSet &predSet = mapI->second; + mergedAccesses.merge(SparseAccessSet(predSet, result)); + } +} + +// Compute access reachability within the given block. +void AccessConflictAnalysis::visitBlock(SILBasicBlock *BB) { + // Sparse set for tracking accesses with an individual block. + SparseAccessSet accessSet(result); + mergePredAccesses(BB, accessSet); + + for (auto &I : *BB) { + if (auto *BAI = dyn_cast(&I)) { + visitBeginAccess(BAI, accessSet); + continue; + } + if (auto *EAI = dyn_cast(&I)) { + visitEndAccess(EAI, accessSet); + continue; + } + if (auto fullApply = FullApplySite::isa(&I)) { + visitFullApply(fullApply, accessSet); + } + } + DEBUG(if (accessSet.hasConflictFreeAccess()) { + llvm::dbgs() << "Initializing no-conflict access out of bb" + << BB->getDebugID() << "\n"; + accessSet.dump(); + }); + if (BB->getTerminator()->isFunctionExiting()) + assert(!accessSet.hasInScopeAccess() && "no postdominating end_access"); + + // Initialize blockOutAccess for this block with the current access set. + accessSet.copyNoNestedConflictInto(blockOutAccess[BB]); +} + +// ----------------------------------------------------------------------------- +// MARK: Access Enforcement Optimization +// ----------------------------------------------------------------------------- + +/// Perform access folding. +/// +/// Data-flow analysis is now complete. Any begin_access that has seen a +/// conflict can be given the [no_nested_conflict] instruction attribute. +/// +/// Note: If we later support marking begin_unpaired_access +/// [no_nested_conflict], then we also need to remove any corresponding +/// end_unpaired_access. That can be done either by recording the +/// end_unpaired_access instructions during analysis and deleting them here in +/// the same order, or sorting them here by their begin_unpaired_access index. +static bool +foldNonNestedAccesses(AccessConflictAnalysis::AccessMap &accessMap) { + bool changed = false; + // Iteration over accessMap is nondeterministic. Setting the conflict flags + // can be done in any order. + for (auto &beginAccessAndInfo : accessMap) { + BeginAccessInst *beginAccess = beginAccessAndInfo.first; + AccessInfo &info = beginAccessAndInfo.second; + if (info.seenNestedConflict()) + continue; + + // Optimize this begin_access by setting [no_nested_conflict]. + beginAccess->setNoNestedConflict(true); + changed = true; + DEBUG(llvm::dbgs() << "Folding " << *beginAccess); + } + return changed; +} + +/// Eliminate accesses to uniquely identified local storage for which no +/// accesses can have nested conflicts. This is only valid if the function's +/// local storage cannot be potentially modified by unidentified access. +/// +/// This simply invalidates the AccessMap result rather than erasing individual +/// entries. +static bool +removeLocalNonNestedAccess(AccessConflictAnalysis::Result &&result, + const FunctionAccessedStorage &functionAccess) { + if (functionAccess.hasUnidentifiedAccess()) + return false; + + bool changed = false; + SmallVector deadAccesses; + for (auto &beginAccessAndInfo : result.accessMap) { + BeginAccessInst *beginAccess = beginAccessAndInfo.first; + AccessInfo &info = beginAccessAndInfo.second; + if (info.seenNestedConflict() || !info.isLocal()) + continue; + + // This particular access to local storage is marked + // [no_nested_conflict]. Now check FunctionAccessedStorage to determine if + // that is true for all access to the same storage. + if (functionAccess.hasNoNestedConflict(info)) + deadAccesses.push_back(beginAccess); + } + std::sort(deadAccesses.begin(), deadAccesses.end(), + [&result](BeginAccessInst *a, BeginAccessInst *b) { + return result.getAccessIndex(a) < result.getAccessIndex(b); + }); + for (BeginAccessInst *beginAccess : deadAccesses) { + DEBUG(llvm::dbgs() << "Removing dead access " << *beginAccess); + changed = true; + removeBeginAccess(beginAccess); + } + return changed; +} + +namespace { +struct AccessEnforcementOpts : public SILFunctionTransform { + void run() override { + SILFunction *F = getFunction(); + if (F->empty()) + return; + + PostOrderFunctionInfo *PO = getAnalysis()->get(F); + AccessedStorageAnalysis *ASA = getAnalysis(); + auto result = AccessConflictAnalysis(F, PO, ASA).analyze(); + + // Perform access folding by setting the [no_nested_conflict] flag on + // begin_access instructions. + if (!foldNonNestedAccesses(result.accessMap)) + return; + + // Recompute AccessStorageAnalysis, just for this function, to update the + // StorageAccessInfo::noNestedConflict status for each accessed storage. + invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions); + + // Use the updated AccessedStorageAnalysis to find any uniquely identified + // local storage that has no nested conflict on any of its accesses within + // this function. These can be removed entirely. + // + // Note that the storage address may be passed as an argument and there may + // be nested conflicts within that call, but none of the accesses within + // this function will overlap. + const FunctionAccessedStorage &functionAccess = ASA->getEffects(F); + if (removeLocalNonNestedAccess(std::move(result), functionAccess)) + invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions); + // `result` is now invalid. + } +}; +} // namespace + +SILTransform *swift::createAccessEnforcementOpts() { + return new AccessEnforcementOpts(); +} diff --git a/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp b/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp index 76bfe630381b8..ed4de1c8765b2 100644 --- a/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp +++ b/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp @@ -642,7 +642,7 @@ SILFunction *PromotedParamCloner::initCloned(SILFunction *Orig, OrigFTI->getGenericSignature()); paramTy = boxTy->getFieldType(Orig->getModule(), 0); } - auto promotedParam = SILParameterInfo(paramTy.getSwiftRValueType(), + auto promotedParam = SILParameterInfo(paramTy.getASTType(), ParameterConvention::Indirect_InoutAliasable); ClonedInterfaceArgTys.push_back(promotedParam); } else { @@ -871,7 +871,7 @@ specializePartialApply(PartialApplyInst *PartialApply, SILValue FunctionRef = Builder.createFunctionRef(PartialApply->getLoc(), ClonedFn); return Builder.createPartialApply( - PartialApply->getLoc(), FunctionRef, PartialApply->getSubstitutions(), + PartialApply->getLoc(), FunctionRef, PartialApply->getSubstitutionMap(), Args, PartialApply->getType().getAs()->getCalleeConvention()); } diff --git a/lib/SILOptimizer/Transforms/ArrayElementValuePropagation.cpp b/lib/SILOptimizer/Transforms/ArrayElementValuePropagation.cpp index be7f376ef302c..d00af30ae62d3 100644 --- a/lib/SILOptimizer/Transforms/ArrayElementValuePropagation.cpp +++ b/lib/SILOptimizer/Transforms/ArrayElementValuePropagation.cpp @@ -324,24 +324,20 @@ class ArrayElementPropagation : public SILFunctionTransform { assert(AppendContentsOf && "Must be AppendContentsOf call"); NominalTypeDecl *AppendSelfArray = AppendContentsOf.getSelf()->getType(). - getSwiftRValueType()->getAnyNominal(); + getASTType()->getAnyNominal(); // In case if it's not an Array, but e.g. an ContiguousArray if (AppendSelfArray != Ctx.getArrayDecl()) continue; SILType ArrayType = Repl.Array->getType(); - auto *NTD = ArrayType.getSwiftRValueType()->getAnyNominal(); - SubstitutionMap ArraySubMap = ArrayType.getSwiftRValueType() + auto *NTD = ArrayType.getASTType()->getAnyNominal(); + SubstitutionMap ArraySubMap = ArrayType.getASTType() ->getContextSubstitutionMap(M.getSwiftModule(), NTD); - GenericSignature *Sig = NTD->getGenericSignature(); - assert(Sig && "Array type must have generic signature"); - SmallVector Subs; - Sig->getSubstitutions(ArraySubMap, Subs); - AppendContentsOf.replaceByAppendingValues(M, AppendFn, ReserveFn, - Repl.ReplacementValues, Subs); + Repl.ReplacementValues, + ArraySubMap); } return true; } diff --git a/lib/SILOptimizer/Transforms/CMakeLists.txt b/lib/SILOptimizer/Transforms/CMakeLists.txt index 0f454d8b41ea0..5adf891d0ecd9 100644 --- a/lib/SILOptimizer/Transforms/CMakeLists.txt +++ b/lib/SILOptimizer/Transforms/CMakeLists.txt @@ -1,5 +1,6 @@ set(TRANSFORMS_SOURCES Transforms/ARCCodeMotion.cpp + Transforms/AccessEnforcementOpts.cpp Transforms/AllocBoxToStack.cpp Transforms/ArrayCountPropagation.cpp Transforms/ArrayElementValuePropagation.cpp @@ -23,7 +24,6 @@ set(TRANSFORMS_SOURCES Transforms/RedundantOverflowCheckRemoval.cpp Transforms/ReleaseDevirtualizer.cpp Transforms/RemovePin.cpp - Transforms/SILCleanup.cpp Transforms/SILCodeMotion.cpp Transforms/SILLowerAggregateInstrs.cpp Transforms/SILMem2Reg.cpp diff --git a/lib/SILOptimizer/Transforms/CSE.cpp b/lib/SILOptimizer/Transforms/CSE.cpp index 4f7c20695774e..398e565ea193c 100644 --- a/lib/SILOptimizer/Transforms/CSE.cpp +++ b/lib/SILOptimizer/Transforms/CSE.cpp @@ -741,7 +741,7 @@ bool CSE::processOpenExistentialRef(OpenExistentialRefInst *Inst, ValueBase *V, // Check if the result type depends on this specific opened existential. auto ResultDependsOnOldOpenedArchetype = - CandidateResult->getType().getSwiftRValueType().findIf( + CandidateResult->getType().getASTType().findIf( [&OldOpenedArchetype](Type t) -> bool { return (CanType(t) == OldOpenedArchetype); }); @@ -874,8 +874,8 @@ bool CSE::canHandle(SILInstruction *Inst) { // We can CSE function calls which do not read or write memory and don't // have any other side effects. - SideEffectAnalysis::FunctionEffects Effects; - SEA->getEffects(Effects, AI); + FunctionSideEffects Effects; + SEA->getCalleeEffects(Effects, AI); // Note that the function also may not contain any retains. And there are // functions which are read-none and have a retain, e.g. functions which @@ -1043,7 +1043,8 @@ static bool tryToCSEOpenExtCall(OpenExistentialAddrInst *From, "Invalid number of arguments"); // Don't handle any apply instructions that involve substitutions. - if (ToAI->getSubstitutions().size() != 1) return false; + if (ToAI->getSubstitutionMap().getReplacementTypes().size() != 1) + return false; // Prepare the Apply args. SmallVector Args; @@ -1052,7 +1053,7 @@ static bool tryToCSEOpenExtCall(OpenExistentialAddrInst *From, } ApplyInst *NAI = Builder.createApply(ToAI->getLoc(), ToWMI, - ToAI->getSubstitutions(), Args, + ToAI->getSubstitutionMap(), Args, ToAI->isNonThrowing()); FromAI->replaceAllUsesWith(NAI); FromAI->eraseFromParent(); diff --git a/lib/SILOptimizer/Transforms/ConditionForwarding.cpp b/lib/SILOptimizer/Transforms/ConditionForwarding.cpp index 601fa52b55331..ecb0800827795 100644 --- a/lib/SILOptimizer/Transforms/ConditionForwarding.cpp +++ b/lib/SILOptimizer/Transforms/ConditionForwarding.cpp @@ -160,7 +160,7 @@ bool ConditionForwarding::tryOptimize(SwitchEnumInst *SEI) { if (ArgUser == SEI) continue; - if (isDebugInst(ArgUser)) + if (ArgUser->isDebugInstruction()) continue; if (ArgUser->getParent()->getSinglePredecessorBlock() == SEI->getParent()) { @@ -227,7 +227,7 @@ bool ConditionForwarding::tryOptimize(SwitchEnumInst *SEI) { while (!Arg->use_empty()) { Operand *ArgUse = *Arg->use_begin(); SILInstruction *ArgUser = ArgUse->getUser(); - if (isDebugInst(ArgUser)) { + if (ArgUser->isDebugInstruction()) { // Don't care about debug instructions. Just remove them. ArgUser->eraseFromParent(); continue; diff --git a/lib/SILOptimizer/Transforms/CopyForwarding.cpp b/lib/SILOptimizer/Transforms/CopyForwarding.cpp index 1ef6e521d86fe..f962b1d573d4e 100644 --- a/lib/SILOptimizer/Transforms/CopyForwarding.cpp +++ b/lib/SILOptimizer/Transforms/CopyForwarding.cpp @@ -65,6 +65,7 @@ #include "swift/SILOptimizer/Analysis/AliasAnalysis.h" #include "swift/SILOptimizer/Analysis/DominanceAnalysis.h" #include "swift/SILOptimizer/Analysis/PostOrderAnalysis.h" +#include "swift/SILOptimizer/Analysis/RCIdentityAnalysis.h" #include "swift/SILOptimizer/PassManager/Passes.h" #include "swift/SILOptimizer/PassManager/Transforms.h" #include "swift/SILOptimizer/Utils/CFG.h" @@ -167,6 +168,19 @@ static SILArgumentConvention getAddressArgConvention(ApplyInst *Apply, return Apply->getArgumentConvention(FoundArgIdx.getValue()); } +/// If the given instruction is a store, return the stored value. +static SILValue getStoredValue(SILInstruction *I) { + switch (I->getKind()) { + case SILInstructionKind::StoreInst: + case SILInstructionKind::StoreBorrowInst: + case SILInstructionKind::StoreUnownedInst: + case SILInstructionKind::StoreWeakInst: + return I->getOperand(0); + default: + return SILValue(); + } +} + //===----------------------------------------------------------------------===// // Forward and backward copy propagation //===----------------------------------------------------------------------===// @@ -481,6 +495,7 @@ class CopyForwarding { // Per-function state. PostOrderAnalysis *PostOrder; DominanceAnalysis *DomAnalysis; + RCIdentityAnalysis *RCIAnalysis; bool DoGlobalHoisting; bool HasChanged; bool HasChangedCFG; @@ -495,10 +510,15 @@ class CopyForwarding { // beyond the value's immediate uses. bool IsSrcLoadedFrom; + // Does the address defined by CurrentDef have unrecognized uses of a + // nontrivial value stored at its address? + bool HasUnknownStoredValue; + bool HasForwardedToCopy; SmallPtrSet SrcUserInsts; SmallPtrSet SrcDebugValueInsts; SmallVector TakePoints; + SmallPtrSet StoredValueUserInsts; SmallVector DestroyPoints; SmallPtrSet DeadInBlocks; @@ -514,6 +534,11 @@ class CopyForwarding { if (isa(user)) CPF.IsSrcLoadedFrom = true; + if (SILValue storedValue = getStoredValue(user)) { + if (!CPF.markStoredValueUsers(storedValue)) + CPF.HasUnknownStoredValue = true; + } + // Bail on multiple uses in the same instruction to avoid complexity. return CPF.SrcUserInsts.insert(user).second; } @@ -534,10 +559,12 @@ class CopyForwarding { }; public: - CopyForwarding(PostOrderAnalysis *PO, DominanceAnalysis *DA) - : PostOrder(PO), DomAnalysis(DA), DoGlobalHoisting(false), - HasChanged(false), HasChangedCFG(false), IsSrcLoadedFrom(false), - HasForwardedToCopy(false), CurrentCopy(nullptr) {} + CopyForwarding(PostOrderAnalysis *PO, DominanceAnalysis *DA, + RCIdentityAnalysis *RCIAnalysis) + : PostOrder(PO), DomAnalysis(DA), RCIAnalysis(RCIAnalysis), + DoGlobalHoisting(false), HasChanged(false), HasChangedCFG(false), + IsSrcLoadedFrom(false), HasUnknownStoredValue(false), + HasForwardedToCopy(false), CurrentCopy(nullptr) {} void reset(SILFunction *F) { // Don't hoist destroy_addr globally in transparent functions. Avoid cloning @@ -552,13 +579,16 @@ class CopyForwarding { // We'll invalidate the analysis that are used by other passes at the end. DomAnalysis->invalidate(F, SILAnalysis::InvalidationKind::Everything); PostOrder->invalidate(F, SILAnalysis::InvalidationKind::Everything); + RCIAnalysis->invalidate(F, SILAnalysis::InvalidationKind::Everything); } CurrentDef = SILValue(); IsSrcLoadedFrom = false; + HasUnknownStoredValue = false; HasForwardedToCopy = false; SrcUserInsts.clear(); SrcDebugValueInsts.clear(); TakePoints.clear(); + StoredValueUserInsts.clear(); DestroyPoints.clear(); DeadInBlocks.clear(); CurrentCopy = nullptr; @@ -585,6 +615,8 @@ class CopyForwarding { typedef llvm::SmallSetVector UserVector; bool doesCopyDominateDestUsers(const UserVector &DirectDestUses); + + bool markStoredValueUsers(SILValue storedValue); }; class CopyDestUserVisitor : public AddressUserVisitor { @@ -805,6 +837,51 @@ bool CopyForwarding::doesCopyDominateDestUsers( return true; } +// Add all recognized users of storedValue to StoredValueUserInsts. Return true +// if all users were recgonized. +// +// To find all SSA users of storedValue, we first find the RC root, then search +// past any instructions that may propagate the reference. +bool CopyForwarding::markStoredValueUsers(SILValue storedValue) { + if (storedValue->getType().isTrivial(*storedValue->getModule())) + return true; + + // Find the RC root, peeking past things like struct_extract. + RCIdentityFunctionInfo *RCI = RCIAnalysis->get(storedValue->getFunction()); + SILValue root = RCI->getRCIdentityRoot(storedValue); + + SmallVector users; + RCI->getRCUsers(root, users); + + for (SILInstruction *user : users) { + // Recognize any uses that have no results as normal uses. They cannot + // transitively propagate a reference. + if (user->getResults().empty()) { + StoredValueUserInsts.insert(user); + continue; + } + // Recognize full applies as normal uses. They may transitively retain, but + // the caller cannot rely on that. + if (FullApplySite::isa(user)) { + StoredValueUserInsts.insert(user); + continue; + } + // A single-valued use is nontransitive if its result is trivial. + if (auto *SVI = dyn_cast(user)) { + if (SVI->getType().isTrivial(user->getModule())) { + StoredValueUserInsts.insert(user); + continue; + } + } + // Conservatively treat everything else as potentially transitively + // retaining the stored value. + DEBUG(llvm::dbgs() << " Cannot reduce lifetime. May retain " << storedValue + << " at: " << *user << "\n"); + return false; + } + return true; +} + /// Returns the associated dealloc_stack if \p ASI has a single dealloc_stack. /// Usually this is the case, but the optimizations may generate something like: /// %1 = alloc_stack @@ -814,15 +891,7 @@ bool CopyForwarding::doesCopyDominateDestUsers( /// dealloc_stack %1 /// } static DeallocStackInst *getSingleDealloc(AllocStackInst *ASI) { - DeallocStackInst *SingleDSI = nullptr; - for (Operand *Use : ASI->getUses()) { - if (auto *DSI = dyn_cast(Use->getUser())) { - if (SingleDSI) - return nullptr; - SingleDSI = DSI; - } - } - return SingleDSI; + return ASI->getSingleDeallocStack(); } /// Perform forward copy-propagation. Find a set of uses that the given copy can @@ -1112,10 +1181,10 @@ bool CopyForwarding::backwardPropagateCopy() { /// The copy will be eliminated if the original is not accessed between the /// point of copy and the original's destruction. /// -/// Def = // no aliases +/// CurrentDef = // no aliases /// ... /// Copy = copy_addr [init] Def -/// ... // no access to Def +/// ... // no access to CurrentDef /// destroy_addr Def /// /// Return true if a destroy was inserted, forwarded from a copy, or the @@ -1137,6 +1206,19 @@ bool CopyForwarding::hoistDestroy(SILInstruction *DestroyPoint, --SI; SILInstruction *Inst = &*SI; if (!SrcUserInsts.count(Inst)) { + if (StoredValueUserInsts.count(Inst)) { + // The current definition may take ownership of a value stored into its + // address. Its lifetime cannot end before the last use of that stored + // value. + // CurrentDef = ... + // Copy = copy_addr CurrentDef to ... + // store StoredValue to CurrentDef + // ... // no access to CurrentDef + // retain StoredValue + // destroy_addr CurrentDef + DEBUG(llvm::dbgs() << " Cannot hoist above stored value use:" << *Inst); + return false; + } if (!IsWorthHoisting && isa(Inst)) IsWorthHoisting = true; continue; @@ -1185,7 +1267,7 @@ void CopyForwarding::forwardCopiesOf(SILValue Def, SILFunction *F) { // TODO: Record all loads during collectUsers. Implement findRetainPoints to // peek though projections of the load, like unchecked_enum_data to find the // true extent of the lifetime including transitively referenced objects. - if (IsSrcLoadedFrom) + if (IsSrcLoadedFrom || HasUnknownStoredValue) return; bool HoistedDestroyFound = false; @@ -1420,7 +1502,8 @@ class CopyForwardingPass : public SILFunctionTransform auto *PO = getAnalysis(); auto *DA = getAnalysis(); - auto Forwarding = CopyForwarding(PO, DA); + auto *RCIA = getAnalysis(); + auto Forwarding = CopyForwarding(PO, DA, RCIA); for (SILValue Def : CopiedDefs) { #ifndef NDEBUG diff --git a/lib/SILOptimizer/Transforms/Devirtualizer.cpp b/lib/SILOptimizer/Transforms/Devirtualizer.cpp index 691acb8c63b4f..2ddd68aa4e936 100644 --- a/lib/SILOptimizer/Transforms/Devirtualizer.cpp +++ b/lib/SILOptimizer/Transforms/Devirtualizer.cpp @@ -52,7 +52,6 @@ class Devirtualizer : public SILFunctionTransform { bool Devirtualizer::devirtualizeAppliesInFunction(SILFunction &F, ClassHierarchyAnalysis *CHA) { bool Changed = false; - llvm::SmallVector DeadApplies; llvm::SmallVector NewApplies; OptRemark::Emitter ORE(DEBUG_TYPE, F.getModule()); @@ -76,20 +75,10 @@ bool Devirtualizer::devirtualizeAppliesInFunction(SILFunction &F, Changed = true; - auto *AI = Apply.getInstruction(); - if (!isa(AI)) - cast(AI)->replaceAllUsesWith(NewInstPair.first); - - DeadApplies.push_back(AI); + replaceDeadApply(Apply, NewInstPair.first); NewApplies.push_back(NewInstPair.second); } - // Remove all the now-dead applies. - while (!DeadApplies.empty()) { - auto *AI = DeadApplies.pop_back_val(); - recursivelyDeleteTriviallyDeadInstructions(AI, true); - } - // For each new apply, attempt to link in function bodies if we do // not already have them, then notify the pass manager of the new // functions. @@ -104,11 +93,9 @@ bool Devirtualizer::devirtualizeAppliesInFunction(SILFunction &F, assert(CalleeFn && "Expected devirtualized callee!"); // We need to ensure that we link after devirtualizing in order to pull in - // everything we reference from another module. This is especially important - // for transparent functions, because if transparent functions are not - // inlined for some reason, we need to generate code for them. - // Note that functions, which are only referenced from witness/vtables, are - // not linked upfront by the SILLinker. + // everything we reference from another module, which may expose optimization + // opportunities and is also needed for correctness if we reference functions + // with non-public linkage. See lib/SIL/Linker.cpp for details. if (!CalleeFn->isDefinition()) F.getModule().linkFunction(CalleeFn, SILModule::LinkingMode::LinkAll); diff --git a/lib/SILOptimizer/Transforms/FunctionSignatureOpts.cpp b/lib/SILOptimizer/Transforms/FunctionSignatureOpts.cpp index cef08230d63f3..11d85eb2c7161 100644 --- a/lib/SILOptimizer/Transforms/FunctionSignatureOpts.cpp +++ b/lib/SILOptimizer/Transforms/FunctionSignatureOpts.cpp @@ -30,22 +30,23 @@ //===----------------------------------------------------------------------===// #define DEBUG_TYPE "sil-function-signature-opt" +#include "FunctionSignatureOpts.h" +#include "swift/SIL/DebugUtils.h" +#include "swift/SIL/SILCloner.h" +#include "swift/SIL/SILFunction.h" +#include "swift/SIL/SILValue.h" #include "swift/SILOptimizer/Analysis/ARCAnalysis.h" #include "swift/SILOptimizer/Analysis/CallerAnalysis.h" #include "swift/SILOptimizer/Analysis/EpilogueARCAnalysis.h" #include "swift/SILOptimizer/Analysis/RCIdentityAnalysis.h" #include "swift/SILOptimizer/PassManager/Passes.h" #include "swift/SILOptimizer/PassManager/Transforms.h" -#include "swift/SILOptimizer/Utils/FunctionSignatureOptUtils.h" #include "swift/SILOptimizer/Utils/Local.h" #include "swift/SILOptimizer/Utils/SILInliner.h" #include "swift/SILOptimizer/Utils/SpecializationMangler.h" -#include "swift/SIL/DebugUtils.h" -#include "swift/SIL/SILFunction.h" -#include "swift/SIL/SILCloner.h" -#include "swift/SIL/SILValue.h" #include "llvm/ADT/Statistic.h" #include "llvm/Support/Debug.h" +#include "llvm/Support/CommandLine.h" using namespace swift; @@ -58,6 +59,86 @@ STATISTIC(NumSROAArguments, "Total SROA arguments optimized"); using SILParameterInfoList = llvm::SmallVector; using ArgumentIndexMap = llvm::SmallDenseMap; +//===----------------------------------------------------------------------===// +// Optimization Hueristic +//===----------------------------------------------------------------------===// + +/// Set to true to enable the support for partial specialization. +llvm::cl::opt + FSOEnableGenerics("sil-fso-enable-generics", llvm::cl::init(true), + llvm::cl::desc("Support function signature optimization " + "of generic functions")); + +static llvm::cl::opt FSODisableOwnedToGuaranteed( + "sil-fso-disable-owned-to-guaranteed", + llvm::cl::desc("Do not perform owned to guaranteed during FSO. Intended " + "only for testing purposes.")); + +static llvm::cl::opt FSODisableDeadArgument( + "sil-fso-disable-dead-argument", + llvm::cl::desc("Do not perform dead argument elimination during FSO. " + "Intended only for testing purposes")); + +static llvm::cl::opt FSODisableArgExplosion( + "sil-fso-disable-arg-explosion", + llvm::cl::desc("Do not perform argument explosion during FSO. Intended " + "only for testing purposes")); + +static bool isSpecializableRepresentation(SILFunctionTypeRepresentation Rep, + bool OptForPartialApply) { + switch (Rep) { + case SILFunctionTypeRepresentation::Method: + case SILFunctionTypeRepresentation::Closure: + case SILFunctionTypeRepresentation::Thin: + case SILFunctionTypeRepresentation::Thick: + case SILFunctionTypeRepresentation::CFunctionPointer: + return true; + case SILFunctionTypeRepresentation::WitnessMethod: + return OptForPartialApply; + case SILFunctionTypeRepresentation::ObjCMethod: + case SILFunctionTypeRepresentation::Block: + return false; + } + + llvm_unreachable("Unhandled SILFunctionTypeRepresentation in switch."); +} + +/// Returns true if F is a function which the pass know show to specialize +/// function signatures for. +static bool canSpecializeFunction(SILFunction *F, + const CallerAnalysis::FunctionInfo *FuncInfo, + bool OptForPartialApply) { + // Do not specialize the signature of SILFunctions that are external + // declarations since there is no body to optimize. + if (F->isExternalDeclaration()) + return false; + + // For now ignore functions with indirect results. + if (F->getConventions().hasIndirectSILResults()) + return false; + + // Do not specialize the signature of always inline functions. We + // will just inline them and specialize each one of the individual + // functions that these sorts of functions are inlined into. + // It is OK to specialize always inline functions if they are + // used by partial_apply instructions. + assert(!OptForPartialApply || FuncInfo); + if (F->getInlineStrategy() == Inline_t::AlwaysInline && + (!OptForPartialApply || !FuncInfo->getMinPartialAppliedArgs())) + return false; + + // For now ignore generic functions to keep things simple... + if (!FSOEnableGenerics && F->getLoweredFunctionType()->isPolymorphic()) + return false; + + // Make sure F has a linkage that we can optimize. + if (!isSpecializableRepresentation(F->getRepresentation(), + OptForPartialApply)) + return false; + + return true; +} + //===----------------------------------------------------------------------===// // Utilities //===----------------------------------------------------------------------===// @@ -87,264 +168,37 @@ static SILInstruction *findOnlyApply(SILFunction *F) { } //===----------------------------------------------------------------------===// -// Function Signature Transform +// Function Signature Transform Descriptor //===----------------------------------------------------------------------===// -struct FunctionSignatureTransformDescriptor { - /// The original function that we are analyzing/transforming. - SILFunction *OriginalFunction; - - /// The new optimize function that we will create. - NullablePtr OptimizedFunction; - - /// A map from a pre-transformed argument to a post-transformed argument. - ArgumentIndexMap &AIM; - - /// Set to true if we are going to modify self during our transformation. - /// - /// TODO: Rename to willModifySelfArgument. - bool shouldModifySelfArgument; - - /// Keep a "view" of precompiled information on arguments that we use - /// during our optimization. - MutableArrayRef ArgumentDescList; - - /// Keep a "view" of precompiled information on the direct results that we - /// will use during our optimization. - MutableArrayRef ResultDescList; -}; - -class FunctionSignatureTransform { - /// A struct that contains all data that we use during our - /// transformation. This is an initial step towards splitting this struct into - /// multiple "transforms" that can be tested independently of each other. - FunctionSignatureTransformDescriptor TransformDescriptor; - - /// The RC identity analysis we are using. - RCIdentityAnalysis *RCIA; - - /// Post order analysis we are using. - EpilogueARCAnalysis *EA; - - // The function signature mangler we are using. - Mangle::FunctionSignatureSpecializationMangler &Mangler; - - /// Return a function name based on ArgumentDescList and ResultDescList. - std::string createOptimizedSILFunctionName(); - - /// Return a function type based on ArgumentDescList and ResultDescList. - CanSILFunctionType createOptimizedSILFunctionType(); - -private: - /// ----------------------------------------------------------/// - /// Dead argument transformation. /// - /// ----------------------------------------------------------/// - /// Find any dead argument opportunities. - bool DeadArgumentAnalyzeParameters(); - /// Modify the current function so that later function signature analysis - /// are more effective. - void DeadArgumentTransformFunction(); - /// Remove the dead argument once the new function is created. - void DeadArgumentFinalizeOptimizedFunction(); - - /// ----------------------------------------------------------/// - /// Owned to guaranteed transformation. /// - /// ----------------------------------------------------------/// - bool OwnedToGuaranteedAnalyzeResults(); - bool OwnedToGuaranteedAnalyzeParameters(); - - /// Modify the current function so that later function signature analysis - /// are more effective. - void OwnedToGuaranteedTransformFunctionResults(); - void OwnedToGuaranteedTransformFunctionParameters(); - - /// Find any owned to guaranteed opportunities. - bool OwnedToGuaranteedAnalyze() { - bool Result = OwnedToGuaranteedAnalyzeResults(); - bool Params = OwnedToGuaranteedAnalyzeParameters(); - return Params || Result; - } - - /// Do the actual owned to guaranteed transformations. - void OwnedToGuaranteedTransform() { - OwnedToGuaranteedTransformFunctionResults(); - OwnedToGuaranteedTransformFunctionParameters(); - } - - /// Set up epilogue work for the thunk result based in the given argument. - void OwnedToGuaranteedAddResultRelease(ResultDescriptor &RD, - SILBuilder &Builder, - SILFunction *F); - - /// Set up epilogue work for the thunk argument based in the given argument. - void OwnedToGuaranteedAddArgumentRelease(ArgumentDescriptor &AD, - SILBuilder &Builder, - SILFunction *F); - - /// Add the release for converted arguments and result. - void OwnedToGuaranteedFinalizeThunkFunction(SILBuilder &B, SILFunction *F); - - /// ----------------------------------------------------------/// - /// Argument explosion transformation. /// - /// ----------------------------------------------------------/// - /// Find any argument explosion opportunities. - bool ArgumentExplosionAnalyzeParameters(); - /// Explode the argument in the optimized function and replace the uses of - /// the original argument. - void ArgumentExplosionFinalizeOptimizedFunction(); - - /// Setup the thunk arguments based on the given argument descriptor info. - /// Every transformation must defines this interface. Default implementation - /// simply passes it through. - void addThunkArgument(ArgumentDescriptor &AD, SILBuilder &Builder, - SILBasicBlock *BB, - llvm::SmallVectorImpl &NewArgs) { - // Dead argument. - if (AD.IsEntirelyDead) { - return; - } - - // Explode the argument. - if (AD.Explode) { - llvm::SmallVector LeafValues; - AD.ProjTree.createTreeFromValue(Builder, BB->getParent()->getLocation(), - BB->getArgument(AD.Index), LeafValues); - NewArgs.append(LeafValues.begin(), LeafValues.end()); - return; - } - - // All other arguments get pushed as what they are. - NewArgs.push_back(BB->getArgument(AD.Index)); - } - - /// Take ArgumentDescList and ResultDescList and create an optimized function - /// based on the current function we are analyzing. This also has the side effect - /// of turning the current function into a thunk. - void createFunctionSignatureOptimizedFunction(); - - /// Compute the optimized function type based on the given argument descriptor. - void computeOptimizedArgInterface(ArgumentDescriptor &A, SILParameterInfoList &O); - -public: - /// Constructor. - FunctionSignatureTransform( - SILFunction *F, RCIdentityAnalysis *RCIA, EpilogueARCAnalysis *EA, - Mangle::FunctionSignatureSpecializationMangler &Mangler, - ArgumentIndexMap &AIM, llvm::SmallVector &ADL, - llvm::SmallVector &RDL) - : TransformDescriptor{F, nullptr, AIM, false, ADL, RDL}, RCIA(RCIA), - EA(EA), Mangler(Mangler) {} - - /// Return the optimized function. - SILFunction *getOptimizedFunction() { - return TransformDescriptor.OptimizedFunction.getPtrOrNull(); +void FunctionSignatureTransformDescriptor::addThunkArgument( + ArgumentDescriptor &AD, SILBuilder &Builder, SILBasicBlock *BB, + llvm::SmallVectorImpl &NewArgs) { + // Dead argument. + if (AD.IsEntirelyDead) { + return; } - /// Run the optimization. - bool run(bool hasCaller) { - bool Changed = false; - SILFunction *F = TransformDescriptor.OriginalFunction; - - if (!hasCaller && canBeCalledIndirectly(F->getRepresentation())) { - DEBUG(llvm::dbgs() << " function has no caller -> abort\n"); - return false; - } - - // Run OwnedToGuaranteed optimization. - if (OwnedToGuaranteedAnalyze()) { - Changed = true; - DEBUG(llvm::dbgs() << " transform owned-to-guaranteed\n"); - OwnedToGuaranteedTransform(); - } - - // Run DeadArgument elimination transformation. We only specialize - // if this function has a caller inside the current module or we have - // already created a thunk. - if ((hasCaller || Changed) && DeadArgumentAnalyzeParameters()) { - Changed = true; - DEBUG(llvm::dbgs() << " remove dead arguments\n"); - DeadArgumentTransformFunction(); - } - - // Run ArgumentExplosion transformation. We only specialize - // if this function has a caller inside the current module or we have - // already created a thunk. - // - // NOTE: we run argument explosion last because we've already initialized - // the ArgumentDescList to have unexploded number of arguments. Exploding - // it without changing the argument count is not going to help with - // owned-to-guaranteed transformation. - // - // In order to not miss any opportunity, we send the optimized function - // to the passmanager to optimize any opportunities exposed by argument - // explosion. - if ((hasCaller || Changed) && ArgumentExplosionAnalyzeParameters()) { - Changed = true; - } - - // Check if generic signature of the function could be changed by - // removed some unused generic arguments. - if (F->getLoweredFunctionType()->isPolymorphic() && - createOptimizedSILFunctionType() != F->getLoweredFunctionType()) { - Changed = true; - } - - // Create the specialized function and invalidate the old function. - if (Changed) { - createFunctionSignatureOptimizedFunction(); - } - return Changed; + // Explode the argument. + if (AD.Explode) { + llvm::SmallVector LeafValues; + AD.ProjTree.createTreeFromValue(Builder, BB->getParent()->getLocation(), + BB->getArgument(AD.Index), LeafValues); + NewArgs.append(LeafValues.begin(), LeafValues.end()); + return; } - /// Run dead argument elimination of partially applied functions. - /// After this optimization CapturePropagation can replace the partial_apply - /// by a direct reference to the specialized function. - bool removeDeadArgs(int minPartialAppliedArgs) { - if (minPartialAppliedArgs < 1) - return false; - - if (!DeadArgumentAnalyzeParameters()) - return false; - - SILFunction *F = TransformDescriptor.OriginalFunction; - auto ArgumentDescList = TransformDescriptor.ArgumentDescList; - - // Check if at least the minimum number of partially applied arguments - // are dead. Otherwise no partial_apply can be removed anyway. - unsigned Size = ArgumentDescList.size(); - for (unsigned Idx : range(Size)) { - if (Idx < Size - minPartialAppliedArgs) { - // Don't remove arguments other than the partial applied ones, even if - // they are dead. - ArgumentDescList[Idx].IsEntirelyDead = false; - continue; - } - - // Is the partially applied argument dead? - if (!ArgumentDescList[Idx].IsEntirelyDead) - return false; - - // Currently we require that all dead parameters have trivial types. The - // reason is that it's very hard to find places where we can release those - // parameters (as a replacement for the removed partial_apply). - // - // TODO: Maybe we can skip this restriction when we have semantic ARC. - if (ArgumentDescList[Idx].Arg->getType().isTrivial(F->getModule())) - continue; - return false; - } + // All other arguments get pushed as what they are. + NewArgs.push_back(BB->getArgument(AD.Index)); +} - DEBUG(llvm::dbgs() << " remove dead arguments for partial_apply\n"); - DeadArgumentTransformFunction(); - createFunctionSignatureOptimizedFunction(); - return true; - } -}; +std::string +FunctionSignatureTransformDescriptor::createOptimizedSILFunctionName() { + SILFunction *F = OriginalFunction; -std::string FunctionSignatureTransform::createOptimizedSILFunctionName() { - SILFunction *F = TransformDescriptor.OriginalFunction; - auto ArgumentDescList = TransformDescriptor.ArgumentDescList; - auto ResultDescList = TransformDescriptor.ResultDescList; + auto P = Demangle::SpecializationPass::FunctionSignatureOpts; + Mangle::FunctionSignatureSpecializationMangler Mangler(P, F->isSerialized(), + F); // Handle arguments' changes. for (unsigned i : indices(ArgumentDescList)) { @@ -353,7 +207,7 @@ std::string FunctionSignatureTransform::createOptimizedSILFunctionName() { Mangler.setArgumentDead(i); // No point setting other attribute if argument is dead. continue; - } + } // If we have an @owned argument and found a callee release for it, // convert the argument to guaranteed. @@ -381,84 +235,8 @@ std::string FunctionSignatureTransform::createOptimizedSILFunctionName() { MangledName = Mangler.mangle(UniqueID); ++UniqueID; } while (M.hasFunction(MangledName)); - - return MangledName; -} - -/// Compute what the function interface will look like based on the -/// optimization we are doing on the given argument descriptor. Default -/// implementation simply passes it through. -void -FunctionSignatureTransform:: -computeOptimizedArgInterface(ArgumentDescriptor &AD, SILParameterInfoList &Out) { - // If this argument is live, but we cannot optimize it. - if (!AD.canOptimizeLiveArg()) { - if (AD.PInfo.hasValue()) - Out.push_back(AD.PInfo.getValue()); - return; - } - - // If we have a dead argument, bail. - if (AD.IsEntirelyDead) { - ++NumDeadArgsEliminated; - return; - } - - // Explode the argument or not ? - if (AD.Explode) { - ++NumSROAArguments; - llvm::SmallVector LeafNodes; - AD.ProjTree.getLeafNodes(LeafNodes); - for (auto Node : LeafNodes) { - SILType Ty = Node->getType(); - DEBUG(llvm::dbgs() << " " << Ty << "\n"); - // If Ty is trivial, just pass it directly. - if (Ty.isTrivial(AD.Arg->getModule())) { - SILParameterInfo NewInfo(Ty.getSwiftRValueType(), - ParameterConvention::Direct_Unowned); - Out.push_back(NewInfo); - continue; - } - - // Ty is not trivial, pass it through as the original calling convention. - auto ParameterConvention = AD.PInfo.getValue().getConvention(); - if (AD.OwnedToGuaranteed) { - if (ParameterConvention == ParameterConvention::Direct_Owned) - ParameterConvention = ParameterConvention::Direct_Guaranteed; - else if (ParameterConvention == ParameterConvention::Indirect_In) - ParameterConvention = ParameterConvention::Indirect_In_Guaranteed; - else { - llvm_unreachable("Unknown parameter convention transformation"); - } - } - SILParameterInfo NewInfo(Ty.getSwiftRValueType(), ParameterConvention); - Out.push_back(NewInfo); - } - return; - } - - // If we cannot explode this value, handle callee release and return. - // If we found releases in the callee in the last BB on an @owned - // parameter, change the parameter to @guaranteed and continue... - if (AD.OwnedToGuaranteed) { - ++NumOwnedConvertedToGuaranteed; - auto ParameterConvention = AD.PInfo.getValue().getConvention(); - if (ParameterConvention == ParameterConvention::Direct_Owned) - ParameterConvention = ParameterConvention::Direct_Guaranteed; - else if (ParameterConvention == ParameterConvention::Indirect_In) - ParameterConvention = ParameterConvention::Indirect_In_Guaranteed; - else { - llvm_unreachable("Unknown parameter convention transformation"); - } - SILParameterInfo NewInfo(AD.PInfo.getValue().getType(), - ParameterConvention); - Out.push_back(NewInfo); - return; - } - - // Otherwise just propagate through the parameter info. - Out.push_back(AD.PInfo.getValue()); + return MangledName; } /// Collect all archetypes used by a function. @@ -496,33 +274,33 @@ static bool usesGenerics(SILFunction *F, if (&BB != &*F->begin()) { // Scan types of all BB arguments. Ignore the entry BB, because // it is handled in a special way. - Arg->getType().getSwiftRValueType().visit(FindArchetypesAndGenericTypes); + Arg->getType().getASTType().visit(FindArchetypesAndGenericTypes); if (UsesGenerics) return UsesGenerics; } } // Scan types of all operands. for (auto &Op : I.getAllOperands()) { - Op.get()->getType().getSwiftRValueType().visit(FindArchetypesAndGenericTypes); + Op.get()->getType().getASTType().visit(FindArchetypesAndGenericTypes); } // Scan all substitutions of apply instructions. if (auto AI = ApplySite::isa(&I)) { - auto Subs = AI.getSubstitutions(); - for (auto Sub : Subs) { - Sub.getReplacement().visit(FindArchetypesAndGenericTypes); + auto Subs = AI.getSubstitutionMap(); + for (auto Replacement : Subs.getReplacementTypes()) { + Replacement.visit(FindArchetypesAndGenericTypes); } } // Scan all substitutions of builtin instructions. if (auto *BI = dyn_cast(&I)) { auto Subs = BI->getSubstitutions(); - for (auto Sub : Subs) { - Sub.getReplacement().visit(FindArchetypesAndGenericTypes); + for (auto Ty : Subs.getReplacementTypes()) { + Ty.visit(FindArchetypesAndGenericTypes); } } // Scan the result type of the instruction. for (auto V : I.getResults()) { - V->getType().getSwiftRValueType().visit(FindArchetypesAndGenericTypes); + V->getType().getASTType().visit(FindArchetypesAndGenericTypes); } if (UsesGenerics) @@ -534,43 +312,42 @@ static bool usesGenerics(SILFunction *F, // Map the parameter, result and error types out of context to get the interface // type. -static void -mapInterfaceTypes(SILFunction *F, - MutableArrayRef InterfaceParams, - MutableArrayRef InterfaceResults, - Optional &InterfaceErrorResult) { +static void mapInterfaceTypes(SILFunction *F, + MutableArrayRef InterfaceParams, + MutableArrayRef InterfaceResults, + Optional &InterfaceErrorResult) { for (auto &Param : InterfaceParams) { if (!Param.getType()->hasArchetype()) continue; Param = SILParameterInfo( - Param.getType()->mapTypeOutOfContext()->getCanonicalType(), - Param.getConvention()); + Param.getType()->mapTypeOutOfContext()->getCanonicalType(), + Param.getConvention()); } for (auto &Result : InterfaceResults) { if (!Result.getType()->hasArchetype()) continue; auto InterfaceResult = Result.getWithType( - Result.getType()->mapTypeOutOfContext()->getCanonicalType()); + Result.getType()->mapTypeOutOfContext()->getCanonicalType()); Result = InterfaceResult; } if (InterfaceErrorResult.hasValue()) { if (InterfaceErrorResult.getValue().getType()->hasArchetype()) { - InterfaceErrorResult = SILResultInfo( - InterfaceErrorResult.getValue().getType()->mapTypeOutOfContext() - ->getCanonicalType(), - InterfaceErrorResult.getValue().getConvention()); + InterfaceErrorResult = + SILResultInfo(InterfaceErrorResult.getValue() + .getType() + ->mapTypeOutOfContext() + ->getCanonicalType(), + InterfaceErrorResult.getValue().getConvention()); } } } -CanSILFunctionType FunctionSignatureTransform::createOptimizedSILFunctionType() { - SILFunction *F = TransformDescriptor.OriginalFunction; - auto ArgumentDescList = TransformDescriptor.ArgumentDescList; - auto ResultDescList = TransformDescriptor.ResultDescList; - +CanSILFunctionType +FunctionSignatureTransformDescriptor::createOptimizedSILFunctionType() { + SILFunction *F = OriginalFunction; CanSILFunctionType FTy = F->getLoweredFunctionType(); auto ExpectedFTy = F->getLoweredType().castTo(); auto HasGenericSignature = FTy->getGenericSignature() != nullptr; @@ -643,7 +420,7 @@ CanSILFunctionType FunctionSignatureTransform::createOptimizedSILFunctionType() // Don't use a method representation if we modified self. auto ExtInfo = FTy->getExtInfo(); auto witnessMethodConformance = FTy->getWitnessMethodConformanceOrNone(); - if (TransformDescriptor.shouldModifySelfArgument) { + if (shouldModifySelfArgument) { ExtInfo = ExtInfo.withRepresentation(SILFunctionTypeRepresentation::Thin); witnessMethodConformance = None; } @@ -666,16 +443,95 @@ CanSILFunctionType FunctionSignatureTransform::createOptimizedSILFunctionType() F->getModule().getASTContext(), witnessMethodConformance); } +/// Compute what the function interface will look like based on the +/// optimization we are doing on the given argument descriptor. Default +/// implementation simply passes it through. +void FunctionSignatureTransformDescriptor::computeOptimizedArgInterface( + ArgumentDescriptor &AD, SmallVectorImpl &Out) { + // If this argument is live, but we cannot optimize it. + if (!AD.canOptimizeLiveArg()) { + if (AD.PInfo.hasValue()) + Out.push_back(AD.PInfo.getValue()); + return; + } + + // If we have a dead argument, bail. + if (AD.IsEntirelyDead) { + ++NumDeadArgsEliminated; + return; + } + + // Explode the argument or not ? + if (AD.Explode) { + ++NumSROAArguments; + llvm::SmallVector LeafNodes; + AD.ProjTree.getLiveLeafNodes(LeafNodes); + for (auto Node : LeafNodes) { + SILType Ty = Node->getType(); + DEBUG(llvm::dbgs() << " " << Ty << "\n"); + // If Ty is trivial, just pass it directly. + if (Ty.isTrivial(AD.Arg->getModule())) { + SILParameterInfo NewInfo(Ty.getASTType(), + ParameterConvention::Direct_Unowned); + Out.push_back(NewInfo); + continue; + } + + // Ty is not trivial, pass it through as the original calling convention. + auto ParameterConvention = AD.PInfo.getValue().getConvention(); + if (AD.OwnedToGuaranteed) { + if (ParameterConvention == ParameterConvention::Direct_Owned) + ParameterConvention = ParameterConvention::Direct_Guaranteed; + else if (ParameterConvention == ParameterConvention::Indirect_In) + ParameterConvention = ParameterConvention::Indirect_In_Guaranteed; + else { + llvm_unreachable("Unknown parameter convention transformation"); + } + } + SILParameterInfo NewInfo(Ty.getASTType(), ParameterConvention); + Out.push_back(NewInfo); + } + return; + } + + // If we cannot explode this value, handle callee release and return. + // If we found releases in the callee in the last BB on an @owned + // parameter, change the parameter to @guaranteed and continue... + if (AD.OwnedToGuaranteed) { + ++NumOwnedConvertedToGuaranteed; + auto ParameterConvention = AD.PInfo.getValue().getConvention(); + if (ParameterConvention == ParameterConvention::Direct_Owned) + ParameterConvention = ParameterConvention::Direct_Guaranteed; + else if (ParameterConvention == ParameterConvention::Indirect_In) + ParameterConvention = ParameterConvention::Indirect_In_Guaranteed; + else { + llvm_unreachable("Unknown parameter convention transformation"); + } + + SILParameterInfo NewInfo(AD.PInfo.getValue().getType(), + ParameterConvention); + Out.push_back(NewInfo); + return; + } + + // Otherwise just propagate through the parameter info. + Out.push_back(AD.PInfo.getValue()); +} + +//===----------------------------------------------------------------------===// +// Function Signature Transform +//===----------------------------------------------------------------------===// + void FunctionSignatureTransform::createFunctionSignatureOptimizedFunction() { // Create the optimized function! SILFunction *F = TransformDescriptor.OriginalFunction; SILModule &M = F->getModule(); - std::string Name = createOptimizedSILFunctionName(); + std::string Name = TransformDescriptor.createOptimizedSILFunctionName(); SILLinkage linkage = getSpecializedLinkage(F, F->getLinkage()); DEBUG(llvm::dbgs() << " -> create specialized function " << Name << "\n"); - auto NewFTy = createOptimizedSILFunctionType(); + auto NewFTy = TransformDescriptor.createOptimizedSILFunctionType(); GenericEnvironment *NewFGenericEnv; if (NewFTy->getGenericSignature()) { NewFGenericEnv = F->getGenericEnvironment(); @@ -704,8 +560,8 @@ void FunctionSignatureTransform::createFunctionSignatureOptimizedFunction() { } // Do the last bit of work to the newly created optimized function. - ArgumentExplosionFinalizeOptimizedFunction(); DeadArgumentFinalizeOptimizedFunction(); + ArgumentExplosionFinalizeOptimizedFunction(); // Update the ownership kinds of function entry BB arguments. for (auto Arg : NewF->begin()->getFunctionArguments()) { @@ -736,7 +592,8 @@ void FunctionSignatureTransform::createFunctionSignatureOptimizedFunction() { // Create the args for the thunk's apply, ignoring any dead arguments. llvm::SmallVector ThunkArgs; for (auto &ArgDesc : TransformDescriptor.ArgumentDescList) { - addThunkArgument(ArgDesc, Builder, ThunkBody, ThunkArgs); + TransformDescriptor.addThunkArgument(ArgDesc, Builder, ThunkBody, + ThunkArgs); } SILValue ReturnValue; @@ -744,12 +601,12 @@ void FunctionSignatureTransform::createFunctionSignatureOptimizedFunction() { SILType ResultType = NewF->getConventions().getSILResultType(); auto GenCalleeType = NewF->getLoweredFunctionType(); auto SubstCalleeSILType = LoweredType; - ArrayRef Subs; + SubstitutionMap Subs; // Handle generic functions. if (GenCalleeType->isPolymorphic()) { // Produce a substitutions list and a set of substituted SIL types // required for creating a new SIL function. - Subs = F->getForwardingSubstitutions(); + Subs = F->getForwardingSubstitutionMap(); auto SubstCalleeType = GenCalleeType->substGenericArgs(M, Subs); SubstCalleeSILType = SILType::getPrimitiveObjectType(SubstCalleeType); @@ -790,11 +647,116 @@ void FunctionSignatureTransform::createFunctionSignatureOptimizedFunction() { assert(F->getDebugScope()->Parent != NewF->getDebugScope()->Parent); } +// Run the optimization. +bool FunctionSignatureTransform::run(bool hasCaller) { + bool Changed = false; + SILFunction *F = TransformDescriptor.OriginalFunction; + + if (!hasCaller && canBeCalledIndirectly(F->getRepresentation())) { + DEBUG(llvm::dbgs() << " function has no caller -> abort\n"); + return false; + } + + // Run OwnedToGuaranteed optimization. + if (OwnedToGuaranteedAnalyze()) { + Changed = true; + DEBUG(llvm::dbgs() << " transform owned-to-guaranteed\n"); + OwnedToGuaranteedTransform(); + } + + // Run DeadArgument elimination transformation. We only specialize + // if this function has a caller inside the current module or we have + // already created a thunk. + if ((hasCaller || Changed) && DeadArgumentAnalyzeParameters()) { + Changed = true; + DEBUG(llvm::dbgs() << " remove dead arguments\n"); + DeadArgumentTransformFunction(); + } + + // Run ArgumentExplosion transformation. We only specialize + // if this function has a caller inside the current module or we have + // already created a thunk. + // + // NOTE: we run argument explosion last because we've already initialized + // the ArgumentDescList to have unexploded number of arguments. Exploding + // it without changing the argument count is not going to help with + // owned-to-guaranteed transformation. + // + // In order to not miss any opportunity, we send the optimized function + // to the passmanager to optimize any opportunities exposed by argument + // explosion. + if ((hasCaller || Changed) && ArgumentExplosionAnalyzeParameters()) { + Changed = true; + } + + // Check if generic signature of the function could be changed by + // removed some unused generic arguments. + if (F->getLoweredFunctionType()->isPolymorphic() && + TransformDescriptor.createOptimizedSILFunctionType() != + F->getLoweredFunctionType()) { + Changed = true; + } + + // Create the specialized function and invalidate the old function. + if (Changed) { + createFunctionSignatureOptimizedFunction(); + } + return Changed; +} + +// Run dead argument elimination of partially applied functions. +// +// After this optimization CapturePropagation can replace the partial_apply by a +// direct reference to the specialized function. +bool FunctionSignatureTransform::removeDeadArgs(int minPartialAppliedArgs) { + if (minPartialAppliedArgs < 1) + return false; + + if (!DeadArgumentAnalyzeParameters()) + return false; + + SILFunction *F = TransformDescriptor.OriginalFunction; + auto ArgumentDescList = TransformDescriptor.ArgumentDescList; + + // Check if at least the minimum number of partially applied arguments + // are dead. Otherwise no partial_apply can be removed anyway. + unsigned Size = ArgumentDescList.size(); + for (unsigned Idx : range(Size)) { + if (Idx < Size - minPartialAppliedArgs) { + // Don't remove arguments other than the partial applied ones, even if + // they are dead. + ArgumentDescList[Idx].IsEntirelyDead = false; + continue; + } + + // Is the partially applied argument dead? + if (!ArgumentDescList[Idx].IsEntirelyDead) + return false; + + // Currently we require that all dead parameters have trivial types. The + // reason is that it's very hard to find places where we can release those + // parameters (as a replacement for the removed partial_apply). + // + // TODO: Maybe we can skip this restriction when we have semantic ARC. + if (ArgumentDescList[Idx].Arg->getType().isTrivial(F->getModule())) + continue; + return false; + } + + DEBUG(llvm::dbgs() << " remove dead arguments for partial_apply\n"); + DeadArgumentTransformFunction(); + createFunctionSignatureOptimizedFunction(); + return true; +} + //===----------------------------------------------------------------------===// // Dead Argument Elimination //===----------------------------------------------------------------------===// bool FunctionSignatureTransform::DeadArgumentAnalyzeParameters() { + if (FSODisableDeadArgument) + return false; + // Did we decide we should optimize any parameter? SILFunction *F = TransformDescriptor.OriginalFunction; bool SignatureOptimize = false; @@ -802,7 +764,7 @@ bool FunctionSignatureTransform::DeadArgumentAnalyzeParameters() { auto OrigShouldModifySelfArgument = TransformDescriptor.shouldModifySelfArgument; // Analyze the argument information. - for (unsigned i = 0, e = Args.size(); i != e; ++i) { + for (unsigned i : indices(Args)) { ArgumentDescriptor &A = TransformDescriptor.ArgumentDescList[i]; if (!A.PInfo.hasValue()) { // It is not an argument. It could be an indirect result. @@ -814,7 +776,7 @@ bool FunctionSignatureTransform::DeadArgumentAnalyzeParameters() { } // Check whether argument is dead. - if (!hasNonTrivialNonDebugUse(Args[i])) { + if (!hasNonTrivialNonDebugTransitiveUsers(Args[i])) { A.IsEntirelyDead = true; SignatureOptimize = true; if (Args[i]->isSelf()) @@ -829,7 +791,7 @@ bool FunctionSignatureTransform::DeadArgumentAnalyzeParameters() { bool HasNonTypeDeadArguments = false; for (auto &AD : TransformDescriptor.ArgumentDescList) { if (AD.IsEntirelyDead && - !isa(AD.Arg->getType().getSwiftRValueType())) { + !isa(AD.Arg->getType().getASTType())) { HasNonTypeDeadArguments = true; break; } @@ -864,10 +826,10 @@ void FunctionSignatureTransform::DeadArgumentTransformFunction() { void FunctionSignatureTransform::DeadArgumentFinalizeOptimizedFunction() { auto *BB = &*TransformDescriptor.OptimizedFunction.get()->begin(); // Remove any dead argument starting from the last argument to the first. - for (const ArgumentDescriptor &AD : - reverse(TransformDescriptor.ArgumentDescList)) { + for (ArgumentDescriptor &AD : reverse(TransformDescriptor.ArgumentDescList)) { if (!AD.IsEntirelyDead) continue; + AD.WasErased = true; BB->eraseArgument(AD.Arg->getIndex()); } } @@ -895,7 +857,7 @@ bool FunctionSignatureTransform::OwnedToGuaranteedAnalyzeParameters() { bool SignatureOptimize = false; // Analyze the argument information. - for (unsigned i = 0, e = Args.size(); i != e; ++i) { + for (unsigned i : indices(Args)) { ArgumentDescriptor &A = TransformDescriptor.ArgumentDescList[i]; if (!A.canOptimizeLiveArg()) { continue; @@ -1076,11 +1038,29 @@ OwnedToGuaranteedAddResultRelease(ResultDescriptor &RD, SILBuilder &Builder, } } +bool FunctionSignatureTransform::OwnedToGuaranteedAnalyze() { + if (FSODisableOwnedToGuaranteed) + return false; + + bool Result = OwnedToGuaranteedAnalyzeResults(); + bool Params = OwnedToGuaranteedAnalyzeParameters(); + return Params || Result; +} + +void FunctionSignatureTransform::OwnedToGuaranteedTransform() { + OwnedToGuaranteedTransformFunctionResults(); + OwnedToGuaranteedTransformFunctionParameters(); +} + //===----------------------------------------------------------------------===// // Argument Explosion Transformation //===----------------------------------------------------------------------===// bool FunctionSignatureTransform::ArgumentExplosionAnalyzeParameters() { + // If we are not supposed to perform argument explosion, bail. + if (FSODisableArgExplosion) + return false; + SILFunction *F = TransformDescriptor.OriginalFunction; // Did we decide we should optimize any parameter? bool SignatureOptimize = false; @@ -1089,15 +1069,21 @@ bool FunctionSignatureTransform::ArgumentExplosionAnalyzeParameters() { RCIA->get(F), F, {SILArgumentConvention::Direct_Owned}); // Analyze the argument information. - for (unsigned i = 0, e = Args.size(); i != e; ++i) { + for (unsigned i : indices(Args)) { ArgumentDescriptor &A = TransformDescriptor.ArgumentDescList[i]; + // If the argument is dead, there is no point in trying to explode it. The + // dead argument pass will get it. + if (A.IsEntirelyDead) { + continue; + } + // Do not optimize argument. if (!A.canOptimizeLiveArg()) { continue; } // Explosion of generic parameters is not supported yet. - if (A.Arg->getType().getSwiftRValueType()->hasArchetype()) + if (A.Arg->getType().hasArchetype()) continue; A.ProjTree.computeUsesAndLiveness(A.Arg); @@ -1120,13 +1106,22 @@ void FunctionSignatureTransform::ArgumentExplosionFinalizeOptimizedFunction() { Builder.setCurrentDebugScope(BB->getParent()->getDebugScope()); unsigned TotalArgIndex = 0; for (ArgumentDescriptor &AD : TransformDescriptor.ArgumentDescList) { + // If this argument descriptor was dead and we removed it, just skip it. Do + // not increment the argument index. + if (AD.WasErased) { + continue; + } + // Simply continue if do not explode. if (!AD.Explode) { TransformDescriptor.AIM[TotalArgIndex] = AD.Index; - TotalArgIndex ++; + ++TotalArgIndex; continue; } + assert(!AD.IsEntirelyDead && + "Should never see completely dead values here"); + // OK, we need to explode this argument. unsigned ArgOffset = ++TotalArgIndex; unsigned OldArgIndex = ArgOffset - 1; @@ -1135,15 +1130,16 @@ void FunctionSignatureTransform::ArgumentExplosionFinalizeOptimizedFunction() { // We do this in the same order as leaf types since ProjTree expects that the // order of leaf values matches the order of leaf types. llvm::SmallVector LeafNodes; - AD.ProjTree.getLeafNodes(LeafNodes); + AD.ProjTree.getLiveLeafNodes(LeafNodes); for (auto *Node : LeafNodes) { auto OwnershipKind = *AD.getTransformedOwnershipKind(Node->getType()); - LeafValues.push_back(BB->insertFunctionArgument( - ArgOffset++, Node->getType(), OwnershipKind, - BB->getArgument(OldArgIndex)->getDecl())); + LeafValues.push_back( + BB->insertFunctionArgument(ArgOffset, Node->getType(), OwnershipKind, + BB->getArgument(OldArgIndex)->getDecl())); TransformDescriptor.AIM[TotalArgIndex - 1] = AD.Index; - TotalArgIndex ++; + ++ArgOffset; + ++TotalArgIndex; } // Then go through the projection tree constructing aggregates and replacing @@ -1165,14 +1161,16 @@ void FunctionSignatureTransform::ArgumentExplosionFinalizeOptimizedFunction() { // Now erase the old argument since it does not have any uses. We also // decrement ArgOffset since we have one less argument now. BB->eraseArgument(OldArgIndex); - TotalArgIndex --; + --TotalArgIndex; } } //===----------------------------------------------------------------------===// // Top Level Entry Point //===----------------------------------------------------------------------===// + namespace { + class FunctionSignatureOpts : public SILFunctionTransform { /// If true, perform a special kind of dead argument elimination to enable @@ -1220,6 +1218,7 @@ class FunctionSignatureOpts : public SILFunctionTransform { return; } + // Ok, we think we can perform optimization. Now perform a quick check auto *RCIA = getAnalysis(); auto *EA = PM->getAnalysis(); @@ -1234,7 +1233,7 @@ class FunctionSignatureOpts : public SILFunctionTransform { /// index. llvm::SmallDenseMap AIM; int asize = F->begin()->getArguments().size(); - for (auto i = 0; i < asize; ++i) { + for (unsigned i : range(asize)) { AIM[i] = i; } @@ -1242,7 +1241,7 @@ class FunctionSignatureOpts : public SILFunctionTransform { llvm::SmallVector ArgumentDescList; llvm::SmallVector ResultDescList; auto Args = F->begin()->getFunctionArguments(); - for (unsigned i = 0, e = Args.size(); i != e; ++i) { + for (unsigned i : indices(Args)) { ArgumentDescList.emplace_back(Args[i]); } for (SILResultInfo IR : F->getLoweredFunctionType()->getResults()) { @@ -1259,25 +1258,28 @@ class FunctionSignatureOpts : public SILFunctionTransform { } else { Changed = FST.run(FuncInfo.hasCaller()); } - if (Changed) { - ++ NumFunctionSignaturesOptimized; - // The old function must be a thunk now. - assert(F->isThunk() && "Old function should have been turned into a thunk"); - - invalidateAnalysis(SILAnalysis::InvalidationKind::Everything); - - // Make sure the PM knows about this function. This will also help us - // with self-recursion. - notifyAddFunction(FST.getOptimizedFunction(), F); - - if (!OptForPartialApply) { - // We have to restart the pipeline for this thunk in order to run the - // inliner (and other opts) again. This is important if the new - // specialized function (which is called from this thunk) is - // function-signature-optimized again and also becomes an - // always-inline-thunk. - restartPassPipeline(); - } + + if (!Changed) { + return; + } + + ++NumFunctionSignaturesOptimized; + // The old function must be a thunk now. + assert(F->isThunk() && "Old function should have been turned into a thunk"); + + invalidateAnalysis(SILAnalysis::InvalidationKind::Everything); + + // Make sure the PM knows about this function. This will also help us + // with self-recursion. + notifyAddFunction(FST.getOptimizedFunction(), F); + + if (!OptForPartialApply) { + // We have to restart the pipeline for this thunk in order to run the + // inliner (and other opts) again. This is important if the new + // specialized function (which is called from this thunk) is + // function-signature-optimized again and also becomes an + // always-inline-thunk. + restartPassPipeline(); } } diff --git a/lib/SILOptimizer/Transforms/FunctionSignatureOpts.h b/lib/SILOptimizer/Transforms/FunctionSignatureOpts.h new file mode 100644 index 0000000000000..2ec9a012e57a9 --- /dev/null +++ b/lib/SILOptimizer/Transforms/FunctionSignatureOpts.h @@ -0,0 +1,343 @@ +//===--- FunctionSignatureOpts.h --------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SILOPTIMIZER_TRANSFORMS_FUNCTIONSIGNATUREOPTS_H +#define SWIFT_SILOPTIMIZER_TRANSFORMS_FUNCTIONSIGNATUREOPTS_H + +#include "swift/Basic/LLVM.h" +#include "swift/SIL/Projection.h" +#include "swift/SIL/SILArgument.h" +#include "swift/SIL/SILDebugScope.h" +#include "swift/SIL/SILFunction.h" +#include "swift/SIL/SILInstruction.h" +#include "swift/SIL/SILValue.h" +#include "swift/SILOptimizer/Analysis/ARCAnalysis.h" +#include "swift/SILOptimizer/Analysis/AliasAnalysis.h" +#include "swift/SILOptimizer/Analysis/Analysis.h" +#include "swift/SILOptimizer/Analysis/CallerAnalysis.h" +#include "swift/SILOptimizer/Analysis/RCIdentityAnalysis.h" +#include "swift/SILOptimizer/PassManager/PassManager.h" +#include "swift/SILOptimizer/Utils/Local.h" +#include "swift/SILOptimizer/Utils/SpecializationMangler.h" +#include "llvm/ADT/DenseMap.h" + +namespace swift { + +/// A structure that maintains all of the information about a specific +/// SILArgument that we are tracking. +struct ArgumentDescriptor { + /// The argument that we are tracking original data for. + SILFunctionArgument *Arg; + + /// Parameter Info. + Optional PInfo; + + /// The original index of this argument. + unsigned Index; + + /// The original decl of this Argument. + const ValueDecl *Decl; + + /// Was this parameter originally dead? + bool IsEntirelyDead; + + /// Was this argument completely removed already? + /// + /// TODO: Could we make ArgumentDescriptors just optional and once they have + /// been consumed no longer process them? + bool WasErased; + + /// Should the argument be exploded ? + bool Explode; + + /// This parameter is owned to guaranteed. + bool OwnedToGuaranteed; + + /// Is this parameter an indirect result? + bool IsIndirectResult; + + /// If non-null, this is the release in the return block of the callee, which + /// is associated with this parameter if it is @owned. If the parameter is not + /// @owned or we could not find such a release in the callee, this is null. + ReleaseList CalleeRelease; + + /// The same as CalleeRelease, but the release in the throw block, if it is a + /// function which has a throw block. + ReleaseList CalleeReleaseInThrowBlock; + + /// The projection tree of this arguments. + ProjectionTree ProjTree; + + ArgumentDescriptor() = delete; + + /// Initialize this argument descriptor with all information from A that we + /// use in our optimization. + /// + /// *NOTE* We cache a lot of data from the argument and maintain a reference + /// to the original argument. The reason why we do this is to make sure we + /// have access to the original argument's state if we modify the argument + /// when optimizing. + ArgumentDescriptor(SILFunctionArgument *A) + : Arg(A), PInfo(A->getKnownParameterInfo()), Index(A->getIndex()), + Decl(A->getDecl()), IsEntirelyDead(false), WasErased(false), + Explode(false), OwnedToGuaranteed(false), + IsIndirectResult(A->isIndirectResult()), CalleeRelease(), + CalleeReleaseInThrowBlock(), ProjTree(A->getModule(), A->getType()) { + if (!A->isIndirectResult()) { + PInfo = Arg->getKnownParameterInfo(); + } + } + + ArgumentDescriptor(const ArgumentDescriptor &) = delete; + ArgumentDescriptor(ArgumentDescriptor &&) = default; + ArgumentDescriptor &operator=(const ArgumentDescriptor &) = delete; + ArgumentDescriptor &operator=(ArgumentDescriptor &&) = default; + + /// \returns true if this argument's convention is P. + bool hasConvention(SILArgumentConvention P) const { + return Arg->hasConvention(P); + } + + bool canOptimizeLiveArg() const { + if (Arg->getType().isObject()) + return true; + // @in arguments of generic types can be processed. + if (Arg->getType().hasArchetype() && + Arg->getType().isAddress() && + (Arg->hasConvention(SILArgumentConvention::Indirect_In) || + Arg->hasConvention(SILArgumentConvention::Indirect_In_Guaranteed))) + return true; + return false; + } + + /// Return true if it's both legal and a good idea to explode this argument. + bool shouldExplode(ConsumedArgToEpilogueReleaseMatcher &ERM) const { + // We cannot optimize the argument. + if (!canOptimizeLiveArg()) + return false; + + // See if the projection tree consists of potentially multiple levels of + // structs containing one field. In such a case, there is no point in + // exploding the argument. + // + // Also, in case of a type can not be exploded, e.g an enum, we treat it + // as a singleton. + if (ProjTree.isSingleton()) + return false; + + auto Ty = Arg->getType().getObjectType(); + if (!shouldExpand(Arg->getModule(), Ty)) { + return false; + } + + // If this argument is @owned and we can not find all the releases for it + // try to explode it, maybe we can find some of the releases and O2G some + // of its components. + // + // This is a potentially a very profitable optimization. Ignore other + // heuristics. + if (hasConvention(SILArgumentConvention::Direct_Owned) && + ERM.hasSomeReleasesForArgument(Arg)) + return true; + + unsigned explosionSize = ProjTree.getLiveLeafCount(); + return explosionSize >= 1 && explosionSize <= 3; + } + + llvm::Optional + getTransformedOwnershipKind(SILType SubTy) { + if (IsEntirelyDead) + return None; + if (SubTy.isTrivial(Arg->getModule())) + return Optional(ValueOwnershipKind::Trivial); + if (OwnedToGuaranteed) + return Optional(ValueOwnershipKind::Guaranteed); + return Arg->getOwnershipKind(); + } +}; + +/// A structure that maintains all of the information about a specific +/// direct result that we are tracking. +struct ResultDescriptor { + /// The original parameter info of this argument. + SILResultInfo ResultInfo; + + /// If non-null, this is the release in the return block of the callee, which + /// is associated with this parameter if it is @owned. If the parameter is not + /// @owned or we could not find such a release in the callee, this is null. + llvm::SmallSetVector CalleeRetain; + + /// This is owned to guaranteed. + bool OwnedToGuaranteed; + + /// Initialize this argument descriptor with all information from A that we + /// use in our optimization. + /// + /// *NOTE* We cache a lot of data from the argument and maintain a reference + /// to the original argument. The reason why we do this is to make sure we + /// have access to the original argument's state if we modify the argument + /// when optimizing. + ResultDescriptor() {} + ResultDescriptor(SILResultInfo RI) + : ResultInfo(RI), CalleeRetain(), OwnedToGuaranteed(false) {} + + ResultDescriptor(const ResultDescriptor &) = delete; + ResultDescriptor(ResultDescriptor &&) = default; + ResultDescriptor &operator=(const ResultDescriptor &) = delete; + ResultDescriptor &operator=(ResultDescriptor &&) = default; + + /// \returns true if this argument's ParameterConvention is P. + bool hasConvention(ResultConvention R) const { + return ResultInfo.getConvention() == R; + } +}; + +struct FunctionSignatureTransformDescriptor { + /// The original function that we are analyzing/transforming. + SILFunction *OriginalFunction; + + /// The new optimize function that we will create. + NullablePtr OptimizedFunction; + + /// A map from a pre-transformed argument to a post-transformed argument. + llvm::SmallDenseMap &AIM; + + /// Set to true if we are going to modify self during our transformation. + /// + /// TODO: Rename to willModifySelfArgument. + bool shouldModifySelfArgument; + + /// Keep a "view" of precompiled information on arguments that we use + /// during our optimization. + MutableArrayRef ArgumentDescList; + + /// Keep a "view" of precompiled information on the direct results that we + /// will use during our optimization. + MutableArrayRef ResultDescList; + + /// Return a function name based on the current state of ArgumentDescList and + /// ResultDescList. + /// + /// FIXME: Change this to take a SmallString as an out parameter? + std::string createOptimizedSILFunctionName(); + + /// Return a function type based on the current state of ArgumentDescList and + /// ResultDescList. + CanSILFunctionType createOptimizedSILFunctionType(); + + /// Compute the optimized function type based on the given argument + /// descriptor. + void computeOptimizedArgInterface(ArgumentDescriptor &A, + SmallVectorImpl &O); + + /// Setup the thunk arguments based on the given argument descriptor info. + /// Every transformation must defines this interface. Default implementation + /// simply passes it through. + void addThunkArgument(ArgumentDescriptor &AD, SILBuilder &Builder, + SILBasicBlock *BB, SmallVectorImpl &NewArgs); +}; + +class FunctionSignatureTransform { + /// A struct that contains all data that we use during our + /// transformation. This is an initial step towards splitting this struct into + /// multiple "transforms" that can be tested independently of each other. + FunctionSignatureTransformDescriptor TransformDescriptor; + + /// The RC identity analysis we are using. + RCIdentityAnalysis *RCIA; + + /// Post order analysis we are using. + EpilogueARCAnalysis *EA; + +private: + /// ----------------------------------------------------------/// + /// Dead argument transformation. /// + /// ----------------------------------------------------------/// + /// Find any dead argument opportunities. + bool DeadArgumentAnalyzeParameters(); + /// Modify the current function so that later function signature analysis + /// are more effective. + void DeadArgumentTransformFunction(); + /// Remove the dead argument once the new function is created. + void DeadArgumentFinalizeOptimizedFunction(); + + /// ----------------------------------------------------------/// + /// Owned to guaranteed transformation. /// + /// ----------------------------------------------------------/// + bool OwnedToGuaranteedAnalyzeResults(); + bool OwnedToGuaranteedAnalyzeParameters(); + + /// Modify the current function so that later function signature analysis + /// are more effective. + void OwnedToGuaranteedTransformFunctionResults(); + void OwnedToGuaranteedTransformFunctionParameters(); + + /// Find any owned to guaranteed opportunities. + bool OwnedToGuaranteedAnalyze(); + + /// Do the actual owned to guaranteed transformations. + void OwnedToGuaranteedTransform(); + + /// Set up epilogue work for the thunk result based in the given argument. + void OwnedToGuaranteedAddResultRelease(ResultDescriptor &RD, + SILBuilder &Builder, SILFunction *F); + + /// Set up epilogue work for the thunk argument based in the given argument. + void OwnedToGuaranteedAddArgumentRelease(ArgumentDescriptor &AD, + SILBuilder &Builder, SILFunction *F); + + /// Add the release for converted arguments and result. + void OwnedToGuaranteedFinalizeThunkFunction(SILBuilder &B, SILFunction *F); + + /// ----------------------------------------------------------/// + /// Argument explosion transformation. /// + /// ----------------------------------------------------------/// + /// Find any argument explosion opportunities. + bool ArgumentExplosionAnalyzeParameters(); + /// Explode the argument in the optimized function and replace the uses of + /// the original argument. + void ArgumentExplosionFinalizeOptimizedFunction(); + + /// Take ArgumentDescList and ResultDescList and create an optimized function + /// based on the current function we are analyzing. This also has the side + /// effect of turning the current function into a thunk. + void createFunctionSignatureOptimizedFunction(); + +public: + /// Constructor. + FunctionSignatureTransform( + SILFunction *F, RCIdentityAnalysis *RCIA, EpilogueARCAnalysis *EA, + Mangle::FunctionSignatureSpecializationMangler &Mangler, + llvm::SmallDenseMap &AIM, + llvm::SmallVector &ADL, + llvm::SmallVector &RDL) + : TransformDescriptor{F, nullptr, AIM, false, ADL, RDL}, RCIA(RCIA), + EA(EA) {} + + /// Return the optimized function. + SILFunction *getOptimizedFunction() { + return TransformDescriptor.OptimizedFunction.getPtrOrNull(); + } + + /// Run the optimization. + bool run(bool hasCaller); + + /// Run dead argument elimination of partially applied functions. + /// + /// After this optimization CapturePropagation can replace the partial_apply + /// by a direct reference to the specialized function. + bool removeDeadArgs(int minPartialAppliedArgs); +}; + +} // namespace swift + +#endif diff --git a/lib/SILOptimizer/Transforms/ObjectOutliner.cpp b/lib/SILOptimizer/Transforms/ObjectOutliner.cpp index ee77a477553d0..7c8620fe1ee8d 100644 --- a/lib/SILOptimizer/Transforms/ObjectOutliner.cpp +++ b/lib/SILOptimizer/Transforms/ObjectOutliner.cpp @@ -473,7 +473,7 @@ void ObjectOutliner::replaceFindStringCall(ApplyInst *FindStringCall) { FunctionRefInst *FRI = B.createFunctionRef(FindStringCall->getLoc(), replacementFunc); ApplyInst *NewCall = B.createApply(FindStringCall->getLoc(), FRI, - FindStringCall->getSubstitutions(), + FindStringCall->getSubstitutionMap(), { FindStringCall->getArgument(0), FindStringCall->getArgument(1), CacheAddr }, diff --git a/lib/SILOptimizer/Transforms/Outliner.cpp b/lib/SILOptimizer/Transforms/Outliner.cpp index 599296b44a6c6..33bf9c20bde0c 100644 --- a/lib/SILOptimizer/Transforms/Outliner.cpp +++ b/lib/SILOptimizer/Transforms/Outliner.cpp @@ -13,6 +13,7 @@ #define DEBUG_TYPE "sil-outliner" #include "swift/AST/ASTMangler.h" +#include "swift/AST/Module.h" #include "swift/AST/ProtocolConformance.h" #include "swift/AST/Types.h" #include "swift/Demangling/Demangler.h" @@ -217,7 +218,7 @@ class BridgedProperty : public OutlinePattern { SingleValueInstruction *FirstInst; // A load or class_method SILBasicBlock *StartBB; SwitchInfo switchInfo; - ObjCMethodInst *ObjCMethod;; + ObjCMethodInst *ObjCMethod; StrongReleaseInst *Release; ApplyInst *PropApply; @@ -277,18 +278,18 @@ CanSILFunctionType BridgedProperty::getOutlinedFunctionType(SILModule &M) { SmallVector Parameters; if (auto *Load = dyn_cast(FirstInst)) Parameters.push_back( - SILParameterInfo(Load->getType().getSwiftRValueType(), + SILParameterInfo(Load->getType().getASTType(), ParameterConvention::Indirect_In_Guaranteed)); else Parameters.push_back(SILParameterInfo(cast(FirstInst) ->getOperand() ->getType() - .getSwiftRValueType(), + .getASTType(), ParameterConvention::Direct_Unowned)); SmallVector Results; Results.push_back(SILResultInfo( - switchInfo.Br->getArg(0)->getType().getSwiftRValueType(), + switchInfo.Br->getArg(0)->getType().getASTType(), ResultConvention::Owned)); auto ExtInfo = SILFunctionType::ExtInfo(SILFunctionType::Representation::Thin, @@ -476,7 +477,7 @@ static bool matchSwitch(SwitchInfo &SI, SILInstruction *Inst, return false; // Check that we call the _unconditionallyBridgeFromObjectiveC witness. - auto NativeType = Apply->getType().getSwiftRValueType(); + auto NativeType = Apply->getType().getASTType(); auto *BridgeFun = FunRef->getReferencedFunction(); auto *SwiftModule = BridgeFun->getModule().getSwiftModule(); auto bridgeWitness = getBridgeFromObjectiveC(NativeType, SwiftModule); @@ -762,7 +763,7 @@ BridgedArgument BridgedArgument::match(unsigned ArgIdx, SILValue Arg, } // Make sure we are calling the actual bridge witness. - auto NativeType = BridgedValue->getType().getSwiftRValueType(); + auto NativeType = BridgedValue->getType().getASTType(); auto *BridgeFun = FunRef->getReferencedFunction(); auto *SwiftModule = BridgeFun->getModule().getSwiftModule(); auto bridgeWitness = getBridgeToObjectiveC(NativeType, SwiftModule); @@ -802,7 +803,7 @@ class BridgedReturn { operator bool() { return switchInfo.SomeBB != nullptr; } CanType getReturnType() { - return switchInfo.Br->getArg(0)->getType().getSwiftRValueType(); + return switchInfo.Br->getArg(0)->getType().getASTType(); } /// Outline the return value bridging blocks. @@ -905,6 +906,7 @@ void ObjCMethodCall::clearState() { BridgedCall = nullptr; BridgedArguments.clear(); OutlinedName.clear(); + IsBridgedArgument.clear(); } std::pair @@ -1090,7 +1092,7 @@ CanSILFunctionType ObjCMethodCall::getOutlinedFunctionType(SILModule &M) { Parameters.push_back(SILParameterInfo(BridgedArguments[BridgedArgIdx] .bridgedValue() ->getType() - .getSwiftRValueType(), + .getASTType(), ParameterConvention::Direct_Owned)); ++BridgedArgIdx; } else { diff --git a/lib/SILOptimizer/Transforms/PerformanceInliner.cpp b/lib/SILOptimizer/Transforms/PerformanceInliner.cpp index 51d5536f59106..6ca65fd21737d 100644 --- a/lib/SILOptimizer/Transforms/PerformanceInliner.cpp +++ b/lib/SILOptimizer/Transforms/PerformanceInliner.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #define DEBUG_TYPE "sil-inliner" +#include "swift/AST/Module.h" #include "swift/SIL/OptimizationRemark.h" #include "swift/SILOptimizer/Analysis/SideEffectAnalysis.h" #include "swift/SILOptimizer/PassManager/Passes.h" @@ -177,7 +178,7 @@ class SILPerformanceInliner { // Returns true if it is possible to perform a generic // specialization for a given call. static bool canSpecializeGeneric(ApplySite AI, SILFunction *F, - SubstitutionList Subs) { + SubstitutionMap Subs) { return ReabstractionInfo::canBeSpecialized(AI, F, Subs); } @@ -239,7 +240,7 @@ bool SILPerformanceInliner::isProfitableToInline( int &NumCallerBlocks, const llvm::DenseMap &BBToWeightMap) { SILFunction *Callee = AI.getReferencedFunction(); - bool IsGeneric = !AI.getSubstitutions().empty(); + bool IsGeneric = AI.hasSubstitutions(); assert(EnableSILInliningOfGenerics || !IsGeneric); @@ -278,7 +279,7 @@ bool SILPerformanceInliner::isProfitableToInline( // Bail out if this generic call can be optimized by means of // the generic specialization, because we prefer generic specialization // to inlining of generics. - if (IsGeneric && canSpecializeGeneric(AI, Callee, AI.getSubstitutions())) { + if (IsGeneric && canSpecializeGeneric(AI, Callee, AI.getSubstitutionMap())) { return false; } @@ -295,12 +296,7 @@ bool SILPerformanceInliner::isProfitableToInline( int CalleeCost = 0; int Benefit = 0; - SubstitutionMap CalleeSubstMap; - if (IsGeneric) { - CalleeSubstMap = Callee->getLoweredFunctionType() - ->getGenericSignature() - ->getSubstitutionMap(AI.getSubstitutions()); - } + SubstitutionMap CalleeSubstMap = AI.getSubstitutionMap(); CallerWeight.updateBenefit(Benefit, BaseBenefit); @@ -329,7 +325,7 @@ bool SILPerformanceInliner::isProfitableToInline( if (!def) continue; - auto Subs = FAI.getSubstitutions(); + auto Subs = FAI.getSubstitutionMap(); // Bail if it is not a generic call or inlining of generics is forbidden. if (!EnableSILInliningOfGenerics || Subs.empty()) @@ -345,12 +341,7 @@ bool SILPerformanceInliner::isProfitableToInline( // Create the list of substitutions as they will be after // inlining. - auto Sig = FAI.getOrigCalleeType()->getGenericSignature(); - auto SubMap = Sig->getSubstitutionMap(Subs); - SubMap = SubMap.subst(CalleeSubstMap); - - SmallVector NewSubs; - Sig->getSubstitutions(SubMap, NewSubs); + auto SubMap = Subs.subst(CalleeSubstMap); // Check if the call can be devirtualized. if (isa(def) || isa(def) || @@ -367,7 +358,7 @@ bool SILPerformanceInliner::isProfitableToInline( // Check if a generic specialization would be possible. if (isa(def)) { auto CalleeF = FAI.getCalleeFunction(); - if (!canSpecializeGeneric(FAI, CalleeF, NewSubs)) + if (!canSpecializeGeneric(FAI, CalleeF, SubMap)) continue; DEBUG(llvm::dbgs() << "Generic specialization will be possible after " "inlining for the call:\n"; @@ -484,7 +475,7 @@ bool SILPerformanceInliner::isProfitableToInline( /// It returns None if the decision cannot be made without a more complex /// analysis. static Optional shouldInlineGeneric(FullApplySite AI) { - assert(!AI.getSubstitutions().empty() && + assert(AI.hasSubstitutions() && "Expected a generic apply"); SILFunction *Callee = AI.getReferencedFunction(); @@ -514,7 +505,7 @@ static Optional shouldInlineGeneric(FullApplySite AI) { // If all substitutions are concrete, then there is no need to perform the // generic inlining. Let the generic specializer create a specialized // function and then decide if it is beneficial to inline it. - if (!hasArchetypes(AI.getSubstitutions())) + if (!AI.getSubstitutionMap().hasArchetypes()) return false; // It is not clear yet if this function should be decided or not. @@ -525,7 +516,7 @@ bool SILPerformanceInliner::decideInWarmBlock( FullApplySite AI, Weight CallerWeight, ConstantTracker &callerTracker, int &NumCallerBlocks, const llvm::DenseMap &BBToWeightMap) { - if (!AI.getSubstitutions().empty()) { + if (AI.hasSubstitutions()) { // Only inline generics if definitively clear that it should be done. auto ShouldInlineGeneric = shouldInlineGeneric(AI); if (ShouldInlineGeneric.hasValue()) @@ -549,7 +540,7 @@ bool SILPerformanceInliner::decideInWarmBlock( /// Return true if inlining this call site into a cold block is profitable. bool SILPerformanceInliner::decideInColdBlock(FullApplySite AI, SILFunction *Callee) { - if (!AI.getSubstitutions().empty()) { + if (AI.hasSubstitutions()) { // Only inline generics if definitively clear that it should be done. auto ShouldInlineGeneric = shouldInlineGeneric(AI); if (ShouldInlineGeneric.hasValue()) @@ -846,7 +837,7 @@ bool SILPerformanceInliner::inlineCallsIntoFunction(SILFunction *Caller) { SILInliner Inliner(*Caller, *Callee, SILInliner::InlineKind::PerformanceInline, - AI.getSubstitutions(), + AI.getSubstitutionMap(), OpenedArchetypesTracker); // We've already determined we should be able to inline this, so diff --git a/lib/SILOptimizer/Transforms/ReleaseDevirtualizer.cpp b/lib/SILOptimizer/Transforms/ReleaseDevirtualizer.cpp index 3e203587e4081..ed6aede4a9c2c 100644 --- a/lib/SILOptimizer/Transforms/ReleaseDevirtualizer.cpp +++ b/lib/SILOptimizer/Transforms/ReleaseDevirtualizer.cpp @@ -144,8 +144,8 @@ bool ReleaseDevirtualizer::createDeallocCall(SILType AllocType, return false; CanSILFunctionType DeallocType = Dealloc->getLoweredFunctionType(); - auto *NTD = AllocType.getSwiftRValueType()->getAnyNominal(); - auto AllocSubMap = AllocType.getSwiftRValueType() + auto *NTD = AllocType.getASTType()->getAnyNominal(); + auto AllocSubMap = AllocType.getASTType() ->getContextSubstitutionMap(M.getSwiftModule(), NTD); DeallocType = DeallocType->substGenericArgs(M, AllocSubMap); @@ -163,11 +163,7 @@ bool ReleaseDevirtualizer::createDeallocCall(SILType AllocType, // argument. auto *MI = B.createFunctionRef(ReleaseInst->getLoc(), Dealloc); - SmallVector AllocSubsts; - if (auto *Sig = NTD->getGenericSignature()) - Sig->getSubstitutions(AllocSubMap, AllocSubsts); - - B.createApply(ReleaseInst->getLoc(), MI, AllocSubsts, {object}, false); + B.createApply(ReleaseInst->getLoc(), MI, AllocSubMap, {object}, false); NumReleasesDevirtualized++; ReleaseInst->eraseFromParent(); diff --git a/lib/SILOptimizer/Transforms/SILCleanup.cpp b/lib/SILOptimizer/Transforms/SILCleanup.cpp deleted file mode 100644 index 3fd0d641ec8c1..0000000000000 --- a/lib/SILOptimizer/Transforms/SILCleanup.cpp +++ /dev/null @@ -1,67 +0,0 @@ -//===--- SILCleanup.cpp - Removes diagnostics instructions ----------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// -// -// Cleanup SIL to make it suitable for IRGen. Specifically, removes the calls to -// Builtin.staticReport(), which are not needed post SIL. -// -// FIXME: This pass is mandatory so should probably be in -// SILOptimizer/Mandatory. -// -//===----------------------------------------------------------------------===// - -#include "swift/SILOptimizer/PassManager/Passes.h" -#include "swift/SIL/SILFunction.h" -#include "swift/SIL/SILInstruction.h" -#include "swift/SIL/SILModule.h" -#include "swift/SILOptimizer/Utils/Local.h" -#include "swift/SILOptimizer/PassManager/Transforms.h" - -using namespace swift; - -static void cleanFunction(SILFunction &Fn) { - for (auto &BB : Fn) { - auto I = BB.begin(), E = BB.end(); - while (I != E) { - // Make sure there is no iterator invalidation if the inspected - // instruction gets removed from the block. - SILInstruction *Inst = &*I; - ++I; - - // Remove calls to Builtin.staticReport(). - if (auto *BI = dyn_cast(Inst)) { - const BuiltinInfo &B = BI->getBuiltinInfo(); - if (B.ID == BuiltinValueKind::StaticReport) { - // The call to the builtin should get removed before we reach - // IRGen. - recursivelyDeleteTriviallyDeadInstructions(BI, /* Force */true); - } - } - } - } -} - -namespace { -class SILCleanup : public swift::SILFunctionTransform { - - /// The entry point to the transformation. - void run() override { - cleanFunction(*getFunction()); - invalidateAnalysis(SILAnalysis::InvalidationKind::FunctionBody); - } - -}; -} // end anonymous namespace - - -SILTransform *swift::createSILCleanup() { - return new SILCleanup(); -} diff --git a/lib/SILOptimizer/Transforms/SimplifyCFG.cpp b/lib/SILOptimizer/Transforms/SimplifyCFG.cpp index 6cf28ed67e941..5188cd45cd292 100644 --- a/lib/SILOptimizer/Transforms/SimplifyCFG.cpp +++ b/lib/SILOptimizer/Transforms/SimplifyCFG.cpp @@ -266,7 +266,7 @@ static SILValue getTerminatorCondition(TermInst *Term) { /// Is this basic block jump threadable. static bool isThreadableBlock(SILBasicBlock *BB, - SmallPtrSet &LoopHeaders) { + SmallPtrSetImpl &LoopHeaders) { if (isa(BB->getTerminator())) return false; @@ -444,7 +444,7 @@ static SILValue createValueForEdge(SILInstruction *UserInst, /// of the operand of 'DominatingBB's terminator. static bool tryDominatorBasedSimplifications( SILBasicBlock *DominatingBB, DominanceInfo *DT, - SmallPtrSet &LoopHeaders, + SmallPtrSetImpl &LoopHeaders, SmallVectorImpl &JumpThreadableEdges, llvm::DenseSet> &ThreadedEdgeSet, @@ -767,7 +767,7 @@ bool SimplifyCFG::simplifyAfterDroppingPredecessor(SILBasicBlock *BB) { static NullablePtr getEnumCaseRecursive(SILValue Val, SILBasicBlock *UsedInBB, int RecursionDepth, - llvm::SmallPtrSet HandledArgs) { + llvm::SmallPtrSetImpl &HandledArgs) { // Limit the number of recursions. This is an easy way to cope with cycles // in the SSA graph. if (RecursionDepth > 3) @@ -1127,7 +1127,7 @@ static bool onlyHasTerminatorAndDebugInsts(SILBasicBlock *BB) { TermInst *Terminator = BB->getTerminator(); SILBasicBlock::iterator Iter = BB->begin(); while (&*Iter != Terminator) { - if (!isDebugInst(&*Iter)) + if (!(&*Iter)->isDebugInstruction()) return false; Iter++; } @@ -2085,14 +2085,14 @@ bool SimplifyCFG::simplifyTryApplyBlock(TryApplyInst *TAI) { auto TargetFnTy = CalleeFnTy; if (TargetFnTy->isPolymorphic()) { TargetFnTy = TargetFnTy->substGenericArgs(TAI->getModule(), - TAI->getSubstitutions()); + TAI->getSubstitutionMap()); } SILFunctionConventions targetConv(TargetFnTy, TAI->getModule()); auto OrigFnTy = TAI->getCallee()->getType().getAs(); if (OrigFnTy->isPolymorphic()) { OrigFnTy = OrigFnTy->substGenericArgs(TAI->getModule(), - TAI->getSubstitutions()); + TAI->getSubstitutionMap()); } SILFunctionConventions origConv(OrigFnTy, TAI->getModule()); @@ -2112,7 +2112,7 @@ bool SimplifyCFG::simplifyTryApplyBlock(TryApplyInst *TAI) { DEBUG(llvm::dbgs() << "replace with apply: " << *TAI); ApplyInst *NewAI = Builder.createApply(TAI->getLoc(), Callee, - TAI->getSubstitutions(), + TAI->getSubstitutionMap(), Args, CalleeFnTy->hasErrorResult()); auto Loc = TAI->getLoc(); diff --git a/lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp b/lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp index a68288568444f..aadd7a1fc2275 100644 --- a/lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp +++ b/lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp @@ -62,17 +62,17 @@ static FullApplySite CloneApply(FullApplySite AI, SILBuilder &Builder) { switch (AI.getInstruction()->getKind()) { case SILInstructionKind::ApplyInst: NAI = Builder.createApply(AI.getLoc(), AI.getCallee(), - AI.getSubstitutions(), - Ret, - cast(AI)->isNonThrowing()); + AI.getSubstitutionMap(), + Ret, + cast(AI)->isNonThrowing()); break; case SILInstructionKind::TryApplyInst: { auto *TryApplyI = cast(AI.getInstruction()); NAI = Builder.createTryApply(AI.getLoc(), AI.getCallee(), - AI.getSubstitutions(), - Ret, - TryApplyI->getNormalBB(), - TryApplyI->getErrorBB()); + AI.getSubstitutionMap(), + Ret, + TryApplyI->getNormalBB(), + TryApplyI->getErrorBB()); } break; default: @@ -93,7 +93,7 @@ static FullApplySite speculateMonomorphicTarget(FullApplySite AI, if (!canDevirtualizeClassMethod(AI, SubType)) return FullApplySite(); - if (SubType.getSwiftRValueType()->hasDynamicSelfType()) + if (SubType.getASTType()->hasDynamicSelfType()) return FullApplySite(); // Create a diamond shaped control flow and a checked_cast_branch @@ -209,7 +209,7 @@ static FullApplySite speculateMonomorphicTarget(FullApplySite AI, Args.push_back(Arg); } FullApplySite NewVirtAI = Builder.createTryApply(VirtAI.getLoc(), VirtAI.getCallee(), - VirtAI.getSubstitutions(), + VirtAI.getSubstitutionMap(), Args, NormalBB, ErrorBB); VirtAI.getInstruction()->eraseFromParent(); VirtAI = NewVirtAI; @@ -467,7 +467,7 @@ static bool tryToSpeculateTarget(FullApplySite AI, ClassHierarchyAnalysis *CHA, auto ClassOrMetatypeType = ClassType; if (auto EMT = SubType.getAs()) { - auto InstTy = ClassType.getSwiftRValueType(); + auto InstTy = ClassType.getASTType(); auto *MetaTy = MetatypeType::get(InstTy, EMT->getRepresentation()); auto CanMetaTy = CanMetatypeType(MetaTy); ClassOrMetatypeType = SILType::getPrimitiveObjectType(CanMetaTy); diff --git a/lib/SILOptimizer/UtilityPasses/AccessedStorageDumper.cpp b/lib/SILOptimizer/UtilityPasses/AccessedStorageDumper.cpp new file mode 100644 index 0000000000000..93c8af56ac13e --- /dev/null +++ b/lib/SILOptimizer/UtilityPasses/AccessedStorageDumper.cpp @@ -0,0 +1,49 @@ +//===--- AccessedStorageDumper.cpp - Dump accessed storage for functions ---===// +// +// 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-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 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()); + } + } +}; + +} // end anonymous namespace + +SILTransform *swift::createAccessedStorageDumper() { + return new AccessedStorageDumper(); +} diff --git a/lib/SILOptimizer/UtilityPasses/BugReducerTester.cpp b/lib/SILOptimizer/UtilityPasses/BugReducerTester.cpp index 4219ac246789a..cca17a8be66d5 100644 --- a/lib/SILOptimizer/UtilityPasses/BugReducerTester.cpp +++ b/lib/SILOptimizer/UtilityPasses/BugReducerTester.cpp @@ -77,7 +77,7 @@ class BugReducerTester : public SILFunctionTransform { auto EmptyTupleCanType = getFunction() ->getModule() .Types.getEmptyTupleType() - .getSwiftRValueType(); + .getASTType(); ResultInfoArray.push_back( SILResultInfo(EmptyTupleCanType, ResultConvention::Unowned)); auto FuncType = SILFunctionType::get( @@ -148,7 +148,7 @@ class BugReducerTester : public SILFunctionTransform { RuntimeCrasherFunc->dump(); SILBuilder B(Apply->getIterator()); B.createApply(Loc, B.createFunctionRef(Loc, RuntimeCrasherFunc), - SubstitutionList(), + SubstitutionMap(), ArrayRef(), false /*NoThrow*/); Apply->replaceAllUsesWith(SILUndef::get(Apply->getType(), M)); diff --git a/lib/SILOptimizer/UtilityPasses/CMakeLists.txt b/lib/SILOptimizer/UtilityPasses/CMakeLists.txt index 93137a9924e03..4681be81457db 100644 --- a/lib/SILOptimizer/UtilityPasses/CMakeLists.txt +++ b/lib/SILOptimizer/UtilityPasses/CMakeLists.txt @@ -1,6 +1,7 @@ set(UTILITYPASSES_SOURCES UtilityPasses/AADumper.cpp UtilityPasses/AccessSummaryDumper.cpp + UtilityPasses/AccessedStorageDumper.cpp UtilityPasses/BasicCalleePrinter.cpp UtilityPasses/BasicInstructionPropertyDumper.cpp UtilityPasses/BugReducerTester.cpp diff --git a/lib/SILOptimizer/UtilityPasses/Link.cpp b/lib/SILOptimizer/UtilityPasses/Link.cpp index 608ccbea7edf2..bf080a33f8e6f 100644 --- a/lib/SILOptimizer/UtilityPasses/Link.cpp +++ b/lib/SILOptimizer/UtilityPasses/Link.cpp @@ -20,30 +20,20 @@ using namespace swift; // Top Level Driver //===----------------------------------------------------------------------===// -void swift::performSILLinking(SILModule *M, bool LinkAll) { - auto LinkMode = LinkAll? SILModule::LinkingMode::LinkAll : - SILModule::LinkingMode::LinkNormal; - for (auto &Fn : *M) - M->linkFunction(&Fn, LinkMode); - - if (!LinkAll) - return; - - M->linkAllWitnessTables(); - M->linkAllVTables(); -} - - namespace { /// Copies code from the standard library into the user program to enable /// optimizations. class SILLinker : public SILModuleTransform { + SILModule::LinkingMode LinkMode; + +public: + explicit SILLinker(SILModule::LinkingMode LinkMode) : LinkMode(LinkMode) {} void run() override { SILModule &M = *getModule(); for (auto &Fn : M) - if (M.linkFunction(&Fn, SILModule::LinkingMode::LinkAll)) + if (M.linkFunction(&Fn, LinkMode)) invalidateAnalysis(&Fn, SILAnalysis::InvalidationKind::Everything); } @@ -51,6 +41,10 @@ class SILLinker : public SILModuleTransform { } // end anonymous namespace -SILTransform *swift::createSILLinker() { - return new SILLinker(); +SILTransform *swift::createMandatorySILLinker() { + return new SILLinker(SILModule::LinkingMode::LinkNormal); +} + +SILTransform *swift::createPerformanceSILLinker() { + return new SILLinker(SILModule::LinkingMode::LinkAll); } diff --git a/lib/SILOptimizer/Utils/CFG.cpp b/lib/SILOptimizer/Utils/CFG.cpp index 7dcd7b8446ad3..6bbb36d326538 100644 --- a/lib/SILOptimizer/Utils/CFG.cpp +++ b/lib/SILOptimizer/Utils/CFG.cpp @@ -300,7 +300,7 @@ void swift::changeBranchTarget(TermInst *T, unsigned EdgeIdx, for (auto &Op : TAI->getArgumentOperands()) Arguments.push_back(Op.get()); - B.createTryApply(TAI->getLoc(), TAI->getCallee(), TAI->getSubstitutions(), + B.createTryApply(TAI->getLoc(), TAI->getCallee(), TAI->getSubstitutionMap(), Arguments, NormalBB, ErrorBB); TAI->eraseFromParent(); diff --git a/lib/SILOptimizer/Utils/CMakeLists.txt b/lib/SILOptimizer/Utils/CMakeLists.txt index 2cfc3a793e011..0fbb7e93ec599 100644 --- a/lib/SILOptimizer/Utils/CMakeLists.txt +++ b/lib/SILOptimizer/Utils/CMakeLists.txt @@ -4,7 +4,6 @@ set(UTILS_SOURCES Utils/CheckedCastBrJumpThreading.cpp Utils/ConstantFolding.cpp Utils/Devirtualize.cpp - Utils/FunctionSignatureOptUtils.cpp Utils/GenericCloner.cpp Utils/Generics.cpp Utils/LoadStoreOptUtils.cpp diff --git a/lib/SILOptimizer/Utils/CastOptimizer.cpp b/lib/SILOptimizer/Utils/CastOptimizer.cpp index 6325309f39a8a..595bf2147091c 100644 --- a/lib/SILOptimizer/Utils/CastOptimizer.cpp +++ b/lib/SILOptimizer/Utils/CastOptimizer.cpp @@ -18,6 +18,7 @@ #include "swift/SILOptimizer/Utils/CastOptimizer.h" #include "swift/AST/GenericSignature.h" +#include "swift/AST/Module.h" #include "swift/AST/SubstitutionMap.h" #include "swift/SIL/BasicBlockUtils.h" #include "swift/SIL/DebugUtils.h" @@ -206,7 +207,7 @@ SILInstruction *CastOptimizer::optimizeBridgedObjCToSwiftCast( SILValue InOutOptionalParam; if (isConditional) { // Create a temporary - OptionalTy = OptionalType::get(Dest->getType().getSwiftRValueType()) + OptionalTy = OptionalType::get(Dest->getType().getASTType()) ->getImplementationType() ->getCanonicalType(); Tmp = Builder.createAllocStack(Loc, @@ -233,10 +234,7 @@ SILInstruction *CastOptimizer::optimizeBridgedObjCToSwiftCast( Args.push_back(SrcOp); Args.push_back(MetaTyVal); - SmallVector Subs; - Conf.getRequirement()->getGenericSignature()->getSubstitutions(SubMap, Subs); - - auto *AI = Builder.createApply(Loc, FuncRef, Subs, Args, false); + auto *AI = Builder.createApply(Loc, FuncRef, SubMap, Args, false); // If we have guaranteed normal arguments, insert the destroy. // @@ -313,14 +311,14 @@ static bool canOptimizeCast(const swift::Type &BridgedTargetTy, return true; } // check if it is a bridgeable CF type - if (ConvTy.getSwiftRValueType() == + if (ConvTy.getASTType() == getNSBridgedClassOfCFClass(M.getSwiftModule(), - DestTy.getSwiftRValueType())) { + DestTy.getASTType())) { return true; } - if (DestTy.getSwiftRValueType() == + if (DestTy.getASTType() == getNSBridgedClassOfCFClass(M.getSwiftModule(), - ConvTy.getSwiftRValueType())) { + ConvTy.getASTType())) { return true; } // All else failed - can't optimize this case @@ -515,12 +513,8 @@ SILInstruction *CastOptimizer::optimizeBridgedSwiftToObjCCast( } } - SmallVector Subs; - if (auto *Sig = Source->getAnyNominal()->getGenericSignature()) - Sig->getSubstitutions(SubMap, Subs); - // Generate a code to invoke the bridging function. - auto *NewAI = Builder.createApply(Loc, FnRef, Subs, Src, false); + auto *NewAI = Builder.createApply(Loc, FnRef, SubMap, Src, false); auto releaseSrc = [&](SILBuilder &Builder) { if (AddressOnlyType) { @@ -587,12 +581,12 @@ SILInstruction *CastOptimizer::optimizeBridgedSwiftToObjCCast( CastedValue = SILValue( Builder.createUnconditionalCheckedCast(Loc, NewAI, DestTy)); } - } else if (ConvTy.getSwiftRValueType() == + } else if (ConvTy.getASTType() == getNSBridgedClassOfCFClass(M.getSwiftModule(), - DestTy.getSwiftRValueType()) || - DestTy.getSwiftRValueType() == + DestTy.getASTType()) || + DestTy.getASTType() == getNSBridgedClassOfCFClass(M.getSwiftModule(), - ConvTy.getSwiftRValueType())) { + ConvTy.getASTType())) { // Handle NS <-> CF toll-free bridging here. CastedValue = SILValue(Builder.createUncheckedRefCast(Loc, NewAI, DestTy)); @@ -1063,7 +1057,7 @@ SILInstruction *CastOptimizer::optimizeCheckedCastAddrBranchInst( if (MI) { if (SuccessBB->getSinglePredecessorBlock() && canUseScalarCheckedCastInstructions( - Inst->getModule(), MI->getType().getSwiftRValueType(), + Inst->getModule(), MI->getType().getASTType(), Inst->getTargetType())) { SILBuilderWithScope B(Inst); auto NewI = B.createCheckedCastBranch( @@ -1173,7 +1167,7 @@ CastOptimizer::optimizeCheckedCastBranchInst(CheckedCastBranchInst *Inst) { return nullptr; // Get the metatype of this type. auto EMT = EmiTy.castTo(); - auto *MetaTy = MetatypeType::get(LoweredConcreteTy.getSwiftRValueType(), + auto *MetaTy = MetatypeType::get(LoweredConcreteTy.getASTType(), EMT->getRepresentation()); auto CanMetaTy = CanTypeWrapper(MetaTy); auto SILMetaTy = SILType::getPrimitiveObjectType(CanMetaTy); @@ -1299,8 +1293,8 @@ ValueBase *CastOptimizer::optimizeUnconditionalCheckedCastInst( SILBuilderWithScope Builder(Inst); // Try to apply the bridged casts optimizations - auto SourceType = LoweredSourceType.getSwiftRValueType(); - auto TargetType = LoweredTargetType.getSwiftRValueType(); + auto SourceType = LoweredSourceType.getASTType(); + auto TargetType = LoweredTargetType.getASTType(); auto Src = Inst->getOperand(); auto NewI = optimizeBridgedCasts(Inst, CastConsumptionKind::CopyOnSuccess, false, Src, SILValue(), SourceType, @@ -1327,8 +1321,8 @@ ValueBase *CastOptimizer::optimizeUnconditionalCheckedCastInst( auto Result = emitSuccessfulScalarUnconditionalCast( Builder, Mod.getSwiftModule(), Loc, Op, LoweredTargetType, - LoweredSourceType.getSwiftRValueType(), - LoweredTargetType.getSwiftRValueType(), Inst); + LoweredSourceType.getASTType(), + LoweredTargetType.getASTType(), Inst); if (!Result) { // No optimization was possible. diff --git a/lib/SILOptimizer/Utils/ConstantFolding.cpp b/lib/SILOptimizer/Utils/ConstantFolding.cpp index eb57919edb566..96f5d2be45ada 100644 --- a/lib/SILOptimizer/Utils/ConstantFolding.cpp +++ b/lib/SILOptimizer/Utils/ConstantFolding.cpp @@ -20,6 +20,7 @@ #include "swift/SILOptimizer/Utils/Local.h" #include "llvm/ADT/Statistic.h" #include "llvm/Support/Debug.h" +#include "llvm/ADT/APSInt.h" #define DEBUG_TYPE "constant-folding" @@ -859,6 +860,116 @@ constantFoldAndCheckIntegerConversions(BuiltinInst *BI, } +/// A utility function that extracts the literal text corresponding +/// to a given FloatLiteralInst the way it appears in the AST. +/// This function can be used on FloatLiteralInsts generated by the +/// constant folding phase. +/// If the extraction is successful, the function returns true and +/// 'fpStr' contains the literal the way it appears in the AST. +/// If the extraction is unsuccessful, e.g. because there is no AST +/// for the FloatLiteralInst, the function returns false. +template +static bool tryExtractLiteralText(FloatLiteralInst *flitInst, + SmallString &fpStr) { + + Expr *expr = flitInst->getLoc().getAsASTNode(); + if (!expr) + return false; + + // 'expr' may not be a FloatLiteralExpr since 'flitInst' could have been + // created by the ConstantFolder by folding floating-point constructor calls. + // So we iterate through the sequence of folded constructors if any, and + // try to extract the FloatLiteralExpr. + while (auto *callExpr = dyn_cast(expr)) { + if (callExpr->getNumArguments() != 1 || + !dyn_cast(callExpr->getFn())) + break; + + auto *tupleExpr = dyn_cast(callExpr->getArg()); + if (!tupleExpr) + break; + + expr = tupleExpr->getElement(0); + } + + auto *flitExpr = dyn_cast(expr); + if (!flitExpr) + return false; + + if (flitExpr->isNegative()) + fpStr += '-'; + fpStr += flitExpr->getDigitsText(); + return true; +} + +static SILValue foldFPToIntConversion(BuiltinInst *BI, + const BuiltinInfo &Builtin, Optional &ResultsInError) { + + assert(Builtin.ID == BuiltinValueKind::FPToSI || + Builtin.ID == BuiltinValueKind::FPToUI); + + OperandValueArrayRef Args = BI->getArguments(); + bool conversionToUnsigned = (Builtin.ID == BuiltinValueKind::FPToUI); + + auto *flitInst = dyn_cast(Args[0]); + if (!flitInst) + return nullptr; + APFloat fpVal = flitInst->getValue(); + auto *destTy = Builtin.Types[1]->castTo(); + + // Check non-negativeness of 'fpVal' for conversion to unsigned int. + if (conversionToUnsigned && fpVal.isNegative() && !fpVal.isZero()) { + // Stop folding and emit diagnostics if enabled. + if (ResultsInError.hasValue()) { + SILModule &M = BI->getModule(); + const ApplyExpr *CE = BI->getLoc().getAsASTNode(); + + SmallString<10> fpStr; + if (!tryExtractLiteralText(flitInst, fpStr)) + flitInst->getValue().toString(fpStr); + + diagnose(M.getASTContext(), BI->getLoc().getSourceLoc(), + diag::negative_fp_literal_overflow_unsigned, fpStr, + CE ? CE->getType() : destTy, + CE ? false : conversionToUnsigned); + ResultsInError = Optional(true); + } + return nullptr; + } + + llvm::APSInt resInt(destTy->getFixedWidth(), conversionToUnsigned); + bool isExact = false; + APFloat::opStatus status = + fpVal.convertToInteger(resInt, APFloat::rmTowardZero, &isExact); + + if (status & APFloat::opStatus::opInvalidOp) { + // Stop folding and emit diagnostics if enabled. + if (ResultsInError.hasValue()) { + SILModule &M = BI->getModule(); + const ApplyExpr *CE = BI->getLoc().getAsASTNode(); + + SmallString<10> fpStr; + if (!tryExtractLiteralText(flitInst, fpStr)) + flitInst->getValue().toString(fpStr); + + diagnose(M.getASTContext(), BI->getLoc().getSourceLoc(), + diag::float_to_int_overflow, fpStr, + CE ? CE->getType() : destTy, + CE ? CE->isImplicit() : false); + ResultsInError = Optional(true); + } + return nullptr; + } + + if (status != APFloat::opStatus::opOK && + status != APFloat::opStatus::opInexact) { + return nullptr; + } + // The call to the builtin should be replaced with the constant value. + SILBuilderWithScope B(BI); + return B.createIntegerLiteral(BI->getLoc(), BI->getType(), resInt); +} + static SILValue constantFoldBuiltin(BuiltinInst *BI, Optional &ResultsInError) { const IntrinsicInfo &Intrinsic = BI->getIntrinsicInfo(); @@ -989,6 +1100,12 @@ case BuiltinValueKind::id: return B.createFloatLiteral(Loc, BI->getType(), TruncVal); } + // Conversions from floating point to integer, + case BuiltinValueKind::FPToSI: + case BuiltinValueKind::FPToUI: { + return foldFPToIntConversion(BI, Builtin, ResultsInError); + } + case BuiltinValueKind::AssumeNonNegative: { auto *V = dyn_cast(Args[0]); if (!V) diff --git a/lib/SILOptimizer/Utils/Devirtualize.cpp b/lib/SILOptimizer/Utils/Devirtualize.cpp index e71d1566cc514..068787800c545 100644 --- a/lib/SILOptimizer/Utils/Devirtualize.cpp +++ b/lib/SILOptimizer/Utils/Devirtualize.cpp @@ -226,7 +226,7 @@ SILValue swift::getInstanceWithExactDynamicType(SILValue S, SILModule &M, S = stripCasts(S); if (isa(S) || isa(S)) { - if (S->getType().getSwiftRValueType()->hasDynamicSelfType()) + if (S->getType().getASTType()->hasDynamicSelfType()) return SILValue(); return S; } @@ -407,17 +407,16 @@ swift::getExactDynamicTypeOfUnderlyingObject(SILValue S, SILModule &M, // Start with the substitutions from the apply. // Try to propagate them to find out the real substitutions required // to invoke the method. -static void +static SubstitutionMap getSubstitutionsForCallee(SILModule &M, CanSILFunctionType baseCalleeType, CanType derivedSelfType, - FullApplySite AI, - SmallVectorImpl &newSubs) { + FullApplySite AI) { // If the base method is not polymorphic, no substitutions are required, // even if we originally had substitutions for calling the derived method. if (!baseCalleeType->isPolymorphic()) - return; + return SubstitutionMap(); // Add any generic substitutions for the base class. Type baseSelfType = baseCalleeType->getSelfParameter().getType(); @@ -442,9 +441,7 @@ getSubstitutionsForCallee(SILModule &M, M.getSwiftModule(), baseClassDecl); } - SubstitutionMap origSubMap; - if (auto origCalleeSig = AI.getOrigCalleeType()->getGenericSignature()) - origSubMap = origCalleeSig->getSubstitutionMap(AI.getSubstitutions()); + SubstitutionMap origSubMap = AI.getSubstitutionMap(); Type calleeSelfType = AI.getOrigCalleeType()->getSelfParameter().getType(); if (auto metatypeType = calleeSelfType->getAs()) @@ -460,16 +457,13 @@ getSubstitutionsForCallee(SILModule &M, auto baseCalleeSig = baseCalleeType->getGenericSignature(); - auto subMap = + return SubstitutionMap::combineSubstitutionMaps(baseSubMap, origSubMap, CombineSubstitutionMaps::AtDepth, baseDepth, origDepth, baseCalleeSig); - - // Build the new substitutions using the base method signature. - baseCalleeSig->getSubstitutions(subMap, newSubs); } SILFunction *swift::getTargetClassMethod(SILModule &M, @@ -568,10 +562,10 @@ DevirtualizationResult swift::devirtualizeClassMethod(FullApplySite AI, CanSILFunctionType GenCalleeType = F->getLoweredFunctionType(); - SmallVector Subs; - getSubstitutionsForCallee(Mod, GenCalleeType, - ClassOrMetatypeType.getSwiftRValueType(), - AI, Subs); + SubstitutionMap Subs = + getSubstitutionsForCallee(Mod, GenCalleeType, + ClassOrMetatypeType.getASTType(), + AI); CanSILFunctionType SubstCalleeType = GenCalleeType; if (GenCalleeType->isPolymorphic()) SubstCalleeType = GenCalleeType->substGenericArgs(Mod, Subs); @@ -769,7 +763,7 @@ swift::tryDevirtualizeClassMethod(FullApplySite AI, SILValue ClassInstance, /// \param conformanceRef The (possibly-specialized) conformance /// \param requirementSig The generic signature of the requirement /// \param witnessThunkSig The generic signature of the witness method -/// \param origSubs The substitutions from the call instruction +/// \param origSubMap The substitutions from the call instruction /// \param isDefaultWitness True if this is a default witness method /// \param classWitness The ClassDecl if this is a class witness method static SubstitutionMap @@ -778,15 +772,13 @@ getWitnessMethodSubstitutions( ProtocolConformanceRef conformanceRef, GenericSignature *requirementSig, GenericSignature *witnessThunkSig, - SubstitutionList origSubs, + SubstitutionMap origSubMap, bool isDefaultWitness, ClassDecl *classWitness) { if (witnessThunkSig == nullptr) return SubstitutionMap(); - auto origSubMap = requirementSig->getSubstitutionMap(origSubs); - if (isDefaultWitness) return origSubMap; @@ -845,7 +837,7 @@ getWitnessMethodSubstitutions(SILModule &Module, ApplySite AI, SILFunction *F, auto requirementSig = AI.getOrigCalleeType()->getGenericSignature(); auto witnessThunkSig = witnessFnTy->getGenericSignature(); - SubstitutionList origSubs = AI.getSubstitutions(); + SubstitutionMap origSubs = AI.getSubstitutionMap(); auto *mod = Module.getSwiftModule(); bool isDefaultWitness = @@ -905,13 +897,9 @@ devirtualizeWitnessMethod(ApplySite AI, SILFunction *F, ApplySite SAI; - SmallVector NewSubs; - if (auto GenericSig = CalleeCanType->getGenericSignature()) - GenericSig->getSubstitutions(SubMap, NewSubs); - SILValue ResultValue; if (auto *A = dyn_cast(AI)) { - auto *NewAI = Builder.createApply(Loc, FRI, NewSubs, Arguments, + auto *NewAI = Builder.createApply(Loc, FRI, SubMap, Arguments, A->isNonThrowing()); // Check if any casting is required for the return value. ResultValue = castValueToABICompatibleType(&Builder, Loc, NewAI, @@ -919,15 +907,13 @@ devirtualizeWitnessMethod(ApplySite AI, SILFunction *F, SAI = NewAI; } if (auto *TAI = dyn_cast(AI)) - SAI = Builder.createTryApply(Loc, FRI, NewSubs, Arguments, + SAI = Builder.createTryApply(Loc, FRI, SubMap, Arguments, TAI->getNormalBB(), TAI->getErrorBB()); if (auto *PAI = dyn_cast(AI)) { - auto PartialApplyConvention = PAI->getType() - .getSwiftRValueType() - ->getAs() + auto PartialApplyConvention = PAI->getType().getAs() ->getCalleeConvention(); auto *NewPAI = Builder.createPartialApply( - Loc, FRI, NewSubs, Arguments, PartialApplyConvention); + Loc, FRI, SubMap, Arguments, PartialApplyConvention); // Check if any casting is required for the return value. ResultValue = castValueToABICompatibleType( &Builder, Loc, NewPAI, NewPAI->getType(), PAI->getType()); diff --git a/lib/SILOptimizer/Utils/FunctionSignatureOptUtils.cpp b/lib/SILOptimizer/Utils/FunctionSignatureOptUtils.cpp deleted file mode 100644 index d6b8661246d72..0000000000000 --- a/lib/SILOptimizer/Utils/FunctionSignatureOptUtils.cpp +++ /dev/null @@ -1,114 +0,0 @@ -//===--- FunctionSignatureOptUtils.cpp ------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -#include "swift/SIL/SILBasicBlock.h" -#include "swift/SIL/SILFunction.h" -#include "swift/SIL/SILInstruction.h" -#include "swift/SIL/SILModule.h" -#include "swift/SIL/SILValue.h" -#include "swift/SIL/DebugUtils.h" -#include "swift/SILOptimizer/Utils/FunctionSignatureOptUtils.h" -#include "swift/SILOptimizer/Utils/Local.h" -#include "llvm/Support/CommandLine.h" - -using namespace swift; - -/// Set to true to enable the support for partial specialization. -llvm::cl::opt FSOEnableGenerics( - "sil-fso-enable-generics", llvm::cl::init(true), - llvm::cl::desc("Support function signature optimization " - "of generic functions")); - -bool swift::hasNonTrivialNonDebugUse(SILArgument *Arg) { - llvm::SmallVector Worklist; - llvm::SmallPtrSet SeenInsts; - - for (Operand *I : getNonDebugUses(SILValue(Arg))) - Worklist.push_back(I->getUser()); - - while (!Worklist.empty()) { - SILInstruction *U = Worklist.pop_back_val(); - if (!SeenInsts.insert(U).second) - continue; - - // If U is a terminator inst, return false. - if (isa(U)) - return true; - - // If U has side effects... - if (U->mayHaveSideEffects()) - return true; - - // Otherwise add all non-debug uses of I to the worklist. - for (auto result : U->getResults()) { - for (Operand *I : getNonDebugUses(result)) - Worklist.push_back(I->getUser()); - } - } - return false; -} - -static bool isSpecializableRepresentation(SILFunctionTypeRepresentation Rep, - bool OptForPartialApply) { - switch (Rep) { - case SILFunctionTypeRepresentation::Method: - case SILFunctionTypeRepresentation::Closure: - case SILFunctionTypeRepresentation::Thin: - case SILFunctionTypeRepresentation::Thick: - case SILFunctionTypeRepresentation::CFunctionPointer: - return true; - case SILFunctionTypeRepresentation::WitnessMethod: - return OptForPartialApply; - case SILFunctionTypeRepresentation::ObjCMethod: - case SILFunctionTypeRepresentation::Block: - return false; - } - - llvm_unreachable("Unhandled SILFunctionTypeRepresentation in switch."); -} - -/// Returns true if F is a function which the pass know show to specialize -/// function signatures for. -bool swift::canSpecializeFunction(SILFunction *F, - const CallerAnalysis::FunctionInfo *FuncInfo, - bool OptForPartialApply) { - // Do not specialize the signature of SILFunctions that are external - // declarations since there is no body to optimize. - if (F->isExternalDeclaration()) - return false; - - // For now ignore functions with indirect results. - if (F->getConventions().hasIndirectSILResults()) - return false; - - // Do not specialize the signature of always inline functions. We - // will just inline them and specialize each one of the individual - // functions that these sorts of functions are inlined into. - // It is OK to specialize always inline functions if they are - // used by partial_apply instructions. - assert(!OptForPartialApply || FuncInfo); - if (F->getInlineStrategy() == Inline_t::AlwaysInline && - (!OptForPartialApply || !FuncInfo->getMinPartialAppliedArgs())) - return false; - - // For now ignore generic functions to keep things simple... - if (!FSOEnableGenerics && F->getLoweredFunctionType()->isPolymorphic()) - return false; - - // Make sure F has a linkage that we can optimize. - if (!isSpecializableRepresentation(F->getRepresentation(), - OptForPartialApply)) - return false; - - return true; -} - diff --git a/lib/SILOptimizer/Utils/Generics.cpp b/lib/SILOptimizer/Utils/Generics.cpp index 43a51626d329d..4b0e12d6db596 100644 --- a/lib/SILOptimizer/Utils/Generics.cpp +++ b/lib/SILOptimizer/Utils/Generics.cpp @@ -232,19 +232,21 @@ class TypeComparator : public TypeMatcher { } // anonymous namespace -/// Checks if a second substitution list is an expanded version of -/// the first substitution list. +/// Checks if a second substitution map is an expanded version of +/// the first substitution map. /// This is the case if at least one of the substitution type in Subs2 is /// "bigger" than the corresponding substitution type in Subs1. /// Type T2 is "smaller" than type T1 if T2 is structurally contained in T1. -static bool growingSubstitutions(SubstitutionList Subs1, - SubstitutionList Subs2) { - assert(Subs1.size() == Subs2.size()); +static bool growingSubstitutions(SubstitutionMap Subs1, + SubstitutionMap Subs2) { + auto Replacements1 = Subs1.getReplacementTypes(); + auto Replacements2 = Subs2.getReplacementTypes(); + assert(Replacements1.size() == Replacements2.size()); TypeComparator TypeCmp; // Perform component-wise comparisions for substitutions. - for (unsigned idx = 0, e = Subs1.size(); idx < e; ++idx) { - auto Type1 = Subs1[idx].getReplacement()->getCanonicalType(); - auto Type2 = Subs2[idx].getReplacement()->getCanonicalType(); + for (unsigned idx : indices(Replacements1)) { + auto Type1 = Replacements1[idx]->getCanonicalType(); + auto Type2 = Replacements2[idx]->getCanonicalType(); // If types are the same, the substitution type does not grow. if (TypeCmp.isEqual(Type2, Type1)) continue; @@ -255,7 +257,7 @@ static bool growingSubstitutions(SubstitutionList Subs1, if (TypeCmp.isPartiallyContainedIn(Type1, Type2)) { DEBUG(llvm::dbgs() << "Type:\n"; Type1.dump(); llvm::dbgs() << "is (partially) contained in type:\n"; Type2.dump(); - llvm::dbgs() << "SubstitutionList[" << idx + llvm::dbgs() << "Replacements[" << idx << "] has got bigger since last time.\n"); return true; } @@ -288,9 +290,8 @@ static bool createsInfiniteSpecializationLoop(ApplySite Apply) { << "Caller: " << Caller->getName() << "\n" << "Callee: " << Callee->getName() << "\n"; llvm::dbgs() << "Substitutions:\n"; - for (auto Sub: Apply.getSubstitutions()) { - Sub.getReplacement()->dump(); - }); + Apply.getSubstitutionMap().dump(llvm::dbgs()); + ); auto *CurSpecializationInfo = Apply.getSpecializationInfo(); if (CurSpecializationInfo) { @@ -307,8 +308,10 @@ static bool createsInfiniteSpecializationLoop(ApplySite Apply) { << "Parent: " << CurSpecializationInfo->getParent()->getName() << "\n"; llvm::dbgs() << "Substitutions:\n"; - for (auto Sub: CurSpecializationInfo->getSubstitutions()) { - Sub.getReplacement()->dump(); + for (auto Replacement : + CurSpecializationInfo->getSubstitutions() + .getReplacementTypes()) { + Replacement->dump(); }); if (CurSpecializationInfo->getParent() == GenericFunc) { @@ -316,7 +319,7 @@ static bool createsInfiniteSpecializationLoop(ApplySite Apply) { // Consider if components of the substitution list gets bigger compared to // the previously seen specialization of the same generic function. if (growingSubstitutions(CurSpecializationInfo->getSubstitutions(), - Apply.getSubstitutions())) { + Apply.getSubstitutionMap())) { DEBUG(llvm::dbgs() << "Found a generic specialization loop!\n"); return true; } @@ -344,11 +347,11 @@ static bool createsInfiniteSpecializationLoop(ApplySite Apply) { // ============================================================================= static bool shouldNotSpecializeCallee(SILFunction *Callee, - SubstitutionList Subs = {}) { + SubstitutionMap Subs = {}) { if (Callee->hasSemanticsAttr("optimize.sil.specialize.generic.never")) return true; - if (!Subs.empty() && + if (Subs.hasAnySubstitutableParams() && Callee->hasSemanticsAttr("optimize.sil.specialize.generic.partial.never")) return true; @@ -360,36 +363,32 @@ static bool shouldNotSpecializeCallee(SILFunction *Callee, /// Returns false, if the current function cannot be specialized. /// Returns true otherwise. bool ReabstractionInfo::prepareAndCheck(ApplySite Apply, SILFunction *Callee, - SubstitutionList ParamSubs, + SubstitutionMap ParamSubs, OptRemark::Emitter *ORE) { if (shouldNotSpecializeCallee(Callee)) return false; SpecializedGenericEnv = nullptr; SpecializedGenericSig = nullptr; - CalleeParamSubs = ParamSubs; auto CalleeGenericSig = Callee->getLoweredFunctionType()->getGenericSignature(); auto CalleeGenericEnv = Callee->getGenericEnvironment(); this->Callee = Callee; this->Apply = Apply; - SubstitutionMap InterfaceSubs; - // Get the original substitution map. - if (CalleeGenericSig) - InterfaceSubs = CalleeGenericSig->getSubstitutionMap(ParamSubs); + CalleeParamSubMap = ParamSubs; using namespace OptRemark; // We do not support partial specialization. - if (!EnablePartialSpecialization && InterfaceSubs.hasArchetypes()) { + if (!EnablePartialSpecialization && CalleeParamSubMap.hasArchetypes()) { DEBUG(llvm::dbgs() << " Partial specialization is not supported.\n"); - DEBUG(for (auto Sub : ParamSubs) { Sub.dump(); }); + DEBUG(ParamSubs.dump(llvm::dbgs())); return false; } // Perform some checks to see if we need to bail. - if (InterfaceSubs.hasDynamicSelf()) { + if (CalleeParamSubMap.hasDynamicSelf()) { REMARK_OR_DEBUG(ORE, [&]() { return RemarkMissed("DynamicSelf", *Apply.getInstruction()) << IndentDebug(4) << "Cannot specialize with dynamic self"; @@ -400,8 +399,7 @@ bool ReabstractionInfo::prepareAndCheck(ApplySite Apply, SILFunction *Callee, // Check if the substitution contains any generic types that are too deep. // If this is the case, bail to avoid the explosion in the number of // generated specializations. - for (auto Sub : ParamSubs) { - auto Replacement = Sub.getReplacement(); + for (auto Replacement : ParamSubs.getReplacementTypes()) { if (isTypeTooComplex(Replacement)) { REMARK_OR_DEBUG(ORE, [&]() { return RemarkMissed("TypeTooDeep", *Apply.getInstruction()) @@ -421,7 +419,7 @@ bool ReabstractionInfo::prepareAndCheck(ApplySite Apply, SILFunction *Callee, for (auto GP : CalleeGenericSig->getSubstitutableParams()) { // Check only the substitutions for the generic parameters. // Ignore any dependent types, etc. - auto Replacement = Type(GP).subst(InterfaceSubs); + auto Replacement = Type(GP).subst(CalleeParamSubMap); if (!Replacement->is()) HasNonArchetypeGenericParams = true; @@ -454,18 +452,14 @@ bool ReabstractionInfo::prepareAndCheck(ApplySite Apply, SILFunction *Callee, if (!HasConcreteGenericParams && !SupportGenericSubstitutions) { DEBUG(llvm::dbgs() << " Partial specialization is not supported if " "all substitutions are generic.\n"); - DEBUG(for (auto Sub : ParamSubs) { - Sub.dump(); - }); + DEBUG(ParamSubs.dump(llvm::dbgs())); return false; } if (!HasNonArchetypeGenericParams && !HasConcreteGenericParams) { DEBUG(llvm::dbgs() << " Partial specialization is not supported if " "all substitutions are archetypes.\n"); - DEBUG(for (auto Sub : ParamSubs) { - Sub.dump(); - }); + DEBUG(ParamSubs.dump(llvm::dbgs())); return false; } @@ -501,13 +495,13 @@ bool ReabstractionInfo::prepareAndCheck(ApplySite Apply, SILFunction *Callee, } bool ReabstractionInfo::canBeSpecialized(ApplySite Apply, SILFunction *Callee, - SubstitutionList ParamSubs) { + SubstitutionMap ParamSubs) { ReabstractionInfo ReInfo; return ReInfo.prepareAndCheck(Apply, Callee, ParamSubs); } ReabstractionInfo::ReabstractionInfo(ApplySite Apply, SILFunction *Callee, - ArrayRef ParamSubs, + SubstitutionMap ParamSubs, bool ConvertIndirectToDirect, OptRemark::Emitter *ORE) { if (!prepareAndCheck(Apply, Callee, ParamSubs, ORE)) @@ -540,18 +534,18 @@ ReabstractionInfo::ReabstractionInfo(ApplySite Apply, SILFunction *Callee, auto SpecializedSubstFnTy = SpecializedFnTy; if (SpecializedFnTy->isPolymorphic() && - !getCallerParamSubstitutions().empty()) { + !getCallerParamSubstitutionMap().empty()) { auto CalleeFnTy = Callee->getLoweredFunctionType(); assert(CalleeFnTy->isPolymorphic()); auto CalleeSubstFnTy = CalleeFnTy->substGenericArgs( - Callee->getModule(), getCalleeParamSubstitutions()); + Callee->getModule(), getCalleeParamSubstitutionMap()); assert(!CalleeSubstFnTy->isPolymorphic() && "Substituted callee type should not be polymorphic"); assert(!CalleeSubstFnTy->hasTypeParameter() && "Substituted callee type should not have type parameters"); SpecializedSubstFnTy = SpecializedFnTy->substGenericArgs( - Callee->getModule(), getCallerParamSubstitutions()); + Callee->getModule(), getCallerParamSubstitutionMap()); assert(!SpecializedSubstFnTy->isPolymorphic() && "Substituted callee type should not be polymorphic"); @@ -564,18 +558,12 @@ ReabstractionInfo::ReabstractionInfo(ApplySite Apply, SILFunction *Callee, if (SpecializedSubstFnTy != SpecializedCalleeSubstFnTy) { llvm::dbgs() << "SpecializedFnTy:\n" << SpecializedFnTy << "\n"; llvm::dbgs() << "SpecializedSubstFnTy:\n" << SpecializedSubstFnTy << "\n"; - for (auto Sub : getCallerParamSubstitutions()) { - llvm::dbgs() << "Sub:\n"; - Sub.dump(); - } + getCallerParamSubstitutionMap().getCanonical().dump(llvm::dbgs()); llvm::dbgs() << "\n\n"; llvm::dbgs() << "CalleeFnTy:\n" << CalleeFnTy << "\n"; llvm::dbgs() << "SpecializedCalleeSubstFnTy:\n" << SpecializedCalleeSubstFnTy << "\n"; - for (auto Sub : ParamSubs) { - llvm::dbgs() << "Sub:\n"; - Sub.dump(); - } + ParamSubs.getCanonical().dump(llvm::dbgs()); llvm::dbgs() << "\n\n"; assert(SpecializedSubstFnTy == SpecializedCalleeSubstFnTy && "Substituted function types should be the same"); @@ -605,20 +593,13 @@ ReabstractionInfo::ReabstractionInfo(ApplySite Apply, SILFunction *Callee, llvm::dbgs() << "Callee generic function type:\n" << Callee->getLoweredFunctionType() << "\n\n"; llvm::dbgs() << "Callee's call substitution:\n"; - for (auto Sub : getCalleeParamSubstitutions()) { - llvm::dbgs() << "Sub:\n"; - Sub.dump(); - llvm::dbgs() << "\n"; - } + getCalleeParamSubstitutionMap().getCanonical().dump(llvm::dbgs()); llvm::dbgs() << "Partially specialized generic function type:\n" << getSpecializedType() << "\n\n"; llvm::dbgs() << "\nSpecialization call substitution:\n"; - for (auto Sub : getCallerParamSubstitutions()) { - llvm::dbgs() << "Sub:\n"; - Sub.dump(); - llvm::dbgs() << "\n"; - }); + getCallerParamSubstitutionMap().getCanonical().dump(llvm::dbgs()); + ); } } @@ -627,11 +608,11 @@ bool ReabstractionInfo::canBeSpecialized() const { } bool ReabstractionInfo::isFullSpecialization() const { - return !hasArchetypes(getCalleeParamSubstitutions()); + return !getCalleeParamSubstitutionMap().hasArchetypes(); } bool ReabstractionInfo::isPartialSpecialization() const { - return hasArchetypes(getCalleeParamSubstitutions()); + return getCalleeParamSubstitutionMap().hasArchetypes(); } void ReabstractionInfo::createSubstitutedAndSpecializedTypes() { @@ -709,7 +690,7 @@ void ReabstractionInfo::createSubstitutedAndSpecializedTypes() { /// Create a new substituted type with the updated signature. CanSILFunctionType ReabstractionInfo::createSubstitutedType(SILFunction *OrigF, - const SubstitutionMap &SubstMap, + SubstitutionMap SubstMap, bool HasUnboundGenericParams) { auto &M = OrigF->getModule(); if ((SpecializedGenericSig && @@ -839,25 +820,11 @@ getGenericEnvironmentAndSignatureWithRequirements( return { NewGenEnv, NewGenSig }; } -/// Perform some sanity checks on the newly formed substitution lists. -static void verifySubstitutionList(SubstitutionList Subs, StringRef Name) { - DEBUG(llvm::dbgs() << "\nSubstitutions for " << Name << "\n"; - for (auto Sub : Subs) { - Sub.getReplacement()->dump(); - }); -#ifndef NDEBUG - for (auto Sub : Subs) { - assert(!Sub.getReplacement()->hasError() && - "There should be no error types in substitutions"); - } -#endif -} - /// This is a fast path for full specializations. /// There is no need to form a new generic signature in such cases, /// because the specialized function will be non-generic. void ReabstractionInfo::performFullSpecializationPreparation( - SILFunction *Callee, ArrayRef ParamSubs) { + SILFunction *Callee, SubstitutionMap ParamSubs) { assert((!EnablePartialSpecialization || !HasUnboundGenericParams) && "Only full specializations are handled here"); @@ -865,17 +832,12 @@ void ReabstractionInfo::performFullSpecializationPreparation( this->Callee = Callee; - auto CalleeGenericSig = - Callee->getLoweredFunctionType()->getGenericSignature(); - // Get the original substitution map. - auto CalleeInterfaceToCallerArchetypeMap = - CalleeGenericSig->getSubstitutionMap(ParamSubs); + ClonerParamSubMap = ParamSubs; SubstitutedType = Callee->getLoweredFunctionType()->substGenericArgs( - M, CalleeInterfaceToCallerArchetypeMap); - ClonerParamSubs = CalleeParamSubs; - CallerParamSubs = {}; + M, ClonerParamSubMap); + CallerParamSubMap = {}; createSubstitutedAndSpecializedTypes(); } @@ -1017,7 +979,6 @@ static void collectRequirements(ArchetypeType *Archetype, GenericSignature *Sig, /// really used? static bool shouldBePartiallySpecialized(Type Replacement, - ArrayRef Conformances, GenericSignature *Sig, GenericEnvironment *Env) { // If replacement is a concrete type, this substitution // should participate. @@ -1063,8 +1024,8 @@ shouldBePartiallySpecialized(Type Replacement, if (OptimizeGenericSubstitutions) { // Is it an unconstrained generic parameter? - if (Conformances.empty()) { - if (Replacement->is()) { + if (auto Archetype = Replacement->getAs()) { + if (Archetype->getConformsTo().empty()) { // TODO: If Replacement add a new layout constraint, then // it may be still useful to perform the partial specialization. return false; @@ -1105,7 +1066,7 @@ class FunctionSignaturePartialSpecializer { SubstitutionMap CallerInterfaceToSpecializedInterfaceMap; /// Maps callee's interface types to caller's contextual types. - /// It is computed from the original SubstitutionList. + /// It is computed from the original substitutions. SubstitutionMap CalleeInterfaceToCallerArchetypeMap; /// Maps callee's interface types to specialized functions interface types. @@ -1167,7 +1128,7 @@ class FunctionSignaturePartialSpecializer { /// Take into account only those archetypes that occur in the /// substitutions of generic parameters which will be partially /// specialized. Ignore all others. - void collectUsedCallerArchetypes(SubstitutionList ParamSubs); + void collectUsedCallerArchetypes(SubstitutionMap ParamSubs); /// Create a new generic parameter. GenericTypeParamType *createGenericParam(); @@ -1178,15 +1139,14 @@ class FunctionSignaturePartialSpecializer { GenericEnvironment *CallerGenericEnv, GenericSignature *CalleeGenericSig, GenericEnvironment *CalleeGenericEnv, - SubstitutionList ParamSubs) + SubstitutionMap ParamSubs) : CallerGenericSig(CallerGenericSig), CallerGenericEnv(CallerGenericEnv), CalleeGenericSig(CalleeGenericSig), CalleeGenericEnv(CalleeGenericEnv), M(M), SM(M.getSwiftModule()), Ctx(M.getASTContext()), Builder(Ctx) { SpecializedGenericSig = nullptr; SpecializedGenericEnv = nullptr; - CalleeInterfaceToCallerArchetypeMap = - CalleeGenericSig->getSubstitutionMap(ParamSubs); + CalleeInterfaceToCallerArchetypeMap = ParamSubs; } /// This constructor is used by when processing @_specialize. @@ -1233,14 +1193,13 @@ class FunctionSignaturePartialSpecializer { return SpecializedGenericEnv; } - void createSpecializedGenericSignature(SubstitutionList ParamSubs); + void createSpecializedGenericSignature(SubstitutionMap ParamSubs); void createSpecializedGenericSignatureWithNonGenericSubs(); - void computeClonerParamSubs(SubstitutionList &ClonerParamSubs); + SubstitutionMap computeClonerParamSubs(); - void computeCallerParamSubs(GenericSignature *SpecializedGenericSig, - SubstitutionList &CallerParamSubs); + SubstitutionMap getCallerParamSubs(); void computeCallerInterfaceSubs(SubstitutionMap &CallerInterfaceSubs); }; @@ -1257,16 +1216,15 @@ FunctionSignaturePartialSpecializer::createGenericParam() { /// Collect all used caller's archetypes from all the substitutions. void FunctionSignaturePartialSpecializer::collectUsedCallerArchetypes( - SubstitutionList ParamSubs) { - for (auto Sub : ParamSubs) { - auto Replacement = Sub.getReplacement(); + SubstitutionMap ParamSubs) { + for (auto Replacement : ParamSubs.getReplacementTypes()) { if (!Replacement->hasArchetype()) continue; // If the substitution will not be performed in the specialized // function, there is no need to check for any archetypes inside // the replacement. - if (!shouldBePartiallySpecialized(Replacement, Sub.getConformances(), + if (!shouldBePartiallySpecialized(Replacement, CallerGenericSig, CallerGenericEnv)) continue; @@ -1390,7 +1348,7 @@ void FunctionSignaturePartialSpecializer:: DEBUG(llvm::dbgs() << "Replacement found:\n"; Replacement->dump()); bool ShouldSpecializeGP = shouldBePartiallySpecialized( - Replacement, {}, CallerGenericSig, CallerGenericEnv); + Replacement, CallerGenericSig, CallerGenericEnv); if (ShouldSpecializeGP) { DEBUG(llvm::dbgs() << "Should be partially specialized.\n"); @@ -1526,9 +1484,8 @@ FunctionSignaturePartialSpecializer:: return { GenEnv, GenSig }; } -void FunctionSignaturePartialSpecializer::computeClonerParamSubs( - SubstitutionList &ClonerParamSubs) { - auto SubMap = CalleeGenericSig->getSubstitutionMap( +SubstitutionMap FunctionSignaturePartialSpecializer::computeClonerParamSubs() { + return CalleeGenericSig->getSubstitutionMap( [&](SubstitutableType *type) -> Type { DEBUG(llvm::dbgs() << "\ngetSubstitution for ClonerParamSubs:\n" << Type(type) << "\n" @@ -1540,23 +1497,10 @@ void FunctionSignaturePartialSpecializer::computeClonerParamSubs( SpecializedInterfaceTy); }, LookUpConformanceInSignature(*SpecializedGenericSig)); - - SmallVector List; - CalleeGenericSig->getSubstitutions(SubMap, List); - ClonerParamSubs = Ctx.AllocateCopy(List); - verifySubstitutionList(ClonerParamSubs, "ClonerParamSubs"); } -void FunctionSignaturePartialSpecializer::computeCallerParamSubs( - GenericSignature *SpecializedGenericSig, - SubstitutionList &CallerParamSubs) { - SmallVector List; - - SpecializedGenericSig->getSubstitutions( - SpecializedInterfaceToCallerArchetypeMap, List); - - CallerParamSubs = Ctx.AllocateCopy(List); - verifySubstitutionList(CallerParamSubs, "CallerParamSubs"); +SubstitutionMap FunctionSignaturePartialSpecializer::getCallerParamSubs() { + return SpecializedInterfaceToCallerArchetypeMap; } void FunctionSignaturePartialSpecializer::computeCallerInterfaceSubs( @@ -1613,7 +1557,7 @@ void FunctionSignaturePartialSpecializer:: } void FunctionSignaturePartialSpecializer::createSpecializedGenericSignature( - SubstitutionList ParamSubs) { + SubstitutionMap ParamSubs) { // Collect all used caller's archetypes from all the substitutions. collectUsedCallerArchetypes(ParamSubs); @@ -1674,7 +1618,7 @@ void FunctionSignaturePartialSpecializer::createSpecializedGenericSignature( /// re-formulating them in terms of the newly introduced generic parameters. void ReabstractionInfo::performPartialSpecializationPreparation( SILFunction *Caller, SILFunction *Callee, - ArrayRef ParamSubs) { + SubstitutionMap ParamSubs) { SILModule &M = Callee->getModule(); // Caller is the SILFunction containing the apply instruction. @@ -1724,14 +1668,15 @@ void ReabstractionInfo::finishPartialSpecializationPreparation( } // Create substitution lists for the caller and cloner. - FSPS.computeClonerParamSubs(ClonerParamSubs); - FSPS.computeCallerParamSubs(SpecializedGenericSig, CallerParamSubs); + ClonerParamSubMap = FSPS.computeClonerParamSubs(); + CallerParamSubMap = FSPS.getCallerParamSubs(); + // Create a substitution map for the caller interface substitutions. FSPS.computeCallerInterfaceSubs(CallerInterfaceSubs); - if (CalleeParamSubs.empty()) { + if (CalleeParamSubMap.empty()) { // It can happen if there is no caller or it is an eager specialization. - CalleeParamSubs = CallerParamSubs; + CalleeParamSubMap = CallerParamSubMap; } HasUnboundGenericParams = @@ -1806,7 +1751,7 @@ ReabstractionInfo::ReabstractionInfo(SILFunction *Callee, // ============================================================================= GenericFuncSpecializer::GenericFuncSpecializer(SILFunction *GenericFunc, - SubstitutionList ParamSubs, + SubstitutionMap ParamSubs, IsSerialized_t Serialized, const ReabstractionInfo &ReInfo) : M(GenericFunc->getModule()), @@ -1876,7 +1821,7 @@ SILFunction *GenericFuncSpecializer::tryCreateSpecialization() { GenericFunc, Serialized, ReInfo, // Use these substitutions inside the new specialized function being // created. - ReInfo.getClonerParamSubstitutions(), + ReInfo.getClonerParamSubstitutionMap(), ClonedName); assert((SpecializedF->getLoweredFunctionType()->isPolymorphic() && SpecializedF->getGenericEnvironment()) || @@ -1887,8 +1832,8 @@ SILFunction *GenericFuncSpecializer::tryCreateSpecialization() { linkSpecialization(M, SpecializedF); // Store the meta-information about how this specialization was created. auto *Caller = ReInfo.getApply() ? ReInfo.getApply().getFunction() : nullptr; - SubstitutionList Subs = Caller ? ReInfo.getApply().getSubstitutions() - : ReInfo.getClonerParamSubstitutions(); + SubstitutionMap Subs = Caller ? ReInfo.getApply().getSubstitutionMap() + : ReInfo.getClonerParamSubstitutionMap(); SpecializedF->setSpecializationInfo( GenericSpecializationInformation::create(Caller, GenericFunc, Subs)); return SpecializedF; @@ -1959,7 +1904,7 @@ static void prepareCallArguments(ApplySite AI, SILBuilder &Builder, /// Return a substituted callee function type. static CanSILFunctionType -getCalleeSubstFunctionType(SILValue Callee, SubstitutionList Subs) { +getCalleeSubstFunctionType(SILValue Callee, SubstitutionMap Subs) { // Create a substituted callee type. auto CanFnTy = Callee->getType().castTo(); return CanFnTy->substGenericArgs(*Callee->getModule(), Subs); @@ -1978,16 +1923,9 @@ static ApplySite replaceWithSpecializedCallee(ApplySite AI, prepareCallArguments(AI, Builder, ReInfo, Arguments, StoreResultTo); // Create a substituted callee type. - ArrayRef Subs; + SubstitutionMap Subs; if (ReInfo.getSpecializedType()->isPolymorphic()) { - Subs = ReInfo.getCallerParamSubstitutions(); - if (auto FRI = dyn_cast(Callee)) { - assert(Subs.size() == - FRI->getReferencedFunction() - ->getLoweredFunctionType() - ->getGenericSignature() - ->getSubstitutionListSize()); - } + Subs = ReInfo.getCallerParamSubstitutionMap(); } auto CalleeSubstFnTy = getCalleeSubstFunctionType(Callee, Subs); @@ -2090,7 +2028,7 @@ class ReabstractionThunkGenerator { { if (!ReInfo.isPartialSpecialization()) { Mangle::GenericSpecializationMangler Mangler( - OrigF, ReInfo.getCalleeParamSubstitutions(), Serialized, + OrigF, ReInfo.getCalleeParamSubstitutionMap(), Serialized, /*isReAbstracted*/ false); ThunkName = Mangler.mangle(); @@ -2175,7 +2113,7 @@ SILValue ReabstractionThunkGenerator::createReabstractionThunkApply( SILBuilder &Builder) { SILFunction *Thunk = &Builder.getFunction(); auto *FRI = Builder.createFunctionRef(Loc, SpecializedFunc); - auto Subs = Thunk->getForwardingSubstitutions(); + auto Subs = Thunk->getForwardingSubstitutionMap(); auto specConv = SpecializedFunc->getConventions(); if (!SpecializedFunc->getLoweredFunctionType()->hasErrorResult()) { return Builder.createApply(Loc, FRI, Subs, Arguments, false); @@ -2315,7 +2253,7 @@ void swift::trySpecializeApplyOfGeneric( if (Apply.getModule().isOptimizedOnoneSupportModule()) Serialized = IsNotSerialized; - ReabstractionInfo ReInfo(Apply, RefF, Apply.getSubstitutions(), + ReabstractionInfo ReInfo(Apply, RefF, Apply.getSubstitutionMap(), /*ConvertIndirectToDirect=*/true, &ORE); if (!ReInfo.canBeSpecialized()) return; @@ -2340,7 +2278,7 @@ void swift::trySpecializeApplyOfGeneric( SILInstruction *User = Use->getUser(); if (isa(User)) continue; - if (isDebugInst(User)) + if (User->isDebugInstruction()) continue; auto FAS = FullApplySite::isa(User); @@ -2359,7 +2297,7 @@ void swift::trySpecializeApplyOfGeneric( } } - GenericFuncSpecializer FuncSpecializer(RefF, Apply.getSubstitutions(), + GenericFuncSpecializer FuncSpecializer(RefF, Apply.getSubstitutionMap(), Serialized, ReInfo); SILFunction *SpecializedF = FuncSpecializer.lookupSpecialization(); if (SpecializedF) { @@ -2410,7 +2348,7 @@ void swift::trySpecializeApplyOfGeneric( for (auto &Op : PAI->getArgumentOperands()) { Arguments.push_back(Op.get()); } - auto Subs = ReInfo.getCallerParamSubstitutions(); + auto Subs = ReInfo.getCallerParamSubstitutionMap(); auto *NewPAI = Builder.createPartialApply( PAI->getLoc(), FRI, Subs, Arguments, PAI->getType().getAs()->getCalleeConvention()); diff --git a/lib/SILOptimizer/Utils/Local.cpp b/lib/SILOptimizer/Utils/Local.cpp index a15308d7eb63a..3dbce08dbb6b1 100644 --- a/lib/SILOptimizer/Utils/Local.cpp +++ b/lib/SILOptimizer/Utils/Local.cpp @@ -169,7 +169,7 @@ bool swift::isIntermediateRelease(SILInstruction *I, } namespace { - using CallbackTy = std::function; + using CallbackTy = llvm::function_ref; } // end anonymous namespace void swift:: @@ -322,13 +322,6 @@ void swift::replaceDeadApply(ApplySite Old, ValueBase *New) { recursivelyDeleteTriviallyDeadInstructions(OldApply, true); } -bool swift::hasArchetypes(SubstitutionList Subs) { - // Check whether any of the substitutions are dependent. - return llvm::any_of(Subs, [](const Substitution &S) { - return S.getReplacement()->hasArchetype(); - }); -} - bool swift::mayBindDynamicSelf(SILFunction *F) { if (!F->hasSelfMetadataParam()) return false; @@ -552,7 +545,7 @@ SILValue swift::castValueToABICompatibleType(SILBuilder *B, SILLocation Loc, // Src is not optional, but dest is optional. if (!OptionalSrcTy && OptionalDestTy) { - auto OptionalSrcCanTy = OptionalType::get(SrcTy.getSwiftRValueType()) + auto OptionalSrcCanTy = OptionalType::get(SrcTy.getASTType()) ->getCanonicalType(); auto LoweredOptionalSrcType = SILType::getPrimitiveObjectType( OptionalSrcCanTy); @@ -854,7 +847,7 @@ SingleValueInstruction *StringConcatenationOptimizer::optimize() { Arguments.push_back(FuncResultType); return Builder.createApply(AI->getLoc(), FRIConvertFromBuiltin, - SubstitutionList(), Arguments, + SubstitutionMap(), Arguments, false); } diff --git a/lib/SILOptimizer/Utils/PerformanceInlinerUtils.cpp b/lib/SILOptimizer/Utils/PerformanceInlinerUtils.cpp index bf98ceacd7a9d..69ff8c689136c 100644 --- a/lib/SILOptimizer/Utils/PerformanceInlinerUtils.cpp +++ b/lib/SILOptimizer/Utils/PerformanceInlinerUtils.cpp @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +#include "swift/AST/Module.h" #include "swift/SILOptimizer/Utils/PerformanceInlinerUtils.h" #include "swift/Strings.h" @@ -559,12 +560,12 @@ static bool calleeHasPartialApplyWithOpenedExistentials(FullApplySite AI) { return false; SILFunction *Callee = AI.getReferencedFunction(); - auto Subs = AI.getSubstitutions(); + auto SubsMap = AI.getSubstitutionMap(); // Bail if there are no open existentials in the list of substitutions. bool HasNoOpenedExistentials = true; - for (auto Sub : Subs) { - if (Sub.getReplacement()->hasOpenedExistential()) { + for (auto Replacement : SubsMap.getReplacementTypes()) { + if (Replacement->hasOpenedExistential()) { HasNoOpenedExistentials = false; break; } @@ -573,20 +574,15 @@ static bool calleeHasPartialApplyWithOpenedExistentials(FullApplySite AI) { if (HasNoOpenedExistentials) return false; - auto SubsMap = Callee->getLoweredFunctionType() - ->getGenericSignature()->getSubstitutionMap(Subs); - for (auto &BB : *Callee) { for (auto &I : BB) { if (auto PAI = dyn_cast(&I)) { - auto PAISubs = PAI->getSubstitutions(); - if (PAISubs.empty()) + if (!PAI->hasSubstitutions()) continue; // Check if any of substitutions would contain open existentials // after inlining. - auto PAISubMap = PAI->getOrigCalleeType() - ->getGenericSignature()->getSubstitutionMap(PAISubs); + auto PAISubMap = PAI->getSubstitutionMap(); PAISubMap = PAISubMap.subst(SubsMap); if (PAISubMap.hasOpenedExistential()) return true; @@ -631,7 +627,7 @@ static bool isCallerAndCalleeLayoutConstraintsCompatible(FullApplySite AI) { SILFunction *Callee = AI.getReferencedFunction(); auto CalleeSig = Callee->getLoweredFunctionType()->getGenericSignature(); auto SubstParams = CalleeSig->getSubstitutableParams(); - auto AISubs = AI.getSubstitutions(); + auto AISubs = AI.getSubstitutionMap(); for (auto idx : indices(SubstParams)) { auto Param = SubstParams[idx]; // Map the parameter into context @@ -644,7 +640,7 @@ static bool isCallerAndCalleeLayoutConstraintsCompatible(FullApplySite AI) { continue; // The generic parameter has a layout constraint. // Check that the substitution has the same constraint. - auto AIReplacement = AISubs[idx].getReplacement(); + auto AIReplacement = Type(Param).subst(AISubs); auto AIArchetype = AIReplacement->getAs(); if (!AIArchetype) return false; @@ -852,8 +848,8 @@ bool swift::isPureCall(FullApplySite AI, SideEffectAnalysis *SEA) { // If a call has only constant arguments and the call is pure, i.e. has // no side effects, then we should always inline it. // This includes arguments which are objects initialized with constant values. - SideEffectAnalysis::FunctionEffects ApplyEffects; - SEA->getEffects(ApplyEffects, AI); + FunctionSideEffects ApplyEffects; + SEA->getCalleeEffects(ApplyEffects, AI); auto GE = ApplyEffects.getGlobalEffects(); if (GE.mayRead() || GE.mayWrite() || GE.mayRetain() || GE.mayRelease()) return false; diff --git a/lib/SILOptimizer/Utils/SILInliner.cpp b/lib/SILOptimizer/Utils/SILInliner.cpp index e77e9021ba6d6..dbd20ceb15e97 100644 --- a/lib/SILOptimizer/Utils/SILInliner.cpp +++ b/lib/SILOptimizer/Utils/SILInliner.cpp @@ -398,6 +398,7 @@ InlineCost swift::instructionInlineCost(SILInstruction &I) { case SILInstructionKind::CondBranchInst: case SILInstructionKind::CondFailInst: case SILInstructionKind::CopyBlockInst: + case SILInstructionKind::CopyBlockWithoutEscapingInst: case SILInstructionKind::CopyAddrInst: case SILInstructionKind::RetainValueInst: case SILInstructionKind::RetainValueAddrInst: diff --git a/lib/SILOptimizer/Utils/SpecializationMangler.cpp b/lib/SILOptimizer/Utils/SpecializationMangler.cpp index 591a1eae107ef..38fd628db6275 100644 --- a/lib/SILOptimizer/Utils/SpecializationMangler.cpp +++ b/lib/SILOptimizer/Utils/SpecializationMangler.cpp @@ -80,7 +80,6 @@ std::string GenericSpecializationMangler::mangle() { SILFunctionType *FTy = Function->getLoweredFunctionType(); CanGenericSignature Sig = FTy->getGenericSignature(); - auto SubMap = Sig->getSubstitutionMap(Subs); bool First = true; for (auto ParamType : Sig->getSubstitutableParams()) { appendType(Type(ParamType).subst(SubMap)->getCanonicalType()); @@ -157,6 +156,18 @@ void FunctionSignatureSpecializationMangler::setArgumentSROA( OrigArgs[OrigArgIdx].first |= ArgumentModifierIntBase(ArgumentModifier::SROA); } +void FunctionSignatureSpecializationMangler::setArgumentGuaranteedToOwned( + unsigned OrigArgIdx) { + OrigArgs[OrigArgIdx].first |= + ArgumentModifierIntBase(ArgumentModifier::GuaranteedToOwned); +} + +void FunctionSignatureSpecializationMangler::setArgumentExistentialToGeneric( + unsigned OrigArgIdx) { + OrigArgs[OrigArgIdx].first |= + ArgumentModifierIntBase(ArgumentModifier::ExistentialToGeneric); +} + void FunctionSignatureSpecializationMangler::setArgumentBoxToValue( unsigned OrigArgIdx) { OrigArgs[OrigArgIdx].first = @@ -250,7 +261,7 @@ FunctionSignatureSpecializationMangler::mangleClosureProp(SILInstruction *Inst) // specializing. for (auto &Op : PAI->getArgumentOperands()) { SILType Ty = Op.get()->getType(); - appendType(Ty.getSwiftRValueType()); + appendType(Ty.getASTType()); } } @@ -282,6 +293,11 @@ void FunctionSignatureSpecializationMangler::mangleArgument( } bool hasSomeMod = false; + if (ArgMod & ArgumentModifierIntBase(ArgumentModifier::ExistentialToGeneric)) { + ArgOpBuffer << 'e'; + hasSomeMod = true; + } + if (ArgMod & ArgumentModifierIntBase(ArgumentModifier::Dead)) { ArgOpBuffer << 'd'; hasSomeMod = true; @@ -291,6 +307,12 @@ void FunctionSignatureSpecializationMangler::mangleArgument( ArgOpBuffer << (hasSomeMod ? 'G' : 'g'); hasSomeMod = true; } + + if (ArgMod & ArgumentModifierIntBase(ArgumentModifier::GuaranteedToOwned)) { + ArgOpBuffer << (hasSomeMod ? 'O' : 'o'); + hasSomeMod = true; + } + if (ArgMod & ArgumentModifierIntBase(ArgumentModifier::SROA)) { ArgOpBuffer << (hasSomeMod ? 'X' : 'x'); hasSomeMod = true; diff --git a/lib/Sema/CMakeLists.txt b/lib/Sema/CMakeLists.txt index db1ed260d3b81..c01fb95d1eebf 100644 --- a/lib/Sema/CMakeLists.txt +++ b/lib/Sema/CMakeLists.txt @@ -27,7 +27,6 @@ add_swift_library(swiftSema STATIC DerivedConformances.cpp ITCDecl.cpp ITCNameLookup.cpp - ITCType.cpp InstrumenterSupport.cpp IterativeTypeChecker.cpp MiscDiagnostics.cpp diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 20dd3a2ee2b65..1674ec38197a5 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -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 - 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 @@ -17,6 +17,7 @@ //===----------------------------------------------------------------------===// #include "ConstraintSystem.h" +#include "MiscDiagnostics.h" #include "swift/AST/ASTVisitor.h" #include "swift/AST/ASTWalker.h" #include "swift/AST/ExistentialLayout.h" @@ -58,20 +59,20 @@ static bool isOpenedAnyObject(Type type) { return existential->isAnyObject(); } -void Solution::computeSubstitutions( - GenericSignature *sig, - ConstraintLocator *locator, - SmallVectorImpl &result) const { +SubstitutionMap Solution::computeSubstitutions( + GenericSignature *sig, + ConstraintLocatorBuilder locatorBuilder) const { if (sig == nullptr) - return; + return SubstitutionMap(); // Gather the substitutions from dependent types to concrete types. + auto locator = getConstraintSystem().getConstraintLocator(locatorBuilder); auto openedTypes = OpenedTypes.find(locator); // If we have a member reference on an existential, there are no // opened types or substitutions. if (openedTypes == OpenedTypes.end()) - return; + return SubstitutionMap(); TypeSubstitutionMap subs; for (const auto &opened : openedTypes->second) @@ -80,38 +81,22 @@ void Solution::computeSubstitutions( auto &tc = getConstraintSystem().getTypeChecker(); auto lookupConformanceFn = - [&](CanType original, Type replacement, ProtocolType *protoType) + [&](CanType original, Type replacement, ProtocolDecl *protoType) -> Optional { if (replacement->hasError() || isOpenedAnyObject(replacement) || replacement->is()) { - return ProtocolConformanceRef(protoType->getDecl()); + return ProtocolConformanceRef(protoType); } - return tc.conformsToProtocol(replacement, - protoType->getDecl(), + return tc.conformsToProtocol(replacement, protoType, getConstraintSystem().DC, (ConformanceCheckFlags::InExpression| ConformanceCheckFlags::Used)); }; - auto subMap = sig->getSubstitutionMap( - QueryTypeSubstitutionMap{subs}, - lookupConformanceFn); - - sig->getSubstitutions(subMap, result); -} - -void Solution::computeSubstitutions( - GenericSignature *sig, - ConstraintLocatorBuilder locator, - SmallVectorImpl &result) const { - if (sig == nullptr) - return; - - computeSubstitutions(sig, - getConstraintSystem().getConstraintLocator(locator), - result); + return sig->getSubstitutionMap(QueryTypeSubstitutionMap{subs}, + lookupConformanceFn); } /// \brief Find a particular named function witness for a type that conforms to @@ -130,17 +115,17 @@ void Solution::computeSubstitutions( /// \param diag The diagnostic to emit if the protocol definition doesn't /// have a requirement with the given name. /// -/// \returns The named witness, or nullptr if no witness could be found. -template -static DeclTy *findNamedWitnessImpl( +/// \returns The named witness, or an empty ConcreteDeclRef if no witness +/// could be found. +ConcreteDeclRef findNamedWitnessImpl( TypeChecker &tc, DeclContext *dc, Type type, ProtocolDecl *proto, DeclName name, Diag<> diag, Optional conformance = None) { // Find the named requirement. - DeclTy *requirement = nullptr; + ValueDecl *requirement = nullptr; for (auto member : proto->getMembers()) { - auto d = dyn_cast(member); + auto d = dyn_cast(member); if (!d || !d->hasName()) continue; @@ -158,18 +143,21 @@ static DeclTy *findNamedWitnessImpl( // Find the member used to satisfy the named requirement. if (!conformance) { conformance = tc.conformsToProtocol(type, proto, dc, - ConformanceCheckFlags::InExpression); + ConformanceCheckFlags::InExpression); if (!conformance) return nullptr; } // For a type with dependent conformance, just return the requirement from // the protocol. There are no protocol conformance tables. - if (!conformance->isConcrete()) - return requirement; + if (!conformance->isConcrete()) { + auto subMap = SubstitutionMap::getProtocolSubstitutions(proto, type, + *conformance); + return ConcreteDeclRef(requirement, subMap); + } + auto concrete = conformance->getConcrete(); - // FIXME: Dropping substitutions here. - return cast_or_null(concrete->getWitnessDecl(requirement, &tc)); + return concrete->getWitnessDeclRef(requirement, &tc); } static bool shouldAccessStorageDirectly(Expr *base, VarDecl *member, @@ -542,6 +530,25 @@ namespace { /// look-through. Expr *coerceImplicitlyUnwrappedOptionalToValue(Expr *expr, Type objTy); + /// Peephole an array upcast. + void peepholeArrayUpcast(ArrayExpr *expr, Type toType, bool bridged, + Type elementType, + ConstraintLocatorBuilder locator); + + /// Peephole a dictionary upcast. + void peepholeDictionaryUpcast(DictionaryExpr *expr, Type toType, + bool bridged, Type keyType, + Type valueType, + ConstraintLocatorBuilder locator); + + /// Try to peephole the collection upcast, eliminating the need for + /// a separate collection-upcast expression. + /// + /// \returns true if the peephole operation succeeded, in which case + /// \c expr already subsumes the upcast. + bool peepholeCollectionUpcast(Expr *expr, Type toType, bool bridged, + ConstraintLocatorBuilder locator); + /// \brief Build a collection upcast expression. /// /// \param bridged Whether this is a bridging conversion, meaning that the @@ -555,6 +562,18 @@ namespace { Expr *buildObjCBridgeExpr(Expr *expr, Type toType, ConstraintLocatorBuilder locator); + static Type getBaseType(AnyFunctionType *fnType, + bool wantsRValueInstanceType = true) { + auto params = fnType->getParams(); + assert(params.size() == 1); + + const auto &base = params.front(); + if (wantsRValueInstanceType) + return base.getType()->getRValueInstanceType(); + + return base.getType(); + } + public: /// \brief Build a reference to the given declaration. Expr *buildDeclRef(OverloadChoice choice, DeclNameLoc loc, Type openedType, @@ -573,7 +592,7 @@ namespace { auto openedFnType = openedType->castTo(); auto simplifiedFnType = simplifyType(openedFnType)->castTo(); - auto baseTy = simplifiedFnType->getInput()->getRValueInstanceType(); + auto baseTy = getBaseType(simplifiedFnType); // Handle operator requirements found in protocols. if (auto proto = dyn_cast(decl->getDeclContext())) { @@ -657,19 +676,19 @@ namespace { return nullptr; } - SmallVector substitutions; + SubstitutionMap substitutions; // Due to a SILGen quirk, unqualified property references do not // need substitutions. if (!isa(decl)) { - solution.computeSubstitutions( + substitutions = + solution.computeSubstitutions( decl->getInnermostDeclContext()->getGenericSignatureOfContext(), - locator, - substitutions); + locator); } auto declRefExpr = - new (ctx) DeclRefExpr(ConcreteDeclRef(ctx, decl, substitutions), + new (ctx) DeclRefExpr(ConcreteDeclRef(decl, substitutions), loc, implicit, semantics, type); cs.cacheType(declRefExpr); declRefExpr->setFunctionRefKind(functionRefKind); @@ -739,14 +758,8 @@ namespace { return ctorRef->getArg(); } else if (auto apply = dyn_cast(expr)) { return apply->getFn(); - } else if (auto memberRef = dyn_cast(expr)) { - return memberRef->getBase(); - } else if (auto dynMemberRef = dyn_cast(expr)) { - return dynMemberRef->getBase(); - } else if (auto subscriptRef = dyn_cast(expr)) { - return subscriptRef->getBase(); - } else if (auto dynSubscriptRef = dyn_cast(expr)) { - return dynSubscriptRef->getBase(); + } else if (auto lookupRef = dyn_cast(expr)) { + return lookupRef->getBase(); } else if (auto load = dyn_cast(expr)) { return load->getSubExpr(); } else if (auto inout = dyn_cast(expr)) { @@ -945,11 +958,11 @@ namespace { } // Build a member reference. - SmallVector substitutions; - solution.computeSubstitutions( - member->getInnermostDeclContext()->getGenericSignatureOfContext(), - memberLocator, substitutions); - auto memberRef = ConcreteDeclRef(context, member, substitutions); + SubstitutionMap substitutions = + solution.computeSubstitutions( + member->getInnermostDeclContext()->getGenericSignatureOfContext(), + memberLocator); + auto memberRef = ConcreteDeclRef(member, substitutions); cs.TC.requestMemberLayout(member); @@ -969,9 +982,7 @@ namespace { } // The formal type of the 'self' value for the member's declaration. - Type containerTy = - refTy->castTo() - ->getInput()->getRValueInstanceType(); + Type containerTy = getBaseType(refTy->castTo()); // If we have an opened existential, selfTy and baseTy will both be // the same opened existential type. @@ -1176,7 +1187,7 @@ namespace { } /// \brief Describes either a type or the name of a type to be resolved. - typedef llvm::PointerUnion TypeOrName; + using TypeOrName = llvm::PointerUnion; /// \brief Convert the given literal expression via a protocol pair. /// @@ -1490,12 +1501,11 @@ namespace { index = cs.coerceToRValue(index); // The index argument should be (keyPath: KeyPath). - // Dig the key path expression out of the argument tuple. + // Dig the key path expression out of the arguments. auto indexKP = cast(index)->getElement(0); auto keyPathExprTy = cs.getType(indexKP); - auto keyPathTy = applicationTy->getInput()->castTo() - ->getElementType(0); - + auto keyPathTy = applicationTy->getParams().front().getType(); + Type valueTy; Type baseTy; bool resultIsLValue; @@ -1623,12 +1633,11 @@ namespace { // Form the subscript expression. // Compute the substitutions used to reference the subscript. - SmallVector substitutions; - solution.computeSubstitutions( - subscript->getInnermostDeclContext()->getGenericSignatureOfContext(), - locator.withPathElement(locatorKind), - substitutions); - ConcreteDeclRef subscriptRef(tc.Context, subscript, substitutions); + SubstitutionMap substitutions = + solution.computeSubstitutions( + subscript->getInnermostDeclContext()->getGenericSignatureOfContext(), + locator.withPathElement(locatorKind)); + ConcreteDeclRef subscriptRef(subscript, substitutions); // Handle dynamic lookup. if (choice.getKind() == OverloadChoiceKind::DeclViaDynamic || @@ -1650,7 +1659,8 @@ namespace { // Convert the base. auto openedFullFnType = selected.openedFullType->castTo(); - auto openedBaseType = openedFullFnType->getInput(); + auto openedBaseType = + getBaseType(openedFullFnType, /*wantsRValue*/ false); auto containerTy = solution.simplifyType(openedBaseType); base = coerceObjectArgumentToType( base, containerTy, subscript, AccessSemantics::Ordinary, @@ -1679,20 +1689,16 @@ namespace { auto &ctx = tc.Context; // Compute the concrete reference. - SmallVector substitutions; - solution.computeSubstitutions( - ctor->getGenericSignature(), - locator, - substitutions); + SubstitutionMap substitutions = + solution.computeSubstitutions(ctor->getGenericSignature(), locator); - auto ref = ConcreteDeclRef(ctx, ctor, substitutions); + auto ref = ConcreteDeclRef(ctor, substitutions); // The constructor was opened with the allocating type, not the // initializer type. Map the former into the latter. auto resultTy = solution.simplifyType(openedFullType); - auto selfTy = resultTy->castTo()->getInput() - ->getRValueInstanceType(); + auto selfTy = getBaseType(resultTy->castTo()); // Also replace the result type with the base type, so that calls // to constructors defined in a superclass will know to cast the @@ -1823,16 +1829,13 @@ namespace { assert(type->isEqual(genericSig->getGenericParams()[0])); return valueType; }, - [&](CanType origType, Type replacementType, ProtocolType *protoType) + [&](CanType origType, Type replacementType, ProtocolDecl *protoType) -> ProtocolConformanceRef { assert(bridgedToObjectiveCConformance); return *bridgedToObjectiveCConformance; }); - SmallVector subs; - genericSig->getSubstitutions(subMap, subs); - - ConcreteDeclRef fnSpecRef(tc.Context, fn, subs); + ConcreteDeclRef fnSpecRef(fn, subMap); auto resultType = OptionalType::get(valueType); @@ -1855,7 +1858,6 @@ namespace { return bridgeFromObjectiveC(object, valueType, false); } - TypeAliasDecl *MaxIntegerTypeDecl = nullptr; TypeAliasDecl *MaxFloatTypeDecl = nullptr; public: @@ -1883,6 +1885,8 @@ namespace { Expr *visitCodeCompletionExpr(CodeCompletionExpr *expr) { // Do nothing with code completion expressions. + auto toType = simplifyType(cs.getType(expr)); + cs.setType(expr, toType); return expr; } @@ -1916,28 +1920,6 @@ namespace { } } - // Find the maximum-sized builtin integer type. - - if (!MaxIntegerTypeDecl) { - SmallVector lookupResults; - tc.getStdlibModule(dc)->lookupValue(/*AccessPath=*/{}, - tc.Context.Id_MaxBuiltinIntegerType, - NLKind::QualifiedLookup, - lookupResults); - if (lookupResults.size() == 1) { - MaxIntegerTypeDecl = dyn_cast(lookupResults.front()); - tc.validateDecl(MaxIntegerTypeDecl); - } - } - if (!MaxIntegerTypeDecl || - !MaxIntegerTypeDecl->hasInterfaceType() || - !MaxIntegerTypeDecl->getDeclaredInterfaceType()->is()) { - tc.diagnose(expr->getLoc(), diag::no_MaxBuiltinIntegerType_found); - return nullptr; - } - tc.validateDecl(MaxIntegerTypeDecl); - auto maxType = MaxIntegerTypeDecl->getUnderlyingTypeLoc().getType(); - DeclName initName(tc.Context, DeclBaseName::createConstructor(), { tc.Context.Id_integerLiteral }); DeclName builtinInitName(tc.Context, DeclBaseName::createConstructor(), @@ -1951,7 +1933,9 @@ namespace { tc.Context.Id_IntegerLiteralType, initName, builtinProtocol, - maxType, + // Note that 'MaxIntegerType' is guaranteed to be available. + // Otherwise it would be caught by CSGen::visitLiteralExpr + tc.getMaxIntegerType(dc), builtinInitName, nullptr, diag::integer_literal_broken_proto, @@ -1981,8 +1965,12 @@ namespace { if (!nilDecl->hasInterfaceType()) return nullptr; - Substitution sub(objectType, {}); - ConcreteDeclRef concreteDeclRef(tc.Context, nilDecl, {sub}); + auto genericSig = + nilDecl->getDeclContext()->getGenericSignatureOfContext(); + SubstitutionMap subs = + SubstitutionMap::get(genericSig, llvm::makeArrayRef(objectType), + { }); + ConcreteDeclRef concreteDeclRef(nilDecl, subs); auto nilType = FunctionType::get( {MetatypeType::get(type)}, type); @@ -2346,7 +2334,7 @@ namespace { DeclName name(tc.Context, DeclBaseName::createConstructor(), { tc.Context.Id_stringInterpolation }); auto member - = findNamedWitnessImpl( + = findNamedWitnessImpl( tc, dc, type, interpolationProto, name, diag::interpolation_broken_proto); @@ -2354,10 +2342,13 @@ namespace { DeclName segmentName(tc.Context, DeclBaseName::createConstructor(), { tc.Context.Id_stringInterpolationSegment }); auto segmentMember - = findNamedWitnessImpl( + = findNamedWitnessImpl( tc, dc, type, interpolationProto, segmentName, diag::interpolation_broken_proto); - if (!member || !segmentMember) + if (!member || + !segmentMember || + !isa(member.getDecl()) || + !isa(segmentMember.getDecl())) return nullptr; // Build a reference to the init(stringInterpolation:) initializer. @@ -2368,7 +2359,7 @@ namespace { Expr *memberRef = new (tc.Context) MemberRefExpr(typeRef, expr->getStartLoc(), - member, + member.getDecl(), DeclNameLoc(expr->getStartLoc()), /*Implicit=*/true); cs.cacheSubExprTypes(memberRef); @@ -2679,7 +2670,7 @@ namespace { ApplyExpr *apply = CallExpr::create( tc.Context, result, arg, expr->getArgumentLabels(), expr->getArgumentLabelLocs(), expr->hasTrailingClosure(), - /*implicit=*/false, Type(), getType); + /*implicit=*/expr->isImplicit(), Type(), getType); result = finishApply(apply, Type(), cs.getConstraintLocator(expr)); } @@ -2800,6 +2791,22 @@ namespace { } auto selected = *selectedElt; + if (!selected.choice.getBaseType()) { + // This is one of the "outer alternatives", meaning the innermost + // methods didn't work out. + // + // The only way to get here is via an UnresolvedDotExpr with outer + // alternatives. + auto UDE = cast(expr); + cs.diagnoseDeprecatedConditionalConformanceOuterAccess( + UDE, selected.choice.getDecl()); + + return buildDeclRef(selected.choice, nameLoc, selected.openedFullType, + memberLocator, implicit, + selected.choice.getFunctionRefKind(), + AccessSemantics::Ordinary); + } + switch (selected.choice.getKind()) { case OverloadChoiceKind::DeclViaBridge: { base = cs.coerceToRValue(base); @@ -2840,11 +2847,22 @@ namespace { case OverloadChoiceKind::TupleIndex: { Type toType = simplifyType(cs.getType(expr)); - - // If the result type is an rvalue and the base contains lvalues, need a full - // tuple coercion to properly load & set access kind on all underlying elements - // before taking a single element. + auto baseTy = cs.getType(base); + // If the base type is not a tuple l-value, access to + // its elements supposed to be r-value as well. + // + // This is modeled in constraint system in a way + // that when member type is resolved by `resolveOverload` + // it would take r-value type of the element at + // specified index, but if it's a type variable it + // could still be bound to l-value later. + if (!baseTy->is()) + toType = toType->getRValueType(); + + // If the result type is an rvalue and the base contains lvalues, + // need a full tuple coercion to properly load & set access kind + // on all underlying elements before taking a single element. if (!toType->hasLValueType() && baseTy->hasLValueType()) base = coerceToType(base, baseTy->getRValueType(), cs.getConstraintLocator(base)); @@ -2945,9 +2963,9 @@ namespace { expr->getAccessSemantics()); } - Expr *visitArrayExpr(ArrayExpr *expr) { - Type openedType = cs.getType(expr); - Type arrayTy = simplifyType(openedType); + /// "Finish" an array expression by filling in the semantic expression. + ArrayExpr *finishArrayExpr(ArrayExpr *expr) { + Type arrayTy = cs.getType(expr); auto &tc = cs.getTypeChecker(); ProtocolDecl *arrayProto @@ -2986,7 +3004,7 @@ namespace { first = false; continue; - } + } typeElements.push_back(cs.getType(elt)); names.push_back(Identifier()); @@ -2996,13 +3014,13 @@ namespace { assert(isa(argType.getPointer())); Expr *arg = - TupleExpr::create(tc.Context, SourceLoc(), - expr->getElements(), - names, - { }, - SourceLoc(), /*HasTrailingClosure=*/false, - /*Implicit=*/true, - argType); + TupleExpr::create(tc.Context, SourceLoc(), + expr->getElements(), + names, + { }, + SourceLoc(), /*HasTrailingClosure=*/false, + /*Implicit=*/true, + argType); cs.cacheExprTypes(arg); @@ -3017,7 +3035,14 @@ namespace { cs.cacheExprTypes(result); expr->setSemanticExpr(result); + return expr; + } + + Expr *visitArrayExpr(ArrayExpr *expr) { + Type openedType = cs.getType(expr); + Type arrayTy = simplifyType(openedType); cs.setType(expr, arrayTy); + if (!finishArrayExpr(expr)) return nullptr; // If the array element type was defaulted, note that in the expression. if (solution.DefaultedConstraints.count(cs.getConstraintLocator(expr))) @@ -3026,11 +3051,11 @@ namespace { return expr; } - Expr *visitDictionaryExpr(DictionaryExpr *expr) { - Type openedType = cs.getType(expr); - Type dictionaryTy = simplifyType(openedType); - auto &tc = cs.getTypeChecker(); + /// "Finish" a dictionary expression by filling in the semantic expression. + DictionaryExpr *finishDictionaryExpr(DictionaryExpr *expr) { + Type dictionaryTy = cs.getType(expr); + auto &tc = cs.getTypeChecker(); ProtocolDecl *dictionaryProto = tc.getProtocol(expr->getLoc(), KnownProtocolKind::ExpressibleByDictionaryLiteral); @@ -3067,7 +3092,7 @@ namespace { first = false; continue; - } + } typeElements.push_back(cs.getType(elt)); names.push_back(Identifier()); @@ -3100,7 +3125,14 @@ namespace { cs.cacheExprTypes(result); expr->setSemanticExpr(result); + return expr; + } + + Expr *visitDictionaryExpr(DictionaryExpr *expr) { + Type openedType = cs.getType(expr); + Type dictionaryTy = simplifyType(openedType); cs.setType(expr, dictionaryTy); + if (!finishDictionaryExpr(expr)) return nullptr; // If the dictionary key or value type was defaulted, note that in the // expression. @@ -3237,11 +3269,7 @@ namespace { auto cond = solution.convertBooleanTypeToBuiltinI1(expr->getCondExpr(), cs.getConstraintLocator(expr)); - if (!cond) { - cs.setType(expr->getCondExpr(), ErrorType::get(resultTy)); - } else { - expr->setCondExpr(cond); - } + expr->setCondExpr(cond); // Coerce the then/else branches to the common type. expr->setThenExpr(coerceToType(expr->getThenExpr(), resultTy, @@ -3382,6 +3410,11 @@ namespace { auto &tc = cs.getTypeChecker(); tc.diagnose(cast->getLoc(), diag::conditional_downcast_foreign, destValueType); + ConcreteDeclRef refDecl = sub->getReferencedDecl(); + if (refDecl) { + tc.diagnose(cast->getLoc(), diag::note_explicitly_compare_cftypeid, + refDecl.getDecl()->getBaseName(), destValueType); + } } } } @@ -3849,7 +3882,15 @@ namespace { Expr *src = coerceToType(expr->getSrc(), destTy, locator); if (!src) return nullptr; + expr->setSrc(src); + + if (!SuppressDiagnostics) { + // If we're performing an assignment to a weak or unowned variable from + // a constructor call, emit a warning that the instance will be + // immediately deallocated. + diagnoseUnownedImmediateDeallocation(cs.getTypeChecker(), expr); + } return expr; } @@ -4387,16 +4428,15 @@ namespace { auto dc = property->getInnermostDeclContext(); - SmallVector subs; - if (auto sig = dc->getGenericSignatureOfContext()) { - // Compute substitutions to refer to the member. - solution.computeSubstitutions(sig, locator, subs); - } - + // Compute substitutions to refer to the member. + SubstitutionMap subs = + solution.computeSubstitutions(dc->getGenericSignatureOfContext(), + locator); + auto resolvedTy = foundDecl->openedType; resolvedTy = simplifyType(resolvedTy); - auto ref = ConcreteDeclRef(cs.getASTContext(), property, subs); + auto ref = ConcreteDeclRef(property, subs); component = KeyPathExpr::Component::forProperty(ref, resolvedTy, @@ -4431,13 +4471,13 @@ namespace { cs.TC.requestMemberLayout(subscript); auto dc = subscript->getInnermostDeclContext(); - SmallVector subs; auto indexType = subscript->getIndicesInterfaceType(); + SubstitutionMap subs; if (auto sig = dc->getGenericSignatureOfContext()) { // Compute substitutions to refer to the member. - solution.computeSubstitutions(sig, locator, subs); - indexType = indexType.subst(sig->getSubstitutionMap(subs)); + subs = solution.computeSubstitutions(sig, locator); + indexType = indexType.subst(subs); } // If this is a @dynamicMemberLookup reference to resolve a property @@ -4466,7 +4506,7 @@ namespace { resolvedTy = simplifyType(resolvedTy); - auto ref = ConcreteDeclRef(cs.getASTContext(), subscript, subs); + auto ref = ConcreteDeclRef(subscript, subs); // Coerce the indices to the type the subscript expects. auto indexExpr = coerceToType(origComponent.getIndexExpr(), @@ -4713,14 +4753,13 @@ namespace { /// /// \returns the decl to which the locator resolved. /// -static ConcreteDeclRef -resolveLocatorToDecl(ConstraintSystem &cs, ConstraintLocator *locator, - std::function(ConstraintLocator *)> findOvlChoice, - std::function - getConcreteDeclRef) -{ +static ConcreteDeclRef resolveLocatorToDecl( + ConstraintSystem &cs, ConstraintLocator *locator, + llvm::function_ref(ConstraintLocator *)> + findOvlChoice, + llvm::function_ref + getConcreteDeclRef) { assert(locator && "Null locator"); if (!locator->getAnchor()) return ConcreteDeclRef(); @@ -4867,11 +4906,12 @@ ConcreteDeclRef Solution::resolveLocatorToDecl( }, [&](ValueDecl *decl, Type openedType, ConstraintLocator *locator) -> ConcreteDeclRef { - SmallVector subs; - computeSubstitutions( - decl->getInnermostDeclContext()->getGenericSignatureOfContext(), - locator, subs); - return ConcreteDeclRef(cs.getASTContext(), decl, subs); + SubstitutionMap subs = + computeSubstitutions( + decl->getInnermostDeclContext() + ->getGenericSignatureOfContext(), + locator); + return ConcreteDeclRef(decl, subs); })) { return resolved; } @@ -4982,7 +5022,7 @@ getCallerDefaultArg(ConstraintSystem &cs, DeclContext *dc, case DefaultArgumentKind::Inherited: // Update the owner to reflect inheritance here. - owner = owner.getOverriddenDecl(tc.Context); + owner = owner.getOverriddenDecl(); return getCallerDefaultArg(cs, dc, loc, owner, index); case DefaultArgumentKind::Column: @@ -5675,8 +5715,9 @@ Expr *ExprRewriter::coerceCallArguments( ArrayRef argLabels, bool hasTrailingClosure, ConstraintLocatorBuilder locator) { - - auto paramType = funcType->getInput(); + auto &tc = getConstraintSystem().getTypeChecker(); + auto params = funcType->getParams(); + // Local function to produce a locator to refer to the ith element of the // argument tuple. auto getArgLocator = [&](unsigned argIdx, unsigned paramIdx) @@ -5685,34 +5726,40 @@ Expr *ExprRewriter::coerceCallArguments( LocatorPathElt::getApplyArgToParam(argIdx, paramIdx)); }; - bool matchCanFail = false; - - if (paramType->hasUnresolvedType()) - matchCanFail = true; + bool matchCanFail = + llvm::any_of(params, [](const AnyFunctionType::Param ¶m) { + return param.getType()->hasUnresolvedType(); + }); // If you value your sanity, ignore the body of this 'if' statement. - if (cs.getASTContext().isSwiftVersion3()) { + if (cs.getASTContext().isSwiftVersion3() && params.size() == 1) { + const auto ¶m = params.front(); + auto paramType = param.getType(); + // Total hack: In Swift 3 mode, we can end up with an arity mismatch due to // loss of ParenType sugar. - if (isa(arg)) - if (auto *parenType = dyn_cast(paramType.getPointer())) - if (isa(parenType->getUnderlyingType().getPointer())) - paramType = parenType->getUnderlyingType(); + if (isa(arg)) { + auto *tupleType = paramType->getAs(); + if (!param.hasLabel() && !param.isVariadic() && tupleType) { + // Rebuild the function type. + funcType = FunctionType::get(tupleType, funcType->getResult(), + funcType->getExtInfo()); + params = funcType->getParams(); + } + } // Total hack: In Swift 3 mode, argument labels are ignored when calling // function type with a single Any parameter. - if (paramType->isAny()) { - if (auto tupleArgType = dyn_cast(cs.getType(arg).getPointer())) { - if (tupleArgType->getNumElements() == 1) { + if (params.size() == 1 && params.front().getType()->isAny()) { + auto argType = cs.getType(arg); + if (auto *tupleArgType = dyn_cast(argType.getPointer())) { + if (tupleArgType->getNumElements() == 1) matchCanFail = true; - } } } - - // Rebuild the function type. - funcType = FunctionType::get(paramType, funcType->getResult()); } + auto paramType = AnyFunctionType::composeInput(tc.Context, params, false); bool allParamsMatch = cs.getType(arg)->isEqual(paramType); // Find the callee declaration. @@ -5723,9 +5770,8 @@ Expr *ExprRewriter::coerceCallArguments( unsigned level = apply ? computeCallLevel(cs, callee, apply) : 0; // Determine the parameter bindings. - auto params = funcType->getParams(); SmallVector defaultMap; - computeDefaultMap(paramType, callee.getDecl(), level, defaultMap); + computeDefaultMap(params, callee.getDecl(), level, defaultMap); auto args = decomposeArgType(cs.getType(arg), argLabels); // Quickly test if any further fix-ups for the argument types are necessary. @@ -5793,7 +5839,6 @@ Expr *ExprRewriter::coerceCallArguments( return Identifier(); }; - auto &tc = getConstraintSystem().getTypeChecker(); SmallVector toSugarFields; SmallVector fromTupleExprFields( argTuple? argTuple->getNumElements() : 1); @@ -5970,6 +6015,7 @@ Expr *ExprRewriter::coerceCallArguments( } // If we don't have to shuffle anything, we're done. + paramType = AnyFunctionType::composeInput(tc.Context, params, false); if (cs.getType(arg)->isEqual(paramType)) return arg; @@ -6077,10 +6123,8 @@ ClosureExpr *ExprRewriter::coerceClosureExprToVoid(ClosureExpr *closureExpr) { // Finally, compute the proper type for the closure. auto fnType = cs.getType(closureExpr)->getAs(); - Type inputType = fnType->getInput(); - auto newClosureType = FunctionType::get(inputType, - tc.Context.TheEmptyTupleType, - fnType->getExtInfo()); + auto newClosureType = FunctionType::get( + fnType->getParams(), tc.Context.TheEmptyTupleType, fnType->getExtInfo()); cs.setType(closureExpr, newClosureType); return closureExpr; } @@ -6192,14 +6236,38 @@ maybeDiagnoseUnsupportedFunctionConversion(ConstraintSystem &cs, Expr *expr, } } +/// Build the conversion of an element in a collection upcast. +static Expr *buildElementConversion(ExprRewriter &rewriter, + SourceLoc srcLoc, + Type srcType, + Type destType, + bool bridged, + ConstraintLocatorBuilder locator, + Expr *element) { + auto &cs = rewriter.getConstraintSystem(); + + auto &tc = rewriter.getConstraintSystem().getTypeChecker(); + if (bridged && + tc.typeCheckCheckedCast(srcType, destType, + CheckedCastContextKind::None, cs.DC, + SourceLoc(), nullptr, SourceRange()) + != CheckedCastKind::Coercion) { + if (auto conversion = + rewriter.buildObjCBridgeExpr(element, destType, locator)) + return conversion; + } + + return rewriter.coerceToType(element, destType, locator); +} + static CollectionUpcastConversionExpr::ConversionPair -buildElementConversion(ExprRewriter &rewriter, - SourceLoc srcLoc, - Type srcCollectionType, - Type destCollectionType, - bool bridged, - ConstraintLocatorBuilder locator, - unsigned typeArgIndex) { +buildOpaqueElementConversion(ExprRewriter &rewriter, + SourceLoc srcLoc, + Type srcCollectionType, + Type destCollectionType, + bool bridged, + ConstraintLocatorBuilder locator, + unsigned typeArgIndex) { // We don't need this stuff unless we've got generalized casts. Type srcType = srcCollectionType->castTo() ->getGenericArgs()[typeArgIndex]; @@ -6211,35 +6279,138 @@ buildElementConversion(ExprRewriter &rewriter, ASTContext &ctx = cs.getASTContext(); auto opaque = rewriter.cs.cacheType(new (ctx) OpaqueValueExpr(srcLoc, srcType)); - Expr *conversion = nullptr; - auto &tc = rewriter.getConstraintSystem().getTypeChecker(); - if (bridged && - tc.typeCheckCheckedCast(srcType, destType, - CheckedCastContextKind::None, cs.DC, - SourceLoc(), nullptr, SourceRange()) - != CheckedCastKind::Coercion) { - conversion = rewriter.buildObjCBridgeExpr(opaque, destType, - locator.withPathElement( - ConstraintLocator::PathElement::getGenericArgument(typeArgIndex))); + Expr *conversion = + buildElementConversion(rewriter, srcLoc, srcType, destType, bridged, + locator.withPathElement( + ConstraintLocator::PathElement::getGenericArgument( + typeArgIndex)), + opaque); + + return { opaque, conversion }; +} + +void ExprRewriter::peepholeArrayUpcast(ArrayExpr *expr, Type toType, + bool bridged, Type elementType, + ConstraintLocatorBuilder locator) { + // Update the type of the array literal. + cs.setType(expr, toType); + + // Convert the elements. + ConstraintLocatorBuilder innerLocator = + locator.withPathElement( + ConstraintLocator::PathElement::getGenericArgument(0)); + for (auto &element : expr->getElements()) { + if (auto newElement = buildElementConversion(*this, expr->getLoc(), + cs.getType(element), + elementType, + bridged, innerLocator, + element)) { + element = newElement; + } } - if (!conversion) { - conversion = rewriter.coerceToType(opaque, destType, - locator.withPathElement( - ConstraintLocator::PathElement::getGenericArgument(typeArgIndex))); + (void)finishArrayExpr(expr); +} + +void ExprRewriter::peepholeDictionaryUpcast(DictionaryExpr *expr, + Type toType, bool bridged, + Type keyType, Type valueType, + ConstraintLocatorBuilder locator) { + // Update the type of the dictionary literal. + cs.setType(expr, toType); + + ConstraintLocatorBuilder keyLocator = + locator.withPathElement( + ConstraintLocator::PathElement::getGenericArgument(0)); + ConstraintLocatorBuilder valueLocator = + locator.withPathElement( + ConstraintLocator::PathElement::getGenericArgument(1)); + + // Convert the elements. + TupleTypeElt tupleTypeElts[2] = { keyType, valueType }; + auto tupleType = TupleType::get(tupleTypeElts, cs.getASTContext()); + for (auto element : expr->getElements()) { + if (auto tuple = dyn_cast(element)) { + auto key = tuple->getElement(0); + if (auto newKey = buildElementConversion(*this, expr->getLoc(), + cs.getType(key), keyType, + bridged, valueLocator, key)) + tuple->setElement(0, newKey); + + auto value = tuple->getElement(1); + if (auto newValue = buildElementConversion(*this, expr->getLoc(), + cs.getType(value), valueType, + bridged, valueLocator, + value)) { + tuple->setElement(1, newValue); + } + + cs.setType(tuple, tupleType); + } } - return { opaque, conversion }; + (void)finishDictionaryExpr(expr); } -Expr *ExprRewriter::buildCollectionUpcastExpr(Expr *expr, Type toType, - bool bridged, - ConstraintLocatorBuilder locator) { +bool ExprRewriter::peepholeCollectionUpcast(Expr *expr, Type toType, + bool bridged, + ConstraintLocatorBuilder locator) { + // Recurse into parenthesized expressions. + if (auto paren = dyn_cast(expr)) { + // If we can't peephole the subexpression, we're done. + if (!peepholeCollectionUpcast(paren->getSubExpr(), toType, bridged, + locator)) + return false; + + // Update the type of this expression. + cs.setType(paren, ParenType::get(cs.getASTContext(), + cs.getType(paren->getSubExpr()))); + return true; + } + + // Array literals. + if (auto arrayLiteral = dyn_cast(expr)) { + if (Optional elementType = ConstraintSystem::isArrayType(toType)) { + peepholeArrayUpcast(arrayLiteral, toType, bridged, *elementType, locator); + return true; + } + + if (Optional elementType = ConstraintSystem::isSetType(toType)) { + peepholeArrayUpcast(arrayLiteral, toType, bridged, *elementType, locator); + return true; + } + + return false; + } + + // Dictionary literals. + if (auto dictLiteral = dyn_cast(expr)) { + if (auto elementType = ConstraintSystem::isDictionaryType(toType)) { + peepholeDictionaryUpcast(dictLiteral, toType, bridged, + elementType->first, elementType->second, + locator); + return true; + } + + return false; + } + + return false; +} + +Expr *ExprRewriter::buildCollectionUpcastExpr( + Expr *expr, Type toType, + bool bridged, + ConstraintLocatorBuilder locator) { + if (peepholeCollectionUpcast(expr, toType, bridged, locator)) + return expr; + ASTContext &ctx = cs.getASTContext(); // Build the first value conversion. - auto conv = buildElementConversion(*this, expr->getLoc(), cs.getType(expr), - toType, bridged, locator, 0); + auto conv = + buildOpaqueElementConversion(*this, expr->getLoc(), cs.getType(expr), + toType, bridged, locator, 0); // For single-parameter collections, form the upcast. if (ConstraintSystem::isArrayType(toType) || @@ -6252,8 +6423,9 @@ Expr *ExprRewriter::buildCollectionUpcastExpr(Expr *expr, Type toType, "Unhandled collection upcast"); // Build the second value conversion. - auto conv2 = buildElementConversion(*this, expr->getLoc(), cs.getType(expr), - toType, bridged, locator, 1); + auto conv2 = + buildOpaqueElementConversion(*this, expr->getLoc(), cs.getType(expr), + toType, bridged, locator, 1); return cs.cacheType( new (ctx) CollectionUpcastConversionExpr(expr, toType, conv, conv2)); @@ -6369,12 +6541,15 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType, auto *fn2 = obj2->getAs(); if (fn1 && fn2) { - auto *params1 = fn1->getInput()->getAs(); - auto *params2 = fn2->getInput()->getAs(); + auto params1 = fn1->getParams(); + auto params2 = fn2->getParams(); // This handles situations like argument: (()), parameter: (). - if (params1 && params2 && params1->isEqual(params2)) - break; + if (params1.size() == 1 && params2.empty()) { + auto tupleTy = params1.front().getType()->getAs(); + if (tupleTy && tupleTy->getNumElements() == 0) + break; + } } } } @@ -6697,8 +6872,9 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType, ->castTo(); // Convert the value to the expected result type of the function. - expr = coerceToType(expr, toFunc->getResult(), - locator.withPathElement(ConstraintLocator::Load)); + expr = coerceToType( + expr, toFunc->getResult(), + locator.withPathElement(ConstraintLocator::AutoclosureResult)); // We'll set discriminator values on all the autoclosures in a // later pass. @@ -6730,12 +6906,10 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType, // bit to the closure without invalidating prior analysis. auto fromEI = fromFunc->getExtInfo(); if (toEI.isNoEscape() && !fromEI.isNoEscape()) { - auto newFromFuncType = FunctionType::get(fromFunc->getInput(), - fromFunc->getResult(), - fromEI.withNoEscape()); + auto newFromFuncType = fromFunc->withExtInfo(fromEI.withNoEscape()); if (!isInDefaultArgumentContext && applyTypeToClosureExpr(cs, expr, newFromFuncType)) { - fromFunc = newFromFuncType; + fromFunc = newFromFuncType->castTo(); // Propagating the 'no escape' bit might have satisfied the entire // conversion. If so, we're done, otherwise keep converting. if (fromFunc->isEqual(toType)) @@ -6812,21 +6986,15 @@ static Type adjustSelfTypeForMember(Type baseTy, ValueDecl *member, AccessSemantics semantics, DeclContext *UseDC) { auto baseObjectTy = baseTy->getWithoutSpecifierType(); - if (auto func = dyn_cast(member)) { + + if (isa(member)) + return baseObjectTy; + + if (auto func = dyn_cast(member)) { // If 'self' is an inout type, turn the base type into an lvalue // type with the same qualifiers. - auto selfParam = func->getInterfaceType()->getAs()->getParams(); - assert(selfParam.size() == 1 && "found invalid arity of self param"); - if (selfParam[0].getParameterFlags().isInOut()) { - // Unless we're looking at a nonmutating existential member. In which - // case, the member will be modeled as an inout but ExistentialMemberRef - // and ArchetypeMemberRef want to take the base as an rvalue. - if (auto *fd = dyn_cast(func)) - if (!fd->isMutating() && baseObjectTy->is()) - return baseObjectTy; - + if (func->isMutating()) return InOutType::get(baseObjectTy); - } // Otherwise, return the rvalue type. return baseObjectTy; @@ -6834,23 +7002,22 @@ static Type adjustSelfTypeForMember(Type baseTy, ValueDecl *member, // If the base of the access is mutable, then we may be invoking a getter or // setter that requires the base to be mutable. - if (auto *SD = dyn_cast(member)) { - bool isSettableFromHere = SD->isSettable(UseDC) - && (!UseDC->getASTContext().LangOpts.EnableAccessControl - || SD->isSetterAccessibleFrom(UseDC)); - - // If neither the property's getter nor its setter are mutating, the base - // can be an rvalue. - if (!SD->isGetterMutating() - && (!isSettableFromHere || !SD->isSetterMutating())) - return baseObjectTy; - - // If we're calling an accessor, keep the base as an inout type, because the - // getter may be mutating. - if (SD->hasAccessorFunctions() && baseTy->is() && - semantics != AccessSemantics::DirectToStorage) - return InOutType::get(baseObjectTy); - } + auto *SD = cast(member); + bool isSettableFromHere = SD->isSettable(UseDC) + && (!UseDC->getASTContext().LangOpts.EnableAccessControl + || SD->isSetterAccessibleFrom(UseDC)); + + // If neither the property's getter nor its setter are mutating, the base + // can be an rvalue. + if (!SD->isGetterMutating() + && (!isSettableFromHere || !SD->isSetterMutating())) + return baseObjectTy; + + // If we're calling an accessor, keep the base as an inout type, because the + // getter may be mutating. + if (SD->hasAccessorFunctions() && baseTy->is() && + semantics != AccessSemantics::DirectToStorage) + return InOutType::get(baseObjectTy); // Accesses to non-function members in value types are done through an @lvalue // type. @@ -7086,39 +7253,21 @@ Expr *ExprRewriter::convertLiteralInPlace(Expr *literal, // Find the witness that we'll use to initialize the type via a builtin // literal. - auto witness = findNamedWitnessImpl( + auto witness = findNamedWitnessImpl( tc, dc, type->getRValueType(), builtinProtocol, builtinLiteralFuncName, brokenBuiltinProtocolDiag, *builtinConformance); - if (!witness) + if (!witness || !isa(witness.getDecl())) return nullptr; // Form a reference to the builtin conversion function. - // FIXME: Bogus location info. - Expr *base = TypeExpr::createImplicitHack(literal->getLoc(), type, - tc.Context); - - Expr *unresolvedDot = new (tc.Context) UnresolvedDotExpr( - base, SourceLoc(), - witness->getFullName(), - DeclNameLoc(base->getEndLoc()), - /*Implicit=*/true); - (void)tc.typeCheckExpression(unresolvedDot, dc); - - cs.cacheExprTypes(unresolvedDot); - - ConcreteDeclRef builtinRef = unresolvedDot->getReferencedDecl(); - if (!builtinRef) { - tc.diagnose(base->getLoc(), brokenBuiltinProtocolDiag); - return nullptr; - } // Set the builtin initializer. if (auto stringLiteral = dyn_cast(literal)) - stringLiteral->setBuiltinInitializer(builtinRef); + stringLiteral->setBuiltinInitializer(witness); else { cast(literal) - ->setBuiltinInitializer(builtinRef); + ->setBuiltinInitializer(witness); } // The literal expression has this type. @@ -7151,37 +7300,18 @@ Expr *ExprRewriter::convertLiteralInPlace(Expr *literal, } // Find the witness that we'll use to initialize the literal value. - auto witness = findNamedWitnessImpl( + auto witness = findNamedWitnessImpl( tc, dc, type->getRValueType(), protocol, literalFuncName, brokenProtocolDiag, conformance); - if (!witness) - return nullptr; - - // Form a reference to the conversion function. - // FIXME: Bogus location info. - Expr *base = TypeExpr::createImplicitHack(literal->getLoc(), type, - tc.Context); - - Expr *unresolvedDot = new (tc.Context) UnresolvedDotExpr( - base, SourceLoc(), - witness->getFullName(), - DeclNameLoc(base->getEndLoc()), - /*Implicit=*/true); - (void)tc.typeCheckExpression(unresolvedDot, dc); - cs.cacheExprTypes(unresolvedDot); - - ConcreteDeclRef ref = unresolvedDot->getReferencedDecl(); - if (!ref) { - tc.diagnose(base->getLoc(), brokenProtocolDiag); + if (!witness || !isa(witness.getDecl())) return nullptr; - } // Set the initializer. if (auto stringLiteral = dyn_cast(literal)) - stringLiteral->setInitializer(ref); + stringLiteral->setInitializer(witness); else - cast(literal)->setInitializer(ref); + cast(literal)->setInitializer(witness); // The literal expression has this type. cs.setType(literal, type); @@ -7244,7 +7374,7 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType, auto body = arg->getElements()[1]; auto bodyTy = cs.getType(body)->getWithoutSpecifierType(); auto bodyFnTy = bodyTy->castTo(); - auto escapableType = bodyFnTy->getInput(); + auto escapableParams = bodyFnTy->getParams(); auto resultType = bodyFnTy->getResult(); // The body is immediately called, so is obviously noescape. @@ -7255,7 +7385,8 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType, auto escapable = new (tc.Context) OpaqueValueExpr(apply->getFn()->getLoc(), Type()); - cs.setType(escapable, escapableType); + cs.setType(escapable, FunctionType::composeInput( + tc.Context, escapableParams, false)); auto getType = [&](const Expr *E) -> Type { return cs.getType(E); @@ -7286,7 +7417,7 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType, auto body = cs.coerceToRValue(arg->getElements()[1]); auto bodyFnTy = cs.getType(body)->castTo(); - auto openedTy = bodyFnTy->getInput(); + auto openedTy = getBaseType(bodyFnTy, /*wantsRValue*/ false); auto resultTy = bodyFnTy->getResult(); // The body is immediately called, so is obviously noescape. @@ -8165,18 +8296,20 @@ Expr *TypeChecker::callWitness(Expr *base, DeclContext *dc, if (auto metaType = type->getAs()) type = metaType->getInstanceType(); - auto witness = findNamedWitnessImpl( + auto witness = findNamedWitnessImpl( *this, dc, type->getRValueType(), protocol, name, brokenProtocolDiag); - if (!witness) + if (!witness || !isa(witness.getDecl())) return nullptr; + auto *witnessFn = cast(witness.getDecl()); + // Form a syntactic expression that describes the reference to the // witness. // FIXME: Egregious hack. auto unresolvedDot = new (Context) UnresolvedDotExpr( base, SourceLoc(), - witness->getFullName(), + witness.getDecl()->getFullName(), DeclNameLoc(base->getEndLoc()), /*Implicit=*/true); unresolvedDot->setFunctionRefKind(FunctionRefKind::SingleApply); @@ -8185,7 +8318,7 @@ Expr *TypeChecker::callWitness(Expr *base, DeclContext *dc, // Form a reference to the witness itself. Type openedFullType, openedType; std::tie(openedFullType, openedType) - = cs.getTypeOfMemberReference(base->getType(), witness, dc, + = cs.getTypeOfMemberReference(base->getType(), witness.getDecl(), dc, /*isDynamicResult=*/false, FunctionRefKind::DoubleApply, dotLocator); @@ -8198,9 +8331,9 @@ Expr *TypeChecker::callWitness(Expr *base, DeclContext *dc, // FIXME: Standardize all callers to always provide all argument names, // rather than hack around this. CallExpr *call; - auto argLabels = witness->getFullName().getArgumentNames(); + auto argLabels = witness.getDecl()->getFullName().getArgumentNames(); if (arguments.size() == 1 && - (isVariadicWitness(witness) || + (isVariadicWitness(witnessFn) || argumentNamesMatch(cs.getType(arguments[0]), argLabels))) { call = CallExpr::create(Context, unresolvedDot, arguments[0], {}, {}, /*hasTrailingClosure=*/false, @@ -8228,11 +8361,12 @@ Expr *TypeChecker::callWitness(Expr *base, DeclContext *dc, cs.cacheSubExprTypes(call); // Add the conversion from the argument to the function parameter type. - cs.addConstraint(ConstraintKind::ArgumentTupleConversion, - cs.getType(call->getArg()), - openedType->castTo()->getInput(), - cs.getConstraintLocator(call, - ConstraintLocator::ApplyArgument)); + auto openedFuncType = openedType->castTo(); + cs.addConstraint( + ConstraintKind::ArgumentTupleConversion, cs.getType(call->getArg()), + FunctionType::composeInput(cs.getASTContext(), + openedFuncType->getParams(), false), + cs.getConstraintLocator(call, ConstraintLocator::ApplyArgument)); // Solve the system. SmallVector solutions; @@ -8250,7 +8384,7 @@ Expr *TypeChecker::callWitness(Expr *base, DeclContext *dc, /*suppressDiagnostics=*/false); auto choice = - OverloadChoice(openedFullType, witness, FunctionRefKind::SingleApply); + OverloadChoice(openedFullType, witnessFn, FunctionRefKind::SingleApply); auto memberRef = rewriter.buildMemberRef( base, openedFullType, base->getStartLoc(), choice, DeclNameLoc(base->getEndLoc()), openedType, dotLocator, dotLocator, @@ -8282,6 +8416,11 @@ Solution::convertBooleanTypeToBuiltinI1(Expr *expr, auto type = cs.getType(expr); + // We allow UnresolvedType is()) + return expr; + // Look for the builtin name. If we don't have it, we need to call the // general name via the witness table. NameLookupOptions lookupOptions = defaultMemberLookupOptions; @@ -8294,45 +8433,48 @@ Solution::convertBooleanTypeToBuiltinI1(Expr *expr, // Find the builtin method. if (members.size() != 1) { tc.diagnose(expr->getLoc(), diag::broken_bool); - return nullptr; + return expr; } auto *builtinMethod = dyn_cast(members[0].getValueDecl()); if (!builtinMethod) { tc.diagnose(expr->getLoc(), diag::broken_bool); - return nullptr; + return expr; } - // Form a reference to the builtin method. - Expr *memberRef = new (ctx) MemberRefExpr(expr, SourceLoc(), - builtinMethod, - DeclNameLoc(expr->getLoc()), - /*Implicit=*/true); - cs.cacheSubExprTypes(memberRef); - cs.setSubExprTypes(memberRef); - bool failed = tc.typeCheckExpressionShallow(memberRef, cs.DC); - cs.cacheExprTypes(memberRef); - assert(!failed && "Could not reference witness?"); - (void)failed; + // The method is not generic, so there are no substitutions. + auto builtinMethodType = builtinMethod->getInterfaceType() + ->castTo(); + + // Form an unbound reference to the builtin method. + auto *declRef = new (ctx) DeclRefExpr(builtinMethod, + DeclNameLoc(expr->getLoc()), + /*Implicit=*/true); + declRef->setFunctionRefKind(FunctionRefKind::DoubleApply); + cs.setType(declRef, builtinMethodType); - // Call the builtin method. auto getType = [&](const Expr *E) -> Type { return cs.getType(E); }; - expr = CallExpr::createImplicit(ctx, memberRef, { }, { }, getType); - cs.cacheSubExprTypes(expr); - cs.setSubExprTypes(expr); - failed = tc.typeCheckExpressionShallow(expr, cs.DC); - cs.cacheExprTypes(expr); - assert(!failed && "Could not call witness?"); - (void)failed; + // Apply 'self' to get the method value. + auto *methodRef = new (ctx) DotSyntaxCallExpr(declRef, + SourceLoc(), + expr); + cs.setType(methodRef, builtinMethodType->getResult()); - if (expr && !cs.getType(expr)->isBuiltinIntegerType(1)) { + // Apply the empty argument list to get the final result. + auto *result = CallExpr::createImplicit(ctx, methodRef, + { }, { }, getType); + cs.setType(result, builtinMethodType->getResult() + ->castTo()->getResult()); + cs.setType(result->getArg(), ctx.TheEmptyTupleType); + + if (!cs.getType(result)->isBuiltinIntegerType(1)) { tc.diagnose(expr->getLoc(), diag::broken_bool); - return nullptr; + return result; } - return expr; + return result; } Expr *Solution::convertOptionalToBool(Expr *expr, diff --git a/lib/Sema/CSBindings.cpp b/lib/Sema/CSBindings.cpp index 57df2f011b5a0..155981926ddca 100644 --- a/lib/Sema/CSBindings.cpp +++ b/lib/Sema/CSBindings.cpp @@ -253,6 +253,17 @@ ConstraintSystem::getPotentialBindingForRelationalConstraint( if (type->hasError()) return None; + // Don't deduce autoclosure types or single-element, non-variadic + // tuples. + if (shouldBindToValueType(constraint)) { + if (auto funcTy = type->getAs()) { + if (funcTy->isAutoClosure()) + type = funcTy->getResult(); + } + + type = type->getWithoutImmediateLabel(); + } + // If the source of the binding is 'OptionalObject' constraint // and type variable is on the left-hand side, that means // that it _has_ to be of optional type, since the right-hand @@ -306,17 +317,6 @@ ConstraintSystem::getPotentialBindingForRelationalConstraint( return None; } - // Don't deduce autoclosure types or single-element, non-variadic - // tuples. - if (shouldBindToValueType(constraint)) { - if (auto funcTy = type->getAs()) { - if (funcTy->isAutoClosure()) - type = funcTy->getResult(); - } - - type = type->getWithoutImmediateLabel(); - } - // Make sure we aren't trying to equate type variables with different // lvalue-binding rules. if (auto otherTypeVar = diff --git a/lib/Sema/CSDiag.cpp b/lib/Sema/CSDiag.cpp index 806bbf06edb60..b6513af5f2143 100644 --- a/lib/Sema/CSDiag.cpp +++ b/lib/Sema/CSDiag.cpp @@ -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 - 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 @@ -18,6 +18,7 @@ #include "CSDiag.h" #include "CalleeCandidateInfo.h" #include "MiscDiagnostics.h" +#include "TypoCorrection.h" #include "swift/AST/ASTWalker.h" #include "swift/AST/GenericEnvironment.h" #include "swift/AST/Initializer.h" @@ -193,11 +194,13 @@ void constraints::simplifyLocator(Expr *&anchor, break; - case ConstraintLocator::Load: + case ConstraintLocator::AutoclosureResult: case ConstraintLocator::RvalueAdjustment: case ConstraintLocator::ScalarToTuple: case ConstraintLocator::UnresolvedMember: - // Loads, rvalue adjustment, and scalar-to-tuple conversions are implicit. + // Arguments in autoclosure positions, rvalue adjustments, and + // scalar-to-tuple conversions, and unresolved members are + // implicit. path = path.slice(1); continue; @@ -656,7 +659,7 @@ static void diagnoseSubElementFailure(Expr *destExpr, Diag diagID, Diag unknownDiagID) { auto &TC = CS.getTypeChecker(); - + // Walk through the destination expression, resolving what the problem is. If // we find a node in the lvalue path that is problematic, this returns it. auto immInfo = resolveImmutableBase(destExpr, CS); @@ -705,18 +708,23 @@ static void diagnoseSubElementFailure(Expr *destExpr, .highlight(immInfo.first->getSourceRange()); return; } - + // If we're trying to set an unapplied method, say that. - if (auto *VD = dyn_cast_or_null(immInfo.second)) { + if (auto *VD = immInfo.second) { std::string message = "'"; message += VD->getBaseName().getIdentifier().str(); message += "'"; - - if (auto *AFD = dyn_cast(VD)) - message += AFD->getImplicitSelfDecl() ? " is a method" : " is a function"; - else + + if (auto *AFD = dyn_cast(VD)) { + if (AFD->getImplicitSelfDecl()) { + message += " is a method"; + diagID = diag::assignment_lhs_is_immutable_variable; + } else { + message += " is a function"; + } + } else message += " is not settable"; - + TC.diagnose(loc, diagID, message) .highlight(immInfo.first->getSourceRange()); return; @@ -729,11 +737,13 @@ static void diagnoseSubElementFailure(Expr *destExpr, auto argsTuple = dyn_cast(AE->getArg()->getSemanticsProvidingExpr()); if (isa(AE) && AE->isImplicit() && argsTuple && - argsTuple->getNumElements() == 1 && - isa(argsTuple->getElement(0)-> - getSemanticsProvidingExpr())) { - TC.diagnose(loc, diagID, "literals are not mutable"); - return; + argsTuple->getNumElements() == 1) { + if (auto LE = dyn_cast(argsTuple->getElement(0)-> + getSemanticsProvidingExpr())) { + TC.diagnose(loc, diagID, "literals are not mutable") + .highlight(LE->getSourceRange()); + return; + } } std::string name = "call"; @@ -754,7 +764,7 @@ static void diagnoseSubElementFailure(Expr *destExpr, .highlight(AE->getSourceRange()); return; } - + if (auto *ICE = dyn_cast(immInfo.first)) if (isa(ICE->getSubExpr())) { TC.diagnose(loc, diagID, @@ -795,7 +805,7 @@ enum TCCFlags { TCC_AllowUnresolvedTypeVariables = 0x04 }; -typedef OptionSet TCCOptions; +using TCCOptions = OptionSet; inline TCCOptions operator|(TCCFlags flag1, TCCFlags flag2) { return TCCOptions(flag1) | flag2; @@ -885,7 +895,8 @@ class FailureDiagnosis :public ASTVisitor{ /// Attempt to produce a diagnostic for a mismatch between an expression's /// type and its assumed contextual type. bool diagnoseContextualConversionError(Expr *expr, Type contextualType, - ContextualTypePurpose CTP); + ContextualTypePurpose CTP, + Type suggestedType = Type()); /// For an expression being type checked with a CTP_CalleeResult contextual /// type, try to diagnose a problem. @@ -985,11 +996,16 @@ class FailureDiagnosis :public ASTVisitor{ bool diagnoseTrailingClosureErrors(ApplyExpr *expr); - bool diagnoseClosureExpr(ClosureExpr *closureExpr, Type contextualType, - std::function resultTypeProcessor); + bool + diagnoseClosureExpr(ClosureExpr *closureExpr, Type contextualType, + llvm::function_ref resultTypeProcessor); bool diagnoseSubscriptErrors(SubscriptExpr *SE, bool performingSet); + /// Diagnose the usage of 'subscript' instead of the operator when calling + /// a subscript and offer a fixit if the inputs are compatible. + bool diagnoseSubscriptMisuse(ApplyExpr *callExpr); + bool visitExpr(Expr *E); bool visitIdentityExpr(IdentityExpr *E); bool visitTryExpr(TryExpr *E); @@ -1054,7 +1070,7 @@ bool FailureDiagnosis::diagnoseConstraintFailure() { }; // Start out by classifying all the constraints. - typedef std::pair RCElt; + using RCElt = std::pair; std::vector rankedConstraints; // This is a predicate that classifies constraints according to our @@ -1330,25 +1346,20 @@ diagnoseTypeMemberOnInstanceLookup(Type baseObjTy, /// lower case counterparts are identical. /// - DeclName is valid when such a correct case is found; invalid otherwise. static DeclName -findCorrectEnumCaseName(Type Ty, LookupResult &Result, +findCorrectEnumCaseName(Type Ty, TypoCorrectionResults &corrections, DeclName memberName) { - if (!memberName.isSimpleName()) + if (memberName.isSpecial() || !memberName.isSimpleName()) return DeclName(); if (!Ty->is() && !Ty->is()) return DeclName(); - llvm::SmallVector candidates; - for (auto &correction : Result) { - DeclName correctName = correction.getValueDecl()->getFullName(); - if (!isa(correction.getValueDecl())) - continue; - if (correctName.getBaseIdentifier().str().equals_lower( - memberName.getBaseIdentifier().str())) - candidates.push_back(correctName.getBaseName()); - } - if (candidates.size() == 1) - return candidates.front(); - return DeclName(); + auto candidate = + corrections.getUniqueCandidateMatching([&](ValueDecl *candidate) { + return (isa(candidate) && + candidate->getFullName().getBaseIdentifier().str() + .equals_lower(memberName.getBaseIdentifier().str())); + }); + return (candidate ? candidate->getFullName() : DeclName()); } /// Given a result of name lookup that had no viable results, diagnose the @@ -1362,12 +1373,10 @@ diagnoseUnviableLookupResults(MemberLookupResult &result, Type baseObjTy, // If we found no results at all, mention that fact. if (result.UnviableCandidates.empty()) { - LookupResult correctionResults; + TypoCorrectionResults corrections(CS.TC, memberName, nameLoc); auto tryTypoCorrection = [&] { CS.TC.performTypoCorrection(CS.DC, DeclRefKind::Ordinary, baseObjTy, - memberName, nameLoc.getBaseNameLoc(), - defaultMemberLookupOptions, - correctionResults); + defaultMemberLookupOptions, corrections); }; // TODO: This should handle tuple member lookups, like x.1231 as well. @@ -1382,7 +1391,7 @@ diagnoseUnviableLookupResults(MemberLookupResult &result, Type baseObjTy, tryTypoCorrection(); if (DeclName rightName = findCorrectEnumCaseName(instanceTy, - correctionResults, + corrections, memberName)) { diagnose(loc, diag::could_not_find_enum_case, instanceTy, memberName, rightName) @@ -1390,8 +1399,17 @@ diagnoseUnviableLookupResults(MemberLookupResult &result, Type baseObjTy, rightName.getBaseIdentifier().str()); return; } - diagnose(loc, diag::could_not_find_type_member, instanceTy, memberName) - .highlight(baseRange).highlight(nameLoc.getSourceRange()); + + if (auto correction = corrections.claimUniqueCorrection()) { + auto diagnostic = + diagnose(loc, diag::could_not_find_type_member_corrected, + instanceTy, memberName, correction->CorrectedName); + diagnostic.highlight(baseRange).highlight(nameLoc.getSourceRange()); + correction->addFixits(diagnostic); + } else { + diagnose(loc, diag::could_not_find_type_member, instanceTy, memberName) + .highlight(baseRange).highlight(nameLoc.getSourceRange()); + } } else if (auto moduleTy = baseObjTy->getAs()) { diagnose(baseExpr->getLoc(), diag::no_member_of_module, moduleTy->getModule()->getName(), memberName) @@ -1399,31 +1417,43 @@ diagnoseUnviableLookupResults(MemberLookupResult &result, Type baseObjTy, .highlight(nameLoc.getSourceRange()); return; } else { - diagnose(loc, diag::could_not_find_value_member, - baseObjTy, memberName) - .highlight(baseRange).highlight(nameLoc.getSourceRange()); - tryTypoCorrection(); + auto emitBasicError = [&] { + diagnose(loc, diag::could_not_find_value_member, + baseObjTy, memberName) + .highlight(baseRange).highlight(nameLoc.getSourceRange()); + }; // Check for a few common cases that can cause missing members. if (baseObjTy->is() && memberName.isSimpleName("rawValue")) { auto loc = baseObjTy->castTo()->getDecl()->getNameLoc(); if (loc.isValid()) { + emitBasicError(); diagnose(loc, diag::did_you_mean_raw_type); - return; // Always prefer this over typo corrections. + return; } } else if (baseObjTy->isAny()) { + emitBasicError(); diagnose(loc, diag::any_as_anyobject_fixit) .fixItInsert(baseExpr->getStartLoc(), "(") .fixItInsertAfter(baseExpr->getEndLoc(), " as AnyObject)"); return; } + + tryTypoCorrection(); + + if (auto correction = corrections.claimUniqueCorrection()) { + auto diagnostic = + diagnose(loc, diag::could_not_find_value_member_corrected, + baseObjTy, memberName, correction->CorrectedName); + diagnostic.highlight(baseRange).highlight(nameLoc.getSourceRange()); + correction->addFixits(diagnostic); + } else { + emitBasicError(); + } } // Note all the correction candidates. - for (auto &correction : correctionResults) { - CS.TC.noteTypoCorrection(memberName, nameLoc, - correction.getValueDecl()); - } + corrections.noteAllCandidates(); // TODO: recover? return; @@ -1546,8 +1576,15 @@ diagnoseUnviableLookupResults(MemberLookupResult &result, Type baseObjTy, case MemberLookupResult::UR_Inaccessible: { auto decl = result.UnviableCandidates[0].first.getDecl(); // FIXME: What if the unviable candidates have different levels of access? + // + // If we found an inaccessible member of a protocol extension, it might + // be declared 'public'. This can only happen if the protocol is not + // visible to us, but the conforming type is. In this case, we need to + // clamp the formal access for diagnostics purposes to the formal access + // of the protocol itself. diagnose(nameLoc, diag::candidate_inaccessible, decl->getBaseName(), - decl->getFormalAccess()); + decl->adjustAccessLevelForProtocolExtension( + decl->getFormalAccess())); for (auto cand : result.UnviableCandidates) diagnose(cand.first.getDecl(), diag::decl_declared_here, memberName); @@ -1608,7 +1645,7 @@ bool FailureDiagnosis::diagnoseGeneralOverloadFailure(Constraint *constraint) { // This happens, for example, with ambiguous OverloadedDeclRefExprs. We should // just implement visitOverloadedDeclRefExprs and nuke this. - + // If we couldn't resolve an argument, then produce a generic "ambiguity" // diagnostic. diagnose(anchor->getLoc(), diag::ambiguous_member_overload_set, @@ -1783,8 +1820,8 @@ bool FailureDiagnosis::diagnoseGeneralConversionFailure(Constraint *constraint){ if (!srcFT->isNoEscape()) destExtInfo = destExtInfo.withNoEscape(false); if (!srcFT->throws()) destExtInfo = destExtInfo.withThrows(false); if (destExtInfo != destFT->getExtInfo()) - toType = FunctionType::get(destFT->getInput(), - destFT->getResult(), destExtInfo); + toType = FunctionType::get(destFT->getParams(), destFT->getResult(), + destExtInfo); // If this is a function conversion that discards throwability or // noescape, emit a specific diagnostic about that. @@ -2157,98 +2194,6 @@ namespace { }; } // end anonymous namespace -/// Erase an expression tree's open existentials after a re-typecheck operation. -/// -/// This is done in the case of a typecheck failure, after we re-typecheck -/// partially-typechecked subexpressions in a context-free manner. -/// -static void eraseOpenedExistentials(Expr *&expr, ConstraintSystem &CS) { - - class ExistentialEraser : public ASTWalker { - ConstraintSystem &CS; - llvm::SmallDenseMap OpenExistentials; - - public: - ExistentialEraser(ConstraintSystem &CS) : CS(CS) {} - - std::pair walkToExprPre(Expr *expr) override { - if (auto OOE = dyn_cast(expr)) { - auto archetypeVal = OOE->getOpaqueValue(); - auto base = OOE->getExistentialValue(); - - // Walk the base expression to ensure we erase any existentials within - // it. - base = base->walk(*this); - - registerOpaqueValue(archetypeVal, base); - return { true, OOE->getSubExpr() }; - } - - if (auto *MTEE = dyn_cast(expr)) { - registerOpaqueValue(MTEE->getOpaqueValue(), - MTEE->getNonescapingClosureValue()); - return {true, MTEE->getSubExpr()}; - } - - if (auto OVE = dyn_cast(expr)) { - auto value = OpenExistentials.find(OVE); - assert(value != OpenExistentials.end() && - "didn't see this OVE in a containing OpenExistentialExpr?"); - return { true, value->second }; - } - - // Handle collection upcasts specially so that we don't blow up on - // their embedded OVEs. - if (auto CDE = dyn_cast(expr)) { - if (auto result = CDE->getSubExpr()->walk(*this)) { - CDE->setSubExpr(result); - return { false, CDE }; - } else { - return { true, CDE }; - } - } - - return { true, expr }; - } - - Expr *walkToExprPost(Expr *expr) override { - if (!CS.hasType(expr)) - return expr; - - Type type = CS.getType(expr); - if (!type->hasOpenedExistential()) - return expr; - - type = type.transform([&](Type type) -> Type { - if (auto archetype = type->getAs()) - if (auto existentialType = archetype->getOpenedExistentialType()) - return existentialType; - - return type; - }); - CS.setType(expr, type); - // Set new type to the expression directly. - expr->setType(type); - - return expr; - } - - // Don't walk into statements. This handles the BraceStmt in - // non-single-expr closures, so we don't walk into their body. - std::pair walkToStmtPre(Stmt *S) override { - return { false, S }; - } - - void registerOpaqueValue(OpaqueValueExpr *opaque, Expr *base) { - bool inserted = OpenExistentials.insert({opaque, base}).second; - assert(inserted && "OpaqueValue appears multiple times?"); - (void)inserted; - } - }; - - expr = expr->walk(ExistentialEraser(CS)); -} - /// Unless we've already done this, retypecheck the specified subexpression on /// its own, without including any contextual constraints or parent expr /// nodes. This is more likely to succeed than type checking the original @@ -2338,12 +2283,6 @@ Expr *FailureDiagnosis::typeCheckChildIndependently( TCEOptions |= TypeCheckExprFlags::PreferForceUnwrapToOptional; } - // Ensure that the expression we're about to type-check doesn't have - // anything that the type-checker doesn't expect to see. This can happen - // because of repeated type-checking; the removal below, while independently - // important, isn't itself sufficient because of AST mutation. - eraseOpenedExistentials(subExpr, CS); - auto resultTy = CS.TC.typeCheckExpression( subExpr, CS.DC, TypeLoc::withoutLoc(convertType), convertTypePurpose, TCEOptions, listener, &CS); @@ -2356,7 +2295,8 @@ Expr *FailureDiagnosis::typeCheckChildIndependently( // holding on to an expression containing open existential types but // no OpenExistentialExpr, which breaks invariants enforced by the // ASTChecker. - eraseOpenedExistentials(subExpr, CS); + if (isa(subExpr)) + eraseOpenedExistentials(CS, subExpr); // If recursive type checking failed, then an error was emitted. Return // null to indicate this to the caller. @@ -2855,7 +2795,8 @@ static bool tryDiagnoseNonEscapingParameterToEscaping( } bool FailureDiagnosis::diagnoseContextualConversionError( - Expr *expr, Type contextualType, ContextualTypePurpose CTP) { + Expr *expr, Type contextualType, ContextualTypePurpose CTP, + Type suggestedType) { // If the constraint system has a contextual type, then we can test to see if // this is the problem that prevents us from solving the system. if (!contextualType) { @@ -2874,11 +2815,16 @@ bool FailureDiagnosis::diagnoseContextualConversionError( if (contextualType->is()) options |= TCC_AllowLValue; - auto recheckedExpr = typeCheckChildIndependently(expr, options); + auto *recheckedExpr = typeCheckChildIndependently(expr, options); auto exprType = recheckedExpr ? CS.getType(recheckedExpr) : Type(); + // If there is a suggested type and re-typecheck failed, let's use it. + if (!exprType) + exprType = suggestedType; + // If it failed and diagnosed something, then we're done. - if (!exprType) return true; + if (!exprType) + return CS.TC.Diags.hadAnyError(); // If we contextually had an inout type, and got a non-lvalue result, then // we fail with a mutability error. @@ -3032,6 +2978,10 @@ bool FailureDiagnosis::diagnoseContextualConversionError( // If we're diagnostic an issue with 'nil', produce a specific diagnostic, // instead of uttering ExpressibleByNilLiteral. if (isa(expr->getValueProvidingExpr())) { + // If the source type is some kind of optional, the contextual conversion + // to 'nil' didn't fail, something else did. + if (contextualType->getOptionalObjectType()) + return false; diagnose(expr->getLoc(), nilDiag, contextualType); if (nilFollowup) nilFollowup(); @@ -3049,7 +2999,7 @@ bool FailureDiagnosis::diagnoseContextualConversionError( // If we're trying to convert something of type "() -> T" to T, then we // probably meant to call the value. if (auto srcFT = exprType->getAs()) { - if (srcFT->getInput()->isVoid() && + if (srcFT->getParams().empty() && !isUnresolvedOrTypeVarType(srcFT->getResult()) && CS.TC.isConvertibleTo(srcFT->getResult(), contextualType, CS.DC)) { diagnose(expr->getLoc(), diag::missing_nullary_call, srcFT->getResult()) @@ -3142,7 +3092,7 @@ bool FailureDiagnosis::diagnoseContextualConversionError( if (!srcFT->isNoEscape()) destExtInfo = destExtInfo.withNoEscape(false); if (!srcFT->throws()) destExtInfo = destExtInfo.withThrows(false); if (destExtInfo != destFT->getExtInfo()) - contextualType = FunctionType::get(destFT->getInput(), + contextualType = FunctionType::get(destFT->getParams(), destFT->getResult(), destExtInfo); // If this is a function conversion that discards throwability or @@ -3226,7 +3176,9 @@ void ConstraintSystem::diagnoseAssignmentFailure(Expr *dest, Type destTy, } Diag diagID; - if (isa(dest)) + if (isa(dest)) + diagID = diag::assignment_lhs_is_apply_expression; + else if (isa(dest)) diagID = diag::assignment_lhs_is_immutable_variable; else if (isa(dest)) diagID = diag::assignment_bang_has_immutable_subcomponent; @@ -3234,7 +3186,7 @@ void ConstraintSystem::diagnoseAssignmentFailure(Expr *dest, Type destTy, diagID = diag::assignment_lhs_is_immutable_property; else if (auto sub = dyn_cast(dest)) { diagID = diag::assignment_subscript_has_immutable_base; - + // If the destination is a subscript with a 'dynamicLookup:' label and if // the tuple is implicit, then this was actually a @dynamicMemberLookup // access. Emit a more specific diagnostic. @@ -3428,7 +3380,7 @@ typeCheckArgumentChildIndependently(Expr *argExpr, Type argType, // identify operators that have implicit inout arguments. Type exampleInputType; if (!candidates.empty()) { - exampleInputType = candidates[0].getArgumentType(); + exampleInputType = candidates[0].getArgumentType(CS.getASTContext()); // If we found a single candidate, and have no contextually known argument // type information, use that one candidate as the type information for @@ -3437,7 +3389,7 @@ typeCheckArgumentChildIndependently(Expr *argExpr, Type argType, // TODO: If all candidates have the same type for some argument, we could // pass down partial information. if (candidates.size() == 1 && !argType) - argType = candidates[0].getArgumentType(); + argType = candidates[0].getArgumentType(CS.getASTContext()); } // If our candidates are instance members at curry level #0, then the argument @@ -3511,7 +3463,7 @@ typeCheckArgumentChildIndependently(Expr *argExpr, Type argType, if (candidates.empty()) { defaultMap.assign(params.size(), false); } else { - computeDefaultMap(argType, candidates[0].getDecl(), + computeDefaultMap(params, candidates[0].getDecl(), candidates[0].level, defaultMap); } @@ -3620,6 +3572,58 @@ typeCheckArgumentChildIndependently(Expr *argExpr, Type argType, TE->isImplicit(), TT)); } +static DeclName getBaseName(DeclContext *context) { + if (auto generic = context->getAsNominalTypeOrNominalTypeExtensionContext()) { + return generic->getName(); + } else if (context->isModuleScopeContext()) + return context->getParentModule()->getName(); + else + llvm_unreachable("Unsupported base"); +}; + +static void emitFixItForExplicitlyQualifiedReference( + TypeChecker &tc, UnresolvedDotExpr *UDE, + decltype(diag::fix_unqualified_access_top_level) diag, DeclName baseName, + DescriptiveDeclKind kind) { + auto name = baseName.getBaseIdentifier(); + SmallString<32> namePlusDot = name.str(); + namePlusDot.push_back('.'); + + tc.diagnose(UDE->getLoc(), diag, namePlusDot, kind, name) + .fixItInsert(UDE->getStartLoc(), namePlusDot); +} + +void ConstraintSystem::diagnoseDeprecatedConditionalConformanceOuterAccess( + UnresolvedDotExpr *UDE, ValueDecl *choice) { + auto result = TC.lookupUnqualified(DC, UDE->getName(), UDE->getLoc()); + assert(result && "names can't just disappear"); + // These should all come from the same place. + auto exampleInner = result.front(); + auto innerChoice = exampleInner.getValueDecl(); + auto innerDC = exampleInner.getDeclContext()->getInnermostTypeContext(); + auto innerParentDecl = + innerDC->getAsNominalTypeOrNominalTypeExtensionContext(); + auto innerBaseName = getBaseName(innerDC); + + auto choiceKind = choice->getDescriptiveKind(); + auto choiceDC = choice->getDeclContext(); + auto choiceBaseName = getBaseName(choiceDC); + auto choiceParentDecl = choiceDC->getAsDeclOrDeclExtensionContext(); + auto choiceParentKind = choiceParentDecl + ? choiceParentDecl->getDescriptiveKind() + : DescriptiveDeclKind::Module; + + TC.diagnose(UDE->getLoc(), + diag::warn_deprecated_conditional_conformance_outer_access, + UDE->getName(), choiceKind, choiceParentKind, choiceBaseName, + innerChoice->getDescriptiveKind(), + innerParentDecl->getDescriptiveKind(), innerBaseName); + + emitFixItForExplicitlyQualifiedReference( + TC, UDE, diag::fix_deprecated_conditional_conformance_outer_access, + choiceBaseName, choiceKind); +} + static bool diagnoseImplicitSelfErrors(Expr *fnExpr, Expr *argExpr, CalleeCandidateInfo &CCI, ArrayRef argLabels, @@ -3726,16 +3730,6 @@ static bool diagnoseImplicitSelfErrors(Expr *fnExpr, Expr *argExpr, return kind; }; - auto getBaseName = [](DeclContext *context) -> DeclName { - if (auto generic = - context->getAsNominalTypeOrNominalTypeExtensionContext()) { - return generic->getName(); - } else if (context->isModuleScopeContext()) - return context->getParentModule()->getName(); - else - llvm_unreachable("Unsupported base"); - }; - auto diagnoseShadowing = [&](ValueDecl *base, ArrayRef candidates) -> bool { CalleeCandidateInfo calleeInfo(base ? base->getInterfaceType() : nullptr, @@ -3783,13 +3777,8 @@ static bool diagnoseImplicitSelfErrors(Expr *fnExpr, Expr *argExpr, if (baseKind == DescriptiveDeclKind::Module) topLevelDiag = diag::fix_unqualified_access_top_level_multi; - auto name = baseName.getBaseIdentifier(); - SmallString<32> namePlusDot = name.str(); - namePlusDot.push_back('.'); - - TC.diagnose(UDE->getLoc(), topLevelDiag, namePlusDot, - choice->getDescriptiveKind(), name) - .fixItInsert(UDE->getStartLoc(), namePlusDot); + emitFixItForExplicitlyQualifiedReference(TC, UDE, topLevelDiag, baseName, + choice->getDescriptiveKind()); for (auto &candidate : calleeInfo.candidates) { if (auto decl = candidate.getDecl()) @@ -3867,8 +3856,7 @@ static bool diagnoseInstanceMethodAsCurriedMemberOnType(CalleeCandidateInfo &CCI, Expr *fnExpr, Expr *argExpr) { for (auto &candidate : CCI.candidates) { - auto argTy = candidate.getArgumentType(); - if (!argTy) + if (!candidate.hasParameters()) return false; auto *decl = candidate.getDecl(); @@ -3883,9 +3871,10 @@ diagnoseInstanceMethodAsCurriedMemberOnType(CalleeCandidateInfo &CCI, (decl->isInstanceMember() && candidate.level == 1)) continue; - auto params = candidate.getUncurriedFunctionType()->getParams(); + auto params = candidate.getParameters(); + SmallVector defaultMap; - computeDefaultMap(argTy, decl, candidate.level, defaultMap); + computeDefaultMap(params, decl, candidate.level, defaultMap); // If one of the candidates is an instance method with a single parameter // at the level 0, this might be viable situation for calling instance // method as curried member of type problem. @@ -3967,7 +3956,8 @@ diagnoseInstanceMethodAsCurriedMemberOnType(CalleeCandidateInfo &CCI, } static bool diagnoseTupleParameterMismatch(CalleeCandidateInfo &CCI, - Type paramType, Type argType, + ArrayRef params, + ArrayRef args, Expr *fnExpr, Expr *argExpr, bool isTopLevel = true) { // Try to diagnose function call tuple parameter splat only if @@ -3984,72 +3974,95 @@ static bool diagnoseTupleParameterMismatch(CalleeCandidateInfo &CCI, } } - if (auto *paramFnType = paramType->getAs()) { - // Only if both of the parameter and argument types are functions - // let's recur into diagnosing their arguments. - if (auto *argFnType = argType->getAs()) - return diagnoseTupleParameterMismatch(CCI, paramFnType->getInput(), - argFnType->getInput(), fnExpr, - argExpr, /* isTopLevel */ false); - return false; + if (params.size() == 1 && args.size() == 1) { + auto paramType = params.front().getType(); + auto argType = args.front().getType(); + + if (auto *paramFnType = paramType->getAs()) { + // Only if both of the parameter and argument types are functions + // let's recur into diagnosing their arguments. + if (auto *argFnType = argType->getAs()) + return diagnoseTupleParameterMismatch(CCI, paramFnType->getParams(), + argFnType->getParams(), fnExpr, + argExpr, /* isTopLevel */ false); + return false; + } } - unsigned parameterCount = 1, argumentCount = 1; + if (params.size() != 1 || args.empty()) + return false; - // Don't try to desugar ParenType which is going to result in incorrect - // inferred argument/parameter count. + auto paramType = params.front().getType(); - if (auto *paramTypeTy = dyn_cast(paramType.getPointer())) - parameterCount = paramTypeTy->getNumElements(); + if (args.size() == 1) { + auto argType = args.front().getType(); + if (auto *paramFnType = paramType->getAs()) { + // Only if both of the parameter and argument types are functions + // let's recur into diagnosing their arguments. + if (auto *argFnType = argType->getAs()) + return diagnoseTupleParameterMismatch(CCI, paramFnType->getParams(), + argFnType->getParams(), fnExpr, + argExpr, /* isTopLevel */ false); + } - if (auto *argTupleTy = dyn_cast(argType.getPointer())) - argumentCount = argTupleTy->getNumElements(); + return false; + } - if (parameterCount == 1 && argumentCount > 1) { - // Let's see if inferred argument is actually a tuple inside of Paren. - auto *paramTupleTy = paramType->getAs(); - if (!paramTupleTy) - return false; + // Let's see if inferred argument is actually a tuple inside of Paren. + auto *paramTupleTy = paramType->getAs(); + if (!paramTupleTy) + return false; - // Looks like the number of tuple elements matches number - // of function arguments, which means we can we can emit an - // error about an attempt to make use of tuple splat or tuple - // destructuring, unfortunately we can't provide a fix-it for - // this case. - if (paramTupleTy->getNumElements() == argumentCount) { - auto &TC = CCI.CS.TC; - if (isTopLevel) { - if (auto *decl = CCI[0].getDecl()) { - Identifier name; - auto kind = decl->getDescriptiveKind(); - // Constructors/descructors and subscripts don't really have names. - if (!(isa(decl) || isa(decl) || - isa(decl))) { - name = decl->getBaseName().getIdentifier(); - } + if (paramTupleTy->getNumElements() != args.size()) + return false; - TC.diagnose(argExpr->getLoc(), diag::single_tuple_parameter_mismatch, - kind, name, paramType, !name.empty()) - .highlight(argExpr->getSourceRange()) - .fixItInsertAfter(argExpr->getStartLoc(), "(") - .fixItInsert(argExpr->getEndLoc(), ")"); - } else { - TC.diagnose(argExpr->getLoc(), - diag::unknown_single_tuple_parameter_mismatch, paramType) - .highlight(argExpr->getSourceRange()) - .fixItInsertAfter(argExpr->getStartLoc(), "(") - .fixItInsert(argExpr->getEndLoc(), ")"); - } - } else { - TC.diagnose(argExpr->getLoc(), - diag::nested_tuple_parameter_destructuring, paramType, - CCI.CS.getType(fnExpr)); + // Looks like the number of tuple elements matches number + // of function arguments, which means we can we can emit an + // error about an attempt to make use of tuple splat or tuple + // destructuring, unfortunately we can't provide a fix-it for + // this case. + auto &TC = CCI.CS.TC; + if (isTopLevel) { + if (auto *decl = CCI[0].getDecl()) { + Identifier name; + auto kind = decl->getDescriptiveKind(); + // Constructors/descructors and subscripts don't really have names. + if (!(isa(decl) || isa(decl) || + isa(decl))) { + name = decl->getBaseName().getIdentifier(); } - return true; + + TC.diagnose(argExpr->getLoc(), diag::single_tuple_parameter_mismatch, + kind, name, paramTupleTy, !name.empty()) + .highlight(argExpr->getSourceRange()) + .fixItInsertAfter(argExpr->getStartLoc(), "(") + .fixItInsert(argExpr->getEndLoc(), ")"); + } else { + TC.diagnose(argExpr->getLoc(), + diag::unknown_single_tuple_parameter_mismatch, paramTupleTy) + .highlight(argExpr->getSourceRange()) + .fixItInsertAfter(argExpr->getStartLoc(), "(") + .fixItInsert(argExpr->getEndLoc(), ")"); } + } else { + TC.diagnose(argExpr->getLoc(), + diag::nested_tuple_parameter_destructuring, paramTupleTy, + CCI.CS.getType(fnExpr)); } - return false; + return true; +} + +static bool diagnoseTupleParameterMismatch(CalleeCandidateInfo &CCI, + ArrayRef params, + Type argType, Expr *fnExpr, + Expr *argExpr, + bool isTopLevel = true) { + llvm::SmallVector args; + FunctionType::decomposeInput(argType, args); + + return diagnoseTupleParameterMismatch(CCI, params, args, fnExpr, argExpr, + isTopLevel); } class ArgumentMatcher : public MatchCallArgumentListener { @@ -4361,14 +4374,13 @@ diagnoseSingleCandidateFailures(CalleeCandidateInfo &CCI, Expr *fnExpr, auto candidate = CCI[0]; auto &TC = CCI.CS.TC; - auto argTy = candidate.getArgumentType(); - if (!argTy) + if (!candidate.hasParameters()) return false; - auto params = candidate.getUncurriedFunctionType()->getParams(); + auto params = candidate.getParameters(); + SmallVector defaultMap; - computeDefaultMap(argTy, candidate.getDecl(), candidate.level, - defaultMap); + computeDefaultMap(params, candidate.getDecl(), candidate.level, defaultMap); auto args = decomposeArgType(CCI.CS.getType(argExpr), argLabels); // Check the case where a raw-representable type is constructed from an @@ -4409,9 +4421,8 @@ diagnoseSingleCandidateFailures(CalleeCandidateInfo &CCI, Expr *fnExpr, } } - if (diagnoseTupleParameterMismatch(CCI, candidate.getArgumentType(), - CCI.CS.getType(argExpr), fnExpr, - argExpr)) + if (diagnoseTupleParameterMismatch(CCI, candidate.getParameters(), + CCI.CS.getType(argExpr), fnExpr, argExpr)) return true; // We only handle structural errors here. @@ -4484,11 +4495,13 @@ static bool diagnoseRawRepresentableMismatch(CalleeCandidateInfo &CCI, if (!decl) continue; - auto parameters = candidate.getUncurriedFunctionType()->getParams(); - SmallVector defaultMap; - computeDefaultMap(candidate.getArgumentType(), decl, - candidate.level, defaultMap); + if (!candidate.hasParameters()) + continue; + + auto parameters = candidate.getParameters(); + SmallVector defaultMap; + computeDefaultMap(parameters, decl, candidate.level, defaultMap); if (parameters.size() != arguments.size()) continue; @@ -4721,7 +4734,9 @@ bool FailureDiagnosis::diagnoseSubscriptErrors(SubscriptExpr *SE, auto candType = baseType->getTypeOfMember(CS.DC->getParentModule(), cand.getDecl(), nullptr); if (auto *candFunc = candType->getAs()) { - auto paramsType = candFunc->getInput(); + auto paramsType = FunctionType::composeInput(CS.getASTContext(), + candFunc->getParams(), + false); if (!typeCheckChildIndependently( indexExpr, paramsType, CTP_CallArgument, TCC_ForceRecheck)) return true; @@ -4986,11 +5001,13 @@ bool FailureDiagnosis::diagnoseArgumentGenericRequirements( return false; auto const &candidate = candidates.candidates[0]; - auto params = candidate.getUncurriedFunctionType()->getParams(); - SmallVector defaultMap; - computeDefaultMap(candidate.getArgumentType(), candidate.getDecl(), - candidate.level, defaultMap); + if (!candidate.hasParameters()) + return false; + + auto params = candidate.getParameters(); + SmallVector defaultMap; + computeDefaultMap(params, candidate.getDecl(), candidate.level, defaultMap); auto args = decomposeArgType(CS.getType(argExpr), argLabels); SmallVector bindings; @@ -5270,7 +5287,7 @@ bool FailureDiagnosis::diagnoseTrailingClosureErrors(ApplyExpr *callExpr) { } }; - SmallVector possibleTypes; + SmallPtrSet possibleTypes; auto currentType = CS.getType(fnExpr); // If current type has type variables or unresolved types @@ -5290,33 +5307,19 @@ bool FailureDiagnosis::diagnoseTrailingClosureErrors(ApplyExpr *callExpr) { return diagnoseContextualConversionError(callExpr, contextualType, CS.getContextualTypePurpose()); } else { - possibleTypes.push_back(currentType); + possibleTypes.insert(currentType.getPointer()); } - for (auto type : possibleTypes) { + for (Type type : possibleTypes) { auto *fnType = type->getAs(); if (!fnType) continue; - auto paramType = fnType->getInput(); - switch (paramType->getKind()) { - case TypeKind::Tuple: { - auto tuple = paramType->getAs(); - if (tuple->getNumElements() != 1) - continue; - - paramType = tuple->getElement(0).getType(); - break; - } - - case TypeKind::Paren: - paramType = paramType->getWithoutParens(); - break; - - default: + auto params = fnType->getParams(); + if (params.size() != 1) return false; - } + Type paramType = params.front().getType(); if (auto paramFnType = paramType->getAs()) { auto closureType = CS.getType(closureExpr); if (auto *argFnType = closureType->getAs()) { @@ -5369,7 +5372,7 @@ bool FailureDiagnosis::diagnoseTrailingClosureErrors(ApplyExpr *callExpr) { } }; - auto expectedArgType = FunctionType::get(fnType->getInput(), resultType, + auto expectedArgType = FunctionType::get(fnType->getParams(), resultType, fnType->getExtInfo()); llvm::SaveAndRestore SavedDC(CS.DC, DC); @@ -5398,7 +5401,7 @@ bool FailureDiagnosis::diagnoseCallContextualConversionErrors( auto *DC = CS.DC; auto typeCheckExpr = [](TypeChecker &TC, Expr *expr, DeclContext *DC, - SmallVectorImpl &types, + SmallPtrSetImpl &types, Type contextualType = Type()) { CalleeListener listener(contextualType); TC.getPossibleTypesOfExpressionWithoutApplying( @@ -5408,7 +5411,7 @@ bool FailureDiagnosis::diagnoseCallContextualConversionErrors( // First let's type-check expression without contextual type, and // see if that's going to produce a type, if so, let's type-check // again, this time using given contextual type. - SmallVector withoutContextual; + SmallPtrSet withoutContextual; typeCheckExpr(TC, callExpr, DC, withoutContextual); // If there are no types returned, it means that problem was @@ -5417,12 +5420,17 @@ bool FailureDiagnosis::diagnoseCallContextualConversionErrors( if (withoutContextual.empty()) return false; - SmallVector withContextual; + SmallPtrSet withContextual; typeCheckExpr(TC, callExpr, DC, withContextual, contextualType); // If type-checking with contextual type didn't produce any results // it means that we have a contextual mismatch. - if (withContextual.empty()) - return diagnoseContextualConversionError(callExpr, contextualType, CTP); + if (withContextual.empty()) { + // If there is just a single choice, we can hit contextual diagnostics + // about it in case re-typecheck fails. + Type exprType = withoutContextual.size() == 1 ? *withoutContextual.begin() : Type(); + return diagnoseContextualConversionError(callExpr, contextualType, CTP, + exprType); + } // If call produces a single type when type-checked with contextual // expression, it means that the problem is elsewhere, any other @@ -5430,6 +5438,144 @@ bool FailureDiagnosis::diagnoseCallContextualConversionErrors( return false; } +bool FailureDiagnosis::diagnoseSubscriptMisuse(ApplyExpr *callExpr) { + auto UDE = dyn_cast(callExpr->getFn()); + if (!UDE) + return false; + + auto baseExpr = UDE->getBase(); + if (!baseExpr || UDE->getName().getBaseName() != getTokenText(tok::kw_subscript)) + return false; + + auto baseType = CS.getType(baseExpr); + // Look up subscript declarations. + auto lookup = CS.lookupMember(baseType->getRValueType(), + DeclName(DeclBaseName::createSubscript())); + auto nonSubscrLookup = CS.lookupMember(baseType->getRValueType(), + UDE->getName()); + // Make sure we only found subscript declarations. If not, the problem + // is different - return. + if (lookup.empty() || !nonSubscrLookup.empty()) + return false; + // Try to resolve a type of the argument expression. + auto argExpr = typeCheckChildIndependently(callExpr->getArg(), + Type(), CTP_CallArgument); + if (!argExpr) + return CS.TC.Diags.hadAnyError(); + + SmallVector scratch; + ArrayRef argLabels = callExpr->getArgumentLabels(scratch); + SmallVector choices; + + for (auto candidate : lookup) + choices.push_back(OverloadChoice(baseType, candidate.getValueDecl(), + UDE->getFunctionRefKind())); + CalleeCandidateInfo candidateInfo(baseType, choices, + callArgHasTrailingClosure(argExpr), + CS, true); + struct TypeConvertibleChecker { + ConstraintSystem &CS; + + TypeConvertibleChecker(ConstraintSystem &CS) : CS(CS) {} + + // Checks whether type1 is implicitly convertible to type2 + bool isImplicitlyConvertible(Type type1, Type type2) { + if (!type1 || !type2) + return false; + + if (auto tup1 = type1->getAs()) { + if (auto tup2 = type2->getAs()) + return isImplicitlyConvertibleTuple(tup1, tup2); + else + return false; + } + if (type1->isEqual(type2) || type2->is() || + CS.TC.isSubtypeOf(type1, type2, CS.DC)) + return true; + return false; + } + + bool isImplicitlyConvertibleTuple(TupleType *tup1, TupleType *tup2) { + if (tup1->getNumElements() != tup2->getNumElements()) + return false; + + for (unsigned i = 0, e = tup1->getNumElements(); i < e; i ++) { + auto element1 = tup1->getElement(i); + auto element2 = tup2->getElement(i); + if (element1.hasName()) { + if (!element2.hasName() || element1.getName() != + element2.getName()) + return false; + } + if (!isImplicitlyConvertible(element1.getType(), + element2.getType())) + return false; + } + return true; + } + } checker(CS); + auto params = swift::decomposeArgType(CS.getType(argExpr), argLabels); + using ClosenessPair = CalleeCandidateInfo::ClosenessResultTy; + + candidateInfo.filterList([&](UncurriedCandidate cand) -> ClosenessPair { + auto candFuncType = cand.getUncurriedFunctionType(); + if (!candFuncType) + return {CC_GeneralMismatch, {}}; + + auto candParams = candFuncType->getParams(); + if (params.size() != candParams.size()) + return {CC_GeneralMismatch, {}}; + + for (unsigned i = 0, e = params.size(); i < e; i ++) { + if (checker.isImplicitlyConvertible(params[i].getType(), + candParams[i].getType())) + continue; + return {CC_GeneralMismatch, {}}; + } + return {CC_ExactMatch, {}}; + }); + + auto *locator = CS.getConstraintLocator(UDE, ConstraintLocator::Member); + auto memberRange = baseExpr->getSourceRange(); + if (locator) + locator = simplifyLocator(CS, locator, memberRange); + auto nameLoc = DeclNameLoc(memberRange.Start); + + auto diag = diagnose(baseExpr->getLoc(), + diag::could_not_find_subscript_member_did_you_mean, + baseType); + diag.highlight(memberRange).highlight(nameLoc.getSourceRange()); + + auto showNote = [&]() { + diag.flush(); + if (candidateInfo.size() == 1) + diagnose(candidateInfo.candidates.front().getDecl(), + diag::kind_declared_here, DescriptiveDeclKind::Subscript); + }; + if (candidateInfo.closeness != CC_ExactMatch) { + showNote(); + return true; + } + auto toCharSourceRange = Lexer::getCharSourceRangeFromSourceRange; + auto lastArgSymbol = toCharSourceRange(CS.TC.Context.SourceMgr, + argExpr->getEndLoc()); + + diag.fixItReplace(SourceRange(argExpr->getStartLoc()), + getTokenText(tok::l_square)); + diag.fixItRemove(nameLoc.getSourceRange()); + diag.fixItRemove(SourceRange(UDE->getDotLoc())); + if (CS.TC.Context.SourceMgr.extractText(lastArgSymbol) == + getTokenText(tok::r_paren)) + diag.fixItReplace(SourceRange(argExpr->getEndLoc()), + getTokenText(tok::r_square)); + else + diag.fixItInsertAfter(argExpr->getEndLoc(), + getTokenText(tok::r_square)); + showNote(); + + return true; +} + // Check if there is a structural problem in the function expression // by performing type checking with the option to allow unresolved // type variables. If that is going to produce a function type with @@ -5441,7 +5587,7 @@ static bool shouldTypeCheckFunctionExpr(TypeChecker &TC, DeclContext *DC, if (!isa(fnExpr)) return true; - SmallVector fnTypes; + SmallPtrSet fnTypes; TC.getPossibleTypesOfExpressionWithoutApplying(fnExpr, DC, fnTypes, FreeTypeVariableBinding::UnresolvedType); @@ -5449,7 +5595,7 @@ static bool shouldTypeCheckFunctionExpr(TypeChecker &TC, DeclContext *DC, // Some member types depend on the arguments to produce a result type, // type-checking such expressions without associated arguments is // going to produce unrelated diagnostics. - if (auto fn = fnTypes[0]->getAs()) { + if (auto fn = (*fnTypes.begin())->getAs()) { auto resultType = fn->getResult(); if (resultType->hasUnresolvedType() || resultType->hasTypeVariable()) return false; @@ -5476,11 +5622,18 @@ bool FailureDiagnosis::visitApplyExpr(ApplyExpr *callExpr) { auto originalFnType = CS.getType(callExpr->getFn()); if (shouldTypeCheckFunctionExpr(CS.TC, CS.DC, fnExpr)) { + + // If we are misusing a subscript, diagnose that and provide a fixit. + // We diagnose this here to have enough context to offer an appropriate fixit. + if (diagnoseSubscriptMisuse(callExpr)) { + return CS.TC.Diags.hadAnyError(); + } // Type check the function subexpression to resolve a type for it if // possible. fnExpr = typeCheckChildIndependently(callExpr->getFn()); - if (!fnExpr) - return true; + if (!fnExpr) { + return CS.TC.Diags.hadAnyError(); + } } SWIFT_DEFER { @@ -5504,7 +5657,7 @@ bool FailureDiagnosis::visitApplyExpr(ApplyExpr *callExpr) { isa(callExpr->getFn())) { fnExpr = callExpr->getFn(); - SmallVector types; + SmallPtrSet types; CS.TC.getPossibleTypesOfExpressionWithoutApplying(fnExpr, CS.DC, types); auto isFunctionType = [getFuncType](Type type) -> bool { @@ -5653,10 +5806,11 @@ bool FailureDiagnosis::visitApplyExpr(ApplyExpr *callExpr) { calleeInfo)) return true; - Type argType; // Type of the argument list, if knowable. - if (auto FTy = fnType->getAs()) - argType = FTy->getInput(); - else if (auto MTT = fnType->getAs()) { + Type argType; // argument list, if known. + if (auto FTy = fnType->getAs()) { + argType = FunctionType::composeInput(CS.getASTContext(), FTy->getParams(), + false); + } else if (auto MTT = fnType->getAs()) { // If we are constructing a tuple with initializer syntax, the expected // argument list is the tuple type itself - and there is no initdecl. auto instanceTy = MTT->getInstanceType(); @@ -6293,7 +6447,7 @@ bool FailureDiagnosis::visitClosureExpr(ClosureExpr *CE) { bool FailureDiagnosis::diagnoseClosureExpr( ClosureExpr *CE, Type contextualType, - std::function resultTypeProcessor) { + llvm::function_ref resultTypeProcessor) { // Look through IUO because it doesn't influence // neither parameter nor return type diagnostics itself, // but if we have function type inside, that might @@ -6313,7 +6467,7 @@ bool FailureDiagnosis::diagnoseClosureExpr( if (contextualType && contextualType->is()) { auto fnType = contextualType->getAs(); auto *params = CE->getParameters(); - Type inferredArgType = fnType->getInput(); + auto inferredArgs = fnType->getParams(); // It is very common for a contextual type to disagree with the argument // list built into the closure expr. This can be because the closure expr @@ -6323,11 +6477,7 @@ bool FailureDiagnosis::diagnoseClosureExpr( // { $0 + $1 } // in either case, we want to produce nice and clear diagnostics. unsigned actualArgCount = params->size(); - unsigned inferredArgCount = 1; - // Don't try to desugar ParenType which is going to result in incorrect - // inferred argument count. - if (auto *argTupleTy = dyn_cast(inferredArgType.getPointer())) - inferredArgCount = argTupleTy->getNumElements(); + unsigned inferredArgCount = inferredArgs.size(); if (actualArgCount != inferredArgCount) { // If the closure didn't specify any arguments and it is in a context that @@ -6336,31 +6486,34 @@ bool FailureDiagnosis::diagnoseClosureExpr( auto diag = diagnose(CE->getStartLoc(), diag::closure_argument_list_missing, inferredArgCount); - StringRef fixText; // We only handle the most common cases. - if (inferredArgCount == 1) - fixText = " _ in "; - else if (inferredArgCount == 2) - fixText = " _,_ in "; - else if (inferredArgCount == 3) - fixText = " _,_,_ in "; - + std::string fixText; // Let's provide fixits for up to 10 args. + + if (inferredArgCount <= 10) { + fixText += " _"; + for (unsigned i = 0; i < inferredArgCount - 1; i ++) { + fixText += ",_"; + } + fixText += " in "; + } + if (!fixText.empty()) { // Determine if there is already a space after the { in the closure to // make sure we introduce the right whitespace. auto afterBrace = CE->getStartLoc().getAdvancedLoc(1); auto text = CS.TC.Context.SourceMgr.extractText({afterBrace, 1}); if (text.size() == 1 && text == " ") - fixText = fixText.drop_back(); + fixText = fixText.erase(fixText.size() - 1); else - fixText = fixText.drop_front(); + fixText = fixText.erase(0, 1); diag.fixItInsertAfter(CE->getStartLoc(), fixText); } return true; } if (inferredArgCount == 1 && actualArgCount > 1) { + auto *argTupleTy = inferredArgs.front().getType()->getAs(); // Let's see if inferred argument is actually a tuple inside of Paren. - if (auto *argTupleTy = inferredArgType->getAs()) { + if (argTupleTy) { // Looks like the number of closure parameters matches number // of inferred arguments, which means we can we can emit an // error about an attempt to make use of tuple splat or tuple @@ -6381,9 +6534,6 @@ bool FailureDiagnosis::diagnoseClosureExpr( auto diag = diagnose(params->getStartLoc(), diag::closure_tuple_parameter_destructuring, argTupleTy); - Type actualArgType; - if (auto *actualFnType = CS.getType(CE)->getAs()) - actualArgType = actualFnType->getInput(); auto *closureBody = CE->getBody(); if (!closureBody) @@ -6503,7 +6653,8 @@ bool FailureDiagnosis::diagnoseClosureExpr( // Okay, the wrong number of arguments was used, complain about that. // Before doing so, strip attributes off the function type so that they // don't confuse the issue. - fnType = FunctionType::get(fnType->getInput(), fnType->getResult()); + fnType = FunctionType::get(fnType->getParams(), fnType->getResult(), + fnType->getExtInfo(), false); auto diag = diagnose( params->getStartLoc(), diag::closure_argument_list_tuple, fnType, inferredArgCount, actualArgCount, (actualArgCount == 1)); @@ -6557,18 +6708,18 @@ bool FailureDiagnosis::diagnoseClosureExpr( } expectedResultType = fnType->getResult(); - } else { - // Defend against type variables from our constraint system leaking into - // recursive constraints systems formed when checking the body of the - // closure. These typevars come into them when the body does name - // lookups against the parameter decls. - // - // Handle this by rewriting the arguments to UnresolvedType(). - for (auto VD : *CE->getParameters()) { - if (VD->getType()->hasTypeVariable() || VD->getType()->hasError()) { - VD->setType(CS.getASTContext().TheUnresolvedType); - VD->setInterfaceType(VD->getType()->getInOutObjectType()); - } + } + + // Defend against type variables from our constraint system leaking into + // recursive constraints systems formed when checking the body of the + // closure. These typevars come into them when the body does name + // lookups against the parameter decls. + // + // Handle this by rewriting the arguments to UnresolvedType(). + for (auto VD : *CE->getParameters()) { + if (VD->getType()->hasTypeVariable() || VD->getType()->hasError()) { + VD->setType(CS.getASTContext().TheUnresolvedType); + VD->setInterfaceType(VD->getType()->getInOutObjectType()); } } @@ -6889,11 +7040,13 @@ static bool diagnoseKeyPathComponents(ConstraintSystem &CS, KeyPathExpr *KPE, // If we didn't find anything, try to apply typo-correction. bool resultsAreFromTypoCorrection = false; if (!lookup) { + TypoCorrectionResults corrections(TC, componentName, + DeclNameLoc(componentNameLoc)); + TC.performTypoCorrection(CS.DC, DeclRefKind::Ordinary, lookupType, - componentName, componentNameLoc, (lookupType ? defaultMemberTypeLookupOptions : defaultUnqualifiedLookupOptions), - lookup); + corrections); if (currentType) TC.diagnose(componentNameLoc, diag::could_not_find_type_member, @@ -6903,10 +7056,8 @@ static bool diagnoseKeyPathComponents(ConstraintSystem &CS, KeyPathExpr *KPE, componentName, false); // Note all the correction candidates. - for (auto &result : lookup) { - TC.noteTypoCorrection(componentName, DeclNameLoc(componentNameLoc), - result.getValueDecl()); - } + corrections.noteAllCandidates(); + corrections.addAllCandidatesToLookup(lookup); isInvalid = true; if (!lookup) @@ -6919,7 +7070,7 @@ static bool diagnoseKeyPathComponents(ConstraintSystem &CS, KeyPathExpr *KPE, // If we have more than one result, filter out unavailable or // obviously unusable candidates. if (lookup.size() > 1) { - lookup.filter([&](LookupResultEntry result) -> bool { + lookup.filter([&](LookupResultEntry result, bool isOuter) -> bool { // Drop unavailable candidates. if (result.getValueDecl()->getAttrs().isUnavailable(TC.Context)) return false; @@ -7071,7 +7222,7 @@ bool FailureDiagnosis::visitKeyPathExpr(KeyPathExpr *KPE) { bool builtConstraints(ConstraintSystem &cs, Expr *expr) override { auto *locator = cs.getConstraintLocator(expr); - auto valueType = cs.createTypeVariable(locator, /*options*/0); + auto valueType = cs.createTypeVariable(locator); auto keyPathType = BoundGenericClassType::get(Decl, ParentType, {RootType, valueType}); @@ -7382,7 +7533,7 @@ bool FailureDiagnosis::visitUnresolvedMemberExpr(UnresolvedMemberExpr *E) { } auto *argExpr = E->getArgument(); - auto candidateArgTy = candidateInfo[0].getArgumentType(); + auto candidateArgTy = candidateInfo[0].getArgumentType(CS.getASTContext()); // Depending on how we matched, produce tailored diagnostics. switch (candidateInfo.closeness) { @@ -7561,7 +7712,7 @@ bool FailureDiagnosis::diagnoseMemberFailures( // call the function, e.g. in "a.b.c" where they had to write "a.b().c". // Produce a specific diagnostic + fixit for this situation. if (auto baseFTy = baseObjTy->getAs()) { - if (baseExpr && baseFTy->getInput()->isVoid()) { + if (baseExpr && baseFTy->getParams().empty()) { SourceLoc insertLoc = baseExpr->getEndLoc(); if (auto *DRE = dyn_cast(baseExpr)) { @@ -7587,18 +7738,23 @@ bool FailureDiagnosis::diagnoseMemberFailures( // If this is a tuple, then the index needs to be valid. if (auto tuple = baseObjTy->getAs()) { - StringRef nameStr = memberName.getBaseIdentifier().str(); - int fieldIdx = -1; - // Resolve a number reference into the tuple type. - unsigned Value = 0; - if (!nameStr.getAsInteger(10, Value) && Value < tuple->getNumElements()) { - fieldIdx = Value; - } else { - fieldIdx = tuple->getNamedElementId(memberName.getBaseIdentifier()); - } + auto baseName = memberName.getBaseName(); + + if (!baseName.isSpecial()) { + StringRef nameStr = baseName.userFacingName(); + + int fieldIdx = -1; + // Resolve a number reference into the tuple type. + unsigned Value = 0; + if (!nameStr.getAsInteger(10, Value) && Value < tuple->getNumElements()) { + fieldIdx = Value; + } else { + fieldIdx = tuple->getNamedElementId(memberName.getBaseIdentifier()); + } - if (fieldIdx != -1) - return false; // Lookup is valid. + if (fieldIdx != -1) + return false; // Lookup is valid. + } diagnose(BaseLoc, diag::could_not_find_tuple_member, baseObjTy, memberName) .highlight(memberRange); diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 4c9e3a0310679..4d04dc558d282 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -562,7 +562,7 @@ namespace { /// overloads which inhibit any overload from being favored. void favorCallOverloads(ApplyExpr *expr, ConstraintSystem &CS, - std::function isFavored, + llvm::function_ref isFavored, std::function mustConsider = nullptr) { // Find the type variable associated with the function, if any. @@ -712,8 +712,9 @@ namespace { if (value->getDeclContext()->isTypeContext()) { fnTy = fnTy->getResult()->castTo(); } - - Type paramTy = fnTy->getInput(); + + Type paramTy = FunctionType::composeInput(CS.getASTContext(), + fnTy->getParams(), false); auto resultTy = fnTy->getResult(); auto contextualTy = CS.getContextualType(expr); @@ -787,7 +788,10 @@ namespace { } } } - Type paramTy = fnTy->getInput(); + + auto paramTy = + AnyFunctionType::composeInput(CS.getASTContext(), fnTy->getParams(), + /*canonicalVararg*/ false); return favoredTy->isEqual(paramTy); }; @@ -884,15 +888,14 @@ namespace { if (value->getDeclContext()->isTypeContext()) { fnTy = fnTy->getResult()->castTo(); } - - Type paramTy = fnTy->getInput(); - auto paramTupleTy = paramTy->getAs(); - if (!paramTupleTy || paramTupleTy->getNumElements() != 2) + + auto params = fnTy->getParams(); + if (params.size() != 2) return false; - - auto firstParamTy = paramTupleTy->getElement(0).getType(); - auto secondParamTy = paramTupleTy->getElement(1).getType(); - + + auto firstParamTy = params[0].getType(); + auto secondParamTy = params[1].getType(); + auto resultTy = fnTy->getResult(); auto contextualTy = CS.getContextualType(expr); @@ -978,7 +981,8 @@ namespace { /// \brief Add constraints for a reference to a named member of the given /// base type, and return the type of such a reference. Type addMemberRefConstraints(Expr *expr, Expr *base, DeclName name, - FunctionRefKind functionRefKind) { + FunctionRefKind functionRefKind, + ArrayRef outerAlternatives) { // The base must have a member of the given name, such that accessing // that member through the base returns a value convertible to the type // of this expression. @@ -986,8 +990,13 @@ namespace { auto tv = CS.createTypeVariable( CS.getConstraintLocator(expr, ConstraintLocator::Member), TVO_CanBindToLValue); - CS.addValueMemberConstraint(baseTy, name, tv, CurDC, functionRefKind, - CS.getConstraintLocator(expr, ConstraintLocator::Member)); + SmallVector outerChoices; + for (auto decl : outerAlternatives) { + outerChoices.push_back(OverloadChoice(Type(), decl, functionRefKind)); + } + CS.addValueMemberConstraint( + baseTy, name, tv, CurDC, functionRefKind, outerChoices, + CS.getConstraintLocator(expr, ConstraintLocator::Member)); return tv; } @@ -1033,8 +1042,8 @@ namespace { // I -> inout? O, where I and O are fresh type variables. The index // expression must be convertible to I and the subscript expression // itself has type inout? O, where O may or may not be an lvalue. - auto inputTv = CS.createTypeVariable(indexLocator, /*options*/0); - + auto inputTv = CS.createTypeVariable(indexLocator); + // For an integer subscript expression on an array slice type, instead of // introducing a new type variable we can easily obtain the element type. if (isa(anchor)) { @@ -1116,6 +1125,7 @@ namespace { } else { CS.addValueMemberConstraint(baseTy, DeclBaseName::createSubscript(), fnTy, CurDC, FunctionRefKind::DoubleApply, + /*outerAlternatives=*/{}, memberLocator); } @@ -1154,19 +1164,59 @@ namespace { } virtual Type visitCodeCompletionExpr(CodeCompletionExpr *E) { - // If the expression has already been assigned a type; just use that type. - return E->getType(); + if (!E->isActivated()) + return Type(); + + return CS.createTypeVariable(CS.getConstraintLocator(E), + TVO_CanBindToLValue); } Type visitLiteralExpr(LiteralExpr *expr) { // If the expression has already been assigned a type; just use that type. - if (expr->getType() && !expr->getType()->hasTypeVariable()) + if (expr->getType()) return expr->getType(); auto protocol = CS.getTypeChecker().getLiteralProtocol(expr); if (!protocol) return nullptr; - + + // Make sure that MaxIntegerType is defined if it would used later + // during constraint solving (CSApply). + if (isa(expr) || + (isa(expr) && + dyn_cast(expr)->isColumn())) { + + auto maxIntType = CS.TC.getMaxIntegerType(CS.DC); + if (maxIntType.isNull()) { + CS.TC.diagnose(expr->getLoc(), diag::no_MaxBuiltinIntegerType_found); + return nullptr; + } + + // In the case of integer literal, make sure that the literal value + // will fit within the bit width of the maximum integer type. + if (IntegerLiteralExpr *intLit = dyn_cast(expr)) { + unsigned maxWidth = + maxIntType->castTo()->getGreatestWidth(); + APInt magnitude = intLit->getRawMagnitude(); + unsigned magWidth = magnitude.getActiveBits(); + bool isNegative = intLit->isNegative(); + + // Compute the literal bit width in the signed two's complement form. + // This is generally one more than the magnitude width, but is the + // same when the literal is of the form -2^i (for some Nat `i`). + unsigned signedLitWidth = + (isNegative && (magnitude.countTrailingZeros() == magWidth - 1)) + ? magWidth + : (magWidth + 1); + + if (signedLitWidth > maxWidth) { // overflow? + CS.TC.diagnose(expr->getLoc(), + diag::integer_literal_overflows_maxwidth, maxWidth, + signedLitWidth); + return nullptr; + } + } + } auto tv = CS.createTypeVariable(CS.getConstraintLocator(expr), TVO_PrefersSubtypeBinding); @@ -1224,7 +1274,7 @@ namespace { Type visitObjectLiteralExpr(ObjectLiteralExpr *expr) { // If the expression has already been assigned a type; just use that type. - if (expr->getType() && !expr->getType()->hasTypeVariable()) + if (expr->getType()) return expr->getType(); auto &tc = CS.getTypeChecker(); @@ -1419,7 +1469,7 @@ namespace { auto memberLocator = CS.getConstraintLocator(expr, ConstraintLocator::UnresolvedMember); - auto baseTy = CS.createTypeVariable(baseLocator, /*options*/0); + auto baseTy = CS.createTypeVariable(baseLocator); auto memberTy = CS.createTypeVariable(memberLocator, TVO_CanBindToLValue); // An unresolved member expression '.member' is modeled as a value member @@ -1440,8 +1490,7 @@ namespace { // TODO: we definitely want this to include ImplicitlyUnwrappedOptional; does it // need to include everything else in the world? auto outputTy = CS.createTypeVariable( - CS.getConstraintLocator(expr, ConstraintLocator::FunctionResult), - /*options*/0); + CS.getConstraintLocator(expr, ConstraintLocator::FunctionResult)); CS.addConstraint(ConstraintKind::Conversion, outputTy, baseTy, CS.getConstraintLocator(expr, ConstraintLocator::RvalueAdjustment)); @@ -1495,12 +1544,14 @@ namespace { CS.getConstraintLocator(expr), TVO_CanBindToLValue | TVO_PrefersSubtypeBinding); - auto resultTy = CS.createTypeVariable(CS.getConstraintLocator(expr), - /*options*/0); + auto resultTy = CS.createTypeVariable(CS.getConstraintLocator(expr)); auto methodTy = FunctionType::get(argsTy, resultTy); - CS.addValueMemberConstraint(baseTy, expr->getName(), - methodTy, CurDC, expr->getFunctionRefKind(), - CS.getConstraintLocator(expr, ConstraintLocator::ConstructorMember)); + CS.addValueMemberConstraint( + baseTy, expr->getName(), methodTy, CurDC, + expr->getFunctionRefKind(), + /*outerAlternatives=*/{}, + CS.getConstraintLocator(expr, + ConstraintLocator::ConstructorMember)); // The result of the expression is the partial application of the // constructor to the subexpression. @@ -1508,7 +1559,8 @@ namespace { } return addMemberRefConstraints(expr, expr->getBase(), expr->getName(), - expr->getFunctionRefKind()); + expr->getFunctionRefKind(), + expr->getOuterAlternatives()); } Type visitUnresolvedSpecializeExpr(UnresolvedSpecializeExpr *expr) { @@ -1540,8 +1592,8 @@ namespace { false) .highlight(SourceRange(expr->getLAngleLoc(), expr->getRAngleLoc())); - tc.diagnose(bgt->getDecl(), diag::generic_type_declared_here, - bgt->getDecl()->getName()); + tc.diagnose(bgt->getDecl(), diag::kind_identifier_declared_here, + DescriptiveDeclKind::GenericType, bgt->getDecl()->getName()); return Type(); } @@ -1913,7 +1965,8 @@ namespace { Identifier name = context.getIdentifier(llvm::utostr(expr->getFieldNumber())); return addMemberRefConstraints(expr, expr->getBase(), name, - FunctionRefKind::Unapplied); + FunctionRefKind::Unapplied, + /*outerAlternatives=*/{}); } /// Give each parameter in a ClosureExpr a fresh type variable if parameter @@ -1972,8 +2025,7 @@ namespace { return CS.getType(boundExpr)->getRValueType(); } - return CS.createTypeVariable(CS.getConstraintLocator(locator), - /*options*/0); + return CS.createTypeVariable(CS.getConstraintLocator(locator)); } case PatternKind::Named: { @@ -2003,16 +2055,14 @@ namespace { case ReferenceOwnership::Unmanaged: if (ty) return ty; - return CS.createTypeVariable(CS.getConstraintLocator(locator), - /*options*/0); + return CS.createTypeVariable(CS.getConstraintLocator(locator)); case ReferenceOwnership::Weak: // For weak variables, use Optional. if (ty && ty->getOptionalObjectType()) return ty; // Already Optional. // Create a fresh type variable to handle overloaded expressions. if (!ty || ty->is()) - ty = CS.createTypeVariable(CS.getConstraintLocator(locator), - /*options*/0); + ty = CS.createTypeVariable(CS.getConstraintLocator(locator)); return CS.getTypeChecker().getOptionalType(var->getLoc(), ty); } @@ -2052,8 +2102,7 @@ namespace { #include "swift/AST/PatternNodes.def" // TODO: we could try harder here, e.g. for enum elements to provide the // enum type. - return CS.createTypeVariable(CS.getConstraintLocator(locator), - /*options*/0); + return CS.createTypeVariable(CS.getConstraintLocator(locator)); } llvm_unreachable("Unhandled pattern kind"); @@ -2289,7 +2338,7 @@ namespace { // If no return type was specified, create a fresh type // variable for it. - funcTy = CS.createTypeVariable(locator, /*options*/0); + funcTy = CS.createTypeVariable(locator); // Allow it to default to () if there are no return statements. if (closureHasNoResult(expr)) { @@ -2326,8 +2375,7 @@ namespace { // S < lvalue T // // where T is a fresh type variable. - auto lvalue = CS.createTypeVariable(CS.getConstraintLocator(expr), - /*options*/0); + auto lvalue = CS.createTypeVariable(CS.getConstraintLocator(expr)); auto bound = LValueType::get(lvalue); auto result = InOutType::get(lvalue); CS.addConstraint(ConstraintKind::Conversion, @@ -2337,8 +2385,7 @@ namespace { } Type visitDynamicTypeExpr(DynamicTypeExpr *expr) { - auto tv = CS.createTypeVariable(CS.getConstraintLocator(expr), - /*options*/0); + auto tv = CS.createTypeVariable(CS.getConstraintLocator(expr)); CS.addConstraint(ConstraintKind::DynamicTypeOf, tv, CS.getType(expr->getBase()), CS.getConstraintLocator(expr, ConstraintLocator::RvalueAdjustment)); @@ -2415,8 +2462,7 @@ namespace { // variables T1 and T2. if (outputTy.isNull()) { outputTy = CS.createTypeVariable( - CS.getConstraintLocator(expr, ConstraintLocator::FunctionResult), - /*options*/0); + CS.getConstraintLocator(expr, ConstraintLocator::FunctionResult)); } else { // Since we know what the output type is, we can set it as the favored // type of this expression. @@ -2507,7 +2553,7 @@ namespace { Type createTypeVariableAndDisjunctionForIUOCoercion(Type toType, ConstraintLocator *locator) { - auto typeVar = CS.createTypeVariable(locator, /*options=*/0); + auto typeVar = CS.createTypeVariable(locator); CS.buildDisjunctionForImplicitlyUnwrappedOptional(typeVar, toType, locator); return typeVar; @@ -2643,7 +2689,7 @@ namespace { Type visitDiscardAssignmentExpr(DiscardAssignmentExpr *expr) { auto locator = CS.getConstraintLocator(expr); - auto typeVar = CS.createTypeVariable(locator, /*options=*/0); + auto typeVar = CS.createTypeVariable(locator); return LValueType::get(typeVar); } @@ -2658,8 +2704,7 @@ namespace { if (!destTy) return Type(); if (destTy->is()) { - return CS.createTypeVariable(CS.getConstraintLocator(expr), - /*options=*/0); + return CS.createTypeVariable(CS.getConstraintLocator(expr)); } // The source must be convertible to the destination. @@ -2774,7 +2819,7 @@ namespace { auto &placeholderTy = editorPlaceholderVariables[currentEditorPlaceholderVariable]; if (!placeholderTy) { - placeholderTy = CS.createTypeVariable(locator, /*options*/0); + placeholderTy = CS.createTypeVariable(locator); CS.addConstraint(ConstraintKind::Defaultable, placeholderTy, @@ -2830,16 +2875,18 @@ namespace { // For native key paths, traverse the key path components to set up // appropriate type relationships at each level. auto locator = CS.getConstraintLocator(E); - Type root = CS.createTypeVariable(locator, /*options*/0); - + Type root = CS.createTypeVariable(locator); + // If a root type was explicitly given, then resolve it now. if (auto rootRepr = E->getRootType()) { auto rootObjectTy = resolveTypeReferenceInExpression(rootRepr); if (!rootObjectTy || rootObjectTy->hasError()) return Type(); rootObjectTy = CS.openUnboundGenericType(rootObjectTy, locator); - CS.addConstraint(ConstraintKind::Bind, root, rootObjectTy, - locator); + // Allow \Derived.property to be inferred as \Base.property to + // simulate a sort of covariant conversion from + // KeyPath to KeyPath. + CS.addConstraint(ConstraintKind::Subtype, rootObjectTy, root, locator); } bool didOptionalChain = false; @@ -2870,6 +2917,7 @@ namespace { memberTy, CurDC, refKind, + /*outerAlternatives=*/{}, memberLocator); base = memberTy; break; @@ -2893,7 +2941,7 @@ namespace { // We can't assign an optional back through an optional chain // today. Force the base to an rvalue. - auto rvalueTy = CS.createTypeVariable(locator, 0); + auto rvalueTy = CS.createTypeVariable(locator); CS.addConstraint(ConstraintKind::Equal, base, rvalueTy, locator); base = rvalueTy; LLVM_FALLTHROUGH; @@ -2921,14 +2969,14 @@ namespace { // If there was an optional chaining component, the end result must be // optional. if (didOptionalChain) { - auto objTy = CS.createTypeVariable(locator, /*options*/0); + auto objTy = CS.createTypeVariable(locator); auto optTy = OptionalType::get(objTy); CS.addConstraint(ConstraintKind::Conversion, base, optTy, locator); base = optTy; } - - auto rvalueBase = CS.createTypeVariable(locator, /*options*/0); + + auto rvalueBase = CS.createTypeVariable(locator); CS.addConstraint(ConstraintKind::Equal, base, rvalueBase, locator); // The result is a KeyPath from the root to the end component. @@ -2939,8 +2987,7 @@ namespace { } else { // The type of key path depends on the overloads chosen for the key // path components. - kpTy = CS.createTypeVariable(CS.getConstraintLocator(E), - /*options*/0); + kpTy = CS.createTypeVariable(CS.getConstraintLocator(E)); CS.addKeyPathConstraint(kpTy, root, rvalueBase, CS.getConstraintLocator(E)); } @@ -3063,14 +3110,50 @@ namespace { /// \brief AST walker that "sanitizes" an expression for the /// constraint-based type checker. /// - /// This is only necessary because Sema fills in too much type information - /// before the type-checker runs, causing redundant work. + /// This is necessary because Sema fills in too much type information before + /// the type-checker runs, causing redundant work, and for expression that + /// have already been typechecked and may contain unhandled AST nodes. class SanitizeExpr : public ASTWalker { + ConstraintSystem &CS; TypeChecker &TC; + bool eraseOpenExistentialsOnly; + llvm::SmallDenseMap OpenExistentials; + public: - SanitizeExpr(TypeChecker &tc) : TC(tc) { } + SanitizeExpr(ConstraintSystem &cs, bool eraseOEsOnly = false) + : CS(cs), TC(cs.getTypeChecker()), + eraseOpenExistentialsOnly(eraseOEsOnly) { } std::pair walkToExprPre(Expr *expr) override { + bool walkIntoChildren = true; + if (auto OOE = dyn_cast(expr)) { + auto archetypeVal = OOE->getOpaqueValue(); + auto base = OOE->getExistentialValue(); + + // Walk the base expression to erase any existentials within it + base = base->walk(*this); + + bool inserted = OpenExistentials.insert({archetypeVal, base}).second; + assert(inserted && "OpaqueValue appears multiple times?"); + (void)inserted; + expr = OOE->getSubExpr(); + } else if (auto OVE = dyn_cast(expr)) { + auto value = OpenExistentials.find(OVE); + assert(value != OpenExistentials.end() && + "didn't see this OVE in a containing OpenExistentialExpr?"); + expr = value->second; + } else if (auto CDE = dyn_cast(expr)) { + // Handle collection upcasts specially so that we don't blow up on + // their embedded OVEs. + if (auto result = CDE->getSubExpr()->walk(*this)) { + CDE->setSubExpr(result); + walkIntoChildren = false; + } + } + + if (eraseOpenExistentialsOnly) + return {true, expr}; + // Let's check if condition of the IfExpr looks properly // type-checked, and roll it back to the original state, // because otherwise, since condition is implicitly Int1, @@ -3097,6 +3180,25 @@ namespace { } Expr *walkToExprPost(Expr *expr) override { + if (CS.hasType(expr)) { + Type type = CS.getType(expr); + if (type->hasOpenedExistential()) { + type = type.transform([&](Type type) -> Type { + if (auto archetype = type->getAs()) + if (auto existentialType = archetype->getOpenedExistentialType()) + return existentialType; + + return type; + }); + CS.setType(expr, type); + // Set new type to the expression directly. + expr->setType(type); + } + } + + if (eraseOpenExistentialsOnly) + return expr; + if (auto implicit = dyn_cast(expr)) { // Skip implicit conversions completely. return implicit->getSubExpr(); @@ -3152,6 +3254,12 @@ namespace { /// \brief Ignore declarations. bool walkToDeclPre(Decl *decl) override { return false; } + + // Don't walk into statements. This handles the BraceStmt in + // non-single-expr closures, so we don't walk into their body. + std::pair walkToStmtPre(Stmt *S) override { + return { false, S }; + } }; class ConstraintWalker : public ASTWalker { @@ -3351,7 +3459,7 @@ namespace { Expr *ConstraintSystem::generateConstraints(Expr *expr) { // Remove implicit conversions from the expression. - expr = expr->walk(SanitizeExpr(getTypeChecker())); + expr = expr->walk(SanitizeExpr(*this)); // Walk the expression to associate labeled arguments. expr->walk(ArgumentLabelWalker(*this, expr)); @@ -3370,7 +3478,7 @@ Expr *ConstraintSystem::generateConstraints(Expr *expr) { Expr *ConstraintSystem::generateConstraintsShallow(Expr *expr) { // Sanitize the expression. - expr = SanitizeExpr(getTypeChecker()).walkToExprPost(expr); + expr = SanitizeExpr(*this).walkToExprPost(expr); cacheSubExprTypes(expr); @@ -3483,9 +3591,9 @@ bool swift::typeCheckUnresolvedExpr(DeclContext &DC, ConstraintSystemOptions Options = ConstraintSystemFlags::AllowFixes; auto *TC = static_cast(DC.getASTContext().getLazyResolver()); - Parent = Parent->walk(SanitizeExpr(*TC)); ConstraintSystem CS(*TC, &DC, Options); - CleanupIllFormedExpressionRAII cleanup(TC->Context, Parent); + Parent = Parent->walk(SanitizeExpr(CS)); + CleanupIllFormedExpressionRAII cleanup(Parent); InferUnresolvedMemberConstraintGenerator MCG(E, CS); ConstraintWalker cw(MCG); Parent->walk(cw); @@ -3586,6 +3694,10 @@ bool swift::isConvertibleTo(Type T1, Type T2, DeclContext &DC) { return canSatisfy(T1, T2, false, ConstraintKind::Conversion, &DC); } +void swift::eraseOpenedExistentials(ConstraintSystem &CS, Expr *&expr) { + expr = expr->walk(SanitizeExpr(CS, /*eraseOEsOnly=*/true)); +} + struct ResolvedMemberResult::Implementation { llvm::SmallVector AllDecls; unsigned ViableStartIdx; diff --git a/lib/Sema/CSRanking.cpp b/lib/Sema/CSRanking.cpp index 56eb1a4b84b23..991e11aa26b58 100644 --- a/lib/Sema/CSRanking.cpp +++ b/lib/Sema/CSRanking.cpp @@ -527,16 +527,22 @@ static bool isDeclAsSpecializedAs(TypeChecker &tc, DeclContext *dc, } // Extract the self types from the declarations, if they have them. + auto getSelfType = [](AnyFunctionType *fnType) -> Type { + auto params = fnType->getParams(); + assert(params.size() == 1); + return params.front().getType()->getRValueInstanceType(); + }; + Type selfTy1; Type selfTy2; if (outerDC1->isTypeContext()) { auto funcTy1 = openedType1->castTo(); - selfTy1 = funcTy1->getInput()->getRValueInstanceType(); + selfTy1 = getSelfType(funcTy1); openedType1 = funcTy1->getResult(); } if (outerDC2->isTypeContext()) { auto funcTy2 = openedType2->castTo(); - selfTy2 = funcTy2->getInput()->getRValueInstanceType(); + selfTy2 = getSelfType(funcTy2); openedType2 = funcTy2->getResult(); } @@ -597,8 +603,7 @@ static bool isDeclAsSpecializedAs(TypeChecker &tc, DeclContext *dc, auto params1 = funcTy1->getParams(); auto params2 = funcTy2->getParams(); SmallVector defaultMapType2; - computeDefaultMap(funcTy2->getInput(), decl2, - outerDC2->isTypeContext(), + computeDefaultMap(params2, decl2, outerDC2->isTypeContext(), defaultMapType2); unsigned numParams1 = params1.size(); @@ -991,15 +996,18 @@ SolutionCompareResult ConstraintSystem::compareSolutions( // Check that the standard library hasn't added another overload of // the ?? operator. - auto inputTupleTy = fnTy->getInput()->castTo(); - auto inputTypes = inputTupleTy->getElementTypes(); - assert(inputTypes.size() == 2); - assert(inputTypes[0]->getOptionalObjectType()); - auto autoclosure = inputTypes[1]->castTo(); - assert(autoclosure->isAutoClosure()); - auto secondParamTy = autoclosure->getResult(); - assert(secondParamTy->getOptionalObjectType()); - (void)secondParamTy; + auto params = fnTy->getParams(); + assert(params.size() == 2); + + auto param1 = params[0].getType(); + auto param2 = params[1].getType()->castTo(); + + assert(param1->getOptionalObjectType()); + assert(param2->isAutoClosure()); + assert(param2->getResult()->getOptionalObjectType()); + + (void) param1; + (void) param2; return true; }; diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index a986c68d1d968..6556fa6c80d5c 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -103,8 +103,8 @@ areConservativelyCompatibleArgumentLabels(ValueDecl *decl, auto params = levelTy->getParams(); SmallVector defaultMap; - computeDefaultMap(levelTy->getInput(), decl, parameterDepth, defaultMap); - + computeDefaultMap(params, decl, parameterDepth, defaultMap); + MatchCallArgumentListener listener; SmallVector unusedParamBindings; @@ -702,7 +702,7 @@ matchCallArguments(ConstraintSystem &cs, ConstraintKind kind, AnyFunctionType::decomposeInput(paramType, params); SmallVector defaultMap; - computeDefaultMap(paramType, callee, calleeLevel, defaultMap); + computeDefaultMap(params, callee, calleeLevel, defaultMap); if (callee && cs.getASTContext().isSwiftVersion3() && argType->is()) { @@ -1033,7 +1033,7 @@ ConstraintSystem::matchFunctionParamTypes(ArrayRef type1 } SmallVector defaultMap; - computeDefaultMap(argType, callee, calleeLevel, defaultMap); + computeDefaultMap(type1, callee, calleeLevel, defaultMap); // Match up the call arguments to the parameters. MatchCallArgumentListener listener; @@ -1207,8 +1207,13 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2, if (last->getKind() == ConstraintLocator::ApplyArgToParam) { if (auto *paren1 = dyn_cast(func1Input.getPointer())) { auto innerTy = paren1->getUnderlyingType(); - if (func2Input->isVoid() && innerTy->isVoid()) + if (func2Input->isVoid() && innerTy->isVoid()) { func1Input = innerTy; + // If the other input is also parenthesized, remove one + // layer of parens from it as well. + if (auto *paren2 = dyn_cast(func2Input.getPointer())) + func2Input = paren2->getUnderlyingType(); + } } } } @@ -1470,7 +1475,7 @@ ConstraintSystem::TypeMatchResult ConstraintSystem::matchTypesBindTypeVar( TypeVariableType *typeVar, Type type, ConstraintKind kind, TypeMatchOptions flags, ConstraintLocatorBuilder locator, - std::function formUnsolvedResult) { + llvm::function_ref formUnsolvedResult) { assert(typeVar->is() && "Expected a type variable!"); // FIXME: Due to some SE-0110 related code farther up we can end // up with type variables wrapped in parens that will trip this @@ -1690,8 +1695,7 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, // left-hand side is bound to type variable which // is wrapped in `inout` type to preserve inout/lvalue pairing. if (auto *lvt = type2->getAs()) { - auto *tv = createTypeVariable(typeVar1->getImpl().getLocator(), - /*options=*/0); + auto *tv = createTypeVariable(typeVar1->getImpl().getLocator()); assignFixedType(typeVar1, InOutType::get(tv)); typeVar1 = tv; @@ -2157,10 +2161,18 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, // an implicit closure. if (auto function2 = type2->getAs()) { if (function2->isAutoClosure()) - return matchTypes(type1, function2->getResult(), kind, subflags, - locator.withPathElement(ConstraintLocator::Load)); + return matchTypes( + type1, function2->getResult(), kind, subflags, + locator.withPathElement(ConstraintLocator::AutoclosureResult)); } + // It is never legal to form an autoclosure that results in these + // implicit conversions to pointer types. + bool isAutoClosureArgument = false; + if (auto last = locator.last()) + if (last->getKind() == ConstraintLocator::AutoclosureResult) + isAutoClosureArgument = true; + // Pointer arguments can be converted from pointer-compatible types. if (kind >= ConstraintKind::ArgumentConversion) { Type unwrappedType2 = type2; @@ -2179,23 +2191,24 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, case PTK_UnsafeMutablePointer: // UnsafeMutablePointer can be converted from an inout reference to a // scalar or array. - if (auto inoutType1 = dyn_cast(desugar1)) { - auto inoutBaseType = inoutType1->getInOutObjectType(); - - Type simplifiedInoutBaseType = - getFixedTypeRecursive(inoutBaseType, - kind == ConstraintKind::Equal, - isArgumentTupleConversion); - - // FIXME: If the base is still a type variable, we can't tell - // what to do here. Might have to try \c ArrayToPointer and make it - // more robust. - if (isArrayType(simplifiedInoutBaseType)) { + if (!isAutoClosureArgument) { + if (auto inoutType1 = dyn_cast(desugar1)) { + auto inoutBaseType = inoutType1->getInOutObjectType(); + + Type simplifiedInoutBaseType = getFixedTypeRecursive( + inoutBaseType, kind == ConstraintKind::Equal, + isArgumentTupleConversion); + + // FIXME: If the base is still a type variable, we can't tell + // what to do here. Might have to try \c ArrayToPointer and make + // it more robust. + if (isArrayType(simplifiedInoutBaseType)) { + conversionsOrFixes.push_back( + ConversionRestrictionKind::ArrayToPointer); + } conversionsOrFixes.push_back( - ConversionRestrictionKind::ArrayToPointer); + ConversionRestrictionKind::InoutToPointer); } - conversionsOrFixes.push_back( - ConversionRestrictionKind::InoutToPointer); } if (!flags.contains(TMF_ApplyingOperatorParameter) && @@ -2243,20 +2256,22 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, // AutoreleasingUnsafeMutablePointer. if (pointerKind == PTK_UnsafePointer || pointerKind == PTK_UnsafeRawPointer) { - if (isArrayType(type1)) { - conversionsOrFixes.push_back( - ConversionRestrictionKind::ArrayToPointer); - } - - // The pointer can be converted from a string, if the element type - // is compatible. - if (type1->isEqual(TC.getStringType(DC))) { - auto baseTy = getFixedTypeRecursive(pointeeTy, false); - - if (baseTy->isTypeVariableOrMember() || - isStringCompatiblePointerBaseType(TC, DC, baseTy)) + if (!isAutoClosureArgument) { + if (isArrayType(type1)) { conversionsOrFixes.push_back( - ConversionRestrictionKind::StringToPointer); + ConversionRestrictionKind::ArrayToPointer); + } + + // The pointer can be converted from a string, if the element + // type is compatible. + if (type1->isEqual(TC.getStringType(DC))) { + auto baseTy = getFixedTypeRecursive(pointeeTy, false); + + if (baseTy->isTypeVariableOrMember() || + isStringCompatiblePointerBaseType(TC, DC, baseTy)) + conversionsOrFixes.push_back( + ConversionRestrictionKind::StringToPointer); + } } if (type1IsPointer && optionalityMatches && @@ -2272,7 +2287,7 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, case PTK_AutoreleasingUnsafeMutablePointer: // PTK_AutoreleasingUnsafeMutablePointer can be converted from an // inout reference to a scalar. - if (type1->is()) { + if (!isAutoClosureArgument && type1->is()) { conversionsOrFixes.push_back( ConversionRestrictionKind::InoutToPointer); } @@ -2551,6 +2566,7 @@ ConstraintSystem::simplifyConstructionConstraint( DeclBaseName::createConstructor(), FunctionType::get(tv, resultType), useDC, functionRefKind, + /*outerAlternatives=*/{}, getConstraintLocator( fnLocator, ConstraintLocator::ConstructorMember)); @@ -3439,14 +3455,11 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName, return result; } -ConstraintSystem::SolutionKind -ConstraintSystem::simplifyMemberConstraint(ConstraintKind kind, - Type baseTy, DeclName member, - Type memberTy, - DeclContext *useDC, - FunctionRefKind functionRefKind, - TypeMatchOptions flags, - ConstraintLocatorBuilder locatorB) { +ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint( + ConstraintKind kind, Type baseTy, DeclName member, Type memberTy, + DeclContext *useDC, FunctionRefKind functionRefKind, + ArrayRef outerAlternatives, TypeMatchOptions flags, + ConstraintLocatorBuilder locatorB) { // Resolve the base type, if we can. If we can't resolve the base type, // then we can't solve this constraint. // FIXME: simplifyType() call here could be getFixedTypeRecursive? @@ -3463,8 +3476,8 @@ ConstraintSystem::simplifyMemberConstraint(ConstraintKind kind, // If requested, generate a constraint. if (flags.contains(TMF_GenerateConstraints)) { addUnsolvedConstraint( - Constraint::createMember(*this, kind, baseTy, memberTy, member, useDC, - functionRefKind, locator)); + Constraint::createMemberOrOuterDisjunction(*this, kind, baseTy, memberTy, member, useDC, + functionRefKind, outerAlternatives, locator)); return SolutionKind::Solved; } @@ -3481,8 +3494,8 @@ ConstraintSystem::simplifyMemberConstraint(ConstraintKind kind, // If we found viable candidates, then we're done! if (!result.ViableCandidates.empty()) { addOverloadSet(memberTy, result.ViableCandidates, useDC, locator, - result.getFavoredChoice()); - + result.getFavoredChoice(), outerAlternatives); + return SolutionKind::Solved; } @@ -3518,7 +3531,8 @@ ConstraintSystem::simplifyMemberConstraint(ConstraintKind kind, // Look through one level of optional. addValueMemberConstraint(baseObjTy->getOptionalObjectType(), - member, memberTy, useDC, functionRefKind, locator); + member, memberTy, useDC, functionRefKind, + outerAlternatives, locator); return SolutionKind::Solved; } return SolutionKind::Error; @@ -4029,7 +4043,23 @@ ConstraintSystem::simplifyKeyPathConstraint(Type keyPathTy, return SolutionKind::Error; } - if (!storage->isSettable(DC)) { + // See whether key paths can store to this component. (Key paths don't + // get any special power from being formed in certain contexts, such + // as the ability to assign to `let`s in initialization contexts, so + // we pass null for the DC to `isSettable` here.) + if (!getASTContext().isSwiftVersionAtLeast(5)) { + // As a source-compatibility measure, continue to allow + // WritableKeyPaths to be formed in the same conditions we did + // in previous releases even if we should not be able to set + // the value in this context. + if (!storage->isSettable(DC)) { + // A non-settable component makes the key path read-only, unless + // a reference-writable component shows up later. + capability = ReadOnly; + continue; + } + } else if (!storage->isSettable(nullptr) + || !storage->isSetterAccessibleFrom(DC)) { // A non-settable component makes the key path read-only, unless // a reference-writable component shows up later. capability = ReadOnly; @@ -5193,6 +5223,7 @@ ConstraintSystem::simplifyConstraint(const Constraint &constraint) { constraint.getSecondType(), constraint.getMemberUseDC(), constraint.getFunctionRefKind(), + /*outerAlternatives=*/{}, TMF_GenerateConstraints, constraint.getLocator()); diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index 4c2bc7c05deeb..0c84f2af8b44c 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -984,7 +984,7 @@ void ConstraintSystem::Candidate::applySolutions( } void ConstraintSystem::shrink(Expr *expr) { - typedef llvm::SmallDenseMap> DomainMap; + using DomainMap = llvm::SmallDenseMap>; // A collection of original domains of all of the expressions, // so they can be restored in case of failure. @@ -1364,7 +1364,7 @@ ConstraintSystem::solve(Expr *&expr, if (allowFreeTypeVariables == FreeTypeVariableBinding::UnresolvedType) { convertType = convertType.transform([&](Type type) -> Type { if (type->is()) - return createTypeVariable(getConstraintLocator(expr), /*options*/0); + return createTypeVariable(getConstraintLocator(expr)); return type; }); } @@ -1802,15 +1802,96 @@ static bool shouldSkipDisjunctionChoice(ConstraintSystem &cs, return false; } +// Attempt to find a disjunction of bind constraints where all options +// in the disjunction are binding the same type variable. +// +// Prefer disjunctions where the bound type variable is also the +// right-hand side of a conversion constraint, since having a concrete +// type that we're converting to can make it possible to split the +// constraint system into multiple ones. +static Constraint *selectBestBindingDisjunction( + ConstraintSystem &cs, SmallVectorImpl &disjunctions) { + + // Collect any disjunctions that simply attempt bindings for a + // type variable. + SmallVector bindingDisjunctions; + for (auto *disjunction : disjunctions) { + llvm::Optional commonTypeVariable; + if (llvm::all_of( + disjunction->getNestedConstraints(), + [&](Constraint *bindingConstraint) { + if (bindingConstraint->getKind() != ConstraintKind::Bind) + return false; + + auto *tv = + bindingConstraint->getFirstType()->getAs(); + // Only do this for simple type variable bindings, not for + // bindings like: ($T1) -> $T2 bind String -> Int + if (!tv) + return false; + + if (!commonTypeVariable.hasValue()) + commonTypeVariable = tv; + + if (commonTypeVariable.getValue() != tv) + return false; + + return true; + })) { + bindingDisjunctions.push_back(disjunction); + } + } + + for (auto *disjunction : bindingDisjunctions) { + auto nested = disjunction->getNestedConstraints(); + assert(!nested.empty()); + auto *tv = cs.simplifyType(nested[0]->getFirstType()) + ->getRValueType() + ->getAs(); + assert(tv); + + SmallVector constraints; + cs.getConstraintGraph().gatherConstraints( + tv, constraints, ConstraintGraph::GatheringKind::EquivalenceClass); + + for (auto *constraint : constraints) { + if (constraint->getKind() != ConstraintKind::Conversion) + continue; + + auto toType = + cs.simplifyType(constraint->getSecondType())->getRValueType(); + auto *toTV = toType->getAs(); + if (tv != toTV) + continue; + + return disjunction; + } + } + + // If we had any binding disjunctions, return the first of + // those. These ensure that we attempt to bind types earlier than + // trying the elements of other disjunctions, which can often mean + // we fail faster. + if (!bindingDisjunctions.empty()) + return bindingDisjunctions[0]; + + return nullptr; +} + Constraint *ConstraintSystem::selectDisjunction( SmallVectorImpl &disjunctions) { if (disjunctions.empty()) return nullptr; + auto *disjunction = + selectBestBindingDisjunction(*this, disjunctions); + if (disjunction) + return disjunction; + // Pick the smallest disjunction. // FIXME: This heuristic isn't great, but it helped somewhat for // overload sets. - auto disjunction = disjunctions[0]; + disjunction = disjunctions[0]; auto bestSize = disjunction->countActiveNestedConstraints(); if (bestSize > 2) { for (auto contender : llvm::makeArrayRef(disjunctions).slice(1)) { @@ -1980,6 +2061,18 @@ bool ConstraintSystem::solveSimplified( auto locator = disjunction->getLocator(); assert(locator && "remembered disjunction doesn't have a locator?"); DisjunctionChoices.push_back({locator, index}); + + // Implicit unwraps of optionals are worse solutions than those + // not involving implicit unwraps. + if (!locator->getPath().empty()) { + auto kind = locator->getPath().back().getKind(); + if (kind == ConstraintLocator::ImplicitlyUnwrappedDisjunctionChoice || + kind == ConstraintLocator::DynamicLookupResult) { + assert(index == 0 || index == 1); + if (index == 1) + increaseScore(SK_ForceUnchecked); + } + } } if (auto score = currentChoice.solve(solutions, allowFreeTypeVariables)) { diff --git a/lib/Sema/CalleeCandidateInfo.cpp b/lib/Sema/CalleeCandidateInfo.cpp index e7b51ccd7691b..ef251079fecca 100644 --- a/lib/Sema/CalleeCandidateInfo.cpp +++ b/lib/Sema/CalleeCandidateInfo.cpp @@ -65,7 +65,7 @@ UncurriedCandidate::UncurriedCandidate(ValueDecl *decl, unsigned level) auto *DC = decl->getInnermostDeclContext(); if (auto *GFT = entityType->getAs()) { auto subs = DC->getGenericEnvironmentOfContext() - ->getForwardingSubstitutions(); + ->getForwardingSubstitutionMap(); entityType = GFT->substGenericArgs(subs); } else { // FIXME: look through unforced IUOs here? @@ -84,20 +84,6 @@ UncurriedCandidate::UncurriedCandidate(ValueDecl *decl, unsigned level) } } -/// Helper to gather the argument labels from a tuple or paren type, for use -/// when the AST doesn't store argument-label information properly. -static void gatherArgumentLabels(Type type, - SmallVectorImpl &labels) { - // Handle tuple types. - if (auto tupleTy = dyn_cast(type.getPointer())) { - for (auto i : range(tupleTy->getNumElements())) - labels.push_back(tupleTy->getElement(i).getName()); - return; - } - - labels.push_back(Identifier()); -} - ArrayRef UncurriedCandidate::getArgumentLabels( SmallVectorImpl &scratch) { scratch.clear(); @@ -129,13 +115,14 @@ ArrayRef UncurriedCandidate::getArgumentLabels( } } } - - if (auto argType = getArgumentType()) { - gatherArgumentLabels(argType, scratch); - return scratch; - } - - return { }; + + if (!hasParameters()) + return {}; + + for (const auto ¶m : getParameters()) + scratch.push_back(param.getLabel()); + + return scratch; } void UncurriedCandidate::dump() const { @@ -316,11 +303,14 @@ CalleeCandidateInfo::evaluateCloseness(UncurriedCandidate candidate, auto *dc = candidate.getDecl() ? candidate.getDecl()->getInnermostDeclContext() : nullptr; - - auto candArgs = candidate.getUncurriedFunctionType()->getParams(); + + if (!candidate.hasParameters()) + return {CC_GeneralMismatch, {}}; + + auto candArgs = candidate.getParameters(); SmallVector candDefaultMap; - computeDefaultMap(candidate.getArgumentType(), candidate.getDecl(), - candidate.level, candDefaultMap); + computeDefaultMap(candArgs, candidate.getDecl(), candidate.level, + candDefaultMap); struct OurListener : public MatchCallArgumentListener { CandidateCloseness result = CC_ExactMatch; @@ -655,20 +645,26 @@ void CalleeCandidateInfo::collectCalleeCandidates(Expr *fn, if (isa(AE) && !isUnresolvedOrTypeVarType(CS.getType(AE->getArg()))) baseType = CS.getType(AE->getArg())->getWithoutSpecifierType(); - + for (auto &C : candidates) { C.level += 1; - + baseType = replaceTypeVariablesWithUnresolved(baseType); - + // Compute a new substituted type if we have a base type to apply. if (baseType && C.level == 1 && C.getDecl()) { baseType = baseType - ->getWithoutSpecifierType() - ->getRValueInstanceType(); - C.entityType = baseType->getTypeOfMember(CS.DC->getParentModule(), - C.getDecl(), nullptr); - C.substituted = true; + ->getWithoutSpecifierType() + ->getRValueInstanceType(); + + if (baseType->isAnyObject()) + baseType = Type(); + + if (baseType) { + C.entityType = baseType->getTypeOfMember(CS.DC->getParentModule(), + C.getDecl(), nullptr); + C.substituted = true; + } } } @@ -763,7 +759,8 @@ void CalleeCandidateInfo::filterListArgs(ArrayRef actual filterList([&](UncurriedCandidate candidate) -> ClosenessResultTy { // If this isn't a function or isn't valid at this uncurry level, treat it // as a general mismatch. - if (!candidate.getArgumentType()) return { CC_GeneralMismatch, {}}; + if (!candidate.hasParameters()) + return {CC_GeneralMismatch, {}}; return evaluateCloseness(candidate, actualArgs); }); } @@ -774,10 +771,10 @@ void CalleeCandidateInfo::filterContextualMemberList(Expr *argExpr) { // If the argument is not present then we expect members without arguments. if (!argExpr) { return filterList([&](UncurriedCandidate candidate) -> ClosenessResultTy { - auto inputType = candidate.getArgumentType(); // If this candidate has no arguments, then we're a match. - if (!inputType) return { CC_ExactMatch, {}}; - + if (!candidate.hasParameters()) + return {CC_ExactMatch, {}}; + // Otherwise, if this is a function candidate with an argument, we // mismatch argument count. return { CC_ArgumentCountMismatch, {}}; @@ -838,7 +835,7 @@ CalleeCandidateInfo::CalleeCandidateInfo(Type baseType, // the uncurry level is 1 if self has already been applied. unsigned uncurryLevel = 0; if (decl->getDeclContext()->isTypeContext() && - selfAlreadyApplied) + selfAlreadyApplied && !isa(decl)) uncurryLevel = 1; candidates.push_back({ decl, uncurryLevel }); @@ -865,10 +862,13 @@ CalleeCandidateInfo::CalleeCandidateInfo(Type baseType, // a substitution. substType = Type(); } - + + if (substType->isAnyObject()) + substType = Type(); + if (substType && selfAlreadyApplied) substType = - substType->getTypeOfMember(CS.DC->getParentModule(), decl, nullptr); + substType->getTypeOfMember(CS.DC->getParentModule(), decl, nullptr); if (substType) { candidates.back().entityType = substType; candidates.back().substituted = true; @@ -890,7 +890,8 @@ suggestPotentialOverloads(SourceLoc loc, bool isResult) { // FIXME2: For (T,T) & (Self, Self), emit this as two candidates, one using // the LHS and one using the RHS type for T's. for (auto cand : candidates) { - auto type = isResult ? cand.getResultType() : cand.getArgumentType(); + auto type = isResult ? cand.getResultType() + : cand.getArgumentType(CS.getASTContext()); if (type.isNull()) continue; diff --git a/lib/Sema/CalleeCandidateInfo.h b/lib/Sema/CalleeCandidateInfo.h index e93b189ea2995..1da31d7fa3c24 100644 --- a/lib/Sema/CalleeCandidateInfo.h +++ b/lib/Sema/CalleeCandidateInfo.h @@ -104,10 +104,21 @@ namespace swift { /// Given a function candidate with an uncurry level, return the parameter /// type at the specified uncurry level. If there is an error getting to /// the specified input, this returns a null Type. - Type getArgumentType() const { - if (auto *funcTy = getUncurriedFunctionType()) - return funcTy->getInput(); - return Type(); + Type getArgumentType(ASTContext &ctx) const { + if (!hasParameters()) + return Type(); + + auto params = getParameters(); + return FunctionType::composeInput(ctx, params, false); + } + + bool hasParameters() const { + return getUncurriedFunctionType(); + } + + ArrayRef getParameters() const { + assert(hasParameters()); + return getUncurriedFunctionType()->getParams(); } /// Given a function candidate with an uncurry level, return the parameter @@ -176,11 +187,11 @@ namespace swift { CalleeCandidateInfo(Type baseType, ArrayRef candidates, bool hasTrailingClosure, ConstraintSystem &CS, bool selfAlreadyApplied = true); - - typedef std::pair ClosenessResultTy; - typedef const std::function - &ClosenessPredicate; - + + using ClosenessResultTy = std::pair; + using ClosenessPredicate = + const std::function &; + /// After the candidate list is formed, it can be filtered down to discard /// obviously mismatching candidates and compute a "closeness" for the /// resultant set. diff --git a/lib/Sema/CodeSynthesis.cpp b/lib/Sema/CodeSynthesis.cpp index 7def20a025c63..d28b0c56c8aa7 100644 --- a/lib/Sema/CodeSynthesis.cpp +++ b/lib/Sema/CodeSynthesis.cpp @@ -243,17 +243,6 @@ static bool needsDynamicMaterializeForSet(AbstractStorageDecl *storage) { return storage->isDynamic() || storage->hasClangNode(); } -// True if a generated accessor needs to be registered as an external decl. -bool needsToBeRegisteredAsExternalDecl(AbstractStorageDecl *storage) { - // Either the storage itself was imported from Clang... - if (storage->hasClangNode()) - return true; - - // ...or it was synthesized into an imported context. - const DeclContext *dc = storage->getDeclContext(); - return isa(dc->getModuleScopeContext()); -} - /// Mark the accessor as transparent if we can. /// /// If the storage is inside a fixed-layout nominal type, we can mark the @@ -396,12 +385,9 @@ createMaterializeForSetPrototype(AbstractStorageDecl *storage, maybeMarkTransparent(materializeForSet, storage, TC); AvailabilityInference::applyInferredAvailableAttrs(materializeForSet, - asAvailableAs, ctx); + asAvailableAs, ctx); - // If the property came from ObjC, we need to register this as an external - // definition to be compiled. - if (needsToBeRegisteredAsExternalDecl(storage)) - TC.Context.addExternalDecl(materializeForSet); + TC.Context.addSynthesizedDecl(materializeForSet); TC.DeclsToFinalize.insert(materializeForSet); return materializeForSet; @@ -734,9 +720,7 @@ static void synthesizeTrivialGetter(AccessorDecl *getter, SourceLoc loc = storage->getLoc(); getter->setBody(BraceStmt::create(ctx, loc, returnStmt, loc, true)); - // Register the accessor as an external decl if the storage was imported. - if (needsToBeRegisteredAsExternalDecl(storage)) - TC.Context.addExternalDecl(getter); + TC.Context.addSynthesizedDecl(getter); TC.DeclsToFinalize.insert(getter); } @@ -754,9 +738,7 @@ static void synthesizeTrivialSetter(AccessorDecl *setter, setterBody, TC); setter->setBody(BraceStmt::create(ctx, loc, setterBody, loc, true)); - // Register the accessor as an external decl if the storage was imported. - if (needsToBeRegisteredAsExternalDecl(storage)) - TC.Context.addExternalDecl(setter); + TC.Context.addSynthesizedDecl(setter); TC.DeclsToFinalize.insert(setter); } @@ -1165,19 +1147,15 @@ void TypeChecker::completePropertyBehaviorStorage(VarDecl *VD, Type SelfTy, Type StorageTy, NormalProtocolConformance *BehaviorConformance, - SubstitutionList SelfInterfaceSubs, - SubstitutionList SelfContextSubs) { + SubstitutionMap interfaceMap, + SubstitutionMap contextMap) { assert(BehaviorStorage); assert((bool)DefaultInitStorage != (bool)ParamInitStorage); // Substitute the storage type into the conforming context. - auto sig = BehaviorConformance->getProtocol()->getGenericSignatureOfContext(); - - auto interfaceMap = sig->getSubstitutionMap(SelfInterfaceSubs); auto SubstStorageInterfaceTy = StorageTy.subst(interfaceMap); assert(SubstStorageInterfaceTy && "storage type substitution failed?!"); - auto contextMap = sig->getSubstitutionMap(SelfContextSubs); auto SubstStorageContextTy = StorageTy.subst(contextMap); assert(SubstStorageContextTy && "storage type substitution failed?!"); @@ -1207,8 +1185,7 @@ void TypeChecker::completePropertyBehaviorStorage(VarDecl *VD, // Initialize the storage immediately, if we can. Expr *InitStorageExpr = nullptr; auto Method = DefaultInitStorage ? DefaultInitStorage : ParamInitStorage; - auto SpecializeInitStorage = ConcreteDeclRef(Context, Method, - SelfContextSubs); + auto SpecializeInitStorage = ConcreteDeclRef(Method, contextMap); if (DefaultInitStorage || (ParamInitStorage && VD->getParentInitializer())) { @@ -1249,8 +1226,11 @@ void TypeChecker::completePropertyBehaviorStorage(VarDecl *VD, InitValue->walk(RecontextualizeClosures(DC)); // Coerce to the property type. + auto PropertyType = + Type(contextMap.getGenericSignature()->getGenericParams()[1]) + .subst(contextMap); InitValue = new (Context) CoerceExpr(InitValue, SourceLoc(), - TypeLoc::withoutLoc(SelfContextSubs[1].getReplacement())); + TypeLoc::withoutLoc(PropertyType)); // Type-check the expression. typeCheckExpression(InitValue, DC); @@ -1307,8 +1287,8 @@ void TypeChecker::completePropertyBehaviorStorage(VarDecl *VD, void TypeChecker::completePropertyBehaviorParameter(VarDecl *VD, FuncDecl *BehaviorParameter, NormalProtocolConformance *BehaviorConformance, - SubstitutionList SelfInterfaceSubs, - SubstitutionList SelfContextSubs) { + SubstitutionMap interfaceMap, + SubstitutionMap contextMap) { // Create a method to witness the requirement. auto DC = VD->getDeclContext(); SmallString<64> NameBuf = VD->getName().str(); @@ -1316,7 +1296,6 @@ void TypeChecker::completePropertyBehaviorParameter(VarDecl *VD, auto ParameterBaseName = Context.getIdentifier(NameBuf); // Substitute the requirement type into the conforming context. - auto sig = BehaviorConformance->getProtocol()->getGenericSignatureOfContext(); auto ParameterTy = BehaviorParameter->getInterfaceType() ->castTo() ->getResult(); @@ -1324,12 +1303,9 @@ void TypeChecker::completePropertyBehaviorParameter(VarDecl *VD, GenericSignature *genericSig = nullptr; GenericEnvironment *genericEnv = nullptr; - auto interfaceMap = sig->getSubstitutionMap(SelfInterfaceSubs); auto SubstInterfaceTy = ParameterTy.subst(interfaceMap); assert(SubstInterfaceTy && "storage type substitution failed?!"); - auto contextMap = sig->getSubstitutionMap(SelfContextSubs); - auto SubstBodyResultTy = SubstInterfaceTy->castTo() ->getResult(); @@ -1431,8 +1407,7 @@ void TypeChecker::completePropertyBehaviorParameter(VarDecl *VD, SourceLoc(), /*implicit*/ true); Parameter->setBody(Body); - typeCheckDecl(Parameter, true); - typeCheckDecl(Parameter, false); + typeCheckDecl(Parameter); addMemberToContextIfNeeded(Parameter, DC); // Add the witnesses to the conformance. @@ -1442,10 +1417,11 @@ void TypeChecker::completePropertyBehaviorParameter(VarDecl *VD, void TypeChecker::completePropertyBehaviorAccessors(VarDecl *VD, VarDecl *ValueImpl, Type valueTy, - SubstitutionList SelfInterfaceSubs, - SubstitutionList SelfContextSubs) { - auto selfTy = SelfContextSubs[0].getReplacement(); - auto selfIfaceTy = SelfInterfaceSubs[0].getReplacement(); + SubstitutionMap SelfInterfaceSubs, + SubstitutionMap SelfContextSubs) { + auto selfGenericParamTy = Type(GenericTypeParamType::get(0, 0, Context)); + auto selfTy = selfGenericParamTy.subst(SelfContextSubs); + auto selfIfaceTy = selfGenericParamTy.subst(SelfInterfaceSubs); SmallVector bodyStmts; @@ -1511,7 +1487,7 @@ void TypeChecker::completePropertyBehaviorAccessors(VarDecl *VD, Expr *selfExpr = makeSelfExpr(getter, ValueImpl->getGetter()); - auto implRef = ConcreteDeclRef(Context, ValueImpl, SelfContextSubs); + auto implRef = ConcreteDeclRef(ValueImpl, SelfContextSubs); auto implMemberExpr = new (Context) MemberRefExpr(selfExpr, SourceLoc(), implRef, @@ -1542,7 +1518,7 @@ void TypeChecker::completePropertyBehaviorAccessors(VarDecl *VD, if (auto setter = VD->getSetter()) { Expr *selfExpr = makeSelfExpr(setter, ValueImpl->getSetter()); - auto implRef = ConcreteDeclRef(Context, ValueImpl, SelfContextSubs); + auto implRef = ConcreteDeclRef(ValueImpl, SelfContextSubs); auto implMemberExpr = new (Context) MemberRefExpr(selfExpr, SourceLoc(), implRef, @@ -1762,11 +1738,9 @@ void swift::maybeAddAccessorsToVariable(VarDecl *var, TypeChecker &TC) { TC.diagnose(behavior->getLoc(), diag::property_behavior_protocol_reqt_ambiguous, TC.Context.Id_value); - TC.diagnose(valueProp->getLoc(), - diag::property_behavior_protocol_reqt_here, + TC.diagnose(valueProp->getLoc(), diag::identifier_declared_here, TC.Context.Id_value); - TC.diagnose(foundVar->getLoc(), - diag::property_behavior_protocol_reqt_here, + TC.diagnose(foundVar->getLoc(), diag::identifier_declared_here, TC.Context.Id_value); break; } @@ -2015,6 +1989,75 @@ static void createStubBody(TypeChecker &tc, ConstructorDecl *ctor) { ctor->setStubImplementation(true); } +static void configureDesignatedInitAttributes(TypeChecker &tc, + ClassDecl *classDecl, + ConstructorDecl *ctor, + ConstructorDecl *superclassCtor) { + auto &ctx = tc.Context; + + AccessLevel access = classDecl->getFormalAccess(); + access = std::max(access, AccessLevel::Internal); + access = std::min(access, superclassCtor->getFormalAccess()); + + ctor->setAccess(access); + + // Inherit the @inlinable attribute. + if (superclassCtor->getFormalAccess(/*useDC=*/nullptr, + /*treatUsableFromInlineAsPublic=*/true) + >= AccessLevel::Public) { + if (superclassCtor->getAttrs().hasAttribute()) { + auto *clonedAttr = new (ctx) InlinableAttr(/*implicit=*/true); + ctor->getAttrs().add(clonedAttr); + } + } + + // Inherit the @usableFromInline attribute. We need better abstractions + // for dealing with @usableFromInline. + if (superclassCtor->getFormalAccess(/*useDC=*/nullptr, + /*treatUsableFromInlineAsPublic=*/true) + >= AccessLevel::Public) { + if (access == AccessLevel::Internal && + !superclassCtor->isDynamic() && + !ctor->getAttrs().hasAttribute()) { + auto *clonedAttr = new (ctx) UsableFromInlineAttr(/*implicit=*/true); + ctor->getAttrs().add(clonedAttr); + } + } + + // Inherit the @discardableResult attribute. + if (superclassCtor->getAttrs().hasAttribute()) { + auto *clonedAttr = new (ctx) DiscardableResultAttr(/*implicit=*/true); + ctor->getAttrs().add(clonedAttr); + } + + // Make sure the constructor is only as available as its superclass's + // constructor. + AvailabilityInference::applyInferredAvailableAttrs(ctor, superclassCtor, ctx); + + if (superclassCtor->isObjC()) { + // Inherit the @objc name from the superclass initializer, if it + // has one. + if (auto objcAttr = superclassCtor->getAttrs().getAttribute()) { + if (objcAttr->hasName()) { + auto *clonedAttr = objcAttr->clone(ctx); + clonedAttr->setImplicit(true); + ctor->getAttrs().add(clonedAttr); + } + } + + auto errorConvention = superclassCtor->getForeignErrorConvention(); + markAsObjC(tc, ctor, ObjCReason::ImplicitlyObjC, errorConvention); + } + if (superclassCtor->isRequired()) + ctor->getAttrs().add(new (ctx) RequiredAttr(/*IsImplicit=*/true)); + if (superclassCtor->isDynamic()) + ctor->getAttrs().add(new (ctx) DynamicAttr(/*IsImplicit*/true)); + + // Wire up the overrides. + ctor->getAttrs().add(new (ctx) OverrideAttr(/*IsImplicit=*/true)); + ctor->setOverriddenDecl(superclassCtor); +} + ConstructorDecl * swift::createDesignatedInitOverride(TypeChecker &tc, ClassDecl *classDecl, @@ -2100,55 +2143,14 @@ swift::createDesignatedInitOverride(TypeChecker &tc, ctor->setImplicit(); - AccessLevel access = classDecl->getFormalAccess(); - access = std::max(access, AccessLevel::Internal); - access = std::min(access, superclassCtor->getFormalAccess()); - ctor->setAccess(access); - - // Inherit the @usableFromInline attribute. - if (superclassCtor->getAttrs().hasAttribute()) { - auto *clonedAttr = new (ctx) UsableFromInlineAttr(/*implicit=*/true); - ctor->getAttrs().add(clonedAttr); - } - - // Inherit the @inlinable attribute. - if (superclassCtor->getAttrs().hasAttribute()) { - auto *clonedAttr = new (ctx) InlinableAttr(/*implicit=*/true); - ctor->getAttrs().add(clonedAttr); - } - - // Make sure the constructor is only as available as its superclass's - // constructor. - AvailabilityInference::applyInferredAvailableAttrs(ctor, superclassCtor, ctx); - // Set the interface type of the initializer. ctor->setGenericEnvironment(classDecl->getGenericEnvironmentOfContext()); tc.configureInterfaceType(ctor, ctor->getGenericSignature()); - - if (superclassCtor->isObjC()) { - // Inherit the @objc name from the superclass initializer, if it - // has one. - if (auto objcAttr = superclassCtor->getAttrs().getAttribute()) { - if (objcAttr->hasName()) { - auto *clonedAttr = objcAttr->clone(ctx); - clonedAttr->setImplicit(true); - ctor->getAttrs().add(clonedAttr); - } - } - - auto errorConvention = superclassCtor->getForeignErrorConvention(); - markAsObjC(tc, ctor, ObjCReason::ImplicitlyObjC, errorConvention); - } - if (superclassCtor->isRequired()) - ctor->getAttrs().add(new (tc.Context) RequiredAttr(/*IsImplicit=*/true)); - if (superclassCtor->isDynamic()) - ctor->getAttrs().add(new (tc.Context) DynamicAttr(/*IsImplicit*/true)); - - // Wire up the overrides. - ctor->getAttrs().add(new (tc.Context) OverrideAttr(/*IsImplicit=*/true)); - ctor->setOverriddenDecl(superclassCtor); ctor->setValidationStarted(); + configureDesignatedInitAttributes(tc, classDecl, + ctor, superclassCtor); + if (kind == DesignatedInitKind::Stub) { // Make this a stub implementation. createStubBody(tc, ctor); diff --git a/lib/Sema/Constraint.cpp b/lib/Sema/Constraint.cpp index adfdfd3f84478..68a7545c403e9 100644 --- a/lib/Sema/Constraint.cpp +++ b/lib/Sema/Constraint.cpp @@ -642,6 +642,25 @@ Constraint *Constraint::create(ConstraintSystem &cs, ConstraintKind kind, locator, typeVars); } +Constraint *Constraint::createMemberOrOuterDisjunction( + ConstraintSystem &cs, ConstraintKind kind, Type first, Type second, + DeclName member, DeclContext *useDC, FunctionRefKind functionRefKind, + ArrayRef outerAlternatives, ConstraintLocator *locator) { + auto memberConstraint = createMember(cs, kind, first, second, member, + useDC, functionRefKind, locator); + + if (outerAlternatives.empty()) + return memberConstraint; + + SmallVector constraints; + constraints.push_back(memberConstraint); + memberConstraint->setFavored(); + for (auto choice : outerAlternatives) { + constraints.push_back( + Constraint::createBindOverload(cs, first, choice, useDC, locator)); + } + return Constraint::createDisjunction(cs, constraints, locator, ForgetChoice); +} Constraint *Constraint::createMember(ConstraintSystem &cs, ConstraintKind kind, Type first, Type second, DeclName member, diff --git a/lib/Sema/Constraint.h b/lib/Sema/Constraint.h index 0ea734f415569..ca09d09561a05 100644 --- a/lib/Sema/Constraint.h +++ b/lib/Sema/Constraint.h @@ -435,6 +435,13 @@ class Constraint final : public llvm::ilist_node, Type First, Type Second, Type Third, ConstraintLocator *locator); + /// Create a new member constraint, or a disjunction of that with the outer + /// alternatives. + static Constraint *createMemberOrOuterDisjunction( + ConstraintSystem &cs, ConstraintKind kind, Type first, Type second, + DeclName member, DeclContext *useDC, FunctionRefKind functionRefKind, + ArrayRef outerAlternatives, ConstraintLocator *locator); + /// Create a new member constraint. static Constraint *createMember(ConstraintSystem &cs, ConstraintKind kind, Type first, Type second, DeclName member, @@ -704,7 +711,7 @@ namespace llvm { template<> struct ilist_traits : public ilist_default_traits { - typedef swift::constraints::Constraint Element; + using Element = swift::constraints::Constraint; static Element *createNode(const Element &V) = delete; static void deleteNode(Element *V) { /* never deleted */ } diff --git a/lib/Sema/ConstraintGraph.cpp b/lib/Sema/ConstraintGraph.cpp index 3ba036557e4d1..a493141b3f2dd 100644 --- a/lib/Sema/ConstraintGraph.cpp +++ b/lib/Sema/ConstraintGraph.cpp @@ -18,6 +18,7 @@ #include "ConstraintGraph.h" #include "ConstraintGraphScope.h" #include "ConstraintSystem.h" +#include "swift/Basic/Statistic.h" #include "llvm/Support/Debug.h" #include "llvm/Support/SaveAndRestore.h" #include @@ -27,6 +28,8 @@ using namespace swift; using namespace constraints; +#define DEBUG_TYPE "ConstraintGraph" + #pragma mark Graph construction/destruction ConstraintGraph::ConstraintGraph(ConstraintSystem &cs) : CS(cs) { } @@ -145,7 +148,7 @@ ConstraintGraphNode::getAdjacency(TypeVariableType *typeVar) { void ConstraintGraphNode::modifyAdjacency( TypeVariableType *typeVar, - std::function modify) { + llvm::function_ref modify) { // Find the adjacency information. auto pos = AdjacencyInfo.find(typeVar); assert(pos != AdjacencyInfo.end() && "Type variables not adjacent"); @@ -649,93 +652,89 @@ static bool shouldContractEdge(ConstraintKind kind) { } bool ConstraintGraph::contractEdges() { - llvm::SetVector> contractions; - - auto tyvars = getTypeVariables(); - auto didContractEdges = false; + SmallVector constraints; + CS.findConstraints(constraints, [&](const Constraint &constraint) { + // Track how many constraints did contraction algorithm iterated over. + incrementConstraintsPerContractionCounter(); + return shouldContractEdge(constraint.getKind()); + }); - for (auto tyvar : tyvars) { - SmallVector constraints; - gatherConstraints(tyvar, constraints, - ConstraintGraph::GatheringKind::EquivalenceClass); + bool didContractEdges = false; + for (auto *constraint : constraints) { + auto kind = constraint->getKind(); - for (auto constraint : constraints) { - auto kind = constraint->getKind(); - // Contract binding edges between type variables. - if (shouldContractEdge(kind)) { - auto t1 = constraint->getFirstType()->getDesugaredType(); - auto t2 = constraint->getSecondType()->getDesugaredType(); + // Contract binding edges between type variables. + assert(shouldContractEdge(kind)); - auto tyvar1 = t1->getAs(); - auto tyvar2 = t2->getAs(); + auto t1 = constraint->getFirstType()->getDesugaredType(); + auto t2 = constraint->getSecondType()->getDesugaredType(); - if (!(tyvar1 && tyvar2)) - continue; + auto tyvar1 = t1->getAs(); + auto tyvar2 = t2->getAs(); - auto isParamBindingConstraint = kind == ConstraintKind::BindParam; - - // If the argument is allowed to bind to `inout`, in general, - // it's invalid to contract the edge between argument and parameter, - // but if we can prove that there are no possible bindings - // which result in attempt to bind `inout` type to argument - // type variable, we should go ahead and allow (temporary) - // contraction, because that greatly helps with performance. - // Such action is valid because argument type variable can - // only get its bindings from related overload, which gives - // us enough information to decided on l-valueness. - if (isParamBindingConstraint && tyvar1->getImpl().canBindToInOut()) { - bool isNotContractable = true; - if (auto bindings = CS.getPotentialBindings(tyvar1)) { - for (auto &binding : bindings.Bindings) { - auto type = binding.BindingType; - isNotContractable = type.findIf([&](Type nestedType) -> bool { - if (auto tv = nestedType->getAs()) { - if (!tv->getImpl().mustBeMaterializable()) - return true; - } - - return nestedType->is(); - }); + if (!(tyvar1 && tyvar2)) + continue; - // If there is at least one non-contractable binding, let's - // not risk contracting this edge. - if (isNotContractable) - break; + auto isParamBindingConstraint = kind == ConstraintKind::BindParam; + + // If the argument is allowed to bind to `inout`, in general, + // it's invalid to contract the edge between argument and parameter, + // but if we can prove that there are no possible bindings + // which result in attempt to bind `inout` type to argument + // type variable, we should go ahead and allow (temporary) + // contraction, because that greatly helps with performance. + // Such action is valid because argument type variable can + // only get its bindings from related overload, which gives + // us enough information to decided on l-valueness. + if (isParamBindingConstraint && tyvar1->getImpl().canBindToInOut()) { + bool isNotContractable = true; + if (auto bindings = CS.getPotentialBindings(tyvar1)) { + for (auto &binding : bindings.Bindings) { + auto type = binding.BindingType; + isNotContractable = type.findIf([&](Type nestedType) -> bool { + if (auto tv = nestedType->getAs()) { + if (!tv->getImpl().mustBeMaterializable()) + return true; } - } + return nestedType->is(); + }); + + // If there is at least one non-contractable binding, let's + // not risk contracting this edge. if (isNotContractable) - continue; + break; } + } - auto rep1 = CS.getRepresentative(tyvar1); - auto rep2 = CS.getRepresentative(tyvar2); - - if (((rep1->getImpl().canBindToLValue() == - rep2->getImpl().canBindToLValue()) || - // Allow l-value contractions when binding parameter types. - isParamBindingConstraint)) { - if (CS.TC.getLangOpts().DebugConstraintSolver) { - auto &log = CS.getASTContext().TypeCheckerDebug->getStream(); - if (CS.solverState) - log.indent(CS.solverState->depth * 2); - - log << "Contracting constraint "; - constraint->print(log, &CS.getASTContext().SourceMgr); - log << "\n"; - } - - // Merge the edges and remove the constraint. - removeEdge(constraint); - if (rep1 != rep2) - CS.mergeEquivalenceClasses(rep1, rep2, /*updateWorkList*/ false); - didContractEdges = true; - } + if (isNotContractable) + continue; + } + + auto rep1 = CS.getRepresentative(tyvar1); + auto rep2 = CS.getRepresentative(tyvar2); + + if (((rep1->getImpl().canBindToLValue() == + rep2->getImpl().canBindToLValue()) || + // Allow l-value contractions when binding parameter types. + isParamBindingConstraint)) { + if (CS.TC.getLangOpts().DebugConstraintSolver) { + auto &log = CS.getASTContext().TypeCheckerDebug->getStream(); + if (CS.solverState) + log.indent(CS.solverState->depth * 2); + + log << "Contracting constraint "; + constraint->print(log, &CS.getASTContext().SourceMgr); + log << "\n"; } + + // Merge the edges and remove the constraint. + removeEdge(constraint); + if (rep1 != rep2) + CS.mergeEquivalenceClasses(rep1, rep2, /*updateWorkList*/ false); + didContractEdges = true; } } - return didContractEdges; } @@ -773,6 +772,14 @@ void ConstraintGraph::optimize() { while (contractEdges()) {} } +void ConstraintGraph::incrementConstraintsPerContractionCounter() { + SWIFT_FUNC_STAT; + auto &context = CS.getASTContext(); + if (context.Stats) + context.Stats->getFrontendCounters() + .NumConstraintsConsideredForEdgeContraction++; +} + #pragma mark Debugging output void ConstraintGraphNode::print(llvm::raw_ostream &out, unsigned indent) { diff --git a/lib/Sema/ConstraintGraph.h b/lib/Sema/ConstraintGraph.h index e34d6a5058234..328dd76a8eb84 100644 --- a/lib/Sema/ConstraintGraph.h +++ b/lib/Sema/ConstraintGraph.h @@ -104,7 +104,7 @@ class ConstraintGraphNode { /// directly. If the adjacency becomes empty afterward, it will be /// removed. void modifyAdjacency(TypeVariableType *typeVar, - std::function modify); + llvm::function_ref modify); /// Add an adjacency to the list of adjacencies. void addAdjacency(TypeVariableType *typeVar); @@ -332,6 +332,10 @@ class ConstraintGraph { /// Constraints that are "orphaned" because they contain no type variables. SmallVector OrphanedConstraints; + /// Increment the number of constraints considered per attempt + /// to contract constrant graph edges. + void incrementConstraintsPerContractionCounter(); + /// The kind of change made to the graph. enum class ChangeKind { /// Added a type variable. diff --git a/lib/Sema/ConstraintLocator.cpp b/lib/Sema/ConstraintLocator.cpp index 0411d9c3928f9..41e67da75f401 100644 --- a/lib/Sema/ConstraintLocator.cpp +++ b/lib/Sema/ConstraintLocator.cpp @@ -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 - 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 @@ -69,7 +69,7 @@ void ConstraintLocator::Profile(llvm::FoldingSetNodeID &id, Expr *anchor, case GeneratorElementType: case ArrayElementType: case ScalarToTuple: - case Load: + case AutoclosureResult: case GenericArgument: case NamedTupleElement: case TupleElement: @@ -173,8 +173,8 @@ void ConstraintLocator::dump(SourceManager *sm, raw_ostream &out) { out << "instance type"; break; - case Load: - out << "load"; + case AutoclosureResult: + out << "@autoclosure result"; break; case Member: @@ -252,7 +252,7 @@ void ConstraintLocator::dump(SourceManager *sm, raw_ostream &out) { break; case ImplicitlyUnwrappedDisjunctionChoice: - out << "implictly unwrapped disjunction choice"; + out << "implicitly unwrapped disjunction choice"; break; case DynamicLookupResult: diff --git a/lib/Sema/ConstraintLocator.h b/lib/Sema/ConstraintLocator.h index 45c60556854a0..c4f750634de1f 100644 --- a/lib/Sema/ConstraintLocator.h +++ b/lib/Sema/ConstraintLocator.h @@ -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 - 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 @@ -111,8 +111,9 @@ class ConstraintLocator : public llvm::FoldingSetNode { ArrayElementType, /// \brief The scalar type of a tuple type. ScalarToTuple, - /// \brief The load of an lvalue. - Load, + /// \brief An argument passed in an autoclosure parameter + /// position, which must match the autoclosure return type. + AutoclosureResult, /// The requirement that we're matching during protocol conformance /// checking. Requirement, @@ -159,7 +160,7 @@ class ConstraintLocator : public llvm::FoldingSetNode { case GeneratorElementType: case ArrayElementType: case ScalarToTuple: - case Load: + case AutoclosureResult: case Requirement: case Witness: case OpenedGeneric: @@ -205,7 +206,7 @@ class ConstraintLocator : public llvm::FoldingSetNode { case ClosureResult: case ConstructorMember: case InstanceType: - case Load: + case AutoclosureResult: case OptionalPayload: case Member: case MemberRefBase: @@ -530,7 +531,7 @@ class ConstraintLocator : public llvm::FoldingSetNode { friend class ConstraintSystem; }; -typedef ConstraintLocator::PathElement LocatorPathElt; +using LocatorPathElt = ConstraintLocator::PathElement; /// \brief A simple stack-only builder object that constructs a /// constraint locator without allocating memory. diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 52b3c85fe0624..0dd794551d045 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -263,15 +263,49 @@ LookupResult &ConstraintSystem::lookupMember(Type base, DeclName name) { return *result; // We are performing dynamic lookup. Filter out redundant results early. - llvm::DenseSet> known; - result->filter([&](LookupResultEntry entry) -> bool { + llvm::DenseMap, ValueDecl *> known; + bool anyRemovals = false; + for (const auto &entry : *result) { auto *decl = entry.getValueDecl(); - if (decl->isInvalid()) - return false; + // Remove invalid declarations so the constraint solver doesn't need to + // cope with them. + if (decl->isInvalid()) { + anyRemovals = true; + continue; + } - return known.insert(getDynamicResultSignature(decl)).second; - }); + // If this is the first entry with the signature, record it. + auto &uniqueEntry = known[getDynamicResultSignature(decl)]; + if (!uniqueEntry) { + uniqueEntry = decl; + continue; + } + + // We have duplication; note that we'll need to remove something, + anyRemovals = true; + + // If the entry we recorded was unavailable but this new entry is not, + // replace the recorded entry with this one. + if (uniqueEntry->getAttrs().isUnavailable(TC.Context) && + !decl->getAttrs().isUnavailable(TC.Context)) { + uniqueEntry = decl; + } + } + + // If there's anything to remove, filter it out now. + if (anyRemovals) { + result->filter([&](LookupResultEntry entry, bool isOuter) -> bool { + auto *decl = entry.getValueDecl(); + + // Remove invalid declarations so the constraint solver doesn't need to + // cope with them. + if (decl->isInvalid()) + return false; + + return known[getDynamicResultSignature(decl)] == decl; + }); + } return *result; } @@ -427,13 +461,13 @@ Type ConstraintSystem::openUnboundGenericType(UnboundGenericType *unbound, } // Map the generic parameters to their corresponding type variables. - llvm::SmallVector arguments; + llvm::SmallVector arguments; for (auto gp : unboundDecl->getInnermostGenericParamTypes()) { auto found = replacements.find( cast(gp->getCanonicalType())); assert(found != replacements.end() && "Missing generic parameter?"); - arguments.push_back(TypeLoc::withoutLoc(found->second)); + arguments.push_back(found->second); } // FIXME: For some reason we can end up with unbound->getDecl() @@ -443,7 +477,6 @@ Type ConstraintSystem::openUnboundGenericType(UnboundGenericType *unbound, return TC.applyUnboundGenericArguments( unbound, unboundDecl, SourceLoc(), DC, arguments, - /*options*/TypeResolutionOptions(), /*resolver*/nullptr, /*unsatisfiedDependency*/nullptr); } @@ -500,20 +533,15 @@ static Type removeArgumentLabels(Type type, unsigned numArgumentLabels) { auto fnType = type->getAs(); // Drop argument labels from the input type. - Type inputType = fnType->getInput(); - if (auto tupleTy = dyn_cast(inputType.getPointer())) { - SmallVector elements; - elements.reserve(tupleTy->getNumElements()); - for (const auto &elt : tupleTy->getElements()) { - elements.push_back(elt.getWithoutName()); - } - inputType = TupleType::get(elements, type->getASTContext()); - } - - return FunctionType::get(inputType, - removeArgumentLabels(fnType->getResult(), - numArgumentLabels - 1), - fnType->getExtInfo()); + llvm::SmallVector unlabeledParams; + unlabeledParams.reserve(fnType->getNumParams()); + for (const auto ¶m : fnType->getParams()) + unlabeledParams.push_back(param.getWithoutLabel()); + + return FunctionType::get( + unlabeledParams, + removeArgumentLabels(fnType->getResult(), numArgumentLabels - 1), + fnType->getExtInfo()); } Type ConstraintSystem::openFunctionType( @@ -535,14 +563,21 @@ Type ConstraintSystem::openFunctionType( locator, replacements); - // Transform the input and output types. - auto inputTy = openType(genericFn->getInput(), replacements); + // Transform the parameters and output type. + llvm::SmallVector openedParams; + openedParams.reserve(genericFn->getNumParams()); + for (const auto ¶m : genericFn->getParams()) { + auto type = openType(param.getPlainType(), replacements); + openedParams.push_back(AnyFunctionType::Param(type, param.getLabel(), + param.getParameterFlags())); + } + auto resultTy = openType(genericFn->getResult(), replacements); // Build the resulting (non-generic) function type. - funcType = FunctionType::get(inputTy, resultTy, - FunctionType::ExtInfo(). - withThrows(genericFn->throws())); + funcType = FunctionType::get( + openedParams, resultTy, + FunctionType::ExtInfo().withThrows(genericFn->throws())); } return removeArgumentLabels(funcType, numArgumentLabelsToRemove); @@ -714,7 +749,7 @@ Type TypeChecker::getUnopenedTypeOfReference(VarDecl *value, Type baseType, const DeclRefExpr *base, bool wantInterfaceType) { validateDecl(value); - if (value->isInvalid()) + if (!value->hasValidSignature() || value->isInvalid()) return ErrorType::get(Context); Type requestedType = (wantInterfaceType @@ -863,7 +898,9 @@ ConstraintSystem::getTypeOfReference(ValueDecl *value, // DynamicSelf with the actual object type. if (!func->getDeclContext()->getAsProtocolOrProtocolExtensionContext()) { if (func->hasDynamicSelf()) { - Type selfTy = openedFnType->getInput()->getRValueInstanceType(); + auto params = openedFnType->getParams(); + assert(params.size() == 1); + Type selfTy = params.front().getType()->getRValueInstanceType(); openedType = openedType->replaceCovariantResultType( selfTy, func->getNumParameterLists()); @@ -1324,7 +1361,10 @@ ConstraintSystem::getTypeOfMemberReference( // Constrain the 'self' object type. auto openedFnType = openedType->castTo(); - Type selfObjTy = openedFnType->getInput()->getRValueInstanceType(); + auto openedParams = openedFnType->getParams(); + assert(openedParams.size() == 1); + + Type selfObjTy = openedParams.front().getType()->getRValueInstanceType(); if (outerDC->getAsProtocolOrProtocolExtensionContext()) { // For a protocol, substitute the base object directly. We don't need a // conformance constraint because we wouldn't have found the declaration @@ -1376,41 +1416,79 @@ ConstraintSystem::getTypeOfMemberReference( return { openedType, type }; } +// Performance hack: if there are two generic overloads, and one is +// more specialized than the other, prefer the more-specialized one. +static void tryOptimizeGenericDisjunction(ConstraintSystem &cs, + ArrayRef choices, + OverloadChoice *&favoredChoice) { + if (favoredChoice || choices.size() != 2) + return; + + const auto &choiceA = choices[0]; + const auto &choiceB = choices[1]; + + if (!choiceA.isDecl() || !choiceB.isDecl()) + return; + + auto isViable = [](ValueDecl *decl) -> bool { + assert(decl); + + auto *AFD = dyn_cast(decl); + if (!AFD || !AFD->isGeneric()) + return false; + + auto funcType = AFD->getInterfaceType(); + auto hasAny = funcType.findIf([](Type type) -> bool { + if (auto objType = type->getOptionalObjectType()) + return objType->isAny(); + + return type->isAny(); + }); + + // If function declaration references `Any` or `Any?` type + // let's not attempt it, because it's unclear + // without solving which overload is going to be better. + return !hasAny; + }; + + auto *declA = choiceA.getDecl(); + auto *declB = choiceB.getDecl(); + + if (!isViable(declA) || !isViable(declB)) + return; + + auto &TC = cs.TC; + auto *DC = cs.DC; + + switch (TC.compareDeclarations(DC, declA, declB)) { + case Comparison::Better: + favoredChoice = const_cast(&choiceA); + break; + + case Comparison::Worse: + favoredChoice = const_cast(&choiceB); + break; + + case Comparison::Unordered: + break; + } +} + void ConstraintSystem::addOverloadSet(Type boundType, ArrayRef choices, DeclContext *useDC, ConstraintLocator *locator, - OverloadChoice *favoredChoice) { + OverloadChoice *favoredChoice, + ArrayRef outerAlternatives) { assert(!choices.empty() && "Empty overload set"); // If there is a single choice, add the bind overload directly. - if (choices.size() == 1) { + if (choices.size() == 1 && outerAlternatives.empty()) { addBindOverloadConstraint(boundType, choices.front(), locator, useDC); return; } - // Performance hack: if there are two generic overloads, and one is - // more specialized than the other, prefer the more-specialized one. - if (!favoredChoice && choices.size() == 2 && - choices[0].isDecl() && choices[1].isDecl() && - isa(choices[0].getDecl()) && - cast(choices[0].getDecl())->isGeneric() && - isa(choices[1].getDecl()) && - cast(choices[1].getDecl())->isGeneric()) { - switch (TC.compareDeclarations(DC, choices[0].getDecl(), - choices[1].getDecl())) { - case Comparison::Better: - favoredChoice = const_cast(&choices[0]); - break; - - case Comparison::Worse: - favoredChoice = const_cast(&choices[1]); - break; - - case Comparison::Unordered: - break; - } - } + tryOptimizeGenericDisjunction(*this, choices, favoredChoice); SmallVector overloads; @@ -1441,7 +1519,26 @@ void ConstraintSystem::addOverloadSet(Type boundType, useDC, locator)); } - addDisjunctionConstraint(overloads, locator, ForgetChoice, favoredChoice); + auto innerDisjunction = Constraint::createDisjunction(*this, overloads, + locator, ForgetChoice); + if (outerAlternatives.empty()) { + if (favoredChoice) + innerDisjunction->setFavored(); + + addUnsolvedConstraint(innerDisjunction); + return; + } + + SmallVector outerConstraints; + outerConstraints.push_back(innerDisjunction); + innerDisjunction->setFavored(); + for (auto choice : outerAlternatives) { + outerConstraints.push_back(Constraint::createBindOverload( + *this, boundType, choice, + useDC, locator)); + } + + addDisjunctionConstraint(outerConstraints, locator, ForgetChoice, favoredChoice); } /// If we're resolving an overload set with a decl that has special type @@ -1466,12 +1563,10 @@ resolveOverloadForDeclWithSpecialTypeCheckingSemantics(ConstraintSystem &CS, // existentials (as seen from the current abstraction level), which can't // be expressed in the type system currently. auto input = CS.createTypeVariable( - CS.getConstraintLocator(locator, ConstraintLocator::FunctionArgument), - /*options*/0); + CS.getConstraintLocator(locator, ConstraintLocator::FunctionArgument)); auto output = CS.createTypeVariable( - CS.getConstraintLocator(locator, ConstraintLocator::FunctionResult), - /*options*/0); - + CS.getConstraintLocator(locator, ConstraintLocator::FunctionResult)); + auto inputArg = TupleTypeElt(input, CS.getASTContext().getIdentifier("of")); auto inputTuple = TupleType::get(inputArg, CS.getASTContext()); @@ -1486,17 +1581,14 @@ resolveOverloadForDeclWithSpecialTypeCheckingSemantics(ConstraintSystem &CS, // receives a copy of the argument closure that is temporarily made // @escaping. auto noescapeClosure = CS.createTypeVariable( - CS.getConstraintLocator(locator, ConstraintLocator::FunctionArgument), - /*options*/0); + CS.getConstraintLocator(locator, ConstraintLocator::FunctionArgument)); auto escapeClosure = CS.createTypeVariable( - CS.getConstraintLocator(locator, ConstraintLocator::FunctionArgument), - /*options*/0); + CS.getConstraintLocator(locator, ConstraintLocator::FunctionArgument)); CS.addConstraint(ConstraintKind::EscapableFunctionOf, escapeClosure, noescapeClosure, CS.getConstraintLocator(locator, ConstraintLocator::RvalueAdjustment)); auto result = CS.createTypeVariable( - CS.getConstraintLocator(locator, ConstraintLocator::FunctionResult), - /*options*/0); + CS.getConstraintLocator(locator, ConstraintLocator::FunctionResult)); auto bodyClosure = FunctionType::get( ParenType::get(CS.getASTContext(), escapeClosure), result, FunctionType::ExtInfo(FunctionType::Representation::Swift, @@ -1521,17 +1613,14 @@ resolveOverloadForDeclWithSpecialTypeCheckingSemantics(ConstraintSystem &CS, // The body closure receives a freshly-opened archetype constrained by the // existential type as its input. auto openedTy = CS.createTypeVariable( - CS.getConstraintLocator(locator, ConstraintLocator::FunctionArgument), - /*options*/0); + CS.getConstraintLocator(locator, ConstraintLocator::FunctionArgument)); auto existentialTy = CS.createTypeVariable( - CS.getConstraintLocator(locator, ConstraintLocator::FunctionArgument), - /*options*/0); + CS.getConstraintLocator(locator, ConstraintLocator::FunctionArgument)); CS.addConstraint(ConstraintKind::OpenedExistentialOf, openedTy, existentialTy, CS.getConstraintLocator(locator, ConstraintLocator::RvalueAdjustment)); auto result = CS.createTypeVariable( - CS.getConstraintLocator(locator, ConstraintLocator::FunctionResult), - /*options*/0); + CS.getConstraintLocator(locator, ConstraintLocator::FunctionResult)); auto bodyClosure = FunctionType::get( ParenType::get(CS.getASTContext(), openedTy), result, FunctionType::ExtInfo(FunctionType::Representation::Swift, @@ -1613,31 +1702,6 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator, choice.getFunctionRefKind(), locator); } - if (!isRequirementOrWitness(locator) && - choice.getDecl()->getAttrs().hasAttribute() && - !isa(choice.getDecl())) { - // For a non-subscript declaration that is an optional - // requirement in a protocol, strip off the lvalue-ness (FIXME: - // one cannot assign to such declarations for now) and make a - // reference to that declaration be optional. - // - // Subscript declarations are handled within - // getTypeOfMemberReference(); their result types are optional. - - // Deal with values declared as implicitly unwrapped, or - // functions with return types that are implicitly unwrapped. - if (choice.isImplicitlyUnwrappedValueOrReturnValue()) { - // Build the disjunction to attempt binding both T? and T (or - // function returning T? and function returning T). - Type ty = createTypeVariable(locator, /*options*/0); - buildDisjunctionForImplicitlyUnwrappedOptional(ty, refType, - locator); - addConstraint(ConstraintKind::Bind, boundType, - OptionalType::get(ty->getRValueType()), locator); - bindConstraintCreated = true; - } - refType = OptionalType::get(refType->getRValueType()); - } // For a non-subscript declaration found via dynamic lookup, strip // off the lvalue-ness (FIXME: as a temporary hack. We eventually // want this to work) and make a reference to that declaration be @@ -1646,7 +1710,7 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator, // Subscript declarations are handled within // getTypeOfMemberReference(); their result types are unchecked // optional. - else if (isDynamicResult) { + if (isDynamicResult) { if (isa(choice.getDecl())) { // We always expect function type for subscripts. auto fnTy = refType->castTo(); @@ -1659,7 +1723,7 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator, // For our original type T -> U?? we will generate: // A disjunction V = { U?, U } // and a disjunction boundType = { T -> V?, T -> V } - Type ty = createTypeVariable(locator, /*options*/0); + Type ty = createTypeVariable(locator); buildDisjunctionForImplicitlyUnwrappedOptional(ty, optTy, locator); @@ -1711,8 +1775,31 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator, } bindConstraintCreated = true; - } + } else if (!isRequirementOrWitness(locator) && + choice.getDecl()->getAttrs().hasAttribute() && + !isa(choice.getDecl())) { + // For a non-subscript declaration that is an optional + // requirement in a protocol, strip off the lvalue-ness (FIXME: + // one cannot assign to such declarations for now) and make a + // reference to that declaration be optional. + // + // Subscript declarations are handled within + // getTypeOfMemberReference(); their result types are optional. + // Deal with values declared as implicitly unwrapped, or + // functions with return types that are implicitly unwrapped. + if (choice.isImplicitlyUnwrappedValueOrReturnValue()) { + // Build the disjunction to attempt binding both T? and T (or + // function returning T? and function returning T). + Type ty = createTypeVariable(locator); + buildDisjunctionForImplicitlyUnwrappedOptional(ty, refType, locator); + addConstraint(ConstraintKind::Bind, boundType, + OptionalType::get(ty->getRValueType()), locator); + bindConstraintCreated = true; + } + + refType = OptionalType::get(refType->getRValueType()); + } // If the declaration is unavailable, note that in the score. if (choice.getDecl()->getAttrs().isUnavailable(getASTContext())) { increaseScore(SK_Unavailable); @@ -1769,14 +1856,12 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator, // The element type is T or @lvalue T based on the key path subtype and // the mutability of the base. auto keyPathIndexTy = createTypeVariable( - getConstraintLocator(locator, ConstraintLocator::FunctionArgument), - /*options*/0); + getConstraintLocator(locator, ConstraintLocator::FunctionArgument)); auto elementTy = createTypeVariable( getConstraintLocator(locator, ConstraintLocator::FunctionArgument), TVO_CanBindToLValue); auto elementObjTy = createTypeVariable( - getConstraintLocator(locator, ConstraintLocator::FunctionArgument), - /*options*/0); + getConstraintLocator(locator, ConstraintLocator::FunctionArgument)); addConstraint(ConstraintKind::Equal, elementTy, elementObjTy, locator); // The element result is an lvalue or rvalue based on the key path class. @@ -1808,10 +1893,8 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator, if (boundFunctionType && CD->hasThrows() != boundFunctionType->throws()) { - boundType = FunctionType::get(boundFunctionType->getInput(), - boundFunctionType->getResult(), - boundFunctionType->getExtInfo(). - withThrows()); + boundType = boundFunctionType->withExtInfo( + boundFunctionType->getExtInfo().withThrows()); } } } diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index bc4b6ada3459c..051a925de46cf 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -84,7 +84,7 @@ class SavedTypeVariableBinding { }; /// \brief A set of saved type variable bindings. -typedef SmallVector SavedTypeVariableBindings; +using SavedTypeVariableBindings = SmallVector; class ConstraintLocator; @@ -547,9 +547,10 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &out, const Score &score); /// Describes a dependent type that has been opened to a particular type /// variable. -typedef std::pair OpenedType; +using OpenedType = std::pair; -typedef llvm::DenseMap OpenedTypeMap; +using OpenedTypeMap = + llvm::DenseMap; /// \brief A complete solution to a constraint system. /// @@ -672,17 +673,8 @@ class Solution { /// /// \param locator The locator that describes where the substitutions came /// from. - /// - /// \param substitutions Will be populated with the set of substitutions - /// to be applied to the generic function type. - void computeSubstitutions(GenericSignature *sig, - ConstraintLocator *locator, - SmallVectorImpl &substitutions) const; - - /// Same as above but with a lazily-constructed locator. - void computeSubstitutions(GenericSignature *sig, - ConstraintLocatorBuilder locator, - SmallVectorImpl &substitutions) const; + SubstitutionMap computeSubstitutions(GenericSignature *sig, + ConstraintLocatorBuilder locator) const; /// Return the disjunction choice for the given constraint location. unsigned getDisjunctionChoice(ConstraintLocator *locator) const { @@ -789,7 +781,7 @@ struct SpecificConstraint { }; /// An intrusive, doubly-linked list of constraints. -typedef llvm::ilist ConstraintList; +using ConstraintList = llvm::ilist; enum class ConstraintSystemFlags { /// Whether we allow the solver to attempt fixes to the system. @@ -805,7 +797,7 @@ enum class ConstraintSystemFlags { }; /// Options that affect the constraint system as a whole. -typedef OptionSet ConstraintSystemOptions; +using ConstraintSystemOptions = OptionSet; /// This struct represents the results of a member lookup of struct MemberLookupResult { @@ -1182,7 +1174,7 @@ class ConstraintSystem { /// /// \param processor The processor function to be applied to each of /// the constraints retrieved. - void forEachRetired(std::function processor) { + void forEachRetired(llvm::function_ref processor) { for (auto &constraint : retiredConstraints) processor(constraint); } @@ -1566,7 +1558,7 @@ class ConstraintSystem { /// \brief Create a new type variable. TypeVariableType *createTypeVariable(ConstraintLocator *locator, - unsigned options); + unsigned options = 0); /// Retrieve the set of active type variables. ArrayRef getTypeVariables() const { @@ -1747,6 +1739,13 @@ class ConstraintSystem { /// emits an error message. void diagnoseFailureForExpr(Expr *expr); + /// \brief Give the deprecation warning for referring to a global function + /// when there's a method from a conditional conformance in a smaller/closer + /// scope. + void + diagnoseDeprecatedConditionalConformanceOuterAccess(UnresolvedDotExpr *UDE, + ValueDecl *choice); + /// \brief Add a constraint to the constraint system. void addConstraint(ConstraintKind kind, Type first, Type second, ConstraintLocatorBuilder locator, @@ -1783,14 +1782,15 @@ class ConstraintSystem { void addValueMemberConstraint(Type baseTy, DeclName name, Type memberTy, DeclContext *useDC, FunctionRefKind functionRefKind, + ArrayRef outerAlternatives, ConstraintLocatorBuilder locator) { assert(baseTy); assert(memberTy); assert(name); assert(useDC); - switch (simplifyMemberConstraint(ConstraintKind::ValueMember, baseTy, name, - memberTy, useDC, functionRefKind, - TMF_GenerateConstraints, locator)) { + switch (simplifyMemberConstraint( + ConstraintKind::ValueMember, baseTy, name, memberTy, useDC, + functionRefKind, outerAlternatives, TMF_GenerateConstraints, locator)) { case SolutionKind::Unsolved: llvm_unreachable("Unsolved result when generating constraints!"); @@ -1799,10 +1799,9 @@ class ConstraintSystem { case SolutionKind::Error: if (shouldAddNewFailingConstraint()) { - addNewFailingConstraint( - Constraint::createMember(*this, ConstraintKind::ValueMember, baseTy, - memberTy, name, useDC, functionRefKind, - getConstraintLocator(locator))); + addNewFailingConstraint(Constraint::createMemberOrOuterDisjunction( + *this, ConstraintKind::ValueMember, baseTy, memberTy, name, useDC, + functionRefKind, outerAlternatives, getConstraintLocator(locator))); } break; } @@ -1821,6 +1820,7 @@ class ConstraintSystem { switch (simplifyMemberConstraint(ConstraintKind::UnresolvedValueMember, baseTy, name, memberTy, useDC, functionRefKind, + /*outerAlternatives=*/{}, TMF_GenerateConstraints, locator)) { case SolutionKind::Unsolved: llvm_unreachable("Unsolved result when generating constraints!"); @@ -1909,6 +1909,12 @@ class ConstraintSystem { /// due to a change. ConstraintList &getActiveConstraints() { return ActiveConstraints; } + void findConstraints(SmallVectorImpl &found, + llvm::function_ref pred) { + filterConstraints(ActiveConstraints, pred, found); + filterConstraints(InactiveConstraints, pred, found); + } + /// \brief Retrieve the representative of the equivalence class containing /// this type variable. TypeVariableType *getRepresentative(TypeVariableType *typeVar) { @@ -1942,7 +1948,7 @@ class ConstraintSystem { }; /// Options that govern how type matching should proceed. - typedef OptionSet TypeMatchOptions; + using TypeMatchOptions = OptionSet; /// \brief Retrieve the fixed type corresponding to the given type variable, /// or a null type if there is no fixed type. @@ -2074,6 +2080,16 @@ class ConstraintSystem { /// into the worklist. void addTypeVariableConstraintsToWorkList(TypeVariableType *typeVar); + static void + filterConstraints(ConstraintList &constraints, + llvm::function_ref pred, + SmallVectorImpl &found) { + for (auto &constraint : constraints) { + if (pred(constraint)) + found.push_back(&constraint); + } + } + public: /// \brief Coerce the given expression to an rvalue, if it isn't already. @@ -2189,7 +2205,8 @@ class ConstraintSystem { /// sets. void addOverloadSet(Type boundType, ArrayRef choices, DeclContext *useDC, ConstraintLocator *locator, - OverloadChoice *favored = nullptr); + OverloadChoice *favored = nullptr, + ArrayRef outerAlternatives = {}); /// \brief Retrieve the allocator used by this constraint system. llvm::BumpPtrAllocator &getAllocator() { return Allocator; } @@ -2197,7 +2214,7 @@ class ConstraintSystem { template ArrayRef::value_type> allocateCopy(It start, It end) { - typedef typename std::iterator_traits::value_type T; + using T = typename std::iterator_traits::value_type; T *result = (T*)getAllocator().Allocate(sizeof(T)*(end-start), alignof(T)); unsigned i; for (i = 0; start != end; ++start, ++i) @@ -2337,11 +2354,10 @@ class ConstraintSystem { /// \brief Subroutine of \c matchTypes(), used to bind a type to a /// type variable. - TypeMatchResult - matchTypesBindTypeVar(TypeVariableType *typeVar, Type type, - ConstraintKind kind, TypeMatchOptions flags, - ConstraintLocatorBuilder locator, - std::function formUnsolvedResult); + TypeMatchResult matchTypesBindTypeVar( + TypeVariableType *typeVar, Type type, ConstraintKind kind, + TypeMatchOptions flags, ConstraintLocatorBuilder locator, + llvm::function_ref formUnsolvedResult); public: // FIXME: public due to statics in CSSimplify.cpp /// \brief Attempt to match up types \c type1 and \c type2, which in effect @@ -2413,6 +2429,13 @@ class ConstraintSystem { void buildDisjunctionForOptionalVsUnderlying(Type boundTy, Type type, ConstraintLocator *locator) { + // NOTE: If we use other locator kinds for these disjunctions, we + // need to account for it in solution scores for forced-unwraps. + assert(locator->getPath().back().getKind() == + ConstraintLocator::ImplicitlyUnwrappedDisjunctionChoice || + locator->getPath().back().getKind() == + ConstraintLocator::DynamicLookupResult); + // Create the constraint to bind to the optional type and make it // the favored choice. auto *bindToOptional = @@ -2553,14 +2576,12 @@ class ConstraintSystem { ConstraintLocatorBuilder locator); /// \brief Attempt to simplify the given member constraint. - SolutionKind simplifyMemberConstraint(ConstraintKind kind, - Type baseType, DeclName member, - Type memberType, DeclContext *useDC, - FunctionRefKind functionRefKind, - TypeMatchOptions flags, - ConstraintLocatorBuilder locator); + SolutionKind simplifyMemberConstraint( + ConstraintKind kind, Type baseType, DeclName member, Type memberType, + DeclContext *useDC, FunctionRefKind functionRefKind, + ArrayRef outerAlternatives, TypeMatchOptions flags, + ConstraintLocatorBuilder locator); - /// \brief Attempt to simplify the optional object constraint. SolutionKind simplifyOptionalObjectConstraint( Type first, Type second, @@ -2714,8 +2735,8 @@ class ConstraintSystem { }; struct PotentialBindings { - typedef std::tuple - BindingScore; + using BindingScore = + std::tuple; TypeVariableType *TypeVar; @@ -2769,10 +2790,7 @@ class ConstraintSystem { if (formBindingScore(x) < formBindingScore(y)) return true; - if (!x.hasNonDefaultableBindings()) - return false; - - if (x.FullyBound || x.SubtypeOfExistentialType) + if (formBindingScore(y) < formBindingScore(x)) return false; // If the only difference is default types, @@ -3136,7 +3154,7 @@ static inline bool computeTupleShuffle(TupleType *fromTuple, /// Describes the arguments to which a parameter binds. /// FIXME: This is an awful data structure. We want the equivalent of a /// TinyPtrVector for unsigned values. -typedef SmallVector ParamBinding; +using ParamBinding = SmallVector; /// Class used as the base for listeners to the \c matchCallArguments process. /// @@ -3369,6 +3387,12 @@ TypeVariableType *TypeVariableType::getNew(const ASTContext &C, unsigned ID, /// underlying forced downcast expression. ForcedCheckedCastExpr *findForcedDowncast(ASTContext &ctx, Expr *expr); + +// Erases any opened existentials from the given expression. +// Note: this may update the provided expr pointer. +void eraseOpenedExistentials(constraints::ConstraintSystem &CS, Expr *&expr); + + /// ExprCleaner - This class is used by shrink to ensure that in /// no situation will an expr node be left with a dangling type variable stuck /// to it. Often type checking will create new AST nodes and replace old ones @@ -3377,9 +3401,6 @@ ForcedCheckedCastExpr *findForcedDowncast(ASTContext &ctx, Expr *expr); /// their type variables, and we don't want pointers into the original AST to /// dereference these now-dangling types. class ExprCleaner { - llvm::SmallVector Exprs; - llvm::SmallVector TypeLocs; - llvm::SmallVector Patterns; llvm::SmallVector Vars; public: @@ -3388,21 +3409,6 @@ class ExprCleaner { ExprCleaner *TS; ExprCleanerImpl(ExprCleaner *TS) : TS(TS) {} - std::pair walkToExprPre(Expr *expr) override { - TS->Exprs.push_back(expr); - return { true, expr }; - } - - bool walkToTypeLocPre(TypeLoc &TL) override { - TS->TypeLocs.push_back(&TL); - return true; - } - - std::pair walkToPatternPre(Pattern *P) override { - TS->Patterns.push_back(P); - return { true, P }; - } - bool walkToDeclPre(Decl *D) override { if (auto VD = dyn_cast(D)) TS->Vars.push_back(VD); @@ -3423,22 +3429,6 @@ class ExprCleaner { ~ExprCleaner() { // Check each of the expression nodes to verify that there are no type // variables hanging out. If so, just nuke the type. - for (auto E : Exprs) { - if (E->getType() && E->getType()->hasTypeVariable()) - E->setType(Type()); - } - - for (auto TL : TypeLocs) { - if (TL->getTypeRepr() && TL->getType() && - TL->getType()->hasTypeVariable()) - TL->setType(Type(), false); - } - - for (auto P : Patterns) { - if (P->hasType() && P->getType()->hasTypeVariable()) - P->setType(Type()); - } - for (auto VD : Vars) { if (VD->hasType() && VD->getType()->hasTypeVariable()) { VD->setType(Type()); diff --git a/lib/Sema/DebuggerTestingTransform.cpp b/lib/Sema/DebuggerTestingTransform.cpp index c44621d5e0e3b..77b8f8ac99a5c 100644 --- a/lib/Sema/DebuggerTestingTransform.cpp +++ b/lib/Sema/DebuggerTestingTransform.cpp @@ -74,12 +74,19 @@ class DebuggerTestingTransform : public ASTWalker { bool walkToDeclPre(Decl *D) override { pushLocalDeclContext(D); + // Skip implicit decls, because the debugger isn't used to step through + // these. + if (D->isImplicit()) + return false; + // Whitelist the kinds of decls to transform. // TODO: Expand the set of decls visited here. if (auto *FD = dyn_cast(D)) - return !FD->isImplicit() && FD->getBody(); + return FD->getBody(); if (auto *TLCD = dyn_cast(D)) - return !TLCD->isImplicit() && TLCD->getBody(); + return TLCD->getBody(); + if (isa(D)) + return true; return false; } @@ -130,18 +137,20 @@ class DebuggerTestingTransform : public ASTWalker { return LocalDeclContextStack.back(); } - /// Try to extract a DeclRefExpr from the expression. - DeclRefExpr *extractDecl(Expr *E) { - while (!isa(E)) { + /// Try to extract a DeclRefExpr or MemberRefExpr from the expression. + Expr *extractDeclOrMemberRef(Expr *E) { + while (!isa(E) && !isa(E)) { // TODO: Try more ways to extract interesting decl refs. if (auto *Subscript = dyn_cast(E)) E = Subscript->getBase(); else if (auto *InOut = dyn_cast(E)) E = InOut->getSubExpr(); + else if (auto *MemberRef = dyn_cast(E)) + E = MemberRef->getBase(); else return nullptr; } - return cast(E); + return E; } /// Attempt to create a functionally-equivalent replacement for OriginalExpr, @@ -152,11 +161,17 @@ class DebuggerTestingTransform : public ASTWalker { /// recursively transform the children of the transformed expression, and 2) /// the transformed expression itself. std::pair insertCheckExpect(Expr *OriginalExpr, Expr *DstExpr) { - auto *DstDRE = extractDecl(DstExpr); - if (!DstDRE) + auto *DstRef = extractDeclOrMemberRef(DstExpr); + if (!DstRef) return {true, OriginalExpr}; - ValueDecl *DstDecl = DstDRE->getDecl(); + ValueDecl *DstDecl; + if (auto *DRE = dyn_cast(DstRef)) + DstDecl = DRE->getDecl(); + else { + auto *MRE = cast(DstRef); + DstDecl = MRE->getMember().getDecl(); + } if (!DstDecl->hasName()) return {true, OriginalExpr}; @@ -184,7 +199,7 @@ class DebuggerTestingTransform : public ASTWalker { auto *PODeclRef = new (Ctx) UnresolvedDeclRefExpr(Ctx.getIdentifier("_stringForPrintObject"), DeclRefKind::Ordinary, DeclNameLoc()); - Expr *POArgs[] = {DstDRE}; + Expr *POArgs[] = {DstRef}; Identifier POLabels[] = {Identifier()}; auto *POCall = CallExpr::createImplicit(Ctx, PODeclRef, POArgs, POLabels); POCall->setThrows(false); diff --git a/lib/Sema/DerivedConformanceCaseIterable.cpp b/lib/Sema/DerivedConformanceCaseIterable.cpp index 6961d6e7022a6..772593ab6d758 100644 --- a/lib/Sema/DerivedConformanceCaseIterable.cpp +++ b/lib/Sema/DerivedConformanceCaseIterable.cpp @@ -22,7 +22,6 @@ #include "DerivedConformances.h" using namespace swift; -using namespace DerivedConformance; /// Common preconditions for CaseIterable. static bool canDeriveConformance(NominalTypeDecl *type) { @@ -43,7 +42,7 @@ static bool canDeriveConformance(NominalTypeDecl *type) { void deriveCaseIterable_enum_getter(AbstractFunctionDecl *funcDecl) { auto *parentDC = funcDecl->getDeclContext(); auto *parentEnum = parentDC->getAsEnumOrEnumExtensionContext(); - auto enumTy = parentEnum->getDeclaredTypeInContext(); + auto enumTy = parentDC->getDeclaredTypeInContext(); auto &C = parentDC->getASTContext(); SmallVector elExprs; @@ -69,96 +68,64 @@ static ArraySliceType *computeAllCasesType(NominalTypeDecl *enumType) { return ArraySliceType::get(metaTy->getRValueInstanceType()); } -static Type deriveCaseIterable_AllCases(TypeChecker &tc, Decl *parentDecl, - EnumDecl *enumDecl) { +static Type deriveCaseIterable_AllCases(DerivedConformance &derived) { // enum SomeEnum : CaseIterable { // @derived // typealias AllCases = [SomeEnum] // } - auto *rawInterfaceType = computeAllCasesType(enumDecl); - return cast(parentDecl)->mapTypeIntoContext(rawInterfaceType); + auto *rawInterfaceType = computeAllCasesType(cast(derived.Nominal)); + return derived.getConformanceContext()->mapTypeIntoContext(rawInterfaceType); } -ValueDecl *DerivedConformance::deriveCaseIterable(TypeChecker &tc, - Decl *parentDecl, - NominalTypeDecl *targetDecl, - ValueDecl *requirement) { +ValueDecl *DerivedConformance::deriveCaseIterable(ValueDecl *requirement) { // Conformance can't be synthesized in an extension. - auto caseIterableProto - = tc.Context.getProtocol(KnownProtocolKind::CaseIterable); - auto caseIterableType = caseIterableProto->getDeclaredType(); - if (targetDecl != parentDecl) { - tc.diagnose(parentDecl->getLoc(), diag::cannot_synthesize_in_extension, - caseIterableType); + if (checkAndDiagnoseDisallowedContext(requirement)) return nullptr; - } // Check that we can actually derive CaseIterable for this type. - if (!canDeriveConformance(targetDecl)) + if (!canDeriveConformance(Nominal)) return nullptr; // Build the necessary decl. - if (requirement->getBaseName() != tc.Context.Id_allCases) { - tc.diagnose(requirement->getLoc(), - diag::broken_case_iterable_requirement); + if (requirement->getBaseName() != TC.Context.Id_allCases) { + TC.diagnose(requirement->getLoc(), diag::broken_case_iterable_requirement); return nullptr; } - auto enumDecl = cast(targetDecl); - ASTContext &C = tc.Context; - + ASTContext &C = TC.Context; // Define the property. - auto *returnTy = computeAllCasesType(targetDecl); + auto *returnTy = computeAllCasesType(Nominal); VarDecl *propDecl; PatternBindingDecl *pbDecl; - std::tie(propDecl, pbDecl) - = declareDerivedProperty(tc, parentDecl, enumDecl, C.Id_allCases, - returnTy, returnTy, + std::tie(propDecl, pbDecl) = + declareDerivedProperty(C.Id_allCases, returnTy, returnTy, /*isStatic=*/true, /*isFinal=*/true); // Define the getter. - auto *getterDecl = addGetterToReadOnlyDerivedProperty(tc, propDecl, returnTy); + auto *getterDecl = addGetterToReadOnlyDerivedProperty(TC, propDecl, returnTy); getterDecl->setBodySynthesizer(&deriveCaseIterable_enum_getter); - auto dc = cast(parentDecl); - dc->addMember(getterDecl); - dc->addMember(propDecl); - dc->addMember(pbDecl); + addMembersToConformanceContext({getterDecl, propDecl, pbDecl}); return propDecl; } -Type DerivedConformance::deriveCaseIterable(TypeChecker &tc, Decl *parentDecl, - NominalTypeDecl *targetDecl, - AssociatedTypeDecl *assocType) { - // Conformance can't be synthesized in an extension. - auto caseIterableProto - = tc.Context.getProtocol(KnownProtocolKind::CaseIterable); - auto caseIterableType = caseIterableProto->getDeclaredType(); - if (targetDecl != parentDecl) { - tc.diagnose(parentDecl->getLoc(), diag::cannot_synthesize_in_extension, - caseIterableType); - return nullptr; - } - - // We can only synthesize CaseIterable for enums. - auto enumDecl = dyn_cast(targetDecl); - if (!enumDecl) +Type DerivedConformance::deriveCaseIterable(AssociatedTypeDecl *assocType) { + if (checkAndDiagnoseDisallowedContext(assocType)) return nullptr; // Check that we can actually derive CaseIterable for this type. - if (!canDeriveConformance(targetDecl)) + if (!canDeriveConformance(Nominal)) return nullptr; - if (assocType->getName() == tc.Context.Id_AllCases) { - return deriveCaseIterable_AllCases(tc, parentDecl, enumDecl); + if (assocType->getName() == TC.Context.Id_AllCases) { + return deriveCaseIterable_AllCases(*this); } - tc.diagnose(assocType->getLoc(), - diag::broken_case_iterable_requirement); + TC.diagnose(assocType->getLoc(), diag::broken_case_iterable_requirement); return nullptr; } diff --git a/lib/Sema/DerivedConformanceCodable.cpp b/lib/Sema/DerivedConformanceCodable.cpp index c8e687f840b59..c401658b42c48 100644 --- a/lib/Sema/DerivedConformanceCodable.cpp +++ b/lib/Sema/DerivedConformanceCodable.cpp @@ -26,7 +26,6 @@ #include "DerivedConformances.h" using namespace swift; -using namespace DerivedConformance; /// Returns whether the type represented by the given ClassDecl inherits from a /// type which conforms to the given protocol. @@ -84,6 +83,7 @@ static CodableConformanceType typeConformsToCodable(TypeChecker &tc, DeclContext *context, Type target, bool isIUO, ProtocolDecl *proto) { + target = context->mapTypeIntoContext(target->mapTypeOutOfContext()); // Some generic types need to be introspected to get at their "true" Codable // conformance. if (auto referenceType = target->getAs()) { @@ -106,7 +106,7 @@ static CodableConformanceType typeConformsToCodable(TypeChecker &tc, /// /// \param tc The typechecker to use in validating {En,De}codable conformance. /// -/// \param context The \c DeclContext the var declarations belong to. +/// \param context The \c DeclContext in which to check conformance. /// /// \param varDecl The \c VarDecl to validate. /// @@ -145,17 +145,12 @@ static CodableConformanceType varConformsToCodable(TypeChecker &tc, /// Validates the given CodingKeys enum decl by ensuring its cases are a 1-to-1 /// match with the stored vars of the given type. /// -/// \param tc The typechecker to use in validating {En,De}codable conformance. -/// /// \param codingKeysDecl The \c CodingKeys enum decl to validate. -/// -/// \param target The nominal type decl to validate the \c CodingKeys against. -/// -/// \param proto The {En,De}codable protocol to validate all the keys conform -/// to. -static bool -validateCodingKeysEnum(TypeChecker &tc, EnumDecl *codingKeysDecl, - NominalTypeDecl *target, ProtocolDecl *proto) { +static bool validateCodingKeysEnum(DerivedConformance &derived, + EnumDecl *codingKeysDecl) { + auto &tc = derived.TC; + auto conformanceDC = derived.getConformanceContext(); + // Look through all var decls in the given type. // * Filter out lazy/computed vars. // * Filter out ones which are present in the given decl (by name). @@ -168,7 +163,8 @@ validateCodingKeysEnum(TypeChecker &tc, EnumDecl *codingKeysDecl, // Here we'll hold on to properties by name -- when we've validated a property // against its CodingKey entry, it will get removed. llvm::SmallDenseMap properties; - for (auto *varDecl : target->getStoredProperties(/*skipInaccessible=*/true)) { + for (auto *varDecl : + derived.Nominal->getStoredProperties(/*skipInaccessible=*/true)) { if (varDecl->getAttrs().hasAttribute()) continue; @@ -188,8 +184,8 @@ validateCodingKeysEnum(TypeChecker &tc, EnumDecl *codingKeysDecl, } // We have a property to map to. Ensure it's {En,De}codable. - auto conformance = varConformsToCodable(tc, target->getDeclContext(), - it->second, proto); + auto conformance = + varConformsToCodable(tc, conformanceDC, it->second, derived.Protocol); switch (conformance) { case Conforms: // The property was valid. Remove it from the list. @@ -199,7 +195,7 @@ validateCodingKeysEnum(TypeChecker &tc, EnumDecl *codingKeysDecl, case DoesNotConform: tc.diagnose(it->second->getLoc(), diag::codable_non_conforming_property_here, - proto->getDeclaredType(), it->second->getType()); + derived.getProtocolType(), it->second->getType()); LLVM_FALLTHROUGH; case TypeNotValidated: @@ -217,7 +213,7 @@ validateCodingKeysEnum(TypeChecker &tc, EnumDecl *codingKeysDecl, // we can skip them on encode. On decode, though, we can only skip them if // they have a default value. if (!properties.empty() && - proto->isSpecificProtocol(KnownProtocolKind::Decodable)) { + derived.Protocol->isSpecificProtocol(KnownProtocolKind::Decodable)) { for (auto it = properties.begin(); it != properties.end(); ++it) { // If the var is default initializable, then it need not have an explicit // initial value. @@ -234,7 +230,7 @@ validateCodingKeysEnum(TypeChecker &tc, EnumDecl *codingKeysDecl, // initial value. propertiesAreValid = false; tc.diagnose(it->second->getLoc(), diag::codable_non_decoded_property_here, - proto->getDeclaredType(), it->first); + derived.getProtocolType(), it->first); } } @@ -257,19 +253,12 @@ struct CodingKeysValidity { /// diagnostics here, we don't want to then attempt to synthesize a CodingKeys /// enum. /// -/// \param tc The typechecker to use in validating {En,Decodable} conformance. -/// -/// \param target The type decl whose nested \c CodingKeys type to validate. -/// -/// \param proto The {En,De}codable protocol to ensure the properties matching -/// the keys conform to. -/// /// \returns A \c CodingKeysValidity value representing the result of the check. -static CodingKeysValidity hasValidCodingKeysEnum(TypeChecker &tc, - NominalTypeDecl *target, - ProtocolDecl *proto) { +static CodingKeysValidity hasValidCodingKeysEnum(DerivedConformance &derived) { + auto &tc = derived.TC; auto &C = tc.Context; - auto codingKeysDecls = target->lookupDirect(DeclName(C.Id_CodingKeys)); + auto codingKeysDecls = + derived.Nominal->lookupDirect(DeclName(C.Id_CodingKeys)); if (codingKeysDecls.empty()) return CodingKeysValidity(/*hasType=*/false, /*isValid=*/true); @@ -282,7 +271,7 @@ static CodingKeysValidity hasValidCodingKeysEnum(TypeChecker &tc, if (!codingKeysTypeDecl) { tc.diagnose(result->getLoc(), diag::codable_codingkeys_type_is_not_an_enum_here, - proto->getDeclaredType()); + derived.getProtocolType()); return CodingKeysValidity(/*hasType=*/true, /*isValid=*/false); } @@ -298,11 +287,19 @@ static CodingKeysValidity hasValidCodingKeysEnum(TypeChecker &tc, // Ensure that the type we found conforms to the CodingKey protocol. auto *codingKeyProto = C.getProtocol(KnownProtocolKind::CodingKey); if (!tc.conformsToProtocol(codingKeysType, codingKeyProto, - target->getDeclContext(), + derived.getConformanceContext(), ConformanceCheckFlags::Used)) { - tc.diagnose(codingKeysTypeDecl->getLoc(), - diag::codable_codingkeys_type_does_not_conform_here, - proto->getDeclaredType()); + // If CodingKeys is a typealias which doesn't point to a valid nominal type, + // codingKeysTypeDecl will be nullptr here. In that case, we need to warn on + // the location of the usage, since there isn't an underlying type to + // diagnose on. + SourceLoc loc = codingKeysTypeDecl ? + codingKeysTypeDecl->getLoc() : + cast(result)->getLoc(); + + tc.diagnose(loc, diag::codable_codingkeys_type_does_not_conform_here, + derived.getProtocolType()); + return CodingKeysValidity(/*hasType=*/true, /*isValid=*/false); } @@ -311,30 +308,26 @@ static CodingKeysValidity hasValidCodingKeysEnum(TypeChecker &tc, if (!codingKeysEnum) { tc.diagnose(codingKeysTypeDecl->getLoc(), diag::codable_codingkeys_type_is_not_an_enum_here, - proto->getDeclaredType()); + derived.getProtocolType()); return CodingKeysValidity(/*hasType=*/true, /*isValid=*/false); } - bool valid = validateCodingKeysEnum(tc, codingKeysEnum, target, proto); + bool valid = validateCodingKeysEnum(derived, codingKeysEnum); return CodingKeysValidity(/*hasType=*/true, /*isValid=*/valid); } /// Synthesizes a new \c CodingKeys enum based on the {En,De}codable members of /// the given type (\c nullptr if unable to synthesize). /// -/// If able to synthesize the enum, adds it directly to \c type. -/// -/// \param tc The typechecker to use in validating {En,De}codable conformance. -/// -/// \param target The nominal type decl whose nested \c CodingKeys type to -/// synthesize. -/// -/// \param proto The {En,De}codable protocol to validate all the keys conform -/// to. -static EnumDecl *synthesizeCodingKeysEnum(TypeChecker &tc, - NominalTypeDecl *target, - ProtocolDecl *proto) { +/// If able to synthesize the enum, adds it directly to \c derived.Nominal. +static EnumDecl *synthesizeCodingKeysEnum(DerivedConformance &derived) { + auto &tc = derived.TC; auto &C = tc.Context; + // Create CodingKeys in the parent type always, because both + // Encodable and Decodable might want to use it, and they may have + // different conditional bounds. CodingKeys is simple and can't + // depend on those bounds. + auto target = derived.Nominal; // We want to look through all the var declarations of this type to create // enum cases based on those var names. @@ -369,8 +362,12 @@ static EnumDecl *synthesizeCodingKeysEnum(TypeChecker &tc, if (varDecl->getAttrs().hasAttribute()) continue; - auto conformance = varConformsToCodable(tc, target->getDeclContext(), - varDecl, proto); + // Despite creating the enum in the context of the type, we're + // concurrently checking the variables for the current protocol + // conformance being synthesized, for which we use the conformance + // context, not the type. + auto conformance = varConformsToCodable(tc, derived.getConformanceContext(), + varDecl, derived.Protocol); switch (conformance) { case Conforms: { @@ -385,7 +382,7 @@ static EnumDecl *synthesizeCodingKeysEnum(TypeChecker &tc, case DoesNotConform: tc.diagnose(varDecl->getLoc(), diag::codable_non_conforming_property_here, - proto->getDeclaredType(), varDecl->getType()); + derived.getProtocolType(), varDecl->getType()); LLVM_FALLTHROUGH; case TypeNotValidated: @@ -534,7 +531,8 @@ static void deriveBodyEncodable_encode(AbstractFunctionDecl *encodeDecl) { // } // The enclosing type decl. - auto *targetDecl = cast(encodeDecl->getDeclContext()); + auto *targetDecl = encodeDecl->getDeclContext() + ->getAsNominalTypeOrNominalTypeExtensionContext(); auto *funcDC = cast(encodeDecl); auto &C = funcDC->getASTContext(); @@ -596,13 +594,19 @@ static void deriveBodyEncodable_encode(AbstractFunctionDecl *encodeDecl) { // Now need to generate `try container.encode(x, forKey: .x)` for all // existing properties. Optional properties get `encodeIfPresent`. for (auto *elt : codingKeysEnum->getAllElements()) { - VarDecl *varDecl; - for (auto decl : targetDecl->lookupDirect(DeclName(elt->getName()))) - if ((varDecl = dyn_cast(decl))) - break; + VarDecl *varDecl = nullptr; + for (auto decl : targetDecl->lookupDirect(DeclName(elt->getName()))) { + if (auto *vd = dyn_cast(decl)) { + if (!vd->isStatic()) { + varDecl = vd; + break; + } + } + } + assert(varDecl && "Should have found at least 1 var decl"); // self.x - auto *selfRef = createSelfDeclRef(encodeDecl); + auto *selfRef = DerivedConformance::createSelfDeclRef(encodeDecl); auto *varExpr = new (C) MemberRefExpr(selfRef, SourceLoc(), ConcreteDeclRef(varDecl), DeclNameLoc(), /*Implicit=*/true); @@ -690,15 +694,9 @@ static void deriveBodyEncodable_encode(AbstractFunctionDecl *encodeDecl) { /// lazily synthesized body for the given type. /// /// Adds the function declaration to the given type before returning it. -/// -/// \param tc The type checker whose AST context to synthesize the decl in. -/// -/// \param parentDecl The parent declaration of the type. -/// -/// \param target The nominal type to synthesize the function for. -static FuncDecl *deriveEncodable_encode(TypeChecker &tc, Decl *parentDecl, - NominalTypeDecl *target) { - auto &C = tc.Context; +static FuncDecl *deriveEncodable_encode(DerivedConformance &derived) { + auto &C = derived.TC.Context; + auto conformanceDC = derived.getConformanceContext(); // Expected type: (Self) -> (Encoder) throws -> () // Constructed as: func type @@ -724,10 +722,10 @@ static FuncDecl *deriveEncodable_encode(TypeChecker &tc, Decl *parentDecl, auto innerType = FunctionType::get(inputType, returnType, extInfo); // Params: (self [implicit], Encoder) - auto *selfDecl = ParamDecl::createSelf(SourceLoc(), target); + auto *selfDecl = ParamDecl::createSelf(SourceLoc(), conformanceDC); auto *encoderParam = new (C) ParamDecl(VarDecl::Specifier::Default, SourceLoc(), SourceLoc(), C.Id_to, - SourceLoc(), C.Id_encoder, encoderType, target); + SourceLoc(), C.Id_encoder, encoderType, conformanceDC); encoderParam->setInterfaceType(encoderType); ParameterList *params[] = {ParameterList::createWithoutLoc(selfDecl), @@ -735,29 +733,29 @@ static FuncDecl *deriveEncodable_encode(TypeChecker &tc, Decl *parentDecl, // Func name: encode(to: Encoder) DeclName name(C, C.Id_encode, params[1]); - auto *encodeDecl = FuncDecl::create(C, SourceLoc(), StaticSpellingKind::None, - SourceLoc(), name, SourceLoc(), - /*Throws=*/true, SourceLoc(), - nullptr, params, - TypeLoc::withoutLoc(returnType), - target); + auto *encodeDecl = FuncDecl::create( + C, SourceLoc(), StaticSpellingKind::None, SourceLoc(), name, SourceLoc(), + /*Throws=*/true, SourceLoc(), nullptr, params, + TypeLoc::withoutLoc(returnType), conformanceDC); encodeDecl->setImplicit(); + encodeDecl->setSynthesized(); encodeDecl->setBodySynthesizer(deriveBodyEncodable_encode); // This method should be marked as 'override' for classes inheriting Encodable // conformance from a parent class. - auto *classDecl = dyn_cast(target); + auto *classDecl = dyn_cast(derived.Nominal); if (classDecl && superclassIsEncodable(classDecl)) { - auto *attr = new (C) SimpleDeclAttr(/*IsImplicit=*/true); + auto *attr = new (C) OverrideAttr(/*IsImplicit=*/true); encodeDecl->getAttrs().add(attr); } // Evaluate the type of Self in (Self) -> (Encoder) throws -> (). - Type selfType = target->getDeclaredInterfaceType(); + Type selfType = conformanceDC->getDeclaredInterfaceType(); Type interfaceType; - if (auto sig = target->getGenericSignatureOfContext()) { + if (auto sig = conformanceDC->getGenericSignatureOfContext()) { // Evaluate the below, but in a generic environment (if Self is generic). - encodeDecl->setGenericEnvironment(target->getGenericEnvironmentOfContext()); + encodeDecl->setGenericEnvironment( + conformanceDC->getGenericEnvironmentOfContext()); interfaceType = GenericFunctionType::get(sig, selfType, innerType, FunctionType::ExtInfo()); } else { @@ -767,15 +765,12 @@ static FuncDecl *deriveEncodable_encode(TypeChecker &tc, Decl *parentDecl, encodeDecl->setInterfaceType(interfaceType); encodeDecl->setValidationStarted(); - encodeDecl->setAccess(target->getFormalAccess()); + encodeDecl->copyFormalAccessFrom(derived.Nominal, + /*sourceIsParentContext*/ true); - // If the type was not imported, the derived conformance is either from the - // type itself or an extension, in which case we will emit the declaration - // normally. - if (target->hasClangNode()) - tc.Context.addExternalDecl(encodeDecl); + C.addSynthesizedDecl(encodeDecl); - target->addMember(encodeDecl); + derived.addMembersToConformanceContext({encodeDecl}); return encodeDecl; } @@ -801,7 +796,9 @@ static void deriveBodyDecodable_init(AbstractFunctionDecl *initDecl) { // } // The enclosing type decl. - auto *targetDecl = cast(initDecl->getDeclContext()); + auto conformanceDC = initDecl->getDeclContext(); + auto *targetDecl = + conformanceDC->getAsNominalTypeOrNominalTypeExtensionContext(); auto *funcDC = cast(initDecl); auto &C = funcDC->getASTContext(); @@ -878,7 +875,8 @@ static void deriveBodyDecodable_init(AbstractFunctionDecl *initDecl) { // is Optional, we want to decodeIfPresent(T.self, forKey: ...); // otherwise, we can just decode(T.self, forKey: ...). // This is also true if the type is an ImplicitlyUnwrappedOptional. - auto varType = varDecl->getType(); + auto varType = conformanceDC->mapTypeIntoContext( + varDecl->getType()->mapTypeOutOfContext()); auto methodName = C.Id_decode; if (auto referenceType = varType->getAs()) { // This is a weak/unowned/unmanaged var. Get the inner type before @@ -927,7 +925,7 @@ static void deriveBodyDecodable_init(AbstractFunctionDecl *initDecl) { auto *tryExpr = new (C) TryExpr(SourceLoc(), callExpr, Type(), /*Implicit=*/true); - auto *selfRef = createSelfDeclRef(initDecl); + auto *selfRef = DerivedConformance::createSelfDeclRef(initDecl); auto *varExpr = new (C) UnresolvedDotExpr(selfRef, SourceLoc(), DeclName(varDecl->getName()), DeclNameLoc(), @@ -1024,15 +1022,11 @@ static void deriveBodyDecodable_init(AbstractFunctionDecl *initDecl) { /// lazily synthesized body for the given type. /// /// Adds the function declaration to the given type before returning it. -/// -/// \param tc The type checker whose AST context to synthesize the decl in. -/// -/// \param parentDecl The parent declaration of the type. -/// -/// \param target The nominal type to synthesize the function for. -static ValueDecl *deriveDecodable_init(TypeChecker &tc, Decl *parentDecl, - NominalTypeDecl *target) { - auto &C = tc.Context; +static ValueDecl *deriveDecodable_init(DerivedConformance &derived) { + auto &C = derived.TC.Context; + + auto classDecl = dyn_cast(derived.Nominal); + auto conformanceDC = derived.getConformanceContext(); // Expected type: (Self) -> (Decoder) throws -> (Self) // Constructed as: func type @@ -1053,20 +1047,19 @@ static ValueDecl *deriveDecodable_init(TypeChecker &tc, Decl *parentDecl, /*Throws=*/true); // (Self) - auto returnType = target->getDeclaredInterfaceType(); + auto returnType = derived.Nominal->getDeclaredInterfaceType(); // (from: Decoder) throws -> (Self) Type innerType = FunctionType::get(inputType, returnType, extInfo); // Params: (self [implicit], Decoder) // self should be inout if the type is a value type; not inout otherwise. - auto inOut = !isa(target); - auto *selfDecl = ParamDecl::createSelf(SourceLoc(), target, + auto *selfDecl = ParamDecl::createSelf(SourceLoc(), conformanceDC, /*isStatic=*/false, - /*isInOut=*/inOut); - auto *decoderParamDecl = new (C) - ParamDecl(VarDecl::Specifier::Default, SourceLoc(), SourceLoc(), - C.Id_from, SourceLoc(), C.Id_decoder, decoderType, target); + /*isInOut=*/!classDecl); + auto *decoderParamDecl = new (C) ParamDecl( + VarDecl::Specifier::Default, SourceLoc(), SourceLoc(), C.Id_from, + SourceLoc(), C.Id_decoder, decoderType, conformanceDC); decoderParamDecl->setImplicit(); decoderParamDecl->setInterfaceType(decoderType); @@ -1075,16 +1068,17 @@ static ValueDecl *deriveDecodable_init(TypeChecker &tc, Decl *parentDecl, // Func name: init(from: Decoder) DeclName name(C, DeclBaseName::createConstructor(), paramList); - auto *initDecl = new (C) ConstructorDecl(name, SourceLoc(), OTK_None, - SourceLoc(), /*Throws=*/true, - SourceLoc(), selfDecl, paramList, - /*GenericParams=*/nullptr, target); + auto *initDecl = + new (C) ConstructorDecl(name, SourceLoc(), OTK_None, SourceLoc(), + /*Throws=*/true, SourceLoc(), selfDecl, paramList, + /*GenericParams=*/nullptr, conformanceDC); initDecl->setImplicit(); + initDecl->setSynthesized(); initDecl->setBodySynthesizer(deriveBodyDecodable_init); // This constructor should be marked as `required` for non-final classes. - if (isa(target) && !target->getAttrs().hasAttribute()) { - auto *reqAttr = new (C) SimpleDeclAttr(/*IsImplicit=*/true); + if (classDecl && !classDecl->getAttrs().hasAttribute()) { + auto *reqAttr = new (C) RequiredAttr(/*IsImplicit=*/true); initDecl->getAttrs().add(reqAttr); } @@ -1092,9 +1086,10 @@ static ValueDecl *deriveDecodable_init(TypeChecker &tc, Decl *parentDecl, auto initSelfParam = computeSelfParam(initDecl, /*init=*/true); Type interfaceType; Type initializerType; - if (auto sig = target->getGenericSignatureOfContext()) { + if (auto sig = conformanceDC->getGenericSignatureOfContext()) { // Evaluate the below, but in a generic environment (if Self is generic). - initDecl->setGenericEnvironment(target->getGenericEnvironmentOfContext()); + initDecl->setGenericEnvironment( + conformanceDC->getGenericEnvironmentOfContext()); interfaceType = GenericFunctionType::get(sig, {selfParam}, innerType, FunctionType::ExtInfo()); initializerType = GenericFunctionType::get(sig, {initSelfParam}, innerType, @@ -1110,15 +1105,12 @@ static ValueDecl *deriveDecodable_init(TypeChecker &tc, Decl *parentDecl, initDecl->setInterfaceType(interfaceType); initDecl->setValidationStarted(); initDecl->setInitializerInterfaceType(initializerType); - initDecl->setAccess(target->getFormalAccess()); + initDecl->copyFormalAccessFrom(derived.Nominal, + /*sourceIsParentContext*/ true); - // If the type was not imported, the derived conformance is either from the - // type itself or an extension, in which case we will emit the declaration - // normally. - if (target->hasClangNode()) - tc.Context.addExternalDecl(initDecl); + C.addSynthesizedDecl(initDecl); - target->addMember(initDecl); + derived.addMembersToConformanceContext({initDecl}); return initDecl; } @@ -1127,15 +1119,8 @@ static ValueDecl *deriveDecodable_init(TypeChecker &tc, Decl *parentDecl, /// Checks to see whether the given type has a valid \c CodingKeys enum, and if /// not, attempts to synthesize one for it. /// -/// \param tc The typechecker to use in validating {En,Decodable} conformance. -/// -/// \param target The type to validate. -/// /// \param requirement The requirement we want to synthesize. -/// -/// \param proto The *codable protocol to check for validity. -static bool canSynthesize(TypeChecker &tc, NominalTypeDecl *target, - ValueDecl *requirement, ProtocolDecl *proto) { +static bool canSynthesize(DerivedConformance &derived, ValueDecl *requirement) { // Before we attempt to look up (or more importantly, synthesize) a CodingKeys // entity on target, we need to make sure the type is otherwise valid. // @@ -1146,8 +1131,10 @@ static bool canSynthesize(TypeChecker &tc, NominalTypeDecl *target, // // If the required initializer is not available, we shouldn't attempt to // synthesize CodingKeys. + auto &tc = derived.TC; ASTContext &C = tc.Context; - auto *classDecl = dyn_cast(target); + auto proto = derived.Protocol; + auto *classDecl = dyn_cast(derived.Nominal); if (proto->isSpecificProtocol(KnownProtocolKind::Decodable) && classDecl) { if (auto *superclassDecl = classDecl->getSuperclassDecl()) { DeclName memberName; @@ -1179,15 +1166,16 @@ static bool canSynthesize(TypeChecker &tc, NominalTypeDecl *target, } else { auto *initializer = cast(result.front().getValueDecl()); + auto conformanceDC = derived.getConformanceContext(); if (!initializer->isDesignatedInit()) { // We must call a superclass's designated initializer. tc.diagnose(initializer, diag::decodable_super_init_not_designated_here, requirement->getFullName(), memberName); return false; - } else if (!initializer->isAccessibleFrom(target)) { + } else if (!initializer->isAccessibleFrom(conformanceDC)) { // Cannot call an inaccessible method. - auto accessScope = initializer->getFormalAccessScope(target); + auto accessScope = initializer->getFormalAccessScope(conformanceDC); tc.diagnose(initializer, diag::decodable_inaccessible_super_init_here, requirement->getFullName(), memberName, accessScope.accessLevelForDiagnostics()); @@ -1205,7 +1193,7 @@ static bool canSynthesize(TypeChecker &tc, NominalTypeDecl *target, // If the target already has a valid CodingKeys enum, we won't need to // synthesize one. - auto validity = hasValidCodingKeysEnum(tc, target, proto); + auto validity = hasValidCodingKeysEnum(derived); // We found a type, but it wasn't valid. if (!validity.isValid) @@ -1213,7 +1201,7 @@ static bool canSynthesize(TypeChecker &tc, NominalTypeDecl *target, // We can try to synthesize a type here. if (!validity.hasType) { - auto *synthesizedEnum = synthesizeCodingKeysEnum(tc, target, proto); + auto *synthesizedEnum = synthesizeCodingKeysEnum(derived); if (!synthesizedEnum) return false; } @@ -1221,28 +1209,19 @@ static bool canSynthesize(TypeChecker &tc, NominalTypeDecl *target, return true; } -ValueDecl *DerivedConformance::deriveEncodable(TypeChecker &tc, - Decl *parentDecl, - NominalTypeDecl *target, - ValueDecl *requirement) { +ValueDecl *DerivedConformance::deriveEncodable(ValueDecl *requirement) { // We can only synthesize Encodable for structs and classes. - if (!isa(target) && !isa(target)) + if (!isa(Nominal) && !isa(Nominal)) return nullptr; - if (requirement->getBaseName() != tc.Context.Id_encode) { + if (requirement->getBaseName() != TC.Context.Id_encode) { // Unknown requirement. - tc.diagnose(requirement->getLoc(), diag::broken_encodable_requirement); + TC.diagnose(requirement->getLoc(), diag::broken_encodable_requirement); return nullptr; } - // Conformance can't be synthesized in an extension. - auto encodableProto = tc.Context.getProtocol(KnownProtocolKind::Encodable); - auto encodableType = encodableProto->getDeclaredType(); - if (target != parentDecl) { - tc.diagnose(parentDecl->getLoc(), diag::cannot_synthesize_in_extension, - encodableType); + if (checkAndDiagnoseDisallowedContext(requirement)) return nullptr; - } // We're about to try to synthesize Encodable. If something goes wrong, // we'll have to output at least one error diagnostic because we returned @@ -1259,62 +1238,54 @@ ValueDecl *DerivedConformance::deriveEncodable(TypeChecker &tc, // diagnostics, then potentially collect notes. If we succeed in // synthesizing Encodable, we can cancel the transaction and get rid of the // fake failures. - auto diagnosticTransaction = DiagnosticTransaction(tc.Context.Diags); - tc.diagnose(target, diag::type_does_not_conform, target->getDeclaredType(), - encodableType); - tc.diagnose(requirement, diag::no_witnesses, diag::RequirementKind::Func, - requirement->getFullName(), encodableType, /*AddFixIt=*/false); + auto diagnosticTransaction = DiagnosticTransaction(TC.Context.Diags); + TC.diagnose(ConformanceDecl, diag::type_does_not_conform, + Nominal->getDeclaredType(), getProtocolType()); + TC.diagnose(requirement, diag::no_witnesses, diag::RequirementKind::Func, + requirement->getFullName(), getProtocolType(), + /*AddFixIt=*/false); // Check other preconditions for synthesized conformance. // This synthesizes a CodingKeys enum if possible. - if (canSynthesize(tc, target, requirement, encodableProto)) { + if (canSynthesize(*this, requirement)) { diagnosticTransaction.abort(); - return deriveEncodable_encode(tc, parentDecl, target); + return deriveEncodable_encode(*this); } return nullptr; } -ValueDecl *DerivedConformance::deriveDecodable(TypeChecker &tc, - Decl *parentDecl, - NominalTypeDecl *target, - ValueDecl *requirement) { +ValueDecl *DerivedConformance::deriveDecodable(ValueDecl *requirement) { // We can only synthesize Encodable for structs and classes. - if (!isa(target) && !isa(target)) + if (!isa(Nominal) && !isa(Nominal)) return nullptr; if (requirement->getBaseName() != DeclBaseName::createConstructor()) { // Unknown requirement. - tc.diagnose(requirement->getLoc(), diag::broken_decodable_requirement); + TC.diagnose(requirement->getLoc(), diag::broken_decodable_requirement); return nullptr; } - // Conformance can't be synthesized in an extension. - auto decodableProto = tc.Context.getProtocol(KnownProtocolKind::Decodable); - auto decodableType = decodableProto->getDeclaredType(); - if (target != parentDecl) { - tc.diagnose(parentDecl->getLoc(), diag::cannot_synthesize_in_extension, - decodableType); + if (checkAndDiagnoseDisallowedContext(requirement)) return nullptr; - } // We're about to try to synthesize Decodable. If something goes wrong, // we'll have to output at least one error diagnostic. We need to collate // diagnostics produced by canSynthesize and deriveDecodable_init to produce // them in the right order -- see the comment in deriveEncodable for // background on this transaction. - auto diagnosticTransaction = DiagnosticTransaction(tc.Context.Diags); - tc.diagnose(target, diag::type_does_not_conform, target->getDeclaredType(), - decodableType); - tc.diagnose(requirement, diag::no_witnesses, + auto diagnosticTransaction = DiagnosticTransaction(TC.Context.Diags); + TC.diagnose(ConformanceDecl->getLoc(), diag::type_does_not_conform, + Nominal->getDeclaredType(), getProtocolType()); + TC.diagnose(requirement, diag::no_witnesses, diag::RequirementKind::Constructor, requirement->getFullName(), - decodableType, /*AddFixIt=*/false); + getProtocolType(), /*AddFixIt=*/false); // Check other preconditions for synthesized conformance. // This synthesizes a CodingKeys enum if possible. - if (canSynthesize(tc, target, requirement, decodableProto)) { + if (canSynthesize(*this, requirement)) { diagnosticTransaction.abort(); - return deriveDecodable_init(tc, parentDecl, target); + return deriveDecodable_init(*this); } return nullptr; diff --git a/lib/Sema/DerivedConformanceCodingKey.cpp b/lib/Sema/DerivedConformanceCodingKey.cpp index c79da2944810c..6aff6e904052a 100644 --- a/lib/Sema/DerivedConformanceCodingKey.cpp +++ b/lib/Sema/DerivedConformanceCodingKey.cpp @@ -25,7 +25,6 @@ #include "DerivedConformances.h" using namespace swift; -using namespace DerivedConformance; /// Sets the body of the given function to `return nil`. /// @@ -48,7 +47,7 @@ static void deriveRawValueReturn(AbstractFunctionDecl *funcDecl) { auto *parentDC = funcDecl->getDeclContext(); auto &C = parentDC->getASTContext(); - auto *selfRef = createSelfDeclRef(funcDecl); + auto *selfRef = DerivedConformance::createSelfDeclRef(funcDecl); auto *memberRef = new (C) UnresolvedDotExpr(selfRef, SourceLoc(), C.Id_rawValue, DeclNameLoc(), /*Implicit=*/true); @@ -85,7 +84,7 @@ static void deriveRawValueInit(AbstractFunctionDecl *initDecl) { DeclName ctorName(C, DeclBaseName::createConstructor(), paramList); // self.init(rawValue:) expr - auto *selfRef = createSelfDeclRef(initDecl); + auto *selfRef = DerivedConformance::createSelfDeclRef(initDecl); auto *initExpr = new (C) UnresolvedDotExpr(selfRef, SourceLoc(), ctorName, DeclNameLoc(), /*Implicit=*/true); @@ -103,24 +102,17 @@ static void deriveRawValueInit(AbstractFunctionDecl *initDecl) { /// Synthesizes a constructor declaration with the given parameter name and /// type. /// -/// \param tc The type checker to use in synthesizing the constructor. -/// -/// \param parentDecl The parent declaration of the enum. -/// -/// \param enumDecl The enum on which to synthesize the constructor. -/// /// \param paramType The type of the parameter. /// /// \param paramName The name of the parameter. /// /// \param synthesizer A lambda to call to set the constructor's body. template -static ValueDecl *deriveInitDecl(TypeChecker &tc, Decl *parentDecl, - EnumDecl *enumDecl, Type paramType, +static ValueDecl *deriveInitDecl(DerivedConformance &derived, Type paramType, Identifier paramName, const Synthesizer &synthesizer) { - auto &C = tc.Context; - auto *parentDC = cast(parentDecl); + auto &C = derived.TC.Context; + auto *parentDC = derived.getConformanceContext(); // rawValue auto *rawDecl = @@ -180,50 +172,41 @@ static ValueDecl *deriveInitDecl(TypeChecker &tc, Decl *parentDecl, } initDecl->setInterfaceType(allocIfaceType); initDecl->setInitializerInterfaceType(initIfaceType); - initDecl->setAccess(enumDecl->getFormalAccess()); + initDecl->setAccess(derived.Nominal->getFormalAccess()); initDecl->setValidationStarted(); - // If the enum was not imported, the derived conformance is either from the - // enum itself or an extension, in which case we will emit the declaration - // normally. - if (enumDecl->hasClangNode()) - tc.Context.addExternalDecl(initDecl); + C.addSynthesizedDecl(initDecl); - cast(parentDecl)->addMember(initDecl); + derived.addMembersToConformanceContext({initDecl}); return initDecl; } /// Synthesizes a read-only computed property with a given type and name. /// -/// \param tc The type checker to use in synthesizing the property. -/// -/// \param parentDecl The parent declaration of the enum. -/// -/// \param enumDecl The enum on which to synthesize the property. -/// /// \param type The type of the property. /// /// \param name The name of the property. /// /// \param synthesizer A lambda to call to set the property's getter. template -static ValueDecl *deriveProperty(TypeChecker &tc, Decl *parentDecl, - EnumDecl *enumDecl, Type type, Identifier name, +static ValueDecl *deriveProperty(DerivedConformance &derived, Type type, + Identifier name, const Synthesizer &synthesizer) { // Define the property. VarDecl *propDecl; PatternBindingDecl *pbDecl; - std::tie(propDecl, pbDecl) - = declareDerivedProperty(tc, parentDecl, enumDecl, name, type, type, - /*isStatic=*/false, /*isFinal=*/false); + std::tie(propDecl, pbDecl) = + derived.declareDerivedProperty(name, type, type, + /*isStatic=*/false, /*isFinal=*/false); // Define the getter. - auto *getterDecl = addGetterToReadOnlyDerivedProperty(tc, propDecl, type); + auto *getterDecl = + derived.addGetterToReadOnlyDerivedProperty(derived.TC, propDecl, type); // Synthesize the body. synthesizer(getterDecl); - auto *dc = cast(parentDecl); + auto *dc = cast(derived.ConformanceDecl); dc->addMember(getterDecl); dc->addMember(propDecl); dc->addMember(pbDecl); @@ -272,8 +255,7 @@ deriveBodyCodingKey_enum_stringValue(AbstractFunctionDecl *strValDecl) { Identifier(), elt, nullptr); pat->setImplicit(); - auto labelItem = CaseLabelItem(/*IsDefault=*/false, pat, SourceLoc(), - nullptr); + auto labelItem = CaseLabelItem(pat); auto *caseValue = new (C) StringLiteralExpr(elt->getNameStr(), SourceRange(), @@ -283,10 +265,10 @@ deriveBodyCodingKey_enum_stringValue(AbstractFunctionDecl *strValDecl) { SourceLoc()); cases.push_back(CaseStmt::create(C, SourceLoc(), labelItem, /*HasBoundDecls=*/false, SourceLoc(), - caseBody)); + SourceLoc(), caseBody)); } - auto *selfRef = createSelfDeclRef(strValDecl); + auto *selfRef = DerivedConformance::createSelfDeclRef(strValDecl); auto *switchStmt = SwitchStmt::create(LabeledStmtInfo(), SourceLoc(), selfRef, SourceLoc(), cases, SourceLoc(), C); @@ -329,7 +311,7 @@ deriveBodyCodingKey_init_stringValue(AbstractFunctionDecl *initDecl) { return; } - auto *selfRef = createSelfDeclRef(initDecl); + auto *selfRef = DerivedConformance::createSelfDeclRef(initDecl); SmallVector cases; for (auto *elt : elements) { auto *litExpr = new (C) StringLiteralExpr(elt->getNameStr(), SourceRange(), @@ -338,8 +320,7 @@ deriveBodyCodingKey_init_stringValue(AbstractFunctionDecl *initDecl) { nullptr); litPat->setImplicit(); - auto labelItem = CaseLabelItem(/*IsDefault=*/false, litPat, SourceLoc(), - nullptr); + auto labelItem = CaseLabelItem(litPat); auto *eltRef = new (C) DeclRefExpr(elt, DeclNameLoc(), /*Implicit=*/true); auto *metaTyRef = TypeExpr::createImplicit(enumType, C); @@ -352,20 +333,19 @@ deriveBodyCodingKey_init_stringValue(AbstractFunctionDecl *initDecl) { SourceLoc()); cases.push_back(CaseStmt::create(C, SourceLoc(), labelItem, /*HasBoundDecls=*/false, SourceLoc(), - body)); + SourceLoc(), body)); } auto *anyPat = new (C) AnyPattern(SourceLoc()); anyPat->setImplicit(); - auto dfltLabelItem = CaseLabelItem(/*IsDefault=*/true, anyPat, SourceLoc(), - nullptr); + auto dfltLabelItem = CaseLabelItem::getDefault(anyPat); auto *dfltReturnStmt = new (C) FailStmt(SourceLoc(), SourceLoc()); auto *dfltBody = BraceStmt::create(C, SourceLoc(), ASTNode(dfltReturnStmt), SourceLoc()); cases.push_back(CaseStmt::create(C, SourceLoc(), dfltLabelItem, /*HasBoundDecls=*/false, SourceLoc(), - dfltBody)); + SourceLoc(), dfltBody)); auto *stringValueDecl = initDecl->getParameterList(1)->get(0); auto *stringValueRef = new (C) DeclRefExpr(stringValueDecl, DeclNameLoc(), @@ -379,24 +359,18 @@ deriveBodyCodingKey_init_stringValue(AbstractFunctionDecl *initDecl) { } /// Returns whether the given enum is eligible for CodingKey synthesis. -/// -/// \param tc The type checker to use in checking eligibility. -/// -/// \param parentDecl The parent declaration of the enum. -/// -/// \param enumDecl The enum to check. -static bool canSynthesizeCodingKey(TypeChecker &tc, Decl *parentDecl, - EnumDecl *enumDecl) { +static bool canSynthesizeCodingKey(DerivedConformance &derived) { + auto enumDecl = cast(derived.Nominal); // Validate the enum and its raw type. - tc.validateDecl(enumDecl); + derived.TC.validateDecl(enumDecl); // If the enum has a raw type (optional), it must be String or Int. Type rawType = enumDecl->getRawType(); if (rawType) { - auto *parentDC = cast(parentDecl); + auto *parentDC = derived.getConformanceContext(); rawType = parentDC->mapTypeIntoContext(rawType); - auto &C = tc.Context; + auto &C = derived.TC.Context; auto *nominal = rawType->getCanonicalType()->getAnyNominal(); if (nominal != C.getStringDecl() && nominal != C.getIntDecl()) return false; @@ -412,21 +386,18 @@ static bool canSynthesizeCodingKey(TypeChecker &tc, Decl *parentDecl, return true; } -ValueDecl *DerivedConformance::deriveCodingKey(TypeChecker &tc, - Decl *parentDecl, - NominalTypeDecl *type, - ValueDecl *requirement) { +ValueDecl *DerivedConformance::deriveCodingKey(ValueDecl *requirement) { // We can only synthesize CodingKey for enums. - auto *enumDecl = dyn_cast(type); + auto *enumDecl = dyn_cast(Nominal); if (!enumDecl) return nullptr; // Check other preconditions for synthesized conformance. - if (!canSynthesizeCodingKey(tc, parentDecl, enumDecl)) + if (!canSynthesizeCodingKey(*this)) return nullptr; - auto &C = tc.Context; + auto &C = TC.Context; auto rawType = enumDecl->getRawType(); auto name = requirement->getBaseName(); if (name == C.Id_stringValue) { @@ -458,8 +429,7 @@ ValueDecl *DerivedConformance::deriveCodingKey(TypeChecker &tc, } }; - return deriveProperty(tc, parentDecl, enumDecl, stringType, - C.Id_stringValue, synth); + return deriveProperty(*this, stringType, C.Id_stringValue, synth); } else if (name == C.Id_intValue) { // Synthesize `var intValue: Int? { get }` @@ -486,8 +456,7 @@ ValueDecl *DerivedConformance::deriveCodingKey(TypeChecker &tc, } }; - return deriveProperty(tc, parentDecl, enumDecl, optionalIntType, - C.Id_intValue, synth); + return deriveProperty(*this, optionalIntType, C.Id_intValue, synth); } else if (name == DeclBaseName::createConstructor()) { auto argumentNames = requirement->getFullName().getArgumentNames(); if (argumentNames.size() == 1) { @@ -523,8 +492,7 @@ ValueDecl *DerivedConformance::deriveCodingKey(TypeChecker &tc, } }; - return deriveInitDecl(tc, parentDecl, enumDecl, stringType, - C.Id_stringValue, synth); + return deriveInitDecl(*this, stringType, C.Id_stringValue, synth); } else if (argumentNames[0] == C.Id_intValue) { // Synthesize `init?(intValue:)` auto intType = C.getIntDecl()->getDeclaredType(); @@ -548,12 +516,11 @@ ValueDecl *DerivedConformance::deriveCodingKey(TypeChecker &tc, } }; - return deriveInitDecl(tc, parentDecl, enumDecl, intType, C.Id_intValue, - synthesizer); + return deriveInitDecl(*this, intType, C.Id_intValue, synthesizer); } } } - tc.diagnose(requirement->getLoc(), diag::broken_coding_key_requirement); + TC.diagnose(requirement->getLoc(), diag::broken_coding_key_requirement); return nullptr; } diff --git a/lib/Sema/DerivedConformanceEquatableHashable.cpp b/lib/Sema/DerivedConformanceEquatableHashable.cpp index 979ca36451fa9..b093b779a8bf7 100644 --- a/lib/Sema/DerivedConformanceEquatableHashable.cpp +++ b/lib/Sema/DerivedConformanceEquatableHashable.cpp @@ -23,6 +23,7 @@ #include "swift/AST/Module.h" #include "swift/AST/Pattern.h" #include "swift/AST/ParameterList.h" +#include "swift/AST/ProtocolConformance.h" #include "swift/AST/Types.h" #include "llvm/ADT/APInt.h" #include "llvm/ADT/SmallString.h" @@ -30,17 +31,16 @@ #include "DerivedConformances.h" using namespace swift; -using namespace DerivedConformance; /// Returns true if, for every element of the given enum, it either has no /// associated values or all of them conform to a protocol. /// \p theEnum The enum whose elements and associated values should be checked. /// \p protocol The protocol being requested. /// \return True if all associated values of all elements of the enum conform. -bool allAssociatedValuesConformToProtocol(TypeChecker &tc, EnumDecl *theEnum, - ProtocolDecl *protocol) { - auto declContext = theEnum->getDeclContext(); - +static bool allAssociatedValuesConformToProtocol(TypeChecker &tc, + DeclContext *DC, + EnumDecl *theEnum, + ProtocolDecl *protocol) { for (auto elt : theEnum->getAllElements()) { if (!elt->hasInterfaceType()) tc.validateDecl(elt); @@ -50,8 +50,9 @@ bool allAssociatedValuesConformToProtocol(TypeChecker &tc, EnumDecl *theEnum, continue; for (auto param : *PL) { - if (!tc.conformsToProtocol(param->getType(), protocol, declContext, - ConformanceCheckFlags::Used)) { + auto type = param->getType()->mapTypeOutOfContext(); + if (!tc.conformsToProtocol(DC->mapTypeIntoContext(type), protocol, DC, + ConformanceCheckFlags::Used)) { return false; } } @@ -64,19 +65,20 @@ bool allAssociatedValuesConformToProtocol(TypeChecker &tc, EnumDecl *theEnum, /// \p theStruct The struct whose stored properties should be checked. /// \p protocol The protocol being requested. /// \return True if all stored properties of the struct conform. -bool allStoredPropertiesConformToProtocol(TypeChecker &tc, - StructDecl *theStruct, - ProtocolDecl *protocol) { - auto declContext = theStruct->getDeclContext(); - +static bool allStoredPropertiesConformToProtocol(TypeChecker &tc, + DeclContext *DC, + StructDecl *theStruct, + ProtocolDecl *protocol) { auto storedProperties = theStruct->getStoredProperties(/*skipInaccessible=*/true); for (auto propertyDecl : storedProperties) { if (!propertyDecl->hasType()) tc.validateDecl(propertyDecl); + if (!propertyDecl->hasType()) + return false; - if (!propertyDecl->hasType() || - !tc.conformsToProtocol(propertyDecl->getType(), protocol, declContext, + auto type = propertyDecl->getType()->mapTypeOutOfContext(); + if (!tc.conformsToProtocol(DC->mapTypeIntoContext(type), protocol, DC, ConformanceCheckFlags::Used)) { return false; } @@ -85,7 +87,8 @@ bool allStoredPropertiesConformToProtocol(TypeChecker &tc, } /// Common preconditions for Equatable and Hashable. -static bool canDeriveConformance(TypeChecker &tc, NominalTypeDecl *target, +static bool canDeriveConformance(TypeChecker &tc, DeclContext *DC, + NominalTypeDecl *target, ProtocolDecl *protocol) { // The type must be an enum or a struct. if (auto enumDecl = dyn_cast(target)) { @@ -95,12 +98,12 @@ static bool canDeriveConformance(TypeChecker &tc, NominalTypeDecl *target, // The cases must not have associated values, or all associated values must // conform to the protocol. - return allAssociatedValuesConformToProtocol(tc, enumDecl, protocol); + return allAssociatedValuesConformToProtocol(tc, DC, enumDecl, protocol); } if (auto structDecl = dyn_cast(target)) { // All stored properties of the struct must conform to the protocol. - return allStoredPropertiesConformToProtocol(tc, structDecl, protocol); + return allStoredPropertiesConformToProtocol(tc, DC, structDecl, protocol); } return false; @@ -191,6 +194,20 @@ enumElementPayloadSubpattern(EnumElementDecl *enumElementDecl, return pat; } +/// Returns a new integer literal expression with the given value. +/// \p C The AST context. +/// \p value The integer value. +/// \return The integer literal expression. +static Expr *integerLiteralExpr(ASTContext &C, int64_t value) { + llvm::SmallString<8> integerVal; + APInt(32, value).toString(integerVal, 10, /*signed*/ false); + auto integerStr = C.AllocateCopy(integerVal); + auto integerExpr = new (C) IntegerLiteralExpr( + StringRef(integerStr.data(), integerStr.size()), SourceLoc(), + /*implicit*/ true); + return integerExpr; +} + /// Create AST statements which convert from an enum to an Int with a switch. /// \p stmts The generated statements are appended to this vector. /// \p parentDC Either an extension or the enum itself. @@ -235,17 +252,10 @@ static DeclRefExpr *convertEnumToIndex(SmallVectorImpl &stmts, Identifier(), elt, nullptr); pat->setImplicit(); - auto labelItem = CaseLabelItem(/*IsDefault=*/false, pat, SourceLoc(), - nullptr); + auto labelItem = CaseLabelItem(pat); // generate: indexVar = - llvm::SmallString<8> indexVal; - APInt(32, index++).toString(indexVal, 10, /*signed*/ false); - auto indexStr = C.AllocateCopy(indexVal); - - auto indexExpr = new (C) IntegerLiteralExpr(StringRef(indexStr.data(), - indexStr.size()), SourceLoc(), - /*implicit*/ true); + auto indexExpr = integerLiteralExpr(C, index++); auto indexRef = new (C) DeclRefExpr(indexVar, DeclNameLoc(), /*implicit*/true); auto assignExpr = new (C) AssignExpr(indexRef, SourceLoc(), @@ -253,7 +263,7 @@ static DeclRefExpr *convertEnumToIndex(SmallVectorImpl &stmts, auto body = BraceStmt::create(C, SourceLoc(), ASTNode(assignExpr), SourceLoc()); cases.push_back(CaseStmt::create(C, SourceLoc(), labelItem, - /*HasBoundDecls=*/false, + /*HasBoundDecls=*/false, SourceLoc(), SourceLoc(), body)); } @@ -418,8 +428,7 @@ deriveBodyEquatable_enum_hasAssociatedValues_eq(AbstractFunctionDecl *eqDecl) { SourceLoc()); caseTuplePattern->setImplicit(); - auto labelItem = CaseLabelItem(/*IsDefault*/ false, caseTuplePattern, - SourceLoc(), nullptr); + auto labelItem = CaseLabelItem(caseTuplePattern); // Generate a guard statement for each associated value in the payload, // breaking out early if any pair is unequal. (This is done to avoid @@ -448,7 +457,7 @@ deriveBodyEquatable_enum_hasAssociatedValues_eq(AbstractFunctionDecl *eqDecl) { auto body = BraceStmt::create(C, SourceLoc(), statementsInCase, SourceLoc()); cases.push_back(CaseStmt::create(C, SourceLoc(), labelItem, hasBoundDecls, - SourceLoc(), body)); + SourceLoc(), SourceLoc(), body)); } // default: result = false @@ -458,8 +467,7 @@ deriveBodyEquatable_enum_hasAssociatedValues_eq(AbstractFunctionDecl *eqDecl) { if (elementCount > 1) { auto defaultPattern = new (C) AnyPattern(SourceLoc()); defaultPattern->setImplicit(); - auto defaultItem = CaseLabelItem(/*IsDefault*/ true, defaultPattern, - SourceLoc(), nullptr); + auto defaultItem = CaseLabelItem::getDefault(defaultPattern); auto falseExpr = new (C) BooleanLiteralExpr(false, SourceLoc(), /*implicit*/ true); auto returnStmt = new (C) ReturnStmt(SourceLoc(), falseExpr); @@ -467,7 +475,7 @@ deriveBodyEquatable_enum_hasAssociatedValues_eq(AbstractFunctionDecl *eqDecl) { SourceLoc()); cases.push_back(CaseStmt::create(C, SourceLoc(), defaultItem, /*HasBoundDecls*/ false, - SourceLoc(), body)); + SourceLoc(), SourceLoc(), body)); } // switch (a, b) { } @@ -535,8 +543,7 @@ static void deriveBodyEquatable_struct_eq(AbstractFunctionDecl *eqDecl) { /// Derive an '==' operator implementation for an enum or a struct. static ValueDecl * -deriveEquatable_eq(TypeChecker &tc, Decl *parentDecl, NominalTypeDecl *typeDecl, - Identifier generatedIdentifier, +deriveEquatable_eq(DerivedConformance &derived, Identifier generatedIdentifier, void (*bodySynthesizer)(AbstractFunctionDecl *)) { // enum SomeEnum { // case A, B(Int), C(String, Int) @@ -573,9 +580,9 @@ deriveEquatable_eq(TypeChecker &tc, Decl *parentDecl, NominalTypeDecl *typeDecl, // } // } - ASTContext &C = tc.Context; + ASTContext &C = derived.TC.Context; - auto parentDC = cast(parentDecl); + auto parentDC = derived.getConformanceContext(); auto enumTy = parentDC->getDeclaredTypeInContext(); auto enumIfaceTy = parentDC->getDeclaredInterfaceType(); @@ -628,14 +635,15 @@ deriveEquatable_eq(TypeChecker &tc, Decl *parentDecl, NominalTypeDecl *typeDecl, DeclNameLoc())); if (!C.getEqualIntDecl()) { - tc.diagnose(parentDecl->getLoc(), diag::no_equal_overload_for_int); + derived.TC.diagnose(derived.ConformanceDecl->getLoc(), + diag::no_equal_overload_for_int); return nullptr; } eqDecl->setBodySynthesizer(bodySynthesizer); // Compute the type. - Type paramsTy = params[1]->getType(tc.Context); + Type paramsTy = params[1]->getType(C); // Compute the interface type. Type interfaceTy; @@ -658,197 +666,291 @@ deriveEquatable_eq(TypeChecker &tc, Decl *parentDecl, NominalTypeDecl *typeDecl, FunctionType::ExtInfo()); } eqDecl->setInterfaceType(interfaceTy); - eqDecl->copyFormalAccessFrom(typeDecl); + eqDecl->copyFormalAccessFrom(derived.Nominal, /*sourceIsParentContext*/ true); eqDecl->setValidationStarted(); - // If the enum was not imported, the derived conformance is either from the - // enum itself or an extension, in which case we will emit the declaration - // normally. - if (typeDecl->hasClangNode()) - tc.Context.addExternalDecl(eqDecl); + C.addSynthesizedDecl(eqDecl); // Add the operator to the parent scope. - cast(parentDecl)->addMember(eqDecl); + derived.addMembersToConformanceContext({eqDecl}); return eqDecl; } -bool DerivedConformance::canDeriveEquatable(TypeChecker &tc, - NominalTypeDecl *type, - ValueDecl *requirement) { +bool DerivedConformance::canDeriveEquatable(TypeChecker &tc, DeclContext *DC, + NominalTypeDecl *type) { auto equatableProto = tc.Context.getProtocol(KnownProtocolKind::Equatable); - return canDeriveConformance(tc, type, equatableProto); + return canDeriveConformance(tc, DC, type, equatableProto); } -ValueDecl *DerivedConformance::deriveEquatable(TypeChecker &tc, - Decl *parentDecl, - NominalTypeDecl *type, - ValueDecl *requirement) { - // Conformance can't be synthesized in an extension; we allow it as a special - // case for enums with no associated values to preserve source compatibility. - auto theEnum = dyn_cast(type); - if (!(theEnum && theEnum->hasOnlyCasesWithoutAssociatedValues()) && - type != parentDecl) { - auto equatableProto = tc.Context.getProtocol(KnownProtocolKind::Equatable); - auto equatableType = equatableProto->getDeclaredType(); - tc.diagnose(parentDecl->getLoc(), diag::cannot_synthesize_in_extension, - equatableType); +ValueDecl *DerivedConformance::deriveEquatable(ValueDecl *requirement) { + if (checkAndDiagnoseDisallowedContext(requirement)) return nullptr; - } // Build the necessary decl. if (requirement->getBaseName() == "==") { - if (theEnum) { + if (auto ED = dyn_cast(Nominal)) { auto bodySynthesizer = - theEnum->hasOnlyCasesWithoutAssociatedValues() + ED->hasOnlyCasesWithoutAssociatedValues() ? &deriveBodyEquatable_enum_noAssociatedValues_eq : &deriveBodyEquatable_enum_hasAssociatedValues_eq; - return deriveEquatable_eq(tc, parentDecl, theEnum, - tc.Context.Id_derived_enum_equals, + return deriveEquatable_eq(*this, TC.Context.Id_derived_enum_equals, bodySynthesizer); - } - else if (auto theStruct = dyn_cast(type)) - return deriveEquatable_eq(tc, parentDecl, theStruct, - tc.Context.Id_derived_struct_equals, + } else if (isa(Nominal)) + return deriveEquatable_eq(*this, TC.Context.Id_derived_struct_equals, &deriveBodyEquatable_struct_eq); else llvm_unreachable("todo"); } - tc.diagnose(requirement->getLoc(), - diag::broken_equatable_requirement); + TC.diagnose(requirement->getLoc(), diag::broken_equatable_requirement); return nullptr; } -/// Returns a new integer literal expression with the given value. -/// \p C The AST context. -/// \p value The integer value. -/// \return The integer literal expression. -static Expr* integerLiteralExpr(ASTContext &C, int64_t value) { - llvm::SmallString<8> integerVal; - APInt(32, value).toString(integerVal, 10, /*signed*/ false); - auto integerStr = C.AllocateCopy(integerVal); - auto integerExpr = new (C) IntegerLiteralExpr( - StringRef(integerStr.data(), integerStr.size()), SourceLoc(), - /*implicit*/ true); - return integerExpr; +/// Returns a new \c CallExpr representing +/// +/// hasher.combine(hashable) +/// +/// \param C The AST context to create the expression in. +/// +/// \param hasher The parameter decl to make the call on. +/// +/// \param hashable The parameter to the call. +static CallExpr *createHasherCombineCall(ASTContext &C, + ParamDecl *hasher, + Expr *hashable) { + Expr *hasherExpr = new (C) DeclRefExpr(ConcreteDeclRef(hasher), + DeclNameLoc(), /*implicit*/ true); + DeclName name(C, C.Id_combine, {Identifier()}); + // hasher.combine(_:) + auto *combineCall = new (C) UnresolvedDotExpr(hasherExpr, SourceLoc(), + name, DeclNameLoc(), + /*implicit*/ true); + + // hasher.combine(hashable) + return CallExpr::createImplicit(C, combineCall, {hashable}, {Identifier()}); } -/// Returns a new assignment expression that combines the hash value of an -/// expression into a variable. -/// \p C The AST context. -/// \p resultVar The variable into which the hash value will be combined. -/// \p exprToHash The expression whose hash value should be combined. -/// \return The expression that combines the hash value into the variable. -static Expr* combineHashValuesAssignmentExpr(ASTContext &C, - VarDecl* resultVar, - Expr *exprToHash) { - // .hashValue - auto hashValueExpr = new (C) UnresolvedDotExpr(exprToHash, SourceLoc(), +static FuncDecl * +deriveHashable_hashInto(DerivedConformance &derived, + void (*bodySynthesizer)(AbstractFunctionDecl *)) { + // @derived func hash(into hasher: inout Hasher) + + ASTContext &C = derived.TC.Context; + auto parentDC = derived.getConformanceContext(); + + // Expected type: (Self) -> (into: inout Hasher) -> () + // Constructed as: + // func type(input: Self, + // output: func type(input: inout Hasher, + // output: ())) + // Created from the inside out: + + auto hasherDecl = C.getHasherDecl(); + if (!hasherDecl) { + auto hashableProto = C.getProtocol(KnownProtocolKind::Hashable); + derived.TC.diagnose(hashableProto->getLoc(), + diag::broken_hashable_no_hasher); + return nullptr; + } + Type hasherType = hasherDecl->getDeclaredType(); + + // Params: self (implicit), hasher + auto *selfDecl = ParamDecl::createSelf(SourceLoc(), parentDC); + auto *hasherParamDecl = new (C) ParamDecl(VarDecl::Specifier::InOut, + SourceLoc(), + SourceLoc(), C.Id_into, SourceLoc(), + C.Id_hasher, hasherType, parentDC); + hasherParamDecl->setInterfaceType(hasherType); + + ParameterList *params[] = {ParameterList::createWithoutLoc(selfDecl), + ParameterList::createWithoutLoc(hasherParamDecl)}; + + // Return type: () + auto returnType = TupleType::getEmpty(C); + + // Func name: hash(into: inout Hasher) -> () + DeclName name(C, C.Id_hash, params[1]); + auto *hashDecl = FuncDecl::create(C, + SourceLoc(), StaticSpellingKind::None, + SourceLoc(), name, SourceLoc(), + /*Throws=*/false, SourceLoc(), + nullptr, params, + TypeLoc::withoutLoc(returnType), + parentDC); + hashDecl->setImplicit(); + hashDecl->setBodySynthesizer(bodySynthesizer); + + // Evaluate type of Self in (Self) -> (into: inout Hasher) -> () + auto selfParam = computeSelfParam(hashDecl); + auto inoutFlag = ParameterTypeFlags().withInOut(true); + auto hasherParam = AnyFunctionType::Param(hasherType, C.Id_into, inoutFlag); + auto innerType = FunctionType::get({hasherParam}, returnType, + FunctionType::ExtInfo()); + + Type interfaceType; + if (auto sig = parentDC->getGenericSignatureOfContext()) { + hashDecl->setGenericEnvironment(parentDC->getGenericEnvironmentOfContext()); + interfaceType = GenericFunctionType::get(sig, {selfParam}, innerType, + FunctionType::ExtInfo()); + } else { + // (Self) -> innerType == (inout Hasher) -> () + interfaceType = FunctionType::get({selfParam}, innerType, + FunctionType::ExtInfo()); + } + hashDecl->setInterfaceType(interfaceType); + hashDecl->copyFormalAccessFrom(derived.Nominal); + hashDecl->setValidationStarted(); + + C.addSynthesizedDecl(hashDecl); + + derived.addMembersToConformanceContext({hashDecl}); + return hashDecl; +} + +/// Derive the body for the hash(into:) method when hashValue has a +/// user-supplied implementation. +static void +deriveBodyHashable_compat_hashInto(AbstractFunctionDecl *hashIntoDecl) { + // func hash(into hasher: inout Hasher) { + // hasher.combine(self.hashValue) + // } + auto parentDC = hashIntoDecl->getDeclContext(); + ASTContext &C = parentDC->getASTContext(); + + auto selfDecl = hashIntoDecl->getImplicitSelfDecl(); + auto selfRef = new (C) DeclRefExpr(selfDecl, DeclNameLoc(), + /*implicit*/ true); + auto hashValueExpr = new (C) UnresolvedDotExpr(selfRef, SourceLoc(), C.Id_hashValue, DeclNameLoc(), /*implicit*/ true); + auto hasherParam = hashIntoDecl->getParameterList(1)->get(0); + auto hasherExpr = createHasherCombineCall(C, hasherParam, hashValueExpr); - // _combineHashValues(result, .hashValue) - auto combineFunc = C.getCombineHashValuesDecl(); - auto combineFuncExpr = new (C) DeclRefExpr(combineFunc, DeclNameLoc(), - /*implicit*/ true); - auto rhsResultExpr = new (C) DeclRefExpr(resultVar, DeclNameLoc(), - /*implicit*/ true); - auto combineResultExpr = CallExpr::createImplicit( - C, combineFuncExpr, { rhsResultExpr, hashValueExpr }, {}); + auto body = BraceStmt::create(C, SourceLoc(), {ASTNode(hasherExpr)}, + SourceLoc(), /*implicit*/ true); + hashIntoDecl->setBody(body); +} - // result = _combineHashValues(result, .hashValue) - auto lhsResultExpr = new (C) DeclRefExpr(resultVar, DeclNameLoc(), - /*implicit*/ true); - auto assignExpr = new (C) AssignExpr(lhsResultExpr, SourceLoc(), - combineResultExpr, /*implicit*/ true); - return assignExpr; +/// Derive the body for the 'hash(into:)' method for an enum without associated +/// values. +static void +deriveBodyHashable_enum_noAssociatedValues_hashInto( + AbstractFunctionDecl *hashIntoDecl +) { + // enum SomeEnum { + // case A, B, C + // @derived func hash(into hasher: inout Hasher) { + // let discriminator: Int + // switch self { + // case A: + // discriminator = 0 + // case B: + // discriminator = 1 + // case C: + // discriminator = 2 + // } + // hasher.combine(discriminator) + // } + // } + auto parentDC = hashIntoDecl->getDeclContext(); + ASTContext &C = parentDC->getASTContext(); + + auto enumDecl = parentDC->getAsEnumOrEnumExtensionContext(); + auto selfDecl = hashIntoDecl->getImplicitSelfDecl(); + + // generate: switch self {...} + SmallVector stmts; + auto discriminatorExpr = convertEnumToIndex(stmts, parentDC, enumDecl, + selfDecl, hashIntoDecl, + "discriminator"); + // generate: hasher.combine(discriminator) + auto hasherParam = hashIntoDecl->getParameterList(1)->get(0); + auto combineStmt = createHasherCombineCall(C, hasherParam, discriminatorExpr); + stmts.push_back(combineStmt); + + auto body = BraceStmt::create(C, SourceLoc(), stmts, SourceLoc(), + /*implicit*/ true); + hashIntoDecl->setBody(body); } +/// Derive the body for the 'hash(into:)' method for an enum with associated +/// values. static void -deriveBodyHashable_enum_hashValue(AbstractFunctionDecl *hashValueDecl) { - auto parentDC = hashValueDecl->getDeclContext(); +deriveBodyHashable_enum_hasAssociatedValues_hashInto( + AbstractFunctionDecl *hashIntoDecl +) { + // enum SomeEnumWithAssociatedValues { + // case A, B(Int), C(String, Int) + // @derived func hash(into hasher: inout Hasher) { + // switch self { + // case A: + // hasher.combine(0) + // case B(let a0): + // hasher.combine(1) + // hasher.combine(a0) + // case C(let a0, let a1): + // hasher.combine(2) + // hasher.combine(a0) + // hasher.combine(a1) + // } + // } + // } + auto parentDC = hashIntoDecl->getDeclContext(); ASTContext &C = parentDC->getASTContext(); auto enumDecl = parentDC->getAsEnumOrEnumExtensionContext(); - SmallVector statements; - auto selfDecl = hashValueDecl->getImplicitSelfDecl(); + auto selfDecl = hashIntoDecl->getImplicitSelfDecl(); Type enumType = selfDecl->getType(); - Type intType = C.getIntDecl()->getDeclaredType(); - auto resultVar = new (C) VarDecl(/*IsStatic*/ false, VarDecl::Specifier::Var, - /*IsCaptureList*/ false, SourceLoc(), - C.getIdentifier("result"), intType, - hashValueDecl); - resultVar->setInterfaceType(intType); - resultVar->setImplicit(); - - // var result - Pattern *resultPat = new (C) NamedPattern(resultVar, /*implicit*/ true); - resultPat->setType(intType); - resultPat = new (C) TypedPattern(resultPat, TypeLoc::withoutLoc(intType)); - resultPat->setType(intType); - auto resultBind = PatternBindingDecl::create(C, SourceLoc(), - StaticSpellingKind::None, - SourceLoc(), - resultPat, nullptr, - hashValueDecl); + // Extract the decl for the hasher parameter. + auto hasherParam = hashIntoDecl->getParameterList(1)->get(0); unsigned index = 0; SmallVector cases; - auto hasNoAssociatedValues = enumDecl->hasOnlyCasesWithoutAssociatedValues(); - // For each enum element, generate a case statement that binds the associated - // values so that their hash values can be obtained. + // values so that they can be fed to the hasher. for (auto elt : enumDecl->getAllElements()) { // case .(let a0, let a1, ...): SmallVector payloadVars; - SmallVector combineExprs; + SmallVector statements; - auto payloadPattern = enumElementPayloadSubpattern(elt, 'a', hashValueDecl, + auto payloadPattern = enumElementPayloadSubpattern(elt, 'a', hashIntoDecl, payloadVars); auto pat = new (C) EnumElementPattern(TypeLoc::withoutLoc(enumType), SourceLoc(), SourceLoc(), elt->getName(), elt, payloadPattern); pat->setImplicit(); - auto labelItem = CaseLabelItem(/*IsDefault*/ false, pat, SourceLoc(), - nullptr); + auto labelItem = CaseLabelItem(pat); - // If the enum has no associated values, we use the ordinal alone as the - // hash value, because that is sufficient for a good distribution. If any - // case does have associated values, then the ordinal is used as the first - // term combined into _combineHashValues, and the final result after - // combining the payload is passed to _mixInt to improve the distribution. + // If the enum has no associated values, we use the ordinal as the single + // hash component, because that is sufficient for a good distribution. If + // any case does have associated values, then the ordinal is used as the + // first term fed into the hasher. - // result = { + // Generate: hasher.combine() auto ordinalExpr = integerLiteralExpr(C, index++); - auto resultRef = new (C) DeclRefExpr(resultVar, DeclNameLoc(), - /*implicit*/ true); - auto assignExpr = new (C) AssignExpr(resultRef, SourceLoc(), - ordinalExpr, /*implicit*/ true); - combineExprs.emplace_back(ASTNode(assignExpr)); + auto combineExpr = createHasherCombineCall(C, hasherParam, ordinalExpr); + statements.emplace_back(ASTNode(combineExpr)); } - if (!hasNoAssociatedValues) { - // Generate a sequence of expressions that combine the payload's hash - // values into result. - for (auto payloadVar : payloadVars) { - auto payloadVarRef = new (C) DeclRefExpr(payloadVar, DeclNameLoc(), - /*implicit*/ true); - // result = _combineHashValues(result, .hashValue) - auto combineExpr = combineHashValuesAssignmentExpr(C, resultVar, - payloadVarRef); - combineExprs.emplace_back(ASTNode(combineExpr)); - } + // Generate a sequence of statements that feed the payloads into hasher. + for (auto payloadVar : payloadVars) { + auto payloadVarRef = new (C) DeclRefExpr(payloadVar, DeclNameLoc(), + /*implicit*/ true); + // Generate: hasher.combine() + auto combineExpr = createHasherCombineCall(C, hasherParam, payloadVarRef); + statements.emplace_back(ASTNode(combineExpr)); } auto hasBoundDecls = !payloadVars.empty(); - auto body = BraceStmt::create(C, SourceLoc(), combineExprs, SourceLoc()); + auto body = BraceStmt::create(C, SourceLoc(), statements, SourceLoc()); cases.push_back(CaseStmt::create(C, SourceLoc(), labelItem, hasBoundDecls, - SourceLoc(), body)); + SourceLoc(), SourceLoc(), body, + /*implicit*/ true)); } // generate: switch enumVar { } @@ -857,66 +959,36 @@ deriveBodyHashable_enum_hashValue(AbstractFunctionDecl *hashValueDecl) { auto switchStmt = SwitchStmt::create(LabeledStmtInfo(), SourceLoc(), enumRef, SourceLoc(), cases, SourceLoc(), C); - statements.push_back(resultBind); - statements.push_back(switchStmt); - - // generate: return result - auto resultRef = new (C) DeclRefExpr(resultVar, DeclNameLoc(), - /*implicit*/ true, - AccessSemantics::Ordinary, intType); - auto returnStmt = new (C) ReturnStmt(SourceLoc(), resultRef); - statements.push_back(returnStmt); - - auto body = BraceStmt::create(C, SourceLoc(), statements, SourceLoc()); - hashValueDecl->setBody(body); + auto body = BraceStmt::create(C, SourceLoc(), {ASTNode(switchStmt)}, + SourceLoc()); + hashIntoDecl->setBody(body); } -/// Derive the body for the 'hashValue' getter for a struct. +/// Derive the body for the 'hash(into:)' method for a struct. static void -deriveBodyHashable_struct_hashValue(AbstractFunctionDecl *hashValueDecl) { - auto parentDC = hashValueDecl->getDeclContext(); +deriveBodyHashable_struct_hashInto(AbstractFunctionDecl *hashIntoDecl) { + // struct SomeStruct { + // var x: Int + // var y: String + // @derived func hash(into hasher: inout Hasher) { + // hasher.combine(x) + // hasher.combine(y) + // } + // } + auto parentDC = hashIntoDecl->getDeclContext(); ASTContext &C = parentDC->getASTContext(); auto structDecl = parentDC->getAsStructOrStructExtensionContext(); SmallVector statements; - auto selfDecl = hashValueDecl->getImplicitSelfDecl(); - - Type intType = C.getIntDecl()->getDeclaredType(); + auto selfDecl = hashIntoDecl->getImplicitSelfDecl(); - auto resultVar = new (C) VarDecl(/*IsStatic*/ false, VarDecl::Specifier::Var, - /*IsCaptureList*/ false, SourceLoc(), - C.getIdentifier("result"), intType, - hashValueDecl); - resultVar->setInterfaceType(intType); - resultVar->setImplicit(); - - // var result: Int - Pattern *resultPat = new (C) NamedPattern(resultVar, /*implicit*/ true); - resultPat->setType(intType); - resultPat = new (C) TypedPattern(resultPat, TypeLoc::withoutLoc(intType)); - resultPat->setType(intType); - auto resultBind = PatternBindingDecl::create(C, SourceLoc(), - StaticSpellingKind::None, - SourceLoc(), - resultPat, nullptr, - hashValueDecl); - statements.push_back(resultBind); - - // result = 0 - { - auto resultRef = new (C) DeclRefExpr(resultVar, DeclNameLoc(), - /*implicit*/ true); - auto assignExpr = new (C) AssignExpr(resultRef, SourceLoc(), - integerLiteralExpr(C, 0), - /*implicit*/ true); - statements.emplace_back(ASTNode(assignExpr)); - } + // Extract the decl for the hasher parameter. + auto hasherParam = hashIntoDecl->getParameterList(1)->get(0); auto storedProperties = structDecl->getStoredProperties(/*skipInaccessible=*/true); - // For each stored property, generate a statement that combines its hash value - // into the result. + // Feed each stored property into the hasher. for (auto propertyDecl : storedProperties) { auto propertyRef = new (C) DeclRefExpr(propertyDecl, DeclNameLoc(), /*implicit*/ true); @@ -924,92 +996,62 @@ deriveBodyHashable_struct_hashValue(AbstractFunctionDecl *hashValueDecl) { /*implicit*/ true); auto selfPropertyExpr = new (C) DotSyntaxCallExpr(propertyRef, SourceLoc(), selfRef); - // result = _combineHashValues(result, .hashValue) - auto combineExpr = combineHashValuesAssignmentExpr(C, resultVar, - selfPropertyExpr); + // Generate: hasher.combine(self.) + auto combineExpr = createHasherCombineCall(C, hasherParam, selfPropertyExpr); statements.emplace_back(ASTNode(combineExpr)); } - { - // return result - auto resultRef = new (C) DeclRefExpr(resultVar, DeclNameLoc(), - /*implicit*/ true, - AccessSemantics::Ordinary, intType); - auto returnStmt = new (C) ReturnStmt(SourceLoc(), resultRef); - statements.push_back(returnStmt); - } + auto body = BraceStmt::create(C, SourceLoc(), statements, + SourceLoc(), /*implicit*/ true); + hashIntoDecl->setBody(body); +} - auto body = BraceStmt::create(C, SourceLoc(), statements, SourceLoc()); +/// Derive the body for the 'hashValue' getter. +static void +deriveBodyHashable_hashValue(AbstractFunctionDecl *hashValueDecl) { + auto parentDC = hashValueDecl->getDeclContext(); + ASTContext &C = parentDC->getASTContext(); + + // return _hashValue(for: self) + auto *hashFunc = C.getHashValueForDecl(); + auto hashExpr = new (C) DeclRefExpr(hashFunc, DeclNameLoc(), + /*implicit*/ true); + auto selfDecl = hashValueDecl->getImplicitSelfDecl(); + auto selfRef = new (C) DeclRefExpr(selfDecl, DeclNameLoc(), + /*implicit*/ true); + auto callExpr = CallExpr::createImplicit(C, hashExpr, + { selfRef }, { C.Id_for }); + auto returnStmt = new (C) ReturnStmt(SourceLoc(), callExpr); + + auto body = BraceStmt::create(C, SourceLoc(), {returnStmt}, SourceLoc(), + /*implicit*/ true); hashValueDecl->setBody(body); } -/// Derive a 'hashValue' implementation for an enum. -static ValueDecl * -deriveHashable_hashValue(TypeChecker &tc, Decl *parentDecl, - NominalTypeDecl *typeDecl, - void (*bodySynthesizer)(AbstractFunctionDecl *)) { - // enum SomeEnum { - // case A, B, C - // @derived var hashValue: Int { - // var result: Int - // switch self { - // case A: - // result = 0 - // case B: - // result = 1 - // case C: - // result = 2 - // } - // return result - // } - // } - // - // enum SomeEnumWithAssociatedValues { - // case A, B(Int), C(String, Int) - // @derived var hashValue: Int { - // var result: Int - // switch self { - // case A: - // result = 0 - // case B(let a0): - // result = 1 - // result = _combineHashValues(result, a0.hashValue) - // case C(let a0, let a1): - // result = 2 - // result = _combineHashValues(result, a0.hashValue) - // result = _combineHashValues(result, a1.hashValue) - // } - // return result - // } - // } - // - // struct SomeStruct { - // var x: Int - // var y: String - // @derived var hashValue: Int { - // var result = 0 - // result = _combineHashValues(result, x.hashValue) - // result = _combineHashValues(result, y.hashValue) - // return result - // } +/// Derive a 'hashValue' implementation. +static ValueDecl *deriveHashable_hashValue(DerivedConformance &derived) { + // @derived var hashValue: Int { + // return _hashValue(for: self) // } + auto &tc = derived.TC; ASTContext &C = tc.Context; - auto parentDC = cast(parentDecl); + auto parentDC = derived.getConformanceContext(); Type intType = C.getIntDecl()->getDeclaredType(); // We can't form a Hashable conformance if Int isn't Hashable or // ExpressibleByIntegerLiteral. - if (!tc.conformsToProtocol(intType,C.getProtocol(KnownProtocolKind::Hashable), - typeDecl, None)) { - tc.diagnose(typeDecl->getLoc(), diag::broken_int_hashable_conformance); + if (!tc.conformsToProtocol(intType, + C.getProtocol(KnownProtocolKind::Hashable), + parentDC, None)) { + tc.diagnose(derived.ConformanceDecl, diag::broken_int_hashable_conformance); return nullptr; } ProtocolDecl *intLiteralProto = C.getProtocol(KnownProtocolKind::ExpressibleByIntegerLiteral); - if (!tc.conformsToProtocol(intType, intLiteralProto, typeDecl, None)) { - tc.diagnose(typeDecl->getLoc(), + if (!tc.conformsToProtocol(intType, intLiteralProto, parentDC, None)) { + tc.diagnose(derived.ConformanceDecl, diag::broken_int_integer_literal_convertible_conformance); return nullptr; } @@ -1034,10 +1076,10 @@ deriveHashable_hashValue(TypeChecker &tc, Decl *parentDecl, /*GenericParams=*/nullptr, params, TypeLoc::withoutLoc(intType), parentDC); getterDecl->setImplicit(); - getterDecl->setBodySynthesizer(bodySynthesizer); + getterDecl->setBodySynthesizer(&deriveBodyHashable_hashValue); // Compute the type of hashValue(). - Type methodType = FunctionType::get(TupleType::getEmpty(tc.Context), intType); + Type methodType = FunctionType::get(TupleType::getEmpty(C), intType); // Compute the interface type of hashValue(). Type interfaceType; @@ -1052,13 +1094,8 @@ deriveHashable_hashValue(TypeChecker &tc, Decl *parentDecl, getterDecl->setInterfaceType(interfaceType); getterDecl->setValidationStarted(); - getterDecl->copyFormalAccessFrom(typeDecl); - - // If the enum was not imported, the derived conformance is either from the - // enum itself or an extension, in which case we will emit the declaration - // normally. - if (typeDecl->hasClangNode()) - tc.Context.addExternalDecl(getterDecl); + getterDecl->copyFormalAccessFrom(derived.Nominal, + /*sourceIsParentContext*/ true); // Finish creating the property. hashValueDecl->setImplicit(); @@ -1066,7 +1103,8 @@ deriveHashable_hashValue(TypeChecker &tc, Decl *parentDecl, hashValueDecl->setValidationStarted(); hashValueDecl->makeComputed(SourceLoc(), getterDecl, nullptr, nullptr, SourceLoc()); - hashValueDecl->copyFormalAccessFrom(typeDecl); + hashValueDecl->copyFormalAccessFrom(derived.Nominal, + /*sourceIsParentContext*/ true); Pattern *hashValuePat = new (C) NamedPattern(hashValueDecl, /*implicit*/true); hashValuePat->setType(intType); @@ -1081,48 +1119,109 @@ deriveHashable_hashValue(TypeChecker &tc, Decl *parentDecl, parentDC); patDecl->setImplicit(); - auto dc = cast(parentDecl); - dc->addMember(getterDecl); - dc->addMember(hashValueDecl); - dc->addMember(patDecl); + C.addSynthesizedDecl(hashValueDecl); + C.addSynthesizedDecl(getterDecl); + + derived.addMembersToConformanceContext({getterDecl, hashValueDecl, patDecl}); return hashValueDecl; } +static ValueDecl * +getHashValueRequirement(ASTContext &C) { + auto hashableProto = C.getProtocol(KnownProtocolKind::Hashable); + for (auto member: hashableProto->getMembers()) { + if (auto fd = dyn_cast(member)) { + if (fd->getBaseName() == C.Id_hashValue) + return fd; + } + } + return nullptr; +} + +static ProtocolConformance * +getHashableConformance(Decl *parentDecl) { + ASTContext &C = parentDecl->getASTContext(); + auto DC = cast(parentDecl); + auto hashableProto = C.getProtocol(KnownProtocolKind::Hashable); + for (auto conformance: DC->getLocalConformances()) { + if (conformance->getProtocol() == hashableProto) { + return conformance; + } + } + return nullptr; +} + bool DerivedConformance::canDeriveHashable(TypeChecker &tc, - NominalTypeDecl *type, - ValueDecl *requirement) { - auto hashableProto = tc.Context.getProtocol(KnownProtocolKind::Hashable); - return canDeriveConformance(tc, type, hashableProto); + NominalTypeDecl *type) { + if (!isa(type) && !isa(type) && !isa(type)) + return false; + // FIXME: This is not actually correct. We cannot promise to always + // provide a witness here in all cases. Unfortunately, figuring out + // whether this is actually possible requires a parent decl context. + // When the answer is no, DerivedConformance::deriveHashable will output + // its own diagnostics. + return true; } -ValueDecl *DerivedConformance::deriveHashable(TypeChecker &tc, - Decl *parentDecl, - NominalTypeDecl *type, - ValueDecl *requirement) { - // Conformance can't be synthesized in an extension; we allow it as a special - // case for enums with no associated values to preserve source compatibility. - auto theEnum = dyn_cast(type); - if (!(theEnum && theEnum->hasOnlyCasesWithoutAssociatedValues()) && - type != parentDecl) { - auto hashableProto = tc.Context.getProtocol(KnownProtocolKind::Hashable); - auto hashableType = hashableProto->getDeclaredType(); - tc.diagnose(parentDecl->getLoc(), diag::cannot_synthesize_in_extension, - hashableType); - return nullptr; +ValueDecl *DerivedConformance::deriveHashable(ValueDecl *requirement) { + ASTContext &C = ConformanceDecl->getASTContext(); + + // var hashValue: Int + if (requirement->getBaseName() == C.Id_hashValue) { + // We always allow hashValue to be synthesized; invalid cases are diagnosed + // during hash(into:) synthesis. + return deriveHashable_hashValue(*this); } - // Build the necessary decl. - if (requirement->getBaseName() == "hashValue") { - if (theEnum) - return deriveHashable_hashValue(tc, parentDecl, theEnum, - &deriveBodyHashable_enum_hashValue); - else if (auto theStruct = dyn_cast(type)) - return deriveHashable_hashValue(tc, parentDecl, theStruct, - &deriveBodyHashable_struct_hashValue); - else - llvm_unreachable("todo"); + // Hashable.hash(into:) + if (requirement->getBaseName() == C.Id_hash) { + // Start by resolving hashValue conformance. + auto hashValueReq = getHashValueRequirement(C); + auto conformance = getHashableConformance(ConformanceDecl); + auto hashValueDecl = conformance->getWitnessDecl(hashValueReq, &TC); + if (!hashValueDecl) { + // We won't derive hash(into:) if hashValue cannot be resolved. + // The hashValue failure will produce a diagnostic elsewhere. + return nullptr; + } + if (hashValueDecl && hashValueDecl->isImplicit()) { + // Neither hashValue nor hash(into:) is explicitly defined; we need to do + // a full Hashable derivation. + + // Refuse to synthesize Hashable if type isn't a struct or enum, or if it + // has non-Hashable stored properties/associated values. + auto hashableProto = C.getProtocol(KnownProtocolKind::Hashable); + if (!canDeriveConformance(TC, getConformanceContext(), Nominal, + hashableProto)) { + TC.diagnose(ConformanceDecl->getLoc(), diag::type_does_not_conform, + Nominal->getDeclaredType(), + hashableProto->getDeclaredType()); + return nullptr; + } + + if (checkAndDiagnoseDisallowedContext(requirement)) + return nullptr; + + if (auto ED = dyn_cast(Nominal)) { + auto bodySynthesizer = + !ED->hasOnlyCasesWithoutAssociatedValues() + ? &deriveBodyHashable_enum_hasAssociatedValues_hashInto + : &deriveBodyHashable_enum_noAssociatedValues_hashInto; + return deriveHashable_hashInto(*this, bodySynthesizer); + } else if (isa(Nominal)) + return deriveHashable_hashInto(*this, + &deriveBodyHashable_struct_hashInto); + else // This should've been caught by canDeriveHashable above. + llvm_unreachable("Attempt to derive Hashable for a type other " + "than a struct or enum"); + } else { + // We can always derive hash(into:) if hashValue has an explicit + // implementation. + return deriveHashable_hashInto(*this, + &deriveBodyHashable_compat_hashInto); + } } - tc.diagnose(requirement->getLoc(), - diag::broken_hashable_requirement); + + TC.diagnose(requirement->getLoc(), diag::broken_hashable_requirement); return nullptr; } diff --git a/lib/Sema/DerivedConformanceError.cpp b/lib/Sema/DerivedConformanceError.cpp index d3e48db0257d9..7ce89490fafdc 100644 --- a/lib/Sema/DerivedConformanceError.cpp +++ b/lib/Sema/DerivedConformanceError.cpp @@ -24,7 +24,6 @@ #include "swift/AST/Types.h" using namespace swift; -using namespace DerivedConformance; static void deriveBodyBridgedNSError_enum_nsErrorDomain( AbstractFunctionDecl *domainDecl) { @@ -53,9 +52,8 @@ static void deriveBodyBridgedNSError_enum_nsErrorDomain( domainDecl->setBody(body); } -static ValueDecl *deriveBridgedNSError_enum_nsErrorDomain(TypeChecker &tc, - Decl *parentDecl, - EnumDecl *enumDecl) { +static ValueDecl * +deriveBridgedNSError_enum_nsErrorDomain(DerivedConformance &derived) { // enum SomeEnum { // @derived // static var _nsErrorDomain: String { @@ -65,45 +63,35 @@ static ValueDecl *deriveBridgedNSError_enum_nsErrorDomain(TypeChecker &tc, // Note that for @objc enums the format is assumed to be "MyModule.SomeEnum". // If this changes, please change PrintAsObjC as well. - - ASTContext &C = tc.Context; - + + ASTContext &C = derived.TC.Context; + auto stringTy = C.getStringDecl()->getDeclaredType(); // Define the property. VarDecl *propDecl; PatternBindingDecl *pbDecl; - std::tie(propDecl, pbDecl) - = declareDerivedProperty(tc, parentDecl, enumDecl, C.Id_nsErrorDomain, - stringTy, stringTy, /*isStatic=*/true, - /*isFinal=*/true); + std::tie(propDecl, pbDecl) = derived.declareDerivedProperty( + C.Id_nsErrorDomain, stringTy, stringTy, /*isStatic=*/true, + /*isFinal=*/true); // Define the getter. - auto getterDecl = - addGetterToReadOnlyDerivedProperty(tc, propDecl, stringTy); + auto getterDecl = derived.addGetterToReadOnlyDerivedProperty( + derived.TC, propDecl, stringTy); getterDecl->setBodySynthesizer(&deriveBodyBridgedNSError_enum_nsErrorDomain); - auto dc = cast(parentDecl); - dc->addMember(getterDecl); - dc->addMember(propDecl); - dc->addMember(pbDecl); + derived.addMembersToConformanceContext({getterDecl, propDecl, pbDecl}); return propDecl; } -ValueDecl *DerivedConformance::deriveBridgedNSError(TypeChecker &tc, - Decl *parentDecl, - NominalTypeDecl *type, - ValueDecl *requirement) { - if (!isa(type)) +ValueDecl *DerivedConformance::deriveBridgedNSError(ValueDecl *requirement) { + if (!isa(Nominal)) return nullptr; - auto enumType = cast(type); - - if (requirement->getBaseName() == tc.Context.Id_nsErrorDomain) - return deriveBridgedNSError_enum_nsErrorDomain(tc, parentDecl, enumType); + if (requirement->getBaseName() == TC.Context.Id_nsErrorDomain) + return deriveBridgedNSError_enum_nsErrorDomain(*this); - tc.diagnose(requirement->getLoc(), - diag::broken_errortype_requirement); + TC.diagnose(requirement->getLoc(), diag::broken_errortype_requirement); return nullptr; } diff --git a/lib/Sema/DerivedConformanceRawRepresentable.cpp b/lib/Sema/DerivedConformanceRawRepresentable.cpp index df2c045cc3c29..e2ca4cab86c93 100644 --- a/lib/Sema/DerivedConformanceRawRepresentable.cpp +++ b/lib/Sema/DerivedConformanceRawRepresentable.cpp @@ -26,7 +26,6 @@ #include "DerivedConformances.h" using namespace swift; -using namespace DerivedConformance; static LiteralExpr *cloneRawLiteralExpr(ASTContext &C, LiteralExpr *expr) { LiteralExpr *clone; @@ -51,14 +50,13 @@ static LiteralExpr *cloneRawLiteralExpr(ASTContext &C, LiteralExpr *expr) { return clone; } -static Type deriveRawRepresentable_Raw(TypeChecker &tc, Decl *parentDecl, - EnumDecl *enumDecl) { +static Type deriveRawRepresentable_Raw(DerivedConformance &derived) { // enum SomeEnum : SomeType { // @derived // typealias Raw = SomeType // } - auto rawInterfaceType = enumDecl->getRawType(); - return cast(parentDecl)->mapTypeIntoContext(rawInterfaceType); + auto rawInterfaceType = cast(derived.Nominal)->getRawType(); + return derived.getConformanceContext()->mapTypeIntoContext(rawInterfaceType); } static void deriveBodyRawRepresentable_raw(AbstractFunctionDecl *toRawDecl) { @@ -101,8 +99,7 @@ static void deriveBodyRawRepresentable_raw(AbstractFunctionDecl *toRawDecl) { Identifier(), elt, nullptr); pat->setImplicit(); - auto labelItem = - CaseLabelItem(/*IsDefault=*/false, pat, SourceLoc(), nullptr); + auto labelItem = CaseLabelItem(pat); auto returnExpr = cloneRawLiteralExpr(C, elt->getRawValueExpr()); auto returnStmt = new (C) ReturnStmt(SourceLoc(), returnExpr); @@ -112,10 +109,10 @@ static void deriveBodyRawRepresentable_raw(AbstractFunctionDecl *toRawDecl) { cases.push_back(CaseStmt::create(C, SourceLoc(), labelItem, /*HasBoundDecls=*/false, SourceLoc(), - body)); + SourceLoc(), body)); } - auto selfRef = createSelfDeclRef(toRawDecl); + auto selfRef = DerivedConformance::createSelfDeclRef(toRawDecl); auto switchStmt = SwitchStmt::create(LabeledStmtInfo(), SourceLoc(), selfRef, SourceLoc(), cases, SourceLoc(), C); auto body = BraceStmt::create(C, SourceLoc(), @@ -124,32 +121,38 @@ static void deriveBodyRawRepresentable_raw(AbstractFunctionDecl *toRawDecl) { toRawDecl->setBody(body); } -static VarDecl *deriveRawRepresentable_raw(TypeChecker &tc, - Decl *parentDecl, - EnumDecl *enumDecl) { - ASTContext &C = tc.Context; - - auto parentDC = cast(parentDecl); +static VarDecl *deriveRawRepresentable_raw(DerivedConformance &derived) { + ASTContext &C = derived.TC.Context; + + auto enumDecl = cast(derived.Nominal); + auto parentDC = derived.getConformanceContext(); auto rawInterfaceType = enumDecl->getRawType(); auto rawType = parentDC->mapTypeIntoContext(rawInterfaceType); // Define the property. VarDecl *propDecl; PatternBindingDecl *pbDecl; - std::tie(propDecl, pbDecl) - = declareDerivedProperty(tc, parentDecl, enumDecl, C.Id_rawValue, - rawInterfaceType, rawType, /*isStatic=*/false, - /*isFinal=*/false); + std::tie(propDecl, pbDecl) = derived.declareDerivedProperty( + C.Id_rawValue, rawInterfaceType, rawType, /*isStatic=*/false, + /*isFinal=*/false); // Define the getter. - auto getterDecl = - addGetterToReadOnlyDerivedProperty(tc, propDecl, rawType); + auto getterDecl = DerivedConformance::addGetterToReadOnlyDerivedProperty( + derived.TC, propDecl, rawType); getterDecl->setBodySynthesizer(&deriveBodyRawRepresentable_raw); - auto dc = cast(parentDecl); - dc->addMember(getterDecl); - dc->addMember(propDecl); - dc->addMember(pbDecl); + // If the containing module is not resilient, make sure clients can get at + // the raw value without function call overhead. + if (parentDC->getParentModule()->getResilienceStrategy() != + ResilienceStrategy::Resilient) { + AccessScope access = + enumDecl->getFormalAccessScope(nullptr, + /*treatUsableFromInlineAsPublic*/true); + if (access.isPublic()) + getterDecl->getAttrs().add(new (C) InlinableAttr(/*implicit*/false)); + } + + derived.addMembersToConformanceContext({getterDecl, propDecl, pbDecl}); return propDecl; } @@ -214,8 +217,7 @@ deriveBodyRawRepresentable_init(AbstractFunctionDecl *initDecl) { nullptr, nullptr); litPat->setImplicit(); - auto labelItem = - CaseLabelItem(/*IsDefault=*/false, litPat, SourceLoc(), nullptr); + auto labelItem = CaseLabelItem(litPat); auto eltRef = new (C) DeclRefExpr(elt, DeclNameLoc(), /*implicit*/true); auto metaTyRef = TypeExpr::createImplicit(enumType, C); @@ -233,21 +235,20 @@ deriveBodyRawRepresentable_init(AbstractFunctionDecl *initDecl) { cases.push_back(CaseStmt::create(C, SourceLoc(), labelItem, /*HasBoundDecls=*/false, SourceLoc(), - body)); + SourceLoc(), body)); Idx++; } auto anyPat = new (C) AnyPattern(SourceLoc()); anyPat->setImplicit(); - auto dfltLabelItem = - CaseLabelItem(/*IsDefault=*/true, anyPat, SourceLoc(), nullptr); + auto dfltLabelItem = CaseLabelItem::getDefault(anyPat); auto dfltReturnStmt = new (C) FailStmt(SourceLoc(), SourceLoc()); auto dfltBody = BraceStmt::create(C, SourceLoc(), ASTNode(dfltReturnStmt), SourceLoc()); cases.push_back(CaseStmt::create(C, SourceLoc(), dfltLabelItem, /*HasBoundDecls=*/false, SourceLoc(), - dfltBody)); + SourceLoc(), dfltBody)); auto rawDecl = initDecl->getParameterList(1)->get(0); auto rawRef = new (C) DeclRefExpr(rawDecl, DeclNameLoc(), /*implicit*/true); @@ -274,12 +275,13 @@ deriveBodyRawRepresentable_init(AbstractFunctionDecl *initDecl) { initDecl->setBody(body); } -static ConstructorDecl *deriveRawRepresentable_init(TypeChecker &tc, - Decl *parentDecl, - EnumDecl *enumDecl) { +static ConstructorDecl * +deriveRawRepresentable_init(DerivedConformance &derived) { + auto &tc = derived.TC; ASTContext &C = tc.Context; - - auto parentDC = cast(parentDecl); + + auto enumDecl = cast(derived.Nominal); + auto parentDC = derived.getConformanceContext(); auto rawInterfaceType = enumDecl->getRawType(); auto rawType = parentDC->mapTypeIntoContext(rawInterfaceType); @@ -343,21 +345,30 @@ static ConstructorDecl *deriveRawRepresentable_init(TypeChecker &tc, } initDecl->setInterfaceType(allocIfaceType); initDecl->setInitializerInterfaceType(initIfaceType); - initDecl->copyFormalAccessFrom(enumDecl); + initDecl->copyFormalAccessFrom(enumDecl, /*sourceIsParentContext*/true); initDecl->setValidationStarted(); - // If the enum was not imported, the derived conformance is either from the - // enum itself or an extension, in which case we will emit the declaration - // normally. - if (enumDecl->hasClangNode()) - tc.Context.addExternalDecl(initDecl); + // If the containing module is not resilient, make sure clients can construct + // an instance without function call overhead. + if (parentDC->getParentModule()->getResilienceStrategy() != + ResilienceStrategy::Resilient) { + AccessScope access = + enumDecl->getFormalAccessScope(nullptr, + /*treatUsableFromInlineAsPublic*/true); + if (access.isPublic()) + initDecl->getAttrs().add(new (C) InlinableAttr(/*implicit*/false)); + } + + C.addSynthesizedDecl(initDecl); - cast(parentDecl)->addMember(initDecl); + derived.addMembersToConformanceContext({initDecl}); return initDecl; } -static bool canSynthesizeRawRepresentable(TypeChecker &tc, Decl *parentDecl, - EnumDecl *enumDecl) { +static bool canSynthesizeRawRepresentable(DerivedConformance &derived) { + auto enumDecl = cast(derived.Nominal); + auto &tc = derived.TC; + // Validate the enum and its raw type. tc.validateDecl(enumDecl); @@ -365,7 +376,7 @@ static bool canSynthesizeRawRepresentable(TypeChecker &tc, Decl *parentDecl, Type rawType = enumDecl->getRawType(); if (!rawType) return false; - auto parentDC = cast(parentDecl); + auto parentDC = cast(derived.ConformanceDecl); rawType = parentDC->mapTypeIntoContext(rawType); auto inherited = enumDecl->getInherited(); @@ -375,8 +386,8 @@ static bool canSynthesizeRawRepresentable(TypeChecker &tc, Decl *parentDecl, // The raw type must be Equatable, so that we have a suitable ~= for // synthesized switch statements. - auto equatableProto = tc.getProtocol(enumDecl->getLoc(), - KnownProtocolKind::Equatable); + auto equatableProto = + tc.getProtocol(enumDecl->getLoc(), KnownProtocolKind::Equatable); if (!equatableProto) return false; @@ -401,50 +412,41 @@ static bool canSynthesizeRawRepresentable(TypeChecker &tc, Decl *parentDecl, return true; } -ValueDecl *DerivedConformance::deriveRawRepresentable(TypeChecker &tc, - Decl *parentDecl, - NominalTypeDecl *type, - ValueDecl *requirement) { +ValueDecl *DerivedConformance::deriveRawRepresentable(ValueDecl *requirement) { // We can only synthesize RawRepresentable for enums. - auto enumDecl = dyn_cast(type); - if (!enumDecl) + if (!isa(Nominal)) return nullptr; // Check other preconditions for synthesized conformance. - if (!canSynthesizeRawRepresentable(tc, parentDecl, enumDecl)) + if (!canSynthesizeRawRepresentable(*this)) return nullptr; - if (requirement->getBaseName() == tc.Context.Id_rawValue) - return deriveRawRepresentable_raw(tc, parentDecl, enumDecl); + if (requirement->getBaseName() == TC.Context.Id_rawValue) + return deriveRawRepresentable_raw(*this); if (requirement->getBaseName() == DeclBaseName::createConstructor()) - return deriveRawRepresentable_init(tc, parentDecl, enumDecl); - - tc.diagnose(requirement->getLoc(), + return deriveRawRepresentable_init(*this); + + TC.diagnose(requirement->getLoc(), diag::broken_raw_representable_requirement); return nullptr; } -Type DerivedConformance::deriveRawRepresentable(TypeChecker &tc, - Decl *parentDecl, - NominalTypeDecl *type, - AssociatedTypeDecl *assocType) { +Type DerivedConformance::deriveRawRepresentable(AssociatedTypeDecl *assocType) { // We can only synthesize RawRepresentable for enums. - auto enumDecl = dyn_cast(type); - if (!enumDecl) + if (!isa(Nominal)) return nullptr; // Check other preconditions for synthesized conformance. - if (!canSynthesizeRawRepresentable(tc, parentDecl, enumDecl)) + if (!canSynthesizeRawRepresentable(*this)) return nullptr; - if (assocType->getName() == tc.Context.Id_RawValue) { - return deriveRawRepresentable_Raw(tc, parentDecl, enumDecl); + if (assocType->getName() == TC.Context.Id_RawValue) { + return deriveRawRepresentable_Raw(*this); } - - tc.diagnose(assocType->getLoc(), - diag::broken_raw_representable_requirement); + + TC.diagnose(assocType->getLoc(), diag::broken_raw_representable_requirement); return nullptr; } diff --git a/lib/Sema/DerivedConformances.cpp b/lib/Sema/DerivedConformances.cpp index d4bbb49bd46ab..b114d6b4ad07c 100644 --- a/lib/Sema/DerivedConformances.cpp +++ b/lib/Sema/DerivedConformances.cpp @@ -16,34 +16,66 @@ #include "swift/AST/Expr.h" #include "swift/AST/Pattern.h" #include "swift/AST/ParameterList.h" +#include "swift/AST/ProtocolConformance.h" #include "swift/AST/Types.h" #include "swift/ClangImporter/ClangModule.h" #include "DerivedConformances.h" using namespace swift; -using namespace DerivedConformance; -bool DerivedConformance::derivesProtocolConformance(TypeChecker &tc, - NominalTypeDecl *nominal, - ProtocolDecl *protocol) { +DerivedConformance::DerivedConformance(TypeChecker &tc, Decl *conformanceDecl, + NominalTypeDecl *nominal, + ProtocolDecl *protocol) + : TC(tc), ConformanceDecl(conformanceDecl), Nominal(nominal), + Protocol(protocol) { + assert(getConformanceContext() + ->getAsNominalTypeOrNominalTypeExtensionContext() == nominal); +} + +DeclContext *DerivedConformance::getConformanceContext() const { + return cast(ConformanceDecl); +} + +void DerivedConformance::addMembersToConformanceContext( + ArrayRef children) { + auto IDC = cast(ConformanceDecl); + for (auto child : children) { + IDC->addMember(child); + } +} + +Type DerivedConformance::getProtocolType() const { + return Protocol->getDeclaredType(); +} + +bool DerivedConformance::derivesProtocolConformance(TypeChecker &TC, + DeclContext *DC, + NominalTypeDecl *Nominal, + ProtocolDecl *Protocol) { // Only known protocols can be derived. - auto knownProtocol = protocol->getKnownProtocolKind(); + auto knownProtocol = Protocol->getKnownProtocolKind(); if (!knownProtocol) return false; - if (auto *enumDecl = dyn_cast(nominal)) { + if (*knownProtocol == KnownProtocolKind::Hashable) { + // We can always complete a partial Hashable implementation, and we can + // synthesize a full Hashable implementation for structs and enums with + // Hashable components. + return canDeriveHashable(TC, Nominal); + } + + if (auto *enumDecl = dyn_cast(Nominal)) { switch (*knownProtocol) { // The presence of a raw type is an explicit declaration that // the compiler should derive a RawRepresentable conformance. case KnownProtocolKind::RawRepresentable: return enumDecl->hasRawType(); - // Enums without associated values can implicitly derive Equatable and - // Hashable conformances. + // Enums without associated values can implicitly derive Equatable + // conformance. case KnownProtocolKind::Equatable: - return canDeriveEquatable(tc, enumDecl, protocol); - case KnownProtocolKind::Hashable: - return canDeriveHashable(tc, enumDecl, protocol); + return canDeriveEquatable(TC, DC, Nominal); + // "Simple" enums without availability attributes can explicitly derive // a CaseIterable conformance. // @@ -77,7 +109,7 @@ bool DerivedConformance::derivesProtocolConformance(TypeChecker &tc, default: return false; } - } else if (isa(nominal) || isa(nominal)) { + } else if (isa(Nominal) || isa(Nominal)) { // Structs and classes can explicitly derive Encodable and Decodable // conformance (explicitly meaning we can synthesize an implementation if // a type conforms manually). @@ -94,13 +126,11 @@ bool DerivedConformance::derivesProtocolConformance(TypeChecker &tc, return true; } - // Structs can explicitly derive Equatable and Hashable conformance. - if (auto structDecl = dyn_cast(nominal)) { + // Structs can explicitly derive Equatable conformance. + if (auto structDecl = dyn_cast(Nominal)) { switch (*knownProtocol) { case KnownProtocolKind::Equatable: - return canDeriveEquatable(tc, structDecl, protocol); - case KnownProtocolKind::Hashable: - return canDeriveHashable(tc, structDecl, protocol); + return canDeriveEquatable(TC, DC, Nominal); default: return false; } @@ -124,8 +154,15 @@ ValueDecl *DerivedConformance::getDerivableRequirement(TypeChecker &tc, auto proto = ctx.getProtocol(kind); if (!proto) return nullptr; - // Check whether this nominal type derives conformances to the protocol. - if (!derivesProtocolConformance(tc, nominal, proto)) return nullptr; + if (auto conformance = tc.conformsToProtocol( + nominal->getDeclaredInterfaceType(), proto, nominal, + ConformanceCheckFlags::SkipConditionalRequirements)) { + auto DC = conformance->getConcrete()->getDeclContext(); + // Check whether this nominal type derives conformances to the protocol. + if (!DerivedConformance::derivesProtocolConformance(tc, DC, nominal, + proto)) + return nullptr; + } // Retrieve the requirement. auto results = proto->lookupDirect(name); @@ -173,6 +210,13 @@ ValueDecl *DerivedConformance::getDerivableRequirement(TypeChecker &tc, return getRequirement(KnownProtocolKind::Encodable); } + // Hashable.hash(into: inout Hasher) + if (name.isCompoundName() && name.getBaseName() == ctx.Id_hash) { + auto argumentNames = name.getArgumentNames(); + if (argumentNames.size() == 1 && argumentNames[0] == ctx.Id_into) + return getRequirement(KnownProtocolKind::Hashable); + } + return nullptr; } @@ -283,31 +327,24 @@ DerivedConformance::declareDerivedPropertyGetter(TypeChecker &tc, getterDecl->copyFormalAccessFrom(property); getterDecl->setValidationStarted(); - // If the enum was not imported, the derived conformance is either from the - // enum itself or an extension, in which case we will emit the declaration - // normally. - if (isa(parentDC->getModuleScopeContext())) - tc.Context.addExternalDecl(getterDecl); + tc.Context.addSynthesizedDecl(getterDecl); return getterDecl; } std::pair -DerivedConformance::declareDerivedProperty(TypeChecker &tc, Decl *parentDecl, - NominalTypeDecl *typeDecl, - Identifier name, +DerivedConformance::declareDerivedProperty(Identifier name, Type propertyInterfaceType, Type propertyContextType, - bool isStatic, - bool isFinal) { - auto &C = tc.Context; - auto parentDC = cast(parentDecl); + bool isStatic, bool isFinal) { + auto &C = TC.Context; + auto parentDC = getConformanceContext(); VarDecl *propDecl = new (C) VarDecl(/*IsStatic*/isStatic, VarDecl::Specifier::Var, /*IsCaptureList*/false, SourceLoc(), name, propertyContextType, parentDC); propDecl->setImplicit(); - propDecl->copyFormalAccessFrom(typeDecl); + propDecl->copyFormalAccessFrom(Nominal, /*sourceIsParentContext*/ true); propDecl->setInterfaceType(propertyInterfaceType); propDecl->setValidationStarted(); @@ -319,9 +356,11 @@ DerivedConformance::declareDerivedProperty(TypeChecker &tc, Decl *parentDecl, Pattern *propPat = new (C) NamedPattern(propDecl, /*implicit*/ true); propPat->setType(propertyContextType); + propPat = new (C) TypedPattern(propPat, TypeLoc::withoutLoc(propertyContextType), /*implicit*/ true); + propPat->setType(propertyContextType); auto pbDecl = PatternBindingDecl::create(C, SourceLoc(), StaticSpellingKind::None, @@ -331,3 +370,54 @@ DerivedConformance::declareDerivedProperty(TypeChecker &tc, Decl *parentDecl, return {propDecl, pbDecl}; } + +bool DerivedConformance::checkAndDiagnoseDisallowedContext( + ValueDecl *synthesizing) const { + // In general, conformances can't be synthesized in extensions across files; + // but we have to allow it as a special case for Equatable and Hashable on + // enums with no associated values to preserve source compatibility. + bool allowCrossfileExtensions = false; + if (Protocol->isSpecificProtocol(KnownProtocolKind::Equatable) || + Protocol->isSpecificProtocol(KnownProtocolKind::Hashable)) { + auto ED = dyn_cast(Nominal); + allowCrossfileExtensions = ED && ED->hasOnlyCasesWithoutAssociatedValues(); + } + + if (TC.Context.isSwiftVersion3()) { + // In Swift 3, a 'private' property can't be accessed in any extensions, so + // we can't synthesize anything that uses them. Thus, we stick to the old + // rule for synthesis, which is never in an extension except for the + // Equatable/Hashable cases mentioned above. + if (!allowCrossfileExtensions && Nominal != ConformanceDecl) { + TC.diagnose(ConformanceDecl->getLoc(), + diag::swift3_cannot_synthesize_in_extension, + getProtocolType()); + return true; + } + } + + if (!allowCrossfileExtensions && + Nominal->getModuleScopeContext() != + getConformanceContext()->getModuleScopeContext()) { + TC.diagnose(ConformanceDecl->getLoc(), + diag::cannot_synthesize_in_crossfile_extension, + getProtocolType()); + TC.diagnose(Nominal->getLoc(), diag::kind_declared_here, + DescriptiveDeclKind::Type); + return true; + } + + // A non-final class can't have an protocol-witnesss initializer in an + // extension. + if (auto CD = dyn_cast(Nominal)) { + if (!CD->isFinal() && isa(synthesizing) && + isa(ConformanceDecl)) { + TC.diagnose(ConformanceDecl->getLoc(), + diag::cannot_synthesize_init_in_extension_of_nonfinal, + getProtocolType(), synthesizing->getFullName()); + return true; + } + } + + return false; +} diff --git a/lib/Sema/DerivedConformances.h b/lib/Sema/DerivedConformances.h index 2b5a6d1206fd8..01fd7bd0032ce 100644 --- a/lib/Sema/DerivedConformances.h +++ b/lib/Sema/DerivedConformances.h @@ -21,194 +21,177 @@ #include namespace swift { - class Decl; - class DeclRefExpr; - class AccessorDecl; - class NominalTypeDecl; - class PatternBindingDecl; - class Type; - class TypeChecker; - class ValueDecl; - class VarDecl; - -namespace DerivedConformance { - -/// True if the type can implicitly derive a conformance for the given protocol. -/// -/// If true, explicit conformance checking will synthesize implicit declarations -/// for requirements of the protocol that are not satisfied by the type's -/// explicit members. -/// -/// \param tc The type checker. -/// -/// \param nominal The nominal type for which we are determining whether to -/// derive a witness. -/// -/// \param protocol The protocol whose requirements are being derived. -/// -/// \return True if the type can implicitly derive a conformance for the given -/// protocol. -bool derivesProtocolConformance(TypeChecker &tc, - NominalTypeDecl *nominal, - ProtocolDecl *protocol); - -/// Determine the derivable requirement that would satisfy the given -/// requirement, if there is one. -/// -/// \param tc The type checker. -/// -/// \param nominal The nominal type for which we are determining whether to -/// derive a witness. -/// -/// \param requirement The requirement for which we are checking for a -/// derivation. This requirement need not be within a derivable protocol, -/// because derivable requirements can get restated in inherited unrelated or -/// unrelated protocols. -/// -/// \returns The requirement whose witness could be derived to potentially -/// satisfy this given requirement, or NULL if there is no such requirement. -ValueDecl *getDerivableRequirement(TypeChecker &tc, - NominalTypeDecl *nominal, - ValueDecl *requirement); - - -/// Derive a CaseIterable requirement for an enum if it has no associated -/// values for any of its cases. -/// -/// \returns the derived member, which will also be added to the type. -ValueDecl *deriveCaseIterable(TypeChecker &tc, - Decl *parentDecl, - NominalTypeDecl *type, - ValueDecl *requirement); - -/// Derive a CaseIterable type witness for an enum if it has no associated -/// values for any of its cases. -/// -/// \returns the derived member, which will also be added to the type. -Type deriveCaseIterable(TypeChecker &tc, - Decl *parentDecl, - NominalTypeDecl *type, - AssociatedTypeDecl *assocType); - -/// Derive a RawRepresentable requirement for an enum, if it has a valid -/// raw type and raw values for all of its cases. -/// -/// \returns the derived member, which will also be added to the type. -ValueDecl *deriveRawRepresentable(TypeChecker &tc, - Decl *parentDecl, - NominalTypeDecl *type, - ValueDecl *requirement); - -/// Derive a RawRepresentable type witness for an enum, if it has a valid -/// raw type and raw values for all of its cases. -/// -/// \returns the derived member, which will also be added to the type. -Type deriveRawRepresentable(TypeChecker &tc, - Decl *parentDecl, - NominalTypeDecl *type, - AssociatedTypeDecl *assocType); - -/// Determine if an Equatable requirement can be derived for a type. -/// -/// This is implemented for enums without associated values or all-Equatable -/// associated values, and for structs with all-Equatable stored properties. -/// -/// \returns True if the requirement can be derived. -bool canDeriveEquatable(TypeChecker &tc, - NominalTypeDecl *type, - ValueDecl *requirement); - -/// Derive an Equatable requirement for a type. -/// -/// This is implemented for enums without associated values or all-Equatable -/// associated values, and for structs with all-Equatable stored properties. -/// -/// \returns the derived member, which will also be added to the type. -ValueDecl *deriveEquatable(TypeChecker &tc, - Decl *parentDecl, - NominalTypeDecl *type, - ValueDecl *requirement); - -/// Determine if a Hashable requirement can be derived for a type. -/// -/// This is implemented for enums without associated values or all-Hashable -/// associated values, and for structs with all-Hashable stored properties. -/// -/// \returns True if the requirement can be derived. -bool canDeriveHashable(TypeChecker &tc, - NominalTypeDecl *type, - ValueDecl *requirement); - -/// Derive a Hashable requirement for a type. -/// -/// This is implemented for enums without associated values or all-Hashable -/// associated values, and for structs with all-Hashable stored properties. -/// -/// \returns the derived member, which will also be added to the type. -ValueDecl *deriveHashable(TypeChecker &tc, - Decl *parentDecl, - NominalTypeDecl *type, - ValueDecl *requirement); - -/// Derive a _BridgedNSError requirement for an @objc enum type. -/// -/// \returns the derived member, which will also be added to the type. -ValueDecl *deriveBridgedNSError(TypeChecker &tc, - Decl *parentDecl, - NominalTypeDecl *type, - ValueDecl *requirement); - -/// Derive a CodingKey requirement for an enum type. -/// -/// \returns the derived member, which will also be added to the type. -ValueDecl *deriveCodingKey(TypeChecker &tc, - Decl *parentDecl, - NominalTypeDecl *type, - ValueDecl *requirement); - -/// Derive an Encodable requirement for a struct type. -/// -/// \returns the derived member, which will also be added to the type. -ValueDecl *deriveEncodable(TypeChecker &tc, - Decl *parentDecl, - NominalTypeDecl *type, - ValueDecl *requirement); - -/// Derive a Decodable requirement for a struct type. -/// -/// \returns the derived member, which will also be added to the type. -ValueDecl *deriveDecodable(TypeChecker &tc, - Decl *parentDecl, - NominalTypeDecl *type, - ValueDecl *requirement); - -/// Declare a read-only property. -std::pair -declareDerivedProperty(TypeChecker &tc, - Decl *parentDecl, - NominalTypeDecl *typeDecl, - Identifier name, - Type propertyInterfaceType, - Type propertyContextType, - bool isStatic, - bool isFinal); - -/// Add a getter to a derived property. The property becomes read-only. -AccessorDecl *addGetterToReadOnlyDerivedProperty(TypeChecker &tc, - VarDecl *property, - Type propertyContextType); - -/// Declare a getter for a derived property. -/// The getter will not be added to the property yet. -AccessorDecl *declareDerivedPropertyGetter(TypeChecker &tc, - VarDecl *property, - Type propertyContextType); - -/// Build a reference to the 'self' decl of a derived function. -DeclRefExpr *createSelfDeclRef(AbstractFunctionDecl *fn); - -} - +class Decl; +class DeclRefExpr; +class AccessorDecl; +class NominalTypeDecl; +class PatternBindingDecl; +class Type; +class TypeChecker; +class ValueDecl; +class VarDecl; + +class DerivedConformance { +public: + TypeChecker &TC; + Decl *ConformanceDecl; + NominalTypeDecl *Nominal; + ProtocolDecl *Protocol; + + DerivedConformance(TypeChecker &tc, Decl *conformanceDecl, + NominalTypeDecl *nominal, ProtocolDecl *protocol); + + /// Retrieve the context in which the conformance is declared (either the + /// nominal type, or an extension of it) as a \c DeclContext. + DeclContext *getConformanceContext() const; + + /// Add \c children as members of the context that declares the conformance. + void addMembersToConformanceContext(ArrayRef children); + + /// Get the declared type of the protocol that this is conformance is for. + Type getProtocolType() const; + + /// True if the type can implicitly derive a conformance for the given + /// protocol. + /// + /// If true, explicit conformance checking will synthesize implicit + /// declarations for requirements of the protocol that are not satisfied by + /// the type's explicit members. + /// + /// \param tc The type checker. + /// + /// \param nominal The nominal type for which we are determining whether to + /// derive a witness. + /// + /// \param protocol The protocol whose requirements are being derived. + /// + /// \return True if the type can implicitly derive a conformance for the + /// given protocol. + static bool derivesProtocolConformance(TypeChecker &tc, DeclContext *DC, + NominalTypeDecl *nominal, + ProtocolDecl *protocol); + + /// Determine the derivable requirement that would satisfy the given + /// requirement, if there is one. + /// + /// \param tc The type checker. + /// + /// \param nominal The nominal type for which we are determining whether to + /// derive a witness. + /// + /// \param requirement The requirement for which we are checking for a + /// derivation. This requirement need not be within a derivable protocol, + /// because derivable requirements can get restated in inherited unrelated + /// or unrelated protocols. + /// + /// \returns The requirement whose witness could be derived to potentially + /// satisfy this given requirement, or NULL if there is no such requirement. + static ValueDecl *getDerivableRequirement(TypeChecker &tc, + NominalTypeDecl *nominal, + ValueDecl *requirement); + + /// Derive a CaseIterable requirement for an enum if it has no associated + /// values for any of its cases. + /// + /// \returns the derived member, which will also be added to the type. + ValueDecl *deriveCaseIterable(ValueDecl *requirement); + + /// Derive a CaseIterable type witness for an enum if it has no associated + /// values for any of its cases. + /// + /// \returns the derived member, which will also be added to the type. + Type deriveCaseIterable(AssociatedTypeDecl *assocType); + + /// Derive a RawRepresentable requirement for an enum, if it has a valid + /// raw type and raw values for all of its cases. + /// + /// \returns the derived member, which will also be added to the type. + ValueDecl *deriveRawRepresentable(ValueDecl *requirement); + + /// Derive a RawRepresentable type witness for an enum, if it has a valid + /// raw type and raw values for all of its cases. + /// + /// \returns the derived member, which will also be added to the type. + Type deriveRawRepresentable(AssociatedTypeDecl *assocType); + + /// Determine if an Equatable requirement can be derived for a type. + /// + /// This is implemented for enums without associated values or all-Equatable + /// associated values, and for structs with all-Equatable stored properties. + /// + /// \returns True if the requirement can be derived. + static bool canDeriveEquatable(TypeChecker &tc, DeclContext *DC, + NominalTypeDecl *type); + + /// Derive an Equatable requirement for a type. + /// + /// This is implemented for enums without associated values or all-Equatable + /// associated values, and for structs with all-Equatable stored properties. + /// + /// \returns the derived member, which will also be added to the type. + ValueDecl *deriveEquatable(ValueDecl *requirement); + + /// Determine if a Hashable requirement can be derived for a type. + /// + /// This is implemented for enums without associated values or all-Hashable + /// associated values, and for structs with all-Hashable stored properties. + /// + /// \returns True if the requirement can be derived. + static bool canDeriveHashable(TypeChecker &tc, NominalTypeDecl *type); + + /// Derive a Hashable requirement for a type. + /// + /// This is implemented for enums without associated values or all-Hashable + /// associated values, and for structs with all-Hashable stored properties. + /// + /// \returns the derived member, which will also be added to the type. + ValueDecl *deriveHashable(ValueDecl *requirement); + + /// Derive a _BridgedNSError requirement for an @objc enum type. + /// + /// \returns the derived member, which will also be added to the type. + ValueDecl *deriveBridgedNSError(ValueDecl *requirement); + + /// Derive a CodingKey requirement for an enum type. + /// + /// \returns the derived member, which will also be added to the type. + ValueDecl *deriveCodingKey(ValueDecl *requirement); + + /// Derive an Encodable requirement for a struct type. + /// + /// \returns the derived member, which will also be added to the type. + ValueDecl *deriveEncodable(ValueDecl *requirement); + + /// Derive a Decodable requirement for a struct type. + /// + /// \returns the derived member, which will also be added to the type. + ValueDecl *deriveDecodable(ValueDecl *requirement); + + /// Declare a read-only property. + std::pair + declareDerivedProperty(Identifier name, Type propertyInterfaceType, + Type propertyContextType, bool isStatic, bool isFinal); + + /// Add a getter to a derived property. The property becomes read-only. + static AccessorDecl * + addGetterToReadOnlyDerivedProperty(TypeChecker &tc, VarDecl *property, + Type propertyContextType); + + /// Declare a getter for a derived property. + /// The getter will not be added to the property yet. + static AccessorDecl *declareDerivedPropertyGetter(TypeChecker &tc, + VarDecl *property, + Type propertyContextType); + + /// Build a reference to the 'self' decl of a derived function. + static DeclRefExpr *createSelfDeclRef(AbstractFunctionDecl *fn); + + /// Returns true if this derivation is trying to use a context that isn't + /// appropriate for deriving. + /// + /// \param synthesizing The decl that is being synthesized. + bool checkAndDiagnoseDisallowedContext(ValueDecl *synthesizing) const; +}; } #endif diff --git a/lib/Sema/ITCType.cpp b/lib/Sema/ITCType.cpp deleted file mode 100644 index 598fbb69b847b..0000000000000 --- a/lib/Sema/ITCType.cpp +++ /dev/null @@ -1,88 +0,0 @@ -//===--- ITCType.cpp - Iterative Type Checker for Types -------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// -// -// This file implements the portions of the IterativeTypeChecker -// class that involve types. -// -//===----------------------------------------------------------------------===// -#include "GenericTypeResolver.h" -#include "TypeChecker.h" -#include "swift/Sema/IterativeTypeChecker.h" -#include "swift/AST/ASTContext.h" -#include "swift/AST/ASTWalker.h" -#include "swift/AST/Decl.h" -using namespace swift; - -//===----------------------------------------------------------------------===// -// Type resolution. -//===----------------------------------------------------------------------===// -bool IterativeTypeChecker::isResolveTypeReprSatisfied( - std::tuple payload) { - auto typeRepr = std::get<0>(payload); - auto options = static_cast(std::get<2>(payload)); - - // FIXME: Introduce a bit indicating when everything in the TypeRepr - // has been name-bound, which indicates that we can always compute a - // structural type. - struct FindUnboundTypeRepr : public ASTWalker { - TypeResolutionOptions Options; - - // Whether we've found an unbound type. - bool HasUnbound = false; - - FindUnboundTypeRepr(TypeResolutionOptions options) : Options(options) { } - - bool walkToTypeReprPre(TypeRepr *T) override { - // If we already found an unbound type, we're done. - if (HasUnbound) return false; - - if (auto ident = dyn_cast(T)) { - if (!ident->isBound()) { - HasUnbound = true; - return false; - } - - // If we're only looking to resolve the structure of the type, - // don't walk into generic arguments. They don't affect the - // structure. - if (Options.contains(TypeResolutionFlags::ResolveStructure) && - isa(ident)) - return false; - } - - // Keep walking. - return true; - } - } findUnboundTypeRepr(options); - - typeRepr->walk(findUnboundTypeRepr); - return findUnboundTypeRepr.HasUnbound; -} - -void IterativeTypeChecker::processResolveTypeRepr( - std::tuple payload, - UnsatisfiedDependency unsatisfiedDependency) { - auto typeRepr = std::get<0>(payload); - auto dc = std::get<1>(payload); - auto options = static_cast(std::get<2>(payload)); - - // FIXME: TypeChecker::resolveType() is mostly non-ad-hoc-recursive - // when given an UnsatisfiedDependency. - TC.resolveType(typeRepr, dc, options, nullptr, &unsatisfiedDependency); -} - -bool IterativeTypeChecker::breakCycleForResolveTypeRepr( - std::tuple payload) { - std::get<0>(payload)->setInvalid(); - return true; -} - diff --git a/lib/Sema/MiscDiagnostics.cpp b/lib/Sema/MiscDiagnostics.cpp index 50ceb089d50b9..1163dd6d3a9a2 100644 --- a/lib/Sema/MiscDiagnostics.cpp +++ b/lib/Sema/MiscDiagnostics.cpp @@ -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 - 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 @@ -86,14 +86,15 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E, // Selector for the partial_application_of_function_invalid diagnostic // message. struct PartialApplication { - unsigned level : 29; enum : unsigned { Function, MutatingMethod, SuperInit, SelfInit, }; + // 'kind' before 'level' is better for code gen. unsigned kind : 3; + unsigned level : 29; }; // Partial applications of functions that are not permitted. This is @@ -121,7 +122,7 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E, // Partial applications of delegated initializers aren't allowed, and // don't really make sense to begin with. - InvalidPartialApplications.insert({ expr, {1, kind} }); + InvalidPartialApplications.insert({ expr, {kind, 1} }); return; } @@ -141,7 +142,7 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E, if (!expr->getArg()->getType()->isMaterializable()) { // We need to apply all argument clauses. InvalidPartialApplications.insert({ - fnExpr, {fn->getNumParameterLists(), kind} + fnExpr, {kind, fn->getNumParameterLists()} }); } } @@ -172,7 +173,7 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E, InvalidPartialApplications.erase(foundApplication); if (level > 1) { // We have remaining argument clauses. - InvalidPartialApplications.insert({ AE, {level - 1, kind} }); + InvalidPartialApplications.insert({ AE, {kind, level - 1} }); } return; } @@ -554,14 +555,11 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E, // We care about whether the parameter list of the callee syntactically // has more than one argument. It has to *syntactically* have a tuple // type as its argument. A ParenType wrapping a TupleType is a single - // parameter. - if (isa(FT->getInput().getPointer())) { - auto TT = FT->getInput()->getAs(); - if (TT->getNumElements() > 1) { - TC.diagnose(Call->getLoc(), diag::tuple_splat_use, - TT->getNumElements()) - .highlight(Call->getArg()->getSourceRange()); - } + // parameter. + auto params = FT->getParams(); + if (params.size() > 1) { + TC.diagnose(Call->getLoc(), diag::tuple_splat_use, params.size()) + .highlight(Call->getArg()->getSourceRange()); } } @@ -873,7 +871,8 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E, DeclContext *topLevelContext = DC->getModuleScopeContext(); UnqualifiedLookup lookup(VD->getBaseName(), topLevelContext, &TC, - /*knownPrivate*/true); + /*Loc=*/SourceLoc(), + UnqualifiedLookup::Flags::KnownPrivate); // Group results by module. Pick an arbitrary result from each module. llvm::SmallDenseMap resultsByModule; @@ -967,16 +966,19 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E, if (DRE->getDecl() != TC.Context.getUnsafeBitCast(&TC)) return; - if (DRE->getDeclRef().getSubstitutions().size() != 2) + if (DRE->getDeclRef().getSubstitutions().empty()) return; // Don't check the same use of unsafeBitCast twice. if (!AlreadyDiagnosedBitCasts.insert(DRE).second) return; - - auto fromTy = DRE->getDeclRef().getSubstitutions()[0].getReplacement(); - auto toTy = DRE->getDeclRef().getSubstitutions()[1].getReplacement(); - + + auto subMap = DRE->getDeclRef().getSubstitutions(); + auto fromTy = + Type(GenericTypeParamType::get(0, 0, TC.Context)).subst(subMap); + auto toTy = + Type(GenericTypeParamType::get(0, 1, TC.Context)).subst(subMap); + // Warn about `unsafeBitCast` formulations that are undefined behavior // or have better-defined alternative APIs that can be used instead. @@ -2010,6 +2012,182 @@ bool swift::diagnoseArgumentLabelError(TypeChecker &TC, const Expr *expr, return true; } +static const Expr *lookThroughExprsToImmediateDeallocation(const Expr *E) { + // Look through various expressions that don't affect the fact that the user + // will be assigning a class instance that will be immediately deallocated. + while (true) { + E = E->getValueProvidingExpr(); + + // We don't currently deal with tuple shuffles. + if (isa(E)) + return E; + + // If we have a TupleElementExpr with a child TupleExpr, dig into that + // element. + if (auto *TEE = dyn_cast(E)) { + auto *subExpr = lookThroughExprsToImmediateDeallocation(TEE->getBase()); + if (auto *TE = dyn_cast(subExpr)) { + auto *element = TE->getElements()[TEE->getFieldNumber()]; + return lookThroughExprsToImmediateDeallocation(element); + } + return subExpr; + } + + if (auto *ICE = dyn_cast(E)) { + E = ICE->getSubExpr(); + continue; + } + if (auto *CE = dyn_cast(E)) { + E = CE->getSubExpr(); + continue; + } + if (auto *OEE = dyn_cast(E)) { + E = OEE->getSubExpr(); + continue; + } + + // Look through optional evaluations, we still want to diagnose on + // things like initializers called through optional chaining and the + // unwrapping of failable initializers. + if (auto *OEE = dyn_cast(E)) { + E = OEE->getSubExpr(); + continue; + } + if (auto *OBE = dyn_cast(E)) { + E = OBE->getSubExpr(); + continue; + } + if (auto *FOE = dyn_cast(E)) { + E = FOE->getSubExpr(); + continue; + } + + if (auto *ATE = dyn_cast(E)) { + E = ATE->getSubExpr(); + continue; + } + if (auto *DSBIE = dyn_cast(E)) { + E = DSBIE->getRHS(); + continue; + } + return E; + } +} + +static void diagnoseUnownedImmediateDeallocationImpl(TypeChecker &TC, + const VarDecl *varDecl, + const Expr *initExpr, + SourceLoc diagLoc, + SourceRange diagRange) { + auto *ownershipAttr = + varDecl->getAttrs().getAttribute(); + if (!ownershipAttr || ownershipAttr->isInvalid()) + return; + + // Only diagnose for non-owning ownerships such as 'weak' and 'unowned'. + switch (ownershipAttr->get()) { + case ReferenceOwnership::Strong: + return; + case ReferenceOwnership::Weak: + case ReferenceOwnership::Unowned: + case ReferenceOwnership::Unmanaged: + break; + } + + // Try to find a call to a constructor. + initExpr = lookThroughExprsToImmediateDeallocation(initExpr); + auto *CE = dyn_cast(initExpr); + if (!CE) + return; + + auto *CRCE = dyn_cast(CE->getFn()); + if (!CRCE) + return; + + auto *DRE = dyn_cast(CRCE->getFn()); + if (!DRE) + return; + + auto *constructorDecl = dyn_cast(DRE->getDecl()); + if (!constructorDecl) + return; + + // Make sure the constructor constructs an instance that allows ownership. + // This is to ensure we don't diagnose on constructors such as + // Optional.init(nilLiteral:). + auto selfType = constructorDecl->getDeclContext()->getSelfTypeInContext(); + if (!selfType->allowsOwnership()) + return; + + // This must stay in sync with + // diag::unowned_assignment_immediate_deallocation. + enum { + SK_Variable = 0, + SK_Property + } storageKind = SK_Variable; + + if (varDecl->getDeclContext()->isTypeContext()) + storageKind = SK_Property; + + TC.diagnose(diagLoc, diag::unowned_assignment_immediate_deallocation, + varDecl->getName(), ownershipAttr->get(), unsigned(storageKind)) + .highlight(diagRange); + + TC.diagnose(diagLoc, diag::unowned_assignment_requires_strong) + .highlight(diagRange); + + TC.diagnose(varDecl, diag::decl_declared_here, varDecl->getFullName()); +} + +void swift::diagnoseUnownedImmediateDeallocation(TypeChecker &TC, + const AssignExpr *assignExpr) { + auto *destExpr = assignExpr->getDest()->getValueProvidingExpr(); + auto *initExpr = assignExpr->getSrc(); + + // Try to find a referenced VarDecl. + const VarDecl *VD = nullptr; + if (auto *DRE = dyn_cast(destExpr)) { + VD = dyn_cast(DRE->getDecl()); + } else if (auto *MRE = dyn_cast(destExpr)) { + VD = dyn_cast(MRE->getMember().getDecl()); + } + + if (VD) + diagnoseUnownedImmediateDeallocationImpl(TC, VD, initExpr, + assignExpr->getLoc(), + initExpr->getSourceRange()); +} + +void swift::diagnoseUnownedImmediateDeallocation(TypeChecker &TC, + const Pattern *pattern, + const Expr *initExpr) { + pattern = pattern->getSemanticsProvidingPattern(); + + if (auto *TP = dyn_cast(pattern)) { + initExpr = lookThroughExprsToImmediateDeallocation(initExpr); + + // If we've found a matching tuple initializer with the same number of + // elements as our pattern, diagnose each element individually. + auto TE = dyn_cast(initExpr); + if (TE && TE->getNumElements() == TP->getNumElements()) { + for (unsigned i = 0, e = TP->getNumElements(); i != e; ++i) { + const TuplePatternElt &elt = TP->getElement(i); + const Pattern *subPattern = elt.getPattern(); + Expr *subInitExpr = TE->getElement(i); + + diagnoseUnownedImmediateDeallocation(TC, subPattern, subInitExpr); + } + } + } else if (auto *NP = dyn_cast(pattern)) { + // FIXME: Ideally the diagnostic location should be on the equals '=' token + // of the pattern binding rather than at the start of the initializer + // expression (matching the above diagnostic logic for an assignment). + diagnoseUnownedImmediateDeallocationImpl(TC, NP->getDecl(), initExpr, + initExpr->getStartLoc(), + initExpr->getSourceRange()); + } +} + bool swift::fixItOverrideDeclarationTypes(InFlightDiagnostic &diag, ValueDecl *decl, const ValueDecl *base) { @@ -3546,6 +3724,39 @@ static void diagnoseUnintendedOptionalBehavior(TypeChecker &TC, const Expr *E, .fixItInsertAfter(E->getEndLoc(), coercionString); } + static bool hasImplicitlyUnwrappedResult(Expr *E) { + auto getDeclForExpr = [&](Expr *E) -> ValueDecl * { + if (auto *call = dyn_cast(E)) + E = call->getDirectCallee(); + + if (auto *subscript = dyn_cast(E)) { + if (subscript->hasDecl()) + return subscript->getDecl().getDecl(); + + return nullptr; + } + + if (auto *memberRef = dyn_cast(E)) + return memberRef->getMember().getDecl(); + if (auto *declRef = dyn_cast(E)) + return declRef->getDecl(); + if (auto *apply = dyn_cast(E)) + return apply->getCalledValue(); + + return nullptr; + }; + + // Look through implicit conversions like loads, derived-to-base + // conversion, etc. + if (auto *ICE = dyn_cast(E)) + E = ICE->getSubExpr(); + + auto *decl = getDeclForExpr(E); + + return decl + && decl->getAttrs().hasAttribute(); + } + void visitErasureExpr(ErasureExpr *E, OptionalToAnyCoercion coercion) { if (coercion.shouldSuppressDiagnostic()) return; @@ -3557,6 +3768,12 @@ static void diagnoseUnintendedOptionalBehavior(TypeChecker &TC, const Expr *E, while (auto *bindExpr = dyn_cast(subExpr)) subExpr = bindExpr->getSubExpr(); + // Do not warn on coercions from implicitly unwrapped optionals + // for Swift versions less than 5. + if (!TC.Context.isSwiftVersionAtLeast(5) && + hasImplicitlyUnwrappedResult(subExpr)) + return; + // We're taking the source type from the child of any BindOptionalExprs, // and the destination from the parent of any // (InjectIntoOptional/OptionalEvaluation)Exprs in order to take into diff --git a/lib/Sema/MiscDiagnostics.h b/lib/Sema/MiscDiagnostics.h index 0c5b811b4a17b..fe56a51cb7322 100644 --- a/lib/Sema/MiscDiagnostics.h +++ b/lib/Sema/MiscDiagnostics.h @@ -14,6 +14,8 @@ #define SWIFT_SEMA_MISC_DIAGNOSTICS_H #include "swift/AST/AttrKind.h" +#include "swift/AST/Pattern.h" +#include "swift/AST/Expr.h" #include "swift/AST/Identifier.h" #include "swift/Basic/LLVM.h" #include "swift/Basic/SourceLoc.h" @@ -64,6 +66,20 @@ bool diagnoseArgumentLabelError(TypeChecker &TC, const Expr *expr, bool isSubscript, InFlightDiagnostic *existingDiag = nullptr); +/// If \p assignExpr has a destination expression that refers to a declaration +/// with a non-owning attribute, such as 'weak' or 'unowned' and the initializer +/// expression refers to a class constructor, emit a warning that the assigned +/// instance will be immediately deallocated. +void diagnoseUnownedImmediateDeallocation(TypeChecker &TC, + const AssignExpr *assignExpr); + +/// If \p pattern binds to a declaration with a non-owning attribute, such as +/// 'weak' or 'unowned' and \p initializer refers to a class constructor, +/// emit a warning that the bound instance will be immediately deallocated. +void diagnoseUnownedImmediateDeallocation(TypeChecker &TC, + const Pattern *pattern, + const Expr *initializer); + /// Attempt to fix the type of \p decl so that it's a valid override for /// \p base...but only if we're highly confident that we know what the user /// should have written. diff --git a/lib/Sema/OverloadChoice.h b/lib/Sema/OverloadChoice.h index 4e4afac2d21cb..9cca7bf884ee7 100644 --- a/lib/Sema/OverloadChoice.h +++ b/lib/Sema/OverloadChoice.h @@ -85,9 +85,9 @@ class OverloadChoice { /// We mash together OverloadChoiceKind with tuple indices into a single /// integer representation. - typedef llvm::PointerEmbeddedInt - OverloadChoiceKindWithTupleIndex; - + using OverloadChoiceKindWithTupleIndex = + llvm::PointerEmbeddedInt; + /// Depending on the OverloadChoiceKind, this could be one of two cases: /// 1) A ValueDecl for the cases that match to a Decl. The exactly kind of /// decl reference is disambiguated with the DeclKind bits in diff --git a/lib/Sema/PlaygroundTransform.cpp b/lib/Sema/PlaygroundTransform.cpp index e38a5af028b73..96f8da8e45d81 100644 --- a/lib/Sema/PlaygroundTransform.cpp +++ b/lib/Sema/PlaygroundTransform.cpp @@ -52,7 +52,7 @@ class Instrumenter : InstrumenterBase { BracePair(const SourceRange &BR) : BraceRange(BR) {} }; - typedef std::forward_list BracePairStack; + using BracePairStack = std::forward_list; BracePairStack BracePairs; class BracePairPusher { @@ -95,7 +95,7 @@ class Instrumenter : InstrumenterBase { } }; - typedef SmallVector ElementVector; + using ElementVector = SmallVector; // Before a "return," "continue" or similar statement, emit pops of // all the braces up to its target. @@ -370,7 +370,7 @@ class Instrumenter : InstrumenterBase { BraceStmt *transformBraceStmt(BraceStmt *BS, bool TopLevel = false) override { ArrayRef OriginalElements = BS->getElements(); - typedef SmallVector ElementVector; + using ElementVector = SmallVector; ElementVector Elements(OriginalElements.begin(), OriginalElements.end()); SourceRange SR = BS->getSourceRange(); diff --git a/lib/Sema/ResilienceDiagnostics.cpp b/lib/Sema/ResilienceDiagnostics.cpp index 06783ae388bba..70d72e98bf92d 100644 --- a/lib/Sema/ResilienceDiagnostics.cpp +++ b/lib/Sema/ResilienceDiagnostics.cpp @@ -23,13 +23,24 @@ using namespace swift; using FragileFunctionKind = TypeChecker::FragileFunctionKind; -FragileFunctionKind TypeChecker::getFragileFunctionKind(const DeclContext *DC) { +std::pair +TypeChecker::getFragileFunctionKind(const DeclContext *DC) { for (; DC->isLocalContext(); DC = DC->getParent()) { - if (isa(DC)) - return FragileFunctionKind::DefaultArgument; + if (isa(DC)) { + // Default argument generators of public functions cannot reference + // @usableFromInline declarations; all other fragile function kinds + // can. + auto *VD = cast(DC->getInnermostDeclarationDeclContext()); + auto access = + VD->getFormalAccessScope(/*useDC=*/nullptr, + /*treatUsableFromInlineAsPublic=*/false); + return std::make_pair(FragileFunctionKind::DefaultArgument, + !access.isPublic()); + } if (isa(DC)) - return FragileFunctionKind::PropertyInitializer; + return std::make_pair(FragileFunctionKind::PropertyInitializer, + /*treatUsableFromInlineAsPublic=*/true); if (auto *AFD = dyn_cast(DC)) { // If the function is a nested function, we will serialize its body if @@ -40,20 +51,24 @@ FragileFunctionKind TypeChecker::getFragileFunctionKind(const DeclContext *DC) { // Bodies of public transparent and always-inline functions are // serialized, so use conservative access patterns. if (AFD->isTransparent()) - return FragileFunctionKind::Transparent; + return std::make_pair(FragileFunctionKind::Transparent, + /*treatUsableFromInlineAsPublic=*/true); if (AFD->getAttrs().hasAttribute()) - return FragileFunctionKind::Inlinable; + return std::make_pair(FragileFunctionKind::Inlinable, + /*treatUsableFromInlineAsPublic=*/true); if (auto attr = AFD->getAttrs().getAttribute()) if (attr->getKind() == InlineKind::Always) - return FragileFunctionKind::InlineAlways; + return std::make_pair(FragileFunctionKind::InlineAlways, + /*treatUsableFromInlineAsPublic=*/true); // If a property or subscript is @inlinable, the accessors are // @inlinable also. if (auto accessor = dyn_cast(AFD)) if (accessor->getStorage()->getAttrs().getAttribute()) - return FragileFunctionKind::Inlinable; + return std::make_pair(FragileFunctionKind::Inlinable, + /*treatUsableFromInlineAsPublic=*/true); } } @@ -64,21 +79,18 @@ void TypeChecker::diagnoseInlinableLocalType(const NominalTypeDecl *NTD) { auto *DC = NTD->getDeclContext(); auto expansion = DC->getResilienceExpansion(); if (expansion == ResilienceExpansion::Minimal) { + auto kind = getFragileFunctionKind(DC); diagnose(NTD, diag::local_type_in_inlinable_function, NTD->getFullName(), - static_cast(getFragileFunctionKind(DC))); + static_cast(kind.first)); } } bool TypeChecker::diagnoseInlinableDeclRef(SourceLoc loc, const ValueDecl *D, - const DeclContext *DC) { - auto expansion = DC->getResilienceExpansion(); - - // Internal declarations referenced from non-inlinable contexts are OK. - if (expansion == ResilienceExpansion::Maximal) - return false; - + const DeclContext *DC, + FragileFunctionKind Kind, + bool TreatUsableFromInlineAsPublic) { // Local declarations are OK. if (D->getDeclContext()->isLocalContext()) return false; @@ -89,7 +101,7 @@ bool TypeChecker::diagnoseInlinableDeclRef(SourceLoc loc, // Public declarations are OK. if (D->getFormalAccessScope(/*useDC=*/nullptr, - /*respectVersionedAttr=*/true).isPublic()) + TreatUsableFromInlineAsPublic).isPublic()) return false; // Enum cases are handled as part of their containing enum. @@ -114,23 +126,13 @@ bool TypeChecker::diagnoseInlinableDeclRef(SourceLoc loc, diagnose(loc, diag::resilience_decl_unavailable, D->getDescriptiveKind(), D->getFullName(), D->getFormalAccessScope().accessLevelForDiagnostics(), - static_cast(getFragileFunctionKind(DC))); - - bool isDefaultArgument = false; - while (DC->isLocalContext()) { - if (isa(DC)) { - isDefaultArgument = true; - break; - } - - DC = DC->getParent(); - } + static_cast(Kind)); - if (isDefaultArgument) { + if (TreatUsableFromInlineAsPublic) { diagnose(D, diag::resilience_decl_declared_here, D->getDescriptiveKind(), D->getFullName()); } else { - diagnose(D, diag::resilience_decl_declared_here_versioned, + diagnose(D, diag::resilience_decl_declared_here_public, D->getDescriptiveKind(), D->getFullName()); } diff --git a/lib/Sema/SourceLoader.cpp b/lib/Sema/SourceLoader.cpp index e349a1be298db..73392811a18fa 100644 --- a/lib/Sema/SourceLoader.cpp +++ b/lib/Sema/SourceLoader.cpp @@ -18,6 +18,7 @@ #include "swift/Sema/SourceLoader.h" #include "swift/Subsystems.h" #include "swift/AST/DiagnosticsSema.h" +#include "swift/AST/Module.h" #include "swift/Parse/DelayedParsingCallbacks.h" #include "swift/Parse/PersistentParserState.h" #include "swift/Basic/SourceManager.h" @@ -154,6 +155,7 @@ ModuleDecl *SourceLoader::loadModule(SourceLoc importLoc, else performTypeChecking(*importFile, persistentState.getTopLevelContext(), None); + importMod->setHasResolvedImports(); return importMod; } diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 6f7ff3bdf60b0..b80426fda96ea 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -70,6 +70,7 @@ class AttributeEarlyChecker : public AttributeVisitor { bool visitDeclAttribute(DeclAttribute *A) = delete; #define IGNORED_ATTR(X) void visit##X##Attr(X##Attr *) {} + IGNORED_ATTR(Available) IGNORED_ATTR(CDecl) IGNORED_ATTR(ClangImporterSynthesizedType) IGNORED_ATTR(Convenience) @@ -115,14 +116,6 @@ class AttributeEarlyChecker : public AttributeVisitor { IGNORED_ATTR(WeakLinked) #undef IGNORED_ATTR - void visitAvailableAttr(AvailableAttr *attr) { - if (!isa(D)) - return; - if (attr->hasPlatform()) - return; - diagnoseAndRemoveAttr(attr, diag::availability_extension_platform_agnostic); - } - // @noreturn has been replaced with a 'Never' return type. void visitNoReturnAttr(NoReturnAttr *attr) { if (auto FD = dyn_cast(D)) { @@ -185,7 +178,7 @@ class AttributeEarlyChecker : public AttributeVisitor { void visitAlignmentAttr(AlignmentAttr *attr) { // Alignment must be a power of two. - unsigned value = attr->Value; + auto value = attr->getValue(); if (value == 0 || (value & (value - 1)) != 0) TC.diagnose(attr->getLocation(), diag::alignment_not_power_of_two); } @@ -274,8 +267,7 @@ void AttributeEarlyChecker::visitTransparentAttr(TransparentAttr *attr) { DeclContext *Ctx = D->getDeclContext(); // Protocol declarations cannot be transparent. if (isa(Ctx)) - return diagnoseAndRemoveAttr(attr, - diag::transparent_in_protocols_not_supported); + diagnoseAndRemoveAttr(attr, diag::transparent_in_protocols_not_supported); // Class declarations cannot be transparent. if (isa(Ctx)) { @@ -283,16 +275,14 @@ void AttributeEarlyChecker::visitTransparentAttr(TransparentAttr *attr) { // be dispatched (even in classes) when the references are within the // class themself. if (!(isa(D) && D->isImplicit())) - return diagnoseAndRemoveAttr(attr, - diag::transparent_in_classes_not_supported); + diagnoseAndRemoveAttr(attr, diag::transparent_in_classes_not_supported); } if (auto *VD = dyn_cast(D)) { // Stored properties and variables can't be transparent. if (VD->hasStorage()) - return diagnoseAndRemoveAttr(attr, - diag::attribute_invalid_on_stored_property, - attr); + diagnoseAndRemoveAttr(attr, diag::attribute_invalid_on_stored_property, + attr); } } @@ -315,19 +305,19 @@ void AttributeEarlyChecker::visitMutationAttr(DeclAttribute *attr) { } // mutation attributes may only appear in type context. - auto contextTy = FD->getDeclContext()->getDeclaredInterfaceType(); - if (!contextTy) - return diagnoseAndRemoveAttr(attr, diag::mutating_invalid_global_scope, - unsigned(attrModifier)); - - // 'mutating' and 'nonmutating' are not valid on types - // with reference semantics. - if (contextTy->hasReferenceSemantics()) { - if (attrModifier != SelfAccessKind::__Consuming) - return diagnoseAndRemoveAttr(attr, diag::mutating_invalid_classes, - unsigned(attrModifier)); + if (auto contextTy = FD->getDeclContext()->getDeclaredInterfaceType()) { + // 'mutating' and 'nonmutating' are not valid on types + // with reference semantics. + if (contextTy->hasReferenceSemantics()) { + if (attrModifier != SelfAccessKind::__Consuming) + diagnoseAndRemoveAttr(attr, diag::mutating_invalid_classes, + unsigned(attrModifier)); + } + } else { + diagnoseAndRemoveAttr(attr, diag::mutating_invalid_global_scope, + unsigned(attrModifier)); } - + // Verify we don't have more than one of mutating, nonmutating, // and __consuming. if ((FD->getAttrs().hasAttribute() + @@ -360,22 +350,22 @@ void AttributeEarlyChecker::visitMutationAttr(DeclAttribute *attr) { // Verify that we don't have a static function. if (FD->isStatic()) - return diagnoseAndRemoveAttr(attr, diag::static_functions_not_mutating); + diagnoseAndRemoveAttr(attr, diag::static_functions_not_mutating); } void AttributeEarlyChecker::visitDynamicAttr(DynamicAttr *attr) { // Only instance members of classes can be dynamic. auto classDecl = D->getDeclContext()->getAsClassOrClassExtensionContext(); if (!classDecl) - return diagnoseAndRemoveAttr(attr, diag::dynamic_not_in_class); + diagnoseAndRemoveAttr(attr, diag::dynamic_not_in_class); // Members cannot be both dynamic and final. if (D->getAttrs().hasAttribute()) - return diagnoseAndRemoveAttr(attr, diag::dynamic_with_final); + diagnoseAndRemoveAttr(attr, diag::dynamic_with_final); // Members cannot be both dynamic and @nonobjc. if (D->getAttrs().hasAttribute()) - return diagnoseAndRemoveAttr(attr, diag::dynamic_with_nonobjc); + diagnoseAndRemoveAttr(attr, diag::dynamic_with_nonobjc); } @@ -383,7 +373,7 @@ void AttributeEarlyChecker::visitIBActionAttr(IBActionAttr *attr) { // Only instance methods returning () can be IBActions. const FuncDecl *FD = cast(D); if (!FD->isPotentialIBActionTarget()) - return diagnoseAndRemoveAttr(attr, diag::invalid_ibaction_decl); + diagnoseAndRemoveAttr(attr, diag::invalid_ibaction_decl); } void AttributeEarlyChecker::visitIBDesignableAttr(IBDesignableAttr *attr) { @@ -391,7 +381,7 @@ void AttributeEarlyChecker::visitIBDesignableAttr(IBDesignableAttr *attr) { if (auto extendedType = ED->getExtendedType()) { if (auto *nominalDecl = extendedType->getAnyNominal()) { if (!isa(nominalDecl)) - return diagnoseAndRemoveAttr(attr, diag::invalid_ibdesignable_extension); + diagnoseAndRemoveAttr(attr, diag::invalid_ibdesignable_extension); } } } @@ -402,7 +392,7 @@ void AttributeEarlyChecker::visitIBInspectableAttr(IBInspectableAttr *attr) { auto *VD = cast(D); if (!VD->getDeclContext()->getAsClassOrClassExtensionContext() || VD->isStatic()) - return diagnoseAndRemoveAttr(attr, diag::invalid_ibinspectable, + diagnoseAndRemoveAttr(attr, diag::invalid_ibinspectable, attr->getAttrName()); } @@ -411,7 +401,7 @@ void AttributeEarlyChecker::visitGKInspectableAttr(GKInspectableAttr *attr) { auto *VD = cast(D); if (!VD->getDeclContext()->getAsClassOrClassExtensionContext() || VD->isStatic()) - return diagnoseAndRemoveAttr(attr, diag::invalid_ibinspectable, + diagnoseAndRemoveAttr(attr, diag::invalid_ibinspectable, attr->getAttrName()); } @@ -423,7 +413,7 @@ void AttributeEarlyChecker::visitSILStoredAttr(SILStoredAttr *attr) { ->getAsNominalTypeOrNominalTypeExtensionContext(); if (nominalDecl && isa(nominalDecl)) return; - return diagnoseAndRemoveAttr(attr, diag::invalid_decl_attribute_simple); + diagnoseAndRemoveAttr(attr, diag::invalid_decl_attribute_simple); } static Optional> @@ -473,10 +463,10 @@ void AttributeEarlyChecker::visitIBOutletAttr(IBOutletAttr *attr) { auto *VD = cast(D); if (!VD->getDeclContext()->getAsClassOrClassExtensionContext() || VD->isStatic()) - return diagnoseAndRemoveAttr(attr, diag::invalid_iboutlet); + diagnoseAndRemoveAttr(attr, diag::invalid_iboutlet); if (!VD->isSettable(nullptr)) - return diagnoseAndRemoveAttr(attr, diag::iboutlet_only_mutable); + diagnoseAndRemoveAttr(attr, diag::iboutlet_only_mutable); // Verify that the field type is valid as an outlet. auto type = VD->getType(); @@ -494,21 +484,33 @@ void AttributeEarlyChecker::visitIBOutletAttr(IBOutletAttr *attr) { bool isArray = false; if (auto isError = isAcceptableOutletType(type, isArray, TC)) - return diagnoseAndRemoveAttr(attr, isError.getValue(), + diagnoseAndRemoveAttr(attr, isError.getValue(), /*array=*/isArray, type); // If the type wasn't optional, an array, or unowned, complain. if (!wasOptional && !isArray) { - auto symbolLoc = Lexer::getLocForEndOfToken( - TC.Context.SourceMgr, - VD->getTypeSourceRangeForDiagnostics().End); - TC.diagnose(attr->getLocation(), diag::iboutlet_non_optional, - type); - TC.diagnose(symbolLoc, diag::note_make_optional, - OptionalType::get(type)) - .fixItInsert(symbolLoc, "?"); - TC.diagnose(symbolLoc, diag::note_make_implicitly_unwrapped_optional) - .fixItInsert(symbolLoc, "!"); + TC.diagnose(attr->getLocation(), diag::iboutlet_non_optional, type); + auto typeRange = VD->getTypeSourceRangeForDiagnostics(); + { // Only one diagnostic can be active at a time. + auto diag = TC.diagnose(typeRange.Start, diag::note_make_optional, + OptionalType::get(type)); + if (type->hasSimpleTypeRepr()) { + diag.fixItInsertAfter(typeRange.End, "?"); + } else { + diag.fixItInsert(typeRange.Start, "(") + .fixItInsertAfter(typeRange.End, ")?"); + } + } + { // Only one diagnostic can be active at a time. + auto diag = TC.diagnose(typeRange.Start, + diag::note_make_implicitly_unwrapped_optional); + if (type->hasSimpleTypeRepr()) { + diag.fixItInsertAfter(typeRange.End, "!"); + } else { + diag.fixItInsert(typeRange.Start, "(") + .fixItInsertAfter(typeRange.End, ")!"); + } + } } } @@ -516,14 +518,13 @@ void AttributeEarlyChecker::visitNSManagedAttr(NSManagedAttr *attr) { // @NSManaged only applies to instance methods and properties within a class. if (cast(D)->isStatic() || !D->getDeclContext()->getAsClassOrClassExtensionContext()) { - return diagnoseAndRemoveAttr(attr, - diag::attr_NSManaged_not_instance_member); + diagnoseAndRemoveAttr(attr, diag::attr_NSManaged_not_instance_member); } if (auto *method = dyn_cast(D)) { // Separate out the checks for methods. if (method->hasBody()) - return diagnoseAndRemoveAttr(attr, diag::attr_NSManaged_method_body); + diagnoseAndRemoveAttr(attr, diag::attr_NSManaged_method_body); return; } @@ -532,7 +533,7 @@ void AttributeEarlyChecker::visitNSManagedAttr(NSManagedAttr *attr) { auto *VD = cast(D); if (VD->isLet()) - return diagnoseAndRemoveAttr(attr, diag::attr_NSManaged_let_property); + diagnoseAndRemoveAttr(attr, diag::attr_NSManaged_let_property); auto diagnoseNotStored = [&](unsigned kind) { TC.diagnose(attr->getLocation(), diag::attr_NSManaged_not_stored, kind); @@ -570,7 +571,7 @@ void AttributeEarlyChecker::visitNSManagedAttr(NSManagedAttr *attr) { // @NSManaged properties cannot be @NSCopying if (auto *NSCopy = VD->getAttrs().getAttribute()) - return diagnoseAndRemoveAttr(NSCopy, diag::attr_NSManaged_NSCopying); + diagnoseAndRemoveAttr(NSCopy, diag::attr_NSManaged_NSCopying); } @@ -578,13 +579,13 @@ void AttributeEarlyChecker:: visitLLDBDebuggerFunctionAttr(LLDBDebuggerFunctionAttr *attr) { // This is only legal when debugger support is on. if (!D->getASTContext().LangOpts.DebuggerSupport) - return diagnoseAndRemoveAttr(attr, diag::attr_for_debugger_support_only); + diagnoseAndRemoveAttr(attr, diag::attr_for_debugger_support_only); } void AttributeEarlyChecker::visitOverrideAttr(OverrideAttr *attr) { if (!isa(D->getDeclContext()) && !isa(D->getDeclContext())) - return diagnoseAndRemoveAttr(attr, diag::override_nonclass_decl); + diagnoseAndRemoveAttr(attr, diag::override_nonclass_decl); } void AttributeEarlyChecker::visitLazyAttr(LazyAttr *attr) { @@ -594,35 +595,38 @@ void AttributeEarlyChecker::visitLazyAttr(LazyAttr *attr) { // It cannot currently be used on let's since we don't have a mutability model // that supports it. if (VD->isLet()) - return diagnoseAndRemoveAttr(attr, diag::lazy_not_on_let); + diagnoseAndRemoveAttr(attr, diag::lazy_not_on_let); + + auto attrs = VD->getAttrs(); + // 'lazy' is not allowed to have reference attributes + if (auto *refAttr = attrs.getAttribute()) + diagnoseAndRemoveAttr(attr, diag::lazy_not_strong, refAttr->get()); // lazy is not allowed on a protocol requirement. auto varDC = VD->getDeclContext(); if (isa(varDC)) - return diagnoseAndRemoveAttr(attr, diag::lazy_not_in_protocol); + diagnoseAndRemoveAttr(attr, diag::lazy_not_in_protocol); - // It only works with stored properties. - if (!VD->hasStorage()) - return diagnoseAndRemoveAttr(attr, diag::lazy_not_on_computed); - // lazy is not allowed on a lazily initialized global variable or on a - // static property (which is already lazily initialized). + // 'lazy' is not allowed on a global variable or on a static property (which + // are already lazily initialized). + // TODO: we can't currently support lazy properties on non-type-contexts. if (VD->isStatic() || (varDC->isModuleScopeContext() && - !varDC->getParentSourceFile()->isScriptMode())) - return diagnoseAndRemoveAttr(attr, diag::lazy_on_already_lazy_global); + !varDC->getParentSourceFile()->isScriptMode())) { + diagnoseAndRemoveAttr(attr, diag::lazy_on_already_lazy_global); + } else if (!VD->getDeclContext()->isTypeContext()) { + diagnoseAndRemoveAttr(attr, diag::lazy_must_be_property); + } // lazy must have an initializer, and the pattern binding must be a simple // one. if (!VD->getParentInitializer()) - return diagnoseAndRemoveAttr(attr, diag::lazy_requires_initializer); + diagnoseAndRemoveAttr(attr, diag::lazy_requires_initializer); if (!VD->getParentPatternBinding()->getSingleVar()) - return diagnoseAndRemoveAttr(attr, diag::lazy_requires_single_var); + diagnoseAndRemoveAttr(attr, diag::lazy_requires_single_var); - // TODO: we can't currently support lazy properties on non-type-contexts. - if (!VD->getDeclContext()->isTypeContext()) - return diagnoseAndRemoveAttr(attr, diag::lazy_must_be_property); // TODO: Lazy properties can't yet be observed. switch (VD->getStorageKind()) { @@ -631,7 +635,8 @@ void AttributeEarlyChecker::visitLazyAttr(LazyAttr *attr) { break; case AbstractStorageDecl::StoredWithObservers: - return diagnoseAndRemoveAttr(attr, diag::lazy_not_observable); + diagnoseAndRemoveAttr(attr, diag::lazy_not_observable); + break; case AbstractStorageDecl::InheritedWithObservers: case AbstractStorageDecl::ComputedWithMutableAddress: @@ -639,7 +644,9 @@ void AttributeEarlyChecker::visitLazyAttr(LazyAttr *attr) { case AbstractStorageDecl::Addressed: case AbstractStorageDecl::AddressedWithTrivialAccessors: case AbstractStorageDecl::AddressedWithObservers: - llvm_unreachable("non-stored variable not filtered out?"); + assert(!VD->hasStorage() && "Non-stored AbstractStorageDecl has storage?"); + diagnoseAndRemoveAttr(attr, diag::lazy_not_on_computed); + break; } } @@ -683,8 +690,7 @@ void AttributeEarlyChecker::visitSetterAccessAttr( SetterAccessAttr *attr) { auto storage = dyn_cast(D); if (!storage) - return diagnoseAndRemoveAttr(attr, diag::access_control_setter, - attr->getAccess()); + diagnoseAndRemoveAttr(attr, diag::access_control_setter, attr->getAccess()); if (visitAbstractAccessControlAttr(attr)) return; @@ -705,14 +711,14 @@ void AttributeEarlyChecker::visitSetterAccessAttr( storageKind = SK_Constant; else storageKind = SK_Variable; - return diagnoseAndRemoveAttr(attr, diag::access_control_setter_read_only, - attr->getAccess(), storageKind); + diagnoseAndRemoveAttr(attr, diag::access_control_setter_read_only, + attr->getAccess(), storageKind); } } void AttributeEarlyChecker::visitObjCMembersAttr(ObjCMembersAttr *attr) { if (!isa(D)) - return diagnoseAndRemoveAttr(attr, diag::objcmembers_attribute_nonclass); + diagnoseAndRemoveAttr(attr, diag::objcmembers_attribute_nonclass); } void TypeChecker::checkDeclAttributesEarly(Decl *D) { @@ -738,35 +744,26 @@ void TypeChecker::checkDeclAttributesEarly(Decl *D) { // Otherwise, this attribute cannot be applied to this declaration. If the // attribute is only valid on one kind of declaration (which is pretty // common) give a specific helpful error. - unsigned PossibleDeclKinds = attr->getOptions() & DeclAttribute::OnAnyDecl; + auto PossibleDeclKinds = attr->getOptions() & DeclAttribute::OnAnyDecl; StringRef OnlyKind; switch (PossibleDeclKinds) { - case DeclAttribute::OnImport: - OnlyKind = "import"; - break; - case DeclAttribute::OnVar: - OnlyKind = "var"; - break; - case DeclAttribute::OnFunc: - OnlyKind = "func"; - break; - case DeclAttribute::OnClass: - OnlyKind = "class"; - break; - case DeclAttribute::OnStruct: - OnlyKind = "struct"; - break; - case DeclAttribute::OnConstructor: - OnlyKind = "init"; - break; - case DeclAttribute::OnProtocol: - OnlyKind = "protocol"; - break; - case DeclAttribute::OnParam: - OnlyKind = "parameter"; - break; - default: - break; + case DeclAttribute::OnAccessor: OnlyKind = "accessor"; break; + case DeclAttribute::OnClass: OnlyKind = "class"; break; + case DeclAttribute::OnConstructor: OnlyKind = "init"; break; + case DeclAttribute::OnDestructor: OnlyKind = "deinit"; break; + case DeclAttribute::OnEnum: OnlyKind = "enum"; break; + case DeclAttribute::OnEnumCase: OnlyKind = "case"; break; + case DeclAttribute::OnFunc | DeclAttribute::OnAccessor: // FIXME + case DeclAttribute::OnFunc: OnlyKind = "func"; break; + case DeclAttribute::OnImport: OnlyKind = "import"; break; + case DeclAttribute::OnModule: OnlyKind = "module"; break; + case DeclAttribute::OnParam: OnlyKind = "parameter"; break; + case DeclAttribute::OnProtocol: OnlyKind = "protocol"; break; + case DeclAttribute::OnStruct: OnlyKind = "struct"; break; + case DeclAttribute::OnSubscript: OnlyKind = "subscript"; break; + case DeclAttribute::OnTypeAlias: OnlyKind = "typealias"; break; + case DeclAttribute::OnVar: OnlyKind = "var"; break; + default: break; } if (!OnlyKind.empty()) @@ -988,11 +985,11 @@ visitDynamicMemberLookupAttr(DynamicMemberLookupAttr *attr) { // If none of the ones we find are acceptable, then reject one. auto oneCandidate = candidates.front(); - candidates.filter([&](LookupResultEntry entry)->bool { + candidates.filter([&](LookupResultEntry entry, bool isOuter) -> bool { auto cand = cast(entry.getValueDecl()); return isAcceptableDynamicMemberLookupSubscript(cand, decl, TC); }); - + if (candidates.empty()) { TC.diagnose(oneCandidate.getValueDecl()->getLoc(), diag::type_invalid_dml, type); @@ -1574,7 +1571,7 @@ AttributeChecker::visitSetterAccessAttr(SetterAccessAttr *attr) { /// Collect all used generic parameter types from a given type. static void collectUsedGenericParameters( - Type Ty, SmallPtrSet &ConstrainedGenericParams) { + Type Ty, SmallPtrSetImpl &ConstrainedGenericParams) { if (!Ty) return; @@ -1946,7 +1943,7 @@ void AttributeChecker::visitFixedLayoutAttr(FixedLayoutAttr *attr) { auto *VD = cast(D); auto access = VD->getFormalAccess(/*useDC=*/nullptr, - /*isUsageFromInline=*/true); + /*treatUsableFromInlineAsPublic=*/true); if (access < AccessLevel::Public) { diagnoseAndRemoveAttr(attr, diag::fixed_layout_attr_on_internal_type, VD->getFullName(), access); @@ -1959,13 +1956,14 @@ void AttributeChecker::visitUsableFromInlineAttr(UsableFromInlineAttr *attr) { // FIXME: Once protocols can contain nominal types, do we want to allow // these nominal types to have access control (and also @usableFromInline)? if (isa(VD->getDeclContext())) { - diagnoseAndRemoveAttr(attr, diag::versioned_attr_in_protocol); + diagnoseAndRemoveAttr(attr, diag::usable_from_inline_attr_in_protocol); return; } // @usableFromInline can only be applied to internal declarations. if (VD->getFormalAccess() != AccessLevel::Internal) { - diagnoseAndRemoveAttr(attr, diag::versioned_attr_with_explicit_access, + diagnoseAndRemoveAttr(attr, + diag::usable_from_inline_attr_with_explicit_access, VD->getFullName(), VD->getFormalAccess()); return; @@ -1974,7 +1972,16 @@ void AttributeChecker::visitUsableFromInlineAttr(UsableFromInlineAttr *attr) { // Symbols of dynamically-dispatched declarations are never referenced // directly, so marking them as @usableFromInline does not make sense. if (VD->isDynamic()) { - diagnoseAndRemoveAttr(attr, diag::versioned_dynamic_not_supported); + diagnoseAndRemoveAttr(attr, diag::usable_from_inline_dynamic_not_supported); + return; + } + + // On internal declarations, @inlinable implies @usableFromInline. + if (VD->getAttrs().hasAttribute()) { + if (attr->isImplicit()) + attr->setInvalid(); + else + diagnoseAndRemoveAttr(attr, diag::inlinable_implies_usable_from_inline); return; } } @@ -2003,11 +2010,9 @@ void AttributeChecker::visitInlinableAttr(InlinableAttr *attr) { return; } - // @inlinable can only be applied to public or @usableFromInline - // declarations. - auto access = VD->getFormalAccess(/*useDC=*/nullptr, - /*isUsageFromInline=*/true); - if (access < AccessLevel::Public) { + // @inlinable can only be applied to public or internal declarations. + auto access = VD->getFormalAccess(); + if (access < AccessLevel::Internal) { diagnoseAndRemoveAttr(attr, diag::inlinable_decl_not_public, VD->getBaseName(), access); @@ -2096,7 +2101,7 @@ void AttributeChecker::visitFrozenAttr(FrozenAttr *attr) { } auto access = ED->getFormalAccess(/*useDC=*/nullptr, - /*isUsageFromInline=*/true); + /*treatUsableFromInlineAsPublic=*/true); if (access < AccessLevel::Public) { diagnoseAndRemoveAttr(attr, diag::enum_frozen_nonpublic, attr); } @@ -2135,7 +2140,9 @@ void TypeChecker::checkReferenceOwnershipAttr(VarDecl *var, auto ownershipKind = attr->get(); // A weak variable must have type R? or R! for some ownership-capable type R. - Type underlyingType = type; + auto underlyingType = type->getOptionalObjectType(); + auto isOptional = bool(underlyingType); + auto allowOptional = false; switch (ownershipKind) { case ReferenceOwnership::Strong: llvm_unreachable("Cannot specify 'strong' in an ownership attribute"); @@ -2143,24 +2150,45 @@ void TypeChecker::checkReferenceOwnershipAttr(VarDecl *var, case ReferenceOwnership::Unmanaged: break; case ReferenceOwnership::Weak: + allowOptional = true; if (var->isLet()) { diagnose(var->getStartLoc(), diag::invalid_weak_let); attr->setInvalid(); } - if (Type objType = type->getOptionalObjectType()) { - underlyingType = objType; + if (isOptional) + break; + + attr->setInvalid(); + + // While @IBOutlet can be weak, it must be optional. Let it diagnose. + if (var->getAttrs().hasAttribute()) + break; + + auto diag = diagnose(var->getStartLoc(), + diag::invalid_weak_ownership_not_optional, + OptionalType::get(type)); + auto typeRange = var->getTypeSourceRangeForDiagnostics(); + if (type->hasSimpleTypeRepr()) { + diag.fixItInsertAfter(typeRange.End, "?"); } else { - // @IBOutlet must be optional, but not necessarily weak. Let it diagnose. - if (!var->getAttrs().hasAttribute()) { - diagnose(var->getStartLoc(), diag::invalid_weak_ownership_not_optional, - OptionalType::get(type)); - } - attr->setInvalid(); + diag.fixItInsert(typeRange.Start, "(") + .fixItInsertAfter(typeRange.End, ")?"); } break; } + + if (!allowOptional && isOptional) { + diagnose(var->getStartLoc(), diag::invalid_ownership_with_optional, + ownershipKind) + .fixItReplace(attr->getRange(), "weak"); + attr->setInvalid(); + } + + if (!underlyingType) + underlyingType = type; + if (!underlyingType->allowsOwnership()) { auto D = diag::invalid_ownership_type; @@ -2170,23 +2198,19 @@ void TypeChecker::checkReferenceOwnershipAttr(VarDecl *var, D = diag::invalid_ownership_protocol_type; } - diagnose(var->getStartLoc(), D, (unsigned) ownershipKind, underlyingType); + diagnose(var->getStartLoc(), D, ownershipKind, underlyingType); attr->setInvalid(); - } else if (isa(var->getDeclContext()) && - !cast(var->getDeclContext())->isObjC()) { + } + + auto PDC = dyn_cast((var->getDeclContext())); + if (PDC && !PDC->isObjC()) { // Ownership does not make sense in protocols, except for "weak" on // properties of Objective-C protocols. - if (Context.isSwiftVersionAtLeast(5)) - diagnose(attr->getLocation(), - diag::ownership_invalid_in_protocols, - (unsigned) ownershipKind) - .fixItRemove(attr->getRange()); - else - diagnose(attr->getLocation(), - diag::ownership_invalid_in_protocols_compat_warning, - (unsigned) ownershipKind) - .fixItRemove(attr->getRange()); - + auto D = Context.isSwiftVersionAtLeast(5) + ? diag::ownership_invalid_in_protocols + : diag::ownership_invalid_in_protocols_compat_warning; + diagnose(attr->getLocation(), D, ownershipKind) + .fixItRemove(attr->getRange()); attr->setInvalid(); } diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index 6bbe34de8f0d4..9abe9d4e4cbe2 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -736,7 +736,7 @@ class InnermostAncestorFinder : private ASTWalker { /// The type of a match predicate, which takes as input a node and its /// parent and returns a bool indicating whether the node matches. - typedef std::function MatchPredicate; + using MatchPredicate = std::function; private: const SourceRange TargetRange; @@ -1245,8 +1245,9 @@ static void fixAvailabilityByAddingVersionCheck( return; SourceLoc ReplaceLocStart = RangeToWrap.Start; - StringRef OriginalIndent = - Lexer::getIndentationForLine(TC.Context.SourceMgr, ReplaceLocStart); + StringRef ExtraIndent; + StringRef OriginalIndent = Lexer::getIndentationForLine( + TC.Context.SourceMgr, ReplaceLocStart, &ExtraIndent); std::string IfText; { @@ -1260,16 +1261,15 @@ static void fixAvailabilityByAddingVersionCheck( ReplaceLocStart, ReplaceLocEnd)).str(); - // We'll indent with 4 spaces - std::string ExtraIndent = " "; std::string NewLine = "\n"; + std::string NewLineReplacement = (NewLine + ExtraIndent).str(); // Indent the body of the Fix-It if. Because the body may be a compound // statement, we may have to indent multiple lines. size_t StartAt = 0; while ((StartAt = GuardedText.find(NewLine, StartAt)) != std::string::npos) { - GuardedText.replace(StartAt, NewLine.length(), NewLine + ExtraIndent); + GuardedText.replace(StartAt, NewLine.length(), NewLineReplacement); StartAt += NewLine.length(); } @@ -1534,6 +1534,9 @@ static void fixItAvailableAttrRename(TypeChecker &TC, const ValueDecl *renamedDecl, const AvailableAttr *attr, const ApplyExpr *call) { + if (isa(renamedDecl)) + return; + ParsedDeclName parsed = swift::parseDeclName(attr->Rename); if (!parsed) return; @@ -1906,6 +1909,26 @@ describeRename(ASTContext &ctx, const AvailableAttr *attr, const ValueDecl *D, return ReplacementDeclKind::None; } +/// Returns a value that can be used to select between accessor kinds in +/// diagnostics. +/// +/// This is correlated with diag::availability_deprecated and others. +static std::pair +getAccessorKindAndNameForDiagnostics(const ValueDecl *D) { + // This should always be one more than the last AccessorKind supported in + // the diagnostics. If you need to change it, change the assertion below as + // well. + static const unsigned NOT_ACCESSOR_INDEX = 2; + + if (auto *accessor = dyn_cast(D)) { + DeclName Name = accessor->getStorage()->getFullName(); + assert(accessor->isGetterOrSetter()); + return {static_cast(accessor->getAccessorKind()), Name}; + } + + return {NOT_ACCESSOR_INDEX, D->getFullName()}; +} + void TypeChecker::diagnoseIfDeprecated(SourceRange ReferenceRange, const DeclContext *ReferenceDC, const ValueDecl *DeprecatedDecl, @@ -1933,26 +1956,19 @@ void TypeChecker::diagnoseIfDeprecated(SourceRange ReferenceRange, } DeclName Name; - Optional rawAccessorKind; - if (auto *accessor = dyn_cast(DeprecatedDecl)) { - Name = accessor->getStorage()->getFullName(); - assert(accessor->isGetterOrSetter()); - rawAccessorKind = static_cast(accessor->getAccessorKind()); - } else { - Name = DeprecatedDecl->getFullName(); - } + unsigned RawAccessorKind; + std::tie(RawAccessorKind, Name) = + getAccessorKindAndNameForDiagnostics(DeprecatedDecl); StringRef Platform = Attr->prettyPlatformString(); clang::VersionTuple DeprecatedVersion; if (Attr->Deprecated) DeprecatedVersion = Attr->Deprecated.getValue(); - static const unsigned NOT_ACCESSOR_INDEX = 2; if (Attr->Message.empty() && Attr->Rename.empty()) { diagnose(ReferenceRange.Start, diag::availability_deprecated, - rawAccessorKind.getValueOr(NOT_ACCESSOR_INDEX), Name, - Attr->hasPlatform(), Platform, Attr->Deprecated.hasValue(), - DeprecatedVersion) + RawAccessorKind, Name, Attr->hasPlatform(), Platform, + Attr->Deprecated.hasValue(), DeprecatedVersion) .highlight(Attr->getRange()); return; } @@ -1965,22 +1981,21 @@ void TypeChecker::diagnoseIfDeprecated(SourceRange ReferenceRange, if (!Attr->Message.empty()) { EncodedDiagnosticMessage EncodedMessage(Attr->Message); diagnose(ReferenceRange.Start, diag::availability_deprecated_msg, - rawAccessorKind.getValueOr(NOT_ACCESSOR_INDEX), Name, - Attr->hasPlatform(), Platform, Attr->Deprecated.hasValue(), - DeprecatedVersion, EncodedMessage.Message) + RawAccessorKind, Name, Attr->hasPlatform(), Platform, + Attr->Deprecated.hasValue(), DeprecatedVersion, + EncodedMessage.Message) .highlight(Attr->getRange()); } else { unsigned rawReplaceKind = static_cast( replacementDeclKind.getValueOr(ReplacementDeclKind::None)); diagnose(ReferenceRange.Start, diag::availability_deprecated_rename, - rawAccessorKind.getValueOr(NOT_ACCESSOR_INDEX), Name, - Attr->hasPlatform(), Platform, Attr->Deprecated.hasValue(), - DeprecatedVersion, replacementDeclKind.hasValue(), rawReplaceKind, - newName) + RawAccessorKind, Name, Attr->hasPlatform(), Platform, + Attr->Deprecated.hasValue(), DeprecatedVersion, + replacementDeclKind.hasValue(), rawReplaceKind, newName) .highlight(Attr->getRange()); } - if (!Attr->Rename.empty() && !rawAccessorKind.hasValue()) { + if (!Attr->Rename.empty() && !isa(DeprecatedDecl)) { auto renameDiag = diagnose(ReferenceRange.Start, diag::note_deprecated_rename, newName); @@ -1999,8 +2014,13 @@ void TypeChecker::diagnoseUnavailableOverride(ValueDecl *override, else diagnose(override, diag::override_unavailable_msg, override->getBaseName(), attr->Message); + + DeclName name; + unsigned rawAccessorKind; + std::tie(rawAccessorKind, name) = + getAccessorKindAndNameForDiagnostics(base); diagnose(base, diag::availability_marked_unavailable, - base->getFullName()); + rawAccessorKind, name); return; } @@ -2074,7 +2094,15 @@ bool isSubscriptReturningString(const ValueDecl *D, ASTContext &Context) { // We're only going to warn for BoundGenericStructType with a single // type argument that is not Int! - auto inputTy = fnTy->getInput()->getAs(); + auto params = fnTy->getParams(); + if (params.size() != 1) + return false; + + const auto ¶m = params.front(); + if (param.hasLabel() || param.isVariadic()) + return false; + + auto inputTy = param.getType()->getAs(); if (!inputTy) return false; @@ -2127,7 +2155,9 @@ bool TypeChecker::diagnoseExplicitUnavailability( } SourceLoc Loc = R.Start; - auto Name = D->getFullName(); + DeclName Name; + unsigned RawAccessorKind; + std::tie(RawAccessorKind, Name) = getAccessorKindAndNameForDiagnostics(D); switch (Attr->getPlatformAgnosticAvailability()) { case PlatformAgnosticAvailabilityKind::Deprecated: @@ -2150,14 +2180,14 @@ bool TypeChecker::diagnoseExplicitUnavailability( if (Attr->Message.empty()) { auto diag = diagnose(Loc, diag::availability_decl_unavailable_rename, - Name, replaceKind.hasValue(), rawReplaceKind, - newName); + RawAccessorKind, Name, replaceKind.hasValue(), + rawReplaceKind, newName); attachRenameFixIts(diag); } else { EncodedDiagnosticMessage EncodedMessage(Attr->Message); auto diag = diagnose(Loc, diag::availability_decl_unavailable_rename_msg, - Name, replaceKind.hasValue(), rawReplaceKind, - newName, EncodedMessage.Message); + RawAccessorKind, Name, replaceKind.hasValue(), + rawReplaceKind, newName, EncodedMessage.Message); attachRenameFixIts(diag); } } else if (isSubscriptReturningString(D, Context)) { @@ -2171,12 +2201,13 @@ bool TypeChecker::diagnoseExplicitUnavailability( } else if (Attr->Message.empty()) { diagnose(Loc, inSwift ? diag::availability_decl_unavailable_in_swift : diag::availability_decl_unavailable, - Name).highlight(R); + RawAccessorKind, Name) + .highlight(R); } else { EncodedDiagnosticMessage EncodedMessage(Attr->Message); diagnose(Loc, inSwift ? diag::availability_decl_unavailable_in_swift_msg : diag::availability_decl_unavailable_msg, - Name, EncodedMessage.Message) + RawAccessorKind, Name, EncodedMessage.Message) .highlight(R); } break; @@ -2191,20 +2222,23 @@ bool TypeChecker::diagnoseExplicitUnavailability( case AvailableVersionComparison::Unavailable: if (Attr->isLanguageVersionSpecific() && Attr->Introduced.hasValue()) - diagnose(D, diag::availability_introduced_in_swift, Name, - *Attr->Introduced).highlight(Attr->getRange()); + diagnose(D, diag::availability_introduced_in_swift, + RawAccessorKind, Name, *Attr->Introduced) + .highlight(Attr->getRange()); else - diagnose(D, diag::availability_marked_unavailable, Name) + diagnose(D, diag::availability_marked_unavailable, RawAccessorKind, Name) .highlight(Attr->getRange()); break; case AvailableVersionComparison::Obsoleted: // FIXME: Use of the platformString here is non-awesome for application // extensions. - diagnose(D, diag::availability_obsoleted, Name, + diagnose(D, diag::availability_obsoleted, + RawAccessorKind, Name, (Attr->isLanguageVersionSpecific() ? "Swift" : Attr->prettyPlatformString()), - *Attr->Obsoleted).highlight(Attr->getRange()); + *Attr->Obsoleted) + .highlight(Attr->getRange()); break; } return true; @@ -2233,10 +2267,18 @@ class AvailabilityWalker : public ASTWalker { DeclContext *DC; MemberAccessContext AccessContext = MemberAccessContext::Getter; SmallVector ExprStack; + ResilienceExpansion Expansion; + Optional FragileKind; + bool TreatUsableFromInlineAsPublic = false; public: AvailabilityWalker( - TypeChecker &TC, DeclContext *DC) : TC(TC), DC(DC) {} + TypeChecker &TC, DeclContext *DC) : TC(TC), DC(DC) { + Expansion = DC->getResilienceExpansion(); + if (Expansion == ResilienceExpansion::Minimal) + std::tie(FragileKind, TreatUsableFromInlineAsPublic) + = TC.getFragileFunctionKind(DC); + } std::pair walkToExprPre(Expr *E) override { ExprStack.push_back(E); @@ -2425,8 +2467,18 @@ class AvailabilityWalker : public ASTWalker { return; } - // Make sure not to diagnose an accessor if we already complained about - // the property/subscript. + // If the property/subscript is unconditionally unavailable, don't bother + // with any of the rest of this. + if (AvailableAttr::isUnavailable(D->getStorage())) + return; + + if (TC.diagnoseExplicitUnavailability(D, ReferenceRange, ReferenceDC, + /*call*/nullptr)) { + return; + } + + // Make sure not to diagnose an accessor's deprecation if we already + // complained about the property/subscript. if (!TypeChecker::getDeprecated(D->getStorage())) TC.diagnoseIfDeprecated(ReferenceRange, ReferenceDC, D, /*call*/nullptr); @@ -2457,9 +2509,11 @@ bool AvailabilityWalker::diagAvailability(const ValueDecl *D, SourceRange R, return true; } - if (R.isValid()) - if (TC.diagnoseInlinableDeclRef(R.Start, D, DC)) - return true; + if (FragileKind) + if (R.isValid()) + if (TC.diagnoseInlinableDeclRef(R.Start, D, DC, *FragileKind, + TreatUsableFromInlineAsPublic)) + return true; if (TC.diagnoseExplicitUnavailability(D, R, DC, call)) return true; @@ -2539,9 +2593,14 @@ bool AvailabilityWalker::diagnoseIncDecRemoval(const ValueDecl *D, } if (!replacement.empty()) { + DeclName Name; + unsigned RawAccessorKind; + std::tie(RawAccessorKind, Name) = getAccessorKindAndNameForDiagnostics(D); + // If we emit a deprecation diagnostic, produce a fixit hint as well. auto diag = TC.diagnose(R.Start, diag::availability_decl_unavailable_msg, - D->getFullName(), "it has been removed in Swift 3"); + RawAccessorKind, Name, + "it has been removed in Swift 3"); if (isa(call)) { // Prefix: remove the ++ or --. diag.fixItRemove(call->getFn()->getSourceRange()); @@ -2584,9 +2643,13 @@ bool AvailabilityWalker::diagnoseMemoryLayoutMigration(const ValueDecl *D, if (!args) return false; + DeclName Name; + unsigned RawAccessorKind; + std::tie(RawAccessorKind, Name) = getAccessorKindAndNameForDiagnostics(D); + EncodedDiagnosticMessage EncodedMessage(Attr->Message); auto diag = TC.diagnose(R.Start, diag::availability_decl_unavailable_msg, - D->getFullName(), EncodedMessage.Message); + RawAccessorKind, Name, EncodedMessage.Message); diag.highlight(R); auto subject = args->getSubExpr(); diff --git a/lib/Sema/TypeCheckCaptures.cpp b/lib/Sema/TypeCheckCaptures.cpp index 3a8bcefe40878..20df65a2d5272 100644 --- a/lib/Sema/TypeCheckCaptures.cpp +++ b/lib/Sema/TypeCheckCaptures.cpp @@ -20,6 +20,7 @@ #include "swift/AST/ASTWalker.h" #include "swift/AST/Decl.h" #include "swift/AST/ForeignErrorConvention.h" +#include "swift/AST/GenericSignature.h" #include "swift/AST/ParameterList.h" #include "swift/AST/TypeWalker.h" #include "swift/Basic/Defer.h" @@ -162,7 +163,9 @@ class FindCapturedVars : public ASTWalker { } }); - gft->getInput().walk(walker); + for (const auto ¶m : gft->getParams()) + param.getType().walk(walker); + gft->getResult().walk(walker); } } @@ -232,8 +235,13 @@ class FindCapturedVars : public ASTWalker { // parameter references transitively. if (!D->getDeclContext()->isLocalContext()) { if (!AFR.isObjC() || !D->isObjC() || isa(D)) { - for (auto sub : DRE->getDeclRef().getSubstitutions()) { - checkType(sub.getReplacement(), DRE->getLoc()); + if (auto subMap = DRE->getDeclRef().getSubstitutions()) { + auto genericSig = subMap.getGenericSignature(); + for (auto gp : genericSig->getGenericParams()) { + if (auto type = Type(gp).subst(subMap)) { + checkType(type, DRE->getLoc()); + } + } } } } @@ -263,7 +271,8 @@ class FindCapturedVars : public ASTWalker { NTD->getDescriptiveKind(), D->getBaseName().getIdentifier()); - TC.diagnose(NTD->getLoc(), diag::type_declared_here); + TC.diagnose(NTD->getLoc(), diag::kind_declared_here, + DescriptiveDeclKind::Type); TC.diagnose(D, diag::decl_declared_here, D->getFullName()); return { false, DRE }; diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 18742bb18681d..9b51a891f8320 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -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 - 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 @@ -19,6 +19,7 @@ #include "ConstraintSystem.h" #include "GenericTypeResolver.h" #include "TypeChecker.h" +#include "TypoCorrection.h" #include "MiscDiagnostics.h" #include "swift/AST/ASTVisitor.h" #include "swift/AST/ASTWalker.h" @@ -218,12 +219,18 @@ static unsigned getNumArgs(ValueDecl *value) { AnyFunctionType *fnTy = value->getInterfaceType()->castTo(); if (value->getDeclContext()->isTypeContext()) fnTy = fnTy->getResult()->castTo(); - Type argTy = fnTy->getInput(); - if (auto tuple = argTy->getAs()) { - return tuple->getNumElements(); - } else { - return 1; + + auto params = fnTy->getParams(); + if (params.size() == 1) { + const auto ¶m = params.front(); + if (param.hasLabel() || param.isVariadic()) + return 1; + + if (auto *tuple = param.getType()->getAs()) + return tuple->getNumElements(); } + + return params.size(); } static bool matchesDeclRefKind(ValueDecl *value, DeclRefKind refKind) { @@ -396,6 +403,56 @@ static bool diagnoseOperatorJuxtaposition(UnresolvedDeclRefExpr *UDRE, } } +static bool findNonMembers(TypeChecker &TC, + ArrayRef lookupResults, + DeclRefKind refKind, bool breakOnMember, + SmallVectorImpl &ResultValues, + llvm::function_ref isValid) { + bool AllDeclRefs = true; + for (auto Result : lookupResults) { + // If we find a member, then all of the results aren't non-members. + bool IsMember = + (Result.getBaseDecl() && !isa(Result.getBaseDecl())); + if (IsMember) { + AllDeclRefs = false; + if (breakOnMember) + break; + continue; + } + + ValueDecl *D = Result.getValueDecl(); + if (!D->hasInterfaceType()) + TC.validateDecl(D); + + // FIXME: Circularity hack. + if (!D->hasInterfaceType()) { + AllDeclRefs = false; + continue; + } + + if (!isValid(D)) + return false; + + if (matchesDeclRefKind(D, refKind)) + ResultValues.push_back(D); + } + + return AllDeclRefs; +} + +/// Whether we should be looking at the outer results for a function called \c +/// name. +/// +/// This is very restrictive because it's a source compatibility issue (see the +/// if (AllConditionalConformances) { (void)findNonMembers(...); } below). +static bool shouldConsiderOuterResultsFor(DeclName name) { + const StringRef specialNames[] = {"min", "max"}; + for (auto specialName : specialNames) + if (name.isSimpleName(specialName)) + return true; + + return false; +} /// Bind an UnresolvedDeclRefExpr by performing name lookup and /// returning the resultant expression. Context is the DeclContext used @@ -410,6 +467,9 @@ resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, DeclContext *DC) { NameLookupOptions lookupOptions = defaultUnqualifiedLookupOptions; if (isa(DC)) lookupOptions |= NameLookupFlags::KnownPrivate; + if (shouldConsiderOuterResultsFor(Name)) + lookupOptions |= NameLookupFlags::IncludeOuterResults; + auto Lookup = lookupUnqualified(DC, Name, Loc, lookupOptions); if (!Lookup) { @@ -429,8 +489,9 @@ resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, DeclContext *DC) { if (inaccessibleResults) { // FIXME: What if the unviable candidates have different levels of access? const ValueDecl *first = inaccessibleResults.front().getValueDecl(); - diagnose(Loc, diag::candidate_inaccessible, - Name, first->getFormalAccess()); + diagnose(Loc, diag::candidate_inaccessible, Name, + first->adjustAccessLevelForProtocolExtension( + first->getFormalAccess())); // FIXME: If any of the candidates (usually just one) are in the same // module we could offer a fix-it. @@ -448,12 +509,6 @@ resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, DeclContext *DC) { // one, but we should also try to propagate labels into this. DeclNameLoc nameLoc = UDRE->getNameLoc(); - performTypoCorrection(DC, UDRE->getRefKind(), Type(), Name, Loc, - lookupOptions, Lookup); - - diagnose(Loc, diag::use_unresolved_identifier, Name, Name.isOperator()) - .highlight(UDRE->getSourceRange()); - Identifier simpleName = Name.getBaseIdentifier(); const char *buffer = simpleName.get(); llvm::SmallString<64> expectedIdentifier; @@ -477,16 +532,33 @@ resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, DeclContext *DC) { offset += length; } - if (isConfused) { + auto emitBasicError = [&] { + diagnose(Loc, diag::use_unresolved_identifier, Name, Name.isOperator()) + .highlight(UDRE->getSourceRange()); + }; + + if (!isConfused) { + TypoCorrectionResults corrections(*this, Name, nameLoc); + performTypoCorrection(DC, UDRE->getRefKind(), Type(), + lookupOptions, corrections); + + if (auto typo = corrections.claimUniqueCorrection()) { + auto diag = diagnose(Loc, diag::use_unresolved_identifier_corrected, + Name, Name.isOperator(), typo->CorrectedName); + diag.highlight(UDRE->getSourceRange()); + typo->addFixits(diag); + } else { + emitBasicError(); + } + + corrections.noteAllCandidates(); + } else { + emitBasicError(); + diagnose(Loc, diag::confusable_character, UDRE->getName().isOperator(), simpleName.str(), expectedIdentifier) .fixItReplace(Loc, expectedIdentifier); - } else { - // Note all the correction candidates. - for (auto &result : Lookup) { - noteTypoCorrection(Name, nameLoc, result.getValueDecl()); - } } // TODO: consider recovering from here. We may want some way to suppress @@ -514,40 +586,27 @@ resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, DeclContext *DC) { UDRE->isImplicit()); } - bool AllDeclRefs = true; SmallVector ResultValues; - for (auto Result : Lookup) { - // If we find a member, then all of the results aren't non-members. - bool IsMember = (Result.getBaseDecl() && - !isa(Result.getBaseDecl())); - if (IsMember) { - AllDeclRefs = false; - break; - } - - ValueDecl *D = Result.getValueDecl(); - if (!D->hasInterfaceType()) validateDecl(D); - - // FIXME: Circularity hack. - if (!D->hasInterfaceType()) { - AllDeclRefs = false; - continue; - } + Expr *error = nullptr; + bool AllDeclRefs = findNonMembers( + *this, Lookup.innerResults(), UDRE->getRefKind(), /*breakOnMember=*/true, + ResultValues, [&](ValueDecl *D) { + // FIXME: The source-location checks won't make sense once + // EnableASTScopeLookup is the default. + if (Loc.isValid() && D->getLoc().isValid() && + D->getDeclContext()->isLocalContext() && + D->getDeclContext() == DC && + Context.SourceMgr.isBeforeInBuffer(Loc, D->getLoc())) { + diagnose(Loc, diag::use_local_before_declaration, Name); + diagnose(D, diag::decl_declared_here, Name); + error = new (Context) ErrorExpr(UDRE->getSourceRange()); + return false; + } + return true; + }); + if (error) + return error; - // FIXME: The source-location checks won't make sense once - // EnableASTScopeLookup is the default. - if (Loc.isValid() && D->getLoc().isValid() && - D->getDeclContext()->isLocalContext() && - D->getDeclContext() == DC && - Context.SourceMgr.isBeforeInBuffer(Loc, D->getLoc())) { - diagnose(Loc, diag::use_local_before_declaration, Name); - diagnose(D, diag::decl_declared_here, Name); - return new (Context) ErrorExpr(UDRE->getSourceRange()); - } - if (matchesDeclRefKind(D, UDRE->getRefKind())) - ResultValues.push_back(D); - } - if (AllDeclRefs) { // Diagnose uses of operators that found no matching candidates. if (ResultValues.empty()) { @@ -587,20 +646,43 @@ resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, DeclContext *DC) { ResultValues.clear(); bool AllMemberRefs = true; + bool AllConditionalConformances = true; ValueDecl *Base = nullptr; DeclContext *BaseDC = nullptr; for (auto Result : Lookup) { + auto ThisBase = Result.getBaseDecl(); + // Track the base for member declarations. - if (Result.getBaseDecl() && - !isa(Result.getBaseDecl())) { - ResultValues.push_back(Result.getValueDecl()); - if (Base && Result.getBaseDecl() != Base) { + if (ThisBase && !isa(ThisBase)) { + auto Value = Result.getValueDecl(); + ResultValues.push_back(Value); + if (Base && ThisBase != Base) { AllMemberRefs = false; break; } - Base = Result.getBaseDecl(); + Base = ThisBase; BaseDC = Result.getDeclContext(); + + // Check if this result is derived through a conditional conformance, + // meaning it comes from a protocol (or extension) where there's a + // conditional conformance for the type with the method in question + // (NB. that type may not be the type associated with DC, for tested types + // with static methods). + if (auto Proto = Value->getDeclContext() + ->getAsProtocolOrProtocolExtensionContext()) { + auto contextSelfType = + BaseDC->getInnermostTypeContext()->getDeclaredInterfaceType(); + auto conformance = conformsToProtocol( + contextSelfType, Proto, DC, + ConformanceCheckFlags::InExpression | + ConformanceCheckFlags::SkipConditionalRequirements); + + if (!conformance || conformance->getConditionalRequirements().empty()) { + AllConditionalConformances = false; + } + } + continue; } @@ -621,13 +703,29 @@ resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, DeclContext *DC) { BaseExpr = new (Context) DeclRefExpr(Base, UDRE->getNameLoc(), /*Implicit=*/true); } - - + + // We *might* include any non-members that we found in outer contexts in + // some special cases, for backwards compatibility: first, we have to be + // looking for one of the special names + // ('shouldConsiderOuterResultsFor(Name)'), and second, all of the inner + // results need to come from conditional conformances. The second condition + // is how the problem here was encountered: a type ('Range') was made to + // conditionally conform to a new protocol ('Sequence'), which introduced + // some extra methods ('min' and 'max') that shadowed global functions that + // people regularly called within extensions to that type (usually adding + // 'clamp'). + llvm::SmallVector outerAlternatives; + if (AllConditionalConformances) { + (void)findNonMembers(*this, Lookup.outerResults(), UDRE->getRefKind(), + /*breakOnMember=*/false, outerAlternatives, + /*isValid=*/[&](ValueDecl *) { return true; }); + } + // Otherwise, form an UnresolvedDotExpr and sema will resolve it based on // type information. - return new (Context) UnresolvedDotExpr(BaseExpr, SourceLoc(), Name, - UDRE->getNameLoc(), - UDRE->isImplicit()); + return new (Context) UnresolvedDotExpr( + BaseExpr, SourceLoc(), Name, UDRE->getNameLoc(), UDRE->isImplicit(), + Context.AllocateCopy(outerAlternatives)); } // FIXME: If we reach this point, the program we're being handed is likely @@ -819,10 +917,8 @@ namespace { if (auto captureList = dyn_cast(expr)) { // Validate the capture list. for (auto capture : captureList->getCaptureList()) { - TC.typeCheckDecl(capture.Init, true); - TC.typeCheckDecl(capture.Init, false); - TC.typeCheckDecl(capture.Var, true); - TC.typeCheckDecl(capture.Var, false); + TC.typeCheckDecl(capture.Init); + TC.typeCheckDecl(capture.Var); } // Since closure expression is contained by capture list @@ -1554,31 +1650,9 @@ void PreCheckExpression::resolveKeyPathExpr(KeyPathExpr *KPE) { /// \brief Clean up the given ill-formed expression, removing any references /// to type variables and setting error types on erroneous expression nodes. -void CleanupIllFormedExpressionRAII::doIt(Expr *expr, ASTContext &Context) { +void CleanupIllFormedExpressionRAII::doIt(Expr *expr) { class CleanupIllFormedExpression : public ASTWalker { - ASTContext &context; - public: - CleanupIllFormedExpression(ASTContext &context) : context(context) { } - - std::pair walkToExprPre(Expr *expr) override { - // If the type of this expression has a type variable or is invalid, - // overwrite it with ErrorType. - Type type = expr->getType(); - if (type && type->hasTypeVariable()) - expr->setType(ErrorType::get(context)); - - return { true, expr }; - } - - // If we find a TypeLoc (e.g. in an as? expr) with a type variable, rewrite - // it. - bool walkToTypeLocPre(TypeLoc &TL) override { - if (TL.getType() && TL.getType()->hasTypeVariable()) - TL.setType(Type(), /*was validated*/false); - return true; - } - bool walkToDeclPre(Decl *D) override { // This handles parameter decls in ClosureExprs. if (auto VD = dyn_cast(D)) { @@ -1597,13 +1671,13 @@ void CleanupIllFormedExpressionRAII::doIt(Expr *expr, ASTContext &Context) { }; if (expr) - expr->walk(CleanupIllFormedExpression(Context)); + expr->walk(CleanupIllFormedExpression()); } CleanupIllFormedExpressionRAII::~CleanupIllFormedExpressionRAII() { if (expr) - CleanupIllFormedExpressionRAII::doIt(*expr, Context); + CleanupIllFormedExpressionRAII::doIt(*expr); } /// Pre-check the expression, validating any types that occur in the @@ -1730,7 +1804,7 @@ Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc, csOptions |= ConstraintSystemFlags::PreferForceUnwrapToOptional; ConstraintSystem cs(*this, dc, csOptions); cs.baseCS = baseCS; - CleanupIllFormedExpressionRAII cleanup(Context, expr); + CleanupIllFormedExpressionRAII cleanup(expr); ExprCleaner cleanup2(expr); // Verify that a purpose was specified if a convertType was. Note that it is @@ -1845,7 +1919,7 @@ getTypeOfExpressionWithoutApplying(Expr *&expr, DeclContext *dc, // Construct a constraint system from this expression. ConstraintSystem cs(*this, dc, ConstraintSystemFlags::AllowFixes); - CleanupIllFormedExpressionRAII cleanup(Context, expr); + CleanupIllFormedExpressionRAII cleanup(expr); // Attempt to solve the constraint system. SmallVector viable; @@ -1920,7 +1994,7 @@ getTypeOfExpressionWithoutApplying(Expr *&expr, DeclContext *dc, } void TypeChecker::getPossibleTypesOfExpressionWithoutApplying( - Expr *&expr, DeclContext *dc, SmallVectorImpl &types, + Expr *&expr, DeclContext *dc, SmallPtrSetImpl &types, FreeTypeVariableBinding allowFreeTypeVariables, ExprTypeCheckListener *listener) { PrettyStackTraceExpr stackTrace(Context, "type-checking", expr); @@ -1940,7 +2014,7 @@ void TypeChecker::getPossibleTypesOfExpressionWithoutApplying( // If the previous checking gives the expr error type, // clear the result and re-check. { - CleanupIllFormedExpressionRAII cleanup(Context, expr); + CleanupIllFormedExpressionRAII cleanup(expr); const Type originalType = expr->getType(); if (originalType && originalType->hasError()) @@ -1954,7 +2028,7 @@ void TypeChecker::getPossibleTypesOfExpressionWithoutApplying( for (auto &solution : viable) { auto exprType = solution.simplifyType(cs.getType(expr)); assert(exprType && !exprType->hasTypeVariable()); - types.push_back(exprType); + types.insert(exprType.getPointer()); } } @@ -1963,7 +2037,7 @@ bool TypeChecker::typeCheckCompletionSequence(Expr *&expr, DeclContext *DC) { // Construct a constraint system from this expression. ConstraintSystem CS(*this, DC, ConstraintSystemFlags::AllowFixes); - CleanupIllFormedExpressionRAII cleanup(Context, expr); + CleanupIllFormedExpressionRAII cleanup(expr); auto *SE = cast(expr); assert(SE->getNumElements() >= 3); @@ -1994,9 +2068,7 @@ bool TypeChecker::typeCheckCompletionSequence(Expr *&expr, DeclContext *DC) { assert(exprAsBinOp == expr && "found wrong expr?"); // Add type variable for the code-completion expression. - auto tvRHS = - CS.createTypeVariable(CS.getConstraintLocator(CCE), TVO_CanBindToLValue); - CCE->setType(tvRHS); + CCE->setActivated(); if (auto generated = CS.generateConstraints(expr)) { expr = generated; @@ -2042,7 +2114,7 @@ bool TypeChecker::typeCheckExpressionShallow(Expr *&expr, DeclContext *dc) { // Construct a constraint system from this expression. ConstraintSystem cs(*this, dc, ConstraintSystemFlags::AllowFixes); - CleanupIllFormedExpressionRAII cleanup(Context, expr); + CleanupIllFormedExpressionRAII cleanup(expr); if (auto generatedExpr = cs.generateConstraintsShallow(expr)) expr = generatedExpr; else @@ -2228,6 +2300,11 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer, nullptr, TypeLoc())) { return true; } + + // If we're performing an binding to a weak or unowned variable from a + // constructor call, emit a warning that the instance will be immediately + // deallocated. + diagnoseUnownedImmediateDeallocation(*this, pattern, initializer); } if (!resultTy && !initializer->getType()) @@ -2326,8 +2403,7 @@ bool TypeChecker::typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt) { return true; } - SequenceType = - cs.createTypeVariable(Locator, /*options=*/0); + SequenceType = cs.createTypeVariable(Locator); cs.addConstraint(ConstraintKind::Conversion, cs.getType(expr), SequenceType, Locator); cs.addConstraint(ConstraintKind::ConformsTo, SequenceType, @@ -2400,7 +2476,7 @@ bool TypeChecker::typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt) { } if (elementType.isNull()) { - elementType = cs.createTypeVariable(elementLocator, /*options=*/0); + elementType = cs.createTypeVariable(elementLocator); } // Add a conversion constraint between the element type of the sequence @@ -2484,8 +2560,7 @@ Type ConstraintSystem::computeAssignDestType(Expr *dest, SourceLoc equalLoc) { if (auto typeVar = dyn_cast(destTy.getPointer())) { // Newly allocated type should be explicitly materializable, // it's invalid to use non-materializable types as assignment destination. - auto objectTv = createTypeVariable(getConstraintLocator(dest), - /*options=*/0); + auto objectTv = createTypeVariable(getConstraintLocator(dest)); auto refTv = LValueType::get(objectTv); addConstraint(ConstraintKind::Bind, typeVar, refTv, getConstraintLocator(dest)); @@ -2539,7 +2614,7 @@ bool TypeChecker::typeCheckCondition(Expr *&expr, DeclContext *dc) { // Convert the result to a Builtin.i1. Expr *appliedSolution(constraints::Solution &solution, - Expr *expr) override { + Expr *expr) override { auto &cs = solution.getConstraintSystem(); auto converted = @@ -2709,7 +2784,7 @@ static Type replaceArchetypesWithTypeVariables(ConstraintSystem &cs, return Type(); auto locator = cs.getConstraintLocator(nullptr); - auto replacement = cs.createTypeVariable(locator, /*options*/0); + auto replacement = cs.createTypeVariable(locator); if (auto superclass = archetypeType->getSuperclass()) { cs.addConstraint(ConstraintKind::Subtype, replacement, @@ -2726,13 +2801,13 @@ static Type replaceArchetypesWithTypeVariables(ConstraintSystem &cs, // FIXME: Remove this case assert(cast(origType)); auto locator = cs.getConstraintLocator(nullptr); - auto replacement = cs.createTypeVariable(locator, /*options*/0); + auto replacement = cs.createTypeVariable(locator); types[origType] = replacement; return replacement; }, - [&](CanType origType, Type substType, ProtocolType *conformedProtocol) + [&](CanType origType, Type substType, ProtocolDecl *conformedProtocol) -> Optional { - return ProtocolConformanceRef(conformedProtocol->getDecl()); + return ProtocolConformanceRef(conformedProtocol); }); } @@ -2905,7 +2980,7 @@ bool TypeChecker::convertToType(Expr *&expr, Type type, DeclContext *dc, // TODO: need to add kind arg? // Construct a constraint system from this expression. ConstraintSystem cs(*this, dc, ConstraintSystemFlags::AllowFixes); - CleanupIllFormedExpressionRAII cleanup(Context, expr); + CleanupIllFormedExpressionRAII cleanup(expr); // If there is a type that we're expected to convert to, add the conversion // constraint. diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index e64c8384233bd..e1f3f93ccbf6a 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -707,7 +707,7 @@ static void breakInheritanceCycle(EnumDecl *enumDecl) { template static void checkCircularity(TypeChecker &tc, T *decl, Diag circularDiag, - Diag declHereDiag, + DescriptiveDeclKind declKind, SmallVectorImpl &path) { switch (decl->getCircularityCheck()) { case CircularityCheck::Checked: @@ -750,7 +750,8 @@ static void checkCircularity(TypeChecker &tc, T *decl, // Diagnose the cycle. tc.diagnose(decl->getLoc(), circularDiag, pathStr); for (auto i = cycleStart + 1, iEnd = path.end(); i != iEnd; ++i) { - tc.diagnose(*i, declHereDiag, (*i)->getName()); + tc.diagnose(*i, diag::kind_identifier_declared_here, + declKind, (*i)->getName()); } // Set this declaration as invalid, then break the cycle somehow. @@ -766,7 +767,7 @@ static void checkCircularity(TypeChecker &tc, T *decl, decl->setCircularityCheck(CircularityCheck::Checking); T *scratch = nullptr; for (auto inherited : getInheritedForCycleCheck(tc, decl, &scratch)) { - checkCircularity(tc, inherited, circularDiag, declHereDiag, path); + checkCircularity(tc, inherited, circularDiag, declKind, path); } decl->setCircularityCheck(CircularityCheck::Checked); path.pop_back(); @@ -1193,9 +1194,9 @@ static void validatePatternBindingEntry(TypeChecker &tc, } // If we have any type-adjusting attributes, apply them here. - if (binding->getPattern(entryNumber)->hasType()) - if (auto var = binding->getSingleVar()) - tc.checkTypeModifyingDeclAttributes(var); + assert(binding->getPattern(entryNumber)->hasType() && "Type missing?"); + if (auto var = binding->getSingleVar()) + tc.checkTypeModifyingDeclAttributes(var); } /// Validate the entries in the given pattern binding declaration. @@ -1213,6 +1214,7 @@ static void validatePatternBindingEntries(TypeChecker &tc, void swift::makeFinal(ASTContext &ctx, ValueDecl *D) { if (D && !D->isFinal()) { + assert(isa(D) || D->isPotentiallyOverridable()); D->getAttrs().add(new (ctx) FinalAttr(/*IsImplicit=*/true)); } } @@ -1223,6 +1225,57 @@ void swift::makeDynamic(ASTContext &ctx, ValueDecl *D) { } } +namespace { +// The raw values of this enum must be kept in sync with +// diag::implicitly_final_cannot_be_open. +enum class ImplicitlyFinalReason : unsigned { + /// A property was declared with 'let'. + Let, + /// The containing class is final. + FinalClass, + /// A member was declared as 'static'. + Static +}; +} + +static void inferFinalAndDiagnoseIfNeeded(TypeChecker &TC, ValueDecl *D, + StaticSpellingKind staticSpelling) { + auto cls = D->getDeclContext()->getAsClassOrClassExtensionContext(); + if (!cls) + return; + + // Are there any reasons to infer 'final'? Prefer 'static' over the class + // being final for the purposes of diagnostics. + Optional reason; + if (staticSpelling == StaticSpellingKind::KeywordStatic) { + reason = ImplicitlyFinalReason::Static; + + if (auto finalAttr = D->getAttrs().getAttribute()) { + auto finalRange = finalAttr->getRange(); + if (finalRange.isValid()) { + TC.diagnose(finalRange.Start, diag::static_decl_already_final) + .fixItRemove(finalRange); + } + } + } else if (cls->isFinal()) { + reason = ImplicitlyFinalReason::FinalClass; + } + + if (!reason) + return; + + if (D->getFormalAccess() == AccessLevel::Open) { + auto diagID = diag::implicitly_final_cannot_be_open; + if (!TC.Context.isSwiftVersionAtLeast(5)) + diagID = diag::implicitly_final_cannot_be_open_swift4; + auto inFlightDiag = TC.diagnose(D, diagID, + static_cast(reason.getValue())); + fixItAccess(inFlightDiag, D, AccessLevel::Public); + } + + makeFinal(TC.Context, D); +} + /// Configure the implicit 'self' parameter of a function, setting its type, /// pattern, etc. /// @@ -1356,8 +1409,7 @@ class TypeAccessScopeChecker : private TypeWalker, AccessScopeChecker { return Action::Stop; if (!CanonicalizeParentTypes) { - return isa(T.getPointer()) ? Action::SkipChildren - : Action::Continue; + return Action::Continue; } Type nominalParentTy; @@ -1498,14 +1550,26 @@ void TypeChecker::computeAccessLevel(ValueDecl *D) { // decl. A setter attribute can also override this. AbstractStorageDecl *storage = accessor->getStorage(); if (storage->hasAccess()) { - if (accessor->getAccessorKind() == AccessorKind::IsSetter || - accessor->getAccessorKind() == AccessorKind::IsMutableAddressor || - accessor->getAccessorKind() == AccessorKind::IsMaterializeForSet) - accessor->setAccess(storage->getSetterFormalAccess()); - else + switch (accessor->getAccessorKind()) { + case AccessorKind::IsGetter: + case AccessorKind::IsAddressor: accessor->setAccess(storage->getFormalAccess()); + break; + case AccessorKind::IsSetter: + case AccessorKind::IsMutableAddressor: + case AccessorKind::IsMaterializeForSet: + accessor->setAccess(storage->getSetterFormalAccess()); + break; + case AccessorKind::IsWillSet: + case AccessorKind::IsDidSet: + // These are only needed to synthesize the setter. + accessor->setAccess(AccessLevel::Private); + break; + } } else { computeAccessLevel(storage); + assert(accessor->hasAccess() && + "if the accessor isn't just the getter/setter this isn't enough"); } } @@ -1772,7 +1836,7 @@ static void highlightOffendingType(TypeChecker &TC, InFlightDiagnostic &diag, if (auto CITR = dyn_cast(complainRepr)) { const ValueDecl *VD = CITR->getBoundDecl(); - TC.diagnose(VD, diag::type_declared_here); + TC.diagnose(VD, diag::kind_declared_here, DescriptiveDeclKind::Type); } } @@ -1785,51 +1849,41 @@ static void checkGenericParamAccess(TypeChecker &TC, return; // This must stay in sync with diag::generic_param_access. - enum { - ACEK_Parameter = 0, - ACEK_Requirement + enum class ACEK { + Parameter = 0, + Requirement } accessControlErrorKind; auto minAccessScope = AccessScope::getPublic(); const TypeRepr *complainRepr = nullptr; auto downgradeToWarning = DowngradeToWarning::Yes; + auto callbackACEK = ACEK::Parameter; + + auto callback = [&](AccessScope typeAccessScope, + const TypeRepr *thisComplainRepr, + DowngradeToWarning thisDowngrade) { + if (typeAccessScope.isChildOf(minAccessScope) || + (thisDowngrade == DowngradeToWarning::No && + downgradeToWarning == DowngradeToWarning::Yes) || + (!complainRepr && + typeAccessScope.hasEqualDeclContextWith(minAccessScope))) { + minAccessScope = typeAccessScope; + complainRepr = thisComplainRepr; + accessControlErrorKind = callbackACEK; + downgradeToWarning = thisDowngrade; + } + }; for (auto param : *params) { if (param->getInherited().empty()) continue; assert(param->getInherited().size() == 1); checkTypeAccessImpl(TC, param->getInherited().front(), accessScope, owner->getDeclContext(), - [&](AccessScope typeAccessScope, - const TypeRepr *thisComplainRepr, - DowngradeToWarning thisDowngrade) { - if (typeAccessScope.isChildOf(minAccessScope) || - (thisDowngrade == DowngradeToWarning::No && - downgradeToWarning == DowngradeToWarning::Yes) || - (!complainRepr && - typeAccessScope.hasEqualDeclContextWith(minAccessScope))) { - minAccessScope = typeAccessScope; - complainRepr = thisComplainRepr; - accessControlErrorKind = ACEK_Parameter; - downgradeToWarning = thisDowngrade; - } - }); + callback); } + callbackACEK = ACEK::Requirement; for (auto &requirement : params->getRequirements()) { - auto callback = [&](AccessScope typeAccessScope, - const TypeRepr *thisComplainRepr, - DowngradeToWarning thisDowngrade) { - if (typeAccessScope.isChildOf(minAccessScope) || - (thisDowngrade == DowngradeToWarning::No && - downgradeToWarning == DowngradeToWarning::Yes) || - (!complainRepr && - typeAccessScope.hasEqualDeclContextWith(minAccessScope))) { - minAccessScope = typeAccessScope; - complainRepr = thisComplainRepr; - accessControlErrorKind = ACEK_Requirement; - downgradeToWarning = thisDowngrade; - } - }; switch (requirement.getKind()) { case RequirementReprKind::TypeConstraint: checkTypeAccessImpl(TC, requirement.getSubjectLoc(), @@ -1880,7 +1934,7 @@ static void checkGenericParamAccess(TypeChecker &TC, owner->getDescriptiveKind(), isExplicit, contextAccess, minAccess, isa(owner->getDeclContext()), - accessControlErrorKind); + accessControlErrorKind == ACEK::Requirement); highlightOffendingType(TC, diag, complainRepr); } @@ -2168,12 +2222,13 @@ static void checkAccessControl(TypeChecker &TC, const Decl *D) { bool isExplicit = CD->getAttrs().hasAttribute(); auto diagID = diag::class_super_access; if (downgradeToWarning == DowngradeToWarning::Yes || - outerDowngradeToWarning == DowngradeToWarning::Yes) { + outerDowngradeToWarning == DowngradeToWarning::Yes) diagID = diag::class_super_access_warn; - } + auto diag = TC.diagnose(CD, diagID, isExplicit, CD->getFormalAccess(), typeAccess, - isa(CD->getDeclContext())); + isa(CD->getDeclContext()), + superclassLocIter->getTypeRepr() != complainRepr); highlightOffendingType(TC, diag, complainRepr); }); } @@ -2571,36 +2626,14 @@ static void inferDynamic(ASTContext &ctx, ValueDecl *D) { if (D->isFinal() && !isNSManaged) return; - // Variables declared with 'let' cannot be 'dynamic'. - if (auto VD = dyn_cast(D)) { - auto staticSpelling = VD->getParentPatternBinding()->getStaticSpelling(); - - // The presence of 'static' blocks the inference of 'dynamic'. - if (staticSpelling == StaticSpellingKind::KeywordStatic) - return; - - if (VD->isLet() && !isNSManaged) - return; - } - // Accessors should not infer 'dynamic' on their own; they can get it from // their storage decls. - if (auto FD = dyn_cast(D)) { - if (isa(FD)) - return; - - auto staticSpelling = FD->getStaticSpelling(); - - // The presence of 'static' bocks the inference of 'dynamic'. - if (staticSpelling == StaticSpellingKind::KeywordStatic) - return; - } + if (isa(D)) + return; - // The presence of 'final' on a class prevents 'dynamic'. + // Only classes can use 'dynamic'. auto classDecl = D->getDeclContext()->getAsClassOrClassExtensionContext(); - if (!classDecl) return; - if (!isNSManaged && classDecl->isFinal() && - !classDecl->requiresStoredPropertyInits()) + if (!classDecl) return; // Add the 'dynamic' attribute. @@ -2968,7 +3001,11 @@ static LiteralExpr *getAutomaticRawValueExpr(TypeChecker &TC, return new (TC.Context) IntegerLiteralExpr("0", SourceLoc(), /*Implicit=*/true); } - + // If the prevValue is not a well-typed integer, then break. + // This could happen if the literal value overflows _MaxBuiltinIntegerType. + if (!prevValue->getType()) + return nullptr; + if (auto intLit = dyn_cast(prevValue)) { APInt nextVal = intLit->getValue() + 1; bool negative = nextVal.slt(0); @@ -3280,7 +3317,6 @@ static void checkVarBehavior(VarDecl *decl, TypeChecker &TC) { conformance->setState(ProtocolConformanceState::CheckingTypeWitnesses); // First, satisfy any associated type requirements. - Substitution valueSub; AssociatedTypeDecl *valueReqt = nullptr; for (auto assocTy : behaviorProto->getAssociatedTypeMembers()) { @@ -3296,7 +3332,6 @@ static void checkVarBehavior(VarDecl *decl, TypeChecker &TC) { // TODO: Handle secondary 'where' constraints on the associated types. // TODO: Handle non-protocol constraints ('class', base class) auto propTy = decl->getType(); - SmallVector valueConformances; for (auto proto : assocTy->getConformingProtocols()) { auto valueConformance = TC.conformsToProtocol(propTy, proto, dc, ConformanceCheckFlags::Used, @@ -3309,12 +3344,9 @@ static void checkVarBehavior(VarDecl *decl, TypeChecker &TC) { behaviorProto->getName()); goto next_requirement; } - valueConformances.push_back(*valueConformance); } { - auto conformancesCopy = TC.Context.AllocateCopy(valueConformances); - valueSub = Substitution(propTy, conformancesCopy); // FIXME: Maybe we should synthesize an implicit TypeAliasDecl? We // really don't want the behavior conformances to show up in the // enclosing namespace though. @@ -3329,27 +3361,18 @@ static void checkVarBehavior(VarDecl *decl, TypeChecker &TC) { return; } - // Build a Substitution vector from the conformance. - auto conformanceMem = - TC.Context.AllocateUninitialized(1); - auto selfConformance = new ((void*)conformanceMem.data()) - ProtocolConformanceRef(conformance); - Substitution allInterfaceSubs[] = { - Substitution(behaviorInterfaceSelf, *selfConformance), - Substitution(decl->getInterfaceType(), valueSub.getConformances()), - }; - Substitution allContextSubs[] = { - Substitution(behaviorSelf, *selfConformance), - Substitution(decl->getType(), valueSub.getConformances()), - }; - - SubstitutionList interfaceSubs = allInterfaceSubs; - if (interfaceSubs.back().getConformances().empty()) - interfaceSubs = interfaceSubs.drop_back(); + // Build interface and context substitution maps. + auto interfaceSubsMap = + SubstitutionMap::getProtocolSubstitutions( + behaviorProto, + behaviorInterfaceSelf, + ProtocolConformanceRef(conformance)); - SubstitutionList contextSubs = allContextSubs; - if (contextSubs.back().getConformances().empty()) - contextSubs = contextSubs.drop_back(); + auto contextSubsMap = + SubstitutionMap::getProtocolSubstitutions( + behaviorProto, + behaviorSelf, + ProtocolConformanceRef(conformance)); // Now that type witnesses are done, satisfy property and method requirements. conformance->setState(ProtocolConformanceState::Checking); @@ -3406,11 +3429,9 @@ static void checkVarBehavior(VarDecl *decl, TypeChecker &TC) { diag::property_behavior_protocol_reqt_ambiguous, TC.Context.Id_initStorage); TC.diagnose(defaultInitStorageDecl->getLoc(), - diag::property_behavior_protocol_reqt_here, - TC.Context.Id_initStorage); + diag::identifier_declared_here, TC.Context.Id_initStorage); TC.diagnose(parameterizedInitStorageDecl->getLoc(), - diag::property_behavior_protocol_reqt_here, - TC.Context.Id_initStorage); + diag::identifier_declared_here, TC.Context.Id_initStorage); conformance->setInvalid(); continue; } @@ -3458,8 +3479,8 @@ static void checkVarBehavior(VarDecl *decl, TypeChecker &TC) { behaviorSelf, storageTy, conformance, - interfaceSubs, - contextSubs); + interfaceSubsMap, + contextSubsMap); continue; } } else if (auto func = dyn_cast(requirement)) { @@ -3479,8 +3500,7 @@ static void checkVarBehavior(VarDecl *decl, TypeChecker &TC) { TC.diagnose(behavior->getLoc(), diag::property_behavior_invalid_parameter_reqt, behaviorProto->getName()); - TC.diagnose(varReqt->getLoc(), - diag::property_behavior_protocol_reqt_here, + TC.diagnose(varReqt->getLoc(), diag::identifier_declared_here, TC.Context.Id_parameter); conformance->setInvalid(); continue; @@ -3508,8 +3528,8 @@ static void checkVarBehavior(VarDecl *decl, TypeChecker &TC) { // Build the parameter witness method. TC.completePropertyBehaviorParameter(decl, func, conformance, - interfaceSubs, - contextSubs); + interfaceSubsMap, + contextSubsMap); continue; } } @@ -3545,9 +3565,8 @@ static void checkVarBehavior(VarDecl *decl, TypeChecker &TC) { // Check that the 'value' property from the protocol matches the // declared property type in context. - auto sig = behaviorProto->getGenericSignatureOfContext(); - auto map = sig->getSubstitutionMap(interfaceSubs); - auto substValueTy = behavior->ValueDecl->getInterfaceType().subst(map); + auto substValueTy = + behavior->ValueDecl->getInterfaceType().subst(interfaceSubsMap); if (!substValueTy->isEqual(decl->getInterfaceType())) { TC.diagnose(behavior->getLoc(), @@ -3566,7 +3585,7 @@ static void checkVarBehavior(VarDecl *decl, TypeChecker &TC) { // 'value' implementation. TC.completePropertyBehaviorAccessors(decl, behavior->ValueDecl, decl->getType(), - interfaceSubs, contextSubs); + interfaceSubsMap, contextSubsMap); return; } @@ -3751,7 +3770,8 @@ void TypeChecker::validateDecl(PrecedenceGroupDecl *PGD) { == dc->getParentModule()) { if (!PGD->isInvalid()) { diagnose(rel.NameLoc, diag::precedence_group_lower_within_module); - diagnose(group->getNameLoc(), diag::precedence_group_declared_here); + diagnose(group->getNameLoc(), diag::kind_declared_here, + DescriptiveDeclKind::PrecedenceGroup); isInvalid = true; } } else { @@ -3954,19 +3974,129 @@ static void finalizeAbstractStorageDecl(TypeChecker &TC, } } +static void adjustFunctionTypeForOverride(Type &type) { + // Drop 'throws'. + // FIXME: Do we want to allow overriding a function returning a value + // with one returning Never? + auto fnType = type->castTo(); + auto extInfo = fnType->getExtInfo(); + extInfo = extInfo.withThrows(false); + if (fnType->getExtInfo() != extInfo) + type = fnType->withExtInfo(extInfo); +} + +/// Drop the optionality of the result type of the given function type. +static Type dropResultOptionality(Type type, unsigned uncurryLevel) { + // We've hit the result type. + if (uncurryLevel == 0) { + if (auto objectTy = type->getOptionalObjectType()) + return objectTy; + + return type; + } + + // Determine the input and result types of this function. + auto fnType = type->castTo(); + auto parameters = fnType->getParams(); + Type resultType = + dropResultOptionality(fnType->getResult(), uncurryLevel - 1); + + // Produce the resulting function type. + if (auto genericFn = dyn_cast(fnType)) { + return GenericFunctionType::get(genericFn->getGenericSignature(), + parameters, resultType, + fnType->getExtInfo()); + } + + return FunctionType::get(parameters, resultType, fnType->getExtInfo()); +} + +static Type getMemberTypeForComparison(ASTContext &ctx, ValueDecl *member, + ValueDecl *derivedDecl = nullptr, + bool stripLabels = true) { + auto *method = dyn_cast(member); + ConstructorDecl *ctor = nullptr; + if (method) + ctor = dyn_cast(method); + + auto abstractStorage = dyn_cast(member); + assert((method || abstractStorage) && "Not a method or abstractStorage?"); + SubscriptDecl *subscript = nullptr; + if (abstractStorage) + subscript = dyn_cast(abstractStorage); + + auto memberType = member->getInterfaceType(); + if (derivedDecl) { + auto *dc = derivedDecl->getDeclContext(); + auto owningType = dc->getDeclaredInterfaceType(); + assert(owningType); + + memberType = owningType->adjustSuperclassMemberDeclType(member, derivedDecl, + memberType); + if (memberType->hasError()) + return memberType; + } + + if (stripLabels) + memberType = memberType->getUnlabeledType(ctx); + + if (method) { + // For methods, strip off the 'Self' type. + memberType = memberType->castTo()->getResult(); + adjustFunctionTypeForOverride(memberType); + } else if (subscript) { + // For subscripts, we don't have a 'Self' type, but turn it + // into a monomorphic function type. + auto funcTy = memberType->castTo(); + memberType = FunctionType::get(funcTy->getParams(), funcTy->getResult(), + FunctionType::ExtInfo()); + } else { + // For properties, strip off ownership. + memberType = memberType->getReferenceStorageReferent(); + } + + // Ignore the optionality of initializers when comparing types; + // we'll enforce this separately + if (ctor) { + memberType = dropResultOptionality(memberType, 1); + } + + return memberType; +} + +static bool isOverrideBasedOnType(ValueDecl *decl, Type declTy, + ValueDecl *parentDecl, Type parentDeclTy) { + auto *genericSig = + decl->getInnermostDeclContext()->getGenericSignatureOfContext(); + + auto canDeclTy = declTy->getCanonicalType(genericSig); + auto canParentDeclTy = parentDeclTy->getCanonicalType(genericSig); + + auto declIUOAttr = + decl->getAttrs().hasAttribute(); + auto parentDeclIUOAttr = + parentDecl->getAttrs().hasAttribute(); + + if (declIUOAttr != parentDeclIUOAttr) + return false; + + // If this is a constructor, let's compare only parameter types. + if (isa(decl)) { + auto fnType1 = declTy->castTo(); + auto fnType2 = parentDeclTy->castTo(); + return AnyFunctionType::equalParams(fnType1->getParams(), + fnType2->getParams()); + } + + return canDeclTy == canParentDeclTy; +} + namespace { class DeclChecker : public DeclVisitor { public: TypeChecker &TC; - // For library-style parsing, we need to make two passes over the global - // scope. These booleans indicate whether this is currently the first or - // second pass over the global scope (or neither, if we're in a context where - // we only visit each decl once). - unsigned IsFirstPass : 1; - - DeclChecker(TypeChecker &TC, bool IsFirstPass) - : TC(TC), IsFirstPass(IsFirstPass) {} + explicit DeclChecker(TypeChecker &TC) : TC(TC) {} void visit(Decl *decl) { FrontendStatsTracer StatsTracer(TC.Context.Stats, "typecheck-decl", decl); @@ -3974,6 +4104,8 @@ class DeclChecker : public DeclVisitor { DeclVisitor::visit(decl); + TC.checkUnsupportedProtocolType(decl); + if (auto VD = dyn_cast(decl)) { checkRedeclaration(TC, VD); @@ -3994,17 +4126,6 @@ class DeclChecker : public DeclVisitor { "`" + VD->getBaseName().userFacingName().str() + "`"); } } - - if (!IsFirstPass) { - TC.checkUnsupportedProtocolType(decl); - if (auto nominal = dyn_cast(decl)) { - TC.checkDeclCircularity(nominal); - } - if (auto protocol = dyn_cast(decl)) { - if (protocol->isResilient()) - TC.inferDefaultWitnesses(protocol); - } - } } //===--------------------------------------------------------------------===// @@ -4090,7 +4211,7 @@ class DeclChecker : public DeclVisitor { // Stored type variables in a generic context need to logically // occur once per instantiation, which we don't yet handle. } else if (DC->getAsProtocolExtensionContext()) { - unimplementedStatic(ProtocolExtensions); + unimplementedStatic(ProtocolExtensions); } else if (DC->isGenericContext() && !DC->getGenericSignatureOfContext()->areAllParamsConcrete()) { unimplementedStatic(GenericTypes); @@ -4122,14 +4243,12 @@ class DeclChecker : public DeclVisitor { // (that were previously only validated at point of synthesis) if (auto getter = VD->getGetter()) { if (getter->hasBody()) { - TC.typeCheckDecl(getter, true); - TC.typeCheckDecl(getter, false); + TC.typeCheckDecl(getter); } } if (auto setter = VD->getSetter()) { if (setter->hasBody()) { - TC.typeCheckDecl(setter, true); - TC.typeCheckDecl(setter, false); + TC.typeCheckDecl(setter); } } @@ -4142,66 +4261,56 @@ class DeclChecker : public DeclVisitor { } void visitPatternBindingDecl(PatternBindingDecl *PBD) { - // Check all the pattern/init pairs in the PBD. - validatePatternBindingEntries(TC, PBD); - if (PBD->isBeingValidated()) return; - // If the initializers in the PBD aren't checked yet, do so now. - if (!IsFirstPass) { - for (unsigned i = 0, e = PBD->getNumPatternEntries(); i != e; ++i) { - if (!PBD->isInitializerChecked(i) && PBD->getInit(i)) - TC.typeCheckPatternBinding(PBD, i, /*skipApplyingSolution*/false); - } - } + // Check all the pattern/init pairs in the PBD. + validatePatternBindingEntries(TC, PBD); TC.checkDeclAttributesEarly(PBD); - if (IsFirstPass) { - for (unsigned i = 0, e = PBD->getNumPatternEntries(); i != e; ++i) { - // Type check each VarDecl that this PatternBinding handles. - visitBoundVars(PBD->getPattern(i)); - - // If we have a type but no initializer, check whether the type is - // default-initializable. If so, do it. - if (PBD->getPattern(i)->hasType() && - !PBD->getInit(i) && - PBD->getPattern(i)->hasStorage() && - !PBD->getPattern(i)->getType()->hasError()) { - - // If we have a type-adjusting attribute (like ownership), apply it now. - if (auto var = PBD->getSingleVar()) - TC.checkTypeModifyingDeclAttributes(var); - - // Decide whether we should suppress default initialization. - // - // Note: Swift 4 had a bug where properties with a desugared optional - // type like Optional had a half-way behavior where sometimes - // they behave like they are default initialized, and sometimes not. - // - // In Swift 5 mode, use the right condition here, and only default - // initialize properties with a sugared Optional type. - // - // (The restriction to sugared types only comes because we don't have - // the iterative declaration checker yet; so in general, we cannot - // look at the type of a property at all, and can only look at the - // TypeRepr, because we haven't validated the property yet.) - if (TC.Context.isSwiftVersionAtLeast(5)) { - if (!PBD->isDefaultInitializable(i)) - continue; - } else { - if (PBD->getPattern(i)->isNeverDefaultInitializable()) - continue; - } + for (unsigned i = 0, e = PBD->getNumPatternEntries(); i != e; ++i) { + // Type check each VarDecl that this PatternBinding handles. + visitBoundVars(PBD->getPattern(i)); - auto type = PBD->getPattern(i)->getType(); - if (auto defaultInit = buildDefaultInitializer(TC, type)) { - // If we got a default initializer, install it and re-type-check it - // to make sure it is properly coerced to the pattern type. - PBD->setInit(i, defaultInit); - TC.typeCheckPatternBinding(PBD, i, /*skipApplyingSolution*/false); - } + // If we have a type but no initializer, check whether the type is + // default-initializable. If so, do it. + if (PBD->getPattern(i)->hasType() && + !PBD->getInit(i) && + PBD->getPattern(i)->hasStorage() && + !PBD->getPattern(i)->getType()->hasError()) { + + // If we have a type-adjusting attribute (like ownership), apply it now. + if (auto var = PBD->getSingleVar()) + TC.checkTypeModifyingDeclAttributes(var); + + // Decide whether we should suppress default initialization. + // + // Note: Swift 4 had a bug where properties with a desugared optional + // type like Optional had a half-way behavior where sometimes + // they behave like they are default initialized, and sometimes not. + // + // In Swift 5 mode, use the right condition here, and only default + // initialize properties with a sugared Optional type. + // + // (The restriction to sugared types only comes because we don't have + // the iterative declaration checker yet; so in general, we cannot + // look at the type of a property at all, and can only look at the + // TypeRepr, because we haven't validated the property yet.) + if (TC.Context.isSwiftVersionAtLeast(5)) { + if (!PBD->isDefaultInitializable(i)) + continue; + } else { + if (PBD->getPattern(i)->isNeverDefaultInitializable()) + continue; + } + + auto type = PBD->getPattern(i)->getType(); + if (auto defaultInit = buildDefaultInitializer(TC, type)) { + // If we got a default initializer, install it and re-type-check it + // to make sure it is properly coerced to the pattern type. + PBD->setInit(i, defaultInit); + TC.typeCheckPatternBinding(PBD, i, /*skipApplyingSolution*/false); } } } @@ -4268,26 +4377,22 @@ class DeclChecker : public DeclVisitor { } TC.checkDeclAttributes(PBD); + checkAccessControl(TC, PBD); - if (IsFirstPass) - checkAccessControl(TC, PBD); + // If the initializers in the PBD aren't checked yet, do so now. + for (unsigned i = 0, e = PBD->getNumPatternEntries(); i != e; ++i) { + if (!PBD->isInitializerChecked(i) && PBD->getInit(i)) + TC.typeCheckPatternBinding(PBD, i, /*skipApplyingSolution*/false); + } } void visitSubscriptDecl(SubscriptDecl *SD) { - if (!IsFirstPass) { - return; - } - TC.validateDecl(SD); TC.checkDeclAttributes(SD); checkAccessControl(TC, SD); } void visitTypeAliasDecl(TypeAliasDecl *TAD) { - if (!IsFirstPass) { - return; - } - TC.checkDeclAttributesEarly(TAD); TC.computeAccessLevel(TAD); @@ -4297,10 +4402,6 @@ class DeclChecker : public DeclVisitor { } void visitAssociatedTypeDecl(AssociatedTypeDecl *AT) { - if (!IsFirstPass) { - return; - } - TC.validateDecl(AT); auto *proto = AT->getProtocol(); @@ -4363,49 +4464,37 @@ class DeclChecker : public DeclVisitor { TC.checkDeclAttributesEarly(ED); TC.computeAccessLevel(ED); - if (IsFirstPass) { - checkUnsupportedNestedType(ED); - - TC.validateDecl(ED); - - TC.DeclsToFinalize.remove(ED); - - { - // Check for circular inheritance of the raw type. - SmallVector path; - path.push_back(ED); - checkCircularity(TC, ED, diag::circular_enum_inheritance, - diag::enum_here, path); - } - } - - if (!IsFirstPass) { - checkAccessControl(TC, ED); + checkUnsupportedNestedType(ED); - if (ED->hasRawType() && !ED->isObjC()) { - // ObjC enums have already had their raw values checked, but pure Swift - // enums haven't. - checkEnumRawValues(TC, ED); - } + TC.validateDecl(ED); + TC.DeclsToFinalize.remove(ED); + ED->setHasValidatedLayout(); - TC.checkConformancesInContext(ED, ED); + { + // Check for circular inheritance of the raw type. + SmallVector path; + path.push_back(ED); + checkCircularity(TC, ED, diag::circular_enum_inheritance, + DescriptiveDeclKind::Enum, path); } for (Decl *member : ED->getMembers()) visit(member); - TC.checkDeclAttributes(ED); - } + checkAccessControl(TC, ED); - void visitStructDecl(StructDecl *SD) { - if (!IsFirstPass) { - for (Decl *Member : SD->getMembers()) - visit(Member); - - return; + if (ED->hasRawType() && !ED->isObjC()) { + // ObjC enums have already had their raw values checked, but pure Swift + // enums haven't. + checkEnumRawValues(TC, ED); } + TC.checkDeclCircularity(ED); + TC.ConformanceContexts.push_back(ED); + } + + void visitStructDecl(StructDecl *SD) { TC.checkDeclAttributesEarly(SD); TC.computeAccessLevel(SD); @@ -4413,6 +4502,7 @@ class DeclChecker : public DeclVisitor { TC.validateDecl(SD); TC.DeclsToFinalize.remove(SD); + SD->setHasValidatedLayout(); TC.addImplicitConstructors(SD); @@ -4422,8 +4512,8 @@ class DeclChecker : public DeclVisitor { TC.checkDeclAttributes(SD); checkAccessControl(TC, SD); - if (!SD->isInvalid()) - TC.checkConformancesInContext(SD, SD); + TC.checkDeclCircularity(SD); + TC.ConformanceContexts.push_back(SD); } /// Check whether the given properties can be @NSManaged in this class. @@ -4533,34 +4623,21 @@ class DeclChecker : public DeclVisitor { TC.checkDeclAttributesEarly(CD); TC.computeAccessLevel(CD); - if (IsFirstPass) { - checkUnsupportedNestedType(CD); + checkUnsupportedNestedType(CD); - TC.validateDecl(CD); - if (!CD->hasValidSignature()) - return; - - TC.requestSuperclassLayout(CD); - TC.DeclsToFinalize.remove(CD); + TC.validateDecl(CD); + TC.requestSuperclassLayout(CD); + TC.DeclsToFinalize.remove(CD); + CD->setHasValidatedLayout(); - { - // Check for circular inheritance. - SmallVector path; - path.push_back(CD); - checkCircularity(TC, CD, diag::circular_class_inheritance, - diag::class_here, path); - } + { + // Check for circular inheritance. + SmallVector path; + path.push_back(CD); + checkCircularity(TC, CD, diag::circular_class_inheritance, + DescriptiveDeclKind::Class, path); } - // If this class needs an implicit constructor, add it. - if (!IsFirstPass) - TC.addImplicitConstructors(CD); - - CD->addImplicitDestructor(); - - if (!IsFirstPass && !CD->isInvalid()) - TC.checkConformancesInContext(CD, CD); - for (Decl *Member : CD->getMembers()) visit(Member); @@ -4569,106 +4646,105 @@ class DeclChecker : public DeclVisitor { if (CD->requiresStoredPropertyInits()) checkRequiredInClassInits(CD); - if (!IsFirstPass) { - if (auto superclassTy = CD->getSuperclass()) { - ClassDecl *Super = superclassTy->getClassOrBoundGenericClass(); + TC.addImplicitConstructors(CD); + CD->addImplicitDestructor(); + + if (auto superclassTy = CD->getSuperclass()) { + ClassDecl *Super = superclassTy->getClassOrBoundGenericClass(); - if (auto *SF = CD->getParentSourceFile()) { - if (auto *tracker = SF->getReferencedNameTracker()) { - bool isPrivate = - CD->getFormalAccess() <= AccessLevel::FilePrivate; - tracker->addUsedMember({Super, Identifier()}, !isPrivate); - } + if (auto *SF = CD->getParentSourceFile()) { + if (auto *tracker = SF->getReferencedNameTracker()) { + bool isPrivate = + CD->getFormalAccess() <= AccessLevel::FilePrivate; + tracker->addUsedMember({Super, Identifier()}, !isPrivate); } + } - bool isInvalidSuperclass = false; + bool isInvalidSuperclass = false; - if (Super->isFinal()) { - TC.diagnose(CD, diag::inheritance_from_final_class, - Super->getName()); - // FIXME: should this really be skipping the rest of decl-checking? - return; - } + if (Super->isFinal()) { + TC.diagnose(CD, diag::inheritance_from_final_class, + Super->getName()); + // FIXME: should this really be skipping the rest of decl-checking? + return; + } - if (Super->hasClangNode() && Super->getGenericParams() - && superclassTy->hasTypeParameter()) { - TC.diagnose(CD, - diag::inheritance_from_unspecialized_objc_generic_class, - Super->getName()); - } + if (Super->hasClangNode() && Super->getGenericParams() + && superclassTy->hasTypeParameter()) { + TC.diagnose(CD, + diag::inheritance_from_unspecialized_objc_generic_class, + Super->getName()); + } - switch (Super->getForeignClassKind()) { - case ClassDecl::ForeignKind::Normal: - break; - case ClassDecl::ForeignKind::CFType: - TC.diagnose(CD, diag::inheritance_from_cf_class, - Super->getName()); - isInvalidSuperclass = true; - break; - case ClassDecl::ForeignKind::RuntimeOnly: - TC.diagnose(CD, diag::inheritance_from_objc_runtime_visible_class, - Super->getName()); - isInvalidSuperclass = true; - break; - } + switch (Super->getForeignClassKind()) { + case ClassDecl::ForeignKind::Normal: + break; + case ClassDecl::ForeignKind::CFType: + TC.diagnose(CD, diag::inheritance_from_cf_class, + Super->getName()); + isInvalidSuperclass = true; + break; + case ClassDecl::ForeignKind::RuntimeOnly: + TC.diagnose(CD, diag::inheritance_from_objc_runtime_visible_class, + Super->getName()); + isInvalidSuperclass = true; + break; + } - if (!isInvalidSuperclass && Super->hasMissingVTableEntries()) { - auto *superFile = Super->getModuleScopeContext(); - if (auto *serialized = dyn_cast(superFile)) { - if (serialized->getLanguageVersionBuiltWith() != - TC.getLangOpts().EffectiveLanguageVersion) { - TC.diagnose(CD, - diag::inheritance_from_class_with_missing_vtable_entries_versioned, - Super->getName(), - serialized->getLanguageVersionBuiltWith(), - TC.getLangOpts().EffectiveLanguageVersion); - isInvalidSuperclass = true; - } - } - if (!isInvalidSuperclass) { - TC.diagnose( - CD, diag::inheritance_from_class_with_missing_vtable_entries, - Super->getName()); + if (!isInvalidSuperclass && Super->hasMissingVTableEntries()) { + auto *superFile = Super->getModuleScopeContext(); + if (auto *serialized = dyn_cast(superFile)) { + if (serialized->getLanguageVersionBuiltWith() != + TC.getLangOpts().EffectiveLanguageVersion) { + TC.diagnose(CD, + diag::inheritance_from_class_with_missing_vtable_entries_versioned, + Super->getName(), + serialized->getLanguageVersionBuiltWith(), + TC.getLangOpts().EffectiveLanguageVersion); isInvalidSuperclass = true; } } - - // Require the superclass to be open if this is outside its - // defining module. But don't emit another diagnostic if we - // already complained about the class being inherently - // un-subclassable. - if (!isInvalidSuperclass && - Super->getFormalAccess(CD->getDeclContext()) - < AccessLevel::Open && - Super->getModuleContext() != CD->getModuleContext()) { - TC.diagnose(CD, diag::superclass_not_open, superclassTy); + if (!isInvalidSuperclass) { + TC.diagnose( + CD, diag::inheritance_from_class_with_missing_vtable_entries, + Super->getName()); isInvalidSuperclass = true; } + } - // Require superclasses to be open if the subclass is open. - // This is a restriction we can consider lifting in the future, - // e.g. to enable a "sealed" superclass whose subclasses are all - // of one of several alternatives. - if (!isInvalidSuperclass && - CD->getFormalAccess() == AccessLevel::Open && - Super->getFormalAccess() != AccessLevel::Open) { - TC.diagnose(CD, diag::superclass_of_open_not_open, superclassTy); - TC.diagnose(Super, diag::superclass_here); - } + // Require the superclass to be open if this is outside its + // defining module. But don't emit another diagnostic if we + // already complained about the class being inherently + // un-subclassable. + if (!isInvalidSuperclass && + Super->getFormalAccess(CD->getDeclContext()) + < AccessLevel::Open && + Super->getModuleContext() != CD->getModuleContext()) { + TC.diagnose(CD, diag::superclass_not_open, superclassTy); + isInvalidSuperclass = true; + } + // Require superclasses to be open if the subclass is open. + // This is a restriction we can consider lifting in the future, + // e.g. to enable a "sealed" superclass whose subclasses are all + // of one of several alternatives. + if (!isInvalidSuperclass && + CD->getFormalAccess() == AccessLevel::Open && + Super->getFormalAccess() != AccessLevel::Open) { + TC.diagnose(CD, diag::superclass_of_open_not_open, superclassTy); + TC.diagnose(Super, diag::superclass_here); } - checkAccessControl(TC, CD); } TC.checkDeclAttributes(CD); + checkAccessControl(TC, CD); + + TC.checkDeclCircularity(CD); + TC.ConformanceContexts.push_back(CD); } void visitProtocolDecl(ProtocolDecl *PD) { - if (!IsFirstPass) { - return; - } - TC.checkDeclAttributesEarly(PD); TC.computeAccessLevel(PD); @@ -4683,7 +4759,7 @@ class DeclChecker : public DeclVisitor { SmallVector path; path.push_back(PD); checkCircularity(TC, PD, diag::circular_protocol_def, - diag::protocol_here, path); + DescriptiveDeclKind::Protocol, path); // Make sure the parent protocols have been fully validated. for (auto inherited : PD->getLocalProtocols()) { @@ -4710,14 +4786,15 @@ class DeclChecker : public DeclVisitor { TC.checkDeclAttributes(PD); checkAccessControl(TC, PD); - for (auto member : PD->getMembers()) { - TC.checkUnsupportedProtocolType(member); - } TC.checkInheritanceClause(PD); GenericTypeToArchetypeResolver resolver(PD); TC.validateWhereClauses(PD, &resolver); + TC.checkDeclCircularity(PD); + if (PD->isResilient()) + TC.inferDefaultWitnesses(PD); + if (TC.Context.LangOpts.DebugGenericSignatures) { auto requirementsSig = GenericSignature::get({PD->getProtocolSelfType()}, @@ -4757,6 +4834,10 @@ class DeclChecker : public DeclVisitor { if (decl->isInvalid() || decl->isImplicit() || decl->hasClangNode()) return false; + // Protocol requirements do not require definitions. + if (isa(decl->getDeclContext())) + return false; + // Functions can have _silgen_name, semantics, and NSManaged attributes. if (auto func = dyn_cast(decl)) { if (func->getAttrs().hasAttribute() || @@ -4776,22 +4857,17 @@ class DeclChecker : public DeclVisitor { } void visitFuncDecl(FuncDecl *FD) { - if (!IsFirstPass) { - if (FD->hasBody()) { - // Record the body. - TC.definedFunctions.push_back(FD); - } else if (requiresDefinition(FD)) { - // Complain if we should have a body. - TC.diagnose(FD->getLoc(), diag::func_decl_without_brace); - } - - return; - } - TC.validateDecl(FD); checkAccessControl(TC, FD); - } + if (FD->hasBody()) { + // Record the body. + TC.definedFunctions.push_back(FD); + } else if (requiresDefinition(FD)) { + // Complain if we should have a body. + TC.diagnose(FD->getLoc(), diag::func_decl_without_brace); + } + } void visitModuleDecl(ModuleDecl *) { } @@ -4829,32 +4905,6 @@ class DeclChecker : public DeclVisitor { return true; } - /// Drop the optionality of the result type of the given function type. - static Type dropResultOptionality(Type type, unsigned uncurryLevel) { - // We've hit the result type. - if (uncurryLevel == 0) { - if (auto objectTy = type->getOptionalObjectType()) - return objectTy; - - return type; - } - - // Determine the input and result types of this function. - auto fnType = type->castTo(); - Type inputType = fnType->getInput(); - Type resultType = dropResultOptionality(fnType->getResult(), - uncurryLevel - 1); - - // Produce the resulting function type. - if (auto genericFn = dyn_cast(fnType)) { - return GenericFunctionType::get(genericFn->getGenericSignature(), - inputType, resultType, - fnType->getExtInfo()); - } - - return FunctionType::get(inputType, resultType, fnType->getExtInfo()); - } - static bool diagnoseMismatchedOptionals(TypeChecker &TC, const ValueDecl *member, const ParameterList *params, TypeLoc resultTL, @@ -5021,17 +5071,6 @@ class DeclChecker : public DeclVisitor { } } - static void adjustFunctionTypeForOverride(Type &type) { - // Drop 'throws'. - // FIXME: Do we want to allow overriding a function returning a value - // with one returning Never? - auto fnType = type->castTo(); - auto extInfo = fnType->getExtInfo(); - extInfo = extInfo.withThrows(false); - if (fnType->getExtInfo() != extInfo) - type = fnType->withExtInfo(extInfo); - } - /// If the difference between the types of \p decl and \p base is something /// we feel confident about fixing (even partially), emit a note with fix-its /// attached. Otherwise, no note will be emitted. @@ -5050,8 +5089,12 @@ class DeclChecker : public DeclVisitor { if (auto *baseInit = dyn_cast(base)) { // Special-case initializers, whose "type" isn't useful besides the // input arguments. - baseTy = baseTy->getAs()->getResult(); - Type argTy = baseTy->getAs()->getInput(); + auto *fnType = baseTy->getAs(); + baseTy = fnType->getResult(); + Type argTy = FunctionType::composeInput(TC.Context, + baseTy->getAs() + ->getParams(), + false); auto diagKind = diag::override_type_mismatch_with_fixits_init; unsigned numArgs = baseInit->getParameters()->size(); activeDiag.emplace(TC.diagnose(decl, diagKind, @@ -5222,7 +5265,7 @@ class DeclChecker : public DeclVisitor { // when their storage decl is processed. if (isa(decl)) return false; - + auto method = dyn_cast(decl); ConstructorDecl *ctor = nullptr; if (method) @@ -5235,27 +5278,7 @@ class DeclChecker : public DeclVisitor { subscript = dyn_cast(abstractStorage); // Figure out the type of the declaration that we're using for comparisons. - auto declTy = decl->getInterfaceType()->getUnlabeledType(TC.Context); - if (method) { - // For methods, strip off the 'Self' type. - declTy = declTy->castTo()->getResult(); - adjustFunctionTypeForOverride(declTy); - } if (subscript) { - // For subscripts, we don't have a 'Self' type, but turn it - // into a monomorphic function type. - auto funcTy = declTy->castTo(); - declTy = FunctionType::get(funcTy->getInput(), - funcTy->getResult()); - } else { - // For properties, strip off ownership. - declTy = declTy->getReferenceStorageReferent(); - } - - // Ignore the optionality of initializers when comparing types; - // we'll enforce this separately - if (ctor) { - declTy = dropResultOptionality(declTy, 1); - } + auto declTy = getMemberTypeForComparison(TC.Context, decl); // Look for members with the same name and matching types as this // one. @@ -5350,44 +5373,20 @@ class DeclChecker : public DeclVisitor { // checked by name. } - // Check whether the types are identical. - auto parentDeclTy = owningTy->adjustSuperclassMemberDeclType( - parentDecl, decl, parentDecl->getInterfaceType()); - parentDeclTy = parentDeclTy->getUnlabeledType(TC.Context); - if (method) { - // For methods, strip off the 'Self' type. - parentDeclTy = parentDeclTy->castTo()->getResult(); - adjustFunctionTypeForOverride(parentDeclTy); - } else { - // For properties, strip off ownership. - parentDeclTy = parentDeclTy->getReferenceStorageReferent(); - } - - // Ignore the optionality of initializers when comparing types; - // we'll enforce this separately if (ctor) { - parentDeclTy = dropResultOptionality(parentDeclTy, 1); - // Factory methods cannot be overridden. auto parentCtor = cast(parentDecl); if (parentCtor->isFactoryInit()) continue; } - // Canonicalize with respect to the override's generic signature, if any. - auto *genericSig = decl->getInnermostDeclContext() - ->getGenericSignatureOfContext(); - - auto canDeclTy = declTy->getCanonicalType(genericSig); - auto canParentDeclTy = parentDeclTy->getCanonicalType(genericSig); - - auto declIUOAttr = - decl->getAttrs().hasAttribute(); - auto parentDeclIUOAttr = - parentDecl->getAttrs() - .hasAttribute(); + // Check whether the types are identical. + auto parentDeclTy = + getMemberTypeForComparison(TC.Context, parentDecl, decl); + if (parentDeclTy->hasError()) + continue; - if (declIUOAttr == parentDeclIUOAttr && canDeclTy == canParentDeclTy) { + if (isOverrideBasedOnType(decl, declTy, parentDecl, parentDeclTy)) { matches.push_back({parentDecl, true, parentDeclTy}); hadExactMatch = true; continue; @@ -5506,8 +5505,7 @@ class DeclChecker : public DeclVisitor { parentOwnership = ReferenceOwnership::Strong; if (parentOwnership != ownershipAttr->get()) { TC.diagnose(decl, diag::override_ownership_mismatch, - (unsigned)parentOwnership, - (unsigned)ownershipAttr->get()); + parentOwnership, ownershipAttr->get()); TC.diagnose(matchDecl, diag::overridden_here); } } @@ -6192,22 +6190,12 @@ class DeclChecker : public DeclVisitor { } void visitEnumElementDecl(EnumElementDecl *EED) { - if (!IsFirstPass) { - return; - } - TC.validateDecl(EED); TC.checkDeclAttributes(EED); checkAccessControl(TC, EED); } void visitExtensionDecl(ExtensionDecl *ED) { - if (!IsFirstPass) { - for (Decl *Member : ED->getMembers()) - visit(Member); - return; - } - TC.validateExtension(ED); TC.checkDeclAttributesEarly(ED); @@ -6248,7 +6236,7 @@ class DeclChecker : public DeclVisitor { for (Decl *Member : ED->getMembers()) visit(Member); - TC.checkConformancesInContext(ED, ED); + TC.ConformanceContexts.push_back(ED); if (!ED->isInvalid()) TC.checkDeclAttributes(ED); @@ -6302,19 +6290,6 @@ class DeclChecker : public DeclVisitor { } void visitConstructorDecl(ConstructorDecl *CD) { - if (!IsFirstPass) { - if (CD->getBody()) { - TC.definedFunctions.push_back(CD); - } else if (requiresDefinition(CD)) { - // Complain if we should have a body. - TC.diagnose(CD->getLoc(), diag::missing_initializer_def); - } - } - - if (!IsFirstPass) { - return; - } - TC.validateDecl(CD); // If this initializer overrides a 'required' initializer, it must itself @@ -6359,18 +6334,21 @@ class DeclChecker : public DeclVisitor { TC.checkDeclAttributes(CD); checkAccessControl(TC, CD); - } - void visitDestructorDecl(DestructorDecl *DD) { - if (!IsFirstPass) { - if (DD->getBody()) - TC.definedFunctions.push_back(DD); - - return; + if (CD->hasBody() && !CD->isMemberwiseInitializer()) { + TC.definedFunctions.push_back(CD); + } else if (requiresDefinition(CD)) { + // Complain if we should have a body. + TC.diagnose(CD->getLoc(), diag::missing_initializer_def); } + } + void visitDestructorDecl(DestructorDecl *DD) { TC.validateDecl(DD); TC.checkDeclAttributes(DD); + + if (DD->hasBody()) + TC.definedFunctions.push_back(DD); } }; } // end anonymous namespace @@ -6435,9 +6413,9 @@ bool TypeChecker::isAvailabilitySafeForConformance( return requirementInfo.isContainedIn(witnessInfo); } -void TypeChecker::typeCheckDecl(Decl *D, bool isFirstPass) { +void TypeChecker::typeCheckDecl(Decl *D) { checkForForbiddenPrefix(D); - DeclChecker(*this, isFirstPass).visit(D); + DeclChecker(*this).visit(D); } // A class is @objc if it does not have generic ancestry, and it either has @@ -6461,10 +6439,13 @@ static Optional shouldMarkClassAsObjC(TypeChecker &TC, // Only allow ObjC-rooted classes to be @objc. // (Leave a hole for test cases.) - if (kind == ObjCClassKind::ObjCWithSwiftRoot && - TC.getLangOpts().EnableObjCAttrRequiresFoundation) { - TC.diagnose(attr->getLocation(), diag::invalid_objc_swift_rooted_class) - .fixItRemove(attr->getRangeWithAt()); + if (kind == ObjCClassKind::ObjCWithSwiftRoot) { + if (TC.getLangOpts().EnableObjCAttrRequiresFoundation) + TC.diagnose(attr->getLocation(), diag::invalid_objc_swift_rooted_class) + .fixItRemove(attr->getRangeWithAt()); + if (!TC.getLangOpts().EnableObjCInterop) + TC.diagnose(attr->getLocation(), diag::objc_interop_disabled) + .fixItRemove(attr->getRangeWithAt()); } return ObjCReason::ExplicitlyObjC; @@ -6485,6 +6466,15 @@ static void validateTypealiasType(TypeChecker &tc, TypeAliasDecl *typeAlias) { options |= TypeResolutionFlags::KnownNonCascadingDependency; } + // This can happen when code completion is attempted inside + // of typealias underlying type e.g. `typealias F = () -> Int#^TOK^#` + auto underlyingType = typeAlias->getUnderlyingTypeLoc(); + if (underlyingType.isNull()) { + typeAlias->setInterfaceType(ErrorType::get(tc.Context)); + typeAlias->setInvalid(); + return; + } + if (typeAlias->getDeclContext()->isModuleScopeContext() && typeAlias->getGenericParams() == nullptr) { IterativeTypeChecker ITC(tc); @@ -6886,7 +6876,7 @@ void TypeChecker::validateDecl(ValueDecl *D) { if (mentionsItself) { diagnose(defaultDefinition.getLoc(), diag::recursive_type_reference, assocType->getDescriptiveKind(), assocType->getName()); - diagnose(assocType, diag::type_declared_here); + diagnose(assocType, diag::kind_declared_here, DescriptiveDeclKind::Type); } } } @@ -6953,7 +6943,7 @@ void TypeChecker::validateDecl(ValueDecl *D) { } if (!isa(nominal)) - DeclsToFinalize.insert(nominal); + requestNominalLayout(nominal); break; } @@ -6977,29 +6967,26 @@ void TypeChecker::validateDecl(ValueDecl *D) { if (!aliasDecl->isGeneric()) { aliasDecl->setGenericEnvironment(proto->getGenericEnvironment()); - // If the underlying alias declaration has a type parameter, - // we have unresolved dependent member types we will need to deal - // with. Wipe out the types and validate them again. - // FIXME: We never should have recorded such a type in the first - // place. - if (!aliasDecl->getUnderlyingTypeLoc().getType() || - aliasDecl->getUnderlyingTypeLoc().getType() - ->findUnresolvedDependentMemberType()) { - aliasDecl->getUnderlyingTypeLoc().setType(Type(), - /*validated=*/false); - validateAccessControl(aliasDecl); - - // Check generic parameters, if needed. - bool validated = aliasDecl->hasValidationStarted(); + // The generic environment didn't exist until now, we may have + // unresolved types we will need to deal with, and need to record the + // appropriate substitutions for that environment. Wipe out the types + // and validate them again. + aliasDecl->getUnderlyingTypeLoc().setType(Type(), + /*validated=*/false); + aliasDecl->setInterfaceType(Type()); + + validateAccessControl(aliasDecl); + + // Check generic parameters, if needed. + bool validated = aliasDecl->hasValidationStarted(); + if (!validated) + aliasDecl->setIsBeingValidated(); + SWIFT_DEFER { if (!validated) - aliasDecl->setIsBeingValidated(); - SWIFT_DEFER { - if (!validated) - aliasDecl->setIsBeingValidated(false); - }; + aliasDecl->setIsBeingValidated(false); + }; - validateTypealiasType(*this, aliasDecl); - } + validateTypealiasType(*this, aliasDecl); } } } @@ -7019,8 +7006,8 @@ void TypeChecker::validateDecl(ValueDecl *D) { diagnose(proto->getLoc(), diag::objc_protocol_inherits_non_objc_protocol, proto->getDeclaredType(), inherited->getDeclaredType()); - diagnose(inherited->getLoc(), diag::protocol_here, - inherited->getName()); + diagnose(inherited->getLoc(), diag::kind_identifier_declared_here, + DescriptiveDeclKind::Protocol, inherited->getName()); isObjC = None; } } @@ -7039,50 +7026,58 @@ void TypeChecker::validateDecl(ValueDecl *D) { break; } - case DeclKind::Var: case DeclKind::Param: { - auto VD = cast(D); + // FIXME: This case is hit when code completion occurs in a function + // parameter list. Previous parameters are definitely in scope, but + // we don't really know how to type-check them. + // We can also hit this when code-completing in a closure body. + // + // FIXME: Also, note that we don't call setValidationStarted() here, + // because the ExprCleanser clears the type of ParamDecls, so we + // can end up here multiple times for the same ParamDecl. + auto *PD = cast(D); + if (!PD->hasInterfaceType()) + PD->markInvalid(); + + break; + } + + case DeclKind::Var: { + auto *VD = cast(D); + auto *PBD = VD->getParentPatternBinding(); + + // Note that we need to handle the fact that some VarDecls don't + // have a PatternBindingDecl, for example the iterator in a + // 'for ... in ...' loop. + if (PBD == nullptr) { + if (!VD->hasInterfaceType()) { + VD->setValidationStarted(); + VD->markInvalid(); + } + + break; + } + + // If we're already checking our PatternBindingDecl, bail out + // without setting our own 'is being validated' flag, since we + // will attempt validation again later. + if (PBD->isBeingValidated()) + return; D->setIsBeingValidated(); if (!VD->hasInterfaceType()) { - if (VD->isSelfParameter()) { - if (!VD->hasInterfaceType()) { - VD->setInterfaceType(ErrorType::get(Context)); - VD->setInvalid(); - } - recordSelfContextType(cast(VD->getDeclContext())); - } else if (PatternBindingDecl *PBD = VD->getParentPatternBinding()) { - if (PBD->isBeingValidated()) { - diagnose(VD, diag::pattern_used_in_type, VD->getName()); - - } else { - validatePatternBindingEntries(*this, PBD); - } + // Attempt to infer the type using initializer expressions. + validatePatternBindingEntries(*this, PBD); - auto parentPattern = VD->getParentPattern(); - if (PBD->isInvalid() || !parentPattern->hasType()) { - parentPattern->setType(ErrorType::get(Context)); - setBoundVarsTypeError(parentPattern, Context); - - // If no type has been set for the initializer, we need to diagnose - // the failure. - if (VD->getParentInitializer() && - !VD->getParentInitializer()->getType()) { - diagnose(parentPattern->getLoc(), diag::identifier_init_failure, - parentPattern->getBoundName()); - } - } - } else { - // FIXME: This case is hit when code completion occurs in a function - // parameter list. Previous parameters are definitely in scope, but - // we don't really know how to type-check them. - // We can also hit this when code-completing in a closure body. - assert(isa(D->getDeclContext()) || - isa(D->getDeclContext()) || - isa(D->getDeclContext())); - VD->markInvalid(); + auto parentPattern = VD->getParentPattern(); + if (PBD->isInvalid() || !parentPattern->hasType()) { + parentPattern->setType(ErrorType::get(Context)); + setBoundVarsTypeError(parentPattern, Context); } + + // Should have set a type above. + assert(VD->hasInterfaceType()); } // We're not really done with processing the signature yet, but @@ -7136,33 +7131,28 @@ void TypeChecker::validateDecl(ValueDecl *D) { } } - // Infer 'dynamic' before touching accessors. - inferDynamic(Context, VD); - // If this variable is a class member, mark it final if the // class is final, or if it was declared with 'let'. - if (auto cls = dyn_cast(nominalDecl)) { - if (cls->isFinal() || VD->isLet()) { - if (!VD->isFinal()) { - makeFinal(Context, VD); - } - } - if (VD->isStatic()) { - auto staticSpelling = - VD->getParentPatternBinding()->getStaticSpelling(); - if (staticSpelling == StaticSpellingKind::KeywordStatic) { - auto finalAttr = VD->getAttrs().getAttribute(); - if (finalAttr) { - auto finalRange = finalAttr->getRange(); - if (finalRange.isValid()) - diagnose(finalRange.Start, diag::decl_already_final) - .highlight(finalRange) - .fixItRemove(finalRange); - } - makeFinal(Context, VD); - } + auto staticSpelling = + VD->getParentPatternBinding()->getStaticSpelling(); + inferFinalAndDiagnoseIfNeeded(*this, VD, staticSpelling); + + if (VD->isLet() && isa(nominalDecl)) { + makeFinal(Context, VD); + + if (VD->getFormalAccess() == AccessLevel::Open) { + auto diagID = diag::implicitly_final_cannot_be_open; + if (!Context.isSwiftVersionAtLeast(5)) + diagID = diag::implicitly_final_cannot_be_open_swift4; + auto inFlightDiag = + diagnose(D, diagID, + static_cast(ImplicitlyFinalReason::Let)); + fixItAccess(inFlightDiag, D, AccessLevel::Public); } } + + // Infer 'dynamic' after 'final' but before touching accessors. + inferDynamic(Context, VD); } // Perform accessor-related validation. @@ -7179,6 +7169,14 @@ void TypeChecker::validateDecl(ValueDecl *D) { auto *FD = cast(D); assert(!FD->hasInterfaceType()); + // Bail out if we're in a recursive validation situation. + if (auto accessor = dyn_cast(FD)) { + auto *storage = accessor->getStorage(); + validateDecl(storage); + if (!storage->hasValidSignature()) + return; + } + checkDeclAttributesEarly(FD); computeAccessLevel(FD); @@ -7215,7 +7213,6 @@ void TypeChecker::validateDecl(ValueDecl *D) { // FIXME: should this include the generic signature? if (auto accessor = dyn_cast(FD)) { auto storage = accessor->getStorage(); - validateDecl(storage); // Note that it's important for correctness that we're filling in // empty TypeLocs, because otherwise revertGenericFuncSignature might @@ -7453,6 +7450,9 @@ void TypeChecker::validateDecl(ValueDecl *D) { errorConvention))) isObjC = None; markAsObjC(*this, FD, isObjC, errorConvention); + + inferFinalAndDiagnoseIfNeeded(*this, FD, FD->getStaticSpelling()); + inferDynamic(Context, FD); } // If the function is exported to C, it must be representable in (Obj-)C. @@ -7467,28 +7467,6 @@ void TypeChecker::validateDecl(ValueDecl *D) { } } - inferDynamic(Context, FD); - - // If this is a class member, mark it final if the class is final. - if (auto cls = FD->getDeclContext()->getAsClassOrClassExtensionContext()) { - if (cls->isFinal() && !FD->isFinal()) { - makeFinal(Context, FD); - } - // static func declarations in classes are synonyms - // for `class final func` declarations. - if (FD->getStaticSpelling() == StaticSpellingKind::KeywordStatic) { - auto finalAttr = FD->getAttrs().getAttribute(); - if (finalAttr) { - auto finalRange = finalAttr->getRange(); - if (finalRange.isValid()) - diagnose(finalRange.Start, diag::decl_already_final) - .highlight(finalRange) - .fixItRemove(finalRange); - } - makeFinal(Context, FD); - } - } - checkDeclAttributes(FD); break; @@ -7691,7 +7669,7 @@ void TypeChecker::validateDecl(ValueDecl *D) { && "Decl parsing must prevent destructors outside of types!"); checkDeclAttributesEarly(DD); - DD->copyFormalAccessFrom(enclosingClass); + DD->copyFormalAccessFrom(enclosingClass, /*sourceIsParentContext*/true); configureImplicitSelf(*this, DD); @@ -7801,13 +7779,9 @@ void TypeChecker::validateDecl(ValueDecl *D) { } // Member subscripts need some special validation logic. - if (auto nominalDecl = dc->getAsNominalTypeOrNominalTypeExtensionContext()) { + if (dc->isTypeContext()) { // If this is a class member, mark it final if the class is final. - if (auto cls = dyn_cast(nominalDecl)) { - if (cls->isFinal() && !SD->isFinal()) { - makeFinal(Context, SD); - } - } + inferFinalAndDiagnoseIfNeeded(*this, SD, StaticSpellingKind::None); // A subscript is ObjC-compatible if it's explicitly @objc, or a // member of an ObjC-compatible class or protocol. @@ -8061,11 +8035,6 @@ static void finalizeType(TypeChecker &TC, NominalTypeDecl *nominal) { assert(!nominal->hasClangNode()); assert(isa(nominal->getModuleScopeContext())); - Optional lazyVarsAlreadyHaveImplementation; - - if (auto *classDecl = dyn_cast(nominal)) - TC.requestSuperclassLayout(classDecl); - for (auto *D : nominal->getMembers()) { auto VD = dyn_cast(D); if (!VD) @@ -8077,40 +8046,44 @@ static void finalizeType(TypeChecker &TC, NominalTypeDecl *nominal) { TC.validateDecl(VD); // The only thing left to do is synthesize storage for lazy variables. - // We only have to do that if it's a type from another file, though. - // In NDEBUG builds, bail out as soon as we can. -#ifdef NDEBUG - if (lazyVarsAlreadyHaveImplementation.hasValue() && - lazyVarsAlreadyHaveImplementation.getValue()) - continue; -#endif auto *prop = dyn_cast(D); if (!prop) continue; if (prop->getAttrs().hasAttribute() && !prop->isStatic() && prop->getGetter()) { - bool hasImplementation = prop->getGetter()->hasBody(); - - if (lazyVarsAlreadyHaveImplementation.hasValue()) { - assert(lazyVarsAlreadyHaveImplementation.getValue() == - hasImplementation && - "only some lazy vars already have implementations"); - } else { - lazyVarsAlreadyHaveImplementation = hasImplementation; - } - - if (!hasImplementation) - TC.completeLazyVarImplementation(prop); + assert(!prop->getGetter()->hasBody()); + TC.completeLazyVarImplementation(prop); } } - // FIXME: We need to add implicit initializers and dtors when a decl is - // touched, because it affects vtable layout. If you're not defining the - // class, you shouldn't have to know what the vtable layout is. if (auto *CD = dyn_cast(nominal)) { + // We need to add implicit initializers and dtors because it + // affects vtable layout. TC.addImplicitConstructors(CD); CD->addImplicitDestructor(); + + // We need the superclass vtable layout as well. + TC.requestSuperclassLayout(CD); + + auto useConformance = [&](ProtocolDecl *protocol) { + if (auto ref = TC.conformsToProtocol( + CD->getDeclaredInterfaceType(), protocol, CD, + ConformanceCheckFlags::SkipConditionalRequirements, + SourceLoc())) { + if (ref->getConcrete()->getDeclContext() == CD) + TC.markConformanceUsed(*ref, CD); + } + }; + + // If the class is Encodable, Decodable or Hashable, force those + // conformances to ensure that the synthesized members appear in the vtable. + // + // FIXME: Generalize this to other protocols for which + // we can derive conformances. + useConformance(TC.Context.getProtocol(KnownProtocolKind::Decodable)); + useConformance(TC.Context.getProtocol(KnownProtocolKind::Encodable)); + useConformance(TC.Context.getProtocol(KnownProtocolKind::Hashable)); } // validateDeclForNameLookup will not trigger an immediate full @@ -8327,8 +8300,7 @@ static Type formExtensionInterfaceType(TypeChecker &tc, ExtensionDecl *ext, }, [](CanType dependentType, Type replacementType, - ProtocolType *protoType) { - auto proto = protoType->getDecl(); + ProtocolDecl *proto) { return ProtocolConformanceRef(proto); }); @@ -8336,7 +8308,7 @@ static Type formExtensionInterfaceType(TypeChecker &tc, ExtensionDecl *ext, } resultType = NameAliasType::get(typealias, parentType, subMap, - resultType); + resultType); } return resultType; @@ -8661,6 +8633,27 @@ static void diagnoseClassWithoutInitializers(TypeChecker &tc, } } +void TypeChecker::maybeDiagnoseClassWithoutInitializers(ClassDecl *classDecl) { + // Some heuristics to skip emitting a diagnostic if the class is already + // irreperably busted. + if (classDecl->isInvalid() || + classDecl->inheritsSuperclassInitializers(nullptr)) + return; + + auto *superclassDecl = classDecl->getSuperclassDecl(); + if (superclassDecl && + superclassDecl->hasMissingDesignatedInitializers()) + return; + + for (auto member : classDecl->lookupDirect(DeclBaseName::createConstructor())) { + auto ctor = dyn_cast(member); + if (ctor && ctor->isDesignatedInit()) + return; + } + + diagnoseClassWithoutInitializers(*this, classDecl); +} + /// Diagnose a missing required initializer. static void diagnoseMissingRequiredInitializer( TypeChecker &TC, @@ -8702,8 +8695,9 @@ static void diagnoseMissingRequiredInitializer( insertionLoc); // Find the indentation used on the indentation line. - StringRef indentation = Lexer::getIndentationForLine(TC.Context.SourceMgr, - indentationLoc); + StringRef extraIndentation; + StringRef indentation = Lexer::getIndentationForLine( + TC.Context.SourceMgr, indentationLoc, &extraIndentation); // Pretty-print the superclass initializer into a string. // FIXME: Form a new initializer by performing the appropriate @@ -8733,12 +8727,9 @@ static void diagnoseMissingRequiredInitializer( superInitializer->print(printer, options); } - // FIXME: Infer body indentation from the source rather than hard-coding - // 4 spaces. - // Add a dummy body. out << " {\n"; - out << indentation << " fatalError(\""; + out << indentation << extraIndentation << "fatalError(\""; superInitializer->getFullName().printPretty(out); out << " has not been implemented\")\n"; out << indentation << "}\n"; @@ -8767,34 +8758,16 @@ void TypeChecker::addImplicitConstructors(NominalTypeDecl *decl) { if (decl->isInvalid()) return; - // Local function that produces the canonical parameter type of the given - // initializer. - // FIXME: Doesn't work properly for generics. - auto getInitializerParamType = [](ConstructorDecl *ctor) -> CanType { - auto interfaceTy = ctor->getInterfaceType(); - - // Skip the 'self' parameter. - auto uncurriedInitTy = interfaceTy->castTo()->getResult(); - - // Grab the parameter type; - auto paramTy = uncurriedInitTy->castTo()->getInput(); - - return paramTy->getCanonicalType(); - }; - // Bail out if we're validating one of our constructors already; we'll // revisit the issue later. if (isa(decl)) { - bool alreadyValidatingCtor = false; for (auto member : decl->getMembers()) { if (auto ctor = dyn_cast(member)) { validateDecl(ctor); if (!ctor->hasValidSignature()) - alreadyValidatingCtor = true; + return; } } - if (alreadyValidatingCtor) - return; } decl->setAddedImplicitInitializers(); @@ -8804,38 +8777,9 @@ void TypeChecker::addImplicitConstructors(NominalTypeDecl *decl) { bool FoundMemberwiseInitializedProperty = false; bool SuppressDefaultInitializer = false; bool SuppressMemberwiseInitializer = false; - bool FoundSynthesizedInit = false; bool FoundDesignatedInit = false; - // Before we look for constructors, we need to make sure that all synthesized - // initializers are properly synthesized. - // - // NOTE: Lookups of synthesized initializers MUST come after - // decl->setAddedImplicitInitializers() in case synthesis requires - // protocol conformance checking, which might be recursive here. - // FIXME: Disable this code and prevent _any_ implicit constructors from doing - // this. Investigate why this hasn't worked otherwise. - DeclName synthesizedInitializers[1] = { - // init(from:) is synthesized by derived conformance to Decodable. - DeclName(Context, DeclBaseName::createConstructor(), Context.Id_from) - }; - - auto initializerIsSynthesized = [=](ConstructorDecl *initializer) { - if (!initializer->isImplicit()) - return false; - - for (auto &name : synthesizedInitializers) - if (initializer->getFullName() == name) - return true; - - return false; - }; - - for (auto &name : synthesizedInitializers) { - synthesizeMemberForLookup(decl, name); - } - - SmallPtrSet initializerParamTypes; + SmallVector, 4> declaredInitializers; llvm::SmallPtrSet overriddenInits; if (decl->hasClangNode() && isa(decl)) { // Objective-C classes may have interesting initializers in extensions. @@ -8856,19 +8800,19 @@ void TypeChecker::addImplicitConstructors(NominalTypeDecl *decl) { } else { for (auto member : decl->getMembers()) { if (auto ctor = dyn_cast(member)) { - // Synthesized initializers others than the default initializer should - // not prevent default initializer synthesis. - if (initializerIsSynthesized(ctor)) { - FoundSynthesizedInit = true; - } else if (ctor->isDesignatedInit()) { + // Initializers that were synthesized to fulfill derived conformances + // should not prevent default initializer synthesis. + if (ctor->isDesignatedInit() && !ctor->isSynthesized()) FoundDesignatedInit = true; - } if (isa(decl)) continue; - if (!ctor->isInvalid()) - initializerParamTypes.insert(getInitializerParamType(ctor)); + if (!ctor->isInvalid()) { + auto type = getMemberTypeForComparison(Context, ctor, nullptr, + /*stripLabels*/ false); + declaredInitializers.push_back({ctor, type}); + } if (auto overridden = ctor->getOverriddenDecl()) overriddenInits.insert(overridden); @@ -8934,8 +8878,10 @@ void TypeChecker::addImplicitConstructors(NominalTypeDecl *decl) { } if (auto structDecl = dyn_cast(decl)) { - if (!FoundDesignatedInit && !SuppressMemberwiseInitializer - && !structDecl->hasUnreferenceableStorage()) { + assert(!structDecl->hasUnreferenceableStorage() && + "User-defined structs cannot have unreferenceable storage"); + + if (!FoundDesignatedInit && !SuppressMemberwiseInitializer) { // For a struct with memberwise initialized properties, we add a // memberwise init. if (FoundMemberwiseInitializedProperty) { @@ -8957,13 +8903,13 @@ void TypeChecker::addImplicitConstructors(NominalTypeDecl *decl) { // FIXME: Currently skipping generic classes. auto classDecl = cast(decl); if (classDecl->hasSuperclass()) { - bool canInheritInitializers = !FoundDesignatedInit; + bool canInheritInitializers = (!SuppressDefaultInitializer && + !FoundDesignatedInit); // We can't define these overrides if we have any uninitialized // stored properties. - if (SuppressDefaultInitializer && !FoundDesignatedInit - && !FoundSynthesizedInit && !classDecl->hasClangNode()) { - diagnoseClassWithoutInitializers(*this, classDecl); + if (SuppressDefaultInitializer && !FoundDesignatedInit && + !classDecl->hasClangNode()) { return; } @@ -9021,10 +8967,22 @@ void TypeChecker::addImplicitConstructors(NominalTypeDecl *decl) { // A designated or required initializer has not been overridden. + bool alreadyDeclared = false; + for (const auto &ctorAndType : declaredInitializers) { + auto *ctor = ctorAndType.first; + auto type = ctorAndType.second; + auto parentType = getMemberTypeForComparison( + Context, superclassCtor, ctor, /*stripLabels*/ false); + + if (isOverrideBasedOnType(ctor, type, superclassCtor, parentType)) { + alreadyDeclared = true; + break; + } + } + // If we have already introduced an initializer with this parameter type, // don't add one now. - if (!initializerParamTypes.insert( - getInitializerParamType(superclassCtor)).second) + if (alreadyDeclared) continue; // If we're inheriting initializers, create an override delegating @@ -9044,6 +9002,7 @@ void TypeChecker::addImplicitConstructors(NominalTypeDecl *decl) { // We have a designated initializer. Create an override of it. if (auto ctor = createDesignatedInitOverride( *this, classDecl, superclassCtor, kind)) { + Context.addSynthesizedDecl(ctor); classDecl->addMember(ctor); } } @@ -9063,12 +9022,8 @@ void TypeChecker::addImplicitConstructors(NominalTypeDecl *decl) { // constructor. // ... unless there are uninitialized stored properties. - if (SuppressDefaultInitializer) { - if (!FoundSynthesizedInit) - diagnoseClassWithoutInitializers(*this, classDecl); - + if (SuppressDefaultInitializer) return; - } defineDefaultConstructor(decl); } @@ -9079,14 +9034,9 @@ void TypeChecker::synthesizeMemberForLookup(NominalTypeDecl *target, auto baseName = member.getBaseName(); // Checks whether the target conforms to the given protocol. If the - // conformance is incomplete, check the conformance to force synthesis, if - // possible. + // conformance is incomplete, force the conformance. // - // Swallows diagnostics if conformance checking is already in progress (so we - // don't display diagnostics twice). - // - // Returns whether the target conforms to the protocol and the conformance is - // complete. + // Returns whether the target conforms to the protocol. auto evaluateTargetConformanceTo = [&](ProtocolDecl *protocol) { auto targetType = target->getDeclaredInterfaceType(); if (auto ref = conformsToProtocol( @@ -9095,26 +9045,12 @@ void TypeChecker::synthesizeMemberForLookup(NominalTypeDecl *target, ConformanceCheckFlags::SkipConditionalRequirements), SourceLoc())) { if (auto *conformance = ref->getConcrete()->getRootNormalConformance()) { - if (conformance->isIncomplete()) { - // Check conformance, forcing synthesis. - // - // If synthesizing conformance fails, this will produce diagnostics. - // If conformance checking was already in progress elsewhere, though, - // this could produce diagnostics twice. - // - // To prevent this duplication, we swallow the diagnostics if the - // state of the conformance is not Incomplete. - DiagnosticTransaction transaction(Context.Diags); - auto shouldSwallowDiagnostics = - conformance->getState() != ProtocolConformanceState::Incomplete; - + if (conformance->getState() == ProtocolConformanceState::Incomplete) { checkConformance(conformance); - if (shouldSwallowDiagnostics) - transaction.abort(); - - return conformance->isComplete(); } } + + return true; } return false; @@ -9136,13 +9072,6 @@ void TypeChecker::synthesizeMemberForLookup(NominalTypeDecl *target, auto *encodableProto = Context.getProtocol(KnownProtocolKind::Encodable); if (!evaluateTargetConformanceTo(decodableProto)) (void)evaluateTargetConformanceTo(encodableProto); - } else if (baseName.getIdentifier() == Context.Id_allCases || - baseName.getIdentifier() == Context.Id_AllCases) { - // If the target should conform to the CaseIterable protocol, check the - // conformance here to attempt synthesis. - auto *caseIterableProto - = Context.getProtocol(KnownProtocolKind::CaseIterable); - (void)evaluateTargetConformanceTo(caseIterableProto); } } else { auto argumentNames = member.getArgumentNames(); @@ -9171,32 +9100,6 @@ void TypeChecker::synthesizeMemberForLookup(NominalTypeDecl *target, } } -void TypeChecker::addImplicitStructConformances(StructDecl *SD) { - // Type-check the protocol conformances of the struct decl to instantiate its - // derived conformances. - checkConformancesInContext(SD, SD); -} - -void TypeChecker::addImplicitEnumConformances(EnumDecl *ED) { - // Type-check the raw values of the enum. - for (auto elt : ED->getAllElements()) { - assert(elt->hasRawValueExpr()); - if (elt->getTypeCheckedRawValueExpr()) continue; - Expr *typeChecked = elt->getRawValueExpr(); - Type rawTy = ED->mapTypeIntoContext(ED->getRawType()); - auto resultTy = typeCheckExpression( - typeChecked, ED, TypeLoc::withoutLoc(rawTy), CTP_EnumCaseRawValue); - assert(resultTy); - (void)resultTy; - elt->setTypeCheckedRawValueExpr(typeChecked); - checkEnumElementErrorHandling(elt); - } - - // Type-check the protocol conformances of the enum decl to instantiate its - // derived conformances. - checkConformancesInContext(ED, ED); -} - void TypeChecker::defineDefaultConstructor(NominalTypeDecl *decl) { PrettyStackTraceDecl stackTrace("defining default constructor for", decl); @@ -9274,6 +9177,9 @@ void TypeChecker::defineDefaultConstructor(NominalTypeDecl *decl) { // Create an empty body for the default constructor. The type-check of the // constructor body will introduce default initializations of the members. ctor->setBody(BraceStmt::create(Context, SourceLoc(), { }, SourceLoc())); + + // Make sure we type check the constructor later. + Context.addSynthesizedDecl(ctor); } static void validateAttributes(TypeChecker &TC, Decl *D) { diff --git a/lib/Sema/TypeCheckError.cpp b/lib/Sema/TypeCheckError.cpp index 7ed5732e9dac0..1fe7f16568ab7 100644 --- a/lib/Sema/TypeCheckError.cpp +++ b/lib/Sema/TypeCheckError.cpp @@ -41,23 +41,19 @@ class AbstractFunction { }; unsigned TheKind : 2; unsigned IsRethrows : 1; - unsigned IsProtocolMethod : 1; unsigned ParamCount : 28; public: explicit AbstractFunction(Kind kind, Expr *fn) : TheKind(kind), IsRethrows(false), - IsProtocolMethod(false), ParamCount(1) { TheExpr = fn; } - explicit AbstractFunction(AbstractFunctionDecl *fn, - bool isProtocolMethod) + explicit AbstractFunction(AbstractFunctionDecl *fn) : TheKind(Kind::Function), IsRethrows(fn->getAttrs().hasAttribute()), - IsProtocolMethod(isProtocolMethod), ParamCount(fn->getNumParameterLists()) { TheFunction = fn; } @@ -65,7 +61,6 @@ class AbstractFunction { explicit AbstractFunction(AbstractClosureExpr *closure) : TheKind(Kind::Closure), IsRethrows(false), - IsProtocolMethod(false), ParamCount(1) { TheClosure = closure; } @@ -73,7 +68,6 @@ class AbstractFunction { explicit AbstractFunction(ParamDecl *parameter) : TheKind(Kind::Parameter), IsRethrows(false), - IsProtocolMethod(false), ParamCount(1) { TheParameter = parameter; } @@ -84,7 +78,7 @@ class AbstractFunction { bool isBodyRethrows() const { return IsRethrows; } unsigned getNumArgumentsForFullApply() const { - return (ParamCount - unsigned(IsProtocolMethod)); + return ParamCount; } Type getType() const { @@ -120,10 +114,6 @@ class AbstractFunction { return TheExpr; } - bool isProtocolMethod() const { - return IsProtocolMethod; - } - static AbstractFunction decomposeApply(ApplyExpr *apply, SmallVectorImpl &args) { Expr *fn; @@ -150,6 +140,9 @@ class AbstractFunction { // Look through base-ignored qualified references (Module.methodName). } else if (auto baseIgnored = dyn_cast(fn)) { fn = baseIgnored->getRHS(); + // Look through closure capture lists. + } else if (auto captureList = dyn_cast(fn)) { + fn = captureList->getClosureBody(); } else { break; } @@ -159,18 +152,11 @@ class AbstractFunction { if (auto declRef = dyn_cast(fn)) { ValueDecl *decl = declRef->getDecl(); if (auto fn = dyn_cast(decl)) { - return AbstractFunction(fn, false); + return AbstractFunction(fn); } else if (auto param = dyn_cast(decl)) { return AbstractFunction(param); } - // Archetype function references. - } else if (auto memberRef = dyn_cast(fn)) { - if (auto fn = dyn_cast( - memberRef->getMember().getDecl())) { - return AbstractFunction(fn, true); - } - // Closures. } else if (auto closure = dyn_cast(fn)) { return AbstractFunction(closure); @@ -459,21 +445,15 @@ class ApplyClassifier { Type type = fnRef.getType(); if (!type) return Classification::forInvalidCode(); - if (fnRef.isProtocolMethod()) { - if (auto fnType = type->getAs()) { - type = fnType->getResult(); - } else { - Classification::forInvalidCode(); - } - } - // Use the most significant result from the arguments. Classification result; for (auto arg : reversed(args)) { auto fnType = type->getAs(); if (!fnType) return Classification::forInvalidCode(); - result.merge(classifyRethrowsArgument(arg, fnType->getInput())); + auto paramType = FunctionType::composeInput(fnType->getASTContext(), + fnType->getParams(), false); + result.merge(classifyRethrowsArgument(arg, paramType)); type = fnType->getResult(); } return result; diff --git a/lib/Sema/TypeCheckExpr.cpp b/lib/Sema/TypeCheckExpr.cpp index f5d9ca45a4f13..c85c184d233f0 100644 --- a/lib/Sema/TypeCheckExpr.cpp +++ b/lib/Sema/TypeCheckExpr.cpp @@ -102,7 +102,7 @@ Expr *TypeChecker::substituteInputSugarTypeForResult(ApplyExpr *E) { // constructed. Apply the sugar onto it. if (auto FT = E->getType()->getAs()) if (FT->getResult()->isEqual(resultSugar) && !resultSugar->isCanonical()){ - auto NFT = FunctionType::get(FT->getInput(), resultSugar, + auto NFT = FunctionType::get(FT->getParams(), resultSugar, FT->getExtInfo()); E->setType(NFT); return E; diff --git a/lib/Sema/TypeCheckExprObjC.cpp b/lib/Sema/TypeCheckExprObjC.cpp index 6a9e8899bf3ca..cb7f3c8649697 100644 --- a/lib/Sema/TypeCheckExprObjC.cpp +++ b/lib/Sema/TypeCheckExprObjC.cpp @@ -15,6 +15,7 @@ // //===----------------------------------------------------------------------===// #include "TypeChecker.h" +#include "TypoCorrection.h" #include "swift/Basic/Range.h" using namespace swift; @@ -248,11 +249,12 @@ Optional TypeChecker::checkObjCKeyPathExpr(DeclContext *dc, // If we didn't find anything, try to apply typo-correction. bool resultsAreFromTypoCorrection = false; if (!lookup) { + TypoCorrectionResults corrections(*this, componentName, + DeclNameLoc(componentNameLoc)); performTypoCorrection(dc, DeclRefKind::Ordinary, lookupType, - componentName, componentNameLoc, (lookupType ? defaultMemberTypeLookupOptions : defaultUnqualifiedLookupOptions), - lookup); + corrections); if (currentType) diagnose(componentNameLoc, diag::could_not_find_type_member, @@ -262,10 +264,8 @@ Optional TypeChecker::checkObjCKeyPathExpr(DeclContext *dc, componentName, false); // Note all the correction candidates. - for (auto &result : lookup) { - noteTypoCorrection(componentName, DeclNameLoc(componentNameLoc), - result.getValueDecl()); - } + corrections.noteAllCandidates(); + corrections.addAllCandidatesToLookup(lookup); isInvalid = true; if (!lookup) break; @@ -277,7 +277,7 @@ Optional TypeChecker::checkObjCKeyPathExpr(DeclContext *dc, // If we have more than one result, filter out unavailable or // obviously unusable candidates. if (lookup.size() > 1) { - lookup.filter([&](LookupResultEntry result) -> bool { + lookup.filter([&](LookupResultEntry result, bool isOuter) -> bool { // Drop unavailable candidates. if (result.getValueDecl()->getAttrs().isUnavailable(Context)) return false; diff --git a/lib/Sema/TypeCheckGeneric.cpp b/lib/Sema/TypeCheckGeneric.cpp index d620bc7710f74..a6e044ff1c335 100644 --- a/lib/Sema/TypeCheckGeneric.cpp +++ b/lib/Sema/TypeCheckGeneric.cpp @@ -15,6 +15,7 @@ //===----------------------------------------------------------------------===// #include "TypeChecker.h" #include "GenericTypeResolver.h" +#include "TypoCorrection.h" #include "swift/AST/GenericEnvironment.h" #include "swift/AST/GenericSignatureBuilder.h" #include "swift/AST/ProtocolConformance.h" @@ -147,20 +148,18 @@ Type CompleteGenericTypeResolver::resolveDependentMemberType( } else { // Resolve the base to a potential archetype. // Perform typo correction. - LookupResult corrections; + TypoCorrectionResults corrections(tc, ref->getIdentifier(), + DeclNameLoc(ref->getIdLoc())); tc.performTypoCorrection(DC, DeclRefKind::Ordinary, MetatypeType::get(baseTy), - ref->getIdentifier(), ref->getIdLoc(), NameLookupFlags::ProtocolMembers, corrections, &builder); - // Filter out non-types. - corrections.filter([](const LookupResultEntry &result) { - return isa(result.getValueDecl()); - }); - // Check whether we have a single type result. - auto singleType = corrections.getSingleTypeResult(); + auto singleType = cast_or_null( + corrections.getUniqueCandidateMatching([](ValueDecl *result) { + return isa(result); + })); // If we don't have a single result, complain and fail. if (!singleType) { @@ -168,9 +167,7 @@ Type CompleteGenericTypeResolver::resolveDependentMemberType( SourceLoc nameLoc = ref->getIdLoc(); tc.diagnose(nameLoc, diag::invalid_member_type, name, baseTy) .highlight(baseRange); - for (const auto &suggestion : corrections) - tc.noteTypoCorrection(name, DeclNameLoc(nameLoc), - suggestion.getValueDecl()); + corrections.noteAllCandidates(); return ErrorType::get(tc.Context); } @@ -196,6 +193,16 @@ Type CompleteGenericTypeResolver::resolveDependentMemberType( // base type into it. auto concrete = ref->getBoundDecl(); tc.validateDeclForNameLookup(concrete); + + if (auto typeAlias = dyn_cast(concrete)) { + if (auto protocol = dyn_cast(typeAlias->getDeclContext())) { + // We need to make sure the generic environment of a surrounding protocol + // propagates to the typealias, since the former may not have existed when + // the typealiases type was first computed. + // FIXME: See the comment in the ProtocolDecl case of validateDecl(). + tc.validateDecl(protocol); + } + } if (!concrete->hasInterfaceType()) return ErrorType::get(tc.Context); if (baseTy->isTypeParameter()) { @@ -661,7 +668,7 @@ static void checkReferencedGenericParams(GenericContext *dc, return Action::Continue; } - SmallPtrSet &getReferencedGenericParams() { + SmallPtrSetImpl &getReferencedGenericParams() { return ReferencedGenericParams; } }; @@ -670,7 +677,8 @@ static void checkReferencedGenericParams(GenericContext *dc, // return type. ReferencedGenericTypeWalker paramsAndResultWalker; auto *funcTy = decl->getInterfaceType()->castTo(); - funcTy->getInput().walk(paramsAndResultWalker); + for (const auto ¶m : funcTy->getParams()) + param.getType().walk(paramsAndResultWalker); funcTy->getResult().walk(paramsAndResultWalker); // Set of generic params referenced in parameter types, diff --git a/lib/Sema/TypeCheckNameLookup.cpp b/lib/Sema/TypeCheckNameLookup.cpp index 0067607ab4d6a..02dba5cc08259 100644 --- a/lib/Sema/TypeCheckNameLookup.cpp +++ b/lib/Sema/TypeCheckNameLookup.cpp @@ -16,6 +16,7 @@ // //===----------------------------------------------------------------------===// #include "TypeChecker.h" +#include "TypoCorrection.h" #include "swift/AST/ExistentialLayout.h" #include "swift/AST/Initializer.h" #include "swift/AST/NameLookup.h" @@ -25,10 +26,23 @@ using namespace swift; -void LookupResult::filter(const std::function &pred) { +void LookupResult::filter( + llvm::function_ref pred) { + size_t index = 0; + size_t originalFirstOuter = IndexOfFirstOuterResult; Results.erase(std::remove_if(Results.begin(), Results.end(), [&](LookupResultEntry result) -> bool { - return !pred(result); + auto isInner = index < originalFirstOuter; + index++; + if (pred(result, !isInner)) + return false; + + // Need to remove this, which means, if it is + // an inner result, the outer results need to + // shift down. + if (isInner) + IndexOfFirstOuterResult--; + return true; }), Results.end()); } @@ -45,6 +59,8 @@ namespace { /// The vector of found declarations. SmallVector FoundDecls; + /// The vector of found declarations. + SmallVector FoundOuterDecls; /// The set of known declarations. llvm::SmallDenseMap, bool, 4> Known; @@ -54,7 +70,10 @@ namespace { NameLookupOptions options, bool isMemberLookup) : TC(tc), Result(result), DC(dc), Options(options), - IsMemberLookup(isMemberLookup) { } + IsMemberLookup(isMemberLookup) { + if (!TC.Context.LangOpts.EnableAccessControl) + Options |= NameLookupFlags::IgnoreAccessControl; + } ~LookupResultBuilder() { // If any of the results have a base, we need to remove @@ -72,25 +91,35 @@ namespace { // Remove any overridden declarations from the found-declarations set. removeOverriddenDecls(FoundDecls); + removeOverriddenDecls(FoundOuterDecls); // Remove any shadowed declarations from the found-declarations set. removeShadowedDecls(FoundDecls, DC->getParentModule(), &TC); + removeShadowedDecls(FoundOuterDecls, DC->getParentModule(), &TC); // Filter out those results that have been removed from the // found-declarations set. - unsigned foundIdx = 0, foundSize = FoundDecls.size(); - Result.filter([&](LookupResultEntry result) -> bool { - // If the current result matches the remaining found declaration, - // keep it and move to the next found declaration. - if (foundIdx < foundSize && - result.getValueDecl() == FoundDecls[foundIdx]) { - ++foundIdx; - return true; - } - - // Otherwise, this result should be filtered out. - return false; - }); + unsigned foundIdx = 0, foundSize = FoundDecls.size(), + foundOuterSize = FoundOuterDecls.size(); + Result.filter([&](LookupResultEntry result, bool isOuter) -> bool { + unsigned idx = foundIdx; + unsigned limit = foundSize; + ArrayRef decls = FoundDecls; + if (isOuter) { + idx = foundIdx - foundSize; + limit = foundOuterSize; + decls = FoundOuterDecls; + } + // If the current result matches the remaining found declaration, + // keep it and move to the next found declaration. + if (idx < limit && result.getValueDecl() == decls[idx]) { + ++foundIdx; + return true; + } + + // Otherwise, this result should be filtered out. + return false; + }); } /// Add a new result. @@ -102,7 +131,11 @@ namespace { /// /// \param foundInType The type through which we found the /// declaration. - void add(ValueDecl *found, DeclContext *baseDC, Type foundInType) { + /// + /// \param isOuter Whether this is an outer result (i.e. a result that isn't + /// from the innermost scope with results) + void add(ValueDecl *found, DeclContext *baseDC, Type foundInType, + bool isOuter) { ConformanceCheckOptions conformanceOptions; if (Options.contains(NameLookupFlags::KnownPrivate)) conformanceOptions |= ConformanceCheckFlags::InExpression; @@ -112,8 +145,11 @@ namespace { auto addResult = [&](ValueDecl *result) { if (Known.insert({{result, baseDC}, false}).second) { - Result.add(LookupResultEntry(baseDC, result)); - FoundDecls.push_back(result); + Result.add(LookupResultEntry(baseDC, result), isOuter); + if (isOuter) + FoundOuterDecls.push_back(result); + else + FoundDecls.push_back(result); } }; @@ -184,6 +220,16 @@ namespace { .second; } else if (found->isProtocolRequirement()) { witness = concrete->getWitnessDecl(found, &TC); + + // It is possible that a requirement is visible to us, but + // not the witness. In this case, just return the requirement; + // we will perform virtual dispatch on the concrete type. + if (witness && + !Options.contains(NameLookupFlags::IgnoreAccessControl) && + !witness->isAccessibleFrom(DC)) { + addResult(found); + return; + } } // FIXME: the "isa()" check will be wrong for @@ -198,21 +244,32 @@ namespace { }; } // end anonymous namespace +static UnqualifiedLookup::Options +convertToUnqualifiedLookupOptions(NameLookupOptions options) { + UnqualifiedLookup::Options newOptions; + if (options.contains(NameLookupFlags::KnownPrivate)) + newOptions |= UnqualifiedLookup::Flags::KnownPrivate; + if (options.contains(NameLookupFlags::ProtocolMembers)) + newOptions |= UnqualifiedLookup::Flags::AllowProtocolMembers; + if (options.contains(NameLookupFlags::IgnoreAccessControl)) + newOptions |= UnqualifiedLookup::Flags::IgnoreAccessControl; + if (options.contains(NameLookupFlags::IncludeOuterResults)) + newOptions |= UnqualifiedLookup::Flags::IncludeOuterResults; + + return newOptions; +} + LookupResult TypeChecker::lookupUnqualified(DeclContext *dc, DeclName name, SourceLoc loc, NameLookupOptions options) { - UnqualifiedLookup lookup( - name, dc, this, - options.contains(NameLookupFlags::KnownPrivate), - loc, - /*IsTypeLookup=*/false, - options.contains(NameLookupFlags::ProtocolMembers), - options.contains(NameLookupFlags::IgnoreAccessControl)); + UnqualifiedLookup lookup(name, dc, this, loc, + convertToUnqualifiedLookupOptions(options)); LookupResult result; LookupResultBuilder builder(*this, result, dc, options, /*memberLookup*/false); - for (const auto &found : lookup.Results) { + for (auto idx : indices(lookup.Results)) { + const auto &found = lookup.Results[idx]; // Determine which type we looked through to find this result. Type foundInType; @@ -226,7 +283,8 @@ LookupResult TypeChecker::lookupUnqualified(DeclContext *dc, DeclName name, assert(foundInType && "bogus base declaration?"); } - builder.add(found.getValueDecl(), found.getDeclContext(), foundInType); + builder.add(found.getValueDecl(), found.getDeclContext(), foundInType, + /*isOuter=*/idx >= lookup.IndexOfFirstOuterResult); } return result; } @@ -235,19 +293,17 @@ LookupResult TypeChecker::lookupUnqualifiedType(DeclContext *dc, DeclName name, SourceLoc loc, NameLookupOptions options) { + auto ulOptions = convertToUnqualifiedLookupOptions(options) | + UnqualifiedLookup::Flags::TypeLookup; { // Try lookup without ProtocolMembers first. UnqualifiedLookup lookup( - name, dc, this, - options.contains(NameLookupFlags::KnownPrivate), - loc, - /*IsTypeLookup=*/true, - /*AllowProtocolMembers=*/false, - options.contains(NameLookupFlags::IgnoreAccessControl)); + name, dc, this, loc, + ulOptions - UnqualifiedLookup::Flags::AllowProtocolMembers); if (!lookup.Results.empty() || !options.contains(NameLookupFlags::ProtocolMembers)) { - return LookupResult(lookup.Results); + return LookupResult(lookup.Results, lookup.IndexOfFirstOuterResult); } } @@ -258,14 +314,10 @@ TypeChecker::lookupUnqualifiedType(DeclContext *dc, DeclName name, // is called too early, we start resolving extensions -- even those // which do provide not conformances. UnqualifiedLookup lookup( - name, dc, this, - options.contains(NameLookupFlags::KnownPrivate), - loc, - /*IsTypeLookup=*/true, - /*AllowProtocolMembers=*/true, - options.contains(NameLookupFlags::IgnoreAccessControl)); - - return LookupResult(lookup.Results); + name, dc, this, loc, + ulOptions | UnqualifiedLookup::Flags::AllowProtocolMembers); + + return LookupResult(lookup.Results, lookup.IndexOfFirstOuterResult); } } @@ -296,7 +348,7 @@ LookupResult TypeChecker::lookupMember(DeclContext *dc, dc->lookupQualified(type, name, subOptions, this, lookupResults); for (auto found : lookupResults) - builder.add(found, nullptr, type); + builder.add(found, nullptr, type, /*isOuter=*/false); return result; } @@ -484,28 +536,30 @@ enum : unsigned { MaxCallEditDistanceFromBestCandidate = 1 }; -static unsigned getCallEditDistance(DeclName argName, DeclName paramName, +static unsigned getCallEditDistance(DeclName writtenName, + DeclName correctedName, unsigned maxEditDistance) { // TODO: consider arguments. // TODO: maybe ignore certain kinds of missing / present labels for the // first argument label? // TODO: word-based rather than character-based? - if (argName.getBaseName().getKind() != paramName.getBaseName().getKind()) { + if (writtenName.getBaseName().getKind() != + correctedName.getBaseName().getKind()) { return UnreasonableCallEditDistance; } - if (argName.getBaseName().getKind() != DeclBaseName::Kind::Normal) { + if (writtenName.getBaseName().getKind() != DeclBaseName::Kind::Normal) { return 0; } - assert(argName.getBaseName().getKind() == DeclBaseName::Kind::Normal); - StringRef argBase = argName.getBaseName().userFacingName(); - StringRef paramBase = paramName.getBaseName().userFacingName(); + StringRef writtenBase = writtenName.getBaseName().userFacingName(); + StringRef correctedBase = correctedName.getBaseName().userFacingName(); - unsigned distance = argBase.edit_distance(paramBase, maxEditDistance); + unsigned distance = writtenBase.edit_distance(correctedBase, maxEditDistance); // Bound the distance to UnreasonableCallEditDistance. - if (distance >= maxEditDistance || distance > (paramBase.size() + 2) / 3) { + if (distance >= maxEditDistance || + distance > (correctedBase.size() + 2) / 3) { return UnreasonableCallEditDistance; } @@ -539,34 +593,10 @@ static bool isLocInVarInit(TypeChecker &TC, VarDecl *var, SourceLoc loc) { return TC.Context.SourceMgr.rangeContainsTokenLoc(initRange, loc); } -namespace { - class TypoCorrectionResolver : public DelegatingLazyResolver { - TypeChecker &TC() { return static_cast(Principal); } - SourceLoc NameLoc; - public: - TypoCorrectionResolver(TypeChecker &TC, SourceLoc nameLoc) - : DelegatingLazyResolver(TC), NameLoc(nameLoc) {} - - void resolveDeclSignature(ValueDecl *VD) override { - if (VD->isInvalid() || VD->hasInterfaceType()) return; - - // Don't process a variable if we're within its initializer. - if (auto var = dyn_cast(VD)) { - if (isLocInVarInit(TC(), var, NameLoc)) - return; - } - - DelegatingLazyResolver::resolveDeclSignature(VD); - } - }; -} // end anonymous namespace - void TypeChecker::performTypoCorrection(DeclContext *DC, DeclRefKind refKind, Type baseTypeOrNull, - DeclName targetDeclName, - SourceLoc nameLoc, NameLookupOptions lookupOptions, - LookupResult &result, + TypoCorrectionResults &corrections, GenericSignatureBuilder *gsb, unsigned maxResults) { // Disable typo-correction if we won't show the diagnostic anyway or if @@ -579,27 +609,30 @@ void TypeChecker::performTypoCorrection(DeclContext *DC, DeclRefKind refKind, ++NumTypoCorrections; // Fill in a collection of the most reasonable entries. - TopCollection entries(maxResults); + TopCollection entries(maxResults); auto consumer = makeDeclConsumer([&](ValueDecl *decl, DeclVisibilityKind reason) { // Never match an operator with an identifier or vice-versa; this is // not a plausible typo. - if (!isPlausibleTypo(refKind, targetDeclName, decl)) + if (!isPlausibleTypo(refKind, corrections.WrittenName, decl)) return; // Don't suggest a variable within its own initializer. if (auto var = dyn_cast(decl)) { - if (isLocInVarInit(*this, var, nameLoc)) + if (isLocInVarInit(*this, var, corrections.Loc.getBaseNameLoc())) return; } + auto candidateName = decl->getFullName(); + // Don't waste time computing edit distances that are more than // the worst in our collection. unsigned maxDistance = entries.getMinUninterestingScore(UnreasonableCallEditDistance); unsigned distance = - getCallEditDistance(targetDeclName, decl->getFullName(), maxDistance); + getCallEditDistance(corrections.WrittenName, candidateName, + maxDistance); // Ignore values that are further than a reasonable distance. if (distance >= UnreasonableCallEditDistance) @@ -608,60 +641,129 @@ void TypeChecker::performTypoCorrection(DeclContext *DC, DeclRefKind refKind, entries.insert(distance, std::move(decl)); }); - TypoCorrectionResolver resolver(*this, nameLoc); if (baseTypeOrNull) { - lookupVisibleMemberDecls(consumer, baseTypeOrNull, DC, &resolver, + lookupVisibleMemberDecls(consumer, baseTypeOrNull, DC, this, /*include instance members*/ true, gsb); } else { - lookupVisibleDecls(consumer, DC, &resolver, /*top level*/ true, nameLoc); + lookupVisibleDecls(consumer, DC, this, /*top level*/ true, + corrections.Loc.getBaseNameLoc()); } // Impose a maximum distance from the best score. entries.filterMaxScoreRange(MaxCallEditDistanceFromBestCandidate); for (auto &entry : entries) - result.add(LookupResultEntry(entry.Value)); + corrections.Candidates.push_back(entry.Value); +} + +void +TypoCorrectionResults::addAllCandidatesToLookup(LookupResult &lookup) const { + for (auto candidate : Candidates) + lookup.add(LookupResultEntry(candidate), /*isOuter=*/false); +} + +static Decl *findExplicitParentForImplicitDecl(ValueDecl *decl) { + if (!decl->getLoc().isValid() && decl->getDeclContext()->isTypeContext()) { + Decl *parentDecl = dyn_cast(decl->getDeclContext()); + if (!parentDecl) parentDecl = cast(decl->getDeclContext()); + if (parentDecl->getLoc().isValid()) + return parentDecl; + } + + return nullptr; } static InFlightDiagnostic -diagnoseTypoCorrection(TypeChecker &tc, DeclNameLoc loc, ValueDecl *decl) { +noteTypoCorrection(TypeChecker &tc, DeclNameLoc loc, ValueDecl *decl, + bool wasClaimed) { if (auto var = dyn_cast(decl)) { // Suggest 'self' at the use point instead of pointing at the start // of the function. - if (var->isSelfParameter()) + if (var->isSelfParameter()) { + if (wasClaimed) { + // We don't need an extra note for this case because the programmer + // knows what 'self' refers to. + return InFlightDiagnostic(); + } + return tc.diagnose(loc.getBaseNameLoc(), diag::note_typo_candidate, var->getName().str()); + } } - if (!decl->getLoc().isValid() && decl->getDeclContext()->isTypeContext()) { - Decl *parentDecl = dyn_cast(decl->getDeclContext()); - if (!parentDecl) parentDecl = cast(decl->getDeclContext()); + if (Decl *parentDecl = findExplicitParentForImplicitDecl(decl)) { + StringRef kind = (isa(decl) ? "property" : + isa(decl) ? "initializer" : + isa(decl) ? "method" : + "member"); - if (parentDecl->getLoc().isValid()) { - StringRef kind = (isa(decl) ? "property" : - isa(decl) ? "initializer" : - isa(decl) ? "method" : - "member"); + return tc.diagnose(parentDecl, + wasClaimed ? diag::implicit_member_declared_here + : diag::note_typo_candidate_implicit_member, + decl->getBaseName().userFacingName(), kind); + } - return tc.diagnose(parentDecl, diag::note_typo_candidate_implicit_member, - decl->getBaseName().userFacingName(), kind); + if (wasClaimed) { + return tc.diagnose(decl, diag::decl_declared_here, decl->getBaseName()); + } else { + return tc.diagnose(decl, diag::note_typo_candidate, + decl->getBaseName().userFacingName()); + } +} + +void TypoCorrectionResults::noteAllCandidates() const { + for (auto candidate : Candidates) { + auto &&diagnostic = + noteTypoCorrection(TC, Loc, candidate, ClaimedCorrection); + + // Don't add fix-its if we claimed the correction for the primary + // diagnostic. + if (!ClaimedCorrection) { + SyntacticTypoCorrection correction(WrittenName, Loc, + candidate->getFullName()); + correction.addFixits(diagnostic); } } +} - return tc.diagnose(decl, diag::note_typo_candidate, - decl->getBaseName().userFacingName()); +void SyntacticTypoCorrection::addFixits(InFlightDiagnostic &diagnostic) const { + if (WrittenName.getBaseName() != CorrectedName.getBaseName()) + diagnostic.fixItReplace(Loc.getBaseNameLoc(), + CorrectedName.getBaseName().userFacingName()); + + // TODO: add fix-its for typo'ed argument labels. This is trickier + // because of the reordering rules. } -void TypeChecker::noteTypoCorrection(DeclName writtenName, DeclNameLoc loc, - ValueDecl *decl) { - auto &&diagnostic = diagnoseTypoCorrection(*this, loc, decl); +Optional +TypoCorrectionResults::claimUniqueCorrection() { + // Look for a unique base name. We ignore the rest of the name for now + // because we don't actually typo-correct any of that. + DeclBaseName uniqueCorrectedName; + for (auto candidate : Candidates) { + auto candidateName = candidate->getBaseName(); + + // If this is the first name, record it. + if (uniqueCorrectedName.empty()) + uniqueCorrectedName = candidateName; + + // If this is a different name from the last candidate, we don't have + // a unique correction. + else if (uniqueCorrectedName != candidateName) + return None; + } - DeclName declName = decl->getFullName(); + // If we didn't find any candidates, we're done. + if (uniqueCorrectedName.empty()) + return None; - if (writtenName.getBaseName() != declName.getBaseName()) - diagnostic.fixItReplace(loc.getBaseNameLoc(), - declName.getBaseName().userFacingName()); + // If the corrected name doesn't differ from the written name in its base + // name, it's not simple enough for this (for now). + if (WrittenName.getBaseName() == uniqueCorrectedName) + return None; - // TODO: add fix-its for typo'ed argument labels. This is trickier - // because of the reordering rules. + // Flag that we've claimed the correction. + ClaimedCorrection = true; + + return SyntacticTypoCorrection(WrittenName, Loc, uniqueCorrectedName); } diff --git a/lib/Sema/TypeCheckPattern.cpp b/lib/Sema/TypeCheckPattern.cpp index cdadb3b1a2262..893392505c445 100644 --- a/lib/Sema/TypeCheckPattern.cpp +++ b/lib/Sema/TypeCheckPattern.cpp @@ -1383,9 +1383,10 @@ bool TypeChecker::coercePatternToType(Pattern *&P, DeclContext *dc, Type type, EEP->getName().str() == "Some") { SmallString<4> Rename; camel_case::toLowercaseWord(EEP->getName().str(), Rename); - diagnose(EEP->getLoc(), diag::availability_decl_unavailable_rename, - EEP->getName(), /*replaced*/false, - /*special kind*/0, Rename.str()) + diagnose(EEP->getLoc(), + diag::availability_decl_unavailable_rename, + /*"getter" prefix*/2, EEP->getName(), /*replaced*/false, + /*special kind*/0, Rename.str()) .fixItReplace(EEP->getLoc(), Rename.str()); return true; @@ -1591,8 +1592,14 @@ bool TypeChecker::coercePatternToType(Pattern *&P, DeclContext *dc, Type type, /// bool TypeChecker::coerceParameterListToType(ParameterList *P, ClosureExpr *CE, AnyFunctionType *FN) { - Type paramListType = FN->getInput(); - bool hadError = paramListType->hasError(); + llvm::SmallVector params; + params.reserve(FN->getNumParams()); + + bool hadError = false; + for (const auto ¶m : FN->getParams()) { + params.push_back(param); + hadError |= param.getType()->hasError(); + } // Local function to check if the given type is valid e.g. doesn't have // errors, type variables or unresolved types related to it. @@ -1659,45 +1666,66 @@ bool TypeChecker::coerceParameterListToType(ParameterList *P, ClosureExpr *CE, return hadError; }; - // Check if paramListType only contains one single tuple. - // If it is, then paramListType would be sugared ParenType + auto hasParenSugar = [](ArrayRef params) -> bool { + if (params.size() == 1) { + const auto ¶m = params.front(); + return !param.hasLabel() && !param.isVariadic(); + } + + return false; + }; + + auto getType = [](const AnyFunctionType::Param ¶m) -> Type { + auto type = param.getPlainType(); + + if (param.isInOut()) + return InOutType::get(type); + + if (param.isVariadic()) + return ArraySliceType::get(type); + + return type; + }; + + // Check if parameter list only contains one single tuple. + // If it is, then parameter type would be sugared ParenType // with a single underlying TupleType. In that case, check if // the closure argument is also one to avoid the tuple splat // from happening. - if (!hadError && paramListType->hasParenSugar()) { - auto underlyingTy = cast(paramListType.getPointer()) - ->getUnderlyingType(); - + if (!hadError && hasParenSugar(params)) { + auto underlyingTy = params.front().getType(); + if (underlyingTy->is() && !underlyingTy->castTo()->getVarArgsBaseType()) { if (P->size() == 1) return handleParameter(P->get(0), underlyingTy, /*mutable*/false); } - - //pass + + // pass (strip paren sugar) + params.clear(); + FunctionType::decomposeInput(underlyingTy, params); } - + // The context type must be a tuple. - TupleType *tupleTy = paramListType->getAs(); - if (!tupleTy && !hadError) { + if (hasParenSugar(params) && !hadError) { + const auto ¶m = params.front(); if (P->size() == 1) { - assert(P->size() == FN->getParams().size()); - return handleParameter(P->get(0), paramListType, - /*mutable*/FN->getParams().front().isInOut()); + assert(P->size() == params.size()); + return handleParameter(P->get(0), getType(param), + /*mutable*/ param.isInOut()); } diagnose(P->getStartLoc(), diag::tuple_pattern_in_non_tuple_context, - paramListType); + param.getType()); hadError = true; } // The number of elements must match exactly. // TODO: incomplete tuple patterns, with some syntax. - if (!hadError && tupleTy->getNumElements() != P->size()) { - auto fnType = FunctionType::get(paramListType->getDesugaredType(), - FN->getResult()); - diagnose(P->getStartLoc(), diag::closure_argument_list_tuple, - fnType, tupleTy->getNumElements(), - P->size(), (P->size() == 1)); + if (!hadError && params.size() != P->size()) { + auto fnType = + FunctionType::get(params, FN->getResult(), FunctionType::ExtInfo()); + diagnose(P->getStartLoc(), diag::closure_argument_list_tuple, fnType, + params.size(), P->size(), (P->size() == 1)); hadError = true; } @@ -1710,8 +1738,8 @@ bool TypeChecker::coerceParameterListToType(ParameterList *P, ClosureExpr *CE, if (hadError) { CoercionType = ErrorType::get(Context); } else { - CoercionType = tupleTy->getElement(i).getType(); - isMutableParam = tupleTy->getElement(i).isInOut(); + CoercionType = getType(params[i]); + isMutableParam = params[i].isInOut(); } assert(param->getArgumentName().empty() && diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index ee61b005cc4e0..a85f440a84132 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -91,16 +91,11 @@ struct swift::RequirementCheck { }; swift::Witness RequirementMatch::getWitness(ASTContext &ctx) const { - SmallVector syntheticSubs; auto syntheticEnv = ReqEnv->getSyntheticEnvironment(); - ReqEnv->getRequirementSignature()->getSubstitutions( - ReqEnv->getRequirementToSyntheticMap(), - syntheticSubs); return swift::Witness(this->Witness, WitnessSubstitutions, - syntheticEnv, syntheticSubs); + syntheticEnv, ReqEnv->getRequirementToSyntheticMap()); } - AssociatedTypeDecl * swift::getReferencedAssocTypeOfProtocol(Type type, ProtocolDecl *proto) { if (auto dependentMember = type->getAs()) { @@ -351,14 +346,14 @@ RequirementMatch swift::matchWitness( TypeChecker &tc, DeclContext *dc, ValueDecl *req, ValueDecl *witness, - const std::function< + llvm::function_ref< std::tuple, Type, Type>(void)> - &setup, - const std::function(Type, Type)> - &matchTypes, - const std::function< + setup, + llvm::function_ref(Type, Type)> + matchTypes, + llvm::function_ref< RequirementMatch(bool, ArrayRef) - > &finalize) { + > finalize) { assert(!req->isInvalid() && "Cannot have an invalid requirement here"); /// Make sure the witness is of the same kind as the requirement. @@ -692,7 +687,7 @@ RequirementMatch swift::matchWitness(TypeChecker &tc, cs.emplace(tc, dc, ConstraintSystemOptions()); auto reqGenericEnv = reqEnvironment->getSyntheticEnvironment(); - auto &reqSubMap = reqEnvironment->getRequirementToSyntheticMap(); + auto reqSubMap = reqEnvironment->getRequirementToSyntheticMap(); Type selfTy = proto->getSelfInterfaceType().subst(reqSubMap); if (reqGenericEnv) @@ -719,7 +714,7 @@ RequirementMatch swift::matchWitness(TypeChecker &tc, // If substitution failed, skip the requirement. This only occurs in // invalid code. - if (!replacedInReq) + if (!replacedInReq || replacedInReq->hasError()) continue; if (reqGenericEnv) { @@ -787,10 +782,10 @@ RequirementMatch swift::matchWitness(TypeChecker &tc, optionalAdjustments); // Compute the set of substitutions we'll need for the witness. - solution->computeSubstitutions( - witness->getInnermostDeclContext()->getGenericSignatureOfContext(), - witnessLocator, - result.WitnessSubstitutions); + auto witnessSig = + witness->getInnermostDeclContext()->getGenericSignatureOfContext(); + result.WitnessSubstitutions = + solution->computeSubstitutions(witnessSig, witnessLocator); return result; }; @@ -1051,6 +1046,8 @@ bool WitnessChecker::checkWitnessAccess(AccessScope &requiredAccessScope, bool *isSetter) { *isSetter = false; + // Compute the intersection of the conforming type's access scope + // and the protocol's access scope. auto scopeIntersection = requiredAccessScope.intersectWith(Proto->getFormalAccessScope(DC)); assert(scopeIntersection.hasValue()); @@ -1058,7 +1055,12 @@ bool WitnessChecker::checkWitnessAccess(AccessScope &requiredAccessScope, requiredAccessScope = *scopeIntersection; AccessScope actualScopeToCheck = requiredAccessScope; - if (!witness->isAccessibleFrom(actualScopeToCheck.getDeclContext())) { + + // Setting the 'forConformance' flag means that we admit witnesses in + // protocol extensions that we can see, but are not necessarily as + // visible as the conforming type and protocol. + if (!witness->isAccessibleFrom(actualScopeToCheck.getDeclContext(), + /*forConformance=*/true)) { // Special case: if we have `@testable import` of the witness's module, // allow the witness to match if it would have matched for just this file. // That is, if '@testable' allows us to see the witness here, it should @@ -1081,7 +1083,10 @@ bool WitnessChecker::checkWitnessAccess(AccessScope &requiredAccessScope, *isSetter = true; auto ASD = cast(witness); - if (!ASD->isSetterAccessibleFrom(actualScopeToCheck.getDeclContext())) + + // See above about the forConformance flag. + if (!ASD->isSetterAccessibleFrom(actualScopeToCheck.getDeclContext(), + /*forConformance=*/true)) return true; } @@ -1263,6 +1268,133 @@ void MultiConformanceChecker::checkAllConformances() { } } +static void diagnoseConformanceImpliedByConditionalConformance( + DiagnosticEngine &Diags, NormalProtocolConformance *conformance, + NormalProtocolConformance *implyingConf, bool issueFixit) { + Type T = conformance->getType(); + auto proto = conformance->getProtocol(); + Type protoType = proto->getDeclaredType(); + auto implyingProto = implyingConf->getProtocol()->getDeclaredType(); + auto loc = implyingConf->getLoc(); + Diags.diagnose(loc, diag::conditional_conformances_cannot_imply_conformances, + T, implyingProto, protoType); + + if (!issueFixit) + return; + + // Now we get down to business: constructing a few options for new + // extensions. They all look like: + // + // extension T: ProtoType where ... { + // <# witnesses #> + // } + // + // The options are: + // + // - if possible, the original bounds relaxed, when the requirements match the + // conforming protocol, e.g. 'X: Hashable where T: Hashable' often + // corresponds to 'X: Equatable where T: Equatable'. This fixit is included + // if all the requirements are conformance constraints to the protocol + // that implies the conformance. + // - the same bounds: ... is copied from the implying extension + // - new bounds: ... becomes a placeholder + // + // We could also suggest adding ", ProtoType" to the existing extension, + // but we don't think having multiple conformances in a single extension + // (especially conditional ones) is good Swift style, and so we don't + // want to encourage it. + + auto ext = cast(implyingConf->getDeclContext()); + auto &ctxt = ext->getASTContext(); + + auto &SM = ctxt.SourceMgr; + StringRef extraIndent; + StringRef indent = Lexer::getIndentationForLine(SM, loc, &extraIndent); + + // First, the bits that aren't the requirements are the same for all the + // types. + llvm::SmallString<128> prefix; + llvm::SmallString<128> suffix; + { + llvm::raw_svector_ostream prefixStream(prefix); + llvm::raw_svector_ostream suffixStream(suffix); + + ValueDecl *decl = T->getAnyNominal(); + if (!decl) + decl = T->getAnyGeneric(); + + prefixStream << "extension " << decl->getFullName() << ": " << protoType << " "; + suffixStream << " {\n" + << indent << extraIndent << "<#witnesses#>\n" + << indent << "}\n\n" + << indent; + } + + if (!ctxt.LangOpts.DiagnosticsEditorMode) { + // The fixits below are too complicated for the command line: the suggested + // code ends up not being displayed, and the text by itself doesn't help. So + // instead we skip all that and just have some text. + Diags.diagnose(loc, + diag::note_explicitly_state_conditional_conformance_noneditor, + prefix.str()); + return; + } + + // First, we do the fixit for "matching" requirements (i.e. X: P where T: P). + bool matchingIsValid = true; + llvm::SmallString<128> matchingFixit = prefix; + { + llvm::raw_svector_ostream matchingStream(matchingFixit); + matchingStream << "where "; + bool first = true; + for (const auto &req : implyingConf->getConditionalRequirements()) { + auto firstType = req.getFirstType(); + // T: ImplyingProto => T: Proto + if (req.getKind() == RequirementKind::Conformance && + req.getSecondType()->isEqual(implyingProto)) { + auto comma = first ? "" : ", "; + matchingStream << comma << firstType << ": " << protoType; + first = false; + continue; + } + // something didn't work out, so give up on this fixit. + matchingIsValid = false; + break; + } + } + + if (matchingIsValid) { + matchingFixit += suffix; + Diags + .diagnose(loc, + diag::note_explicitly_state_conditional_conformance_relaxed) + .fixItInsert(loc, matchingFixit); + } + + // Next, do the fixit for using the same requirements, but be resilient to a + // missing `where` clause: this is one of a few fixits that get emitted here, + // and so is a very low priority diagnostic, and so shouldn't crash. + if (auto TWC = ext->getTrailingWhereClause()) { + llvm::SmallString<128> sameFixit = prefix; + auto CSR = + Lexer::getCharSourceRangeFromSourceRange(SM, TWC->getSourceRange()); + sameFixit += SM.extractText(CSR); + sameFixit += suffix; + Diags + .diagnose(loc, diag::note_explicitly_state_conditional_conformance_same) + .fixItInsert(loc, sameFixit); + } + + // And finally, just the generic new-requirements one: + llvm::SmallString<128> differentFixit = prefix; + differentFixit += "where <#requirements#>"; + differentFixit += suffix; + Diags + .diagnose(loc, + diag::note_explicitly_state_conditional_conformance_different) + .fixItInsert(loc, differentFixit); +} + /// \brief Determine whether the type \c T conforms to the protocol \c Proto, /// recording the complete witness table if it does. ProtocolConformance *MultiConformanceChecker:: @@ -1300,6 +1432,7 @@ checkIndividualConformance(NormalProtocolConformance *conformance, auto canT = T->getCanonicalType(); DeclContext *DC = conformance->getDeclContext(); auto Proto = conformance->getProtocol(); + auto ProtoType = Proto->getDeclaredType(); SourceLoc ComplainLoc = conformance->getLoc(); // Note that we are checking this conformance now. @@ -1317,13 +1450,13 @@ checkIndividualConformance(NormalProtocolConformance *conformance, // If the protocol requires a class, non-classes are a non-starter. if (Proto->requiresClass() && !canT->getClassOrBoundGenericClass()) { TC.diagnose(ComplainLoc, diag::non_class_cannot_conform_to_class_protocol, - T, Proto->getDeclaredType()); + T, ProtoType); conformance->setInvalid(); return conformance; } - // Foreign classes cannot conform to objc protocols. if (Proto->isObjC()) { + // Foreign classes cannot conform to objc protocols. if (auto clas = canT->getClassOrBoundGenericClass()) { Optional diagKind; @@ -1338,12 +1471,42 @@ checkIndividualConformance(NormalProtocolConformance *conformance, break; } if (diagKind) { - TC.diagnose(ComplainLoc, diagKind.getValue(), - T, Proto->getDeclaredType()); + TC.diagnose(ComplainLoc, diagKind.getValue(), T, ProtoType); conformance->setInvalid(); return conformance; } } + + // @objc protocols can't be conditionally-conformed to. We could, in theory, + // front-load the requirement checking to generic-instantiation time (rather + // than conformance-lookup/construction time) and register the conformance + // with the Obj-C runtime when they're satisfied, but we'd still have solve + // the problem with extensions that we check for below. + if (!conformance->getConditionalRequirements().empty()) { + TC.diagnose(ComplainLoc, + diag::objc_protocol_cannot_have_conditional_conformance, + T, ProtoType); + conformance->setInvalid(); + return conformance; + } + // And... even if it isn't conditional, we still don't currently support + // @objc protocols in extensions of Swift generic classes, because there's + // no stable Objective-C class object to install the protocol conformance + // category onto. + if (isa(DC)) { + if (auto genericT = T->getGenericAncestor()) { + if (!cast(genericT->getAnyNominal()) + ->usesObjCGenericsModel()) { + auto isSubclass = !genericT->isEqual(T); + auto genericTIsGeneric = (bool)genericT->getAnyGeneric() + ->getGenericParams(); + TC.diagnose(ComplainLoc, diag::objc_protocol_in_generic_extension, T, + ProtoType, isSubclass, genericTIsGeneric); + conformance->setInvalid(); + return conformance; + } + } + } } // Not every protocol/type is compatible with conditional conformances. @@ -1357,7 +1520,7 @@ checkIndividualConformance(NormalProtocolConformance *conformance, if (clas->usesObjCGenericsModel()) { TC.diagnose(ComplainLoc, diag::objc_generics_cannot_conditionally_conform, T, - Proto->getDeclaredType()); + ProtoType); conformance->setInvalid(); return conformance; } @@ -1376,21 +1539,49 @@ checkIndividualConformance(NormalProtocolConformance *conformance, if (serialized->getLanguageVersionBuiltWith() != TC.getLangOpts().EffectiveLanguageVersion) { TC.diagnose(ComplainLoc, - diag::protocol_has_missing_requirements_versioned, - T, Proto->getDeclaredType(), - serialized->getLanguageVersionBuiltWith(), + diag::protocol_has_missing_requirements_versioned, T, + ProtoType, serialized->getLanguageVersionBuiltWith(), TC.getLangOpts().EffectiveLanguageVersion); hasDiagnosed = true; } } if (!hasDiagnosed) { - TC.diagnose(ComplainLoc, diag::protocol_has_missing_requirements, - T, Proto->getDeclaredType()); + TC.diagnose(ComplainLoc, diag::protocol_has_missing_requirements, T, + ProtoType); } conformance->setInvalid(); return conformance; } + bool impliedDisablesMissingWitnessFixits = false; + if (conformance->getSourceKind() == ConformanceEntryKind::Implied) { + // We've got something like: + // + // protocol Foo : Proto {} + // extension SomeType : Foo {} + // + // We don't want to allow this when the SomeType : Foo conformance is + // conditional + auto implyingConf = conformance->getImplyingConformance(); + // There might be a long chain of implications, e.g. protocol Foo: Proto {} + // protocol Bar: Foo {} extension SomeType: Bar {}, so keep looking all the + // way up. + while (implyingConf->getSourceKind() == ConformanceEntryKind::Implied) { + implyingConf = implyingConf->getImplyingConformance(); + } + if (!implyingConf->getConditionalRequirements().empty()) { + // We shouldn't suggest including witnesses for the conformance, because + // those suggestions will go in the current DeclContext, but really they + // should go into the new extension we (might) suggest here. + impliedDisablesMissingWitnessFixits = true; + + diagnoseConformanceImpliedByConditionalConformance( + TC.Diags, conformance, implyingConf, issueFixit); + + conformance->setInvalid(); + } + } + // Check that T conforms to all inherited protocols. for (auto InheritedProto : Proto->getInheritedProtocols()) { auto InheritedConformance = @@ -1420,9 +1611,11 @@ checkIndividualConformance(NormalProtocolConformance *conformance, AllUsedCheckers.emplace_back(TC, conformance, MissingWitnesses); MissingWitnesses.insert(revivedMissingWitnesses.begin(), revivedMissingWitnesses.end()); - AllUsedCheckers.back().checkConformance(issueFixit ? - MissingWitnessDiagnosisKind::ErrorFixIt : - MissingWitnessDiagnosisKind::ErrorOnly); + + auto missingWitnessFixits = issueFixit && !impliedDisablesMissingWitnessFixits; + AllUsedCheckers.back().checkConformance( + missingWitnessFixits ? MissingWitnessDiagnosisKind::ErrorFixIt + : MissingWitnessDiagnosisKind::ErrorOnly); return conformance; } @@ -1462,7 +1655,7 @@ static Type getTypeForDisplay(ModuleDecl *module, ValueDecl *decl) { auto sigWithoutReqts = GenericSignature::get(genericFn->getGenericParams(), {}); return GenericFunctionType::get(sigWithoutReqts, - resultFn->getInput(), + resultFn->getParams(), resultFn->getResult(), resultFn->getExtInfo()); } @@ -1849,6 +2042,39 @@ void ConformanceChecker::recordInvalidWitness(ValueDecl *requirement) { Conformance->setWitness(requirement, Witness()); } +/// Returns the location we should use for a primary diagnostic (an error or +/// warning) that concerns \p witness but arose as part of checking +/// \p conformance. +/// +/// Ideally we'd like to use the location of \p witness for this, but that +/// could be confusing if the conformance is declared somewhere else. Moreover, +/// if the witness and the conformance declaration are in different files, we +/// could be issuing diagnostics in one file that wouldn't be present if we +/// recompiled just that file. Therefore, we only use the witness's location if +/// it's in the same type or extension that declares the conformance. +static SourceLoc +getLocForDiagnosingWitness(const NormalProtocolConformance *conformance, + const ValueDecl *witness) { + if (witness && witness->getDeclContext() == conformance->getDeclContext()) { + SourceLoc witnessLoc = witness->getLoc(); + if (witnessLoc.isValid()) + return witnessLoc; + } + return conformance->getLoc(); +} + +/// Emits a "'foo' declared here" note unless \p mainDiagLoc is already the +/// location of \p value. +static void emitDeclaredHereIfNeeded(DiagnosticEngine &diags, + SourceLoc mainDiagLoc, + const ValueDecl *value) { + if (!value) + return; + if (mainDiagLoc == value->getLoc()) + return; + diags.diagnose(value, diag::decl_declared_here, value->getFullName()); +} + bool ConformanceChecker::checkObjCTypeErasedGenerics( AssociatedTypeDecl *assocType, Type type, @@ -1881,17 +2107,11 @@ bool ConformanceChecker::checkObjCTypeErasedGenerics( }); // Diagnose the problem. - auto &diags = assocType->getASTContext().Diags; - if (typeDecl) { - diags.diagnose(typeDecl, diag::type_witness_objc_generic_parameter, - type, genericParam, !genericParam.isNull(), - assocType->getFullName(), Proto->getFullName()); - } else { - diags.diagnose(Conformance->getLoc(), - diag::type_witness_objc_generic_parameter, - type, genericParam, !genericParam.isNull(), - assocType->getFullName(), Proto->getFullName()); - } + SourceLoc diagLoc = getLocForDiagnosingWitness(Conformance, typeDecl); + ctx.Diags.diagnose(diagLoc, diag::type_witness_objc_generic_parameter, + type, genericParam, !genericParam.isNull(), + assocType->getFullName(), Proto->getFullName()); + emitDeclaredHereIfNeeded(ctx.Diags, diagLoc, typeDecl); return true; } @@ -1936,12 +2156,16 @@ void ConformanceChecker::recordTypeWitness(AssociatedTypeDecl *assocType, ? diag::type_witness_not_accessible_proto : diag::type_witness_not_accessible_type; auto &diags = DC->getASTContext().Diags; - auto diag = diags.diagnose(typeDecl, diagKind, - typeDecl->getDescriptiveKind(), - typeDecl->getFullName(), - requiredAccess, - proto->getName()); - fixItAccess(diag, typeDecl, requiredAccess); + diags.diagnose(getLocForDiagnosingWitness(conformance, typeDecl), + diagKind, + typeDecl->getDescriptiveKind(), + typeDecl->getFullName(), + requiredAccess, + proto->getName()); + auto fixItDiag = diags.diagnose(typeDecl, diag::witness_fix_access, + typeDecl->getDescriptiveKind(), + requiredAccess); + fixItAccess(fixItDiag, typeDecl, requiredAccess); }); } } else { @@ -1986,9 +2210,7 @@ void ConformanceChecker::recordTypeWitness(AssociatedTypeDecl *assocType, // a typealias added for an internal protocol shouldn't need to be // public---but that can be problematic if the same type conforms to two // protocols with different access levels. - AccessLevel aliasAccess = nominal->getFormalAccess(); - aliasAccess = std::max(aliasAccess, AccessLevel::Internal); - aliasDecl->setAccess(aliasAccess); + aliasDecl->copyFormalAccessFrom(nominal, /*sourceIsParentContext*/true); if (nominal == DC) { nominal->addMember(aliasDecl); @@ -2058,10 +2280,10 @@ printRequirementStub(ValueDecl *Requirement, DeclContext *Adopter, // FIXME: Infer body indentation from the source rather than hard-coding // 4 spaces. ASTContext &Ctx = Requirement->getASTContext(); - std::string ExtraIndent = " "; - StringRef CurrentIndent = Lexer::getIndentationForLine(Ctx.SourceMgr, - TypeLoc); - std::string StubIndent = CurrentIndent.str() + ExtraIndent; + StringRef ExtraIndent; + StringRef CurrentIndent = + Lexer::getIndentationForLine(Ctx.SourceMgr, TypeLoc, &ExtraIndent); + std::string StubIndent = (CurrentIndent + ExtraIndent).str(); ExtraIndentStreamPrinter Printer(OS, StubIndent); Printer.printNewline(); @@ -2291,13 +2513,23 @@ void ConformanceChecker::checkNonFinalClassWitness(ValueDecl *requirement, [ctor, requirement](NormalProtocolConformance *conformance) { bool inExtension = isa(ctor->getDeclContext()); auto &diags = ctor->getASTContext().Diags; - auto diag = diags.diagnose(ctor->getLoc(), - diag::witness_initializer_not_required, - requirement->getFullName(), - inExtension, - conformance->getType()); - if (!ctor->isImplicit() && !inExtension) - diag.fixItInsert(ctor->getStartLoc(), "required "); + SourceLoc diagLoc = getLocForDiagnosingWitness(conformance, ctor); + Optional fixItDiag = + diags.diagnose(diagLoc, diag::witness_initializer_not_required, + requirement->getFullName(), inExtension, + conformance->getType()); + if (diagLoc != ctor->getLoc() && !ctor->isImplicit()) { + // If the main diagnostic is emitted on the conformance, we want to + // attach the fix-it to the note that shows where the initializer is + // defined. + fixItDiag.getValue().flush(); + fixItDiag.emplace(diags.diagnose(ctor, diag::decl_declared_here, + ctor->getFullName())); + } + if (!inExtension) { + fixItDiag->fixItInsert(ctor->getAttributeInsertionLoc(true), + "required "); + } }); } } @@ -2316,9 +2548,11 @@ void ConformanceChecker::checkNonFinalClassWitness(ValueDecl *requirement, [witness, requirement](NormalProtocolConformance *conformance) { auto proto = conformance->getProtocol(); auto &diags = proto->getASTContext().Diags; - diags.diagnose(witness->getLoc(), diag::witness_self_non_subtype, + SourceLoc diagLoc = getLocForDiagnosingWitness(conformance, witness); + diags.diagnose(diagLoc, diag::witness_self_non_subtype, proto->getDeclaredType(), requirement->getFullName(), conformance->getType()); + emitDeclaredHereIfNeeded(diags, diagLoc, witness); }); } else if (selfKind.result) { // The reference to Self occurs in the result type. A non-final class @@ -2332,11 +2566,13 @@ void ConformanceChecker::checkNonFinalClassWitness(ValueDecl *requirement, [witness, requirement](NormalProtocolConformance *conformance) { auto proto = conformance->getProtocol(); auto &diags = proto->getASTContext().Diags; - diags.diagnose(witness->getLoc(), + SourceLoc diagLoc = getLocForDiagnosingWitness(conformance,witness); + diags.diagnose(diagLoc, diag::witness_requires_dynamic_self, requirement->getFullName(), conformance->getType(), proto->getDeclaredType()); + emitDeclaredHereIfNeeded(diags, diagLoc, witness); }); } @@ -2347,10 +2583,12 @@ void ConformanceChecker::checkNonFinalClassWitness(ValueDecl *requirement, [witness, requirement](NormalProtocolConformance *conformance) { auto proto = conformance->getProtocol(); auto &diags = proto->getASTContext().Diags; - diags.diagnose(witness->getLoc(), diag::witness_self_non_subtype, + SourceLoc diagLoc = getLocForDiagnosingWitness(conformance, witness); + diags.diagnose(diagLoc, diag::witness_self_non_subtype, proto->getDeclaredType(), requirement->getFullName(), conformance->getType()); + emitDeclaredHereIfNeeded(diags, diagLoc, witness); }); } } else if (selfKind.requirement) { @@ -2359,14 +2597,15 @@ void ConformanceChecker::checkNonFinalClassWitness(ValueDecl *requirement, // A "Self ==" constraint works incorrectly with subclasses. Complain. auto proto = Conformance->getProtocol(); auto &diags = proto->getASTContext().Diags; - diags.diagnose(witness->getLoc(), - diag::witness_self_same_type, + SourceLoc diagLoc = getLocForDiagnosingWitness(Conformance, witness); + diags.diagnose(diagLoc, diag::witness_self_same_type, witness->getDescriptiveKind(), witness->getFullName(), Conformance->getType(), requirement->getDescriptiveKind(), requirement->getFullName(), proto->getDeclaredType()); + emitDeclaredHereIfNeeded(diags, diagLoc, witness); if (auto requirementRepr = *constraint) { diags.diagnose(requirementRepr->getEqualLoc(), @@ -2398,10 +2637,12 @@ void ConformanceChecker::checkNonFinalClassWitness(ValueDecl *requirement, [witness, requirement](NormalProtocolConformance *conformance) { auto proto = conformance->getProtocol(); auto &diags = proto->getASTContext().Diags; - diags.diagnose(witness->getLoc(), + diags.diagnose(conformance->getLoc(), diag::witness_requires_class_implementation, requirement->getFullName(), conformance->getType()); + diags.diagnose(witness, diag::decl_declared_here, + witness->getFullName()); }); } } @@ -2475,18 +2716,25 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) { auto proto = conformance->getProtocol(); auto &diags = proto->getASTContext().Diags; { - auto diag = diags.diagnose(witness, + SourceLoc diagLoc = getLocForDiagnosingWitness(conformance,witness); + auto diag = diags.diagnose(diagLoc, diag::witness_argument_name_mismatch, isa(witness), witness->getFullName(), proto->getDeclaredType(), requirement->getFullName()); - fixDeclarationName(diag, witness, requirement->getFullName()); + if (diagLoc == witness->getLoc()) { + fixDeclarationName(diag, witness, requirement->getFullName()); + } else { + diag.flush(); + diags.diagnose(witness, diag::decl_declared_here, + witness->getFullName()); + } } - diags.diagnose(requirement, diag::protocol_requirement_here, + diags.diagnose(requirement, diag::kind_declname_declared_here, + DescriptiveDeclKind::Requirement, requirement->getFullName()); - }); } @@ -2517,15 +2765,18 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) { bool isSetter = (check.Kind == CheckKind::AccessOfSetter); auto &diags = DC->getASTContext().Diags; - auto diag = diags.diagnose( - witness, diagKind, - getRequirementKind(requirement), - witness->getFullName(), - isSetter, - requiredAccess, - protoAccessScope.accessLevelForDiagnostics(), - proto->getName()); - fixItAccess(diag, witness, requiredAccess, isSetter); + diags.diagnose(getLocForDiagnosingWitness(conformance, witness), + diagKind, + getRequirementKind(requirement), + witness->getFullName(), + isSetter, + requiredAccess, + protoAccessScope.accessLevelForDiagnostics(), + proto->getName()); + auto fixItDiag = diags.diagnose(witness, diag::witness_fix_access, + witness->getDescriptiveKind(), + requiredAccess); + fixItAccess(fixItDiag, witness, requiredAccess, isSetter); }); break; } @@ -2537,17 +2788,16 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) { // FIXME: The problem may not be the OS version. ASTContext &ctx = witness->getASTContext(); auto &diags = ctx.Diags; + SourceLoc diagLoc = getLocForDiagnosingWitness(conformance, witness); diags.diagnose( - witness, - diag::availability_protocol_requires_version, - conformance->getProtocol()->getFullName(), - witness->getFullName(), - prettyPlatformString(targetPlatform(ctx.LangOpts)), - check.RequiredAvailability.getOSVersion().getLowerEndpoint()); - diags.diagnose(requirement, + diagLoc, diag::availability_protocol_requires_version, + conformance->getProtocol()->getFullName(), + witness->getFullName(), + prettyPlatformString(targetPlatform(ctx.LangOpts)), + check.RequiredAvailability.getOSVersion().getLowerEndpoint()); + emitDeclaredHereIfNeeded(diags, diagLoc, witness); + diags.diagnose(requirement, diag::availability_protocol_requirement_here); - diags.diagnose(conformance->getLoc(), - diag::availability_conformance_introduced_here); }); break; } @@ -2567,18 +2817,26 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) { auto &ctx = witness->getASTContext(); auto &diags = ctx.Diags; { + SourceLoc diagLoc = getLocForDiagnosingWitness(conformance,witness); auto diag = diags.diagnose( - witness, - hasAnyError(adjustments) - ? diag::err_protocol_witness_optionality - : diag::warn_protocol_witness_optionality, - classifyOptionalityIssues(adjustments, requirement), - witness->getFullName(), - proto->getFullName()); - addOptionalityFixIts(adjustments, ctx, witness, diag); + diagLoc, + hasAnyError(adjustments) + ? diag::err_protocol_witness_optionality + : diag::warn_protocol_witness_optionality, + classifyOptionalityIssues(adjustments, requirement), + witness->getFullName(), + proto->getFullName()); + if (diagLoc == witness->getLoc()) { + addOptionalityFixIts(adjustments, ctx, witness, diag); + } else { + diag.flush(); + diags.diagnose(witness, diag::decl_declared_here, + witness->getFullName()); + } } - diags.diagnose(requirement, diag::protocol_requirement_here, + diags.diagnose(requirement, diag::kind_declname_declared_here, + DescriptiveDeclKind::Requirement, requirement->getFullName()); }); break; @@ -2590,12 +2848,14 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) { auto ctor = cast(requirement); auto witnessCtor = cast(witness); auto &diags = witness->getASTContext().Diags; - diags.diagnose(witness, + SourceLoc diagLoc = getLocForDiagnosingWitness(conformance, witness); + diags.diagnose(diagLoc, diag::witness_initializer_failability, ctor->getFullName(), witnessCtor->getFailability() == OTK_ImplicitlyUnwrappedOptional) .highlight(witnessCtor->getFailabilityLoc()); + emitDeclaredHereIfNeeded(diags, diagLoc, witness); }); break; @@ -2606,13 +2866,16 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) { [witness, requirement, emitError]( NormalProtocolConformance *conformance) { auto &diags = witness->getASTContext().Diags; - diags.diagnose(witness, + SourceLoc diagLoc = getLocForDiagnosingWitness(conformance, witness); + diags.diagnose(diagLoc, emitError ? diag::witness_unavailable : diag::witness_unavailable_warn, witness->getDescriptiveKind(), witness->getFullName(), conformance->getProtocol()->getFullName()); - diags.diagnose(requirement, diag::protocol_requirement_here, + emitDeclaredHereIfNeeded(diags, diagLoc, witness); + diags.diagnose(requirement, diag::kind_declname_declared_here, + DescriptiveDeclKind::Requirement, requirement->getFullName()); }); break; @@ -2707,7 +2970,7 @@ ResolveWitnessResult ConformanceChecker::resolveWitnessViaDerivation( // Find the declaration that derives the protocol conformance. NominalTypeDecl *derivingTypeDecl = nullptr; auto *nominal = Adoptee->getAnyNominal(); - if (DerivedConformance::derivesProtocolConformance(TC, nominal, Proto)) + if (DerivedConformance::derivesProtocolConformance(TC, DC, nominal, Proto)) derivingTypeDecl = nominal; if (!derivingTypeDecl) { @@ -2792,12 +3055,10 @@ CheckTypeWitnessResult swift::checkTypeWitness(TypeChecker &tc, DeclContext *dc, dc->getParentModule(), decl, decl->getGenericEnvironmentOfContext()); - auto result = decl->getGenericSignature()->enumeratePairedRequirements( - [&](Type t, ArrayRef reqt) -> bool { - return t.subst(subMap, SubstFlags::UseErrorType)->hasError(); - }); - if (result) - return CheckTypeWitnessResult(reqProto->getDeclaredType()); + for (auto replacement : subMap.getReplacementTypes()) { + if (replacement->hasError()) + return CheckTypeWitnessResult(reqProto->getDeclaredType()); + } } } @@ -3183,43 +3444,70 @@ void ConformanceChecker::checkConformance(MissingWitnessDiagnosisKind Kind) { if (!witness->isObjC()) { bool isOptional = requirement->getAttrs().hasAttribute(); + SourceLoc diagLoc = getLocForDiagnosingWitness(Conformance, witness); if (auto witnessFunc = dyn_cast(witness)) { auto diagInfo = getObjCMethodDiagInfo(witnessFunc); - auto diag = TC.diagnose(witness, - isOptional ? diag::witness_non_objc_optional - : diag::witness_non_objc, - diagInfo.first, diagInfo.second, - Proto->getFullName()); + Optional fixItDiag = + TC.diagnose(diagLoc, + isOptional ? diag::witness_non_objc_optional + : diag::witness_non_objc, + diagInfo.first, diagInfo.second, + Proto->getFullName()); + if (diagLoc != witness->getLoc()) { + // If the main diagnostic is emitted on the conformance, we want + // to attach the fix-it to the note that shows where the + // witness is defined. + fixItDiag.getValue().flush(); + fixItDiag.emplace(TC.diagnose(witness, diag::make_decl_objc, + witness->getDescriptiveKind())); + } if (!witness->canInferObjCFromRequirement(requirement)) { fixDeclarationObjCName( - diag, witness, - cast(requirement)->getObjCSelector()); + fixItDiag.getValue(), witness, + cast(requirement)->getObjCSelector()); } } else if (isa(witness)) { - auto diag = TC.diagnose(witness, - isOptional - ? diag::witness_non_objc_storage_optional - : diag::witness_non_objc_storage, - /*isSubscript=*/false, - witness->getFullName(), - Proto->getFullName()); + Optional fixItDiag = + TC.diagnose(diagLoc, + isOptional ? diag::witness_non_objc_storage_optional + : diag::witness_non_objc_storage, + /*isSubscript=*/false, + witness->getFullName(), + Proto->getFullName()); + if (diagLoc != witness->getLoc()) { + // If the main diagnostic is emitted on the conformance, we want + // to attach the fix-it to the note that shows where the + // witness is defined. + fixItDiag.getValue().flush(); + fixItDiag.emplace(TC.diagnose(witness, diag::make_decl_objc, + witness->getDescriptiveKind())); + } if (!witness->canInferObjCFromRequirement(requirement)) { fixDeclarationObjCName( - diag, witness, + fixItDiag.getValue(), witness, ObjCSelector(requirement->getASTContext(), 0, cast(requirement) ->getObjCPropertyName())); } } else if (isa(witness)) { - TC.diagnose(witness, - isOptional - ? diag::witness_non_objc_storage_optional - : diag::witness_non_objc_storage, - /*isSubscript=*/true, - witness->getFullName(), - Proto->getFullName()) - .fixItInsert(witness->getAttributeInsertionLoc(false), - "@objc "); + Optional fixItDiag = + TC.diagnose(diagLoc, + isOptional + ? diag::witness_non_objc_storage_optional + : diag::witness_non_objc_storage, + /*isSubscript=*/true, + witness->getFullName(), + Proto->getFullName()); + if (diagLoc != witness->getLoc()) { + // If the main diagnostic is emitted on the conformance, we want + // to attach the fix-it to the note that shows where the + // witness is defined. + fixItDiag.getValue().flush(); + fixItDiag.emplace(TC.diagnose(witness, diag::make_decl_objc, + witness->getDescriptiveKind())); + } + fixItDiag->fixItInsert(witness->getAttributeInsertionLoc(false), + "@objc "); } // If the requirement is optional, @nonobjc suppresses the @@ -3230,7 +3518,8 @@ void ConformanceChecker::checkConformance(MissingWitnessDiagnosisKind Kind) { "@nonobjc "); } - TC.diagnose(requirement, diag::protocol_requirement_here, + TC.diagnose(requirement, diag::kind_declname_declared_here, + DescriptiveDeclKind::Requirement, requirement->getFullName()); Conformance->setInvalid(); @@ -3383,7 +3672,8 @@ static void diagnoseConformanceFailure(TypeChecker &TC, Type T, // conformance to RawRepresentable was inferred. if (auto enumDecl = T->getEnumOrBoundGenericEnum()) { if (Proto->isSpecificProtocol(KnownProtocolKind::RawRepresentable) && - DerivedConformance::derivesProtocolConformance(TC, enumDecl, Proto) && + DerivedConformance::derivesProtocolConformance(TC, DC, enumDecl, + Proto) && enumDecl->hasRawType()) { auto rawType = enumDecl->getRawType(); @@ -3642,13 +3932,13 @@ Optional TypeChecker::LookUpConformance::operator()( CanType dependentType, Type conformingReplacementType, - ProtocolType *conformedProtocol) const { + ProtocolDecl *conformedProtocol) const { if (conformingReplacementType->isTypeParameter()) - return ProtocolConformanceRef(conformedProtocol->getDecl()); + return ProtocolConformanceRef(conformedProtocol); return tc.conformsToProtocol( conformingReplacementType, - conformedProtocol->getDecl(), + conformedProtocol, dc, (ConformanceCheckFlags::Used| ConformanceCheckFlags::InExpression| @@ -3834,6 +4124,7 @@ void TypeChecker::checkConformanceRequirements( llvm::SetVector globalMissingWitnesses; ConformanceChecker checker(*this, conformance, globalMissingWitnesses); checker.ensureRequirementsAreSatisfied(/*failUnsubstituted=*/true); + checker.diagnoseMissingWitnesses(MissingWitnessDiagnosisKind::ErrorFixIt); } /// Determine the score when trying to match two identifiers together. @@ -4196,7 +4487,8 @@ static void diagnosePotentialWitness(TypeChecker &tc, .fixItInsert(witness->getAttributeInsertionLoc(false), "@nonobjc "); } - tc.diagnose(req, diag::protocol_requirement_here, req->getFullName()); + tc.diagnose(req, diag::kind_declname_declared_here, + DescriptiveDeclKind::Requirement, req->getFullName()); } /// Whether the given protocol is "NSCoding". @@ -4380,17 +4672,20 @@ void TypeChecker::checkConformancesInContext(DeclContext *dc, inferStaticInitializeObjCMetadata(*this, classDecl); } } + } + + // Check all conformances. + groupChecker.checkAllConformances(); - // When requested, print out information about this conformance. - if (Context.LangOpts.DebugGenericSignatures) { + if (Context.LangOpts.DebugGenericSignatures) { + // Now that they're filled out, print out information about the conformances + // here, when requested. + for (auto conformance : conformances) { dc->dumpContext(); conformance->dump(); } } - // Check all conformances. - groupChecker.checkAllConformances(); - // Catalog all of members of this declaration context that satisfy // requirements of conformances in this context. SmallVector @@ -4406,6 +4701,12 @@ void TypeChecker::checkConformancesInContext(DeclContext *dc, // Complain about the redundant conformance. + auto currentSig = dc->getGenericSignatureOfContext(); + auto existingSig = diag.ExistingDC->getGenericSignatureOfContext(); + auto differentlyConditional = currentSig && existingSig && + currentSig->getCanonicalSignature() != + existingSig->getCanonicalSignature(); + // If we've redundantly stated a conformance for which the original // conformance came from the module of the type or the module of the // protocol, just warn; we'll pick up the original conformance. @@ -4417,11 +4718,13 @@ void TypeChecker::checkConformancesInContext(DeclContext *dc, extendedNominal->getParentModule()->getName() || existingModule == diag.Protocol->getParentModule())) { // Warn about the conformance. - diagnose(diag.Loc, diag::redundant_conformance_adhoc, - dc->getDeclaredInterfaceType(), + auto diagID = differentlyConditional + ? diag::redundant_conformance_adhoc_conditional + : diag::redundant_conformance_adhoc; + diagnose(diag.Loc, diagID, dc->getDeclaredInterfaceType(), diag.Protocol->getName(), existingModule->getName() == - extendedNominal->getParentModule()->getName(), + extendedNominal->getParentModule()->getName(), existingModule->getName()); // Complain about any declarations in this extension whose names match @@ -4452,19 +4755,22 @@ void TypeChecker::checkConformancesInContext(DeclContext *dc, } } } else { - diagnose(diag.Loc, diag::redundant_conformance, - dc->getDeclaredInterfaceType(), + auto diagID = differentlyConditional + ? diag::redundant_conformance_conditional + : diag::redundant_conformance; + diagnose(diag.Loc, diagID, dc->getDeclaredInterfaceType(), diag.Protocol->getName()); } // Special case: explain that 'RawRepresentable' conformance // is implied for enums which already declare a raw type. if (auto enumDecl = dyn_cast(existingDecl)) { - if (diag.Protocol->isSpecificProtocol(KnownProtocolKind::RawRepresentable) - && DerivedConformance::derivesProtocolConformance(*this, enumDecl, - diag.Protocol) - && enumDecl->hasRawType() - && enumDecl->getInherited()[0].getSourceRange().isValid()) { + if (diag.Protocol->isSpecificProtocol( + KnownProtocolKind::RawRepresentable) && + DerivedConformance::derivesProtocolConformance(*this, dc, enumDecl, + diag.Protocol) && + enumDecl->hasRawType() && + enumDecl->getInherited()[0].getSourceRange().isValid()) { diagnose(enumDecl->getInherited()[0].getSourceRange().Start, diag::enum_declares_rawrep_with_raw_type, dc->getDeclaredInterfaceType(), enumDecl->getRawType()); @@ -4723,8 +5029,7 @@ TypeChecker::findWitnessedObjCRequirements(const ValueDecl *witness, = cast(lhs->getDeclContext()); ProtocolDecl *rhsProto = cast(rhs->getDeclContext()); - return ProtocolType::compareProtocols(&lhsProto, - &rhsProto) < 0; + return TypeDecl::compare(lhsProto, rhsProto) < 0; }); } return result; @@ -4742,6 +5047,7 @@ void TypeChecker::resolveTypeWitness( checker.resolveTypeWitnesses(); else checker.resolveSingleTypeWitness(assocType); + checker.diagnoseMissingWitnesses(MissingWitnessDiagnosisKind::ErrorFixIt); } void TypeChecker::resolveWitness(const NormalProtocolConformance *conformance, @@ -4752,6 +5058,7 @@ void TypeChecker::resolveWitness(const NormalProtocolConformance *conformance, const_cast(conformance), MissingWitnesses); checker.resolveSingleWitness(requirement); + checker.diagnoseMissingWitnesses(MissingWitnessDiagnosisKind::ErrorFixIt); } ValueDecl *TypeChecker::deriveProtocolRequirement(DeclContext *DC, @@ -4770,35 +5077,32 @@ ValueDecl *TypeChecker::deriveProtocolRequirement(DeclContext *DC, if (Decl->isInvalid()) return nullptr; + DerivedConformance derived(*this, Decl, TypeDecl, protocol); + switch (*knownKind) { case KnownProtocolKind::RawRepresentable: - return DerivedConformance::deriveRawRepresentable(*this, Decl, - TypeDecl, Requirement); + return derived.deriveRawRepresentable(Requirement); case KnownProtocolKind::CaseIterable: - return DerivedConformance::deriveCaseIterable(*this, Decl, - TypeDecl, Requirement); + return derived.deriveCaseIterable(Requirement); case KnownProtocolKind::Equatable: - return DerivedConformance::deriveEquatable(*this, Decl, TypeDecl, - Requirement); - + return derived.deriveEquatable(Requirement); + case KnownProtocolKind::Hashable: - return DerivedConformance::deriveHashable(*this, Decl, TypeDecl, - Requirement); - + return derived.deriveHashable(Requirement); + case KnownProtocolKind::BridgedNSError: - return DerivedConformance::deriveBridgedNSError(*this, Decl, TypeDecl, - Requirement); + return derived.deriveBridgedNSError(Requirement); case KnownProtocolKind::CodingKey: - return DerivedConformance::deriveCodingKey(*this, Decl, TypeDecl, Requirement); + return derived.deriveCodingKey(Requirement); case KnownProtocolKind::Encodable: - return DerivedConformance::deriveEncodable(*this, Decl, TypeDecl, Requirement); + return derived.deriveEncodable(Requirement); case KnownProtocolKind::Decodable: - return DerivedConformance::deriveDecodable(*this, Decl, TypeDecl, Requirement); + return derived.deriveDecodable(Requirement); default: return nullptr; @@ -4817,13 +5121,12 @@ Type TypeChecker::deriveTypeWitness(DeclContext *DC, auto Decl = DC->getInnermostDeclarationDeclContext(); + DerivedConformance derived(*this, Decl, TypeDecl, protocol); switch (*knownKind) { case KnownProtocolKind::RawRepresentable: - return DerivedConformance::deriveRawRepresentable(*this, Decl, - TypeDecl, AssocType); + return derived.deriveRawRepresentable(AssocType); case KnownProtocolKind::CaseIterable: - return DerivedConformance::deriveCaseIterable(*this, Decl, - TypeDecl, AssocType); + return derived.deriveCaseIterable(AssocType); default: return nullptr; } diff --git a/lib/Sema/TypeCheckProtocol.h b/lib/Sema/TypeCheckProtocol.h index 793804637c273..3a25dc056b16f 100644 --- a/lib/Sema/TypeCheckProtocol.h +++ b/lib/Sema/TypeCheckProtocol.h @@ -115,13 +115,12 @@ struct InferredAssociatedTypesByWitness { /// The set of witnesses that were considered when attempting to /// infer associated types. -typedef SmallVector - InferredAssociatedTypesByWitnesses; +using InferredAssociatedTypesByWitnesses = + SmallVector; /// A mapping from requirements to the set of matches with witnesses. -typedef SmallVector, 4> - InferredAssociatedTypes; +using InferredAssociatedTypes = + SmallVector, 4>; /// A potential solution to the set of inferred type witnesses. struct InferredTypeWitnessesSolution { @@ -369,7 +368,7 @@ struct RequirementMatch { /// Substitutions mapping the type of the witness to the requirement /// environment. - SmallVector WitnessSubstitutions; + SubstitutionMap WitnessSubstitutions; /// \brief Determine whether this match is viable. bool isViable() const { @@ -816,8 +815,8 @@ class AssociatedTypeInference { public: /// Describes a mapping from associated type declarations to their /// type witnesses (as interface types). - typedef std::vector> - InferredTypeWitnesses; + using InferredTypeWitnesses = + std::vector>; /// Perform associated type inference. /// @@ -831,14 +830,14 @@ class AssociatedTypeInference { RequirementMatch matchWitness( TypeChecker &tc, DeclContext *dc, ValueDecl *req, ValueDecl *witness, - const std::function< + llvm::function_ref< std::tuple, Type, Type>(void)> - &setup, - const std::function(Type, Type)> - &matchTypes, - const std::function< + setup, + llvm::function_ref(Type, Type)> + matchTypes, + llvm::function_ref< RequirementMatch(bool, ArrayRef) - > &finalize); + > finalize); RequirementMatch matchWitness(TypeChecker &tc, ProtocolDecl *proto, diff --git a/lib/Sema/TypeCheckProtocolInference.cpp b/lib/Sema/TypeCheckProtocolInference.cpp index 75898e65aabd4..a55a81d5dd8ff 100644 --- a/lib/Sema/TypeCheckProtocolInference.cpp +++ b/lib/Sema/TypeCheckProtocolInference.cpp @@ -503,7 +503,7 @@ static Type getWitnessTypeForMatching(TypeChecker &tc, // common, because most of the recursion involves the requirements // of the generic type. if (auto genericFn = type->getAs()) { - type = FunctionType::get(genericFn->getInput(), + type = FunctionType::get(genericFn->getParams(), genericFn->getResult(), genericFn->getExtInfo()); } @@ -877,7 +877,7 @@ Type AssociatedTypeInference::computeDerivedTypeWitness( // Can we derive conformances for this protocol and adoptee? NominalTypeDecl *derivingTypeDecl = adoptee->getAnyNominal(); - if (!DerivedConformance::derivesProtocolConformance(tc, derivingTypeDecl, + if (!DerivedConformance::derivesProtocolConformance(tc, dc, derivingTypeDecl, proto)) return Type(); @@ -1185,7 +1185,7 @@ void AssociatedTypeInference::findSolutionsRec( unsigned numTypeWitnesses, unsigned numValueWitnessesInProtocolExtensions, unsigned reqDepth) { - typedef decltype(typeWitnesses)::ScopeTy TypeWitnessesScope; + using TypeWitnessesScope = decltype(typeWitnesses)::ScopeTy; // If we hit the last requirement, record and check this solution. if (reqDepth == inferred.size()) { diff --git a/lib/Sema/TypeCheckREPL.cpp b/lib/Sema/TypeCheckREPL.cpp index f9a54dc46c27e..7df507ab8430d 100644 --- a/lib/Sema/TypeCheckREPL.cpp +++ b/lib/Sema/TypeCheckREPL.cpp @@ -268,9 +268,9 @@ void REPLChecker::generatePrintOfExpression(StringRef NameStr, Expr *E) { CE->setBody(Body, false); TC.typeCheckClosureBody(CE); - Expr *TheCall = CallExpr::createImplicit(Context, CE, { E }, { }); - if (TC.typeCheckExpressionShallow(TheCall, Arg->getDeclContext())) - return ; + auto *TheCall = CallExpr::createImplicit(Context, CE, { E }, { }); + TheCall->getArg()->setType(ParenType::get(Context, E->getType())); + TheCall->setType(Context.TheEmptyTupleType); // Inject the call into the top level stream by wrapping it with a TLCD. auto *BS = BraceStmt::create(Context, Loc, ASTNode(TheCall), diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index beff5f5b6265b..6e34e24fc2596 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -377,6 +377,7 @@ class StmtChecker : public StmtVisitor { template bool typeCheckStmt(StmtTy *&S) { + PrettyStackTraceStmt trace(TC.Context, "type-checking", S); StmtTy *S2 = cast_or_null(visit(S)); if (S2 == nullptr) return true; @@ -480,8 +481,7 @@ class StmtChecker : public StmtVisitor { } Stmt *visitDeferStmt(DeferStmt *DS) { - TC.typeCheckDecl(DS->getTempDecl(), /*isFirstPass*/true); - TC.typeCheckDecl(DS->getTempDecl(), /*isFirstPass*/false); + TC.typeCheckDecl(DS->getTempDecl()); Expr *theCall = DS->getCallExpr(); TC.typeCheckExpression(theCall, DC); @@ -822,7 +822,7 @@ class StmtChecker : public StmtVisitor { // Type-check the subject expression. Expr *subjectExpr = S->getSubjectExpr(); auto resultTy = TC.typeCheckExpression(subjectExpr, DC); - auto hadError = !resultTy; + auto limitExhaustivityChecks = !resultTy; if (Expr *newSubjectExpr = TC.coerceToRValue(subjectExpr)) subjectExpr = newSubjectExpr; S->setSubjectExpr(subjectExpr); @@ -836,8 +836,7 @@ class StmtChecker : public StmtVisitor { // the list of raw cases. for (auto node : S->getRawCases()) { if (!node.is()) continue; - TC.typeCheckDecl(node.get(), /*isFirstPass*/true); - TC.typeCheckDecl(node.get(), /*isFirstPass*/false); + TC.typeCheckDecl(node.get()); } auto cases = S->getCases(); @@ -858,7 +857,7 @@ class StmtChecker : public StmtVisitor { // Coerce the pattern to the subject's type. if (!subjectType || TC.coercePatternToType(pattern, DC, subjectType, TypeResolutionFlags::InExpression)) { - hadError = true; + limitExhaustivityChecks = true; // If that failed, mark any variables binding pieces of the pattern // as invalid to silence follow-on errors. @@ -909,11 +908,44 @@ class StmtChecker : public StmtVisitor { } // Check the guard expression, if present. if (auto *guard = labelItem.getGuardExpr()) { - hadError |= TC.typeCheckCondition(guard, DC); + limitExhaustivityChecks |= TC.typeCheckCondition(guard, DC); labelItem.setGuardExpr(guard); } } - + + // Check restrictions on '@unknown'. + if (caseBlock->hasUnknownAttr()) { + if (caseBlock->getCaseLabelItems().size() != 1) { + assert(!caseBlock->getCaseLabelItems().empty() && + "parser should not produce case blocks with no items"); + TC.diagnose(caseBlock->getLoc(), + diag::unknown_case_multiple_patterns) + .highlight(caseBlock->getCaseLabelItems()[1].getSourceRange()); + limitExhaustivityChecks = true; + } + + if (FallthroughDest != nullptr) { + if (!caseBlock->isDefault()) + TC.diagnose(caseBlock->getLoc(), diag::unknown_case_must_be_last); + limitExhaustivityChecks = true; + } + + const CaseLabelItem &labelItem = caseBlock->getCaseLabelItems().front(); + if (labelItem.getGuardExpr() && !labelItem.isDefault()) { + TC.diagnose(labelItem.getStartLoc(), + diag::unknown_case_where_clause) + .highlight(labelItem.getGuardExpr()->getSourceRange()); + } + + const Pattern *pattern = + labelItem.getPattern()->getSemanticsProvidingPattern(); + if (!isa(pattern)) { + TC.diagnose(labelItem.getStartLoc(), + diag::unknown_case_must_be_catchall) + .highlight(pattern->getSourceRange()); + } + } + // If the previous case fellthrough, similarly check that that case's bindings // includes our first label item's pattern bindings and types. if (PreviousFallthrough) { @@ -952,13 +984,13 @@ class StmtChecker : public StmtVisitor { // Type-check the body statements. PreviousFallthrough = nullptr; Stmt *body = caseBlock->getBody(); - hadError |= typeCheckStmt(body); + limitExhaustivityChecks |= typeCheckStmt(body); caseBlock->setBody(body); previousBlock = caseBlock; } if (!S->isImplicit()) { - TC.checkSwitchExhaustiveness(S, DC, /*limitChecking*/hadError); + TC.checkSwitchExhaustiveness(S, DC, limitExhaustivityChecks); } return S; @@ -1290,7 +1322,7 @@ void TypeChecker::checkIgnoredExpr(Expr *E) { .highlight(SR1).highlight(SR2); } else diagnose(fn->getLoc(), diag::expression_unused_result_unknown, - valueE->getType()) + isa(fn), valueE->getType()) .highlight(SR1).highlight(SR2); return; @@ -1356,8 +1388,7 @@ Stmt *StmtChecker::visitBraceStmt(BraceStmt *BS) { (Loc == EndTypeCheckLoc || SM.isBeforeInBuffer(EndTypeCheckLoc, Loc))) break; - TC.typeCheckDecl(SubDecl, /*isFirstPass*/true); - TC.typeCheckDecl(SubDecl, /*isFirstPass*/false); + TC.typeCheckDecl(SubDecl); } return BS; @@ -1397,29 +1428,27 @@ static void checkDefaultArguments(TypeChecker &tc, /// Check the default arguments that occur within this pattern. void TypeChecker::checkDefaultArguments(ArrayRef paramLists, ValueDecl *VD) { + auto access = + VD->getFormalAccessScope(/*useDC=*/nullptr, + /*treatUsableFromInlineAsPublic=*/true); + // In Swift 4 mode, default argument bodies are inlined into the // caller. if (auto *func = dyn_cast(VD)) { auto expansion = func->getResilienceExpansion(); - if (!Context.isSwiftVersion3() && - func->getFormalAccessScope(/*useDC=*/nullptr, - /*respectVersionedAttr=*/true).isPublic()) + if (!Context.isSwiftVersion3() && access.isPublic()) expansion = ResilienceExpansion::Minimal; func->setDefaultArgumentResilienceExpansion(expansion); } else { auto *EED = cast(VD); - auto expansion = EED->getParentEnum()->getResilienceExpansion(); - // Enum payloads parameter lists may have default arguments as of Swift 5. - if (Context.isSwiftVersionAtLeast(5) && - EED->getFormalAccessScope(/*useDC=*/nullptr, - /*respectVersionedAttr=*/true).isPublic()) + auto expansion = ResilienceExpansion::Maximal; + if (access.isPublic()) expansion = ResilienceExpansion::Minimal; EED->setDefaultArgumentResilienceExpansion(expansion); } - unsigned nextArgIndex = 0; for (auto *paramList : paramLists) ::checkDefaultArguments(*this, paramList, nextArgIndex); @@ -1443,9 +1472,18 @@ bool TypeChecker::typeCheckAbstractFunctionBodyUntil(AbstractFunctionDecl *AFD, } bool TypeChecker::typeCheckAbstractFunctionBody(AbstractFunctionDecl *AFD) { + // HACK: don't type-check the same function body twice. This is + // supposed to be handled by just not enqueuing things twice, + // but that gets tricky with synthesized function bodies. + if (AFD->isBodyTypeChecked()) + return false; + if (!AFD->getBody()) return false; + FrontendStatsTracer StatsTracer(Context.Stats, "typecheck-fn", AFD); + PrettyStackTraceDecl StackEntry("type-checking", AFD); + if (Context.Stats) Context.Stats->getFrontendCounters().NumFunctionsTypechecked++; @@ -1456,9 +1494,12 @@ bool TypeChecker::typeCheckAbstractFunctionBody(AbstractFunctionDecl *AFD) { for (auto paramList : AFD->getParameterLists()) requestRequiredNominalTypeLayoutForParameters(paramList); - if (typeCheckAbstractFunctionBodyUntil(AFD, SourceLoc())) + bool error = typeCheckAbstractFunctionBodyUntil(AFD, SourceLoc()); + AFD->setBodyTypeCheckedIfPresent(); + + if (error) return true; - + performAbstractFuncDeclDiagnostics(*this, AFD); return false; } @@ -1659,9 +1700,10 @@ bool TypeChecker::typeCheckConstructorBodyUntil(ConstructorDecl *ctor, // declarations. if (!isDelegating && ClassD->isResilient() && ctor->getResilienceExpansion() == ResilienceExpansion::Minimal) { + auto kind = getFragileFunctionKind(ctor); diagnose(ctor, diag::class_designated_init_inlinable_resilient, ClassD->getDeclaredInterfaceType(), - static_cast(getFragileFunctionKind(ctor))); + static_cast(kind.first)); } } diff --git a/lib/Sema/TypeCheckSwitchStmt.cpp b/lib/Sema/TypeCheckSwitchStmt.cpp index 479045d268af8..b2cf0c913d840 100644 --- a/lib/Sema/TypeCheckSwitchStmt.cpp +++ b/lib/Sema/TypeCheckSwitchStmt.cpp @@ -78,13 +78,22 @@ namespace { Type, Constructor, Disjunct, - BooleanConstant + BooleanConstant, + UnknownCase, }; + // The order of cases is used for disjunctions: a disjunction's + // DowngradeToWarning condition is the std::min of its spaces'. enum class DowngradeToWarning { No, + ForUnknownCase, ForSwift3Case, - ForSwift4Exhaustive, + + LAST = ForSwift3Case + }; + + enum UnknownCase_t { + UnknownCase }; /// A data structure for conveniently pattern-matching on the kinds of @@ -112,7 +121,7 @@ namespace { #define PAIRCASE(XS, YS) case PairSwitch(XS, YS) - class Space final { + class Space final : public RelationalOperationsBase { private: SpaceKind Kind; llvm::PointerIntPair TypeAndVal; @@ -134,6 +143,8 @@ namespace { return 0; case SpaceKind::BooleanConstant: return 1; + case SpaceKind::UnknownCase: + return isAllowedButNotRequired() ? 0 : 1; case SpaceKind::Type: { if (!canDecompose(getType(), DC)) { return 1; @@ -176,27 +187,68 @@ namespace { } } - public: - explicit Space(Type T, Identifier NameForPrinting, bool downgrade) - : Kind(SpaceKind::Type), TypeAndVal(T, downgrade), + explicit Space(Type T, Identifier NameForPrinting) + : Kind(SpaceKind::Type), TypeAndVal(T), Head(NameForPrinting), Spaces({}){} + explicit Space(UnknownCase_t, bool allowedButNotRequired) + : Kind(SpaceKind::UnknownCase), + TypeAndVal(Type(), allowedButNotRequired), Head(Identifier()), + Spaces({}) {} explicit Space(Type T, Identifier H, bool downgrade, - SmallVectorImpl &SP) + ArrayRef SP) : Kind(SpaceKind::Constructor), TypeAndVal(T, downgrade), Head(H), Spaces(SP.begin(), SP.end()) {} explicit Space(Type T, Identifier H, bool downgrade, - const std::forward_list &SP) + std::forward_list SP) : Kind(SpaceKind::Constructor), TypeAndVal(T, downgrade), Head(H), - Spaces(SP.begin(), SP.end()) {} - explicit Space(SmallVectorImpl &SP) - : Kind(SpaceKind::Disjunct), TypeAndVal(Type(), false), + Spaces(SP) {} + explicit Space(ArrayRef SP) + : Kind(SpaceKind::Disjunct), TypeAndVal(Type()), Head(Identifier()), Spaces(SP.begin(), SP.end()) {} - explicit Space() - : Kind(SpaceKind::Empty), TypeAndVal(Type(), false), Head(Identifier()), - Spaces({}) {} explicit Space(bool C) : Kind(SpaceKind::BooleanConstant), TypeAndVal(Type(), C), Head(Identifier()), Spaces({}) {} + public: + explicit Space() + : Kind(SpaceKind::Empty), TypeAndVal(Type()), Head(Identifier()), + Spaces({}) {} + + static Space forType(Type T, Identifier NameForPrinting) { + return Space(T, NameForPrinting); + } + static Space forUnknown(bool allowedButNotRequired) { + return Space(UnknownCase, allowedButNotRequired); + } + static Space forConstructor(Type T, Identifier H, bool downgrade, + ArrayRef SP) { + return Space(T, H, downgrade, SP); + } + static Space forConstructor(Type T, Identifier H, bool downgrade, + std::forward_list SP) { + return Space(T, H, downgrade, SP); + } + static Space forBool(bool C) { + return Space(C); + } + static Space forDisjunct(ArrayRef SP) { + SmallVector spaces(SP.begin(), SP.end()); + spaces.erase( + std::remove_if(spaces.begin(), spaces.end(), + [](const Space &space) { return space.isEmpty(); }), + spaces.end()); + + if (spaces.empty()) + return Space(); + if (spaces.size() == 1) + return spaces.front(); + + return Space(spaces); + } + + bool operator==(const Space &other) const { + return Kind == other.Kind && TypeAndVal == other.TypeAndVal && + Head == other.Head && Spaces == other.Spaces; + } SpaceKind getKind() const { return Kind; } @@ -220,6 +272,12 @@ namespace { return TypeAndVal.getInt(); } + bool isAllowedButNotRequired() const { + assert(getKind() == SpaceKind::UnknownCase + && "Wrong kind of space tried to access not-required flag"); + return TypeAndVal.getInt(); + } + Type getType() const { assert((getKind() == SpaceKind::Type || getKind() == SpaceKind::Constructor) @@ -269,6 +327,7 @@ namespace { return false; case SpaceKind::Type: case SpaceKind::BooleanConstant: + case SpaceKind::UnknownCase: return true; case SpaceKind::Disjunct: if (getSpaces().empty()) { @@ -300,7 +359,8 @@ namespace { PAIRCASE (SpaceKind::Disjunct, SpaceKind::Type): PAIRCASE (SpaceKind::Disjunct, SpaceKind::Constructor): PAIRCASE (SpaceKind::Disjunct, SpaceKind::Disjunct): - PAIRCASE (SpaceKind::Disjunct, SpaceKind::BooleanConstant): { + PAIRCASE (SpaceKind::Disjunct, SpaceKind::BooleanConstant): + PAIRCASE (SpaceKind::Disjunct, SpaceKind::UnknownCase): { // (S1 | ... | Sn) <= S iff (S1 <= S) && ... && (Sn <= S) for (auto &space : this->getSpaces()) { if (!space.isSubspace(other, TC, DC)) { @@ -317,18 +377,14 @@ namespace { // (_ : Ty1) <= (_ : Ty2) iff D(Ty1) == D(Ty2) if (canDecompose(this->getType(), DC)) { - SmallVector disjuncts; - decompose(TC, DC, this->getType(), disjuncts); - Space or1Space(disjuncts); + Space or1Space = decompose(TC, DC, this->getType()); if (or1Space.isSubspace(other, TC, DC)) { return true; } } if (canDecompose(other.getType(), DC)) { - SmallVector disjuncts; - decompose(TC, DC, other.getType(), disjuncts); - Space or2Space(disjuncts); + Space or2Space = decompose(TC, DC, other.getType()); return this->isSubspace(or2Space, TC, DC); } @@ -346,25 +402,26 @@ namespace { if (!canDecompose(this->getType(), DC)) { return false; } - SmallVector disjuncts; - decompose(TC, DC, this->getType(), disjuncts); - Space or1Space(disjuncts); + Space or1Space = decompose(TC, DC, this->getType()); return or1Space.isSubspace(other, TC, DC); } PAIRCASE (SpaceKind::Type, SpaceKind::Constructor): { // (_ : Ty1) <= H(p1 | ... | pn) iff D(Ty1) <= H(p1 | ... | pn) if (canDecompose(this->getType(), DC)) { - SmallVector disjuncts; - decompose(TC, DC, this->getType(), disjuncts); - Space or1Space(disjuncts); + Space or1Space = decompose(TC, DC, this->getType()); return or1Space.isSubspace(other, TC, DC); } // An undecomposable type is always larger than its constructor space. return false; } + PAIRCASE (SpaceKind::Type, SpaceKind::UnknownCase): + return false; + PAIRCASE (SpaceKind::Constructor, SpaceKind::Type): // Typechecking guaranteed this constructor is a subspace of the type. return true; + PAIRCASE (SpaceKind::UnknownCase, SpaceKind::Type): + return true; PAIRCASE (SpaceKind::BooleanConstant, SpaceKind::Type): return other.getType()->isBool(); PAIRCASE (SpaceKind::Constructor, SpaceKind::Constructor): { @@ -391,8 +448,17 @@ namespace { } return true; } + PAIRCASE (SpaceKind::Constructor, SpaceKind::UnknownCase): + for (auto ¶m : this->getSpaces()) { + if (param.isSubspace(other, TC, DC)) { + return true; + } + } + return false; + PAIRCASE (SpaceKind::Constructor, SpaceKind::Disjunct): - PAIRCASE (SpaceKind::BooleanConstant, SpaceKind::Disjunct): { + PAIRCASE (SpaceKind::BooleanConstant, SpaceKind::Disjunct): + PAIRCASE (SpaceKind::UnknownCase, SpaceKind::Disjunct): { // S <= (S1 | ... | Sn) <= S iff (S <= S1) || ... || (S <= Sn) for (auto ¶m : other.getSpaces()) { if (this->isSubspace(param, TC, DC)) { @@ -405,16 +471,33 @@ namespace { PAIRCASE (SpaceKind::BooleanConstant, SpaceKind::BooleanConstant): return this->getBoolValue() == other.getBoolValue(); + PAIRCASE (SpaceKind::BooleanConstant, SpaceKind::UnknownCase): + return false; + PAIRCASE (SpaceKind::Empty, SpaceKind::BooleanConstant): PAIRCASE (SpaceKind::Constructor, SpaceKind::BooleanConstant): PAIRCASE (SpaceKind::Type, SpaceKind::BooleanConstant): - return false; + PAIRCASE (SpaceKind::UnknownCase, SpaceKind::BooleanConstant): + return false; + + PAIRCASE (SpaceKind::UnknownCase, SpaceKind::Constructor): + return false; + + PAIRCASE (SpaceKind::UnknownCase, SpaceKind::UnknownCase): + if (other.isAllowedButNotRequired()) + return this->isAllowedButNotRequired(); + return true; default: llvm_unreachable("Uncovered pair found while computing subspaces?"); } } + static Space intersect(const Space &a, const Space &b, TypeChecker &TC, + const DeclContext *DC) { + return a.intersect(b, TC, DC).simplify(TC, DC); + } + // Returns the intersection of this space with another. The intersection // is the largest shared subspace occupied by both arguments. Space intersect(const Space &other, TypeChecker &TC, @@ -424,91 +507,75 @@ namespace { return Space(); } - llvm::function_ref &)> examineDecomp - = [&](SmallVectorImpl &decomposition) -> Space { - if (decomposition.empty()) { - return Space(); - } else if (decomposition.size() == 1) { - return decomposition.front(); - } - Space ds(decomposition); - return ds; - }; - switch (PairSwitch(getKind(), other.getKind())) { PAIRCASE (SpaceKind::Empty, SpaceKind::Disjunct): PAIRCASE (SpaceKind::Type, SpaceKind::Disjunct): PAIRCASE (SpaceKind::Constructor, SpaceKind::Disjunct): PAIRCASE (SpaceKind::Disjunct, SpaceKind::Disjunct): - PAIRCASE (SpaceKind::BooleanConstant, SpaceKind::Disjunct): { + PAIRCASE (SpaceKind::BooleanConstant, SpaceKind::Disjunct): + PAIRCASE (SpaceKind::UnknownCase, SpaceKind::Disjunct): { // S & (S1 || ... || Sn) iff (S & S1) && ... && (S & Sn) SmallVector intersectedCases; - std::transform(other.getSpaces().begin(), other.getSpaces().end(), - std::back_inserter(intersectedCases), - [&](const Space &s) { - return this->intersect(s, TC, DC); - }); - // Optimization: Remove all empty spaces. - SmallVector filteredCases; - std::copy_if(intersectedCases.begin(), intersectedCases.end(), - std::back_inserter(filteredCases), - [&](const Space &s) { - return !s.isEmpty(); - }); - return examineDecomp(filteredCases); + std::transform( + other.getSpaces().begin(), other.getSpaces().end(), + std::back_inserter(intersectedCases), + [&](const Space &s) { return intersect(*this, s, TC, DC); }); + return Space::forDisjunct(intersectedCases); } PAIRCASE (SpaceKind::Disjunct, SpaceKind::Empty): PAIRCASE (SpaceKind::Disjunct, SpaceKind::Type): PAIRCASE (SpaceKind::Disjunct, SpaceKind::Constructor): - PAIRCASE (SpaceKind::Disjunct, SpaceKind::BooleanConstant): { + PAIRCASE (SpaceKind::Disjunct, SpaceKind::BooleanConstant): + PAIRCASE (SpaceKind::Disjunct, SpaceKind::UnknownCase): { // (S1 || ... || Sn) & S iff (S & S1) && ... && (S & Sn) - SmallVector intersectedCases; - std::transform(this->getSpaces().begin(), this->getSpaces().end(), - std::back_inserter(intersectedCases), - [&](const Space &s) { - return s.intersect(other, TC, DC); - }); - // Optimization: Remove all empty spaces. - SmallVector filteredCases; - std::copy_if(intersectedCases.begin(), intersectedCases.end(), - std::back_inserter(filteredCases), - [&](const Space &s) { - return !s.isEmpty(); - }); - return examineDecomp(filteredCases); + return intersect(other, *this, TC, DC); } PAIRCASE (SpaceKind::Type, SpaceKind::Type): { // Optimization: The intersection of equal types is that type. if (this->getType()->isEqual(other.getType())) { return other; } else if (canDecompose(this->getType(), DC)) { - SmallVector spaces; - decompose(TC, DC, this->getType(), spaces); - auto decomposition = examineDecomp(spaces); - return decomposition.intersect(other, TC, DC); + auto decomposition = decompose(TC, DC, this->getType()); + return intersect(decomposition, other, TC, DC); } else if (canDecompose(other.getType(), DC)) { - SmallVector spaces; - decompose(TC, DC, other.getType(), spaces); - auto disjunctSp = examineDecomp(spaces); - return this->intersect(disjunctSp, TC, DC); + auto decomposition = decompose(TC, DC, other.getType()); + return intersect(*this, decomposition, TC, DC); } else { return other; } } PAIRCASE (SpaceKind::Type, SpaceKind::Constructor): { if (canDecompose(this->getType(), DC)) { - SmallVector spaces; - decompose(TC, DC, this->getType(), spaces); - auto decomposition = examineDecomp(spaces); - return decomposition.intersect(other, TC, DC); + auto decomposition = decompose(TC, DC, this->getType()); + return intersect(decomposition, other, TC, DC); } else { return other; } } + PAIRCASE (SpaceKind::Type, SpaceKind::UnknownCase): + // Note: This is not technically correct for decomposable types, but + // you'd only get "typeSpace - unknownCaseSpace" if you haven't tried + // to match any of the decompositions of the space yet. In that case, + // we'd rather not expand the type, because it might be infinitely + // decomposable. + return *this; + PAIRCASE (SpaceKind::Constructor, SpaceKind::Type): return *this; - + + PAIRCASE (SpaceKind::Constructor, SpaceKind::UnknownCase): { + SmallVector newSubSpaces; + std::transform(this->getSpaces().begin(), this->getSpaces().end(), + std::back_inserter(newSubSpaces), + [&](const Space &subSpace) { + return intersect(subSpace, other, TC, DC); + }); + return Space::forConstructor(this->getType(), this->getHead(), + this->canDowngradeToWarning(), + newSubSpaces); + } + PAIRCASE (SpaceKind::Constructor, SpaceKind::Constructor): { // Optimization: If the heads don't match, the intersection of // the constructor spaces is empty. @@ -527,16 +594,26 @@ namespace { auto j = other.getSpaces().begin(); for (; i != this->getSpaces().end() && j != other.getSpaces().end(); ++i, ++j) { - auto intersection = (*i).intersect(*j, TC, DC); - if (intersection.simplify(TC, DC).isEmpty()) { + auto result = intersect(*i, *j, TC, DC); + // If at least one of the constructor sub-spaces is empty, + // it makes the whole space empty as well. + if (result.isEmpty()) { return Space(); } - paramSpace.push_back(intersection); + paramSpace.push_back(result); } - return examineDecomp(paramSpace); + return Space::forDisjunct(paramSpace); } + PAIRCASE (SpaceKind::UnknownCase, SpaceKind::Type): + PAIRCASE (SpaceKind::UnknownCase, SpaceKind::Constructor): + return intersect(other, *this, TC, DC); + PAIRCASE (SpaceKind::UnknownCase, SpaceKind::UnknownCase): + if (other.isAllowedButNotRequired()) + return other; + return *this; + PAIRCASE (SpaceKind::BooleanConstant, SpaceKind::BooleanConstant): return this->getBoolValue() == other.getBoolValue() ? *this : Space(); @@ -546,30 +623,23 @@ namespace { } if (canDecompose(other.getType(), DC)) { - SmallVector spaces; - decompose(TC, DC, other.getType(), spaces); - auto disjunctSp = examineDecomp(spaces); - return this->intersect(disjunctSp, TC, DC); + auto decomposition = decompose(TC, DC, other.getType()); + return intersect(*this, decomposition, TC, DC); } return Space(); } PAIRCASE (SpaceKind::BooleanConstant, SpaceKind::Empty): PAIRCASE (SpaceKind::BooleanConstant, SpaceKind::Constructor): + PAIRCASE (SpaceKind::BooleanConstant, SpaceKind::UnknownCase): return Space(); PAIRCASE (SpaceKind::Type, SpaceKind::BooleanConstant): { - if (canDecompose(this->getType(), DC)) { - SmallVector spaces; - decompose(TC, DC, this->getType(), spaces); - auto disjunctSp = examineDecomp(spaces); - return disjunctSp.intersect(other, TC, DC); - } else { - return Space(); - } + return intersect(other, *this, TC, DC); } PAIRCASE (SpaceKind::Empty, SpaceKind::BooleanConstant): PAIRCASE (SpaceKind::Constructor, SpaceKind::BooleanConstant): + PAIRCASE (SpaceKind::UnknownCase, SpaceKind::BooleanConstant): return Space(); default: @@ -580,9 +650,19 @@ namespace { // Returns the result of subtracting the other space from this space. The // result is empty if the other space completely covers this space, or // non-empty if there were any uncovered cases. The difference of spaces - // is the smallest uncovered set of cases. - Space minus(const Space &other, TypeChecker &TC, - const DeclContext *DC) const { + // is the smallest uncovered set of cases. The result is absent if the + // computation had to be abandoned. + // + // \p minusCount is an optional pointer counting the number of + // times minus has run. + // Returns None if the computation "timed out". + Optional minus(const Space &other, TypeChecker &TC, + const DeclContext *DC, unsigned *minusCount) const { + + if (minusCount && TC.getSwitchCheckingInvocationThreshold() && + (*minusCount)++ >= TC.getSwitchCheckingInvocationThreshold()) + return None; + if (this->isEmpty()) { return Space(); } @@ -591,70 +671,83 @@ namespace { return *this; } - llvm::function_ref &)> examineDecomp - = [&](SmallVectorImpl &decomposition) -> Space { - if (decomposition.empty()) { - return Space(); - } else if (decomposition.size() == 1) { - return decomposition.front(); - } - return Space(decomposition); - }; - switch (PairSwitch(this->getKind(), other.getKind())) { PAIRCASE (SpaceKind::Type, SpaceKind::Type): { // Optimization: Are the types equal? If so, the space is covered. if (this->getType()->isEqual(other.getType())) { return Space(); } else if (canDecompose(this->getType(), DC)) { - SmallVector spaces; - this->decompose(TC, DC, this->getType(), spaces); - return examineDecomp(spaces).intersect(other, TC, DC); + auto decomposition = decompose(TC, DC, this->getType()); + return intersect(decomposition, other, TC, DC); } else if (canDecompose(other.getType(), DC)) { - SmallVector spaces; - this->decompose(TC, DC, other.getType(), spaces); - auto decomp = examineDecomp(spaces); - return this->intersect(decomp, TC, DC); + auto decomposition = decompose(TC, DC, other.getType()); + return intersect(*this, decomposition, TC, DC); } return Space(); } PAIRCASE (SpaceKind::Type, SpaceKind::Constructor): { if (canDecompose(this->getType(), DC)) { - SmallVector spaces; - this->decompose(TC, DC, this->getType(), spaces); - auto decomp = examineDecomp(spaces); - return decomp.minus(other, TC, DC); + auto decomposition = decompose(TC, DC, this->getType()); + return decomposition.minus(other, TC, DC, minusCount); } else { return *this; } } + PAIRCASE (SpaceKind::Type, SpaceKind::UnknownCase): + // Note: This is not technically correct for decomposable types, but + // you'd only get "typeSpace - unknownCaseSpace" if you haven't tried + // to match any of the decompositions of the space yet. In that case, + // we'd rather not expand the type, because it might be infinitely + // decomposable. + return *this; + PAIRCASE (SpaceKind::Empty, SpaceKind::Disjunct): PAIRCASE (SpaceKind::Type, SpaceKind::Disjunct): PAIRCASE (SpaceKind::Constructor, SpaceKind::Disjunct): PAIRCASE (SpaceKind::Disjunct, SpaceKind::Disjunct): - PAIRCASE (SpaceKind::BooleanConstant, SpaceKind::Disjunct): { - return std::accumulate(other.getSpaces().begin(), - other.getSpaces().end(), - *this, - [&](const Space &left, const Space &right){ - return left.minus(right, TC, DC); - }); + PAIRCASE (SpaceKind::BooleanConstant, SpaceKind::Disjunct): + PAIRCASE (SpaceKind::UnknownCase, SpaceKind::Disjunct): { + Space tot = *this; + for (auto s : other.getSpaces()) { + if (auto diff = tot.minus(s, TC, DC, minusCount)) + tot = *diff; + else + return None; + } + return tot; } PAIRCASE (SpaceKind::Disjunct, SpaceKind::Empty): PAIRCASE (SpaceKind::Disjunct, SpaceKind::Type): PAIRCASE (SpaceKind::Disjunct, SpaceKind::Constructor): - PAIRCASE (SpaceKind::Disjunct, SpaceKind::BooleanConstant): { + PAIRCASE (SpaceKind::Disjunct, SpaceKind::BooleanConstant): + PAIRCASE (SpaceKind::Disjunct, SpaceKind::UnknownCase): { SmallVector smallSpaces; - std::transform(this->getSpaces().begin(), this->getSpaces().end(), - std::back_inserter(smallSpaces), - [&](const Space &first){ - return first.minus(other, TC, DC); - }); - return examineDecomp(smallSpaces); + for (auto s : this->getSpaces()) { + if (auto diff = s.minus(other, TC, DC, minusCount)) + smallSpaces.push_back(*diff); + else + return None; + } + return Space::forDisjunct(smallSpaces); } PAIRCASE (SpaceKind::Constructor, SpaceKind::Type): return Space(); + PAIRCASE (SpaceKind::Constructor, SpaceKind::UnknownCase): { + SmallVector newSubSpaces; + for (auto subSpace : this->getSpaces()) { + auto diff = subSpace.minus(other, TC, DC, minusCount); + if (!diff) + return None; + auto nextSpace = diff->simplify(TC, DC); + if (nextSpace.isEmpty()) + return Space(); + newSubSpaces.push_back(nextSpace); + } + return Space::forConstructor(this->getType(), this->getHead(), + this->canDowngradeToWarning(), + newSubSpaces); + } PAIRCASE (SpaceKind::Constructor, SpaceKind::Constructor): { // Optimization: If the heads of the constructors don't match then @@ -680,7 +773,7 @@ namespace { auto &s2 = *j; // If the intersection of each subspace is ever empty then the // two spaces are disjoint and their difference is the first space. - if (s1.intersect(s2, TC, DC).simplify(TC, DC).isEmpty()) { + if (intersect(s1, s2, TC, DC).isEmpty()) { return *this; } @@ -694,14 +787,35 @@ namespace { // into each parameter. SmallVector copyParams(this->getSpaces().begin(), this->getSpaces().end()); - copyParams[idx] = s1.minus(s2, TC, DC); - Space CS(this->getType(), this->getHead(), - this->canDowngradeToWarning(), copyParams); + + auto reducedSpaceOrNone = s1.minus(s2, TC, DC, minusCount); + if (!reducedSpaceOrNone) + return None; + auto reducedSpace = *reducedSpaceOrNone; + + // If one of the constructor parameters is empty it means + // the whole constructor space is empty as well, so we can + // safely skip it. + if (reducedSpace.isEmpty()) + continue; + + // If reduced produced the same space as original one, we + // should return it directly instead of trying to create + // a disjunction of its sub-spaces because nothing got reduced. + // This is especially helpful when dealing with `unknown` case + // in parameter positions. + if (s1 == reducedSpace) + return *this; + + copyParams[idx] = reducedSpace; + Space CS = Space::forConstructor(this->getType(), this->getHead(), + this->canDowngradeToWarning(), + copyParams); constrSpaces.push_back(CS); } if (foundBad) { - return examineDecomp(constrSpaces); + return Space::forDisjunct(constrSpaces); } return Space(); } @@ -719,31 +833,40 @@ namespace { } if (canDecompose(other.getType(), DC)) { - SmallVector spaces; - this->decompose(TC, DC, other.getType(), spaces); - auto disjunctSp = examineDecomp(spaces); - return this->minus(disjunctSp, TC, DC); + auto decomposition = decompose(TC, DC, other.getType()); + return this->minus(decomposition, TC, DC, minusCount); } return *this; } PAIRCASE (SpaceKind::BooleanConstant, SpaceKind::Empty): PAIRCASE (SpaceKind::BooleanConstant, SpaceKind::Constructor): + PAIRCASE (SpaceKind::BooleanConstant, SpaceKind::UnknownCase): return *this; PAIRCASE (SpaceKind::Type, SpaceKind::BooleanConstant): { if (canDecompose(this->getType(), DC)) { - SmallVector spaces; - this->decompose(TC, DC, this->getType(), spaces); - auto orSpace = examineDecomp(spaces); - return orSpace.minus(other, TC, DC); + auto orSpace = decompose(TC, DC, this->getType()); + return orSpace.minus(other, TC, DC, minusCount); } else { return *this; } } + PAIRCASE (SpaceKind::UnknownCase, SpaceKind::Type): + return Space(); + PAIRCASE (SpaceKind::UnknownCase, SpaceKind::Constructor): + return *this; + PAIRCASE (SpaceKind::UnknownCase, SpaceKind::UnknownCase): + if (other.isAllowedButNotRequired() && + !this->isAllowedButNotRequired()) { + return *this; + } + return Space(); + PAIRCASE (SpaceKind::Empty, SpaceKind::BooleanConstant): PAIRCASE (SpaceKind::Constructor, SpaceKind::BooleanConstant): - return Space(); + PAIRCASE (SpaceKind::UnknownCase, SpaceKind::BooleanConstant): + return *this; default: llvm_unreachable("Uncovered pair found while computing difference?"); } @@ -799,15 +922,27 @@ namespace { buffer << ")"; } break; - case SpaceKind::Type: - if (!forDisplay) { - getType()->print(buffer); - } + case SpaceKind::Type: { Identifier Name = getPrintingName(); if (Name.empty()) buffer << "_"; else buffer << tok::kw_let << " " << Name.str(); + if (!forDisplay) { + buffer << ": "; + getType()->print(buffer); + } + } + break; + case SpaceKind::UnknownCase: + if (forDisplay) { + // We special-case this to use "@unknown default" at the top level. + buffer << "_"; + } else { + buffer << "UNKNOWN"; + if (isAllowedButNotRequired()) + buffer << "(not_required)"; + } break; } } @@ -826,18 +961,16 @@ namespace { // Simplify each component subspace. If, after simplification, any // subspace contains an empty, then the whole space is empty. SmallVector simplifiedSpaces; - std::transform(getSpaces().begin(), getSpaces().end(), - std::back_inserter(simplifiedSpaces), - [&](const Space &el) { - return el.simplify(TC, DC); - }); - for (auto &el : simplifiedSpaces) { - if (el.isEmpty()) { + for (const auto &space : Spaces) { + auto simplified = space.simplify(TC, DC); + if (simplified.isEmpty()) return Space(); - } + + simplifiedSpaces.push_back(simplified); } - return Space(getType(), Head, canDowngradeToWarning(), - simplifiedSpaces); + + return Space::forConstructor(getType(), Head, canDowngradeToWarning(), + simplifiedSpaces); } case SpaceKind::Type: { // If the decomposition of a space is empty, the space is empty. @@ -860,27 +993,7 @@ namespace { [&](const Space &el){ return el.simplify(TC, DC); }); - // If the disjunct is singular, unpack it into its component. - if (simplifiedSpaces.size() == 1) { - return simplifiedSpaces.front(); - } - - // Otherwise, remove any empties. - SmallVector compatifiedSpaces; - std::copy_if(simplifiedSpaces.begin(), simplifiedSpaces.end(), - std::back_inserter(compatifiedSpaces), - [&](const Space &el) { - return !el.isEmpty(); - }); - // If the disjunct was all empty, the space is empty. - if (compatifiedSpaces.empty()) { - return Space(); - } - // Else if the disjunct is singular, unpack it into its component. - if (compatifiedSpaces.size() == 1) { - return compatifiedSpaces.front(); - } - return Space(compatifiedSpaces); + return Space::forDisjunct(simplifiedSpaces); } default: return *this; @@ -894,22 +1007,14 @@ namespace { return eed->getAttrs().hasAttribute(); } - static bool isNonExhaustiveEnum(TypeChecker &TC, const DeclContext *DC, - Type tp) { - auto *enumDecl = tp->getEnumOrBoundGenericEnum(); - if (!enumDecl) - return false; - return !enumDecl->isExhaustive(DC); - } - // Decompose a type into its component spaces. static void decompose(TypeChecker &TC, const DeclContext *DC, Type tp, SmallVectorImpl &arr) { assert(canDecompose(tp, DC) && "Non-decomposable type?"); if (tp->isBool()) { - arr.push_back(Space(true)); - arr.push_back(Space(false)); + arr.push_back(Space::forBool(true)); + arr.push_back(Space::forBool(false)); } else if (auto *E = tp->getEnumOrBoundGenericEnum()) { // Look into each case of the enum and decompose it in turn. auto children = E->getAllElements(); @@ -928,6 +1033,13 @@ namespace { if (!eed->hasInterfaceType()) { return Space(); } + + // Don't force people to match unavailable cases; they can't even + // write them. + if (AvailableAttr::isUnavailable(eed)) { + return Space(); + } + auto eedTy = tp->getCanonicalType() ->getTypeOfMember(E->getModuleContext(), eed, eed->getArgumentInterfaceType()); @@ -937,47 +1049,51 @@ namespace { llvm::transform(TTy->getElements(), std::back_inserter(constElemSpaces), [&](TupleTypeElt elt) { - bool canDowngrade = isNonExhaustiveEnum(TC, DC, - elt.getType()); - return Space(elt.getType(), elt.getName(), canDowngrade); + return Space::forType(elt.getType(), elt.getName()); }); } else if (auto *TTy = dyn_cast(eedTy.getPointer())) { - bool canDowngrade = isNonExhaustiveEnum(TC, DC, TTy); - constElemSpaces.push_back(Space(TTy->getUnderlyingType(), - Identifier(), canDowngrade)); + constElemSpaces.push_back( + Space::forType(TTy->getUnderlyingType(), Identifier())); } } bool canDowngrade = isSwift3DowngradeExhaustivityCase(TC, eed); - return Space(tp, eed->getName(), canDowngrade, constElemSpaces); + return Space::forConstructor(tp, eed->getName(), canDowngrade, + constElemSpaces); }); + + if (!E->isExhaustive(DC)) { + arr.push_back(Space::forUnknown(/*allowedButNotRequired*/false)); + } else if (!E->getAttrs().hasAttribute()) { + arr.push_back(Space::forUnknown(/*allowedButNotRequired*/true)); + } + } else if (auto *TTy = tp->castTo()) { // Decompose each of the elements into its component type space. SmallVector constElemSpaces; llvm::transform(TTy->getElements(), std::back_inserter(constElemSpaces), [&](TupleTypeElt elt) { - bool canDowngrade = isNonExhaustiveEnum(TC, DC, elt.getType()); - return Space(elt.getType(), elt.getName(), canDowngrade); + return Space::forType(elt.getType(), elt.getName()); }); // Create an empty constructor head for the tuple space. - arr.push_back(Space(tp, Identifier(), /*canDowngrade*/false, - constElemSpaces)); + arr.push_back(Space::forConstructor(tp, Identifier(), + /*canDowngrade*/false, + constElemSpaces)); } else { llvm_unreachable("Can't decompose type?"); } } + static Space decompose(TypeChecker &TC, const DeclContext *DC, + Type type) { + SmallVector spaces; + decompose(TC, DC, type, spaces); + return Space::forDisjunct(spaces); + } + static bool canDecompose(Type tp, const DeclContext *DC) { - if (tp->is() || tp->isBool()) - return true; - if (const EnumDecl *enumDecl = tp->getEnumOrBoundGenericEnum()) { - ASTContext &ctx = tp->getASTContext(); - if (ctx.LangOpts.EnableNonFrozenEnumExhaustivityDiagnostics) - return enumDecl->isExhaustive(DC); - else - return true; - } - return false; + return tp->is() || tp->isBool() || + tp->getEnumOrBoundGenericEnum(); } // HACK: Search the space for any remaining cases that were labelled @@ -985,24 +1101,30 @@ namespace { DowngradeToWarning checkDowngradeToWarning() const { switch (getKind()) { case SpaceKind::Type: - if (canDowngradeToWarning()) - return DowngradeToWarning::ForSwift4Exhaustive; - return DowngradeToWarning::No; case SpaceKind::BooleanConstant: case SpaceKind::Empty: return DowngradeToWarning::No; - // Traverse the constructor and its subspaces. - case SpaceKind::Constructor: - if (canDowngradeToWarning()) - return DowngradeToWarning::ForSwift3Case; - LLVM_FALLTHROUGH; - case SpaceKind::Disjunct: - // Traverse the disjunct's subspaces. + case SpaceKind::UnknownCase: + return DowngradeToWarning::ForUnknownCase; + case SpaceKind::Constructor: { auto result = DowngradeToWarning::No; + if (canDowngradeToWarning()) + result = DowngradeToWarning::ForSwift3Case; + // Traverse the constructor and its subspaces. for (const Space &space : getSpaces()) result = std::max(result, space.checkDowngradeToWarning()); return result; } + case SpaceKind::Disjunct: { + if (getSpaces().empty()) + return DowngradeToWarning::No; + // Traverse the disjunct's subspaces. + auto result = DowngradeToWarning::LAST; + for (const Space &space : getSpaces()) + result = std::min(result, space.checkDowngradeToWarning()); + return result; + } + } } }; @@ -1084,8 +1206,15 @@ namespace { bool sawDowngradablePattern = false; bool sawRedundantPattern = false; + const CaseStmt *unknownCase = nullptr; SmallVector spaces; for (auto *caseBlock : Switch->getCases()) { + if (caseBlock->hasUnknownAttr()) { + assert(unknownCase == nullptr && "multiple unknown cases"); + unknownCase = caseBlock; + continue; + } + for (auto &caseItem : caseBlock->getCaseLabelItems()) { // 'where'-clauses on cases mean the case does not contribute to // the exhaustiveness of the pattern. @@ -1096,10 +1225,11 @@ namespace { if (caseItem.isDefault()) return; - auto projection = projectPattern(TC, caseItem.getPattern(), - sawDowngradablePattern); + Space projection = projectPattern(TC, caseItem.getPattern(), + sawDowngradablePattern); + if (projection.isUseful() - && projection.isSubspace(Space(spaces), TC, DC)) { + && projection.isSubspace(Space::forDisjunct(spaces), TC, DC)) { sawRedundantPattern |= true; TC.diagnose(caseItem.getStartLoc(), @@ -1120,32 +1250,39 @@ namespace { spaces.push_back(projection); } } - - Space totalSpace(subjectType, Identifier(), - Space::isNonExhaustiveEnum(TC, DC, subjectType)); - Space coveredSpace(spaces); - size_t totalSpaceSize = totalSpace.getSize(TC, DC); - if (totalSpaceSize > Space::getMaximumSize()) { - // Because the space is large, we have to extend the size - // heuristic to compensate for actually exhaustively pattern matching - // over enormous spaces. In this case, if the covered space covers - // as much as the total space, and there were no duplicates, then we - // can assume the user did the right thing and that they don't need - // a 'default' to be inserted. - if (!sawRedundantPattern - && coveredSpace.getSize(TC, DC) >= totalSpaceSize) { - return; - } + Space totalSpace = Space::forType(subjectType, Identifier()); + Space coveredSpace = Space::forDisjunct(spaces); - diagnoseMissingCases(RequiresDefault::SpaceTooLarge, Space()); + const size_t totalSpaceSize = totalSpace.getSize(TC, DC); + if (totalSpaceSize > Space::getMaximumSize()) { + diagnoseCannotCheck(sawRedundantPattern, totalSpaceSize, coveredSpace, + unknownCase); return; } - - auto uncovered = totalSpace.minus(coveredSpace, TC, DC).simplify(TC, DC); - if (uncovered.isEmpty()) { + unsigned minusCount = 0; + auto diff = totalSpace.minus(coveredSpace, TC, DC, &minusCount); + if (!diff) { + diagnoseCannotCheck(sawRedundantPattern, totalSpaceSize, coveredSpace, + unknownCase); return; } + + auto uncovered = diff->simplify(TC, DC); + if (unknownCase && uncovered.isEmpty()) { + TC.diagnose(unknownCase->getLoc(), diag::redundant_particular_case) + .highlight(unknownCase->getSourceRange()); + } + + // Account for unknown cases. If the developer wrote `unknown`, they're + // all handled; otherwise, we ignore the ones that were added for enums + // that are implicitly frozen. + uncovered = *uncovered.minus(Space::forUnknown(unknownCase == nullptr), + TC, DC, /*&minusCount*/ nullptr); + uncovered = uncovered.simplify(TC, DC); + + if (uncovered.isEmpty()) + return; // If the entire space is left uncovered we have two choices: We can // decompose the type space and offer them as fixits, or simply offer @@ -1154,23 +1291,19 @@ namespace { if (Space::canDecompose(uncovered.getType(), DC)) { SmallVector spaces; Space::decompose(TC, DC, uncovered.getType(), spaces); - diagnoseMissingCases(RequiresDefault::No, Space(spaces)); + diagnoseMissingCases(RequiresDefault::No, Space::forDisjunct(spaces), + unknownCase, /*sawDowngradablePattern*/false); } else { diagnoseMissingCases(Switch->getCases().empty() ? RequiresDefault::EmptySwitchBody : RequiresDefault::UncoveredSwitch, - uncovered); + uncovered, unknownCase, + /*sawDowngradablePattern*/false); } return; } - // If the space isn't a disjunct then make it one. - if (uncovered.getKind() != SpaceKind::Disjunct) { - SmallVector spaces = { uncovered }; - uncovered = Space(spaces); - } - - diagnoseMissingCases(RequiresDefault::No, uncovered, + diagnoseMissingCases(RequiresDefault::No, uncovered, unknownCase, sawDowngradablePattern); } @@ -1180,11 +1313,35 @@ namespace { UncoveredSwitch, SpaceTooLarge, }; - + + void diagnoseCannotCheck(const bool sawRedundantPattern, + const size_t totalSpaceSize, + const Space &coveredSpace, + const CaseStmt *unknownCase) { + // Because the space is large or the check is too slow, + // we have to extend the size + // heuristic to compensate for actually exhaustively pattern matching + // over enormous spaces. In this case, if the covered space covers + // as much as the total space, and there were no duplicates, then we + // can assume the user did the right thing and that they don't need + // a 'default' to be inserted. + // FIXME: Do something sensible for non-frozen enums. + if (!sawRedundantPattern && + coveredSpace.getSize(TC, DC) >= totalSpaceSize) + return; + diagnoseMissingCases(RequiresDefault::SpaceTooLarge, Space(), + unknownCase); + } + void diagnoseMissingCases(RequiresDefault defaultReason, Space uncovered, + const CaseStmt *unknownCase = nullptr, bool sawDowngradablePattern = false) { SourceLoc startLoc = Switch->getStartLoc(); - SourceLoc endLoc = Switch->getEndLoc(); + SourceLoc insertLoc; + if (unknownCase) + insertLoc = unknownCase->getStartLoc(); + else + insertLoc = Switch->getEndLoc(); StringRef placeholder = getCodePlaceholder(); llvm::SmallString<128> buffer; llvm::raw_svector_ostream OS(buffer); @@ -1192,21 +1349,49 @@ namespace { bool InEditor = TC.Context.LangOpts.DiagnosticsEditorMode; // Decide whether we want an error or a warning. - // FIXME: We should be able to emit more specific errors based on what's - // missing, and that /also/ needs to be either an error or a warning. auto mainDiagType = diag::non_exhaustive_switch; + if (unknownCase) { + switch (defaultReason) { + case RequiresDefault::EmptySwitchBody: + llvm_unreachable("there's an @unknown case; the body can't be empty"); + case RequiresDefault::No: + if (!uncovered.isEmpty()) + mainDiagType = diag::non_exhaustive_switch_warn; + break; + case RequiresDefault::UncoveredSwitch: + case RequiresDefault::SpaceTooLarge: { + auto diagnostic = defaultReason == RequiresDefault::UncoveredSwitch + ? diag::non_exhaustive_switch + : diag::possibly_non_exhaustive_switch; + TC.diagnose(startLoc, diagnostic); + TC.diagnose(unknownCase->getLoc(), + diag::non_exhaustive_switch_drop_unknown) + .fixItRemoveChars(unknownCase->getStartLoc(), + unknownCase->getLoc()); + return; + } + } + } + switch (uncovered.checkDowngradeToWarning()) { case DowngradeToWarning::No: break; case DowngradeToWarning::ForSwift3Case: // If someone's used one of the cases introduced in the Swift 4 // timeframe, force them to handle all of them. - if (sawDowngradablePattern) - break; - LLVM_FALLTHROUGH; - case DowngradeToWarning::ForSwift4Exhaustive: - if (!TC.getLangOpts().isSwiftVersionAtLeast(5)) - mainDiagType = diag::non_exhaustive_switch_warn_swift3; + if (!sawDowngradablePattern) + mainDiagType = diag::non_exhaustive_switch_warn; + break; + case DowngradeToWarning::ForUnknownCase: + if (TC.Context.LangOpts.DebuggerSupport || + TC.Context.LangOpts.Playground || + !TC.getLangOpts().EnableNonFrozenEnumExhaustivityDiagnostics) { + // Don't require covering unknown cases in the debugger or in + // playgrounds. + return; + } + // Missing '@unknown' is just a warning. + mainDiagType = diag::non_exhaustive_switch_warn; break; } @@ -1216,21 +1401,21 @@ namespace { case RequiresDefault::EmptySwitchBody: { OS << tok::kw_default << ":\n" << placeholder << "\n"; TC.diagnose(startLoc, diag::empty_switch_stmt) - .fixItInsert(endLoc, buffer.str()); + .fixItInsert(insertLoc, buffer.str()); } return; case RequiresDefault::UncoveredSwitch: { OS << tok::kw_default << ":\n" << placeholder << "\n"; TC.diagnose(startLoc, mainDiagType); TC.diagnose(startLoc, diag::missing_several_cases, /*default*/true) - .fixItInsert(endLoc, buffer.str()); + .fixItInsert(insertLoc, buffer.str()); } return; case RequiresDefault::SpaceTooLarge: { OS << tok::kw_default << ":\n" << "<#fatalError()#>" << "\n"; - TC.diagnose(startLoc, diag::non_exhaustive_switch); + TC.diagnose(startLoc, diag::possibly_non_exhaustive_switch); TC.diagnose(startLoc, diag::missing_several_cases, /*default*/true) - .fixItInsert(endLoc, buffer.str()); + .fixItInsert(insertLoc, buffer.str()); } return; } @@ -1238,6 +1423,63 @@ namespace { // If there's nothing else to diagnose, bail. if (uncovered.isEmpty()) return; + TC.diagnose(startLoc, mainDiagType); + + // Add notes to explain what's missing. + auto processUncoveredSpaces = + [&](llvm::function_ref process) { + + // Flatten away all disjunctions. + SmallVector flats; + flatten(uncovered, flats); + + // Then figure out which of the remaining spaces are interesting. + // To do this, we sort by size, largest spaces first... + SmallVector flatsSortedBySize; + flatsSortedBySize.reserve(flats.size()); + for (const Space &space : flats) + flatsSortedBySize.push_back(&space); + std::stable_sort(flatsSortedBySize.begin(), flatsSortedBySize.end(), + [&](const Space *left, const Space *right) { + return left->getSize(TC, DC) > right->getSize(TC, DC); + }); + + // ...and then remove any of the later spaces that are contained + // entirely in an earlier one. + SmallPtrSet flatsToEmit; + for (const Space *space : flatsSortedBySize) { + bool alreadyHandled = + llvm::any_of(flatsToEmit, [&](const Space *previousSpace) { + return space->isSubspace(*previousSpace, TC, DC); + }); + if (alreadyHandled) + continue; + flatsToEmit.insert(space); + } + + // Finally we can iterate over the flat spaces in their original order, + // but only emit the interesting ones. + for (const Space &flat : flats) { + if (!flatsToEmit.count(&flat)) + continue; + + if (flat.getKind() == SpaceKind::UnknownCase) { + assert(&flat == &flats.back() && "unknown case must be last"); + if (unknownCase) { + // This can occur if the /only/ case in the switch is 'unknown'. + // In that case we won't do any analysis on the input space, but + // will later decompose the space into cases. + continue; + } + if (!TC.getLangOpts().EnableNonFrozenEnumExhaustivityDiagnostics) + continue; + } + + process(flat, flats.size() == 1); + } + }; + // If editing is enabled, emit a formatted error of the form: // // switch must be exhaustive, do you want to add missing cases? @@ -1254,45 +1496,45 @@ namespace { // missing case '(.some(_), .none)' if (InEditor) { buffer.clear(); - SmallVector emittedSpaces; - for (auto &uncoveredSpace : uncovered.getSpaces()) { - SmallVector flats; - flatten(uncoveredSpace, flats); - for (auto &flat : flats) { - if (flat.isSubspace(Space(emittedSpaces), TC, DC)) { - continue; + + bool alreadyEmittedSomething = false; + processUncoveredSpaces([&](const Space &space, + bool onlyOneUncoveredSpace) { + if (space.getKind() == SpaceKind::UnknownCase) { + OS << "@unknown " << tok::kw_default; + if (onlyOneUncoveredSpace) { + OS << ":\n<#fatalError()#>\n"; + TC.diagnose(startLoc, diag::missing_unknown_case) + .fixItInsert(insertLoc, buffer.str()); + alreadyEmittedSomething = true; + return; } - + } else { OS << tok::kw_case << " "; - flat.show(OS); - OS << ":\n" << placeholder << "\n"; - - emittedSpaces.push_back(flat); + space.show(OS); } + OS << ":\n" << placeholder << "\n"; + }); + + if (!alreadyEmittedSomething) { + TC.diagnose(startLoc, diag::missing_several_cases, false) + .fixItInsert(insertLoc, buffer.str()); } - TC.diagnose(startLoc, mainDiagType); - TC.diagnose(startLoc, diag::missing_several_cases, false) - .fixItInsert(endLoc, buffer.str()); } else { - TC.Context.Diags.diagnose(startLoc, mainDiagType); - - SmallVector emittedSpaces; - for (auto &uncoveredSpace : uncovered.getSpaces()) { - SmallVector flats; - flatten(uncoveredSpace, flats); - for (auto &flat : flats) { - if (flat.isSubspace(Space(emittedSpaces), TC, DC)) { - continue; - } - - buffer.clear(); - flat.show(OS); - TC.diagnose(startLoc, diag::missing_particular_case, buffer.str()); - - emittedSpaces.push_back(flat); + processUncoveredSpaces([&](const Space &space, + bool onlyOneUncoveredSpace) { + if (space.getKind() == SpaceKind::UnknownCase) { + auto note = TC.diagnose(startLoc, diag::missing_unknown_case); + if (onlyOneUncoveredSpace) + note.fixItInsert(insertLoc, "@unknown default:\n<#fatalError#>()\n"); + return; } - } + + buffer.clear(); + space.show(OS); + TC.diagnose(startLoc, diag::missing_particular_case, buffer.str()); + }); } } @@ -1375,8 +1617,10 @@ namespace { // Wrap the matrix rows into this constructor. for (auto &row : matrix) { - flats.push_back(Space(space.getType(), space.getHead(), - space.canDowngradeToWarning(), row)); + flats.push_back(Space::forConstructor(space.getType(), + space.getHead(), + space.canDowngradeToWarning(), + row)); } } break; @@ -1404,26 +1648,36 @@ namespace { bool &sawDowngradablePattern) { switch (item->getKind()) { case PatternKind::Any: - return Space(item->getType(), Identifier(), /*canDowngrade*/false); + return Space::forType(item->getType(), Identifier()); case PatternKind::Named: - return Space(item->getType(), cast(item)->getBoundName(), - /*canDowngrade*/false); - case PatternKind::Bool: { - return Space(cast(item)->getValue()); - } + return Space::forType(item->getType(), + cast(item)->getBoundName()); + case PatternKind::Bool: + return Space::forBool(cast(item)->getValue()); case PatternKind::Is: { auto *IP = cast(item); switch (IP->getCastKind()) { case CheckedCastKind::Coercion: case CheckedCastKind::BridgingCoercion: { - auto *subPattern = IP->getSubPattern(); - if (subPattern) - return projectPattern(TC, subPattern, sawDowngradablePattern); + if (auto *subPattern = IP->getSubPattern()) { + // Project the cast target's subpattern. + Space castSubSpace = projectPattern(TC, subPattern, + sawDowngradablePattern); + // If we recieved a type space from a named pattern or a wildcard + // we have to re-project with the cast's target type to maintain + // consistency with the scrutinee's type. + if (castSubSpace.getKind() == SpaceKind::Type) { + + return Space::forType(IP->getType(), + castSubSpace.getPrintingName()); + } + return castSubSpace; + } - // With no subpattern coercions are irrefutable. Project with the original - // type instead of the cast's target type to maintain consistency with the - // scrutinee's type. - return Space(IP->getType(), Identifier(), /*canDowngrade*/false); + // With no subpattern coercions are irrefutable. Project with the + // original type instead of the cast's target type to maintain + // consistency with the scrutinee's type. + return Space::forType(IP->getType(), Identifier()); } case CheckedCastKind::Unresolved: case CheckedCastKind::ValueCast: @@ -1447,20 +1701,20 @@ namespace { } case PatternKind::OptionalSome: { auto *OSP = cast(item); + Identifier name = TC.Context.getOptionalSomeDecl()->getName(); + auto subSpace = projectPattern(TC, OSP->getSubPattern(), sawDowngradablePattern); // To match patterns like (_, _, ...)?, we must rewrite the underlying // tuple pattern to .some(_, _, ...) first. if (subSpace.getKind() == SpaceKind::Constructor && subSpace.getHead().empty()) { - return Space(item->getType(), TC.Context.getIdentifier("some"), - /*canDowngrade*/false, subSpace.getSpaces()); + return Space::forConstructor(item->getType(), name, + /*canDowngrade*/false, + std::move(subSpace.getSpaces())); } - SmallVector payload = { - projectPattern(TC, OSP->getSubPattern(), sawDowngradablePattern) - }; - return Space(item->getType(), TC.Context.getIdentifier("some"), - /*canDowngrade*/false, payload); + return Space::forConstructor(item->getType(), name, + /*canDowngrade*/false, subSpace); } case PatternKind::EnumElement: { auto *VP = cast(item); @@ -1472,15 +1726,15 @@ namespace { } } - SmallVector conArgSpace; auto *SP = VP->getSubPattern(); if (!SP) { // If there's no sub-pattern then there's no further recursive // structure here. Yield the constructor space. - return Space(item->getType(), VP->getName(), /*canDowngrade*/false, - conArgSpace); + return Space::forConstructor(item->getType(), VP->getName(), + /*canDowngrade*/false, None); } + SmallVector conArgSpace; switch (SP->getKind()) { case PatternKind::Tuple: { auto *TP = dyn_cast(SP); @@ -1490,8 +1744,8 @@ namespace { return projectPattern(TC, pate.getPattern(), sawDowngradablePattern); }); - return Space(item->getType(), VP->getName(), /*canDowngrade*/false, - conArgSpace); + return Space::forConstructor(item->getType(), VP->getName(), + /*canDowngrade*/false, conArgSpace); } case PatternKind::Paren: { auto *PP = dyn_cast(SP); @@ -1503,23 +1757,29 @@ namespace { // // FIXME: SE-0155 makes this case unreachable. if (SP->getKind() == PatternKind::Named - || SP->getKind() == PatternKind::Any - || SP->getKind() == PatternKind::Tuple) { + || SP->getKind() == PatternKind::Any) { if (auto *TTy = SP->getType()->getAs()) { for (auto ty : TTy->getElements()) { - conArgSpace.push_back(Space(ty.getType(), ty.getName(), - /*canDowngrade*/false)); + conArgSpace.push_back(Space::forType(ty.getType(), + ty.getName())); } } else { conArgSpace.push_back(projectPattern(TC, SP, sawDowngradablePattern)); } + } else if (SP->getKind() == PatternKind::Tuple) { + Space argTupleSpace = projectPattern(TC, SP, + sawDowngradablePattern); + assert(argTupleSpace.getKind() == SpaceKind::Constructor); + conArgSpace.insert(conArgSpace.end(), + argTupleSpace.getSpaces().begin(), + argTupleSpace.getSpaces().end()); } else { conArgSpace.push_back(projectPattern(TC, SP, sawDowngradablePattern)); } - return Space(item->getType(), VP->getName(), /*canDowngrade*/false, - conArgSpace); + return Space::forConstructor(item->getType(), VP->getName(), + /*canDowngrade*/false, conArgSpace); } default: return projectPattern(TC, SP, sawDowngradablePattern); @@ -1533,8 +1793,8 @@ namespace { [&](TuplePatternElt pate) { return projectPattern(TC, pate.getPattern(), sawDowngradablePattern); }); - return Space(item->getType(), Identifier(), /*canDowngrade*/false, - conArgSpace); + return Space::forConstructor(item->getType(), Identifier(), + /*canDowngrade*/false, conArgSpace); } } } @@ -1548,8 +1808,6 @@ void TypeChecker::checkSwitchExhaustiveness(const SwitchStmt *stmt, } void SpaceEngine::Space::dump() const { - SmallString<128> buf; - llvm::raw_svector_ostream os(buf); - this->show(os, /*normalize*/false); - llvm::errs() << buf.str(); + this->show(llvm::errs(), /*normalize*/ false); + llvm::errs() << '\n'; } diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index 4587cb492d07a..3b23afd9fdce4 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -144,6 +144,31 @@ Type TypeChecker::getUInt8Type(DeclContext *dc) { return ::getStdlibType(*this, UInt8Type, dc, "UInt8"); } +/// Returns the maximum-sized builtin integer type. +Type TypeChecker::getMaxIntegerType(DeclContext *dc) { + if (!MaxIntegerType.isNull()) + return MaxIntegerType; + + SmallVector lookupResults; + getStdlibModule(dc)->lookupValue(/*AccessPath=*/{}, + Context.Id_MaxBuiltinIntegerType, + NLKind::QualifiedLookup, lookupResults); + if (lookupResults.size() != 1) + return MaxIntegerType; + + auto *maxIntegerTypeDecl = dyn_cast(lookupResults.front()); + if (!maxIntegerTypeDecl) + return MaxIntegerType; + + validateDecl(maxIntegerTypeDecl); + if (!maxIntegerTypeDecl->hasInterfaceType() || + !maxIntegerTypeDecl->getDeclaredInterfaceType()->is()) + return MaxIntegerType; + + MaxIntegerType = maxIntegerTypeDecl->getUnderlyingTypeLoc().getType(); + return MaxIntegerType; +} + /// Find the standard type of exceptions. /// /// We call this the "exception type" to try to avoid confusion with @@ -231,6 +256,11 @@ Type TypeChecker::resolveTypeInContext( TypeResolutionOptions options, bool isSpecialized, GenericTypeResolver *resolver) { + // If we're just resolving the structure, the decl itself is all we need to + // know: return the unbound generic type. + if (options.contains(TypeResolutionFlags::ResolveStructure)) + return typeDecl->getDeclaredInterfaceType(); + GenericTypeToArchetypeResolver defaultResolver(fromDC); if (!resolver) resolver = &defaultResolver; @@ -334,6 +364,18 @@ Type TypeChecker::resolveTypeInContext( selfType); } +static TypeResolutionOptions +adjustOptionsForGenericArgs(TypeResolutionOptions options) { + options -= TypeResolutionFlags::SILType; + options -= TypeResolutionFlags::ImmediateFunctionInput; + options -= TypeResolutionFlags::FunctionInput; + options -= TypeResolutionFlags::TypeAliasUnderlyingType; + options -= TypeResolutionFlags::AllowUnavailableProtocol; + options -= TypeResolutionFlags::AllowIUO; + + return options; +} + /// This function checks if a bound generic type is UnsafePointer or /// UnsafeMutablePointer. For these two type representations, we should /// warn users that they are deprecated and replace them with more handy @@ -358,6 +400,8 @@ Type TypeChecker::applyGenericArguments(Type type, TypeDecl *decl, TypeResolutionOptions options, GenericTypeResolver *resolver, UnsatisfiedDependency *unsatisfiedDependency) { + assert(!options.contains(TypeResolutionFlags::ResolveStructure) && + "should not touch generic arguments when resolving structure"); if (type->hasError()) { generic->setInvalid(); @@ -416,8 +460,8 @@ Type TypeChecker::applyGenericArguments(Type type, TypeDecl *decl, genericParams->size(), genericArgs.size(), genericArgs.size() < genericParams->size()) .highlight(generic->getAngleBrackets()); - diagnose(decl, diag::generic_type_declared_here, - decl->getName()); + diagnose(decl, diag::kind_identifier_declared_here, + DescriptiveDeclKind::GenericType, decl->getName()); return ErrorType::get(Context); } @@ -451,19 +495,40 @@ Type TypeChecker::applyGenericArguments(Type type, TypeDecl *decl, if (!genericDecl->hasValidSignature()) { diagnose(loc, diag::recursive_type_reference, genericDecl->getDescriptiveKind(), genericDecl->getName()); - diagnose(genericDecl, diag::type_declared_here); + diagnose(genericDecl, diag::kind_declared_here, + DescriptiveDeclKind::Type); return ErrorType::get(Context); } - SmallVector args; - for (auto tyR : genericArgs) - args.push_back(tyR); + // Resolve the types of the generic arguments. + assert(!options.contains(TypeResolutionFlags::ResolveStructure) && + "should not touch generic arguments when resolving structure"); + options = adjustOptionsForGenericArgs(options); + + SmallVector args; + for (auto tyR : genericArgs) { + // Propagate failure. + TypeLoc genericArg = tyR; + if (validateType(genericArg, dc, options, resolver, + unsatisfiedDependency)) + return ErrorType::get(Context); + + auto substTy = genericArg.getType(); + + // Unsatisfied dependency case. + if (!substTy) + return nullptr; + + args.push_back(substTy); + } auto result = applyUnboundGenericArguments(unboundType, genericDecl, loc, - dc, args, options, - resolver, unsatisfiedDependency); + dc, args, resolver, + unsatisfiedDependency); if (!result) return result; + + // Migration hack. bool isMutablePointer; if (isPointerToVoid(dc->getASTContext(), result, isMutablePointer)) { if (isMutablePointer) @@ -480,18 +545,9 @@ Type TypeChecker::applyGenericArguments(Type type, TypeDecl *decl, Type TypeChecker::applyUnboundGenericArguments( UnboundGenericType *unboundType, GenericTypeDecl *decl, SourceLoc loc, DeclContext *dc, - MutableArrayRef genericArgs, - TypeResolutionOptions options, + ArrayRef genericArgs, GenericTypeResolver *resolver, UnsatisfiedDependency *unsatisfiedDependency) { - - options -= TypeResolutionFlags::SILType; - options -= TypeResolutionFlags::ImmediateFunctionInput; - options -= TypeResolutionFlags::FunctionInput; - options -= TypeResolutionFlags::TypeAliasUnderlyingType; - options -= TypeResolutionFlags::AllowUnavailableProtocol; - options -= TypeResolutionFlags::AllowIUO; - assert(genericArgs.size() == decl->getGenericParams()->size() && "invalid arguments, use applyGenericArguments for diagnostic emitting"); @@ -519,23 +575,7 @@ Type TypeChecker::applyUnboundGenericArguments( // If we're working with a nominal type declaration, just construct // a bound generic type without checking the generic arguments. if (auto *nominalDecl = dyn_cast(decl)) { - SmallVector args; - for (auto &genericArg : genericArgs) { - // Propagate failure. - if (validateType(genericArg, dc, options, resolver, - unsatisfiedDependency)) - return ErrorType::get(Context); - - auto substTy = genericArg.getType(); - - // Unsatisfied dependency case. - if (!substTy) - return nullptr; - - args.push_back(substTy); - } - - return BoundGenericType::get(nominalDecl, parentType, args); + return BoundGenericType::get(nominalDecl, parentType, genericArgs); } assert(!resultType->hasTypeParameter()); @@ -555,18 +595,8 @@ Type TypeChecker::applyUnboundGenericArguments( // Realize the types of the generic arguments and add them to the // substitution map. for (unsigned i = 0, e = genericArgs.size(); i < e; i++) { - auto &genericArg = genericArgs[i]; - - // Propagate failure. - if (validateType(genericArg, dc, options, resolver, unsatisfiedDependency)) - return ErrorType::get(Context); - auto origTy = genericSig->getInnermostGenericParams()[i]; - auto substTy = genericArg.getType(); - - // Unsatisfied dependency case. - if (!substTy) - return nullptr; + auto substTy = genericArgs[i]; // Enter a substitution. subs[origTy->getCanonicalType()->castTo()] = @@ -642,8 +672,8 @@ static void diagnoseUnboundGenericType(TypeChecker &tc, Type ty,SourceLoc loc) { diag.fixItInsertAfter(loc, genericArgsToAdd); } } - tc.diagnose(unbound->getDecl(), diag::generic_type_declared_here, - unbound->getDecl()->getName()); + tc.diagnose(unbound->getDecl(), diag::kind_identifier_declared_here, + DescriptiveDeclKind::GenericType, unbound->getDecl()->getName()); } // Produce a diagnostic if the type we referenced was an @@ -701,7 +731,8 @@ static Type resolveTypeDecl(TypeChecker &TC, TypeDecl *typeDecl, SourceLoc loc, if (!typeDecl->hasInterfaceType()) { TC.diagnose(loc, diag::recursive_type_reference, typeDecl->getDescriptiveKind(), typeDecl->getName()); - TC.diagnose(typeDecl->getLoc(), diag::type_declared_here); + TC.diagnose(typeDecl->getLoc(), diag::kind_declared_here, + DescriptiveDeclKind::Type); return ErrorType::get(TC.Context); } } @@ -842,7 +873,8 @@ static Type diagnoseUnknownType(TypeChecker &tc, DeclContext *dc, // FIXME: If any of the candidates (usually just one) are in the same // module we could offer a fix-it. for (auto lookupResult : inaccessibleResults) - tc.diagnose(lookupResult.getValueDecl(), diag::type_declared_here); + tc.diagnose(lookupResult.getValueDecl(), diag::kind_declared_here, + DescriptiveDeclKind::Type); // Don't try to recover here; we'll get more access-related diagnostics // downstream if we do. @@ -908,7 +940,8 @@ static Type diagnoseUnknownType(TypeChecker &tc, DeclContext *dc, // FIXME: If any of the candidates (usually just one) are in the same module // we could offer a fix-it. for (auto lookupResult : inaccessibleMembers) - tc.diagnose(lookupResult.first, diag::type_declared_here); + tc.diagnose(lookupResult.first, diag::kind_declared_here, + DescriptiveDeclKind::Type); // Don't try to recover here; we'll get more access-related diagnostics // downstream if we do. @@ -1267,10 +1300,12 @@ static Type resolveNestedIdentTypeComponent( if (!memberType || memberType->hasError()) return memberType; // If there are generic arguments, apply them now. - if (auto genComp = dyn_cast(comp)) { - return TC.applyGenericArguments( - memberType, member, comp->getIdLoc(), DC, genComp, - options, resolver, unsatisfiedDependency); + if (!options.contains(TypeResolutionFlags::ResolveStructure)) { + if (auto genComp = dyn_cast(comp)) { + return TC.applyGenericArguments(memberType, member, comp->getIdLoc(), + DC, genComp, options, resolver, + unsatisfiedDependency); + } } if (memberType->is() && @@ -1290,10 +1325,9 @@ static Type resolveNestedIdentTypeComponent( // If the parent is a type parameter, the member is a dependent member, // and we skip much of the work below. if (parentTy->isTypeParameter()) { - auto memberType = resolver->resolveDependentMemberType(parentTy, DC, - parentRange, comp); - assert(memberType && "Received null dependent member type"); - return memberType; + if (auto memberType = resolver->resolveDependentMemberType( + parentTy, DC, parentRange, comp)) + return memberType; } // Phase 2: If a declaration has already been bound, use it. @@ -1458,7 +1492,7 @@ static Type applyNonEscapingFromContext(DeclContext *DC, // which would wrap the NameAliasType or ParenType, and apply the // isNoEscape bit when de-sugaring. // - return FunctionType::get(funcTy->getInput(), funcTy->getResult(), extInfo); + return FunctionType::get(funcTy->getParams(), funcTy->getResult(), extInfo); } // Note: original sugared type @@ -1524,7 +1558,9 @@ bool TypeChecker::validateType(TypeLoc &Loc, DeclContext *DC, GenericTypeResolver *resolver, UnsatisfiedDependency *unsatisfiedDependency) { // FIXME: Verify that these aren't circular and infinite size. - + assert(!options.contains(TypeResolutionFlags::ResolveStructure) && + "ResolveStructure does not do full validation"); + // If we've already validated this type, don't do so again. if (Loc.wasValidated()) return Loc.isError(); @@ -1532,9 +1568,10 @@ bool TypeChecker::validateType(TypeLoc &Loc, DeclContext *DC, if (Context.Stats) Context.Stats->getFrontendCounters().NumTypesValidated++; - if (Loc.getType().isNull()) { - auto type = resolveType(Loc.getTypeRepr(), DC, options, resolver, - unsatisfiedDependency); + Type type = Loc.getType(); + if (type.isNull()) { + type = resolveType(Loc.getTypeRepr(), DC, options, resolver, + unsatisfiedDependency); if (!type) { // If a dependency went unsatisfied, just return false. if (unsatisfiedDependency) return false; @@ -1548,13 +1585,10 @@ bool TypeChecker::validateType(TypeLoc &Loc, DeclContext *DC, Loc.setType(ErrorType::get(Context), true); return true; } - - Loc.setType(type, true); - return Loc.isError(); } - Loc.setType(Loc.getType(), true); - return Loc.isError(); + Loc.setType(type, true); + return type->hasError(); } namespace { @@ -2268,8 +2302,8 @@ Type TypeResolver::resolveSILBoxType(SILBoxTypeRepr *repr, } for (auto &fieldRepr : repr->getFields()) { - auto fieldTy = resolveType(fieldRepr.FieldType, options); - fields.push_back({fieldTy->getCanonicalType(), fieldRepr.Mutable}); + auto fieldTy = resolveType(fieldRepr.getFieldType(), options); + fields.push_back({fieldTy->getCanonicalType(), fieldRepr.isMutable()}); } } @@ -2286,13 +2320,13 @@ Type TypeResolver::resolveSILBoxType(SILBoxTypeRepr *repr, // Resolve the generic arguments. // Start by building a TypeSubstitutionMap. - SmallVector genericArgs; + SubstitutionMap subMap; if (genericSig) { TypeSubstitutionMap genericArgMap; auto params = genericSig->getGenericParams(); if (repr->getGenericArguments().size() - != genericSig->getSubstitutionListSize()) { + != genericSig->getGenericParams().size()) { TC.diagnose(repr->getLoc(), diag::sil_box_arg_mismatch); return ErrorType::get(Context); } @@ -2303,28 +2337,27 @@ Type TypeResolver::resolveSILBoxType(SILBoxTypeRepr *repr, } bool ok = true; - auto subMap = genericSig->getSubstitutionMap( + subMap = genericSig->getSubstitutionMap( QueryTypeSubstitutionMap{genericArgMap}, - [&](CanType depTy, Type replacement, ProtocolType *proto) + [&](CanType depTy, Type replacement, ProtocolDecl *proto) -> ProtocolConformanceRef { - auto result = TC.conformsToProtocol(replacement, proto->getDecl(), DC, + auto result = TC.conformsToProtocol(replacement, proto, DC, ConformanceCheckOptions()); // TODO: getSubstitutions callback ought to return Optional. if (!result) { ok = false; - return ProtocolConformanceRef(proto->getDecl()); + return ProtocolConformanceRef(proto); } return *result; }); - genericSig->getSubstitutions(subMap, genericArgs); if (!ok) return ErrorType::get(Context); } auto layout = SILLayout::get(Context, genericSig, fields); - return SILBoxType::get(Context, layout, genericArgs); + return SILBoxType::get(Context, layout, subMap); } Type TypeResolver::resolveSILFunctionType(FunctionTypeRepr *repr, @@ -2715,14 +2748,18 @@ Type TypeResolver::resolveArrayType(ArrayTypeRepr *repr, if (!sliceTy) return ErrorType::get(Context); - // Check for _ObjectiveCBridgeable conformances in the element type. - TC.useObjectiveCBridgeableConformances(DC, baseTy); + if (!options.contains(TypeResolutionFlags::ResolveStructure)) { + // Check for _ObjectiveCBridgeable conformances in the element type. + TC.useObjectiveCBridgeableConformances(DC, baseTy); + } return sliceTy; } Type TypeResolver::resolveDictionaryType(DictionaryTypeRepr *repr, TypeResolutionOptions options) { + options = adjustOptionsForGenericArgs(options); + // FIXME: diagnose non-materializability of key/value type? Type keyTy = resolveType(repr->getKey(), withoutContext(options)); if (!keyTy || keyTy->hasError()) return keyTy; @@ -2736,20 +2773,21 @@ Type TypeResolver::resolveDictionaryType(DictionaryTypeRepr *repr, valueTy)) { // Check the requirements on the generic arguments. auto unboundTy = dictDecl->getDeclaredType()->castTo(); - TypeLoc args[2] = { TypeLoc(repr->getKey()), TypeLoc(repr->getValue()) }; - args[0].setType(keyTy, true); - args[1].setType(valueTy, true); - if (!TC.applyUnboundGenericArguments( - unboundTy, dictDecl, repr->getStartLoc(), DC, args, - options, Resolver, UnsatisfiedDependency)) { - return nullptr; - } + if (!options.contains(TypeResolutionFlags::ResolveStructure)) { + Type args[] = {keyTy, valueTy}; + + if (!TC.applyUnboundGenericArguments( + unboundTy, dictDecl, repr->getStartLoc(), DC, args, + Resolver, UnsatisfiedDependency)) { + return nullptr; + } - // Check for _ObjectiveCBridgeable conformances in the key and value - // types. - TC.useObjectiveCBridgeableConformances(DC, keyTy); - TC.useObjectiveCBridgeableConformances(DC, valueTy); + // Check for _ObjectiveCBridgeable conformances in the key and value + // types. + TC.useObjectiveCBridgeableConformances(DC, keyTy); + TC.useObjectiveCBridgeableConformances(DC, valueTy); + } return dictTy; } @@ -3385,24 +3423,18 @@ static bool checkObjCInExtensionContext(TypeChecker &tc, return true; } - // Check if any classes in the inheritance hierarchy have generic + // Check if any Swift classes in the inheritance hierarchy have generic // parameters. // FIXME: This is a current limitation, not inherent. We don't have // a concrete class to attach Objective-C category metadata to. - Type extendedTy = ED->getDeclaredInterfaceType(); - while (!extendedTy.isNull()) { - const ClassDecl *CD = extendedTy->getClassOrBoundGenericClass(); - if (!CD) - break; - - if (!CD->hasClangNode() && CD->getGenericParams()) { + if (auto generic = ED->getDeclaredInterfaceType() + ->getGenericAncestor()) { + if (!generic->getClassOrBoundGenericClass()->hasClangNode()) { if (diagnose) { tc.diagnose(value, diag::objc_in_generic_extension); } return true; } - - extendedTy = CD->getSuperclass(); } } diff --git a/lib/Sema/TypeChecker.cpp b/lib/Sema/TypeChecker.cpp index 70da39f9f3bc5..d863aa1c0d9d8 100644 --- a/lib/Sema/TypeChecker.cpp +++ b/lib/Sema/TypeChecker.cpp @@ -22,6 +22,7 @@ #include "swift/AST/ASTWalker.h" #include "swift/AST/ASTVisitor.h" #include "swift/AST/Attr.h" +#include "swift/AST/ExistentialLayout.h" #include "swift/AST/Identifier.h" #include "swift/AST/Initializer.h" #include "swift/AST/ModuleLoader.h" @@ -64,18 +65,6 @@ TypeChecker::~TypeChecker() { Context.setLazyResolver(nullptr); } -void TypeChecker::handleExternalDecl(Decl *decl) { - if (auto SD = dyn_cast(decl)) { - addImplicitStructConformances(SD); - } - if (auto CD = dyn_cast(decl)) { - CD->addImplicitDestructor(); - } - if (auto ED = dyn_cast(decl)) { - addImplicitEnumConformances(ED); - } -} - ProtocolDecl *TypeChecker::getProtocol(SourceLoc loc, KnownProtocolKind kind) { auto protocol = Context.getProtocol(kind); if (!protocol && loc.isValid()) { @@ -404,11 +393,59 @@ static void bindExtensionDecl(ExtensionDecl *ED, TypeChecker &TC) { void TypeChecker::bindExtension(ExtensionDecl *ext) { ::bindExtensionDecl(ext, *this); } +void TypeChecker::resolveExtensionForConformanceConstruction( + ExtensionDecl *ext, + SmallVectorImpl &protocols) { + // To be able to know the conformances that an extension declares, we just + // need to know which type it is connected to: + ::bindExtensionDecl(ext, *this); -static void typeCheckFunctionsAndExternalDecls(TypeChecker &TC) { + // and the protocols which it inherits from: + DependentGenericTypeResolver resolver; + TypeResolutionOptions options = TypeResolutionFlags::GenericSignature; + options |= TypeResolutionFlags::InheritanceClause; + options |= TypeResolutionFlags::AllowUnavailableProtocol; + options |= TypeResolutionFlags::ResolveStructure; + for (auto &inherited : ext->getInherited()) { + // We don't want to have know about any generic params/archetypes, because + // that requires knowing a full generic environment for the extension (which + // can recur with conformance construction). Furthermore, we only *need* to + // resolve the protocol references, which won't involve any archetypes: an + // invalid inheritance like `struct Foo {} extension Foo: SomeClass + // {}` isn't relevant for conformance construction and is caught elsewhere. + auto type = inherited.getType(); + if (!type) + type = resolveType(inherited.getTypeRepr(), ext, options, &resolver); + + if (type && type->isExistentialType()) { + auto layout = type->getExistentialLayout(); + for (auto proto : layout.getProtocols()) + protocols.push_back({inherited.getLoc(), proto->getDecl()}); + } + } +} + +static void typeCheckFunctionsAndExternalDecls(SourceFile &SF, TypeChecker &TC) { unsigned currentFunctionIdx = 0; unsigned currentExternalDef = TC.Context.LastCheckedExternalDefinition; + unsigned currentSynthesizedDecl = SF.LastCheckedSynthesizedDecl; do { + // Type check conformance contexts. + for (unsigned i = 0; i != TC.ConformanceContexts.size(); ++i) { + auto decl = TC.ConformanceContexts[i]; + if (auto *ext = dyn_cast(decl)) + TC.checkConformancesInContext(ext, ext); + else { + auto *ntd = cast(decl); + TC.checkConformancesInContext(ntd, ntd); + + // Finally, we can check classes for missing initializers. + if (auto *classDecl = dyn_cast(ntd)) + TC.maybeDiagnoseClassWithoutInitializers(classDecl); + } + } + TC.ConformanceContexts.clear(); + // Type check the body of each of the function in turn. Note that outside // functions must be visited before nested functions for type-checking to // work correctly. @@ -416,37 +453,22 @@ static void typeCheckFunctionsAndExternalDecls(TypeChecker &TC) { ++currentFunctionIdx) { auto *AFD = TC.definedFunctions[currentFunctionIdx]; - // HACK: don't type-check the same function body twice. This is - // supposed to be handled by just not enqueuing things twice, - // but that gets tricky with synthesized function bodies. - if (AFD->isBodyTypeChecked()) continue; - - FrontendStatsTracer StatsTracer(TC.Context.Stats, "typecheck-fn", AFD); - PrettyStackTraceDecl StackEntry("type-checking", AFD); TC.typeCheckAbstractFunctionBody(AFD); - - AFD->setBodyTypeCheckedIfPresent(); } + // Type check external definitions. for (unsigned n = TC.Context.ExternalDefinitions.size(); currentExternalDef != n; ++currentExternalDef) { auto decl = TC.Context.ExternalDefinitions[currentExternalDef]; - - if (auto *AFD = dyn_cast(decl)) { - // HACK: don't type-check the same function body twice. This is - // supposed to be handled by just not enqueuing things twice, - // but that gets tricky with synthesized function bodies. - if (AFD->isBodyTypeChecked()) continue; - PrettyStackTraceDecl StackEntry("type-checking", AFD); + if (auto *AFD = dyn_cast(decl)) { TC.typeCheckAbstractFunctionBody(AFD); + TC.checkFunctionErrorHandling(AFD); continue; } - if (isa(decl)) { - TC.handleExternalDecl(decl); + if (isa(decl)) continue; - } if (isa(decl)) continue; llvm_unreachable("Unhandled external definition kind"); @@ -474,6 +496,14 @@ static void typeCheckFunctionsAndExternalDecls(TypeChecker &TC) { TC.finalizeDecl(decl); } + // Type check synthesized functions and their bodies. + for (unsigned n = SF.SynthesizedDecls.size(); + currentSynthesizedDecl != n; + ++currentSynthesizedDecl) { + auto decl = SF.SynthesizedDecls[currentSynthesizedDecl]; + TC.typeCheckDecl(decl); + } + // Ensure that the requirements of the given conformance are // fully checked. for (unsigned i = 0; i != TC.PartiallyCheckedConformances.size(); ++i) { @@ -492,13 +522,16 @@ static void typeCheckFunctionsAndExternalDecls(TypeChecker &TC) { } while (currentFunctionIdx < TC.definedFunctions.size() || currentExternalDef < TC.Context.ExternalDefinitions.size() || + currentSynthesizedDecl < SF.SynthesizedDecls.size() || !TC.DeclsToFinalize.empty() || + !TC.ConformanceContexts.empty() || !TC.DelayedRequirementSignatures.empty() || !TC.UsedConformances.empty() || !TC.PartiallyCheckedConformances.empty()); // FIXME: Horrible hack. Store this somewhere more appropriate. TC.Context.LastCheckedExternalDefinition = currentExternalDef; + SF.LastCheckedSynthesizedDecl = currentSynthesizedDecl; // Now that all types have been finalized, run any delayed // circularity checks. @@ -529,18 +562,13 @@ static void typeCheckFunctionsAndExternalDecls(TypeChecker &TC) { for (AbstractFunctionDecl *FD : TC.definedFunctions) { TC.checkFunctionErrorHandling(FD); } - for (auto decl : TC.Context.ExternalDefinitions) { - if (auto fn = dyn_cast(decl)) { - TC.checkFunctionErrorHandling(fn); - } - } } void swift::typeCheckExternalDefinitions(SourceFile &SF) { assert(SF.ASTStage == SourceFile::TypeChecked); auto &Ctx = SF.getASTContext(); TypeChecker TC(Ctx); - typeCheckFunctionsAndExternalDecls(TC); + typeCheckFunctionsAndExternalDecls(SF, TC); } void swift::performTypeChecking(SourceFile &SF, TopLevelContext &TLC, @@ -548,13 +576,20 @@ void swift::performTypeChecking(SourceFile &SF, TopLevelContext &TLC, unsigned StartElem, unsigned WarnLongFunctionBodies, unsigned WarnLongExpressionTypeChecking, - unsigned ExpressionTimeoutThreshold) { + unsigned ExpressionTimeoutThreshold, + unsigned SwitchCheckingInvocationThreshold) { if (SF.ASTStage == SourceFile::TypeChecked) return; auto &Ctx = SF.getASTContext(); // Make sure we have a type checker. + // + // FIXME: We should never have a type checker here, but currently do when + // we're using immediate together with -enable-source-import. + // + // This possibility should be eliminated, since it results in duplicated + // work. Optional MyTC; if (!Ctx.getLazyResolver()) MyTC.emplace(Ctx); @@ -574,6 +609,10 @@ void swift::performTypeChecking(SourceFile &SF, TopLevelContext &TLC, if (ExpressionTimeoutThreshold != 0) MyTC->setExpressionTimeoutThreshold(ExpressionTimeoutThreshold); + if (SwitchCheckingInvocationThreshold != 0) + MyTC->setSwitchCheckingInvocationThreshold( + SwitchCheckingInvocationThreshold); + if (Options.contains(TypeCheckingFlags::DebugTimeFunctionBodies)) MyTC->enableDebugTimeFunctionBodies(); @@ -618,22 +657,7 @@ void swift::performTypeChecking(SourceFile &SF, TopLevelContext &TLC, } }); - // FIXME: Check for cycles in class inheritance here? - // Type check the top-level elements of the source file. - for (auto D : llvm::makeArrayRef(SF.Decls).slice(StartElem)) { - if (isa(D)) - continue; - - TC.typeCheckDecl(D, /*isFirstPass*/true); - } - - // At this point, we can perform general name lookup into any type. - - // We don't know the types of all the global declarations in the first - // pass, which means we can't completely analyze everything. Perform the - // second pass now. - bool hasTopLevelCode = false; for (auto D : llvm::makeArrayRef(SF.Decls).slice(StartElem)) { if (auto *TLCD = dyn_cast(D)) { @@ -641,7 +665,7 @@ void swift::performTypeChecking(SourceFile &SF, TopLevelContext &TLC, // Immediately perform global name-binding etc. TC.typeCheckTopLevelCodeDecl(TLCD); } else { - TC.typeCheckDecl(D, /*isFirstPass*/false); + TC.typeCheckDecl(D); } } @@ -655,7 +679,7 @@ void swift::performTypeChecking(SourceFile &SF, TopLevelContext &TLC, if (SF.Kind == SourceFileKind::REPL && !Ctx.hadError()) TC.processREPLTopLevel(SF, TLC, StartElem); - typeCheckFunctionsAndExternalDecls(TC); + typeCheckFunctionsAndExternalDecls(SF, TC); } // Checking that benefits from having the whole module available. @@ -775,7 +799,7 @@ bool swift::typeCheckCompletionDecl(Decl *D) { if (auto ext = dyn_cast(D)) TC.validateExtension(ext); else - TC.typeCheckDecl(D, true); + TC.typeCheckDecl(D); return true; } diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 068ccfe057a5a..aa14f6f1e42cd 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -42,6 +42,7 @@ class NominalTypeDecl; class NormalProtocolConformance; class TopLevelContext; class TypeChecker; +class TypoCorrectionResults; class ExprPattern; namespace constraints { @@ -53,14 +54,14 @@ namespace constraints { /// \brief A mapping from substitutable types to the protocol-conformance /// mappings for those types. -typedef llvm::DenseMap> ConformanceMap; +using ConformanceMap = + llvm::DenseMap>; /// \brief Used for recursive lookups into an expr that is already /// being type-checked and the constraint system in which its type is /// stored. -typedef std::pair ExprAndConstraintSystem; +using ExprAndConstraintSystem = + std::pair; /// Special-case type checking semantics for certain declarations. enum class DeclTypeCheckingSemantics { @@ -85,35 +86,57 @@ class LookupResult { private: /// The set of results found. SmallVector Results; + size_t IndexOfFirstOuterResult = 0; public: LookupResult() {} - explicit - LookupResult(const SmallVectorImpl &Results) - : Results(Results.begin(), Results.end()) {} + explicit LookupResult(const SmallVectorImpl &Results, + size_t indexOfFirstOuterResult) + : Results(Results.begin(), Results.end()), + IndexOfFirstOuterResult(indexOfFirstOuterResult) {} - typedef SmallVectorImpl::iterator iterator; + using iterator = SmallVectorImpl::iterator; iterator begin() { return Results.begin(); } - iterator end() { return Results.end(); } - unsigned size() const { return Results.size(); } - bool empty() const { return Results.empty(); } + iterator end() { + return Results.begin() + IndexOfFirstOuterResult; + } + unsigned size() const { return innerResults().size(); } + bool empty() const { return innerResults().empty(); } + + ArrayRef innerResults() const { + return llvm::makeArrayRef(Results).take_front(IndexOfFirstOuterResult); + } + + ArrayRef outerResults() const { + return llvm::makeArrayRef(Results).drop_front(IndexOfFirstOuterResult); + } const LookupResultEntry& operator[](unsigned index) const { return Results[index]; } - LookupResultEntry front() const { return Results.front(); } - LookupResultEntry back() const { return Results.back(); } + LookupResultEntry front() const { return innerResults().front(); } + LookupResultEntry back() const { return innerResults().back(); } /// Add a result to the set of results. - void add(LookupResultEntry result) { Results.push_back(result); } + void add(LookupResultEntry result, bool isOuter) { + Results.push_back(result); + if (!isOuter) { + IndexOfFirstOuterResult++; + assert(IndexOfFirstOuterResult == Results.size() && + "found an outer result before an inner one"); + } else { + assert(IndexOfFirstOuterResult > 0 && + "found outer results without an inner one"); + } + } void clear() { Results.clear(); } /// Determine whether the result set is nonempty. explicit operator bool() const { - return !Results.empty(); + return !empty(); } TypeDecl *getSingleTypeResult() const { @@ -124,7 +147,8 @@ class LookupResult { } /// Filter out any results that aren't accepted by the given predicate. - void filter(const std::function &pred); + void + filter(llvm::function_ref pred); }; /// The result of name lookup for types. @@ -135,7 +159,7 @@ class LookupTypeResult { friend class TypeChecker; public: - typedef SmallVectorImpl>::iterator iterator; + using iterator = SmallVectorImpl>::iterator; iterator begin() { return Results.begin(); } iterator end() { return Results.end(); } unsigned size() const { return Results.size(); } @@ -232,7 +256,7 @@ enum class TypeCheckExprFlags { SkipApplyingSolution = 0x100, }; -typedef OptionSet TypeCheckExprOptions; +using TypeCheckExprOptions = OptionSet; inline TypeCheckExprOptions operator|(TypeCheckExprFlags flag1, TypeCheckExprFlags flag2) { @@ -259,10 +283,13 @@ enum class NameLookupFlags { /// Whether to ignore access control for this lookup, allowing inaccessible /// results to be returned. IgnoreAccessControl = 0x10, + /// Whether to include results from outside the innermost scope that has a + /// result. + IncludeOuterResults = 0x20, }; /// A set of options that control name lookup. -typedef OptionSet NameLookupOptions; +using NameLookupOptions = OptionSet; inline NameLookupOptions operator|(NameLookupFlags flag1, NameLookupFlags flag2) { @@ -545,7 +572,7 @@ enum class TypeResolutionFlags : unsigned { }; /// Option set describing how type resolution should work. -typedef OptionSet TypeResolutionOptions; +using TypeResolutionOptions = OptionSet; /// Strip the contextual options from the given type resolution options. static inline TypeResolutionOptions @@ -677,7 +704,7 @@ enum class ConformanceCheckFlags { }; /// Options that control protocol conformance checking. -typedef OptionSet ConformanceCheckOptions; +using ConformanceCheckOptions = OptionSet; inline ConformanceCheckOptions operator|(ConformanceCheckFlags lhs, ConformanceCheckFlags rhs) { @@ -711,6 +738,9 @@ class TypeChecker final : public LazyResolver { /// \brief The list of function definitions we've encountered. std::vector definedFunctions; + /// Declarations that need their conformances checked. + llvm::SmallVector ConformanceContexts; + /// The list of protocol conformances that were "used" and will need to be /// completed before type checking is considered complete. llvm::SetVector UsedConformances; @@ -840,6 +870,7 @@ class TypeChecker final : public LazyResolver { private: Type IntLiteralType; + Type MaxIntegerType; Type FloatLiteralType; Type BooleanLiteralType; Type UnicodeScalarType; @@ -892,6 +923,14 @@ class TypeChecker final : public LazyResolver { /// than this many seconds. unsigned ExpressionTimeoutThreshold = 600; + /// If non-zero, abort the switch statement exhaustiveness checker if + /// the Space::minus function is called more than this many times. + /// + /// Why this number? Times out in about a second on a 2017 iMac, Retina 5K, + // 4.2 GHz Intel Core i7. + // (It's arbitrary, but will keep the compiler from taking too much time.) + unsigned SwitchCheckingInvocationThreshold = 200000; + /// If true, the time it takes to type-check each function will be dumped /// to llvm::errs(). bool DebugTimeFunctionBodies = false; @@ -967,10 +1006,26 @@ class TypeChecker final : public LazyResolver { /// the upper bound for the number of seconds we'll let the /// expression type checker run before considering an expression /// "too complex". + /// If zero, do not limit the checking. unsigned getExpressionTimeoutThresholdInSeconds() { return ExpressionTimeoutThreshold; } + /// Get the threshold that determines the upper bound for the number + /// of times we'll let the Space::minus routine run before + /// considering a switch statement "too complex". + /// If zero, do not limit the checking. + unsigned getSwitchCheckingInvocationThreshold() const { + return SwitchCheckingInvocationThreshold; + } + + /// Set the threshold that determines the upper bound for the number + /// of times we'll let the Space::minus routine run before + /// considering a switch statement "too complex". + void setSwitchCheckingInvocationThreshold(unsigned invocationCount) { + SwitchCheckingInvocationThreshold = invocationCount; + } + bool getInImmediateMode() { return InImmediateMode; } @@ -994,6 +1049,7 @@ class TypeChecker final : public LazyResolver { Type getIntType(DeclContext *dc); Type getInt8Type(DeclContext *dc); Type getUInt8Type(DeclContext *dc); + Type getMaxIntegerType(DeclContext *dc); Type getNSObjectType(DeclContext *dc); Type getNSErrorType(DeclContext *dc); Type getObjCSelectorType(DeclContext *dc); @@ -1156,7 +1212,6 @@ class TypeChecker final : public LazyResolver { /// \param loc The source location for diagnostic reporting. /// \param dc The context where the arguments are applied. /// \param genericArgs The list of generic arguments to apply to the type. - /// \param options The type resolution context. /// \param resolver The generic type resolver. /// /// \returns A BoundGenericType bound to the given arguments, or null on @@ -1166,8 +1221,7 @@ class TypeChecker final : public LazyResolver { Type applyUnboundGenericArguments(UnboundGenericType *unboundType, GenericTypeDecl *decl, SourceLoc loc, DeclContext *dc, - MutableArrayRef genericArgs, - TypeResolutionOptions options, + ArrayRef genericArgs, GenericTypeResolver *resolver, UnsatisfiedDependency *unsatisfiedDependency); @@ -1302,7 +1356,7 @@ class TypeChecker final : public LazyResolver { unsigned StartElem); Identifier getNextResponseVariableName(DeclContext *DC); - void typeCheckDecl(Decl *D, bool isFirstPass); + void typeCheckDecl(Decl *D); void checkDeclAttributesEarly(Decl *D); void checkDeclAttributes(Decl *D); @@ -1330,6 +1384,9 @@ class TypeChecker final : public LazyResolver { validateExtension(ext); checkInheritanceClause(ext); } + virtual void resolveExtensionForConformanceConstruction( + ExtensionDecl *ext, + SmallVectorImpl &protocols) override; virtual void resolveImplicitConstructors(NominalTypeDecl *nominal) override { addImplicitConstructors(nominal); @@ -1528,18 +1585,14 @@ class TypeChecker final : public LazyResolver { /// Retrieve the set of inherited protocols for this protocol type. llvm::TinyPtrVector getDirectConformsTo(ProtocolDecl *proto); + /// Diagnose if the class has no designated initializers. + void maybeDiagnoseClassWithoutInitializers(ClassDecl *classDecl); + + /// /// \brief Add any implicitly-defined constructors required for the given /// struct or class. void addImplicitConstructors(NominalTypeDecl *typeDecl); - /// \brief Add the RawOptionSet (todo:, Equatable, and Hashable) methods to an - /// imported NS_OPTIONS struct. - void addImplicitStructConformances(StructDecl *ED); - - /// \brief Add the RawRepresentable, Equatable, and Hashable methods to an - /// enum with a raw type. - void addImplicitEnumConformances(EnumDecl *ED); - /// Synthesize the member with the given name on the target if applicable, /// i.e. if the member is synthesizable and has not yet been added to the /// target. @@ -1563,24 +1616,24 @@ class TypeChecker final : public LazyResolver { Type SelfTy, Type StorageTy, NormalProtocolConformance *BehaviorConformance, - SubstitutionList SelfInterfaceSubs, - SubstitutionList SelfContextSubs); + SubstitutionMap interfaceMap, + SubstitutionMap contextMap); /// Instantiate the parameter implementation for a behavior-backed /// property. void completePropertyBehaviorParameter(VarDecl *VD, FuncDecl *BehaviorParameter, NormalProtocolConformance *BehaviorConformance, - SubstitutionList SelfInterfaceSubs, - SubstitutionList SelfContextSubs); + SubstitutionMap interfaceMap, + SubstitutionMap contextMap); /// Instantiate the accessor implementations for a behavior-backed /// property. void completePropertyBehaviorAccessors(VarDecl *VD, VarDecl *ValueImpl, Type valueTy, - SubstitutionList SelfInterfaceSubs, - SubstitutionList SelfContextSubs); + SubstitutionMap interfaceMap, + SubstitutionMap contextMap); /// Pre-check the expression, validating any types that occur in the /// expression and folding sequence expressions. @@ -1692,7 +1745,7 @@ class TypeChecker final : public LazyResolver { ExprTypeCheckListener *listener = nullptr); void getPossibleTypesOfExpressionWithoutApplying( - Expr *&expr, DeclContext *dc, SmallVectorImpl &types, + Expr *&expr, DeclContext *dc, SmallPtrSetImpl &types, FreeTypeVariableBinding allowFreeTypeVariables = FreeTypeVariableBinding::Disallow, ExprTypeCheckListener *listener = nullptr); @@ -2033,7 +2086,7 @@ class TypeChecker final : public LazyResolver { Optional operator()(CanType dependentType, Type conformingReplacementType, - ProtocolType *conformedProtocol) const; + ProtocolDecl *conformedProtocol) const; }; /// Completely check the given conformance. @@ -2262,12 +2315,6 @@ class TypeChecker final : public LazyResolver { /// we're parsing the standard library. ModuleDecl *getStdlibModule(const DeclContext *dc); - /// \name AST Mutation Listener Implementation - /// @{ - void handleExternalDecl(Decl *decl); - - /// @} - /// \name Lazy resolution. /// /// Routines that perform lazy resolution as required for AST operations. @@ -2295,9 +2342,6 @@ class TypeChecker final : public LazyResolver { void diagnoseInlinableLocalType(const NominalTypeDecl *NTD); - bool diagnoseInlinableDeclRef(SourceLoc loc, const ValueDecl *D, - const DeclContext *DC); - /// Used in diagnostic %selects. enum class FragileFunctionKind : unsigned { Transparent, @@ -2307,11 +2351,20 @@ class TypeChecker final : public LazyResolver { PropertyInitializer }; + bool diagnoseInlinableDeclRef(SourceLoc loc, const ValueDecl *D, + const DeclContext *DC, + FragileFunctionKind Kind, + bool TreatUsableFromInlineAsPublic); + /// Given that \p DC is within a fragile context for some reason, describe /// why. /// + /// The second element of the pair is true if references to @usableFromInline + /// declarations are permitted. + /// /// \see FragileFunctionKind - FragileFunctionKind getFragileFunctionKind(const DeclContext *DC); + std::pair + getFragileFunctionKind(const DeclContext *DC); /// \name Availability checking /// @@ -2482,16 +2535,11 @@ class TypeChecker final : public LazyResolver { void performTypoCorrection(DeclContext *DC, DeclRefKind refKind, Type baseTypeOrNull, - DeclName name, - SourceLoc lookupLoc, NameLookupOptions lookupOptions, - LookupResult &result, + TypoCorrectionResults &corrections, GenericSignatureBuilder *gsb = nullptr, unsigned maxResults = 4); - void noteTypoCorrection(DeclName name, DeclNameLoc nameLoc, - ValueDecl *decl); - /// Check if the given decl has a @_semantics attribute that gives it /// special case type-checking behavior. DeclTypeCheckingSemantics getDeclTypeCheckingSemantics(ValueDecl *decl); @@ -2500,16 +2548,15 @@ class TypeChecker final : public LazyResolver { /// \brief RAII object that cleans up the given expression if not explicitly /// disabled. class CleanupIllFormedExpressionRAII { - ASTContext &Context; Expr **expr; public: - CleanupIllFormedExpressionRAII(ASTContext &Context, Expr *&expr) - : Context(Context), expr(&expr) { } + CleanupIllFormedExpressionRAII(Expr *&expr) + : expr(&expr) { } ~CleanupIllFormedExpressionRAII(); - static void doIt(Expr *expr, ASTContext &Context); + static void doIt(Expr *expr); /// \brief Disable the cleanup of this expression; it doesn't need it. void disable() { diff --git a/lib/Sema/TypoCorrection.h b/lib/Sema/TypoCorrection.h new file mode 100644 index 0000000000000..7253e51a41ad9 --- /dev/null +++ b/lib/Sema/TypoCorrection.h @@ -0,0 +1,103 @@ +//===--- TypoCorrection.h - Typo correction ---------------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines the interface for doing typo correction. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SEMA_TYPOCORRECTION_H +#define SWIFT_SEMA_TYPOCORRECTION_H + +#include "swift/Basic/LLVM.h" +#include "swift/AST/Identifier.h" +#include "swift/AST/DeclNameLoc.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/STLExtras.h" + +namespace swift { + +class LookupResult; +class TypeChecker; + +/// A summary of how to fix a typo. Note that this intentionally doesn't +/// carry a candidate declaration because we should be able to apply a typo +/// correction even if the corrected name resolves to an overload set. +class SyntacticTypoCorrection { +public: + DeclName WrittenName; + DeclNameLoc Loc; + DeclName CorrectedName; + + SyntacticTypoCorrection(DeclName writtenName, DeclNameLoc writtenLoc, + DeclName correctedName) + : WrittenName(writtenName), Loc(writtenLoc), CorrectedName(correctedName) {} + + void addFixits(InFlightDiagnostic &diagnostic) const; +}; + +/// A collection of typo-correction candidates. +class TypoCorrectionResults { +public: + TypeChecker &TC; + DeclName WrittenName; + DeclNameLoc Loc; + bool ClaimedCorrection = false; + + SmallVector Candidates; + + TypoCorrectionResults(TypeChecker &tc, DeclName writtenName, DeclNameLoc loc) + : TC(tc), WrittenName(writtenName), Loc(loc) {} + + /// Try to claim a unique correction from this collection that's simple + /// enough to include "inline" in the primary diagnostic. Note that + /// a single correction might still correspond to multiple candidates. + /// + /// The expected pattern is that CorrectedName will be added to the + /// diagnostic (as in "did you mean <>"), and then addFixits + /// will be called on the still-in-flight diagnostic. + /// + /// If this returns a correction, it flags that that's been done, and + /// the notes subsequently emitted by noteAllCandidates will only make + /// sense in the context of a diagnostic that suggests that the correction + /// has happened. + Optional claimUniqueCorrection(); + + /// Emit a note for every candidate in the set. + void noteAllCandidates() const; + + /// Add all the candidates to a lookup set. + void addAllCandidatesToLookup(LookupResult &lookup) const; + + /// Look for a single candidate in this set that matches the given predicate. + template + ValueDecl *getUniqueCandidateMatching(Fn &&predicate) { + ValueDecl *match = nullptr; + + for (auto candidate : Candidates) { + // Ignore candidates that don't match the predicate. + if (!predicate(candidate)) continue; + + // If we've already got a match, the match is no longer unique. + if (match) return nullptr; + + // Record the still-unique match. + match = candidate; + } + + return match; + } +}; + +} // end namespace swift + +#endif diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index 599b4986f44e4..e6b26d7a7ffed 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -489,9 +489,9 @@ ProtocolConformanceRef ModuleFile::readConformance( case SPECIALIZED_PROTOCOL_CONFORMANCE: { TypeID conformingTypeID; - unsigned numSubstitutions; + SubstitutionMapID substitutionMapID; SpecializedProtocolConformanceLayout::readRecord(scratch, conformingTypeID, - numSubstitutions); + substitutionMapID); ASTContext &ctx = getContext(); Type conformingType = getType(conformingTypeID); @@ -503,13 +503,7 @@ ProtocolConformanceRef ModuleFile::readConformance( "reading specialized conformance for", conformingType); - // Read the substitutions. - SmallVector substitutions; - while (numSubstitutions--) { - auto sub = maybeReadSubstitution(Cursor, genericEnv); - assert(sub.hasValue() && "Missing substitution?"); - substitutions.push_back(*sub); - } + auto subMap = getSubstitutionMap(substitutionMapID); ProtocolConformanceRef genericConformance = readConformance(Cursor, genericEnv); @@ -519,7 +513,7 @@ ProtocolConformanceRef ModuleFile::readConformance( auto conformance = ctx.getSpecializedConformance(conformingType, genericConformance.getConcrete(), - substitutions); + subMap); return ProtocolConformanceRef(conformance); } @@ -625,6 +619,8 @@ NormalProtocolConformance *ModuleFile::readNormalConformance( ASTContext &ctx = getContext(); DeclContext *dc = getDeclContext(contextID); + assert(!isa(dc->getModuleScopeContext()) + && "should not have serialized a conformance from a clang module"); Type conformingType = dc->getDeclaredInterfaceType(); PrettyStackTraceType trace(ctx, "reading conformance for", conformingType); @@ -654,42 +650,6 @@ NormalProtocolConformance *ModuleFile::readNormalConformance( return conformance; } -Optional -ModuleFile::maybeReadSubstitution(llvm::BitstreamCursor &cursor, - GenericEnvironment *genericEnv) { - BCOffsetRAII lastRecordOffset(cursor); - - auto entry = cursor.advance(AF_DontPopBlockAtEnd); - if (entry.Kind != llvm::BitstreamEntry::Record) - return None; - - StringRef blobData; - SmallVector scratch; - unsigned recordID = cursor.readRecord(entry.ID, scratch, &blobData); - if (recordID != decls_block::BOUND_GENERIC_SUBSTITUTION) - return None; - - TypeID replacementID; - unsigned numConformances; - decls_block::BoundGenericSubstitutionLayout::readRecord(scratch, - replacementID, - numConformances); - - auto replacementTy = getType(replacementID); - if (genericEnv) { - replacementTy = genericEnv->mapTypeIntoContext(replacementTy); - } - - SmallVector conformanceBuf; - while (numConformances--) { - conformanceBuf.push_back(readConformance(cursor)); - } - - lastRecordOffset.reset(); - return Substitution{replacementTy, - getContext().AllocateCopy(conformanceBuf)}; -} - GenericParamList *ModuleFile::maybeReadGenericParams(DeclContext *DC, GenericParamList *outerParams) { using namespace decls_block; @@ -1095,7 +1055,77 @@ GenericEnvironment *ModuleFile::getGenericEnvironment( serialization::GenericEnvironmentID ID) { return getGenericSignatureOrEnvironment(ID, /*wantEnvironment=*/true) .get(); -; +} + +SubstitutionMap ModuleFile::getSubstitutionMap( + serialization::SubstitutionMapID id) { + using namespace decls_block; + + // Zero is a sentinel for having an empty substitution map. + if (id == 0) return SubstitutionMap(); + + assert(id <= SubstitutionMaps.size() && "invalid SubstitutionMap ID"); + auto &substitutionsOrOffset = SubstitutionMaps[id-1]; + + // If we've already deserialized this substitution map, return it. + if (substitutionsOrOffset.isComplete()) { + return substitutionsOrOffset.get(); + } + + // Read the substitution map. + BCOffsetRAII restoreOffset(DeclTypeCursor); + DeclTypeCursor.JumpToBit(substitutionsOrOffset); + DeserializingEntityRAII deserializingEntity(*this); + + // Read the substitution map. + auto entry = DeclTypeCursor.advance(AF_DontPopBlockAtEnd); + if (entry.Kind != llvm::BitstreamEntry::Record) { + error(); + return SubstitutionMap(); + } + + StringRef blobData; + SmallVector scratch; + unsigned recordID = DeclTypeCursor.readRecord(entry.ID, scratch, &blobData); + if (recordID != SUBSTITUTION_MAP) { + error(); + return SubstitutionMap(); + } + + GenericSignatureID genericSigID; + uint64_t numConformances; + ArrayRef replacementTypeIDs; + SubstitutionMapLayout::readRecord(scratch, genericSigID, numConformances, + replacementTypeIDs); + + // Generic signature. + auto genericSig = getGenericSignature(genericSigID); + if (!genericSig) { + error(); + return SubstitutionMap(); + } + + // Load the replacement types. + SmallVector replacementTypes; + replacementTypes.reserve(replacementTypeIDs.size()); + for (auto typeID : replacementTypeIDs) { + replacementTypes.push_back(getType(typeID)); + } + + // Read the conformances. + SmallVector conformances; + conformances.reserve(numConformances); + for (unsigned i : range(numConformances)) { + (void)i; + conformances.push_back(readConformance(DeclTypeCursor)); + } + + // Form the substitution map and record it. + auto substitutions = + SubstitutionMap::get(genericSig, ArrayRef(replacementTypes), + ArrayRef(conformances)); + substitutionsOrOffset = substitutions; + return substitutions; } bool ModuleFile::readDefaultWitnessTable(ProtocolDecl *proto) { @@ -1657,8 +1687,11 @@ ModuleFile::resolveCrossReference(ModuleDecl *baseModule, uint32_t pathLen) { } } else if (auto alias = dyn_cast(base)) { paramList = alias->getGenericParams(); - } else if (auto fn = dyn_cast(base)) + } else if (auto fn = dyn_cast(base)) { paramList = fn->getGenericParams(); + } else if (auto subscript = dyn_cast(base)) { + paramList = subscript->getGenericParams(); + } if (!paramList) { return llvm::make_error( @@ -4216,9 +4249,11 @@ Expected ModuleFile::getTypeChecked(TypeID TID) { DeclID typealiasID; TypeID parentTypeID; TypeID underlyingTypeID; + SubstitutionMapID substitutionsID; decls_block::NameAliasTypeLayout::readRecord(scratch, typealiasID, - parentTypeID, - underlyingTypeID); + parentTypeID, + underlyingTypeID, + substitutionsID); auto aliasOrError = getDeclChecked(typealiasID); if (!aliasOrError) return aliasOrError.takeError(); @@ -4246,16 +4281,7 @@ Expected ModuleFile::getTypeChecked(TypeID TID) { Type parentType = getType(parentTypeID); // Read the substitutions. - SubstitutionMap subMap; - if (auto genericSig = alias->getGenericSignature()) { - SmallVector substitutions; - for (unsigned i : range(genericSig->getSubstitutionListSize())) { - (void)i; - substitutions.push_back(*maybeReadSubstitution(DeclTypeCursor)); - } - - subMap = genericSig->getSubstitutionMap(substitutions); - } + SubstitutionMap subMap = getSubstitutionMap(substitutionsID); // Look through compatibility aliases that are now unavailable. if (alias->getAttrs().isUnavailable(ctx) && @@ -4265,7 +4291,7 @@ Expected ModuleFile::getTypeChecked(TypeID TID) { } typeOrOffset = NameAliasType::get(alias, parentType, subMap, - underlyingType); + underlyingType); break; } case decls_block::NOMINAL_TYPE: { @@ -4281,6 +4307,31 @@ Expected ModuleFile::getTypeChecked(TypeID TID) { if (!nominalOrError) return nominalOrError.takeError(); + // Look through compatibility aliases. + if (auto *alias = dyn_cast(nominalOrError.get())) { + // Reminder: TypeBase::getAs will look through sugar. But we don't want to + // do that here, so we do isa<> checks on the TypeBase itself instead of + // using the Type wrapper. + const TypeBase *underlyingTy = nullptr; + while (alias->isCompatibilityAlias()) { + underlyingTy = alias->getUnderlyingTypeLoc().getType().getPointer(); + + // If the underlying type is itself a typealias, it might be another + // compatibility alias, meaning we need to go around the loop again. + auto aliasTy = dyn_cast(underlyingTy); + if (!aliasTy) + break; + alias = aliasTy->getDecl(); + } + + // We only want to use the type we found if it's a simple non-generic + // nominal type. + if (auto simpleNominalTy = dyn_cast_or_null(underlyingTy)) { + nominalOrError = simpleNominalTy->getDecl(); + (void)!nominalOrError; // "Check" the llvm::Expected<> value. + } + } + auto nominal = dyn_cast(nominalOrError.get()); if (!nominal) { XRefTracePath tinyTrace{*nominalOrError.get()->getModuleContext()}; @@ -4675,8 +4726,8 @@ Expected ModuleFile::getTypeChecked(TypeID TID) { case decls_block::SIL_BOX_TYPE: { SILLayoutID layoutID; - - decls_block::SILBoxTypeLayout::readRecord(scratch, layoutID); + SubstitutionMapID subMapID; + decls_block::SILBoxTypeLayout::readRecord(scratch, layoutID, subMapID); // Get the layout. auto getLayout = [&]() -> SILLayout * { @@ -4702,24 +4753,9 @@ Expected ModuleFile::getTypeChecked(TypeID TID) { auto layout = getLayout(); if (!layout) return nullptr; - - SmallVector genericArgs; - if (auto sig = layout->getGenericSignature()) { - for (unsigned i : range(sig->getSubstitutionListSize())) { - (void)i; - auto sub = maybeReadSubstitution(DeclTypeCursor); - if (!sub) { - error(); - return nullptr; - } - - genericArgs.push_back( - Substitution(sub->getReplacement()->getCanonicalType(), - sub->getConformances())); - } - } - typeOrOffset = SILBoxType::get(getContext(), layout, genericArgs); + auto subMap = getSubstitutionMap(subMapID); + typeOrOffset = SILBoxType::get(getContext(), layout, subMap); break; } @@ -5303,28 +5339,16 @@ void ModuleFile::finishNormalConformance(NormalProtocolConformance *conformance, }; // Requirement -> synthetic map. - SmallVector reqToSyntheticSubs; if (auto syntheticSig = getGenericSignature(*rawIDIter++)) { // Create the synthetic environment. syntheticEnv = syntheticSig->createGenericEnvironment(); - - // Requirement -> synthetic substitutions. - if (unsigned numReqSubstitutions = *rawIDIter++) { - while (numReqSubstitutions--) { - auto sub = maybeReadSubstitution(DeclTypeCursor, nullptr); - reqToSyntheticSubs.push_back(*sub); - } - } } + // Requirement -> synthetic substitutions. + SubstitutionMap reqToSyntheticSubs = getSubstitutionMap(*rawIDIter++); + // Witness substitutions. - SmallVector witnessSubstitutions; - if (unsigned numWitnessSubstitutions = *rawIDIter++) { - while (numWitnessSubstitutions--) { - auto sub = maybeReadSubstitution(DeclTypeCursor, syntheticEnv); - witnessSubstitutions.push_back(*sub); - } - } + SubstitutionMap witnessSubstitutions = getSubstitutionMap(*rawIDIter++); // Handle opaque witnesses that couldn't be deserialized. if (isOpaque) { @@ -5332,13 +5356,6 @@ void ModuleFile::finishNormalConformance(NormalProtocolConformance *conformance, continue; } - // Handle simple witnesses. - if (witnessSubstitutions.empty() && !syntheticSig && !syntheticEnv && - reqToSyntheticSubs.empty()) { - trySetWitness(Witness(witness)); - continue; - } - // Set the witness. trySetWitness(Witness(witness, witnessSubstitutions, syntheticEnv, reqToSyntheticSubs)); diff --git a/lib/Serialization/DeserializeSIL.cpp b/lib/Serialization/DeserializeSIL.cpp index fb3cb8e39b2d6..b70f18d45293c 100644 --- a/lib/Serialization/DeserializeSIL.cpp +++ b/lib/Serialization/DeserializeSIL.cpp @@ -387,6 +387,19 @@ SILFunction *SILDeserializer::readSILFunction(DeclID FID, StringRef name, bool declarationOnly, bool errorIfEmptyBody) { + // We can't deserialize function bodies after IRGen lowering passes have + // happened since other definitions in the module will no longer be in + // canonical SIL form. + switch (SILMod.getStage()) { + case SILStage::Raw: + case SILStage::Canonical: + break; + + case SILStage::Lowered: + llvm_unreachable("cannot deserialize into a module that has entered " + "Lowered stage"); + } + if (FID == 0) return nullptr; assert(FID <= Funcs.size() && "invalid SILFunction ID"); @@ -842,16 +855,11 @@ SILDeserializer::readKeyPathComponent(ArrayRef ListOfValues, case KeyPathComponentKindEncoding::External: { auto declID = ListOfValues[nextValue++]; auto decl = cast(MF->getDecl(declID)); - auto numComponentSubstitutions = ListOfValues[nextValue++]; - SmallVector subs; - while (numComponentSubstitutions-- > 0) { - auto sub = MF->maybeReadSubstitution(SILCursor); - subs.push_back(*sub); - } + auto subMap = MF->getSubstitutionMap(ListOfValues[nextValue++]); handleComputedIndices(); - return KeyPathPatternComponent::forExternal(decl, - MF->getContext().AllocateCopy(subs), - indices, indicesEquals, indicesHash, type); + return KeyPathPatternComponent::forExternal(decl, subMap, indices, + indicesEquals, indicesHash, + type); } } } @@ -1094,11 +1102,12 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, assert(RecordKind == SIL_ONE_TYPE_ONE_OPERAND && "Layout should be OneTypeOneOperand."); bool isLifetimeGuaranteed = Attr & 0x01; + bool isEscaped = Attr & 0x02; ResultVal = Builder.createConvertEscapeToNoEscape( Loc, getLocalValue(ValID, getSILType(MF->getType(TyID2), (SILValueCategory)TyCategory2)), - getSILType(MF->getType(TyID), (SILValueCategory)TyCategory), + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory), isEscaped, isLifetimeGuaranteed); break; } @@ -1252,14 +1261,7 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, for (unsigned I = 0, E = ListOfValues.size(); I < E; I++) Args.push_back(getLocalValue(ListOfValues[I], substConventions.getSILArgumentType(I))); - unsigned NumSub = NumSubs; - - SmallVector Substitutions; - while (NumSub--) { - auto sub = MF->maybeReadSubstitution(SILCursor); - assert(sub.hasValue() && "missing substitution"); - Substitutions.push_back(*sub); - } + SubstitutionMap Substitutions = MF->getSubstitutionMap(NumSubs); if (OpCode == SILInstructionKind::ApplyInst) { ResultVal = Builder.createApply(Loc, getLocalValue(ValID, FnTy), @@ -1295,17 +1297,11 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, for (unsigned I = 0, E = ListOfValues.size(); I < E; I++) Args.push_back(getLocalValue(ListOfValues[I], substConventions.getSILArgumentType(I))); - unsigned NumSub = NumSubs; - - SmallVector Substitutions; - while (NumSub--) { - auto sub = MF->maybeReadSubstitution(SILCursor); - assert(sub.hasValue() && "missing substitution"); - Substitutions.push_back(*sub); - } + SubstitutionMap Substitutions = MF->getSubstitutionMap(NumSubs); ResultVal = Builder.createTryApply(Loc, getLocalValue(ValID, FnTy), - Substitutions, Args, normalBB, errorBB); + Substitutions, Args, normalBB, + errorBB); break; } case SILInstructionKind::PartialApplyInst: { @@ -1314,14 +1310,8 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, SILType FnTy = getSILType(Ty, SILValueCategory::Object); SILType closureTy = getSILType(Ty2, SILValueCategory::Object); - unsigned NumSub = NumSubs; - SmallVector Substitutions; - while (NumSub--) { - auto sub = MF->maybeReadSubstitution(SILCursor); - assert(sub.hasValue() && "missing substitution"); - Substitutions.push_back(*sub); - } - + SubstitutionMap Substitutions = MF->getSubstitutionMap(NumSubs); + auto SubstFnTy = SILType::getPrimitiveObjectType( FnTy.castTo() ->substGenericArgs(Builder.getModule(), Substitutions)); @@ -1355,13 +1345,7 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, (SILValueCategory)(unsigned)ListOfValues[i+2]); Args.push_back(getLocalValue(ListOfValues[i], ArgTy)); } - unsigned NumSub = NumSubs; - SmallVector Substitutions; - while (NumSub--) { - auto sub = MF->maybeReadSubstitution(SILCursor); - assert(sub.hasValue() && "missing substitution"); - Substitutions.push_back(*sub); - } + SubstitutionMap Substitutions = MF->getSubstitutionMap(NumSubs); Identifier Name = MF->getIdentifier(ValID); ResultVal = Builder.createBuiltin(Loc, Name, ResultTy, Substitutions, @@ -1444,6 +1428,14 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, getSILType(Ty2, (SILValueCategory)TyCategory2))); break; } + case SILInstructionKind::CopyBlockWithoutEscapingInst: { + auto Ty = MF->getType(TyID); + auto Ty2 = MF->getType(TyID2); + ResultVal = Builder.createCopyBlockWithoutEscaping( + Loc, getLocalValue(ValID, getSILType(Ty, (SILValueCategory)TyCategory)), + getLocalValue(ValID2, getSILType(Ty2, (SILValueCategory)TyCategory2))); + break; + } case SILInstructionKind::IndexAddrInst: { auto Ty = MF->getType(TyID); auto Ty2 = MF->getType(TyID2); @@ -1594,12 +1586,22 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, REFCOUNTING_INSTRUCTION(UnownedRelease) UNARY_INSTRUCTION(IsUnique) UNARY_INSTRUCTION(IsUniqueOrPinned) - UNARY_INSTRUCTION(IsEscapingClosure) UNARY_INSTRUCTION(AbortApply) UNARY_INSTRUCTION(EndApply) #undef UNARY_INSTRUCTION #undef REFCOUNTING_INSTRUCTION + case SILInstructionKind::IsEscapingClosureInst: { + assert(RecordKind == SIL_ONE_OPERAND && "Layout should be OneOperand."); + unsigned verificationType = Attr; + ResultVal = Builder.createIsEscapingClosure( + Loc, + getLocalValue( + ValID, getSILType(MF->getType(TyID), (SILValueCategory)TyCategory)), + verificationType); + break; + } + case SILInstructionKind::DestructureTupleInst: { assert(RecordKind == SIL_ONE_OPERAND && "Layout should be OneOperand."); SILValue Operand = getLocalValue( @@ -1688,10 +1690,11 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, ValID, getSILType(MF->getType(TyID), (SILValueCategory)TyCategory)); auto accessKind = SILAccessKind(Attr & 0x3); auto enforcement = SILAccessEnforcement((Attr >> 2) & 0x3); - bool noNestedConflict = Attr >> 4; + bool noNestedConflict = (Attr >> 4) & 0x01; + bool fromBuiltin = (Attr >> 5) & 0x01; ResultVal = Builder.createBeginAccess(Loc, op, accessKind, enforcement, - noNestedConflict); + noNestedConflict, fromBuiltin); break; } case SILInstructionKind::EndAccessInst: { @@ -1708,17 +1711,21 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, ValID2, getSILType(MF->getType(TyID2), (SILValueCategory)TyCategory2)); auto accessKind = SILAccessKind(Attr & 0x3); auto enforcement = SILAccessEnforcement((Attr >> 2) & 0x03); - bool noNestedConflict = Attr >> 4; + bool noNestedConflict = (Attr >> 4) & 0x01; + bool fromBuiltin = (Attr >> 5) & 0x01; ResultVal = Builder.createBeginUnpairedAccess( - Loc, source, buffer, accessKind, enforcement, noNestedConflict); + Loc, source, buffer, accessKind, enforcement, noNestedConflict, + fromBuiltin); break; } case SILInstructionKind::EndUnpairedAccessInst: { SILValue op = getLocalValue( ValID, getSILType(MF->getType(TyID), (SILValueCategory)TyCategory)); bool aborted = Attr & 0x1; - auto enforcement = SILAccessEnforcement(Attr >> 1); - ResultVal = Builder.createEndUnpairedAccess(Loc, op, enforcement, aborted); + auto enforcement = SILAccessEnforcement((Attr >> 1) & 0x03); + bool fromBuiltin = (Attr >> 3) & 0x01; + ResultVal = Builder.createEndUnpairedAccess(Loc, op, enforcement, aborted, + fromBuiltin); break; } case SILInstructionKind::StoreUnownedInst: { @@ -2279,17 +2286,10 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, SILValue invoke = getLocalValue(ListOfValues[2], invokeTy); - unsigned NumSub = ListOfValues[4]; + auto SubMap = MF->getSubstitutionMap(ListOfValues[4]); - SmallVector Substitutions; - while (NumSub--) { - auto sub = MF->maybeReadSubstitution(SILCursor); - assert(sub.hasValue() && "missing substitution"); - Substitutions.push_back(*sub); - } - ResultVal = Builder.createInitBlockStorageHeader(Loc, storage, invoke, - blockTy, Substitutions); + blockTy, SubMap); break; } case SILInstructionKind::UnreachableInst: { @@ -2326,7 +2326,7 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, auto valueTy = MF->getType(ListOfValues[nextValue++]); auto numComponents = ListOfValues[nextValue++]; auto numOperands = ListOfValues[nextValue++]; - auto numSubstitutions = ListOfValues[nextValue++]; + auto subMap = MF->getSubstitutionMap(ListOfValues[nextValue++]); auto objcString = MF->getIdentifier(ListOfValues[nextValue++]).str(); auto numGenericParams = ListOfValues[nextValue++]; @@ -2345,12 +2345,6 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, SmallVector requirements; MF->readGenericRequirements(requirements, SILCursor); - SmallVector substitutions; - while (numSubstitutions-- > 0) { - auto sub = MF->maybeReadSubstitution(SILCursor); - substitutions.push_back(*sub); - } - CanGenericSignature sig = nullptr; if (!genericParams.empty() || !requirements.empty()) sig = GenericSignature::get(genericParams, requirements) @@ -2372,8 +2366,7 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, operands.push_back(getLocalValue(opValue, getSILType(opTy, opCat))); } - ResultVal = Builder.createKeyPath(Loc, pattern, - substitutions, operands, kpTy); + ResultVal = Builder.createKeyPath(Loc, pattern, subMap, operands, kpTy); break; } case SILInstructionKind::MarkUninitializedBehaviorInst: @@ -2814,6 +2807,14 @@ SILWitnessTable *SILDeserializer::readWitnessTable(DeclID WId, if (Callback) Callback->didDeserialize(MF->getAssociatedModule(), wT); } + + // We may see multiple shared-linkage definitions of the same witness table + // for the same conformance. + if (wT->isDefinition() && hasSharedVisibility(*Linkage) + && hasSharedVisibility(wT->getLinkage())) { + wTableOrOffset.set(wT, /*fully deserialized*/ true); + return wT; + } assert(wT->isDeclaration() && "Our witness table at this point must be a " "declaration."); diff --git a/lib/Serialization/ModuleFile.cpp b/lib/Serialization/ModuleFile.cpp index de5c88ca927b5..68576db201767 100644 --- a/lib/Serialization/ModuleFile.cpp +++ b/lib/Serialization/ModuleFile.cpp @@ -429,7 +429,7 @@ class ModuleFile::LocalDeclTableInfo { public: using internal_key_type = StringRef; using external_key_type = internal_key_type; - using data_type = std::pair; // ID, local discriminator + using data_type = DeclID; using hash_value_type = uint32_t; using offset_type = unsigned; @@ -447,7 +447,7 @@ class ModuleFile::LocalDeclTableInfo { static std::pair ReadKeyDataLength(const uint8_t *&data) { unsigned keyLength = endian::readNext(data); - return { keyLength, sizeof(uint32_t) + sizeof(unsigned) }; + return { keyLength, sizeof(uint32_t) }; } static internal_key_type ReadKey(const uint8_t *data, unsigned length) { @@ -456,9 +456,7 @@ class ModuleFile::LocalDeclTableInfo { static data_type ReadData(internal_key_type key, const uint8_t *data, unsigned length) { - auto declID = endian::readNext(data); - auto discriminator = endian::readNext(data); - return { declID, discriminator }; + return endian::readNext(data); } }; @@ -852,6 +850,10 @@ bool ModuleFile::readIndexBlock(llvm::BitstreamCursor &cursor) { assert(blobData.empty()); GenericEnvironments.assign(scratch.begin(), scratch.end()); break; + case index_block::SUBSTITUTION_MAP_OFFSETS: + assert(blobData.empty()); + SubstitutionMaps.assign(scratch.begin(), scratch.end()); + break; case index_block::NORMAL_CONFORMANCE_OFFSETS: assert(blobData.empty()); NormalConformances.assign(scratch.begin(), scratch.end()); @@ -1128,7 +1130,6 @@ ModuleFile::ModuleFile( } cursor.EnterSubBlock(INPUT_BLOCK_ID); - bool seenFlags = false; auto next = cursor.advance(); while (next.Kind == llvm::BitstreamEntry::Record) { @@ -1169,15 +1170,6 @@ ModuleFile::ModuleFile( importedHeaderInfo.contents = blobData; break; } - case input_block::MODULE_FLAGS: { - assert(!seenFlags && "only one flags record allowed"); - seenFlags = true; - bool hasUnderlyingModule; - input_block::ModuleFlagsLayout::readRecord(scratch, - hasUnderlyingModule); - Bits.HasUnderlyingModule = hasUnderlyingModule; - break; - } case input_block::SEARCH_PATH: { bool isFramework; bool isSystem; @@ -1433,11 +1425,6 @@ Status ModuleFile::associateWithFileContext(FileUnit *file, continue; } - // This is for backwards-compatibility with modules that still rely on the - // "HasUnderlyingModule" flag. - if (Bits.HasUnderlyingModule && module == ShadowedModule) - dependency.forceExported(); - if (scopePath.empty()) { dependency.Import = { {}, module }; } else { @@ -1448,6 +1435,13 @@ Status ModuleFile::associateWithFileContext(FileUnit *file, dependency.Import = {ctx.AllocateCopy(llvm::makeArrayRef(accessPathElem)), module}; } + + if (!module->hasResolvedImports()) { + // Notice that we check this condition /after/ recording the module that + // caused the problem. Clients need to be able to track down what the + // cycle was. + return error(Status::CircularDependency); + } } if (missingDependency) { @@ -1520,7 +1514,7 @@ TypeDecl *ModuleFile::lookupLocalType(StringRef MangledName) { if (iter == LocalTypeDecls->end()) return nullptr; - return cast(getDecl((*iter).first)); + return cast(getDecl(*iter)); } TypeDecl *ModuleFile::lookupNestedType(Identifier name, @@ -1591,9 +1585,26 @@ void ModuleFile::getImportedModules( PrettyStackTraceModuleFile stackEntry(*this); for (auto &dep : Dependencies) { - if (filter != ModuleDecl::ImportFilter::All && - (filter == ModuleDecl::ImportFilter::Public) ^ dep.isExported()) - continue; + switch (filter) { + case ModuleDecl::ImportFilter::All: + // We're including all imports. + break; + + case ModuleDecl::ImportFilter::Private: + // Skip @_exported imports. + if (dep.isExported()) + continue; + + break; + + case ModuleDecl::ImportFilter::Public: + // Only include @_exported imports. + if (!dep.isExported()) + continue; + + break; + } + assert(dep.isLoaded()); results.push_back(dep.Import); } @@ -2008,10 +2019,8 @@ ModuleFile::getLocalTypeDecls(SmallVectorImpl &results) { if (!LocalTypeDecls) return; - for (auto entry : LocalTypeDecls->data()) { - auto DeclID = entry.first; + for (auto DeclID : LocalTypeDecls->data()) { auto TD = cast(getDecl(DeclID)); - TD->setLocalDiscriminator(entry.second); results.push_back(TD); } } diff --git a/lib/Serialization/SILFormat.h b/lib/Serialization/SILFormat.h index 225be47cd1f75..2c77d6edab6a3 100644 --- a/lib/Serialization/SILFormat.h +++ b/lib/Serialization/SILFormat.h @@ -140,8 +140,7 @@ namespace sil_block { // These IDs must \em not be renumbered or reordered without incrementing // VERSION_MAJOR. enum RecordKind : uint8_t { - // To avoid overlapping with BOUND_GENERIC_SUBSTITUTION, we start from +1. - SIL_FUNCTION = decls_block::BOUND_GENERIC_SUBSTITUTION + 1, + SIL_FUNCTION = 1, SIL_BASIC_BLOCK, SIL_ONE_VALUE_ONE_OPERAND, SIL_ONE_TYPE, @@ -174,7 +173,6 @@ namespace sil_block { // We also share these layouts from the decls block. Their enumerators must // not overlap with ours. - BOUND_GENERIC_SUBSTITUTION = decls_block::BOUND_GENERIC_SUBSTITUTION, ABSTRACT_PROTOCOL_CONFORMANCE = decls_block::ABSTRACT_PROTOCOL_CONFORMANCE, NORMAL_PROTOCOL_CONFORMANCE = decls_block::NORMAL_PROTOCOL_CONFORMANCE, SPECIALIZED_PROTOCOL_CONFORMANCE @@ -387,7 +385,7 @@ namespace sil_block { using SILInstApplyLayout = BCRecordLayout< SIL_INST_APPLY, BCFixed<3>, // ApplyKind - BCFixed<32>, // num substitutions + SubstitutionMapIDField, // substitution map TypeIDField, // callee unsubstituted type TypeIDField, // callee substituted type ValueIDField, // callee value @@ -415,7 +413,7 @@ namespace sil_block { using SILOneOperandExtraAttributeLayout = BCRecordLayout< SIL_ONE_OPERAND_EXTRA_ATTR, SILInstOpCodeField, - BCFixed<5>, // Optional attributes + BCFixed<6>, // Optional attributes TypeIDField, SILTypeCategoryField, ValueIDField >; @@ -435,7 +433,7 @@ namespace sil_block { using SILTwoOperandsExtraAttributeLayout = BCRecordLayout< SIL_TWO_OPERANDS_EXTRA_ATTR, SILInstOpCodeField, - BCFixed<5>, // Optional attributes + BCFixed<6>, // Optional attributes TypeIDField, SILTypeCategoryField, ValueIDField, diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index fa7b05bf83e0d..831de7e754580 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -228,7 +228,7 @@ namespace { public: using key_type = std::string; using key_type_ref = StringRef; - using data_type = std::pair; // ID, local discriminator + using data_type = DeclID; using data_type_ref = const data_type &; using hash_value_type = uint32_t; using offset_type = unsigned; @@ -242,7 +242,7 @@ namespace { key_type_ref key, data_type_ref data) { uint32_t keyLength = key.size(); - uint32_t dataLength = sizeof(uint32_t) + sizeof(unsigned); + uint32_t dataLength = sizeof(uint32_t); endian::Writer writer(out); writer.write(keyLength); return { keyLength, dataLength }; @@ -256,8 +256,7 @@ namespace { unsigned len) { static_assert(declIDFitsIn32Bits(), "DeclID too large"); endian::Writer writer(out); - writer.write(data.first); - writer.write(data.second); + writer.write(data); } }; @@ -568,6 +567,19 @@ GenericEnvironmentID Serializer::addGenericEnvironmentRef( return id; } +SubstitutionMapID Serializer::addSubstitutionMapRef( + SubstitutionMap substitutions) { + if (!substitutions) return 0; + + auto &id = SubstitutionMapIDs[substitutions]; + if (id != 0) + return id; + + id = ++LastSubstitutionMapID; + SubstitutionMapsToWrite.push(substitutions); + return id; +} + DeclContextID Serializer::addDeclContextRef(const DeclContext *DC) { switch (DC->getContextKind()) { case DeclContextKind::Module: @@ -787,6 +799,7 @@ void Serializer::writeBlockInfoBlock() { BLOCK_RECORD(index_block, LOCAL_DECL_CONTEXT_OFFSETS); BLOCK_RECORD(index_block, GENERIC_SIGNATURE_OFFSETS); BLOCK_RECORD(index_block, GENERIC_ENVIRONMENT_OFFSETS); + BLOCK_RECORD(index_block, SUBSTITUTION_MAP_OFFSETS); BLOCK_RECORD(index_block, DECL_CONTEXT_OFFSETS); BLOCK_RECORD(index_block, LOCAL_TYPE_DECLS); BLOCK_RECORD(index_block, NORMAL_CONFORMANCE_OFFSETS); @@ -831,8 +844,6 @@ void Serializer::writeBlockInfoBlock() { // These layouts can exist in both decl blocks and sil blocks. #define BLOCK_RECORD_WITH_NAMESPACE(K, X) emitRecordID(Out, X, #X, nameBuffer) - BLOCK_RECORD_WITH_NAMESPACE(sil_block, - decls_block::BOUND_GENERIC_SUBSTITUTION); BLOCK_RECORD_WITH_NAMESPACE(sil_block, decls_block::ABSTRACT_PROTOCOL_CONFORMANCE); BLOCK_RECORD_WITH_NAMESPACE(sil_block, @@ -1043,7 +1054,6 @@ void Serializer::writeInputBlock(const SerializationOptions &options) { input_block::LinkLibraryLayout LinkLibrary(Out); input_block::ImportedHeaderLayout ImportedHeader(Out); input_block::ImportedHeaderContentsLayout ImportedHeaderContents(Out); - input_block::ModuleFlagsLayout ModuleFlags(Out); input_block::SearchPathLayout SearchPath(Out); if (options.SerializeOptionsForDebugging) { @@ -1460,6 +1470,34 @@ void Serializer::writeGenericEnvironment(const GenericEnvironment *env) { DeclTypeAbbrCodes); } +void Serializer::writeSubstitutionMap(const SubstitutionMap substitutions) { + using namespace decls_block; + assert(substitutions); + + // Record the offset of this substitution map. + auto id = SubstitutionMapIDs[substitutions]; + assert(id != 0 && "generic environment not referenced properly"); + (void)id; + + // Record the current bit. + assert((id - 1) == SubstitutionMapOffsets.size()); + SubstitutionMapOffsets.push_back(Out.GetCurrentBitNo()); + + // Collect the replacement types. + SmallVector rawReplacementTypes; + for (auto type : substitutions.getReplacementTypes()) + rawReplacementTypes.push_back(addTypeRef(type)); + + auto substitutionsAbbrCode = DeclTypeAbbrCodes[SubstitutionMapLayout::Code]; + SubstitutionMapLayout::emitRecord(Out, ScratchRecord, substitutionsAbbrCode, + addGenericSignatureRef( + substitutions.getGenericSignature()), + substitutions.getConformances().size(), + rawReplacementTypes); + + writeConformances(substitutions.getConformances(), DeclTypeAbbrCodes); +} + void Serializer::writeSILLayout(SILLayout *layout) { using namespace decls_block; auto foundLayoutID = SILLayouts.find(layout); @@ -1530,20 +1568,18 @@ void Serializer::writeNormalConformance( // If there is no witness, we're done. if (!witness.getDecl()) return; - if (auto genericEnv = witness.requiresSubstitution() - ? witness.getSyntheticEnvironment() - : nullptr) { + if (auto *genericEnv = witness.getSyntheticEnvironment()) { // Generic signature. auto *genericSig = genericEnv->getGenericSignature(); data.push_back(addGenericSignatureRef(genericSig)); - - auto reqToSyntheticSubs = witness.getRequirementToSyntheticSubs(); - data.push_back(reqToSyntheticSubs.size()); } else { data.push_back(/*null generic signature*/0); } - data.push_back(witness.getSubstitutions().size()); + data.push_back( + addSubstitutionMapRef(witness.getRequirementToSyntheticSubs())); + data.push_back( + addSubstitutionMapRef(witness.getSubstitutions())); }); unsigned numSignatureConformances = @@ -1562,26 +1598,6 @@ void Serializer::writeNormalConformance( // Write requirement signature conformances. for (auto reqConformance : conformance->getSignatureConformances()) writeConformance(reqConformance, DeclTypeAbbrCodes); - - conformance->forEachValueWitness(nullptr, - [&](ValueDecl *req, Witness witness) { - // Bail out early for simple witnesses. - if (!witness.getDecl()) return; - - if (witness.requiresSubstitution()) { - // Write requirement-to-synthetic substitutions. - writeSubstitutions(witness.getRequirementToSyntheticSubs(), - DeclTypeAbbrCodes, - nullptr); - } - - // Write the witness substitutions. - writeSubstitutions(witness.getSubstitutions(), - DeclTypeAbbrCodes, - witness.requiresSubstitution() - ? witness.getSyntheticEnvironment() - : nullptr); - }); } void @@ -1608,7 +1624,9 @@ Serializer::writeConformance(ProtocolConformanceRef conformanceRef, switch (conformance->getKind()) { case ProtocolConformanceKind::Normal: { auto normal = cast(conformance); - if (!isDeclXRef(getDeclForContext(normal->getDeclContext()))) { + if (!isDeclXRef(getDeclForContext(normal->getDeclContext())) + && !isa(normal->getDeclContext() + ->getModuleScopeContext())) { // A normal conformance in this module file. unsigned abbrCode = abbrCodes[NormalProtocolConformanceIdLayout::Code]; NormalProtocolConformanceIdLayout::emitRecord(Out, ScratchRecord, @@ -1629,16 +1647,15 @@ Serializer::writeConformance(ProtocolConformanceRef conformanceRef, case ProtocolConformanceKind::Specialized: { auto conf = cast(conformance); - auto substitutions = conf->getGenericSubstitutions(); unsigned abbrCode = abbrCodes[SpecializedProtocolConformanceLayout::Code]; auto type = conf->getType(); if (genericEnv && type->hasArchetype()) type = type->mapTypeOutOfContext(); - SpecializedProtocolConformanceLayout::emitRecord(Out, ScratchRecord, - abbrCode, - addTypeRef(type), - substitutions.size()); - writeSubstitutions(substitutions, abbrCodes, genericEnv); + SpecializedProtocolConformanceLayout::emitRecord( + Out, ScratchRecord, + abbrCode, + addTypeRef(type), + addSubstitutionMapRef(conf->getSubstitutionMap())); writeConformance(conf->getGenericConformance(), abbrCodes, genericEnv); break; @@ -1680,30 +1697,6 @@ Serializer::writeConformances(ArrayRef conformances, writeConformance(conformance, abbrCodes); } -void -Serializer::writeSubstitutions(SubstitutionList substitutions, - const std::array &abbrCodes, - GenericEnvironment *genericEnv) { - using namespace decls_block; - auto abbrCode = abbrCodes[BoundGenericSubstitutionLayout::Code]; - - for (auto &sub : substitutions) { - auto replacementType = sub.getReplacement(); - if (genericEnv && replacementType->hasArchetype()) { - replacementType = replacementType->mapTypeOutOfContext(); - } - - BoundGenericSubstitutionLayout::emitRecord( - Out, ScratchRecord, abbrCode, - addTypeRef(replacementType), - sub.getConformances().size()); - - for (auto conformance : sub.getConformances()) { - writeConformance(conformance, abbrCodes, genericEnv); - } - } -} - static uint8_t getRawStableOptionalTypeKind(swift::OptionalTypeKind kind) { switch (kind) { case swift::OTK_None: @@ -2230,7 +2223,7 @@ void Serializer::writeDeclAttribute(const DeclAttribute *DA) { auto abbrCode = DeclTypeAbbrCodes[AlignmentDeclAttrLayout::Code]; AlignmentDeclAttrLayout::emitRecord(Out, ScratchRecord, abbrCode, theAlignment->isImplicit(), - theAlignment->Value); + theAlignment->getValue()); return; } @@ -3629,17 +3622,13 @@ void Serializer::writeType(Type ty) { unsigned abbrCode = DeclTypeAbbrCodes[NameAliasTypeLayout::Code]; NameAliasTypeLayout::emitRecord( - Out, ScratchRecord, abbrCode, - addDeclRef(typeAlias, - /*forceSerialization*/false, - /*allowTypeAliasXRef*/true), - addTypeRef(alias->getParent()), - addTypeRef(alias->getSinglyDesugaredType())); - // Write the set of substitutions. - SmallVector flatSubs; - if (auto genericSig = typeAlias->getGenericSignature()) - genericSig->getSubstitutions(alias->getSubstitutionMap(), flatSubs); - writeSubstitutions(flatSubs, DeclTypeAbbrCodes); + Out, ScratchRecord, abbrCode, + addDeclRef(typeAlias, + /*forceSerialization*/false, + /*allowTypeAliasXRef*/true), + addTypeRef(alias->getParent()), + addTypeRef(alias->getSinglyDesugaredType()), + addSubstitutionMapRef(alias->getSubstitutionMap())); break; } @@ -3822,17 +3811,8 @@ void Serializer::writeType(Type ty) { unsigned abbrCode = DeclTypeAbbrCodes[SILBoxTypeLayout::Code]; SILLayoutID layoutRef = addSILLayoutRef(boxTy->getLayout()); -#ifndef NDEBUG - if (auto sig = boxTy->getLayout()->getGenericSignature()) { - assert(sig->getSubstitutionListSize() - == boxTy->getGenericArgs().size()); - } -#endif - - SILBoxTypeLayout::emitRecord(Out, ScratchRecord, abbrCode, layoutRef); - - // Write the set of substitutions. - writeSubstitutions(boxTy->getGenericArgs(), DeclTypeAbbrCodes); + SILBoxTypeLayout::emitRecord(Out, ScratchRecord, abbrCode, layoutRef, + addSubstitutionMapRef(boxTy->getSubstitutions())); break; } @@ -3963,7 +3943,9 @@ void Serializer::writeType(Type ty) { unsigned abbrCode = DeclTypeAbbrCodes[UnboundGenericTypeLayout::Code]; UnboundGenericTypeLayout::emitRecord(Out, ScratchRecord, abbrCode, - addDeclRef(generic->getDecl()), + addDeclRef(generic->getDecl(), + /*forceSerialization*/false, + /*allowTypeAliasXRef*/true), addTypeRef(generic->getParent())); break; } @@ -4010,7 +3992,6 @@ void Serializer::writeAllDeclsAndTypes() { registerDeclTypeAbbr(); registerDeclTypeAbbr(); registerDeclTypeAbbr(); - registerDeclTypeAbbr(); registerDeclTypeAbbr(); registerDeclTypeAbbr(); registerDeclTypeAbbr(); @@ -4062,6 +4043,7 @@ void Serializer::writeAllDeclsAndTypes() { registerDeclTypeAbbr(); registerDeclTypeAbbr(); registerDeclTypeAbbr(); + registerDeclTypeAbbr(); registerDeclTypeAbbr(); registerDeclTypeAbbr(); @@ -4132,6 +4114,12 @@ void Serializer::writeAllDeclsAndTypes() { writeGenericEnvironment(next); } + while (!SubstitutionMapsToWrite.empty()) { + auto next = SubstitutionMapsToWrite.front(); + SubstitutionMapsToWrite.pop(); + writeSubstitutionMap(next); + } + while (!NormalConformancesToWrite.empty()) { auto next = NormalConformancesToWrite.front(); NormalConformancesToWrite.pop(); @@ -4149,6 +4137,7 @@ void Serializer::writeAllDeclsAndTypes() { !SILLayoutsToWrite.empty() || !GenericSignaturesToWrite.empty() || !GenericEnvironmentsToWrite.empty() || + !SubstitutionMapsToWrite.empty() || !NormalConformancesToWrite.empty()); } @@ -4377,8 +4366,8 @@ class DeclCommentTableInfo { } // end unnamed namespace -typedef llvm::StringMap FileNameToGroupNameMap; -typedef std::unique_ptr pFileNameToGroupNameMap; +using FileNameToGroupNameMap = llvm::StringMap; +using pFileNameToGroupNameMap = std::unique_ptr; class YamlGroupInputParser { StringRef RecordPath; @@ -4921,9 +4910,7 @@ void Serializer::writeAST(ModuleOrSourceFile DC, Mangle::ASTMangler Mangler; std::string MangledName = Mangler.mangleDeclAsUSR(TD, /*USRPrefix*/""); assert(!MangledName.empty() && "Mangled type came back empty!"); - localTypeGenerator.insert(MangledName, { - addDeclRef(TD), TD->getLocalDiscriminator() - }); + localTypeGenerator.insert(MangledName, addDeclRef(TD)); if (auto IDC = dyn_cast(TD)) { collectInterestingNestedDeclarations(*this, IDC->getMembers(), @@ -4947,6 +4934,7 @@ void Serializer::writeAST(ModuleOrSourceFile DC, writeOffsets(Offsets, LocalDeclContextOffsets); writeOffsets(Offsets, GenericSignatureOffsets); writeOffsets(Offsets, GenericEnvironmentOffsets); + writeOffsets(Offsets, SubstitutionMapOffsets); writeOffsets(Offsets, NormalConformanceOffsets); writeOffsets(Offsets, SILLayoutOffsets); diff --git a/lib/Serialization/Serialization.h b/lib/Serialization/Serialization.h index 340f88a235dda..79a0387c4b98c 100644 --- a/lib/Serialization/Serialization.h +++ b/lib/Serialization/Serialization.h @@ -32,7 +32,7 @@ namespace swift { namespace serialization { -typedef ArrayRef FilenamesTy; +using FilenamesTy = ArrayRef; class Serializer { SmallVector Buffer; @@ -108,6 +108,9 @@ class Serializer { llvm::DenseMap GenericEnvironmentIDs; + /// A map from substitution maps to their serialized IDs. + llvm::DenseMap SubstitutionMapIDs; + // A map from NormalProtocolConformances to their serialized IDs. llvm::DenseMap NormalConformances; @@ -177,6 +180,9 @@ class Serializer { /// Generic environments that need to be serialized. std::queue GenericEnvironmentsToWrite; + /// Substitution maps that need to be serialized. + std::queue SubstitutionMapsToWrite; + /// NormalProtocolConformances that need to be serialized. std::queue NormalConformancesToWrite; @@ -217,6 +223,10 @@ class Serializer { /// GenericEnvironmentID. std::vector GenericEnvironmentOffsets; + /// The offset of each SubstitutionMap in the bitstream, indexed by + /// SubstitutionMapID. + std::vector SubstitutionMapOffsets; + /// The offset of each NormalProtocolConformance in the bitstream, indexed by /// NormalConformanceID. std::vector NormalConformanceOffsets; @@ -261,6 +271,10 @@ class Serializer { /// module. uint32_t /*GenericEnvironmentID*/ LastGenericEnvironmentID = 0; + /// The last assigned SubstitutionMapID for substitution maps from this + /// module. + uint32_t /*SubstitutionMapID*/ LastSubstitutionMapID = 0; + /// Returns the record code for serializing the given vector of offsets. /// /// This allows the offset-serialization code to be generic over all kinds @@ -280,6 +294,8 @@ class Serializer { return index_block::GENERIC_SIGNATURE_OFFSETS; if (&values == &GenericEnvironmentOffsets) return index_block::GENERIC_ENVIRONMENT_OFFSETS; + if (&values == &SubstitutionMapOffsets) + return index_block::SUBSTITUTION_MAP_OFFSETS; if (&values == &NormalConformanceOffsets) return index_block::NORMAL_CONFORMANCE_OFFSETS; if (&values == &SILLayoutOffsets) @@ -377,6 +393,9 @@ class Serializer { /// Writes a generic environment. void writeGenericEnvironment(const GenericEnvironment *env); + /// Writes a substitution map. + void writeSubstitutionMap(const SubstitutionMap substitutions); + /// Registers the abbreviation for the given decl or type layout. template void registerDeclTypeAbbr() { @@ -466,6 +485,11 @@ class Serializer { /// The GenericEnvironment will be scheduled for serialization if necessary. GenericEnvironmentID addGenericEnvironmentRef(const GenericEnvironment *env); + /// Records the use of the given substitution map. + /// + /// The SubstitutionMap will be scheduled for serialization if necessary. + SubstitutionMapID addSubstitutionMapRef(SubstitutionMap substitutions); + /// Records the use of the given normal protocol conformance. /// /// The normal protocol conformance will be scheduled for @@ -486,17 +510,6 @@ class Serializer { /// special module codes defined above. IdentifierID addModuleRef(const ModuleDecl *M); - /// Writes a list of generic substitutions. abbrCode is needed to support - /// usage out of decl block. - /// - /// \param genericEnv When provided, the generic environment that describes - /// the archetypes within the substitutions. The replacement types within - /// the substitution will be mapped out of the generic environment before - /// being written. - void writeSubstitutions(SubstitutionList substitutions, - const std::array &abbrCodes, - GenericEnvironment *genericEnv = nullptr); - /// Write a normal protocol conformance. void writeNormalConformance(const NormalProtocolConformance *conformance); diff --git a/lib/Serialization/SerializeSIL.cpp b/lib/Serialization/SerializeSIL.cpp index 5debc36ed07f6..4246efb9f49d0 100644 --- a/lib/Serialization/SerializeSIL.cpp +++ b/lib/Serialization/SerializeSIL.cpp @@ -135,24 +135,6 @@ namespace { } }; - // Some key path components need to serialize additional Substitutions or - // ProtocolConformances after the main record is emitted. - struct ConformanceOrSubstitution { - enum { ProtocolConformance, SubstitutionList } Kind; - union { - swift::ProtocolConformanceRef Conformance; - swift::SubstitutionList Substitutions; - }; - - ConformanceOrSubstitution(ProtocolConformanceRef c) - : Kind(ProtocolConformance), Conformance(c) - {} - - ConformanceOrSubstitution(swift::SubstitutionList s) - : Kind(SubstitutionList), Substitutions(s) - {} - }; - class SILSerializer { Serializer &S; ASTContext &Ctx; @@ -252,7 +234,7 @@ namespace { void writeIndexTables(); void writeConversionLikeInstruction(const SingleValueInstruction *I, - bool guaranteed); + bool guaranteed, bool escaped); void writeOneTypeLayout(SILInstructionKind valueKind, SILType type); void writeOneTypeOneOperandLayout(SILInstructionKind valueKind, unsigned attrs, @@ -271,7 +253,7 @@ namespace { void writeKeyPathPatternComponent( const KeyPathPatternComponent &component, SmallVectorImpl &ListOfValues, - SmallVectorImpl &serializeAfter); + SmallVectorImpl &serializeAfter); /// Helper function to determine if given the current state of the /// deserialization if the function body for F should be deserialized. @@ -373,7 +355,7 @@ void SILSerializer::writeSILFunction(const SILFunction &F, bool DeclOnly) { FuncTable[Ctx.getIdentifier(F.getName())] = NextFuncID++; Funcs.push_back(Out.GetCurrentBitNo()); unsigned abbrCode = SILAbbrCodes[SILFunctionLayout::Code]; - TypeID FnID = S.addTypeRef(F.getLoweredType().getSwiftRValueType()); + TypeID FnID = S.addTypeRef(F.getLoweredType().getASTType()); DEBUG(llvm::dbgs() << "SILFunction " << F.getName() << " @ BitNo " << Out.GetCurrentBitNo() << " abbrCode " << abbrCode << " FnID " << FnID << "\n"); @@ -463,7 +445,7 @@ void SILSerializer::writeSILBasicBlock(const SILBasicBlock &BB) { SmallVector Args; for (auto I = BB.args_begin(), E = BB.args_end(); I != E; ++I) { SILArgument *SA = *I; - DeclID tId = S.addTypeRef(SA->getType().getSwiftRValueType()); + DeclID tId = S.addTypeRef(SA->getType().getASTType()); DeclID vId = addValueRef(static_cast(SA)); Args.push_back(tId); @@ -521,7 +503,7 @@ void SILSerializer::handleMethodInst(const MethodInst *MI, SmallVectorImpl &ListOfValues) { handleSILDeclRef(S, MI->getMember(), ListOfValues); ListOfValues.push_back( - S.addTypeRef(operand->getType().getSwiftRValueType())); + S.addTypeRef(operand->getType().getASTType())); ListOfValues.push_back((unsigned)operand->getType().getCategory()); ListOfValues.push_back(addValueRef(operand)); } @@ -531,7 +513,7 @@ void SILSerializer::writeOneTypeLayout(SILInstructionKind valueKind, unsigned abbrCode = SILAbbrCodes[SILOneTypeLayout::Code]; SILOneTypeLayout::emitRecord(Out, ScratchRecord, abbrCode, (unsigned) valueKind, - S.addTypeRef(type.getSwiftRValueType()), + S.addTypeRef(type.getASTType()), (unsigned)type.getCategory()); } @@ -540,7 +522,7 @@ void SILSerializer::writeOneOperandLayout(SILInstructionKind valueKind, SILValue operand) { auto operandType = operand->getType(); - auto operandTypeRef = S.addTypeRef(operandType.getSwiftRValueType()); + auto operandTypeRef = S.addTypeRef(operandType.getASTType()); auto operandRef = addValueRef(operand); SILOneOperandLayout::emitRecord(Out, ScratchRecord, @@ -556,7 +538,7 @@ writeOneOperandExtraAttributeLayout(SILInstructionKind valueKind, SILValue operand) { auto operandType = operand->getType(); - auto operandTypeRef = S.addTypeRef(operandType.getSwiftRValueType()); + auto operandTypeRef = S.addTypeRef(operandType.getASTType()); auto operandRef = addValueRef(operand); SILOneOperandExtraAttributeLayout::emitRecord( @@ -569,9 +551,9 @@ void SILSerializer::writeOneTypeOneOperandLayout(SILInstructionKind valueKind, unsigned attrs, SILType type, SILValue operand) { - auto typeRef = S.addTypeRef(type.getSwiftRValueType()); + auto typeRef = S.addTypeRef(type.getASTType()); auto operandType = operand->getType(); - auto operandTypeRef = S.addTypeRef(operandType.getSwiftRValueType()); + auto operandTypeRef = S.addTypeRef(operandType.getASTType()); auto operandRef = addValueRef(operand); SILOneTypeOneOperandLayout::emitRecord(Out, ScratchRecord, @@ -587,7 +569,7 @@ void SILSerializer::writeOneTypeOneOperandLayout(SILInstructionKind valueKind, SILValue operand) { auto typeRef = S.addTypeRef(type); auto operandType = operand->getType(); - auto operandTypeRef = S.addTypeRef(operandType.getSwiftRValueType()); + auto operandTypeRef = S.addTypeRef(operandType.getASTType()); auto operandRef = addValueRef(operand); SILOneTypeOneOperandLayout::emitRecord(Out, ScratchRecord, @@ -601,17 +583,18 @@ void SILSerializer::writeOneTypeOneOperandLayout(SILInstructionKind valueKind, /// Write an instruction that looks exactly like a conversion: all /// important information is encoded in the operand and the result type. void SILSerializer::writeConversionLikeInstruction( - const SingleValueInstruction *I, bool guaranteed) { + const SingleValueInstruction *I, bool guaranteed, bool escaped) { assert(I->getNumOperands() - I->getTypeDependentOperands().size() == 1); - writeOneTypeOneOperandLayout(I->getKind(), guaranteed ? 1 : 0, I->getType(), - I->getOperand(0)); + writeOneTypeOneOperandLayout(I->getKind(), + (guaranteed ? 1 : 0) | (escaped ? 2 : 0), + I->getType(), I->getOperand(0)); } void SILSerializer::writeKeyPathPatternComponent( const KeyPathPatternComponent &component, SmallVectorImpl &ListOfValues, - SmallVectorImpl &serializeAfter) { + SmallVectorImpl &serializeAfter) { auto handleComponentCommon = [&](KeyPathComponentKindEncoding kind) { ListOfValues.push_back((unsigned)kind); @@ -644,7 +627,7 @@ SILSerializer::writeKeyPathPatternComponent( ListOfValues.push_back(index.Operand); ListOfValues.push_back(S.addTypeRef(index.FormalType)); ListOfValues.push_back( - S.addTypeRef(index.LoweredType.getSwiftRValueType())); + S.addTypeRef(index.LoweredType.getASTType())); ListOfValues.push_back((unsigned)index.LoweredType.getCategory()); serializeAfter.push_back(index.Hashable); } @@ -689,8 +672,8 @@ SILSerializer::writeKeyPathPatternComponent( case KeyPathPatternComponent::Kind::External: handleComponentCommon(KeyPathComponentKindEncoding::External); ListOfValues.push_back(S.addDeclRef(component.getExternalDecl())); - ListOfValues.push_back(component.getExternalSubstitutions().size()); - serializeAfter.push_back(component.getExternalSubstitutions()); + ListOfValues.push_back( + S.addSubstitutionMapRef(component.getExternalSubstitutions())); handleComputedIndices(component); break; } @@ -773,7 +756,7 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { SILValueCategory operandCategory = SILValueCategory::Object; ValueID operandID = 0; if (operand) { - operandType = S.addTypeRef(operand->getType().getSwiftRValueType()); + operandType = S.addTypeRef(operand->getType().getASTType()); operandCategory = operand->getType().getCategory(); operandID = addValueRef(operand); } @@ -781,7 +764,7 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { unsigned abbrCode = SILAbbrCodes[SILInitExistentialLayout::Code]; SILInitExistentialLayout::emitRecord(Out, ScratchRecord, abbrCode, (unsigned)SI.getKind(), - S.addTypeRef(Ty.getSwiftRValueType()), + S.addTypeRef(Ty.getASTType()), (unsigned)Ty.getCategory(), operandType, (unsigned)operandCategory, @@ -857,18 +840,18 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { for (unsigned Idx = 0; Idx < NumOpsToWrite; ++Idx) { if (Idx < NumTailAllocs) { assert(TailTypes[Idx].isObject()); - Args.push_back(S.addTypeRef(TailTypes[Idx].getSwiftRValueType())); + Args.push_back(S.addTypeRef(TailTypes[Idx].getASTType())); } SILValue OpVal = AllOps[Idx].get(); Args.push_back(addValueRef(OpVal)); SILType OpType = OpVal->getType(); assert(OpType.isObject()); - Args.push_back(S.addTypeRef(OpType.getSwiftRValueType())); + Args.push_back(S.addTypeRef(OpType.getASTType())); } SILOneTypeValuesLayout::emitRecord(Out, ScratchRecord, abbrCode, (unsigned)SI.getKind(), S.addTypeRef( - ARI->getType().getSwiftRValueType()), + ARI->getType().getASTType()), (unsigned)ARI->getType().getCategory(), Args); break; @@ -892,7 +875,7 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { auto boxOperand = PBI->getOperand(); auto boxRef = addValueRef(boxOperand); auto boxType = boxOperand->getType(); - auto boxTypeRef = S.addTypeRef(boxType.getSwiftRValueType()); + auto boxTypeRef = S.addTypeRef(boxType.getASTType()); SILOneTypeOneOperandLayout::emitRecord(Out, ScratchRecord, SILAbbrCodes[SILOneTypeOneOperandLayout::Code], @@ -910,7 +893,7 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { break; } case SILInstructionKind::BuiltinInst: { - // Format: number of substitutions, the builtin name, result type, and + // Format: substitutions map ID, the builtin name, result type, and // a list of values for the arguments. Each value in the list // is represented with 4 IDs: // ValueID, ValueResultNumber, TypeID, TypeCategory. @@ -919,18 +902,17 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { SmallVector Args; for (auto Arg : BI->getArguments()) { Args.push_back(addValueRef(Arg)); - Args.push_back(S.addTypeRef(Arg->getType().getSwiftRValueType())); + Args.push_back(S.addTypeRef(Arg->getType().getASTType())); Args.push_back((unsigned)Arg->getType().getCategory()); } SILInstApplyLayout::emitRecord(Out, ScratchRecord, SILAbbrCodes[SILInstApplyLayout::Code], SIL_BUILTIN, - BI->getSubstitutions().size(), - S.addTypeRef(BI->getType().getSwiftRValueType()), + S.addSubstitutionMapRef(BI->getSubstitutions()), + S.addTypeRef(BI->getType().getASTType()), (unsigned)BI->getType().getCategory(), S.addDeclBaseNameRef(BI->getName()), Args); - S.writeSubstitutions(BI->getSubstitutions(), SILAbbrCodes); break; } case SILInstructionKind::ApplyInst: { @@ -947,12 +929,11 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { SILInstApplyLayout::emitRecord(Out, ScratchRecord, SILAbbrCodes[SILInstApplyLayout::Code], AI->isNonThrowing() ? SIL_NON_THROWING_APPLY : SIL_APPLY, - AI->getSubstitutions().size(), - S.addTypeRef(AI->getCallee()->getType().getSwiftRValueType()), + S.addSubstitutionMapRef(AI->getSubstitutionMap()), + S.addTypeRef(AI->getCallee()->getType().getASTType()), S.addTypeRef(AI->getSubstCalleeType()), addValueRef(AI->getCallee()), Args); - S.writeSubstitutions(AI->getSubstitutions(), SILAbbrCodes); break; } case SILInstructionKind::BeginApplyInst: { @@ -969,12 +950,11 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { SILInstApplyLayout::emitRecord(Out, ScratchRecord, SILAbbrCodes[SILInstApplyLayout::Code], AI->isNonThrowing() ? SIL_NON_THROWING_BEGIN_APPLY : SIL_BEGIN_APPLY, - AI->getSubstitutions().size(), - S.addTypeRef(AI->getCallee()->getType().getSwiftRValueType()), + S.addSubstitutionMapRef(AI->getSubstitutionMap()), + S.addTypeRef(AI->getCallee()->getType().getASTType()), S.addTypeRef(AI->getSubstCalleeType()), addValueRef(AI->getCallee()), Args); - S.writeSubstitutions(AI->getSubstitutions(), SILAbbrCodes); break; } case SILInstructionKind::TryApplyInst: { @@ -993,12 +973,11 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { Args.push_back(BasicBlockMap[AI->getErrorBB()]); SILInstApplyLayout::emitRecord(Out, ScratchRecord, SILAbbrCodes[SILInstApplyLayout::Code], SIL_TRY_APPLY, - AI->getSubstitutions().size(), - S.addTypeRef(AI->getCallee()->getType().getSwiftRValueType()), + S.addSubstitutionMapRef(AI->getSubstitutionMap()), + S.addTypeRef(AI->getCallee()->getType().getASTType()), S.addTypeRef(AI->getSubstCalleeType()), addValueRef(AI->getCallee()), Args); - S.writeSubstitutions(AI->getSubstitutions(), SILAbbrCodes); break; } case SILInstructionKind::PartialApplyInst: { @@ -1009,12 +988,11 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { } SILInstApplyLayout::emitRecord(Out, ScratchRecord, SILAbbrCodes[SILInstApplyLayout::Code], SIL_PARTIAL_APPLY, - PAI->getSubstitutions().size(), - S.addTypeRef(PAI->getCallee()->getType().getSwiftRValueType()), - S.addTypeRef(PAI->getType().getSwiftRValueType()), + S.addSubstitutionMapRef(PAI->getSubstitutionMap()), + S.addTypeRef(PAI->getCallee()->getType().getASTType()), + S.addTypeRef(PAI->getType().getASTType()), addValueRef(PAI->getCallee()), Args); - S.writeSubstitutions(PAI->getSubstitutions(), SILAbbrCodes); break; } case SILInstructionKind::AllocGlobalInst: { @@ -1034,7 +1012,7 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { SILOneOperandLayout::emitRecord(Out, ScratchRecord, SILAbbrCodes[SILOneOperandLayout::Code], (unsigned)SI.getKind(), 0, - S.addTypeRef(GI->getType().getSwiftRValueType()), + S.addTypeRef(GI->getType().getASTType()), (unsigned)GI->getType().getCategory(), S.addDeclBaseNameRef( Ctx.getIdentifier(GI->getReferencedGlobal()->getName()))); @@ -1046,7 +1024,7 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { const BranchInst *BrI = cast(&SI); SmallVector ListOfValues; for (auto Elt : BrI->getArgs()) { - ListOfValues.push_back(S.addTypeRef(Elt->getType().getSwiftRValueType())); + ListOfValues.push_back(S.addTypeRef(Elt->getType().getASTType())); ListOfValues.push_back((unsigned)Elt->getType().getCategory()); ListOfValues.push_back(addValueRef(Elt)); } @@ -1070,12 +1048,12 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { ListOfValues.push_back(BasicBlockMap[CBI->getFalseBB()]); ListOfValues.push_back(CBI->getTrueArgs().size()); for (auto Elt : CBI->getTrueArgs()) { - ListOfValues.push_back(S.addTypeRef(Elt->getType().getSwiftRValueType())); + ListOfValues.push_back(S.addTypeRef(Elt->getType().getASTType())); ListOfValues.push_back((unsigned)Elt->getType().getCategory()); ListOfValues.push_back(addValueRef(Elt)); } for (auto Elt : CBI->getFalseArgs()) { - ListOfValues.push_back(S.addTypeRef(Elt->getType().getSwiftRValueType())); + ListOfValues.push_back(S.addTypeRef(Elt->getType().getASTType())); ListOfValues.push_back((unsigned)Elt->getType().getCategory()); ListOfValues.push_back(addValueRef(Elt)); } @@ -1083,7 +1061,7 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { SILOneTypeValuesLayout::emitRecord(Out, ScratchRecord, SILAbbrCodes[SILOneTypeValuesLayout::Code], (unsigned)SI.getKind(), - S.addTypeRef(CBI->getCondition()->getType().getSwiftRValueType()), + S.addTypeRef(CBI->getCondition()->getType().getASTType()), (unsigned)CBI->getCondition()->getType().getCategory(), ListOfValues); break; @@ -1113,7 +1091,7 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { SILOneTypeValuesLayout::emitRecord(Out, ScratchRecord, SILAbbrCodes[SILOneTypeValuesLayout::Code], (unsigned)SI.getKind(), - S.addTypeRef(SOI->getOperand()->getType().getSwiftRValueType()), + S.addTypeRef(SOI->getOperand()->getType().getASTType()), (unsigned)SOI->getOperand()->getType().getCategory(), ListOfValues); break; @@ -1128,7 +1106,7 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { const SelectEnumInstBase *SOI = cast(&SI); SmallVector ListOfValues; ListOfValues.push_back(addValueRef(SOI->getEnumOperand())); - ListOfValues.push_back(S.addTypeRef(SOI->getType().getSwiftRValueType())); + ListOfValues.push_back(S.addTypeRef(SOI->getType().getASTType())); ListOfValues.push_back((unsigned)SOI->getType().getCategory()); ListOfValues.push_back((unsigned)SOI->hasDefault()); if (SOI->hasDefault()) { @@ -1146,7 +1124,7 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { SILOneTypeValuesLayout::emitRecord(Out, ScratchRecord, SILAbbrCodes[SILOneTypeValuesLayout::Code], (unsigned)SI.getKind(), - S.addTypeRef(SOI->getEnumOperand()->getType().getSwiftRValueType()), + S.addTypeRef(SOI->getEnumOperand()->getType().getASTType()), (unsigned)SOI->getEnumOperand()->getType().getCategory(), ListOfValues); break; @@ -1175,7 +1153,7 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { SILOneTypeValuesLayout::emitRecord(Out, ScratchRecord, SILAbbrCodes[SILOneTypeValuesLayout::Code], (unsigned)SI.getKind(), - S.addTypeRef(SII->getOperand()->getType().getSwiftRValueType()), + S.addTypeRef(SII->getOperand()->getType().getASTType()), (unsigned)SII->getOperand()->getType().getCategory(), ListOfValues); break; @@ -1189,7 +1167,7 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { const SelectValueInst *SVI = cast(&SI); SmallVector ListOfValues; ListOfValues.push_back(addValueRef(SVI->getOperand())); - ListOfValues.push_back(S.addTypeRef(SVI->getType().getSwiftRValueType())); + ListOfValues.push_back(S.addTypeRef(SVI->getType().getASTType())); ListOfValues.push_back((unsigned)SVI->getType().getCategory()); ListOfValues.push_back((unsigned)SVI->hasDefault()); if (SVI->hasDefault()) { @@ -1207,7 +1185,7 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { SILOneTypeValuesLayout::emitRecord(Out, ScratchRecord, SILAbbrCodes[SILOneTypeValuesLayout::Code], (unsigned)SI.getKind(), - S.addTypeRef(SVI->getOperand()->getType().getSwiftRValueType()), + S.addTypeRef(SVI->getOperand()->getType().getASTType()), (unsigned)SVI->getOperand()->getType().getCategory(), ListOfValues); break; @@ -1252,11 +1230,11 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { case SILInstructionKind::UnownedReleaseInst: case SILInstructionKind::IsUniqueInst: case SILInstructionKind::IsUniqueOrPinnedInst: - case SILInstructionKind::IsEscapingClosureInst: case SILInstructionKind::AbortApplyInst: case SILInstructionKind::EndApplyInst: case SILInstructionKind::ReturnInst: case SILInstructionKind::UncheckedOwnershipConversionInst: + case SILInstructionKind::IsEscapingClosureInst: case SILInstructionKind::ThrowInst: { unsigned Attr = 0; if (auto *LI = dyn_cast(&SI)) @@ -1273,6 +1251,8 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { Attr = RCI->isNonAtomic(); else if (auto *UOCI = dyn_cast(&SI)) { Attr = unsigned(SILValue(UOCI).getOwnershipKind()); + } else if (auto *IEC = dyn_cast(&SI)) { + Attr = IEC->getVerificationType(); } writeOneOperandLayout(SI.getKind(), Attr, SI.getOperand(0)); break; @@ -1286,7 +1266,7 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { auto YI = cast(&SI); SmallVector args; for (auto arg: YI->getYieldedValues()) { - args.push_back(S.addTypeRef(arg->getType().getSwiftRValueType())); + args.push_back(S.addTypeRef(arg->getType().getASTType())); args.push_back((unsigned)arg->getType().getCategory()); args.push_back(addValueRef(arg)); } @@ -1305,19 +1285,24 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { unsigned abbrCode = SILAbbrCodes[SILOneOperandLayout::Code]; SILOneOperandLayout::emitRecord(Out, ScratchRecord, abbrCode, (unsigned)SI.getKind(), 0, - S.addTypeRef(FRI->getType().getSwiftRValueType()), + S.addTypeRef(FRI->getType().getASTType()), (unsigned)FRI->getType().getCategory(), addSILFunctionRef(ReferencedFunction)); break; } + case SILInstructionKind::CopyBlockWithoutEscapingInst: case SILInstructionKind::DeallocPartialRefInst: case SILInstructionKind::MarkDependenceInst: case SILInstructionKind::IndexAddrInst: case SILInstructionKind::IndexRawPointerInst: { SILValue operand, operand2; unsigned Attr = 0; - if (SI.getKind() == SILInstructionKind::DeallocPartialRefInst) { + if (SI.getKind() == SILInstructionKind::CopyBlockWithoutEscapingInst) { + const CopyBlockWithoutEscapingInst *C = cast(&SI); + operand = C->getBlock(); + operand2 = C->getClosure(); + } else if (SI.getKind() == SILInstructionKind::DeallocPartialRefInst) { const DeallocPartialRefInst *DPRI = cast(&SI); operand = DPRI->getInstance(); operand2 = DPRI->getMetatype(); @@ -1337,10 +1322,10 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { SILTwoOperandsLayout::emitRecord(Out, ScratchRecord, SILAbbrCodes[SILTwoOperandsLayout::Code], (unsigned)SI.getKind(), Attr, - S.addTypeRef(operand->getType().getSwiftRValueType()), + S.addTypeRef(operand->getType().getASTType()), (unsigned)operand->getType().getCategory(), addValueRef(operand), - S.addTypeRef(operand2->getType().getSwiftRValueType()), + S.addTypeRef(operand2->getType().getASTType()), (unsigned)operand2->getType().getCategory(), addValueRef(operand2)); break; @@ -1350,11 +1335,11 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { SILTailAddrLayout::emitRecord(Out, ScratchRecord, SILAbbrCodes[SILTailAddrLayout::Code], (unsigned)SI.getKind(), - S.addTypeRef(TAI->getBase()->getType().getSwiftRValueType()), + S.addTypeRef(TAI->getBase()->getType().getASTType()), addValueRef(TAI->getBase()), - S.addTypeRef(TAI->getIndex()->getType().getSwiftRValueType()), + S.addTypeRef(TAI->getIndex()->getType().getASTType()), addValueRef(TAI->getIndex()), - S.addTypeRef(TAI->getTailType().getSwiftRValueType())); + S.addTypeRef(TAI->getTailType().getASTType())); break; } case SILInstructionKind::StringLiteralInst: { @@ -1397,7 +1382,7 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { unsigned abbrCode = SILAbbrCodes[SILOneOperandLayout::Code]; SILOneOperandLayout::emitRecord(Out, ScratchRecord, abbrCode, (unsigned)SI.getKind(), 0, - S.addTypeRef(Ty.getSwiftRValueType()), + S.addTypeRef(Ty.getASTType()), (unsigned)Ty.getCategory(), S.addDeclBaseNameRef(Ctx.getIdentifier(Str))); break; @@ -1408,7 +1393,7 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { const MarkFunctionEscapeInst *MFE = cast(&SI); SmallVector ListOfValues; for (auto Elt : MFE->getElements()) { - ListOfValues.push_back(S.addTypeRef(Elt->getType().getSwiftRValueType())); + ListOfValues.push_back(S.addTypeRef(Elt->getType().getASTType())); ListOfValues.push_back((unsigned)Elt->getType().getCategory()); ListOfValues.push_back(addValueRef(Elt)); } @@ -1428,7 +1413,7 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { unsigned abbrCode = SILAbbrCodes[SILOneOperandLayout::Code]; SILOneOperandLayout::emitRecord(Out, ScratchRecord, abbrCode, (unsigned)SI.getKind(), 0, - S.addTypeRef(PI->getType().getSwiftRValueType()), + S.addTypeRef(PI->getType().getASTType()), (unsigned)PI->getType().getCategory(), S.addDeclRef(PI->getProtocol())); break; @@ -1473,10 +1458,13 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { case SILInstructionKind::ObjCExistentialMetatypeToObjectInst: case SILInstructionKind::ProjectBlockStorageInst: { bool guaranteed = false; - if (SI.getKind() == SILInstructionKind::ConvertEscapeToNoEscapeInst) + bool escaped = false; + if (SI.getKind() == SILInstructionKind::ConvertEscapeToNoEscapeInst) { + escaped = cast(SI).isEscapedByUser(); guaranteed = cast(SI).isLifetimeGuaranteed(); + } writeConversionLikeInstruction(cast(&SI), - guaranteed); + guaranteed, escaped); break; } case SILInstructionKind::PointerToAddressInst: { @@ -1493,10 +1481,10 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { SILTwoOperandsLayout::emitRecord(Out, ScratchRecord, SILAbbrCodes[SILTwoOperandsLayout::Code], (unsigned)SI.getKind(), /*attr*/ 0, - S.addTypeRef(RI->getConverted()->getType().getSwiftRValueType()), + S.addTypeRef(RI->getConverted()->getType().getASTType()), (unsigned)RI->getConverted()->getType().getCategory(), addValueRef(RI->getConverted()), - S.addTypeRef(RI->getBitsOperand()->getType().getSwiftRValueType()), + S.addTypeRef(RI->getBitsOperand()->getType().getASTType()), (unsigned)RI->getBitsOperand()->getType().getCategory(), addValueRef(RI->getBitsOperand())); break; @@ -1507,9 +1495,9 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { SILInstCastLayout::emitRecord(Out, ScratchRecord, SILAbbrCodes[SILInstCastLayout::Code], (unsigned)SI.getKind(), /*attr*/ 0, - S.addTypeRef(CI->getType().getSwiftRValueType()), + S.addTypeRef(CI->getType().getASTType()), (unsigned)CI->getType().getCategory(), - S.addTypeRef(CI->getOperand()->getType().getSwiftRValueType()), + S.addTypeRef(CI->getOperand()->getType().getASTType()), (unsigned)CI->getOperand()->getType().getCategory(), addValueRef(CI->getOperand())); break; @@ -1519,14 +1507,14 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { ValueID listOfValues[] = { S.addTypeRef(CI->getSourceType()), addValueRef(CI->getSrc()), - S.addTypeRef(CI->getSrc()->getType().getSwiftRValueType()), + S.addTypeRef(CI->getSrc()->getType().getASTType()), (unsigned)CI->getSrc()->getType().getCategory(), S.addTypeRef(CI->getTargetType()), addValueRef(CI->getDest()) }; SILOneTypeValuesLayout::emitRecord(Out, ScratchRecord, SILAbbrCodes[SILOneTypeValuesLayout::Code], (unsigned)SI.getKind(), - S.addTypeRef(CI->getDest()->getType().getSwiftRValueType()), + S.addTypeRef(CI->getDest()->getType().getASTType()), (unsigned)CI->getDest()->getType().getCategory(), llvm::makeArrayRef(listOfValues)); break; @@ -1537,9 +1525,9 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { Out, ScratchRecord, SILAbbrCodes[SILInstCastLayout::Code], (unsigned)SI.getKind(), /*attr*/ 0, - S.addTypeRef(CI->getType().getSwiftRValueType()), + S.addTypeRef(CI->getType().getASTType()), (unsigned)CI->getType().getCategory(), - S.addTypeRef(CI->getOperand()->getType().getSwiftRValueType()), + S.addTypeRef(CI->getOperand()->getType().getASTType()), (unsigned)CI->getOperand()->getType().getCategory(), addValueRef(CI->getOperand())); break; @@ -1549,14 +1537,14 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { ValueID listOfValues[] = { S.addTypeRef(CI->getSourceType()), addValueRef(CI->getSrc()), - S.addTypeRef(CI->getSrc()->getType().getSwiftRValueType()), + S.addTypeRef(CI->getSrc()->getType().getASTType()), (unsigned)CI->getSrc()->getType().getCategory(), S.addTypeRef(CI->getTargetType()), addValueRef(CI->getDest()) }; SILOneTypeValuesLayout::emitRecord(Out, ScratchRecord, SILAbbrCodes[SILOneTypeValuesLayout::Code], (unsigned)SI.getKind(), - S.addTypeRef(CI->getDest()->getType().getSwiftRValueType()), + S.addTypeRef(CI->getDest()->getType().getASTType()), (unsigned)CI->getDest()->getType().getCategory(), llvm::makeArrayRef(listOfValues)); break; @@ -1571,10 +1559,10 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { SILTwoOperandsLayout::emitRecord( Out, ScratchRecord, abbrCode, (unsigned)SI.getKind(), Attr, - S.addTypeRef(BorrowedValue->getType().getSwiftRValueType()), + S.addTypeRef(BorrowedValue->getType().getASTType()), (unsigned)BorrowedValue->getType().getCategory(), addValueRef(BorrowedValue), - S.addTypeRef(OriginalValue->getType().getSwiftRValueType()), + S.addTypeRef(OriginalValue->getType().getASTType()), (unsigned)OriginalValue->getType().getCategory(), addValueRef(OriginalValue)); break; @@ -1585,12 +1573,13 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { auto *BAI = cast(&SI); unsigned attr = unsigned(BAI->getAccessKind()) + (unsigned(BAI->getEnforcement()) << 2) - + (BAI->hasNoNestedConflict() << 4); + + (BAI->hasNoNestedConflict() << 4) + + (BAI->isFromBuiltin() << 5); SILValue operand = BAI->getOperand(); SILOneOperandExtraAttributeLayout::emitRecord( Out, ScratchRecord, abbrCode, (unsigned)SI.getKind(), attr, - S.addTypeRef(operand->getType().getSwiftRValueType()), + S.addTypeRef(operand->getType().getASTType()), (unsigned)operand->getType().getCategory(), addValueRef(operand)); break; @@ -1604,7 +1593,7 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { SILOneOperandLayout::emitRecord( Out, ScratchRecord, abbrCode, (unsigned)SI.getKind(), attr, - S.addTypeRef(operand->getType().getSwiftRValueType()), + S.addTypeRef(operand->getType().getASTType()), (unsigned)operand->getType().getCategory(), addValueRef(operand)); break; @@ -1615,16 +1604,17 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { auto *BAI = cast(&SI); unsigned attr = unsigned(BAI->getAccessKind()) + (unsigned(BAI->getEnforcement()) << 2) - + (unsigned(BAI->hasNoNestedConflict()) << 4); + + (unsigned(BAI->hasNoNestedConflict()) << 4) + + (unsigned(BAI->isFromBuiltin()) << 5); SILValue source = BAI->getSource(); SILValue buffer = BAI->getBuffer(); SILTwoOperandsExtraAttributeLayout::emitRecord( Out, ScratchRecord, abbrCode, (unsigned)SI.getKind(), attr, - S.addTypeRef(source->getType().getSwiftRValueType()), + S.addTypeRef(source->getType().getASTType()), (unsigned)source->getType().getCategory(), addValueRef(source), - S.addTypeRef(buffer->getType().getSwiftRValueType()), + S.addTypeRef(buffer->getType().getASTType()), (unsigned)buffer->getType().getCategory(), addValueRef(buffer)); break; @@ -1634,12 +1624,13 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { unsigned abbrCode = SILAbbrCodes[SILOneOperandExtraAttributeLayout::Code]; auto *EAI = cast(&SI); unsigned attr = unsigned(EAI->isAborting()) - + (unsigned(EAI->getEnforcement()) << 1); + + (unsigned(EAI->getEnforcement()) << 1) + + (unsigned(EAI->isFromBuiltin()) << 3); SILValue operand = EAI->getOperand(); SILOneOperandExtraAttributeLayout::emitRecord( Out, ScratchRecord, abbrCode, (unsigned)SI.getKind(), attr, - S.addTypeRef(operand->getType().getSwiftRValueType()), + S.addTypeRef(operand->getType().getASTType()), (unsigned)operand->getType().getCategory(), addValueRef(operand)); break; @@ -1683,7 +1674,7 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { unsigned abbrCode = SILAbbrCodes[SILOneValueOneOperandLayout::Code]; SILOneValueOneOperandLayout::emitRecord(Out, ScratchRecord, abbrCode, (unsigned)SI.getKind(), Attr, addValueRef(value), - S.addTypeRef(operand->getType().getSwiftRValueType()), + S.addTypeRef(operand->getType().getASTType()), (unsigned)operand->getType().getCategory(), addValueRef(operand)); break; @@ -1695,11 +1686,11 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { SILType boundType = BI->getBoundType(); SmallVector ListOfValues; ListOfValues.push_back(S.addTypeRef( - baseOperand->getType().getSwiftRValueType())); + baseOperand->getType().getASTType())); ListOfValues.push_back((unsigned)baseOperand->getType().getCategory()); ListOfValues.push_back(addValueRef(baseOperand)); ListOfValues.push_back(S.addTypeRef( - indexOperand->getType().getSwiftRValueType())); + indexOperand->getType().getASTType())); ListOfValues.push_back((unsigned)indexOperand->getType().getCategory()); ListOfValues.push_back(addValueRef(indexOperand)); @@ -1708,7 +1699,7 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { ScratchRecord, SILAbbrCodes[SILOneTypeValuesLayout::Code], (unsigned)SI.getKind(), - S.addTypeRef(boundType.getSwiftRValueType()), + S.addTypeRef(boundType.getASTType()), (unsigned)boundType.getCategory(), ListOfValues); break; @@ -1758,7 +1749,7 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { SILOneValueOneOperandLayout::emitRecord(Out, ScratchRecord, SILAbbrCodes[SILOneValueOneOperandLayout::Code], (unsigned)SI.getKind(), 0, S.addDeclRef(tDecl), - S.addTypeRef(operand->getType().getSwiftRValueType()), + S.addTypeRef(operand->getType().getASTType()), (unsigned)operand->getType().getCategory(), addValueRef(operand)); break; @@ -1776,7 +1767,7 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { const StructInst *StrI = cast(&SI); SmallVector ListOfValues; for (auto Elt : StrI->getElements()) { - ListOfValues.push_back(S.addTypeRef(Elt->getType().getSwiftRValueType())); + ListOfValues.push_back(S.addTypeRef(Elt->getType().getASTType())); ListOfValues.push_back((unsigned)Elt->getType().getCategory()); ListOfValues.push_back(addValueRef(Elt)); } @@ -1784,7 +1775,7 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { SILOneTypeValuesLayout::emitRecord(Out, ScratchRecord, SILAbbrCodes[SILOneTypeValuesLayout::Code], (unsigned)SI.getKind(), - S.addTypeRef(StrI->getType().getSwiftRValueType()), + S.addTypeRef(StrI->getType().getASTType()), (unsigned)StrI->getType().getCategory(), ListOfValues); break; } @@ -1809,7 +1800,7 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { SILAbbrCodes[SILOneTypeOneOperandLayout::Code], (unsigned)SI.getKind(), 0, FieldNo, 0, - S.addTypeRef(operand->getType().getSwiftRValueType()), + S.addTypeRef(operand->getType().getASTType()), (unsigned)operand->getType().getCategory(), addValueRef(operand)); break; @@ -1826,7 +1817,7 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { unsigned abbrCode = SILAbbrCodes[SILOneTypeValuesLayout::Code]; SILOneTypeValuesLayout::emitRecord(Out, ScratchRecord, abbrCode, (unsigned)SI.getKind(), - S.addTypeRef(TI->getType().getSwiftRValueType()), + S.addTypeRef(TI->getType().getASTType()), (unsigned)TI->getType().getCategory(), ListOfValues); break; @@ -1836,13 +1827,13 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { // (DeclID + hasOperand), and an operand. const EnumInst *UI = cast(&SI); TypeID OperandTy = UI->hasOperand() ? - S.addTypeRef(UI->getOperand()->getType().getSwiftRValueType()) : TypeID(); + S.addTypeRef(UI->getOperand()->getType().getASTType()) : TypeID(); unsigned OperandTyCategory = UI->hasOperand() ? (unsigned)UI->getOperand()->getType().getCategory() : 0; SILTwoOperandsLayout::emitRecord(Out, ScratchRecord, SILAbbrCodes[SILTwoOperandsLayout::Code], (unsigned)SI.getKind(), UI->hasOperand(), - S.addTypeRef(UI->getType().getSwiftRValueType()), + S.addTypeRef(UI->getType().getASTType()), (unsigned)UI->getType().getCategory(), S.addDeclRef(UI->getElement()), OperandTy, OperandTyCategory, @@ -1868,7 +1859,7 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { SILInstWitnessMethodLayout::emitRecord( Out, ScratchRecord, SILAbbrCodes[SILInstWitnessMethodLayout::Code], S.addTypeRef(Ty), 0, 0, - S.addTypeRef(Ty2.getSwiftRValueType()), (unsigned)Ty2.getCategory(), + S.addTypeRef(Ty2.getASTType()), (unsigned)Ty2.getCategory(), OperandTy, OperandTyCategory, OperandValueId, ListOfValues); S.writeConformance(WMI->getConformance(), SILAbbrCodes); @@ -1886,7 +1877,7 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { SILOneTypeValuesLayout::emitRecord(Out, ScratchRecord, SILAbbrCodes[SILOneTypeValuesLayout::Code], (unsigned)SI.getKind(), - S.addTypeRef(Ty.getSwiftRValueType()), + S.addTypeRef(Ty.getASTType()), (unsigned)Ty.getCategory(), ListOfValues); break; } @@ -1901,7 +1892,7 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { SILOneTypeValuesLayout::emitRecord(Out, ScratchRecord, SILAbbrCodes[SILOneTypeValuesLayout::Code], (unsigned)SI.getKind(), - S.addTypeRef(Ty.getSwiftRValueType()), + S.addTypeRef(Ty.getASTType()), (unsigned)Ty.getCategory(), ListOfValues); break; } @@ -1916,7 +1907,7 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { SILOneTypeValuesLayout::emitRecord(Out, ScratchRecord, SILAbbrCodes[SILOneTypeValuesLayout::Code], (unsigned)SI.getKind(), - S.addTypeRef(Ty.getSwiftRValueType()), + S.addTypeRef(Ty.getASTType()), (unsigned)Ty.getCategory(), ListOfValues); break; } @@ -1931,7 +1922,7 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { SILOneTypeValuesLayout::emitRecord(Out, ScratchRecord, SILAbbrCodes[SILOneTypeValuesLayout::Code], (unsigned)SI.getKind(), - S.addTypeRef(Ty.getSwiftRValueType()), + S.addTypeRef(Ty.getASTType()), (unsigned)Ty.getCategory(), ListOfValues); break; } @@ -1947,7 +1938,7 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { SILOneTypeValuesLayout::emitRecord(Out, ScratchRecord, SILAbbrCodes[SILOneTypeValuesLayout::Code], (unsigned)SI.getKind(), - S.addTypeRef(DMB->getOperand()->getType().getSwiftRValueType()), + S.addTypeRef(DMB->getOperand()->getType().getASTType()), (unsigned)DMB->getOperand()->getType().getCategory(), ListOfValues); break; } @@ -1959,14 +1950,14 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { ListOfValues.push_back(CBI->isExact()), ListOfValues.push_back(addValueRef(CBI->getOperand())); ListOfValues.push_back( - S.addTypeRef(CBI->getOperand()->getType().getSwiftRValueType())); + S.addTypeRef(CBI->getOperand()->getType().getASTType())); ListOfValues.push_back((unsigned)CBI->getOperand()->getType().getCategory()); ListOfValues.push_back(BasicBlockMap[CBI->getSuccessBB()]); ListOfValues.push_back(BasicBlockMap[CBI->getFailureBB()]); SILOneTypeValuesLayout::emitRecord(Out, ScratchRecord, SILAbbrCodes[SILOneTypeValuesLayout::Code], (unsigned)SI.getKind(), - S.addTypeRef(CBI->getCastType().getSwiftRValueType()), + S.addTypeRef(CBI->getCastType().getASTType()), (unsigned)CBI->getCastType().getCategory(), ListOfValues); break; @@ -1979,7 +1970,7 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { SmallVector ListOfValues; ListOfValues.push_back(addValueRef(CBI->getOperand())); ListOfValues.push_back( - S.addTypeRef(CBI->getOperand()->getType().getSwiftRValueType())); + S.addTypeRef(CBI->getOperand()->getType().getASTType())); ListOfValues.push_back( (unsigned)CBI->getOperand()->getType().getCategory()); ListOfValues.push_back(BasicBlockMap[CBI->getSuccessBB()]); @@ -1988,7 +1979,7 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { SILOneTypeValuesLayout::emitRecord( Out, ScratchRecord, SILAbbrCodes[SILOneTypeValuesLayout::Code], (unsigned)SI.getKind(), - S.addTypeRef(CBI->getCastType().getSwiftRValueType()), + S.addTypeRef(CBI->getCastType().getASTType()), (unsigned)CBI->getCastType().getCategory(), ListOfValues); break; } @@ -2001,7 +1992,7 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { toStableCastConsumptionKind(CBI->getConsumptionKind()), S.addTypeRef(CBI->getSourceType()), addValueRef(CBI->getSrc()), - S.addTypeRef(CBI->getSrc()->getType().getSwiftRValueType()), + S.addTypeRef(CBI->getSrc()->getType().getASTType()), (unsigned)CBI->getSrc()->getType().getCategory(), S.addTypeRef(CBI->getTargetType()), addValueRef(CBI->getDest()), @@ -2010,7 +2001,7 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { }; SILOneTypeValuesLayout::emitRecord(Out, ScratchRecord, SILAbbrCodes[SILOneTypeValuesLayout::Code], (unsigned)SI.getKind(), - S.addTypeRef(CBI->getDest()->getType().getSwiftRValueType()), + S.addTypeRef(CBI->getDest()->getType().getASTType()), (unsigned)CBI->getDest()->getType().getCategory(), llvm::makeArrayRef(listOfValues)); break; @@ -2020,21 +2011,20 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { SmallVector ListOfValues; ListOfValues.push_back(addValueRef(IBSHI->getBlockStorage())); ListOfValues.push_back( - S.addTypeRef(IBSHI->getBlockStorage()->getType().getSwiftRValueType())); + S.addTypeRef(IBSHI->getBlockStorage()->getType().getASTType())); // Always an address, don't need to save category ListOfValues.push_back(addValueRef(IBSHI->getInvokeFunction())); ListOfValues.push_back( - S.addTypeRef(IBSHI->getInvokeFunction()->getType().getSwiftRValueType())); + S.addTypeRef(IBSHI->getInvokeFunction()->getType().getASTType())); // Always a value, don't need to save category - ListOfValues.push_back(IBSHI->getSubstitutions().size()); + ListOfValues.push_back(S.addSubstitutionMapRef(IBSHI->getSubstitutions())); SILOneTypeValuesLayout::emitRecord(Out, ScratchRecord, SILAbbrCodes[SILOneTypeValuesLayout::Code], (unsigned)SI.getKind(), - S.addTypeRef(IBSHI->getType().getSwiftRValueType()), + S.addTypeRef(IBSHI->getType().getASTType()), (unsigned)IBSHI->getType().getCategory(), ListOfValues); - S.writeSubstitutions(IBSHI->getSubstitutions(), SILAbbrCodes); break; } @@ -2047,8 +2037,8 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { ListOfValues.push_back(S.addTypeRef(pattern->getValueType())); ListOfValues.push_back(pattern->getComponents().size()); ListOfValues.push_back(pattern->getNumOperands()); - ListOfValues.push_back(KPI->getSubstitutions().size()); - + ListOfValues.push_back(S.addSubstitutionMapRef(KPI->getSubstitutions())); + ListOfValues.push_back( S.addDeclBaseNameRef(Ctx.getIdentifier(pattern->getObjCString()))); @@ -2061,9 +2051,9 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { } else { ListOfValues.push_back(0); } - - SmallVector serializeAfter; - + + SmallVector serializeAfter; + for (auto &component : pattern->getComponents()) { writeKeyPathPatternComponent(component, ListOfValues, serializeAfter); @@ -2072,27 +2062,19 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { for (auto &operand : KPI->getAllOperands()) { auto value = operand.get(); ListOfValues.push_back(addValueRef(value)); - ListOfValues.push_back(S.addTypeRef(value->getType().getSwiftRValueType())); + ListOfValues.push_back(S.addTypeRef(value->getType().getASTType())); ListOfValues.push_back((unsigned)value->getType().getCategory()); } SILOneTypeValuesLayout::emitRecord(Out, ScratchRecord, SILAbbrCodes[SILOneTypeValuesLayout::Code], (unsigned)SI.getKind(), - S.addTypeRef(KPI->getType().getSwiftRValueType()), + S.addTypeRef(KPI->getType().getASTType()), (unsigned)KPI->getType().getCategory(), ListOfValues); - for (auto &confOrSub : serializeAfter) { - switch (confOrSub.Kind) { - case ConformanceOrSubstitution::ProtocolConformance: - S.writeConformance(confOrSub.Conformance, SILAbbrCodes); - break; - case ConformanceOrSubstitution::SubstitutionList: - S.writeSubstitutions(confOrSub.Substitutions, SILAbbrCodes); - break; - } + for (const auto conf : serializeAfter) { + S.writeConformance(conf, SILAbbrCodes); } S.writeGenericRequirements(reqts, SILAbbrCodes); - S.writeSubstitutions(KPI->getSubstitutions(), SILAbbrCodes); break; } @@ -2181,7 +2163,7 @@ void SILSerializer::writeIndexTables() { void SILSerializer::writeSILGlobalVar(const SILGlobalVariable &g) { GlobalVarList[Ctx.getIdentifier(g.getName())] = NextGlobalVarID++; GlobalVarOffset.push_back(Out.GetCurrentBitNo()); - TypeID TyID = S.addTypeRef(g.getLoweredType().getSwiftRValueType()); + TypeID TyID = S.addTypeRef(g.getLoweredType().getASTType()); DeclID dID = S.addDeclRef(g.getDecl()); SILGlobalVarLayout::emitRecord(Out, ScratchRecord, SILAbbrCodes[SILGlobalVarLayout::Code], @@ -2229,7 +2211,7 @@ void SILSerializer::writeSILProperty(const SILProperty &prop) { PropertyOffset.push_back(Out.GetCurrentBitNo()); SmallVector componentValues; - SmallVector serializeAfter; + SmallVector serializeAfter; writeKeyPathPatternComponent(prop.getComponent(), componentValues, serializeAfter); @@ -2241,15 +2223,8 @@ void SILSerializer::writeSILProperty(const SILProperty &prop) { prop.isSerialized(), componentValues); - for (auto &confOrSub : serializeAfter) { - switch (confOrSub.Kind) { - case ConformanceOrSubstitution::ProtocolConformance: - S.writeConformance(confOrSub.Conformance, SILAbbrCodes); - break; - case ConformanceOrSubstitution::SubstitutionList: - S.writeSubstitutions(confOrSub.Substitutions, SILAbbrCodes); - break; - } + for (const auto conf : serializeAfter) { + S.writeConformance(conf, SILAbbrCodes); } } @@ -2430,9 +2405,6 @@ void SILSerializer::writeSILBlock(const SILModule *SILMod) { // Register the abbreviation codes so these layouts can exist in both // decl blocks and sil blocks. - // We have to make sure BOUND_GENERIC_SUBSTITUTION does not overlap with - // SIL-specific records. - registerSILAbbr(); registerSILAbbr(); registerSILAbbr(); registerSILAbbr(); @@ -2454,21 +2426,23 @@ void SILSerializer::writeSILBlock(const SILModule *SILMod) { assert(assocDC && "cannot serialize SIL without an associated DeclContext"); for (const SILVTable &vt : SILMod->getVTables()) { if ((ShouldSerializeAll || vt.isSerialized()) && - vt.getClass()->isChildContextOf(assocDC)) + SILMod->shouldSerializeEntitiesAssociatedWithDeclContext(vt.getClass())) writeSILVTable(vt); } // Write out property descriptors. for (const SILProperty &prop : SILMod->getPropertyList()) { if ((ShouldSerializeAll || prop.isSerialized()) && - prop.getDecl()->getInnermostDeclContext()->isChildContextOf(assocDC)) + SILMod->shouldSerializeEntitiesAssociatedWithDeclContext( + prop.getDecl()->getInnermostDeclContext())) writeSILProperty(prop); } // Write out fragile WitnessTables. for (const SILWitnessTable &wt : SILMod->getWitnessTables()) { if ((ShouldSerializeAll || wt.isSerialized()) && - wt.getConformance()->getDeclContext()->isChildContextOf(assocDC)) + SILMod->shouldSerializeEntitiesAssociatedWithDeclContext( + wt.getConformance()->getDeclContext())) writeSILWitnessTable(wt); } @@ -2476,7 +2450,8 @@ void SILSerializer::writeSILBlock(const SILModule *SILMod) { for (const SILDefaultWitnessTable &wt : SILMod->getDefaultWitnessTables()) { // FIXME: Don't need to serialize private and internal default witness // tables. - if (wt.getProtocol()->getDeclContext()->isChildContextOf(assocDC)) + if (SILMod->shouldSerializeEntitiesAssociatedWithDeclContext( + wt.getProtocol())) writeSILDefaultWitnessTable(wt); } diff --git a/lib/Serialization/SerializedModuleLoader.cpp b/lib/Serialization/SerializedModuleLoader.cpp index eac4d4ce801ae..d7f8729b454cb 100644 --- a/lib/Serialization/SerializedModuleLoader.cpp +++ b/lib/Serialization/SerializedModuleLoader.cpp @@ -15,6 +15,7 @@ #include "swift/Strings.h" #include "swift/AST/ASTContext.h" #include "swift/AST/DiagnosticsSema.h" +#include "swift/Basic/Defer.h" #include "swift/Basic/STLExtras.h" #include "swift/Basic/SourceManager.h" #include "swift/Basic/Version.h" @@ -28,7 +29,7 @@ using namespace swift; namespace { -typedef std::pair AccessPathElem; +using AccessPathElem = std::pair; } // end unnamed namespace // Defined out-of-line so that we can see ~ModuleFile. @@ -200,10 +201,7 @@ FileUnit *SerializedModuleLoader::loadAST( isFramework, loadedModuleFile, &extendedInfo); if (loadInfo.status == serialization::Status::Valid) { - // In LLDB always use the default resilience strategy, so IRGen can query - // the size of resilient types. - if (!Ctx.LangOpts.DebuggerSupport) - M.setResilienceStrategy(extendedInfo.getResilienceStrategy()); + M.setResilienceStrategy(extendedInfo.getResilienceStrategy()); // We've loaded the file. Now try to bring it into the AST. auto fileUnit = new (Ctx) SerializedASTFile(M, *loadedModuleFile, @@ -319,6 +317,25 @@ FileUnit *SerializedModuleLoader::loadAST( break; } + case serialization::Status::CircularDependency: { + auto circularDependencyIter = + llvm::find_if(loadedModuleFile->getDependencies(), + [](const ModuleFile::Dependency &next) { + return !next.Import.second->hasResolvedImports(); + }); + assert(circularDependencyIter != loadedModuleFile->getDependencies().end() + && "circular dependency reported, but no module with unresolved " + "imports found"); + + // FIXME: We should include the path of the circularity as well, but that's + // hard because we're discovering this /while/ resolving imports, which + // means the problematic modules haven't been recorded yet. + Ctx.Diags.diagnose(*diagLoc, diag::serialization_circular_dependency, + circularDependencyIter->getPrettyPrintedPath(), + M.getName()); + break; + } + case serialization::Status::MissingShadowedModule: { Ctx.Diags.diagnose(*diagLoc, diag::serialization_missing_shadowed_module, M.getName()); @@ -437,6 +454,7 @@ ModuleDecl *SerializedModuleLoader::loadModule(SourceLoc importLoc, auto M = ModuleDecl::create(moduleID.first, Ctx); Ctx.LoadedModules[moduleID.first] = M; + SWIFT_DEFER { M->setHasResolvedImports(); }; if (!loadAST(*M, moduleID.second, std::move(moduleInputBuffer), std::move(moduleDocInputBuffer), isFramework)) { diff --git a/lib/Syntax/RawSyntax.cpp b/lib/Syntax/RawSyntax.cpp index 0613fecadcabb..99555942d0260 100644 --- a/lib/Syntax/RawSyntax.cpp +++ b/lib/Syntax/RawSyntax.cpp @@ -76,6 +76,14 @@ RawSyntax::RawSyntax(SyntaxKind Kind, ArrayRef> Layout, Bits.ManualMemory = unsigned(ManualMemory); Bits.NumChildren = Layout.size(); + // Compute the text length + Bits.TextLength = 0; + for (const auto ChildNode : Layout) { + if (ChildNode && !ChildNode->isMissing()) { + Bits.TextLength += ChildNode->getTextLength(); + } + } + // Initialize layout data. std::uninitialized_copy(Layout.begin(), Layout.end(), getTrailingObjects>()); @@ -189,6 +197,24 @@ RawSyntax::accumulateAbsolutePosition(AbsolutePosition &Pos) const { return Ret; } +bool RawSyntax::accumulateLeadingTrivia(AbsolutePosition &Pos) const { + if (isToken()) { + if (!isMissing()) { + for (auto &Leader: getLeadingTrivia()) + Leader.accumulateAbsolutePosition(Pos); + return true; + } + } else { + for (auto &Child: getLayout()) { + if (!Child) + continue; + if (Child->accumulateLeadingTrivia(Pos)) + return true; + } + } + return false; +} + void RawSyntax::print(llvm::raw_ostream &OS, SyntaxPrintOptions Opts) const { if (isMissing()) return; @@ -219,6 +245,7 @@ void RawSyntax::print(llvm::raw_ostream &OS, SyntaxPrintOptions Opts) const { void RawSyntax::dump() const { dump(llvm::errs(), /*Indent*/ 0); + llvm::errs() << '\n'; } void RawSyntax::dump(llvm::raw_ostream &OS, unsigned Indent) const { diff --git a/lib/Syntax/Status.md b/lib/Syntax/Status.md index 3d014a52af3db..389c327c9881e 100644 --- a/lib/Syntax/Status.md +++ b/lib/Syntax/Status.md @@ -43,8 +43,6 @@ * IsExpr * AsExpr * ArrowExpr - -### Not-started (UnknownExpr): * ObjCSelectorExpr ## Declaration @@ -65,17 +63,10 @@ * SubscriptDecl * ConstructorDecl * DestructorDecl - -### In-progress (UnknownDecl): - -### Not-started (UnknownDecl): + * EnumDecl * EnumCaseDecl + * OperatorDecl * PrecedenceGroupDecl - * InfixOperatorDecl - * PrefixOperatorDecl - * PostfixOperatorDecl - * EnumDecl - * EnumElementDecl ## Statement ### Done: diff --git a/lib/Syntax/Syntax.cpp b/lib/Syntax/Syntax.cpp index 0c53cd3d3738a..914c0a199f854 100644 --- a/lib/Syntax/Syntax.cpp +++ b/lib/Syntax/Syntax.cpp @@ -96,59 +96,3 @@ llvm::Optional Syntax::getChild(const size_t N) const { return llvm::None; return Syntax {Root, ChildData.get()}; } - -AbsolutePosition Syntax::getAbsolutePosition() const { - assert(getRoot().is() && - "Absolute position can only be calculated for nodes which has " - "SourceFile root"); - AbsolutePosition Pos; - - /// This visitor collects all of the nodes before this node to calculate its - /// offset from the begenning of the file. - class Visitor: public SyntaxVisitor { - AbsolutePosition &Pos; - const SyntaxData *Target; - bool Found = false; - - public: - Visitor(AbsolutePosition &Pos, const SyntaxData *Target) - : Pos(Pos), Target(Target) {} - ~Visitor() { assert(Found); } - void visitPre(Syntax Node) override { - // Check if this node is the target; - Found |= Node.getDataPointer() == Target; - } - void visit(TokenSyntax Node) override { - // Ignore missing node and ignore the nodes after this node. - if (Found || Node.isMissing()) - return; - // Collect all the offsets. - Node.getRaw()->accumulateAbsolutePosition(Pos); - } - } Calculator(Pos, getDataPointer()); - - /// This visitor visit the first token node of this node to accumulate its - /// leading trivia. Therefore, the calculated absolute location will point - /// to the actual token start. - class FirstTokenFinder: public SyntaxVisitor { - AbsolutePosition &Pos; - bool Found = false; - - public: - FirstTokenFinder(AbsolutePosition &Pos): Pos(Pos) {} - void visit(TokenSyntax Node) override { - if (Found || Node.isMissing()) - return; - Found = true; - for (auto &Leader : Node.getRaw()->getLeadingTrivia()) - Leader.accumulateAbsolutePosition(Pos); - } - } FTFinder(Pos); - - // Visit the root to get all the nodes before this node. - getRoot().accept(Calculator); - - // Visit this node to accumulate the leading trivia of its first token. - const_cast(this)->accept(FTFinder); - return Pos; -} diff --git a/lib/Syntax/SyntaxData.cpp b/lib/Syntax/SyntaxData.cpp index 480b8190df922..e74d7383f9fdb 100644 --- a/lib/Syntax/SyntaxData.cpp +++ b/lib/Syntax/SyntaxData.cpp @@ -49,4 +49,79 @@ bool SyntaxData::isUnknown() const { void SyntaxData::dump(llvm::raw_ostream &OS) const { Raw->dump(OS, 0); + OS << '\n'; +} + +void SyntaxData::dump() const { dump(llvm::errs()); } + +RC SyntaxData::getPreviousNode() const { + if (size_t N = getIndexInParent()) { + if (hasParent()) { + for (size_t I = N - 1; ; I--) { + if (auto C = getParent()->getChild(I)) { + return C; + } + if (I == 0) + break; + } + } + } + return hasParent() ? Parent->getPreviousNode() : nullptr; +} + +RC SyntaxData::getNextNode() const { + if (hasParent()) { + for (size_t I = getIndexInParent() + 1, N = Parent->getNumChildren(); + I != N; I++) { + if (auto C = getParent()->getChild(I)) + return C; + } + return Parent->getNextNode(); + } + return nullptr; +} + +RC SyntaxData::getFirstToken() const { + for (size_t I = 0, E = getNumChildren(); I < E; ++I) { + if (auto Child = getChild(I)) { + if (!Child->getRaw()->isMissing()) { + return Child->getFirstToken(); + } + } + } + + // Get a reference counted version of this + assert(getRaw()->isToken() && "Leaf node that is no token?"); + assert(hasParent() && "The syntax tree should not conisist only of the root"); + return getParent()->getChild(getIndexInParent()); +} + +AbsolutePosition SyntaxData::getAbsolutePositionWithLeadingTrivia() const { + if (PositionCache.hasValue()) + return *PositionCache; + if (auto P = getPreviousNode()) { + auto Result = P->getAbsolutePositionWithLeadingTrivia(); + P->getRaw()->accumulateAbsolutePosition(Result); + // FIXME: avoid using const_cast. + const_cast(this)->PositionCache = Result; + } else { + const_cast(this)->PositionCache = AbsolutePosition(); + } + return *PositionCache; +} + +AbsolutePosition SyntaxData::getAbsolutePosition() const { + auto Result = getAbsolutePositionWithLeadingTrivia(); + getRaw()->accumulateLeadingTrivia(Result); + return Result; +} + +AbsolutePosition SyntaxData::getAbsoluteEndPosition() const { + if (auto N = getNextNode()) { + return N->getAbsolutePositionWithLeadingTrivia(); + } else { + auto Result = getAbsolutePositionWithLeadingTrivia(); + getRaw()->accumulateAbsolutePosition(Result); + return Result; + } } diff --git a/lib/Syntax/SyntaxKind.cpp.gyb b/lib/Syntax/SyntaxKind.cpp.gyb index d4c2965e51635..2b16108ea2067 100644 --- a/lib/Syntax/SyntaxKind.cpp.gyb +++ b/lib/Syntax/SyntaxKind.cpp.gyb @@ -19,6 +19,7 @@ #include "swift/Syntax/SyntaxKind.h" #include "swift/Syntax/TokenKinds.h" +#include "llvm/Support/raw_ostream.h" namespace swift { @@ -43,6 +44,19 @@ StringRef getTokenText(tok kind) { return text; } +bool parserShallOmitWhenNoChildren(syntax::SyntaxKind Kind) { + switch(Kind) { +% for node in SYNTAX_NODES: +% if node.shall_be_omitted_when_empty(): + case syntax::SyntaxKind::${node.syntax_kind}: +% end +% end + return true; + default: + return false; + } +} + namespace syntax { void dumpSyntaxKind(llvm::raw_ostream &os, const SyntaxKind kind) { @@ -123,3 +137,21 @@ SyntaxKind getUnknownKind(SyntaxKind Kind) { } } // end namespace syntax } // end namespace swift + +llvm::raw_ostream &llvm::operator<<(llvm::raw_ostream &OS, + swift::syntax::SyntaxKind Kind) { + switch (Kind) { +% for node in SYNTAX_NODES: + case swift::syntax::SyntaxKind::${node.syntax_kind}: + OS << "${node.syntax_kind}"; + break; +% end + case swift::syntax::SyntaxKind::Token: + OS << "TokenSyntax"; + break; + case swift::syntax::SyntaxKind::Unknown: + OS << "UnknownSyntax"; + break; + } + return OS; +} diff --git a/lib/TBDGen/TBDGen.cpp b/lib/TBDGen/TBDGen.cpp index 2b55d9ba63321..0e2aec9be215a 100644 --- a/lib/TBDGen/TBDGen.cpp +++ b/lib/TBDGen/TBDGen.cpp @@ -92,34 +92,44 @@ void TBDGenVisitor::addConformances(DeclContext *DC) { auto conformanceIsFixed = SILWitnessTable::conformanceIsSerialized( normalConformance); - auto addSymbolIfNecessary = [&](SILDeclRef declRef) { - auto witnessLinkage = declRef.getLinkage(ForDefinition); + auto addSymbolIfNecessary = [&](ValueDecl *requirementDecl, + ValueDecl *witnessDecl) { + auto witnessLinkage = SILDeclRef(witnessDecl).getLinkage(ForDefinition); if (conformanceIsFixed && fixmeWitnessHasLinkageThatNeedsToBePublic(witnessLinkage)) { Mangle::ASTMangler Mangler; - addSymbol(Mangler.mangleWitnessThunk(normalConformance, - declRef.getDecl())); + addSymbol( + Mangler.mangleWitnessThunk(normalConformance, requirementDecl)); } }; - normalConformance->forEachValueWitness(nullptr, [&](ValueDecl *valueReq, - Witness witness) { - if (isa(valueReq)) { - addSymbolIfNecessary(SILDeclRef(valueReq)); - } else if (auto *storage = dyn_cast(valueReq)) { - if (auto *getter = storage->getGetter()) - addSymbolIfNecessary(SILDeclRef(getter)); - if (auto *setter = storage->getGetter()) - addSymbolIfNecessary(SILDeclRef(setter)); - if (auto *materializeForSet = storage->getMaterializeForSetFunc()) - addSymbolIfNecessary(SILDeclRef(materializeForSet)); - } - }); + normalConformance->forEachValueWitness( + nullptr, [&](ValueDecl *valueReq, Witness witness) { + auto witnessDecl = witness.getDecl(); + if (isa(valueReq)) { + addSymbolIfNecessary(valueReq, witnessDecl); + } else if (auto *storage = dyn_cast(valueReq)) { + auto witnessStorage = cast(witnessDecl); + if (auto *getter = storage->getGetter()) + addSymbolIfNecessary(getter, witnessStorage->getGetter()); + if (auto *setter = storage->getSetter()) + addSymbolIfNecessary(setter, witnessStorage->getSetter()); + if (auto *materializeForSet = storage->getMaterializeForSetFunc()) + addSymbolIfNecessary(materializeForSet, + witnessStorage->getMaterializeForSetFunc()); + } + }); } } void TBDGenVisitor::visitAbstractFunctionDecl(AbstractFunctionDecl *AFD) { addSymbol(SILDeclRef(AFD)); + if (AFD->getAttrs().hasAttribute()) { + // A @_cdecl("...") function has an extra symbol, with the name from the + // attribute. + addSymbol(SILDeclRef(AFD).asForeign()); + } + if (!SwiftModule->getASTContext().isSwiftVersion3()) return; @@ -140,6 +150,22 @@ void TBDGenVisitor::visitAbstractFunctionDecl(AbstractFunctionDecl *AFD) { } } +void TBDGenVisitor::visitAccessorDecl(AccessorDecl *AD) { + // Do nothing: accessors are always nested within the storage decl, but + // sometimes appear outside it too. To avoid double-walking them, we + // explicitly visit them as members of the storage and ignore them when we + // visit them as part of the main walk, here. +} + +void TBDGenVisitor::visitAbstractStorageDecl(AbstractStorageDecl *ASD) { + // Explicitly look at each accessor here: see visitAccessorDecl. + SmallVector accessors; + ASD->getAllAccessorFunctions(accessors); + for (auto accessor : accessors) { + visitAbstractFunctionDecl(cast(accessor)); + } +} + void TBDGenVisitor::visitVarDecl(VarDecl *VD) { // statically/globally stored variables have some special handling. if (VD->hasStorage() && isGlobalOrStaticVar(VD)) { @@ -152,6 +178,8 @@ void TBDGenVisitor::visitVarDecl(VarDecl *VD) { if (!FileHasEntryPoint || VD->isStatic()) addSymbol(SILDeclRef(VD, SILDeclRef::Kind::GlobalAccessor)); } + + visitAbstractStorageDecl(VD); } void TBDGenVisitor::visitNominalTypeDecl(NominalTypeDecl *NTD) { @@ -216,12 +244,26 @@ void TBDGenVisitor::visitClassDecl(ClassDecl *CD) { visitNominalTypeDecl(CD); - // The below symbols are only emitted if the class is resilient. - if (!CD->isResilient(SwiftModule, ResilienceExpansion::Minimal)) + auto hasResilientAncestor = + CD->isResilient(SwiftModule, ResilienceExpansion::Minimal); + auto ancestor = CD->getSuperclassDecl(); + while (ancestor && !hasResilientAncestor) { + hasResilientAncestor |= + ancestor->isResilient(SwiftModule, ResilienceExpansion::Maximal); + ancestor = ancestor->getSuperclassDecl(); + } + + // Types with resilient superclasses have some extra symbols. + if (!hasResilientAncestor) return; addSymbol(LinkEntity::forClassMetadataBaseOffset(CD)); + // And classes that are themselves resilient (not just a superclass) have even + // more. + if (!CD->isResilient()) + return; + // Emit dispatch thunks for every new vtable entry. struct VTableVisitor : public SILVTableVisitor { TBDGenVisitor &TBD; @@ -271,7 +313,7 @@ void TBDGenVisitor::visitProtocolDecl(ProtocolDecl *PD) { if (!PD->isObjC()) { addSymbol(LinkEntity::forProtocolDescriptor(PD)); - if (PD->isResilient(SwiftModule, ResilienceExpansion::Minimal)) { + if (PD->isResilient()) { for (auto *member : PD->getMembers()) { if (auto *funcDecl = dyn_cast(member)) { addDispatchThunk(SILDeclRef(funcDecl)); @@ -299,18 +341,41 @@ void TBDGenVisitor::visitProtocolDecl(ProtocolDecl *PD) { #endif } +void TBDGenVisitor::visitEnumDecl(EnumDecl *ED) { + visitNominalTypeDecl(ED); + + if (!ED->isResilient()) + return; + + // Emit resilient tags. + for (auto *elt : ED->getAllElements()) { + auto entity = LinkEntity::forEnumCase(elt); + addSymbol(entity); + } +} + +void TBDGenVisitor::addFirstFileSymbols() { + if (!Opts.ModuleLinkName.empty()) { + SmallString<32> buf; + addSymbol(irgen::encodeForceLoadSymbolName(buf, Opts.ModuleLinkName)); + } +} + static void enumeratePublicSymbolsAndWrite(ModuleDecl *M, FileUnit *singleFile, StringSet &symbols, - bool hasMultipleIGMs, llvm::raw_ostream *os, - StringRef installName) { + TBDGenOptions &opts) { auto isWholeModule = singleFile == nullptr; const auto &target = M->getASTContext().LangOpts.Target; - UniversalLinkageInfo linkInfo(target, hasMultipleIGMs, isWholeModule); + UniversalLinkageInfo linkInfo(target, opts.HasMultipleIGMs, isWholeModule); - TBDGenVisitor visitor(symbols, target, linkInfo, M, installName); + TBDGenVisitor visitor(symbols, target, linkInfo, M, opts); auto visitFile = [&](FileUnit *file) { + if (file == M->getFiles()[0]) { + visitor.addFirstFileSymbols(); + } + SmallVector decls; file->getTopLevelDecls(decls); @@ -343,18 +408,16 @@ static void enumeratePublicSymbolsAndWrite(ModuleDecl *M, FileUnit *singleFile, } void swift::enumeratePublicSymbols(FileUnit *file, StringSet &symbols, - bool hasMultipleIGMs) { + TBDGenOptions &opts) { enumeratePublicSymbolsAndWrite(file->getParentModule(), file, symbols, - hasMultipleIGMs, nullptr, StringRef()); + nullptr, opts); } void swift::enumeratePublicSymbols(ModuleDecl *M, StringSet &symbols, - bool hasMultipleIGMs) { - enumeratePublicSymbolsAndWrite(M, nullptr, symbols, hasMultipleIGMs, nullptr, - StringRef()); + TBDGenOptions &opts) { + enumeratePublicSymbolsAndWrite(M, nullptr, symbols, nullptr, opts); } void swift::writeTBDFile(ModuleDecl *M, llvm::raw_ostream &os, - bool hasMultipleIGMs, StringRef installName) { + TBDGenOptions &opts) { StringSet symbols; - enumeratePublicSymbolsAndWrite(M, nullptr, symbols, hasMultipleIGMs, &os, - installName); + enumeratePublicSymbolsAndWrite(M, nullptr, symbols, &os, opts); } diff --git a/lib/TBDGen/TBDGenVisitor.h b/lib/TBDGen/TBDGenVisitor.h index 5c57bb54f3049..741e9c4621ff9 100644 --- a/lib/TBDGen/TBDGenVisitor.h +++ b/lib/TBDGen/TBDGenVisitor.h @@ -33,6 +33,9 @@ using namespace swift::irgen; using StringSet = llvm::StringSet<>; namespace swift { + +struct TBDGenOptions; + namespace tbdgen { class TBDGenVisitor : public ASTVisitor { @@ -41,7 +44,7 @@ class TBDGenVisitor : public ASTVisitor { const llvm::Triple &Triple; const UniversalLinkageInfo &UniversalLinkInfo; ModuleDecl *SwiftModule; - StringRef InstallName; + TBDGenOptions &Opts; private: bool FileHasEntryPoint = false; @@ -73,9 +76,9 @@ class TBDGenVisitor : public ASTVisitor { public: TBDGenVisitor(StringSet &symbols, const llvm::Triple &triple, const UniversalLinkageInfo &universalLinkInfo, - ModuleDecl *swiftModule, StringRef installName) + ModuleDecl *swiftModule, TBDGenOptions &opts) : Symbols(symbols), Triple(triple), UniversalLinkInfo(universalLinkInfo), - SwiftModule(swiftModule), InstallName(installName) {} + SwiftModule(swiftModule), Opts(opts) {} void setFileHasEntryPoint(bool hasEntryPoint) { FileHasEntryPoint = hasEntryPoint; @@ -84,10 +87,15 @@ class TBDGenVisitor : public ASTVisitor { addSymbol("main"); } + /// \brief Adds the global symbols associated with the first file. + void addFirstFileSymbols(); + void visitPatternBindingDecl(PatternBindingDecl *PBD); void visitAbstractFunctionDecl(AbstractFunctionDecl *AFD); + void visitAccessorDecl(AccessorDecl *AD); + void visitNominalTypeDecl(NominalTypeDecl *NTD); void visitClassDecl(ClassDecl *CD); @@ -98,8 +106,12 @@ class TBDGenVisitor : public ASTVisitor { void visitProtocolDecl(ProtocolDecl *PD); + void visitAbstractStorageDecl(AbstractStorageDecl *ASD); + void visitVarDecl(VarDecl *VD); + void visitEnumDecl(EnumDecl *ED); + void visitDecl(Decl *D) {} }; } // end namespace tbdgen diff --git a/stdlib/CMakeLists.txt b/stdlib/CMakeLists.txt index aff9379d0049a..7dcf8753b8ce9 100644 --- a/stdlib/CMakeLists.txt +++ b/stdlib/CMakeLists.txt @@ -58,6 +58,15 @@ foreach(SDK ${SWIFT_SDKS}) add_custom_target("swift-stdlib-${SWIFT_SDK_${SDK}_LIB_SUBDIR}-sib") add_custom_target("swift-stdlib-${SWIFT_SDK_${SDK}_LIB_SUBDIR}-sibopt") add_custom_target("swift-stdlib-${SWIFT_SDK_${SDK}_LIB_SUBDIR}-sibgen") + + set_property(TARGET + "swift-stdlib-${SWIFT_SDK_${SDK}_LIB_SUBDIR}" + "swift-test-stdlib-${SWIFT_SDK_${SDK}_LIB_SUBDIR}" + "swift-stdlib-${SWIFT_SDK_${SDK}_LIB_SUBDIR}-sib" + "swift-stdlib-${SWIFT_SDK_${SDK}_LIB_SUBDIR}-sibopt" + "swift-stdlib-${SWIFT_SDK_${SDK}_LIB_SUBDIR}-sibgen" + PROPERTY FOLDER "Swift libraries/Aggregate") + foreach(ARCH ${SWIFT_SDK_${SDK}_ARCHITECTURES}) set(VARIANT_SUFFIX "-${SWIFT_SDK_${SDK}_LIB_SUBDIR}-${ARCH}") add_custom_target("swift-stdlib${VARIANT_SUFFIX}") @@ -66,6 +75,14 @@ foreach(SDK ${SWIFT_SDKS}) add_custom_target("swift-stdlib${VARIANT_SUFFIX}-sibgen") add_custom_target("swift-test-stdlib${VARIANT_SUFFIX}") + set_property(TARGET + "swift-stdlib${VARIANT_SUFFIX}" + "swift-stdlib${VARIANT_SUFFIX}-sib" + "swift-stdlib${VARIANT_SUFFIX}-sibopt" + "swift-stdlib${VARIANT_SUFFIX}-sibgen" + "swift-test-stdlib${VARIANT_SUFFIX}" + PROPERTY FOLDER "Swift libraries/Aggregate") + add_dependencies(swift-stdlib-all "swift-stdlib${VARIANT_SUFFIX}") add_dependencies(swift-stdlib-sib-all "swift-stdlib${VARIANT_SUFFIX}-sib") add_dependencies(swift-stdlib-sibopt-all "swift-stdlib${VARIANT_SUFFIX}-sibopt") @@ -95,6 +112,18 @@ add_custom_target(swift-stdlib-sibgen add_custom_target(swift-test-stdlib ALL DEPENDS "swift-test-stdlib${SWIFT_PRIMARY_VARIANT_SUFFIX}") +set_property(TARGET + swift-stdlib-all + swift-stdlib-sib-all + swift-stdlib-sibopt-all + swift-stdlib-sibgen-all + swift-stdlib + swift-stdlib-sib + swift-stdlib-sibopt + swift-stdlib-sibgen + swift-test-stdlib + PROPERTY FOLDER "Swift libraries/Aggregate") + add_subdirectory(public) add_subdirectory(private) diff --git a/stdlib/private/StdlibCollectionUnittest/CMakeLists.txt b/stdlib/private/StdlibCollectionUnittest/CMakeLists.txt index 2776ce4efc70f..553036abca9a4 100644 --- a/stdlib/private/StdlibCollectionUnittest/CMakeLists.txt +++ b/stdlib/private/StdlibCollectionUnittest/CMakeLists.txt @@ -7,19 +7,23 @@ add_swift_library(swiftStdlibCollectionUnittest ${SWIFT_STDLIB_LIBRARY_BUILD_TYP # filename. StdlibCollectionUnittest.swift - CheckCollectionInstance.swift.gyb - CheckCollectionType.swift.gyb - CheckMutableCollectionType.swift.gyb + CheckCollectionInstance.swift + CheckCollectionType.swift + CheckMutableCollectionType.swift CheckRangeReplaceableCollectionType.swift CheckRangeReplaceableSliceType.swift - CheckSequenceInstance.swift.gyb + CheckSequenceInstance.swift CheckSequenceType.swift - LoggingWrappers.swift.gyb - MinimalCollections.swift.gyb + LoggingWrappers.swift + MinimalCollections.swift RangeSelection.swift ../../public/core/WriteBackMutableSlice.swift SWIFT_MODULE_DEPENDS StdlibUnittest + SWIFT_MODULE_DEPENDS_LINUX Glibc + SWIFT_MODULE_DEPENDS_FREEBSD Glibc + SWIFT_MODULE_DEPENDS_CYGWIN Glibc + SWIFT_MODULE_DEPENDS_HAIKU Glibc SWIFT_COMPILE_FLAGS ${swift_stdlib_unittest_compile_flags} TARGET_SDKS ALL_POSIX_PLATFORMS INSTALL_IN_COMPONENT stdlib-experimental) diff --git a/stdlib/private/StdlibCollectionUnittest/CheckCollectionInstance.swift b/stdlib/private/StdlibCollectionUnittest/CheckCollectionInstance.swift new file mode 100644 index 0000000000000..b6c287647629a --- /dev/null +++ b/stdlib/private/StdlibCollectionUnittest/CheckCollectionInstance.swift @@ -0,0 +1,1917 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +import StdlibUnittest + +public struct CollectionMisuseResiliencyChecks { + public enum FailureKind { + case none + case trap + } + + public var creatingOutOfBoundsIndicesBehavior: FailureKind = .trap + public var subscriptOnOutOfBoundsIndicesBehavior: FailureKind = .trap + public var subscriptRangeOnOutOfBoundsRangesBehavior: FailureKind = .trap + + public static var all: CollectionMisuseResiliencyChecks { + return CollectionMisuseResiliencyChecks() + } + + public static var none: CollectionMisuseResiliencyChecks { + return CollectionMisuseResiliencyChecks( + creatingOutOfBoundsIndicesBehavior: .none, + subscriptOnOutOfBoundsIndicesBehavior: .none, + subscriptRangeOnOutOfBoundsRangesBehavior: .none) + } +} + + +/// Test that the elements of `instances` satisfy +/// the semantic +/// requirements of `Collection`, using `equalityOracle` to +/// generate equality expectations from pairs of positions in +/// `instances`. +/// +/// - Precondition: `endIndex` is reachable from all +/// elements of `instances`. +public func checkIncrementable( + _ instances: Instances, + of baseCollection: BaseCollection, + equalityOracle: (Instances.Index, Instances.Index) -> Bool, + endIndex: Instances.Element, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line +) where + Instances : Collection, + BaseCollection : Collection, + Instances.Element == BaseCollection.Index { + + checkEquatable(instances, oracle: equalityOracle, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) + for i in instances { + if i != endIndex { + let next = baseCollection.index(after: i) + // index(after:) gets us a new index value + expectNotEqual(i, next, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) + + // Which is the same as if we apply formIndex(after:) + var j = i + baseCollection.formIndex(after: &j) + expectEqual(j, next, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) + } + } +} + +/// Test that the elements of `instances` satisfy +/// some of the semantic +/// requirements of `BidirectionalCollection`, using `equalityOracle` to +/// generate equality expectations from pairs of positions in +/// `instances`. +/// +/// - Precondition: all +/// elements of `instances` are reachable from `startIndex`. +public func checkDecrementable( + _ instances: Instances, + of baseCollection: BaseCollection, + equalityOracle: (Instances.Index, Instances.Index) -> Bool, + startIndex: Instances.Element, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line +) where + Instances : Collection, + BaseCollection : BidirectionalCollection, + Instances.Element == BaseCollection.Index { + + checkEquatable(instances, oracle: equalityOracle, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) + for i in instances { + if i != startIndex { + let next = baseCollection.index(before: i) + // index(before:) gets us a new index value + expectNotEqual(i, next, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) + + // Which is the same as if we apply formIndex(before:) + var j = i + baseCollection.formIndex(before: &j) + expectEqual(j, next, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) + } + } +} + +internal func _checkIncrementalAdvance( + _ instances: Instances, + of baseCollection : BaseCollection, + equalityOracle: (Instances.Index, Instances.Index) -> Bool, + limit: Instances.Element, + sign: Int, // 1 or -1 + next: (Instances.Element) -> Instances.Element, + + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line +) where + Instances : Collection, + BaseCollection : Collection, + Instances.Element == BaseCollection.Index { + for i in instances { + let d: Int = sign > 0 ? + baseCollection.distance(from: i, to: limit) : + -baseCollection.distance(from: limit, to: i) + + var offset: Int = 0 + for _ in 0...Int64(d * sign) { + let j = baseCollection.index(i, offsetBy: offset) + let k = baseCollection.index(i, offsetBy: offset + sign, limitedBy: limit) ?? limit + let jAtLimit = offset == d + if jAtLimit { + expectEqual(limit, j, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) + } + expectEqual(jAtLimit ? j : next(j), k, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) + offset += sign + } + } +} + +/// Test that the elements of `instances` satisfy the semantic requirements of +/// index for `Collection`, using `equalityOracle` to generate equality +/// expectations from pairs of positions in `instances`. +/// +/// - Precondition: `endIndex` is reachable from all elements of +/// `instances` +public func checkForwardIndex( + _ instances: Instances, + of baseCollection: BaseCollection, + equalityOracle: (Instances.Index, Instances.Index) -> Bool, + endIndex: Instances.Element, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line +) where + Instances : Collection, + BaseCollection : Collection, + Instances.Element == BaseCollection.Index { + + checkIncrementable(instances, of: baseCollection, + equalityOracle: equalityOracle, endIndex: endIndex, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) + + _checkIncrementalAdvance(instances, of: baseCollection, + equalityOracle: equalityOracle, limit: endIndex, + sign: 1, next: { baseCollection.index(after: $0) }, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) +} + +/// Test that the elements of `instances` satisfy the semantic requirements of +/// index for `BidirectionalCollection`, using `equalityOracle` to generate +/// equality expectations from pairs of positions in `instances`. +/// +/// - Precondition: +/// - all elements of `instances` are reachable from `startIndex`. +/// - `endIndex` is reachable from all elements of `instances`. +public func checkBidirectionalIndex( + _ instances: Instances, + of baseCollection: BaseCollection, + equalityOracle: (Instances.Index, Instances.Index) -> Bool, + startIndex: Instances.Element, + endIndex: Instances.Element, + + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line +) where + Instances: Collection, + BaseCollection : BidirectionalCollection, + Instances.Element == BaseCollection.Index { + + checkForwardIndex(instances, of: baseCollection, + equalityOracle: equalityOracle, endIndex: endIndex) + + checkDecrementable(instances, of: baseCollection, + equalityOracle: equalityOracle, startIndex: startIndex, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) + + _checkIncrementalAdvance(instances, of: baseCollection, + equalityOracle: equalityOracle, limit: startIndex, + sign: -1, next: { baseCollection.index(before: $0) }, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) +} + +/// Test that the elements of `instances` satisfy the semantic requirements of +/// index for `RandomAccessCollection`, using `advanceOracle` and +/// 'distanceOracle' to generate expectations about the results of +/// `advanced(by:)` and `distance(to:)` from pairs of positions in `instances` +/// and `distances`. +/// +/// - Precondition: +/// - all elements of `instances` are reachable from `startIndex`. +/// - `endIndex` is reachable from all elements of `instances`. +public func checkRandomAccessIndex( + _ instances: Instances, distances: Distances, + of baseCollection: BaseCollection, + distanceOracle: + (Instances.Index, Instances.Index) -> Distances.Element, + advanceOracle: + (Instances.Index, Distances.Index) -> Instances.Element, + startIndex: Instances.Iterator.Element, + endIndex: Instances.Iterator.Element, + + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line +) where + Instances : Collection, + Distances : Collection, + BaseCollection : RandomAccessCollection, + Instances.Element == BaseCollection.Index, + Distances.Element == Int { + + checkBidirectionalIndex(instances, of: baseCollection, + equalityOracle: { distanceOracle($0, $1) == 0 }, + startIndex: startIndex, endIndex: endIndex, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) + + checkAdvancesAndDistances( + instances, distances: distances, + of: baseCollection, + distanceOracle: distanceOracle, + advanceOracle: advanceOracle, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) +} + +// Copies what checkStrideable is doing, but instead of calling +// advanced(by:) and distance(to:) on an Strideable's, +// calls corresponding methods on a base collection. +public func checkAdvancesAndDistances( + _ instances: Instances, distances: Distances, + of baseCollection: BaseCollection, + distanceOracle: + (Instances.Index, Instances.Index) -> Distances.Element, + advanceOracle: + (Instances.Index, Distances.Index) -> Instances.Element, + + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line +) where + Instances : Collection, + Distances : Collection, + BaseCollection : Collection, + Instances.Element == BaseCollection.Index, + Distances.Element == Int { + + checkComparable( + instances, + oracle: { + let d = distanceOracle($1, $0); + return d < 0 ? .lt : d == 0 ? .eq : .gt + }, + message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) + + for i in instances.indices { + let x = instances[i] + expectEqual(x, baseCollection.index(x, offsetBy: 0)) + + for j in distances.indices { + let y = distances[j] + expectEqual(advanceOracle(i, j), baseCollection.index(x, offsetBy: y)) + } + + for j in instances.indices { + let y = instances[j] + expectEqual(distanceOracle(i, j), baseCollection.distance(from: x, to: y)) + } + } +} + +// Generate two overloads: one for Array (which will get +// picked up when the caller passes a literal), and another that +// accepts any appropriate Collection type. + +// Top-level check for Collection instances. Alias for checkForwardCollection. +// Checks all slices: O(n^2). +public func checkCollection( + _ expected: Expected, + _ collection: C, + + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line, + resiliencyChecks: CollectionMisuseResiliencyChecks = .all, + sameValue: (Expected.Element, Expected.Element) -> Bool +) where C.Element == Expected.Element { + + checkForwardCollection(expected, collection, message(), + stackTrace: stackTrace, showFrame: showFrame, file: file, line: line, + resiliencyChecks: resiliencyChecks, + sameValue: sameValue) +} + + +// Calls checkForwardCollection with default `sameValue`. +public func checkForwardCollection< + Expected: Collection, C : Collection +>( + _ expected: Expected, _ collection: C, + + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line, + resiliencyChecks: CollectionMisuseResiliencyChecks = .all +) where + C.Element == Expected.Element, + Expected.Element : Equatable { + + checkForwardCollection( + expected, collection, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), + resiliencyChecks: resiliencyChecks) { $0 == $1 } +} + +// Top-Level check for all Collection semantics on a single +// instance. This constrains SubSequence associated types in order to check +// slice semantics. +// Checks all slices: O(n^2). +public func checkForwardCollection< + Expected: Collection, C : Collection +>( + _ expected: Expected, _ collection: C, + + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line, + resiliencyChecks: CollectionMisuseResiliencyChecks = .all, + sameValue: (Expected.Element, Expected.Element) -> Bool +) where + C.Element == Expected.Element { + + checkOneLevelOfForwardCollection(expected, collection, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), + resiliencyChecks: resiliencyChecks, sameValue: sameValue) + + // Avoid validation of all possible (n^2) slices on large collection. + // Test cases should call checkOneLevelOfForwardCollection instead. + expectLT(expected.count, 30) + + _checkSliceableWithForwardIndex(expected, collection, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), + resiliencyChecks: resiliencyChecks, sameValue: sameValue) +} + +// Helper for checkForwardCollection. Check that instance of `C`, +// `collection`, upholds the semantics of `Collection`, +// non-recursively. This does not check subsequences. It may be called for each +// subsequence without combinatorial explosion. Also, since recursive protocol +// constraints are not supported, our second level of checks cannot depend on the +// associated type properties of SubSequence. +// +// Checks all slices: O(n^2). +public func checkOneLevelOfForwardCollection< + Expected: Collection, C : Collection +>( + _ expected: Expected, _ collection: C, + + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line, + resiliencyChecks: CollectionMisuseResiliencyChecks = .all, + sameValue: (Expected.Element, Expected.Element) -> Bool +) where C.Element == Expected.Element { + + // A `Collection` is a multi-pass `Sequence`. + for _ in 0..<3 { + checkSequence( + expected, collection, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), + resiliencyChecks: resiliencyChecks, sameValue: sameValue) + } + + //===------------------------------------------------------------------===// + // Check Index semantics + //===------------------------------------------------------------------===// + + let succ = { collection.index(after: $0) } + // Advances up to 1 positions without passing endIndex. Don't use + // advanced(by: n) to do this because it's under test here. + let next = { $0 == collection.endIndex ? $0 : succ($0) } + + // advances up to 5 positions without passing endIndex. Picking a + // small constant to avoid complexity explosion on large input + // collections. + let next5 = { next(next(next(next(next($0))))) } + + let partWay0 = next5(collection.startIndex) + let partWay1 = next5(partWay0) + + + let instances = _allIndices(into: collection, + in: collection.startIndex..= 2 { + for i in 0..= 2` + + do { + var allIndices2: [C.Index] = [] + for i in collection.indices { + allIndices2.append(i) + } + + expectEqualSequence( + allIndices, allIndices2, "iteration should not invalidate indices", + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) + + expectEqualSequence( + expectedArray, allIndices.map { collection[$0] }, + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), sameValue: sameValue) + expectEqualSequence( + expectedArray, allIndices2.map { collection[$0] }, + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), sameValue: sameValue) + } + } // end of `for _ in 0..<3` + + // FIXME: more checks for bidirectional and random access collections. +} + +// Helper for checkForwardCollection to check Slices. +// +// Checks all slices: O(n^2). +internal func _checkSliceableWithForwardIndex< +Expected: Collection, S : Collection +>( + _ expected: Expected, _ sliceable: S, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line, + resiliencyChecks: CollectionMisuseResiliencyChecks = .all, + sameValue: (Expected.Element, Expected.Element) -> Bool +) where + S.Element == Expected.Element { + + let expectedArray = Array(expected) + + let succ = { sliceable.index(after: $0) } + + var start = sliceable.startIndex + for startNumericIndex in 0...expectedArray.count { + var end = start + for endNumericIndex in startNumericIndex...expectedArray.count { + let expectedSlice = expectedArray[startNumericIndex..( + _ expected: Expected, _ collection: C, + + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line, + resiliencyChecks: CollectionMisuseResiliencyChecks = .all +) where + C.Element == Expected.Element, + Expected.Element : Equatable { + + checkBidirectionalCollection( + expected, collection, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), + resiliencyChecks: resiliencyChecks) { $0 == $1 } +} + +// Top-Level check for all BidirectionalCollection semantics on a single +// instance. This constrains SubSequence associated types in order to check +// slice semantics. +// Checks all slices: O(n^2). +public func checkBidirectionalCollection< + Expected: Collection, C : BidirectionalCollection +>( + _ expected: Expected, _ collection: C, + + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line, + resiliencyChecks: CollectionMisuseResiliencyChecks = .all, + sameValue: (Expected.Element, Expected.Element) -> Bool +) where + C.Element == Expected.Element { + + checkOneLevelOfBidirectionalCollection(expected, collection, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), + resiliencyChecks: resiliencyChecks, sameValue: sameValue) + + // Avoid validation of all possible (n^2) slices on large collection. + // Test cases should call checkOneLevelOfBidirectionalCollection instead. + expectLT(expected.count, 30) + + _checkSliceableWithBidirectionalIndex(expected, collection, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), + resiliencyChecks: resiliencyChecks, sameValue: sameValue) +} + +// Helper for checkBidirectionalCollection. Check that instance of `C`, +// `collection`, upholds the semantics of `BidirectionalCollection`, +// non-recursively. This does not check subsequences. It may be called for each +// subsequence without combinatorial explosion. Also, since recursive protocol +// constraints are not supported, our second level of checks cannot depend on the +// associated type properties of SubSequence. +// +// Checks all slices: O(n^2). +public func checkOneLevelOfBidirectionalCollection< + Expected: Collection, C : BidirectionalCollection +>( + _ expected: Expected, _ collection: C, + + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line, + resiliencyChecks: CollectionMisuseResiliencyChecks = .all, + sameValue: (Expected.Element, Expected.Element) -> Bool +) where C.Element == Expected.Element { + + // A `Collection` is a multi-pass `Sequence`. + for _ in 0..<3 { + checkSequence( + expected, collection, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), + resiliencyChecks: resiliencyChecks, sameValue: sameValue) + } + + //===------------------------------------------------------------------===// + // Check Index semantics + //===------------------------------------------------------------------===// + + let succ = { collection.index(after: $0) } + let pred = { collection.index(before: $0) } + // Advances up to 1 positions without passing endIndex. Don't use + // advanced(by: n) to do this because it's under test here. + let next = { $0 == collection.endIndex ? $0 : succ($0) } + + // advances up to 5 positions without passing endIndex. Picking a + // small constant to avoid complexity explosion on large input + // collections. + let next5 = { next(next(next(next(next($0))))) } + + let partWay0 = next5(collection.startIndex) + let partWay1 = next5(partWay0) + + + let instances = _allIndices(into: collection, in: partWay0..= 2 { + for i in 0..= 2` + + do { + var allIndices2: [C.Index] = [] + for i in collection.indices { + allIndices2.append(i) + } + + expectEqualSequence( + allIndices, allIndices2, "iteration should not invalidate indices", + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) + + expectEqualSequence( + expectedArray, allIndices.map { collection[$0] }, + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), sameValue: sameValue) + expectEqualSequence( + expectedArray, allIndices2.map { collection[$0] }, + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), sameValue: sameValue) + } + } // end of `for _ in 0..<3` + + // FIXME: more checks for bidirectional and random access collections. +} + +// Helper for checkBidirectionalCollection to check Slices. +// +// Checks all slices: O(n^2). +internal func _checkSliceableWithBidirectionalIndex< +Expected: Collection, S : BidirectionalCollection +>( + _ expected: Expected, _ sliceable: S, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line, + resiliencyChecks: CollectionMisuseResiliencyChecks = .all, + sameValue: (Expected.Element, Expected.Element) -> Bool +) where + S.Element == Expected.Element { + + let expectedArray = Array(expected) + + let succ = { sliceable.index(after: $0) } + let pred = { sliceable.index(before: $0) } + + var start = sliceable.startIndex + for startNumericIndex in 0...expectedArray.count { + if start != sliceable.endIndex { + start = succ(start) + start = pred(start) + start = succ(start) + start = pred(start) + } + var end = start + for endNumericIndex in startNumericIndex...expectedArray.count { + if end != sliceable.endIndex { + end = succ(end) + end = pred(end) + end = succ(end) + end = pred(end) + } + let expectedSlice = expectedArray[startNumericIndex..( + _ expected: Expected, _ collection: C, + + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line, + resiliencyChecks: CollectionMisuseResiliencyChecks = .all +) where + C.Element == Expected.Element, + Expected.Element : Equatable { + + checkRandomAccessCollection( + expected, collection, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), + resiliencyChecks: resiliencyChecks) { $0 == $1 } +} + +// Top-Level check for all RandomAccessCollection semantics on a single +// instance. This constrains SubSequence associated types in order to check +// slice semantics. +// Checks all slices: O(n^2). +public func checkRandomAccessCollection< + Expected: Collection, C : RandomAccessCollection +>( + _ expected: Expected, _ collection: C, + + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line, + resiliencyChecks: CollectionMisuseResiliencyChecks = .all, + sameValue: (Expected.Element, Expected.Element) -> Bool +) where + C.Element == Expected.Element { + + checkOneLevelOfRandomAccessCollection(expected, collection, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), + resiliencyChecks: resiliencyChecks, sameValue: sameValue) + + // Avoid validation of all possible (n^2) slices on large collection. + // Test cases should call checkOneLevelOfRandomAccessCollection instead. + expectLT(expected.count, 30) + + _checkSliceableWithRandomAccessIndex(expected, collection, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), + resiliencyChecks: resiliencyChecks, sameValue: sameValue) +} + +// Helper for checkRandomAccessCollection. Check that instance of `C`, +// `collection`, upholds the semantics of `RandomAccessCollection`, +// non-recursively. This does not check subsequences. It may be called for each +// subsequence without combinatorial explosion. Also, since recursive protocol +// constraints are not supported, our second level of checks cannot depend on the +// associated type properties of SubSequence. +// +// Checks all slices: O(n^2). +public func checkOneLevelOfRandomAccessCollection< + Expected: Collection, C : RandomAccessCollection +>( + _ expected: Expected, _ collection: C, + + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line, + resiliencyChecks: CollectionMisuseResiliencyChecks = .all, + sameValue: (Expected.Element, Expected.Element) -> Bool +) where C.Element == Expected.Element { + + // A `Collection` is a multi-pass `Sequence`. + for _ in 0..<3 { + checkSequence( + expected, collection, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), + resiliencyChecks: resiliencyChecks, sameValue: sameValue) + } + + //===------------------------------------------------------------------===// + // Check Index semantics + //===------------------------------------------------------------------===// + + let succ = { collection.index(after: $0) } + let pred = { collection.index(before: $0) } + // Advances up to 1 positions without passing endIndex. Don't use + // advanced(by: n) to do this because it's under test here. + let next = { $0 == collection.endIndex ? $0 : succ($0) } + + // advances up to 5 positions without passing endIndex. Picking a + // small constant to avoid complexity explosion on large input + // collections. + let next5 = { next(next(next(next(next($0))))) } + + let partWay0 = next5(collection.startIndex) + let partWay1 = next5(partWay0) + + typealias Distance = Int + + let count: Distance = collection.count + let offset0 = min(5, count) + let offset1 = min(10, count) + let offset2 = min(15, count) + + let distanceCandidates: [Distance] = [ + -11, -7, -5, -3, -2, -1, 0, 1, 2, 3, 5, 7, 11] + + let distances = distanceCandidates.filter { (x: Distance) -> Bool in + x + offset0 >= 0 && x + offset1 <= count + } + + func nextN(_ n: Distance, _ i: C.Index) -> C.Index { + return collection.index(i, offsetBy: n) + } + + let instances = _allIndices(into: collection, in: partWay0..= 2 { + for i in 0..= 2` + + do { + var allIndices2: [C.Index] = [] + for i in collection.indices { + allIndices2.append(i) + } + + expectEqualSequence( + allIndices, allIndices2, "iteration should not invalidate indices", + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) + + expectEqualSequence( + expectedArray, allIndices.map { collection[$0] }, + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), sameValue: sameValue) + expectEqualSequence( + expectedArray, allIndices2.map { collection[$0] }, + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), sameValue: sameValue) + } + } // end of `for _ in 0..<3` + + // FIXME: more checks for bidirectional and random access collections. +} + +// Helper for checkRandomAccessCollection to check Slices. +// +// Checks all slices: O(n^2). +internal func _checkSliceableWithRandomAccessIndex< +Expected: Collection, S : RandomAccessCollection +>( + _ expected: Expected, _ sliceable: S, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line, + resiliencyChecks: CollectionMisuseResiliencyChecks = .all, + sameValue: (Expected.Element, Expected.Element) -> Bool +) where + S.Element == Expected.Element { + + let expectedArray = Array(expected) + + let succ = { sliceable.index(after: $0) } + let pred = { sliceable.index(before: $0) } + + var start = sliceable.startIndex + for startNumericIndex in 0...expectedArray.count { + if start != sliceable.endIndex { + start = succ(start) + start = pred(start) + start = succ(start) + start = pred(start) + } + var end = start + for endNumericIndex in startNumericIndex...expectedArray.count { + if end != sliceable.endIndex { + end = succ(end) + end = pred(end) + end = succ(end) + end = pred(end) + } + let expectedSlice = expectedArray[startNumericIndex..( + _ expected: Array, + _ collection: C, + + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line, + resiliencyChecks: CollectionMisuseResiliencyChecks = .all, + sameValue: (Element, Element) -> Bool +) where C.Element == Element { + + checkForwardCollection(expected, collection, message(), + stackTrace: stackTrace, showFrame: showFrame, file: file, line: line, + resiliencyChecks: resiliencyChecks, + sameValue: sameValue) +} + + +// Calls checkForwardCollection with default `sameValue`. +public func checkForwardCollection< + Element, C : Collection +>( + _ expected: Array, _ collection: C, + + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line, + resiliencyChecks: CollectionMisuseResiliencyChecks = .all +) where + C.Element == Element, + Element : Equatable { + + checkForwardCollection( + expected, collection, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), + resiliencyChecks: resiliencyChecks) { $0 == $1 } +} + +// Top-Level check for all Collection semantics on a single +// instance. This constrains SubSequence associated types in order to check +// slice semantics. +// Checks all slices: O(n^2). +public func checkForwardCollection< + Element, C : Collection +>( + _ expected: Array, _ collection: C, + + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line, + resiliencyChecks: CollectionMisuseResiliencyChecks = .all, + sameValue: (Element, Element) -> Bool +) where + C.Element == Element { + + checkOneLevelOfForwardCollection(expected, collection, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), + resiliencyChecks: resiliencyChecks, sameValue: sameValue) + + // Avoid validation of all possible (n^2) slices on large collection. + // Test cases should call checkOneLevelOfForwardCollection instead. + expectLT(expected.count, 30) + + _checkSliceableWithForwardIndex(expected, collection, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), + resiliencyChecks: resiliencyChecks, sameValue: sameValue) +} + +// Helper for checkForwardCollection. Check that instance of `C`, +// `collection`, upholds the semantics of `Collection`, +// non-recursively. This does not check subsequences. It may be called for each +// subsequence without combinatorial explosion. Also, since recursive protocol +// constraints are not supported, our second level of checks cannot depend on the +// associated type properties of SubSequence. +// +// Checks all slices: O(n^2). +public func checkOneLevelOfForwardCollection< + Element, C : Collection +>( + _ expected: Array, _ collection: C, + + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line, + resiliencyChecks: CollectionMisuseResiliencyChecks = .all, + sameValue: (Element, Element) -> Bool +) where C.Element == Element { + + // A `Collection` is a multi-pass `Sequence`. + for _ in 0..<3 { + checkSequence( + expected, collection, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), + resiliencyChecks: resiliencyChecks, sameValue: sameValue) + } + + //===------------------------------------------------------------------===// + // Check Index semantics + //===------------------------------------------------------------------===// + + let succ = { collection.index(after: $0) } + // Advances up to 1 positions without passing endIndex. Don't use + // advanced(by: n) to do this because it's under test here. + let next = { $0 == collection.endIndex ? $0 : succ($0) } + + // advances up to 5 positions without passing endIndex. Picking a + // small constant to avoid complexity explosion on large input + // collections. + let next5 = { next(next(next(next(next($0))))) } + + let partWay0 = next5(collection.startIndex) + let partWay1 = next5(partWay0) + + + let instances = _allIndices(into: collection, + in: collection.startIndex..= 2 { + for i in 0..= 2` + + do { + var allIndices2: [C.Index] = [] + for i in collection.indices { + allIndices2.append(i) + } + + expectEqualSequence( + allIndices, allIndices2, "iteration should not invalidate indices", + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) + + expectEqualSequence( + expectedArray, allIndices.map { collection[$0] }, + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), sameValue: sameValue) + expectEqualSequence( + expectedArray, allIndices2.map { collection[$0] }, + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), sameValue: sameValue) + } + } // end of `for _ in 0..<3` + + // FIXME: more checks for bidirectional and random access collections. +} + +// Helper for checkForwardCollection to check Slices. +// +// Checks all slices: O(n^2). +internal func _checkSliceableWithForwardIndex< +Element, S : Collection +>( + _ expected: Array, _ sliceable: S, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line, + resiliencyChecks: CollectionMisuseResiliencyChecks = .all, + sameValue: (Element, Element) -> Bool +) where + S.Element == Element { + + let expectedArray = Array(expected) + + let succ = { sliceable.index(after: $0) } + + var start = sliceable.startIndex + for startNumericIndex in 0...expectedArray.count { + var end = start + for endNumericIndex in startNumericIndex...expectedArray.count { + let expectedSlice = expectedArray[startNumericIndex..( + _ expected: Array, _ collection: C, + + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line, + resiliencyChecks: CollectionMisuseResiliencyChecks = .all +) where + C.Element == Element, + Element : Equatable { + + checkBidirectionalCollection( + expected, collection, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), + resiliencyChecks: resiliencyChecks) { $0 == $1 } +} + +// Top-Level check for all BidirectionalCollection semantics on a single +// instance. This constrains SubSequence associated types in order to check +// slice semantics. +// Checks all slices: O(n^2). +public func checkBidirectionalCollection< + Element, C : BidirectionalCollection +>( + _ expected: Array, _ collection: C, + + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line, + resiliencyChecks: CollectionMisuseResiliencyChecks = .all, + sameValue: (Element, Element) -> Bool +) where + C.Element == Element { + + checkOneLevelOfBidirectionalCollection(expected, collection, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), + resiliencyChecks: resiliencyChecks, sameValue: sameValue) + + // Avoid validation of all possible (n^2) slices on large collection. + // Test cases should call checkOneLevelOfBidirectionalCollection instead. + expectLT(expected.count, 30) + + _checkSliceableWithBidirectionalIndex(expected, collection, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), + resiliencyChecks: resiliencyChecks, sameValue: sameValue) +} + +// Helper for checkBidirectionalCollection. Check that instance of `C`, +// `collection`, upholds the semantics of `BidirectionalCollection`, +// non-recursively. This does not check subsequences. It may be called for each +// subsequence without combinatorial explosion. Also, since recursive protocol +// constraints are not supported, our second level of checks cannot depend on the +// associated type properties of SubSequence. +// +// Checks all slices: O(n^2). +public func checkOneLevelOfBidirectionalCollection< + Element, C : BidirectionalCollection +>( + _ expected: Array, _ collection: C, + + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line, + resiliencyChecks: CollectionMisuseResiliencyChecks = .all, + sameValue: (Element, Element) -> Bool +) where C.Element == Element { + + // A `Collection` is a multi-pass `Sequence`. + for _ in 0..<3 { + checkSequence( + expected, collection, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), + resiliencyChecks: resiliencyChecks, sameValue: sameValue) + } + + //===------------------------------------------------------------------===// + // Check Index semantics + //===------------------------------------------------------------------===// + + let succ = { collection.index(after: $0) } + let pred = { collection.index(before: $0) } + // Advances up to 1 positions without passing endIndex. Don't use + // advanced(by: n) to do this because it's under test here. + let next = { $0 == collection.endIndex ? $0 : succ($0) } + + // advances up to 5 positions without passing endIndex. Picking a + // small constant to avoid complexity explosion on large input + // collections. + let next5 = { next(next(next(next(next($0))))) } + + let partWay0 = next5(collection.startIndex) + let partWay1 = next5(partWay0) + + + let instances = _allIndices(into: collection, in: partWay0..= 2 { + for i in 0..= 2` + + do { + var allIndices2: [C.Index] = [] + for i in collection.indices { + allIndices2.append(i) + } + + expectEqualSequence( + allIndices, allIndices2, "iteration should not invalidate indices", + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) + + expectEqualSequence( + expectedArray, allIndices.map { collection[$0] }, + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), sameValue: sameValue) + expectEqualSequence( + expectedArray, allIndices2.map { collection[$0] }, + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), sameValue: sameValue) + } + } // end of `for _ in 0..<3` + + // FIXME: more checks for bidirectional and random access collections. +} + +// Helper for checkBidirectionalCollection to check Slices. +// +// Checks all slices: O(n^2). +internal func _checkSliceableWithBidirectionalIndex< +Element, S : BidirectionalCollection +>( + _ expected: Array, _ sliceable: S, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line, + resiliencyChecks: CollectionMisuseResiliencyChecks = .all, + sameValue: (Element, Element) -> Bool +) where + S.Element == Element { + + let expectedArray = Array(expected) + + let succ = { sliceable.index(after: $0) } + let pred = { sliceable.index(before: $0) } + + var start = sliceable.startIndex + for startNumericIndex in 0...expectedArray.count { + if start != sliceable.endIndex { + start = succ(start) + start = pred(start) + start = succ(start) + start = pred(start) + } + var end = start + for endNumericIndex in startNumericIndex...expectedArray.count { + if end != sliceable.endIndex { + end = succ(end) + end = pred(end) + end = succ(end) + end = pred(end) + } + let expectedSlice = expectedArray[startNumericIndex..( + _ expected: Array, _ collection: C, + + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line, + resiliencyChecks: CollectionMisuseResiliencyChecks = .all +) where + C.Element == Element, + Element : Equatable { + + checkRandomAccessCollection( + expected, collection, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), + resiliencyChecks: resiliencyChecks) { $0 == $1 } +} + +// Top-Level check for all RandomAccessCollection semantics on a single +// instance. This constrains SubSequence associated types in order to check +// slice semantics. +// Checks all slices: O(n^2). +public func checkRandomAccessCollection< + Element, C : RandomAccessCollection +>( + _ expected: Array, _ collection: C, + + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line, + resiliencyChecks: CollectionMisuseResiliencyChecks = .all, + sameValue: (Element, Element) -> Bool +) where + C.Element == Element { + + checkOneLevelOfRandomAccessCollection(expected, collection, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), + resiliencyChecks: resiliencyChecks, sameValue: sameValue) + + // Avoid validation of all possible (n^2) slices on large collection. + // Test cases should call checkOneLevelOfRandomAccessCollection instead. + expectLT(expected.count, 30) + + _checkSliceableWithRandomAccessIndex(expected, collection, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), + resiliencyChecks: resiliencyChecks, sameValue: sameValue) +} + +// Helper for checkRandomAccessCollection. Check that instance of `C`, +// `collection`, upholds the semantics of `RandomAccessCollection`, +// non-recursively. This does not check subsequences. It may be called for each +// subsequence without combinatorial explosion. Also, since recursive protocol +// constraints are not supported, our second level of checks cannot depend on the +// associated type properties of SubSequence. +// +// Checks all slices: O(n^2). +public func checkOneLevelOfRandomAccessCollection< + Element, C : RandomAccessCollection +>( + _ expected: Array, _ collection: C, + + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line, + resiliencyChecks: CollectionMisuseResiliencyChecks = .all, + sameValue: (Element, Element) -> Bool +) where C.Element == Element { + + // A `Collection` is a multi-pass `Sequence`. + for _ in 0..<3 { + checkSequence( + expected, collection, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), + resiliencyChecks: resiliencyChecks, sameValue: sameValue) + } + + //===------------------------------------------------------------------===// + // Check Index semantics + //===------------------------------------------------------------------===// + + let succ = { collection.index(after: $0) } + let pred = { collection.index(before: $0) } + // Advances up to 1 positions without passing endIndex. Don't use + // advanced(by: n) to do this because it's under test here. + let next = { $0 == collection.endIndex ? $0 : succ($0) } + + // advances up to 5 positions without passing endIndex. Picking a + // small constant to avoid complexity explosion on large input + // collections. + let next5 = { next(next(next(next(next($0))))) } + + let partWay0 = next5(collection.startIndex) + let partWay1 = next5(partWay0) + + typealias Distance = Int + + let count: Distance = collection.count + let offset0 = min(5, count) + let offset1 = min(10, count) + let offset2 = min(15, count) + + let distanceCandidates: [Distance] = [ + -11, -7, -5, -3, -2, -1, 0, 1, 2, 3, 5, 7, 11] + + let distances = distanceCandidates.filter { (x: Distance) -> Bool in + x + offset0 >= 0 && x + offset1 <= count + } + + func nextN(_ n: Distance, _ i: C.Index) -> C.Index { + return collection.index(i, offsetBy: n) + } + + let instances = _allIndices(into: collection, in: partWay0..= 2 { + for i in 0..= 2` + + do { + var allIndices2: [C.Index] = [] + for i in collection.indices { + allIndices2.append(i) + } + + expectEqualSequence( + allIndices, allIndices2, "iteration should not invalidate indices", + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) + + expectEqualSequence( + expectedArray, allIndices.map { collection[$0] }, + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), sameValue: sameValue) + expectEqualSequence( + expectedArray, allIndices2.map { collection[$0] }, + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), sameValue: sameValue) + } + } // end of `for _ in 0..<3` + + // FIXME: more checks for bidirectional and random access collections. +} + +// Helper for checkRandomAccessCollection to check Slices. +// +// Checks all slices: O(n^2). +internal func _checkSliceableWithRandomAccessIndex< +Element, S : RandomAccessCollection +>( + _ expected: Array, _ sliceable: S, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line, + resiliencyChecks: CollectionMisuseResiliencyChecks = .all, + sameValue: (Element, Element) -> Bool +) where + S.Element == Element { + + let expectedArray = Array(expected) + + let succ = { sliceable.index(after: $0) } + let pred = { sliceable.index(before: $0) } + + var start = sliceable.startIndex + for startNumericIndex in 0...expectedArray.count { + if start != sliceable.endIndex { + start = succ(start) + start = pred(start) + start = succ(start) + start = pred(start) + } + var end = start + for endNumericIndex in startNumericIndex...expectedArray.count { + if end != sliceable.endIndex { + end = succ(end) + end = pred(end) + end = succ(end) + end = pred(end) + } + let expectedSlice = expectedArray[startNumericIndex..( + _ makeCollection: @escaping () -> C, + _ makeNewValues: (Int) -> N +) where + C : RangeReplaceableCollection, + N : Collection, + C.Element : Equatable, + C.Element == N.Element { + + typealias A = C + + // First make an independent copy of the array that we can use for + // comparison later. + let source = Array(makeCollection()) + + for (ix, i) in source.indices.enumerated() { + for (jx_, j) in (i..( - _ instances: Instances, - of baseCollection: BaseCollection, - equalityOracle: (Instances.Index, Instances.Index) -> Bool, - ${end}Index: Instances.Element, ${TRACE} -) where - Instances : Collection, - BaseCollection : ${protocol}, - Instances.Element == BaseCollection.Index { - - checkEquatable(instances, oracle: equalityOracle, ${trace}) - for i in instances { - if i != ${end}Index { - let next = baseCollection.index(${direction}: i) - // index(${direction}:) gets us a new index value - expectNotEqual(i, next, ${trace}) - - // Which is the same as if we apply formIndex(${direction}:) - var j = i - baseCollection.formIndex(${direction}: &j) - expectEqual(j, next, ${trace}) - } - } -} -%end - -internal func _checkIncrementalAdvance( - _ instances: Instances, - of baseCollection : BaseCollection, - equalityOracle: (Instances.Index, Instances.Index) -> Bool, - limit: Instances.Element, - sign: Int, // 1 or -1 - next: (Instances.Element) -> Instances.Element, - ${TRACE} -) where - Instances : Collection, - BaseCollection : Collection, - Instances.Element == BaseCollection.Index { - for i in instances { - let d: Int = sign > 0 ? - baseCollection.distance(from: i, to: limit) : - -baseCollection.distance(from: limit, to: i) - - var offset: Int = 0 - for _ in 0...Int64(d * sign) { - let j = baseCollection.index(i, offsetBy: offset) - let k = baseCollection.index(i, offsetBy: offset + sign, limitedBy: limit) ?? limit - let jAtLimit = offset == d - if jAtLimit { - expectEqual(limit, j, ${trace}) - } - expectEqual(jAtLimit ? j : next(j), k, ${trace}) - offset += sign - } - } -} - -/// Test that the elements of `instances` satisfy the semantic requirements of -/// index for `Collection`, using `equalityOracle` to generate equality -/// expectations from pairs of positions in `instances`. -/// -/// - Precondition: `endIndex` is reachable from all elements of -/// `instances` -public func checkForwardIndex( - _ instances: Instances, - of baseCollection: BaseCollection, - equalityOracle: (Instances.Index, Instances.Index) -> Bool, - endIndex: Instances.Element, ${TRACE} -) where - Instances : Collection, - BaseCollection : Collection, - Instances.Element == BaseCollection.Index { - - checkIncrementable(instances, of: baseCollection, - equalityOracle: equalityOracle, endIndex: endIndex, ${trace}) - - _checkIncrementalAdvance(instances, of: baseCollection, - equalityOracle: equalityOracle, limit: endIndex, - sign: 1, next: { baseCollection.index(after: $0) }, ${trace}) -} - -/// Test that the elements of `instances` satisfy the semantic requirements of -/// index for `BidirectionalCollection`, using `equalityOracle` to generate -/// equality expectations from pairs of positions in `instances`. -/// -/// - Precondition: -/// - all elements of `instances` are reachable from `startIndex`. -/// - `endIndex` is reachable from all elements of `instances`. -public func checkBidirectionalIndex( - _ instances: Instances, - of baseCollection: BaseCollection, - equalityOracle: (Instances.Index, Instances.Index) -> Bool, - startIndex: Instances.Element, - endIndex: Instances.Element, - ${TRACE} -) where - Instances: Collection, - BaseCollection : BidirectionalCollection, - Instances.Element == BaseCollection.Index { - - checkForwardIndex(instances, of: baseCollection, - equalityOracle: equalityOracle, endIndex: endIndex) - - checkDecrementable(instances, of: baseCollection, - equalityOracle: equalityOracle, startIndex: startIndex, ${trace}) - - _checkIncrementalAdvance(instances, of: baseCollection, - equalityOracle: equalityOracle, limit: startIndex, - sign: -1, next: { baseCollection.index(before: $0) }, ${trace}) -} - -/// Test that the elements of `instances` satisfy the semantic requirements of -/// index for `RandomAccessCollection`, using `advanceOracle` and -/// 'distanceOracle' to generate expectations about the results of -/// `advanced(by:)` and `distance(to:)` from pairs of positions in `instances` -/// and `distances`. -/// -/// - Precondition: -/// - all elements of `instances` are reachable from `startIndex`. -/// - `endIndex` is reachable from all elements of `instances`. -public func checkRandomAccessIndex( - _ instances: Instances, distances: Distances, - of baseCollection: BaseCollection, - distanceOracle: - (Instances.Index, Instances.Index) -> Distances.Element, - advanceOracle: - (Instances.Index, Distances.Index) -> Instances.Element, - startIndex: Instances.Iterator.Element, - endIndex: Instances.Iterator.Element, - ${TRACE} -) where - Instances : Collection, - Distances : Collection, - BaseCollection : RandomAccessCollection, - Instances.Element == BaseCollection.Index, - Distances.Element == Int { - - checkBidirectionalIndex(instances, of: baseCollection, - equalityOracle: { distanceOracle($0, $1) == 0 }, - startIndex: startIndex, endIndex: endIndex, ${trace}) - - checkAdvancesAndDistances( - instances, distances: distances, - of: baseCollection, - distanceOracle: distanceOracle, - advanceOracle: advanceOracle, ${trace}) -} - -// Copies what checkStrideable is doing, but instead of calling -// advanced(by:) and distance(to:) on an Strideable's, -// calls corresponding methods on a base collection. -public func checkAdvancesAndDistances( - _ instances: Instances, distances: Distances, - of baseCollection: BaseCollection, - distanceOracle: - (Instances.Index, Instances.Index) -> Distances.Element, - advanceOracle: - (Instances.Index, Distances.Index) -> Instances.Element, - ${TRACE} -) where - Instances : Collection, - Distances : Collection, - BaseCollection : Collection, - Instances.Element == BaseCollection.Index, - Distances.Element == Int { - - checkComparable( - instances, - oracle: { - let d = distanceOracle($1, $0); - return d < 0 ? .lt : d == 0 ? .eq : .gt - }, - ${trace}) - - for i in instances.indices { - let x = instances[i] - expectEqual(x, baseCollection.index(x, offsetBy: 0)) - - for j in distances.indices { - let y = distances[j] - expectEqual(advanceOracle(i, j), baseCollection.index(x, offsetBy: y)) - } - - for j in instances.indices { - let y = instances[j] - expectEqual(distanceOracle(i, j), baseCollection.distance(from: x, to: y)) - } - } -} - -// Generate two overloads: one for Array (which will get -// picked up when the caller passes a literal), and another that -// accepts any appropriate Collection type. -% for genericParam, Element, Expected in [ -% ('Expected: Collection', 'Expected.Element', 'Expected'), -% ('Element' , 'Element' , 'Array')]: - -// Top-level check for Collection instances. Alias for checkForwardCollection. -// Checks all slices: O(n^2). -public func checkCollection<${genericParam}, C : Collection>( - _ expected: ${Expected}, - _ collection: C, - ${TRACE}, - resiliencyChecks: CollectionMisuseResiliencyChecks = .all, - sameValue: (${Element}, ${Element}) -> Bool -) where C.Element == ${Element} { - - checkForwardCollection(expected, collection, message(), - stackTrace: stackTrace, showFrame: showFrame, file: file, line: line, - resiliencyChecks: resiliencyChecks, - sameValue: sameValue) -} - -% for Traversal in TRAVERSALS: -% TraversalCollection = collectionForTraversal(Traversal) - -// Calls check${Traversal}Collection with default `sameValue`. -public func check${Traversal}Collection< - ${genericParam}, C : ${TraversalCollection} ->( - _ expected: ${Expected}, _ collection: C, - ${TRACE}, - resiliencyChecks: CollectionMisuseResiliencyChecks = .all -) where - C.Element == ${Element}, - ${Element} : Equatable { - - check${Traversal}Collection( - expected, collection, ${trace}, - resiliencyChecks: resiliencyChecks) { $0 == $1 } -} - -// Top-Level check for all ${TraversalCollection} semantics on a single -// instance. This constrains SubSequence associated types in order to check -// slice semantics. -// Checks all slices: O(n^2). -public func check${Traversal}Collection< - ${genericParam}, C : ${TraversalCollection} ->( - _ expected: ${Expected}, _ collection: C, - ${TRACE}, - resiliencyChecks: CollectionMisuseResiliencyChecks = .all, - sameValue: (${Element}, ${Element}) -> Bool -) where - C.Element == ${Element} { - - checkOneLevelOf${Traversal}Collection(expected, collection, ${trace}, - resiliencyChecks: resiliencyChecks, sameValue: sameValue) - - // Avoid validation of all possible (n^2) slices on large collection. - // Test cases should call checkOneLevelOf${Traversal}Collection instead. - expectLT(expected.count, 30) - - _checkSliceableWith${Traversal}Index(expected, collection, ${trace}, - resiliencyChecks: resiliencyChecks, sameValue: sameValue) -} - -// Helper for check${Traversal}Collection. Check that instance of `C`, -// `collection`, upholds the semantics of `${TraversalCollection}`, -// non-recursively. This does not check subsequences. It may be called for each -// subsequence without combinatorial explosion. Also, since recursive protocol -// constraints are not supported, our second level of checks cannot depend on the -// associated type properties of SubSequence. -// -// Checks all slices: O(n^2). -public func checkOneLevelOf${Traversal}Collection< - ${genericParam}, C : ${TraversalCollection} ->( - _ expected: ${Expected}, _ collection: C, - ${TRACE}, - resiliencyChecks: CollectionMisuseResiliencyChecks = .all, - sameValue: (${Element}, ${Element}) -> Bool -) where C.Element == ${Element} { - - // A `Collection` is a multi-pass `Sequence`. - for _ in 0..<3 { - checkSequence( - expected, collection, ${trace}, - resiliencyChecks: resiliencyChecks, sameValue: sameValue) - } - - //===------------------------------------------------------------------===// - // Check Index semantics - //===------------------------------------------------------------------===// - - let succ = { collection.index(after: $0) } -% if Traversal != 'Forward': - let pred = { collection.index(before: $0) } -% end - // Advances up to 1 positions without passing endIndex. Don't use - // advanced(by: n) to do this because it's under test here. - let next = { $0 == collection.endIndex ? $0 : succ($0) } - - // advances up to 5 positions without passing endIndex. Picking a - // small constant to avoid complexity explosion on large input - // collections. - let next5 = { next(next(next(next(next($0))))) } - - let partWay0 = next5(collection.startIndex) - let partWay1 = next5(partWay0) - -% if Traversal == 'Forward': - - let instances = _allIndices(into: collection, - in: collection.startIndex.. Bool in - x + offset0 >= 0 && x + offset1 <= count - } - - func nextN(_ n: Distance, _ i: C.Index) -> C.Index { - return collection.index(i, offsetBy: n) - } - - let instances = _allIndices(into: collection, in: partWay0..= 2 { - for i in 0..= 2` - - do { - var allIndices2: [C.Index] = [] - for i in collection.indices { - allIndices2.append(i) - } - - expectEqualSequence( - allIndices, allIndices2, "iteration should not invalidate indices", - stackTrace: ${stackTrace}) - - expectEqualSequence( - expectedArray, allIndices.map { collection[$0] }, - stackTrace: ${stackTrace}, sameValue: sameValue) - expectEqualSequence( - expectedArray, allIndices2.map { collection[$0] }, - stackTrace: ${stackTrace}, sameValue: sameValue) - } - } // end of `for _ in 0..<3` - - // FIXME: more checks for bidirectional and random access collections. -} - -// Helper for check${Traversal}Collection to check Slices. -// -// Checks all slices: O(n^2). -internal func _checkSliceableWith${Traversal}Index< -${genericParam}, S : ${TraversalCollection} ->( - _ expected: ${Expected}, _ sliceable: S, ${TRACE}, - resiliencyChecks: CollectionMisuseResiliencyChecks = .all, - sameValue: (${Element}, ${Element}) -> Bool -) where - S.Element == ${Element} { - - let expectedArray = Array(expected) - - let succ = { sliceable.index(after: $0) } -% if Traversal != "Forward": - let pred = { sliceable.index(before: $0) } -% end - - var start = sliceable.startIndex - for startNumericIndex in 0...expectedArray.count { -% if Traversal != "Forward": - if start != sliceable.endIndex { - start = succ(start) - start = pred(start) - start = succ(start) - start = pred(start) - } -% end - var end = start - for endNumericIndex in startNumericIndex...expectedArray.count { -% if Traversal != "Forward": - if end != sliceable.endIndex { - end = succ(end) - end = pred(end) - end = succ(end) - end = pred(end) - } -% end - let expectedSlice = expectedArray[startNumericIndex..( - _ makeCollection: @escaping () -> C, - _ makeNewValues: (Int) -> N -) where - C : RangeReplaceableCollection, - N : Collection, - C.Element : Equatable, - C.Element == N.Element { - - typealias A = C - - // First make an independent copy of the array that we can use for - // comparison later. - let source = Array(makeCollection()) - - for (ix, i) in source.indices.enumerated() { - for (jx_, j) in (i.. Index { return self.index(self.startIndex, offsetBy: numericCast(offset)) @@ -468,49 +551,36 @@ internal enum _SubSequenceSubscriptOnRangeMode { } } -%{ - from gyb_stdlib_support import collectionForTraversal - def testConstraints(protocol): - return ''' - C : %(protocol)s, - CollectionWithEquatableElement : %(protocol)s, - CollectionWithEquatableElement.Iterator.Element : Equatable - ''' % locals() - - testParams = ''' - _ testNamePrefix: String = "", - makeCollection: @escaping ([C.Iterator.Element]) -> C, - wrapValue: @escaping (OpaqueValue) -> C.Iterator.Element, - extractValue: @escaping (C.Iterator.Element) -> OpaqueValue, - - makeCollectionOfEquatable: @escaping ( - [CollectionWithEquatableElement.Iterator.Element] - ) -> CollectionWithEquatableElement, - - wrapValueIntoEquatable: @escaping ( - MinimalEquatableValue) -> CollectionWithEquatableElement.Iterator.Element, - - extractValueFromEquatable: @escaping ((CollectionWithEquatableElement.Iterator.Element) -> MinimalEquatableValue), - - resiliencyChecks: CollectionMisuseResiliencyChecks = .all, - outOfBoundsIndexOffset: Int = 1, - outOfBoundsSubscriptOffset: Int = 1, - collectionIsBidirectional: Bool - ''' - - import re - forwardTestArgs = ',\n '.join( - [ x.group(1) + ': ' + x.group(1) - for x in re.finditer(r'([a-zA-Z0-9_]+):', testParams) ] - ).replace('testNamePrefix: ', '\n ') -}% extension TestSuite { public func addCollectionTests< C, CollectionWithEquatableElement >( - ${testParams} = false + + _ testNamePrefix: String = "", + makeCollection: @escaping ([C.Iterator.Element]) -> C, + wrapValue: @escaping (OpaqueValue) -> C.Iterator.Element, + extractValue: @escaping (C.Iterator.Element) -> OpaqueValue, + + makeCollectionOfEquatable: @escaping ( + [CollectionWithEquatableElement.Iterator.Element] + ) -> CollectionWithEquatableElement, + + wrapValueIntoEquatable: @escaping ( + MinimalEquatableValue) -> CollectionWithEquatableElement.Iterator.Element, + + extractValueFromEquatable: @escaping ((CollectionWithEquatableElement.Iterator.Element) -> MinimalEquatableValue), + + resiliencyChecks: CollectionMisuseResiliencyChecks = .all, + outOfBoundsIndexOffset: Int = 1, + outOfBoundsSubscriptOffset: Int = 1, + collectionIsBidirectional: Bool + = false ) where - ${testConstraints('Collection')} { + + C : Collection, + CollectionWithEquatableElement : Collection, + CollectionWithEquatableElement.Iterator.Element : Equatable + { var testNamePrefix = testNamePrefix @@ -827,13 +897,13 @@ extension TestSuite { } //===------------------------------------------------------------------===// - // index(of:)/index(where:) + // firstIndex(of:)/firstIndex(where:) //===------------------------------------------------------------------===// - self.test("\(testNamePrefix).index(of:)/semantics") { + self.test("\(testNamePrefix).firstIndex(of:)/semantics") { for test in findTests { let c = makeWrappedCollectionWithEquatableElement(test.sequence) - var result = c.index(of: wrapValueIntoEquatable(test.element)) + var result = c.firstIndex(of: wrapValueIntoEquatable(test.element)) expectType( Optional.self, &result) @@ -847,12 +917,12 @@ extension TestSuite { } } - self.test("\(testNamePrefix).index(where:)/semantics") { + self.test("\(testNamePrefix).firstIndex(where:)/semantics") { for test in findTests { let closureLifetimeTracker = LifetimeTracked(0) expectEqual(1, LifetimeTracked.instances) let c = makeWrappedCollectionWithEquatableElement(test.sequence) - let result = c.index { + let result = c.firstIndex { (candidate) in _blackHole(closureLifetimeTracker) return @@ -1166,15 +1236,49 @@ extension TestSuite { } //===------------------------------------------------------------------===// - self.addCommonTests(${forwardTestArgs}) + self.addCommonTests( + testNamePrefix, + makeCollection: makeCollection, + wrapValue: wrapValue, + extractValue: extractValue, + makeCollectionOfEquatable: makeCollectionOfEquatable, + wrapValueIntoEquatable: wrapValueIntoEquatable, + extractValueFromEquatable: extractValueFromEquatable, + resiliencyChecks: resiliencyChecks, + outOfBoundsIndexOffset: outOfBoundsIndexOffset, + outOfBoundsSubscriptOffset: outOfBoundsSubscriptOffset, + collectionIsBidirectional: collectionIsBidirectional) } // addCollectionTests public func addBidirectionalCollectionTests< C, CollectionWithEquatableElement >( - ${testParams} = true + + _ testNamePrefix: String = "", + makeCollection: @escaping ([C.Iterator.Element]) -> C, + wrapValue: @escaping (OpaqueValue) -> C.Iterator.Element, + extractValue: @escaping (C.Iterator.Element) -> OpaqueValue, + + makeCollectionOfEquatable: @escaping ( + [CollectionWithEquatableElement.Iterator.Element] + ) -> CollectionWithEquatableElement, + + wrapValueIntoEquatable: @escaping ( + MinimalEquatableValue) -> CollectionWithEquatableElement.Iterator.Element, + + extractValueFromEquatable: @escaping ((CollectionWithEquatableElement.Iterator.Element) -> MinimalEquatableValue), + + resiliencyChecks: CollectionMisuseResiliencyChecks = .all, + outOfBoundsIndexOffset: Int = 1, + outOfBoundsSubscriptOffset: Int = 1, + collectionIsBidirectional: Bool + = true ) where - ${testConstraints('BidirectionalCollection')} { + + C : BidirectionalCollection, + CollectionWithEquatableElement : BidirectionalCollection, + CollectionWithEquatableElement.Iterator.Element : Equatable + { var testNamePrefix = testNamePrefix @@ -1184,12 +1288,29 @@ extension TestSuite { return } - addCollectionTests(${forwardTestArgs}) + addCollectionTests( + testNamePrefix, + makeCollection: makeCollection, + wrapValue: wrapValue, + extractValue: extractValue, + makeCollectionOfEquatable: makeCollectionOfEquatable, + wrapValueIntoEquatable: wrapValueIntoEquatable, + extractValueFromEquatable: extractValueFromEquatable, + resiliencyChecks: resiliencyChecks, + outOfBoundsIndexOffset: outOfBoundsIndexOffset, + outOfBoundsSubscriptOffset: outOfBoundsSubscriptOffset, + collectionIsBidirectional: collectionIsBidirectional) func makeWrappedCollection(_ elements: [OpaqueValue]) -> C { return makeCollection(elements.map(wrapValue)) } + func makeWrappedCollectionWithEquatableElement( + _ elements: [MinimalEquatableValue] + ) -> CollectionWithEquatableElement { + return makeCollectionOfEquatable(elements.map(wrapValueIntoEquatable)) + } + testNamePrefix += String(describing: C.Type.self) // FIXME: swift-3-indexing-model - add tests for the follow? @@ -1222,6 +1343,92 @@ extension TestSuite { } } + //===------------------------------------------------------------------===// + // last(where:) + //===------------------------------------------------------------------===// + + self.test("\(testNamePrefix).last(where:)/semantics") { + for test in findLastTests { + let c = makeWrappedCollectionWithEquatableElement(test.sequence) + var closureCounter = 0 + let closureLifetimeTracker = LifetimeTracked(0) + let found = c.last(where: { + _blackHole(closureLifetimeTracker) + closureCounter += 1 + return $0 == wrapValueIntoEquatable(test.element) + }) + expectEqual( + test.expected == nil ? nil : wrapValueIntoEquatable(test.element), + found, + stackTrace: SourceLocStack().with(test.loc)) + expectEqual( + test.comparisons, + closureCounter, + stackTrace: SourceLocStack().with(test.loc)) + if let expectedIdentity = test.expected { + expectEqual( + expectedIdentity, extractValueFromEquatable(found!).identity, + "last(where:) should find only the first element matching its predicate") + } + } + } + + //===------------------------------------------------------------------===// + // lastIndex(of:)/lastIndex(where:) + //===------------------------------------------------------------------===// + + self.test("\(testNamePrefix).lastIndex(of:)/semantics") { + for test in findLastTests { + let c = makeWrappedCollectionWithEquatableElement(test.sequence) + MinimalEquatableValue.timesEqualEqualWasCalled = 0 + let wrappedElement = wrapValueIntoEquatable(test.element) + var result = c.lastIndex(of: wrappedElement) + expectType( + Optional.self, + &result) + let zeroBasedIndex = result.map { + numericCast(c.distance(from: c.startIndex, to: $0)) as Int + } + expectEqual( + test.expected, + zeroBasedIndex, + stackTrace: SourceLocStack().with(test.loc)) + if wrappedElement is MinimalEquatableValue { + expectEqual( + test.comparisons, + MinimalEquatableValue.timesEqualEqualWasCalled, + stackTrace: SourceLocStack().with(test.loc)) + } + } + } + + self.test("\(testNamePrefix).lastIndex(where:)/semantics") { + for test in findLastTests { + let closureLifetimeTracker = LifetimeTracked(0) + expectEqual(1, LifetimeTracked.instances) + let c = makeWrappedCollectionWithEquatableElement(test.sequence) + var closureCounter = 0 + let result = c.lastIndex(where: { + (candidate) in + _blackHole(closureLifetimeTracker) + closureCounter += 1 + return + extractValueFromEquatable(candidate).value == test.element.value + }) + let zeroBasedIndex = result.map { + numericCast(c.distance(from: c.startIndex, to: $0)) as Int + } + expectEqual( + test.expected, + zeroBasedIndex, + stackTrace: SourceLocStack().with(test.loc)) + expectEqual( + test.comparisons, + closureCounter, + stackTrace: SourceLocStack().with(test.loc)) + } + } + //===------------------------------------------------------------------===// // removeLast()/slice //===------------------------------------------------------------------===// @@ -1484,15 +1691,49 @@ extension TestSuite { //===------------------------------------------------------------------===// - self.addCommonTests(${forwardTestArgs}) + self.addCommonTests( + testNamePrefix, + makeCollection: makeCollection, + wrapValue: wrapValue, + extractValue: extractValue, + makeCollectionOfEquatable: makeCollectionOfEquatable, + wrapValueIntoEquatable: wrapValueIntoEquatable, + extractValueFromEquatable: extractValueFromEquatable, + resiliencyChecks: resiliencyChecks, + outOfBoundsIndexOffset: outOfBoundsIndexOffset, + outOfBoundsSubscriptOffset: outOfBoundsSubscriptOffset, + collectionIsBidirectional: collectionIsBidirectional) } // addBidirectionalCollectionTests public func addRandomAccessCollectionTests< C, CollectionWithEquatableElement >( - ${testParams} = true + + _ testNamePrefix: String = "", + makeCollection: @escaping ([C.Iterator.Element]) -> C, + wrapValue: @escaping (OpaqueValue) -> C.Iterator.Element, + extractValue: @escaping (C.Iterator.Element) -> OpaqueValue, + + makeCollectionOfEquatable: @escaping ( + [CollectionWithEquatableElement.Iterator.Element] + ) -> CollectionWithEquatableElement, + + wrapValueIntoEquatable: @escaping ( + MinimalEquatableValue) -> CollectionWithEquatableElement.Iterator.Element, + + extractValueFromEquatable: @escaping ((CollectionWithEquatableElement.Iterator.Element) -> MinimalEquatableValue), + + resiliencyChecks: CollectionMisuseResiliencyChecks = .all, + outOfBoundsIndexOffset: Int = 1, + outOfBoundsSubscriptOffset: Int = 1, + collectionIsBidirectional: Bool + = true ) where - ${testConstraints('RandomAccessCollection')} { + + C : RandomAccessCollection, + CollectionWithEquatableElement : RandomAccessCollection, + CollectionWithEquatableElement.Iterator.Element : Equatable + { var testNamePrefix = testNamePrefix @@ -1502,7 +1743,18 @@ extension TestSuite { return } - addBidirectionalCollectionTests(${forwardTestArgs}) + addBidirectionalCollectionTests( + testNamePrefix, + makeCollection: makeCollection, + wrapValue: wrapValue, + extractValue: extractValue, + makeCollectionOfEquatable: makeCollectionOfEquatable, + wrapValueIntoEquatable: wrapValueIntoEquatable, + extractValueFromEquatable: extractValueFromEquatable, + resiliencyChecks: resiliencyChecks, + outOfBoundsIndexOffset: outOfBoundsIndexOffset, + outOfBoundsSubscriptOffset: outOfBoundsSubscriptOffset, + collectionIsBidirectional: collectionIsBidirectional) testNamePrefix += String(describing: C.Type.self) @@ -1539,16 +1791,49 @@ extension TestSuite { } //===------------------------------------------------------------------===// - self.addCommonTests(${forwardTestArgs}) + self.addCommonTests( + testNamePrefix, + makeCollection: makeCollection, + wrapValue: wrapValue, + extractValue: extractValue, + makeCollectionOfEquatable: makeCollectionOfEquatable, + wrapValueIntoEquatable: wrapValueIntoEquatable, + extractValueFromEquatable: extractValueFromEquatable, + resiliencyChecks: resiliencyChecks, + outOfBoundsIndexOffset: outOfBoundsIndexOffset, + outOfBoundsSubscriptOffset: outOfBoundsSubscriptOffset, + collectionIsBidirectional: collectionIsBidirectional) } // addRandomAccessCollectionTests -% for Traversal in ['Forward', 'Bidirectional', 'RandomAccess']: func addCommonTests< C, CollectionWithEquatableElement >( - ${testParams} + + _ testNamePrefix: String = "", + makeCollection: @escaping ([C.Iterator.Element]) -> C, + wrapValue: @escaping (OpaqueValue) -> C.Iterator.Element, + extractValue: @escaping (C.Iterator.Element) -> OpaqueValue, + + makeCollectionOfEquatable: @escaping ( + [CollectionWithEquatableElement.Iterator.Element] + ) -> CollectionWithEquatableElement, + + wrapValueIntoEquatable: @escaping ( + MinimalEquatableValue) -> CollectionWithEquatableElement.Iterator.Element, + + extractValueFromEquatable: @escaping ((CollectionWithEquatableElement.Iterator.Element) -> MinimalEquatableValue), + + resiliencyChecks: CollectionMisuseResiliencyChecks = .all, + outOfBoundsIndexOffset: Int = 1, + outOfBoundsSubscriptOffset: Int = 1, + collectionIsBidirectional: Bool + ) where - ${testConstraints(collectionForTraversal(Traversal))} { + + C : Collection, + CollectionWithEquatableElement : Collection, + CollectionWithEquatableElement.Iterator.Element : Equatable + { if !checksAdded.insert( "\(testNamePrefix).\(C.self).\(#function)" @@ -1560,8 +1845,7 @@ extension TestSuite { return makeCollection(r.map { wrapValue(OpaqueValue($0)) }) } -% for function_name in ['index', 'formIndex']: - self.test("\(testNamePrefix).${function_name}(after:)/semantics") { + self.test("\(testNamePrefix).index(after:)/semantics") { for test in indexAfterTests { let c = toCollection(test.start..= 0} + ) { + let c = toCollection(0..<20) + let limit = c.nthIndex(test.limit.unsafelyUnwrapped) + let new = c.index( + c.nthIndex(test.startOffset), + offsetBy: numericCast(test.distance), + limitedBy: limit) + if let expectedOffset = test.expectedOffset { + expectEqual(c.nthIndex(expectedOffset), new!, + stackTrace: SourceLocStack().with(test.loc)) + } else { + expectNil(new) + } + } + } + + self.test("\(testNamePrefix)/formIndex(_:offsetBy: n, limitedBy:)/semantics") { + for test in indexOffsetByTests.filter( + {$0.limit != nil && $0.distance >= 0} + ) { + let c = toCollection(0..<20) + let limit = c.nthIndex(test.limit.unsafelyUnwrapped) + var new = c.nthIndex(test.startOffset) + let exact = c.formIndex(&new, offsetBy: numericCast(test.distance), limitedBy: limit) + if let expectedOffset = test.expectedOffset { + expectEqual(c.nthIndex(expectedOffset), new, + stackTrace: SourceLocStack().with(test.loc)) + expectTrue(exact, stackTrace: SourceLocStack().with(test.loc)) + } else { + // Clamped to the limit + expectEqual(limit, new, stackTrace: SourceLocStack().with(test.loc)) + expectFalse(exact, stackTrace: SourceLocStack().with(test.loc)) + } + } + } + + self.test("\(testNamePrefix)/index(_:offsetBy: -n, limitedBy:)/semantics") + .forEach( + in: indexOffsetByTests.filter { $0.limit != nil && $0.distance < 0 } + ) { + test in + let c = toCollection(0..<20) + let limit = c.nthIndex(test.limit.unsafelyUnwrapped) + if collectionIsBidirectional { + let new = c.index( + c.nthIndex(test.startOffset), + offsetBy: numericCast(test.distance), + limitedBy: limit) + if let expectedOffset = test.expectedOffset { + expectEqual(c.nthIndex(expectedOffset), new!, + stackTrace: SourceLocStack().with(test.loc)) + } else { + expectNil(new) + } + } else { + expectCrashLater() + _ = c.index( + c.nthIndex(test.startOffset), + offsetBy: numericCast(test.distance), + limitedBy: limit) + } + } + + self.test("\(testNamePrefix)/formIndex(_:offsetBy: -n, limitedBy:)/semantics") + .forEach( + in: indexOffsetByTests.filter { $0.limit != nil && $0.distance < 0 } + ) { + test in + let c = toCollection(0..<20) + let limit = c.nthIndex(test.limit.unsafelyUnwrapped) + var new = c.nthIndex(test.startOffset) + if collectionIsBidirectional { + let exact = c.formIndex( + &new, + offsetBy: numericCast(test.distance), + limitedBy: limit) + if let expectedOffset = test.expectedOffset { + expectEqual(c.nthIndex(expectedOffset), new, + stackTrace: SourceLocStack().with(test.loc)) + expectTrue(exact, stackTrace: SourceLocStack().with(test.loc)) + } else { + expectEqual(limit, new, stackTrace: SourceLocStack().with(test.loc)) + expectFalse(exact, stackTrace: SourceLocStack().with(test.loc)) + } + } else { + expectCrashLater() + _ = c.formIndex(&new, offsetBy: numericCast(test.distance), limitedBy: limit) + } + } + + } + func addCommonTests< + C, CollectionWithEquatableElement + >( + + _ testNamePrefix: String = "", + makeCollection: @escaping ([C.Iterator.Element]) -> C, + wrapValue: @escaping (OpaqueValue) -> C.Iterator.Element, + extractValue: @escaping (C.Iterator.Element) -> OpaqueValue, + + makeCollectionOfEquatable: @escaping ( + [CollectionWithEquatableElement.Iterator.Element] + ) -> CollectionWithEquatableElement, + + wrapValueIntoEquatable: @escaping ( + MinimalEquatableValue) -> CollectionWithEquatableElement.Iterator.Element, + + extractValueFromEquatable: @escaping ((CollectionWithEquatableElement.Iterator.Element) -> MinimalEquatableValue), + + resiliencyChecks: CollectionMisuseResiliencyChecks = .all, + outOfBoundsIndexOffset: Int = 1, + outOfBoundsSubscriptOffset: Int = 1, + collectionIsBidirectional: Bool + + ) where + + C : BidirectionalCollection, + CollectionWithEquatableElement : BidirectionalCollection, + CollectionWithEquatableElement.Iterator.Element : Equatable + { + + if !checksAdded.insert( + "\(testNamePrefix).\(C.self).\(#function)" + ).inserted { + return + } + + func toCollection(_ r: Range) -> C { + return makeCollection(r.map { wrapValue(OpaqueValue($0)) }) + } + + self.test("\(testNamePrefix).index(after:)/semantics") { + for test in indexAfterTests { + let c = toCollection(test.start.. test.endOffset) + if backwards && !collectionIsBidirectional { + expectCrashLater() + } + + let d = c.distance( + from: c.nthIndex(test.startOffset), to: c.nthIndex(test.endOffset)) + expectEqual( + numericCast(test.expectedDistance), + d, stackTrace: SourceLocStack().with(test.loc)) + } + + self.test("\(testNamePrefix)/index(_:offsetBy: n)/semantics") + .forEach( + in: indexOffsetByTests.filter { $0.limit == nil && $0.distance >= 0 } + ) { + test in + let max = 10 + let c = toCollection(0..= max { + expectCrashLater() + } + let new = c.index( + c.nthIndex(test.startOffset), + offsetBy: numericCast(test.distance)) + + // Since the `nthIndex(offset:)` method performs the same operation + // (i.e. advances `c.startIndex` by `test.distance`, it would be + // silly to compare index values. Luckily the underlying collection + // contains exactly index offsets. + expectEqual(test.expectedOffset!, extractValue(c[new]).value, + stackTrace: SourceLocStack().with(test.loc)) + } + + self.test("\(testNamePrefix)/formIndex(_:offsetBy: n)/semantics") + .forEach( + in: indexOffsetByTests.filter { $0.limit == nil && $0.distance >= 0 } + ) { + test in + let max = 10 + let c = toCollection(0..= max { + expectCrashLater() + } + c.formIndex(&new, offsetBy: numericCast(test.distance)) + expectEqual(test.expectedOffset!, extractValue(c[new]).value, + stackTrace: SourceLocStack().with(test.loc)) + } + + self.test("\(testNamePrefix)/index(_:offsetBy: -n)/semantics") + .forEach( + in: indexOffsetByTests.filter { $0.limit == nil && $0.distance < 0 } + ) { + test in + let c = toCollection(0..<20) + let start = c.nthIndex(test.startOffset) + + if test.expectedOffset! < 0 || !collectionIsBidirectional { + expectCrashLater() + } + let new = c.index(start, offsetBy: numericCast(test.distance)) + expectEqual(test.expectedOffset!, extractValue(c[new]).value, + stackTrace: SourceLocStack().with(test.loc)) + } + self.test("\(testNamePrefix)/formIndex(_:offsetBy: -n)/semantics") + .forEach( + in: indexOffsetByTests.filter { $0.limit == nil && $0.distance < 0 } + ) { + test in + let c = toCollection(0..<20) + var new = c.nthIndex(test.startOffset) + + if test.expectedOffset! < 0 || !collectionIsBidirectional { + expectCrashLater() + } + c.formIndex(&new, offsetBy: numericCast(test.distance)) + expectEqual(test.expectedOffset!, extractValue(c[new]).value, + stackTrace: SourceLocStack().with(test.loc)) + } + + self.test("\(testNamePrefix)/index(_:offsetBy: n, limitedBy:)/semantics") { + for test in indexOffsetByTests.filter( + {$0.limit != nil && $0.distance >= 0} + ) { + let c = toCollection(0..<20) + let limit = c.nthIndex(test.limit.unsafelyUnwrapped) + let new = c.index( + c.nthIndex(test.startOffset), + offsetBy: numericCast(test.distance), + limitedBy: limit) + if let expectedOffset = test.expectedOffset { + expectEqual(c.nthIndex(expectedOffset), new!, + stackTrace: SourceLocStack().with(test.loc)) + } else { + expectNil(new) + } + } + } + + self.test("\(testNamePrefix)/formIndex(_:offsetBy: n, limitedBy:)/semantics") { + for test in indexOffsetByTests.filter( + {$0.limit != nil && $0.distance >= 0} + ) { + let c = toCollection(0..<20) + let limit = c.nthIndex(test.limit.unsafelyUnwrapped) + var new = c.nthIndex(test.startOffset) + let exact = c.formIndex(&new, offsetBy: numericCast(test.distance), limitedBy: limit) + if let expectedOffset = test.expectedOffset { + expectEqual(c.nthIndex(expectedOffset), new, + stackTrace: SourceLocStack().with(test.loc)) + expectTrue(exact, stackTrace: SourceLocStack().with(test.loc)) + } else { + // Clamped to the limit + expectEqual(limit, new, stackTrace: SourceLocStack().with(test.loc)) + expectFalse(exact, stackTrace: SourceLocStack().with(test.loc)) + } + } + } + + self.test("\(testNamePrefix)/index(_:offsetBy: -n, limitedBy:)/semantics") + .forEach( + in: indexOffsetByTests.filter { $0.limit != nil && $0.distance < 0 } + ) { + test in + let c = toCollection(0..<20) + let limit = c.nthIndex(test.limit.unsafelyUnwrapped) + if collectionIsBidirectional { + let new = c.index( + c.nthIndex(test.startOffset), + offsetBy: numericCast(test.distance), + limitedBy: limit) + if let expectedOffset = test.expectedOffset { + expectEqual(c.nthIndex(expectedOffset), new!, + stackTrace: SourceLocStack().with(test.loc)) + } else { + expectNil(new) + } + } else { + expectCrashLater() + _ = c.index( + c.nthIndex(test.startOffset), + offsetBy: numericCast(test.distance), + limitedBy: limit) + } + } + + self.test("\(testNamePrefix)/formIndex(_:offsetBy: -n, limitedBy:)/semantics") + .forEach( + in: indexOffsetByTests.filter { $0.limit != nil && $0.distance < 0 } + ) { + test in + let c = toCollection(0..<20) + let limit = c.nthIndex(test.limit.unsafelyUnwrapped) + var new = c.nthIndex(test.startOffset) + if collectionIsBidirectional { + let exact = c.formIndex( + &new, + offsetBy: numericCast(test.distance), + limitedBy: limit) + if let expectedOffset = test.expectedOffset { + expectEqual(c.nthIndex(expectedOffset), new, + stackTrace: SourceLocStack().with(test.loc)) + expectTrue(exact, stackTrace: SourceLocStack().with(test.loc)) + } else { + expectEqual(limit, new, stackTrace: SourceLocStack().with(test.loc)) + expectFalse(exact, stackTrace: SourceLocStack().with(test.loc)) + } + } else { + expectCrashLater() + _ = c.formIndex(&new, offsetBy: numericCast(test.distance), limitedBy: limit) + } + } + + } + func addCommonTests< + C, CollectionWithEquatableElement + >( + + _ testNamePrefix: String = "", + makeCollection: @escaping ([C.Iterator.Element]) -> C, + wrapValue: @escaping (OpaqueValue) -> C.Iterator.Element, + extractValue: @escaping (C.Iterator.Element) -> OpaqueValue, + + makeCollectionOfEquatable: @escaping ( + [CollectionWithEquatableElement.Iterator.Element] + ) -> CollectionWithEquatableElement, + + wrapValueIntoEquatable: @escaping ( + MinimalEquatableValue) -> CollectionWithEquatableElement.Iterator.Element, + + extractValueFromEquatable: @escaping ((CollectionWithEquatableElement.Iterator.Element) -> MinimalEquatableValue), + + resiliencyChecks: CollectionMisuseResiliencyChecks = .all, + outOfBoundsIndexOffset: Int = 1, + outOfBoundsSubscriptOffset: Int = 1, + collectionIsBidirectional: Bool + + ) where + + C : RandomAccessCollection, + CollectionWithEquatableElement : RandomAccessCollection, + CollectionWithEquatableElement.Iterator.Element : Equatable + { + + if !checksAdded.insert( + "\(testNamePrefix).\(C.self).\(#function)" + ).inserted { + return + } + + func toCollection(_ r: Range) -> C { + return makeCollection(r.map { wrapValue(OpaqueValue($0)) }) + } + + self.test("\(testNamePrefix).index(after:)/semantics") { + for test in indexAfterTests { + let c = toCollection(test.start.. test.endOffset) + if backwards && !collectionIsBidirectional { + expectCrashLater() + } + + let d = c.distance( + from: c.nthIndex(test.startOffset), to: c.nthIndex(test.endOffset)) + expectEqual( + numericCast(test.expectedDistance), + d, stackTrace: SourceLocStack().with(test.loc)) + } + + self.test("\(testNamePrefix)/index(_:offsetBy: n)/semantics") + .forEach( + in: indexOffsetByTests.filter { $0.limit == nil && $0.distance >= 0 } + ) { + test in + let max = 10 + let c = toCollection(0..= max { + expectCrashLater() + } + let new = c.index( + c.nthIndex(test.startOffset), + offsetBy: numericCast(test.distance)) + + // Since the `nthIndex(offset:)` method performs the same operation + // (i.e. advances `c.startIndex` by `test.distance`, it would be + // silly to compare index values. Luckily the underlying collection + // contains exactly index offsets. + expectEqual(test.expectedOffset!, extractValue(c[new]).value, + stackTrace: SourceLocStack().with(test.loc)) + } + + self.test("\(testNamePrefix)/formIndex(_:offsetBy: n)/semantics") + .forEach( + in: indexOffsetByTests.filter { $0.limit == nil && $0.distance >= 0 } + ) { + test in + let max = 10 + let c = toCollection(0..= max { + expectCrashLater() + } + c.formIndex(&new, offsetBy: numericCast(test.distance)) + expectEqual(test.expectedOffset!, extractValue(c[new]).value, + stackTrace: SourceLocStack().with(test.loc)) + } + + self.test("\(testNamePrefix)/index(_:offsetBy: -n)/semantics") + .forEach( + in: indexOffsetByTests.filter { $0.limit == nil && $0.distance < 0 } + ) { + test in + let c = toCollection(0..<20) + let start = c.nthIndex(test.startOffset) + + if test.expectedOffset! < 0 || !collectionIsBidirectional { + expectCrashLater() + } + let new = c.index(start, offsetBy: numericCast(test.distance)) + expectEqual(test.expectedOffset!, extractValue(c[new]).value, + stackTrace: SourceLocStack().with(test.loc)) + } + self.test("\(testNamePrefix)/formIndex(_:offsetBy: -n)/semantics") + .forEach( + in: indexOffsetByTests.filter { $0.limit == nil && $0.distance < 0 } + ) { + test in + let c = toCollection(0..<20) + var new = c.nthIndex(test.startOffset) + + if test.expectedOffset! < 0 || !collectionIsBidirectional { + expectCrashLater() + } c.formIndex(&new, offsetBy: numericCast(test.distance)) -% end expectEqual(test.expectedOffset!, extractValue(c[new]).value, stackTrace: SourceLocStack().with(test.loc)) } -% end self.test("\(testNamePrefix)/index(_:offsetBy: n, limitedBy:)/semantics") { for test in indexOffsetByTests.filter( @@ -1757,5 +2552,4 @@ extension TestSuite { } } -% end } diff --git a/stdlib/private/StdlibCollectionUnittest/CheckMutableCollectionType.swift.gyb b/stdlib/private/StdlibCollectionUnittest/CheckMutableCollectionType.swift similarity index 87% rename from stdlib/private/StdlibCollectionUnittest/CheckMutableCollectionType.swift.gyb rename to stdlib/private/StdlibCollectionUnittest/CheckMutableCollectionType.swift index 00e594064400d..2c2bd702838f7 100644 --- a/stdlib/private/StdlibCollectionUnittest/CheckMutableCollectionType.swift.gyb +++ b/stdlib/private/StdlibCollectionUnittest/CheckMutableCollectionType.swift @@ -493,7 +493,7 @@ func checkSortedPredicateThrow( expectEqualSequence( elements.map { $0.value }, c.map { extract($0).value }) - + // If `sorted` throws then result will be empty else // returned result must be sorted. if thrown { @@ -512,33 +512,137 @@ func checkSortedPredicateThrow( } } -% for predicate in [False, True]: - -self.test("\(testNamePrefix).sorted/DispatchesThrough_withUnsafeMutableBufferPointerIfSupported/${'Predicate' if predicate else 'WhereElementIsComparable'}") { +self.test("\(testNamePrefix).sorted/DispatchesThrough_withUnsafeMutableBufferPointerIfSupported/WhereElementIsComparable") { let sequence = [ 5, 4, 3, 2, 1 ] -% if predicate: - let elements: [OpaqueValue] = + let elements: [MinimalComparableValue] = zip(sequence, 0.. Bool), + lessImpl: @escaping ((Int, Int) -> Bool), + verifyOrder: Bool +) { + MinimalComparableValue.equalImpl.value = equalImpl + MinimalComparableValue.lessImpl.value = lessImpl + + let extract = extractValueFromComparable let elements: [MinimalComparableValue] = zip(sequence, 0.. Bool) in + for i in 0..<7 { + forAllPermutations(i) { (sequence) in + checkSort_WhereElementIsComparable( + sequence: sequence, + equalImpl: { + !comparisonPredicate($0, $1) && + !comparisonPredicate($1, $0) + }, + lessImpl: comparisonPredicate, + verifyOrder: false) + } + } + } +} + +self.test("\(testNamePrefix).sorted/ThrowingPredicate") { + for test in partitionExhaustiveTests { + forAllPermutations(test.sequence) { (sequence) in + for i in 0..] = + zip(sequence, 0.. Bool), lessImpl: @escaping ((Int, Int) -> Bool), verifyOrder: Bool ) { -% if predicate: let extract = extractValue let elements: [OpaqueValue] = zip(sequence, 0.. Bool) in for i in 0..<7 { forAllPermutations(i) { (sequence) in - checkSort_${'Predicate' if predicate else 'WhereElementIsComparable'}( + checkSort_Predicate( sequence: sequence, equalImpl: { !comparisonPredicate($0, $1) && @@ -660,7 +751,6 @@ self.test("\(testNamePrefix).sorted/ThrowingPredicateWithLargeNumberElements") { } } -% end //===----------------------------------------------------------------------===// // partition(by:) @@ -994,22 +1084,111 @@ func checkSortPredicateThrow( return lessImpl(extractValue(lhs).value, extractValue(rhs).value) } } catch {} - + //Check no element should lost and added expectEqualsUnordered( sequence, c.map { extract($0).value }) } -% for predicate in [False, True]: -func checkSortInPlace_${'Predicate' if predicate else 'WhereElementIsComparable'}( +func checkSortInPlace_WhereElementIsComparable( + sequence: [Int], + equalImpl: @escaping ((Int, Int) -> Bool), + lessImpl: @escaping ((Int, Int) -> Bool), + verifyOrder: Bool +) { + MinimalComparableValue.equalImpl.value = equalImpl + MinimalComparableValue.lessImpl.value = lessImpl + + let extract = extractValueFromComparable + let elements: [MinimalComparableValue] = + zip(sequence, 0.. Bool) in + for i in 0..<7 { + forAllPermutations(i) { (sequence) in + checkSortInPlace_WhereElementIsComparable( + sequence: sequence, + equalImpl: { + !comparisonPredicate($0, $1) && + !comparisonPredicate($1, $0) + }, + lessImpl: comparisonPredicate, + verifyOrder: false) + } + } + } +} + +self.test("\(testNamePrefix).sort/ThrowingPredicate") { + for test in partitionExhaustiveTests { + forAllPermutations(test.sequence) { (sequence) in + for i in 0.. Bool), lessImpl: @escaping ((Int, Int) -> Bool), verifyOrder: Bool ) { -% if predicate: let extract = extractValue let elements: [OpaqueValue] = zip(sequence, 0.. Bool) in for i in 0..<7 { forAllPermutations(i) { (sequence) in - checkSortInPlace_${'Predicate' if predicate else 'WhereElementIsComparable'}( + checkSortInPlace_Predicate( sequence: sequence, equalImpl: { !comparisonPredicate($0, $1) && @@ -1108,8 +1274,6 @@ self.test("\(testNamePrefix).sort/ThrowingPredicateWithLargeNumberElements") { } } } - -% end //===----------------------------------------------------------------------===// diff --git a/stdlib/private/StdlibCollectionUnittest/CheckSequenceInstance.swift b/stdlib/private/StdlibCollectionUnittest/CheckSequenceInstance.swift new file mode 100644 index 0000000000000..94ba6cfff11d1 --- /dev/null +++ b/stdlib/private/StdlibCollectionUnittest/CheckSequenceInstance.swift @@ -0,0 +1,257 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + + +import StdlibUnittest + +// Generate two overloads: one for Array (which will get +// picked up when the caller passes a literal), and another that +// accepts any appropriate Collection type. + +public func checkIterator< + Expected: Collection, I : IteratorProtocol +>( + _ expected: Expected, + _ iterator: I, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line, + resiliencyChecks: CollectionMisuseResiliencyChecks = .all, + sameValue: (Expected.Iterator.Element, Expected.Iterator.Element) -> Bool +) where I.Element == Expected.Iterator.Element { + // Copying a `IteratorProtocol` is allowed. + var mutableGen = iterator + var actual: [Expected.Iterator.Element] = [] + while let e = mutableGen.next() { + actual.append(e) + } + expectEqualSequence( + expected, actual, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), sameValue: sameValue) + + // Having returned `.None` once, a `IteratorProtocol` should not generate + // more elements. + for _ in 0..<10 { + expectNil(mutableGen.next(), message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) + } +} + +public func checkIterator< + Expected: Collection, I : IteratorProtocol +>( + _ expected: Expected, + _ iterator: I, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line, + resiliencyChecks: CollectionMisuseResiliencyChecks = .all +) where I.Element == Expected.Iterator.Element, Expected.Iterator.Element : Equatable { + checkIterator( + expected, iterator, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), showFrame: false, + resiliencyChecks: resiliencyChecks + ) { $0 == $1 } +} + +public func checkSequence< + Expected: Collection, S : Sequence +>( + _ expected: Expected, + _ sequence: S, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line, + resiliencyChecks: CollectionMisuseResiliencyChecks = .all, + sameValue: (Expected.Iterator.Element, Expected.Iterator.Element) -> Bool +) where S.Iterator.Element == Expected.Iterator.Element { + let expectedCount: Int = numericCast(expected.count) + checkIterator( + expected, sequence.makeIterator(), message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), + resiliencyChecks: resiliencyChecks, + sameValue: sameValue) + + expectGE( + expectedCount, sequence.underestimatedCount, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) + + // Test `_copyContents(initializing:)` if we can do so without destroying the + // sequence. + _ = sequence._preprocessingPass { () -> Void in + var count = 0 + for _ in sequence { count += 1 } + let ptr = UnsafeMutablePointer.allocate(capacity: count) + let buf = UnsafeMutableBufferPointer(start: ptr, count: count) + var (remainders,writtenUpTo) = sequence._copyContents(initializing: buf) + expectTrue(remainders.next() == nil, + "_copyContents returned unwritten elements") + expectTrue(writtenUpTo == buf.endIndex, + "_copyContents failed to use entire buffer") + expectEqualSequence(expected, buf, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), sameValue: sameValue) + ptr.deinitialize(count: count) + ptr.deallocate() + } + + // Test `_copyToContiguousArray()` if we can do so + // without destroying the sequence. + _ = sequence._preprocessingPass { () -> Void in + let copy = sequence._copyToContiguousArray() + expectEqualSequence(expected, copy, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), sameValue: sameValue) + } +} + +public func checkSequence< + Expected: Collection, S : Sequence +>( + _ expected: Expected, + _ sequence: S, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line, + resiliencyChecks: CollectionMisuseResiliencyChecks = .all +) where + S.Iterator.Element == Expected.Iterator.Element, + S.Iterator.Element : Equatable { + + checkSequence( + expected, sequence, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), showFrame: false, + resiliencyChecks: resiliencyChecks + ) { $0 == $1 } +} + +public func checkIterator< + Element, I : IteratorProtocol +>( + _ expected: Array, + _ iterator: I, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line, + resiliencyChecks: CollectionMisuseResiliencyChecks = .all, + sameValue: (Element, Element) -> Bool +) where I.Element == Element { + // Copying a `IteratorProtocol` is allowed. + var mutableGen = iterator + var actual: [Element] = [] + while let e = mutableGen.next() { + actual.append(e) + } + expectEqualSequence( + expected, actual, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), sameValue: sameValue) + + // Having returned `.None` once, a `IteratorProtocol` should not generate + // more elements. + for _ in 0..<10 { + expectNil(mutableGen.next(), message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) + } +} + +public func checkIterator< + Element, I : IteratorProtocol +>( + _ expected: Array, + _ iterator: I, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line, + resiliencyChecks: CollectionMisuseResiliencyChecks = .all +) where I.Element == Element, Element : Equatable { + checkIterator( + expected, iterator, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), showFrame: false, + resiliencyChecks: resiliencyChecks + ) { $0 == $1 } +} + +public func checkSequence< + Element, S : Sequence +>( + _ expected: Array, + _ sequence: S, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line, + resiliencyChecks: CollectionMisuseResiliencyChecks = .all, + sameValue: (Element, Element) -> Bool +) where S.Iterator.Element == Element { + let expectedCount: Int = numericCast(expected.count) + checkIterator( + expected, sequence.makeIterator(), message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), + resiliencyChecks: resiliencyChecks, + sameValue: sameValue) + + expectGE( + expectedCount, sequence.underestimatedCount, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) + + // Test `_copyContents(initializing:)` if we can do so without destroying the + // sequence. + _ = sequence._preprocessingPass { () -> Void in + var count = 0 + for _ in sequence { count += 1 } + let ptr = UnsafeMutablePointer.allocate(capacity: count) + let buf = UnsafeMutableBufferPointer(start: ptr, count: count) + var (remainders,writtenUpTo) = sequence._copyContents(initializing: buf) + expectTrue(remainders.next() == nil, + "_copyContents returned unwritten elements") + expectTrue(writtenUpTo == buf.endIndex, + "_copyContents failed to use entire buffer") + expectEqualSequence(expected, buf, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), sameValue: sameValue) + ptr.deinitialize(count: count) + ptr.deallocate() + } + + // Test `_copyToContiguousArray()` if we can do so + // without destroying the sequence. + _ = sequence._preprocessingPass { () -> Void in + let copy = sequence._copyToContiguousArray() + expectEqualSequence(expected, copy, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), sameValue: sameValue) + } +} + +public func checkSequence< + Element, S : Sequence +>( + _ expected: Array, + _ sequence: S, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line, + resiliencyChecks: CollectionMisuseResiliencyChecks = .all +) where + S.Iterator.Element == Element, + S.Iterator.Element : Equatable { + + checkSequence( + expected, sequence, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), showFrame: false, + resiliencyChecks: resiliencyChecks + ) { $0 == $1 } +} + diff --git a/stdlib/private/StdlibCollectionUnittest/CheckSequenceInstance.swift.gyb b/stdlib/private/StdlibCollectionUnittest/CheckSequenceInstance.swift.gyb deleted file mode 100644 index 40fad2795f91d..0000000000000 --- a/stdlib/private/StdlibCollectionUnittest/CheckSequenceInstance.swift.gyb +++ /dev/null @@ -1,126 +0,0 @@ -//===--- CheckSequenceInstance.swift.gyb ----------------------*- swift -*-===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -%{ -from gyb_stdlib_unittest_support import TRACE, stackTrace, trace -}% - -import StdlibUnittest - -// Generate two overloads: one for Array (which will get -// picked up when the caller passes a literal), and another that -// accepts any appropriate Collection type. -% for genericParam, Element, Expected in zip( -% ('Expected: Collection', 'Element'), -% ('Expected.Iterator.Element', 'Element'), -% ('Expected', 'Array')): - -public func checkIterator< - ${genericParam}, I : IteratorProtocol ->( - _ expected: ${Expected}, - _ iterator: I, - ${TRACE}, - resiliencyChecks: CollectionMisuseResiliencyChecks = .all, - sameValue: (${Element}, ${Element}) -> Bool -) where I.Element == ${Element} { - // Copying a `IteratorProtocol` is allowed. - var mutableGen = iterator - var actual: [${Element}] = [] - while let e = mutableGen.next() { - actual.append(e) - } - expectEqualSequence( - expected, actual, ${trace}, sameValue: sameValue) - - // Having returned `.None` once, a `IteratorProtocol` should not generate - // more elements. - for _ in 0..<10 { - expectNil(mutableGen.next(), ${trace}) - } -} - -public func checkIterator< - ${genericParam}, I : IteratorProtocol ->( - _ expected: ${Expected}, - _ iterator: I, - ${TRACE}, - resiliencyChecks: CollectionMisuseResiliencyChecks = .all -) where I.Element == ${Element}, ${Element} : Equatable { - checkIterator( - expected, iterator, ${trace}, showFrame: false, - resiliencyChecks: resiliencyChecks - ) { $0 == $1 } -} - -public func checkSequence< - ${genericParam}, S : Sequence ->( - _ expected: ${Expected}, - _ sequence: S, - ${TRACE}, - resiliencyChecks: CollectionMisuseResiliencyChecks = .all, - sameValue: (${Element}, ${Element}) -> Bool -) where S.Iterator.Element == ${Element} { - let expectedCount: Int = numericCast(expected.count) - checkIterator( - expected, sequence.makeIterator(), ${trace}, - resiliencyChecks: resiliencyChecks, - sameValue: sameValue) - - expectGE( - expectedCount, sequence.underestimatedCount, ${trace}) - - // Test `_copyContents(initializing:)` if we can do so without destroying the - // sequence. - _ = sequence._preprocessingPass { () -> Void in - var count = 0 - for _ in sequence { count += 1 } - let ptr = UnsafeMutablePointer.allocate(capacity: count) - let buf = UnsafeMutableBufferPointer(start: ptr, count: count) - var (remainders,writtenUpTo) = sequence._copyContents(initializing: buf) - expectTrue(remainders.next() == nil, - "_copyContents returned unwritten elements") - expectTrue(writtenUpTo == buf.endIndex, - "_copyContents failed to use entire buffer") - expectEqualSequence(expected, buf, ${trace}, sameValue: sameValue) - ptr.deinitialize(count: count) - ptr.deallocate() - } - - // Test `_copyToContiguousArray()` if we can do so - // without destroying the sequence. - _ = sequence._preprocessingPass { () -> Void in - let copy = sequence._copyToContiguousArray() - expectEqualSequence(expected, copy, ${trace}, sameValue: sameValue) - } -} - -public func checkSequence< - ${genericParam}, S : Sequence ->( - _ expected: ${Expected}, - _ sequence: S, - ${TRACE}, - resiliencyChecks: CollectionMisuseResiliencyChecks = .all -) where - S.Iterator.Element == ${Element}, - S.Iterator.Element : Equatable { - - checkSequence( - expected, sequence, ${trace}, showFrame: false, - resiliencyChecks: resiliencyChecks - ) { $0 == $1 } -} - -% end diff --git a/stdlib/private/StdlibCollectionUnittest/CheckSequenceType.swift b/stdlib/private/StdlibCollectionUnittest/CheckSequenceType.swift index e1551c40cccfa..afcc1406aaf56 100644 --- a/stdlib/private/StdlibCollectionUnittest/CheckSequenceType.swift +++ b/stdlib/private/StdlibCollectionUnittest/CheckSequenceType.swift @@ -149,7 +149,7 @@ public struct FindTest { public init( expected: Int?, element: Int, sequence: [Int], - expectedLeftoverSequence: [Int], + expectedLeftoverSequence: [Int] = [], file: String = #file, line: UInt = #line ) { self.expected = expected @@ -1935,17 +1935,17 @@ self.test("\(testNamePrefix).forEach/semantics") { } //===----------------------------------------------------------------------===// -// first() +// first(where:) //===----------------------------------------------------------------------===// -self.test("\(testNamePrefix).first/semantics") { +self.test("\(testNamePrefix).first(where:)/semantics") { for test in findTests { let s = makeWrappedSequenceWithEquatableElement(test.sequence) let closureLifetimeTracker = LifetimeTracked(0) - let found = s.first { + let found = s.first(where: { _blackHole(closureLifetimeTracker) return $0 == wrapValueIntoEquatable(test.element) - } + }) expectEqual( test.expected == nil ? nil : wrapValueIntoEquatable(test.element), found, diff --git a/stdlib/private/StdlibCollectionUnittest/LoggingWrappers.swift b/stdlib/private/StdlibCollectionUnittest/LoggingWrappers.swift new file mode 100644 index 0000000000000..0dad5593cdfaf --- /dev/null +++ b/stdlib/private/StdlibCollectionUnittest/LoggingWrappers.swift @@ -0,0 +1,3169 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +import StdlibUnittest + +public protocol Wrapper { + associatedtype Base + init(wrapping base: Base) + var base: Base { get set } +} + +public protocol LoggingType : Wrapper { + associatedtype Log : AnyObject +} + +extension LoggingType { + public var log: Log.Type { + return Log.self + } + + public var selfType: Any.Type { + return type(of: self) + } +} + +//===----------------------------------------------------------------------===// +// Iterator +//===----------------------------------------------------------------------===// + +public class IteratorLog { + public static func dispatchTester( + _ iterator: I + ) -> LoggingIterator> { + return LoggingIterator(wrapping: LoggingIterator(wrapping: iterator)) + } + public static var next = TypeIndexed(0) +} + +public struct LoggingIterator + : IteratorProtocol, LoggingType { + + public typealias Log = IteratorLog + + public init(wrapping base: Base) { + self.base = base + } + + public mutating func next() -> Base.Element? { + Log.next[selfType] += 1 + return base.next() + } + + public var base: Base +} + +//===----------------------------------------------------------------------===// +// Sequence and Collection logs +//===----------------------------------------------------------------------===// + +public class SequenceLog { + public static func dispatchTester( + _ s: S + ) -> LoggingSequence> { + return LoggingSequence(wrapping: LoggingSequence(wrapping: s)) + } + public static var makeIterator = TypeIndexed(0) + public static var underestimatedCount = TypeIndexed(0) + public static var map = TypeIndexed(0) + public static var filter = TypeIndexed(0) + public static var forEach = TypeIndexed(0) + public static var dropFirst = TypeIndexed(0) + public static var dropLast = TypeIndexed(0) + public static var dropWhile = TypeIndexed(0) + public static var prefixWhile = TypeIndexed(0) + public static var prefixMaxLength = TypeIndexed(0) + public static var suffixMaxLength = TypeIndexed(0) + public static var split = TypeIndexed(0) + public static var _customContainsEquatableElement = TypeIndexed(0) + public static var _preprocessingPass = TypeIndexed(0) + public static var _copyToContiguousArray = TypeIndexed(0) + public static var _copyContents = TypeIndexed(0) +} + +public class CollectionLog : SequenceLog { + public class func dispatchTester( + _ c: C + ) -> LoggingCollection> { + return LoggingCollection(wrapping: LoggingCollection(wrapping: c)) + } + public static var startIndex = TypeIndexed(0) + public static var endIndex = TypeIndexed(0) + public static var subscriptIndex = TypeIndexed(0) + public static var subscriptRange = TypeIndexed(0) + public static var _failEarlyRangeCheckIndex = TypeIndexed(0) + public static var _failEarlyRangeCheckRange = TypeIndexed(0) + public static var successor = TypeIndexed(0) + public static var formSuccessor = TypeIndexed(0) + public static var indices = TypeIndexed(0) + public static var prefixUpTo = TypeIndexed(0) + public static var prefixThrough = TypeIndexed(0) + public static var suffixFrom = TypeIndexed(0) + public static var isEmpty = TypeIndexed(0) + public static var count = TypeIndexed(0) + public static var _customIndexOfEquatableElement = TypeIndexed(0) + public static var first = TypeIndexed(0) + public static var advance = TypeIndexed(0) + public static var advanceLimit = TypeIndexed(0) + public static var distance = TypeIndexed(0) +} + +public class BidirectionalCollectionLog : SequenceLog { + public class func dispatchTester( + _ c: C + ) -> LoggingBidirectionalCollection> { + return LoggingBidirectionalCollection( + wrapping: LoggingBidirectionalCollection(wrapping: c)) + } + public static var predecessor = TypeIndexed(0) + public static var formPredecessor = TypeIndexed(0) + public static var last = TypeIndexed(0) +} + +public class MutableCollectionLog : CollectionLog { + public class func dispatchTester( + _ c: C + ) -> LoggingMutableCollection> { + return LoggingMutableCollection( + wrapping: LoggingMutableCollection(wrapping: c)) + } + public static var subscriptIndexSet = TypeIndexed(0) + public static var subscriptRangeSet = TypeIndexed(0) + public static var partitionBy = TypeIndexed(0) + public static var _withUnsafeMutableBufferPointerIfSupported = TypeIndexed(0) + public static var _withUnsafeMutableBufferPointerIfSupportedNonNilReturns = + TypeIndexed(0) +} + +/// Data container to keep track of how many times each `Base` type calls methods +/// of `RangeReplaceableCollection`. +/// +/// Each static variable is a mapping of Type -> Number of calls. +public class RangeReplaceableCollectionLog : CollectionLog { + public static var init_ = TypeIndexed(0) + public static var initRepeating = TypeIndexed(0) + public static var initWithSequence = TypeIndexed(0) + + public static var _customRemoveLast = TypeIndexed(0) + public static var _customRemoveLastN = TypeIndexed(0) + public static var append = TypeIndexed(0) + public static var appendContentsOf = TypeIndexed(0) + public static var insert = TypeIndexed(0) + public static var insertContentsOf = TypeIndexed(0) + public static var removeAll = TypeIndexed(0) + public static var removeAt = TypeIndexed(0) + public static var removeFirst = TypeIndexed(0) + public static var removeFirstN = TypeIndexed(0) + public static var removeSubrange = TypeIndexed(0) + public static var replaceSubrange = TypeIndexed(0) + public static var reserveCapacity = TypeIndexed(0) + + public class func dispatchTester( + _ rrc: C + ) -> LoggingRangeReplaceableCollection> { + return LoggingRangeReplaceableCollection( + wrapping: LoggingRangeReplaceableCollection(wrapping: rrc) + ) + } +} + +//===----------------------------------------------------------------------===// +// Sequence and Collection that count method calls +//===----------------------------------------------------------------------===// + +/// Interposes between `Sequence` method calls to increment each method's +/// counter. +public struct LoggingSequence< + Base : Sequence +> : Sequence, LoggingType { + + public var base: Base + + public typealias Log = SequenceLog + + public init(wrapping base: Base) { + self.base = base + } + + public typealias Iterator = LoggingIterator + + public func makeIterator() -> Iterator { + Log.makeIterator[selfType] += 1 + return LoggingIterator(wrapping: base.makeIterator()) + } + + public var underestimatedCount: Int { + Log.underestimatedCount[selfType] += 1 + return base.underestimatedCount + } + + public func map( + _ transform: (Base.Iterator.Element) throws -> T + ) rethrows -> [T] { + Log.map[selfType] += 1 + return try base.map(transform) + } + + public func filter( + _ isIncluded: (Base.Iterator.Element) throws -> Bool + ) rethrows -> [Base.Iterator.Element] { + Log.filter[selfType] += 1 + return try base.filter(isIncluded) + } + + public func forEach( + _ body: (Base.Iterator.Element) throws -> Void + ) rethrows { + Log.forEach[selfType] += 1 + try base.forEach(body) + } + + public typealias SubSequence = Base.SubSequence + + public func dropFirst(_ n: Int) -> SubSequence { + Log.dropFirst[selfType] += 1 + return base.dropFirst(n) + } + + public func dropLast(_ n: Int) -> SubSequence { + Log.dropLast[selfType] += 1 + return base.dropLast(n) + } + + public func drop( + while predicate: (Base.Iterator.Element) throws -> Bool + ) rethrows -> SubSequence { + Log.dropWhile[selfType] += 1 + return try base.drop(while: predicate) + } + + public func prefix(_ maxLength: Int) -> SubSequence { + Log.prefixMaxLength[selfType] += 1 + return base.prefix(maxLength) + } + + public func prefix( + while predicate: (Base.Iterator.Element) throws -> Bool + ) rethrows -> SubSequence { + Log.prefixWhile[selfType] += 1 + return try base.prefix(while: predicate) + } + + public func suffix(_ maxLength: Int) -> SubSequence { + Log.suffixMaxLength[selfType] += 1 + return base.suffix(maxLength) + } + + public func split( + maxSplits: Int = Int.max, + omittingEmptySubsequences: Bool = true, + whereSeparator isSeparator: (Base.Iterator.Element) throws -> Bool + ) rethrows -> [SubSequence] { + Log.split[selfType] += 1 + return try base.split( + maxSplits: maxSplits, + omittingEmptySubsequences: omittingEmptySubsequences, + whereSeparator: isSeparator) + } + + public func _customContainsEquatableElement( + _ element: Base.Iterator.Element + ) -> Bool? { + Log._customContainsEquatableElement[selfType] += 1 + return base._customContainsEquatableElement(element) + } + + /// If `self` is multi-pass (i.e., a `Collection`), invoke + /// `preprocess` on `self` and return its result. Otherwise, return + /// `nil`. + public func _preprocessingPass( + _ preprocess: () throws -> R + ) rethrows -> R? { + Log._preprocessingPass[selfType] += 1 + return try base._preprocessingPass(preprocess) + } + + /// Create a native array buffer containing the elements of `self`, + /// in the same order. + public func _copyToContiguousArray() + -> ContiguousArray { + Log._copyToContiguousArray[selfType] += 1 + return base._copyToContiguousArray() + } + + /// Copy a Sequence into an array. + public func _copyContents( + initializing buffer: UnsafeMutableBufferPointer + ) -> (Iterator,UnsafeMutableBufferPointer.Index) { + Log._copyContents[selfType] += 1 + let (it,idx) = base._copyContents(initializing: buffer) + return (Iterator(wrapping: it),idx) + } + + + +} +/// Interposes between `Collection` method calls to increment each method's +/// counter. +public struct LoggingCollection< + Base : Collection & Collection +> : Collection, LoggingType { + + public var base: Base + + public typealias Log = CollectionLog + + public init(wrapping base: Base) { + self.base = base + } + + public typealias Iterator = LoggingIterator + + public func makeIterator() -> Iterator { + Log.makeIterator[selfType] += 1 + return LoggingIterator(wrapping: base.makeIterator()) + } + + public var underestimatedCount: Int { + Log.underestimatedCount[selfType] += 1 + return base.underestimatedCount + } + + public func map( + _ transform: (Base.Iterator.Element) throws -> T + ) rethrows -> [T] { + Log.map[selfType] += 1 + return try base.map(transform) + } + + public func filter( + _ isIncluded: (Base.Iterator.Element) throws -> Bool + ) rethrows -> [Base.Iterator.Element] { + Log.filter[selfType] += 1 + return try base.filter(isIncluded) + } + + public func forEach( + _ body: (Base.Iterator.Element) throws -> Void + ) rethrows { + Log.forEach[selfType] += 1 + try base.forEach(body) + } + + public typealias SubSequence = Base.SubSequence + + public func dropFirst(_ n: Int) -> SubSequence { + Log.dropFirst[selfType] += 1 + return base.dropFirst(n) + } + + public func dropLast(_ n: Int) -> SubSequence { + Log.dropLast[selfType] += 1 + return base.dropLast(n) + } + + public func drop( + while predicate: (Base.Iterator.Element) throws -> Bool + ) rethrows -> SubSequence { + Log.dropWhile[selfType] += 1 + return try base.drop(while: predicate) + } + + public func prefix(_ maxLength: Int) -> SubSequence { + Log.prefixMaxLength[selfType] += 1 + return base.prefix(maxLength) + } + + public func prefix( + while predicate: (Base.Iterator.Element) throws -> Bool + ) rethrows -> SubSequence { + Log.prefixWhile[selfType] += 1 + return try base.prefix(while: predicate) + } + + public func suffix(_ maxLength: Int) -> SubSequence { + Log.suffixMaxLength[selfType] += 1 + return base.suffix(maxLength) + } + + public func split( + maxSplits: Int = Int.max, + omittingEmptySubsequences: Bool = true, + whereSeparator isSeparator: (Base.Iterator.Element) throws -> Bool + ) rethrows -> [SubSequence] { + Log.split[selfType] += 1 + return try base.split( + maxSplits: maxSplits, + omittingEmptySubsequences: omittingEmptySubsequences, + whereSeparator: isSeparator) + } + + public func _customContainsEquatableElement( + _ element: Base.Iterator.Element + ) -> Bool? { + Log._customContainsEquatableElement[selfType] += 1 + return base._customContainsEquatableElement(element) + } + + /// If `self` is multi-pass (i.e., a `Collection`), invoke + /// `preprocess` on `self` and return its result. Otherwise, return + /// `nil`. + public func _preprocessingPass( + _ preprocess: () throws -> R + ) rethrows -> R? { + Log._preprocessingPass[selfType] += 1 + return try base._preprocessingPass(preprocess) + } + + /// Create a native array buffer containing the elements of `self`, + /// in the same order. + public func _copyToContiguousArray() + -> ContiguousArray { + Log._copyToContiguousArray[selfType] += 1 + return base._copyToContiguousArray() + } + + /// Copy a Sequence into an array. + public func _copyContents( + initializing buffer: UnsafeMutableBufferPointer + ) -> (Iterator,UnsafeMutableBufferPointer.Index) { + Log._copyContents[selfType] += 1 + let (it,idx) = base._copyContents(initializing: buffer) + return (Iterator(wrapping: it),idx) + } + + public typealias Index = Base.Index + + public var startIndex: Index { + Log.startIndex[selfType] += 1 + return base.startIndex + } + + public var endIndex: Index { + Log.endIndex[selfType] += 1 + return base.endIndex + } + + public subscript(position: Index) -> Base.Iterator.Element { + get { + Log.subscriptIndex[selfType] += 1 + return base[position] + } + } + + public subscript(bounds: Range) -> SubSequence { + get { + Log.subscriptRange[selfType] += 1 + return base[bounds] + } + } + + public func _failEarlyRangeCheck(_ index: Index, bounds: Range) { + Log._failEarlyRangeCheckIndex[selfType] += 1 + base._failEarlyRangeCheck(index, bounds: bounds) + } + + public func _failEarlyRangeCheck(_ range: Range, bounds: Range) { + Log._failEarlyRangeCheckRange[selfType] += 1 + base._failEarlyRangeCheck(range, bounds: bounds) + } + + public func index(after i: Index) -> Index { + Log.successor[selfType] += 1 + return base.index(after: i) + } + + public func formIndex(after i: inout Index) { + Log.formSuccessor[selfType] += 1 + base.formIndex(after: &i) + } + + public typealias Indices = Base.Indices + + public var indices: Indices { + Log.indices[selfType] += 1 + return base.indices + } + + public func prefix(upTo end: Index) -> SubSequence { + Log.prefixUpTo[selfType] += 1 + return base.prefix(upTo: end) + } + + public func prefix(through position: Index) -> SubSequence { + Log.prefixThrough[selfType] += 1 + return base.prefix(through: position) + } + + public func suffix(from start: Index) -> SubSequence { + Log.suffixFrom[selfType] += 1 + return base.suffix(from: start) + } + + public var isEmpty: Bool { + Log.isEmpty[selfType] += 1 + return base.isEmpty + } + + public var count: Int { + Log.count[selfType] += 1 + return base.count + } + + public func _customIndexOfEquatableElement( + _ element: Base.Iterator.Element + ) -> Index?? { + Log._customIndexOfEquatableElement[selfType] += 1 + return base._customIndexOfEquatableElement(element) + } + + public var first: Base.Iterator.Element? { + Log.first[selfType] += 1 + return base.first + } + + public func index(_ i: Index, offsetBy n: Int) -> Index { + Log.advance[selfType] += 1 + return base.index(i, offsetBy: n) + } + + public func index( + _ i: Index, offsetBy n: Int, limitedBy limit: Index + ) -> Index? { + Log.advanceLimit[selfType] += 1 + return base.index(i, offsetBy: n, limitedBy: limit) + } + + public func distance(from start: Index, to end: Index) -> Int { + Log.distance[selfType] += 1 + return base.distance(from: start, to: end) + } + + +} +/// Interposes between `Collection` method calls to increment each method's +/// counter. +public struct LoggingBidirectionalCollection< + Base : Collection & BidirectionalCollection +> : BidirectionalCollection, Collection, LoggingType { + + public var base: Base + + public typealias Log = CollectionLog + + public init(wrapping base: Base) { + self.base = base + } + + public typealias Iterator = LoggingIterator + + public func makeIterator() -> Iterator { + Log.makeIterator[selfType] += 1 + return LoggingIterator(wrapping: base.makeIterator()) + } + + public var underestimatedCount: Int { + Log.underestimatedCount[selfType] += 1 + return base.underestimatedCount + } + + public func map( + _ transform: (Base.Iterator.Element) throws -> T + ) rethrows -> [T] { + Log.map[selfType] += 1 + return try base.map(transform) + } + + public func filter( + _ isIncluded: (Base.Iterator.Element) throws -> Bool + ) rethrows -> [Base.Iterator.Element] { + Log.filter[selfType] += 1 + return try base.filter(isIncluded) + } + + public func forEach( + _ body: (Base.Iterator.Element) throws -> Void + ) rethrows { + Log.forEach[selfType] += 1 + try base.forEach(body) + } + + public typealias SubSequence = Base.SubSequence + + public func dropFirst(_ n: Int) -> SubSequence { + Log.dropFirst[selfType] += 1 + return base.dropFirst(n) + } + + public func dropLast(_ n: Int) -> SubSequence { + Log.dropLast[selfType] += 1 + return base.dropLast(n) + } + + public func drop( + while predicate: (Base.Iterator.Element) throws -> Bool + ) rethrows -> SubSequence { + Log.dropWhile[selfType] += 1 + return try base.drop(while: predicate) + } + + public func prefix(_ maxLength: Int) -> SubSequence { + Log.prefixMaxLength[selfType] += 1 + return base.prefix(maxLength) + } + + public func prefix( + while predicate: (Base.Iterator.Element) throws -> Bool + ) rethrows -> SubSequence { + Log.prefixWhile[selfType] += 1 + return try base.prefix(while: predicate) + } + + public func suffix(_ maxLength: Int) -> SubSequence { + Log.suffixMaxLength[selfType] += 1 + return base.suffix(maxLength) + } + + public func split( + maxSplits: Int = Int.max, + omittingEmptySubsequences: Bool = true, + whereSeparator isSeparator: (Base.Iterator.Element) throws -> Bool + ) rethrows -> [SubSequence] { + Log.split[selfType] += 1 + return try base.split( + maxSplits: maxSplits, + omittingEmptySubsequences: omittingEmptySubsequences, + whereSeparator: isSeparator) + } + + public func _customContainsEquatableElement( + _ element: Base.Iterator.Element + ) -> Bool? { + Log._customContainsEquatableElement[selfType] += 1 + return base._customContainsEquatableElement(element) + } + + /// If `self` is multi-pass (i.e., a `Collection`), invoke + /// `preprocess` on `self` and return its result. Otherwise, return + /// `nil`. + public func _preprocessingPass( + _ preprocess: () throws -> R + ) rethrows -> R? { + Log._preprocessingPass[selfType] += 1 + return try base._preprocessingPass(preprocess) + } + + /// Create a native array buffer containing the elements of `self`, + /// in the same order. + public func _copyToContiguousArray() + -> ContiguousArray { + Log._copyToContiguousArray[selfType] += 1 + return base._copyToContiguousArray() + } + + /// Copy a Sequence into an array. + public func _copyContents( + initializing buffer: UnsafeMutableBufferPointer + ) -> (Iterator,UnsafeMutableBufferPointer.Index) { + Log._copyContents[selfType] += 1 + let (it,idx) = base._copyContents(initializing: buffer) + return (Iterator(wrapping: it),idx) + } + + public typealias Index = Base.Index + + public var startIndex: Index { + Log.startIndex[selfType] += 1 + return base.startIndex + } + + public var endIndex: Index { + Log.endIndex[selfType] += 1 + return base.endIndex + } + + public subscript(position: Index) -> Base.Iterator.Element { + get { + Log.subscriptIndex[selfType] += 1 + return base[position] + } + } + + public subscript(bounds: Range) -> SubSequence { + get { + Log.subscriptRange[selfType] += 1 + return base[bounds] + } + } + + public func _failEarlyRangeCheck(_ index: Index, bounds: Range) { + Log._failEarlyRangeCheckIndex[selfType] += 1 + base._failEarlyRangeCheck(index, bounds: bounds) + } + + public func _failEarlyRangeCheck(_ range: Range, bounds: Range) { + Log._failEarlyRangeCheckRange[selfType] += 1 + base._failEarlyRangeCheck(range, bounds: bounds) + } + + public func index(after i: Index) -> Index { + Log.successor[selfType] += 1 + return base.index(after: i) + } + + public func formIndex(after i: inout Index) { + Log.formSuccessor[selfType] += 1 + base.formIndex(after: &i) + } + + public typealias Indices = Base.Indices + + public var indices: Indices { + Log.indices[selfType] += 1 + return base.indices + } + + public func prefix(upTo end: Index) -> SubSequence { + Log.prefixUpTo[selfType] += 1 + return base.prefix(upTo: end) + } + + public func prefix(through position: Index) -> SubSequence { + Log.prefixThrough[selfType] += 1 + return base.prefix(through: position) + } + + public func suffix(from start: Index) -> SubSequence { + Log.suffixFrom[selfType] += 1 + return base.suffix(from: start) + } + + public var isEmpty: Bool { + Log.isEmpty[selfType] += 1 + return base.isEmpty + } + + public var count: Int { + Log.count[selfType] += 1 + return base.count + } + + public func _customIndexOfEquatableElement( + _ element: Base.Iterator.Element + ) -> Index?? { + Log._customIndexOfEquatableElement[selfType] += 1 + return base._customIndexOfEquatableElement(element) + } + + public var first: Base.Iterator.Element? { + Log.first[selfType] += 1 + return base.first + } + + public func index(_ i: Index, offsetBy n: Int) -> Index { + Log.advance[selfType] += 1 + return base.index(i, offsetBy: n) + } + + public func index( + _ i: Index, offsetBy n: Int, limitedBy limit: Index + ) -> Index? { + Log.advanceLimit[selfType] += 1 + return base.index(i, offsetBy: n, limitedBy: limit) + } + + public func distance(from start: Index, to end: Index) -> Int { + Log.distance[selfType] += 1 + return base.distance(from: start, to: end) + } + + + public func index(before i: Index) -> Index { + BidirectionalCollectionLog.predecessor[selfType] += 1 + return base.index(before: i) + } + + public func formIndex(before i: inout Index) { + BidirectionalCollectionLog.formPredecessor[selfType] += 1 + base.formIndex(before: &i) + } + + public var last: Iterator.Element? { + BidirectionalCollectionLog.last[selfType] += 1 + return base.last + } +} +/// Interposes between `Collection` method calls to increment each method's +/// counter. +public struct LoggingRandomAccessCollection< + Base : Collection & RandomAccessCollection +> : RandomAccessCollection, Collection, LoggingType { + + public var base: Base + + public typealias Log = CollectionLog + + public init(wrapping base: Base) { + self.base = base + } + + public typealias Iterator = LoggingIterator + + public func makeIterator() -> Iterator { + Log.makeIterator[selfType] += 1 + return LoggingIterator(wrapping: base.makeIterator()) + } + + public var underestimatedCount: Int { + Log.underestimatedCount[selfType] += 1 + return base.underestimatedCount + } + + public func map( + _ transform: (Base.Iterator.Element) throws -> T + ) rethrows -> [T] { + Log.map[selfType] += 1 + return try base.map(transform) + } + + public func filter( + _ isIncluded: (Base.Iterator.Element) throws -> Bool + ) rethrows -> [Base.Iterator.Element] { + Log.filter[selfType] += 1 + return try base.filter(isIncluded) + } + + public func forEach( + _ body: (Base.Iterator.Element) throws -> Void + ) rethrows { + Log.forEach[selfType] += 1 + try base.forEach(body) + } + + public typealias SubSequence = Base.SubSequence + + public func dropFirst(_ n: Int) -> SubSequence { + Log.dropFirst[selfType] += 1 + return base.dropFirst(n) + } + + public func dropLast(_ n: Int) -> SubSequence { + Log.dropLast[selfType] += 1 + return base.dropLast(n) + } + + public func drop( + while predicate: (Base.Iterator.Element) throws -> Bool + ) rethrows -> SubSequence { + Log.dropWhile[selfType] += 1 + return try base.drop(while: predicate) + } + + public func prefix(_ maxLength: Int) -> SubSequence { + Log.prefixMaxLength[selfType] += 1 + return base.prefix(maxLength) + } + + public func prefix( + while predicate: (Base.Iterator.Element) throws -> Bool + ) rethrows -> SubSequence { + Log.prefixWhile[selfType] += 1 + return try base.prefix(while: predicate) + } + + public func suffix(_ maxLength: Int) -> SubSequence { + Log.suffixMaxLength[selfType] += 1 + return base.suffix(maxLength) + } + + public func split( + maxSplits: Int = Int.max, + omittingEmptySubsequences: Bool = true, + whereSeparator isSeparator: (Base.Iterator.Element) throws -> Bool + ) rethrows -> [SubSequence] { + Log.split[selfType] += 1 + return try base.split( + maxSplits: maxSplits, + omittingEmptySubsequences: omittingEmptySubsequences, + whereSeparator: isSeparator) + } + + public func _customContainsEquatableElement( + _ element: Base.Iterator.Element + ) -> Bool? { + Log._customContainsEquatableElement[selfType] += 1 + return base._customContainsEquatableElement(element) + } + + /// If `self` is multi-pass (i.e., a `Collection`), invoke + /// `preprocess` on `self` and return its result. Otherwise, return + /// `nil`. + public func _preprocessingPass( + _ preprocess: () throws -> R + ) rethrows -> R? { + Log._preprocessingPass[selfType] += 1 + return try base._preprocessingPass(preprocess) + } + + /// Create a native array buffer containing the elements of `self`, + /// in the same order. + public func _copyToContiguousArray() + -> ContiguousArray { + Log._copyToContiguousArray[selfType] += 1 + return base._copyToContiguousArray() + } + + /// Copy a Sequence into an array. + public func _copyContents( + initializing buffer: UnsafeMutableBufferPointer + ) -> (Iterator,UnsafeMutableBufferPointer.Index) { + Log._copyContents[selfType] += 1 + let (it,idx) = base._copyContents(initializing: buffer) + return (Iterator(wrapping: it),idx) + } + + public typealias Index = Base.Index + + public var startIndex: Index { + Log.startIndex[selfType] += 1 + return base.startIndex + } + + public var endIndex: Index { + Log.endIndex[selfType] += 1 + return base.endIndex + } + + public subscript(position: Index) -> Base.Iterator.Element { + get { + Log.subscriptIndex[selfType] += 1 + return base[position] + } + } + + public subscript(bounds: Range) -> SubSequence { + get { + Log.subscriptRange[selfType] += 1 + return base[bounds] + } + } + + public func _failEarlyRangeCheck(_ index: Index, bounds: Range) { + Log._failEarlyRangeCheckIndex[selfType] += 1 + base._failEarlyRangeCheck(index, bounds: bounds) + } + + public func _failEarlyRangeCheck(_ range: Range, bounds: Range) { + Log._failEarlyRangeCheckRange[selfType] += 1 + base._failEarlyRangeCheck(range, bounds: bounds) + } + + public func index(after i: Index) -> Index { + Log.successor[selfType] += 1 + return base.index(after: i) + } + + public func formIndex(after i: inout Index) { + Log.formSuccessor[selfType] += 1 + base.formIndex(after: &i) + } + + public typealias Indices = Base.Indices + + public var indices: Indices { + Log.indices[selfType] += 1 + return base.indices + } + + public func prefix(upTo end: Index) -> SubSequence { + Log.prefixUpTo[selfType] += 1 + return base.prefix(upTo: end) + } + + public func prefix(through position: Index) -> SubSequence { + Log.prefixThrough[selfType] += 1 + return base.prefix(through: position) + } + + public func suffix(from start: Index) -> SubSequence { + Log.suffixFrom[selfType] += 1 + return base.suffix(from: start) + } + + public var isEmpty: Bool { + Log.isEmpty[selfType] += 1 + return base.isEmpty + } + + public var count: Int { + Log.count[selfType] += 1 + return base.count + } + + public func _customIndexOfEquatableElement( + _ element: Base.Iterator.Element + ) -> Index?? { + Log._customIndexOfEquatableElement[selfType] += 1 + return base._customIndexOfEquatableElement(element) + } + + public var first: Base.Iterator.Element? { + Log.first[selfType] += 1 + return base.first + } + + public func index(_ i: Index, offsetBy n: Int) -> Index { + Log.advance[selfType] += 1 + return base.index(i, offsetBy: n) + } + + public func index( + _ i: Index, offsetBy n: Int, limitedBy limit: Index + ) -> Index? { + Log.advanceLimit[selfType] += 1 + return base.index(i, offsetBy: n, limitedBy: limit) + } + + public func distance(from start: Index, to end: Index) -> Int { + Log.distance[selfType] += 1 + return base.distance(from: start, to: end) + } + + + public func index(before i: Index) -> Index { + BidirectionalCollectionLog.predecessor[selfType] += 1 + return base.index(before: i) + } + + public func formIndex(before i: inout Index) { + BidirectionalCollectionLog.formPredecessor[selfType] += 1 + base.formIndex(before: &i) + } + + public var last: Iterator.Element? { + BidirectionalCollectionLog.last[selfType] += 1 + return base.last + } +} +/// Interposes between `MutableCollection` method calls to increment each method's +/// counter. +public struct LoggingMutableCollection< + Base : MutableCollection & Collection +> : Collection, MutableCollection, LoggingType { + + public var base: Base + + public typealias Log = MutableCollectionLog + + public init(wrapping base: Base) { + self.base = base + } + + public typealias Iterator = LoggingIterator + + public func makeIterator() -> Iterator { + Log.makeIterator[selfType] += 1 + return LoggingIterator(wrapping: base.makeIterator()) + } + + public var underestimatedCount: Int { + Log.underestimatedCount[selfType] += 1 + return base.underestimatedCount + } + + public func map( + _ transform: (Base.Iterator.Element) throws -> T + ) rethrows -> [T] { + Log.map[selfType] += 1 + return try base.map(transform) + } + + public func filter( + _ isIncluded: (Base.Iterator.Element) throws -> Bool + ) rethrows -> [Base.Iterator.Element] { + Log.filter[selfType] += 1 + return try base.filter(isIncluded) + } + + public func forEach( + _ body: (Base.Iterator.Element) throws -> Void + ) rethrows { + Log.forEach[selfType] += 1 + try base.forEach(body) + } + + public typealias SubSequence = Base.SubSequence + + public func dropFirst(_ n: Int) -> SubSequence { + Log.dropFirst[selfType] += 1 + return base.dropFirst(n) + } + + public func dropLast(_ n: Int) -> SubSequence { + Log.dropLast[selfType] += 1 + return base.dropLast(n) + } + + public func drop( + while predicate: (Base.Iterator.Element) throws -> Bool + ) rethrows -> SubSequence { + Log.dropWhile[selfType] += 1 + return try base.drop(while: predicate) + } + + public func prefix(_ maxLength: Int) -> SubSequence { + Log.prefixMaxLength[selfType] += 1 + return base.prefix(maxLength) + } + + public func prefix( + while predicate: (Base.Iterator.Element) throws -> Bool + ) rethrows -> SubSequence { + Log.prefixWhile[selfType] += 1 + return try base.prefix(while: predicate) + } + + public func suffix(_ maxLength: Int) -> SubSequence { + Log.suffixMaxLength[selfType] += 1 + return base.suffix(maxLength) + } + + public func split( + maxSplits: Int = Int.max, + omittingEmptySubsequences: Bool = true, + whereSeparator isSeparator: (Base.Iterator.Element) throws -> Bool + ) rethrows -> [SubSequence] { + Log.split[selfType] += 1 + return try base.split( + maxSplits: maxSplits, + omittingEmptySubsequences: omittingEmptySubsequences, + whereSeparator: isSeparator) + } + + public func _customContainsEquatableElement( + _ element: Base.Iterator.Element + ) -> Bool? { + Log._customContainsEquatableElement[selfType] += 1 + return base._customContainsEquatableElement(element) + } + + /// If `self` is multi-pass (i.e., a `Collection`), invoke + /// `preprocess` on `self` and return its result. Otherwise, return + /// `nil`. + public func _preprocessingPass( + _ preprocess: () throws -> R + ) rethrows -> R? { + Log._preprocessingPass[selfType] += 1 + return try base._preprocessingPass(preprocess) + } + + /// Create a native array buffer containing the elements of `self`, + /// in the same order. + public func _copyToContiguousArray() + -> ContiguousArray { + Log._copyToContiguousArray[selfType] += 1 + return base._copyToContiguousArray() + } + + /// Copy a Sequence into an array. + public func _copyContents( + initializing buffer: UnsafeMutableBufferPointer + ) -> (Iterator,UnsafeMutableBufferPointer.Index) { + Log._copyContents[selfType] += 1 + let (it,idx) = base._copyContents(initializing: buffer) + return (Iterator(wrapping: it),idx) + } + + public typealias Index = Base.Index + + public var startIndex: Index { + Log.startIndex[selfType] += 1 + return base.startIndex + } + + public var endIndex: Index { + Log.endIndex[selfType] += 1 + return base.endIndex + } + + public subscript(position: Index) -> Base.Iterator.Element { + get { + Log.subscriptIndex[selfType] += 1 + return base[position] + } + set { + Log.subscriptIndexSet[selfType] += 1 + base[position] = newValue + } + } + + public subscript(bounds: Range) -> SubSequence { + get { + Log.subscriptRange[selfType] += 1 + return base[bounds] + } + set { + Log.subscriptRangeSet[selfType] += 1 + base[bounds] = newValue + } + } + + public func _failEarlyRangeCheck(_ index: Index, bounds: Range) { + Log._failEarlyRangeCheckIndex[selfType] += 1 + base._failEarlyRangeCheck(index, bounds: bounds) + } + + public func _failEarlyRangeCheck(_ range: Range, bounds: Range) { + Log._failEarlyRangeCheckRange[selfType] += 1 + base._failEarlyRangeCheck(range, bounds: bounds) + } + + public func index(after i: Index) -> Index { + Log.successor[selfType] += 1 + return base.index(after: i) + } + + public func formIndex(after i: inout Index) { + Log.formSuccessor[selfType] += 1 + base.formIndex(after: &i) + } + + public typealias Indices = Base.Indices + + public var indices: Indices { + Log.indices[selfType] += 1 + return base.indices + } + + public func prefix(upTo end: Index) -> SubSequence { + Log.prefixUpTo[selfType] += 1 + return base.prefix(upTo: end) + } + + public func prefix(through position: Index) -> SubSequence { + Log.prefixThrough[selfType] += 1 + return base.prefix(through: position) + } + + public func suffix(from start: Index) -> SubSequence { + Log.suffixFrom[selfType] += 1 + return base.suffix(from: start) + } + + public var isEmpty: Bool { + Log.isEmpty[selfType] += 1 + return base.isEmpty + } + + public var count: Int { + Log.count[selfType] += 1 + return base.count + } + + public func _customIndexOfEquatableElement( + _ element: Base.Iterator.Element + ) -> Index?? { + Log._customIndexOfEquatableElement[selfType] += 1 + return base._customIndexOfEquatableElement(element) + } + + public var first: Base.Iterator.Element? { + Log.first[selfType] += 1 + return base.first + } + + public func index(_ i: Index, offsetBy n: Int) -> Index { + Log.advance[selfType] += 1 + return base.index(i, offsetBy: n) + } + + public func index( + _ i: Index, offsetBy n: Int, limitedBy limit: Index + ) -> Index? { + Log.advanceLimit[selfType] += 1 + return base.index(i, offsetBy: n, limitedBy: limit) + } + + public func distance(from start: Index, to end: Index) -> Int { + Log.distance[selfType] += 1 + return base.distance(from: start, to: end) + } + + public mutating func partition( + by belongsInSecondPartition: (Iterator.Element) throws -> Bool + ) rethrows -> Index { + Log.partitionBy[selfType] += 1 + return try base.partition(by: belongsInSecondPartition) + } + + public mutating func _withUnsafeMutableBufferPointerIfSupported( + _ body: (inout UnsafeMutableBufferPointer) throws -> R + ) rethrows -> R? { + Log._withUnsafeMutableBufferPointerIfSupported[selfType] += 1 + let result = try base._withUnsafeMutableBufferPointerIfSupported(body) + if result != nil { + Log._withUnsafeMutableBufferPointerIfSupportedNonNilReturns[selfType] += 1 + } + return result + } + +} +/// Interposes between `MutableCollection` method calls to increment each method's +/// counter. +public struct LoggingMutableBidirectionalCollection< + Base : MutableCollection & BidirectionalCollection +> : BidirectionalCollection, MutableCollection, LoggingType { + + public var base: Base + + public typealias Log = MutableCollectionLog + + public init(wrapping base: Base) { + self.base = base + } + + public typealias Iterator = LoggingIterator + + public func makeIterator() -> Iterator { + Log.makeIterator[selfType] += 1 + return LoggingIterator(wrapping: base.makeIterator()) + } + + public var underestimatedCount: Int { + Log.underestimatedCount[selfType] += 1 + return base.underestimatedCount + } + + public func map( + _ transform: (Base.Iterator.Element) throws -> T + ) rethrows -> [T] { + Log.map[selfType] += 1 + return try base.map(transform) + } + + public func filter( + _ isIncluded: (Base.Iterator.Element) throws -> Bool + ) rethrows -> [Base.Iterator.Element] { + Log.filter[selfType] += 1 + return try base.filter(isIncluded) + } + + public func forEach( + _ body: (Base.Iterator.Element) throws -> Void + ) rethrows { + Log.forEach[selfType] += 1 + try base.forEach(body) + } + + public typealias SubSequence = Base.SubSequence + + public func dropFirst(_ n: Int) -> SubSequence { + Log.dropFirst[selfType] += 1 + return base.dropFirst(n) + } + + public func dropLast(_ n: Int) -> SubSequence { + Log.dropLast[selfType] += 1 + return base.dropLast(n) + } + + public func drop( + while predicate: (Base.Iterator.Element) throws -> Bool + ) rethrows -> SubSequence { + Log.dropWhile[selfType] += 1 + return try base.drop(while: predicate) + } + + public func prefix(_ maxLength: Int) -> SubSequence { + Log.prefixMaxLength[selfType] += 1 + return base.prefix(maxLength) + } + + public func prefix( + while predicate: (Base.Iterator.Element) throws -> Bool + ) rethrows -> SubSequence { + Log.prefixWhile[selfType] += 1 + return try base.prefix(while: predicate) + } + + public func suffix(_ maxLength: Int) -> SubSequence { + Log.suffixMaxLength[selfType] += 1 + return base.suffix(maxLength) + } + + public func split( + maxSplits: Int = Int.max, + omittingEmptySubsequences: Bool = true, + whereSeparator isSeparator: (Base.Iterator.Element) throws -> Bool + ) rethrows -> [SubSequence] { + Log.split[selfType] += 1 + return try base.split( + maxSplits: maxSplits, + omittingEmptySubsequences: omittingEmptySubsequences, + whereSeparator: isSeparator) + } + + public func _customContainsEquatableElement( + _ element: Base.Iterator.Element + ) -> Bool? { + Log._customContainsEquatableElement[selfType] += 1 + return base._customContainsEquatableElement(element) + } + + /// If `self` is multi-pass (i.e., a `Collection`), invoke + /// `preprocess` on `self` and return its result. Otherwise, return + /// `nil`. + public func _preprocessingPass( + _ preprocess: () throws -> R + ) rethrows -> R? { + Log._preprocessingPass[selfType] += 1 + return try base._preprocessingPass(preprocess) + } + + /// Create a native array buffer containing the elements of `self`, + /// in the same order. + public func _copyToContiguousArray() + -> ContiguousArray { + Log._copyToContiguousArray[selfType] += 1 + return base._copyToContiguousArray() + } + + /// Copy a Sequence into an array. + public func _copyContents( + initializing buffer: UnsafeMutableBufferPointer + ) -> (Iterator,UnsafeMutableBufferPointer.Index) { + Log._copyContents[selfType] += 1 + let (it,idx) = base._copyContents(initializing: buffer) + return (Iterator(wrapping: it),idx) + } + + public typealias Index = Base.Index + + public var startIndex: Index { + Log.startIndex[selfType] += 1 + return base.startIndex + } + + public var endIndex: Index { + Log.endIndex[selfType] += 1 + return base.endIndex + } + + public subscript(position: Index) -> Base.Iterator.Element { + get { + Log.subscriptIndex[selfType] += 1 + return base[position] + } + set { + Log.subscriptIndexSet[selfType] += 1 + base[position] = newValue + } + } + + public subscript(bounds: Range) -> SubSequence { + get { + Log.subscriptRange[selfType] += 1 + return base[bounds] + } + set { + Log.subscriptRangeSet[selfType] += 1 + base[bounds] = newValue + } + } + + public func _failEarlyRangeCheck(_ index: Index, bounds: Range) { + Log._failEarlyRangeCheckIndex[selfType] += 1 + base._failEarlyRangeCheck(index, bounds: bounds) + } + + public func _failEarlyRangeCheck(_ range: Range, bounds: Range) { + Log._failEarlyRangeCheckRange[selfType] += 1 + base._failEarlyRangeCheck(range, bounds: bounds) + } + + public func index(after i: Index) -> Index { + Log.successor[selfType] += 1 + return base.index(after: i) + } + + public func formIndex(after i: inout Index) { + Log.formSuccessor[selfType] += 1 + base.formIndex(after: &i) + } + + public typealias Indices = Base.Indices + + public var indices: Indices { + Log.indices[selfType] += 1 + return base.indices + } + + public func prefix(upTo end: Index) -> SubSequence { + Log.prefixUpTo[selfType] += 1 + return base.prefix(upTo: end) + } + + public func prefix(through position: Index) -> SubSequence { + Log.prefixThrough[selfType] += 1 + return base.prefix(through: position) + } + + public func suffix(from start: Index) -> SubSequence { + Log.suffixFrom[selfType] += 1 + return base.suffix(from: start) + } + + public var isEmpty: Bool { + Log.isEmpty[selfType] += 1 + return base.isEmpty + } + + public var count: Int { + Log.count[selfType] += 1 + return base.count + } + + public func _customIndexOfEquatableElement( + _ element: Base.Iterator.Element + ) -> Index?? { + Log._customIndexOfEquatableElement[selfType] += 1 + return base._customIndexOfEquatableElement(element) + } + + public var first: Base.Iterator.Element? { + Log.first[selfType] += 1 + return base.first + } + + public func index(_ i: Index, offsetBy n: Int) -> Index { + Log.advance[selfType] += 1 + return base.index(i, offsetBy: n) + } + + public func index( + _ i: Index, offsetBy n: Int, limitedBy limit: Index + ) -> Index? { + Log.advanceLimit[selfType] += 1 + return base.index(i, offsetBy: n, limitedBy: limit) + } + + public func distance(from start: Index, to end: Index) -> Int { + Log.distance[selfType] += 1 + return base.distance(from: start, to: end) + } + + public mutating func partition( + by belongsInSecondPartition: (Iterator.Element) throws -> Bool + ) rethrows -> Index { + Log.partitionBy[selfType] += 1 + return try base.partition(by: belongsInSecondPartition) + } + + public mutating func _withUnsafeMutableBufferPointerIfSupported( + _ body: (inout UnsafeMutableBufferPointer) throws -> R + ) rethrows -> R? { + Log._withUnsafeMutableBufferPointerIfSupported[selfType] += 1 + let result = try base._withUnsafeMutableBufferPointerIfSupported(body) + if result != nil { + Log._withUnsafeMutableBufferPointerIfSupportedNonNilReturns[selfType] += 1 + } + return result + } + + public func index(before i: Index) -> Index { + BidirectionalCollectionLog.predecessor[selfType] += 1 + return base.index(before: i) + } + + public func formIndex(before i: inout Index) { + BidirectionalCollectionLog.formPredecessor[selfType] += 1 + base.formIndex(before: &i) + } + + public var last: Iterator.Element? { + BidirectionalCollectionLog.last[selfType] += 1 + return base.last + } +} +/// Interposes between `MutableCollection` method calls to increment each method's +/// counter. +public struct LoggingMutableRandomAccessCollection< + Base : MutableCollection & RandomAccessCollection +> : RandomAccessCollection, MutableCollection, LoggingType { + + public var base: Base + + public typealias Log = MutableCollectionLog + + public init(wrapping base: Base) { + self.base = base + } + + public typealias Iterator = LoggingIterator + + public func makeIterator() -> Iterator { + Log.makeIterator[selfType] += 1 + return LoggingIterator(wrapping: base.makeIterator()) + } + + public var underestimatedCount: Int { + Log.underestimatedCount[selfType] += 1 + return base.underestimatedCount + } + + public func map( + _ transform: (Base.Iterator.Element) throws -> T + ) rethrows -> [T] { + Log.map[selfType] += 1 + return try base.map(transform) + } + + public func filter( + _ isIncluded: (Base.Iterator.Element) throws -> Bool + ) rethrows -> [Base.Iterator.Element] { + Log.filter[selfType] += 1 + return try base.filter(isIncluded) + } + + public func forEach( + _ body: (Base.Iterator.Element) throws -> Void + ) rethrows { + Log.forEach[selfType] += 1 + try base.forEach(body) + } + + public typealias SubSequence = Base.SubSequence + + public func dropFirst(_ n: Int) -> SubSequence { + Log.dropFirst[selfType] += 1 + return base.dropFirst(n) + } + + public func dropLast(_ n: Int) -> SubSequence { + Log.dropLast[selfType] += 1 + return base.dropLast(n) + } + + public func drop( + while predicate: (Base.Iterator.Element) throws -> Bool + ) rethrows -> SubSequence { + Log.dropWhile[selfType] += 1 + return try base.drop(while: predicate) + } + + public func prefix(_ maxLength: Int) -> SubSequence { + Log.prefixMaxLength[selfType] += 1 + return base.prefix(maxLength) + } + + public func prefix( + while predicate: (Base.Iterator.Element) throws -> Bool + ) rethrows -> SubSequence { + Log.prefixWhile[selfType] += 1 + return try base.prefix(while: predicate) + } + + public func suffix(_ maxLength: Int) -> SubSequence { + Log.suffixMaxLength[selfType] += 1 + return base.suffix(maxLength) + } + + public func split( + maxSplits: Int = Int.max, + omittingEmptySubsequences: Bool = true, + whereSeparator isSeparator: (Base.Iterator.Element) throws -> Bool + ) rethrows -> [SubSequence] { + Log.split[selfType] += 1 + return try base.split( + maxSplits: maxSplits, + omittingEmptySubsequences: omittingEmptySubsequences, + whereSeparator: isSeparator) + } + + public func _customContainsEquatableElement( + _ element: Base.Iterator.Element + ) -> Bool? { + Log._customContainsEquatableElement[selfType] += 1 + return base._customContainsEquatableElement(element) + } + + /// If `self` is multi-pass (i.e., a `Collection`), invoke + /// `preprocess` on `self` and return its result. Otherwise, return + /// `nil`. + public func _preprocessingPass( + _ preprocess: () throws -> R + ) rethrows -> R? { + Log._preprocessingPass[selfType] += 1 + return try base._preprocessingPass(preprocess) + } + + /// Create a native array buffer containing the elements of `self`, + /// in the same order. + public func _copyToContiguousArray() + -> ContiguousArray { + Log._copyToContiguousArray[selfType] += 1 + return base._copyToContiguousArray() + } + + /// Copy a Sequence into an array. + public func _copyContents( + initializing buffer: UnsafeMutableBufferPointer + ) -> (Iterator,UnsafeMutableBufferPointer.Index) { + Log._copyContents[selfType] += 1 + let (it,idx) = base._copyContents(initializing: buffer) + return (Iterator(wrapping: it),idx) + } + + public typealias Index = Base.Index + + public var startIndex: Index { + Log.startIndex[selfType] += 1 + return base.startIndex + } + + public var endIndex: Index { + Log.endIndex[selfType] += 1 + return base.endIndex + } + + public subscript(position: Index) -> Base.Iterator.Element { + get { + Log.subscriptIndex[selfType] += 1 + return base[position] + } + set { + Log.subscriptIndexSet[selfType] += 1 + base[position] = newValue + } + } + + public subscript(bounds: Range) -> SubSequence { + get { + Log.subscriptRange[selfType] += 1 + return base[bounds] + } + set { + Log.subscriptRangeSet[selfType] += 1 + base[bounds] = newValue + } + } + + public func _failEarlyRangeCheck(_ index: Index, bounds: Range) { + Log._failEarlyRangeCheckIndex[selfType] += 1 + base._failEarlyRangeCheck(index, bounds: bounds) + } + + public func _failEarlyRangeCheck(_ range: Range, bounds: Range) { + Log._failEarlyRangeCheckRange[selfType] += 1 + base._failEarlyRangeCheck(range, bounds: bounds) + } + + public func index(after i: Index) -> Index { + Log.successor[selfType] += 1 + return base.index(after: i) + } + + public func formIndex(after i: inout Index) { + Log.formSuccessor[selfType] += 1 + base.formIndex(after: &i) + } + + public typealias Indices = Base.Indices + + public var indices: Indices { + Log.indices[selfType] += 1 + return base.indices + } + + public func prefix(upTo end: Index) -> SubSequence { + Log.prefixUpTo[selfType] += 1 + return base.prefix(upTo: end) + } + + public func prefix(through position: Index) -> SubSequence { + Log.prefixThrough[selfType] += 1 + return base.prefix(through: position) + } + + public func suffix(from start: Index) -> SubSequence { + Log.suffixFrom[selfType] += 1 + return base.suffix(from: start) + } + + public var isEmpty: Bool { + Log.isEmpty[selfType] += 1 + return base.isEmpty + } + + public var count: Int { + Log.count[selfType] += 1 + return base.count + } + + public func _customIndexOfEquatableElement( + _ element: Base.Iterator.Element + ) -> Index?? { + Log._customIndexOfEquatableElement[selfType] += 1 + return base._customIndexOfEquatableElement(element) + } + + public var first: Base.Iterator.Element? { + Log.first[selfType] += 1 + return base.first + } + + public func index(_ i: Index, offsetBy n: Int) -> Index { + Log.advance[selfType] += 1 + return base.index(i, offsetBy: n) + } + + public func index( + _ i: Index, offsetBy n: Int, limitedBy limit: Index + ) -> Index? { + Log.advanceLimit[selfType] += 1 + return base.index(i, offsetBy: n, limitedBy: limit) + } + + public func distance(from start: Index, to end: Index) -> Int { + Log.distance[selfType] += 1 + return base.distance(from: start, to: end) + } + + public mutating func partition( + by belongsInSecondPartition: (Iterator.Element) throws -> Bool + ) rethrows -> Index { + Log.partitionBy[selfType] += 1 + return try base.partition(by: belongsInSecondPartition) + } + + public mutating func _withUnsafeMutableBufferPointerIfSupported( + _ body: (inout UnsafeMutableBufferPointer) throws -> R + ) rethrows -> R? { + Log._withUnsafeMutableBufferPointerIfSupported[selfType] += 1 + let result = try base._withUnsafeMutableBufferPointerIfSupported(body) + if result != nil { + Log._withUnsafeMutableBufferPointerIfSupportedNonNilReturns[selfType] += 1 + } + return result + } + + public func index(before i: Index) -> Index { + BidirectionalCollectionLog.predecessor[selfType] += 1 + return base.index(before: i) + } + + public func formIndex(before i: inout Index) { + BidirectionalCollectionLog.formPredecessor[selfType] += 1 + base.formIndex(before: &i) + } + + public var last: Iterator.Element? { + BidirectionalCollectionLog.last[selfType] += 1 + return base.last + } +} +/// Interposes between `RangeReplaceableCollection` method calls to increment each method's +/// counter. +public struct LoggingRangeReplaceableCollection< + Base : RangeReplaceableCollection & Collection +> : Collection, RangeReplaceableCollection, LoggingType { + + public var base: Base + + public typealias Log = RangeReplaceableCollectionLog + + public init(wrapping base: Base) { + self.base = base + } + + public typealias Iterator = LoggingIterator + + public func makeIterator() -> Iterator { + Log.makeIterator[selfType] += 1 + return LoggingIterator(wrapping: base.makeIterator()) + } + + public var underestimatedCount: Int { + Log.underestimatedCount[selfType] += 1 + return base.underestimatedCount + } + + public func map( + _ transform: (Base.Iterator.Element) throws -> T + ) rethrows -> [T] { + Log.map[selfType] += 1 + return try base.map(transform) + } + + public func filter( + _ isIncluded: (Base.Iterator.Element) throws -> Bool + ) rethrows -> [Base.Iterator.Element] { + Log.filter[selfType] += 1 + return try base.filter(isIncluded) + } + + public func forEach( + _ body: (Base.Iterator.Element) throws -> Void + ) rethrows { + Log.forEach[selfType] += 1 + try base.forEach(body) + } + + public typealias SubSequence = Base.SubSequence + + public func dropFirst(_ n: Int) -> SubSequence { + Log.dropFirst[selfType] += 1 + return base.dropFirst(n) + } + + public func dropLast(_ n: Int) -> SubSequence { + Log.dropLast[selfType] += 1 + return base.dropLast(n) + } + + public func drop( + while predicate: (Base.Iterator.Element) throws -> Bool + ) rethrows -> SubSequence { + Log.dropWhile[selfType] += 1 + return try base.drop(while: predicate) + } + + public func prefix(_ maxLength: Int) -> SubSequence { + Log.prefixMaxLength[selfType] += 1 + return base.prefix(maxLength) + } + + public func prefix( + while predicate: (Base.Iterator.Element) throws -> Bool + ) rethrows -> SubSequence { + Log.prefixWhile[selfType] += 1 + return try base.prefix(while: predicate) + } + + public func suffix(_ maxLength: Int) -> SubSequence { + Log.suffixMaxLength[selfType] += 1 + return base.suffix(maxLength) + } + + public func split( + maxSplits: Int = Int.max, + omittingEmptySubsequences: Bool = true, + whereSeparator isSeparator: (Base.Iterator.Element) throws -> Bool + ) rethrows -> [SubSequence] { + Log.split[selfType] += 1 + return try base.split( + maxSplits: maxSplits, + omittingEmptySubsequences: omittingEmptySubsequences, + whereSeparator: isSeparator) + } + + public func _customContainsEquatableElement( + _ element: Base.Iterator.Element + ) -> Bool? { + Log._customContainsEquatableElement[selfType] += 1 + return base._customContainsEquatableElement(element) + } + + /// If `self` is multi-pass (i.e., a `Collection`), invoke + /// `preprocess` on `self` and return its result. Otherwise, return + /// `nil`. + public func _preprocessingPass( + _ preprocess: () throws -> R + ) rethrows -> R? { + Log._preprocessingPass[selfType] += 1 + return try base._preprocessingPass(preprocess) + } + + /// Create a native array buffer containing the elements of `self`, + /// in the same order. + public func _copyToContiguousArray() + -> ContiguousArray { + Log._copyToContiguousArray[selfType] += 1 + return base._copyToContiguousArray() + } + + /// Copy a Sequence into an array. + public func _copyContents( + initializing buffer: UnsafeMutableBufferPointer + ) -> (Iterator,UnsafeMutableBufferPointer.Index) { + Log._copyContents[selfType] += 1 + let (it,idx) = base._copyContents(initializing: buffer) + return (Iterator(wrapping: it),idx) + } + + public typealias Index = Base.Index + + public var startIndex: Index { + Log.startIndex[selfType] += 1 + return base.startIndex + } + + public var endIndex: Index { + Log.endIndex[selfType] += 1 + return base.endIndex + } + + public subscript(position: Index) -> Base.Iterator.Element { + get { + Log.subscriptIndex[selfType] += 1 + return base[position] + } + } + + public subscript(bounds: Range) -> SubSequence { + get { + Log.subscriptRange[selfType] += 1 + return base[bounds] + } + } + + public func _failEarlyRangeCheck(_ index: Index, bounds: Range) { + Log._failEarlyRangeCheckIndex[selfType] += 1 + base._failEarlyRangeCheck(index, bounds: bounds) + } + + public func _failEarlyRangeCheck(_ range: Range, bounds: Range) { + Log._failEarlyRangeCheckRange[selfType] += 1 + base._failEarlyRangeCheck(range, bounds: bounds) + } + + public func index(after i: Index) -> Index { + Log.successor[selfType] += 1 + return base.index(after: i) + } + + public func formIndex(after i: inout Index) { + Log.formSuccessor[selfType] += 1 + base.formIndex(after: &i) + } + + public typealias Indices = Base.Indices + + public var indices: Indices { + Log.indices[selfType] += 1 + return base.indices + } + + public func prefix(upTo end: Index) -> SubSequence { + Log.prefixUpTo[selfType] += 1 + return base.prefix(upTo: end) + } + + public func prefix(through position: Index) -> SubSequence { + Log.prefixThrough[selfType] += 1 + return base.prefix(through: position) + } + + public func suffix(from start: Index) -> SubSequence { + Log.suffixFrom[selfType] += 1 + return base.suffix(from: start) + } + + public var isEmpty: Bool { + Log.isEmpty[selfType] += 1 + return base.isEmpty + } + + public var count: Int { + Log.count[selfType] += 1 + return base.count + } + + public func _customIndexOfEquatableElement( + _ element: Base.Iterator.Element + ) -> Index?? { + Log._customIndexOfEquatableElement[selfType] += 1 + return base._customIndexOfEquatableElement(element) + } + + public var first: Base.Iterator.Element? { + Log.first[selfType] += 1 + return base.first + } + + public func index(_ i: Index, offsetBy n: Int) -> Index { + Log.advance[selfType] += 1 + return base.index(i, offsetBy: n) + } + + public func index( + _ i: Index, offsetBy n: Int, limitedBy limit: Index + ) -> Index? { + Log.advanceLimit[selfType] += 1 + return base.index(i, offsetBy: n, limitedBy: limit) + } + + public func distance(from start: Index, to end: Index) -> Int { + Log.distance[selfType] += 1 + return base.distance(from: start, to: end) + } + + + public init() { + self.base = Base() + Log.init_[selfType] += 1 + } + + public init(repeating repeatedValue: Iterator.Element, count: Int) { + self.base = Base(repeating: repeatedValue, count: count) + Log.initRepeating[selfType] += 1 + } + + public init(_ elements: S) + where S.Iterator.Element == Iterator.Element { + self.base = Base(elements) + Log.initWithSequence[selfType] += 1 + } + + public mutating func _customRemoveLast() -> Base.Iterator.Element? { + Log._customRemoveLast[selfType] += 1 + return base._customRemoveLast() + } + + public mutating func _customRemoveLast(_ n: Int) -> Bool { + Log._customRemoveLastN[selfType] += 1 + return base._customRemoveLast(n) + } + + public mutating func append(_ newElement: Base.Iterator.Element) { + Log.append[selfType] += 1 + base.append(newElement) + } + + public mutating func append( + contentsOf newElements: S + ) where S.Iterator.Element == Base.Iterator.Element { + Log.appendContentsOf[selfType] += 1 + base.append(contentsOf: newElements) + } + + public mutating func insert( + _ newElement: Base.Iterator.Element, at i: Index + ) { + Log.insert[selfType] += 1 + base.insert(newElement, at: i) + } + + public mutating func insert( + contentsOf newElements: C, at i: Index + ) where C.Iterator.Element == Base.Iterator.Element { + Log.insertContentsOf[selfType] += 1 + base.insert(contentsOf: newElements, at: i) + } + + public mutating func removeAll(keepingCapacity keepCapacity: Bool) { + Log.removeAll[selfType] += 1 + base.removeAll(keepingCapacity: keepCapacity) + } + + @discardableResult + public mutating func remove(at index: Index) -> Base.Iterator.Element { + Log.removeAt[selfType] += 1 + return base.remove(at: index) + } + + @discardableResult + public mutating func removeFirst() -> Base.Iterator.Element { + Log.removeFirst[selfType] += 1 + return base.removeFirst() + } + + public mutating func removeFirst(_ n: Int) { + Log.removeFirstN[selfType] += 1 + base.removeFirst(n) + } + + public mutating func removeSubrange(_ bounds: Range) { + Log.removeSubrange[selfType] += 1 + base.removeSubrange(bounds) + } + + public mutating func replaceSubrange( + _ bounds: Range, with newElements: C + ) where C.Iterator.Element == Base.Iterator.Element { + Log.replaceSubrange[selfType] += 1 + base.replaceSubrange(bounds, with: newElements) + } + + public mutating func reserveCapacity(_ n: Int) { + Log.reserveCapacity[selfType] += 1 + base.reserveCapacity(n) + } +} +/// Interposes between `RangeReplaceableCollection` method calls to increment each method's +/// counter. +public struct LoggingRangeReplaceableBidirectionalCollection< + Base : RangeReplaceableCollection & BidirectionalCollection +> : BidirectionalCollection, RangeReplaceableCollection, LoggingType { + + public var base: Base + + public typealias Log = RangeReplaceableCollectionLog + + public init(wrapping base: Base) { + self.base = base + } + + public typealias Iterator = LoggingIterator + + public func makeIterator() -> Iterator { + Log.makeIterator[selfType] += 1 + return LoggingIterator(wrapping: base.makeIterator()) + } + + public var underestimatedCount: Int { + Log.underestimatedCount[selfType] += 1 + return base.underestimatedCount + } + + public func map( + _ transform: (Base.Iterator.Element) throws -> T + ) rethrows -> [T] { + Log.map[selfType] += 1 + return try base.map(transform) + } + + public func filter( + _ isIncluded: (Base.Iterator.Element) throws -> Bool + ) rethrows -> [Base.Iterator.Element] { + Log.filter[selfType] += 1 + return try base.filter(isIncluded) + } + + public func forEach( + _ body: (Base.Iterator.Element) throws -> Void + ) rethrows { + Log.forEach[selfType] += 1 + try base.forEach(body) + } + + public typealias SubSequence = Base.SubSequence + + public func dropFirst(_ n: Int) -> SubSequence { + Log.dropFirst[selfType] += 1 + return base.dropFirst(n) + } + + public func dropLast(_ n: Int) -> SubSequence { + Log.dropLast[selfType] += 1 + return base.dropLast(n) + } + + public func drop( + while predicate: (Base.Iterator.Element) throws -> Bool + ) rethrows -> SubSequence { + Log.dropWhile[selfType] += 1 + return try base.drop(while: predicate) + } + + public func prefix(_ maxLength: Int) -> SubSequence { + Log.prefixMaxLength[selfType] += 1 + return base.prefix(maxLength) + } + + public func prefix( + while predicate: (Base.Iterator.Element) throws -> Bool + ) rethrows -> SubSequence { + Log.prefixWhile[selfType] += 1 + return try base.prefix(while: predicate) + } + + public func suffix(_ maxLength: Int) -> SubSequence { + Log.suffixMaxLength[selfType] += 1 + return base.suffix(maxLength) + } + + public func split( + maxSplits: Int = Int.max, + omittingEmptySubsequences: Bool = true, + whereSeparator isSeparator: (Base.Iterator.Element) throws -> Bool + ) rethrows -> [SubSequence] { + Log.split[selfType] += 1 + return try base.split( + maxSplits: maxSplits, + omittingEmptySubsequences: omittingEmptySubsequences, + whereSeparator: isSeparator) + } + + public func _customContainsEquatableElement( + _ element: Base.Iterator.Element + ) -> Bool? { + Log._customContainsEquatableElement[selfType] += 1 + return base._customContainsEquatableElement(element) + } + + /// If `self` is multi-pass (i.e., a `Collection`), invoke + /// `preprocess` on `self` and return its result. Otherwise, return + /// `nil`. + public func _preprocessingPass( + _ preprocess: () throws -> R + ) rethrows -> R? { + Log._preprocessingPass[selfType] += 1 + return try base._preprocessingPass(preprocess) + } + + /// Create a native array buffer containing the elements of `self`, + /// in the same order. + public func _copyToContiguousArray() + -> ContiguousArray { + Log._copyToContiguousArray[selfType] += 1 + return base._copyToContiguousArray() + } + + /// Copy a Sequence into an array. + public func _copyContents( + initializing buffer: UnsafeMutableBufferPointer + ) -> (Iterator,UnsafeMutableBufferPointer.Index) { + Log._copyContents[selfType] += 1 + let (it,idx) = base._copyContents(initializing: buffer) + return (Iterator(wrapping: it),idx) + } + + public typealias Index = Base.Index + + public var startIndex: Index { + Log.startIndex[selfType] += 1 + return base.startIndex + } + + public var endIndex: Index { + Log.endIndex[selfType] += 1 + return base.endIndex + } + + public subscript(position: Index) -> Base.Iterator.Element { + get { + Log.subscriptIndex[selfType] += 1 + return base[position] + } + } + + public subscript(bounds: Range) -> SubSequence { + get { + Log.subscriptRange[selfType] += 1 + return base[bounds] + } + } + + public func _failEarlyRangeCheck(_ index: Index, bounds: Range) { + Log._failEarlyRangeCheckIndex[selfType] += 1 + base._failEarlyRangeCheck(index, bounds: bounds) + } + + public func _failEarlyRangeCheck(_ range: Range, bounds: Range) { + Log._failEarlyRangeCheckRange[selfType] += 1 + base._failEarlyRangeCheck(range, bounds: bounds) + } + + public func index(after i: Index) -> Index { + Log.successor[selfType] += 1 + return base.index(after: i) + } + + public func formIndex(after i: inout Index) { + Log.formSuccessor[selfType] += 1 + base.formIndex(after: &i) + } + + public typealias Indices = Base.Indices + + public var indices: Indices { + Log.indices[selfType] += 1 + return base.indices + } + + public func prefix(upTo end: Index) -> SubSequence { + Log.prefixUpTo[selfType] += 1 + return base.prefix(upTo: end) + } + + public func prefix(through position: Index) -> SubSequence { + Log.prefixThrough[selfType] += 1 + return base.prefix(through: position) + } + + public func suffix(from start: Index) -> SubSequence { + Log.suffixFrom[selfType] += 1 + return base.suffix(from: start) + } + + public var isEmpty: Bool { + Log.isEmpty[selfType] += 1 + return base.isEmpty + } + + public var count: Int { + Log.count[selfType] += 1 + return base.count + } + + public func _customIndexOfEquatableElement( + _ element: Base.Iterator.Element + ) -> Index?? { + Log._customIndexOfEquatableElement[selfType] += 1 + return base._customIndexOfEquatableElement(element) + } + + public var first: Base.Iterator.Element? { + Log.first[selfType] += 1 + return base.first + } + + public func index(_ i: Index, offsetBy n: Int) -> Index { + Log.advance[selfType] += 1 + return base.index(i, offsetBy: n) + } + + public func index( + _ i: Index, offsetBy n: Int, limitedBy limit: Index + ) -> Index? { + Log.advanceLimit[selfType] += 1 + return base.index(i, offsetBy: n, limitedBy: limit) + } + + public func distance(from start: Index, to end: Index) -> Int { + Log.distance[selfType] += 1 + return base.distance(from: start, to: end) + } + + + public init() { + self.base = Base() + Log.init_[selfType] += 1 + } + + public init(repeating repeatedValue: Iterator.Element, count: Int) { + self.base = Base(repeating: repeatedValue, count: count) + Log.initRepeating[selfType] += 1 + } + + public init(_ elements: S) + where S.Iterator.Element == Iterator.Element { + self.base = Base(elements) + Log.initWithSequence[selfType] += 1 + } + + public mutating func _customRemoveLast() -> Base.Iterator.Element? { + Log._customRemoveLast[selfType] += 1 + return base._customRemoveLast() + } + + public mutating func _customRemoveLast(_ n: Int) -> Bool { + Log._customRemoveLastN[selfType] += 1 + return base._customRemoveLast(n) + } + + public mutating func append(_ newElement: Base.Iterator.Element) { + Log.append[selfType] += 1 + base.append(newElement) + } + + public mutating func append( + contentsOf newElements: S + ) where S.Iterator.Element == Base.Iterator.Element { + Log.appendContentsOf[selfType] += 1 + base.append(contentsOf: newElements) + } + + public mutating func insert( + _ newElement: Base.Iterator.Element, at i: Index + ) { + Log.insert[selfType] += 1 + base.insert(newElement, at: i) + } + + public mutating func insert( + contentsOf newElements: C, at i: Index + ) where C.Iterator.Element == Base.Iterator.Element { + Log.insertContentsOf[selfType] += 1 + base.insert(contentsOf: newElements, at: i) + } + + public mutating func removeAll(keepingCapacity keepCapacity: Bool) { + Log.removeAll[selfType] += 1 + base.removeAll(keepingCapacity: keepCapacity) + } + + @discardableResult + public mutating func remove(at index: Index) -> Base.Iterator.Element { + Log.removeAt[selfType] += 1 + return base.remove(at: index) + } + + @discardableResult + public mutating func removeFirst() -> Base.Iterator.Element { + Log.removeFirst[selfType] += 1 + return base.removeFirst() + } + + public mutating func removeFirst(_ n: Int) { + Log.removeFirstN[selfType] += 1 + base.removeFirst(n) + } + + public mutating func removeSubrange(_ bounds: Range) { + Log.removeSubrange[selfType] += 1 + base.removeSubrange(bounds) + } + + public mutating func replaceSubrange( + _ bounds: Range, with newElements: C + ) where C.Iterator.Element == Base.Iterator.Element { + Log.replaceSubrange[selfType] += 1 + base.replaceSubrange(bounds, with: newElements) + } + + public mutating func reserveCapacity(_ n: Int) { + Log.reserveCapacity[selfType] += 1 + base.reserveCapacity(n) + } + public func index(before i: Index) -> Index { + BidirectionalCollectionLog.predecessor[selfType] += 1 + return base.index(before: i) + } + + public func formIndex(before i: inout Index) { + BidirectionalCollectionLog.formPredecessor[selfType] += 1 + base.formIndex(before: &i) + } + + public var last: Iterator.Element? { + BidirectionalCollectionLog.last[selfType] += 1 + return base.last + } +} +/// Interposes between `RangeReplaceableCollection` method calls to increment each method's +/// counter. +public struct LoggingRangeReplaceableRandomAccessCollection< + Base : RangeReplaceableCollection & RandomAccessCollection +> : RandomAccessCollection, RangeReplaceableCollection, LoggingType { + + public var base: Base + + public typealias Log = RangeReplaceableCollectionLog + + public init(wrapping base: Base) { + self.base = base + } + + public typealias Iterator = LoggingIterator + + public func makeIterator() -> Iterator { + Log.makeIterator[selfType] += 1 + return LoggingIterator(wrapping: base.makeIterator()) + } + + public var underestimatedCount: Int { + Log.underestimatedCount[selfType] += 1 + return base.underestimatedCount + } + + public func map( + _ transform: (Base.Iterator.Element) throws -> T + ) rethrows -> [T] { + Log.map[selfType] += 1 + return try base.map(transform) + } + + public func filter( + _ isIncluded: (Base.Iterator.Element) throws -> Bool + ) rethrows -> [Base.Iterator.Element] { + Log.filter[selfType] += 1 + return try base.filter(isIncluded) + } + + public func forEach( + _ body: (Base.Iterator.Element) throws -> Void + ) rethrows { + Log.forEach[selfType] += 1 + try base.forEach(body) + } + + public typealias SubSequence = Base.SubSequence + + public func dropFirst(_ n: Int) -> SubSequence { + Log.dropFirst[selfType] += 1 + return base.dropFirst(n) + } + + public func dropLast(_ n: Int) -> SubSequence { + Log.dropLast[selfType] += 1 + return base.dropLast(n) + } + + public func drop( + while predicate: (Base.Iterator.Element) throws -> Bool + ) rethrows -> SubSequence { + Log.dropWhile[selfType] += 1 + return try base.drop(while: predicate) + } + + public func prefix(_ maxLength: Int) -> SubSequence { + Log.prefixMaxLength[selfType] += 1 + return base.prefix(maxLength) + } + + public func prefix( + while predicate: (Base.Iterator.Element) throws -> Bool + ) rethrows -> SubSequence { + Log.prefixWhile[selfType] += 1 + return try base.prefix(while: predicate) + } + + public func suffix(_ maxLength: Int) -> SubSequence { + Log.suffixMaxLength[selfType] += 1 + return base.suffix(maxLength) + } + + public func split( + maxSplits: Int = Int.max, + omittingEmptySubsequences: Bool = true, + whereSeparator isSeparator: (Base.Iterator.Element) throws -> Bool + ) rethrows -> [SubSequence] { + Log.split[selfType] += 1 + return try base.split( + maxSplits: maxSplits, + omittingEmptySubsequences: omittingEmptySubsequences, + whereSeparator: isSeparator) + } + + public func _customContainsEquatableElement( + _ element: Base.Iterator.Element + ) -> Bool? { + Log._customContainsEquatableElement[selfType] += 1 + return base._customContainsEquatableElement(element) + } + + /// If `self` is multi-pass (i.e., a `Collection`), invoke + /// `preprocess` on `self` and return its result. Otherwise, return + /// `nil`. + public func _preprocessingPass( + _ preprocess: () throws -> R + ) rethrows -> R? { + Log._preprocessingPass[selfType] += 1 + return try base._preprocessingPass(preprocess) + } + + /// Create a native array buffer containing the elements of `self`, + /// in the same order. + public func _copyToContiguousArray() + -> ContiguousArray { + Log._copyToContiguousArray[selfType] += 1 + return base._copyToContiguousArray() + } + + /// Copy a Sequence into an array. + public func _copyContents( + initializing buffer: UnsafeMutableBufferPointer + ) -> (Iterator,UnsafeMutableBufferPointer.Index) { + Log._copyContents[selfType] += 1 + let (it,idx) = base._copyContents(initializing: buffer) + return (Iterator(wrapping: it),idx) + } + + public typealias Index = Base.Index + + public var startIndex: Index { + Log.startIndex[selfType] += 1 + return base.startIndex + } + + public var endIndex: Index { + Log.endIndex[selfType] += 1 + return base.endIndex + } + + public subscript(position: Index) -> Base.Iterator.Element { + get { + Log.subscriptIndex[selfType] += 1 + return base[position] + } + } + + public subscript(bounds: Range) -> SubSequence { + get { + Log.subscriptRange[selfType] += 1 + return base[bounds] + } + } + + public func _failEarlyRangeCheck(_ index: Index, bounds: Range) { + Log._failEarlyRangeCheckIndex[selfType] += 1 + base._failEarlyRangeCheck(index, bounds: bounds) + } + + public func _failEarlyRangeCheck(_ range: Range, bounds: Range) { + Log._failEarlyRangeCheckRange[selfType] += 1 + base._failEarlyRangeCheck(range, bounds: bounds) + } + + public func index(after i: Index) -> Index { + Log.successor[selfType] += 1 + return base.index(after: i) + } + + public func formIndex(after i: inout Index) { + Log.formSuccessor[selfType] += 1 + base.formIndex(after: &i) + } + + public typealias Indices = Base.Indices + + public var indices: Indices { + Log.indices[selfType] += 1 + return base.indices + } + + public func prefix(upTo end: Index) -> SubSequence { + Log.prefixUpTo[selfType] += 1 + return base.prefix(upTo: end) + } + + public func prefix(through position: Index) -> SubSequence { + Log.prefixThrough[selfType] += 1 + return base.prefix(through: position) + } + + public func suffix(from start: Index) -> SubSequence { + Log.suffixFrom[selfType] += 1 + return base.suffix(from: start) + } + + public var isEmpty: Bool { + Log.isEmpty[selfType] += 1 + return base.isEmpty + } + + public var count: Int { + Log.count[selfType] += 1 + return base.count + } + + public func _customIndexOfEquatableElement( + _ element: Base.Iterator.Element + ) -> Index?? { + Log._customIndexOfEquatableElement[selfType] += 1 + return base._customIndexOfEquatableElement(element) + } + + public var first: Base.Iterator.Element? { + Log.first[selfType] += 1 + return base.first + } + + public func index(_ i: Index, offsetBy n: Int) -> Index { + Log.advance[selfType] += 1 + return base.index(i, offsetBy: n) + } + + public func index( + _ i: Index, offsetBy n: Int, limitedBy limit: Index + ) -> Index? { + Log.advanceLimit[selfType] += 1 + return base.index(i, offsetBy: n, limitedBy: limit) + } + + public func distance(from start: Index, to end: Index) -> Int { + Log.distance[selfType] += 1 + return base.distance(from: start, to: end) + } + + + public init() { + self.base = Base() + Log.init_[selfType] += 1 + } + + public init(repeating repeatedValue: Iterator.Element, count: Int) { + self.base = Base(repeating: repeatedValue, count: count) + Log.initRepeating[selfType] += 1 + } + + public init(_ elements: S) + where S.Iterator.Element == Iterator.Element { + self.base = Base(elements) + Log.initWithSequence[selfType] += 1 + } + + public mutating func _customRemoveLast() -> Base.Iterator.Element? { + Log._customRemoveLast[selfType] += 1 + return base._customRemoveLast() + } + + public mutating func _customRemoveLast(_ n: Int) -> Bool { + Log._customRemoveLastN[selfType] += 1 + return base._customRemoveLast(n) + } + + public mutating func append(_ newElement: Base.Iterator.Element) { + Log.append[selfType] += 1 + base.append(newElement) + } + + public mutating func append( + contentsOf newElements: S + ) where S.Iterator.Element == Base.Iterator.Element { + Log.appendContentsOf[selfType] += 1 + base.append(contentsOf: newElements) + } + + public mutating func insert( + _ newElement: Base.Iterator.Element, at i: Index + ) { + Log.insert[selfType] += 1 + base.insert(newElement, at: i) + } + + public mutating func insert( + contentsOf newElements: C, at i: Index + ) where C.Iterator.Element == Base.Iterator.Element { + Log.insertContentsOf[selfType] += 1 + base.insert(contentsOf: newElements, at: i) + } + + public mutating func removeAll(keepingCapacity keepCapacity: Bool) { + Log.removeAll[selfType] += 1 + base.removeAll(keepingCapacity: keepCapacity) + } + + @discardableResult + public mutating func remove(at index: Index) -> Base.Iterator.Element { + Log.removeAt[selfType] += 1 + return base.remove(at: index) + } + + @discardableResult + public mutating func removeFirst() -> Base.Iterator.Element { + Log.removeFirst[selfType] += 1 + return base.removeFirst() + } + + public mutating func removeFirst(_ n: Int) { + Log.removeFirstN[selfType] += 1 + base.removeFirst(n) + } + + public mutating func removeSubrange(_ bounds: Range) { + Log.removeSubrange[selfType] += 1 + base.removeSubrange(bounds) + } + + public mutating func replaceSubrange( + _ bounds: Range, with newElements: C + ) where C.Iterator.Element == Base.Iterator.Element { + Log.replaceSubrange[selfType] += 1 + base.replaceSubrange(bounds, with: newElements) + } + + public mutating func reserveCapacity(_ n: Int) { + Log.reserveCapacity[selfType] += 1 + base.reserveCapacity(n) + } + public func index(before i: Index) -> Index { + BidirectionalCollectionLog.predecessor[selfType] += 1 + return base.index(before: i) + } + + public func formIndex(before i: inout Index) { + BidirectionalCollectionLog.formPredecessor[selfType] += 1 + base.formIndex(before: &i) + } + + public var last: Iterator.Element? { + BidirectionalCollectionLog.last[selfType] += 1 + return base.last + } +} + +//===----------------------------------------------------------------------===// +// Collections that count calls to `_withUnsafeMutableBufferPointerIfSupported` +//===----------------------------------------------------------------------===// + +/// Interposes between `_withUnsafeMutableBufferPointerIfSupported` method calls +/// to increment a counter. Calls to this method from within dispatched methods +/// are uncounted by the standard logging collection wrapper. +public struct BufferAccessLoggingMutableCollection< + Base : MutableCollection & Collection +> : MutableCollection, Collection, LoggingType { + + public var base: Base + + public typealias Log = MutableCollectionLog + + public typealias SubSequence = Base.SubSequence + public typealias Iterator = Base.Iterator + public typealias Element = Base.Element + + public init(wrapping base: Base) { + self.base = base + } + + public func makeIterator() -> Iterator { + return base.makeIterator() + } + + public typealias Index = Base.Index + public typealias Indices = Base.Indices + + public var startIndex: Index { + return base.startIndex + } + + public var endIndex: Index { + return base.endIndex + } + + public var indices: Indices { + return base.indices + } + + public subscript(position: Index) -> Element { + get { + return base[position] + } + set { + base[position] = newValue + } + } + + public subscript(bounds: Range) -> SubSequence { + get { + return base[bounds] + } + set { + base[bounds] = newValue + } + } + + public func index(after i: Index) -> Index { + return base.index(after: i) + } + + + public func index(_ i: Index, offsetBy n: Int) -> Index { + return base.index(i, offsetBy: n) + } + + public func distance(from start: Index, to end: Index) -> Int { + return base.distance(from: start, to: end) + } + + public mutating func _withUnsafeMutableBufferPointerIfSupported( + _ body: (inout UnsafeMutableBufferPointer) throws -> R + ) rethrows -> R? { + Log._withUnsafeMutableBufferPointerIfSupported[selfType] += 1 + let result = try base._withUnsafeMutableBufferPointerIfSupported(body) + if result != nil { + Log._withUnsafeMutableBufferPointerIfSupportedNonNilReturns[selfType] += 1 + } + return result + } +} +/// Interposes between `_withUnsafeMutableBufferPointerIfSupported` method calls +/// to increment a counter. Calls to this method from within dispatched methods +/// are uncounted by the standard logging collection wrapper. +public struct BufferAccessLoggingMutableBidirectionalCollection< + Base : MutableCollection & BidirectionalCollection +> : MutableCollection, BidirectionalCollection, LoggingType { + + public var base: Base + + public typealias Log = MutableCollectionLog + + public typealias SubSequence = Base.SubSequence + public typealias Iterator = Base.Iterator + public typealias Element = Base.Element + + public init(wrapping base: Base) { + self.base = base + } + + public func makeIterator() -> Iterator { + return base.makeIterator() + } + + public typealias Index = Base.Index + public typealias Indices = Base.Indices + + public var startIndex: Index { + return base.startIndex + } + + public var endIndex: Index { + return base.endIndex + } + + public var indices: Indices { + return base.indices + } + + public subscript(position: Index) -> Element { + get { + return base[position] + } + set { + base[position] = newValue + } + } + + public subscript(bounds: Range) -> SubSequence { + get { + return base[bounds] + } + set { + base[bounds] = newValue + } + } + + public func index(after i: Index) -> Index { + return base.index(after: i) + } + + public func index(before i: Index) -> Index { + return base.index(before: i) + } + + public func index(_ i: Index, offsetBy n: Int) -> Index { + return base.index(i, offsetBy: n) + } + + public func distance(from start: Index, to end: Index) -> Int { + return base.distance(from: start, to: end) + } + + public mutating func _withUnsafeMutableBufferPointerIfSupported( + _ body: (inout UnsafeMutableBufferPointer) throws -> R + ) rethrows -> R? { + Log._withUnsafeMutableBufferPointerIfSupported[selfType] += 1 + let result = try base._withUnsafeMutableBufferPointerIfSupported(body) + if result != nil { + Log._withUnsafeMutableBufferPointerIfSupportedNonNilReturns[selfType] += 1 + } + return result + } +} +/// Interposes between `_withUnsafeMutableBufferPointerIfSupported` method calls +/// to increment a counter. Calls to this method from within dispatched methods +/// are uncounted by the standard logging collection wrapper. +public struct BufferAccessLoggingMutableRandomAccessCollection< + Base : MutableCollection & RandomAccessCollection +> : MutableCollection, RandomAccessCollection, LoggingType { + + public var base: Base + + public typealias Log = MutableCollectionLog + + public typealias SubSequence = Base.SubSequence + public typealias Iterator = Base.Iterator + public typealias Element = Base.Element + + public init(wrapping base: Base) { + self.base = base + } + + public func makeIterator() -> Iterator { + return base.makeIterator() + } + + public typealias Index = Base.Index + public typealias Indices = Base.Indices + + public var startIndex: Index { + return base.startIndex + } + + public var endIndex: Index { + return base.endIndex + } + + public var indices: Indices { + return base.indices + } + + public subscript(position: Index) -> Element { + get { + return base[position] + } + set { + base[position] = newValue + } + } + + public subscript(bounds: Range) -> SubSequence { + get { + return base[bounds] + } + set { + base[bounds] = newValue + } + } + + public func index(after i: Index) -> Index { + return base.index(after: i) + } + + public func index(before i: Index) -> Index { + return base.index(before: i) + } + + public func index(_ i: Index, offsetBy n: Int) -> Index { + return base.index(i, offsetBy: n) + } + + public func distance(from start: Index, to end: Index) -> Int { + return base.distance(from: start, to: end) + } + + public mutating func _withUnsafeMutableBufferPointerIfSupported( + _ body: (inout UnsafeMutableBufferPointer) throws -> R + ) rethrows -> R? { + Log._withUnsafeMutableBufferPointerIfSupported[selfType] += 1 + let result = try base._withUnsafeMutableBufferPointerIfSupported(body) + if result != nil { + Log._withUnsafeMutableBufferPointerIfSupportedNonNilReturns[selfType] += 1 + } + return result + } +} + +//===----------------------------------------------------------------------===// +// Custom assertions +//===----------------------------------------------------------------------===// + +public func expectCustomizable( + _: T, _ counters: TypeIndexed, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line +) where + T.Base : LoggingType, + T.Log == T.Base.Log { + let newTrace = stackTrace.pushIf(showFrame, file: file, line: line) + expectNotEqual(0, counters[T.self], message(), stackTrace: newTrace) + expectEqual( + counters[T.self], counters[T.Base.self], message(), stackTrace: newTrace) +} + +public func expectNotCustomizable( + _: T, _ counters: TypeIndexed, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line +) where + T.Base : LoggingType, + T.Log == T.Base.Log { + let newTrace = stackTrace.pushIf(showFrame, file: file, line: line) + expectNotEqual(0, counters[T.self], message(), stackTrace: newTrace) + expectEqual(0, counters[T.Base.self], message(), stackTrace: newTrace) +} diff --git a/stdlib/private/StdlibCollectionUnittest/LoggingWrappers.swift.gyb b/stdlib/private/StdlibCollectionUnittest/LoggingWrappers.swift.gyb deleted file mode 100644 index 1cac3f9e9008c..0000000000000 --- a/stdlib/private/StdlibCollectionUnittest/LoggingWrappers.swift.gyb +++ /dev/null @@ -1,700 +0,0 @@ -//===--- LoggingWrappers.swift.gyb ----------------------------*- swift -*-===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -%{ -from gyb_stdlib_support import TRAVERSALS, collectionForTraversal -from gyb_stdlib_unittest_support import TRACE, stackTrace, trace -}% - -import StdlibUnittest - -public protocol Wrapper { - associatedtype Base - init(wrapping base: Base) - var base: Base { get set } -} - -public protocol LoggingType : Wrapper { - associatedtype Log : AnyObject -} - -extension LoggingType { - public var log: Log.Type { - return Log.self - } - - public var selfType: Any.Type { - return type(of: self) - } -} - -//===----------------------------------------------------------------------===// -// Iterator -//===----------------------------------------------------------------------===// - -public class IteratorLog { - public static func dispatchTester( - _ iterator: I - ) -> LoggingIterator> { - return LoggingIterator(wrapping: LoggingIterator(wrapping: iterator)) - } - public static var next = TypeIndexed(0) -} - -public struct LoggingIterator - : IteratorProtocol, LoggingType { - - public typealias Log = IteratorLog - - public init(wrapping base: Base) { - self.base = base - } - - public mutating func next() -> Base.Element? { - Log.next[selfType] += 1 - return base.next() - } - - public var base: Base -} - -//===----------------------------------------------------------------------===// -// Sequence and Collection logs -//===----------------------------------------------------------------------===// - -public class SequenceLog { - public static func dispatchTester( - _ s: S - ) -> LoggingSequence> { - return LoggingSequence(wrapping: LoggingSequence(wrapping: s)) - } - public static var makeIterator = TypeIndexed(0) - public static var underestimatedCount = TypeIndexed(0) - public static var map = TypeIndexed(0) - public static var filter = TypeIndexed(0) - public static var forEach = TypeIndexed(0) - public static var dropFirst = TypeIndexed(0) - public static var dropLast = TypeIndexed(0) - public static var dropWhile = TypeIndexed(0) - public static var prefixWhile = TypeIndexed(0) - public static var prefixMaxLength = TypeIndexed(0) - public static var suffixMaxLength = TypeIndexed(0) - public static var split = TypeIndexed(0) - public static var _customContainsEquatableElement = TypeIndexed(0) - public static var _preprocessingPass = TypeIndexed(0) - public static var _copyToContiguousArray = TypeIndexed(0) - public static var _copyContents = TypeIndexed(0) -} - -public class CollectionLog : SequenceLog { - public class func dispatchTester( - _ c: C - ) -> LoggingCollection> { - return LoggingCollection(wrapping: LoggingCollection(wrapping: c)) - } - public static var startIndex = TypeIndexed(0) - public static var endIndex = TypeIndexed(0) - public static var subscriptIndex = TypeIndexed(0) - public static var subscriptRange = TypeIndexed(0) - public static var _failEarlyRangeCheckIndex = TypeIndexed(0) - public static var _failEarlyRangeCheckRange = TypeIndexed(0) - public static var successor = TypeIndexed(0) - public static var formSuccessor = TypeIndexed(0) - public static var indices = TypeIndexed(0) - public static var prefixUpTo = TypeIndexed(0) - public static var prefixThrough = TypeIndexed(0) - public static var suffixFrom = TypeIndexed(0) - public static var isEmpty = TypeIndexed(0) - public static var count = TypeIndexed(0) - public static var _customIndexOfEquatableElement = TypeIndexed(0) - public static var first = TypeIndexed(0) - public static var advance = TypeIndexed(0) - public static var advanceLimit = TypeIndexed(0) - public static var distance = TypeIndexed(0) -} - -public class BidirectionalCollectionLog : SequenceLog { - public class func dispatchTester( - _ c: C - ) -> LoggingBidirectionalCollection> { - return LoggingBidirectionalCollection( - wrapping: LoggingBidirectionalCollection(wrapping: c)) - } - public static var predecessor = TypeIndexed(0) - public static var formPredecessor = TypeIndexed(0) - public static var last = TypeIndexed(0) -} - -public class MutableCollectionLog : CollectionLog { - public class func dispatchTester( - _ c: C - ) -> LoggingMutableCollection> { - return LoggingMutableCollection( - wrapping: LoggingMutableCollection(wrapping: c)) - } - public static var subscriptIndexSet = TypeIndexed(0) - public static var subscriptRangeSet = TypeIndexed(0) - public static var partitionBy = TypeIndexed(0) - public static var _withUnsafeMutableBufferPointerIfSupported = TypeIndexed(0) - public static var _withUnsafeMutableBufferPointerIfSupportedNonNilReturns = - TypeIndexed(0) -} - -/// Data container to keep track of how many times each `Base` type calls methods -/// of `RangeReplaceableCollection`. -/// -/// Each static variable is a mapping of Type -> Number of calls. -public class RangeReplaceableCollectionLog : CollectionLog { - public static var init_ = TypeIndexed(0) - public static var initRepeating = TypeIndexed(0) - public static var initWithSequence = TypeIndexed(0) - - public static var _customRemoveLast = TypeIndexed(0) - public static var _customRemoveLastN = TypeIndexed(0) - public static var append = TypeIndexed(0) - public static var appendContentsOf = TypeIndexed(0) - public static var insert = TypeIndexed(0) - public static var insertContentsOf = TypeIndexed(0) - public static var removeAll = TypeIndexed(0) - public static var removeAt = TypeIndexed(0) - public static var removeFirst = TypeIndexed(0) - public static var removeFirstN = TypeIndexed(0) - public static var removeSubrange = TypeIndexed(0) - public static var replaceSubrange = TypeIndexed(0) - public static var reserveCapacity = TypeIndexed(0) - - public class func dispatchTester( - _ rrc: C - ) -> LoggingRangeReplaceableCollection> { - return LoggingRangeReplaceableCollection( - wrapping: LoggingRangeReplaceableCollection(wrapping: rrc) - ) - } -} - -//===----------------------------------------------------------------------===// -// Sequence and Collection that count method calls -//===----------------------------------------------------------------------===// - -% for Kind in ['Sequence', 'Collection', 'MutableCollection', 'RangeReplaceableCollection']: -% for Traversal in [''] if Kind == 'Sequence' else TRAVERSALS: -% if Kind == 'Sequence': -% Self = 'LoggingSequence' -% else: -% Self = 'Logging' + Kind.replace('Collection', collectionForTraversal(Traversal)) -% end -% if Kind == 'Sequence': -% Constraints = Kind -% Protocols = Kind -% else: -% Constraints = Kind + ' & ' + collectionForTraversal(Traversal) -% Protocols = ', '.join(set([Kind, collectionForTraversal(Traversal)])) -% end -/// Interposes between `${Kind}` method calls to increment each method's -/// counter. -public struct ${Self}< - Base : ${Constraints} -> : ${Protocols}, LoggingType { - - public var base: Base - - public typealias Log = ${Kind}Log - - public init(wrapping base: Base) { - self.base = base - } - - public typealias Iterator = LoggingIterator - - public func makeIterator() -> Iterator { - Log.makeIterator[selfType] += 1 - return LoggingIterator(wrapping: base.makeIterator()) - } - - public var underestimatedCount: Int { - Log.underestimatedCount[selfType] += 1 - return base.underestimatedCount - } - - public func map( - _ transform: (Base.Iterator.Element) throws -> T - ) rethrows -> [T] { - Log.map[selfType] += 1 - return try base.map(transform) - } - - public func filter( - _ isIncluded: (Base.Iterator.Element) throws -> Bool - ) rethrows -> [Base.Iterator.Element] { - Log.filter[selfType] += 1 - return try base.filter(isIncluded) - } - - public func forEach( - _ body: (Base.Iterator.Element) throws -> Void - ) rethrows { - Log.forEach[selfType] += 1 - try base.forEach(body) - } - - public typealias SubSequence = Base.SubSequence - - public func dropFirst(_ n: Int) -> SubSequence { - Log.dropFirst[selfType] += 1 - return base.dropFirst(n) - } - - public func dropLast(_ n: Int) -> SubSequence { - Log.dropLast[selfType] += 1 - return base.dropLast(n) - } - - public func drop( - while predicate: (Base.Iterator.Element) throws -> Bool - ) rethrows -> SubSequence { - Log.dropWhile[selfType] += 1 - return try base.drop(while: predicate) - } - - public func prefix(_ maxLength: Int) -> SubSequence { - Log.prefixMaxLength[selfType] += 1 - return base.prefix(maxLength) - } - - public func prefix( - while predicate: (Base.Iterator.Element) throws -> Bool - ) rethrows -> SubSequence { - Log.prefixWhile[selfType] += 1 - return try base.prefix(while: predicate) - } - - public func suffix(_ maxLength: Int) -> SubSequence { - Log.suffixMaxLength[selfType] += 1 - return base.suffix(maxLength) - } - - public func split( - maxSplits: Int = Int.max, - omittingEmptySubsequences: Bool = true, - whereSeparator isSeparator: (Base.Iterator.Element) throws -> Bool - ) rethrows -> [SubSequence] { - Log.split[selfType] += 1 - return try base.split( - maxSplits: maxSplits, - omittingEmptySubsequences: omittingEmptySubsequences, - whereSeparator: isSeparator) - } - - public func _customContainsEquatableElement( - _ element: Base.Iterator.Element - ) -> Bool? { - Log._customContainsEquatableElement[selfType] += 1 - return base._customContainsEquatableElement(element) - } - - /// If `self` is multi-pass (i.e., a `Collection`), invoke - /// `preprocess` on `self` and return its result. Otherwise, return - /// `nil`. - public func _preprocessingPass( - _ preprocess: () throws -> R - ) rethrows -> R? { - Log._preprocessingPass[selfType] += 1 - return try base._preprocessingPass(preprocess) - } - - /// Create a native array buffer containing the elements of `self`, - /// in the same order. - public func _copyToContiguousArray() - -> ContiguousArray { - Log._copyToContiguousArray[selfType] += 1 - return base._copyToContiguousArray() - } - - /// Copy a Sequence into an array. - public func _copyContents( - initializing buffer: UnsafeMutableBufferPointer - ) -> (Iterator,UnsafeMutableBufferPointer.Index) { - Log._copyContents[selfType] += 1 - let (it,idx) = base._copyContents(initializing: buffer) - return (Iterator(wrapping: it),idx) - } - -% if Kind in ['Collection', 'MutableCollection', 'RangeReplaceableCollection']: - public typealias Index = Base.Index - - public var startIndex: Index { - Log.startIndex[selfType] += 1 - return base.startIndex - } - - public var endIndex: Index { - Log.endIndex[selfType] += 1 - return base.endIndex - } - - public subscript(position: Index) -> Base.Iterator.Element { - get { - Log.subscriptIndex[selfType] += 1 - return base[position] - } -% if Kind == 'MutableCollection': - set { - Log.subscriptIndexSet[selfType] += 1 - base[position] = newValue - } -% end - } - - public subscript(bounds: Range) -> SubSequence { - get { - Log.subscriptRange[selfType] += 1 - return base[bounds] - } -% if Kind == 'MutableCollection': - set { - Log.subscriptRangeSet[selfType] += 1 - base[bounds] = newValue - } -% end - } - - public func _failEarlyRangeCheck(_ index: Index, bounds: Range) { - Log._failEarlyRangeCheckIndex[selfType] += 1 - base._failEarlyRangeCheck(index, bounds: bounds) - } - - public func _failEarlyRangeCheck(_ range: Range, bounds: Range) { - Log._failEarlyRangeCheckRange[selfType] += 1 - base._failEarlyRangeCheck(range, bounds: bounds) - } - - public func index(after i: Index) -> Index { - Log.successor[selfType] += 1 - return base.index(after: i) - } - - public func formIndex(after i: inout Index) { - Log.formSuccessor[selfType] += 1 - base.formIndex(after: &i) - } - - public typealias Indices = Base.Indices - - public var indices: Indices { - Log.indices[selfType] += 1 - return base.indices - } - - public func prefix(upTo end: Index) -> SubSequence { - Log.prefixUpTo[selfType] += 1 - return base.prefix(upTo: end) - } - - public func prefix(through position: Index) -> SubSequence { - Log.prefixThrough[selfType] += 1 - return base.prefix(through: position) - } - - public func suffix(from start: Index) -> SubSequence { - Log.suffixFrom[selfType] += 1 - return base.suffix(from: start) - } - - public var isEmpty: Bool { - Log.isEmpty[selfType] += 1 - return base.isEmpty - } - - public var count: Int { - Log.count[selfType] += 1 - return base.count - } - - public func _customIndexOfEquatableElement( - _ element: Base.Iterator.Element - ) -> Index?? { - Log._customIndexOfEquatableElement[selfType] += 1 - return base._customIndexOfEquatableElement(element) - } - - public var first: Base.Iterator.Element? { - Log.first[selfType] += 1 - return base.first - } - - public func index(_ i: Index, offsetBy n: Int) -> Index { - Log.advance[selfType] += 1 - return base.index(i, offsetBy: n) - } - - public func index( - _ i: Index, offsetBy n: Int, limitedBy limit: Index - ) -> Index? { - Log.advanceLimit[selfType] += 1 - return base.index(i, offsetBy: n, limitedBy: limit) - } - - public func distance(from start: Index, to end: Index) -> Int { - Log.distance[selfType] += 1 - return base.distance(from: start, to: end) - } -% end - -% if Kind == 'MutableCollection': - public mutating func partition( - by belongsInSecondPartition: (Iterator.Element) throws -> Bool - ) rethrows -> Index { - Log.partitionBy[selfType] += 1 - return try base.partition(by: belongsInSecondPartition) - } - - public mutating func _withUnsafeMutableBufferPointerIfSupported( - _ body: (inout UnsafeMutableBufferPointer) throws -> R - ) rethrows -> R? { - Log._withUnsafeMutableBufferPointerIfSupported[selfType] += 1 - let result = try base._withUnsafeMutableBufferPointerIfSupported(body) - if result != nil { - Log._withUnsafeMutableBufferPointerIfSupportedNonNilReturns[selfType] += 1 - } - return result - } -% end - -% if Kind == 'RangeReplaceableCollection': - public init() { - self.base = Base() - Log.init_[selfType] += 1 - } - - public init(repeating repeatedValue: Iterator.Element, count: Int) { - self.base = Base(repeating: repeatedValue, count: count) - Log.initRepeating[selfType] += 1 - } - - public init(_ elements: S) - where S.Iterator.Element == Iterator.Element { - self.base = Base(elements) - Log.initWithSequence[selfType] += 1 - } - - public mutating func _customRemoveLast() -> Base.Iterator.Element? { - Log._customRemoveLast[selfType] += 1 - return base._customRemoveLast() - } - - public mutating func _customRemoveLast(_ n: Int) -> Bool { - Log._customRemoveLastN[selfType] += 1 - return base._customRemoveLast(n) - } - - public mutating func append(_ newElement: Base.Iterator.Element) { - Log.append[selfType] += 1 - base.append(newElement) - } - - public mutating func append( - contentsOf newElements: S - ) where S.Iterator.Element == Base.Iterator.Element { - Log.appendContentsOf[selfType] += 1 - base.append(contentsOf: newElements) - } - - public mutating func insert( - _ newElement: Base.Iterator.Element, at i: Index - ) { - Log.insert[selfType] += 1 - base.insert(newElement, at: i) - } - - public mutating func insert( - contentsOf newElements: C, at i: Index - ) where C.Iterator.Element == Base.Iterator.Element { - Log.insertContentsOf[selfType] += 1 - base.insert(contentsOf: newElements, at: i) - } - - public mutating func removeAll(keepingCapacity keepCapacity: Bool) { - Log.removeAll[selfType] += 1 - base.removeAll(keepingCapacity: keepCapacity) - } - - @discardableResult - public mutating func remove(at index: Index) -> Base.Iterator.Element { - Log.removeAt[selfType] += 1 - return base.remove(at: index) - } - - @discardableResult - public mutating func removeFirst() -> Base.Iterator.Element { - Log.removeFirst[selfType] += 1 - return base.removeFirst() - } - - public mutating func removeFirst(_ n: Int) { - Log.removeFirstN[selfType] += 1 - base.removeFirst(n) - } - - public mutating func removeSubrange(_ bounds: Range) { - Log.removeSubrange[selfType] += 1 - base.removeSubrange(bounds) - } - - public mutating func replaceSubrange( - _ bounds: Range, with newElements: C - ) where C.Iterator.Element == Base.Iterator.Element { - Log.replaceSubrange[selfType] += 1 - base.replaceSubrange(bounds, with: newElements) - } - - public mutating func reserveCapacity(_ n: Int) { - Log.reserveCapacity[selfType] += 1 - base.reserveCapacity(n) - } -% end -% if Traversal in ['Bidirectional', 'RandomAccess']: - public func index(before i: Index) -> Index { - BidirectionalCollectionLog.predecessor[selfType] += 1 - return base.index(before: i) - } - - public func formIndex(before i: inout Index) { - BidirectionalCollectionLog.formPredecessor[selfType] += 1 - base.formIndex(before: &i) - } - - public var last: Iterator.Element? { - BidirectionalCollectionLog.last[selfType] += 1 - return base.last - } -% end -} -% end -% end - -//===----------------------------------------------------------------------===// -// Collections that count calls to `_withUnsafeMutableBufferPointerIfSupported` -//===----------------------------------------------------------------------===// - -% for Traversal in TRAVERSALS: -% Self = 'BufferAccessLoggingMutable' + collectionForTraversal(Traversal) -/// Interposes between `_withUnsafeMutableBufferPointerIfSupported` method calls -/// to increment a counter. Calls to this method from within dispatched methods -/// are uncounted by the standard logging collection wrapper. -public struct ${Self}< - Base : MutableCollection & ${collectionForTraversal(Traversal)} -> : MutableCollection, ${collectionForTraversal(Traversal)}, LoggingType { - - public var base: Base - - public typealias Log = MutableCollectionLog - - public typealias SubSequence = Base.SubSequence - public typealias Iterator = Base.Iterator - public typealias Element = Base.Element - - public init(wrapping base: Base) { - self.base = base - } - - public func makeIterator() -> Iterator { - return base.makeIterator() - } - - public typealias Index = Base.Index - public typealias Indices = Base.Indices - - public var startIndex: Index { - return base.startIndex - } - - public var endIndex: Index { - return base.endIndex - } - - public var indices: Indices { - return base.indices - } - - public subscript(position: Index) -> Element { - get { - return base[position] - } - set { - base[position] = newValue - } - } - - public subscript(bounds: Range) -> SubSequence { - get { - return base[bounds] - } - set { - base[bounds] = newValue - } - } - - public func index(after i: Index) -> Index { - return base.index(after: i) - } - -% if Traversal in ['Bidirectional', 'RandomAccess']: - public func index(before i: Index) -> Index { - return base.index(before: i) - } -% end - - public func index(_ i: Index, offsetBy n: Int) -> Index { - return base.index(i, offsetBy: n) - } - - public func distance(from start: Index, to end: Index) -> Int { - return base.distance(from: start, to: end) - } - - public mutating func _withUnsafeMutableBufferPointerIfSupported( - _ body: (inout UnsafeMutableBufferPointer) throws -> R - ) rethrows -> R? { - Log._withUnsafeMutableBufferPointerIfSupported[selfType] += 1 - let result = try base._withUnsafeMutableBufferPointerIfSupported(body) - if result != nil { - Log._withUnsafeMutableBufferPointerIfSupportedNonNilReturns[selfType] += 1 - } - return result - } -} -% end - -//===----------------------------------------------------------------------===// -// Custom assertions -//===----------------------------------------------------------------------===// - -public func expectCustomizable( - _: T, _ counters: TypeIndexed, ${TRACE} -) where - T.Base : LoggingType, - T.Log == T.Base.Log { - let newTrace = stackTrace.pushIf(showFrame, file: file, line: line) - expectNotEqual(0, counters[T.self], message(), stackTrace: newTrace) - expectEqual( - counters[T.self], counters[T.Base.self], message(), stackTrace: newTrace) -} - -public func expectNotCustomizable( - _: T, _ counters: TypeIndexed, ${TRACE} -) where - T.Base : LoggingType, - T.Log == T.Base.Log { - let newTrace = stackTrace.pushIf(showFrame, file: file, line: line) - expectNotEqual(0, counters[T.self], message(), stackTrace: newTrace) - expectEqual(0, counters[T.Base.self], message(), stackTrace: newTrace) -} diff --git a/stdlib/private/StdlibCollectionUnittest/MinimalCollections.swift b/stdlib/private/StdlibCollectionUnittest/MinimalCollections.swift new file mode 100644 index 0000000000000..73a3379789bdb --- /dev/null +++ b/stdlib/private/StdlibCollectionUnittest/MinimalCollections.swift @@ -0,0 +1,6060 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +import StdlibUnittest + +/// State shared by all generators of a MinimalSequence. +internal class _MinimalIteratorSharedState { + internal init(_ data: [T]) { + self.data = data + } + + internal let data: [T] + internal var i: Int = 0 + internal var underestimatedCount: Int = 0 +} + +//===----------------------------------------------------------------------===// +// MinimalIterator +//===----------------------------------------------------------------------===// + +/// An IteratorProtocol that implements the protocol contract in the most +/// narrow way possible. +/// +/// This generator will return `nil` only once. +public struct MinimalIterator : IteratorProtocol { + public init(_ s: S) where S.Iterator.Element == T { + self._sharedState = _MinimalIteratorSharedState(Array(s)) + } + + public init(_ data: [T]) { + self._sharedState = _MinimalIteratorSharedState(data) + } + + internal init(_ _sharedState: _MinimalIteratorSharedState) { + self._sharedState = _sharedState + } + + public func next() -> T? { + if _sharedState.i == _sharedState.data.count { + return nil + } + defer { _sharedState.i += 1 } + return _sharedState.data[_sharedState.i] + } + + internal let _sharedState: _MinimalIteratorSharedState +} + +// A protocol to identify MinimalIterator. +public protocol _MinimalIterator {} +extension MinimalIterator : _MinimalIterator {} + +//===----------------------------------------------------------------------===// +// MinimalSequence +//===----------------------------------------------------------------------===// + +public enum UnderestimatedCountBehavior { + /// Return the actual number of elements. + case precise + + /// Return the actual number of elements divided by 2. + case half + + /// Return an overestimated count. Useful to test how algorithms reserve + /// memory. + case overestimate + + /// Return the provided value. + case value(Int) +} + +/// A Sequence that implements the protocol contract in the most +/// narrow way possible. +/// +/// This sequence is consumed when its generator is advanced. +public struct MinimalSequence : Sequence, CustomDebugStringConvertible { + public init( + elements: S, + underestimatedCount: UnderestimatedCountBehavior = .value(0) + ) where S.Iterator.Element == T { + let data = Array(elements) + self._sharedState = _MinimalIteratorSharedState(data) + + switch underestimatedCount { + case .precise: + self._sharedState.underestimatedCount = data.count + + case .half: + self._sharedState.underestimatedCount = data.count / 2 + + case .overestimate: + self._sharedState.underestimatedCount = data.count * 3 + 5 + + case .value(let count): + self._sharedState.underestimatedCount = count + } + } + + public let timesMakeIteratorCalled = ResettableValue(0) + + public func makeIterator() -> MinimalIterator { + timesMakeIteratorCalled.value += 1 + return MinimalIterator(_sharedState) + } + + public var underestimatedCount: Int { + return Swift.max(0, self._sharedState.underestimatedCount - self._sharedState.i) + } + + public var debugDescription: String { + return "MinimalSequence(\(_sharedState.data[_sharedState.i..<_sharedState.data.count]))" + } + + internal let _sharedState: _MinimalIteratorSharedState +} + +//===----------------------------------------------------------------------===// +// Index invalidation checking +//===----------------------------------------------------------------------===// + +internal enum _CollectionOperation : Equatable { + case reserveCapacity(capacity: Int) + case append + case appendContentsOf(count: Int) + case replaceRange(subRange: Range, replacementCount: Int) + case insert(atIndex: Int) + case insertContentsOf(atIndex: Int, count: Int) + case removeAtIndex(index: Int) + case removeLast + case removeRange(subRange: Range) + case removeAll(keepCapacity: Bool) + + internal func _applyTo( + elementsLastMutatedStateIds: [Int], + endIndexLastMutatedStateId: Int, + nextStateId: Int + ) -> ([Int], Int) { + var newElementsIds = elementsLastMutatedStateIds + var newEndIndexId = endIndexLastMutatedStateId + switch self { + case .reserveCapacity: + let invalidIndices = newElementsIds.indices + newElementsIds.replaceSubrange( + invalidIndices, + with: repeatElement(nextStateId, count: invalidIndices.count)) + newEndIndexId = nextStateId + + case .append: + newElementsIds.append(nextStateId) + newEndIndexId = nextStateId + + case .appendContentsOf(let count): + newElementsIds.append(contentsOf: + repeatElement(nextStateId, count: count)) + newEndIndexId = nextStateId + + case .replaceRange(let subRange, let replacementCount): + newElementsIds.replaceSubrange( + subRange, + with: repeatElement(nextStateId, count: replacementCount)) + + let invalidIndices = subRange.lowerBound.. Bool { + switch (lhs, rhs) { + case (.reserveCapacity(let lhsCapacity), .reserveCapacity(let rhsCapacity)): + return lhsCapacity == rhsCapacity + + case (.append, .append): + return true + + case (.appendContentsOf(let lhsCount), .appendContentsOf(let rhsCount)): + return lhsCount == rhsCount + + case ( + .replaceRange(let lhsSubRange, let lhsReplacementCount), + .replaceRange(let rhsSubRange, let rhsReplacementCount)): + + return lhsSubRange == rhsSubRange && + lhsReplacementCount == rhsReplacementCount + + case (.insert(let lhsAtIndex), .insert(let rhsAtIndex)): + return lhsAtIndex == rhsAtIndex + + case ( + .insertContentsOf(let lhsAtIndex, let lhsCount), + .insertContentsOf(let rhsAtIndex, let rhsCount)): + + return lhsAtIndex == rhsAtIndex && lhsCount == rhsCount + + case (.removeAtIndex(let lhsIndex), .removeAtIndex(let rhsIndex)): + return lhsIndex == rhsIndex + + case (.removeLast, .removeLast): + return true + + case (.removeRange(let lhsSubRange), .removeRange(let rhsSubRange)): + return lhsSubRange == rhsSubRange + + case (.removeAll(let lhsKeepCapacity), .removeAll(let rhsKeepCapacity)): + return lhsKeepCapacity == rhsKeepCapacity + + default: + return false + } +} + +public struct _CollectionState : Equatable, Hashable { + internal static var _nextUnusedState: Int = 0 + internal static var _namedStates: [String : _CollectionState] = [:] + + internal let _id: Int + internal let _elementsLastMutatedStateIds: [Int] + internal let _endIndexLastMutatedStateId: Int + + internal init( + id: Int, + elementsLastMutatedStateIds: [Int], + endIndexLastMutatedStateId: Int) { + self._id = id + self._elementsLastMutatedStateIds = elementsLastMutatedStateIds + self._endIndexLastMutatedStateId = endIndexLastMutatedStateId + } + + public init(newRootStateForElementCount count: Int) { + self._id = _CollectionState._nextUnusedState + _CollectionState._nextUnusedState += 1 + self._elementsLastMutatedStateIds = + Array(repeatElement(self._id, count: count)) + self._endIndexLastMutatedStateId = self._id + } + + internal init(name: String, elementCount: Int) { + if let result = _CollectionState._namedStates[name] { + self = result + } else { + self = _CollectionState(newRootStateForElementCount: elementCount) + _CollectionState._namedStates[name] = self + } + } + + public var hashValue: Int { + return _id.hashValue + } +} + +public func == (lhs: _CollectionState, rhs: _CollectionState) -> Bool { + return lhs._id == rhs._id +} + +internal struct _CollectionStateTransition { + internal let _previousState: _CollectionState + internal let _operation: _CollectionOperation + internal let _nextState: _CollectionState + + internal static var _allTransitions: + [_CollectionState : Box<[_CollectionStateTransition]>] = [:] + + internal init( + previousState: _CollectionState, + operation: _CollectionOperation, + nextState: _CollectionState + ) { + var transitions = + _CollectionStateTransition._allTransitions[previousState] + if transitions == nil { + transitions = Box<[_CollectionStateTransition]>([]) + _CollectionStateTransition._allTransitions[previousState] = transitions + } + if let i = transitions!.value.firstIndex(where: { $0._operation == operation }) { + self = transitions!.value[i] + return + } + self._previousState = previousState + self._operation = operation + self._nextState = nextState + transitions!.value.append(self) + } + + internal init( + previousState: _CollectionState, + operation: _CollectionOperation + ) { + let nextStateId = _CollectionState._nextUnusedState + _CollectionState._nextUnusedState += 1 + let (newElementStates, newEndIndexState) = operation._applyTo( + elementsLastMutatedStateIds: previousState._elementsLastMutatedStateIds, + endIndexLastMutatedStateId: previousState._endIndexLastMutatedStateId, + nextStateId: nextStateId) + let nextState = _CollectionState( + id: nextStateId, + elementsLastMutatedStateIds: newElementStates, + endIndexLastMutatedStateId: newEndIndexState) + self = _CollectionStateTransition( + previousState: previousState, + operation: operation, + nextState: nextState) + } +} + + +//===----------------------------------------------------------------------===// +// MinimalIndex +//===----------------------------------------------------------------------===// + +/// Asserts that the two indices are allowed to participate in a binary +/// operation. +internal func _expectCompatibleIndices( + _ first: MinimalIndex, + _ second: MinimalIndex, + + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line +) { + if first._collectionState._id == second._collectionState._id { + // Fast path: the indices are derived from the same state. + return + } + + // The indices are derived from different states. Check that they point + // to a self-consistent view of the collection. + if first._collectionState._id > second._collectionState._id { + return _expectCompatibleIndices(second, first) + } + + func lastMutatedStateId( + of i: MinimalIndex, + in state: _CollectionState + ) -> Int { + let offset = i.position + if offset == state._elementsLastMutatedStateIds.endIndex { + return state._id + } + return state._elementsLastMutatedStateIds[offset] + } + + let newestCollectionState = second._collectionState + let expectedFirstIndexLastMutatedStateId = + lastMutatedStateId(of: first, in: newestCollectionState) + + expectEqual( + expectedFirstIndexLastMutatedStateId, + first._collectionState._id, + "Indices are not compatible:\n" + + "first: \(first)\n" + + "second: \(second)\n" + + "first element last mutated in state id: \(first._collectionState._id)\n" + + "expected state id: \(expectedFirstIndexLastMutatedStateId)\n" + + "newest collection state: \(newestCollectionState)", + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) + + // To make writing assertions easier, perform a trap. + if expectedFirstIndexLastMutatedStateId != first._collectionState._id { + fatalError("Indices are not compatible") + } +} + +public struct MinimalIndex : Comparable { + public init( + collectionState: _CollectionState, + position: Int, + startIndex: Int, + endIndex: Int + ) { + expectTrapping( + position, + in: startIndex...endIndex) + self = MinimalIndex( + _collectionState: collectionState, + uncheckedPosition: position) + } + + internal init( + _collectionState: _CollectionState, + uncheckedPosition: Int + ) { + self._collectionState = _collectionState + self.position = uncheckedPosition + } + + public let _collectionState: _CollectionState + public let position: Int + + public static var trapOnRangeCheckFailure = ResettableValue(true) + +} + +public func == (lhs: MinimalIndex, rhs: MinimalIndex) -> Bool { + _expectCompatibleIndices(lhs, rhs) + return lhs.position == rhs.position +} + +public func < (lhs: MinimalIndex, rhs: MinimalIndex) -> Bool { + _expectCompatibleIndices(lhs, rhs) + return lhs.position < rhs.position +} + + +//===----------------------------------------------------------------------===// +// MinimalStrideableIndex +//===----------------------------------------------------------------------===// + +/// Asserts that the two indices are allowed to participate in a binary +/// operation. +internal func _expectCompatibleIndices( + _ first: MinimalStrideableIndex, + _ second: MinimalStrideableIndex, + + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line +) { + if first._collectionState._id == second._collectionState._id { + // Fast path: the indices are derived from the same state. + return + } + + // The indices are derived from different states. Check that they point + // to a self-consistent view of the collection. + if first._collectionState._id > second._collectionState._id { + return _expectCompatibleIndices(second, first) + } + + func lastMutatedStateId( + of i: MinimalStrideableIndex, + in state: _CollectionState + ) -> Int { + let offset = i.position + if offset == state._elementsLastMutatedStateIds.endIndex { + return state._id + } + return state._elementsLastMutatedStateIds[offset] + } + + let newestCollectionState = second._collectionState + let expectedFirstIndexLastMutatedStateId = + lastMutatedStateId(of: first, in: newestCollectionState) + + expectEqual( + expectedFirstIndexLastMutatedStateId, + first._collectionState._id, + "Indices are not compatible:\n" + + "first: \(first)\n" + + "second: \(second)\n" + + "first element last mutated in state id: \(first._collectionState._id)\n" + + "expected state id: \(expectedFirstIndexLastMutatedStateId)\n" + + "newest collection state: \(newestCollectionState)", + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) + + // To make writing assertions easier, perform a trap. + if expectedFirstIndexLastMutatedStateId != first._collectionState._id { + fatalError("Indices are not compatible") + } +} + +public struct MinimalStrideableIndex : Comparable { + public init( + collectionState: _CollectionState, + position: Int, + startIndex: Int, + endIndex: Int + ) { + expectTrapping( + position, + in: startIndex...endIndex) + self = MinimalStrideableIndex( + _collectionState: collectionState, + uncheckedPosition: position) + } + + internal init( + _collectionState: _CollectionState, + uncheckedPosition: Int + ) { + self._collectionState = _collectionState + self.position = uncheckedPosition + } + + public let _collectionState: _CollectionState + public let position: Int + + public static var trapOnRangeCheckFailure = ResettableValue(true) + + public var timesAdvancedCalled = ResettableValue(0) + public var timesDistanceCalled = ResettableValue(0) +} + +public func == (lhs: MinimalStrideableIndex, rhs: MinimalStrideableIndex) -> Bool { + _expectCompatibleIndices(lhs, rhs) + return lhs.position == rhs.position +} + +public func < (lhs: MinimalStrideableIndex, rhs: MinimalStrideableIndex) -> Bool { + _expectCompatibleIndices(lhs, rhs) + return lhs.position < rhs.position +} + + +extension MinimalStrideableIndex : Strideable { + public typealias Stride = Int + + public func distance(to other: MinimalStrideableIndex) -> Int { + timesDistanceCalled.value += 1 + _expectCompatibleIndices(self, other) + return other.position - position + } + + public func advanced(by n: Int) -> MinimalStrideableIndex { + timesAdvancedCalled.value += 1 + return MinimalStrideableIndex( + _collectionState: _collectionState, + uncheckedPosition: position + n) + } +} + +//===----------------------------------------------------------------------===// +// Minimal***[Mutable]?Collection +//===----------------------------------------------------------------------===// + + +/// A minimal implementation of `Collection` with extra checks. +public struct MinimalCollection : Collection { + /// Creates a collection with given contents, but a unique modification + /// history. No other instance has the same modification history. + public init( + elements: S, + underestimatedCount: UnderestimatedCountBehavior = .value(0) + ) where S.Iterator.Element == T { + self._elements = Array(elements) + + self._collectionState = _CollectionState( + newRootStateForElementCount: self._elements.count) + + switch underestimatedCount { + case .precise: + self.underestimatedCount = _elements.count + + case .half: + self.underestimatedCount = _elements.count / 2 + + case .overestimate: + self.underestimatedCount = _elements.count * 3 + 5 + + case .value(let count): + self.underestimatedCount = count + } + } + + + public let timesMakeIteratorCalled = ResettableValue(0) + + public func makeIterator() -> MinimalIterator { + timesMakeIteratorCalled.value += 1 + return MinimalIterator(_elements) + } + + public typealias Index = MinimalIndex + + internal func _index(forPosition i: Int) -> MinimalIndex { + return MinimalIndex( + collectionState: _collectionState, + position: i, + startIndex: _elements.startIndex, + endIndex: _elements.endIndex) + } + + internal func _uncheckedIndex(forPosition i: Int) -> MinimalIndex { + return MinimalIndex( + _collectionState: _collectionState, + uncheckedPosition: i) + } + + public let timesStartIndexCalled = ResettableValue(0) + + public var startIndex: MinimalIndex { + timesStartIndexCalled.value += 1 + return _uncheckedIndex(forPosition: _elements.startIndex) + } + + public let timesEndIndexCalled = ResettableValue(0) + + public var endIndex: MinimalIndex { + timesEndIndexCalled.value += 1 + return _uncheckedIndex(forPosition: _elements.endIndex) + } + + + public func _failEarlyRangeCheck( + _ index: MinimalIndex, + bounds: Range + ) { + _expectCompatibleIndices( + _uncheckedIndex(forPosition: index.position), + index) + + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.lowerBound.position), + bounds.lowerBound) + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.upperBound.position), + bounds.upperBound) + + expectTrapping( + index.position, + in: bounds.lowerBound.position.., + bounds: Range + ) { + _expectCompatibleIndices( + _uncheckedIndex(forPosition: range.lowerBound.position), + range.lowerBound) + _expectCompatibleIndices( + _uncheckedIndex(forPosition: range.upperBound.position), + range.upperBound) + + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.lowerBound.position), + bounds.lowerBound) + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.upperBound.position), + bounds.upperBound) + + expectTrapping( + range.lowerBound.position.. MinimalIndex { + _failEarlyRangeCheck(i, bounds: startIndex.. Int { + _precondition(start <= end, + "Only BidirectionalCollections can have end come before start") + // FIXME: swift-3-indexing-model: perform a range check properly. + if start != endIndex { + _failEarlyRangeCheck(start, bounds: startIndex.. Index { + _precondition(n >= 0, + "Only BidirectionalCollections can be advanced by a negative amount") + // FIXME: swift-3-indexing-model: perform a range check properly. + if i != endIndex { + _failEarlyRangeCheck(i, bounds: startIndex.. T { + get { + _failEarlyRangeCheck(i, bounds: startIndex..) -> Slice> { + get { + _failEarlyRangeCheck(bounds, bounds: startIndex.. : Collection, RangeReplaceableCollection { + /// Creates a collection with given contents, but a unique modification + /// history. No other instance has the same modification history. + public init( + elements: S, + underestimatedCount: UnderestimatedCountBehavior = .value(0) + ) where S.Iterator.Element == T { + self._elements = Array(elements) + + self._collectionState = _CollectionState( + newRootStateForElementCount: self._elements.count) + + switch underestimatedCount { + case .precise: + self.underestimatedCount = _elements.count + + case .half: + self.underestimatedCount = _elements.count / 2 + + case .overestimate: + self.underestimatedCount = _elements.count * 3 + 5 + + case .value(let count): + self.underestimatedCount = count + } + } + + public init() { + self.underestimatedCount = 0 + self._elements = [] + self._collectionState = + _CollectionState(name: "\(type(of: self))", elementCount: 0) + } + + public init(_ elements: S) where S.Iterator.Element == T { + self.underestimatedCount = 0 + self._elements = Array(elements) + self._collectionState = + _CollectionState(newRootStateForElementCount: self._elements.count) + } + + public let timesMakeIteratorCalled = ResettableValue(0) + + public func makeIterator() -> MinimalIterator { + timesMakeIteratorCalled.value += 1 + return MinimalIterator(_elements) + } + + public typealias Index = MinimalIndex + + internal func _index(forPosition i: Int) -> MinimalIndex { + return MinimalIndex( + collectionState: _collectionState, + position: i, + startIndex: _elements.startIndex, + endIndex: _elements.endIndex) + } + + internal func _uncheckedIndex(forPosition i: Int) -> MinimalIndex { + return MinimalIndex( + _collectionState: _collectionState, + uncheckedPosition: i) + } + + public let timesStartIndexCalled = ResettableValue(0) + + public var startIndex: MinimalIndex { + timesStartIndexCalled.value += 1 + return _uncheckedIndex(forPosition: _elements.startIndex) + } + + public let timesEndIndexCalled = ResettableValue(0) + + public var endIndex: MinimalIndex { + timesEndIndexCalled.value += 1 + return _uncheckedIndex(forPosition: _elements.endIndex) + } + + + public func _failEarlyRangeCheck( + _ index: MinimalIndex, + bounds: Range + ) { + _expectCompatibleIndices( + _uncheckedIndex(forPosition: index.position), + index) + + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.lowerBound.position), + bounds.lowerBound) + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.upperBound.position), + bounds.upperBound) + + expectTrapping( + index.position, + in: bounds.lowerBound.position.., + bounds: Range + ) { + _expectCompatibleIndices( + _uncheckedIndex(forPosition: range.lowerBound.position), + range.lowerBound) + _expectCompatibleIndices( + _uncheckedIndex(forPosition: range.upperBound.position), + range.upperBound) + + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.lowerBound.position), + bounds.lowerBound) + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.upperBound.position), + bounds.upperBound) + + expectTrapping( + range.lowerBound.position.. MinimalIndex { + _failEarlyRangeCheck(i, bounds: startIndex.. Int { + _precondition(start <= end, + "Only BidirectionalCollections can have end come before start") + // FIXME: swift-3-indexing-model: perform a range check properly. + if start != endIndex { + _failEarlyRangeCheck(start, bounds: startIndex.. Index { + _precondition(n >= 0, + "Only BidirectionalCollections can be advanced by a negative amount") + // FIXME: swift-3-indexing-model: perform a range check properly. + if i != endIndex { + _failEarlyRangeCheck(i, bounds: startIndex.. T { + get { + _failEarlyRangeCheck(i, bounds: startIndex..) -> Slice> { + get { + _failEarlyRangeCheck(bounds, bounds: startIndex..(contentsOf newElements: S) + where S.Iterator.Element == T { + let oldCount = count + _elements.append(contentsOf: newElements) + let newCount = count + _willMutate(.appendContentsOf(count: newCount - oldCount)) + } + + public mutating func replaceSubrange( + _ subRange: Range, + with newElements: C + ) where C : Collection, C.Iterator.Element == T { + let oldCount = count + _elements.replaceSubrange( + subRange.lowerBound.position..( + contentsOf newElements: S, at i: MinimalIndex + ) where S.Iterator.Element == T { + let oldCount = count + _elements.insert(contentsOf: newElements, at: i.position) + let newCount = count + + if newCount - oldCount != 0 { + _willMutate(.insertContentsOf( + atIndex: i.position, + count: newCount - oldCount)) + } + } + + @discardableResult + public mutating func remove(at i: MinimalIndex) -> T { + _willMutate(.removeAtIndex(index: i.position)) + return _elements.remove(at: i.position) + } + + @discardableResult + public mutating func removeLast() -> T { + _willMutate(.removeLast) + return _elements.removeLast() + } + + public mutating func removeSubrange(_ subRange: Range) { + if !subRange.isEmpty { + _willMutate(.removeRange( + subRange: subRange.lowerBound.position.. : Collection, MutableCollection { + /// Creates a collection with given contents, but a unique modification + /// history. No other instance has the same modification history. + public init( + elements: S, + underestimatedCount: UnderestimatedCountBehavior = .value(0) + ) where S.Iterator.Element == T { + self._elements = Array(elements) + + self._collectionState = _CollectionState( + newRootStateForElementCount: self._elements.count) + + switch underestimatedCount { + case .precise: + self.underestimatedCount = _elements.count + + case .half: + self.underestimatedCount = _elements.count / 2 + + case .overestimate: + self.underestimatedCount = _elements.count * 3 + 5 + + case .value(let count): + self.underestimatedCount = count + } + } + + + public let timesMakeIteratorCalled = ResettableValue(0) + + public func makeIterator() -> MinimalIterator { + timesMakeIteratorCalled.value += 1 + return MinimalIterator(_elements) + } + + public typealias Index = MinimalIndex + + internal func _index(forPosition i: Int) -> MinimalIndex { + return MinimalIndex( + collectionState: _collectionState, + position: i, + startIndex: _elements.startIndex, + endIndex: _elements.endIndex) + } + + internal func _uncheckedIndex(forPosition i: Int) -> MinimalIndex { + return MinimalIndex( + _collectionState: _collectionState, + uncheckedPosition: i) + } + + public let timesStartIndexCalled = ResettableValue(0) + + public var startIndex: MinimalIndex { + timesStartIndexCalled.value += 1 + return _uncheckedIndex(forPosition: _elements.startIndex) + } + + public let timesEndIndexCalled = ResettableValue(0) + + public var endIndex: MinimalIndex { + timesEndIndexCalled.value += 1 + return _uncheckedIndex(forPosition: _elements.endIndex) + } + + + public func _failEarlyRangeCheck( + _ index: MinimalIndex, + bounds: Range + ) { + _expectCompatibleIndices( + _uncheckedIndex(forPosition: index.position), + index) + + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.lowerBound.position), + bounds.lowerBound) + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.upperBound.position), + bounds.upperBound) + + expectTrapping( + index.position, + in: bounds.lowerBound.position.., + bounds: Range + ) { + _expectCompatibleIndices( + _uncheckedIndex(forPosition: range.lowerBound.position), + range.lowerBound) + _expectCompatibleIndices( + _uncheckedIndex(forPosition: range.upperBound.position), + range.upperBound) + + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.lowerBound.position), + bounds.lowerBound) + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.upperBound.position), + bounds.upperBound) + + expectTrapping( + range.lowerBound.position.. MinimalIndex { + _failEarlyRangeCheck(i, bounds: startIndex.. Int { + _precondition(start <= end, + "Only BidirectionalCollections can have end come before start") + // FIXME: swift-3-indexing-model: perform a range check properly. + if start != endIndex { + _failEarlyRangeCheck(start, bounds: startIndex.. Index { + _precondition(n >= 0, + "Only BidirectionalCollections can be advanced by a negative amount") + // FIXME: swift-3-indexing-model: perform a range check properly. + if i != endIndex { + _failEarlyRangeCheck(i, bounds: startIndex.. T { + get { + _failEarlyRangeCheck(i, bounds: startIndex..) -> Slice> { + get { + _failEarlyRangeCheck(bounds, bounds: startIndex.. : Collection, MutableCollection, RangeReplaceableCollection { + /// Creates a collection with given contents, but a unique modification + /// history. No other instance has the same modification history. + public init( + elements: S, + underestimatedCount: UnderestimatedCountBehavior = .value(0) + ) where S.Iterator.Element == T { + self._elements = Array(elements) + + self._collectionState = _CollectionState( + newRootStateForElementCount: self._elements.count) + + switch underestimatedCount { + case .precise: + self.underestimatedCount = _elements.count + + case .half: + self.underestimatedCount = _elements.count / 2 + + case .overestimate: + self.underestimatedCount = _elements.count * 3 + 5 + + case .value(let count): + self.underestimatedCount = count + } + } + + public init() { + self.underestimatedCount = 0 + self._elements = [] + self._collectionState = + _CollectionState(name: "\(type(of: self))", elementCount: 0) + } + + public init(_ elements: S) where S.Iterator.Element == T { + self.underestimatedCount = 0 + self._elements = Array(elements) + self._collectionState = + _CollectionState(newRootStateForElementCount: self._elements.count) + } + + public let timesMakeIteratorCalled = ResettableValue(0) + + public func makeIterator() -> MinimalIterator { + timesMakeIteratorCalled.value += 1 + return MinimalIterator(_elements) + } + + public typealias Index = MinimalIndex + + internal func _index(forPosition i: Int) -> MinimalIndex { + return MinimalIndex( + collectionState: _collectionState, + position: i, + startIndex: _elements.startIndex, + endIndex: _elements.endIndex) + } + + internal func _uncheckedIndex(forPosition i: Int) -> MinimalIndex { + return MinimalIndex( + _collectionState: _collectionState, + uncheckedPosition: i) + } + + public let timesStartIndexCalled = ResettableValue(0) + + public var startIndex: MinimalIndex { + timesStartIndexCalled.value += 1 + return _uncheckedIndex(forPosition: _elements.startIndex) + } + + public let timesEndIndexCalled = ResettableValue(0) + + public var endIndex: MinimalIndex { + timesEndIndexCalled.value += 1 + return _uncheckedIndex(forPosition: _elements.endIndex) + } + + + public func _failEarlyRangeCheck( + _ index: MinimalIndex, + bounds: Range + ) { + _expectCompatibleIndices( + _uncheckedIndex(forPosition: index.position), + index) + + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.lowerBound.position), + bounds.lowerBound) + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.upperBound.position), + bounds.upperBound) + + expectTrapping( + index.position, + in: bounds.lowerBound.position.., + bounds: Range + ) { + _expectCompatibleIndices( + _uncheckedIndex(forPosition: range.lowerBound.position), + range.lowerBound) + _expectCompatibleIndices( + _uncheckedIndex(forPosition: range.upperBound.position), + range.upperBound) + + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.lowerBound.position), + bounds.lowerBound) + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.upperBound.position), + bounds.upperBound) + + expectTrapping( + range.lowerBound.position.. MinimalIndex { + _failEarlyRangeCheck(i, bounds: startIndex.. Int { + _precondition(start <= end, + "Only BidirectionalCollections can have end come before start") + // FIXME: swift-3-indexing-model: perform a range check properly. + if start != endIndex { + _failEarlyRangeCheck(start, bounds: startIndex.. Index { + _precondition(n >= 0, + "Only BidirectionalCollections can be advanced by a negative amount") + // FIXME: swift-3-indexing-model: perform a range check properly. + if i != endIndex { + _failEarlyRangeCheck(i, bounds: startIndex.. T { + get { + _failEarlyRangeCheck(i, bounds: startIndex..) -> Slice> { + get { + _failEarlyRangeCheck(bounds, bounds: startIndex..(contentsOf newElements: S) + where S.Iterator.Element == T { + let oldCount = count + _elements.append(contentsOf: newElements) + let newCount = count + _willMutate(.appendContentsOf(count: newCount - oldCount)) + } + + public mutating func replaceSubrange( + _ subRange: Range, + with newElements: C + ) where C : Collection, C.Iterator.Element == T { + let oldCount = count + _elements.replaceSubrange( + subRange.lowerBound.position..( + contentsOf newElements: S, at i: MinimalIndex + ) where S.Iterator.Element == T { + let oldCount = count + _elements.insert(contentsOf: newElements, at: i.position) + let newCount = count + + if newCount - oldCount != 0 { + _willMutate(.insertContentsOf( + atIndex: i.position, + count: newCount - oldCount)) + } + } + + @discardableResult + public mutating func remove(at i: MinimalIndex) -> T { + _willMutate(.removeAtIndex(index: i.position)) + return _elements.remove(at: i.position) + } + + @discardableResult + public mutating func removeLast() -> T { + _willMutate(.removeLast) + return _elements.removeLast() + } + + public mutating func removeSubrange(_ subRange: Range) { + if !subRange.isEmpty { + _willMutate(.removeRange( + subRange: subRange.lowerBound.position.. : BidirectionalCollection { + /// Creates a collection with given contents, but a unique modification + /// history. No other instance has the same modification history. + public init( + elements: S, + underestimatedCount: UnderestimatedCountBehavior = .value(0) + ) where S.Iterator.Element == T { + self._elements = Array(elements) + + self._collectionState = _CollectionState( + newRootStateForElementCount: self._elements.count) + + switch underestimatedCount { + case .precise: + self.underestimatedCount = _elements.count + + case .half: + self.underestimatedCount = _elements.count / 2 + + case .overestimate: + self.underestimatedCount = _elements.count * 3 + 5 + + case .value(let count): + self.underestimatedCount = count + } + } + + + public let timesMakeIteratorCalled = ResettableValue(0) + + public func makeIterator() -> MinimalIterator { + timesMakeIteratorCalled.value += 1 + return MinimalIterator(_elements) + } + + public typealias Index = MinimalIndex + + internal func _index(forPosition i: Int) -> MinimalIndex { + return MinimalIndex( + collectionState: _collectionState, + position: i, + startIndex: _elements.startIndex, + endIndex: _elements.endIndex) + } + + internal func _uncheckedIndex(forPosition i: Int) -> MinimalIndex { + return MinimalIndex( + _collectionState: _collectionState, + uncheckedPosition: i) + } + + public let timesStartIndexCalled = ResettableValue(0) + + public var startIndex: MinimalIndex { + timesStartIndexCalled.value += 1 + return _uncheckedIndex(forPosition: _elements.startIndex) + } + + public let timesEndIndexCalled = ResettableValue(0) + + public var endIndex: MinimalIndex { + timesEndIndexCalled.value += 1 + return _uncheckedIndex(forPosition: _elements.endIndex) + } + + + public func _failEarlyRangeCheck( + _ index: MinimalIndex, + bounds: Range + ) { + _expectCompatibleIndices( + _uncheckedIndex(forPosition: index.position), + index) + + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.lowerBound.position), + bounds.lowerBound) + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.upperBound.position), + bounds.upperBound) + + expectTrapping( + index.position, + in: bounds.lowerBound.position.., + bounds: Range + ) { + _expectCompatibleIndices( + _uncheckedIndex(forPosition: range.lowerBound.position), + range.lowerBound) + _expectCompatibleIndices( + _uncheckedIndex(forPosition: range.upperBound.position), + range.upperBound) + + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.lowerBound.position), + bounds.lowerBound) + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.upperBound.position), + bounds.upperBound) + + expectTrapping( + range.lowerBound.position.. MinimalIndex { + _failEarlyRangeCheck(i, bounds: startIndex.. MinimalIndex { + // FIXME: swift-3-indexing-model: perform a range check and use + // return _uncheckedIndex(forPosition: i.position - 1) + return _index(forPosition: i.position - 1) + } + + public func distance(from start: MinimalIndex, to end: MinimalIndex) + -> Int { + // FIXME: swift-3-indexing-model: perform a range check properly. + if start != endIndex { + _failEarlyRangeCheck(start, bounds: startIndex.. Index { + // FIXME: swift-3-indexing-model: perform a range check properly. + if i != endIndex { + _failEarlyRangeCheck(i, bounds: startIndex.. T { + get { + _failEarlyRangeCheck(i, bounds: startIndex..) -> Slice> { + get { + _failEarlyRangeCheck(bounds, bounds: startIndex.. : BidirectionalCollection, RangeReplaceableCollection { + /// Creates a collection with given contents, but a unique modification + /// history. No other instance has the same modification history. + public init( + elements: S, + underestimatedCount: UnderestimatedCountBehavior = .value(0) + ) where S.Iterator.Element == T { + self._elements = Array(elements) + + self._collectionState = _CollectionState( + newRootStateForElementCount: self._elements.count) + + switch underestimatedCount { + case .precise: + self.underestimatedCount = _elements.count + + case .half: + self.underestimatedCount = _elements.count / 2 + + case .overestimate: + self.underestimatedCount = _elements.count * 3 + 5 + + case .value(let count): + self.underestimatedCount = count + } + } + + public init() { + self.underestimatedCount = 0 + self._elements = [] + self._collectionState = + _CollectionState(name: "\(type(of: self))", elementCount: 0) + } + + public init(_ elements: S) where S.Iterator.Element == T { + self.underestimatedCount = 0 + self._elements = Array(elements) + self._collectionState = + _CollectionState(newRootStateForElementCount: self._elements.count) + } + + public let timesMakeIteratorCalled = ResettableValue(0) + + public func makeIterator() -> MinimalIterator { + timesMakeIteratorCalled.value += 1 + return MinimalIterator(_elements) + } + + public typealias Index = MinimalIndex + + internal func _index(forPosition i: Int) -> MinimalIndex { + return MinimalIndex( + collectionState: _collectionState, + position: i, + startIndex: _elements.startIndex, + endIndex: _elements.endIndex) + } + + internal func _uncheckedIndex(forPosition i: Int) -> MinimalIndex { + return MinimalIndex( + _collectionState: _collectionState, + uncheckedPosition: i) + } + + public let timesStartIndexCalled = ResettableValue(0) + + public var startIndex: MinimalIndex { + timesStartIndexCalled.value += 1 + return _uncheckedIndex(forPosition: _elements.startIndex) + } + + public let timesEndIndexCalled = ResettableValue(0) + + public var endIndex: MinimalIndex { + timesEndIndexCalled.value += 1 + return _uncheckedIndex(forPosition: _elements.endIndex) + } + + + public func _failEarlyRangeCheck( + _ index: MinimalIndex, + bounds: Range + ) { + _expectCompatibleIndices( + _uncheckedIndex(forPosition: index.position), + index) + + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.lowerBound.position), + bounds.lowerBound) + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.upperBound.position), + bounds.upperBound) + + expectTrapping( + index.position, + in: bounds.lowerBound.position.., + bounds: Range + ) { + _expectCompatibleIndices( + _uncheckedIndex(forPosition: range.lowerBound.position), + range.lowerBound) + _expectCompatibleIndices( + _uncheckedIndex(forPosition: range.upperBound.position), + range.upperBound) + + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.lowerBound.position), + bounds.lowerBound) + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.upperBound.position), + bounds.upperBound) + + expectTrapping( + range.lowerBound.position.. MinimalIndex { + _failEarlyRangeCheck(i, bounds: startIndex.. MinimalIndex { + // FIXME: swift-3-indexing-model: perform a range check and use + // return _uncheckedIndex(forPosition: i.position - 1) + return _index(forPosition: i.position - 1) + } + + public func distance(from start: MinimalIndex, to end: MinimalIndex) + -> Int { + // FIXME: swift-3-indexing-model: perform a range check properly. + if start != endIndex { + _failEarlyRangeCheck(start, bounds: startIndex.. Index { + // FIXME: swift-3-indexing-model: perform a range check properly. + if i != endIndex { + _failEarlyRangeCheck(i, bounds: startIndex.. T { + get { + _failEarlyRangeCheck(i, bounds: startIndex..) -> Slice> { + get { + _failEarlyRangeCheck(bounds, bounds: startIndex..(contentsOf newElements: S) + where S.Iterator.Element == T { + let oldCount = count + _elements.append(contentsOf: newElements) + let newCount = count + _willMutate(.appendContentsOf(count: newCount - oldCount)) + } + + public mutating func replaceSubrange( + _ subRange: Range, + with newElements: C + ) where C : Collection, C.Iterator.Element == T { + let oldCount = count + _elements.replaceSubrange( + subRange.lowerBound.position..( + contentsOf newElements: S, at i: MinimalIndex + ) where S.Iterator.Element == T { + let oldCount = count + _elements.insert(contentsOf: newElements, at: i.position) + let newCount = count + + if newCount - oldCount != 0 { + _willMutate(.insertContentsOf( + atIndex: i.position, + count: newCount - oldCount)) + } + } + + @discardableResult + public mutating func remove(at i: MinimalIndex) -> T { + _willMutate(.removeAtIndex(index: i.position)) + return _elements.remove(at: i.position) + } + + @discardableResult + public mutating func removeLast() -> T { + _willMutate(.removeLast) + return _elements.removeLast() + } + + public mutating func removeSubrange(_ subRange: Range) { + if !subRange.isEmpty { + _willMutate(.removeRange( + subRange: subRange.lowerBound.position.. : BidirectionalCollection, MutableCollection { + /// Creates a collection with given contents, but a unique modification + /// history. No other instance has the same modification history. + public init( + elements: S, + underestimatedCount: UnderestimatedCountBehavior = .value(0) + ) where S.Iterator.Element == T { + self._elements = Array(elements) + + self._collectionState = _CollectionState( + newRootStateForElementCount: self._elements.count) + + switch underestimatedCount { + case .precise: + self.underestimatedCount = _elements.count + + case .half: + self.underestimatedCount = _elements.count / 2 + + case .overestimate: + self.underestimatedCount = _elements.count * 3 + 5 + + case .value(let count): + self.underestimatedCount = count + } + } + + + public let timesMakeIteratorCalled = ResettableValue(0) + + public func makeIterator() -> MinimalIterator { + timesMakeIteratorCalled.value += 1 + return MinimalIterator(_elements) + } + + public typealias Index = MinimalIndex + + internal func _index(forPosition i: Int) -> MinimalIndex { + return MinimalIndex( + collectionState: _collectionState, + position: i, + startIndex: _elements.startIndex, + endIndex: _elements.endIndex) + } + + internal func _uncheckedIndex(forPosition i: Int) -> MinimalIndex { + return MinimalIndex( + _collectionState: _collectionState, + uncheckedPosition: i) + } + + public let timesStartIndexCalled = ResettableValue(0) + + public var startIndex: MinimalIndex { + timesStartIndexCalled.value += 1 + return _uncheckedIndex(forPosition: _elements.startIndex) + } + + public let timesEndIndexCalled = ResettableValue(0) + + public var endIndex: MinimalIndex { + timesEndIndexCalled.value += 1 + return _uncheckedIndex(forPosition: _elements.endIndex) + } + + + public func _failEarlyRangeCheck( + _ index: MinimalIndex, + bounds: Range + ) { + _expectCompatibleIndices( + _uncheckedIndex(forPosition: index.position), + index) + + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.lowerBound.position), + bounds.lowerBound) + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.upperBound.position), + bounds.upperBound) + + expectTrapping( + index.position, + in: bounds.lowerBound.position.., + bounds: Range + ) { + _expectCompatibleIndices( + _uncheckedIndex(forPosition: range.lowerBound.position), + range.lowerBound) + _expectCompatibleIndices( + _uncheckedIndex(forPosition: range.upperBound.position), + range.upperBound) + + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.lowerBound.position), + bounds.lowerBound) + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.upperBound.position), + bounds.upperBound) + + expectTrapping( + range.lowerBound.position.. MinimalIndex { + _failEarlyRangeCheck(i, bounds: startIndex.. MinimalIndex { + // FIXME: swift-3-indexing-model: perform a range check and use + // return _uncheckedIndex(forPosition: i.position - 1) + return _index(forPosition: i.position - 1) + } + + public func distance(from start: MinimalIndex, to end: MinimalIndex) + -> Int { + // FIXME: swift-3-indexing-model: perform a range check properly. + if start != endIndex { + _failEarlyRangeCheck(start, bounds: startIndex.. Index { + // FIXME: swift-3-indexing-model: perform a range check properly. + if i != endIndex { + _failEarlyRangeCheck(i, bounds: startIndex.. T { + get { + _failEarlyRangeCheck(i, bounds: startIndex..) -> Slice> { + get { + _failEarlyRangeCheck(bounds, bounds: startIndex.. : BidirectionalCollection, MutableCollection, RangeReplaceableCollection { + /// Creates a collection with given contents, but a unique modification + /// history. No other instance has the same modification history. + public init( + elements: S, + underestimatedCount: UnderestimatedCountBehavior = .value(0) + ) where S.Iterator.Element == T { + self._elements = Array(elements) + + self._collectionState = _CollectionState( + newRootStateForElementCount: self._elements.count) + + switch underestimatedCount { + case .precise: + self.underestimatedCount = _elements.count + + case .half: + self.underestimatedCount = _elements.count / 2 + + case .overestimate: + self.underestimatedCount = _elements.count * 3 + 5 + + case .value(let count): + self.underestimatedCount = count + } + } + + public init() { + self.underestimatedCount = 0 + self._elements = [] + self._collectionState = + _CollectionState(name: "\(type(of: self))", elementCount: 0) + } + + public init(_ elements: S) where S.Iterator.Element == T { + self.underestimatedCount = 0 + self._elements = Array(elements) + self._collectionState = + _CollectionState(newRootStateForElementCount: self._elements.count) + } + + public let timesMakeIteratorCalled = ResettableValue(0) + + public func makeIterator() -> MinimalIterator { + timesMakeIteratorCalled.value += 1 + return MinimalIterator(_elements) + } + + public typealias Index = MinimalIndex + + internal func _index(forPosition i: Int) -> MinimalIndex { + return MinimalIndex( + collectionState: _collectionState, + position: i, + startIndex: _elements.startIndex, + endIndex: _elements.endIndex) + } + + internal func _uncheckedIndex(forPosition i: Int) -> MinimalIndex { + return MinimalIndex( + _collectionState: _collectionState, + uncheckedPosition: i) + } + + public let timesStartIndexCalled = ResettableValue(0) + + public var startIndex: MinimalIndex { + timesStartIndexCalled.value += 1 + return _uncheckedIndex(forPosition: _elements.startIndex) + } + + public let timesEndIndexCalled = ResettableValue(0) + + public var endIndex: MinimalIndex { + timesEndIndexCalled.value += 1 + return _uncheckedIndex(forPosition: _elements.endIndex) + } + + + public func _failEarlyRangeCheck( + _ index: MinimalIndex, + bounds: Range + ) { + _expectCompatibleIndices( + _uncheckedIndex(forPosition: index.position), + index) + + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.lowerBound.position), + bounds.lowerBound) + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.upperBound.position), + bounds.upperBound) + + expectTrapping( + index.position, + in: bounds.lowerBound.position.., + bounds: Range + ) { + _expectCompatibleIndices( + _uncheckedIndex(forPosition: range.lowerBound.position), + range.lowerBound) + _expectCompatibleIndices( + _uncheckedIndex(forPosition: range.upperBound.position), + range.upperBound) + + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.lowerBound.position), + bounds.lowerBound) + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.upperBound.position), + bounds.upperBound) + + expectTrapping( + range.lowerBound.position.. MinimalIndex { + _failEarlyRangeCheck(i, bounds: startIndex.. MinimalIndex { + // FIXME: swift-3-indexing-model: perform a range check and use + // return _uncheckedIndex(forPosition: i.position - 1) + return _index(forPosition: i.position - 1) + } + + public func distance(from start: MinimalIndex, to end: MinimalIndex) + -> Int { + // FIXME: swift-3-indexing-model: perform a range check properly. + if start != endIndex { + _failEarlyRangeCheck(start, bounds: startIndex.. Index { + // FIXME: swift-3-indexing-model: perform a range check properly. + if i != endIndex { + _failEarlyRangeCheck(i, bounds: startIndex.. T { + get { + _failEarlyRangeCheck(i, bounds: startIndex..) -> Slice> { + get { + _failEarlyRangeCheck(bounds, bounds: startIndex..(contentsOf newElements: S) + where S.Iterator.Element == T { + let oldCount = count + _elements.append(contentsOf: newElements) + let newCount = count + _willMutate(.appendContentsOf(count: newCount - oldCount)) + } + + public mutating func replaceSubrange( + _ subRange: Range, + with newElements: C + ) where C : Collection, C.Iterator.Element == T { + let oldCount = count + _elements.replaceSubrange( + subRange.lowerBound.position..( + contentsOf newElements: S, at i: MinimalIndex + ) where S.Iterator.Element == T { + let oldCount = count + _elements.insert(contentsOf: newElements, at: i.position) + let newCount = count + + if newCount - oldCount != 0 { + _willMutate(.insertContentsOf( + atIndex: i.position, + count: newCount - oldCount)) + } + } + + @discardableResult + public mutating func remove(at i: MinimalIndex) -> T { + _willMutate(.removeAtIndex(index: i.position)) + return _elements.remove(at: i.position) + } + + @discardableResult + public mutating func removeLast() -> T { + _willMutate(.removeLast) + return _elements.removeLast() + } + + public mutating func removeSubrange(_ subRange: Range) { + if !subRange.isEmpty { + _willMutate(.removeRange( + subRange: subRange.lowerBound.position.. : RandomAccessCollection { + /// Creates a collection with given contents, but a unique modification + /// history. No other instance has the same modification history. + public init( + elements: S, + underestimatedCount: UnderestimatedCountBehavior = .value(0) + ) where S.Iterator.Element == T { + self._elements = Array(elements) + + self._collectionState = _CollectionState( + newRootStateForElementCount: self._elements.count) + + switch underestimatedCount { + case .precise: + self.underestimatedCount = _elements.count + + case .half: + self.underestimatedCount = _elements.count / 2 + + case .overestimate: + self.underestimatedCount = _elements.count * 3 + 5 + + case .value(let count): + self.underestimatedCount = count + } + } + + + public let timesMakeIteratorCalled = ResettableValue(0) + + public func makeIterator() -> MinimalIterator { + timesMakeIteratorCalled.value += 1 + return MinimalIterator(_elements) + } + + public typealias Index = MinimalIndex + + internal func _index(forPosition i: Int) -> MinimalIndex { + return MinimalIndex( + collectionState: _collectionState, + position: i, + startIndex: _elements.startIndex, + endIndex: _elements.endIndex) + } + + internal func _uncheckedIndex(forPosition i: Int) -> MinimalIndex { + return MinimalIndex( + _collectionState: _collectionState, + uncheckedPosition: i) + } + + public let timesStartIndexCalled = ResettableValue(0) + + public var startIndex: MinimalIndex { + timesStartIndexCalled.value += 1 + return _uncheckedIndex(forPosition: _elements.startIndex) + } + + public let timesEndIndexCalled = ResettableValue(0) + + public var endIndex: MinimalIndex { + timesEndIndexCalled.value += 1 + return _uncheckedIndex(forPosition: _elements.endIndex) + } + + // FIXME: this shouldn't be necessary, should come by default + public typealias Indices = DefaultIndices> + + public func _failEarlyRangeCheck( + _ index: MinimalIndex, + bounds: Range + ) { + _expectCompatibleIndices( + _uncheckedIndex(forPosition: index.position), + index) + + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.lowerBound.position), + bounds.lowerBound) + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.upperBound.position), + bounds.upperBound) + + expectTrapping( + index.position, + in: bounds.lowerBound.position.., + bounds: Range + ) { + _expectCompatibleIndices( + _uncheckedIndex(forPosition: range.lowerBound.position), + range.lowerBound) + _expectCompatibleIndices( + _uncheckedIndex(forPosition: range.upperBound.position), + range.upperBound) + + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.lowerBound.position), + bounds.lowerBound) + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.upperBound.position), + bounds.upperBound) + + expectTrapping( + range.lowerBound.position.. MinimalIndex { + _failEarlyRangeCheck(i, bounds: startIndex.. MinimalIndex { + // FIXME: swift-3-indexing-model: perform a range check and use + // return _uncheckedIndex(forPosition: i.position - 1) + return _index(forPosition: i.position - 1) + } + + public func distance(from start: MinimalIndex, to end: MinimalIndex) + -> Int { + // FIXME: swift-3-indexing-model: perform a range check properly. + if start != endIndex { + _failEarlyRangeCheck(start, bounds: startIndex.. Index { + // FIXME: swift-3-indexing-model: perform a range check properly. + if i != endIndex { + _failEarlyRangeCheck(i, bounds: startIndex.. T { + get { + _failEarlyRangeCheck(i, bounds: startIndex..) -> Slice> { + get { + _failEarlyRangeCheck(bounds, bounds: startIndex.. : RandomAccessCollection { + /// Creates a collection with given contents, but a unique modification + /// history. No other instance has the same modification history. + public init( + elements: S, + underestimatedCount: UnderestimatedCountBehavior = .value(0) + ) where S.Iterator.Element == T { + self._elements = Array(elements) + + self._collectionState = _CollectionState( + newRootStateForElementCount: self._elements.count) + + switch underestimatedCount { + case .precise: + self.underestimatedCount = _elements.count + + case .half: + self.underestimatedCount = _elements.count / 2 + + case .overestimate: + self.underestimatedCount = _elements.count * 3 + 5 + + case .value(let count): + self.underestimatedCount = count + } + } + + + public let timesMakeIteratorCalled = ResettableValue(0) + + public func makeIterator() -> MinimalIterator { + timesMakeIteratorCalled.value += 1 + return MinimalIterator(_elements) + } + + public typealias Index = MinimalStrideableIndex + + internal func _index(forPosition i: Int) -> MinimalStrideableIndex { + return MinimalStrideableIndex( + collectionState: _collectionState, + position: i, + startIndex: _elements.startIndex, + endIndex: _elements.endIndex) + } + + internal func _uncheckedIndex(forPosition i: Int) -> MinimalStrideableIndex { + return MinimalStrideableIndex( + _collectionState: _collectionState, + uncheckedPosition: i) + } + + public let timesStartIndexCalled = ResettableValue(0) + + public var startIndex: MinimalStrideableIndex { + timesStartIndexCalled.value += 1 + return _uncheckedIndex(forPosition: _elements.startIndex) + } + + public let timesEndIndexCalled = ResettableValue(0) + + public var endIndex: MinimalStrideableIndex { + timesEndIndexCalled.value += 1 + return _uncheckedIndex(forPosition: _elements.endIndex) + } + + public typealias Indices = Range + + public func _failEarlyRangeCheck( + _ index: MinimalStrideableIndex, + bounds: Range + ) { + _expectCompatibleIndices( + _uncheckedIndex(forPosition: index.position), + index) + + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.lowerBound.position), + bounds.lowerBound) + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.upperBound.position), + bounds.upperBound) + + expectTrapping( + index.position, + in: bounds.lowerBound.position.., + bounds: Range + ) { + _expectCompatibleIndices( + _uncheckedIndex(forPosition: range.lowerBound.position), + range.lowerBound) + _expectCompatibleIndices( + _uncheckedIndex(forPosition: range.upperBound.position), + range.upperBound) + + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.lowerBound.position), + bounds.lowerBound) + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.upperBound.position), + bounds.upperBound) + + expectTrapping( + range.lowerBound.position.. MinimalStrideableIndex { + _failEarlyRangeCheck(i, bounds: startIndex.. MinimalStrideableIndex { + // FIXME: swift-3-indexing-model: perform a range check and use + // return _uncheckedIndex(forPosition: i.position - 1) + return _index(forPosition: i.position - 1) + } + + public func distance(from start: MinimalStrideableIndex, to end: MinimalStrideableIndex) + -> Int { + // FIXME: swift-3-indexing-model: perform a range check properly. + if start != endIndex { + _failEarlyRangeCheck(start, bounds: startIndex.. Index { + // FIXME: swift-3-indexing-model: perform a range check properly. + if i != endIndex { + _failEarlyRangeCheck(i, bounds: startIndex.. T { + get { + _failEarlyRangeCheck(i, bounds: startIndex..) -> Slice> { + get { + _failEarlyRangeCheck(bounds, bounds: startIndex.. : RandomAccessCollection, RangeReplaceableCollection { + /// Creates a collection with given contents, but a unique modification + /// history. No other instance has the same modification history. + public init( + elements: S, + underestimatedCount: UnderestimatedCountBehavior = .value(0) + ) where S.Iterator.Element == T { + self._elements = Array(elements) + + self._collectionState = _CollectionState( + newRootStateForElementCount: self._elements.count) + + switch underestimatedCount { + case .precise: + self.underestimatedCount = _elements.count + + case .half: + self.underestimatedCount = _elements.count / 2 + + case .overestimate: + self.underestimatedCount = _elements.count * 3 + 5 + + case .value(let count): + self.underestimatedCount = count + } + } + + public init() { + self.underestimatedCount = 0 + self._elements = [] + self._collectionState = + _CollectionState(name: "\(type(of: self))", elementCount: 0) + } + + public init(_ elements: S) where S.Iterator.Element == T { + self.underestimatedCount = 0 + self._elements = Array(elements) + self._collectionState = + _CollectionState(newRootStateForElementCount: self._elements.count) + } + + public let timesMakeIteratorCalled = ResettableValue(0) + + public func makeIterator() -> MinimalIterator { + timesMakeIteratorCalled.value += 1 + return MinimalIterator(_elements) + } + + public typealias Index = MinimalIndex + + internal func _index(forPosition i: Int) -> MinimalIndex { + return MinimalIndex( + collectionState: _collectionState, + position: i, + startIndex: _elements.startIndex, + endIndex: _elements.endIndex) + } + + internal func _uncheckedIndex(forPosition i: Int) -> MinimalIndex { + return MinimalIndex( + _collectionState: _collectionState, + uncheckedPosition: i) + } + + public let timesStartIndexCalled = ResettableValue(0) + + public var startIndex: MinimalIndex { + timesStartIndexCalled.value += 1 + return _uncheckedIndex(forPosition: _elements.startIndex) + } + + public let timesEndIndexCalled = ResettableValue(0) + + public var endIndex: MinimalIndex { + timesEndIndexCalled.value += 1 + return _uncheckedIndex(forPosition: _elements.endIndex) + } + + // FIXME: this shouldn't be necessary, should come by default + public typealias Indices = DefaultIndices> + + public func _failEarlyRangeCheck( + _ index: MinimalIndex, + bounds: Range + ) { + _expectCompatibleIndices( + _uncheckedIndex(forPosition: index.position), + index) + + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.lowerBound.position), + bounds.lowerBound) + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.upperBound.position), + bounds.upperBound) + + expectTrapping( + index.position, + in: bounds.lowerBound.position.., + bounds: Range + ) { + _expectCompatibleIndices( + _uncheckedIndex(forPosition: range.lowerBound.position), + range.lowerBound) + _expectCompatibleIndices( + _uncheckedIndex(forPosition: range.upperBound.position), + range.upperBound) + + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.lowerBound.position), + bounds.lowerBound) + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.upperBound.position), + bounds.upperBound) + + expectTrapping( + range.lowerBound.position.. MinimalIndex { + _failEarlyRangeCheck(i, bounds: startIndex.. MinimalIndex { + // FIXME: swift-3-indexing-model: perform a range check and use + // return _uncheckedIndex(forPosition: i.position - 1) + return _index(forPosition: i.position - 1) + } + + public func distance(from start: MinimalIndex, to end: MinimalIndex) + -> Int { + // FIXME: swift-3-indexing-model: perform a range check properly. + if start != endIndex { + _failEarlyRangeCheck(start, bounds: startIndex.. Index { + // FIXME: swift-3-indexing-model: perform a range check properly. + if i != endIndex { + _failEarlyRangeCheck(i, bounds: startIndex.. T { + get { + _failEarlyRangeCheck(i, bounds: startIndex..) -> Slice> { + get { + _failEarlyRangeCheck(bounds, bounds: startIndex..(contentsOf newElements: S) + where S.Iterator.Element == T { + let oldCount = count + _elements.append(contentsOf: newElements) + let newCount = count + _willMutate(.appendContentsOf(count: newCount - oldCount)) + } + + public mutating func replaceSubrange( + _ subRange: Range, + with newElements: C + ) where C : Collection, C.Iterator.Element == T { + let oldCount = count + _elements.replaceSubrange( + subRange.lowerBound.position..( + contentsOf newElements: S, at i: MinimalIndex + ) where S.Iterator.Element == T { + let oldCount = count + _elements.insert(contentsOf: newElements, at: i.position) + let newCount = count + + if newCount - oldCount != 0 { + _willMutate(.insertContentsOf( + atIndex: i.position, + count: newCount - oldCount)) + } + } + + @discardableResult + public mutating func remove(at i: MinimalIndex) -> T { + _willMutate(.removeAtIndex(index: i.position)) + return _elements.remove(at: i.position) + } + + @discardableResult + public mutating func removeLast() -> T { + _willMutate(.removeLast) + return _elements.removeLast() + } + + public mutating func removeSubrange(_ subRange: Range) { + if !subRange.isEmpty { + _willMutate(.removeRange( + subRange: subRange.lowerBound.position.. : RandomAccessCollection, RangeReplaceableCollection { + /// Creates a collection with given contents, but a unique modification + /// history. No other instance has the same modification history. + public init( + elements: S, + underestimatedCount: UnderestimatedCountBehavior = .value(0) + ) where S.Iterator.Element == T { + self._elements = Array(elements) + + self._collectionState = _CollectionState( + newRootStateForElementCount: self._elements.count) + + switch underestimatedCount { + case .precise: + self.underestimatedCount = _elements.count + + case .half: + self.underestimatedCount = _elements.count / 2 + + case .overestimate: + self.underestimatedCount = _elements.count * 3 + 5 + + case .value(let count): + self.underestimatedCount = count + } + } + + public init() { + self.underestimatedCount = 0 + self._elements = [] + self._collectionState = + _CollectionState(name: "\(type(of: self))", elementCount: 0) + } + + public init(_ elements: S) where S.Iterator.Element == T { + self.underestimatedCount = 0 + self._elements = Array(elements) + self._collectionState = + _CollectionState(newRootStateForElementCount: self._elements.count) + } + + public let timesMakeIteratorCalled = ResettableValue(0) + + public func makeIterator() -> MinimalIterator { + timesMakeIteratorCalled.value += 1 + return MinimalIterator(_elements) + } + + public typealias Index = MinimalStrideableIndex + + internal func _index(forPosition i: Int) -> MinimalStrideableIndex { + return MinimalStrideableIndex( + collectionState: _collectionState, + position: i, + startIndex: _elements.startIndex, + endIndex: _elements.endIndex) + } + + internal func _uncheckedIndex(forPosition i: Int) -> MinimalStrideableIndex { + return MinimalStrideableIndex( + _collectionState: _collectionState, + uncheckedPosition: i) + } + + public let timesStartIndexCalled = ResettableValue(0) + + public var startIndex: MinimalStrideableIndex { + timesStartIndexCalled.value += 1 + return _uncheckedIndex(forPosition: _elements.startIndex) + } + + public let timesEndIndexCalled = ResettableValue(0) + + public var endIndex: MinimalStrideableIndex { + timesEndIndexCalled.value += 1 + return _uncheckedIndex(forPosition: _elements.endIndex) + } + + public typealias Indices = Range + + public func _failEarlyRangeCheck( + _ index: MinimalStrideableIndex, + bounds: Range + ) { + _expectCompatibleIndices( + _uncheckedIndex(forPosition: index.position), + index) + + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.lowerBound.position), + bounds.lowerBound) + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.upperBound.position), + bounds.upperBound) + + expectTrapping( + index.position, + in: bounds.lowerBound.position.., + bounds: Range + ) { + _expectCompatibleIndices( + _uncheckedIndex(forPosition: range.lowerBound.position), + range.lowerBound) + _expectCompatibleIndices( + _uncheckedIndex(forPosition: range.upperBound.position), + range.upperBound) + + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.lowerBound.position), + bounds.lowerBound) + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.upperBound.position), + bounds.upperBound) + + expectTrapping( + range.lowerBound.position.. MinimalStrideableIndex { + _failEarlyRangeCheck(i, bounds: startIndex.. MinimalStrideableIndex { + // FIXME: swift-3-indexing-model: perform a range check and use + // return _uncheckedIndex(forPosition: i.position - 1) + return _index(forPosition: i.position - 1) + } + + public func distance(from start: MinimalStrideableIndex, to end: MinimalStrideableIndex) + -> Int { + // FIXME: swift-3-indexing-model: perform a range check properly. + if start != endIndex { + _failEarlyRangeCheck(start, bounds: startIndex.. Index { + // FIXME: swift-3-indexing-model: perform a range check properly. + if i != endIndex { + _failEarlyRangeCheck(i, bounds: startIndex.. T { + get { + _failEarlyRangeCheck(i, bounds: startIndex..) -> Slice> { + get { + _failEarlyRangeCheck(bounds, bounds: startIndex..(contentsOf newElements: S) + where S.Iterator.Element == T { + let oldCount = count + _elements.append(contentsOf: newElements) + let newCount = count + _willMutate(.appendContentsOf(count: newCount - oldCount)) + } + + public mutating func replaceSubrange( + _ subRange: Range, + with newElements: C + ) where C : Collection, C.Iterator.Element == T { + let oldCount = count + _elements.replaceSubrange( + subRange.lowerBound.position..( + contentsOf newElements: S, at i: MinimalStrideableIndex + ) where S.Iterator.Element == T { + let oldCount = count + _elements.insert(contentsOf: newElements, at: i.position) + let newCount = count + + if newCount - oldCount != 0 { + _willMutate(.insertContentsOf( + atIndex: i.position, + count: newCount - oldCount)) + } + } + + @discardableResult + public mutating func remove(at i: MinimalStrideableIndex) -> T { + _willMutate(.removeAtIndex(index: i.position)) + return _elements.remove(at: i.position) + } + + @discardableResult + public mutating func removeLast() -> T { + _willMutate(.removeLast) + return _elements.removeLast() + } + + public mutating func removeSubrange(_ subRange: Range) { + if !subRange.isEmpty { + _willMutate(.removeRange( + subRange: subRange.lowerBound.position.. : RandomAccessCollection, MutableCollection { + /// Creates a collection with given contents, but a unique modification + /// history. No other instance has the same modification history. + public init( + elements: S, + underestimatedCount: UnderestimatedCountBehavior = .value(0) + ) where S.Iterator.Element == T { + self._elements = Array(elements) + + self._collectionState = _CollectionState( + newRootStateForElementCount: self._elements.count) + + switch underestimatedCount { + case .precise: + self.underestimatedCount = _elements.count + + case .half: + self.underestimatedCount = _elements.count / 2 + + case .overestimate: + self.underestimatedCount = _elements.count * 3 + 5 + + case .value(let count): + self.underestimatedCount = count + } + } + + + public let timesMakeIteratorCalled = ResettableValue(0) + + public func makeIterator() -> MinimalIterator { + timesMakeIteratorCalled.value += 1 + return MinimalIterator(_elements) + } + + public typealias Index = MinimalIndex + + internal func _index(forPosition i: Int) -> MinimalIndex { + return MinimalIndex( + collectionState: _collectionState, + position: i, + startIndex: _elements.startIndex, + endIndex: _elements.endIndex) + } + + internal func _uncheckedIndex(forPosition i: Int) -> MinimalIndex { + return MinimalIndex( + _collectionState: _collectionState, + uncheckedPosition: i) + } + + public let timesStartIndexCalled = ResettableValue(0) + + public var startIndex: MinimalIndex { + timesStartIndexCalled.value += 1 + return _uncheckedIndex(forPosition: _elements.startIndex) + } + + public let timesEndIndexCalled = ResettableValue(0) + + public var endIndex: MinimalIndex { + timesEndIndexCalled.value += 1 + return _uncheckedIndex(forPosition: _elements.endIndex) + } + + // FIXME: this shouldn't be necessary, should come by default + public typealias Indices = DefaultIndices> + + public func _failEarlyRangeCheck( + _ index: MinimalIndex, + bounds: Range + ) { + _expectCompatibleIndices( + _uncheckedIndex(forPosition: index.position), + index) + + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.lowerBound.position), + bounds.lowerBound) + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.upperBound.position), + bounds.upperBound) + + expectTrapping( + index.position, + in: bounds.lowerBound.position.., + bounds: Range + ) { + _expectCompatibleIndices( + _uncheckedIndex(forPosition: range.lowerBound.position), + range.lowerBound) + _expectCompatibleIndices( + _uncheckedIndex(forPosition: range.upperBound.position), + range.upperBound) + + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.lowerBound.position), + bounds.lowerBound) + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.upperBound.position), + bounds.upperBound) + + expectTrapping( + range.lowerBound.position.. MinimalIndex { + _failEarlyRangeCheck(i, bounds: startIndex.. MinimalIndex { + // FIXME: swift-3-indexing-model: perform a range check and use + // return _uncheckedIndex(forPosition: i.position - 1) + return _index(forPosition: i.position - 1) + } + + public func distance(from start: MinimalIndex, to end: MinimalIndex) + -> Int { + // FIXME: swift-3-indexing-model: perform a range check properly. + if start != endIndex { + _failEarlyRangeCheck(start, bounds: startIndex.. Index { + // FIXME: swift-3-indexing-model: perform a range check properly. + if i != endIndex { + _failEarlyRangeCheck(i, bounds: startIndex.. T { + get { + _failEarlyRangeCheck(i, bounds: startIndex..) -> Slice> { + get { + _failEarlyRangeCheck(bounds, bounds: startIndex.. : RandomAccessCollection, MutableCollection, RangeReplaceableCollection { + /// Creates a collection with given contents, but a unique modification + /// history. No other instance has the same modification history. + public init( + elements: S, + underestimatedCount: UnderestimatedCountBehavior = .value(0) + ) where S.Iterator.Element == T { + self._elements = Array(elements) + + self._collectionState = _CollectionState( + newRootStateForElementCount: self._elements.count) + + switch underestimatedCount { + case .precise: + self.underestimatedCount = _elements.count + + case .half: + self.underestimatedCount = _elements.count / 2 + + case .overestimate: + self.underestimatedCount = _elements.count * 3 + 5 + + case .value(let count): + self.underestimatedCount = count + } + } + + public init() { + self.underestimatedCount = 0 + self._elements = [] + self._collectionState = + _CollectionState(name: "\(type(of: self))", elementCount: 0) + } + + public init(_ elements: S) where S.Iterator.Element == T { + self.underestimatedCount = 0 + self._elements = Array(elements) + self._collectionState = + _CollectionState(newRootStateForElementCount: self._elements.count) + } + + public let timesMakeIteratorCalled = ResettableValue(0) + + public func makeIterator() -> MinimalIterator { + timesMakeIteratorCalled.value += 1 + return MinimalIterator(_elements) + } + + public typealias Index = MinimalIndex + + internal func _index(forPosition i: Int) -> MinimalIndex { + return MinimalIndex( + collectionState: _collectionState, + position: i, + startIndex: _elements.startIndex, + endIndex: _elements.endIndex) + } + + internal func _uncheckedIndex(forPosition i: Int) -> MinimalIndex { + return MinimalIndex( + _collectionState: _collectionState, + uncheckedPosition: i) + } + + public let timesStartIndexCalled = ResettableValue(0) + + public var startIndex: MinimalIndex { + timesStartIndexCalled.value += 1 + return _uncheckedIndex(forPosition: _elements.startIndex) + } + + public let timesEndIndexCalled = ResettableValue(0) + + public var endIndex: MinimalIndex { + timesEndIndexCalled.value += 1 + return _uncheckedIndex(forPosition: _elements.endIndex) + } + + // FIXME: this shouldn't be necessary, should come by default + public typealias Indices = DefaultIndices> + + public func _failEarlyRangeCheck( + _ index: MinimalIndex, + bounds: Range + ) { + _expectCompatibleIndices( + _uncheckedIndex(forPosition: index.position), + index) + + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.lowerBound.position), + bounds.lowerBound) + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.upperBound.position), + bounds.upperBound) + + expectTrapping( + index.position, + in: bounds.lowerBound.position.., + bounds: Range + ) { + _expectCompatibleIndices( + _uncheckedIndex(forPosition: range.lowerBound.position), + range.lowerBound) + _expectCompatibleIndices( + _uncheckedIndex(forPosition: range.upperBound.position), + range.upperBound) + + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.lowerBound.position), + bounds.lowerBound) + _expectCompatibleIndices( + _uncheckedIndex(forPosition: bounds.upperBound.position), + bounds.upperBound) + + expectTrapping( + range.lowerBound.position.. MinimalIndex { + _failEarlyRangeCheck(i, bounds: startIndex.. MinimalIndex { + // FIXME: swift-3-indexing-model: perform a range check and use + // return _uncheckedIndex(forPosition: i.position - 1) + return _index(forPosition: i.position - 1) + } + + public func distance(from start: MinimalIndex, to end: MinimalIndex) + -> Int { + // FIXME: swift-3-indexing-model: perform a range check properly. + if start != endIndex { + _failEarlyRangeCheck(start, bounds: startIndex.. Index { + // FIXME: swift-3-indexing-model: perform a range check properly. + if i != endIndex { + _failEarlyRangeCheck(i, bounds: startIndex.. T { + get { + _failEarlyRangeCheck(i, bounds: startIndex..) -> Slice> { + get { + _failEarlyRangeCheck(bounds, bounds: startIndex..(contentsOf newElements: S) + where S.Iterator.Element == T { + let oldCount = count + _elements.append(contentsOf: newElements) + let newCount = count + _willMutate(.appendContentsOf(count: newCount - oldCount)) + } + + public mutating func replaceSubrange( + _ subRange: Range, + with newElements: C + ) where C : Collection, C.Iterator.Element == T { + let oldCount = count + _elements.replaceSubrange( + subRange.lowerBound.position..( + contentsOf newElements: S, at i: MinimalIndex + ) where S.Iterator.Element == T { + let oldCount = count + _elements.insert(contentsOf: newElements, at: i.position) + let newCount = count + + if newCount - oldCount != 0 { + _willMutate(.insertContentsOf( + atIndex: i.position, + count: newCount - oldCount)) + } + } + + @discardableResult + public mutating func remove(at i: MinimalIndex) -> T { + _willMutate(.removeAtIndex(index: i.position)) + return _elements.remove(at: i.position) + } + + @discardableResult + public mutating func removeLast() -> T { + _willMutate(.removeLast) + return _elements.removeLast() + } + + public mutating func removeSubrange(_ subRange: Range) { + if !subRange.isEmpty { + _willMutate(.removeRange( + subRange: subRange.lowerBound.position.. : Sequence { + public let base: MinimalSequence + + public init(base: MinimalSequence) { + self.base = base + } + + public init( + elements: S, + underestimatedCount: UnderestimatedCountBehavior = .value(0) + ) where S.Iterator.Element == Element { + self.init(base: MinimalSequence( + elements: elements, underestimatedCount: underestimatedCount)) + } + + public func makeIterator() -> MinimalIterator { + return base.makeIterator() + } + + public var underestimatedCount: Int { + return base.underestimatedCount + } +} + + +/// A Collection that uses as many default implementations as +/// `Collection` can provide. +public struct DefaultedCollection : Collection { + public typealias Base = MinimalCollection + public typealias Iterator = MinimalIterator + public typealias Index = MinimalIndex + + + public let base: Base + + public init(base: Base) { + self.base = base + } + + public init(_ array: [Element]) { + self.base = Base(elements: array) + } + + public init(elements: [Element]) { + self.base = Base(elements: elements) + } + + public init( + elements: S, + underestimatedCount: UnderestimatedCountBehavior = .value(0) + ) where S.Iterator.Element == Element { + self.init(base: + MinimalCollection(elements: elements, underestimatedCount: underestimatedCount)) + } + + public var underestimatedCount: Int { + return base.underestimatedCount + } + + public let timesMakeIteratorCalled = ResettableValue(0) + + public func makeIterator() -> MinimalIterator { + timesMakeIteratorCalled.value += 1 + return base.makeIterator() + } + + + public let timesSuccessorCalled = ResettableValue(0) + + public func index(after i: MinimalIndex) -> MinimalIndex { + timesSuccessorCalled.value += 1 + return base.index(after: i) + } + + + + + public let timesStartIndexCalled = ResettableValue(0) + + public var startIndex: MinimalIndex { + timesStartIndexCalled.value += 1 + return base.startIndex + } + + public let timesEndIndexCalled = ResettableValue(0) + + public var endIndex: MinimalIndex { + timesEndIndexCalled.value += 1 + return base.endIndex + } + + public subscript(i: MinimalIndex) -> Element { + get { + return base[i] + } + } + + // FIXME: swift-3-indexing-model: use defaults. +// if Self not in ['DefaultedCollection', 'DefaultedBidirectionalCollection', 'DefaultedRandomAccessCollection', 'DefaultedMutableCollection', 'DefaultedRangeReplaceableCollection']: + + public subscript(bounds: Range) -> Slice> { + get { + // FIXME: swift-3-indexing-model: range check. + return Slice(base: self, bounds: bounds) + } + } + +} + +/* +FIXME: swift-3-indexing-model: uncomment this. +public struct DefaultedForwardRangeReplaceableSlice + : RangeReplaceableCollection { + + public typealias Self_ = DefaultedForwardRangeReplaceableSlice + public typealias Base = MinimalCollection + public typealias Iterator = MinimalIterator + public typealias Index = MinimalIndex + + public var base: Base + public var startIndex: Index + public var endIndex: Index + + public init() { + expectSliceType(Self_.self) + + self.base = Base() + self.startIndex = base.startIndex + self.endIndex = base.endIndex + } + + public init(base: Base) { + self.base = base + self.startIndex = base.startIndex + self.endIndex = base.endIndex + } + + public init(base: Base, bounds: Range) { + self.base = base + self.startIndex = bounds.lowerBound + self.endIndex = bounds.upperBound + } + + public init(_ array: [Element]) { + self = DefaultedForwardRangeReplaceableSlice( + base: Base(elements: array)) + } + + public init(elements: [Element]) { + self = DefaultedForwardRangeReplaceableSlice( + base: Base(elements: elements)) + } + + public func makeIterator() -> MinimalIterator { + return MinimalIterator(Array(self)) + } + + public subscript(index: Index) -> Element { + Index._failEarlyRangeCheck(index, bounds: startIndex..) -> Self_ { + Index._failEarlyRangeCheck2( + rangeStart: bounds.lowerBound, + rangeEnd: bounds.upperBound, + boundsStart: startIndex, + boundsEnd: endIndex) + return DefaultedForwardRangeReplaceableSlice( + base: base, bounds: bounds) + } + + public mutating func replaceSubrange< + C : Collection + >( + _ subRange: Range, + with newElements: C + ) where C.Iterator.Element == Element { + let startOffset = startIndex.position + let endOffset = + endIndex.position + - subRange.count + + numericCast(newElements.count) as Int + Index._failEarlyRangeCheck2( + rangeStart: subRange.lowerBound, + rangeEnd: subRange.upperBound, + boundsStart: startIndex, + boundsEnd: endIndex) + base.replaceSubrange(subRange, with: newElements) + startIndex = base.startIndex.advanced(by: startOffset) + endIndex = base.startIndex.advanced(by: endOffset) + } +} +*/ + + +/// A Collection that uses as many default implementations as +/// `Collection` can provide. +public struct DefaultedRangeReplaceableCollection : Collection, RangeReplaceableCollection { + public typealias Base = MinimalRangeReplaceableCollection + public typealias Iterator = MinimalIterator + public typealias Index = MinimalIndex + + + public var base: Base + + public init(base: Base) { + self.base = base + } + + public init(_ array: [Element]) { + self.base = Base(elements: array) + } + + public init(elements: [Element]) { + self.base = Base(elements: elements) + } + + public init( + elements: S, + underestimatedCount: UnderestimatedCountBehavior = .value(0) + ) where S.Iterator.Element == Element { + self.init(base: + MinimalRangeReplaceableCollection(elements: elements, underestimatedCount: underestimatedCount)) + } + + public var underestimatedCount: Int { + return base.underestimatedCount + } + + public let timesMakeIteratorCalled = ResettableValue(0) + + public func makeIterator() -> MinimalIterator { + timesMakeIteratorCalled.value += 1 + return base.makeIterator() + } + + + public let timesSuccessorCalled = ResettableValue(0) + + public func index(after i: MinimalIndex) -> MinimalIndex { + timesSuccessorCalled.value += 1 + return base.index(after: i) + } + + + + + public let timesStartIndexCalled = ResettableValue(0) + + public var startIndex: MinimalIndex { + timesStartIndexCalled.value += 1 + return base.startIndex + } + + public let timesEndIndexCalled = ResettableValue(0) + + public var endIndex: MinimalIndex { + timesEndIndexCalled.value += 1 + return base.endIndex + } + + public subscript(i: MinimalIndex) -> Element { + get { + return base[i] + } + } + + // FIXME: swift-3-indexing-model: use defaults. +// if Self not in ['DefaultedCollection', 'DefaultedBidirectionalCollection', 'DefaultedRandomAccessCollection', 'DefaultedMutableCollection', 'DefaultedRangeReplaceableCollection']: + + public subscript(bounds: Range) -> Slice> { + get { + // FIXME: swift-3-indexing-model: range check. + return Slice(base: self, bounds: bounds) + } + } + + public init() { + base = Base() + } + + public mutating func replaceSubrange( + _ subRange: Range.Index>, + with newElements: C + ) where C : Collection, C.Iterator.Element == Element { + base.replaceSubrange(subRange, with: newElements) + } +} + +/* +FIXME: swift-3-indexing-model: uncomment this. +public struct DefaultedForwardRangeReplaceableSlice + : RangeReplaceableCollection { + + public typealias Self_ = DefaultedForwardRangeReplaceableSlice + public typealias Base = MinimalRangeReplaceableCollection + public typealias Iterator = MinimalIterator + public typealias Index = MinimalIndex + + public var base: Base + public var startIndex: Index + public var endIndex: Index + + public init() { + expectSliceType(Self_.self) + + self.base = Base() + self.startIndex = base.startIndex + self.endIndex = base.endIndex + } + + public init(base: Base) { + self.base = base + self.startIndex = base.startIndex + self.endIndex = base.endIndex + } + + public init(base: Base, bounds: Range) { + self.base = base + self.startIndex = bounds.lowerBound + self.endIndex = bounds.upperBound + } + + public init(_ array: [Element]) { + self = DefaultedForwardRangeReplaceableSlice( + base: Base(elements: array)) + } + + public init(elements: [Element]) { + self = DefaultedForwardRangeReplaceableSlice( + base: Base(elements: elements)) + } + + public func makeIterator() -> MinimalIterator { + return MinimalIterator(Array(self)) + } + + public subscript(index: Index) -> Element { + Index._failEarlyRangeCheck(index, bounds: startIndex..) -> Self_ { + Index._failEarlyRangeCheck2( + rangeStart: bounds.lowerBound, + rangeEnd: bounds.upperBound, + boundsStart: startIndex, + boundsEnd: endIndex) + return DefaultedForwardRangeReplaceableSlice( + base: base, bounds: bounds) + } + + public mutating func replaceSubrange< + C : Collection + >( + _ subRange: Range, + with newElements: C + ) where C.Iterator.Element == Element { + let startOffset = startIndex.position + let endOffset = + endIndex.position + - subRange.count + + numericCast(newElements.count) as Int + Index._failEarlyRangeCheck2( + rangeStart: subRange.lowerBound, + rangeEnd: subRange.upperBound, + boundsStart: startIndex, + boundsEnd: endIndex) + base.replaceSubrange(subRange, with: newElements) + startIndex = base.startIndex.advanced(by: startOffset) + endIndex = base.startIndex.advanced(by: endOffset) + } +} +*/ + + +/// A Collection that uses as many default implementations as +/// `Collection` can provide. +public struct DefaultedMutableCollection : Collection, MutableCollection { + public typealias Base = MinimalMutableCollection + public typealias Iterator = MinimalIterator + public typealias Index = MinimalIndex + + + public var base: Base + + public init(base: Base) { + self.base = base + } + + public init(_ array: [Element]) { + self.base = Base(elements: array) + } + + public init(elements: [Element]) { + self.base = Base(elements: elements) + } + + public init( + elements: S, + underestimatedCount: UnderestimatedCountBehavior = .value(0) + ) where S.Iterator.Element == Element { + self.init(base: + MinimalMutableCollection(elements: elements, underestimatedCount: underestimatedCount)) + } + + public var underestimatedCount: Int { + return base.underestimatedCount + } + + public let timesMakeIteratorCalled = ResettableValue(0) + + public func makeIterator() -> MinimalIterator { + timesMakeIteratorCalled.value += 1 + return base.makeIterator() + } + + + public let timesSuccessorCalled = ResettableValue(0) + + public func index(after i: MinimalIndex) -> MinimalIndex { + timesSuccessorCalled.value += 1 + return base.index(after: i) + } + + + + + public let timesStartIndexCalled = ResettableValue(0) + + public var startIndex: MinimalIndex { + timesStartIndexCalled.value += 1 + return base.startIndex + } + + public let timesEndIndexCalled = ResettableValue(0) + + public var endIndex: MinimalIndex { + timesEndIndexCalled.value += 1 + return base.endIndex + } + + public subscript(i: MinimalIndex) -> Element { + get { + return base[i] + } + set { + base[i] = newValue + } + } + + // FIXME: swift-3-indexing-model: use defaults. +// if Self not in ['DefaultedCollection', 'DefaultedBidirectionalCollection', 'DefaultedRandomAccessCollection', 'DefaultedMutableCollection', 'DefaultedRangeReplaceableCollection']: + + public subscript(bounds: Range) -> Slice> { + get { + // FIXME: swift-3-indexing-model: range check. + return Slice(base: self, bounds: bounds) + } + set { + _writeBackMutableSlice(&self, bounds: bounds, slice: newValue) + } + } + +} + +/* +FIXME: swift-3-indexing-model: uncomment this. +public struct DefaultedForwardRangeReplaceableSlice + : RangeReplaceableCollection { + + public typealias Self_ = DefaultedForwardRangeReplaceableSlice + public typealias Base = MinimalMutableCollection + public typealias Iterator = MinimalIterator + public typealias Index = MinimalIndex + + public var base: Base + public var startIndex: Index + public var endIndex: Index + + public init() { + expectSliceType(Self_.self) + + self.base = Base() + self.startIndex = base.startIndex + self.endIndex = base.endIndex + } + + public init(base: Base) { + self.base = base + self.startIndex = base.startIndex + self.endIndex = base.endIndex + } + + public init(base: Base, bounds: Range) { + self.base = base + self.startIndex = bounds.lowerBound + self.endIndex = bounds.upperBound + } + + public init(_ array: [Element]) { + self = DefaultedForwardRangeReplaceableSlice( + base: Base(elements: array)) + } + + public init(elements: [Element]) { + self = DefaultedForwardRangeReplaceableSlice( + base: Base(elements: elements)) + } + + public func makeIterator() -> MinimalIterator { + return MinimalIterator(Array(self)) + } + + public subscript(index: Index) -> Element { + Index._failEarlyRangeCheck(index, bounds: startIndex..) -> Self_ { + Index._failEarlyRangeCheck2( + rangeStart: bounds.lowerBound, + rangeEnd: bounds.upperBound, + boundsStart: startIndex, + boundsEnd: endIndex) + return DefaultedForwardRangeReplaceableSlice( + base: base, bounds: bounds) + } + + public mutating func replaceSubrange< + C : Collection + >( + _ subRange: Range, + with newElements: C + ) where C.Iterator.Element == Element { + let startOffset = startIndex.position + let endOffset = + endIndex.position + - subRange.count + + numericCast(newElements.count) as Int + Index._failEarlyRangeCheck2( + rangeStart: subRange.lowerBound, + rangeEnd: subRange.upperBound, + boundsStart: startIndex, + boundsEnd: endIndex) + base.replaceSubrange(subRange, with: newElements) + startIndex = base.startIndex.advanced(by: startOffset) + endIndex = base.startIndex.advanced(by: endOffset) + } +} +*/ + + +/// A Collection that uses as many default implementations as +/// `Collection` can provide. +public struct DefaultedMutableRangeReplaceableCollection : Collection, MutableCollection, RangeReplaceableCollection { + public typealias Base = MinimalMutableRangeReplaceableCollection + public typealias Iterator = MinimalIterator + public typealias Index = MinimalIndex + + + public var base: Base + + public init(base: Base) { + self.base = base + } + + public init(_ array: [Element]) { + self.base = Base(elements: array) + } + + public init(elements: [Element]) { + self.base = Base(elements: elements) + } + + public init( + elements: S, + underestimatedCount: UnderestimatedCountBehavior = .value(0) + ) where S.Iterator.Element == Element { + self.init(base: + MinimalMutableRangeReplaceableCollection(elements: elements, underestimatedCount: underestimatedCount)) + } + + public var underestimatedCount: Int { + return base.underestimatedCount + } + + public let timesMakeIteratorCalled = ResettableValue(0) + + public func makeIterator() -> MinimalIterator { + timesMakeIteratorCalled.value += 1 + return base.makeIterator() + } + + + public let timesSuccessorCalled = ResettableValue(0) + + public func index(after i: MinimalIndex) -> MinimalIndex { + timesSuccessorCalled.value += 1 + return base.index(after: i) + } + + + + + public let timesStartIndexCalled = ResettableValue(0) + + public var startIndex: MinimalIndex { + timesStartIndexCalled.value += 1 + return base.startIndex + } + + public let timesEndIndexCalled = ResettableValue(0) + + public var endIndex: MinimalIndex { + timesEndIndexCalled.value += 1 + return base.endIndex + } + + public subscript(i: MinimalIndex) -> Element { + get { + return base[i] + } + set { + base[i] = newValue + } + } + + // FIXME: swift-3-indexing-model: use defaults. +// if Self not in ['DefaultedCollection', 'DefaultedBidirectionalCollection', 'DefaultedRandomAccessCollection', 'DefaultedMutableCollection', 'DefaultedRangeReplaceableCollection']: + + public subscript(bounds: Range) -> Slice> { + get { + // FIXME: swift-3-indexing-model: range check. + return Slice(base: self, bounds: bounds) + } + set { + _writeBackMutableSlice(&self, bounds: bounds, slice: newValue) + } + } + + public init() { + base = Base() + } + + public mutating func replaceSubrange( + _ subRange: Range.Index>, + with newElements: C + ) where C : Collection, C.Iterator.Element == Element { + base.replaceSubrange(subRange, with: newElements) + } +} + +/* +FIXME: swift-3-indexing-model: uncomment this. +public struct DefaultedForwardRangeReplaceableSlice + : RangeReplaceableCollection { + + public typealias Self_ = DefaultedForwardRangeReplaceableSlice + public typealias Base = MinimalMutableRangeReplaceableCollection + public typealias Iterator = MinimalIterator + public typealias Index = MinimalIndex + + public var base: Base + public var startIndex: Index + public var endIndex: Index + + public init() { + expectSliceType(Self_.self) + + self.base = Base() + self.startIndex = base.startIndex + self.endIndex = base.endIndex + } + + public init(base: Base) { + self.base = base + self.startIndex = base.startIndex + self.endIndex = base.endIndex + } + + public init(base: Base, bounds: Range) { + self.base = base + self.startIndex = bounds.lowerBound + self.endIndex = bounds.upperBound + } + + public init(_ array: [Element]) { + self = DefaultedForwardRangeReplaceableSlice( + base: Base(elements: array)) + } + + public init(elements: [Element]) { + self = DefaultedForwardRangeReplaceableSlice( + base: Base(elements: elements)) + } + + public func makeIterator() -> MinimalIterator { + return MinimalIterator(Array(self)) + } + + public subscript(index: Index) -> Element { + Index._failEarlyRangeCheck(index, bounds: startIndex..) -> Self_ { + Index._failEarlyRangeCheck2( + rangeStart: bounds.lowerBound, + rangeEnd: bounds.upperBound, + boundsStart: startIndex, + boundsEnd: endIndex) + return DefaultedForwardRangeReplaceableSlice( + base: base, bounds: bounds) + } + + public mutating func replaceSubrange< + C : Collection + >( + _ subRange: Range, + with newElements: C + ) where C.Iterator.Element == Element { + let startOffset = startIndex.position + let endOffset = + endIndex.position + - subRange.count + + numericCast(newElements.count) as Int + Index._failEarlyRangeCheck2( + rangeStart: subRange.lowerBound, + rangeEnd: subRange.upperBound, + boundsStart: startIndex, + boundsEnd: endIndex) + base.replaceSubrange(subRange, with: newElements) + startIndex = base.startIndex.advanced(by: startOffset) + endIndex = base.startIndex.advanced(by: endOffset) + } +} +*/ + + +/// A Collection that uses as many default implementations as +/// `Collection` can provide. +public struct DefaultedBidirectionalCollection : BidirectionalCollection { + public typealias Base = MinimalBidirectionalCollection + public typealias Iterator = MinimalIterator + public typealias Index = MinimalIndex + + + public let base: Base + + public init(base: Base) { + self.base = base + } + + public init(_ array: [Element]) { + self.base = Base(elements: array) + } + + public init(elements: [Element]) { + self.base = Base(elements: elements) + } + + public init( + elements: S, + underestimatedCount: UnderestimatedCountBehavior = .value(0) + ) where S.Iterator.Element == Element { + self.init(base: + MinimalBidirectionalCollection(elements: elements, underestimatedCount: underestimatedCount)) + } + + public var underestimatedCount: Int { + return base.underestimatedCount + } + + public let timesMakeIteratorCalled = ResettableValue(0) + + public func makeIterator() -> MinimalIterator { + timesMakeIteratorCalled.value += 1 + return base.makeIterator() + } + + + public let timesSuccessorCalled = ResettableValue(0) + + public func index(after i: MinimalIndex) -> MinimalIndex { + timesSuccessorCalled.value += 1 + return base.index(after: i) + } + + public let timesPredecessorCalled = ResettableValue(0) + + public func index(before i: MinimalIndex) -> MinimalIndex { + timesPredecessorCalled.value += 1 + return base.index(before: i) + } + + + + public let timesStartIndexCalled = ResettableValue(0) + + public var startIndex: MinimalIndex { + timesStartIndexCalled.value += 1 + return base.startIndex + } + + public let timesEndIndexCalled = ResettableValue(0) + + public var endIndex: MinimalIndex { + timesEndIndexCalled.value += 1 + return base.endIndex + } + + public subscript(i: MinimalIndex) -> Element { + get { + return base[i] + } + } + + // FIXME: swift-3-indexing-model: use defaults. +// if Self not in ['DefaultedCollection', 'DefaultedBidirectionalCollection', 'DefaultedRandomAccessCollection', 'DefaultedMutableCollection', 'DefaultedRangeReplaceableCollection']: + + public subscript(bounds: Range) -> Slice> { + get { + // FIXME: swift-3-indexing-model: range check. + return Slice(base: self, bounds: bounds) + } + } + +} + +/* +FIXME: swift-3-indexing-model: uncomment this. +public struct DefaultedBidirectionalRangeReplaceableSlice + : RangeReplaceableCollection { + + public typealias Self_ = DefaultedBidirectionalRangeReplaceableSlice + public typealias Base = MinimalBidirectionalCollection + public typealias Iterator = MinimalIterator + public typealias Index = MinimalIndex + + public var base: Base + public var startIndex: Index + public var endIndex: Index + + public init() { + expectSliceType(Self_.self) + + self.base = Base() + self.startIndex = base.startIndex + self.endIndex = base.endIndex + } + + public init(base: Base) { + self.base = base + self.startIndex = base.startIndex + self.endIndex = base.endIndex + } + + public init(base: Base, bounds: Range) { + self.base = base + self.startIndex = bounds.lowerBound + self.endIndex = bounds.upperBound + } + + public init(_ array: [Element]) { + self = DefaultedBidirectionalRangeReplaceableSlice( + base: Base(elements: array)) + } + + public init(elements: [Element]) { + self = DefaultedBidirectionalRangeReplaceableSlice( + base: Base(elements: elements)) + } + + public func makeIterator() -> MinimalIterator { + return MinimalIterator(Array(self)) + } + + public subscript(index: Index) -> Element { + Index._failEarlyRangeCheck(index, bounds: startIndex..) -> Self_ { + Index._failEarlyRangeCheck2( + rangeStart: bounds.lowerBound, + rangeEnd: bounds.upperBound, + boundsStart: startIndex, + boundsEnd: endIndex) + return DefaultedBidirectionalRangeReplaceableSlice( + base: base, bounds: bounds) + } + + public mutating func replaceSubrange< + C : Collection + >( + _ subRange: Range, + with newElements: C + ) where C.Iterator.Element == Element { + let startOffset = startIndex.position + let endOffset = + endIndex.position + - subRange.count + + numericCast(newElements.count) as Int + Index._failEarlyRangeCheck2( + rangeStart: subRange.lowerBound, + rangeEnd: subRange.upperBound, + boundsStart: startIndex, + boundsEnd: endIndex) + base.replaceSubrange(subRange, with: newElements) + startIndex = base.startIndex.advanced(by: startOffset) + endIndex = base.startIndex.advanced(by: endOffset) + } +} +*/ + + +/// A Collection that uses as many default implementations as +/// `Collection` can provide. +public struct DefaultedRangeReplaceableBidirectionalCollection : BidirectionalCollection, RangeReplaceableCollection { + public typealias Base = MinimalRangeReplaceableBidirectionalCollection + public typealias Iterator = MinimalIterator + public typealias Index = MinimalIndex + + + public var base: Base + + public init(base: Base) { + self.base = base + } + + public init(_ array: [Element]) { + self.base = Base(elements: array) + } + + public init(elements: [Element]) { + self.base = Base(elements: elements) + } + + public init( + elements: S, + underestimatedCount: UnderestimatedCountBehavior = .value(0) + ) where S.Iterator.Element == Element { + self.init(base: + MinimalRangeReplaceableBidirectionalCollection(elements: elements, underestimatedCount: underestimatedCount)) + } + + public var underestimatedCount: Int { + return base.underestimatedCount + } + + public let timesMakeIteratorCalled = ResettableValue(0) + + public func makeIterator() -> MinimalIterator { + timesMakeIteratorCalled.value += 1 + return base.makeIterator() + } + + + public let timesSuccessorCalled = ResettableValue(0) + + public func index(after i: MinimalIndex) -> MinimalIndex { + timesSuccessorCalled.value += 1 + return base.index(after: i) + } + + public let timesPredecessorCalled = ResettableValue(0) + + public func index(before i: MinimalIndex) -> MinimalIndex { + timesPredecessorCalled.value += 1 + return base.index(before: i) + } + + + + public let timesStartIndexCalled = ResettableValue(0) + + public var startIndex: MinimalIndex { + timesStartIndexCalled.value += 1 + return base.startIndex + } + + public let timesEndIndexCalled = ResettableValue(0) + + public var endIndex: MinimalIndex { + timesEndIndexCalled.value += 1 + return base.endIndex + } + + public subscript(i: MinimalIndex) -> Element { + get { + return base[i] + } + } + + // FIXME: swift-3-indexing-model: use defaults. +// if Self not in ['DefaultedCollection', 'DefaultedBidirectionalCollection', 'DefaultedRandomAccessCollection', 'DefaultedMutableCollection', 'DefaultedRangeReplaceableCollection']: + + public subscript(bounds: Range) -> Slice> { + get { + // FIXME: swift-3-indexing-model: range check. + return Slice(base: self, bounds: bounds) + } + } + + public init() { + base = Base() + } + + public mutating func replaceSubrange( + _ subRange: Range.Index>, + with newElements: C + ) where C : Collection, C.Iterator.Element == Element { + base.replaceSubrange(subRange, with: newElements) + } +} + +/* +FIXME: swift-3-indexing-model: uncomment this. +public struct DefaultedBidirectionalRangeReplaceableSlice + : RangeReplaceableCollection { + + public typealias Self_ = DefaultedBidirectionalRangeReplaceableSlice + public typealias Base = MinimalRangeReplaceableBidirectionalCollection + public typealias Iterator = MinimalIterator + public typealias Index = MinimalIndex + + public var base: Base + public var startIndex: Index + public var endIndex: Index + + public init() { + expectSliceType(Self_.self) + + self.base = Base() + self.startIndex = base.startIndex + self.endIndex = base.endIndex + } + + public init(base: Base) { + self.base = base + self.startIndex = base.startIndex + self.endIndex = base.endIndex + } + + public init(base: Base, bounds: Range) { + self.base = base + self.startIndex = bounds.lowerBound + self.endIndex = bounds.upperBound + } + + public init(_ array: [Element]) { + self = DefaultedBidirectionalRangeReplaceableSlice( + base: Base(elements: array)) + } + + public init(elements: [Element]) { + self = DefaultedBidirectionalRangeReplaceableSlice( + base: Base(elements: elements)) + } + + public func makeIterator() -> MinimalIterator { + return MinimalIterator(Array(self)) + } + + public subscript(index: Index) -> Element { + Index._failEarlyRangeCheck(index, bounds: startIndex..) -> Self_ { + Index._failEarlyRangeCheck2( + rangeStart: bounds.lowerBound, + rangeEnd: bounds.upperBound, + boundsStart: startIndex, + boundsEnd: endIndex) + return DefaultedBidirectionalRangeReplaceableSlice( + base: base, bounds: bounds) + } + + public mutating func replaceSubrange< + C : Collection + >( + _ subRange: Range, + with newElements: C + ) where C.Iterator.Element == Element { + let startOffset = startIndex.position + let endOffset = + endIndex.position + - subRange.count + + numericCast(newElements.count) as Int + Index._failEarlyRangeCheck2( + rangeStart: subRange.lowerBound, + rangeEnd: subRange.upperBound, + boundsStart: startIndex, + boundsEnd: endIndex) + base.replaceSubrange(subRange, with: newElements) + startIndex = base.startIndex.advanced(by: startOffset) + endIndex = base.startIndex.advanced(by: endOffset) + } +} +*/ + + +/// A Collection that uses as many default implementations as +/// `Collection` can provide. +public struct DefaultedMutableBidirectionalCollection : BidirectionalCollection, MutableCollection { + public typealias Base = MinimalMutableBidirectionalCollection + public typealias Iterator = MinimalIterator + public typealias Index = MinimalIndex + + + public var base: Base + + public init(base: Base) { + self.base = base + } + + public init(_ array: [Element]) { + self.base = Base(elements: array) + } + + public init(elements: [Element]) { + self.base = Base(elements: elements) + } + + public init( + elements: S, + underestimatedCount: UnderestimatedCountBehavior = .value(0) + ) where S.Iterator.Element == Element { + self.init(base: + MinimalMutableBidirectionalCollection(elements: elements, underestimatedCount: underestimatedCount)) + } + + public var underestimatedCount: Int { + return base.underestimatedCount + } + + public let timesMakeIteratorCalled = ResettableValue(0) + + public func makeIterator() -> MinimalIterator { + timesMakeIteratorCalled.value += 1 + return base.makeIterator() + } + + + public let timesSuccessorCalled = ResettableValue(0) + + public func index(after i: MinimalIndex) -> MinimalIndex { + timesSuccessorCalled.value += 1 + return base.index(after: i) + } + + public let timesPredecessorCalled = ResettableValue(0) + + public func index(before i: MinimalIndex) -> MinimalIndex { + timesPredecessorCalled.value += 1 + return base.index(before: i) + } + + + + public let timesStartIndexCalled = ResettableValue(0) + + public var startIndex: MinimalIndex { + timesStartIndexCalled.value += 1 + return base.startIndex + } + + public let timesEndIndexCalled = ResettableValue(0) + + public var endIndex: MinimalIndex { + timesEndIndexCalled.value += 1 + return base.endIndex + } + + public subscript(i: MinimalIndex) -> Element { + get { + return base[i] + } + set { + base[i] = newValue + } + } + + // FIXME: swift-3-indexing-model: use defaults. +// if Self not in ['DefaultedCollection', 'DefaultedBidirectionalCollection', 'DefaultedRandomAccessCollection', 'DefaultedMutableCollection', 'DefaultedRangeReplaceableCollection']: + + public subscript(bounds: Range) -> Slice> { + get { + // FIXME: swift-3-indexing-model: range check. + return Slice(base: self, bounds: bounds) + } + set { + _writeBackMutableSlice(&self, bounds: bounds, slice: newValue) + } + } + +} + +/* +FIXME: swift-3-indexing-model: uncomment this. +public struct DefaultedBidirectionalRangeReplaceableSlice + : RangeReplaceableCollection { + + public typealias Self_ = DefaultedBidirectionalRangeReplaceableSlice + public typealias Base = MinimalMutableBidirectionalCollection + public typealias Iterator = MinimalIterator + public typealias Index = MinimalIndex + + public var base: Base + public var startIndex: Index + public var endIndex: Index + + public init() { + expectSliceType(Self_.self) + + self.base = Base() + self.startIndex = base.startIndex + self.endIndex = base.endIndex + } + + public init(base: Base) { + self.base = base + self.startIndex = base.startIndex + self.endIndex = base.endIndex + } + + public init(base: Base, bounds: Range) { + self.base = base + self.startIndex = bounds.lowerBound + self.endIndex = bounds.upperBound + } + + public init(_ array: [Element]) { + self = DefaultedBidirectionalRangeReplaceableSlice( + base: Base(elements: array)) + } + + public init(elements: [Element]) { + self = DefaultedBidirectionalRangeReplaceableSlice( + base: Base(elements: elements)) + } + + public func makeIterator() -> MinimalIterator { + return MinimalIterator(Array(self)) + } + + public subscript(index: Index) -> Element { + Index._failEarlyRangeCheck(index, bounds: startIndex..) -> Self_ { + Index._failEarlyRangeCheck2( + rangeStart: bounds.lowerBound, + rangeEnd: bounds.upperBound, + boundsStart: startIndex, + boundsEnd: endIndex) + return DefaultedBidirectionalRangeReplaceableSlice( + base: base, bounds: bounds) + } + + public mutating func replaceSubrange< + C : Collection + >( + _ subRange: Range, + with newElements: C + ) where C.Iterator.Element == Element { + let startOffset = startIndex.position + let endOffset = + endIndex.position + - subRange.count + + numericCast(newElements.count) as Int + Index._failEarlyRangeCheck2( + rangeStart: subRange.lowerBound, + rangeEnd: subRange.upperBound, + boundsStart: startIndex, + boundsEnd: endIndex) + base.replaceSubrange(subRange, with: newElements) + startIndex = base.startIndex.advanced(by: startOffset) + endIndex = base.startIndex.advanced(by: endOffset) + } +} +*/ + + +/// A Collection that uses as many default implementations as +/// `Collection` can provide. +public struct DefaultedMutableRangeReplaceableBidirectionalCollection : BidirectionalCollection, MutableCollection, RangeReplaceableCollection { + public typealias Base = MinimalMutableRangeReplaceableBidirectionalCollection + public typealias Iterator = MinimalIterator + public typealias Index = MinimalIndex + + + public var base: Base + + public init(base: Base) { + self.base = base + } + + public init(_ array: [Element]) { + self.base = Base(elements: array) + } + + public init(elements: [Element]) { + self.base = Base(elements: elements) + } + + public init( + elements: S, + underestimatedCount: UnderestimatedCountBehavior = .value(0) + ) where S.Iterator.Element == Element { + self.init(base: + MinimalMutableRangeReplaceableBidirectionalCollection(elements: elements, underestimatedCount: underestimatedCount)) + } + + public var underestimatedCount: Int { + return base.underestimatedCount + } + + public let timesMakeIteratorCalled = ResettableValue(0) + + public func makeIterator() -> MinimalIterator { + timesMakeIteratorCalled.value += 1 + return base.makeIterator() + } + + + public let timesSuccessorCalled = ResettableValue(0) + + public func index(after i: MinimalIndex) -> MinimalIndex { + timesSuccessorCalled.value += 1 + return base.index(after: i) + } + + public let timesPredecessorCalled = ResettableValue(0) + + public func index(before i: MinimalIndex) -> MinimalIndex { + timesPredecessorCalled.value += 1 + return base.index(before: i) + } + + + + public let timesStartIndexCalled = ResettableValue(0) + + public var startIndex: MinimalIndex { + timesStartIndexCalled.value += 1 + return base.startIndex + } + + public let timesEndIndexCalled = ResettableValue(0) + + public var endIndex: MinimalIndex { + timesEndIndexCalled.value += 1 + return base.endIndex + } + + public subscript(i: MinimalIndex) -> Element { + get { + return base[i] + } + set { + base[i] = newValue + } + } + + // FIXME: swift-3-indexing-model: use defaults. +// if Self not in ['DefaultedCollection', 'DefaultedBidirectionalCollection', 'DefaultedRandomAccessCollection', 'DefaultedMutableCollection', 'DefaultedRangeReplaceableCollection']: + + public subscript(bounds: Range) -> Slice> { + get { + // FIXME: swift-3-indexing-model: range check. + return Slice(base: self, bounds: bounds) + } + set { + _writeBackMutableSlice(&self, bounds: bounds, slice: newValue) + } + } + + public init() { + base = Base() + } + + public mutating func replaceSubrange( + _ subRange: Range.Index>, + with newElements: C + ) where C : Collection, C.Iterator.Element == Element { + base.replaceSubrange(subRange, with: newElements) + } +} + +/* +FIXME: swift-3-indexing-model: uncomment this. +public struct DefaultedBidirectionalRangeReplaceableSlice + : RangeReplaceableCollection { + + public typealias Self_ = DefaultedBidirectionalRangeReplaceableSlice + public typealias Base = MinimalMutableRangeReplaceableBidirectionalCollection + public typealias Iterator = MinimalIterator + public typealias Index = MinimalIndex + + public var base: Base + public var startIndex: Index + public var endIndex: Index + + public init() { + expectSliceType(Self_.self) + + self.base = Base() + self.startIndex = base.startIndex + self.endIndex = base.endIndex + } + + public init(base: Base) { + self.base = base + self.startIndex = base.startIndex + self.endIndex = base.endIndex + } + + public init(base: Base, bounds: Range) { + self.base = base + self.startIndex = bounds.lowerBound + self.endIndex = bounds.upperBound + } + + public init(_ array: [Element]) { + self = DefaultedBidirectionalRangeReplaceableSlice( + base: Base(elements: array)) + } + + public init(elements: [Element]) { + self = DefaultedBidirectionalRangeReplaceableSlice( + base: Base(elements: elements)) + } + + public func makeIterator() -> MinimalIterator { + return MinimalIterator(Array(self)) + } + + public subscript(index: Index) -> Element { + Index._failEarlyRangeCheck(index, bounds: startIndex..) -> Self_ { + Index._failEarlyRangeCheck2( + rangeStart: bounds.lowerBound, + rangeEnd: bounds.upperBound, + boundsStart: startIndex, + boundsEnd: endIndex) + return DefaultedBidirectionalRangeReplaceableSlice( + base: base, bounds: bounds) + } + + public mutating func replaceSubrange< + C : Collection + >( + _ subRange: Range, + with newElements: C + ) where C.Iterator.Element == Element { + let startOffset = startIndex.position + let endOffset = + endIndex.position + - subRange.count + + numericCast(newElements.count) as Int + Index._failEarlyRangeCheck2( + rangeStart: subRange.lowerBound, + rangeEnd: subRange.upperBound, + boundsStart: startIndex, + boundsEnd: endIndex) + base.replaceSubrange(subRange, with: newElements) + startIndex = base.startIndex.advanced(by: startOffset) + endIndex = base.startIndex.advanced(by: endOffset) + } +} +*/ + + +/// A Collection that uses as many default implementations as +/// `Collection` can provide. +public struct DefaultedRandomAccessCollection : RandomAccessCollection { + public typealias Base = MinimalRandomAccessCollection + public typealias Iterator = MinimalIterator + public typealias Index = MinimalIndex + + // FIXME: this shouldn't be necessary, should come by default + public typealias Indices = DefaultIndices> + + public let base: Base + + public init(base: Base) { + self.base = base + } + + public init(_ array: [Element]) { + self.base = Base(elements: array) + } + + public init(elements: [Element]) { + self.base = Base(elements: elements) + } + + public init( + elements: S, + underestimatedCount: UnderestimatedCountBehavior = .value(0) + ) where S.Iterator.Element == Element { + self.init(base: + MinimalRandomAccessCollection(elements: elements, underestimatedCount: underestimatedCount)) + } + + public var underestimatedCount: Int { + return base.underestimatedCount + } + + public let timesMakeIteratorCalled = ResettableValue(0) + + public func makeIterator() -> MinimalIterator { + timesMakeIteratorCalled.value += 1 + return base.makeIterator() + } + + + public let timesSuccessorCalled = ResettableValue(0) + + public func index(after i: MinimalIndex) -> MinimalIndex { + timesSuccessorCalled.value += 1 + return base.index(after: i) + } + + public let timesPredecessorCalled = ResettableValue(0) + + public func index(before i: MinimalIndex) -> MinimalIndex { + timesPredecessorCalled.value += 1 + return base.index(before: i) + } + + public func distance(from start: MinimalIndex, to end: MinimalIndex) + -> Int { + return base.distance(from: start, to: end) + } + + public func index(_ i: Index, offsetBy n: Int) -> Index { + return base.index(i, offsetBy: n) + } + + + public let timesStartIndexCalled = ResettableValue(0) + + public var startIndex: MinimalIndex { + timesStartIndexCalled.value += 1 + return base.startIndex + } + + public let timesEndIndexCalled = ResettableValue(0) + + public var endIndex: MinimalIndex { + timesEndIndexCalled.value += 1 + return base.endIndex + } + + public subscript(i: MinimalIndex) -> Element { + get { + return base[i] + } + } + + // FIXME: swift-3-indexing-model: use defaults. +// if Self not in ['DefaultedCollection', 'DefaultedBidirectionalCollection', 'DefaultedRandomAccessCollection', 'DefaultedMutableCollection', 'DefaultedRangeReplaceableCollection']: + + public subscript(bounds: Range) -> Slice> { + get { + // FIXME: swift-3-indexing-model: range check. + return Slice(base: self, bounds: bounds) + } + } + +} + +/* +FIXME: swift-3-indexing-model: uncomment this. +public struct DefaultedRandomAccessRangeReplaceableSlice + : RangeReplaceableCollection { + + public typealias Self_ = DefaultedRandomAccessRangeReplaceableSlice + public typealias Base = MinimalRandomAccessCollection + public typealias Iterator = MinimalIterator + public typealias Index = MinimalIndex + + public var base: Base + public var startIndex: Index + public var endIndex: Index + + public init() { + expectSliceType(Self_.self) + + self.base = Base() + self.startIndex = base.startIndex + self.endIndex = base.endIndex + } + + public init(base: Base) { + self.base = base + self.startIndex = base.startIndex + self.endIndex = base.endIndex + } + + public init(base: Base, bounds: Range) { + self.base = base + self.startIndex = bounds.lowerBound + self.endIndex = bounds.upperBound + } + + public init(_ array: [Element]) { + self = DefaultedRandomAccessRangeReplaceableSlice( + base: Base(elements: array)) + } + + public init(elements: [Element]) { + self = DefaultedRandomAccessRangeReplaceableSlice( + base: Base(elements: elements)) + } + + public func makeIterator() -> MinimalIterator { + return MinimalIterator(Array(self)) + } + + public subscript(index: Index) -> Element { + Index._failEarlyRangeCheck(index, bounds: startIndex..) -> Self_ { + Index._failEarlyRangeCheck2( + rangeStart: bounds.lowerBound, + rangeEnd: bounds.upperBound, + boundsStart: startIndex, + boundsEnd: endIndex) + return DefaultedRandomAccessRangeReplaceableSlice( + base: base, bounds: bounds) + } + + public mutating func replaceSubrange< + C : Collection + >( + _ subRange: Range, + with newElements: C + ) where C.Iterator.Element == Element { + let startOffset = startIndex.position + let endOffset = + endIndex.position + - subRange.count + + numericCast(newElements.count) as Int + Index._failEarlyRangeCheck2( + rangeStart: subRange.lowerBound, + rangeEnd: subRange.upperBound, + boundsStart: startIndex, + boundsEnd: endIndex) + base.replaceSubrange(subRange, with: newElements) + startIndex = base.startIndex.advanced(by: startOffset) + endIndex = base.startIndex.advanced(by: endOffset) + } +} +*/ + + +/// A Collection that uses as many default implementations as +/// `Collection` can provide. +public struct DefaultedRandomAccessCollectionWithStrideableIndex : RandomAccessCollection { + public typealias Base = MinimalRandomAccessCollectionWithStrideableIndex + public typealias Iterator = MinimalIterator + public typealias Index = MinimalStrideableIndex + + public typealias Indices = Range + + public let base: Base + + public init(base: Base) { + self.base = base + } + + public init(_ array: [Element]) { + self.base = Base(elements: array) + } + + public init(elements: [Element]) { + self.base = Base(elements: elements) + } + + public init( + elements: S, + underestimatedCount: UnderestimatedCountBehavior = .value(0) + ) where S.Iterator.Element == Element { + self.init(base: + MinimalRandomAccessCollectionWithStrideableIndex(elements: elements, underestimatedCount: underestimatedCount)) + } + + public var underestimatedCount: Int { + return base.underestimatedCount + } + + public let timesMakeIteratorCalled = ResettableValue(0) + + public func makeIterator() -> MinimalIterator { + timesMakeIteratorCalled.value += 1 + return base.makeIterator() + } + + + public let timesStartIndexCalled = ResettableValue(0) + + public var startIndex: MinimalStrideableIndex { + timesStartIndexCalled.value += 1 + return base.startIndex + } + + public let timesEndIndexCalled = ResettableValue(0) + + public var endIndex: MinimalStrideableIndex { + timesEndIndexCalled.value += 1 + return base.endIndex + } + + public subscript(i: MinimalStrideableIndex) -> Element { + get { + return base[i] + } + } + + // FIXME: swift-3-indexing-model: use defaults. +// if Self not in ['DefaultedCollection', 'DefaultedBidirectionalCollection', 'DefaultedRandomAccessCollection', 'DefaultedMutableCollection', 'DefaultedRangeReplaceableCollection']: + + public subscript(bounds: Range) -> Slice> { + get { + // FIXME: swift-3-indexing-model: range check. + return Slice(base: self, bounds: bounds) + } + } + +} + +/* +FIXME: swift-3-indexing-model: uncomment this. +public struct DefaultedRandomAccessRangeReplaceableSlice + : RangeReplaceableCollection { + + public typealias Self_ = DefaultedRandomAccessRangeReplaceableSlice + public typealias Base = MinimalRandomAccessCollectionWithStrideableIndex + public typealias Iterator = MinimalIterator + public typealias Index = MinimalStrideableIndex + + public var base: Base + public var startIndex: Index + public var endIndex: Index + + public init() { + expectSliceType(Self_.self) + + self.base = Base() + self.startIndex = base.startIndex + self.endIndex = base.endIndex + } + + public init(base: Base) { + self.base = base + self.startIndex = base.startIndex + self.endIndex = base.endIndex + } + + public init(base: Base, bounds: Range) { + self.base = base + self.startIndex = bounds.lowerBound + self.endIndex = bounds.upperBound + } + + public init(_ array: [Element]) { + self = DefaultedRandomAccessRangeReplaceableSlice( + base: Base(elements: array)) + } + + public init(elements: [Element]) { + self = DefaultedRandomAccessRangeReplaceableSlice( + base: Base(elements: elements)) + } + + public func makeIterator() -> MinimalIterator { + return MinimalIterator(Array(self)) + } + + public subscript(index: Index) -> Element { + Index._failEarlyRangeCheck(index, bounds: startIndex..) -> Self_ { + Index._failEarlyRangeCheck2( + rangeStart: bounds.lowerBound, + rangeEnd: bounds.upperBound, + boundsStart: startIndex, + boundsEnd: endIndex) + return DefaultedRandomAccessRangeReplaceableSlice( + base: base, bounds: bounds) + } + + public mutating func replaceSubrange< + C : Collection + >( + _ subRange: Range, + with newElements: C + ) where C.Iterator.Element == Element { + let startOffset = startIndex.position + let endOffset = + endIndex.position + - subRange.count + + numericCast(newElements.count) as Int + Index._failEarlyRangeCheck2( + rangeStart: subRange.lowerBound, + rangeEnd: subRange.upperBound, + boundsStart: startIndex, + boundsEnd: endIndex) + base.replaceSubrange(subRange, with: newElements) + startIndex = base.startIndex.advanced(by: startOffset) + endIndex = base.startIndex.advanced(by: endOffset) + } +} +*/ + + +/// A Collection that uses as many default implementations as +/// `Collection` can provide. +public struct DefaultedRangeReplaceableRandomAccessCollection : RandomAccessCollection, RangeReplaceableCollection { + public typealias Base = MinimalRangeReplaceableRandomAccessCollection + public typealias Iterator = MinimalIterator + public typealias Index = MinimalIndex + + // FIXME: this shouldn't be necessary, should come by default + public typealias Indices = DefaultIndices> + + public var base: Base + + public init(base: Base) { + self.base = base + } + + public init(_ array: [Element]) { + self.base = Base(elements: array) + } + + public init(elements: [Element]) { + self.base = Base(elements: elements) + } + + public init( + elements: S, + underestimatedCount: UnderestimatedCountBehavior = .value(0) + ) where S.Iterator.Element == Element { + self.init(base: + MinimalRangeReplaceableRandomAccessCollection(elements: elements, underestimatedCount: underestimatedCount)) + } + + public var underestimatedCount: Int { + return base.underestimatedCount + } + + public let timesMakeIteratorCalled = ResettableValue(0) + + public func makeIterator() -> MinimalIterator { + timesMakeIteratorCalled.value += 1 + return base.makeIterator() + } + + + public let timesSuccessorCalled = ResettableValue(0) + + public func index(after i: MinimalIndex) -> MinimalIndex { + timesSuccessorCalled.value += 1 + return base.index(after: i) + } + + public let timesPredecessorCalled = ResettableValue(0) + + public func index(before i: MinimalIndex) -> MinimalIndex { + timesPredecessorCalled.value += 1 + return base.index(before: i) + } + + public func distance(from start: MinimalIndex, to end: MinimalIndex) + -> Int { + return base.distance(from: start, to: end) + } + + public func index(_ i: Index, offsetBy n: Int) -> Index { + return base.index(i, offsetBy: n) + } + + + public let timesStartIndexCalled = ResettableValue(0) + + public var startIndex: MinimalIndex { + timesStartIndexCalled.value += 1 + return base.startIndex + } + + public let timesEndIndexCalled = ResettableValue(0) + + public var endIndex: MinimalIndex { + timesEndIndexCalled.value += 1 + return base.endIndex + } + + public subscript(i: MinimalIndex) -> Element { + get { + return base[i] + } + } + + // FIXME: swift-3-indexing-model: use defaults. +// if Self not in ['DefaultedCollection', 'DefaultedBidirectionalCollection', 'DefaultedRandomAccessCollection', 'DefaultedMutableCollection', 'DefaultedRangeReplaceableCollection']: + + public subscript(bounds: Range) -> Slice> { + get { + // FIXME: swift-3-indexing-model: range check. + return Slice(base: self, bounds: bounds) + } + } + + public init() { + base = Base() + } + + public mutating func replaceSubrange( + _ subRange: Range.Index>, + with newElements: C + ) where C : Collection, C.Iterator.Element == Element { + base.replaceSubrange(subRange, with: newElements) + } +} + +/* +FIXME: swift-3-indexing-model: uncomment this. +public struct DefaultedRandomAccessRangeReplaceableSlice + : RangeReplaceableCollection { + + public typealias Self_ = DefaultedRandomAccessRangeReplaceableSlice + public typealias Base = MinimalRangeReplaceableRandomAccessCollection + public typealias Iterator = MinimalIterator + public typealias Index = MinimalIndex + + public var base: Base + public var startIndex: Index + public var endIndex: Index + + public init() { + expectSliceType(Self_.self) + + self.base = Base() + self.startIndex = base.startIndex + self.endIndex = base.endIndex + } + + public init(base: Base) { + self.base = base + self.startIndex = base.startIndex + self.endIndex = base.endIndex + } + + public init(base: Base, bounds: Range) { + self.base = base + self.startIndex = bounds.lowerBound + self.endIndex = bounds.upperBound + } + + public init(_ array: [Element]) { + self = DefaultedRandomAccessRangeReplaceableSlice( + base: Base(elements: array)) + } + + public init(elements: [Element]) { + self = DefaultedRandomAccessRangeReplaceableSlice( + base: Base(elements: elements)) + } + + public func makeIterator() -> MinimalIterator { + return MinimalIterator(Array(self)) + } + + public subscript(index: Index) -> Element { + Index._failEarlyRangeCheck(index, bounds: startIndex..) -> Self_ { + Index._failEarlyRangeCheck2( + rangeStart: bounds.lowerBound, + rangeEnd: bounds.upperBound, + boundsStart: startIndex, + boundsEnd: endIndex) + return DefaultedRandomAccessRangeReplaceableSlice( + base: base, bounds: bounds) + } + + public mutating func replaceSubrange< + C : Collection + >( + _ subRange: Range, + with newElements: C + ) where C.Iterator.Element == Element { + let startOffset = startIndex.position + let endOffset = + endIndex.position + - subRange.count + + numericCast(newElements.count) as Int + Index._failEarlyRangeCheck2( + rangeStart: subRange.lowerBound, + rangeEnd: subRange.upperBound, + boundsStart: startIndex, + boundsEnd: endIndex) + base.replaceSubrange(subRange, with: newElements) + startIndex = base.startIndex.advanced(by: startOffset) + endIndex = base.startIndex.advanced(by: endOffset) + } +} +*/ + + +/// A Collection that uses as many default implementations as +/// `Collection` can provide. +public struct DefaultedMutableRandomAccessCollection : RandomAccessCollection, MutableCollection { + public typealias Base = MinimalMutableRandomAccessCollection + public typealias Iterator = MinimalIterator + public typealias Index = MinimalIndex + + // FIXME: this shouldn't be necessary, should come by default + public typealias Indices = DefaultIndices> + + public var base: Base + + public init(base: Base) { + self.base = base + } + + public init(_ array: [Element]) { + self.base = Base(elements: array) + } + + public init(elements: [Element]) { + self.base = Base(elements: elements) + } + + public init( + elements: S, + underestimatedCount: UnderestimatedCountBehavior = .value(0) + ) where S.Iterator.Element == Element { + self.init(base: + MinimalMutableRandomAccessCollection(elements: elements, underestimatedCount: underestimatedCount)) + } + + public var underestimatedCount: Int { + return base.underestimatedCount + } + + public let timesMakeIteratorCalled = ResettableValue(0) + + public func makeIterator() -> MinimalIterator { + timesMakeIteratorCalled.value += 1 + return base.makeIterator() + } + + + public let timesSuccessorCalled = ResettableValue(0) + + public func index(after i: MinimalIndex) -> MinimalIndex { + timesSuccessorCalled.value += 1 + return base.index(after: i) + } + + public let timesPredecessorCalled = ResettableValue(0) + + public func index(before i: MinimalIndex) -> MinimalIndex { + timesPredecessorCalled.value += 1 + return base.index(before: i) + } + + public func distance(from start: MinimalIndex, to end: MinimalIndex) + -> Int { + return base.distance(from: start, to: end) + } + + public func index(_ i: Index, offsetBy n: Int) -> Index { + return base.index(i, offsetBy: n) + } + + + public let timesStartIndexCalled = ResettableValue(0) + + public var startIndex: MinimalIndex { + timesStartIndexCalled.value += 1 + return base.startIndex + } + + public let timesEndIndexCalled = ResettableValue(0) + + public var endIndex: MinimalIndex { + timesEndIndexCalled.value += 1 + return base.endIndex + } + + public subscript(i: MinimalIndex) -> Element { + get { + return base[i] + } + set { + base[i] = newValue + } + } + + // FIXME: swift-3-indexing-model: use defaults. +// if Self not in ['DefaultedCollection', 'DefaultedBidirectionalCollection', 'DefaultedRandomAccessCollection', 'DefaultedMutableCollection', 'DefaultedRangeReplaceableCollection']: + + public subscript(bounds: Range) -> Slice> { + get { + // FIXME: swift-3-indexing-model: range check. + return Slice(base: self, bounds: bounds) + } + set { + _writeBackMutableSlice(&self, bounds: bounds, slice: newValue) + } + } + +} + +/* +FIXME: swift-3-indexing-model: uncomment this. +public struct DefaultedRandomAccessRangeReplaceableSlice + : RangeReplaceableCollection { + + public typealias Self_ = DefaultedRandomAccessRangeReplaceableSlice + public typealias Base = MinimalMutableRandomAccessCollection + public typealias Iterator = MinimalIterator + public typealias Index = MinimalIndex + + public var base: Base + public var startIndex: Index + public var endIndex: Index + + public init() { + expectSliceType(Self_.self) + + self.base = Base() + self.startIndex = base.startIndex + self.endIndex = base.endIndex + } + + public init(base: Base) { + self.base = base + self.startIndex = base.startIndex + self.endIndex = base.endIndex + } + + public init(base: Base, bounds: Range) { + self.base = base + self.startIndex = bounds.lowerBound + self.endIndex = bounds.upperBound + } + + public init(_ array: [Element]) { + self = DefaultedRandomAccessRangeReplaceableSlice( + base: Base(elements: array)) + } + + public init(elements: [Element]) { + self = DefaultedRandomAccessRangeReplaceableSlice( + base: Base(elements: elements)) + } + + public func makeIterator() -> MinimalIterator { + return MinimalIterator(Array(self)) + } + + public subscript(index: Index) -> Element { + Index._failEarlyRangeCheck(index, bounds: startIndex..) -> Self_ { + Index._failEarlyRangeCheck2( + rangeStart: bounds.lowerBound, + rangeEnd: bounds.upperBound, + boundsStart: startIndex, + boundsEnd: endIndex) + return DefaultedRandomAccessRangeReplaceableSlice( + base: base, bounds: bounds) + } + + public mutating func replaceSubrange< + C : Collection + >( + _ subRange: Range, + with newElements: C + ) where C.Iterator.Element == Element { + let startOffset = startIndex.position + let endOffset = + endIndex.position + - subRange.count + + numericCast(newElements.count) as Int + Index._failEarlyRangeCheck2( + rangeStart: subRange.lowerBound, + rangeEnd: subRange.upperBound, + boundsStart: startIndex, + boundsEnd: endIndex) + base.replaceSubrange(subRange, with: newElements) + startIndex = base.startIndex.advanced(by: startOffset) + endIndex = base.startIndex.advanced(by: endOffset) + } +} +*/ + + +/// A Collection that uses as many default implementations as +/// `Collection` can provide. +public struct DefaultedMutableRangeReplaceableRandomAccessCollection : RandomAccessCollection, MutableCollection, RangeReplaceableCollection { + public typealias Base = MinimalMutableRangeReplaceableRandomAccessCollection + public typealias Iterator = MinimalIterator + public typealias Index = MinimalIndex + + // FIXME: this shouldn't be necessary, should come by default + public typealias Indices = DefaultIndices> + + public var base: Base + + public init(base: Base) { + self.base = base + } + + public init(_ array: [Element]) { + self.base = Base(elements: array) + } + + public init(elements: [Element]) { + self.base = Base(elements: elements) + } + + public init( + elements: S, + underestimatedCount: UnderestimatedCountBehavior = .value(0) + ) where S.Iterator.Element == Element { + self.init(base: + MinimalMutableRangeReplaceableRandomAccessCollection(elements: elements, underestimatedCount: underestimatedCount)) + } + + public var underestimatedCount: Int { + return base.underestimatedCount + } + + public let timesMakeIteratorCalled = ResettableValue(0) + + public func makeIterator() -> MinimalIterator { + timesMakeIteratorCalled.value += 1 + return base.makeIterator() + } + + + public let timesSuccessorCalled = ResettableValue(0) + + public func index(after i: MinimalIndex) -> MinimalIndex { + timesSuccessorCalled.value += 1 + return base.index(after: i) + } + + public let timesPredecessorCalled = ResettableValue(0) + + public func index(before i: MinimalIndex) -> MinimalIndex { + timesPredecessorCalled.value += 1 + return base.index(before: i) + } + + public func distance(from start: MinimalIndex, to end: MinimalIndex) + -> Int { + return base.distance(from: start, to: end) + } + + public func index(_ i: Index, offsetBy n: Int) -> Index { + return base.index(i, offsetBy: n) + } + + + public let timesStartIndexCalled = ResettableValue(0) + + public var startIndex: MinimalIndex { + timesStartIndexCalled.value += 1 + return base.startIndex + } + + public let timesEndIndexCalled = ResettableValue(0) + + public var endIndex: MinimalIndex { + timesEndIndexCalled.value += 1 + return base.endIndex + } + + public subscript(i: MinimalIndex) -> Element { + get { + return base[i] + } + set { + base[i] = newValue + } + } + + // FIXME: swift-3-indexing-model: use defaults. +// if Self not in ['DefaultedCollection', 'DefaultedBidirectionalCollection', 'DefaultedRandomAccessCollection', 'DefaultedMutableCollection', 'DefaultedRangeReplaceableCollection']: + + public subscript(bounds: Range) -> Slice> { + get { + // FIXME: swift-3-indexing-model: range check. + return Slice(base: self, bounds: bounds) + } + set { + _writeBackMutableSlice(&self, bounds: bounds, slice: newValue) + } + } + + public init() { + base = Base() + } + + public mutating func replaceSubrange( + _ subRange: Range.Index>, + with newElements: C + ) where C : Collection, C.Iterator.Element == Element { + base.replaceSubrange(subRange, with: newElements) + } +} + +/* +FIXME: swift-3-indexing-model: uncomment this. +public struct DefaultedRandomAccessRangeReplaceableSlice + : RangeReplaceableCollection { + + public typealias Self_ = DefaultedRandomAccessRangeReplaceableSlice + public typealias Base = MinimalMutableRangeReplaceableRandomAccessCollection + public typealias Iterator = MinimalIterator + public typealias Index = MinimalIndex + + public var base: Base + public var startIndex: Index + public var endIndex: Index + + public init() { + expectSliceType(Self_.self) + + self.base = Base() + self.startIndex = base.startIndex + self.endIndex = base.endIndex + } + + public init(base: Base) { + self.base = base + self.startIndex = base.startIndex + self.endIndex = base.endIndex + } + + public init(base: Base, bounds: Range) { + self.base = base + self.startIndex = bounds.lowerBound + self.endIndex = bounds.upperBound + } + + public init(_ array: [Element]) { + self = DefaultedRandomAccessRangeReplaceableSlice( + base: Base(elements: array)) + } + + public init(elements: [Element]) { + self = DefaultedRandomAccessRangeReplaceableSlice( + base: Base(elements: elements)) + } + + public func makeIterator() -> MinimalIterator { + return MinimalIterator(Array(self)) + } + + public subscript(index: Index) -> Element { + Index._failEarlyRangeCheck(index, bounds: startIndex..) -> Self_ { + Index._failEarlyRangeCheck2( + rangeStart: bounds.lowerBound, + rangeEnd: bounds.upperBound, + boundsStart: startIndex, + boundsEnd: endIndex) + return DefaultedRandomAccessRangeReplaceableSlice( + base: base, bounds: bounds) + } + + public mutating func replaceSubrange< + C : Collection + >( + _ subRange: Range, + with newElements: C + ) where C.Iterator.Element == Element { + let startOffset = startIndex.position + let endOffset = + endIndex.position + - subRange.count + + numericCast(newElements.count) as Int + Index._failEarlyRangeCheck2( + rangeStart: subRange.lowerBound, + rangeEnd: subRange.upperBound, + boundsStart: startIndex, + boundsEnd: endIndex) + base.replaceSubrange(subRange, with: newElements) + startIndex = base.startIndex.advanced(by: startOffset) + endIndex = base.startIndex.advanced(by: endOffset) + } +} +*/ + + +// Local Variables: +// eval: (read-only-mode 1) +// End: diff --git a/stdlib/private/StdlibCollectionUnittest/MinimalCollections.swift.gyb b/stdlib/private/StdlibCollectionUnittest/MinimalCollections.swift.gyb deleted file mode 100644 index ddf5a768d47ef..0000000000000 --- a/stdlib/private/StdlibCollectionUnittest/MinimalCollections.swift.gyb +++ /dev/null @@ -1,1091 +0,0 @@ -//===--- MinimalCollections.swift.gyb -------------------------*- swift -*-===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -%{ -from gyb_stdlib_unittest_support import TRACE, stackTrace, trace -from gyb_stdlib_support import ( - TRAVERSALS, - collectionForTraversal, - collectionTypeName, - protocolsForCollectionFeatures -) -}% - -import StdlibUnittest - -/// State shared by all generators of a MinimalSequence. -internal class _MinimalIteratorSharedState { - internal init(_ data: [T]) { - self.data = data - } - - internal let data: [T] - internal var i: Int = 0 - internal var underestimatedCount: Int = 0 -} - -//===----------------------------------------------------------------------===// -// MinimalIterator -//===----------------------------------------------------------------------===// - -/// An IteratorProtocol that implements the protocol contract in the most -/// narrow way possible. -/// -/// This generator will return `nil` only once. -public struct MinimalIterator : IteratorProtocol { - public init(_ s: S) where S.Iterator.Element == T { - self._sharedState = _MinimalIteratorSharedState(Array(s)) - } - - public init(_ data: [T]) { - self._sharedState = _MinimalIteratorSharedState(data) - } - - internal init(_ _sharedState: _MinimalIteratorSharedState) { - self._sharedState = _sharedState - } - - public func next() -> T? { - if _sharedState.i == _sharedState.data.count { - return nil - } - defer { _sharedState.i += 1 } - return _sharedState.data[_sharedState.i] - } - - internal let _sharedState: _MinimalIteratorSharedState -} - -// A protocol to identify MinimalIterator. -public protocol _MinimalIterator {} -extension MinimalIterator : _MinimalIterator {} - -//===----------------------------------------------------------------------===// -// MinimalSequence -//===----------------------------------------------------------------------===// - -public enum UnderestimatedCountBehavior { - /// Return the actual number of elements. - case precise - - /// Return the actual number of elements divided by 2. - case half - - /// Return an overestimated count. Useful to test how algorithms reserve - /// memory. - case overestimate - - /// Return the provided value. - case value(Int) -} - -/// A Sequence that implements the protocol contract in the most -/// narrow way possible. -/// -/// This sequence is consumed when its generator is advanced. -public struct MinimalSequence : Sequence, CustomDebugStringConvertible { - public init( - elements: S, - underestimatedCount: UnderestimatedCountBehavior = .value(0) - ) where S.Iterator.Element == T { - let data = Array(elements) - self._sharedState = _MinimalIteratorSharedState(data) - - switch underestimatedCount { - case .precise: - self._sharedState.underestimatedCount = data.count - - case .half: - self._sharedState.underestimatedCount = data.count / 2 - - case .overestimate: - self._sharedState.underestimatedCount = data.count * 3 + 5 - - case .value(let count): - self._sharedState.underestimatedCount = count - } - } - - public let timesMakeIteratorCalled = ResettableValue(0) - - public func makeIterator() -> MinimalIterator { - timesMakeIteratorCalled.value += 1 - return MinimalIterator(_sharedState) - } - - public var underestimatedCount: Int { - return Swift.max(0, self._sharedState.underestimatedCount - self._sharedState.i) - } - - public var debugDescription: String { - return "MinimalSequence(\(_sharedState.data[_sharedState.i..<_sharedState.data.count]))" - } - - internal let _sharedState: _MinimalIteratorSharedState -} - -//===----------------------------------------------------------------------===// -// Index invalidation checking -//===----------------------------------------------------------------------===// - -internal enum _CollectionOperation : Equatable { - case reserveCapacity(capacity: Int) - case append - case appendContentsOf(count: Int) - case replaceRange(subRange: Range, replacementCount: Int) - case insert(atIndex: Int) - case insertContentsOf(atIndex: Int, count: Int) - case removeAtIndex(index: Int) - case removeLast - case removeRange(subRange: Range) - case removeAll(keepCapacity: Bool) - - internal func _applyTo( - elementsLastMutatedStateIds: [Int], - endIndexLastMutatedStateId: Int, - nextStateId: Int - ) -> ([Int], Int) { - var newElementsIds = elementsLastMutatedStateIds - var newEndIndexId = endIndexLastMutatedStateId - switch self { - case .reserveCapacity: - let invalidIndices = newElementsIds.indices - newElementsIds.replaceSubrange( - invalidIndices, - with: repeatElement(nextStateId, count: invalidIndices.count)) - newEndIndexId = nextStateId - - case .append: - newElementsIds.append(nextStateId) - newEndIndexId = nextStateId - - case .appendContentsOf(let count): - newElementsIds.append(contentsOf: - repeatElement(nextStateId, count: count)) - newEndIndexId = nextStateId - - case .replaceRange(let subRange, let replacementCount): - newElementsIds.replaceSubrange( - subRange, - with: repeatElement(nextStateId, count: replacementCount)) - - let invalidIndices = subRange.lowerBound.. Bool { - switch (lhs, rhs) { - case (.reserveCapacity(let lhsCapacity), .reserveCapacity(let rhsCapacity)): - return lhsCapacity == rhsCapacity - - case (.append, .append): - return true - - case (.appendContentsOf(let lhsCount), .appendContentsOf(let rhsCount)): - return lhsCount == rhsCount - - case ( - .replaceRange(let lhsSubRange, let lhsReplacementCount), - .replaceRange(let rhsSubRange, let rhsReplacementCount)): - - return lhsSubRange == rhsSubRange && - lhsReplacementCount == rhsReplacementCount - - case (.insert(let lhsAtIndex), .insert(let rhsAtIndex)): - return lhsAtIndex == rhsAtIndex - - case ( - .insertContentsOf(let lhsAtIndex, let lhsCount), - .insertContentsOf(let rhsAtIndex, let rhsCount)): - - return lhsAtIndex == rhsAtIndex && lhsCount == rhsCount - - case (.removeAtIndex(let lhsIndex), .removeAtIndex(let rhsIndex)): - return lhsIndex == rhsIndex - - case (.removeLast, .removeLast): - return true - - case (.removeRange(let lhsSubRange), .removeRange(let rhsSubRange)): - return lhsSubRange == rhsSubRange - - case (.removeAll(let lhsKeepCapacity), .removeAll(let rhsKeepCapacity)): - return lhsKeepCapacity == rhsKeepCapacity - - default: - return false - } -} - -public struct _CollectionState : Equatable, Hashable { - internal static var _nextUnusedState: Int = 0 - internal static var _namedStates: [String : _CollectionState] = [:] - - internal let _id: Int - internal let _elementsLastMutatedStateIds: [Int] - internal let _endIndexLastMutatedStateId: Int - - internal init( - id: Int, - elementsLastMutatedStateIds: [Int], - endIndexLastMutatedStateId: Int) { - self._id = id - self._elementsLastMutatedStateIds = elementsLastMutatedStateIds - self._endIndexLastMutatedStateId = endIndexLastMutatedStateId - } - - public init(newRootStateForElementCount count: Int) { - self._id = _CollectionState._nextUnusedState - _CollectionState._nextUnusedState += 1 - self._elementsLastMutatedStateIds = - Array(repeatElement(self._id, count: count)) - self._endIndexLastMutatedStateId = self._id - } - - internal init(name: String, elementCount: Int) { - if let result = _CollectionState._namedStates[name] { - self = result - } else { - self = _CollectionState(newRootStateForElementCount: elementCount) - _CollectionState._namedStates[name] = self - } - } - - public var hashValue: Int { - return _id.hashValue - } -} - -public func == (lhs: _CollectionState, rhs: _CollectionState) -> Bool { - return lhs._id == rhs._id -} - -internal struct _CollectionStateTransition { - internal let _previousState: _CollectionState - internal let _operation: _CollectionOperation - internal let _nextState: _CollectionState - - internal static var _allTransitions: - [_CollectionState : Box<[_CollectionStateTransition]>] = [:] - - internal init( - previousState: _CollectionState, - operation: _CollectionOperation, - nextState: _CollectionState - ) { - var transitions = - _CollectionStateTransition._allTransitions[previousState] - if transitions == nil { - transitions = Box<[_CollectionStateTransition]>([]) - _CollectionStateTransition._allTransitions[previousState] = transitions - } - if let i = transitions!.value.index(where: { $0._operation == operation }) { - self = transitions!.value[i] - return - } - self._previousState = previousState - self._operation = operation - self._nextState = nextState - transitions!.value.append(self) - } - - internal init( - previousState: _CollectionState, - operation: _CollectionOperation - ) { - let nextStateId = _CollectionState._nextUnusedState - _CollectionState._nextUnusedState += 1 - let (newElementStates, newEndIndexState) = operation._applyTo( - elementsLastMutatedStateIds: previousState._elementsLastMutatedStateIds, - endIndexLastMutatedStateId: previousState._endIndexLastMutatedStateId, - nextStateId: nextStateId) - let nextState = _CollectionState( - id: nextStateId, - elementsLastMutatedStateIds: newElementStates, - endIndexLastMutatedStateId: newEndIndexState) - self = _CollectionStateTransition( - previousState: previousState, - operation: operation, - nextState: nextState) - } -} - -% for Index in ['MinimalIndex', 'MinimalStrideableIndex']: - -//===----------------------------------------------------------------------===// -// ${Index} -//===----------------------------------------------------------------------===// - -/// Asserts that the two indices are allowed to participate in a binary -/// operation. -internal func _expectCompatibleIndices( - _ first: ${Index}, - _ second: ${Index}, - ${TRACE} -) { - if first._collectionState._id == second._collectionState._id { - // Fast path: the indices are derived from the same state. - return - } - - // The indices are derived from different states. Check that they point - // to a self-consistent view of the collection. - if first._collectionState._id > second._collectionState._id { - return _expectCompatibleIndices(second, first) - } - - func lastMutatedStateId( - of i: ${Index}, - in state: _CollectionState - ) -> Int { - let offset = i.position - if offset == state._elementsLastMutatedStateIds.endIndex { - return state._id - } - return state._elementsLastMutatedStateIds[offset] - } - - let newestCollectionState = second._collectionState - let expectedFirstIndexLastMutatedStateId = - lastMutatedStateId(of: first, in: newestCollectionState) - - expectEqual( - expectedFirstIndexLastMutatedStateId, - first._collectionState._id, - "Indices are not compatible:\n" + - "first: \(first)\n" + - "second: \(second)\n" + - "first element last mutated in state id: \(first._collectionState._id)\n" + - "expected state id: \(expectedFirstIndexLastMutatedStateId)\n" + - "newest collection state: \(newestCollectionState)", - stackTrace: ${stackTrace}) - - // To make writing assertions easier, perform a trap. - if expectedFirstIndexLastMutatedStateId != first._collectionState._id { - fatalError("Indices are not compatible") - } -} - -public struct ${Index} : Comparable { - public init( - collectionState: _CollectionState, - position: Int, - startIndex: Int, - endIndex: Int - ) { - expectTrapping( - position, - in: startIndex...endIndex) - self = ${Index}( - _collectionState: collectionState, - uncheckedPosition: position) - } - - internal init( - _collectionState: _CollectionState, - uncheckedPosition: Int - ) { - self._collectionState = _collectionState - self.position = uncheckedPosition - } - - public let _collectionState: _CollectionState - public let position: Int - - public static var trapOnRangeCheckFailure = ResettableValue(true) - -% if 'Strideable' in Index: - public var timesAdvancedCalled = ResettableValue(0) - public var timesDistanceCalled = ResettableValue(0) -% end -} - -public func == (lhs: ${Index}, rhs: ${Index}) -> Bool { - _expectCompatibleIndices(lhs, rhs) - return lhs.position == rhs.position -} - -public func < (lhs: ${Index}, rhs: ${Index}) -> Bool { - _expectCompatibleIndices(lhs, rhs) - return lhs.position < rhs.position -} - -% end - -extension MinimalStrideableIndex : Strideable { - public typealias Stride = Int - - public func distance(to other: MinimalStrideableIndex) -> Int { - timesDistanceCalled.value += 1 - _expectCompatibleIndices(self, other) - return other.position - position - } - - public func advanced(by n: Int) -> MinimalStrideableIndex { - timesAdvancedCalled.value += 1 - return MinimalStrideableIndex( - _collectionState: _collectionState, - uncheckedPosition: position + n) - } -} - -//===----------------------------------------------------------------------===// -// Minimal***[Mutable]?Collection -//===----------------------------------------------------------------------===// - -% for Traversal in TRAVERSALS: -% for Mutable in [ False, True ]: -% for RangeReplaceable in [ False, True ]: -% for StrideableIndex in [ False, True ]: -% Self = 'Minimal' + collectionTypeName(traversal=Traversal, mutable=Mutable, rangeReplaceable=RangeReplaceable) -% Self += 'WithStrideableIndex' if StrideableIndex else '' -% SelfProtocols = ', '.join(protocolsForCollectionFeatures(traversal=Traversal, mutable=Mutable, rangeReplaceable=RangeReplaceable)) -% Index = 'MinimalStrideableIndex' if StrideableIndex else 'MinimalIndex' -% # Only generating MinimalRandomAccessCollectionWithStrideableIndex -% if not StrideableIndex or ( -% StrideableIndex and Traversal == 'RandomAccess' and -% not Mutable): - -/// A minimal implementation of `Collection` with extra checks. -public struct ${Self} : ${SelfProtocols} { - /// Creates a collection with given contents, but a unique modification - /// history. No other instance has the same modification history. - public init( - elements: S, - underestimatedCount: UnderestimatedCountBehavior = .value(0) - ) where S.Iterator.Element == T { - self._elements = Array(elements) - - self._collectionState = _CollectionState( - newRootStateForElementCount: self._elements.count) - - switch underestimatedCount { - case .precise: - self.underestimatedCount = _elements.count - - case .half: - self.underestimatedCount = _elements.count / 2 - - case .overestimate: - self.underestimatedCount = _elements.count * 3 + 5 - - case .value(let count): - self.underestimatedCount = count - } - } - -% if RangeReplaceable: - public init() { - self.underestimatedCount = 0 - self._elements = [] - self._collectionState = - _CollectionState(name: "\(type(of: self))", elementCount: 0) - } - - public init(_ elements: S) where S.Iterator.Element == T { - self.underestimatedCount = 0 - self._elements = Array(elements) - self._collectionState = - _CollectionState(newRootStateForElementCount: self._elements.count) - } -% end - - public let timesMakeIteratorCalled = ResettableValue(0) - - public func makeIterator() -> MinimalIterator { - timesMakeIteratorCalled.value += 1 - return MinimalIterator(_elements) - } - - public typealias Index = ${Index} - - internal func _index(forPosition i: Int) -> ${Index} { - return ${Index}( - collectionState: _collectionState, - position: i, - startIndex: _elements.startIndex, - endIndex: _elements.endIndex) - } - - internal func _uncheckedIndex(forPosition i: Int) -> ${Index} { - return ${Index}( - _collectionState: _collectionState, - uncheckedPosition: i) - } - - public let timesStartIndexCalled = ResettableValue(0) - - public var startIndex: ${Index} { - timesStartIndexCalled.value += 1 - return _uncheckedIndex(forPosition: _elements.startIndex) - } - - public let timesEndIndexCalled = ResettableValue(0) - - public var endIndex: ${Index} { - timesEndIndexCalled.value += 1 - return _uncheckedIndex(forPosition: _elements.endIndex) - } - -% if StrideableIndex: - public typealias Indices = Range<${Index}> -% elif Traversal == 'RandomAccess': - // FIXME: this shouldn't be necessary, should come by default - public typealias Indices = DefaultIndices<${Self}> -% end - - public func _failEarlyRangeCheck( - _ index: ${Index}, - bounds: Range<${Index}> - ) { - _expectCompatibleIndices( - _uncheckedIndex(forPosition: index.position), - index) - - _expectCompatibleIndices( - _uncheckedIndex(forPosition: bounds.lowerBound.position), - bounds.lowerBound) - _expectCompatibleIndices( - _uncheckedIndex(forPosition: bounds.upperBound.position), - bounds.upperBound) - - expectTrapping( - index.position, - in: bounds.lowerBound.position.., - bounds: Range<${Index}> - ) { - _expectCompatibleIndices( - _uncheckedIndex(forPosition: range.lowerBound.position), - range.lowerBound) - _expectCompatibleIndices( - _uncheckedIndex(forPosition: range.upperBound.position), - range.upperBound) - - _expectCompatibleIndices( - _uncheckedIndex(forPosition: bounds.lowerBound.position), - bounds.lowerBound) - _expectCompatibleIndices( - _uncheckedIndex(forPosition: bounds.upperBound.position), - bounds.upperBound) - - expectTrapping( - range.lowerBound.position.. ${Index} { - _failEarlyRangeCheck(i, bounds: startIndex.. ${Index} { - // FIXME: swift-3-indexing-model: perform a range check and use - // return _uncheckedIndex(forPosition: i.position - 1) - return _index(forPosition: i.position - 1) - } -% end - - public func distance(from start: ${Index}, to end: ${Index}) - -> Int { -% if Traversal == 'Forward': - _precondition(start <= end, - "Only BidirectionalCollections can have end come before start") -% end - // FIXME: swift-3-indexing-model: perform a range check properly. - if start != endIndex { - _failEarlyRangeCheck(start, bounds: startIndex.. Index { -% if Traversal == 'Forward': - _precondition(n >= 0, - "Only BidirectionalCollections can be advanced by a negative amount") -% end - // FIXME: swift-3-indexing-model: perform a range check properly. - if i != endIndex { - _failEarlyRangeCheck(i, bounds: startIndex.. T { - get { - _failEarlyRangeCheck(i, bounds: startIndex..) -> Slice<${Self}> { - get { - _failEarlyRangeCheck(bounds, bounds: startIndex..(contentsOf newElements: S) - where S.Iterator.Element == T { - let oldCount = count - _elements.append(contentsOf: newElements) - let newCount = count - _willMutate(.appendContentsOf(count: newCount - oldCount)) - } - - public mutating func replaceSubrange( - _ subRange: Range<${Index}>, - with newElements: C - ) where C : Collection, C.Iterator.Element == T { - let oldCount = count - _elements.replaceSubrange( - subRange.lowerBound.position..( - contentsOf newElements: S, at i: ${Index} - ) where S.Iterator.Element == T { - let oldCount = count - _elements.insert(contentsOf: newElements, at: i.position) - let newCount = count - - if newCount - oldCount != 0 { - _willMutate(.insertContentsOf( - atIndex: i.position, - count: newCount - oldCount)) - } - } - - @discardableResult - public mutating func remove(at i: ${Index}) -> T { - _willMutate(.removeAtIndex(index: i.position)) - return _elements.remove(at: i.position) - } - - @discardableResult - public mutating func removeLast() -> T { - _willMutate(.removeLast) - return _elements.removeLast() - } - - public mutating func removeSubrange(_ subRange: Range<${Index}>) { - if !subRange.isEmpty { - _willMutate(.removeRange( - subRange: subRange.lowerBound.position.. : Sequence { - public let base: MinimalSequence - - public init(base: MinimalSequence) { - self.base = base - } - - public init( - elements: S, - underestimatedCount: UnderestimatedCountBehavior = .value(0) - ) where S.Iterator.Element == Element { - self.init(base: MinimalSequence( - elements: elements, underestimatedCount: underestimatedCount)) - } - - public func makeIterator() -> MinimalIterator { - return base.makeIterator() - } - - public var underestimatedCount: Int { - return base.underestimatedCount - } -} - -% for Traversal in TRAVERSALS: -% for Mutable in [ False, True ]: -% for RangeReplaceable in [ False, True ]: -% for StrideableIndex in [ False, True ]: -% Self = 'Defaulted' + collectionTypeName(traversal=Traversal, mutable=Mutable, rangeReplaceable=RangeReplaceable) -% Self += 'WithStrideableIndex' if StrideableIndex else '' -% Base = 'Minimal' + collectionTypeName(traversal=Traversal, mutable=Mutable, rangeReplaceable=RangeReplaceable) -% Base += 'WithStrideableIndex' if StrideableIndex else '' -% SelfProtocols = ', '.join(protocolsForCollectionFeatures(traversal=Traversal, mutable=Mutable, rangeReplaceable=RangeReplaceable)) -% Index = 'MinimalStrideableIndex' if StrideableIndex else 'MinimalIndex' -% # Only generating DefaultedRandomAccessCollectionWithStrideableIndex -% if not StrideableIndex or ( -% StrideableIndex and Traversal == 'RandomAccess' and -% not Mutable and not RangeReplaceable): - -/// A Collection that uses as many default implementations as -/// `Collection` can provide. -public struct ${Self} : ${SelfProtocols} { - public typealias Base = ${Base} - public typealias Iterator = MinimalIterator - public typealias Index = ${Index} - -% if StrideableIndex: - public typealias Indices = Range<${Index}> -% elif Traversal == 'RandomAccess': - // FIXME: this shouldn't be necessary, should come by default - public typealias Indices = DefaultIndices<${Self}> -% end - -% if Mutable or RangeReplaceable: - public var base: Base -% else: - public let base: Base -% end - - public init(base: Base) { - self.base = base - } - - public init(_ array: [Element]) { - self.base = Base(elements: array) - } - - public init(elements: [Element]) { - self.base = Base(elements: elements) - } - - public init( - elements: S, - underestimatedCount: UnderestimatedCountBehavior = .value(0) - ) where S.Iterator.Element == Element { - self.init(base: - ${Base}(elements: elements, underestimatedCount: underestimatedCount)) - } - - public var underestimatedCount: Int { - return base.underestimatedCount - } - - public let timesMakeIteratorCalled = ResettableValue(0) - - public func makeIterator() -> MinimalIterator { - timesMakeIteratorCalled.value += 1 - return base.makeIterator() - } - -% if not StrideableIndex: - - public let timesSuccessorCalled = ResettableValue(0) - - public func index(after i: ${Index}) -> ${Index} { - timesSuccessorCalled.value += 1 - return base.index(after: i) - } - -% if Traversal in ['Bidirectional', 'RandomAccess']: - public let timesPredecessorCalled = ResettableValue(0) - - public func index(before i: ${Index}) -> ${Index} { - timesPredecessorCalled.value += 1 - return base.index(before: i) - } -% end - -% if Traversal == 'RandomAccess': - public func distance(from start: ${Index}, to end: ${Index}) - -> Int { - return base.distance(from: start, to: end) - } - - public func index(_ i: Index, offsetBy n: Int) -> Index { - return base.index(i, offsetBy: n) - } -% end - -% end # if not StrideableIndex - - public let timesStartIndexCalled = ResettableValue(0) - - public var startIndex: ${Index} { - timesStartIndexCalled.value += 1 - return base.startIndex - } - - public let timesEndIndexCalled = ResettableValue(0) - - public var endIndex: ${Index} { - timesEndIndexCalled.value += 1 - return base.endIndex - } - - public subscript(i: ${Index}) -> Element { - get { - return base[i] - } -% if Mutable: - set { - base[i] = newValue - } -% end - } - - // FIXME: swift-3-indexing-model: use defaults. -// if Self not in ['DefaultedCollection', 'DefaultedBidirectionalCollection', 'DefaultedRandomAccessCollection', 'DefaultedMutableCollection', 'DefaultedRangeReplaceableCollection']: - - public subscript(bounds: Range<${Index}>) -> Slice<${Self}> { - get { - // FIXME: swift-3-indexing-model: range check. - return Slice(base: self, bounds: bounds) - } -% if Mutable: - set { - _writeBackMutableSlice(&self, bounds: bounds, slice: newValue) - } -% end - } - -% if RangeReplaceable: - public init() { - base = Base() - } - - public mutating func replaceSubrange( - _ subRange: Range<${Self}.Index>, - with newElements: C - ) where C : Collection, C.Iterator.Element == Element { - base.replaceSubrange(subRange, with: newElements) - } -% end -} - -/* -FIXME: swift-3-indexing-model: uncomment this. -public struct Defaulted${Traversal}RangeReplaceableSlice - : RangeReplaceableCollection { - - public typealias Self_ = Defaulted${Traversal}RangeReplaceableSlice - public typealias Base = ${Base} - public typealias Iterator = MinimalIterator - public typealias Index = ${Index} - - public var base: Base - public var startIndex: Index - public var endIndex: Index - - public init() { - expectSliceType(Self_.self) - - self.base = Base() - self.startIndex = base.startIndex - self.endIndex = base.endIndex - } - - public init(base: Base) { - self.base = base - self.startIndex = base.startIndex - self.endIndex = base.endIndex - } - - public init(base: Base, bounds: Range) { - self.base = base - self.startIndex = bounds.lowerBound - self.endIndex = bounds.upperBound - } - - public init(_ array: [Element]) { - self = Defaulted${Traversal}RangeReplaceableSlice( - base: Base(elements: array)) - } - - public init(elements: [Element]) { - self = Defaulted${Traversal}RangeReplaceableSlice( - base: Base(elements: elements)) - } - - public func makeIterator() -> MinimalIterator { - return MinimalIterator(Array(self)) - } - - public subscript(index: Index) -> Element { - Index._failEarlyRangeCheck(index, bounds: startIndex..) -> Self_ { - Index._failEarlyRangeCheck2( - rangeStart: bounds.lowerBound, - rangeEnd: bounds.upperBound, - boundsStart: startIndex, - boundsEnd: endIndex) - return Defaulted${Traversal}RangeReplaceableSlice( - base: base, bounds: bounds) - } - - public mutating func replaceSubrange< - C : Collection - >( - _ subRange: Range, - with newElements: C - ) where C.Iterator.Element == Element { - let startOffset = startIndex.position - let endOffset = - endIndex.position - - subRange.count - + numericCast(newElements.count) as Int - Index._failEarlyRangeCheck2( - rangeStart: subRange.lowerBound, - rangeEnd: subRange.upperBound, - boundsStart: startIndex, - boundsEnd: endIndex) - base.replaceSubrange(subRange, with: newElements) - startIndex = base.startIndex.advanced(by: startOffset) - endIndex = base.startIndex.advanced(by: endOffset) - } -} -*/ - -% end -% end -% end -% end -% end - -// ${'Local Variables'}: -// eval: (read-only-mode 1) -// End: diff --git a/stdlib/private/StdlibUnicodeUnittest/CMakeLists.txt b/stdlib/private/StdlibUnicodeUnittest/CMakeLists.txt index 313036642c218..b371dde0f2b92 100644 --- a/stdlib/private/StdlibUnicodeUnittest/CMakeLists.txt +++ b/stdlib/private/StdlibUnicodeUnittest/CMakeLists.txt @@ -9,6 +9,10 @@ add_swift_library(swiftStdlibUnicodeUnittest ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} Collation.swift SWIFT_MODULE_DEPENDS StdlibUnittest + SWIFT_MODULE_DEPENDS_LINUX Glibc + SWIFT_MODULE_DEPENDS_FREEBSD Glibc + SWIFT_MODULE_DEPENDS_CYGWIN Glibc + SWIFT_MODULE_DEPENDS_HAIKU Glibc SWIFT_COMPILE_FLAGS ${swift_stdlib_unittest_compile_flags} TARGET_SDKS ALL_POSIX_PLATFORMS INSTALL_IN_COMPONENT stdlib-experimental) diff --git a/stdlib/private/StdlibUnittest/CMakeLists.txt b/stdlib/private/StdlibUnittest/CMakeLists.txt index 5729631d4610e..699bc5bf26f26 100644 --- a/stdlib/private/StdlibUnittest/CMakeLists.txt +++ b/stdlib/private/StdlibUnittest/CMakeLists.txt @@ -15,20 +15,20 @@ endif() add_swift_library(swiftStdlibUnittest ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_STDLIB # This file should be listed the first. Module name is inferred from the # filename. - StdlibUnittest.swift.gyb + StdlibUnittest.swift - CheckStrideable.swift.gyb + CheckStrideable.swift InspectValue.cpp InspectValue.swift InterceptTraps.cpp LifetimeTracked.swift - MinimalTypes.swift.gyb + MinimalTypes.swift OpaqueIdentityFunctions.cpp OpaqueIdentityFunctions.swift RaceTest.swift Statistics.swift StdlibCoreExtras.swift - StringConvertible.swift.gyb + StringConvertible.swift TestHelpers.swift TypeIndexed.swift GetOSVersion.mm diff --git a/stdlib/private/StdlibUnittest/CheckStrideable.swift.gyb b/stdlib/private/StdlibUnittest/CheckStrideable.swift similarity index 85% rename from stdlib/private/StdlibUnittest/CheckStrideable.swift.gyb rename to stdlib/private/StdlibUnittest/CheckStrideable.swift index 0f4b7ffa9ae04..2c15e21a1cc64 100644 --- a/stdlib/private/StdlibUnittest/CheckStrideable.swift.gyb +++ b/stdlib/private/StdlibUnittest/CheckStrideable.swift @@ -10,15 +10,15 @@ // //===----------------------------------------------------------------------===// -%{ -from gyb_stdlib_unittest_support import TRACE, stackTrace, trace -}% - public func checkStrideable( instances: [S], distances: [S.Stride], distanceOracle: (Int, Int) -> S.Stride, - ${TRACE} + + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line ) { for i in instances.indices { let first = instances[i] diff --git a/stdlib/private/StdlibUnittest/MinimalTypes.swift b/stdlib/private/StdlibUnittest/MinimalTypes.swift new file mode 100644 index 0000000000000..0fe7976fc59ce --- /dev/null +++ b/stdlib/private/StdlibUnittest/MinimalTypes.swift @@ -0,0 +1,388 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +/// A type that does not conform to any protocols. +/// +/// This type can be used to check that generic functions don't rely on any +/// conformances. +public struct OpaqueValue { + public var value: Underlying + public var identity: Int + + public init(_ value: Underlying) { + self.value = value + self.identity = 0 + } + + public init(_ value: Underlying, identity: Int) { + self.value = value + self.identity = identity + } +} + +/// A type that conforms only to `Equatable`. +/// +/// This type can be used to check that generic functions don't rely on any +/// other conformances. +public struct MinimalEquatableValue : Equatable { + public static var timesEqualEqualWasCalled: Int = 0 + + public static var equalImpl = + ResettableValue<(Int, Int) -> Bool>({ $0 == $1 }) + + public var value: Int + public var identity: Int + + public init(_ value: Int) { + self.value = value + self.identity = 0 + } + + public init(_ value: Int, identity: Int) { + self.value = value + self.identity = identity + } + + public static func == ( + lhs: MinimalEquatableValue, + rhs: MinimalEquatableValue + ) -> Bool { + MinimalEquatableValue.timesEqualEqualWasCalled += 1 + return MinimalEquatableValue.equalImpl.value(lhs.value, rhs.value) + } +} + +/// A type that conforms only to `Equatable` and `Comparable`. +/// +/// This type can be used to check that generic functions don't rely on any +/// other conformances. +public struct MinimalComparableValue : Equatable, Comparable { + public static var timesEqualEqualWasCalled = ResettableValue(0) + public static var timesLessWasCalled = ResettableValue(0) + + public static var equalImpl = + ResettableValue<(Int, Int) -> Bool>({ $0 == $1 }) + public static var lessImpl = + ResettableValue<(Int, Int) -> Bool>({ $0 < $1 }) + + public var value: Int + public var identity: Int + + public init(_ value: Int) { + self.value = value + self.identity = 0 + } + + public init(_ value: Int, identity: Int) { + self.value = value + self.identity = identity + } + + public static func == ( + lhs: MinimalComparableValue, + rhs: MinimalComparableValue + ) -> Bool { + MinimalComparableValue.timesEqualEqualWasCalled.value += 1 + return MinimalComparableValue.equalImpl.value(lhs.value, rhs.value) + } + + public static func < ( + lhs: MinimalComparableValue, + rhs: MinimalComparableValue + ) -> Bool { + MinimalComparableValue.timesLessWasCalled.value += 1 + return MinimalComparableValue.lessImpl.value(lhs.value, rhs.value) + } +} + + +/// A type that conforms only to `Equatable` and `Hashable`. +/// +/// This type can be used to check that generic functions don't rely on any +/// other conformances. +public struct MinimalHashableValue : Equatable, Hashable { + public static var timesEqualEqualWasCalled: Int = 0 + public static var timesHashIntoWasCalled: Int = 0 + + public static var equalImpl = + ResettableValue<(Int, Int) -> Bool>({ $0 == $1 }) + public static var hashIntoImpl = + ResettableValue<(Int, inout Hasher) -> Void>({ $1.combine($0) }) + + public var value: Int + public var identity: Int + + public init(_ value: Int) { + self.value = value + self.identity = 0 + } + + public init(_ value: Int, identity: Int) { + self.value = value + self.identity = identity + } + + public static func ==( + lhs: MinimalHashableValue, + rhs: MinimalHashableValue + ) -> Bool { + MinimalHashableValue.timesEqualEqualWasCalled += 1 + return MinimalHashableValue.equalImpl.value(lhs.value, rhs.value) + } + + public var hashValue: Int { + var hasher = Hasher() + hasher.combine(self) + return hasher.finalize() + } + + public func hash(into hasher: inout Hasher) { + MinimalHashableValue.timesHashIntoWasCalled += 1 + MinimalHashableValue.hashIntoImpl.value(value, &hasher) + } +} + +extension MinimalHashableValue: CustomStringConvertible { + public var description: String { + return "MinimalHashableValue(value: \(value), identity: \(identity))" + } +} + + +/// A type that conforms only to `Equatable` and `Hashable`. +/// +/// This type can be used to check that generic functions don't rely on any +/// other conformances. +public class MinimalHashableClass : Equatable, Hashable { + public static var timesEqualEqualWasCalled: Int = 0 + public static var timesHashIntoWasCalled: Int = 0 + + public static var equalImpl = + ResettableValue<(Int, Int) -> Bool>({ $0 == $1 }) + public static var hashIntoImpl = + ResettableValue<(Int, inout Hasher) -> Void>({ $1.combine($0) }) + + public var value: Int + public var identity: Int + + public init(_ value: Int) { + self.value = value + self.identity = 0 + } + + public init(_ value: Int, identity: Int) { + self.value = value + self.identity = identity + } + + public static func == ( + lhs: MinimalHashableClass, + rhs: MinimalHashableClass + ) -> Bool { + MinimalHashableClass.timesEqualEqualWasCalled += 1 + return MinimalHashableClass.equalImpl.value(lhs.value, rhs.value) + } + + public var hashValue: Int { + var hasher = Hasher() + hasher.combine(self) + return hasher.finalize() + } + + public func hash(into hasher: inout Hasher) { + MinimalHashableClass.timesHashIntoWasCalled += 1 + MinimalHashableClass.hashIntoImpl.value(value, &hasher) + } +} + +extension MinimalHashableClass: CustomStringConvertible { + public var description: String { + return "MinimalHashableClass(value: \(value), identity: \(identity))" + } +} + + + +public var GenericMinimalHashableValue_timesEqualEqualWasCalled: Int = 0 +public var GenericMinimalHashableValue_timesHashIntoWasCalled: Int = 0 + +public var GenericMinimalHashableValue_equalImpl = + ResettableValue<(Any, Any) -> Bool>({ _, _ in + fatalError("GenericMinimalHashableValue_equalImpl is not set yet") + }) +public var GenericMinimalHashableValue_hashIntoImpl = + ResettableValue<(Any, inout Hasher) -> Void>({ _ in + fatalError("GenericMinimalHashableValue_hashIntoImpl is not set yet") + }) + +/// A type that conforms only to `Equatable` and `Hashable`. +/// +/// This type can be used to check that generic functions don't rely on any +/// other conformances. +public struct GenericMinimalHashableValue : Equatable, Hashable { + public var value: Wrapped + public var identity: Int + + public init(_ value: Wrapped) { + self.value = value + self.identity = 0 + } + + public init(_ value: Wrapped, identity: Int) { + self.value = value + self.identity = identity + } + + public static func == ( + lhs: GenericMinimalHashableValue, + rhs: GenericMinimalHashableValue + ) -> Bool { + GenericMinimalHashableValue_timesEqualEqualWasCalled += 1 + return GenericMinimalHashableValue_equalImpl.value(lhs.value, rhs.value) + } + + public var hashValue: Int { + var hasher = Hasher() + hasher.combine(self) + return hasher.finalize() + } + + public func hash(into hasher: inout Hasher) { + GenericMinimalHashableValue_timesHashIntoWasCalled += 1 + GenericMinimalHashableValue_hashIntoImpl.value(value, &hasher) + } +} + +extension GenericMinimalHashableValue: CustomStringConvertible { + public var description: String { + return "GenericMinimalHashableValue(value: \(value), identity: \(identity))" + } +} + +public var GenericMinimalHashableClass_timesEqualEqualWasCalled: Int = 0 +public var GenericMinimalHashableClass_timesHashIntoWasCalled: Int = 0 + +public var GenericMinimalHashableClass_equalImpl = + ResettableValue<(Any, Any) -> Bool>({ _, _ in + fatalError("GenericMinimalHashableClass_equalImpl is not set yet") + }) +public var GenericMinimalHashableClass_hashIntoImpl = + ResettableValue<(Any, inout Hasher) -> Void>({ _ in + fatalError("GenericMinimalHashableClass_hashIntoImpl is not set yet") + }) + +/// A type that conforms only to `Equatable` and `Hashable`. +/// +/// This type can be used to check that generic functions don't rely on any +/// other conformances. +public class GenericMinimalHashableClass : Equatable, Hashable { + public var value: Wrapped + public var identity: Int + + public init(_ value: Wrapped) { + self.value = value + self.identity = 0 + } + + public init(_ value: Wrapped, identity: Int) { + self.value = value + self.identity = identity + } + + public static func == ( + lhs: GenericMinimalHashableClass, + rhs: GenericMinimalHashableClass + ) -> Bool { + GenericMinimalHashableClass_timesEqualEqualWasCalled += 1 + return GenericMinimalHashableClass_equalImpl.value(lhs.value, rhs.value) + } + + public var hashValue: Int { + var hasher = Hasher() + hasher.combine(self) + return hasher.finalize() + } + + public func hash(into hasher: inout Hasher) { + GenericMinimalHashableClass_timesHashIntoWasCalled += 1 + GenericMinimalHashableClass_hashIntoImpl.value(value, &hasher) + } +} + +extension GenericMinimalHashableClass: CustomStringConvertible { + public var description: String { + return "GenericMinimalHashableClass(value: \(value), identity: \(identity))" + } +} + + +/// A type that conforms only to `Equatable`, `Comparable`, and `Strideable`. +/// +/// This type can be used to check that generic functions don't rely on any +/// other conformances. +public struct MinimalStrideableValue : Equatable, Comparable, Strideable { + public static var timesEqualEqualWasCalled = ResettableValue(0) + public static var timesLessWasCalled = ResettableValue(0) + public static var timesDistanceWasCalled = ResettableValue(0) + public static var timesAdvancedWasCalled = ResettableValue(0) + + public static var equalImpl = + ResettableValue<(Int, Int) -> Bool>({ $0 == $1 }) + public static var lessImpl = + ResettableValue<(Int, Int) -> Bool>({ $0 < $1 }) + + public var value: Int + public var identity: Int + + public init(_ value: Int) { + self.value = value + self.identity = 0 + } + + public init(_ value: Int, identity: Int) { + self.value = value + self.identity = identity + } + + public typealias Stride = Int + + public static func == ( + lhs: MinimalStrideableValue, + rhs: MinimalStrideableValue + ) -> Bool { + MinimalStrideableValue.timesEqualEqualWasCalled.value += 1 + return MinimalStrideableValue.equalImpl.value(lhs.value, rhs.value) + } + + public static func < ( + lhs: MinimalStrideableValue, + rhs: MinimalStrideableValue + ) -> Bool { + MinimalStrideableValue.timesLessWasCalled.value += 1 + return MinimalStrideableValue.lessImpl.value(lhs.value, rhs.value) + } + + public func distance(to other: MinimalStrideableValue) -> Stride { + MinimalStrideableValue.timesDistanceWasCalled.value += 1 + return other.value - self.value + } + + public func advanced(by n: Stride) -> MinimalStrideableValue { + MinimalStrideableValue.timesAdvancedWasCalled.value += 1 + return MinimalStrideableValue(self.value + n, identity: self.identity) + } +} + + +// Local Variables: +// eval: (read-only-mode 1) +// End: diff --git a/stdlib/private/StdlibUnittest/MinimalTypes.swift.gyb b/stdlib/private/StdlibUnittest/MinimalTypes.swift.gyb deleted file mode 100644 index 15bb10602e53d..0000000000000 --- a/stdlib/private/StdlibUnittest/MinimalTypes.swift.gyb +++ /dev/null @@ -1,255 +0,0 @@ -//===--- MinimalTypes.swift -----------------------------------*- swift -*-===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -/// A type that does not conform to any protocols. -/// -/// This type can be used to check that generic functions don't rely on any -/// conformances. -public struct OpaqueValue { - public var value: Underlying - public var identity: Int - - public init(_ value: Underlying) { - self.value = value - self.identity = 0 - } - - public init(_ value: Underlying, identity: Int) { - self.value = value - self.identity = identity - } -} - -/// A type that conforms only to `Equatable`. -/// -/// This type can be used to check that generic functions don't rely on any -/// other conformances. -public struct MinimalEquatableValue : Equatable { - public static var timesEqualEqualWasCalled: Int = 0 - - public static var equalImpl = - ResettableValue<(Int, Int) -> Bool>({ $0 == $1 }) - - public var value: Int - public var identity: Int - - public init(_ value: Int) { - self.value = value - self.identity = 0 - } - - public init(_ value: Int, identity: Int) { - self.value = value - self.identity = identity - } -} -public func == ( - lhs: MinimalEquatableValue, - rhs: MinimalEquatableValue -) -> Bool { - MinimalEquatableValue.timesEqualEqualWasCalled += 1 - return MinimalEquatableValue.equalImpl.value(lhs.value, rhs.value) -} - -/// A type that conforms only to `Equatable` and `Comparable`. -/// -/// This type can be used to check that generic functions don't rely on any -/// other conformances. -public struct MinimalComparableValue : Equatable, Comparable { - public static var timesEqualEqualWasCalled = ResettableValue(0) - public static var timesLessWasCalled = ResettableValue(0) - - public static var equalImpl = - ResettableValue<(Int, Int) -> Bool>({ $0 == $1 }) - public static var lessImpl = - ResettableValue<(Int, Int) -> Bool>({ $0 < $1 }) - - public var value: Int - public var identity: Int - - public init(_ value: Int) { - self.value = value - self.identity = 0 - } - - public init(_ value: Int, identity: Int) { - self.value = value - self.identity = identity - } -} - -public func == ( - lhs: MinimalComparableValue, - rhs: MinimalComparableValue -) -> Bool { - MinimalComparableValue.timesEqualEqualWasCalled.value += 1 - return MinimalComparableValue.equalImpl.value(lhs.value, rhs.value) -} - -public func < ( - lhs: MinimalComparableValue, - rhs: MinimalComparableValue -) -> Bool { - MinimalComparableValue.timesLessWasCalled.value += 1 - return MinimalComparableValue.lessImpl.value(lhs.value, rhs.value) -} - -% for (kind, decl_keyword) in [ ('Value', 'struct'), ('Class', 'class') ]: -% Self = 'MinimalHashable%s' % kind - -/// A type that conforms only to `Equatable` and `Hashable`. -/// -/// This type can be used to check that generic functions don't rely on any -/// other conformances. -public ${decl_keyword} ${Self} : Equatable, Hashable { - public static var timesEqualEqualWasCalled: Int = 0 - public static var timesHashValueWasCalled: Int = 0 - - public static var equalImpl = - ResettableValue<(Int, Int) -> Bool>({ $0 == $1 }) - public static var hashValueImpl = - ResettableValue<(Int) -> Int>({ $0.hashValue }) - - public var value: Int - public var identity: Int - - public init(_ value: Int) { - self.value = value - self.identity = 0 - } - - public init(_ value: Int, identity: Int) { - self.value = value - self.identity = identity - } - - public var hashValue: Int { - ${Self}.timesHashValueWasCalled += 1 - return ${Self}.hashValueImpl.value(value) - } -} - -public func == ( - lhs: ${Self}, - rhs: ${Self} -) -> Bool { - ${Self}.timesEqualEqualWasCalled += 1 - return ${Self}.equalImpl.value(lhs.value, rhs.value) -} - -% end - -% for (kind, decl_keyword) in [ ('Value', 'struct'), ('Class', 'class') ]: -% Self = 'GenericMinimalHashable%s' % kind - -public var ${Self}_timesEqualEqualWasCalled: Int = 0 -public var ${Self}_timesHashValueWasCalled: Int = 0 - -public var ${Self}_equalImpl = ResettableValue<(Any, Any) -> Bool>( - { _, _ in fatalError("${Self}_equalImpl is not set yet"); () }) -public var ${Self}_hashValueImpl = ResettableValue<(Any) -> Int>( - { _ in fatalError("${Self}_hashValueImpl is not set yet"); () }) - -/// A type that conforms only to `Equatable` and `Hashable`. -/// -/// This type can be used to check that generic functions don't rely on any -/// other conformances. -public ${decl_keyword} ${Self} : Equatable, Hashable { - public var value: Wrapped - public var identity: Int - - public init(_ value: Wrapped) { - self.value = value - self.identity = 0 - } - - public init(_ value: Wrapped, identity: Int) { - self.value = value - self.identity = identity - } - - public var hashValue: Int { - ${Self}_timesHashValueWasCalled += 1 - return ${Self}_hashValueImpl.value(value) - } -} - -public func == ( - lhs: ${Self}, - rhs: ${Self} -) -> Bool { - ${Self}_timesEqualEqualWasCalled += 1 - return ${Self}_equalImpl.value(lhs.value, rhs.value) -} - -% end - -/// A type that conforms only to `Equatable`, `Comparable`, and `Strideable`. -/// -/// This type can be used to check that generic functions don't rely on any -/// other conformances. -public struct MinimalStrideableValue : Equatable, Comparable, Strideable { - public static var timesEqualEqualWasCalled = ResettableValue(0) - public static var timesLessWasCalled = ResettableValue(0) - public static var timesDistanceWasCalled = ResettableValue(0) - public static var timesAdvancedWasCalled = ResettableValue(0) - - public static var equalImpl = - ResettableValue<(Int, Int) -> Bool>({ $0 == $1 }) - public static var lessImpl = - ResettableValue<(Int, Int) -> Bool>({ $0 < $1 }) - - public var value: Int - public var identity: Int - - public init(_ value: Int) { - self.value = value - self.identity = 0 - } - - public init(_ value: Int, identity: Int) { - self.value = value - self.identity = identity - } - - public typealias Stride = Int - - public func distance(to other: MinimalStrideableValue) -> Stride { - MinimalStrideableValue.timesDistanceWasCalled.value += 1 - return other.value - self.value - } - - public func advanced(by n: Stride) -> MinimalStrideableValue { - MinimalStrideableValue.timesAdvancedWasCalled.value += 1 - return MinimalStrideableValue(self.value + n, identity: self.identity) - } -} - -public func == ( - lhs: MinimalStrideableValue, - rhs: MinimalStrideableValue -) -> Bool { - MinimalStrideableValue.timesEqualEqualWasCalled.value += 1 - return MinimalStrideableValue.equalImpl.value(lhs.value, rhs.value) -} - -public func < ( - lhs: MinimalStrideableValue, - rhs: MinimalStrideableValue -) -> Bool { - MinimalStrideableValue.timesLessWasCalled.value += 1 - return MinimalStrideableValue.lessImpl.value(lhs.value, rhs.value) -} - -// ${'Local Variables'}: -// eval: (read-only-mode 1) -// End: diff --git a/stdlib/private/StdlibUnittest/StdlibUnittest.swift.gyb b/stdlib/private/StdlibUnittest/StdlibUnittest.swift similarity index 70% rename from stdlib/private/StdlibUnittest/StdlibUnittest.swift.gyb rename to stdlib/private/StdlibUnittest/StdlibUnittest.swift index 98ac5af04119c..3c3239e76497e 100644 --- a/stdlib/private/StdlibUnittest/StdlibUnittest.swift.gyb +++ b/stdlib/private/StdlibUnittest/StdlibUnittest.swift @@ -1,4 +1,4 @@ -//===--- StdlibUnittest.swift.gyb -----------------------------*- swift -*-===// +//===----------------------------------------------------------------------===// // // This source file is part of the Swift.org open source project // @@ -10,9 +10,6 @@ // //===----------------------------------------------------------------------===// -%{ -from gyb_stdlib_unittest_support import TRACE, stackTrace, trace -}% import SwiftPrivate import SwiftPrivatePthreadExtras @@ -120,7 +117,11 @@ var _seenExpectCrash = false /// Run `body` and expect a failure to happen. /// /// The check passes iff `body` triggers one or more failures. -public func expectFailure(${TRACE}, invoking body: () -> Void) { +public func expectFailure( + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line, invoking body: () -> Void) { let startAnyExpectFailed = _anyExpectFailed _anyExpectFailed = false body() @@ -128,7 +129,7 @@ public func expectFailure(${TRACE}, invoking body: () -> Void) { _anyExpectFailed = false expectTrue( endAnyExpectFailed, "running `body` should produce an expected failure", - stackTrace: ${stackTrace} + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line) ) _anyExpectFailed = _anyExpectFailed || startAnyExpectFailed } @@ -146,49 +147,107 @@ public func identityComp(_ element: MinimalComparableValue) return element } -public func expectEqual(_ expected: T, _ actual: T, ${TRACE}) { - expectEqualTest(expected, actual, ${trace}, showFrame: false) {$0 == $1} +public func expectEqual(_ expected: T, _ actual: T, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line) { + expectEqualTest(expected, actual, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), showFrame: false) {$0 == $1} } public func expectEqual( - _ expected: (T, U), _ actual: (T, U), ${TRACE}) { - expectEqualTest(expected.0, actual.0, ${trace}, showFrame: false) {$0 == $1} - expectEqualTest(expected.1, actual.1, ${trace}, showFrame: false) {$0 == $1} + _ expected: (T, U), _ actual: (T, U), + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line) { + expectEqualTest(expected.0, actual.0, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), showFrame: false) {$0 == $1} + expectEqualTest(expected.1, actual.1, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), showFrame: false) {$0 == $1} } public func expectEqual( - _ expected: (T, U, V), _ actual: (T, U, V), ${TRACE}) { - expectEqualTest(expected.0, actual.0, ${trace}, showFrame: false) {$0 == $1} - expectEqualTest(expected.1, actual.1, ${trace}, showFrame: false) {$0 == $1} - expectEqualTest(expected.2, actual.2, ${trace}, showFrame: false) {$0 == $1} + _ expected: (T, U, V), _ actual: (T, U, V), + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line) { + expectEqualTest(expected.0, actual.0, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), showFrame: false) {$0 == $1} + expectEqualTest(expected.1, actual.1, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), showFrame: false) {$0 == $1} + expectEqualTest(expected.2, actual.2, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), showFrame: false) {$0 == $1} } public func expectEqual( - _ expected: (T, U, V, W), _ actual: (T, U, V, W), ${TRACE}) { - expectEqualTest(expected.0, actual.0, ${trace}, showFrame: false) {$0 == $1} - expectEqualTest(expected.1, actual.1, ${trace}, showFrame: false) {$0 == $1} - expectEqualTest(expected.2, actual.2, ${trace}, showFrame: false) {$0 == $1} - expectEqualTest(expected.3, actual.3, ${trace}, showFrame: false) {$0 == $1} -} - -% for (Lhs, Rhs) in [ -% ('String', 'Substring'), -% ('Substring', 'String'), -% ('String', 'String'), # this one is to break ambiguity -% ]: -public func expectEqual(_ expected: ${Lhs}, _ actual: ${Rhs}, ${TRACE}) { + _ expected: (T, U, V, W), _ actual: (T, U, V, W), + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line) { + expectEqualTest(expected.0, actual.0, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), showFrame: false) {$0 == $1} + expectEqualTest(expected.1, actual.1, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), showFrame: false) {$0 == $1} + expectEqualTest(expected.2, actual.2, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), showFrame: false) {$0 == $1} + expectEqualTest(expected.3, actual.3, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), showFrame: false) {$0 == $1} +} + +public func expectEqual(_ expected: String, _ actual: Substring, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line) { if !(expected == actual) { expectationFailure( "expected: \(String(reflecting: expected)) (of type \(String(reflecting: type(of: expected))))\n" + "actual: \(String(reflecting: actual)) (of type \(String(reflecting: type(of: actual))))", - trace: ${trace} + trace: message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line) + ) + } +} +public func expectEqual(_ expected: Substring, _ actual: String, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line) { + if !(expected == actual) { + expectationFailure( + "expected: \(String(reflecting: expected)) (of type \(String(reflecting: type(of: expected))))\n" + + "actual: \(String(reflecting: actual)) (of type \(String(reflecting: type(of: actual))))", + trace: message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line) + ) + } +} +public func expectEqual(_ expected: String, _ actual: String, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line) { + if !(expected == actual) { + expectationFailure( + "expected: \(String(reflecting: expected)) (of type \(String(reflecting: type(of: expected))))\n" + + "actual: \(String(reflecting: actual)) (of type \(String(reflecting: type(of: actual))))", + trace: message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line) ) } } -% end -public func expectEqualReference(_ expected: AnyObject?, _ actual: AnyObject?, ${TRACE}) { - expectEqualTest(expected, actual, ${trace}, showFrame: false) {$0 === $1} +public func expectEqualReference(_ expected: AnyObject?, _ actual: AnyObject?, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line) { + expectEqualTest(expected, actual, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), showFrame: false) {$0 === $1} } public func expectationFailure( @@ -205,22 +264,32 @@ public func expectationFailure( // See Generic type constraints incorrectly applied to // functions with the same name public func expectEqualTest( - _ expected: T, _ actual: T, ${TRACE}, sameValue equal: (T, T) -> Bool + _ expected: T, _ actual: T, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line, sameValue equal: (T, T) -> Bool ) { if !equal(expected, actual) { expectationFailure( "expected: \(String(reflecting: expected)) (of type \(String(reflecting: type(of: expected))))\n" + "actual: \(String(reflecting: actual)) (of type \(String(reflecting: type(of: actual))))", - trace: ${trace} + trace: message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line) ) } } -public func expectNotEqual(_ expected: T, _ actual: T, ${TRACE}) { +public func expectNotEqual(_ expected: T, _ actual: T, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line) { if expected == actual { expectationFailure( "unexpected value: \"\(actual)\" (of type \(String(reflecting: type(of: actual))))", - trace: ${trace} + trace: message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line) ) } } @@ -228,130 +297,296 @@ public func expectNotEqual(_ expected: T, _ actual: T, ${TRACE}) // Cannot write a sane set of overloads using generics because of: // Array -> NSArray implicit conversion insanity public func expectOptionalEqual( - _ expected: T, _ actual: T?, ${TRACE} + _ expected: T, _ actual: T?, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line ) { - expectOptionalEqual(expected, actual, ${trace}, showFrame: false) {$0 == $1} + expectOptionalEqual(expected, actual, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), showFrame: false) {$0 == $1} } public func expectOptionalEqual( - _ expected: T, _ actual: T?, ${TRACE}, sameValue equal: (T, T) -> Bool + _ expected: T, _ actual: T?, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line, sameValue equal: (T, T) -> Bool ) { if (actual == nil) || !equal(expected, actual!) { expectationFailure( "expected: \"\(expected)\" (of type \(String(reflecting: type(of: expected))))\n" + "actual: \"\(actual.debugDescription)\" (of type \(String(reflecting: type(of: actual))))", - trace: ${trace}) + trace: message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) } } -public func expectEqual(_ expected: T?, _ actual: T?, ${TRACE}) { +public func expectEqual(_ expected: T?, _ actual: T?, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line) { if expected != actual { expectationFailure( "expected: \"\(expected.debugDescription)\" (of type \(String(reflecting: type(of: expected))))\n" + "actual: \"\(actual.debugDescription)\" (of type \(String(reflecting: type(of: actual))))", - trace: ${trace}) + trace: message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) } } public func expectNotEqual( - _ expected: T?, _ actual: T?, ${TRACE} + _ expected: T?, _ actual: T?, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line ) { if expected == actual { expectationFailure( "unexpected value: \"\(actual.debugDescription)\" (of type \(String(reflecting: type(of: actual))))", - trace: ${trace}) + trace: message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) } } // Array is not Equatable if T is. Provide additional overloads. // Same for Dictionary. -%for (Generic, EquatableType) in [ -% ('', 'ContiguousArray'), -% ('', 'ArraySlice'), -% ('', 'Array'), -% ('', 'Dictionary')]: - -public func expectEqual${Generic}( - _ expected: ${EquatableType}, _ actual: ${EquatableType}, ${TRACE} + +public func expectEqual( + _ expected: ContiguousArray, _ actual: ContiguousArray, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line +) { + expectEqualTest(expected, actual, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), showFrame: false) { $0 == $1 } +} + +public func expectOptionalEqual( + _ expected: ContiguousArray, _ actual: ContiguousArray?, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line) { + if (actual == nil) || expected != actual! { + expectationFailure( + "expected: \"\(expected)\" (of type \(String(reflecting: type(of: expected))))" + + "actual: \"\(actual.debugDescription)\" (of type \(String(reflecting: type(of: actual))))", + trace: message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) + } +} + + +public func expectEqual( + _ expected: ArraySlice, _ actual: ArraySlice, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line +) { + expectEqualTest(expected, actual, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), showFrame: false) { $0 == $1 } +} + +public func expectOptionalEqual( + _ expected: ArraySlice, _ actual: ArraySlice?, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line) { + if (actual == nil) || expected != actual! { + expectationFailure( + "expected: \"\(expected)\" (of type \(String(reflecting: type(of: expected))))" + + "actual: \"\(actual.debugDescription)\" (of type \(String(reflecting: type(of: actual))))", + trace: message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) + } +} + + +public func expectEqual( + _ expected: Array, _ actual: Array, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line +) { + expectEqualTest(expected, actual, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), showFrame: false) { $0 == $1 } +} + +public func expectOptionalEqual( + _ expected: Array, _ actual: Array?, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line) { + if (actual == nil) || expected != actual! { + expectationFailure( + "expected: \"\(expected)\" (of type \(String(reflecting: type(of: expected))))" + + "actual: \"\(actual.debugDescription)\" (of type \(String(reflecting: type(of: actual))))", + trace: message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) + } +} + + +public func expectEqual( + _ expected: Dictionary, _ actual: Dictionary, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line ) { - expectEqualTest(expected, actual, ${trace}, showFrame: false) { $0 == $1 } + expectEqualTest(expected, actual, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), showFrame: false) { $0 == $1 } } -public func expectOptionalEqual${Generic}( - _ expected: ${EquatableType}, _ actual: ${EquatableType}?, ${TRACE}) { +public func expectOptionalEqual( + _ expected: Dictionary, _ actual: Dictionary?, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line) { if (actual == nil) || expected != actual! { expectationFailure( "expected: \"\(expected)\" (of type \(String(reflecting: type(of: expected))))" + "actual: \"\(actual.debugDescription)\" (of type \(String(reflecting: type(of: actual))))", - trace: ${trace}) + trace: message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) } } -%end public func expectEqual( - _ expected: Any.Type, _ actual: Any.Type, ${TRACE} + _ expected: Any.Type, _ actual: Any.Type, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line ) { - expectEqualTest(expected, actual, ${trace}, showFrame: false) { $0 == $1 } + expectEqualTest(expected, actual, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), showFrame: false) { $0 == $1 } } -public func expectLT(_ lhs: T, _ rhs: T, ${TRACE}) { +public func expectLT(_ lhs: T, _ rhs: T, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line) { if !(lhs < rhs) { - expectationFailure("\(lhs) < \(rhs)", trace: ${trace}) + expectationFailure("\(lhs) < \(rhs)", trace: message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) } } -public func expectLE(_ lhs: T, _ rhs: T, ${TRACE}) { +public func expectLE(_ lhs: T, _ rhs: T, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line) { if !(lhs <= rhs) { - expectationFailure("\(lhs) <= \(rhs)", trace: ${trace}) + expectationFailure("\(lhs) <= \(rhs)", trace: message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) } } -public func expectGT(_ lhs: T, _ rhs: T, ${TRACE}) { +public func expectGT(_ lhs: T, _ rhs: T, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line) { if !(lhs > rhs) { - expectationFailure("\(lhs) > \(rhs)", trace: ${trace}) + expectationFailure("\(lhs) > \(rhs)", trace: message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) } } -public func expectGE(_ lhs: T, _ rhs: T, ${TRACE}) { +public func expectGE(_ lhs: T, _ rhs: T, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line) { if !(lhs >= rhs) { - expectationFailure("\(lhs) >= \(rhs)", trace: ${trace}) + expectationFailure("\(lhs) >= \(rhs)", trace: message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) } } -% for OtherRange in ['Range', 'ClosedRange']: extension Range { - internal func _contains(_ other: ${OtherRange}) -> Bool { + internal func _contains(_ other: Range) -> Bool { if other.lowerBound < lowerBound { return false } -% if 'Closed' in OtherRange: - if upperBound <= other.upperBound { return false } -% else: if upperBound < other.upperBound { return false } -% end return true } } -% end +extension Range { + internal func _contains(_ other: ClosedRange) -> Bool { + if other.lowerBound < lowerBound { return false } + if upperBound <= other.upperBound { return false } + return true + } +} -% for Range in ['Range', 'ClosedRange']: public func expectTrapping( - _ point: Bound, in range: ${Range}, ${TRACE} + _ point: Bound, in range: Range, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line ) { if !range.contains(point) { - expectationFailure("\(point) in \(range)", trace: ${trace}) + expectationFailure("\(point) in \(range)", trace: message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) _trappingExpectationFailedCallback() } } public func expectTrapping( - _ subRange: ${Range}, in range: Range, ${TRACE} + _ subRange: Range, in range: Range, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line ) { if !range._contains(subRange) { - expectationFailure("\(subRange) in \(range)", trace: ${trace}) + expectationFailure("\(subRange) in \(range)", trace: message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) + _trappingExpectationFailedCallback() + } +} +public func expectTrapping( + _ point: Bound, in range: ClosedRange, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line +) { + if !range.contains(point) { + expectationFailure("\(point) in \(range)", trace: message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) + _trappingExpectationFailedCallback() + } +} + +public func expectTrapping( + _ subRange: ClosedRange, in range: Range, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line +) { + if !range._contains(subRange) { + expectationFailure("\(subRange) in \(range)", trace: message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) _trappingExpectationFailedCallback() } } -% end extension ClosedRange { internal func _contains(_ other: ClosedRange) -> Bool { @@ -362,10 +597,15 @@ extension ClosedRange { } public func expectTrapping( - _ subRange: ClosedRange, in range: ClosedRange, ${TRACE} + _ subRange: ClosedRange, in range: ClosedRange, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line ) { if !range._contains(subRange) { - expectationFailure("\(subRange) in \(range)", trace: ${trace}) + expectationFailure("\(subRange) in \(range)", trace: message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) _trappingExpectationFailedCallback() } } @@ -377,11 +617,12 @@ public func expectSequenceType(_ x: X) -> X { return x } -% for Mutable in ['', 'Mutable']: -public func expect${Mutable}CollectionType( +public func expectCollectionType( + _ x: X.Type +) {} +public func expectMutableCollectionType( _ x: X.Type ) {} -% end /// A slice is a `Collection` that when sliced returns an instance of /// itself. @@ -457,43 +698,77 @@ public func assertionFailure() -> AssertionResult { return AssertionResult(isPass: false) } -public func expectUnreachable(${TRACE}) { - expectationFailure("this code should not be executed", trace: ${trace}) +public func expectUnreachable( + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line) { + expectationFailure("this code should not be executed", trace: message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) } -public func expectUnreachableCatch(_ error: Error, ${TRACE}) { +public func expectUnreachableCatch(_ error: Error, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line) { expectationFailure( - "error should not be thrown: \"\(error)\"", trace: ${trace}) + "error should not be thrown: \"\(error)\"", trace: message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) } -public func expectTrue(_ actual: AssertionResult, ${TRACE}) { +public func expectTrue(_ actual: AssertionResult, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line) { if !actual._isPass { expectationFailure( - "expected: passed assertion\n\(actual.description)", trace: ${trace}) + "expected: passed assertion\n\(actual.description)", trace: message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) } } -public func expectFalse(_ actual: AssertionResult, ${TRACE}) { +public func expectFalse(_ actual: AssertionResult, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line) { if actual._isPass { expectationFailure( - "expected: failed assertion\n\(actual.description)", trace: ${trace}) + "expected: failed assertion\n\(actual.description)", trace: message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) } } -public func expectTrue(_ actual: Bool, ${TRACE}) { +public func expectTrue(_ actual: Bool, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line) { if !actual { - expectationFailure("expected: true", trace: ${trace}) + expectationFailure("expected: true", trace: message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) } } -public func expectFalse(_ actual: Bool, ${TRACE}) { +public func expectFalse(_ actual: Bool, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line) { if actual { - expectationFailure("expected: false", trace: ${trace}) + expectationFailure("expected: false", trace: message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) } } public func expectThrows( - _ expectedError: ErrorType? = nil, _ test: () throws -> Void, ${TRACE}) { + _ expectedError: ErrorType? = nil, _ test: () throws -> Void, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line) { do { try test() } catch let error as ErrorType { @@ -501,29 +776,45 @@ public func expectThrows( expectEqual(expectedError, error) } } catch { - expectationFailure("unexpected error thrown: \"\(error)\"", trace: ${trace}) + expectationFailure("unexpected error thrown: \"\(error)\"", trace: message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) } } -public func expectDoesNotThrow(_ test: () throws -> Void, ${TRACE}) { +public func expectDoesNotThrow(_ test: () throws -> Void, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line) { do { try test() } catch { - expectationFailure("unexpected error thrown: \"\(error)\"", trace: ${trace}) + expectationFailure("unexpected error thrown: \"\(error)\"", trace: message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) } } -public func expectNil(_ value: T?, ${TRACE}) { +public func expectNil(_ value: T?, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line) { if value != nil { expectationFailure( - "expected optional to be nil\nactual: \"\(value!)\"", trace: ${trace}) + "expected optional to be nil\nactual: \"\(value!)\"", trace: message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) } } @discardableResult -public func expectNotNil(_ value: T?, ${TRACE}) -> T? { +public func expectNotNil(_ value: T?, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line) -> T? { if value == nil { - expectationFailure("expected optional to be non-nil", trace: ${trace}) + expectationFailure("expected optional to be non-nil", trace: message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) } return value } @@ -1589,7 +1880,7 @@ public enum TestRunPredicate : CustomStringConvertible { case windowsAny(reason: String) case windowsCygnusAny(reason: String) - + case haikuAny(reason: String) case objCRuntime(/*reason:*/ String) @@ -1683,7 +1974,7 @@ public enum TestRunPredicate : CustomStringConvertible { case .windowsCygnusAny(reason: let reason): return "windowsCygnusAny(*, reason: \(reason))" - + case .haikuAny(reason: let reason): return "haikuAny(*, reason: \(reason))" @@ -1969,7 +2260,7 @@ public enum TestRunPredicate : CustomStringConvertible { default: return false } - + case .haikuAny: switch _getRunningOSVersion() { case .haiku: @@ -2009,7 +2300,11 @@ public func checkEquatable( _ instances: Instances, oracle: (Instances.Index, Instances.Index) -> Bool, allowBrokenTransitivity: Bool = false, - ${TRACE} + + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line ) where Instances.Iterator.Element : Equatable { @@ -2018,14 +2313,19 @@ public func checkEquatable( Array(instances), oracle: { oracle(indices[$0], indices[$1]) }, allowBrokenTransitivity: allowBrokenTransitivity, - ${trace}) + message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) } internal func _checkEquatableImpl( _ instances: [Instance], oracle: (Int, Int) -> Bool, allowBrokenTransitivity: Bool = false, - ${TRACE} + + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line ) { // For each index (which corresponds to an instance being tested) track the // set of equal instances. @@ -2044,7 +2344,7 @@ internal func _checkEquatableImpl( expectEqual( predictedXY, oracle(j, i), "bad oracle: broken symmetry between indices \(i), \(j)", - stackTrace: ${stackTrace}) + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) let isEqualXY = x == y expectEqual( @@ -2054,13 +2354,13 @@ internal func _checkEquatableImpl( : "expected not equal, found equal\n") + "lhs (at index \(i)): \(String(reflecting: x))\n" + "rhs (at index \(j)): \(String(reflecting: y))", - stackTrace: ${stackTrace}) + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) // Not-equal is an inverse of equal. expectNotEqual( isEqualXY, x != y, "lhs (at index \(i)): \(String(reflecting: x))\nrhs (at index \(j)): \(String(reflecting: y))", - stackTrace: ${stackTrace}) + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) if !allowBrokenTransitivity { // Check transitivity of the predicate represented by the oracle. @@ -2074,7 +2374,7 @@ internal func _checkEquatableImpl( expectTrue( oracle(j, k), "bad oracle: broken transitivity at indices \(i), \(j), \(k)", - stackTrace: ${stackTrace}) + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) // No need to check equality between actual values, we will check // them with the checks above. } @@ -2088,49 +2388,153 @@ internal func _checkEquatableImpl( public func checkEquatable( - _ expectedEqual: Bool, _ lhs: T, _ rhs: T, ${TRACE} + _ expectedEqual: Bool, _ lhs: T, _ rhs: T, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line ) { checkEquatable( [lhs, rhs], - oracle: { expectedEqual || $0 == $1 }, ${trace}, showFrame: false) + oracle: { expectedEqual || $0 == $1 }, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), + showFrame: false) +} + +internal func hash(_ value: H, seed: Int? = nil) -> Int { + var hasher = Hasher() + if let seed = seed { + hasher.combine(seed) + } + hasher.combine(value) + return hasher.finalize() +} + +/// Test that the elements of `instances` satisfy the semantic requirements of +/// `Hashable`, using `equalityOracle` to generate equality and hashing +/// expectations from pairs of positions in `instances`. +public func checkHashable( + _ instances: Instances, + equalityOracle: (Instances.Index, Instances.Index) -> Bool, + allowBrokenTransitivity: Bool = false, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line +) where Instances.Iterator.Element: Hashable { + checkHashable( + instances, + equalityOracle: equalityOracle, + hashEqualityOracle: equalityOracle, + allowBrokenTransitivity: allowBrokenTransitivity, + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), + showFrame: false) } /// Test that the elements of `instances` satisfy the semantic /// requirements of `Hashable`, using `equalityOracle` to generate -/// equality expectations from pairs of positions in `instances`. -public func checkHashable( +/// equality expectations from pairs of positions in `instances`, +/// and `hashEqualityOracle` to do the same for hashing. +public func checkHashable( _ instances: Instances, equalityOracle: (Instances.Index, Instances.Index) -> Bool, + hashEqualityOracle: (Instances.Index, Instances.Index) -> Bool, allowBrokenTransitivity: Bool = false, - ${TRACE} + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line ) where - Instances.Iterator.Element : Hashable { - + Instances.Iterator.Element: Hashable { checkEquatable( instances, oracle: equalityOracle, allowBrokenTransitivity: allowBrokenTransitivity, - ${trace}) + message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) for i in instances.indices { let x = instances[i] for j in instances.indices { let y = instances[j] + let predicted = hashEqualityOracle(i, j) + expectEqual( + predicted, hashEqualityOracle(j, i), + "bad hash oracle: broken symmetry between indices \(i), \(j)", + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) if x == y { + expectTrue( + predicted, + """ + bad hash oracle: equality must imply hash equality + lhs (at index \(i)): \(x) + rhs (at index \(j)): \(y) + """, + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) + } + if predicted { + expectEqual( + hash(x), hash(y), + """ + hash(into:) expected to match, found to differ + lhs (at index \(i)): \(x) + rhs (at index \(j)): \(y) + """, + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) expectEqual( x.hashValue, y.hashValue, - "lhs (at index \(i)): \(x)\nrhs (at index \(j)): \(y)", - stackTrace: ${stackTrace}) + """ + hashValue expected to match, found to differ + lhs (at index \(i)): \(x) + rhs (at index \(j)): \(y) + """, + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) + expectEqual( + x._rawHashValue(seed: (0, 0)), y._rawHashValue(seed: (0, 0)), + """ + _rawHashValue expected to match, found to differ + lhs (at index \(i)): \(x) + rhs (at index \(j)): \(y) + """, + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) + } else { + // Try a few different seeds; at least one of them should discriminate + // between the hashes. It is extremely unlikely this check will fail + // all ten attempts, unless the type's hash encoding is not unique, + // or unless the hash equality oracle is wrong. + expectTrue( + (0..<10).contains { hash(x, seed: $0) != hash(y, seed: $0) }, + """ + hash(into:) expected to differ, found to match + lhs (at index \(i)): \(x) + rhs (at index \(j)): \(y) + """, + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) + expectTrue( + (0..<10 as Range).contains { i in + x._rawHashValue(seed: (0, i)) != y._rawHashValue(seed: (0, i)) + }, + """ + _rawHashValue(seed:) expected to differ, found to match + lhs (at index \(i)): \(x) + rhs (at index \(j)): \(y) + """, + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) } } } } public func checkHashable( - expectedEqual: Bool, _ lhs: T, _ rhs: T, ${TRACE} + expectedEqual: Bool, _ lhs: T, _ rhs: T, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line ) { checkHashable( - [lhs, rhs], equalityOracle: { expectedEqual || $0 == $1 }, ${trace}) + [lhs, rhs], equalityOracle: { expectedEqual || $0 == $1 }, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) } public enum ExpectedComparisonResult { @@ -2194,13 +2598,18 @@ extension ExpectedComparisonResult : CustomStringConvertible { public func checkComparable( _ instances: Instances, oracle: (Instances.Index, Instances.Index) -> ExpectedComparisonResult, - ${TRACE} + + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line ) where Instances.Iterator.Element : Comparable { // Also checks that equality is consistent with comparison and that // the oracle obeys the equality laws - checkEquatable(instances, oracle: { oracle($0, $1).isEQ() }, ${trace}) + checkEquatable(instances, oracle: { oracle($0, $1).isEQ() }, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) for i in instances.indices { let x = instances[i] @@ -2209,23 +2618,23 @@ public func checkComparable( x < x, "found 'x < x'\n" + "at index \(i): \(String(reflecting: x))", - stackTrace: ${stackTrace}) + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) expectFalse( x > x, "found 'x > x'\n" + "at index \(i): \(String(reflecting: x))", - stackTrace: ${stackTrace}) + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) expectTrue(x <= x, "found 'x <= x' to be false\n" + "at index \(i): \(String(reflecting: x))", - stackTrace: ${stackTrace}) + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) expectTrue(x >= x, "found 'x >= x' to be false\n" + "at index \(i): \(String(reflecting: x))", - stackTrace: ${stackTrace}) + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) for j in instances.indices where i != j { let y = instances[j] @@ -2236,31 +2645,31 @@ public func checkComparable( expected.flip(), oracle(j, i), "bad oracle: missing antisymmetry: " + "(\(String(reflecting: i)), \(String(reflecting: j)))", - stackTrace: ${stackTrace}) + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) expectEqual(expected.isLT(), x < y, "x < y\n" + "lhs (at index \(i)): \(String(reflecting: x))\n" + "rhs (at index \(j)): \(String(reflecting: y))", - stackTrace: ${stackTrace}) + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) expectEqual(expected.isLE(), x <= y, "x <= y\n" + "lhs (at index \(i)): \(String(reflecting: x))\n" + "rhs (at index \(j)): \(String(reflecting: y))", - stackTrace: ${stackTrace}) + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) expectEqual(expected.isGE(), x >= y, "x >= y\n" + "lhs (at index \(i)): \(String(reflecting: x))\n" + "rhs (at index \(j)): \(String(reflecting: y))", - stackTrace: ${stackTrace}) + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) expectEqual(expected.isGT(), x > y, "x > y\n" + "lhs (at index \(i)): \(String(reflecting: x))\n" + "rhs (at index \(j)): \(String(reflecting: y))", - stackTrace: ${stackTrace}) + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) for k in instances.indices { let expected2 = oracle(j, k) @@ -2269,7 +2678,7 @@ public func checkComparable( expected, oracle(i, k), "bad oracle: missing transitivity " + "(\(String(reflecting: i)), \(String(reflecting: j)), " - + "\(String(reflecting: k)))", stackTrace: ${stackTrace}) + + "\(String(reflecting: k)))", stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) } } } @@ -2277,12 +2686,17 @@ public func checkComparable( } public func checkComparable( - _ expected: ExpectedComparisonResult, _ lhs: T, _ rhs: T, ${TRACE} + _ expected: ExpectedComparisonResult, _ lhs: T, _ rhs: T, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line ) { checkComparable( [lhs, rhs], oracle: { [[ .eq, expected], [ expected.flip(), .eq]][$0][$1] }, - ${trace}) + message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) } @@ -2300,7 +2714,11 @@ public func checkStrideable( (Instances.Index, Instances.Index) -> Strides.Iterator.Element, advanceOracle: (Instances.Index, Strides.Index) -> Instances.Iterator.Element, - ${TRACE} + + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line ) where Instances.Iterator.Element : Strideable, Instances.Iterator.Element.Stride == Strides.Iterator.Element { @@ -2311,7 +2729,8 @@ public func checkStrideable( let d = distanceOracle($1, $0); return d < 0 ? .lt : d == 0 ? .eq : .gt }, - ${trace}) + message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) for i in instances.indices { let x = instances[i] @@ -2347,12 +2766,17 @@ public func expectEqualSequence< Expected: Sequence, Actual: Sequence >( - _ expected: Expected, _ actual: Actual, ${TRACE} + _ expected: Expected, _ actual: Actual, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line ) where Expected.Iterator.Element == Actual.Iterator.Element, Expected.Iterator.Element : Equatable { - expectEqualSequence(expected, actual, ${trace}) { $0 == $1 } + expectEqualSequence(expected, actual, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) { $0 == $1 } } public func expectEqualSequence< @@ -2361,13 +2785,18 @@ public func expectEqualSequence< T : Equatable, U : Equatable >( - _ expected: Expected, _ actual: Actual, ${TRACE} + _ expected: Expected, _ actual: Actual, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line ) where Expected.Iterator.Element == Actual.Iterator.Element, Expected.Iterator.Element == (T, U) { expectEqualSequence( - expected, actual, ${trace}) { + expected, actual, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) { (lhs: (T, U), rhs: (T, U)) -> Bool in lhs.0 == rhs.0 && lhs.1 == rhs.1 } @@ -2377,7 +2806,11 @@ public func expectEqualSequence< Expected: Sequence, Actual: Sequence >( - _ expected: Expected, _ actual: Actual, ${TRACE}, + _ expected: Expected, _ actual: Actual, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line, sameValue: (Expected.Iterator.Element, Expected.Iterator.Element) -> Bool ) where Expected.Iterator.Element == Actual.Iterator.Element { @@ -2385,7 +2818,8 @@ public func expectEqualSequence< if !expected.elementsEqual(actual, by: sameValue) { expectationFailure("expected elements: \"\(expected)\"\n" + "actual: \"\(actual)\" (of type \(String(reflecting: type(of: actual))))", - trace: ${trace}) + trace: message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) } } @@ -2393,7 +2827,11 @@ public func expectEqualsUnordered< Expected : Sequence, Actual : Sequence >( - _ expected: Expected, _ actual: Actual, ${TRACE}, + _ expected: Expected, _ actual: Actual, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line, compare: @escaping (Expected.Iterator.Element, Expected.Iterator.Element) -> ExpectedComparisonResult ) where @@ -2404,44 +2842,61 @@ public func expectEqualsUnordered< let y: [Actual.Iterator.Element] = actual.sorted { compare($0, $1).isLT() } expectEqualSequence( - x, y, ${trace}, sameValue: { compare($0, $1).isEQ() }) + x, y, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), sameValue: { compare($0, $1).isEQ() }) } public func expectEqualsUnordered< Expected : Sequence, Actual : Sequence >( - _ expected: Expected, _ actual: Actual, ${TRACE} + _ expected: Expected, _ actual: Actual, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line ) where Expected.Iterator.Element == Actual.Iterator.Element, Expected.Iterator.Element : Comparable { - expectEqualsUnordered(expected, actual, ${trace}) { + expectEqualsUnordered(expected, actual, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) { $0 < $1 ? .lt : $0 == $1 ? .eq : .gt } } public func expectEqualsUnordered( - _ expected: [T], _ actual: [T], ${TRACE} + _ expected: [T], _ actual: [T], + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line ) { let x = expected.sorted() let y = actual.sorted() - expectEqualSequence(x, y, ${trace}) + expectEqualSequence(x, y, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) } public func expectEqualsUnordered( - _ expected: Range, _ actual: [T], ${TRACE} + _ expected: Range, _ actual: [T], + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line ) where T.Stride: SignedInteger { if expected.count != actual.count { expectationFailure("expected elements: \"\(expected)\"\n" + "actual: \"\(actual)\" (of type \(String(reflecting: type(of: actual))))", - trace: ${trace}) + trace: message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) } for e in actual { if !expected.contains(e) { expectationFailure("expected elements: \"\(expected)\"\n" + "actual: \"\(actual)\" (of type \(String(reflecting: type(of: actual))))", - trace: ${trace}) + trace: message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) } } } @@ -2474,7 +2929,11 @@ public func expectEqualsUnordered< Actual : Sequence, T : Comparable >( - _ expected: Expected, _ actual: Actual, ${TRACE} + _ expected: Expected, _ actual: Actual, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line ) where Actual.Iterator.Element == (key: T, value: T), Expected.Iterator.Element == (T, T) { @@ -2493,7 +2952,8 @@ public func expectEqualsUnordered< return lhs.0 == rhs.0 && lhs.1 == rhs.1 } - expectEqualSequence(x, y, ${trace}, sameValue: comparePairEquals) + expectEqualSequence(x, y, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), sameValue: comparePairEquals) } public func expectEqualFunctionsForDomain( @@ -2527,7 +2987,11 @@ public func expectEqualMethodsForDomain< } public func expectEqualUnicodeScalars( - _ expected: [UInt32], _ actual: String, ${TRACE}) { + _ expected: [UInt32], _ actual: String, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line) { let actualUnicodeScalars = Array( actual.unicodeScalars.lazy.map { $0.value }) @@ -2535,10 +2999,11 @@ public func expectEqualUnicodeScalars( expectationFailure( "expected elements: \"\(asHex(expected))\"\n" + "actual: \"\(asHex(actualUnicodeScalars))\"", - trace: ${trace}) + trace: message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) } } -// ${'Local Variables'}: +// Local Variables: // eval: (read-only-mode 1) // End: diff --git a/stdlib/private/StdlibUnittest/StringConvertible.swift.gyb b/stdlib/private/StdlibUnittest/StringConvertible.swift similarity index 68% rename from stdlib/private/StdlibUnittest/StringConvertible.swift.gyb rename to stdlib/private/StdlibUnittest/StringConvertible.swift index 2ddbfeb914af7..8252ba22a313d 100644 --- a/stdlib/private/StdlibUnittest/StringConvertible.swift.gyb +++ b/stdlib/private/StdlibUnittest/StringConvertible.swift @@ -1,4 +1,4 @@ -//===--- StringConvertible.swift.gyb --------------------------*- swift -*-===// +//===----------------------------------------------------------------------===// // // This source file is part of the Swift.org open source project // @@ -10,9 +10,6 @@ // //===----------------------------------------------------------------------===// -%{ -from gyb_stdlib_unittest_support import TRACE, stackTrace, trace -}% /// A type that has different `CustomStringConvertible` and /// `CustomDebugStringConvertible` representations. @@ -110,43 +107,68 @@ extension CustomPrintableValue : CustomDebugStringConvertible { } public func expectPrinted( - expectedOneOf patterns: [String], _ object: T, ${TRACE} + expectedOneOf patterns: [String], _ object: T, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line ) { let actual = String(describing: object) if !patterns.contains(actual) { expectationFailure( "expected: any of \(String(reflecting: patterns))\n" + "actual: \(String(reflecting: actual))", - trace: ${trace}) + trace: message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) } } public func expectPrinted( - _ expected: String, _ object: T, ${TRACE} + _ expected: String, _ object: T, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line ) { - expectPrinted(expectedOneOf: [expected], object, ${trace}) + expectPrinted(expectedOneOf: [expected], object, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) } public func expectDebugPrinted( - expectedOneOf patterns: [String], _ object: T, ${TRACE} + expectedOneOf patterns: [String], _ object: T, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line ) { - expectPrinted(expectedOneOf: patterns, String(reflecting: object), ${trace}) + expectPrinted(expectedOneOf: patterns, String(reflecting: object), message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) } public func expectDebugPrinted( - _ expected: String, _ object: T, ${TRACE} + _ expected: String, _ object: T, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line ) { - expectDebugPrinted(expectedOneOf: [expected], object, ${trace}) + expectDebugPrinted(expectedOneOf: [expected], object, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) } public func expectDumped( - _ expected: String, _ object: T, ${TRACE} + _ expected: String, _ object: T, + _ message: @autoclosure () -> String = "", + stackTrace: SourceLocStack = SourceLocStack(), + showFrame: Bool = true, + file: String = #file, line: UInt = #line ) { var actual = "" dump(object, to: &actual) - expectEqual(expected, actual, ${trace}) + expectEqual(expected, actual, message(), + stackTrace: stackTrace.pushIf(showFrame, file: file, line: line)) } -// ${'Local Variables'}: +// Local Variables: // eval: (read-only-mode 1) // End: diff --git a/stdlib/private/SwiftPrivate/IO.swift b/stdlib/private/SwiftPrivate/IO.swift index 8f9d2d888e632..1a77350a0464c 100644 --- a/stdlib/private/SwiftPrivate/IO.swift +++ b/stdlib/private/SwiftPrivate/IO.swift @@ -26,15 +26,13 @@ public struct _FDInputStream { public mutating func getline() -> String? { if let newlineIndex = _buffer[0..<_bufferUsed].index(of: UInt8(Unicode.Scalar("\n").value)) { - let result = String._fromWellFormedUTF8CodeUnitSequence( - _buffer[0.. 0 { - let result = String._fromWellFormedUTF8CodeUnitSequence( - _buffer[0..<_bufferUsed]) + let result = String(decoding: _buffer[0..<_bufferUsed], as: UTF8.self) _buffer.removeAll() _bufferUsed = 0 return result diff --git a/stdlib/private/SwiftReflectionTest/SwiftReflectionTest.swift b/stdlib/private/SwiftReflectionTest/SwiftReflectionTest.swift index 2a717b118b1b2..ecb24b64b7014 100644 --- a/stdlib/private/SwiftReflectionTest/SwiftReflectionTest.swift +++ b/stdlib/private/SwiftReflectionTest/SwiftReflectionTest.swift @@ -107,12 +107,12 @@ internal func getSectionInfo(_ name: String, /// /// An image of interest must have the following sections in the __DATA /// segment: -/// - __swift4_fieldmd -/// - __swift4_assocty -/// - __swift4_builtin -/// - __swift4_capture -/// - __swift4_typeref -/// - __swift4_reflstr (optional, may have been stripped out) +/// - __swift5_fieldmd +/// - __swift5_assocty +/// - __swift5_builtin +/// - __swift5_capture +/// - __swift5_typeref +/// - __swift5_reflstr (optional, may have been stripped out) /// /// - Parameter i: The index of the loaded image as reported by Dyld. /// - Returns: A `ReflectionInfo` containing the locations of all of the @@ -123,12 +123,12 @@ internal func getReflectionInfoForImage(atIndex i: UInt32) -> ReflectionInfo? { to: UnsafePointer.self) let imageName = _dyld_get_image_name(i)! - let fieldmd = getSectionInfo("__swift4_fieldmd", header) - let assocty = getSectionInfo("__swift4_assocty", header) - let builtin = getSectionInfo("__swift4_builtin", header) - let capture = getSectionInfo("__swift4_capture", header) - let typeref = getSectionInfo("__swift4_typeref", header) - let reflstr = getSectionInfo("__swift4_reflstr", header) + let fieldmd = getSectionInfo("__swift5_fieldmd", header) + let assocty = getSectionInfo("__swift5_assocty", header) + let builtin = getSectionInfo("__swift5_builtin", header) + let capture = getSectionInfo("__swift5_capture", header) + let typeref = getSectionInfo("__swift5_typeref", header) + let reflstr = getSectionInfo("__swift5_reflstr", header) return ReflectionInfo(imageName: String(validatingUTF8: imageName)!, fieldmd: fieldmd, assocty: assocty, diff --git a/stdlib/public/CMakeLists.txt b/stdlib/public/CMakeLists.txt index bbfda22074768..3b9591ce54b2a 100644 --- a/stdlib/public/CMakeLists.txt +++ b/stdlib/public/CMakeLists.txt @@ -13,6 +13,8 @@ if(SWIFT_RUNTIME_USE_SANITIZERS) endif() endif() +list(APPEND SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS "-Xfrontend" "-verify-syntax-tree") + # Build the runtime with -Wall to catch, e.g., uninitialized variables # warnings. list(APPEND SWIFT_RUNTIME_CXX_FLAGS "-Wall") diff --git a/stdlib/public/Platform/CMakeLists.txt b/stdlib/public/Platform/CMakeLists.txt index 14560c6bf99f3..92345b993dd58 100644 --- a/stdlib/public/Platform/CMakeLists.txt +++ b/stdlib/public/Platform/CMakeLists.txt @@ -53,6 +53,9 @@ foreach(sdk ${SWIFT_SDKS}) set(GLIBC_INCLUDE_PATH "/system/develop/headers/posix") set(GLIBC_ARCH_INCLUDE_PATH "/system/develop/headers/posix") set(GLIBC_SYSROOT_RELATIVE_ARCH_INCLUDE_PATH "/system/develop/headers/") + elseif("${sdk}" STREQUAL "ANDROID") + set(GLIBC_SYSROOT_RELATIVE_INCLUDE_PATH "${SWIFT_ANDROID_NDK_PATH}/sysroot/usr/include") + set(GLIBC_SYSROOT_RELATIVE_ARCH_INCLUDE_PATH "${SWIFT_ANDROID_NDK_PATH}/sysroot/usr/include") else() # Determine the location of glibc headers based on the target. set(GLIBC_SYSROOT_RELATIVE_INCLUDE_PATH "/usr/include") @@ -67,7 +70,7 @@ foreach(sdk ${SWIFT_SDKS}) set(GLIBC_INCLUDE_PATH "${GLIBC_SYSROOT_RELATIVE_INCLUDE_PATH}") set(GLIBC_ARCH_INCLUDE_PATH "${GLIBC_SYSROOT_RELATIVE_ARCH_INCLUDE_PATH}") - if(NOT "${SWIFT_SDK_${sdk}_ARCH_${arch}_PATH}" STREQUAL "/") + if(NOT "${SWIFT_SDK_${sdk}_ARCH_${arch}_PATH}" STREQUAL "/" AND NOT "${sdk}" STREQUAL "ANDROID") set(GLIBC_INCLUDE_PATH "${SWIFT_SDK_${sdk}_ARCH_${arch}_PATH}${GLIBC_INCLUDE_PATH}") set(GLIBC_ARCH_INCLUDE_PATH "${SWIFT_SDK_${sdk}_ARCH_${arch}_PATH}${GLIBC_ARCH_INCLUDE_PATH}") endif() diff --git a/stdlib/public/Platform/tgmath.swift.gyb b/stdlib/public/Platform/tgmath.swift.gyb index 7f4553c386b72..d512882daff11 100644 --- a/stdlib/public/Platform/tgmath.swift.gyb +++ b/stdlib/public/Platform/tgmath.swift.gyb @@ -293,7 +293,7 @@ public func ${bfunc}(_ lhs: ${T}, _ rhs: ${T}) -> ${T} { #if (arch(i386) || arch(x86_64)) && !os(Windows) % else: // lgamma not avaialable on Windows, apparently? -#if !os(Windows) +#if !os(Windows) && !os(Android) % end @_transparent public func lgamma(_ x: ${T}) -> (${T}, Int) { diff --git a/stdlib/public/Platform/ucrt.modulemap b/stdlib/public/Platform/ucrt.modulemap index 9ca80d8c9efbc..f670bda810457 100644 --- a/stdlib/public/Platform/ucrt.modulemap +++ b/stdlib/public/Platform/ucrt.modulemap @@ -99,9 +99,14 @@ module ucrt [system] { } } - module io { - header "corecrt_io.h" + module corecrt { + header "corecrt.h" export * + + module io { + header "corecrt_io.h" + export * + } } } diff --git a/stdlib/public/Platform/visualc.modulemap b/stdlib/public/Platform/visualc.modulemap index 33e18be099362..cb4da35034619 100644 --- a/stdlib/public/Platform/visualc.modulemap +++ b/stdlib/public/Platform/visualc.modulemap @@ -15,5 +15,20 @@ module visualc [system] { header "sal.h" export * } + + module vadefs { + header "vadefs.h" + export * + } + + module vcruntime { + header "vcruntime.h" + export * + } + + module stdint { + header "stdint.h" + export * + } } diff --git a/stdlib/public/Reflection/CMakeLists.txt b/stdlib/public/Reflection/CMakeLists.txt index 0bb36deecb493..dbbc72cb42c44 100644 --- a/stdlib/public/Reflection/CMakeLists.txt +++ b/stdlib/public/Reflection/CMakeLists.txt @@ -1,3 +1,12 @@ +set(swift_extra_sources) + +# When we're building with assertions, include the demangle node dumper to aid +# in debugging. +if (LLVM_ENABLE_ASSERTIONS) + list(APPEND swift_extra_sources "${SWIFT_SOURCE_DIR}/lib/Demangling/NodeDumper.cpp") +endif(LLVM_ENABLE_ASSERTIONS) + + add_swift_library(swiftReflection STATIC TARGET_LIBRARY FORCE_BUILD_FOR_HOST_SDK MetadataSource.cpp TypeLowering.cpp @@ -11,7 +20,8 @@ add_swift_library(swiftReflection STATIC TARGET_LIBRARY FORCE_BUILD_FOR_HOST_SDK "${SWIFT_SOURCE_DIR}/lib/Demangling/Punycode.cpp" "${SWIFT_SOURCE_DIR}/lib/Demangling/Remangler.cpp" "${SWIFT_SOURCE_DIR}/lib/Demangling/TypeDecoder.cpp" - + ${swift_extra_sources} C_COMPILE_FLAGS ${SWIFT_RUNTIME_CXX_FLAGS} LINK_FLAGS ${SWIFT_RUNTIME_LINK_FLAGS} INSTALL_IN_COMPONENT dev) + diff --git a/stdlib/public/SDK/CoreFoundation/CoreFoundation.swift b/stdlib/public/SDK/CoreFoundation/CoreFoundation.swift index 29b779905ef77..edf4ec460d24d 100644 --- a/stdlib/public/SDK/CoreFoundation/CoreFoundation.swift +++ b/stdlib/public/SDK/CoreFoundation/CoreFoundation.swift @@ -17,6 +17,9 @@ extension _CFObject { public var hashValue: Int { return Int(bitPattern: CFHash(self)) } + public func hash(into hasher: inout Hasher) { + hasher.combine(self.hashValue) + } public static func ==(left: Self, right: Self) -> Bool { return CFEqual(left, right) } diff --git a/stdlib/public/SDK/Dispatch/Data.swift b/stdlib/public/SDK/Dispatch/Data.swift index 1fd8fcb439bbd..657b1a903cbee 100644 --- a/stdlib/public/SDK/Dispatch/Data.swift +++ b/stdlib/public/SDK/Dispatch/Data.swift @@ -149,9 +149,7 @@ public struct DispatchData : RandomAccessCollection, _ObjectiveCBridgeable { /// /// - parameter buffer: The buffer of bytes to append. The size is calculated from `SourceType` and `buffer.count`. public mutating func append(_ buffer : UnsafeBufferPointer) { - buffer.baseAddress!.withMemoryRebound(to: UInt8.self, capacity: buffer.count * MemoryLayout.stride) { - self.append($0, count: buffer.count * MemoryLayout.stride) - } + self.append(UnsafeRawBufferPointer(buffer)) } private func _copyBytesHelper(to pointer: UnsafeMutableRawPointer, from range: Range) { diff --git a/stdlib/public/SDK/Foundation/CMakeLists.txt b/stdlib/public/SDK/Foundation/CMakeLists.txt index b6cdce95db798..c9d3ef8914009 100644 --- a/stdlib/public/SDK/Foundation/CMakeLists.txt +++ b/stdlib/public/SDK/Foundation/CMakeLists.txt @@ -26,6 +26,7 @@ add_swift_library(swiftFoundation ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPES} IS_SD NSCoder.swift NSDate.swift NSDictionary.swift + NSError.mm NSError.swift NSExpression.swift NSFastEnumeration.swift diff --git a/stdlib/public/SDK/Foundation/Calendar.swift b/stdlib/public/SDK/Foundation/Calendar.swift index bef4023685f18..fdcb38e9342ed 100644 --- a/stdlib/public/SDK/Foundation/Calendar.swift +++ b/stdlib/public/SDK/Foundation/Calendar.swift @@ -917,25 +917,25 @@ public struct Calendar : Hashable, Equatable, ReferenceConvertible, _MutableBoxi switch matchingPolicy { case .nextTime: - let _ = result.insert(.matchNextTime) + result.insert(.matchNextTime) case .nextTimePreservingSmallerComponents: - let _ = result.insert(.matchNextTimePreservingSmallerUnits) + result.insert(.matchNextTimePreservingSmallerUnits) case .previousTimePreservingSmallerComponents: - let _ = result.insert(.matchPreviousTimePreservingSmallerUnits) + result.insert(.matchPreviousTimePreservingSmallerUnits) case .strict: - let _ = result.insert(.matchStrictly) + result.insert(.matchStrictly) } switch repeatedTimePolicy { case .first: - let _ = result.insert(.matchFirst) + result.insert(.matchFirst) case .last: - let _ = result.insert(.matchLast) + result.insert(.matchLast) } switch direction { case .backward: - let _ = result.insert(.searchBackwards) + result.insert(.searchBackwards) case .forward: break } @@ -964,7 +964,7 @@ public struct Calendar : Hashable, Equatable, ReferenceConvertible, _MutableBoxi var result = NSCalendar.Unit() for u in units { - let _ = result.insert(unitMap[u]!) + result.insert(unitMap[u]!) } return result } diff --git a/stdlib/public/SDK/Foundation/Data.swift b/stdlib/public/SDK/Foundation/Data.swift index 5c6967f7bf4d5..56033a37ffb02 100644 --- a/stdlib/public/SDK/Foundation/Data.swift +++ b/stdlib/public/SDK/Foundation/Data.swift @@ -38,6 +38,22 @@ internal func __NSDataIsCompact(_ data: NSData) -> Bool { import _SwiftFoundationOverlayShims import _SwiftCoreFoundationOverlayShims +internal func __NSDataIsCompact(_ data: NSData) -> Bool { + if #available(OSX 10.10, iOS 8.0, tvOS 9.0, watchOS 2.0, *) { + return data._isCompact() + } else { + var compact = true + let len = data.length + data.enumerateBytes { (_, byteRange, stop) in + if byteRange.length != len { + compact = false + } + stop.pointee = true + } + return compact + } +} + #endif public final class _DataStorage { @@ -127,7 +143,7 @@ public final class _DataStorage { case .mutable: return try apply(UnsafeRawBufferPointer(start: _bytes?.advanced(by: range.lowerBound - _offset), count: Swift.min(range.count, _length))) case .customReference(let d): - if d._isCompact() { + if __NSDataIsCompact(d) { let len = d.length guard len > 0 else { return try apply(UnsafeRawBufferPointer(start: nil, count: 0)) @@ -161,7 +177,7 @@ public final class _DataStorage { return try apply(UnsafeRawBufferPointer(buffer)) } case .customMutableReference(let d): - if d._isCompact() { + if __NSDataIsCompact(d) { let len = d.length guard len > 0 else { return try apply(UnsafeRawBufferPointer(start: nil, count: 0)) @@ -509,7 +525,7 @@ public final class _DataStorage { case .mutable: return _bytes!.advanced(by: index - _offset).assumingMemoryBound(to: UInt8.self).pointee case .customReference(let d): - if d._isCompact() { + if __NSDataIsCompact(d) { return d.bytes.advanced(by: index - _offset).assumingMemoryBound(to: UInt8.self).pointee } else { var byte: UInt8 = 0 @@ -523,7 +539,7 @@ public final class _DataStorage { return byte } case .customMutableReference(let d): - if d._isCompact() { + if __NSDataIsCompact(d) { return d.bytes.advanced(by: index - _offset).assumingMemoryBound(to: UInt8.self).pointee } else { var byte: UInt8 = 0 @@ -854,7 +870,7 @@ public final class _DataStorage { } public func withInteriorPointerReference(_ range: Range, _ work: (NSData) throws -> T) rethrows -> T { - if range.count == 0 { + if range.isEmpty { return try work(NSData()) // zero length data can be optimized as a singleton } @@ -886,7 +902,7 @@ public final class _DataStorage { } public func bridgedReference(_ range: Range) -> NSData { - if range.count == 0 { + if range.isEmpty { return NSData() // zero length data can be optimized as a singleton } @@ -1464,7 +1480,7 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl /// - parameter buffer: The buffer of bytes to append. The size is calculated from `SourceType` and `buffer.count`. @inline(__always) public mutating func append(_ buffer : UnsafeBufferPointer) { - if buffer.count == 0 { return } + if buffer.isEmpty { return } if !isKnownUniquelyReferenced(&_backing) { _backing = _backing.mutableCopy(_sliceRange) } @@ -1588,7 +1604,7 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl @inline(__always) public func subdata(in range: Range) -> Data { _validateRange(range) - if count == 0 { + if isEmpty { return Data() } return _backing.subdata(in: range) @@ -1762,14 +1778,23 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl } public struct Iterator : IteratorProtocol { - private let _data: Data + // Both _data and _endIdx should be 'let' rather than 'var'. + // They are 'var' so that the stored properties can be read + // independently of the other contents of the struct. This prevents + // an exclusivity violation when reading '_endIdx' and '_data' + // while simultaneously mutating '_buffer' with the call to + // withUnsafeMutablePointer(). Once we support accessing struct + // let properties independently we should make these variables + // 'let' again. + + private var _data: Data private var _buffer: ( UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8) private var _idx: Data.Index - private let _endIdx: Data.Index + private var _endIdx: Data.Index fileprivate init(_ data: Data) { _data = data diff --git a/stdlib/public/SDK/Foundation/Decimal.swift b/stdlib/public/SDK/Foundation/Decimal.swift index 8af713e62a1ca..6e0cd4be76b7d 100644 --- a/stdlib/public/SDK/Foundation/Decimal.swift +++ b/stdlib/public/SDK/Foundation/Decimal.swift @@ -59,6 +59,7 @@ extension Decimal { public mutating func formTruncatingRemainder(dividingBy other: Decimal) { fatalError("Decimal does not yet fully adopt FloatingPoint") } public mutating func negate() { + guard _length != 0 else { return } _isNegative = _isNegative == 0 ? 1 : 0 } diff --git a/stdlib/public/SDK/Foundation/JSONEncoder.swift b/stdlib/public/SDK/Foundation/JSONEncoder.swift index e8e18025a190a..917259b37cbed 100644 --- a/stdlib/public/SDK/Foundation/JSONEncoder.swift +++ b/stdlib/public/SDK/Foundation/JSONEncoder.swift @@ -2374,7 +2374,7 @@ extension _JSONDecoder { debugDescription: "Invalid URL string.")) } - return url as! T + return (url as! T) } else if type == Decimal.self || type == NSDecimalNumber.self { return try self.unbox(value, as: Decimal.self) as? T } else { diff --git a/stdlib/public/SDK/Foundation/Locale.swift b/stdlib/public/SDK/Foundation/Locale.swift index c76c0b96d3b31..3d3dab0ffedc7 100644 --- a/stdlib/public/SDK/Foundation/Locale.swift +++ b/stdlib/public/SDK/Foundation/Locale.swift @@ -109,7 +109,7 @@ public struct Locale : Hashable, Equatable, ReferenceConvertible { /// For example, in the "en" locale, the result for `.buddhist` is `"Buddhist Calendar"`. public func localizedString(for calendarIdentifier: Calendar.Identifier) -> String? { // NSLocale doesn't export a constant for this - let result = CFLocaleCopyDisplayNameForPropertyValue(unsafeBitCast(_wrapped, to: CFLocale.self), .calendarIdentifier, Calendar._toNSCalendarIdentifier(calendarIdentifier).rawValue as CFString) as String + let result = CFLocaleCopyDisplayNameForPropertyValue(unsafeBitCast(_wrapped, to: CFLocale.self), .calendarIdentifier, Calendar._toNSCalendarIdentifier(calendarIdentifier).rawValue as CFString) as String? return result } diff --git a/stdlib/public/SDK/Foundation/NSError.h b/stdlib/public/SDK/Foundation/NSError.h new file mode 100644 index 0000000000000..cac976cf76150 --- /dev/null +++ b/stdlib/public/SDK/Foundation/NSError.h @@ -0,0 +1,47 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_FOUNDATION_NSERROR_H +#define SWIFT_FOUNDATION_NSERROR_H + +#include "swift/Runtime/Metadata.h" +#include "../../runtime/SwiftHashableSupport.h" +#include + +namespace swift { + +/// The name of the symbol that ErrorObject.mm will look up using dlsym. It uses +/// this to locate various items related to Error bridging to NS/CFError. +#define ERROR_BRIDGING_SYMBOL_NAME swift_errorBridgingInfo +#define ERROR_BRIDGING_SYMBOL_NAME_STRING "swift_errorBridgingInfo" + +/// The items that ErrorObject.mm needs for bridging. The +/// ERROR_BRIDGING_SYMBOL_NAME symbol will contain an instance of this struct. +struct ErrorBridgingInfo { + const SWIFT_CC(swift) WitnessTable *(*GetCFErrorErrorConformance)(); + + const SWIFT_CC(swift) hashable_support::HashableWitnessTable * + (*GetNSObjectHashableConformance)(); + + SWIFT_CC(swift) NSDictionary *(*GetErrorDefaultUserInfo)(const OpaqueValue *error, + const Metadata *T, + const WitnessTable *Error); + + SWIFT_CC(swift) bool (*BridgeErrorToNSError)(NSError *, OpaqueValue*, const Metadata *, + const WitnessTable *); + + const ProtocolDescriptor *ObjectiveCBridgeableError; +}; + +} + +#endif // SWIFT_FOUNDATION_NSERROR_H diff --git a/stdlib/public/SDK/Foundation/NSError.mm b/stdlib/public/SDK/Foundation/NSError.mm new file mode 100644 index 0000000000000..799c0e3a26377 --- /dev/null +++ b/stdlib/public/SDK/Foundation/NSError.mm @@ -0,0 +1,49 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "NSError.h" + +#include "swift/Demangling/ManglingMacros.h" + +using namespace swift; + +// Declare the mangled Swift symbols that we'll be putting in the bridging info. +extern "C" const SWIFT_CC(swift) WitnessTable * + MANGLE_SYM(So10CFErrorRefas5Error10FoundationWa)(); + +extern "C" const SWIFT_CC(swift) hashable_support::HashableWitnessTable * + MANGLE_SYM(So8NSObjectCs8Hashable10ObjectiveCWa)(); + +extern "C" SWIFT_CC(swift) + NSDictionary *MANGLE_SYM(10Foundation24_getErrorDefaultUserInfoyyXlSgxs0C0RzlF)( + const OpaqueValue *error, const Metadata *T, const WitnessTable *Error); + +extern "C" SWIFT_CC(swift) bool + MANGLE_SYM(10Foundation21_bridgeNSErrorToError_3outSbSo0C0C_SpyxGtAA021_ObjectiveCBridgeableE0RzlF)( + NSError *, OpaqueValue*, const Metadata *, const WitnessTable *); + +extern "C" const ProtocolDescriptor + MANGLE_SYM(10Foundation26_ObjectiveCBridgeableErrorMp); + +// Define the bridging info struct. +extern "C" ErrorBridgingInfo ERROR_BRIDGING_SYMBOL_NAME = { + MANGLE_SYM(So10CFErrorRefas5Error10FoundationWa), + MANGLE_SYM(So8NSObjectCs8Hashable10ObjectiveCWa), + MANGLE_SYM(10Foundation24_getErrorDefaultUserInfoyyXlSgxs0C0RzlF), + MANGLE_SYM(10Foundation21_bridgeNSErrorToError_3outSbSo0C0C_SpyxGtAA021_ObjectiveCBridgeableE0RzlF), + &MANGLE_SYM(10Foundation26_ObjectiveCBridgeableErrorMp) +}; + +// This directive ensures that the symbol is preserved even when statically +// linked into an executable and stripped, so that the dlsym lookup from +// ErrorObject.mm still works. +asm(".desc _" ERROR_BRIDGING_SYMBOL_NAME_STRING ", 0x10"); diff --git a/stdlib/public/SDK/Foundation/NSError.swift b/stdlib/public/SDK/Foundation/NSError.swift index f2c1c80bdbb49..8203792163f8d 100644 --- a/stdlib/public/SDK/Foundation/NSError.swift +++ b/stdlib/public/SDK/Foundation/NSError.swift @@ -109,7 +109,7 @@ public protocol RecoverableError : Error { /// "document" granularity, that do not affect the entire /// application. func attemptRecovery(optionIndex recoveryOptionIndex: Int, - resultHandler handler: (_ recovered: Bool) -> Void) + resultHandler handler: @escaping (_ recovered: Bool) -> Void) /// Attempt to recover from this error when the user selected the /// option at the given index. Returns true to indicate @@ -127,7 +127,7 @@ public extension RecoverableError { /// mechanism (``attemptRecovery(optionIndex:)``) to implement /// document-modal recovery. func attemptRecovery(optionIndex recoveryOptionIndex: Int, - resultHandler handler: (_ recovered: Bool) -> Void) { + resultHandler handler: @escaping (_ recovered: Bool) -> Void) { handler(attemptRecovery(optionIndex: recoveryOptionIndex)) } } diff --git a/stdlib/public/SDK/Foundation/NSFastEnumeration.swift b/stdlib/public/SDK/Foundation/NSFastEnumeration.swift index 3834518645331..33f5eff868bc1 100644 --- a/stdlib/public/SDK/Foundation/NSFastEnumeration.swift +++ b/stdlib/public/SDK/Foundation/NSFastEnumeration.swift @@ -12,6 +12,10 @@ @_exported import Foundation // Clang module +/// A dummy value to be used as the target for `mutationsPtr` in fast enumeration implementations. +fileprivate var _fastEnumerationMutationsTarget: CUnsignedLong = 0 +/// A dummy pointer to be used as `mutationsPtr` in fast enumeration implementations. +fileprivate let _fastEnumerationMutationsPtr = UnsafeMutablePointer(&_fastEnumerationMutationsTarget) //===----------------------------------------------------------------------===// // Fast enumeration @@ -19,7 +23,7 @@ public struct NSFastEnumerationIterator : IteratorProtocol { var enumerable: NSFastEnumeration var objects: (Unmanaged?, Unmanaged?, Unmanaged?, Unmanaged?, Unmanaged?, Unmanaged?, Unmanaged?, Unmanaged?, Unmanaged?, Unmanaged?, Unmanaged?, Unmanaged?, Unmanaged?, Unmanaged?, Unmanaged?, Unmanaged?) = (nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil) - var state = NSFastEnumerationState(state: 0, itemsPtr: nil, mutationsPtr: _fastEnumerationStorageMutationsPtr, extra: (0, 0, 0, 0, 0)) + var state = NSFastEnumerationState(state: 0, itemsPtr: nil, mutationsPtr: _fastEnumerationMutationsPtr, extra: (0, 0, 0, 0, 0)) var index = 0 var count = 0 var useObjectsBuffer = false diff --git a/stdlib/public/SDK/Foundation/NSObject.swift b/stdlib/public/SDK/Foundation/NSObject.swift index bef703f461ac4..d64180fcc37a0 100644 --- a/stdlib/public/SDK/Foundation/NSObject.swift +++ b/stdlib/public/SDK/Foundation/NSObject.swift @@ -130,12 +130,12 @@ func _bridgeStringToKeyPath(_ keyPath:String) -> AnyKeyPath? { public class NSKeyValueObservation : NSObject { - weak var object : NSObject? - let callback : (NSObject, NSKeyValueObservedChange) -> Void - let path : String + @nonobjc weak var object : NSObject? + @nonobjc let callback : (NSObject, NSKeyValueObservedChange) -> Void + @nonobjc let path : String //workaround for Erroneous (?) error when using bridging in the Foundation overlay - static var swizzler : NSKeyValueObservation? = { + @nonobjc static var swizzler : NSKeyValueObservation? = { let bridgeClass: AnyClass = NSKeyValueObservation.self let observeSel = #selector(NSObject.observeValue(forKeyPath:of:change:context:)) let swapSel = #selector(NSKeyValueObservation._swizzle_me_observeValue(forKeyPath:of:change:context:)) @@ -157,7 +157,7 @@ public class NSKeyValueObservation : NSObject { } ///invalidate() will be called automatically when an NSKeyValueObservation is deinited - public func invalidate() { + @objc public func invalidate() { object?.removeObserver(self, forKeyPath: path, context: nil) object = nil } diff --git a/stdlib/public/SDK/Foundation/NSStringAPI.swift b/stdlib/public/SDK/Foundation/NSStringAPI.swift index 09c83cc592340..3366c47d101cf 100644 --- a/stdlib/public/SDK/Foundation/NSStringAPI.swift +++ b/stdlib/public/SDK/Foundation/NSStringAPI.swift @@ -436,7 +436,6 @@ extension StringProtocol where Index == String.Index { // self can be a Substring so we need to subtract/add this offset when // passing _ns to the Foundation APIs. Will be 0 if self is String. @inlinable - @usableFromInline internal var _substringOffset: Int { return self.startIndex.encodedOffset } @@ -448,7 +447,6 @@ extension StringProtocol where Index == String.Index { } @inlinable - @usableFromInline internal func _toRelativeNSRange(_ r: Range) -> NSRange { return NSRange( location: r.lowerBound.encodedOffset - _substringOffset, diff --git a/stdlib/public/SwiftRemoteMirror/CMakeLists.txt b/stdlib/public/SwiftRemoteMirror/CMakeLists.txt index 93255e9907117..af4a8e95bf7fb 100644 --- a/stdlib/public/SwiftRemoteMirror/CMakeLists.txt +++ b/stdlib/public/SwiftRemoteMirror/CMakeLists.txt @@ -1,6 +1,3 @@ -# HACK: Force this library to build with undefined symbols. -string(REGEX REPLACE "-Wl,-z,defs" "" CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS}") - # libswiftRemoteMirror.dylib should not have runtime dependencies; it's # always built as a shared library. if(SWIFT_BUILD_DYNAMIC_STDLIB OR SWIFT_BUILD_REMOTE_MIRROR) diff --git a/stdlib/public/SwiftShims/FoundationOverlayShims.h b/stdlib/public/SwiftShims/FoundationOverlayShims.h index 53ef2146c26c3..4a26a2eefdf75 100644 --- a/stdlib/public/SwiftShims/FoundationOverlayShims.h +++ b/stdlib/public/SwiftShims/FoundationOverlayShims.h @@ -17,6 +17,7 @@ #import #import #import +#import #import "FoundationShimSupport.h" #import "NSCalendarShims.h" diff --git a/stdlib/public/SwiftShims/HeapObject.h b/stdlib/public/SwiftShims/HeapObject.h index 2797de44dbddd..b4ccbf0994381 100644 --- a/stdlib/public/SwiftShims/HeapObject.h +++ b/stdlib/public/SwiftShims/HeapObject.h @@ -13,6 +13,7 @@ #define SWIFT_STDLIB_SHIMS_HEAPOBJECT_H #include "RefCount.h" +#include "SwiftStddef.h" #include "System.h" #define SWIFT_ABI_HEAP_OBJECT_HEADER_SIZE_64 16 @@ -30,6 +31,7 @@ template struct TargetHeapMetadata; using HeapMetadata = TargetHeapMetadata; #else typedef struct HeapMetadata HeapMetadata; +typedef struct HeapObject HeapObject; #endif // The members of the HeapObject header that are not shared by a @@ -69,6 +71,15 @@ SWIFT_RUNTIME_STDLIB_INTERFACE void _swift_instantiateInertHeapObject(void *address, const HeapMetadata *metadata); +SWIFT_RUNTIME_STDLIB_INTERFACE +__swift_size_t swift_retainCount(HeapObject *obj); + +SWIFT_RUNTIME_STDLIB_INTERFACE +__swift_size_t swift_unownedRetainCount(HeapObject *obj); + +SWIFT_RUNTIME_STDLIB_INTERFACE +__swift_size_t swift_weakRetainCount(HeapObject *obj); + #ifdef __cplusplus } // extern "C" #endif diff --git a/stdlib/public/SwiftShims/LibcShims.h b/stdlib/public/SwiftShims/LibcShims.h index 5f6e995a8227f..e3246b62fc92b 100644 --- a/stdlib/public/SwiftShims/LibcShims.h +++ b/stdlib/public/SwiftShims/LibcShims.h @@ -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 - 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 @@ -130,7 +130,7 @@ int _stdlib_ioctlPtr(int fd, unsigned long int request, void* ptr); // Environment #if defined(__APPLE__) || defined(__FreeBSD__) SWIFT_RUNTIME_STDLIB_INTERNAL -char * _Nullable *_stdlib_getEnviron(); +char * _Nullable * _Null_unspecified _stdlib_getEnviron(); #endif // System error numbers @@ -149,6 +149,10 @@ __swift_uint32_t _stdlib_cxx11_mt19937(void); SWIFT_RUNTIME_STDLIB_INTERNAL __swift_uint32_t _stdlib_cxx11_mt19937_uniform(__swift_uint32_t upper_bound); +// Random number for stdlib +SWIFT_RUNTIME_STDLIB_INTERNAL +void _stdlib_random(void *buf, __swift_size_t nbytes); + // Math library functions static inline SWIFT_ALWAYS_INLINE float _stdlib_remainderf(float _self, float _other) { diff --git a/stdlib/public/SwiftShims/NSDataShims.h b/stdlib/public/SwiftShims/NSDataShims.h index ce5038d8443dc..770c7d8cc9ef5 100644 --- a/stdlib/public/SwiftShims/NSDataShims.h +++ b/stdlib/public/SwiftShims/NSDataShims.h @@ -21,7 +21,7 @@ FOUNDATION_EXPORT const NSDataDeallocator NSDataDeallocatorFree; FOUNDATION_EXPORT const NSDataDeallocator NSDataDeallocatorNone; @interface NSData (FoundationSPI) -- (BOOL)_isCompact; +- (BOOL)_isCompact API_AVAILABLE(macos(10.10), ios(8.0), watchos(2.0), tvos(9.0)); @end BOOL __NSDataWriteToURL(NS_NON_BRIDGED(NSData *) _Nonnull data NS_RELEASES_ARGUMENT, NSURL * _Nonnull url NS_RELEASES_ARGUMENT, NSDataWritingOptions writingOptions, NSError **errorPtr); diff --git a/stdlib/public/SwiftShims/RefCount.h b/stdlib/public/SwiftShims/RefCount.h index 8adbc2f7e2cbd..e8a07ac9f5bc7 100644 --- a/stdlib/public/SwiftShims/RefCount.h +++ b/stdlib/public/SwiftShims/RefCount.h @@ -35,19 +35,10 @@ typedef InlineRefCountsPlaceholder InlineRefCounts; #include "llvm/Support/Compiler.h" #include "swift/Basic/type_traits.h" +#include "swift/Runtime/Atomic.h" #include "swift/Runtime/Config.h" #include "swift/Runtime/Debug.h" -// FIXME: Workaround for rdar://problem/18889711. 'Consume' does not require -// a barrier on ARM64, but LLVM doesn't know that. Although 'relaxed' -// is formally UB by C++11 language rules, we should be OK because neither -// the processor model nor the optimizer can realistically reorder our uses -// of 'consume'. -#if __arm64__ || __arm__ -# define SWIFT_MEMORY_ORDER_CONSUME (std::memory_order_relaxed) -#else -# define SWIFT_MEMORY_ORDER_CONSUME (std::memory_order_consume) -#endif /* An object conceptually has three refcounts. These refcounts diff --git a/stdlib/public/core/Algorithm.swift b/stdlib/public/core/Algorithm.swift index 9aae722c3c6b2..0e12d06ebad15 100644 --- a/stdlib/public/core/Algorithm.swift +++ b/stdlib/public/core/Algorithm.swift @@ -98,7 +98,6 @@ public struct EnumeratedIterator { /// Construct from a `Base` iterator. @inlinable - @usableFromInline internal init(_base: Base) { self._base = _base self._count = 0 @@ -145,7 +144,6 @@ public struct EnumeratedSequence { /// Construct from a `Base` sequence. @inlinable - @usableFromInline internal init(_base: Base) { self._base = _base } diff --git a/stdlib/public/core/AnyHashable.swift b/stdlib/public/core/AnyHashable.swift index ad02cc881e3bf..deac9702bfe77 100644 --- a/stdlib/public/core/AnyHashable.swift +++ b/stdlib/public/core/AnyHashable.swift @@ -48,7 +48,7 @@ internal protocol _AnyHashableBox { /// no comparison is possible. Otherwise, contains the result of `==`. func _isEqual(to: _AnyHashableBox) -> Bool? var _hashValue: Int { get } - func _hash(_into hasher: inout _Hasher) + func _hash(into hasher: inout Hasher) var _base: Any { get } func _downCastConditional(into result: UnsafeMutablePointer) -> Bool @@ -61,26 +61,22 @@ internal struct _ConcreteHashableBox : _AnyHashableBox { internal var _baseHashable: Base @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_ base: Base) { self._baseHashable = base } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var _typeID: ObjectIdentifier { return ObjectIdentifier(type(of: self)) } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func _unbox() -> T? { return (self as _AnyHashableBox as? _ConcreteHashableBox)?._baseHashable } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func _isEqual(to rhs: _AnyHashableBox) -> Bool? { if let rhs: Base = rhs._unbox() { return _baseHashable == rhs @@ -89,25 +85,21 @@ internal struct _ConcreteHashableBox : _AnyHashableBox { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var _hashValue: Int { return _baseHashable.hashValue } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) - func _hash(_into hasher: inout _Hasher) { - _baseHashable._hash(into: &hasher) + func _hash(into hasher: inout Hasher) { + _baseHashable.hash(into: &hasher) } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var _base: Any { return _baseHashable } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func _downCastConditional(into result: UnsafeMutablePointer) -> Bool { guard let value = _baseHashable as? T else { return false } @@ -122,7 +114,6 @@ internal struct _ConcreteHashableBox : _AnyHashableBox { // turns a non-custom representation into a custom one, which is used as // the lowest-common-denominator for comparisons. @inlinable // FIXME(sil-serialize-all) -@usableFromInline // FIXME(sil-serialize-all) internal func _getBridgedCustomAnyHashable(_ value: T) -> AnyHashable? { let bridgedValue = _bridgeAnythingToObjectiveC(value) return (bridgedValue as? @@ -191,7 +182,6 @@ public struct AnyHashable { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_usingDefaultRepresentationOf base: H) { self._box = _ConcreteHashableBox(base) self._usedCustomRepresentation = false @@ -217,7 +207,6 @@ public struct AnyHashable { /// This avoids the intermediate re-boxing we would get if we just did /// a downcast on `base`. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func _downCastConditional(into result: UnsafeMutablePointer) -> Bool { // Attempt the downcast. @@ -298,14 +287,20 @@ extension AnyHashable : Equatable { } extension AnyHashable : Hashable { + /// The hash value. @inlinable // FIXME(sil-serialize-all) public var hashValue: Int { return _box._hashValue } + /// Hashes the essential components of this value by feeding them into the + /// given hasher. + /// + /// - Parameter hasher: The hasher to use when combining the components + /// of this instance. @inlinable // FIXME(sil-serialize-all) - public func _hash(into hasher: inout _Hasher) { - _box._hash(_into: &hasher) + public func hash(into hasher: inout Hasher) { + _box._hash(into: &hasher) } } diff --git a/stdlib/public/core/ArrayBody.swift b/stdlib/public/core/ArrayBody.swift index 7f282174f5e48..ee6f7d46542bb 100644 --- a/stdlib/public/core/ArrayBody.swift +++ b/stdlib/public/core/ArrayBody.swift @@ -24,7 +24,6 @@ internal struct _ArrayBody { internal var _storage: _SwiftArrayBodyStorage @inlinable - @usableFromInline internal init( count: Int, capacity: Int, elementTypeIsBridgedVerbatim: Bool = false ) { @@ -43,14 +42,12 @@ internal struct _ArrayBody { /// capacity after a new buffer is allocated, it's typical to want /// to update it immediately after construction. @inlinable - @usableFromInline internal init() { _storage = _SwiftArrayBodyStorage(count: 0, _capacityAndFlags: 0) } /// The number of elements stored in this Array. @inlinable - @usableFromInline internal var count: Int { get { return _assumeNonNegative(_storage.count) @@ -63,7 +60,6 @@ internal struct _ArrayBody { /// The number of elements that can be stored in this Array without /// reallocation. @inlinable - @usableFromInline internal var capacity: Int { return Int(_capacityAndFlags &>> 1) } @@ -75,7 +71,6 @@ internal struct _ArrayBody { /// avoid the cost of calls into the runtime that compute the /// answer. @inlinable - @usableFromInline internal var elementTypeIsBridgedVerbatim: Bool { get { return (_capacityAndFlags & 0x1) != 0 @@ -89,7 +84,6 @@ internal struct _ArrayBody { /// Storage optimization: compresses capacity and /// elementTypeIsBridgedVerbatim together. @inlinable - @usableFromInline internal var _capacityAndFlags: UInt { get { return _storage._capacityAndFlags diff --git a/stdlib/public/core/ArrayBuffer.swift b/stdlib/public/core/ArrayBuffer.swift index f666049f83a74..f1578a112d8e8 100644 --- a/stdlib/public/core/ArrayBuffer.swift +++ b/stdlib/public/core/ArrayBuffer.swift @@ -27,13 +27,11 @@ internal struct _ArrayBuffer : _ArrayBufferProtocol { /// Create an empty buffer. @inlinable - @usableFromInline internal init() { _storage = _ArrayBridgeStorage(native: _emptyArrayStorage) } @inlinable - @usableFromInline // FIXME(abi): Used from tests internal init(nsArray: _NSArrayCore) { _sanityCheck(_isClassOrObjCExistential(Element.self)) _storage = _ArrayBridgeStorage(objC: nsArray) @@ -44,7 +42,6 @@ internal struct _ArrayBuffer : _ArrayBufferProtocol { /// - Precondition: The elements actually have dynamic type `U`, and `U` /// is a class or `@objc` existential. @inlinable - @usableFromInline internal func cast(toBufferOf _: U.Type) -> _ArrayBuffer { _sanityCheck(_isClassOrObjCExistential(Element.self)) _sanityCheck(_isClassOrObjCExistential(U.self)) @@ -54,7 +51,6 @@ internal struct _ArrayBuffer : _ArrayBufferProtocol { /// The spare bits that are set when a native array needs deferred /// element type checking. @inlinable - @usableFromInline internal var deferredTypeCheckMask: Int { return 1 } /// Returns an `_ArrayBuffer` containing the same elements, @@ -63,7 +59,6 @@ internal struct _ArrayBuffer : _ArrayBufferProtocol { /// - Precondition: `U` is a class or `@objc` existential derived from /// `Element`. @inlinable - @usableFromInline internal func downcast( toBufferWithDeferredTypeCheckOf _: U.Type ) -> _ArrayBuffer { @@ -80,7 +75,6 @@ internal struct _ArrayBuffer : _ArrayBufferProtocol { } @inlinable - @usableFromInline internal var needsElementTypeCheck: Bool { // NSArray's need an element typecheck when the element type isn't AnyObject return !_isNativeTypeChecked && !(AnyObject.self is Element.Type) @@ -88,7 +82,6 @@ internal struct _ArrayBuffer : _ArrayBufferProtocol { //===--- private --------------------------------------------------------===// @inlinable - @usableFromInline internal init(storage: _ArrayBridgeStorage) { _storage = storage } @@ -100,7 +93,6 @@ internal struct _ArrayBuffer : _ArrayBufferProtocol { extension _ArrayBuffer { /// Adopt the storage of `source`. @inlinable - @usableFromInline internal init(_buffer source: NativeBuffer, shiftedToStartIndex: Int) { _sanityCheck(shiftedToStartIndex == 0, "shiftedToStartIndex must be 0") _storage = _ArrayBridgeStorage(native: source._storage) @@ -108,14 +100,12 @@ extension _ArrayBuffer { /// `true`, if the array is native and does not need a deferred type check. @inlinable - @usableFromInline internal var arrayPropertyIsNativeTypeChecked: Bool { return _isNativeTypeChecked } /// Returns `true` iff this buffer's storage is uniquely-referenced. @inlinable - @usableFromInline internal mutating func isUniquelyReferenced() -> Bool { if !_isClassOrObjCExistential(Element.self) { return _storage.isUniquelyReferenced_native_noSpareBits() @@ -135,7 +125,6 @@ extension _ArrayBuffer { /// Returns `true` iff this buffer's storage is either /// uniquely-referenced or pinned. @inlinable - @usableFromInline internal mutating func isUniquelyReferencedOrPinned() -> Bool { if !_isClassOrObjCExistential(Element.self) { return _storage.isUniquelyReferencedOrPinned_native_noSpareBits() @@ -156,7 +145,6 @@ extension _ArrayBuffer { /// /// O(1) if the element type is bridged verbatim, O(*n*) otherwise. @inlinable - @usableFromInline // FIXME(abi): Used from tests internal func _asCocoaArray() -> _NSArrayCore { return _fastPath(_isNative) ? _native._asCocoaArray() : _nonNative } @@ -166,7 +154,6 @@ extension _ArrayBuffer { /// buffer store minimumCapacity elements, returns that buffer. /// Otherwise, returns `nil`. @inlinable - @usableFromInline internal mutating func requestUniqueMutableBackingBuffer(minimumCapacity: Int) -> NativeBuffer? { if _fastPath(isUniquelyReferenced()) { @@ -179,13 +166,11 @@ extension _ArrayBuffer { } @inlinable - @usableFromInline internal mutating func isMutableAndUniquelyReferenced() -> Bool { return isUniquelyReferenced() } @inlinable - @usableFromInline internal mutating func isMutableAndUniquelyReferencedOrPinned() -> Bool { return isUniquelyReferencedOrPinned() } @@ -194,7 +179,6 @@ extension _ArrayBuffer { /// containing the same number of elements as `self`, return it. /// Otherwise, return `nil`. @inlinable - @usableFromInline internal func requestNativeBuffer() -> NativeBuffer? { if !_isClassOrObjCExistential(Element.self) { return _native @@ -206,9 +190,8 @@ extension _ArrayBuffer { // checks one element. The reason for this is that the ARC optimizer does not // handle loops atm. and so can get blocked by the presence of a loop (over // the range). This loop is not necessary for a single element access. - @inlinable - @usableFromInline @inline(never) + @usableFromInline internal func _typeCheckSlowPath(_ index: Int) { if _fastPath(_isNative) { let element: AnyObject = cast(toBufferOf: AnyObject.self)._native[index] @@ -225,7 +208,6 @@ extension _ArrayBuffer { } @inlinable - @usableFromInline internal func _typeCheck(_ subRange: Range) { if !_isClassOrObjCExistential(Element.self) { return @@ -245,7 +227,6 @@ extension _ArrayBuffer { /// memory starting at `target`. Return a pointer "past the end" of the /// just-initialized memory. @inlinable - @usableFromInline @discardableResult internal func _copyContents( subRange bounds: Range, @@ -280,7 +261,6 @@ extension _ArrayBuffer { /// Returns a `_SliceBuffer` containing the given sub-range of elements in /// `bounds` from this buffer. @inlinable - @usableFromInline internal subscript(bounds: Range) -> _SliceBuffer { get { _typeCheck(bounds) @@ -333,21 +313,18 @@ extension _ArrayBuffer { /// /// - Precondition: The elements are known to be stored contiguously. @inlinable - @usableFromInline internal var firstElementAddress: UnsafeMutablePointer { _sanityCheck(_isNative, "must be a native buffer") return _native.firstElementAddress } @inlinable - @usableFromInline internal var firstElementAddressIfContiguous: UnsafeMutablePointer? { return _fastPath(_isNative) ? firstElementAddress : nil } /// The number of elements the buffer stores. @inlinable - @usableFromInline internal var count: Int { @inline(__always) get { @@ -366,7 +343,6 @@ extension _ArrayBuffer { /// Because the optimizer can hoist the original check it might have /// been invalidated by illegal user code. @inlinable - @usableFromInline internal func _checkInoutAndNativeBounds(_ index: Int, wasNative: Bool) { _precondition( _isNative == wasNative, @@ -386,7 +362,6 @@ extension _ArrayBuffer { /// inout violations. Because the optimizer can hoist the original /// check it might have been invalidated by illegal user code. @inlinable - @usableFromInline internal func _checkInoutAndNativeTypeCheckedBounds( _ index: Int, wasNativeTypeChecked: Bool ) { @@ -401,13 +376,11 @@ extension _ArrayBuffer { /// The number of elements the buffer can store without reallocation. @inlinable - @usableFromInline internal var capacity: Int { return _fastPath(_isNative) ? _native.capacity : _nonNative.count } @inlinable - @usableFromInline @inline(__always) internal func getElement(_ i: Int, wasNativeTypeChecked: Bool) -> Element { if _fastPath(wasNativeTypeChecked) { @@ -416,9 +389,8 @@ extension _ArrayBuffer { return unsafeBitCast(_getElementSlowPath(i), to: Element.self) } - @inlinable - @usableFromInline @inline(never) + @inlinable // @specializable internal func _getElementSlowPath(_ i: Int) -> AnyObject { _sanityCheck( _isClassOrObjCExistential(Element.self), @@ -446,7 +418,6 @@ extension _ArrayBuffer { /// Get or set the value of the ith element. @inlinable - @usableFromInline internal subscript(i: Int) -> Element { get { return getElement(i, wasNativeTypeChecked: _isNativeTypeChecked) @@ -470,7 +441,6 @@ extension _ArrayBuffer { /// underlying contiguous storage. If no such storage exists, it is /// created on-demand. @inlinable - @usableFromInline internal func withUnsafeBufferPointer( _ body: (UnsafeBufferPointer) throws -> R ) rethrows -> R { @@ -487,7 +457,6 @@ extension _ArrayBuffer { /// /// - Precondition: Such contiguous storage exists or the buffer is empty. @inlinable - @usableFromInline internal mutating func withUnsafeMutableBufferPointer( _ body: (UnsafeMutableBufferPointer) throws -> R ) rethrows -> R { @@ -502,7 +471,6 @@ extension _ArrayBuffer { /// An object that keeps the elements stored in this buffer alive. @inlinable - @usableFromInline internal var owner: AnyObject { return _fastPath(_isNative) ? _native._storage : _nonNative } @@ -511,7 +479,6 @@ extension _ArrayBuffer { /// /// - Precondition: This buffer is backed by a `_ContiguousArrayBuffer`. @inlinable - @usableFromInline internal var nativeOwner: AnyObject { _sanityCheck(_isNative, "Expect a native array") return _native._storage @@ -521,7 +488,6 @@ extension _ArrayBuffer { /// buffers address the same elements when they have the same /// identity and count. @inlinable - @usableFromInline internal var identity: UnsafeRawPointer { if _isNative { return _native.identity @@ -536,7 +502,6 @@ extension _ArrayBuffer { /// /// In an empty collection, `startIndex == endIndex`. @inlinable - @usableFromInline internal var startIndex: Int { return 0 } @@ -547,7 +512,6 @@ extension _ArrayBuffer { /// reachable from `startIndex` by zero or more applications of /// `index(after:)`. @inlinable - @usableFromInline internal var endIndex: Int { return count } @@ -559,7 +523,6 @@ extension _ArrayBuffer { internal typealias NativeBuffer = _ContiguousArrayBuffer @inlinable - @usableFromInline internal var _isNative: Bool { if !_isClassOrObjCExistential(Element.self) { return true @@ -570,7 +533,6 @@ extension _ArrayBuffer { /// `true`, if the array is native and does not need a deferred type check. @inlinable - @usableFromInline internal var _isNativeTypeChecked: Bool { if !_isClassOrObjCExistential(Element.self) { return true @@ -583,7 +545,6 @@ extension _ArrayBuffer { /// /// - Precondition: `_isNative`. @inlinable - @usableFromInline internal var _native: NativeBuffer { return NativeBuffer( _isClassOrObjCExistential(Element.self) @@ -594,13 +555,11 @@ extension _ArrayBuffer { /// /// - Precondition: `_isNativeTypeChecked`. @inlinable - @usableFromInline internal var _nativeTypeChecked: NativeBuffer { return NativeBuffer(_storage.nativeInstance_noSpareBits) } @inlinable - @usableFromInline internal var _nonNative: _NSArrayCore { @inline(__always) get { diff --git a/stdlib/public/core/ArrayBufferProtocol.swift b/stdlib/public/core/ArrayBufferProtocol.swift index aa683de880a67..9800c89c3ec90 100644 --- a/stdlib/public/core/ArrayBufferProtocol.swift +++ b/stdlib/public/core/ArrayBufferProtocol.swift @@ -129,13 +129,11 @@ internal protocol _ArrayBufferProtocol extension _ArrayBufferProtocol where Indices == Range{ @inlinable - @usableFromInline internal var subscriptBaseAddress: UnsafeMutablePointer { return firstElementAddress } // Make sure the compiler does not inline _copyBuffer to reduce code size. - @inlinable @inline(never) @usableFromInline internal init(copying buffer: Self) { @@ -148,7 +146,6 @@ extension _ArrayBufferProtocol where Indices == Range{ } @inlinable - @usableFromInline internal mutating func replaceSubrange( _ subrange: Range, with newCount: Int, diff --git a/stdlib/public/core/ArrayCast.swift b/stdlib/public/core/ArrayCast.swift index 034cd6fe15ea1..768da0203f00c 100644 --- a/stdlib/public/core/ArrayCast.swift +++ b/stdlib/public/core/ArrayCast.swift @@ -56,13 +56,11 @@ public func _arrayForceCast( @usableFromInline // FIXME(sil-serialize-all) internal struct _UnwrappingFailed : Error { @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init() {} } extension Optional { @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func unwrappedOrError() throws -> Wrapped { if let x = self { return x } throw _UnwrappingFailed() diff --git a/stdlib/public/core/Arrays.swift.gyb b/stdlib/public/core/Arrays.swift.gyb index 116ef2d46fb1f..c1cacbff4ecc6 100644 --- a/stdlib/public/core/Arrays.swift.gyb +++ b/stdlib/public/core/Arrays.swift.gyb @@ -137,12 +137,12 @@ if True: /// tasked with finding the first two days with absences in the session. To /// find the indices of the two days in question, follow these steps: /// -/// 1) Call `index(where:)` to find the index of the first element in the +/// 1) Call `firstIndex(where:)` to find the index of the first element in the /// `absences` array that is greater than zero. /// 2) Create a slice of the `absences` array starting after the index found in /// step 1. -/// 3) Call `index(where:)` again, this time on the slice created in step 2. -/// Where in some languages you might pass a starting index into an +/// 3) Call `firstIndex(where:)` again, this time on the slice created in step +/// 2. Where in some languages you might pass a starting index into an /// `indexOf` method to find the second day, in Swift you perform the same /// operation on a slice of the original array. /// 4) Print the results using the indices found in steps 1 and 3 on the @@ -150,9 +150,9 @@ if True: /// /// Here's an implementation of those steps: /// -/// if let i = absences.index(where: { $0 > 0 }) { // 1 +/// if let i = absences.firstIndex(where: { $0 > 0 }) { // 1 /// let absencesAfterFirst = absences[(i + 1)...] // 2 -/// if let j = absencesAfterFirst.index(where: { $0 > 0 }) { // 3 +/// if let j = absencesAfterFirst.firstIndex(where: { $0 > 0 }) { // 3 /// print("The first day with absences had \(absences[i]).") // 4 /// print("The second day with absences had \(absences[j]).") /// } @@ -293,7 +293,7 @@ if True: /// You can replace an existing element with a new value by assigning the new /// value to the subscript. /// -/// if let i = students.index(of: "Maxime") { +/// if let i = students.firstIndex(of: "Maxime") { /// students[i] = "Max" /// } /// // ["Ivy", "Jordell", "Liam", "Max", "Shakia"] @@ -474,7 +474,6 @@ public struct ${Self}: _DestructorSafeContainer { /// Initialization from an existing buffer does not have "array.init" /// semantics because the caller may retain an alias to buffer. @inlinable - @usableFromInline internal init(_buffer: _Buffer) { self._buffer = _buffer } @@ -483,7 +482,6 @@ public struct ${Self}: _DestructorSafeContainer { /// Initialization from an existing buffer does not have "array.init" /// semantics because the caller may retain an alias to buffer. @inlinable - @usableFromInline internal init(_buffer buffer: _ContiguousArrayBuffer) { self.init(_buffer: _Buffer(_buffer: buffer, shiftedToStartIndex: 0)) } @@ -535,7 +533,7 @@ extension ${Self}: RandomAccessCollection, MutableCollection { /// safe to use with `endIndex`. For example: /// /// let numbers = [10, 20, 30, 40, 50] - /// if let i = numbers.index(of: 30) { + /// if let i = numbers.firstIndex(of: 30) { /// print(numbers[i ..< numbers.endIndex]) /// } /// // Prints "[30, 40, 50]" @@ -786,7 +784,7 @@ extension ${Self}: RandomAccessCollection, MutableCollection { /// print(streetsSlice) /// // Prints "["Channing", "Douglas", "Evarts"]" /// - /// let i = streetsSlice.index(of: "Evarts") // 4 + /// let i = streetsSlice.firstIndex(of: "Evarts") // 4 /// print(streets[i!]) /// // Prints "Evarts" /// @@ -826,14 +824,12 @@ extension ${Self} { } @inlinable - @usableFromInline @_semantics("array.get_count") internal func _getCount() -> Int { return _buffer.count } @inlinable - @usableFromInline @_semantics("array.get_capacity") internal func _getCapacity() -> Int { return _buffer.capacity @@ -841,14 +837,13 @@ extension ${Self} { /// - Precondition: The array has a native buffer. @inlinable - @usableFromInline @_semantics("array.owner") internal func _getOwnerWithSemanticLabel_native() -> Builtin.NativeObject { return Builtin.unsafeCastToNativeObject(_buffer.nativeOwner) } /// - Precondition: The array has a native buffer. - @usableFromInline + @inlinable @inline(__always) internal func _getOwner_native() -> Builtin.NativeObject { #if _runtime(_ObjC) @@ -866,7 +861,6 @@ extension ${Self} { } @inlinable - @usableFromInline @_semantics("array.make_mutable") internal mutating func _makeMutableAndUnique() { if _slowPath(!_buffer.isMutableAndUniquelyReferenced()) { @@ -875,7 +869,6 @@ extension ${Self} { } @inlinable - @usableFromInline @_semantics("array.make_mutable") internal mutating func _makeMutableAndUniqueOrPinned() { if _slowPath(!_buffer.isMutableAndUniquelyReferencedOrPinned()) { @@ -886,7 +879,7 @@ extension ${Self} { /// Check that the given `index` is valid for subscripting, i.e. /// `0 ≤ index < count`. - @usableFromInline + @inlinable @inline(__always) internal func _checkSubscript_native(_ index: Int) { % if Self != 'Array': @@ -919,7 +912,6 @@ extension ${Self} { /// Check that the specified `index` is valid, i.e. `0 ≤ index ≤ count`. @inlinable - @usableFromInline @_semantics("array.check_index") internal func _checkIndex(_ index: Int) { _precondition(index <= endIndex, "${Self} index is out of range") @@ -946,7 +938,6 @@ extension ${Self} { } @inlinable - @usableFromInline @_semantics("array.get_element_address") internal func _getElementAddress(_ index: Int) -> UnsafeMutablePointer { return _buffer.subscriptBaseAddress + index @@ -1124,9 +1115,8 @@ extension ${Self} : RangeReplaceableCollection, ArrayProtocol { } } - @inlinable - @usableFromInline @inline(never) + @usableFromInline internal static func _allocateBufferUninitialized( minimumCapacity: Int ) -> _Buffer { @@ -1137,7 +1127,6 @@ extension ${Self} : RangeReplaceableCollection, ArrayProtocol { /// Construct a ${Self} of `count` uninitialized elements. @inlinable - @usableFromInline internal init(_uninitializedCount count: Int) { _precondition(count >= 0, "Can't construct ${Self} with count < 0") // Note: Sinking this constructor into an else branch below causes an extra @@ -1157,7 +1146,6 @@ extension ${Self} : RangeReplaceableCollection, ArrayProtocol { /// Entry point for `Array` literal construction; builds and returns /// a ${Self} of `count` uninitialized elements. @inlinable - @usableFromInline @_semantics("array.uninitialized") internal static func _allocateUninitialized( _ count: Int @@ -1174,7 +1162,6 @@ extension ${Self} : RangeReplaceableCollection, ArrayProtocol { /// /// - Precondition: `storage is _ContiguousArrayStorage`. @inlinable - @usableFromInline @_semantics("array.uninitialized") internal static func _adoptStorage( _ storage: __owned _ContiguousArrayStorage, count: Int @@ -1193,7 +1180,6 @@ extension ${Self} : RangeReplaceableCollection, ArrayProtocol { /// Entry point for aborting literal construction: deallocates /// a ${Self} containing only uninitialized elements. @inlinable - @usableFromInline internal mutating func _deallocateUninitialized() { // Set the count to zero and just release as normal. // Somewhat of a hack. @@ -1254,7 +1240,6 @@ extension ${Self} : RangeReplaceableCollection, ArrayProtocol { %if Self != 'Array': # // Array does not necessarily have contiguous storage @inlinable - @usableFromInline internal var _baseAddress: UnsafeMutablePointer { return _buffer.firstElementAddress } @@ -1352,9 +1337,8 @@ extension ${Self} : RangeReplaceableCollection, ArrayProtocol { /// Copy the contents of the current buffer to a new unique mutable buffer. /// The count of the new buffer is set to `oldCount`, the capacity of the /// new buffer is big enough to hold 'oldCount' + 1 elements. - @usableFromInline - @inlinable @inline(never) + @inlinable // @specializable internal mutating func _copyToNewBuffer(oldCount: Int) { let newCount = oldCount + 1 var newBuffer = _buffer._forceCreateUniqueMutableBuffer( @@ -1364,7 +1348,6 @@ extension ${Self} : RangeReplaceableCollection, ArrayProtocol { } @inlinable - @usableFromInline @_semantics("array.make_mutable") internal mutating func _makeUniqueAndReserveCapacityIfNotUnique() { if _slowPath(!_buffer.isMutableAndUniquelyReferenced()) { @@ -1373,7 +1356,6 @@ extension ${Self} : RangeReplaceableCollection, ArrayProtocol { } @inlinable - @usableFromInline @_semantics("array.mutate_unknown") internal mutating func _reserveCapacityAssumingUniqueBuffer(oldCount: Int) { // This is a performance optimization. This code used to be in an || @@ -1403,7 +1385,6 @@ extension ${Self} : RangeReplaceableCollection, ArrayProtocol { } @inlinable - @usableFromInline @_semantics("array.mutate_unknown") internal mutating func _appendElementAssumeUniqueAndCapacity( _ oldCount: Int, @@ -1493,7 +1474,6 @@ extension ${Self} : RangeReplaceableCollection, ArrayProtocol { } @inlinable - @usableFromInline @_semantics("array.reserve_capacity_for_append") internal mutating func reserveCapacityForAppend(newElementsCount: Int) { let oldCount = self.count @@ -1640,8 +1620,7 @@ extension ${Self} : RangeReplaceableCollection, ArrayProtocol { ) rethrows -> R? { return try withUnsafeMutableBufferPointer { (bufferPointer) -> R in - let r = try body(&bufferPointer) - return r + return try body(&bufferPointer) } } @@ -1683,7 +1662,7 @@ extension ${Self} : CustomStringConvertible, CustomDebugStringConvertible { } extension ${Self} { - @usableFromInline + @inlinable @_transparent internal func _cPointerArgs() -> (AnyObject?, UnsafeRawPointer?) { let p = _baseAddressIfContiguous @@ -1848,7 +1827,6 @@ extension ${Self} { // and CustomDebugStringConvertible using a bracketed list of elements, // like an array. @inlinable // FIXME(sil-serialize-all) -@usableFromInline // FIXME(sil-serialize-all) internal func _makeCollectionDescription (for items: C, withTypeName type: String?) -> String { var result = "" @@ -1876,7 +1854,6 @@ internal struct _InitializeMemoryFromCollection< C: Collection > : _PointerFunction { @inlinable - @usableFromInline internal func call(_ rawMemory: UnsafeMutablePointer, count: Int) { var p = rawMemory var q = newValues.startIndex @@ -1889,7 +1866,6 @@ internal struct _InitializeMemoryFromCollection< } @inlinable - @usableFromInline internal init(_ newValues: C) { self.newValues = newValues } @@ -1899,8 +1875,7 @@ internal struct _InitializeMemoryFromCollection< } extension _ArrayBufferProtocol { - @inlinable - @usableFromInline + @inlinable // FIXME @useableFromInline https://bugs.swift.org/browse/SR-7588 @inline(never) internal mutating func _arrayOutOfPlaceReplace( _ bounds: Range, @@ -1925,7 +1900,6 @@ extension _ArrayBufferProtocol { /// `s`. This test is never used to ensure memory safety; that is /// always guaranteed by measuring `s` once and re-using that value. @inlinable -@usableFromInline internal func _expectEnd(of s: C, is i: C.Index) { _debugPrecondition( i == s.endIndex, @@ -1933,7 +1907,6 @@ internal func _expectEnd(of s: C, is i: C.Index) { } @inlinable -@usableFromInline internal func _growArrayCapacity(_ capacity: Int) -> Int { return capacity * 2 } @@ -2016,9 +1989,8 @@ extension _ArrayBufferProtocol { /// The formula used to compute the new buffers capacity is: /// max(requiredCapacity, source.capacity) if newCount <= source.capacity /// max(requiredCapacity, _growArrayCapacity(source.capacity)) otherwise - @inlinable - @usableFromInline @inline(never) + @inlinable // @specializable internal func _forceCreateUniqueMutableBuffer( newCount: Int, requiredCapacity: Int ) -> _ContiguousArrayBuffer { @@ -2033,9 +2005,8 @@ extension _ArrayBufferProtocol { /// The formula used to compute the new buffers capacity is: /// max(minNewCapacity, source.capacity) if minNewCapacity <= source.capacity /// max(minNewCapacity, _growArrayCapacity(source.capacity)) otherwise - @inlinable - @usableFromInline @inline(never) + @inlinable // @specializable internal func _forceCreateUniqueMutableBuffer( countForNewBuffer: Int, minNewCapacity: Int ) -> _ContiguousArrayBuffer { @@ -2052,7 +2023,6 @@ extension _ArrayBufferProtocol { /// max(requiredCapacity, source.capacity) if minNewCapacity <= source.capacity /// max(requiredCapacity, _growArrayCapacity(source.capacity)) otherwise @inlinable - @usableFromInline internal func _forceCreateUniqueMutableBufferImpl( countForBuffer: Int, minNewCapacity: Int, requiredCapacity: Int @@ -2084,9 +2054,8 @@ extension _ArrayBufferProtocol { /// /// As an optimization, may move elements out of source rather than /// copying when it isUniquelyReferenced. - @inlinable - @usableFromInline @inline(never) + @inlinable // @specializable internal mutating func _arrayOutOfPlaceUpdate( _ dest: inout _ContiguousArrayBuffer, _ headCount: Int, // Count of initial source elements to copy/move @@ -2159,21 +2128,18 @@ extension _ArrayBufferProtocol { @_fixed_layout internal struct _IgnorePointer : _PointerFunction { @inlinable - @usableFromInline internal func call(_: UnsafeMutablePointer, count: Int) { _sanityCheck(count == 0) } - @usableFromInline @inlinable internal init() { } } extension _ArrayBufferProtocol { - @inlinable - @usableFromInline @inline(never) + @usableFromInline internal mutating func _outlinedMakeUniqueBuffer(bufferCount: Int) { if _fastPath( @@ -2188,7 +2154,6 @@ extension _ArrayBufferProtocol { /// Append items from `newItems` to a buffer. @inlinable - @usableFromInline internal mutating func _arrayAppendSequence( _ newItems: S ) where S.Element == Element { @@ -2298,16 +2263,28 @@ extension ${Self} : Equatable where Element : Equatable { } extension ${Self}: Hashable where Element: Hashable { + /// Hashes the essential components of this value by feeding them into the + /// given hasher. + /// + /// - Parameter hasher: The hasher to use when combining the components + /// of this instance. @inlinable // FIXME(sil-serialize-all) - public var hashValue: Int { - return _hashValue(for: self) + public func hash(into hasher: inout Hasher) { + hasher.combine(count) // discriminator + for element in self { + hasher.combine(element) + } } @inlinable // FIXME(sil-serialize-all) - public func _hash(into hasher: inout _Hasher) { + public func _rawHashValue(seed: (UInt64, UInt64)) -> Int { + var hasher = Hasher(_seed: seed) + // Note that we don't need to use delimiters/discriminators for top-level + // hashing. for element in self { - hasher.append(element) + hasher.combine(element) } + return hasher._finalize() } } diff --git a/stdlib/public/core/AssertCommon.swift b/stdlib/public/core/AssertCommon.swift index dd9e84683ecdf..a736a5d030f17 100644 --- a/stdlib/public/core/AssertCommon.swift +++ b/stdlib/public/core/AssertCommon.swift @@ -31,7 +31,6 @@ func _isDebugAssertConfiguration() -> Bool { } @inlinable // FIXME(sil-serialize-all) -@usableFromInline @_transparent internal func _isReleaseAssertConfiguration() -> Bool { // The values for the assert_configuration call are: @@ -64,7 +63,6 @@ func _isStdlibInternalChecksEnabled() -> Bool { } @inlinable // FIXME(sil-serialize-all) -@usableFromInline @_transparent internal func _fatalErrorFlags() -> UInt32 { @@ -137,6 +135,31 @@ internal func _assertionFailure( Builtin.int_trap() } +/// This function should be used only in the implementation of user-level +/// assertions. +/// +/// This function should not be inlined because it is cold and inlining just +/// bloats code. +@usableFromInline // FIXME(sil-serialize-all) +@inline(never) +internal func _assertionFailure( + _ prefix: StaticString, _ message: String, + flags: UInt32 +) -> Never { + prefix.withUTF8Buffer { + (prefix) -> Void in + message._withUnsafeBufferPointerToUTF8 { + (messageUTF8) -> Void in + _swift_stdlib_reportFatalError( + prefix.baseAddress!, CInt(prefix.count), + messageUTF8.baseAddress!, CInt(messageUTF8.count), + flags) + } + } + + Builtin.int_trap() +} + /// This function should be used only in the implementation of stdlib /// assertions. /// @@ -238,3 +261,37 @@ func _undefined( ) -> T { _assertionFailure("Fatal error", message(), file: file, line: line, flags: 0) } + +/// Called when falling off the end of a switch and the type can be represented +/// as a raw value. +/// +/// This function should not be inlined because it is cold and inlining just +/// bloats code. It doesn't take a source location because it's most important +/// in release builds anyway (old apps that are run on new OSs). +@inline(never) +@usableFromInline // COMPILER_INTRINSIC +internal func _diagnoseUnexpectedEnumCaseValue( + type: SwitchedValue.Type, + rawValue: RawValue +) -> Never { + _assertionFailure("Fatal error", + "unexpected enum case '\(type)(rawValue: \(rawValue))'", + flags: _fatalErrorFlags()) +} + +/// Called when falling off the end of a switch and the value is not safe to +/// print. +/// +/// This function should not be inlined because it is cold and inlining just +/// bloats code. It doesn't take a source location because it's most important +/// in release builds anyway (old apps that are run on new OSs). +@inline(never) +@usableFromInline // COMPILER_INTRINSIC +internal func _diagnoseUnexpectedEnumCase( + type: SwitchedValue.Type +) -> Never { + _assertionFailure( + "Fatal error", + "unexpected enum case while switching on value of type '\(type)'", + flags: _fatalErrorFlags()) +} diff --git a/stdlib/public/core/BidirectionalCollection.swift b/stdlib/public/core/BidirectionalCollection.swift index 77d037cfc5dfa..3665c567e9043 100644 --- a/stdlib/public/core/BidirectionalCollection.swift +++ b/stdlib/public/core/BidirectionalCollection.swift @@ -119,7 +119,7 @@ where SubSequence: BidirectionalCollection, Indices: BidirectionalCollection { /// print(streetsSlice) /// // Prints "["Channing", "Douglas", "Evarts"]" /// - /// let index = streetsSlice.index(of: "Evarts") // 4 + /// let index = streetsSlice.firstIndex(of: "Evarts") // 4 /// print(streets[index!]) /// // Prints "Evarts" /// diff --git a/stdlib/public/core/Bool.swift b/stdlib/public/core/Bool.swift index 4c5feade9bf46..e757c044e86e3 100644 --- a/stdlib/public/core/Bool.swift +++ b/stdlib/public/core/Bool.swift @@ -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 - 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 @@ -76,7 +76,6 @@ public struct Bool { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline @_transparent internal init(_ v: Builtin.Int1) { self._value = v } @@ -87,6 +86,53 @@ public struct Bool { public init(_ value: Bool) { self = value } + + /// Returns a random Boolean value, using the given generator as a source for + /// randomness. + /// + /// This method returns `true` and `false` with equal probability. Use this + /// method to generate a random Boolean value when you are using a custom + /// random number generator. + /// + /// let flippedHeads = Boolean.random(using: &myGenerator) + /// if flippedHeads { + /// print("Heads, you win!") + /// } else { + /// print("Maybe another try?") + /// } + /// + /// - Parameter generator: The random number generator to use when creating + /// the new random value. + /// - Returns: Either `true` or `false`, randomly chosen with equal + /// probability. + @inlinable + public static func random( + using generator: inout T + ) -> Bool { + return (generator.next() >> 17) & 1 == 0 + } + + /// Returns a random Boolean value. + /// + /// This method returns `true` and `false` with equal probability. + /// + /// let flippedHeads = Boolean.random() + /// if flippedHeads { + /// print("Heads, you win!") + /// } else { + /// print("Maybe another try?") + /// } + /// + /// `Bool.random()` uses the default random generator, `Random.default`. The + /// call in the example above is equivalent to + /// `Bool.random(using: &Random.default)`. + /// + /// - Returns: Either `true` or `false`, randomly chosen with equal + /// probability. + @inlinable + public static func random() -> Bool { + return Bool.random(using: &Random.default) + } } extension Bool : _ExpressibleByBuiltinBooleanLiteral, ExpressibleByBooleanLiteral { @@ -145,24 +191,7 @@ extension Bool : CustomStringConvertible { public // COMPILER_INTRINSIC func _getBool(_ v: Builtin.Int1) -> Bool { return Bool(v) } -extension Bool : Equatable, Hashable { - /// The hash value for the Boolean value. - /// - /// Two values that are equal always have equal hash values. - /// - /// - Note: The hash value is not guaranteed to be stable across different - /// invocations of the same program. Do not persist the hash value across - /// program runs. - @inlinable // FIXME(sil-serialize-all) - public var hashValue: Int { - return _hashValue(for: self) - } - - @inlinable // FIXME(sil-serialize-all) - public func _hash(into hasher: inout _Hasher) { - hasher.append((self ? 1 : 0) as UInt8) - } - +extension Bool: Equatable { @inlinable // FIXME(sil-serialize-all) @_transparent public static func == (lhs: Bool, rhs: Bool) -> Bool { @@ -170,6 +199,18 @@ extension Bool : Equatable, Hashable { } } +extension Bool: Hashable { + /// Hashes the essential components of this value by feeding them into the + /// given hasher. + /// + /// - Parameter hasher: The hasher to use when combining the components + /// of this instance. + @inlinable // FIXME(sil-serialize-all) + public func hash(into hasher: inout Hasher) { + hasher.combine((self ? 1 : 0) as UInt8) + } +} + extension Bool : LosslessStringConvertible { /// Creates a new Boolean value from the given string. /// diff --git a/stdlib/public/core/BridgeObjectiveC.swift b/stdlib/public/core/BridgeObjectiveC.swift index fd6e934192a6f..b26c720f31833 100644 --- a/stdlib/public/core/BridgeObjectiveC.swift +++ b/stdlib/public/core/BridgeObjectiveC.swift @@ -98,7 +98,6 @@ public struct _BridgeableMetatype: _ObjectiveCBridgeable { internal var value: AnyObject.Type @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(value: AnyObject.Type) { self.value = value } @@ -171,7 +170,7 @@ public func _bridgeAnythingToObjectiveC(_ x: T) -> AnyObject { } @_silgen_name("") -public func _bridgeAnythingNonVerbatimToObjectiveC(_ x: T) -> AnyObject +public func _bridgeAnythingNonVerbatimToObjectiveC(_ x: __owned T) -> AnyObject /// Convert a purportedly-nonnull `id` value from Objective-C into an Any. /// @@ -342,7 +341,6 @@ public func _getBridgedNonVerbatimObjectiveCType(_: T.Type) -> Any.Type? // -- Pointer argument bridging @inlinable // FIXME(sil-serialize-all) -@usableFromInline // FIXME(sil-serialize-all) @_transparent internal var _nilNativeObject: AnyObject? { return nil @@ -472,7 +470,6 @@ public struct AutoreleasingUnsafeMutablePointer /// - Warning: Accessing `pointee` as a type that is unrelated to /// the underlying memory's bound type is undefined. @inlinable // FIXME(sil-serialize-all) - @usableFromInline @_transparent internal init(_ from: UnsafePointer) { self._rawValue = from._rawValue @@ -488,7 +485,6 @@ public struct AutoreleasingUnsafeMutablePointer /// - Warning: Accessing `pointee` as a type that is unrelated to /// the underlying memory's bound type is undefined. @inlinable // FIXME(sil-serialize-all) - @usableFromInline @_transparent internal init?(_ from: UnsafePointer?) { guard let unwrapped = from else { return nil } @@ -601,14 +597,12 @@ internal struct _CocoaFastEnumerationStackBuf { internal var _item15: UnsafeRawPointer? @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @_transparent internal var count: Int { return 16 } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal init() { _item0 = nil _item1 = _item0 diff --git a/stdlib/public/core/BridgeStorage.swift b/stdlib/public/core/BridgeStorage.swift index a22f3751d275a..e836a09f2563b 100644 --- a/stdlib/public/core/BridgeStorage.swift +++ b/stdlib/public/core/BridgeStorage.swift @@ -158,7 +158,6 @@ struct _BridgeStorage< //===--- private --------------------------------------------------------===// @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var _isTagged: Bool { @inline(__always) get { return Bool(Builtin.classifyBridgeObject(rawValue).isObjCTaggedPointer) diff --git a/stdlib/public/core/Builtin.swift b/stdlib/public/core/Builtin.swift index cd5f912535a42..4c5761f00047b 100644 --- a/stdlib/public/core/Builtin.swift +++ b/stdlib/public/core/Builtin.swift @@ -19,7 +19,6 @@ import SwiftShims // marked `@inline(__always)` to make primary `_roundUp` entry points seem // cheap enough for the inliner. @inlinable // FIXME(sil-serialize-all) -@usableFromInline @inline(__always) internal func _roundUpImpl(_ offset: UInt, toAlignment alignment: Int) -> UInt { _sanityCheck(alignment > 0) @@ -33,13 +32,11 @@ internal func _roundUpImpl(_ offset: UInt, toAlignment alignment: Int) -> UInt { } @inlinable // FIXME(sil-serialize-all) -@usableFromInline internal func _roundUp(_ offset: UInt, toAlignment alignment: Int) -> UInt { return _roundUpImpl(offset, toAlignment: alignment) } @inlinable // FIXME(sil-serialize-all) -@usableFromInline internal func _roundUp(_ offset: Int, toAlignment alignment: Int) -> Int { _sanityCheck(offset >= 0) return Int(_roundUpImpl(UInt(bitPattern: offset), toAlignment: alignment)) @@ -104,14 +101,12 @@ public func _identityCast(_ x: T, to expectedType: U.Type) -> U { /// `unsafeBitCast` something to `AnyObject`. @inlinable // FIXME(sil-serialize-all) -@usableFromInline // FIXME(sil-serialize-all) @_transparent internal func _reinterpretCastToAnyObject(_ x: T) -> AnyObject { return unsafeBitCast(x, to: AnyObject.self) } @inlinable -@usableFromInline @_transparent internal func == ( lhs: Builtin.NativeObject, rhs: Builtin.NativeObject @@ -120,7 +115,6 @@ internal func == ( } @inlinable -@usableFromInline @_transparent internal func != ( lhs: Builtin.NativeObject, rhs: Builtin.NativeObject @@ -129,7 +123,6 @@ internal func != ( } @inlinable -@usableFromInline @_transparent internal func == ( lhs: Builtin.RawPointer, rhs: Builtin.RawPointer @@ -138,7 +131,6 @@ internal func == ( } @inlinable -@usableFromInline @_transparent internal func != (lhs: Builtin.RawPointer, rhs: Builtin.RawPointer) -> Bool { return !(lhs == rhs) @@ -178,7 +170,6 @@ public func != (t0: Any.Type?, t1: Any.Type?) -> Bool { /// known at compile-time to be true. If condition is false, or true /// but not a compile-time constant, this call has no effect. @inlinable // FIXME(sil-serialize-all) -@usableFromInline // FIXME(sil-serialize-all) @_transparent internal func _unreachable(_ condition: Bool = true) { if condition { @@ -191,21 +182,18 @@ internal func _unreachable(_ condition: Bool = true) { /// Tell the optimizer that this code is unreachable if this builtin is /// reachable after constant folding build configuration builtins. @inlinable // FIXME(sil-serialize-all) -@usableFromInline // FIXME(sil-serialize-all) @_transparent internal func _conditionallyUnreachable() -> Never { Builtin.conditionallyUnreachable() } @inlinable // FIXME(sil-serialize-all) -@usableFromInline @_silgen_name("_swift_isClassOrObjCExistentialType") internal func _swift_isClassOrObjCExistentialType(_ x: T.Type) -> Bool /// Returns `true` iff `T` is a class type or an `@objc` existential such as /// `AnyObject`. @inlinable // FIXME(sil-serialize-all) -@usableFromInline @inline(__always) internal func _isClassOrObjCExistential(_ x: T.Type) -> Bool { @@ -288,7 +276,6 @@ public func _getUnsafePointerToStoredProperties(_ x: AnyObject) // mandatory generic inlining. @inlinable // FIXME(sil-serialize-all) -@usableFromInline @_transparent @_semantics("branchhint") internal func _branchHint(_ actual: Bool, expected: Bool) -> Bool { @@ -328,12 +315,10 @@ public func _onFastPath() { // the type of argument to be AnyClass. This is currently not possible // when using RuntimeShims.h @inlinable // FIXME(sil-serialize-all) -@usableFromInline @_silgen_name("_objcClassUsesNativeSwiftReferenceCounting") internal func _usesNativeSwiftReferenceCounting(_ theClass: AnyClass) -> Bool #else @inlinable // FIXME(sil-serialize-all) -@usableFromInline @inline(__always) internal func _usesNativeSwiftReferenceCounting(_ theClass: AnyClass) -> Bool { return true @@ -341,19 +326,16 @@ internal func _usesNativeSwiftReferenceCounting(_ theClass: AnyClass) -> Bool { #endif @inlinable // FIXME(sil-serialize-all) -@usableFromInline // FIXME(sil-serialize-all) @_silgen_name("_getSwiftClassInstanceExtents") internal func getSwiftClassInstanceExtents(_ theClass: AnyClass) -> (negative: UInt, positive: UInt) @inlinable // FIXME(sil-serialize-all) -@usableFromInline // FIXME(sil-serialize-all) @_silgen_name("_getObjCClassInstanceExtents") internal func getObjCClassInstanceExtents(_ theClass: AnyClass) -> (negative: UInt, positive: UInt) @inlinable // FIXME(sil-serialize-all) -@usableFromInline // FIXME(sil-serialize-all) @inline(__always) internal func _class_getInstancePositiveExtentSize(_ theClass: AnyClass) -> Int { #if _runtime(_ObjC) @@ -364,7 +346,6 @@ internal func _class_getInstancePositiveExtentSize(_ theClass: AnyClass) -> Int } @inlinable -@usableFromInline internal func _isValidAddress(_ address: UInt) -> Bool { // TODO: define (and use) ABI max valid pointer value @@ -376,19 +357,16 @@ func _isValidAddress(_ address: UInt) -> Bool { // TODO(): Get rid of superfluous UInt constructor // calls @inlinable // FIXME(sil-serialize-all) -@usableFromInline internal var _objCTaggedPointerBits: UInt { @inline(__always) get { return UInt(_swift_BridgeObject_TaggedPointerBits) } } @inlinable // FIXME(sil-serialize-all) -@usableFromInline internal var _objectPointerSpareBits: UInt { @inline(__always) get { return UInt(_swift_abi_SwiftSpareBitsMask) & ~_objCTaggedPointerBits } } @inlinable // FIXME(sil-serialize-all) -@usableFromInline internal var _objectPointerLowSpareBitShift: UInt { @inline(__always) get { _sanityCheck(_swift_abi_ObjCReservedLowBits < 2, @@ -400,13 +378,11 @@ internal var _objectPointerLowSpareBitShift: UInt { #if arch(i386) || arch(arm) || arch(powerpc64) || arch(powerpc64le) || arch( s390x) @inlinable // FIXME(sil-serialize-all) -@usableFromInline internal var _objectPointerIsObjCBit: UInt { @inline(__always) get { return 0x0000_0002 } } #else @inlinable // FIXME(sil-serialize-all) -@usableFromInline internal var _objectPointerIsObjCBit: UInt { @inline(__always) get { return 0x4000_0000_0000_0000 } } @@ -414,7 +390,6 @@ internal var _objectPointerIsObjCBit: UInt { /// Extract the raw bits of `x`. @inlinable // FIXME(sil-serialize-all) -@usableFromInline @inline(__always) internal func _bitPattern(_ x: Builtin.BridgeObject) -> UInt { return UInt(Builtin.castBitPatternFromBridgeObject(x)) @@ -422,20 +397,17 @@ internal func _bitPattern(_ x: Builtin.BridgeObject) -> UInt { /// Extract the raw spare bits of `x`. @inlinable // FIXME(sil-serialize-all) -@usableFromInline @inline(__always) internal func _nonPointerBits(_ x: Builtin.BridgeObject) -> UInt { return _bitPattern(x) & _objectPointerSpareBits } @inlinable // FIXME(sil-serialize-all) -@usableFromInline @inline(__always) internal func _isObjCTaggedPointer(_ x: AnyObject) -> Bool { return (Builtin.reinterpretCast(x) & _objCTaggedPointerBits) != 0 } @inlinable // FIXME(sil-serialize-all) -@usableFromInline @inline(__always) internal func _isObjCTaggedPointer(_ x: UInt) -> Bool { return (x & _objCTaggedPointerBits) != 0 @@ -443,23 +415,22 @@ internal func _isObjCTaggedPointer(_ x: UInt) -> Bool { /// TODO: describe extras -@inlinable /*@usableFromInline*/ @inline(__always) public // FIXME +@inlinable @inline(__always) public // FIXME func _isTaggedObject(_ x: Builtin.BridgeObject) -> Bool { return _bitPattern(x) & _objCTaggedPointerBits != 0 } -@inlinable /*@usableFromInline*/ @inline(__always) public // FIXME +@inlinable @inline(__always) public // FIXME func _isNativePointer(_ x: Builtin.BridgeObject) -> Bool { return ( _bitPattern(x) & (_objCTaggedPointerBits | _objectPointerIsObjCBit) ) == 0 } -@inlinable /*@usableFromInline*/ @inline(__always) public // FIXME +@inlinable @inline(__always) public // FIXME func _isNonTaggedObjCPointer(_ x: Builtin.BridgeObject) -> Bool { return !_isTaggedObject(x) && !_isNativePointer(x) } @inlinable -@usableFromInline @inline(__always) func _getNonTagBits(_ x: Builtin.BridgeObject) -> UInt { // Zero out the tag bits, and leave them all at the top. @@ -593,7 +564,6 @@ extension ManagedBufferPointer { /// - Precondition: `bits & _objectPointerIsObjCBit == 0`, /// `bits & _objectPointerSpareBits == bits`. @inlinable // FIXME(sil-serialize-all) -@usableFromInline @inline(__always) internal func _makeNativeBridgeObject( _ nativeObject: AnyObject, _ bits: UInt @@ -627,7 +597,6 @@ func _makeObjCBridgeObject( /// `object` is either a native object, or `bits == /// _objectPointerIsObjCBit`. @inlinable // FIXME(sil-serialize-all) -@usableFromInline @inline(__always) internal func _makeBridgeObject( _ object: AnyObject, _ bits: UInt @@ -691,7 +660,6 @@ func _getSuperclass(_ t: Any.Type) -> AnyClass? { /// Returns `true` if `object` is uniquely referenced. @inlinable // FIXME(sil-serialize-all) -@usableFromInline @_transparent internal func _isUnique(_ object: inout T) -> Bool { return Bool(Builtin.isUnique(&object)) @@ -699,7 +667,6 @@ internal func _isUnique(_ object: inout T) -> Bool { /// Returns `true` if `object` is uniquely referenced or pinned. @inlinable // FIXME(sil-serialize-all) -@usableFromInline @_transparent internal func _isUniqueOrPinned(_ object: inout T) -> Bool { return Bool(Builtin.isUniqueOrPinned(&object)) @@ -757,7 +724,6 @@ func _isOptional(_ type: T.Type) -> Bool { /// Extract an object reference from an Any known to contain an object. @inlinable // FIXME(sil-serialize-all) -@usableFromInline // FIXME(sil-serialize-all) internal func _unsafeDowncastToAnyObject(fromAny any: Any) -> AnyObject { _sanityCheck(type(of: any) is AnyObject.Type || type(of: any) is AnyObject.Protocol, diff --git a/stdlib/public/core/CMakeLists.txt b/stdlib/public/core/CMakeLists.txt index 9df70cbb48cfd..3498c67ee0ee2 100644 --- a/stdlib/public/core/CMakeLists.txt +++ b/stdlib/public/core/CMakeLists.txt @@ -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 - 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 @@ -66,6 +66,7 @@ set(SWIFTLIB_ESSENTIAL AnyHashable.swift # END WORKAROUND HashedCollectionsAnyHashableExtensions.swift + Hasher.swift Hashing.swift HeapBuffer.swift ICU.swift @@ -96,6 +97,7 @@ set(SWIFTLIB_ESSENTIAL Policy.swift PrefixWhile.swift Print.swift + Random.swift RandomAccessCollection.swift Range.swift RangeReplaceableCollection.swift @@ -105,7 +107,7 @@ set(SWIFTLIB_ESSENTIAL Reverse.swift Runtime.swift.gyb RuntimeFunctionCounters.swift - SipHash.swift.gyb + SipHash.swift SentinelCollection.swift Sequence.swift SequenceAlgorithms.swift @@ -237,6 +239,9 @@ endif() list(APPEND swift_stdlib_compile_flags "-Xllvm" "-sil-inline-generics") list(APPEND swift_stdlib_compile_flags "-Xllvm" "-sil-partial-specialization") list(APPEND swift_stdlib_compile_flags "-Xfrontend" "-enable-sil-ownership") +if(SWIFT_STDLIB_ENABLE_STDLIBCORE_EXCLUSIVITY_CHECKING) + list(APPEND swift_stdlib_compile_flags "-enforce-exclusivity=checked") +endif() if(SWIFT_CHECK_ESSENTIAL_STDLIB) add_swift_library(swift_stdlib_essential ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_STDLIB IS_STDLIB_CORE @@ -247,7 +252,7 @@ endif() set(shared_only_libs) if(SWIFT_BUILD_STATIC_STDLIB AND "${SWIFT_HOST_VARIANT_SDK}" STREQUAL "LINUX") - list(APPEND shared_only_libs swiftImageInspectionShared) + list(APPEND swift_core_private_link_libraries swiftImageInspectionShared) endif() add_swift_library(swiftCore ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_STDLIB IS_STDLIB_CORE diff --git a/stdlib/public/core/CString.swift b/stdlib/public/core/CString.swift index a86974dee386b..dc1fabca4255d 100644 --- a/stdlib/public/core/CString.swift +++ b/stdlib/public/core/CString.swift @@ -43,7 +43,6 @@ extension String { /// // Prints "Caf�" /// /// - Parameter cString: A pointer to a null-terminated UTF-8 code sequence. - @inlinable // FIXME(sil-serialize-all) public init(cString: UnsafePointer) { self = _decodeValidCString(cString, repair: true) } @@ -53,7 +52,6 @@ extension String { /// /// This is identical to init(cString: UnsafePointer but operates on an /// unsigned sequence of bytes. - @inlinable // FIXME(sil-serialize-all) public init(cString: UnsafePointer) { self = _decodeValidCString(cString, repair: true) } @@ -84,7 +82,6 @@ extension String { /// // Prints "nil" /// /// - Parameter cString: A pointer to a null-terminated UTF-8 code sequence. - @inlinable // FIXME(sil-serialize-all) public init?(validatingUTF8 cString: UnsafePointer) { guard let str = _decodeCString(cString, repair: false) else { return nil @@ -134,7 +131,8 @@ extension String { /// - Returns: A tuple with the new string and a Boolean value that indicates /// whether any repairs were made. If `isRepairing` is `false` and an /// ill-formed sequence is detected, this method returns `nil`. - @inlinable // FIXME(sil-serialize-all) + @_specialize(where Encoding == Unicode.UTF8) + @_specialize(where Encoding == Unicode.UTF16) public static func decodeCString( _ cString: UnsafePointer?, as encoding: Encoding.Type, @@ -157,7 +155,6 @@ extension String { /// From a non-`nil` `UnsafePointer` to a null-terminated string /// with possibly-transient lifetime, create a null-terminated array of 'C' char. /// Returns `nil` if passed a null pointer. -@inlinable // FIXME(sil-serialize-all) public func _persistCString(_ p: UnsafePointer?) -> [CChar]? { guard let s = p else { return nil @@ -170,8 +167,6 @@ public func _persistCString(_ p: UnsafePointer?) -> [CChar]? { return result } -@inlinable -@usableFromInline internal func _decodeValidCString( _ cString: UnsafePointer, repair: Bool ) -> String { @@ -183,8 +178,6 @@ internal func _decodeValidCString( } } -@inlinable -@usableFromInline internal func _decodeValidCString( _ cString: UnsafePointer, repair: Bool ) -> String { @@ -193,8 +186,6 @@ internal func _decodeValidCString( return String._fromWellFormedUTF8CodeUnitSequence(bufPtr, repair: repair) } -@inlinable -@usableFromInline internal func _decodeCString( _ cString: UnsafePointer, repair: Bool ) -> String? { @@ -206,8 +197,6 @@ internal func _decodeCString( } } -@inlinable -@usableFromInline internal func _decodeCString( _ cString: UnsafePointer, repair: Bool ) -> String? { @@ -220,8 +209,6 @@ internal func _decodeCString( /// the given pointer using the specified encoding. /// /// This internal helper takes the string length as an argument. -@inlinable // FIXME(sil-serialize-all) -@usableFromInline // FIXME(sil-serialize-all) internal func _decodeCString( _ cString: UnsafePointer, as encoding: Encoding.Type, length: Int, diff --git a/stdlib/public/core/CTypes.swift b/stdlib/public/core/CTypes.swift index 29da7eeb69f90..6171591fd65ed 100644 --- a/stdlib/public/core/CTypes.swift +++ b/stdlib/public/core/CTypes.swift @@ -110,7 +110,6 @@ public struct OpaquePointer { internal var _rawValue: Builtin.RawPointer @inlinable // FIXME(sil-serialize-all) - @usableFromInline @_transparent internal init(_ v: Builtin.RawPointer) { self._rawValue = v @@ -175,19 +174,14 @@ extension OpaquePointer: Equatable { } extension OpaquePointer: Hashable { - /// The pointer's hash value. + /// Hashes the essential components of this value by feeding them into the + /// given hasher. /// - /// The hash value is not guaranteed to be stable across different - /// invocations of the same program. Do not persist the hash value across - /// program runs. - @inlinable // FIXME(sil-serialize-all) - public var hashValue: Int { - return _hashValue(for: self) - } - + /// - Parameter hasher: The hasher to use when combining the components + /// of this instance. @inlinable // FIXME(sil-serialize-all) - public func _hash(into hasher: inout _Hasher) { - hasher.append(Int(Builtin.ptrtoint_Word(_rawValue))) + public func hash(into hasher: inout Hasher) { + hasher.combine(Int(Builtin.ptrtoint_Word(_rawValue))) } } @@ -248,7 +242,6 @@ extension CVaListPointer : CustomDebugStringConvertible { } } -@usableFromInline @inlinable internal func _memcpy( dest destination: UnsafeMutableRawPointer, @@ -268,7 +261,6 @@ internal func _memcpy( /// /// The memory regions `source.. internal static func _smallValue(_ value: Builtin.Int63) -> UInt64 { return UInt64(Builtin.zext_Int63_Int64(value)) @@ -86,13 +86,11 @@ public struct Character { typealias UTF16View = String.UTF16View @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var utf16: UTF16View { return String(self).utf16 } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal init(_smallRepresentation b: _SmallUTF16) { _sanityCheck(Int64(b._storage) >= 0) _representation = .smallUTF16( @@ -100,7 +98,6 @@ public struct Character { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal init(_largeRepresentation storage: _UTF16StringStorage) { _representation = .large(storage) } @@ -112,7 +109,6 @@ public struct Character { /// that formally because of grapheme cluster literals and the shifting /// sands of Unicode. https://bugs.swift.org/browse/SR-4955 @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal init(_largeRepresentationString s: String) { let storage = s._guts._extractNativeStorage(of: UTF16.CodeUnit.self) self.init(_largeRepresentation: storage) @@ -272,7 +268,6 @@ extension Character /// Construct a Character from a _StringGuts, assuming it consists of exactly /// one extended grapheme cluster. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_unverified guts: _StringGuts) { self = _visitGuts(guts, ascii: { ascii in @@ -292,7 +287,6 @@ extension Character /// Construct a Character from a slice of a _StringGuts, assuming /// the specified range covers exactly one extended grapheme cluster. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_unverified guts: _StringGuts, range: Range) { self = _visitGuts( guts, range: (range, performBoundsCheck: true), @@ -311,7 +305,6 @@ extension Character } @inlinable - @usableFromInline internal init(_singleCodeUnit cu: UInt16) { _sanityCheck(UTF16._isScalar(cu)) @@ -320,7 +313,6 @@ extension Character } @inlinable - @usableFromInline internal init(_codeUnitPair first: UInt16, _ second: UInt16) { _sanityCheck( @@ -333,7 +325,6 @@ extension Character } @inlinable - @usableFromInline internal init(_unverified storage: _SwiftStringStorage) { if _fastPath(storage.count <= 4) { @@ -350,7 +341,6 @@ extension Character } @inlinable - @usableFromInline internal init(_unverified variant: V) { if _fastPath(variant.count <= 4) { @@ -386,7 +376,6 @@ extension Character { internal typealias _SmallUTF16 = _UIntBuffer @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal var _smallUTF16 : _SmallUTF16? { guard case .smallUTF16(let _63bits) = _representation else { return nil } _onFastPath() @@ -400,7 +389,6 @@ extension Character { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal var _largeUTF16 : _UTF16StringStorage? { guard case .large(let storage) = _representation else { return nil } return storage @@ -409,7 +397,6 @@ extension Character { extension Character { @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal var _count : Int { if let small = _smallUTF16 { return small.count } return _largeUTF16._unsafelyUnwrappedUnchecked.count @@ -443,7 +430,7 @@ extension String { /// 0x7FFFFFFFFFFFFF80 or greater is an invalid UTF-8 sequence, we know if a /// value is ASCII by checking if it is greater than or equal to /// 0x7FFFFFFFFFFFFF00. -// FIXME(sil-serialize-all): Should be @inlinable @usableFromInline +// FIXME(sil-serialize-all): Should be @inlinable // internal var _minASCIICharReprBuiltin: Builtin.Int63 { @inline(__always) get { @@ -490,14 +477,16 @@ extension Character : Comparable { } extension Character: Hashable { - /// The character's hash value. + // not @inlinable (performance) + /// Hashes the essential components of this value by feeding them into the + /// given hasher. /// - /// Hash values are not guaranteed to be equal across different executions of - /// your program. Do not save hash values to use during a future execution. - @inlinable // FIXME(sil-serialize-all) - public var hashValue: Int { + /// - Parameter hasher: The hasher to use when combining the components + /// of this instance. + @effects(releasenone) + public func hash(into hasher: inout Hasher) { // FIXME(performance): constructing a temporary string is extremely // wasteful and inefficient. - return String(self).hashValue + hasher.combine(String(self)) } } diff --git a/stdlib/public/core/CharacterUnicodeScalars.swift b/stdlib/public/core/CharacterUnicodeScalars.swift index ce56aca8bfecc..c76cde65705b3 100644 --- a/stdlib/public/core/CharacterUnicodeScalars.swift +++ b/stdlib/public/core/CharacterUnicodeScalars.swift @@ -16,7 +16,6 @@ extension Character { internal let _base: Character @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_base: Character) { self._base = _base } @@ -35,7 +34,6 @@ extension Character.UnicodeScalarView { internal var _base: IndexingIterator @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_base: IndexingIterator) { self._base = _base } @@ -67,7 +65,6 @@ extension Character.UnicodeScalarView { internal let _stride: UInt8 @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_encodedOffset: Int, _scalar: Unicode.UTF16.EncodedScalar, _stride: UInt8) { self._encodedOffset = _encodedOffset self._scalar = _scalar diff --git a/stdlib/public/core/ClosedRange.swift b/stdlib/public/core/ClosedRange.swift index c95fc7d33c887..c9c1917a05133 100644 --- a/stdlib/public/core/ClosedRange.swift +++ b/stdlib/public/core/ClosedRange.swift @@ -168,19 +168,19 @@ extension ClosedRange.Index : Comparable { extension ClosedRange.Index: Hashable where Bound: Strideable, Bound.Stride: SignedInteger, Bound: Hashable { + /// Hashes the essential components of this value by feeding them into the + /// given hasher. + /// + /// - Parameter hasher: The hasher to use when combining the components + /// of this instance. @inlinable // FIXME(sil-serialize-all) - public var hashValue: Int { - return _hashValue(for: self) - } - - @inlinable // FIXME(sil-serialize-all) - public func _hash(into hasher: inout _Hasher) { + public func hash(into hasher: inout Hasher) { switch self { case .inRange(let value): - hasher.append(0 as Int8) - hasher.append(value) + hasher.combine(0 as Int8) + hasher.combine(value) case .pastEnd: - hasher.append(1 as Int8) + hasher.combine(1 as Int8) } } } @@ -308,6 +308,12 @@ where Bound : Strideable, Bound.Stride : SignedInteger return lowerBound <= element && element <= upperBound ? .inRange(element) : nil } + + @inlinable + public func _customLastIndexOfEquatableElement(_ element: Bound) -> Index?? { + // The first and last elements are the same because each element is unique. + return _customIndexOfEquatableElement(element) + } } extension Comparable { @@ -390,14 +396,9 @@ extension ClosedRange: Equatable { extension ClosedRange: Hashable where Bound: Hashable { @inlinable // FIXME(sil-serialize-all) - public var hashValue: Int { - return _hashValue(for: self) - } - - @inlinable // FIXME(sil-serialize-all) - public func _hash(into hasher: inout _Hasher) { - hasher.append(lowerBound) - hasher.append(upperBound) + public func hash(into hasher: inout Hasher) { + hasher.combine(lowerBound) + hasher.combine(upperBound) } } @@ -498,6 +499,6 @@ extension ClosedRange { @available(*, deprecated, renamed: "ClosedRange.Index") public typealias ClosedRangeIndex = ClosedRange.Index where T: Strideable, T.Stride: SignedInteger -@available(*, deprecated, renamed: "ClosedRange") + public typealias CountableClosedRange = ClosedRange where Bound.Stride : SignedInteger diff --git a/stdlib/public/core/CocoaArray.swift b/stdlib/public/core/CocoaArray.swift index b997bbc86514e..c22c131a9072f 100644 --- a/stdlib/public/core/CocoaArray.swift +++ b/stdlib/public/core/CocoaArray.swift @@ -30,19 +30,16 @@ import SwiftShims internal struct _CocoaArrayWrapper : RandomAccessCollection { typealias Indices = Range @inlinable - @usableFromInline internal var startIndex: Int { return 0 } @inlinable - @usableFromInline internal var endIndex: Int { return buffer.count } @inlinable - @usableFromInline internal subscript(i: Int) -> AnyObject { return buffer.objectAt(i) } @@ -58,7 +55,6 @@ internal struct _CocoaArrayWrapper : RandomAccessCollection { /// contiguous storage exists, e.g., if array doesn't have a smart /// implementation of countByEnumerating. @inlinable - @usableFromInline internal func contiguousStorage( _ subRange: Range ) -> UnsafeMutablePointer? @@ -82,7 +78,6 @@ internal struct _CocoaArrayWrapper : RandomAccessCollection { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline @_transparent internal init(_ buffer: _NSArrayCore) { self.buffer = buffer diff --git a/stdlib/public/core/Codable.swift.gyb b/stdlib/public/core/Codable.swift.gyb index a7d2a15414346..cd4a2847c100f 100644 --- a/stdlib/public/core/Codable.swift.gyb +++ b/stdlib/public/core/Codable.swift.gyb @@ -45,6 +45,10 @@ public protocol Decodable { } /// A type that can convert itself into and out of an external representation. +/// +/// `Codable` is a type alias for the `Encodable` and `Decodable` protocols. +/// When you use `Codable` as a type or a generic constraint, it matches +/// any type that conforms to both protocols. public typealias Codable = Encodable & Decodable //===----------------------------------------------------------------------===// @@ -1094,6 +1098,16 @@ public struct CodingUserInfoKey : RawRepresentable, Equatable, Hashable { public var hashValue: Int { return self.rawValue.hashValue } + + /// Hashes the essential components of this value by feeding them into the + /// given hasher. + /// + /// - Parameter hasher: The hasher to use when combining the components + /// of this instance. + @inlinable // FIXME(sil-serialize-all) + public func hash(into hasher: inout Hasher) { + hasher.combine(self.rawValue) + } } //===----------------------------------------------------------------------===// @@ -1312,13 +1326,11 @@ internal struct _GenericIndexKey : CodingKey { internal var intValue: Int? @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init?(stringValue: String) { return nil } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init?(intValue: Int) { self.stringValue = "Index \(intValue)" self.intValue = intValue @@ -1400,42 +1412,35 @@ public extension DecodingError { @usableFromInline internal class _KeyedEncodingContainerBase { @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(){} @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) deinit {} // These must all be given a concrete implementation in _*Box. @inlinable - @usableFromInline internal var codingPath: [CodingKey] { fatalError("_KeyedEncodingContainerBase cannot be used directly.") } @inlinable - @usableFromInline internal func encodeNil(forKey key: Key) throws { fatalError("_KeyedEncodingContainerBase cannot be used directly.") } % for type in codable_types: @inlinable - @usableFromInline internal func encode(_ value: ${type}, forKey key: Key) throws { fatalError("_KeyedEncodingContainerBase cannot be used directly.") } % end @inlinable - @usableFromInline internal func encode(_ value: T, forKey key: Key) throws { fatalError("_KeyedEncodingContainerBase cannot be used directly.") } @inlinable - @usableFromInline internal func encodeConditional( _ object: T, forKey key: Key) throws { @@ -1444,14 +1449,12 @@ internal class _KeyedEncodingContainerBase { % for type in codable_types: @inlinable - @usableFromInline internal func encodeIfPresent(_ value: ${type}?, forKey key: Key) throws { fatalError("_KeyedEncodingContainerBase cannot be used directly.") } % end @inlinable - @usableFromInline internal func encodeIfPresent( _ value: T?, forKey key: Key) throws { @@ -1459,7 +1462,6 @@ internal class _KeyedEncodingContainerBase { } @inlinable - @usableFromInline internal func nestedContainer( keyedBy keyType: NestedKey.Type, forKey key: Key ) -> KeyedEncodingContainer { @@ -1467,20 +1469,17 @@ internal class _KeyedEncodingContainerBase { } @inlinable - @usableFromInline internal func nestedUnkeyedContainer( forKey key: Key) -> UnkeyedEncodingContainer { fatalError("_KeyedEncodingContainerBase cannot be used directly.") } @inlinable - @usableFromInline internal func superEncoder() -> Encoder { fatalError("_KeyedEncodingContainerBase cannot be used directly.") } @inlinable - @usableFromInline internal func superEncoder(forKey key: Key) -> Encoder { fatalError("_KeyedEncodingContainerBase cannot be used directly.") } @@ -1497,33 +1496,28 @@ internal final class _KeyedEncodingContainerBox< internal var concrete: Concrete @inlinable - @usableFromInline internal init(_ container: Concrete) { concrete = container } @inlinable - @usableFromInline override internal var codingPath: [CodingKey] { return concrete.codingPath } @inlinable - @usableFromInline override internal func encodeNil(forKey key: Key) throws { try concrete.encodeNil(forKey: key) } % for type in codable_types: @inlinable - @usableFromInline override internal func encode(_ value: ${type}, forKey key: Key) throws { try concrete.encode(value, forKey: key) } % end @inlinable - @usableFromInline override internal func encode( _ value: T, forKey key: Key) throws { @@ -1531,7 +1525,6 @@ internal final class _KeyedEncodingContainerBox< } @inlinable - @usableFromInline override internal func encodeConditional( _ object: T, forKey key: Key) throws { @@ -1540,7 +1533,6 @@ internal final class _KeyedEncodingContainerBox< % for type in codable_types: @inlinable - @usableFromInline override internal func encodeIfPresent( _ value: ${type}?, forKey key: Key) throws { @@ -1549,7 +1541,6 @@ internal final class _KeyedEncodingContainerBox< % end @inlinable - @usableFromInline override internal func encodeIfPresent( _ value: T?, forKey key: Key) throws { @@ -1557,7 +1548,6 @@ internal final class _KeyedEncodingContainerBox< } @inlinable - @usableFromInline override internal func nestedContainer( keyedBy keyType: NestedKey.Type, forKey key: Key ) -> KeyedEncodingContainer { @@ -1565,7 +1555,6 @@ internal final class _KeyedEncodingContainerBox< } @inlinable - @usableFromInline override internal func nestedUnkeyedContainer( forKey key: Key) -> UnkeyedEncodingContainer { @@ -1573,13 +1562,11 @@ internal final class _KeyedEncodingContainerBox< } @inlinable - @usableFromInline override internal func superEncoder() -> Encoder { return concrete.superEncoder() } @inlinable - @usableFromInline override internal func superEncoder(forKey key: Key) -> Encoder { return concrete.superEncoder(forKey: key) } @@ -1589,40 +1576,33 @@ internal final class _KeyedEncodingContainerBox< @usableFromInline internal class _KeyedDecodingContainerBase { @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(){} @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) deinit {} @inlinable - @usableFromInline internal var codingPath: [CodingKey] { fatalError("_KeyedDecodingContainerBase cannot be used directly.") } @inlinable - @usableFromInline internal var allKeys: [Key] { fatalError("_KeyedDecodingContainerBase cannot be used directly.") } @inlinable - @usableFromInline internal func contains(_ key: Key) -> Bool { fatalError("_KeyedDecodingContainerBase cannot be used directly.") } @inlinable - @usableFromInline internal func decodeNil(forKey key: Key) throws -> Bool { fatalError("_KeyedDecodingContainerBase cannot be used directly.") } % for type in codable_types: @inlinable - @usableFromInline internal func decode( _ type: ${type}.Type, forKey key: Key) throws -> ${type} { @@ -1631,7 +1611,6 @@ internal class _KeyedDecodingContainerBase { % end @inlinable - @usableFromInline internal func decode( _ type: T.Type, forKey key: Key) throws -> T { @@ -1640,7 +1619,6 @@ internal class _KeyedDecodingContainerBase { % for type in codable_types: @inlinable - @usableFromInline internal func decodeIfPresent( _ type: ${type}.Type, forKey key: Key) throws -> ${type}? { @@ -1649,7 +1627,6 @@ internal class _KeyedDecodingContainerBase { % end @inlinable - @usableFromInline internal func decodeIfPresent( _ type: T.Type, forKey key: Key) throws -> T? { @@ -1657,7 +1634,6 @@ internal class _KeyedDecodingContainerBase { } @inlinable - @usableFromInline internal func nestedContainer( keyedBy type: NestedKey.Type, forKey key: Key ) throws -> KeyedDecodingContainer { @@ -1665,7 +1641,6 @@ internal class _KeyedDecodingContainerBase { } @inlinable - @usableFromInline internal func nestedUnkeyedContainer( forKey key: Key) throws -> UnkeyedDecodingContainer { @@ -1673,13 +1648,11 @@ internal class _KeyedDecodingContainerBase { } @inlinable - @usableFromInline internal func superDecoder() throws -> Decoder { fatalError("_KeyedDecodingContainerBase cannot be used directly.") } @inlinable - @usableFromInline internal func superDecoder(forKey key: Key) throws -> Decoder { fatalError("_KeyedDecodingContainerBase cannot be used directly.") } @@ -1696,38 +1669,32 @@ internal final class _KeyedDecodingContainerBox< internal var concrete: Concrete @inlinable - @usableFromInline internal init(_ container: Concrete) { concrete = container } @inlinable - @usableFromInline override var codingPath: [CodingKey] { return concrete.codingPath } @inlinable - @usableFromInline override var allKeys: [Key] { return concrete.allKeys } @inlinable - @usableFromInline override internal func contains(_ key: Key) -> Bool { return concrete.contains(key) } @inlinable - @usableFromInline override internal func decodeNil(forKey key: Key) throws -> Bool { return try concrete.decodeNil(forKey: key) } % for type in codable_types: @inlinable - @usableFromInline override internal func decode( _ type: ${type}.Type, forKey key: Key) throws -> ${type} { @@ -1736,7 +1703,6 @@ internal final class _KeyedDecodingContainerBox< % end @inlinable - @usableFromInline override internal func decode( _ type: T.Type, forKey key: Key) throws -> T { @@ -1745,7 +1711,6 @@ internal final class _KeyedDecodingContainerBox< % for type in codable_types: @inlinable - @usableFromInline override internal func decodeIfPresent( _ type: ${type}.Type, forKey key: Key) throws -> ${type}? { @@ -1754,7 +1719,6 @@ internal final class _KeyedDecodingContainerBox< % end @inlinable - @usableFromInline override internal func decodeIfPresent( _ type: T.Type, forKey key: Key) throws -> T? { @@ -1762,7 +1726,6 @@ internal final class _KeyedDecodingContainerBox< } @inlinable - @usableFromInline override internal func nestedContainer( keyedBy type: NestedKey.Type, forKey key: Key ) throws -> KeyedDecodingContainer { @@ -1770,7 +1733,6 @@ internal final class _KeyedDecodingContainerBox< } @inlinable - @usableFromInline override internal func nestedUnkeyedContainer( forKey key: Key) throws -> UnkeyedDecodingContainer { @@ -1778,13 +1740,11 @@ internal final class _KeyedDecodingContainerBox< } @inlinable - @usableFromInline override internal func superDecoder() throws -> Decoder { return try concrete.superDecoder() } @inlinable - @usableFromInline override internal func superDecoder(forKey key: Key) throws -> Decoder { return try concrete.superDecoder(forKey: key) } @@ -1821,7 +1781,8 @@ extension ${type} : Codable { } extension RawRepresentable where RawValue == ${type}, Self : Encodable { - /// Encodes this value into the given encoder. + /// Encodes this value into the given encoder, when the type's `RawValue` + /// is `${type}`. /// /// This function throws an error if any values are invalid for the given /// encoder's format. @@ -1835,7 +1796,8 @@ extension RawRepresentable where RawValue == ${type}, Self : Encodable { } extension RawRepresentable where RawValue == ${type}, Self : Decodable { - /// Creates a new instance by decoding from the given decoder. + /// Creates a new instance by decoding from the given decoder, when the + /// type's `RawValue` is `${type}`. /// /// This initializer throws an error if reading from the decoder fails, or /// if the data read is corrupted or otherwise invalid. @@ -1978,14 +1940,12 @@ internal struct _DictionaryCodingKey : CodingKey { internal let intValue: Int? @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init?(stringValue: String) { self.stringValue = stringValue self.intValue = Int(stringValue) } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init?(intValue: Int) { self.stringValue = "\(intValue)" self.intValue = intValue diff --git a/stdlib/public/core/Collection.swift b/stdlib/public/core/Collection.swift index 33768f8170852..ea57e573f6370 100644 --- a/stdlib/public/core/Collection.swift +++ b/stdlib/public/core/Collection.swift @@ -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 - 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 @@ -161,7 +161,7 @@ extension IndexingIterator: IteratorProtocol, Sequence { /// that position. /// /// let text = "Buffalo buffalo buffalo buffalo." -/// if let firstSpace = text.index(of: " ") { +/// if let firstSpace = text.firstIndex(of: " ") { /// print(text[.. Index?? + /// Customization point for `Collection.lastIndex(of:)`. + /// + /// Define this method if the collection can find an element in less than + /// O(*n*) by exploiting collection-specific knowledge. + /// + /// - Returns: `nil` if a linear search should be attempted instead, + /// `Optional(nil)` if the element was not found, or + /// `Optional(Optional(index))` if an element was found. + /// + /// - Complexity: Hopefully less than O(`count`). + func _customLastIndexOfEquatableElement(_ element: Element) -> Index?? + /// The first element of the collection. /// /// If the collection is empty, the value of this property is `nil`. @@ -790,6 +803,25 @@ public protocol Collection: Sequence where SubSequence: Collection { /// `endIndex`. func formIndex(after i: inout Index) + /// Returns a random element of the collection, using the given generator as + /// a source for randomness. + /// + /// You use this method to select a random element from a collection when you + /// are using a custom random number generator. For example, call + /// `randomElement(using:)` to select a random element from an array of names. + /// + /// let names = ["Zoey", "Chloe", "Amani", "Amaia"] + /// let randomName = names.randomElement(using: &myGenerator)! + /// // randomName == "Amani" + /// + /// - Parameter generator: The random number generator to use when choosing + /// a random element. + /// - Returns: A random element from the collection. If the collection is + /// empty, the method returns `nil`. + func randomElement( + using generator: inout T + ) -> Element? + @available(*, deprecated, message: "all index distances are now of type Int") typealias IndexDistance = Int } @@ -1003,9 +1035,56 @@ extension Collection { return count } + /// Returns a random element of the collection, using the given generator as + /// a source for randomness. + /// + /// Call `randomElement(using:)` to select a random element from an array or + /// another collection when you are using a custom random number generator. + /// This example picks a name at random from an array: + /// + /// let names = ["Zoey", "Chloe", "Amani", "Amaia"] + /// let randomName = names.randomElement(using: &myGenerator)! + /// // randomName == "Amani" + /// + /// - Parameter generator: The random number generator to use when choosing + /// a random element. + /// - Returns: A random element from the collection. If the collection is + /// empty, the method returns `nil`. + @inlinable + public func randomElement( + using generator: inout T + ) -> Element? { + guard !isEmpty else { return nil } + let random = generator.next(upperBound: UInt(count)) + let index = self.index( + startIndex, + offsetBy: numericCast(random) + ) + return self[index] + } + + /// Returns a random element of the collection. + /// + /// Call `randomElement()` to select a random element from an array or + /// another collection. This example picks a name at random from an array: + /// + /// let names = ["Zoey", "Chloe", "Amani", "Amaia"] + /// let randomName = names.randomElement()! + /// // randomName == "Amani" + /// + /// This method uses the default random generator, `Random.default`. The call + /// to `names.randomElement()` above is equivalent to calling + /// `names.randomElement(using: &Random.default)`. + /// + /// - Returns: A random element from the collection. If the collection is + /// empty, the method returns `nil`. + @inlinable + public func randomElement() -> Element? { + return randomElement(using: &Random.default) + } + /// Do not use this method directly; call advanced(by: n) instead. @inlinable - @usableFromInline @inline(__always) internal func _advanceForward(_ i: Index, by n: Int) -> Index { _precondition(n >= 0, @@ -1020,7 +1099,6 @@ extension Collection { /// Do not use this method directly; call advanced(by: n, limit) instead. @inlinable - @usableFromInline @inline(__always) internal func _advanceForward( _ i: Index, by n: Int, limitedBy limit: Index @@ -1069,7 +1147,7 @@ extension Collection where SubSequence == Slice { /// print(streetsSlice) /// // Prints "["Channing", "Douglas", "Evarts"]" /// - /// let index = streetsSlice.index(of: "Evarts") // 4 + /// let index = streetsSlice.firstIndex(of: "Evarts") // 4 /// print(streets[index!]) /// // Prints "Evarts" /// @@ -1182,7 +1260,7 @@ extension Collection { } // TODO: swift-3-indexing-model - rename the following to _customIndexOfEquatable(element)? - /// Customization point for `Collection.index(of:)`. + /// Customization point for `Collection.firstIndex(of:)`. /// /// Define this method if the collection can find an element in less than /// O(*n*) by exploiting collection-specific knowledge. @@ -1191,12 +1269,28 @@ extension Collection { /// `Optional(nil)` if the element was not found, or /// `Optional(Optional(index))` if an element was found. /// - /// - Complexity: O(`count`). + /// - Complexity: Hopefully less than O(`count`). @inlinable public // dispatching func _customIndexOfEquatableElement(_: Iterator.Element) -> Index?? { return nil } + + /// Customization point for `Collection.lastIndex(of:)`. + /// + /// Define this method if the collection can find an element in less than + /// O(*n*) by exploiting collection-specific knowledge. + /// + /// - Returns: `nil` if a linear search should be attempted instead, + /// `Optional(nil)` if the element was not found, or + /// `Optional(Optional(index))` if an element was found. + /// + /// - Complexity: Hopefully less than O(`count`). + @inlinable + public // dispatching + func _customLastIndexOfEquatableElement(_ element: Element) -> Index?? { + return nil + } } //===----------------------------------------------------------------------===// @@ -1404,7 +1498,7 @@ extension Collection { /// but not including, that index: /// /// let numbers = [10, 20, 30, 40, 50, 60] - /// if let i = numbers.index(of: 40) { + /// if let i = numbers.firstIndex(of: 40) { /// print(numbers.prefix(upTo: i)) /// } /// // Prints "[10, 20, 30]" @@ -1419,7 +1513,7 @@ extension Collection { /// half-open range as the collection's subscript. The subscript notation is /// preferred over `prefix(upTo:)`. /// - /// if let i = numbers.index(of: 40) { + /// if let i = numbers.firstIndex(of: 40) { /// print(numbers[.. Index? { + public func firstIndex(of element: Element) -> Index? { if let result = _customIndexOfEquatableElement(element) { return result } @@ -68,6 +68,13 @@ extension Collection where Element : Equatable { } return nil } + + /// Returns the first index where the specified value appears in the + /// collection. + @inlinable + public func index(of _element: Element) -> Index? { + return firstIndex(of: _element) + } } extension Collection { @@ -80,7 +87,7 @@ extension Collection { /// begins with the letter "A": /// /// let students = ["Kofi", "Abena", "Peter", "Kweku", "Akosua"] - /// if let i = students.index(where: { $0.hasPrefix("A") }) { + /// if let i = students.firstIndex(where: { $0.hasPrefix("A") }) { /// print("\(students[i]) starts with 'A'!") /// } /// // Prints "Abena starts with 'A'!" @@ -92,7 +99,7 @@ extension Collection { /// `true`. If no elements in the collection satisfy the given predicate, /// returns `nil`. @inlinable - public func index( + public func firstIndex( where predicate: (Element) throws -> Bool ) rethrows -> Index? { var i = self.startIndex @@ -104,6 +111,106 @@ extension Collection { } return nil } + + /// Returns the first index in which an element of the collection satisfies + /// the given predicate. + @inlinable + public func index( + where _predicate: (Element) throws -> Bool + ) rethrows -> Index? { + return try firstIndex(where: _predicate) + } +} + +//===----------------------------------------------------------------------===// +// lastIndex(of:)/lastIndex(where:) +//===----------------------------------------------------------------------===// + +extension BidirectionalCollection { + /// Returns the last element of the sequence that satisfies the given + /// predicate. + /// + /// This example uses the `last(where:)` method to find the last + /// negative number in an array of integers: + /// + /// let numbers = [3, 7, 4, -2, 9, -6, 10, 1] + /// if let lastNegative = numbers.last(where: { $0 < 0 }) { + /// print("The last negative number is \(firstNegative).") + /// } + /// // Prints "The last negative number is -6." + /// + /// - Parameter predicate: A closure that takes an element of the sequence as + /// its argument and returns a Boolean value indicating whether the + /// element is a match. + /// - Returns: The last element of the sequence that satisfies `predicate`, + /// or `nil` if there is no element that satisfies `predicate`. + @inlinable + public func last( + where predicate: (Element) throws -> Bool + ) rethrows -> Element? { + return try lastIndex(where: predicate).map { self[$0] } + } + + /// Returns the index of the last element in the collection that matches the + /// given predicate. + /// + /// You can use the predicate to find an element of a type that doesn't + /// conform to the `Equatable` protocol or to find an element that matches + /// particular criteria. This example finds the index of the last name that + /// begins with the letter "A": + /// + /// let students = ["Kofi", "Abena", "Peter", "Kweku", "Akosua"] + /// if let i = students.lastIndex(where: { $0.hasPrefix("A") }) { + /// print("\(students[i]) starts with 'A'!") + /// } + /// // Prints "Akosua starts with 'A'!" + /// + /// - Parameter predicate: A closure that takes an element as its argument + /// and returns a Boolean value that indicates whether the passed element + /// represents a match. + /// - Returns: The index of the last element in the collection that matches + /// `predicate`, or `nil` if no elements match. + @inlinable + public func lastIndex( + where predicate: (Element) throws -> Bool + ) rethrows -> Index? { + var i = endIndex + while i != startIndex { + formIndex(before: &i) + if try predicate(self[i]) { + return i + } + } + return nil + } +} + +extension BidirectionalCollection where Element : Equatable { + /// Returns the last index where the specified value appears in the + /// collection. + /// + /// After using `lastIndex(of:)` to find the position of the last instance of + /// a particular element in a collection, you can use it to access the + /// element by subscripting. This example shows how you can modify one of + /// the names in an array of students. + /// + /// var students = ["Ben", "Ivy", "Jordell", "Ben", "Maxime"] + /// if let i = students.lastIndex(of: "Ben") { + /// students[i] = "Benjamin" + /// } + /// print(students) + /// // Prints "["Ben", "Ivy", "Jordell", "Benjamin", "Max"]" + /// + /// - Parameter element: An element to search for in the collection. + /// - Returns: The last index where `element` is found. If `element` is not + /// found in the collection, returns `nil`. + @inlinable + public func lastIndex(of element: Element) -> Index? { + if let result = _customLastIndexOfEquatableElement(element) { + return result + } + return lastIndex(where: { $0 == element }) + } } //===----------------------------------------------------------------------===// @@ -260,6 +367,114 @@ extension MutableCollection where Self : BidirectionalCollection { } } +//===----------------------------------------------------------------------===// +// shuffled()/shuffle() +//===----------------------------------------------------------------------===// + +extension Sequence { + /// Returns the elements of the sequence, shuffled using the given generator + /// as a source for randomness. + /// + /// You use this method to randomize the elements of a sequence when you + /// are using a custom random number generator. For example, you can shuffle + /// the numbers between `0` and `9` by calling the `shuffled(using:)` method + /// on that range: + /// + /// let numbers = 0...9 + /// let shuffledNumbers = numbers.shuffled(using: &myGenerator) + /// // shuffledNumbers == [8, 9, 4, 3, 2, 6, 7, 0, 5, 1] + /// + /// - Parameter generator: The random number generator to use when shuffling + /// the sequence. + /// - Returns: An array of this sequence's elements in a shuffled order. + /// + /// - Complexity: O(*n*) + @inlinable + public func shuffled( + using generator: inout T + ) -> [Element] { + var result = ContiguousArray(self) + result.shuffle(using: &generator) + return Array(result) + } + + /// Returns the elements of the sequence, shuffled. + /// + /// For example, you can shuffle the numbers between `0` and `9` by calling + /// the `shuffled()` method on that range: + /// + /// let numbers = 0...9 + /// let shuffledNumbers = numbers.shuffled() + /// // shuffledNumbers == [1, 7, 6, 2, 8, 9, 4, 3, 5, 0] + /// + /// This method uses the default random generator, `Random.default`. The call + /// to `numbers.shuffled()` above is equivalent to calling + /// `numbers.shuffled(using: &Random.default)`. + /// + /// - Returns: A shuffled array of this sequence's elements. + /// + /// - Complexity: O(*n*) + @inlinable + public func shuffled() -> [Element] { + return shuffled(using: &Random.default) + } +} + +extension MutableCollection where Self : RandomAccessCollection { + /// Shuffles the collection in place, using the given generator as a source + /// for randomness. + /// + /// You use this method to randomize the elements of a collection when you + /// are using a custom random number generator. For example, you can use the + /// `shuffle(using:)` method to randomly reorder the elements of an array. + /// + /// var names = ["Alejandro", "Camila", "Diego", "Luciana", "Luis", "Sofía"] + /// names.shuffle(using: &myGenerator) + /// // names == ["Sofía", "Alejandro", "Camila", "Luis", "Diego", "Luciana"] + /// + /// - Parameter generator: The random number generator to use when shuffling + /// the collection. + /// + /// - Complexity: O(*n*) + @inlinable + public mutating func shuffle( + using generator: inout T + ) { + let count = self.count + guard count > 1 else { return } + var amount = count + var currentIndex = startIndex + while amount > 1 { + let random = generator.next(upperBound: UInt(amount)) + amount -= 1 + swapAt( + currentIndex, + index(currentIndex, offsetBy: numericCast(random)) + ) + formIndex(after: ¤tIndex) + } + } + + /// Shuffles the collection in place. + /// + /// Use the `shuffle()` method to randomly reorder the elements of an + /// array. + /// + /// var names = ["Alejandro", "Camila", "Diego", "Luciana", "Luis", "Sofía"] + /// names.shuffle(using: myGenerator) + /// // names == ["Luis", "Camila", "Luciana", "Sofía", "Alejandro", "Diego"] + /// + /// This method uses the default random generator, `Random.default`. The call + /// to `names.shuffle()` above is equivalent to calling + /// `names.shuffle(using: &Random.default)`. + /// + /// - Complexity: O(*n*) + @inlinable + public mutating func shuffle() { + shuffle(using: &Random.default) + } +} + //===----------------------------------------------------------------------===// // sorted()/sort() //===----------------------------------------------------------------------===// diff --git a/stdlib/public/core/CollectionOfOne.swift b/stdlib/public/core/CollectionOfOne.swift index e4c8084152c0b..6bd5a286b09d3 100644 --- a/stdlib/public/core/CollectionOfOne.swift +++ b/stdlib/public/core/CollectionOfOne.swift @@ -10,7 +10,9 @@ // //===----------------------------------------------------------------------===// -/// An iterator that produces one or fewer instances of `Element`. +/// An iterator that produces one or zero instances of an element. +/// +/// `IteratorOverOne` is the iterator for the `CollectionOfOne` type. @_fixed_layout // FIXME(sil-serialize-all) public struct IteratorOverOne { @usableFromInline // FIXME(sil-serialize-all) @@ -31,8 +33,8 @@ extension IteratorOverOne: IteratorProtocol, Sequence { /// /// Once `nil` has been returned, all subsequent calls return `nil`. /// - /// - Precondition: `next()` has not been applied to a copy of `self` - /// since the copy was made. + /// - Returns: The next element in the underlying sequence, if a next element + /// exists; otherwise, `nil`. @inlinable // FIXME(sil-serialize-all) public mutating func next() -> Element? { let result = _elements @@ -41,13 +43,25 @@ extension IteratorOverOne: IteratorProtocol, Sequence { } } -/// A collection containing a single element of type `Element`. +/// A collection containing a single element. +/// +/// You can use a `CollectionOfOne` instance when you need to efficiently +/// represent a single value as a collection. For example, you can add a +/// single element to an array by using a `CollectionOfOne` instance with the +/// concatenation operator (`+`): +/// +/// let a = [1, 2, 3, 4] +/// let toAdd = 100 +/// let b = a + CollectionOfOne(toAdd) +/// // b == [1, 2, 3, 4, 100] @_fixed_layout // FIXME(sil-serialize-all) public struct CollectionOfOne { @usableFromInline // FIXME(sil-serialize-all) internal var _element: Element - /// Creates an instance containing just `element`. + /// Creates an instance containing just the given element. + /// + /// - Parameter element: The element to store in the collection. @inlinable // FIXME(sil-serialize-all) public init(_ element: Element) { self._element = element @@ -60,6 +74,8 @@ extension CollectionOfOne: RandomAccessCollection, MutableCollection { public typealias Indices = Range /// The position of the first element. + /// + /// In a `CollectionOfOne` instance, `startIndex` is always `0`. @inlinable // FIXME(sil-serialize-all) public var startIndex: Int { return 0 @@ -68,38 +84,44 @@ extension CollectionOfOne: RandomAccessCollection, MutableCollection { /// The "past the end" position---that is, the position one greater than the /// last valid subscript argument. /// - /// In a `CollectionOfOne` instance, `endIndex` is always identical to - /// `index(after: startIndex)`. + /// In a `CollectionOfOne` instance, `endIndex` is always `1`. @inlinable // FIXME(sil-serialize-all) public var endIndex: Int { return 1 } - /// Always returns `endIndex`. + /// Returns the position immediately after the given index. + /// + /// - Parameter i: A valid index of the collection. `i` must be `0`. + /// - Returns: The index value immediately after `i`. @inlinable // FIXME(sil-serialize-all) public func index(after i: Int) -> Int { _precondition(i == startIndex) return endIndex } - /// Always returns `startIndex`. + /// Returns the position immediately before the given index. + /// + /// - Parameter i: A valid index of the collection. `i` must be `1`. + /// - Returns: The index value immediately before `i`. @inlinable // FIXME(sil-serialize-all) public func index(before i: Int) -> Int { _precondition(i == endIndex) return startIndex } - /// Returns an iterator over the elements of this sequence. + /// Returns an iterator over the elements of this collection. /// - /// - Complexity: O(1). + /// - Complexity: O(1) @inlinable // FIXME(sil-serialize-all) public func makeIterator() -> IteratorOverOne { return IteratorOverOne(_elements: _element) } - /// Accesses the element at `position`. + /// Accesses the element at the specified position. /// - /// - Precondition: `position == 0`. + /// - Parameter position: The position of the element to access. The only + /// valid position in a `CollectionOfOne` instance is `0`. @inlinable // FIXME(sil-serialize-all) public subscript(position: Int) -> Element { get { @@ -129,7 +151,7 @@ extension CollectionOfOne: RandomAccessCollection, MutableCollection { } } - /// The number of elements (always one). + /// The number of elements in the collection, which is always one. @inlinable // FIXME(sil-serialize-all) public var count: Int { return 1 @@ -137,7 +159,7 @@ extension CollectionOfOne: RandomAccessCollection, MutableCollection { } extension CollectionOfOne : CustomDebugStringConvertible { - /// A textual representation of `self`, suitable for debugging. + /// A textual representation of the collection, suitable for debugging. @inlinable // FIXME(sil-serialize-all) public var debugDescription: String { return "CollectionOfOne(\(String(reflecting: _element)))" diff --git a/stdlib/public/core/CompilerProtocols.swift b/stdlib/public/core/CompilerProtocols.swift index efc45b802243a..d49be2c7d8f15 100644 --- a/stdlib/public/core/CompilerProtocols.swift +++ b/stdlib/public/core/CompilerProtocols.swift @@ -178,37 +178,46 @@ public func != (lhs: T, rhs: T) -> Bool return lhs.rawValue != rhs.rawValue } -/// A type that can produce a collection of all of its values. +/// A type that provides a collection of all of its values. /// -/// Simple Enumerations -/// =================== +/// Types that conform to the `CaseIterable` protocol are typically +/// enumerations without associated values. When using a `CaseIterable` type, +/// you can access a collection of all of the type's cases by using the type's +/// `allCases` property. /// -/// For any Swift enumeration where every case does not have an associated -/// value, the Swift compiler can automatically fill out the `CaseIterable` -/// conformance. When defining your own custom enumeration, specify a -/// conformance to `CaseIterable` to take advantage of this automatic -/// derivation. +/// For example, the `CompassDirection` enumeration declared in this example +/// conforms to `CaseIterable`. You access the number of cases and the cases +/// themselves through `CompassDirection.allCases`. /// -/// For example, every case of the `CardinalPoint` enumeration defined here -/// does not have an associated value: -/// -/// enum CardinalPoint: CaseIterable { +/// enum CompassDirection: CaseIterable { /// case north, south, east, west /// } /// -/// So the compiler automatically creates a conformance. +/// print("There are \(CompassDirection.allCases.count) directions.") +/// // Prints "There are 4 directions." +/// let caseList = CompassDirection.allCases +/// .map({ "\($0)" }) +/// .joined(separator: ", ") +/// // caseList == "north, south, east, west" /// -/// for cardinality in CardinalPoint.allCases { -/// print(cardinality) -/// } -/// // Prints "north" -/// // Prints "south" -/// // Prints "east" -/// // Prints "west" +/// Conforming to the CaseIterable Protocol +/// ======================================= +/// +/// The compiler can automatically provide an implementation of the +/// `CaseIterable` requirements for any enumeration without associated values +/// or `@available` attributes on its cases. The synthesized `allCases` +/// collection provides the cases in order of their declaration. +/// +/// You can take advantage of this compiler support when defining your own +/// custom enumeration by declaring conformance to `CaseIterable` in the +/// enumeration's original declaration. The `CompassDirection` example above +/// demonstrates this automatic implementation. public protocol CaseIterable { + /// A type that can represent a collection of all values of this type. associatedtype AllCases: Collection where AllCases.Element == Self - /// Returns a collection of all values of this type. + + /// A collection of all values of this type. static var allCases: AllCases { get } } diff --git a/stdlib/public/core/ContiguousArrayBuffer.swift b/stdlib/public/core/ContiguousArrayBuffer.swift index f71bce949fb63..3c35a337757ce 100644 --- a/stdlib/public/core/ContiguousArrayBuffer.swift +++ b/stdlib/public/core/ContiguousArrayBuffer.swift @@ -24,7 +24,6 @@ internal final class _EmptyArrayStorage : _ContiguousArrayStorageBase { @inlinable - @usableFromInline @nonobjc internal init(_doNotCallMe: ()) { _sanityCheckFailure("creating instance of _EmptyArrayStorage") @@ -32,7 +31,6 @@ internal final class _EmptyArrayStorage #if _runtime(_ObjC) @inlinable - @usableFromInline override internal func _withVerbatimBridgedUnsafeBuffer( _ body: (UnsafeBufferPointer) throws -> R ) rethrows -> R? { @@ -40,14 +38,12 @@ internal final class _EmptyArrayStorage } @inlinable - @usableFromInline @nonobjc override internal func _getNonVerbatimBridgedCount() -> Int { return 0 } @inlinable - @usableFromInline override internal func _getNonVerbatimBridgedHeapBuffer() -> _HeapBuffer { return _HeapBuffer( _HeapBufferStorage.self, 0, 0) @@ -55,14 +51,12 @@ internal final class _EmptyArrayStorage #endif @inlinable - @usableFromInline override internal func canStoreElements(ofDynamicType _: Any.Type) -> Bool { return false } /// A type that every element in the array is. @inlinable - @usableFromInline override internal var staticElementType: Any.Type { return Void.self } @@ -71,7 +65,6 @@ internal final class _EmptyArrayStorage /// The empty array prototype. We use the same object for all empty /// `[Native]Array`s. @inlinable -@usableFromInline internal var _emptyArrayStorage : _EmptyArrayStorage { return Builtin.bridgeFromRawPointer( Builtin.addressof(&_swiftEmptyArrayStorage)) @@ -85,7 +78,6 @@ internal final class _ContiguousArrayStorage< > : _ContiguousArrayStorageBase { @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) deinit { _elementPointer.deinitialize(count: countAndCapacity.count) _fixLifetime(self) @@ -96,7 +88,6 @@ internal final class _ContiguousArrayStorage< /// `UnsafeBufferPointer` to the elements and return the result. /// Otherwise, return `nil`. @inlinable - @usableFromInline internal final override func _withVerbatimBridgedUnsafeBuffer( _ body: (UnsafeBufferPointer) throws -> R ) rethrows -> R? { @@ -110,7 +101,6 @@ internal final class _ContiguousArrayStorage< /// If `Element` is bridged verbatim, invoke `body` on an /// `UnsafeBufferPointer` to the elements. @inlinable - @usableFromInline internal final func _withVerbatimBridgedUnsafeBufferImpl( _ body: (UnsafeBufferPointer) throws -> Void ) rethrows { @@ -127,7 +117,6 @@ internal final class _ContiguousArrayStorage< /// /// - Precondition: `Element` is bridged non-verbatim. @inlinable - @usableFromInline @nonobjc override internal func _getNonVerbatimBridgedCount() -> Int { _sanityCheck( @@ -140,7 +129,6 @@ internal final class _ContiguousArrayStorage< /// /// - Precondition: `Element` is bridged non-verbatim. @inlinable - @usableFromInline override internal func _getNonVerbatimBridgedHeapBuffer() -> _HeapBuffer { _sanityCheck( @@ -164,7 +152,6 @@ internal final class _ContiguousArrayStorage< /// safety; for example, the destructor has static knowledge that /// all of the elements can be destroyed as `Element`. @inlinable - @usableFromInline internal override func canStoreElements( ofDynamicType proposedElementType: Any.Type ) -> Bool { @@ -179,13 +166,11 @@ internal final class _ContiguousArrayStorage< /// A type that every element in the array is. @inlinable - @usableFromInline internal override var staticElementType: Any.Type { return Element.self } @inlinable - @usableFromInline internal final var _elementPointer : UnsafeMutablePointer { return UnsafeMutablePointer(Builtin.projectTailElems(self, Element.self)) } @@ -200,7 +185,6 @@ internal struct _ContiguousArrayBuffer : _ArrayBufferProtocol { /// result's `.firstElementAddress` or set the result's `.count` /// to zero. @inlinable - @usableFromInline internal init( _uninitializedCount uninitializedCount: Int, minimumCapacity: Int @@ -232,7 +216,6 @@ internal struct _ContiguousArrayBuffer : _ArrayBufferProtocol { /// - Warning: storage may have been stack-allocated, so it's /// crucial not to call, e.g., `malloc_size` on it. @inlinable - @usableFromInline internal init(count: Int, storage: _ContiguousArrayStorage) { _storage = storage @@ -240,7 +223,6 @@ internal struct _ContiguousArrayBuffer : _ArrayBufferProtocol { } @inlinable - @usableFromInline internal init(_ storage: _ContiguousArrayStorageBase) { _storage = storage } @@ -249,7 +231,6 @@ internal struct _ContiguousArrayBuffer : _ArrayBufferProtocol { /// /// - Warning: does not initialize elements @inlinable - @usableFromInline internal func _initStorageHeader(count: Int, capacity: Int) { #if _runtime(_ObjC) let verbatim = _isBridgedVerbatimToObjectiveC(Element.self) @@ -267,21 +248,18 @@ internal struct _ContiguousArrayBuffer : _ArrayBufferProtocol { /// True, if the array is native and does not need a deferred type check. @inlinable - @usableFromInline internal var arrayPropertyIsNativeTypeChecked: Bool { return true } /// A pointer to the first element. @inlinable - @usableFromInline internal var firstElementAddress: UnsafeMutablePointer { return UnsafeMutablePointer(Builtin.projectTailElems(_storage, Element.self)) } @inlinable - @usableFromInline internal var firstElementAddressIfContiguous: UnsafeMutablePointer? { return firstElementAddress } @@ -289,7 +267,6 @@ internal struct _ContiguousArrayBuffer : _ArrayBufferProtocol { /// Call `body(p)`, where `p` is an `UnsafeBufferPointer` over the /// underlying contiguous storage. @inlinable - @usableFromInline internal func withUnsafeBufferPointer( _ body: (UnsafeBufferPointer) throws -> R ) rethrows -> R { @@ -301,7 +278,6 @@ internal struct _ContiguousArrayBuffer : _ArrayBufferProtocol { /// Call `body(p)`, where `p` is an `UnsafeMutableBufferPointer` /// over the underlying contiguous storage. @inlinable - @usableFromInline internal mutating func withUnsafeMutableBufferPointer( _ body: (UnsafeMutableBufferPointer) throws -> R ) rethrows -> R { @@ -313,20 +289,17 @@ internal struct _ContiguousArrayBuffer : _ArrayBufferProtocol { //===--- _ArrayBufferProtocol conformance -----------------------------------===// /// Create an empty buffer. @inlinable - @usableFromInline internal init() { _storage = _emptyArrayStorage } @inlinable - @usableFromInline internal init(_buffer buffer: _ContiguousArrayBuffer, shiftedToStartIndex: Int) { _sanityCheck(shiftedToStartIndex == 0, "shiftedToStartIndex must be 0") self = buffer } @inlinable - @usableFromInline internal mutating func requestUniqueMutableBackingBuffer( minimumCapacity: Int ) -> _ContiguousArrayBuffer? { @@ -337,13 +310,11 @@ internal struct _ContiguousArrayBuffer : _ArrayBufferProtocol { } @inlinable - @usableFromInline internal mutating func isMutableAndUniquelyReferenced() -> Bool { return isUniquelyReferenced() } @inlinable - @usableFromInline internal mutating func isMutableAndUniquelyReferencedOrPinned() -> Bool { return isUniquelyReferencedOrPinned() } @@ -352,13 +323,11 @@ internal struct _ContiguousArrayBuffer : _ArrayBufferProtocol { /// containing the same number of elements as `self`, return it. /// Otherwise, return `nil`. @inlinable - @usableFromInline internal func requestNativeBuffer() -> _ContiguousArrayBuffer? { return self } @inlinable - @usableFromInline @inline(__always) internal func getElement(_ i: Int) -> Element { _sanityCheck(i >= 0 && i < count, "Array index out of range") @@ -367,7 +336,6 @@ internal struct _ContiguousArrayBuffer : _ArrayBufferProtocol { /// Get or set the value of the ith element. @inlinable - @usableFromInline internal subscript(i: Int) -> Element { @inline(__always) get { @@ -389,7 +357,6 @@ internal struct _ContiguousArrayBuffer : _ArrayBufferProtocol { /// The number of elements the buffer stores. @inlinable - @usableFromInline internal var count: Int { get { return _storage.countAndCapacity.count @@ -408,7 +375,6 @@ internal struct _ContiguousArrayBuffer : _ArrayBufferProtocol { /// Traps unless the given `index` is valid for subscripting, i.e. /// `0 ≤ index < count`. @inlinable - @usableFromInline @inline(__always) internal func _checkValidSubscript(_ index : Int) { _precondition( @@ -419,7 +385,6 @@ internal struct _ContiguousArrayBuffer : _ArrayBufferProtocol { /// The number of elements the buffer can store without reallocation. @inlinable - @usableFromInline internal var capacity: Int { return _storage.countAndCapacity.capacity } @@ -428,7 +393,6 @@ internal struct _ContiguousArrayBuffer : _ArrayBufferProtocol { /// memory starting at `target`. Return a pointer "past the end" of the /// just-initialized memory. @inlinable - @usableFromInline @discardableResult internal func _copyContents( subRange bounds: Range, @@ -448,7 +412,6 @@ internal struct _ContiguousArrayBuffer : _ArrayBufferProtocol { /// Returns a `_SliceBuffer` containing the given `bounds` of values /// from this buffer. @inlinable - @usableFromInline internal subscript(bounds: Range) -> _SliceBuffer { get { return _SliceBuffer( @@ -468,7 +431,6 @@ internal struct _ContiguousArrayBuffer : _ArrayBufferProtocol { /// may need to be considered, such as whether the buffer could be /// some immutable Cocoa container. @inlinable - @usableFromInline internal mutating func isUniquelyReferenced() -> Bool { return _isUnique(&_storage) } @@ -477,7 +439,6 @@ internal struct _ContiguousArrayBuffer : _ArrayBufferProtocol { /// uniquely-referenced or pinned. NOTE: this does not mean /// the buffer is mutable; see the comment on isUniquelyReferenced. @inlinable - @usableFromInline internal mutating func isUniquelyReferencedOrPinned() -> Bool { return _isUniqueOrPinned(&_storage) } @@ -489,7 +450,6 @@ internal struct _ContiguousArrayBuffer : _ArrayBufferProtocol { /// /// - Complexity: O(1). @inlinable - @usableFromInline internal func _asCocoaArray() -> _NSArrayCore { if count == 0 { return _emptyArrayStorage @@ -503,14 +463,12 @@ internal struct _ContiguousArrayBuffer : _ArrayBufferProtocol { /// An object that keeps the elements stored in this buffer alive. @inlinable - @usableFromInline internal var owner: AnyObject { return _storage } /// An object that keeps the elements stored in this buffer alive. @inlinable - @usableFromInline internal var nativeOwner: AnyObject { return _storage } @@ -520,7 +478,6 @@ internal struct _ContiguousArrayBuffer : _ArrayBufferProtocol { /// Two buffers address the same elements when they have the same /// identity and count. @inlinable - @usableFromInline internal var identity: UnsafeRawPointer { return UnsafeRawPointer(firstElementAddress) } @@ -528,7 +485,6 @@ internal struct _ContiguousArrayBuffer : _ArrayBufferProtocol { /// Returns `true` iff we have storage for elements of the given /// `proposedElementType`. If not, we'll be treated as immutable. @inlinable - @usableFromInline func canStoreElements(ofDynamicType proposedElementType: Any.Type) -> Bool { return _storage.canStoreElements(ofDynamicType: proposedElementType) } @@ -539,7 +495,6 @@ internal struct _ContiguousArrayBuffer : _ArrayBufferProtocol { /// /// - Complexity: O(*n*) @inlinable - @usableFromInline internal func storesOnlyElementsOfType( _: U.Type ) -> Bool { @@ -565,7 +520,6 @@ internal struct _ContiguousArrayBuffer : _ArrayBufferProtocol { /// Append the elements of `rhs` to `lhs`. @inlinable -@usableFromInline internal func += ( lhs: inout _ContiguousArrayBuffer, rhs: C ) where C.Element == Element { @@ -603,7 +557,6 @@ extension _ContiguousArrayBuffer : RandomAccessCollection { /// /// In an empty collection, `startIndex == endIndex`. @inlinable - @usableFromInline internal var startIndex: Int { return 0 } @@ -613,7 +566,6 @@ extension _ContiguousArrayBuffer : RandomAccessCollection { /// reachable from `startIndex` by zero or more applications of /// `index(after:)`. @inlinable - @usableFromInline internal var endIndex: Int { return count } @@ -629,7 +581,6 @@ extension Sequence { } @inlinable -@usableFromInline internal func _copySequenceToContiguousArray< S : Sequence >(_ source: S) -> ContiguousArray { @@ -664,7 +615,6 @@ extension Collection { extension _ContiguousArrayBuffer { @inlinable - @usableFromInline internal func _copyToContiguousArray() -> ContiguousArray { return ContiguousArray(_buffer: self) } @@ -679,7 +629,6 @@ extension _ContiguousArrayBuffer { /// makes assigning ranges very slow. Once this has been implemented, this code /// should be changed to use _UnsafePartiallyInitializedContiguousArrayBuffer. @inlinable -@usableFromInline internal func _copyCollectionToContiguousArray< C : Collection >(_ source: C) -> ContiguousArray @@ -693,15 +642,17 @@ internal func _copyCollectionToContiguousArray< _uninitializedCount: count, minimumCapacity: 0) - var p = result.firstElementAddress - var i = source.startIndex - for _ in 0.. { /// Initialize the buffer with an initial size of `initialCapacity` /// elements. @inlinable // FIXME(sil-serialize-all) - @usableFromInline @inline(__always) // For performance reasons. internal init(initialCapacity: Int) { if initialCapacity == 0 { @@ -740,7 +690,6 @@ internal struct _UnsafePartiallyInitializedContiguousArrayBuffer { /// Add an element to the buffer, reallocating if necessary. @inlinable // FIXME(sil-serialize-all) - @usableFromInline @inline(__always) // For performance reasons. internal mutating func add(_ element: Element) { if remainingCapacity == 0 { @@ -764,7 +713,6 @@ internal struct _UnsafePartiallyInitializedContiguousArrayBuffer { /// Add an element to the buffer, which must have remaining capacity. @inlinable // FIXME(sil-serialize-all) - @usableFromInline @inline(__always) // For performance reasons. internal mutating func addWithExistingCapacity(_ element: Element) { _sanityCheck(remainingCapacity > 0, @@ -781,7 +729,6 @@ internal struct _UnsafePartiallyInitializedContiguousArrayBuffer { /// Returns the fully-initialized buffer. `self` is reset to contain an /// empty buffer and cannot be used afterward. @inlinable // FIXME(sil-serialize-all) - @usableFromInline @inline(__always) // For performance reasons. internal mutating func finish() -> ContiguousArray { // Adjust the initialized count of the buffer. @@ -797,7 +744,6 @@ internal struct _UnsafePartiallyInitializedContiguousArrayBuffer { /// Returns the fully-initialized buffer. `self` is reset to contain an /// empty buffer and cannot be used afterward. @inlinable // FIXME(sil-serialize-all) - @usableFromInline @inline(__always) // For performance reasons. internal mutating func finishWithOriginalCount() -> ContiguousArray { _sanityCheck(remainingCapacity == result.capacity - result.count, diff --git a/stdlib/public/core/DebuggerSupport.swift b/stdlib/public/core/DebuggerSupport.swift index 0a6246aaddae5..aec67e7c41e8a 100644 --- a/stdlib/public/core/DebuggerSupport.swift +++ b/stdlib/public/core/DebuggerSupport.swift @@ -10,6 +10,8 @@ // //===----------------------------------------------------------------------===// +import SwiftShims + @_frozen // FIXME(sil-serialize-all) public enum _DebuggerSupport { @_frozen // FIXME(sil-serialize-all) @@ -23,13 +25,11 @@ public enum _DebuggerSupport { case ElementOfPair @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var isCollection: Bool { return self != .NotACollection } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func getChildStatus(child: Mirror) -> CollectionStatus { let disposition = child.displayStyle ?? .struct @@ -46,7 +46,6 @@ public enum _DebuggerSupport { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static func isClass(_ value: Any) -> Bool { if let _ = type(of: value) as? AnyClass { return true @@ -55,7 +54,6 @@ public enum _DebuggerSupport { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static func checkValue( _ value: Any, ifClass: (AnyObject) -> T, @@ -68,7 +66,6 @@ public enum _DebuggerSupport { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static func asObjectIdentifier(_ value: Any) -> ObjectIdentifier? { return checkValue(value, ifClass: { return ObjectIdentifier($0) }, @@ -76,7 +73,6 @@ public enum _DebuggerSupport { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static func asNumericValue(_ value: Any) -> Int { return checkValue(value, ifClass: { return unsafeBitCast($0, to: Int.self) }, @@ -84,7 +80,6 @@ public enum _DebuggerSupport { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static func asStringRepresentation( value: Any?, mirror: Mirror, @@ -149,7 +144,6 @@ public enum _DebuggerSupport { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static func ivarCount(mirror: Mirror) -> Int { let count = Int(mirror.children.count) if let sc = mirror.superclassMirror { @@ -161,7 +155,6 @@ public enum _DebuggerSupport { @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static func shouldExpand( mirror: Mirror, collectionStatus: CollectionStatus, @@ -183,7 +176,6 @@ public enum _DebuggerSupport { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static func printForDebuggerImpl( value: Any?, mirror: Mirror, @@ -345,3 +337,11 @@ func _stringForPrintObject(_ value: Any) -> String { public func _debuggerTestingCheckExpect(_ checked_value: String, _ expected_value: String) {} + +// Utilities to get refcount(s) of class objects. +@_silgen_name("swift_retainCount") +public func _getRetainCount(_ Value: AnyObject) -> UInt +@_silgen_name("swift_unownedRetainCount") +public func _getUnownedRetainCount(_ Value : AnyObject) -> UInt +@_silgen_name("swift_weakRetainCount") +public func _getWeakRetainCount(_ Value : AnyObject) -> UInt diff --git a/stdlib/public/core/Dictionary.swift b/stdlib/public/core/Dictionary.swift index 342463782feac..5db3387a29f1a 100644 --- a/stdlib/public/core/Dictionary.swift +++ b/stdlib/public/core/Dictionary.swift @@ -328,11 +328,11 @@ import SwiftShims /// provides, see the `DictionaryLiteral` type for an alternative. /// /// You can search a dictionary's contents for a particular value using the -/// `contains(where:)` or `index(where:)` methods supplied by default +/// `contains(where:)` or `firstIndex(where:)` methods supplied by default /// implementation. The following example checks to see if `imagePaths` contains /// any paths in the `"/glyphs"` directory: /// -/// let glyphIndex = imagePaths.index { $0.value.hasPrefix("/glyphs") } +/// let glyphIndex = imagePaths.firstIndex(where: { $0.value.hasPrefix("/glyphs") }) /// if let index = glyphIndex { /// print("The '\(imagesPaths[index].key)' image is a glyph.") /// } else { @@ -399,16 +399,16 @@ public struct Dictionary { self = Dictionary(_nativeBuffer: _NativeBuffer()) } - /// Creates a dictionary with at least the given number of elements worth of - /// buffer. + /// Creates an empty dictionary with preallocated space for at least the + /// specified number of elements. /// - /// Use this initializer to avoid intermediate reallocations when you know - /// how many key-value pairs you are adding to a dictionary. The actual - /// capacity of the created dictionary is the smallest power of 2 that - /// is greater than or equal to `minimumCapacity`. + /// Use this initializer to avoid intermediate reallocations of a dictionary's + /// storage buffer when you know how many key-value pairs you are adding to a + /// dictionary after creation. /// - /// - Parameter minimumCapacity: The minimum number of key-value pairs to - /// allocate buffer for in the new dictionary. + /// - Parameter minimumCapacity: The minimum number of key-value pairs that + /// the newly created dictionary should be able to store without + // reallocating its storage buffer. @inlinable // FIXME(sil-serialize-all) public init(minimumCapacity: Int) { _variantBuffer = .native(_NativeBuffer(minimumCapacity: minimumCapacity)) @@ -524,14 +524,12 @@ public struct Dictionary { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_nativeBuffer: _NativeDictionaryBuffer) { _variantBuffer = .native(_nativeBuffer) } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_variantBuffer: _VariantBuffer) { self._variantBuffer = _variantBuffer } @@ -670,10 +668,10 @@ extension Dictionary: Collection { /// this subscript with the resulting value. /// /// For example, to find the key for a particular value in a dictionary, use - /// the `index(where:)` method. + /// the `firstIndex(where:)` method. /// /// let countryCodes = ["BR": "Brazil", "GH": "Ghana", "JP": "Japan"] - /// if let index = countryCodes.index(where: { $0.value == "Japan" }) { + /// if let index = countryCodes.firstIndex(where: { $0.value == "Japan" }) { /// print(countryCodes[index]) /// print("Japan's country code is '\(countryCodes[index].key)'.") /// } else { @@ -1211,7 +1209,6 @@ extension Dictionary { internal var _variantBuffer: Dictionary._VariantBuffer @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_ _dictionary: Dictionary) { self._variantBuffer = _dictionary._variantBuffer } @@ -1257,7 +1254,7 @@ extension Dictionary { @inlinable // FIXME(sil-serialize-all) public func _customContainsEquatableElement(_ element: Element) -> Bool? { - return _variantBuffer.index(forKey: element) != nil + return _variantBuffer.containsKey(element) } @inlinable // FIXME(sil-serialize-all) @@ -1265,6 +1262,12 @@ extension Dictionary { return Optional(_variantBuffer.index(forKey: element)) } + @inlinable // FIXME(sil-serialize-all) + public func _customLastIndexOfEquatableElement(_ element: Element) -> Index?? { + // The first and last elements are the same because each element is unique. + return _customIndexOfEquatableElement(element) + } + @inlinable // FIXME(sil-serialize-all) public static func ==(lhs: Keys, rhs: Keys) -> Bool { // Equal if the two dictionaries share storage. @@ -1310,7 +1313,6 @@ extension Dictionary { internal var _variantBuffer: Dictionary._VariantBuffer @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_ _dictionary: Dictionary) { self._variantBuffer = _dictionary._variantBuffer } @@ -1447,27 +1449,28 @@ extension Dictionary: Equatable where Value: Equatable { } extension Dictionary: Hashable where Value: Hashable { + /// Hashes the essential components of this value by feeding them into the + /// given hasher. + /// + /// - Parameter hasher: The hasher to use when combining the components + /// of this instance. @inlinable // FIXME(sil-serialize-all) - public var hashValue: Int { - return _hashValue(for: self) - } - - @inlinable // FIXME(sil-serialize-all) - public func _hash(into hasher: inout _Hasher) { + public func hash(into hasher: inout Hasher) { var commutativeHash = 0 for (k, v) in self { - var elementHasher = _Hasher() - elementHasher.append(k) - elementHasher.append(v) - commutativeHash ^= elementHasher.finalize() + // Note that we use a copy of our own hasher here. This makes hash values + // dependent on its state, eliminating static collision patterns. + var elementHasher = hasher + elementHasher.combine(k) + elementHasher.combine(v) + commutativeHash ^= elementHasher._finalize() } - hasher.append(commutativeHash) + hasher.combine(commutativeHash) } } extension Dictionary: CustomStringConvertible, CustomDebugStringConvertible { @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func _makeDescription() -> String { if count == 0 { return "[:]" @@ -1513,7 +1516,6 @@ internal enum _MergeError: Error { /// Equivalent to `NSDictionary.allKeys`, but does not leave objects on the /// autorelease pool. @inlinable // FIXME(sil-serialize-all) -@usableFromInline // FIXME(sil-serialize-all) internal func _stdlib_NSDictionary_allKeys(_ nsd: _NSDictionary) -> _HeapBuffer { let count = nsd.count @@ -1809,7 +1811,6 @@ internal class _RawNativeDictionaryStorage // This API is unsafe and needs a `_fixLifetime` in the caller. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @nonobjc internal final var _initializedHashtableEntriesBitMapBuffer: UnsafeMutablePointer { @@ -1820,7 +1821,6 @@ internal class _RawNativeDictionaryStorage /// created without any elements. The contents of the storage should never /// be mutated. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @nonobjc internal static var empty: RawStorage { return Builtin.bridgeFromRawPointer( @@ -1830,7 +1830,6 @@ internal class _RawNativeDictionaryStorage // This type is made with allocWithTailElems, so no init is ever called. // But we still need to have an init to satisfy the compiler. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @nonobjc internal init(_doNotCallMe: ()) { _sanityCheckFailure("Only create this by using the `empty` singleton") @@ -1845,7 +1844,6 @@ internal class _RawNativeDictionaryStorage /// _HashableTypedNativeDictionaryStorage overloads this to give /// _NativeSelfNSEnumerator proper type parameters. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @objc internal func enumerator() -> _NSEnumerator { return _NativeDictionaryNSEnumerator( @@ -1853,14 +1851,12 @@ internal class _RawNativeDictionaryStorage } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @objc(copyWithZone:) internal func copy(with zone: _SwiftNSZone?) -> AnyObject { return self } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @objc(countByEnumeratingWithState:objects:count:) internal func countByEnumerating( with state: UnsafeMutablePointer<_SwiftNSFastEnumerationState>, @@ -1881,7 +1877,6 @@ internal class _RawNativeDictionaryStorage } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @objc internal required init( objects: UnsafePointer, @@ -1892,20 +1887,17 @@ internal class _RawNativeDictionaryStorage } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @objc(objectForKey:) internal func objectFor(_ aKey: AnyObject) -> AnyObject? { return nil } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func keyEnumerator() -> _NSEnumerator { return enumerator() } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func getObjects(_ objects: UnsafeMutablePointer?, andKeys keys: UnsafeMutablePointer?) { // Do nothing, we're empty @@ -1944,7 +1936,6 @@ internal class _TypedNativeDictionaryStorage // This type is made with allocWithTailElems, so no init is ever called. // But we still need to have an init to satisfy the compiler. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @nonobjc override internal init(_doNotCallMe: ()) { _sanityCheckFailure("Only create this by calling Buffer's inits") @@ -1952,7 +1943,6 @@ internal class _TypedNativeDictionaryStorage #if _runtime(_ObjC) @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @objc internal required init( objects: UnsafePointer, @@ -1976,7 +1966,6 @@ final internal class _HashableTypedNativeDictionaryStorage // This type is made with allocWithTailElems, so no init is ever called. // But we still need to have an init to satisfy the compiler. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @nonobjc override internal init(_doNotCallMe: ()) { _sanityCheckFailure("Only create this by calling Buffer's inits'") @@ -1989,26 +1978,22 @@ final internal class _HashableTypedNativeDictionaryStorage // just wrappers around a RawNativeDictionaryStorage. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var buffer: Buffer { return Buffer(_storage: self) } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var full: FullContainer { return FullContainer(_nativeBuffer: buffer) } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal override func enumerator() -> _NSEnumerator { return _NativeDictionaryNSEnumerator( Buffer(_storage: self)) } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @objc(countByEnumeratingWithState:objects:count:) internal override func countByEnumerating( with state: UnsafeMutablePointer<_SwiftNSFastEnumerationState>, @@ -2050,7 +2035,6 @@ final internal class _HashableTypedNativeDictionaryStorage } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @nonobjc internal func getObjectFor(_ aKey: AnyObject) -> AnyObject? { guard let nativeKey = _conditionallyBridgeFromObjectiveC(aKey, Key.self) @@ -2066,7 +2050,6 @@ final internal class _HashableTypedNativeDictionaryStorage } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @objc internal required init( objects: UnsafePointer, @@ -2077,7 +2060,6 @@ final internal class _HashableTypedNativeDictionaryStorage } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @objc(objectForKey:) override func objectFor(_ aKey: AnyObject) -> AnyObject? { return getObjectFor(aKey) @@ -2085,7 +2067,6 @@ final internal class _HashableTypedNativeDictionaryStorage // We also override the following methods for efficiency. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @objc override func getObjects(_ objects: UnsafeMutablePointer?, andKeys keys: UnsafeMutablePointer?) { @@ -2143,7 +2124,6 @@ internal struct _NativeDictionaryBuffer { /// Creates a Buffer with a storage that is typed, but doesn't understand /// Hashing. Mostly for bridging; prefer `init(minimumCapacity:)`. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_exactBucketCount bucketCount: Int, unhashable: ()) { let bitmapWordCount = _UnsafeBitMap.sizeInWords(forSizeInBits: bucketCount) let storage = Builtin.allocWithTailElems_3(TypedStorage.self, @@ -2156,7 +2136,6 @@ internal struct _NativeDictionaryBuffer { /// Given a bucket count and uninitialized RawStorage, completes the /// initialization and returns a Buffer. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_exactBucketCount bucketCount: Int, storage: RawStorage) { storage.bucketCount = bucketCount storage.count = 0 @@ -2190,7 +2169,7 @@ internal struct _NativeDictionaryBuffer { // // FIXME: Use an approximation of true per-instance seeding. We can't just // use the base address, because COW copies need to share the same seed. - let seed = _Hasher._seed + let seed = Hasher._seed let perturbation = bucketCount _storage.seed = (seed.0 ^ UInt64(truncatingIfNeeded: perturbation), seed.1) } @@ -2198,13 +2177,11 @@ internal struct _NativeDictionaryBuffer { // Forwarding the individual fields of the storage in various forms @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal var bucketCount: Int { return _assumeNonNegative(_storage.bucketCount) } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal var count: Int { set { _storage.count = newValue @@ -2215,7 +2192,6 @@ internal struct _NativeDictionaryBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var _initializedHashtableEntriesBitMapBuffer: UnsafeMutablePointer { return _storage._initializedHashtableEntriesBitMapBuffer @@ -2223,28 +2199,24 @@ internal struct _NativeDictionaryBuffer { // This API is unsafe and needs a `_fixLifetime` in the caller. @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal var keys: UnsafeMutablePointer { return _storage.keys.assumingMemoryBound(to: Key.self) } // This API is unsafe and needs a `_fixLifetime` in the caller. @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal var values: UnsafeMutablePointer { return _storage.values.assumingMemoryBound(to: Value.self) } /// Constructs a buffer adopting the given storage. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_storage: RawStorage) { self._storage = _storage } /// Constructs an instance from the empty singleton. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init() { self._storage = RawStorage.empty } @@ -2253,7 +2225,6 @@ internal struct _NativeDictionaryBuffer { // but only the parts that don't actually rely on hashing. @inlinable // FIXME(sil-serialize-all) - @usableFromInline @inline(__always) internal func key(at i: Int) -> Key { _sanityCheck(i >= 0 && i < bucketCount) @@ -2269,7 +2240,6 @@ internal struct _NativeDictionaryBuffer { /// /// Intended for use with verbatim bridgeable keys. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func bridgedKey(at index: Index) -> AnyObject { let k = key(at: index.offset) return _bridgeAnythingToObjectiveC(k) @@ -2279,7 +2249,6 @@ internal struct _NativeDictionaryBuffer { /// /// Intended for use with verbatim bridgeable keys. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func bridgedValue(at index: Index) -> AnyObject { let v = value(at: index.offset) return _bridgeAnythingToObjectiveC(v) @@ -2287,7 +2256,6 @@ internal struct _NativeDictionaryBuffer { #endif @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal func isInitializedEntry(at i: Int) -> Bool { _sanityCheck(i >= 0 && i < bucketCount) defer { _fixLifetime(self) } @@ -2296,7 +2264,6 @@ internal struct _NativeDictionaryBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @_transparent internal func destroyEntry(at i: Int) { _sanityCheck(isInitializedEntry(at: i)) @@ -2308,7 +2275,6 @@ internal struct _NativeDictionaryBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @_transparent internal func initializeKey(_ k: Key, value v: Value, at i: Int) { _sanityCheck(!isInitializedEntry(at: i)) @@ -2320,7 +2286,6 @@ internal struct _NativeDictionaryBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @_transparent internal func moveInitializeEntry(from: Buffer, at: Int, toEntryAt: Int) { _sanityCheck(!isInitializedEntry(at: toEntryAt)) @@ -2333,7 +2298,6 @@ internal struct _NativeDictionaryBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline @_transparent internal func value(at i: Int) -> Value { _sanityCheck(isInitializedEntry(at: i)) @@ -2343,7 +2307,6 @@ internal struct _NativeDictionaryBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @_transparent internal func setKey(_ key: Key, value: Value, at i: Int) { _sanityCheck(isInitializedEntry(at: i)) @@ -2354,7 +2317,6 @@ internal struct _NativeDictionaryBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal var startIndex: Index { // We start at "index after -1" instead of "0" because we need to find the // first occupied slot. @@ -2362,13 +2324,11 @@ internal struct _NativeDictionaryBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal var endIndex: Index { return Index(offset: bucketCount) } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal func index(after i: Index) -> Index { _precondition(i != endIndex) var idx = i.offset + 1 @@ -2380,13 +2340,11 @@ internal struct _NativeDictionaryBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal func formIndex(after i: inout Index) { i = index(after: i) } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func assertingGet(_ i: Index) -> SequenceElement { _precondition(i.offset >= 0 && i.offset < bucketCount) _precondition( @@ -2405,7 +2363,6 @@ extension _NativeDictionaryBuffer where Key: Hashable internal typealias SequenceElement = (key: Key, value: Value) @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @inline(__always) internal init(minimumCapacity: Int) { let bucketCount = _NativeDictionaryBuffer.bucketCount( @@ -2415,7 +2372,6 @@ extension _NativeDictionaryBuffer where Key: Hashable } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @inline(__always) internal init(bucketCount: Int) { // Actual bucket count is the next power of 2 greater than or equal to @@ -2428,7 +2384,6 @@ extension _NativeDictionaryBuffer where Key: Hashable /// Create a buffer instance with room for at least 'bucketCount' entries, /// marking all entries invalid. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_exactBucketCount bucketCount: Int) { let bitmapWordCount = _UnsafeBitMap.sizeInWords(forSizeInBits: bucketCount) let storage = Builtin.allocWithTailElems_3(HashTypedStorage.self, @@ -2440,7 +2395,6 @@ extension _NativeDictionaryBuffer where Key: Hashable #if _runtime(_ObjC) @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func bridged() -> _NSDictionary { // We can zero-cost bridge if our keys are verbatim // or if we're the empty singleton. @@ -2465,7 +2419,6 @@ extension _NativeDictionaryBuffer where Key: Hashable /// A textual representation of `self`. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var description: String { var result = "" #if INTERNAL_CHECKS_ENABLED @@ -2482,7 +2435,6 @@ extension _NativeDictionaryBuffer where Key: Hashable } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var _bucketMask: Int { // The bucket count is not negative, therefore subtracting 1 will not // overflow. @@ -2490,23 +2442,18 @@ extension _NativeDictionaryBuffer where Key: Hashable } @inlinable // FIXME(sil-serialize-all) - @usableFromInline @inline(__always) // For performance reasons. internal func _bucket(_ k: Key) -> Int { - var hasher = _Hasher(seed: _storage.seed) - hasher.append(k) - return hasher.finalize() & _bucketMask + return k._rawHashValue(seed: _storage.seed) & _bucketMask } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal func _index(after bucket: Int) -> Int { // Bucket is within 0 and bucketCount. Therefore adding 1 does not overflow. return (bucket &+ 1) & _bucketMask } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func _prev(_ bucket: Int) -> Int { // Bucket is not negative. Therefore subtracting 1 does not overflow. return (bucket &- 1) & _bucketMask @@ -2517,7 +2464,6 @@ extension _NativeDictionaryBuffer where Key: Hashable /// If the key is not present, returns the position where it could be /// inserted. @inlinable // FIXME(sil-serialize-all) - @usableFromInline @inline(__always) internal func _find(_ key: Key, startBucket: Int) -> (pos: Index, found: Bool) { @@ -2539,7 +2485,6 @@ extension _NativeDictionaryBuffer where Key: Hashable } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @_transparent internal static func bucketCount( forCapacity capacity: Int, @@ -2554,7 +2499,6 @@ extension _NativeDictionaryBuffer where Key: Hashable /// The `key` should not be present in the Dictionary. /// This function does *not* update `count`. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func unsafeAddNew(key newKey: Key, value: Value) { let (i, found) = _find(newKey, startBucket: _bucket(newKey)) _precondition( @@ -2567,7 +2511,6 @@ extension _NativeDictionaryBuffer where Key: Hashable // @inlinable // FIXME(sil-serialize-all) - @usableFromInline @inline(__always) internal func index(forKey key: Key) -> Index? { if count == 0 { @@ -2579,7 +2522,6 @@ extension _NativeDictionaryBuffer where Key: Hashable } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func assertingGet(_ key: Key) -> Value { let (i, found) = _find(key, startBucket: _bucket(key)) _precondition(found, "Key not found") @@ -2587,7 +2529,6 @@ extension _NativeDictionaryBuffer where Key: Hashable } @inlinable // FIXME(sil-serialize-all) - @usableFromInline @inline(__always) internal func maybeGet(_ key: Key) -> Value? { if count == 0 { @@ -2603,7 +2544,6 @@ extension _NativeDictionaryBuffer where Key: Hashable } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @discardableResult internal func updateValue(_ value: Value, forKey key: Key) -> Value? { _sanityCheckFailure( @@ -2611,7 +2551,6 @@ extension _NativeDictionaryBuffer where Key: Hashable } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @discardableResult internal func insert( _ value: Value, forKey key: Key @@ -2621,7 +2560,6 @@ extension _NativeDictionaryBuffer where Key: Hashable } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @discardableResult internal func remove(at index: Index) -> SequenceElement { _sanityCheckFailure( @@ -2629,7 +2567,6 @@ extension _NativeDictionaryBuffer where Key: Hashable } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @discardableResult internal func removeValue(forKey key: Key) -> Value? { _sanityCheckFailure( @@ -2637,14 +2574,12 @@ extension _NativeDictionaryBuffer where Key: Hashable } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func removeAll(keepingCapacity keepCapacity: Bool) { _sanityCheckFailure( "don't call mutating methods on _NativeDictionaryBuffer") } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal static func fromArray(_ elements: [SequenceElementWithoutLabels]) -> Buffer { @@ -2678,13 +2613,11 @@ final internal class _NativeDictionaryNSEnumerator internal typealias Index = _NativeDictionaryIndex @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal override required init() { _sanityCheckFailure("don't call this designated initializer") } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_ buffer: Buffer) { self.buffer = buffer nextIndex = buffer.startIndex @@ -2705,7 +2638,6 @@ final internal class _NativeDictionaryNSEnumerator // @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @objc internal func nextObject() -> AnyObject? { if nextIndex == endIndex { @@ -2717,7 +2649,6 @@ final internal class _NativeDictionaryNSEnumerator } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @objc(countByEnumeratingWithState:objects:count:) internal func countByEnumerating( with state: UnsafeMutablePointer<_SwiftNSFastEnumerationState>, @@ -2766,7 +2697,6 @@ final internal class _SwiftDeferredNSDictionary internal typealias BridgedIndex = _NativeDictionaryIndex @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @nonobjc internal init(bucketCount: Int = 2) { nativeBuffer = NativeBuffer(bucketCount: bucketCount) @@ -2774,7 +2704,6 @@ final internal class _SwiftDeferredNSDictionary } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(nativeBuffer: NativeBuffer) { self.nativeBuffer = nativeBuffer super.init() @@ -2793,7 +2722,6 @@ final internal class _SwiftDeferredNSDictionary internal var nativeBuffer: NativeBuffer @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @objc(copyWithZone:) internal func copy(with zone: _SwiftNSZone?) -> AnyObject { // Instances of this class should be visible outside of standard library as @@ -2809,7 +2737,6 @@ final internal class _SwiftDeferredNSDictionary // @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @objc internal required init( objects: UnsafePointer, @@ -2820,21 +2747,18 @@ final internal class _SwiftDeferredNSDictionary } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @objc(objectForKey:) internal func objectFor(_ aKey: AnyObject) -> AnyObject? { return bridgingObjectForKey(aKey) } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @objc internal func keyEnumerator() -> _NSEnumerator { return enumerator() } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @objc internal func getObjects( _ objects: UnsafeMutablePointer?, @@ -2844,7 +2768,6 @@ final internal class _SwiftDeferredNSDictionary } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @objc(enumerateKeysAndObjectsWithOptions:usingBlock:) internal func enumerateKeysAndObjects(options: Int, using block: @convention(block) (Unmanaged, Unmanaged, @@ -2865,7 +2788,6 @@ final internal class _SwiftDeferredNSDictionary /// Returns the pointer to the stored property, which contains bridged /// Dictionary elements. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @nonobjc internal var _heapStorageBridgedPtr: UnsafeMutablePointer { return _getUnsafePointerToStoredProperties(self).assumingMemoryBound( @@ -2874,7 +2796,6 @@ final internal class _SwiftDeferredNSDictionary /// The buffer for bridged Dictionary elements, if present. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @nonobjc internal var _bridgedStorage: BridgedBuffer.RawStorage? { get { @@ -2887,7 +2808,6 @@ final internal class _SwiftDeferredNSDictionary /// Attach a buffer for bridged Dictionary elements. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @nonobjc internal func _initializeHeapStorageBridged(_ newStorage: AnyObject) { _stdlib_atomicInitializeARCRef( @@ -2896,13 +2816,11 @@ final internal class _SwiftDeferredNSDictionary /// Returns the bridged Dictionary values. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var bridgedBuffer: BridgedBuffer { return BridgedBuffer(_storage: _bridgedStorage!) } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @nonobjc internal func bridgeEverything() { if _fastPath(_bridgedStorage != nil) { @@ -2933,7 +2851,6 @@ final internal class _SwiftDeferredNSDictionary } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @nonobjc internal func bridgedAllKeysAndValues( _ objects: UnsafeMutablePointer?, @@ -2979,14 +2896,12 @@ final internal class _SwiftDeferredNSDictionary } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @objc internal var count: Int { return nativeBuffer.count } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @nonobjc internal func bridgingObjectForKey(_ aKey: AnyObject) -> AnyObject? { @@ -3003,7 +2918,6 @@ final internal class _SwiftDeferredNSDictionary } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @objc internal func enumerator() -> _NSEnumerator { bridgeEverything() @@ -3011,7 +2925,6 @@ final internal class _SwiftDeferredNSDictionary } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @objc(countByEnumeratingWithState:objects:count:) internal func countByEnumerating( with state: UnsafeMutablePointer<_SwiftNSFastEnumerationState>, @@ -3073,7 +2986,6 @@ internal struct _CocoaDictionaryBuffer: _HashBuffer { internal var cocoaDictionary: _NSDictionary @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(cocoaDictionary: _NSDictionary) { self.cocoaDictionary = cocoaDictionary } @@ -3086,32 +2998,27 @@ internal struct _CocoaDictionaryBuffer: _HashBuffer { internal typealias Value = AnyObject @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var startIndex: Index { return Index(cocoaDictionary, startIndex: ()) } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var endIndex: Index { return Index(cocoaDictionary, endIndex: ()) } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal func index(after i: Index) -> Index { return i.successor() } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func formIndex(after i: inout Index) { // FIXME: swift-3-indexing-model: optimize if possible. i = i.successor() } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal func index(forKey key: Key) -> Index? { // Fast path that does not involve creating an array of all keys. In case // the key is present, this lookup is a penalty for the slow path, but the @@ -3135,7 +3042,6 @@ internal struct _CocoaDictionaryBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func assertingGet(_ i: Index) -> SequenceElement { let key: Key = i.allKeys[i.currentKeyIndex] let value: Value = i.cocoaDictionary.objectFor(key)! @@ -3144,7 +3050,6 @@ internal struct _CocoaDictionaryBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func assertingGet(_ key: Key) -> Value { let value: Value? = cocoaDictionary.objectFor(key) _precondition(value != nil, "Key not found in underlying NSDictionary") @@ -3152,7 +3057,6 @@ internal struct _CocoaDictionaryBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @inline(__always) internal func maybeGet(_ key: Key) -> Value? { @@ -3161,14 +3065,12 @@ internal struct _CocoaDictionaryBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @discardableResult internal mutating func updateValue(_ value: Value, forKey key: Key) -> Value? { _sanityCheckFailure("cannot mutate NSDictionary") } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @discardableResult internal mutating func insert( _ value: Value, forKey key: Key @@ -3177,33 +3079,28 @@ internal struct _CocoaDictionaryBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @discardableResult internal mutating func remove(at index: Index) -> SequenceElement { _sanityCheckFailure("cannot mutate NSDictionary") } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @discardableResult internal mutating func removeValue(forKey key: Key) -> Value? { _sanityCheckFailure("cannot mutate NSDictionary") } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal mutating func removeAll(keepingCapacity keepCapacity: Bool) { _sanityCheckFailure("cannot mutate NSDictionary") } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var count: Int { return cocoaDictionary.count } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static func fromArray(_ elements: [SequenceElementWithoutLabels]) -> _CocoaDictionaryBuffer { @@ -3231,14 +3128,12 @@ internal enum _VariantDictionaryBuffer: _HashBuffer { #endif @inlinable // FIXME(sil-serialize-all) - @usableFromInline @_transparent internal var guaranteedNative: Bool { return _canBeClass(Key.self) == 0 || _canBeClass(Value.self) == 0 } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal mutating func isUniquelyReferenced() -> Bool { // Note that &self drills down through .native(NativeBuffer) to the first // property in NativeBuffer, which is the reference to the storage. @@ -3259,7 +3154,6 @@ internal enum _VariantDictionaryBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal var asNative: NativeBuffer { get { switch self { @@ -3277,7 +3171,6 @@ internal enum _VariantDictionaryBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal mutating func ensureNativeBuffer() { #if _runtime(_ObjC) if _fastPath(guaranteedNative) { return } @@ -3289,7 +3182,6 @@ internal enum _VariantDictionaryBuffer: _HashBuffer { #if _runtime(_ObjC) @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var asCocoa: CocoaBuffer { switch self { case .native: @@ -3302,7 +3194,6 @@ internal enum _VariantDictionaryBuffer: _HashBuffer { /// Return true if self is native. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var _isNative: Bool { #if _runtime(_ObjC) switch self { @@ -3318,7 +3209,6 @@ internal enum _VariantDictionaryBuffer: _HashBuffer { @inline(__always) @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal mutating func ensureUniqueNativeBufferNative( withBucketCount desiredBucketCount: Int ) -> (reallocated: Bool, capacityChanged: Bool) { @@ -3353,7 +3243,6 @@ internal enum _VariantDictionaryBuffer: _HashBuffer { @inline(__always) @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal mutating func ensureUniqueNativeBuffer( withCapacity minimumCapacity: Int ) -> (reallocated: Bool, capacityChanged: Bool) { @@ -3366,7 +3255,6 @@ internal enum _VariantDictionaryBuffer: _HashBuffer { /// Ensure this we hold a unique reference to a native buffer /// having at least `minimumCapacity` elements. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal mutating func ensureUniqueNativeBuffer( withBucketCount desiredBucketCount: Int ) -> (reallocated: Bool, capacityChanged: Bool) { @@ -3411,9 +3299,8 @@ internal enum _VariantDictionaryBuffer: _HashBuffer { } #if _runtime(_ObjC) - @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @inline(never) + @usableFromInline internal mutating func migrateDataToNativeBuffer( _ cocoaBuffer: _CocoaDictionaryBuffer ) { @@ -3426,7 +3313,6 @@ internal enum _VariantDictionaryBuffer: _HashBuffer { /// Reserves enough space for the specified number of elements to be stored /// without reallocating additional storage. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal mutating func reserveCapacity(_ capacity: Int) { _ = ensureUniqueNativeBuffer(withCapacity: capacity) } @@ -3439,7 +3325,6 @@ internal enum _VariantDictionaryBuffer: _HashBuffer { /// newly allocated storage. For native storage, this is the element count /// at which adding any more elements will exceed the load factor. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var capacity: Int { switch self { case .native: @@ -3459,7 +3344,6 @@ internal enum _VariantDictionaryBuffer: _HashBuffer { internal typealias Index = DictionaryIndex @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var startIndex: Index { if _fastPath(guaranteedNative) { return ._native(asNative.startIndex) @@ -3476,7 +3360,6 @@ internal enum _VariantDictionaryBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var endIndex: Index { if _fastPath(guaranteedNative) { return ._native(asNative.endIndex) @@ -3493,7 +3376,6 @@ internal enum _VariantDictionaryBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal func index(after i: Index) -> Index { if _fastPath(guaranteedNative) { return ._native(asNative.index(after: i._nativeIndex)) @@ -3510,14 +3392,12 @@ internal enum _VariantDictionaryBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func formIndex(after i: inout Index) { // FIXME: swift-3-indexing-model: optimize if possible. i = index(after: i) } @inlinable // FIXME(sil-serialize-all) - @usableFromInline @inline(__always) internal func index(forKey key: Key) -> Index? { if _fastPath(guaranteedNative) { @@ -3545,7 +3425,23 @@ internal enum _VariantDictionaryBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) + @inline(__always) + internal func containsKey(_ key: Key) -> Bool { + if _fastPath(guaranteedNative) { + return asNative.index(forKey: key) != nil + } + + switch self { + case .native: + return asNative.index(forKey: key) != nil +#if _runtime(_ObjC) + case .cocoa(let cocoaBuffer): + return SelfType.maybeGetFromCocoaBuffer(cocoaBuffer, forKey: key) != nil +#endif + } + } + + @inlinable // FIXME(sil-serialize-all) internal func assertingGet(_ i: Index) -> SequenceElement { if _fastPath(guaranteedNative) { return asNative.assertingGet(i._nativeIndex) @@ -3566,7 +3462,6 @@ internal enum _VariantDictionaryBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func assertingGet(_ key: Key) -> Value { if _fastPath(guaranteedNative) { return asNative.assertingGet(key) @@ -3586,9 +3481,8 @@ internal enum _VariantDictionaryBuffer: _HashBuffer { } #if _runtime(_ObjC) - @inlinable // FIXME(sil-serialize-all) - @usableFromInline @inline(never) + @usableFromInline internal static func maybeGetFromCocoaBuffer( _ cocoaBuffer: CocoaBuffer, forKey key: Key ) -> Value? { @@ -3601,7 +3495,6 @@ internal enum _VariantDictionaryBuffer: _HashBuffer { #endif @inlinable // FIXME(sil-serialize-all) - @usableFromInline @inline(__always) internal func maybeGet(_ key: Key) -> Value? { if _fastPath(guaranteedNative) { @@ -3619,7 +3512,6 @@ internal enum _VariantDictionaryBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal mutating func nativeUpdateValue( _ value: Value, forKey key: Key ) -> Value? { @@ -3649,7 +3541,6 @@ internal enum _VariantDictionaryBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @discardableResult internal mutating func updateValue( _ value: Value, forKey key: Key @@ -3671,7 +3562,6 @@ internal enum _VariantDictionaryBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal mutating func nativePointerToValue(at i: Index) -> UnsafeMutablePointer { // This is a performance optimization that was put in to ensure that we did @@ -3688,7 +3578,6 @@ internal enum _VariantDictionaryBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal mutating func pointerToValue(at i: Index) -> UnsafeMutablePointer { if _fastPath(guaranteedNative) { @@ -3716,7 +3605,6 @@ internal enum _VariantDictionaryBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal mutating func nativePointerToValue( forKey key: Key, insertingDefault defaultValue: () -> Value ) -> (inserted: Bool, pointer: UnsafeMutablePointer) { @@ -3741,7 +3629,6 @@ internal enum _VariantDictionaryBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal mutating func pointerToValue( forKey key: Key, insertingDefault defaultValue: () -> Value ) -> (inserted: Bool, pointer: UnsafeMutablePointer) { @@ -3750,7 +3637,6 @@ internal enum _VariantDictionaryBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal mutating func nativeInsert( _ value: Value, forKey key: Key ) -> (inserted: Bool, memberAfterInsert: Value) { @@ -3775,7 +3661,6 @@ internal enum _VariantDictionaryBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @discardableResult internal mutating func insert( _ value: Value, forKey key: Key @@ -3785,7 +3670,6 @@ internal enum _VariantDictionaryBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func nativeMapValues( _ transform: (Value) throws -> T ) rethrows -> _VariantDictionaryBuffer { @@ -3807,7 +3691,6 @@ internal enum _VariantDictionaryBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func mapValues( _ transform: (Value) throws -> T ) rethrows -> _VariantDictionaryBuffer { @@ -3838,7 +3721,6 @@ internal enum _VariantDictionaryBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal mutating func nativeMerge( _ keysAndValues: S, uniquingKeysWith combine: (Value, Value) throws -> Value @@ -3878,7 +3760,6 @@ internal enum _VariantDictionaryBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal mutating func merge( _ keysAndValues: S, uniquingKeysWith combine: (Value, Value) throws -> Value @@ -3888,7 +3769,6 @@ internal enum _VariantDictionaryBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal mutating func nativeGroup( _ values: S, by keyForValue: (S.Element) throws -> Key @@ -3917,7 +3797,6 @@ internal enum _VariantDictionaryBuffer: _HashBuffer { /// - parameter offset: The offset of the element that will be deleted. /// Precondition: there should be an initialized entry at offset. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal mutating func nativeDelete( _ nativeBuffer: NativeBuffer, idealBucket: Int, offset: Int ) { @@ -3982,7 +3861,6 @@ internal enum _VariantDictionaryBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal mutating func nativeRemoveObject(forKey key: Key) -> Value? { var idealBucket = asNative._bucket(key) var (index, found) = asNative._find(key, startBucket: idealBucket) @@ -4017,7 +3895,6 @@ internal enum _VariantDictionaryBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal mutating func nativeRemove( at nativeIndex: NativeIndex ) -> SequenceElement { @@ -4044,7 +3921,6 @@ internal enum _VariantDictionaryBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @discardableResult internal mutating func remove(at index: Index) -> SequenceElement { if _fastPath(guaranteedNative) { @@ -4073,7 +3949,6 @@ internal enum _VariantDictionaryBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @discardableResult internal mutating func removeValue(forKey key: Key) -> Value? { if _fastPath(guaranteedNative) { @@ -4096,7 +3971,6 @@ internal enum _VariantDictionaryBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal mutating func nativeRemoveAll() { if !isUniquelyReferenced() { asNative = NativeBuffer(_exactBucketCount: asNative.bucketCount) @@ -4116,7 +3990,6 @@ internal enum _VariantDictionaryBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal mutating func removeAll(keepingCapacity keepCapacity: Bool) { if count == 0 { return @@ -4143,7 +4016,6 @@ internal enum _VariantDictionaryBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var count: Int { if _fastPath(guaranteedNative) { return asNative.count @@ -4163,7 +4035,6 @@ internal enum _VariantDictionaryBuffer: _HashBuffer { /// /// - Complexity: O(1). @inlinable // FIXME(sil-serialize-all) - @usableFromInline @inline(__always) internal func makeIterator() -> DictionaryIterator { switch self { @@ -4178,7 +4049,6 @@ internal enum _VariantDictionaryBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static func fromArray(_ elements: [SequenceElement]) -> _VariantDictionaryBuffer { @@ -4193,7 +4063,6 @@ internal struct _NativeDictionaryIndex: Comparable { internal var offset: Int @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal init(offset: Int) { self.offset = offset } @@ -4202,7 +4071,6 @@ internal struct _NativeDictionaryIndex: Comparable { extension _NativeDictionaryIndex { @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static func < ( lhs: _NativeDictionaryIndex, rhs: _NativeDictionaryIndex @@ -4210,7 +4078,6 @@ extension _NativeDictionaryIndex { return lhs.offset < rhs.offset } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static func <= ( lhs: _NativeDictionaryIndex, rhs: _NativeDictionaryIndex @@ -4218,7 +4085,6 @@ extension _NativeDictionaryIndex { return lhs.offset <= rhs.offset } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static func > ( lhs: _NativeDictionaryIndex, rhs: _NativeDictionaryIndex @@ -4226,7 +4092,6 @@ extension _NativeDictionaryIndex { return lhs.offset > rhs.offset } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static func >= ( lhs: _NativeDictionaryIndex, rhs: _NativeDictionaryIndex @@ -4234,7 +4099,6 @@ extension _NativeDictionaryIndex { return lhs.offset >= rhs.offset } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static func == ( lhs: _NativeDictionaryIndex, rhs: _NativeDictionaryIndex @@ -4269,7 +4133,6 @@ internal struct _CocoaDictionaryIndex: Comparable { internal var currentKeyIndex: Int @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_ cocoaDictionary: _NSDictionary, startIndex: ()) { self.cocoaDictionary = cocoaDictionary self.allKeys = _stdlib_NSDictionary_allKeys(cocoaDictionary) @@ -4277,7 +4140,6 @@ internal struct _CocoaDictionaryIndex: Comparable { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_ cocoaDictionary: _NSDictionary, endIndex: ()) { self.cocoaDictionary = cocoaDictionary self.allKeys = _stdlib_NSDictionary_allKeys(cocoaDictionary) @@ -4285,7 +4147,6 @@ internal struct _CocoaDictionaryIndex: Comparable { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_ cocoaDictionary: _NSDictionary, _ allKeys: _HeapBuffer, _ currentKeyIndex: Int @@ -4299,7 +4160,6 @@ internal struct _CocoaDictionaryIndex: Comparable { /// /// - Precondition: The next value is representable. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func successor() -> _CocoaDictionaryIndex { // FIXME: swift-3-indexing-model: remove this method. _precondition( @@ -4311,7 +4171,6 @@ internal struct _CocoaDictionaryIndex: Comparable { extension _CocoaDictionaryIndex { @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static func < ( lhs: _CocoaDictionaryIndex, rhs: _CocoaDictionaryIndex @@ -4319,7 +4178,6 @@ extension _CocoaDictionaryIndex { return lhs.currentKeyIndex < rhs.currentKeyIndex } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static func <= ( lhs: _CocoaDictionaryIndex, rhs: _CocoaDictionaryIndex @@ -4327,7 +4185,6 @@ extension _CocoaDictionaryIndex { return lhs.currentKeyIndex <= rhs.currentKeyIndex } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static func > ( lhs: _CocoaDictionaryIndex, rhs: _CocoaDictionaryIndex @@ -4335,7 +4192,6 @@ extension _CocoaDictionaryIndex { return lhs.currentKeyIndex > rhs.currentKeyIndex } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static func >= ( lhs: _CocoaDictionaryIndex, rhs: _CocoaDictionaryIndex @@ -4343,7 +4199,6 @@ extension _CocoaDictionaryIndex { return lhs.currentKeyIndex >= rhs.currentKeyIndex } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static func == ( lhs: _CocoaDictionaryIndex, rhs: _CocoaDictionaryIndex @@ -4395,7 +4250,6 @@ extension Dictionary { #endif @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_value: DictionaryIndexRepresentation) { self._value = _value } @@ -4404,27 +4258,24 @@ extension Dictionary { internal var _value: DictionaryIndexRepresentation @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal static func _native(_ index: _NativeIndex) -> Index { return DictionaryIndex(_value: ._native(index)) } #if _runtime(_ObjC) @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal static func _cocoa(_ index: _CocoaIndex) -> Index { return DictionaryIndex(_value: ._cocoa(index)) } #endif @inlinable // FIXME(sil-serialize-all) - @usableFromInline @_transparent internal var _guaranteedNative: Bool { return _canBeClass(Key.self) == 0 && _canBeClass(Value.self) == 0 } - @usableFromInline // FIXME(sil-serialize-all) + @inlinable // FIXME(sil-serialize-all) @_transparent internal var _nativeIndex: _NativeIndex { switch _value { @@ -4439,7 +4290,6 @@ extension Dictionary { #if _runtime(_ObjC) @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @_transparent internal var _cocoaIndex: _CocoaIndex { switch _value { @@ -4500,19 +4350,24 @@ extension Dictionary.Index { } @inlinable // FIXME(sil-serialize-all) - public var hashValue: Int { + public func hash(into hasher: inout Hasher) { + #if _runtime(_ObjC) if _fastPath(_guaranteedNative) { - return _nativeIndex.offset + hasher.combine(0 as UInt8) + hasher.combine(_nativeIndex.offset) + return } - switch _value { case ._native(let nativeIndex): - return nativeIndex.offset - #if _runtime(_ObjC) + hasher.combine(0 as UInt8) + hasher.combine(nativeIndex.offset) case ._cocoa(let cocoaIndex): - return cocoaIndex.currentKeyIndex - #endif + hasher.combine(1 as UInt8) + hasher.combine(cocoaIndex.currentKeyIndex) } + #else + hasher.combine(_nativeIndex.offset) + #endif } } @@ -4541,7 +4396,6 @@ final internal class _CocoaDictionaryIterator: IteratorProtocol { internal let cocoaDictionary: _NSDictionary @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var _fastEnumerationStatePtr: UnsafeMutablePointer<_SwiftNSFastEnumerationState> { return _getUnsafePointerToStoredProperties(self).assumingMemoryBound( @@ -4549,7 +4403,6 @@ final internal class _CocoaDictionaryIterator: IteratorProtocol { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var _fastEnumerationStackBufPtr: UnsafeMutablePointer<_CocoaFastEnumerationStackBuf> { return UnsafeMutableRawPointer(_fastEnumerationStatePtr + 1) @@ -4566,13 +4419,11 @@ final internal class _CocoaDictionaryIterator: IteratorProtocol { internal var itemCount: Int = 0 @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal init(_ cocoaDictionary: _NSDictionary) { self.cocoaDictionary = cocoaDictionary } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal func next() -> Element? { if itemIndex < 0 { return nil @@ -4649,13 +4500,11 @@ public struct DictionaryIterator: IteratorProtocol { internal var _state: DictionaryIteratorRepresentation @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_state: DictionaryIteratorRepresentation) { self._state = _state } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal static func _native( start: _NativeIndex, end: _NativeIndex, buffer: _NativeBuffer ) -> DictionaryIterator { @@ -4664,7 +4513,6 @@ public struct DictionaryIterator: IteratorProtocol { } #if _runtime(_ObjC) @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal static func _cocoa( _ iterator: _CocoaDictionaryIterator ) -> DictionaryIterator{ @@ -4673,14 +4521,12 @@ public struct DictionaryIterator: IteratorProtocol { #endif @inlinable // FIXME(sil-serialize-all) - @usableFromInline @_transparent internal var _guaranteedNative: Bool { return _canBeClass(Key.self) == 0 || _canBeClass(Value.self) == 0 } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal mutating func _nativeNext() -> (key: Key, value: Value)? { switch _state { case ._native(let startIndex, let endIndex, let buffer): diff --git a/stdlib/public/core/DropWhile.swift b/stdlib/public/core/DropWhile.swift index 085e797f8894f..e488cee67d488 100644 --- a/stdlib/public/core/DropWhile.swift +++ b/stdlib/public/core/DropWhile.swift @@ -19,7 +19,6 @@ public struct LazyDropWhileSequence { /// Create an instance with elements `transform(x)` for each element /// `x` of base. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_base: Base, predicate: @escaping (Element) -> Bool) { self._base = _base self._predicate = predicate @@ -43,7 +42,6 @@ extension LazyDropWhileSequence { public typealias Element = Base.Element @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_base: Base.Iterator, predicate: @escaping (Element) -> Bool) { self._base = _base self._predicate = predicate @@ -126,7 +124,6 @@ public struct LazyDropWhileCollection { public typealias Element = Base.Element @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_base: Base, predicate: @escaping (Element) -> Bool) { self._base = _base self._predicate = predicate @@ -161,7 +158,6 @@ extension LazyDropWhileCollection { public let base: Base.Index @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_base: Base.Index) { self.base = _base } @@ -187,14 +183,20 @@ extension LazyDropWhileCollection.Index: Equatable, Comparable { } extension LazyDropWhileCollection.Index: Hashable where Base.Index: Hashable { + /// The hash value. @inlinable // FIXME(sil-serialize-all) public var hashValue: Int { return base.hashValue } + /// Hashes the essential components of this value by feeding them into the + /// given hasher. + /// + /// - Parameter hasher: The hasher to use when combining the components + /// of this instance. @inlinable // FIXME(sil-serialize-all) - public func _hash(into hasher: inout _Hasher) { - hasher.append(base) + public func hash(into hasher: inout Hasher) { + hasher.combine(base) } } diff --git a/stdlib/public/core/Dump.swift b/stdlib/public/core/Dump.swift index b2b2a5eae5030..40a3ff2e840c0 100644 --- a/stdlib/public/core/Dump.swift +++ b/stdlib/public/core/Dump.swift @@ -86,7 +86,6 @@ public func dump( /// Dump an object's contents. User code should use dump(). @inlinable // FIXME(sil-serialize-all) -@usableFromInline // FIXME(sil-serialize-all) @_semantics("optimize.sil.specialize.generic.never") internal func _dump_unlocked( _ value: Any, @@ -187,7 +186,6 @@ internal func _dump_unlocked( /// Dump information about an object's superclass, given a mirror reflecting /// that superclass. @inlinable // FIXME(sil-serialize-all) -@usableFromInline // FIXME(sil-serialize-all) @_semantics("optimize.sil.specialize.generic.never") internal func _dumpSuperclass_unlocked( mirror: Mirror, diff --git a/stdlib/public/core/ErrorType.swift b/stdlib/public/core/ErrorType.swift index 8691233ef14bc..d0e9228d4ca7f 100644 --- a/stdlib/public/core/ErrorType.swift +++ b/stdlib/public/core/ErrorType.swift @@ -35,7 +35,7 @@ import SwiftShims /// /// enum IntParsingError: Error { /// case overflow -/// case invalidInput(String) +/// case invalidInput(Character) /// } /// /// The `invalidInput` case includes the invalid character as an associated @@ -48,8 +48,9 @@ import SwiftShims /// extension Int { /// init(validating input: String) throws { /// // ... -/// if !_isValid(s) { -/// throw IntParsingError.invalidInput(s) +/// let c = _nextCharacter(from: input) +/// if !_isValid(c) { +/// throw IntParsingError.invalidInput(c) /// } /// // ... /// } @@ -173,7 +174,7 @@ internal func _getErrorDefaultUserInfo(_ error: T) -> AnyObject? /// Provided by the ErrorObject implementation. /// Called by the casting machinery and by the Foundation overlay. @_silgen_name("_swift_stdlib_bridgeErrorToNSError") -public func _bridgeErrorToNSError(_ error: Error) -> AnyObject +public func _bridgeErrorToNSError(_ error: __owned Error) -> AnyObject #endif /// Invoked by the compiler when the subexpression of a `try!` expression diff --git a/stdlib/public/core/ExistentialCollection.swift.gyb b/stdlib/public/core/ExistentialCollection.swift.gyb index 14bb576c0fd0a..7bb8064e2379e 100644 --- a/stdlib/public/core/ExistentialCollection.swift.gyb +++ b/stdlib/public/core/ExistentialCollection.swift.gyb @@ -24,9 +24,8 @@ from gyb_stdlib_support import ( import SwiftShims -@inlinable // FIXME(sil-serialize-all) -@usableFromInline @inline(never) +@usableFromInline internal func _abstract( file: StaticString = #file, line: UInt = #line @@ -95,7 +94,6 @@ public struct AnyIterator { } @inlinable - @usableFromInline internal init(_box: _AnyIteratorBoxBase) { self._box = _box } @@ -120,12 +118,10 @@ extension AnyIterator: Sequence { } @_fixed_layout internal struct _ClosureBasedIterator : IteratorProtocol { @inlinable - @usableFromInline internal init(_ body: @escaping () -> Element?) { self._body = body } @inlinable - @usableFromInline internal func next() -> Element? { return _body() } @usableFromInline internal let _body: () -> Element? @@ -135,11 +131,9 @@ internal struct _ClosureBasedIterator : IteratorProtocol { @usableFromInline internal class _AnyIteratorBoxBase : IteratorProtocol { @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init() {} @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) deinit {} /// Advances to the next element and returns it, or `nil` if no next element /// exists. @@ -148,7 +142,6 @@ internal class _AnyIteratorBoxBase : IteratorProtocol { /// /// - Note: Subclasses must override this method. @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal func next() -> Element? { _abstract() } } @@ -158,13 +151,10 @@ internal final class _IteratorBox< Base : IteratorProtocol > : _AnyIteratorBoxBase { @inlinable - @usableFromInline internal init(_ base: Base) { self._base = base } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) deinit {} @inlinable - @usableFromInline internal override func next() -> Base.Element? { return _base.next() } @usableFromInline internal var _base: Base @@ -194,18 +184,14 @@ internal class _AnyRandomAccessCollectionBox % if Kind == 'Sequence': @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init() { } - @usableFromInline @inlinable internal func _makeIterator() -> AnyIterator { _abstract() } - @usableFromInline @inlinable internal var _underestimatedCount: Int { _abstract() } - @usableFromInline @inlinable internal func _map( _ transform: (Element) throws -> T @@ -213,7 +199,6 @@ internal class _AnyRandomAccessCollectionBox _abstract() } - @usableFromInline @inlinable internal func _filter( _ isIncluded: (Element) throws -> Bool @@ -221,7 +206,6 @@ internal class _AnyRandomAccessCollectionBox _abstract() } - @usableFromInline @inlinable internal func _forEach( _ body: (Element) throws -> Void @@ -229,7 +213,6 @@ internal class _AnyRandomAccessCollectionBox _abstract() } - @usableFromInline @inlinable internal func __customContainsEquatableElement( _ element: Element @@ -237,7 +220,6 @@ internal class _AnyRandomAccessCollectionBox _abstract() } - @usableFromInline @inlinable internal func __preprocessingPass( _ preprocess: () throws -> R @@ -245,13 +227,11 @@ internal class _AnyRandomAccessCollectionBox _abstract() } - @usableFromInline @inlinable internal func __copyToContiguousArray() -> ContiguousArray { _abstract() } - @usableFromInline @inlinable internal func __copyContents(initializing buf: UnsafeMutableBufferPointer) -> (AnyIterator,UnsafeMutableBufferPointer.Index) { @@ -262,11 +242,9 @@ internal class _AnyRandomAccessCollectionBox % # This deinit has to be present on all the types @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) deinit {} % override = 'override' if Kind != 'Sequence' else '' - @usableFromInline @inlinable internal ${override} func _drop( while predicate: (Element) throws -> Bool @@ -274,25 +252,21 @@ internal class _AnyRandomAccessCollectionBox _abstract() } - @usableFromInline @inlinable internal ${override} func _dropFirst(_ n: Int) -> _Any${Kind}Box { _abstract() } - @usableFromInline @inlinable internal ${override} func _dropLast(_ n: Int) -> _Any${Kind}Box { _abstract() } - @usableFromInline @inlinable internal ${override} func _prefix(_ maxLength: Int) -> _Any${Kind}Box { _abstract() } - @usableFromInline @inlinable internal ${override} func _prefix( while predicate: (Element) throws -> Bool @@ -300,13 +274,11 @@ internal class _AnyRandomAccessCollectionBox _abstract() } - @usableFromInline @inlinable internal ${override} func _suffix(_ maxLength: Int) -> _Any${Kind}Box { _abstract() } - @usableFromInline @inlinable internal func _split( maxSplits: Int, omittingEmptySubsequences: Bool, @@ -316,19 +288,15 @@ internal class _AnyRandomAccessCollectionBox } % if Kind == 'Collection': - @usableFromInline @inlinable internal subscript(i: _AnyIndexBox) -> Element { _abstract() } - @usableFromInline @inlinable internal func _index(after i: _AnyIndexBox) -> _AnyIndexBox { _abstract() } - @usableFromInline @inlinable internal func _formIndex(after i: _AnyIndexBox) { _abstract() } - @usableFromInline @inlinable internal func _index( _ i: _AnyIndexBox, offsetBy n: Int @@ -336,7 +304,6 @@ internal class _AnyRandomAccessCollectionBox _abstract() } - @usableFromInline @inlinable internal func _index( _ i: _AnyIndexBox, offsetBy n: Int, limitedBy limit: _AnyIndexBox @@ -344,13 +311,11 @@ internal class _AnyRandomAccessCollectionBox _abstract() } - @usableFromInline @inlinable internal func _formIndex(_ i: inout _AnyIndexBox, offsetBy n: Int) { _abstract() } - @usableFromInline @inlinable internal func _formIndex( _ i: inout _AnyIndexBox, offsetBy n: Int, limitedBy limit: _AnyIndexBox @@ -358,7 +323,6 @@ internal class _AnyRandomAccessCollectionBox _abstract() } - @usableFromInline @inlinable internal func _distance( from start: _AnyIndexBox, to end: _AnyIndexBox @@ -380,19 +344,17 @@ internal class _AnyRandomAccessCollectionBox */ @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal var _count: Int { _abstract() } // TODO: swift-3-indexing-model: forward the following methods. /* func _customIndexOfEquatableElement(element: Element) -> Index?? + func _customLastIndexOfEquatableElement(element: Element) -> Index?? */ @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal var _first: Element? { _abstract() } - @usableFromInline @inlinable internal init( _startIndex: _AnyIndexBox, @@ -411,7 +373,6 @@ internal class _AnyRandomAccessCollectionBox % if Kind in ['Collection', 'BidirectionalCollection', 'RandomAccessCollection']: % override = 'override' if Kind != 'Collection' else '' - @usableFromInline @inlinable internal ${override} subscript( start start: _AnyIndexBox, @@ -420,13 +381,10 @@ internal class _AnyRandomAccessCollectionBox % end % if Kind == 'BidirectionalCollection': - @usableFromInline @inlinable internal func _index(before i: _AnyIndexBox) -> _AnyIndexBox { _abstract() } - @usableFromInline @inlinable internal func _formIndex(before i: _AnyIndexBox) { _abstract() } - @usableFromInline @inlinable internal var _last: Element? { _abstract() } % end @@ -455,99 +413,83 @@ internal final class _${Kind}Box : _Any${Kind}Box AnyIterator { return AnyIterator(_base.makeIterator()) } - @usableFromInline @inlinable internal override var _underestimatedCount: Int { return _base.underestimatedCount } - @usableFromInline @inlinable internal override func _map( _ transform: (Element) throws -> T ) rethrows -> [T] { return try _base.map(transform) } - @usableFromInline @inlinable internal override func _filter( _ isIncluded: (Element) throws -> Bool ) rethrows -> [Element] { return try _base.filter(isIncluded) } - @usableFromInline @inlinable internal override func _forEach( _ body: (Element) throws -> Void ) rethrows { return try _base.forEach(body) } - @usableFromInline @inlinable internal override func __customContainsEquatableElement( _ element: Element ) -> Bool? { return _base._customContainsEquatableElement(element) } - @usableFromInline @inlinable internal override func __preprocessingPass( _ preprocess: () throws -> R ) rethrows -> R? { return try _base._preprocessingPass(preprocess) } - @usableFromInline @inlinable internal override func __copyToContiguousArray() -> ContiguousArray { return _base._copyToContiguousArray() } - @usableFromInline @inlinable internal override func __copyContents(initializing buf: UnsafeMutableBufferPointer) -> (AnyIterator,UnsafeMutableBufferPointer.Index) { let (it,idx) = _base._copyContents(initializing: buf) return (AnyIterator(it),idx) } - @usableFromInline @inlinable internal override func _drop( while predicate: (Element) throws -> Bool ) rethrows -> _Any${Kind}Box { return try _${Kind}Box(_base: _base.drop(while: predicate)) } - @usableFromInline @inlinable internal override func _dropFirst(_ n: Int) -> _Any${Kind}Box { return _${Kind}Box(_base: _base.dropFirst(n)) } - @usableFromInline @inlinable internal override func _dropLast(_ n: Int) -> _Any${Kind}Box { return _${Kind}Box(_base: _base.dropLast(n)) } - @usableFromInline @inlinable internal override func _prefix( while predicate: (Element) throws -> Bool ) rethrows -> _Any${Kind}Box { return try _${Kind}Box(_base: _base.prefix(while: predicate)) } - @usableFromInline @inlinable internal override func _prefix(_ maxLength: Int) -> _Any${Kind}Box { return _${Kind}Box(_base: _base.prefix(maxLength)) } - @usableFromInline @inlinable internal override func _suffix(_ maxLength: Int) -> _Any${Kind}Box { return _${Kind}Box(_base: _base.suffix(maxLength)) } % for ResultKind in EqualAndWeakerKinds: - @usableFromInline @inlinable internal override func _split( maxSplits: Int, omittingEmptySubsequences: Bool, @@ -564,18 +506,15 @@ internal final class _${Kind}Box : _Any${Kind}Box : _Any${Kind}Box : _Any${Kind}Box Element { return _base[_unbox(position)] } - @usableFromInline @inlinable internal override subscript(start start: _AnyIndexBox, end end: _AnyIndexBox) -> _Any${Kind}Box @@ -610,13 +546,11 @@ internal final class _${Kind}Box : _Any${Kind}Box _AnyIndexBox { return _IndexBox(_base: _base.index(after: _unbox(position))) } - @usableFromInline @inlinable internal override func _formIndex(after position: _AnyIndexBox) { if let p = position as? _IndexBox { @@ -625,7 +559,6 @@ internal final class _${Kind}Box : _Any${Kind}Box : _Any${Kind}Box : _Any${Kind}Box : _Any${Kind}Box : _Any${Kind}Box : _Any${Kind}Box _AnyIndexBox { return _IndexBox(_base: _base.index(before: _unbox(position))) } - @usableFromInline @inlinable internal override func _formIndex(before position: _AnyIndexBox) { if let p = position as? _IndexBox { @@ -709,7 +634,6 @@ internal final class _${Kind}Box : _Any${Kind}Box { @usableFromInline internal var _makeUnderlyingIterator: () -> Iterator - @usableFromInline @inlinable internal init(_ makeUnderlyingIterator: @escaping () -> Iterator) { self._makeUnderlyingIterator = makeUnderlyingIterator @@ -736,7 +659,6 @@ internal struct _ClosureBasedSequence { } extension _ClosureBasedSequence: Sequence { - @usableFromInline @inlinable internal func makeIterator() -> Iterator { return _makeUnderlyingIterator() @@ -763,7 +685,6 @@ public struct AnySequence { self.init(_ClosureBasedSequence(makeUnderlyingIterator)) } - @usableFromInline @inlinable internal init(_box: _AnySequenceBox) { self._box = _box @@ -916,37 +837,31 @@ internal final class _IndexBox< @usableFromInline internal var _base: BaseIndex - @usableFromInline @inlinable internal init(_base: BaseIndex) { self._base = _base } - @usableFromInline @inlinable internal func _unsafeUnbox(_ other: _AnyIndexBox) -> BaseIndex { return unsafeDowncast(other, to: _IndexBox.self)._base } - @usableFromInline @inlinable internal var _typeID: ObjectIdentifier { return ObjectIdentifier(type(of: self)) } - @usableFromInline @inlinable internal func _unbox() -> T? { return (self as _AnyIndexBox as? _IndexBox)?._base } - @usableFromInline @inlinable internal func _isEqual(to rhs: _AnyIndexBox) -> Bool { return _base == _unsafeUnbox(rhs) } - @usableFromInline @inlinable internal func _isLess(than rhs: _AnyIndexBox) -> Bool { return _base < _unsafeUnbox(rhs) @@ -965,13 +880,11 @@ public struct AnyIndex { self._box = _IndexBox(_base: base) } - @usableFromInline @inlinable internal init(_box: _AnyIndexBox) { self._box = _box } - @usableFromInline @inlinable internal var _typeID: ObjectIdentifier { return _box._typeID @@ -1032,7 +945,6 @@ public struct ${Self} { @usableFromInline internal let _box: _${Self}Box - @usableFromInline @inlinable internal init(_box: _${Self}Box) { self._box = _box diff --git a/stdlib/public/core/Filter.swift b/stdlib/public/core/Filter.swift index cd802dfe336e9..8ccee844954bf 100644 --- a/stdlib/public/core/Filter.swift +++ b/stdlib/public/core/Filter.swift @@ -55,7 +55,6 @@ extension LazyFilterSequence { /// Creates an instance that produces the elements `x` of `base` /// for which `isIncluded(x) == true`. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_base: Base.Iterator, _ isIncluded: @escaping (Base.Element) -> Bool) { self._base = _base self._predicate = isIncluded @@ -218,7 +217,6 @@ extension LazyFilterCollection : LazyCollectionProtocol { @inline(__always) @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func _advanceIndex(_ i: inout Index, step: Int) { repeat { _base.formIndex(&i, offsetBy: step) @@ -227,7 +225,6 @@ extension LazyFilterCollection : LazyCollectionProtocol { @inline(__always) @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func _ensureBidirectional(step: Int) { // FIXME: This seems to be the best way of checking whether _base is // forward only without adding an extra protocol requirement. @@ -330,6 +327,12 @@ extension LazyFilterCollection : LazyCollectionProtocol { public subscript(bounds: Range) -> SubSequence { return SubSequence(_base: _base[bounds], _predicate) } + + @inlinable + public func _customLastIndexOfEquatableElement(_ element: Element) -> Index?? { + guard _predicate(element) else { return .some(nil) } + return _base._customLastIndexOfEquatableElement(element) + } } extension LazyFilterCollection : BidirectionalCollection diff --git a/stdlib/public/core/FixedArray.swift.gyb b/stdlib/public/core/FixedArray.swift.gyb index f3b904a85fefb..e56a28b05d7df 100644 --- a/stdlib/public/core/FixedArray.swift.gyb +++ b/stdlib/public/core/FixedArray.swift.gyb @@ -43,19 +43,16 @@ internal struct _FixedArray${N} { extension _FixedArray${N} { @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static var capacity: Int { @inline(__always) get { return ${N} } } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var capacity: Int { @inline(__always) get { return ${N} } } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var count: Int { @inline(__always) get { return Int(truncatingIfNeeded: _count) } @inline(__always) set { _count = Int8(newValue) } @@ -66,21 +63,17 @@ extension _FixedArray${N} : RandomAccessCollection, MutableCollection { internal typealias Index = Int @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var startIndex : Index { return 0 } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var endIndex : Index { return count } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal subscript(i: Index) -> T { - @usableFromInline @inline(__always) get { let count = self.count // for exclusive access @@ -97,7 +90,6 @@ extension _FixedArray${N} : RandomAccessCollection, MutableCollection { } return res } - @usableFromInline @inline(__always) set { _sanityCheck(i >= 0 && i < count) @@ -108,14 +100,12 @@ extension _FixedArray${N} : RandomAccessCollection, MutableCollection { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline @inline(__always) internal func index(after i: Index) -> Index { return i+1 } @inlinable // FIXME(sil-serialize-all) - @usableFromInline @inline(__always) internal func index(before i: Index) -> Index { return i-1 @@ -124,7 +114,6 @@ extension _FixedArray${N} : RandomAccessCollection, MutableCollection { extension _FixedArray${N} { @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal mutating func append(_ newElement: T) { _sanityCheck(count < capacity) _count += 1 @@ -134,7 +123,6 @@ extension _FixedArray${N} { extension _FixedArray${N} where T : ExpressibleByIntegerLiteral { @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @inline(__always) internal init(count: Int) { _sanityCheck(count >= 0 && count <= _FixedArray${N}.capacity) @@ -148,14 +136,12 @@ extension _FixedArray${N} where T : ExpressibleByIntegerLiteral { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @inline(__always) internal init() { self.init(count: 0) } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @inline(__always) internal init(allZeros: ()) { self.init(count: ${N}) @@ -164,7 +150,6 @@ extension _FixedArray${N} where T : ExpressibleByIntegerLiteral { extension _FixedArray${N} { @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal mutating func withUnsafeMutableBufferPointer( _ body: (UnsafeMutableBufferPointer) throws -> R ) rethrows -> R { @@ -181,7 +166,6 @@ extension _FixedArray${N} { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal mutating func withUnsafeBufferPointer( _ body: (UnsafeBufferPointer) throws -> R ) rethrows -> R { diff --git a/stdlib/public/core/Flatten.swift b/stdlib/public/core/Flatten.swift index 9e7dbd8270ab6..7d36a8ae11d89 100644 --- a/stdlib/public/core/Flatten.swift +++ b/stdlib/public/core/Flatten.swift @@ -35,7 +35,6 @@ public struct FlattenSequence where Base.Element: Sequence { /// /// - Complexity: O(1) @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_base: Base) { self._base = _base } @@ -51,7 +50,6 @@ extension FlattenSequence { /// Construct around a `base` iterator. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_base: Base.Iterator) { self._base = _base } @@ -188,7 +186,6 @@ extension FlattenCollection { internal let _inner: Base.Element.Index? @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_ _outer: Base.Index, _ inner: Base.Element.Index?) { self._outer = _outer self._inner = inner @@ -232,15 +229,15 @@ extension FlattenCollection.Index : Comparable { extension FlattenCollection.Index : Hashable where Base.Index : Hashable, Base.Element.Index : Hashable { + /// Hashes the essential components of this value by feeding them into the + /// given hasher. + /// + /// - Parameter hasher: The hasher to use when combining the components + /// of this instance. @inlinable // FIXME(sil-serialize-all) - public var hashValue: Int { - return _hashValue(for: self) - } - - @inlinable // FIXME(sil-serialize-all) - public func _hash(into hasher: inout _Hasher) { - hasher.append(_outer) - hasher.append(_inner) + public func hash(into hasher: inout Hasher) { + hasher.combine(_outer) + hasher.combine(_inner) } } @@ -312,7 +309,6 @@ extension FlattenCollection : Collection { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func _index(after i: Index) -> Index { let innerCollection = _base[i._outer] let nextInner = innerCollection.index(after: i._inner!) @@ -333,7 +329,6 @@ extension FlattenCollection : Collection { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func _index(before i: Index) -> Index { var prevOuter = i._outer if prevOuter == _base.endIndex { @@ -393,7 +388,6 @@ extension FlattenCollection : Collection { @inline(__always) @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func _advanceIndex(_ i: inout Index, step: Int) { _sanityCheck(-1...1 ~= step, "step should be within the -1...1 range") i = step < 0 ? _index(before: i) : _index(after: i) @@ -401,7 +395,6 @@ extension FlattenCollection : Collection { @inline(__always) @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func _ensureBidirectional(step: Int) { // FIXME: This seems to be the best way of checking whether _base is // forward only without adding an extra protocol requirement. diff --git a/stdlib/public/core/FloatingPoint.swift.gyb b/stdlib/public/core/FloatingPoint.swift.gyb index 3ccea1d363b02..4ee807b8d4541 100644 --- a/stdlib/public/core/FloatingPoint.swift.gyb +++ b/stdlib/public/core/FloatingPoint.swift.gyb @@ -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 - 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 @@ -1225,6 +1225,31 @@ public enum FloatingPointSign: Int { /// The sign for a negative value. case minus + + // Explicit declarations of otherwise-synthesized members to make them + // @inlinable, promising that we will never change the implementation. + + @inlinable + public init?(rawValue: Int) { + switch rawValue { + case 0: self = .plus + case 1: self = .minus + default: return nil + } + } + + @inlinable + public var rawValue: Int { + switch self { + case .plus: return 0 + case .minus: return 1 + } + } + + @inlinable + public static func ==(a: FloatingPointSign, b: FloatingPointSign) -> Bool { + return a.rawValue == b.rawValue + } } /// The IEEE 754 floating-point classes. @@ -2374,6 +2399,112 @@ extension BinaryFloatingPoint { */ } +% for Range in ['Range', 'ClosedRange']: +% exampleRange = '10.0..<20.0' if Range == 'Range' else '10.0...20.0' +extension BinaryFloatingPoint +where Self.RawSignificand : FixedWidthInteger, + Self.RawSignificand.Stride : SignedInteger, + Self.RawSignificand.Magnitude : UnsignedInteger { + + /// Returns a random value within the specified range, using the given + /// generator as a source for randomness. + /// + /// Use this method to generate a floating-point value within a specific + /// range when you are using a custom random number generator. This example + /// creates three new values in the range `${exampleRange}`. + /// + /// for _ in 1...3 { + /// print(Double.random(in: ${exampleRange}, using: &myGenerator)) + /// } + /// // Prints "18.1900709259179" + /// // Prints "14.2286325689993" + /// // Prints "13.1485686260762" + /// + /// The `random(in:using:)` static method chooses a random value from a + /// continuous uniform distribution in `range`, and then converts that value + /// to the nearest representable value in this type. Depending on the size and + /// span of `range`, some concrete values may be represented more frequently + /// than others. + /// + /// - Parameters: + /// - range: The range in which to create a random value. +% if Range == 'Range': + /// `range` must not be empty. +% end + /// - generator: The random number generator to use when creating the + /// new random value. + /// - Returns: A random value within the bounds of `range`. + @inlinable + public static func random( + in range: ${Range}, + using generator: inout T + ) -> Self { + _precondition( + !range.isEmpty, + "Can't get random value with an empty range" + ) + let delta = range.upperBound - range.lowerBound + let rand: Self.RawSignificand + if Self.RawSignificand.bitWidth == Self.significandBitCount + 1 { + rand = generator.next() +% if 'Closed' in Range: + let tmp: UInt8 = generator.next() & 1 + if rand == Self.RawSignificand.max && tmp == 1 { + return range.upperBound + } +% end + } else { + let significandCount = Self.significandBitCount + 1 + let maxSignificand: Self.RawSignificand = 1 << significandCount +% if 'Closed' not in Range: + rand = generator.next(upperBound: maxSignificand) +% else: + rand = generator.next(upperBound: maxSignificand + 1) + if rand == maxSignificand { + return range.upperBound + } +% end + } + let unitRandom = Self.init(rand) * Self.ulpOfOne / 2 + return delta * unitRandom + range.lowerBound + } + + /// Returns a random value within the specified range. + /// + /// Use this method to generate a floating-point value within a specific + /// range. This example creates three new values in the range + /// `${exampleRange}`. + /// + /// for _ in 1...3 { + /// print(Double.random(in: ${exampleRange})) + /// } + /// // Prints "18.1900709259179" + /// // Prints "14.2286325689993" + /// // Prints "13.1485686260762" + /// + /// The `random()` static method chooses a random value from a continuous + /// uniform distribution in `range`, and then converts that value to the + /// nearest representable value in this type. Depending on the size and span + /// of `range`, some concrete values may be represented more frequently than + /// others. + /// + /// This method uses the default random generator, `Random.default`. The call + /// to `Double.random(in: ${exampleRange})` above is equivalent to calling + /// `Double.random(in: ${exampleRange}, using: &Random.default)`. + /// + /// - Parameter range: The range in which to create a random value. +% if Range == 'Range': + /// `range` must not be empty. +% end + /// - Returns: A random value within the bounds of `range`. + @inlinable + public static func random(in range: ${Range}) -> Self { + return Self.random(in: range, using: &Random.default) + } +} + +% end + /// Returns the absolute value of `x`. @inlinable // FIXME(sil-serialize-all) @_transparent diff --git a/stdlib/public/core/FloatingPointParsing.swift.gyb b/stdlib/public/core/FloatingPointParsing.swift.gyb index 59b187437ac36..7a7dca8a0466a 100644 --- a/stdlib/public/core/FloatingPointParsing.swift.gyb +++ b/stdlib/public/core/FloatingPointParsing.swift.gyb @@ -31,7 +31,6 @@ cFuncSuffix2 = {32: 'f', 64: 'd', 80: 'ld'} /// Returns `true` iff isspace(u) would return nonzero when the current /// locale is the C locale. @inlinable // FIXME(sil-serialize-all) -@usableFromInline // FIXME(sil-serialize-all) internal func _isspace_clocale(_ u: UTF16.CodeUnit) -> Bool { return "\t\n\u{b}\u{c}\r ".utf16.contains(u) } diff --git a/stdlib/public/core/FloatingPointTypes.swift.gyb b/stdlib/public/core/FloatingPointTypes.swift.gyb index c74297e17fcc0..6c41aff2c48fb 100644 --- a/stdlib/public/core/FloatingPointTypes.swift.gyb +++ b/stdlib/public/core/FloatingPointTypes.swift.gyb @@ -172,19 +172,16 @@ extension ${Self}: BinaryFloatingPoint { // Implementation details. @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal static var _infinityExponent: UInt { @inline(__always) get { return 1 &<< UInt(exponentBitCount) - 1 } } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static var _exponentBias: UInt { @inline(__always) get { return _infinityExponent &>> 1 } } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static var _significandMask: ${RawSignificand} { @inline(__always) get { return 1 &<< ${RawSignificand}(significandBitCount) - 1 @@ -192,7 +189,6 @@ extension ${Self}: BinaryFloatingPoint { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal static var _quietNaNMask: ${RawSignificand} { @inline(__always) get { return 1 &<< ${RawSignificand}(significandBitCount - 1) @@ -325,31 +321,26 @@ extension ${Self}: BinaryFloatingPoint { internal var _storage: (UInt64, UInt16, /* pad */ UInt16, UInt16, UInt16) @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @_transparent internal var explicitSignificand: UInt64 { return _storage.0 } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @_transparent internal var signAndExponent: UInt16 { return _storage.1 } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @_transparent internal var sign: FloatingPointSign { return FloatingPointSign(rawValue: Int(signAndExponent &>> 15))! } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @_transparent internal var exponentBitPattern: UInt { return UInt(signAndExponent) & 0x7fff } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @_transparent internal init(explicitSignificand: UInt64, signAndExponent: UInt16) { _storage = (explicitSignificand, signAndExponent, 0, 0, 0) @@ -357,7 +348,6 @@ extension ${Self}: BinaryFloatingPoint { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var _representation: _Representation { return unsafeBitCast(self, to: _Representation.self) } @@ -381,7 +371,6 @@ extension ${Self}: BinaryFloatingPoint { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static var _explicitBitMask: UInt64 { @inline(__always) get { return 1 &<< 63 } } @@ -1532,17 +1521,13 @@ extension ${Self} : _ExpressibleByBuiltinFloatLiteral { % end extension ${Self} : Hashable { - /// The number's hash value. + /// Hashes the essential components of this value by feeding them into the + /// given hasher. /// - /// Hash values are not guaranteed to be equal across different executions of - /// your program. Do not save hash values to use during a future execution. - @inlinable // FIXME(sil-serialize-all) - public var hashValue: Int { - return _hashValue(for: self) - } - + /// - Parameter hasher: The hasher to use when combining the components + /// of this instance. @inlinable // FIXME(sil-serialize-all) - public func _hash(into hasher: inout _Hasher) { + public func hash(into hasher: inout Hasher) { var v = self if isZero { // To satisfy the axiom that equality implies hash equality, we need to @@ -1550,13 +1535,29 @@ extension ${Self} : Hashable { v = 0 } %if bits == 80: - hasher.append(v._representation.signAndExponent) - hasher.append(v.significandBitPattern) + hasher.combine(v._representation.signAndExponent) + hasher.combine(v.significandBitPattern) %else: - hasher.append(v.bitPattern) + hasher.combine(v.bitPattern) %end } + @inlinable // FIXME(sil-serialize-all) + public func _rawHashValue(seed: (UInt64, UInt64)) -> Int { + // To satisfy the axiom that equality implies hash equality, we need to + // finesse the hash value of -0.0 to match +0.0. + let v = isZero ? 0 : self + %if bits == 80: + var hasher = Hasher(_seed: seed) + hasher.combine(v._representation.signAndExponent) + hasher.combine(v.significandBitPattern) + return hasher._finalize() + %elif bits == 64: + return Hasher._hash(seed: seed, v.bitPattern) + %elif bits == 32: + return Hasher._hash(seed: seed, bytes: UInt64(v.bitPattern), count: 4) + %end + } } extension ${Self} { diff --git a/stdlib/public/core/GroupInfo.json b/stdlib/public/core/GroupInfo.json index a1da8d9e74ca2..9fead7b75047b 100644 --- a/stdlib/public/core/GroupInfo.json +++ b/stdlib/public/core/GroupInfo.json @@ -165,6 +165,7 @@ "Interval.swift", "Hashing.swift", "SipHash.swift", + "Hasher.swift", "ErrorType.swift", "InputStream.swift", "LifetimeManager.swift", @@ -175,6 +176,7 @@ "Policy.swift", "Print.swift", "REPL.swift", + "Random.swift", "Runtime.swift", "RuntimeFunctionCounters.swift", "Shims.swift", diff --git a/stdlib/public/core/Hashable.swift b/stdlib/public/core/Hashable.swift index 0be9f2dd2a5a5..2cb56ec7b116a 100644 --- a/stdlib/public/core/Hashable.swift +++ b/stdlib/public/core/Hashable.swift @@ -10,26 +10,27 @@ // //===----------------------------------------------------------------------===// -/// A type that provides an integer hash value. -/// -/// You can use any type that conforms to the `Hashable` protocol in a set or -/// as a dictionary key. Many types in the standard library conform to -/// `Hashable`: Strings, integers, floating-point and Boolean values, and even -/// sets provide a hash value by default. Your own custom types can be -/// hashable as well. When you define an enumeration without associated -/// values, it gains `Hashable` conformance automatically, and you can add -/// `Hashable` conformance to your other custom types by adding a single -/// `hashValue` property. -/// -/// A hash value, provided by a type's `hashValue` property, is an integer that -/// is the same for any two instances that compare equally. That is, for two -/// instances `a` and `b` of the same type, if `a == b`, then -/// `a.hashValue == b.hashValue`. The reverse is not true: Two instances with -/// equal hash values are not necessarily equal to each other. -/// -/// - Important: Hash values are not guaranteed to be equal across different -/// executions of your program. Do not save hash values to use in a future -/// execution. +/// A type that can be hashed into a `Hasher` to produce an integer hash value. +/// +/// You can use any type that conforms to the `Hashable` protocol in a set or as +/// a dictionary key. Many types in the standard library conform to `Hashable`: +/// Strings, integers, floating-point and Boolean values, and even sets are +/// hashable by default. Some other types, such as optionals, arrays and ranges +/// automatically become hashable when their type arguments implement the same. +/// +/// Your own custom types can be hashable as well. When you define an +/// enumeration without associated values, it gains `Hashable` conformance +/// automatically, and you can add `Hashable` conformance to your other custom +/// types by implementing the `hash(into:)` method. For structs whose stored +/// properties are all `Hashable`, and for enum types that have all-`Hashable` +/// associated values, the compiler is able to provide an implementation of +/// `hash(into:)` automatically. +/// +/// Hashing a value means feeding its essential components into a hash function, +/// represented by the `Hasher` type. Essential components are those that +/// contribute to the type's implementation of `Equatable`. Two instances that +/// are equal must feed the same values to `Hasher` in `hash(into:)`, in the +/// same order. /// /// Conforming to the Hashable Protocol /// =================================== @@ -39,9 +40,9 @@ /// from the `Equatable` protocol, so you must also satisfy that protocol's /// requirements. /// -/// A custom type's `Hashable` and `Equatable` requirements are automatically -/// synthesized by the compiler when you declare `Hashable` conformance in the -/// type's original declaration and your type meets these criteria: +/// The compiler automatically synthesizes your custom type's `Hashable` and +/// requirements when you declare `Hashable` conformance in the type's original +/// declaration and your type meets these criteria: /// /// - For a `struct`, all its stored properties must conform to `Hashable`. /// - For an `enum`, all its associated values must conform to `Hashable`. (An @@ -50,10 +51,14 @@ /// /// To customize your type's `Hashable` conformance, to adopt `Hashable` in a /// type that doesn't meet the criteria listed above, or to extend an existing -/// type to conform to `Hashable`, implement the `hashValue` property in your -/// custom type. To ensure that your type meets the semantic requirements of -/// the `Hashable` and `Equatable` protocols, it's a good idea to also -/// customize your type's `Equatable` conformance to match. +/// type to conform to `Hashable`, implement the `hash(into:)` method in your +/// custom type. +/// +/// In your `hash(into:)` implementation, call `combine(_:)` on the provided +/// `Hasher` instance with the essential components of your type. To ensure +/// that your type meets the semantic requirements of the `Hashable` and +/// `Equatable` protocols, it's a good idea to also customize your type's +/// `Equatable` conformance to match. /// /// As an example, consider a `GridPoint` type that describes a location in a /// grid of buttons. Here's the initial declaration of the `GridPoint` type: @@ -66,29 +71,23 @@ /// /// You'd like to create a set of the grid points where a user has already /// tapped. Because the `GridPoint` type is not hashable yet, it can't be used -/// as the `Element` type for a set. To add `Hashable` conformance, provide an -/// `==` operator function and a `hashValue` property. +/// in a set. To add `Hashable` conformance, provide an `==` operator function +/// and implement the `hash(into:)` method. /// /// extension GridPoint: Hashable { -/// var hashValue: Int { -/// return x.hashValue ^ y.hashValue &* 16777619 -/// } -/// /// static func == (lhs: GridPoint, rhs: GridPoint) -> Bool { /// return lhs.x == rhs.x && lhs.y == rhs.y /// } -/// } /// -/// The `hashValue` property in this example combines the hash value of a grid -/// point's `x` property with the hash value of its `y` property multiplied by -/// a prime constant. +/// func hash(into hasher: inout Hasher) { +/// hasher.combine(x) +/// hasher.combine(y) +/// } +/// } /// -/// - Note: The example above is a reasonably good hash function for a -/// simple type. If you're writing a hash function for a custom type, choose -/// a hashing algorithm that is appropriate for the kinds of data your type -/// comprises. Set and dictionary performance depends on hash values that -/// minimize collisions for their associated element and key types, -/// respectively. +/// The `hash(into:)` method in this example feeds the grid point's `x` and `y` +/// properties into the provided hasher. These properties are the same ones +/// used to test for equality in the `==` operator function. /// /// Now that `GridPoint` conforms to the `Hashable` protocol, you can create a /// set of previously tapped grid points. @@ -109,23 +108,46 @@ public protocol Hashable : Equatable { /// your program. Do not save hash values to use during a future execution. var hashValue: Int { get } - /// Feed bits to be hashed into the hash function represented by `hasher`. - func _hash(into hasher: inout _Hasher) + /// Hashes the essential components of this value by feeding them into the + /// given hasher. + /// + /// Implement this method to conform to the `Hashable` protocol. The + /// components used for hashing must be the same as the components compared + /// in your type's `==` operator implementation. Call `hasher.combine(_:)` + /// with each of these components. + /// + /// - Important: Never call `finalize()` on `hasher`. Doing so may become a + /// compile-time error in the future. + /// + /// - Parameter hasher: The hasher to use when combining the components + /// of this instance. + func hash(into hasher: inout Hasher) + + // Raw top-level hashing interface. Some standard library types (mostly + // primitives) specialize this to eliminate small resiliency overheads. (This + // only matters for tiny keys.) + // + // FIXME(hasher): Change to take a Hasher instead. To achieve the same + // performance, this requires Set and Dictionary to store their fully + // initialized local hashers, not just their seeds. + func _rawHashValue(seed: (UInt64, UInt64)) -> Int } extension Hashable { + @inlinable @inline(__always) - public func _hash(into hasher: inout _Hasher) { - hasher.append(self.hashValue) + public func _rawHashValue(seed: (UInt64, UInt64)) -> Int { + var hasher = Hasher(_seed: seed) + hasher.combine(self) + return hasher._finalize() } } // Called by synthesized `hashValue` implementations. +@inlinable @inline(__always) public func _hashValue(for value: H) -> Int { - var hasher = _Hasher() - hasher.append(value) - return hasher.finalize() + return value._rawHashValue(seed: Hasher._seed) } // Called by the SwiftValue implementation. diff --git a/stdlib/public/core/Hasher.swift b/stdlib/public/core/Hasher.swift new file mode 100644 index 0000000000000..877c3cfe8c82e --- /dev/null +++ b/stdlib/public/core/Hasher.swift @@ -0,0 +1,454 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Defines the Hasher struct, representing Swift's standard hash function. +// +//===----------------------------------------------------------------------===// + +import SwiftShims + +internal protocol _HasherCore { + init(seed: (UInt64, UInt64)) + mutating func compress(_ value: UInt64) + mutating func finalize(tailAndByteCount: UInt64) -> UInt64 + + // FIXME(hasher): Remove once one-shot hashing has a hasher parameter. + /// Generate a seed value from the current state of this hasher. + /// + /// Note that the returned value is not same as the seed that was used to + /// initialize the hasher. + /// + /// This comes handy when type's _hash(into:) implementation needs to perform + /// one-shot hashing for some of its components. (E.g., for commutative + /// hashing.) + func _generateSeed() -> (UInt64, UInt64) +} + +@inline(__always) +internal func _loadPartialUnalignedUInt64LE( + _ p: UnsafeRawPointer, + byteCount: Int +) -> UInt64 { + var result: UInt64 = 0 + switch byteCount { + case 7: + result |= UInt64(p.load(fromByteOffset: 6, as: UInt8.self)) &<< 48 + fallthrough + case 6: + result |= UInt64(p.load(fromByteOffset: 5, as: UInt8.self)) &<< 40 + fallthrough + case 5: + result |= UInt64(p.load(fromByteOffset: 4, as: UInt8.self)) &<< 32 + fallthrough + case 4: + result |= UInt64(p.load(fromByteOffset: 3, as: UInt8.self)) &<< 24 + fallthrough + case 3: + result |= UInt64(p.load(fromByteOffset: 2, as: UInt8.self)) &<< 16 + fallthrough + case 2: + result |= UInt64(p.load(fromByteOffset: 1, as: UInt8.self)) &<< 8 + fallthrough + case 1: + result |= UInt64(p.load(fromByteOffset: 0, as: UInt8.self)) + fallthrough + case 0: + return result + default: + _sanityCheckFailure() + } +} + +/// This is a buffer for segmenting arbitrary data into 8-byte chunks. Buffer +/// storage is represented by a single 64-bit value in the format used by the +/// finalization step of SipHash. (The least significant 56 bits hold the +/// trailing bytes, while the most significant 8 bits hold the count of bytes +/// appended so far, modulo 256. The count of bytes currently stored in the +/// buffer is in the lower three bits of the byte count.) +internal struct _HasherTailBuffer { + // msb lsb + // +---------+-------+-------+-------+-------+-------+-------+-------+ + // |byteCount| tail (<= 56 bits) | + // +---------+-------+-------+-------+-------+-------+-------+-------+ + internal var value: UInt64 + + @inline(__always) + internal init() { + self.value = 0 + } + + @inline(__always) + internal init(tail: UInt64, byteCount: UInt64) { + // byteCount can be any value, but we only keep the lower 8 bits. (The + // lower three bits specify the count of bytes stored in this buffer.) + _sanityCheck(tail & ~(1 << ((byteCount & 7) << 3) - 1) == 0) + self.value = (byteCount &<< 56 | tail) + } + + @inline(__always) + internal init(tail: UInt64, byteCount: Int) { + self.init(tail: tail, byteCount: UInt64(truncatingIfNeeded: byteCount)) + } + + internal var tail: UInt64 { + @inline(__always) + get { return value & ~(0xFF &<< 56) } + } + + internal var byteCount: UInt64 { + @inline(__always) + get { return value &>> 56 } + } + + @inline(__always) + internal mutating func append(_ bytes: UInt64) -> UInt64 { + let c = byteCount & 7 + if c == 0 { + value = value &+ (8 &<< 56) + return bytes + } + let shift = c &<< 3 + let chunk = tail | (bytes &<< shift) + value = (((value &>> 56) &+ 8) &<< 56) | (bytes &>> (64 - shift)) + return chunk + } + + @inline(__always) + internal + mutating func append(_ bytes: UInt64, count: UInt64) -> UInt64? { + _sanityCheck(count >= 0 && count < 8) + _sanityCheck(bytes & ~((1 &<< (count &<< 3)) &- 1) == 0) + let c = byteCount & 7 + let shift = c &<< 3 + if c + count < 8 { + value = (value | (bytes &<< shift)) &+ (count &<< 56) + return nil + } + let chunk = tail | (bytes &<< shift) + value = ((value &>> 56) &+ count) &<< 56 + if c + count > 8 { + value |= bytes &>> (64 - shift) + } + return chunk + } +} + +internal struct _BufferingHasher { + private var _buffer: _HasherTailBuffer + private var _core: Core + + @inline(__always) + internal init(seed: (UInt64, UInt64)) { + self._buffer = _HasherTailBuffer() + self._core = Core(seed: seed) + } + + @inline(__always) + internal mutating func combine(_ value: UInt) { +#if arch(i386) || arch(arm) + combine(UInt32(truncatingIfNeeded: value)) +#else + combine(UInt64(truncatingIfNeeded: value)) +#endif + } + + @inline(__always) + internal mutating func combine(_ value: UInt64) { + _core.compress(_buffer.append(value)) + } + + @inline(__always) + internal mutating func combine(_ value: UInt32) { + let value = UInt64(truncatingIfNeeded: value) + if let chunk = _buffer.append(value, count: 4) { + _core.compress(chunk) + } + } + + @inline(__always) + internal mutating func combine(_ value: UInt16) { + let value = UInt64(truncatingIfNeeded: value) + if let chunk = _buffer.append(value, count: 2) { + _core.compress(chunk) + } + } + + @inline(__always) + internal mutating func combine(_ value: UInt8) { + let value = UInt64(truncatingIfNeeded: value) + if let chunk = _buffer.append(value, count: 1) { + _core.compress(chunk) + } + } + + @inline(__always) + internal mutating func combine(bytes: UInt64, count: Int) { + _sanityCheck(count >= 0 && count < 8) + let count = UInt64(truncatingIfNeeded: count) + if let chunk = _buffer.append(bytes, count: count) { + _core.compress(chunk) + } + } + + @inline(__always) + internal mutating func combine(bytes: UnsafeRawBufferPointer) { + var remaining = bytes.count + guard remaining > 0 else { return } + var data = bytes.baseAddress! + + // Load first unaligned partial word of data + do { + let start = UInt(bitPattern: data) + let end = _roundUp(start, toAlignment: MemoryLayout.alignment) + let c = min(remaining, Int(end - start)) + if c > 0 { + let chunk = _loadPartialUnalignedUInt64LE(data, byteCount: c) + combine(bytes: chunk, count: c) + data += c + remaining -= c + } + } + _sanityCheck( + remaining == 0 || + Int(bitPattern: data) & (MemoryLayout.alignment - 1) == 0) + + // Load as many aligned words as there are in the input buffer + while remaining >= MemoryLayout.size { + combine(UInt64(littleEndian: data.load(as: UInt64.self))) + data += MemoryLayout.size + remaining -= MemoryLayout.size + } + + // Load last partial word of data + _sanityCheck(remaining >= 0 && remaining < 8) + if remaining > 0 { + let chunk = _loadPartialUnalignedUInt64LE(data, byteCount: remaining) + combine(bytes: chunk, count: remaining) + } + } + + // Generate a seed value from the current state of this hasher. + // FIXME(hasher): Remove + @inline(__always) + internal func _generateSeed() -> (UInt64, UInt64) { + return _core._generateSeed() + } + + @inline(__always) + internal mutating func finalize() -> UInt64 { + return _core.finalize(tailAndByteCount: _buffer.value) + } +} + +/// The universal hash function used by `Set` and `Dictionary`. +/// +/// `Hasher` can be used to map an arbitrary sequence of bytes to an integer +/// hash value. You can feed data to the hasher using a series of calls to +/// mutating `combine` methods. When you've finished feeding the hasher, the +/// hash value can be retrieved by calling `finalize()`: +/// +/// var hasher = Hasher() +/// hasher.combine(23) +/// hasher.combine("Hello") +/// let hashValue = hasher.finalize() +/// +/// Within the execution of a Swift program, `Hasher` guarantees that finalizing +/// it will always produce the same hash value as long as it is fed the exact +/// same sequence of bytes. However, the underlying hash algorithm is designed +/// to exhibit avalanche effects: slight changes to the seed or the input byte +/// sequence will typically produce drastic changes in the generated hash value. +/// +/// - Note: Do not save or otherwise reuse hash values across executions of your +/// program. `Hasher` is usually randomly seeded, which means it will return +/// different values on every new execution of your program. The hash +/// algorithm implemented by `Hasher` may itself change between any two +/// versions of the standard library. +@_fixed_layout // FIXME: Should be resilient (rdar://problem/38549901) +public struct Hasher { + internal typealias RawCore = _SipHash13Core + internal typealias Core = _BufferingHasher + + internal var _core: Core + + /// Initialize a new hasher. The hasher uses a per-execution seed value that + /// is set during process startup, usually from a high-quality random source. + @effects(releasenone) + public init() { + self._core = Core(seed: Hasher._seed) + } + + /// Initialize a new hasher using the specified seed value. + @usableFromInline + @effects(releasenone) + internal init(_seed seed: (UInt64, UInt64)) { + self._core = Core(seed: seed) + } + + /// Indicates whether we're running in an environment where hashing needs to + /// be deterministic. If this is true, the hash seed is not random, and hash + /// tables do not apply per-instance perturbation that is not repeatable. + /// This is not recommended for production use, but it is useful in certain + /// test environments where randomization may lead to unwanted nondeterminism + /// of test results. + @inlinable + internal static var _isDeterministic: Bool { + @inline(__always) + get { + return _swift_stdlib_Hashing_parameters.deterministic + } + } + + /// The 128-bit hash seed used to initialize the hasher state. Initialized + /// once during process startup. + @inlinable + internal static var _seed: (UInt64, UInt64) { + @inline(__always) + get { + // The seed itself is defined in C++ code so that it is initialized during + // static construction. Almost every Swift program uses hash tables, so + // initializing the seed during the startup seems to be the right + // trade-off. + return ( + _swift_stdlib_Hashing_parameters.seed0, + _swift_stdlib_Hashing_parameters.seed1) + } + } + + /// Feed `value` to this hasher, mixing its essential parts into + /// the hasher state. + @inlinable + @inline(__always) + public mutating func combine(_ value: H) { + value.hash(into: &self) + } + + @effects(releasenone) + @usableFromInline + internal mutating func _combine(_ value: UInt) { + _core.combine(value) + } + + @effects(releasenone) + @usableFromInline + internal mutating func _combine(_ value: UInt64) { + _core.combine(value) + } + + @effects(releasenone) + @usableFromInline + internal mutating func _combine(_ value: UInt32) { + _core.combine(value) + } + + @effects(releasenone) + @usableFromInline + internal mutating func _combine(_ value: UInt16) { + _core.combine(value) + } + + @effects(releasenone) + @usableFromInline + internal mutating func _combine(_ value: UInt8) { + _core.combine(value) + } + + @effects(releasenone) + @usableFromInline + internal mutating func _combine(bytes value: UInt64, count: Int) { + _core.combine(bytes: value, count: count) + } + + /// Feed the contents of `buffer` into this hasher, mixing it into the hasher + /// state. + @effects(releasenone) + public mutating func combine(bytes: UnsafeRawBufferPointer) { + _core.combine(bytes: bytes) + } + + /// Finalize the hasher state and return the hash value. + /// Finalizing invalidates the hasher; additional bits cannot be combined + /// into it, and it cannot be finalized again. + @effects(releasenone) + @usableFromInline + internal mutating func _finalize() -> Int { + return Int(truncatingIfNeeded: _core.finalize()) + } + + /// Finalize the hasher state and return the hash value. + /// + /// Finalizing consumes the hasher: it is illegal to finalize a hasher you + /// don't own, or to perform operations on a finalized hasher. (These may + /// become compile-time errors in the future.) + /// + /// Hash values are not guaranteed to be equal across different executions of + /// your program. Do not save hash values to use during a future execution. + @effects(releasenone) + public __consuming func finalize() -> Int { + var core = _core + return Int(truncatingIfNeeded: core.finalize()) + } + + // Generate a seed value from the current state of this hasher. + // FIXME(hasher): Remove + @effects(readnone) + @usableFromInline + internal func _generateSeed() -> (UInt64, UInt64) { + return _core._generateSeed() + } + + @effects(readnone) + @usableFromInline + internal static func _hash(seed: (UInt64, UInt64), _ value: UInt64) -> Int { + var core = RawCore(seed: seed) + core.compress(value) + let tbc = _HasherTailBuffer(tail: 0, byteCount: 8) + return Int(truncatingIfNeeded: core.finalize(tailAndByteCount: tbc.value)) + } + + @effects(readnone) + @usableFromInline + internal static func _hash(seed: (UInt64, UInt64), _ value: UInt) -> Int { + var core = RawCore(seed: seed) +#if arch(i386) || arch(arm) + _sanityCheck(UInt.bitWidth < UInt64.bitWidth) + let tbc = _HasherTailBuffer( + tail: UInt64(truncatingIfNeeded: value), + byteCount: UInt.bitWidth &>> 3) +#else + _sanityCheck(UInt.bitWidth == UInt64.bitWidth) + core.compress(UInt64(truncatingIfNeeded: value)) + let tbc = _HasherTailBuffer(tail: 0, byteCount: 8) +#endif + return Int(truncatingIfNeeded: core.finalize(tailAndByteCount: tbc.value)) + } + + @effects(readnone) + @usableFromInline + internal static func _hash( + seed: (UInt64, UInt64), + bytes value: UInt64, + count: Int) -> Int { + var core = RawCore(seed: seed) + let tbc = _HasherTailBuffer(tail: value, byteCount: count) + return Int(truncatingIfNeeded: core.finalize(tailAndByteCount: tbc.value)) + } + + @effects(readnone) + @usableFromInline + internal static func _hash( + seed: (UInt64, UInt64), + bytes: UnsafeRawBufferPointer) -> Int { + var core = Core(seed: seed) + core.combine(bytes: bytes) + return Int(truncatingIfNeeded: core.finalize()) + } +} diff --git a/stdlib/public/core/Hashing.swift b/stdlib/public/core/Hashing.swift index 970eca807214d..1e2cf7d42fd61 100644 --- a/stdlib/public/core/Hashing.swift +++ b/stdlib/public/core/Hashing.swift @@ -11,289 +11,8 @@ //===----------------------------------------------------------------------===// // -// This file implements helpers for constructing non-cryptographic hash -// functions. +// This file implements helpers for hashing collections. // -// This code was ported from LLVM's ADT/Hashing.h. -// -// Currently the algorithm is based on CityHash, but this is an implementation -// detail. Even more, there are facilities to mix in a per-execution seed to -// ensure that hash values differ between executions. -// - -import SwiftShims - -@_frozen // FIXME(sil-serialize-all) -public // @testable -enum _HashingDetail { - - // FIXME(hasher): Remove - @inlinable // FIXME(sil-serialize-all) - @usableFromInline - @_transparent - internal static func getExecutionSeed() -> UInt64 { - // FIXME: This needs to be a per-execution seed. This is just a placeholder - // implementation. - return 0xff51afd7ed558ccd - } - - // FIXME(hasher): Remove - @inlinable // FIXME(sil-serialize-all) - @usableFromInline - @_transparent - internal static func hash16Bytes(_ low: UInt64, _ high: UInt64) -> UInt64 { - // Murmur-inspired hashing. - let mul: UInt64 = 0x9ddfea08eb382d69 - var a: UInt64 = (low ^ high) &* mul - a ^= (a >> 47) - var b: UInt64 = (high ^ a) &* mul - b ^= (b >> 47) - b = b &* mul - return b - } -} - -// -// API functions. -// - -// -// _mix*() functions all have type (T) -> T. These functions don't compress -// their inputs and just exhibit avalanche effect. -// - -// FIXME(hasher): Remove -@inlinable // FIXME(sil-serialize-all) -@_transparent -public // @testable -func _mixUInt32(_ value: UInt32) -> UInt32 { - // Zero-extend to 64 bits, hash, select 32 bits from the hash. - // - // NOTE: this differs from LLVM's implementation, which selects the lower - // 32 bits. According to the statistical tests, the 3 lowest bits have - // weaker avalanche properties. - let extendedValue = UInt64(value) - let extendedResult = _mixUInt64(extendedValue) - return UInt32((extendedResult >> 3) & 0xffff_ffff) -} - -// FIXME(hasher): Remove -@inlinable // FIXME(sil-serialize-all) -@_transparent -public // @testable -func _mixInt32(_ value: Int32) -> Int32 { - return Int32(bitPattern: _mixUInt32(UInt32(bitPattern: value))) -} - -// FIXME(hasher): Remove -@inlinable // FIXME(sil-serialize-all) -@_transparent -public // @testable -func _mixUInt64(_ value: UInt64) -> UInt64 { - // Similar to hash_4to8_bytes but using a seed instead of length. - let seed: UInt64 = _HashingDetail.getExecutionSeed() - let low: UInt64 = value & 0xffff_ffff - let high: UInt64 = value >> 32 - return _HashingDetail.hash16Bytes(seed &+ (low << 3), high) -} - -// FIXME(hasher): Remove -@inlinable // FIXME(sil-serialize-all) -@_transparent -public // @testable -func _mixInt64(_ value: Int64) -> Int64 { - return Int64(bitPattern: _mixUInt64(UInt64(bitPattern: value))) -} - -// FIXME(hasher): Remove -@inlinable // FIXME(sil-serialize-all) -@_transparent -public // @testable -func _mixUInt(_ value: UInt) -> UInt { -#if arch(i386) || arch(arm) - return UInt(_mixUInt32(UInt32(value))) -#elseif arch(x86_64) || arch(arm64) || arch(powerpc64) || arch(powerpc64le) || arch(s390x) - return UInt(_mixUInt64(UInt64(value))) -#endif -} - -// FIXME(hasher): Remove -@inlinable // FIXME(sil-serialize-all) -@_transparent -public // @testable -func _mixInt(_ value: Int) -> Int { -#if arch(i386) || arch(arm) - return Int(_mixInt32(Int32(value))) -#elseif arch(x86_64) || arch(arm64) || arch(powerpc64) || arch(powerpc64le) || arch(s390x) - return Int(_mixInt64(Int64(value))) -#endif -} - -/// Returns a new value that combines the two given hash values. -/// -/// Combining is performed using [a hash function][ref] described by T.C. Hoad -/// and J. Zobel, which is also adopted in the Boost C++ libraries. -/// -/// This function is used by synthesized implementations of `hashValue` to -/// combine the hash values of individual `struct` fields and associated values -/// of `enum`s. It is factored out into a standard library function so that the -/// specific hashing logic can be refined without requiring major changes to the -/// code that creates the synthesized AST nodes. -/// -/// [ref]: https://pdfs.semanticscholar.org/03bf/7be88e88ba047c6ab28036d0f28510299226.pdf -@_transparent -public // @testable -func _combineHashValues(_ firstValue: Int, _ secondValue: Int) -> Int { - // Use a magic number based on the golden ratio - // (0x1.9e3779b97f4a7c15f39cc0605cedc8341082276bf3a27251f86c6a11d0c18e95p0). -#if arch(i386) || arch(arm) - let magic = 0x9e3779b9 as UInt -#elseif arch(x86_64) || arch(arm64) || arch(powerpc64) || arch(powerpc64le) || arch(s390x) - let magic = 0x9e3779b97f4a7c15 as UInt -#endif - var x = UInt(bitPattern: firstValue) - x ^= UInt(bitPattern: secondValue) &+ magic &+ (x &<< 6) &+ (x &>> 2) - return Int(bitPattern: x) -} - -// FIXME(hasher): This hasher emulates Swift 4.1 hashValues. It is purely for -// benchmarking; to be removed. -internal struct _LegacyHasher { - internal var _hash: Int - - @inline(__always) - internal init(key: (UInt64, UInt64) = (0, 0)) { // key is ignored - _hash = 0 - } - - @inline(__always) - internal mutating func append(_ value: Int) { - _hash = (_hash == 0 ? value : _combineHashValues(_hash, value)) - } - - @inline(__always) - internal mutating func append(_ value: UInt) { - append(Int(bitPattern: value)) - } - - @inline(__always) - internal mutating func append(_ value: UInt32) { - append(Int(truncatingIfNeeded: value)) - } - - @inline(__always) - internal mutating func append(_ value: UInt64) { - if UInt64.bitWidth > Int.bitWidth { - append(Int(truncatingIfNeeded: value ^ (value &>> 32))) - } else { - append(Int(truncatingIfNeeded: value)) - } - } - - @inline(__always) - internal mutating func finalize() -> UInt64 { - return UInt64( - _truncatingBits: UInt(bitPattern: _mixInt(_hash))._lowWord) - } -} - - -// NOT @_fixed_layout -@_fixed_layout // FIXME - remove - radar 38549901 -public struct _Hasher { - internal typealias Core = _SipHash13 - - // NOT @usableFromInline - internal var _core: Core - - // NOT @inlinable - @effects(releasenone) - public init() { - self._core = Core(key: _Hasher._seed) - } - - // NOT @inlinable - @effects(releasenone) - public init(seed: (UInt64, UInt64)) { - self._core = Core(key: seed) - } - - /// Indicates whether we're running in an environment where hashing needs to - /// be deterministic. If this is true, the hash seed is not random, and hash - /// tables do not apply per-instance perturbation that is not repeatable. - /// This is not recommended for production use, but it is useful in certain - /// test environments where randomization may lead to unwanted nondeterminism - /// of test results. - public // SPI - static var _isDeterministic: Bool { - @inlinable - @inline(__always) - get { - return _swift_stdlib_Hashing_parameters.deterministic; - } - } - - /// The 128-bit hash seed used to initialize the hasher state. Initialized - /// once during process startup. - public // SPI - static var _seed: (UInt64, UInt64) { - @inlinable - @inline(__always) - get { - // The seed itself is defined in C++ code so that it is initialized during - // static construction. Almost every Swift program uses hash tables, so - // initializing the seed during the startup seems to be the right - // trade-off. - return ( - _swift_stdlib_Hashing_parameters.seed0, - _swift_stdlib_Hashing_parameters.seed1) - } - } - - @inlinable - @inline(__always) - public mutating func append(_ value: H) { - value._hash(into: &self) - } - - // NOT @inlinable - @effects(releasenone) - public mutating func append(bits: UInt) { - _core.append(bits) - } - // NOT @inlinable - @effects(releasenone) - public mutating func append(bits: UInt32) { - _core.append(bits) - } - // NOT @inlinable - @effects(releasenone) - public mutating func append(bits: UInt64) { - _core.append(bits) - } - - // NOT @inlinable - @effects(releasenone) - public mutating func append(bits: Int) { - _core.append(UInt(bitPattern: bits)) - } - // NOT @inlinable - @effects(releasenone) - public mutating func append(bits: Int32) { - _core.append(UInt32(bitPattern: bits)) - } - // NOT @inlinable - @effects(releasenone) - public mutating func append(bits: Int64) { - _core.append(UInt64(bitPattern: bits)) - } - - // NOT @inlinable - @effects(releasenone) - public mutating func finalize() -> Int { - return Int(truncatingIfNeeded: _core.finalize()) - } -} /// This protocol is only used for compile-time checks that /// every buffer type implements all required operations. @@ -343,7 +62,6 @@ internal protocol _HashBuffer { /// can be used in multiple places in the implementation and stay consistent. /// Should not be used outside `Dictionary` implementation. @inlinable // FIXME(sil-serialize-all) -@usableFromInline // FIXME(sil-serialize-all) @_transparent internal var _hashContainerDefaultMaxLoadFactorInverse: Double { return 1.0 / 0.75 @@ -355,7 +73,6 @@ internal var _hashContainerDefaultMaxLoadFactorInverse: Double { /// This function is part of the runtime because `Bool` type is bridged to /// `ObjCBool`, which is in Foundation overlay. /// FIXME(sil-serialize-all): this should be internal -@inlinable // FIXME(sil-serialize-all) @usableFromInline // FIXME(sil-serialize-all) @_silgen_name("swift_stdlib_NSObject_isEqual") internal func _stdlib_NSObject_isEqual(_ lhs: AnyObject, _ rhs: AnyObject) -> Bool @@ -375,20 +92,17 @@ internal struct _UnmanagedAnyObjectArray { internal var value: UnsafeMutableRawPointer @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_ up: UnsafeMutablePointer) { self.value = UnsafeMutableRawPointer(up) } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init?(_ up: UnsafeMutablePointer?) { guard let unwrapped = up else { return nil } self.init(unwrapped) } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal subscript(i: Int) -> AnyObject { get { let unmanaged = value.load( diff --git a/stdlib/public/core/HeapBuffer.swift b/stdlib/public/core/HeapBuffer.swift index 30fc5b2dbc0f9..ecce7ec6602ae 100644 --- a/stdlib/public/core/HeapBuffer.swift +++ b/stdlib/public/core/HeapBuffer.swift @@ -25,7 +25,6 @@ internal protocol _HeapBufferHeader_ { internal struct _HeapBufferHeader : _HeapBufferHeader_ { internal typealias Value = T @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_ value: T) { self.value = value } @usableFromInline // FIXME(sil-serialize-all) internal var value: T @@ -41,7 +40,6 @@ extension ManagedBufferPointer where Header : _HeapBufferHeader_ { internal typealias Value = Header.Value @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal init( _ storageClass: AnyClass, _ initializer: Value, _ capacity: Int @@ -55,7 +53,6 @@ extension ManagedBufferPointer where Header : _HeapBufferHeader_ { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal var value: Value { @inline(__always) get { @@ -68,7 +65,6 @@ extension ManagedBufferPointer where Header : _HeapBufferHeader_ { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal subscript(i: Int) -> Element { @inline(__always) get { @@ -77,7 +73,6 @@ extension ManagedBufferPointer where Header : _HeapBufferHeader_ { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal var baseAddress: UnsafeMutablePointer { @inline(__always) get { @@ -86,7 +81,6 @@ extension ManagedBufferPointer where Header : _HeapBufferHeader_ { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal var storage: AnyObject? { @inline(__always) get { diff --git a/stdlib/public/core/ICU.swift b/stdlib/public/core/ICU.swift index c94b8254e942b..1cfcc761f0e5a 100644 --- a/stdlib/public/core/ICU.swift +++ b/stdlib/public/core/ICU.swift @@ -13,17 +13,14 @@ import SwiftShims extension __swift_stdlib_UErrorCode { @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var isFailure: Bool { return rawValue > __swift_stdlib_U_ZERO_ERROR.rawValue } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var isWarning: Bool { return rawValue < __swift_stdlib_U_ZERO_ERROR.rawValue } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var isSuccess: Bool { return rawValue <= __swift_stdlib_U_ZERO_ERROR.rawValue } diff --git a/stdlib/public/core/Indices.swift b/stdlib/public/core/Indices.swift index 6a8c40af4185a..018c19e2d9aaf 100644 --- a/stdlib/public/core/Indices.swift +++ b/stdlib/public/core/Indices.swift @@ -21,7 +21,6 @@ public struct DefaultIndices { internal var _endIndex: Elements.Index @inlinable - @usableFromInline internal init( _elements: Elements, startIndex: Elements.Index, diff --git a/stdlib/public/core/InputStream.swift b/stdlib/public/core/InputStream.swift index 445cd39695a07..6c145c8090f57 100644 --- a/stdlib/public/core/InputStream.swift +++ b/stdlib/public/core/InputStream.swift @@ -26,7 +26,6 @@ import SwiftShims /// or character combinations are preserved. The default is `true`. /// - Returns: The string of characters read from standard input. If EOF has /// already been reached when `readLine()` is called, the result is `nil`. -@inlinable // FIXME(sil-serialize-all) public func readLine(strippingNewline: Bool = true) -> String? { var linePtrVar: UnsafeMutablePointer? var readBytes = swift_stdlib_readLine_stdin(&linePtrVar) @@ -66,7 +65,7 @@ public func readLine(strippingNewline: Bool = true) -> String? { } } let result = String._fromUTF8CodeUnitSequence( - UnsafeMutableBufferPointer(start: linePtr, count: readBytes), + UnsafeBufferPointer(start: linePtr, count: readBytes), repair: true)! _stdlib_free(linePtr) return result diff --git a/stdlib/public/core/IntegerParsing.swift b/stdlib/public/core/IntegerParsing.swift index c9f8fdaa3c563..0ed863a2b7445 100644 --- a/stdlib/public/core/IntegerParsing.swift +++ b/stdlib/public/core/IntegerParsing.swift @@ -11,7 +11,6 @@ //===----------------------------------------------------------------------===// @inlinable // FIXME(sil-serialize-all) -@usableFromInline // FIXME(sil-serialize-all) @inline(__always) internal func _asciiDigit( codeUnit u_: CodeUnit, radix: Result @@ -31,7 +30,6 @@ internal func _asciiDigit( } @inlinable // FIXME(sil-serialize-all) -@usableFromInline // FIXME(sil-serialize-all) @inline(__always) internal func _parseUnsignedASCII< Rest : IteratorProtocol, Result: FixedWidthInteger @@ -68,7 +66,6 @@ where Rest.Element : UnsignedInteger { // @inlinable // FIXME(sil-serialize-all) -@usableFromInline // FIXME(sil-serialize-all) @inline(__always) internal func _parseASCII< CodeUnits : IteratorProtocol, Result: FixedWidthInteger @@ -98,10 +95,9 @@ extension FixedWidthInteger { // _parseASCII function thunk that prevents inlining used as an implementation // detail for FixedWidthInteger.init(_: radix:) on the slow path to save code // size. - @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @_semantics("optimize.sil.specialize.generic.partial.never") @inline(never) + @usableFromInline internal static func _parseASCIISlowPath< CodeUnits : IteratorProtocol, Result: FixedWidthInteger >( diff --git a/stdlib/public/core/Integers.swift.gyb b/stdlib/public/core/Integers.swift.gyb index 755e8c3bee41d..89916f0fad020 100644 --- a/stdlib/public/core/Integers.swift.gyb +++ b/stdlib/public/core/Integers.swift.gyb @@ -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 - 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 @@ -301,6 +301,7 @@ def operatorComment(operator, fixedWidth): /// let x: UInt8 = 5 // 0b00000101 /// let y: UInt8 = 14 // 0b00001110 /// let z = x & y // 0b00000100 + /// // z == 4 /// /// - Parameters: /// - lhs: An integer value. @@ -317,6 +318,7 @@ def operatorComment(operator, fixedWidth): /// let x: UInt8 = 5 // 0b00000101 /// let y: UInt8 = 14 // 0b00001110 /// let z = x | y // 0b00001111 + /// // z == 15 /// /// - Parameters: /// - lhs: An integer value. @@ -333,6 +335,7 @@ def operatorComment(operator, fixedWidth): /// let x: UInt8 = 5 // 0b00000101 /// let y: UInt8 = 14 // 0b00001110 /// let z = x ^ y // 0b00001011 + /// // z == 11 /// /// - Parameters: /// - lhs: An integer value. @@ -343,9 +346,10 @@ def operatorComment(operator, fixedWidth): /// specified number of digits to the right, masking the shift amount to the /// type's bit width. /// - /// The masking right shift operator (`&>>`) performs a *masking shift*, - /// where the value passed as `rhs` is masked to produce a value in the - /// range `0..>`) when you need to perform a + /// shift and are sure that the shift amount is in the range + /// `0..> 2 /// // y == 7 // 0b00000111 /// - /// However, if you use `19` as `rhs`, the operation first bitmasks `rhs` to - /// `3`, and then uses that masked value as the number of bits to shift `lhs`. + /// However, if you use `8` as the shift amount, the method first masks the + /// shift amount to zero, and then performs the shift, resulting in no change + /// to the original value. /// - /// let z = x &>> 19 - /// // z == 3 // 0b00000011 + /// let z = x &>> 8 + /// // z == 30 // 0b00011110 + /// + /// If the bit width of the shifted integer type is a power of two, masking + /// is performed using a bitmask; otherwise, masking is performed using a + /// modulo operation. /// /// - Parameters: /// - lhs: The value to shift. @@ -373,9 +382,11 @@ def operatorComment(operator, fixedWidth): /// specified number of digits to the left, masking the shift amount to the /// type's bit width. /// - /// The masking left shift operator (`&<<`) performs a *masking shift*, where - /// the value used as `rhs` is masked to produce a value in the range - /// `0.. String { _precondition(2...36 ~= radix, "Radix must be between 2 and 36") @@ -1792,6 +1827,10 @@ extension BinaryInteger { let isNegative = Self.isSigned && self < (0 as Self) var value = magnitude + + // TODO(FIXME JIRA): All current stdlib types fit in small. Use a stack + // buffer instead of an array on the heap. + var result: [UInt8] = [] while value != 0 { let (quotient, remainder) = _quotientAndRemainder(value) @@ -1802,7 +1841,11 @@ extension BinaryInteger { if isNegative { result.append(UInt8(("-" as Unicode.Scalar).value)) } - return String._fromASCII(result.reversed()) + + result.reverse() + return result.withUnsafeBufferPointer { + return String._fromASCII($0) + } } /// A textual representation of this value. @@ -2257,23 +2300,23 @@ ${overflowOperationComment(x.operator)} /// otherwise overflow. Unlike traditional truncating multiplication, the /// `multipliedFullWidth(by:)` method returns a tuple containing both the /// `high` and `low` parts of the product of this value and `other`. The - /// following example uses this method to multiply two `UInt8` values that + /// following example uses this method to multiply two `Int8` values that /// normally overflow when multiplied: /// - /// let x: UInt8 = 100 - /// let y: UInt8 = 20 + /// let x: Int8 = 48 + /// let y: Int8 = -40 /// let result = x.multipliedFullWidth(by: y) - /// // result.high == 0b00000111 - /// // result.low == 0b11010000 + /// // result.high == -8 + /// // result.low == 128 /// - /// The product of `x` and `y` is 2000, which is too large to represent in a - /// `UInt8` instance. The `high` and `low` properties of the `result` value - /// represent 2000 when concatenated to form a double-width integer; that + /// The product of `x` and `y` is `-1920`, which is too large to represent in + /// an `Int8` instance. The `high` and `low` compnents of the `result` value + /// represent `-1920` when concatenated to form a double-width integer; that /// is, using `result.high` as the high byte and `result.low` as the low byte - /// of a `UInt16` instance. + /// of an `Int16` instance. /// - /// let z = UInt16(result.high) << 8 | UInt16(result.low) - /// // z == 2000 + /// let z = Int16(result.high) << 8 | Int16(result.low) + /// // z == -1920 /// /// - Parameter other: The value to multiply this value by. /// - Returns: A tuple containing the high and low parts of the result of @@ -2287,6 +2330,18 @@ ${overflowOperationComment(x.operator)} /// type. If the quotient is too large to represent in the type, a runtime /// error may occur. /// + /// The following example divides a value that is too large to be represented + /// using a single `Int` instance by another `Int` value. Because the quotient + /// is representable as an `Int`, the division succeeds. + /// + /// // 'dividend' represents the value 0x506f70652053616e74612049494949 + /// let dividend = (22640526660490081, 7959093232766896457 as UInt) + /// let divisor = 2241543570477705381 + /// + /// let (quotient, remainder) = divisor.dividingFullWidth(dividend) + /// // quotient == 186319822866995413 + /// // remainder == 0 + /// /// - Parameter dividend: A tuple containing the high and low parts of a /// double-width integer. /// - Returns: A tuple containing the quotient and remainder obtained by @@ -2355,6 +2410,9 @@ ${assignmentOperatorComment(x.operator, False)} static func ${x.operator}=(_ lhs: inout Self, _ rhs: Self) % end + static func _random( + using generator: inout R + ) -> Self } extension FixedWidthInteger { @@ -2483,6 +2541,159 @@ ${assignmentOperatorComment(x.operator, False)} % end } +% for Range in ['Range', 'ClosedRange']: +% exampleRange = '1..<100' if Range == 'Range' else '1...100' + +extension ${Range} + where Bound: FixedWidthInteger, Bound.Stride : SignedInteger, + Bound.Magnitude: UnsignedInteger +{ + + /// Returns a random element of the range, using the given generator as + /// a source for randomness. + /// + /// You can use this method to select a random element of a range when you + /// are using a custom random number generator. If you're generating a random + /// number, in most cases, you should prefer using the `random(in:using:)` + /// static method of the desired numeric type. That static method is available + /// for both integer and floating point types, and returns a non-optional + /// value. + /// + /// - Parameter generator: The random number generator to use when choosing + /// a random element. + /// - Returns: A random element of the range. +% if 'Closed' not in Range: + /// If the range is empty, the method returns `nil`. +% else: + /// This method never returns `nil`. +% end + @inlinable + public func randomElement( + using generator: inout T + ) -> Element? { + guard !isEmpty else { + return nil + } + // Compute delta, the distance between the lower and upper bounds. This + // value may not representable by the type Bound if Bound is signed, but + // is always representable as Bound.Magnitude. + var delta = Bound.Magnitude(truncatingIfNeeded: upperBound &- lowerBound) +% if 'Closed' in Range: + // Subtle edge case: if the range is the whole set of representable values, + // then adding one to delta to account for a closed range will overflow. + // If we used &+ instead, the result would be zero, which isn't helpful, + // so we actually need to handle this case separately. + if delta == Bound.Magnitude.max { + return Bound(truncatingIfNeeded: generator.next() as Bound.Magnitude) + } + // Need to widen delta to account for the right-endpoint of a closed range. + delta += 1 +% end + // The mathematical result we want is lowerBound plus a random value in + // 0 ..< delta. We need to be slightly careful about how we do this + // arithmetic; the Bound type cannot generally represent the random value, + // so we use a wrapping addition on Bound.Magnitude. This will often + // overflow, but produces the correct bit pattern for the result when + // converted back to Bound. + return Bound(truncatingIfNeeded: + Bound.Magnitude(truncatingIfNeeded: lowerBound) &+ + generator.next(upperBound: delta) + ) + } + + /// Returns a random element of the range, using the given generator as + /// a source for randomness. + /// + /// You can use this method to select a random element of a range when you + /// are using a custom random number generator. If you're generating a random + /// number, in most cases, you should prefer using the `random(in:)` + /// static method of the desired numeric type. That static method is available + /// for both integer and floating point types, and returns a non-optional + /// value. + /// + /// This method uses the default random generator, `Random.default`. Calling + /// `(${exampleRange}).randomElement()` is equivalent to calling + /// `(${exampleRange}).randomElement(using: &Random.default)`. + /// + /// - Returns: A random element of the range. +% if 'Closed' not in Range: + /// If the range is empty, the method returns `nil`. +% else: + /// This method never returns `nil`. +% end + @inlinable + public func randomElement() -> Element? { + return randomElement(using: &Random.default) + } +} + +extension FixedWidthInteger +where Self.Stride : SignedInteger, + Self.Magnitude : UnsignedInteger { + + /// Returns a random value within the specified range, using the given + /// generator as a source for randomness. + /// + /// Use this method to generate an integer within a specific range when you + /// are using a custom random number generator. This example creates three + /// new values in the range `${exampleRange}`. + /// + /// for _ in 1...3 { + /// print(Int.random(in: ${exampleRange}, using: &myGenerator)) + /// } + /// // Prints "7" + /// // Prints "44" + /// // Prints "21" + /// + /// - Parameters: + /// - range: The range in which to create a random value. +% if Range == 'Range': + /// `range` must not be empty. +% end + /// - generator: The random number generator to use when creating the + /// new random value. + /// - Returns: A random value within the bounds of `range`. + @inlinable + public static func random( + in range: ${Range}, + using generator: inout T + ) -> Self { + _precondition( + !range.isEmpty, + "Can't get random value with an empty range" + ) + return range.randomElement(using: &generator)! + } + + /// Returns a random value within the specified range. + /// + /// Use this method to generate an integer within a specific range. This + /// example creates three new values in the range `${exampleRange}`. + /// + /// for _ in 1...3 { + /// print(Int.random(in: ${exampleRange})) + /// } + /// // Prints "53" + /// // Prints "64" + /// // Prints "5" + /// + /// This method uses the default random generator, `Random.default`. The call + /// to `Int.random(in: ${exampleRange})` above is equivalent to calling + /// `Int.random(in: ${exampleRange}, using: &Random.default)`. + /// + /// - Parameter range: The range in which to create a random value. +% if Range == 'Range': + /// `range` must not be empty. +% end + /// - Returns: A random value within the bounds of `range`. + @inlinable + public static func random(in range: ${Range}) -> Self { + return Self.random(in: range, using: &Random.default) + } +} + +% end + //===----------------------------------------------------------------------===// //===--- Operators on FixedWidthInteger -----------------------------------===// //===----------------------------------------------------------------------===// @@ -2813,6 +3024,26 @@ ${assignmentOperatorComment('&' + x.operator, True)} % end } +extension FixedWidthInteger { + public static func _random( + using generator: inout R + ) -> Self { + if bitWidth <= UInt64.bitWidth { + return Self(truncatingIfNeeded: generator.next() as UInt64) + } + + let (quotient, remainder) = bitWidth.quotientAndRemainder( + dividingBy: UInt64.bitWidth + ) + var tmp: Self = 0 + for i in 0 ..< quotient + remainder.signum() { + let next: UInt64 = generator.next() + tmp += Self(truncatingIfNeeded: next) &<< (UInt64.bitWidth * i) + } + return tmp + } +} + //===----------------------------------------------------------------------===// //===--- UnsignedInteger --------------------------------------------------===// //===----------------------------------------------------------------------===// @@ -2873,7 +3104,7 @@ extension UnsignedInteger where Self : FixedWidthInteger { // This check is potentially removable by the optimizer if source.bitWidth >= Self.bitWidth { _precondition(source <= Self.max, - "Not enough bits to represent a signed value") + "Not enough bits to represent the passed value") } self.init(truncatingIfNeeded: source) } @@ -2983,7 +3214,7 @@ extension SignedInteger where Self : FixedWidthInteger { if (source.bitWidth > Self.bitWidth) || (source.bitWidth == Self.bitWidth && !T.isSigned) { _precondition(source <= Self.max, - "Not enough bits to represent a signed value") + "Not enough bits to represent the passed value") } self.init(truncatingIfNeeded: source) } @@ -3647,34 +3878,42 @@ ${assignmentOperatorComment(x.operator, True)} %# end of concrete type: ${Self} extension ${Self} : Hashable { - /// The integer's hash value. + /// Hashes the essential components of this value by feeding them into the + /// given hasher. /// - /// The hash value is not guaranteed to be stable across different - /// invocations of the same program. Do not persist the hash value across - /// program runs. + /// - Parameter hasher: The hasher to use when combining the components + /// of this instance. @inlinable // FIXME(sil-serialize-all) - public var hashValue: Int { - @inline(__always) - get { - return _hashValue(for: self) - } - } - - @inlinable // FIXME(sil-serialize-all) - public func _hash(into hasher: inout _Hasher) { + public func hash(into hasher: inout Hasher) { // FIXME(hasher): To correctly bridge `Set`s/`Dictionary`s containing // `AnyHashable`-boxed integers, all integer values are currently required // to hash exactly the same way as the corresponding (U)Int64 value. To fix // this, we should introduce a custom AnyHashable box for integer values // that sign-extends values to 64 bits. % if bits <= word_bits: - hasher.append(bits: _lowWord) + hasher._combine(_lowWord) % elif bits == 2 * word_bits: if let word = ${"" if signed else "U"}Int(exactly: self) { - hasher.append(bits: word._lowWord) + hasher._combine(word._lowWord) } else { - hasher.append(bits: UInt64(_value)) + hasher._combine(UInt64(_value)) + } + % else: + fatalError("Unsupported integer width") + % end + } + + @inlinable // FIXME(sil-serialize-all) + public func _rawHashValue(seed: (UInt64, UInt64)) -> Int { + // FIXME(hasher): Note that the AnyHashable concern applies here too, + // because hashValue uses top-level hashing. + % if bits <= word_bits: + return Hasher._hash(seed: seed, _lowWord) + % elif bits == 2 * word_bits: + if let word = ${"" if signed else "U"}Int(exactly: self) { + return Hasher._hash(seed: seed, word._lowWord) } + return Hasher._hash(seed: seed, UInt64(_value)) % else: fatalError("Unsupported integer width") % end @@ -3806,6 +4045,16 @@ public func _assumeNonNegative(_ x: ${Self}) -> ${Self} { } % end +extension ${Self} { + public static func _random( + using generator: inout R + ) -> ${Self} { + var result: ${Self} = 0 + withUnsafeMutableBytes(of: &result) { generator._fill(bytes: $0) } + return result + } +} + //===--- end of FIXME(integers) -------------------------------------------===// % end # end of concrete FixedWidthInteger section @@ -3841,7 +4090,6 @@ public func numericCast(_ x: T) -> U { // FIXME(integers): switch to using `FixedWidthInteger.unsafeAdding` @inlinable // FIXME(sil-serialize-all) -@usableFromInline // FIXME(sil-serialize-all) internal func _unsafePlus(_ lhs: Int, _ rhs: Int) -> Int { #if INTERNAL_CHECKS_ENABLED return lhs + rhs @@ -3852,7 +4100,6 @@ internal func _unsafePlus(_ lhs: Int, _ rhs: Int) -> Int { // FIXME(integers): switch to using `FixedWidthInteger.unsafeSubtracting` @inlinable // FIXME(sil-serialize-all) -@usableFromInline // FIXME(sil-serialize-all) internal func _unsafeMinus(_ lhs: Int, _ rhs: Int) -> Int { #if INTERNAL_CHECKS_ENABLED return lhs - rhs diff --git a/stdlib/public/core/KeyPath.swift b/stdlib/public/core/KeyPath.swift index 3b64f65e8d971..be5890b3334f5 100644 --- a/stdlib/public/core/KeyPath.swift +++ b/stdlib/public/core/KeyPath.swift @@ -13,7 +13,6 @@ import SwiftShims @inlinable // FIXME(sil-serialize-all) -@usableFromInline // FIXME(sil-serialize-all) @_transparent internal func _abstract( methodName: StaticString = #function, @@ -47,20 +46,26 @@ public class AnyKeyPath: Hashable, _AppendKeyPath { @usableFromInline // FIXME(sil-serialize-all) internal final var _kvcKeyPathStringPtr: UnsafePointer? + /// The hash value. @inlinable // FIXME(sil-serialize-all) final public var hashValue: Int { return _hashValue(for: self) } + /// Hashes the essential components of this value by feeding them into the + /// given hasher. + /// + /// - Parameter hasher: The hasher to use when combining the components + /// of this instance. @inlinable // FIXME(sil-serialize-all) - public func _hash(into hasher: inout _Hasher) { + final public func hash(into hasher: inout Hasher) { return withBuffer { var buffer = $0 while true { let (component, type) = buffer.next() - hasher.append(component.value) + hasher.combine(component.value) if let type = type { - hasher.append(unsafeBitCast(type, to: Int.self)) + hasher.combine(unsafeBitCast(type, to: Int.self)) } else { break } @@ -120,7 +125,6 @@ public class AnyKeyPath: Hashable, _AppendKeyPath { // Prevent normal initialization. We use tail allocation via // allocWithTailElems(). @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init() { _sanityCheckFailure("use _create(...)") } @@ -152,13 +156,32 @@ public class AnyKeyPath: Hashable, _AppendKeyPath { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func withBuffer(_ f: (KeyPathBuffer) throws -> T) rethrows -> T { defer { _fixLifetime(self) } let base = UnsafeRawPointer(Builtin.projectTailElems(self, Int32.self)) return try f(KeyPathBuffer(base: base)) } + + @inlinable // FIXME(sil-serialize-all) + internal var _storedInlineOffset: Int? { + return withBuffer { + var buffer = $0 + var offset = 0 + while true { + let (rawComponent, optNextType) = buffer.next() + switch rawComponent.header.kind { + case .struct: + offset += rawComponent._structOrClassOffset + + case .class, .computed, .optionalChain, .optionalForce, .optionalWrap: + return .none + } + + if optNextType == nil { return .some(offset) } + } + } + } } /// A partially type-erased key path, from a concrete root type to any @@ -188,11 +211,9 @@ public class KeyPath: PartialKeyPath { // MARK: Implementation internal typealias Kind = KeyPathKind @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal class var kind: Kind { return .readOnly } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static func appendedType( with t: KeyPath.Type ) -> KeyPath.Type { @@ -217,7 +238,6 @@ public class KeyPath: PartialKeyPath { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal final func projectReadOnly(from root: Root) -> Value { // TODO: For perf, we could use a local growable buffer instead of Any var curBase: Any = root @@ -268,13 +288,11 @@ public class WritableKeyPath: KeyPath { // MARK: Implementation detail @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal override class var kind: Kind { return .value } // `base` is assumed to be undergoing a formal access for the duration of the // call, so must not be mutated by an alias @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func projectMutableAddress(from base: UnsafePointer) -> (pointer: UnsafeMutablePointer, owner: AnyObject?) { var p = UnsafeRawPointer(base) @@ -326,11 +344,9 @@ public class ReferenceWritableKeyPath< // MARK: Implementation detail @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal final override class var kind: Kind { return .reference } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal final override func projectMutableAddress( from base: UnsafePointer ) -> (pointer: UnsafeMutablePointer, owner: AnyObject?) { @@ -340,7 +356,6 @@ public class ReferenceWritableKeyPath< } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal final func projectMutableAddress(from origBase: Root) -> (pointer: UnsafeMutablePointer, owner: AnyObject?) { var keepAlive: AnyObject? @@ -429,7 +444,6 @@ internal enum KeyPathComponentKind { @usableFromInline // FIXME(sil-serialize-all) internal struct ComputedPropertyID: Hashable { @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(value: Int, isStoredProperty: Bool, isTableOffset: Bool) { self.value = value self.isStoredProperty = isStoredProperty @@ -444,7 +458,6 @@ internal struct ComputedPropertyID: Hashable { internal var isTableOffset: Bool @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static func ==( x: ComputedPropertyID, y: ComputedPropertyID ) -> Bool { @@ -454,16 +467,10 @@ internal struct ComputedPropertyID: Hashable { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) - internal var hashValue: Int { - return _hashValue(for: self) - } - - @inlinable // FIXME(sil-serialize-all) - public func _hash(into hasher: inout _Hasher) { - hasher.append(value) - hasher.append(isStoredProperty) - hasher.append(isTableOffset) + internal func hash(into hasher: inout Hasher) { + hasher.combine(value) + hasher.combine(isStoredProperty) + hasher.combine(isTableOffset) } } @@ -480,7 +487,7 @@ internal struct ComputedArgumentWitnesses { (_ xInstanceArguments: UnsafeRawPointer, _ yInstanceArguments: UnsafeRawPointer, _ size: Int) -> Bool - // FIXME(hasher) Append to an inout _Hasher instead + // FIXME(hasher) Combine to an inout Hasher instead internal typealias Hash = @convention(thin) (_ instanceArguments: UnsafeRawPointer, _ size: Int) -> Int @@ -502,7 +509,6 @@ internal enum KeyPathComponent: Hashable { @usableFromInline // FIXME(sil-serialize-all) internal struct ArgumentRef { @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init( data: UnsafeRawBufferPointer, witnesses: UnsafePointer @@ -546,7 +552,6 @@ internal enum KeyPathComponent: Hashable { case optionalWrap @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static func ==(a: KeyPathComponent, b: KeyPathComponent) -> Bool { switch (a, b) { case (.struct(offset: let a), .struct(offset: let b)), @@ -590,14 +595,7 @@ internal enum KeyPathComponent: Hashable { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) - internal var hashValue: Int { - return _hashValue(for: self) - } - - @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) - internal func _hash(into hasher: inout _Hasher) { + internal func hash(into hasher: inout Hasher) { var hasher = hasher func appendHashFromArgument( _ argument: KeyPathComponent.ArgumentRef? @@ -610,58 +608,105 @@ internal enum KeyPathComponent: Hashable { // hash value of the overall key path. // FIXME(hasher): hash witness should just mutate hasher directly if hash != 0 { - hasher.append(hash) + hasher.combine(hash) } } } switch self { case .struct(offset: let a): - hasher.append(0) - hasher.append(a) + hasher.combine(0) + hasher.combine(a) case .class(offset: let b): - hasher.append(1) - hasher.append(b) + hasher.combine(1) + hasher.combine(b) case .optionalChain: - hasher.append(2) + hasher.combine(2) case .optionalForce: - hasher.append(3) + hasher.combine(3) case .optionalWrap: - hasher.append(4) + hasher.combine(4) case .get(id: let id, get: _, argument: let argument): - hasher.append(5) - hasher.append(id) + hasher.combine(5) + hasher.combine(id) appendHashFromArgument(argument) case .mutatingGetSet(id: let id, get: _, set: _, argument: let argument): - hasher.append(6) - hasher.append(id) + hasher.combine(6) + hasher.combine(id) appendHashFromArgument(argument) case .nonmutatingGetSet(id: let id, get: _, set: _, argument: let argument): - hasher.append(7) - hasher.append(id) + hasher.combine(7) + hasher.combine(id) appendHashFromArgument(argument) } } } // A class that maintains ownership of another object while a mutable projection -// into it is underway. +// into it is underway. The lifetime of the instance of this class is also used +// to begin and end exclusive 'modify' access to the projected address. @_fixed_layout // FIXME(sil-serialize-all) @usableFromInline // FIXME(sil-serialize-all) -internal final class ClassHolder { +internal final class ClassHolder { + + /// The type of the scratch record passed to the runtime to record + /// accesses to guarantee exlcusive access. + internal typealias AccessRecord = Builtin.UnsafeValueBuffer + @usableFromInline // FIXME(sil-serialize-all) - internal let previous: AnyObject? + internal var previous: AnyObject? @usableFromInline // FIXME(sil-serialize-all) - internal let instance: AnyObject + internal var instance: AnyObject @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(previous: AnyObject?, instance: AnyObject) { self.previous = previous self.instance = instance } + @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) - deinit {} + internal final class func _create( + previous: AnyObject?, + instance: AnyObject, + accessingAddress address: UnsafeRawPointer, + type: ProjectionType.Type + ) -> ClassHolder { + + // Tail allocate the UnsafeValueBuffer used as the AccessRecord. + // This avoids a second heap allocation since there is no source-level way to + // initialize a Builtin.UnsafeValueBuffer type and thus we cannot have a + // stored property of that type. + let holder: ClassHolder = Builtin.allocWithTailElems_1(self, + 1._builtinWordValue, + AccessRecord.self) + + // Initialize the ClassHolder's instance variables. This is done via + // withUnsafeMutablePointer(to:) because the instance was just allocated with + // allocWithTailElems_1 and so we need to make sure to use an initialization + // rather than an assignment. + withUnsafeMutablePointer(to: &holder.previous) { + $0.initialize(to: previous) + } + + withUnsafeMutablePointer(to: &holder.instance) { + $0.initialize(to: instance) + } + + let accessRecordPtr = Builtin.projectTailElems(holder, AccessRecord.self) + + // Begin a 'modify' access to the address. This access is ended in + // ClassHolder's deinitializer. + Builtin.beginUnpairedModifyAccess(address._rawValue, accessRecordPtr, type) + + return holder + } + + @inlinable // FIXME(sil-serialize-all) + deinit { + let accessRecordPtr = Builtin.projectTailElems(self, AccessRecord.self) + + // Ends the access begun in _create(). + Builtin.endUnpairedAccess(accessRecordPtr) + } } // A class that triggers writeback to a pointer when destroyed. @@ -682,13 +727,11 @@ internal final class MutatingWritebackBuffer { internal var value: NewValue @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) deinit { set(value, &base.pointee, argument, argumentSize) } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(previous: AnyObject?, base: UnsafeMutablePointer, @@ -723,13 +766,11 @@ internal final class NonmutatingWritebackBuffer { internal var value: NewValue @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) deinit { set(value, base, argument, argumentSize) } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(previous: AnyObject?, base: CurValue, @@ -750,7 +791,6 @@ internal final class NonmutatingWritebackBuffer { @usableFromInline // FIXME(sil-serialize-all) internal struct RawKeyPathComponent { @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(header: Header, body: UnsafeRawBufferPointer) { self.header = header self.body = body @@ -765,113 +805,91 @@ internal struct RawKeyPathComponent { @usableFromInline // FIXME(sil-serialize-all) internal struct Header { @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static var payloadMask: UInt32 { return _SwiftKeyPathComponentHeader_PayloadMask } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static var discriminatorMask: UInt32 { return _SwiftKeyPathComponentHeader_DiscriminatorMask } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static var discriminatorShift: UInt32 { return _SwiftKeyPathComponentHeader_DiscriminatorShift } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static var structTag: UInt32 { return _SwiftKeyPathComponentHeader_StructTag } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static var computedTag: UInt32 { return _SwiftKeyPathComponentHeader_ComputedTag } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static var classTag: UInt32 { return _SwiftKeyPathComponentHeader_ClassTag } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static var optionalTag: UInt32 { return _SwiftKeyPathComponentHeader_OptionalTag } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static var optionalChainPayload: UInt32 { return _SwiftKeyPathComponentHeader_OptionalChainPayload } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static var optionalWrapPayload: UInt32 { return _SwiftKeyPathComponentHeader_OptionalWrapPayload } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static var optionalForcePayload: UInt32 { return _SwiftKeyPathComponentHeader_OptionalForcePayload } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static var endOfReferencePrefixFlag: UInt32 { return _SwiftKeyPathComponentHeader_EndOfReferencePrefixFlag } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static var outOfLineOffsetPayload: UInt32 { return _SwiftKeyPathComponentHeader_OutOfLineOffsetPayload } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static var unresolvedFieldOffsetPayload: UInt32 { return _SwiftKeyPathComponentHeader_UnresolvedFieldOffsetPayload } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static var unresolvedIndirectOffsetPayload: UInt32 { return _SwiftKeyPathComponentHeader_UnresolvedIndirectOffsetPayload } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static var computedMutatingFlag: UInt32 { return _SwiftKeyPathComponentHeader_ComputedMutatingFlag } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static var computedSettableFlag: UInt32 { return _SwiftKeyPathComponentHeader_ComputedSettableFlag } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static var computedIDByStoredPropertyFlag: UInt32 { return _SwiftKeyPathComponentHeader_ComputedIDByStoredPropertyFlag } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static var computedIDByVTableOffsetFlag: UInt32 { return _SwiftKeyPathComponentHeader_ComputedIDByVTableOffsetFlag } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static var computedHasArgumentsFlag: UInt32 { return _SwiftKeyPathComponentHeader_ComputedHasArgumentsFlag } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static var computedIDResolutionMask: UInt32 { return _SwiftKeyPathComponentHeader_ComputedIDResolutionMask } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static var computedIDResolved: UInt32 { return _SwiftKeyPathComponentHeader_ComputedIDResolved } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static var computedIDUnresolvedIndirectPointer: UInt32 { return _SwiftKeyPathComponentHeader_ComputedIDUnresolvedIndirectPointer } @@ -880,12 +898,10 @@ internal struct RawKeyPathComponent { internal var _value: UInt32 @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var discriminator: UInt32 { return (_value & Header.discriminatorMask) >> Header.discriminatorShift } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var payload: UInt32 { get { return _value & Header.payloadMask @@ -897,7 +913,6 @@ internal struct RawKeyPathComponent { } } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var endOfReferencePrefix: Bool { get { return _value & Header.endOfReferencePrefixFlag != 0 @@ -912,7 +927,6 @@ internal struct RawKeyPathComponent { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var kind: KeyPathComponentKind { switch (discriminator, payload) { case (Header.structTag, _): @@ -935,7 +949,6 @@ internal struct RawKeyPathComponent { // The component header is 4 bytes, but may be followed by an aligned // pointer field for some kinds of component, forcing padding. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static var pointerAlignmentSkew: Int { return MemoryLayout.size - MemoryLayout.size } @@ -943,7 +956,6 @@ internal struct RawKeyPathComponent { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var bodySize: Int { switch header.kind { case .struct, .class: @@ -971,7 +983,6 @@ internal struct RawKeyPathComponent { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var _structOrClassOffset: Int { _sanityCheck(header.kind == .struct || header.kind == .class, "no offset for this kind") @@ -987,7 +998,6 @@ internal struct RawKeyPathComponent { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var _computedIDValue: Int { _sanityCheck(header.kind == .computed, "not a computed property") @@ -996,7 +1006,6 @@ internal struct RawKeyPathComponent { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var _computedID: ComputedPropertyID { let payload = header.payload return ComputedPropertyID( @@ -1006,7 +1015,6 @@ internal struct RawKeyPathComponent { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var _computedGetter: UnsafeRawPointer { _sanityCheck(header.kind == .computed, "not a computed property") @@ -1017,7 +1025,6 @@ internal struct RawKeyPathComponent { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var _computedSetter: UnsafeRawPointer { _sanityCheck(header.kind == .computed, "not a computed property") @@ -1036,7 +1043,6 @@ internal struct RawKeyPathComponent { _ instanceArguments: UnsafeMutableRawPointer) -> () @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var _computedArgumentHeaderPointer: UnsafeRawPointer { _sanityCheck(header.kind == .computed, "not a computed property") @@ -1050,12 +1056,10 @@ internal struct RawKeyPathComponent { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var _computedArgumentSize: Int { return _computedArgumentHeaderPointer.load(as: Int.self) } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var _computedArgumentWitnesses: UnsafePointer { return _computedArgumentHeaderPointer.load( @@ -1064,18 +1068,15 @@ internal struct RawKeyPathComponent { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var _computedArguments: UnsafeRawPointer { return _computedArgumentHeaderPointer + MemoryLayout.size * 2 } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var _computedMutableArguments: UnsafeMutableRawPointer { return UnsafeMutableRawPointer(mutating: _computedArguments) } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var value: KeyPathComponent { switch header.kind { case .struct: @@ -1126,7 +1127,6 @@ internal struct RawKeyPathComponent { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func destroy() { switch header.kind { case .struct, @@ -1146,7 +1146,6 @@ internal struct RawKeyPathComponent { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func clone(into buffer: inout UnsafeMutableRawBufferPointer, endOfReferencePrefix: Bool) { var newHeader = header @@ -1210,7 +1209,7 @@ internal struct RawKeyPathComponent { } @_frozen // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) + @usableFromInline internal enum ProjectionResult { /// Continue projecting the key path with the given new value. case `continue`(NewValue) @@ -1219,7 +1218,6 @@ internal struct RawKeyPathComponent { case `break`(LeafValue) @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var assumingContinue: NewValue { switch self { case .continue(let x): @@ -1231,7 +1229,6 @@ internal struct RawKeyPathComponent { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func projectReadOnly( _ base: CurValue, to: NewValue.Type, @@ -1253,7 +1250,15 @@ internal struct RawKeyPathComponent { let baseObj = unsafeBitCast(base, to: AnyObject.self) let basePtr = UnsafeRawPointer(Builtin.bridgeToRawPointer(baseObj)) defer { _fixLifetime(baseObj) } - return .continue(basePtr.advanced(by: offset) + + let offsetAddress = basePtr.advanced(by: offset) + + // Perform an instaneous record access on the address in order to + // ensure that the read will not conflict with an already in-progress + // 'modify' access. + Builtin.performInstantaneousReadAccess(offsetAddress._rawValue, + NewValue.self) + return .continue(offsetAddress .assumingMemoryBound(to: NewValue.self) .pointee) @@ -1294,7 +1299,6 @@ internal struct RawKeyPathComponent { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func projectMutableAddress( _ base: UnsafeRawPointer, from _: CurValue.Type, @@ -1313,12 +1317,16 @@ internal struct RawKeyPathComponent { // AnyObject memory can alias any class reference memory, so we can // assume type here let object = base.assumingMemoryBound(to: AnyObject.self).pointee - // The base ought to be kept alive for the duration of the derived access - keepAlive = keepAlive == nil - ? object - : ClassHolder(previous: keepAlive, instance: object) - return UnsafeRawPointer(Builtin.bridgeToRawPointer(object)) + let offsetAddress = UnsafeRawPointer(Builtin.bridgeToRawPointer(object)) .advanced(by: offset) + + // Keep the base alive for the duration of the derived access and also + // enforce exclusive access to the address. + keepAlive = ClassHolder._create(previous: keepAlive, instance: object, + accessingAddress: offsetAddress, + type: NewValue.self) + + return offsetAddress case .mutatingGetSet(id: _, get: let rawGet, set: let rawSet, argument: let argument): @@ -1402,7 +1410,6 @@ internal struct KeyPathBuffer { internal var hasReferencePrefix: Bool @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var mutableData: UnsafeMutableRawBufferPointer { return UnsafeMutableRawBufferPointer(mutating: data) } @@ -1431,7 +1438,6 @@ internal struct KeyPathBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(size: Int, trivial: Bool, hasReferencePrefix: Bool) { _sanityCheck(size <= Int(Header.sizeMask), "key path too big") _value = UInt32(size) @@ -1440,13 +1446,10 @@ internal struct KeyPathBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var size: Int { return Int(_value & Header.sizeMask) } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var trivial: Bool { return _value & Header.trivialFlag != 0 } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var hasReferencePrefix: Bool { get { return _value & Header.hasReferencePrefixFlag != 0 @@ -1463,13 +1466,11 @@ internal struct KeyPathBuffer { // In a key path pattern, the "trivial" flag is used to indicate // "instantiable in-line" @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var instantiableInLine: Bool { return trivial } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func validateReservedBits() { _precondition(_value & Header.reservedMask == 0, "Reserved bits set to an unexpected bit pattern") @@ -1477,7 +1478,6 @@ internal struct KeyPathBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(base: UnsafeRawPointer) { let header = base.load(as: Header.self) data = UnsafeRawBufferPointer( @@ -1488,7 +1488,6 @@ internal struct KeyPathBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func destroy() { // Short-circuit if nothing in the object requires destruction. if trivial { return } @@ -1502,7 +1501,6 @@ internal struct KeyPathBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal mutating func next() -> (RawKeyPathComponent, Any.Type?) { let header = pop(RawKeyPathComponent.Header.self) // Track if this is the last component of the reference prefix. @@ -1530,7 +1528,6 @@ internal struct KeyPathBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal mutating func pop(_ type: T.Type) -> T { _sanityCheck(_isPOD(T.self), "should be POD") let raw = popRaw(size: MemoryLayout.size, @@ -1544,7 +1541,6 @@ internal struct KeyPathBuffer { return result } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal mutating func popRaw(size: Int, alignment: Int) -> UnsafeRawBufferPointer { var baseAddress = data.baseAddress.unsafelyUnwrapped @@ -2065,7 +2061,6 @@ public func _appendingKeyPaths< // pointer to the KVC string. @inlinable // FIXME(sil-serialize-all) -@usableFromInline // FIXME(sil-serialize-all) internal var keyPathObjectHeaderSize: Int { return MemoryLayout.size + MemoryLayout.size } @@ -2121,7 +2116,6 @@ public func _swift_getKeyPath(pattern: UnsafeMutableRawPointer, } @inlinable // FIXME(sil-serialize-all) -@usableFromInline // FIXME(sil-serialize-all) internal func _getKeyPath_instantiatedOutOfLine( _ pattern: UnsafeRawPointer, _ arguments: UnsafeRawPointer) @@ -2151,7 +2145,6 @@ internal func _getKeyPath_instantiatedOutOfLine( } @inlinable // FIXME(sil-serialize-all) -@usableFromInline // FIXME(sil-serialize-all) internal func _getKeyPath_instantiateInline( _ objectRawPtr: Builtin.RawPointer ) { @@ -2192,7 +2185,6 @@ internal typealias MetadataAccessor = @convention(c) (UnsafeRawPointer) -> UnsafeRawPointer @inlinable // FIXME(sil-serialize-all) -@usableFromInline // FIXME(sil-serialize-all) internal func _getKeyPathClassAndInstanceSizeFromPattern( _ pattern: UnsafeRawPointer, _ arguments: UnsafeRawPointer @@ -2347,7 +2339,6 @@ internal func _getKeyPathClassAndInstanceSizeFromPattern( } @inlinable // FIXME(sil-serialize-all) -@usableFromInline // FIXME(sil-serialize-all) internal func _instantiateKeyPathBuffer( _ origPatternBuffer: KeyPathBuffer, _ origDestData: UnsafeMutableRawBufferPointer, @@ -2404,8 +2395,16 @@ internal func _instantiateKeyPathBuffer( // offset within the metadata object. let metadataPtr = unsafeBitCast(base, to: UnsafeRawPointer.self) let offsetOfOffset = patternBuffer.pop(UInt32.self) - let offset = UInt32(metadataPtr.load(fromByteOffset: Int(offsetOfOffset), - as: UInt.self)) + + let offset: UInt32 + if (header.kind == .struct) { + offset = UInt32(metadataPtr.load(fromByteOffset: Int(offsetOfOffset), + as: UInt32.self)) + } else { + offset = UInt32(metadataPtr.load(fromByteOffset: Int(offsetOfOffset), + as: UInt.self)) + } + // Rewrite the header for a resolved offset. var newHeader = header newHeader.payload = RawKeyPathComponent.Header.outOfLineOffsetPayload diff --git a/stdlib/public/core/LazyCollection.swift b/stdlib/public/core/LazyCollection.swift index 82a92ac70cc88..d5dd497128b78 100644 --- a/stdlib/public/core/LazyCollection.swift +++ b/stdlib/public/core/LazyCollection.swift @@ -53,7 +53,6 @@ public struct LazyCollection { /// Creates an instance with `base` as its underlying Collection /// instance. @inlinable - @usableFromInline internal init(_base: Base) { self._base = _base } @@ -175,13 +174,14 @@ extension LazyCollection : Collection { return _base.count } - // The following requirement enables dispatching for index(of:) when - // the element type is Equatable. + // The following requirement enables dispatching for firstIndex(of:) and + // lastIndex(of:) when the element type is Equatable. /// Returns `Optional(Optional(index))` if an element was found; - /// `nil` otherwise. + /// `Optional(nil)` if the element doesn't exist in the collection; + /// `nil` if a search was not performed. /// - /// - Complexity: O(*n*) + /// - Complexity: Better than O(*n*) @inlinable public func _customIndexOfEquatableElement( _ element: Element @@ -189,6 +189,18 @@ extension LazyCollection : Collection { return _base._customIndexOfEquatableElement(element) } + /// Returns `Optional(Optional(index))` if an element was found; + /// `Optional(nil)` if the element doesn't exist in the collection; + /// `nil` if a search was not performed. + /// + /// - Complexity: Better than O(*n*) + @inlinable + public func _customLastIndexOfEquatableElement( + _ element: Element + ) -> Index?? { + return _base._customLastIndexOfEquatableElement(element) + } + /// Returns the first element of `self`, or `nil` if `self` is empty. @inlinable public var first: Element? { diff --git a/stdlib/public/core/LazySequence.swift b/stdlib/public/core/LazySequence.swift index 4480e89fdc4b3..7fc3b738f35b8 100644 --- a/stdlib/public/core/LazySequence.swift +++ b/stdlib/public/core/LazySequence.swift @@ -185,7 +185,6 @@ public struct LazySequence: _SequenceWrapper { /// which some operations such as `map` and `filter` are implemented /// lazily. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_base: Base) { self._base = _base } diff --git a/stdlib/public/core/LifetimeManager.swift b/stdlib/public/core/LifetimeManager.swift index 86c138841c643..43a5ddb38356f 100644 --- a/stdlib/public/core/LifetimeManager.swift +++ b/stdlib/public/core/LifetimeManager.swift @@ -88,8 +88,12 @@ public func _fixLifetime(_ x: T) { /// later use. /// /// - Parameters: -/// - arg: An instance to temporarily use via pointer. -/// - body: A closure that takes a mutable pointer to `arg` as its sole +/// - value: An instance to temporarily use via pointer. Note that the `inout` +/// exclusivity rules mean that, like any other `inout` argument, `value` +/// cannot be directly accessed by other code for the duration of `body`. +/// Access must only occur through the pointer argument to `body` until +/// `body` returns. +/// - body: A closure that takes a mutable pointer to `value` as its sole /// argument. If the closure has a return value, that value is also used /// as the return value of the `withUnsafeMutablePointer(to:_:)` function. /// The pointer argument is valid only for the duration of the function's @@ -97,35 +101,73 @@ public func _fixLifetime(_ x: T) { /// - Returns: The return value, if any, of the `body` closure. @inlinable public func withUnsafeMutablePointer( - to arg: inout T, + to value: inout T, _ body: (UnsafeMutablePointer) throws -> Result ) rethrows -> Result { - return try body(UnsafeMutablePointer(Builtin.addressof(&arg))) + return try body(UnsafeMutablePointer(Builtin.addressof(&value))) } /// Invokes the given closure with a pointer to the given argument. /// /// The `withUnsafePointer(to:_:)` function is useful for calling Objective-C -/// APIs that take in/out parameters (and default-constructible out -/// parameters) by pointer. +/// APIs that take in parameters by const pointer. /// /// The pointer argument to `body` is valid only during the execution of /// `withUnsafePointer(to:_:)`. Do not store or return the pointer for later /// use. /// /// - Parameters: -/// - arg: An instance to temporarily use via pointer. -/// - body: A closure that takes a pointer to `arg` as its sole argument. If +/// - value: An instance to temporarily use via pointer. +/// - body: A closure that takes a pointer to `value` as its sole argument. If /// the closure has a return value, that value is also used as the return /// value of the `withUnsafePointer(to:_:)` function. The pointer argument /// is valid only for the duration of the function's execution. +/// It is undefined behavior to try to mutate through the pointer argument +/// by converting it to `UnsafeMutablePointer` or any other mutable pointer +/// type. If you need to mutate the argument through the pointer, use +/// `withUnsafeMutablePointer(to:_:)` instead. /// - Returns: The return value, if any, of the `body` closure. @inlinable public func withUnsafePointer( - to arg: inout T, + to value: T, _ body: (UnsafePointer) throws -> Result ) rethrows -> Result { - return try body(UnsafePointer(Builtin.addressof(&arg))) + return try body(UnsafePointer(Builtin.addressOfBorrow(value))) } + +/// Invokes the given closure with a pointer to the given argument. +/// +/// The `withUnsafePointer(to:_:)` function is useful for calling Objective-C +/// APIs that take in parameters by const pointer. +/// +/// The pointer argument to `body` is valid only during the execution of +/// `withUnsafePointer(to:_:)`. Do not store or return the pointer for later +/// use. +/// +/// - Parameters: +/// - value: An instance to temporarily use via pointer. Note that the `inout` +/// exclusivity rules mean that, like any other `inout` argument, `value` +/// cannot be directly accessed by other code for the duration of `body`. +/// Access must only occur through the pointer argument to `body` until +/// `body` returns. +/// - body: A closure that takes a pointer to `value` as its sole argument. If +/// the closure has a return value, that value is also used as the return +/// value of the `withUnsafePointer(to:_:)` function. The pointer argument +/// is valid only for the duration of the function's execution. +/// It is undefined behavior to try to mutate through the pointer argument +/// by converting it to `UnsafeMutablePointer` or any other mutable pointer +/// type. If you need to mutate the argument through the pointer, use +/// `withUnsafeMutablePointer(to:_:)` instead. +/// - Returns: The return value, if any, of the `body` closure. +@inlinable +public func withUnsafePointer( + to value: inout T, + _ body: (UnsafePointer) throws -> Result +) rethrows -> Result +{ + return try body(UnsafePointer(Builtin.addressof(&value))) +} + + diff --git a/stdlib/public/core/ManagedBuffer.swift b/stdlib/public/core/ManagedBuffer.swift index 0abf59e448d3f..2f23b627d109a 100644 --- a/stdlib/public/core/ManagedBuffer.swift +++ b/stdlib/public/core/ManagedBuffer.swift @@ -13,7 +13,6 @@ import SwiftShims @inlinable // FIXME(sil-serialize-all) -@usableFromInline // FIXME(sil-serialize-all) @_silgen_name("swift_bufferAllocate") internal func _swift_bufferAllocate( bufferType type: AnyClass, @@ -73,14 +72,12 @@ open class ManagedBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal final var firstElementAddress: UnsafeMutablePointer { return UnsafeMutablePointer(Builtin.projectTailElems(self, Element.self)) } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal final var headerAddress: UnsafeMutablePointer
{ return UnsafeMutablePointer
(Builtin.addressof(&header)) } @@ -134,7 +131,6 @@ open class ManagedBuffer { /// Make ordinary initialization unavailable @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_doNotCallMe: ()) { _sanityCheckFailure("Only initialize these by calling create") } @@ -237,7 +233,6 @@ public struct ManagedBufferPointer : Equatable { /// for the _ContiguousArrayBuffer that this check must always succeed we omit /// it in this specialized constructor. @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal init(_uncheckedUnsafeBufferObject buffer: AnyObject) { ManagedBufferPointer._sanityCheckValidBufferClass(type(of: buffer)) self._nativeBuffer = Builtin.unsafeCastToNativeObject(buffer) @@ -329,7 +324,6 @@ public struct ManagedBufferPointer : Equatable { /// properties. The `deinit` of `bufferClass` must destroy its /// stored `Header` and any constructed `Element`s. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init( bufferClass: AnyClass, minimumCapacity: Int @@ -346,7 +340,6 @@ public struct ManagedBufferPointer : Equatable { /// Internal version for use by _ContiguousArrayBuffer.init where we know that /// we have a valid buffer class and that the capacity is >= 0. @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal init( _uncheckedBufferClass: AnyClass, minimumCapacity: Int @@ -372,7 +365,6 @@ public struct ManagedBufferPointer : Equatable { /// - Note: It is an error to use the `header` property of the resulting /// instance unless it has been initialized. @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal init(_ buffer: ManagedBuffer) { _nativeBuffer = Builtin.unsafeCastToNativeObject(buffer) } @@ -380,7 +372,6 @@ public struct ManagedBufferPointer : Equatable { internal typealias _My = ManagedBufferPointer @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static func _checkValidBufferClass( _ bufferClass: AnyClass, creating: Bool = false ) { @@ -399,7 +390,6 @@ public struct ManagedBufferPointer : Equatable { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static func _sanityCheckValidBufferClass( _ bufferClass: AnyClass, creating: Bool = false ) { @@ -419,7 +409,6 @@ public struct ManagedBufferPointer : Equatable { /// The required alignment for allocations of this type, minus 1 @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static var _alignmentMask: Int { return max( MemoryLayout<_HeapObject>.alignment, @@ -428,21 +417,18 @@ public struct ManagedBufferPointer : Equatable { /// The actual number of bytes allocated for this object. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var _capacityInBytes: Int { return _stdlib_malloc_size(_address) } /// The address of this instance in a convenient pointer-to-bytes form @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var _address: UnsafeMutableRawPointer { return UnsafeMutableRawPointer(Builtin.bridgeToRawPointer(_nativeBuffer)) } /// Offset from the allocated storage for `self` to the stored `Header` @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static var _headerOffset: Int { _onFastPath() return _roundUp( @@ -454,7 +440,6 @@ public struct ManagedBufferPointer : Equatable { /// instance. Not safe to use without _fixLifetime calls to /// guarantee it doesn't dangle @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var _headerPointer: UnsafeMutablePointer
{ _onFastPath() return (_address + _My._headerOffset).assumingMemoryBound( @@ -465,7 +450,6 @@ public struct ManagedBufferPointer : Equatable { /// safe to use without _fixLifetime calls to guarantee it doesn't /// dangle. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var _elementPointer: UnsafeMutablePointer { _onFastPath() return (_address + _My._elementOffset).assumingMemoryBound( @@ -474,7 +458,6 @@ public struct ManagedBufferPointer : Equatable { /// Offset from the allocated storage for `self` to the `Element` storage @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static var _elementOffset: Int { _onFastPath() return _roundUp( @@ -489,7 +472,7 @@ public struct ManagedBufferPointer : Equatable { return lhs._address == rhs._address } - @usableFromInline // FIXME(sil-serialize-all) + @usableFromInline internal var _nativeBuffer: Builtin.NativeObject } @@ -561,7 +544,6 @@ public func isKnownUniquelyReferenced(_ object: inout T) -> Bool } @inlinable -@usableFromInline internal func _isKnownUniquelyReferencedOrPinned(_ object: inout T) -> Bool { return _isUniqueOrPinned(&object) } diff --git a/stdlib/public/core/Map.swift b/stdlib/public/core/Map.swift index b10e14a59b75a..9f2ef4c2836b6 100644 --- a/stdlib/public/core/Map.swift +++ b/stdlib/public/core/Map.swift @@ -27,7 +27,6 @@ public struct LazyMapSequence { /// Creates an instance with elements `transform(x)` for each element /// `x` of base. @inlinable - @usableFromInline internal init(_base: Base, transform: @escaping (Base.Element) -> Element) { self._base = _base self._transform = transform @@ -46,7 +45,6 @@ extension LazyMapSequence { public var base: Base.Iterator { return _base } @inlinable - @usableFromInline internal init( _base: Base.Iterator, _transform: @escaping (Base.Element) -> Element @@ -108,7 +106,6 @@ public struct LazyMapCollection { /// Create an instance with elements `transform(x)` for each element /// `x` of base. @inlinable - @usableFromInline internal init(_base: Base, transform: @escaping (Base.Element) -> Element) { self._base = _base self._transform = transform diff --git a/stdlib/public/core/MemoryLayout.swift b/stdlib/public/core/MemoryLayout.swift index 882caf62afb7b..63b9022669dd0 100644 --- a/stdlib/public/core/MemoryLayout.swift +++ b/stdlib/public/core/MemoryLayout.swift @@ -162,4 +162,75 @@ extension MemoryLayout { public static func alignment(ofValue value: T) -> Int { return MemoryLayout.alignment } + + /// Returns the offset of an inline stored property of `T` within the + /// in-memory representation of `T`. + /// + /// If the given key refers to inline, directly addressable storage within + /// the in-memory representation of `T`, then the return value is a distance + /// in bytes that can be added to a pointer of type `T` to get a pointer to + /// the storage referenced by `key`. + /// + /// If the return value of this method is non-`nil`, then accessing the value + /// by key path or by an offset pointer are equivalent. For example, for a + /// variable `root` of type `T`, `value` of type `U`, and a key path `key` + /// of type `WritableKeyPath`: + /// + /// // Mutation through the key path + /// root[keyPath: key] = value + /// + /// // Mutation through the offset pointer + /// withUnsafeMutableBytes(of: &root) { bytes in + /// let rawPointerToValue = bytes.baseAddress! + MemoryLayout.offset(of: key)! + /// let pointerToValue = rawPointerToValue.assumingMemoryBound(to: U.self) + /// pointerToValue.pointee = value + /// } + /// + /// A property has inline, directly addressable storage when it is a stored + /// property for which no additional work is required to extract or set the + /// value. Properties are not directly accessible if they trigger any + /// `didSet` or `willSet` accessors, perform any representation changes such + /// as bridging or closure reabstraction, or mask the value out of + /// overlapping storage as for packed bitfields. In addition, because class + /// instance properties are always stored out-of-line, their positions are + /// not accessible using `offset(of:)`. + /// + /// For example, in the `ProductCategory` type defined here, only + /// `\.updateCounter`, `\.identifier`, and `\.identifier.name` refer to + /// properties with inline, directly addressable storage: + /// + /// struct ProductCategory { + /// struct Identifier { + /// var name: String // addressable + /// } + /// + /// var identifier: Identifier // addressable + /// var updateCounter: Int // addressable + /// var products: [Product] { // not addressable: didSet handler + /// didSet { updateCounter += 1 } + /// } + /// var productCount: Int { // not addressable: computed property + /// return products.count + /// } + /// } + /// + /// When using `offset(of:)` with a type imported from a library, don't + /// assume that future versions of the library will have the same behavior. + /// If a property is converted from a stored property to a computed property, + /// the result of `offset(of:)` changes to `nil`. That kind of conversion is + /// non-breaking in other contexts, but would trigger a runtime error if the + /// result of `offset(of:)` is force-unwrapped. + /// + /// - Parameter key: A key path referring to storage that can be accessed + /// through a value of type `T`. + /// - Returns: The offset in bytes from a pointer to a value of type `T` + /// to a pointer to the storage referenced by `key`, or `nil` if no + /// such offset is available for the storage referenced by `key`, such as + /// because `key` is computed, has observers, requires reabstraction, or + /// overlaps storage with other properties. + @inlinable // FIXME(sil-serialize-all) + @_transparent + public static func offset(of key: PartialKeyPath) -> Int? { + return key._storedInlineOffset + } } diff --git a/stdlib/public/core/Mirror.swift b/stdlib/public/core/Mirror.swift index 063bd66e2cbf3..d006542db8964 100644 --- a/stdlib/public/core/Mirror.swift +++ b/stdlib/public/core/Mirror.swift @@ -157,12 +157,10 @@ public struct Mirror { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static func _noSuperclassMirror() -> Mirror? { return nil } @_semantics("optimize.sil.specialize.generic.never") @inline(never) - @inlinable // FIXME(sil-serialize-all) @usableFromInline internal static func _superclassIterator( _ subject: Subject, _ ancestorRepresentation: AncestorRepresentation @@ -399,14 +397,12 @@ extension Mirror { @usableFromInline // FIXME(sil-serialize-all) internal struct _Dummy : CustomReflectable { @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(mirror: Mirror) { self.mirror = mirror } @usableFromInline // FIXME(sil-serialize-all) internal var mirror: Mirror @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var customMirror: Mirror { return mirror } } @@ -426,7 +422,7 @@ extension Mirror { /// i0 != children.endIndex /// { /// let grandChildren = Mirror(reflecting: children[i0].value).children - /// if let i1 = grandChildren.index(where: { $0.label == "two" }) { + /// if let i1 = grandChildren.firstIndex(where: { $0.label == "two" }) { /// let greatGrandChildren = /// Mirror(reflecting: grandChildren[i1].value).children /// if let i2 = greatGrandChildren.index( @@ -463,7 +459,7 @@ extension Mirror { let children = Mirror(reflecting: result).children let position: Children.Index if case let label as String = e { - position = children.index { $0.label == label } ?? children.endIndex + position = children.firstIndex { $0.label == label } ?? children.endIndex } else if let offset = e as? Int { position = children.index(children.startIndex, @@ -669,11 +665,11 @@ public protocol _DefaultCustomPlaygroundQuickLookable { /// Some operations that are efficient on a dictionary are slower when using /// `DictionaryLiteral`. In particular, to find the value matching a key, you /// must search through every element of the collection. The call to -/// `index(where:)` in the following example must traverse the whole +/// `firstIndex(where:)` in the following example must traverse the whole /// collection to find the element that matches the predicate: /// /// let runner = "Marlies Gohr" -/// if let index = recordTimes.index(where: { $0.0 == runner }) { +/// if let index = recordTimes.firstIndex(where: { $0.0 == runner }) { /// let time = recordTimes[index].1 /// print("\(runner) set a 100m record of \(time) seconds.") /// } else { diff --git a/stdlib/public/core/Misc.swift b/stdlib/public/core/Misc.swift index 5512950d73c04..651238e8bfab6 100644 --- a/stdlib/public/core/Misc.swift +++ b/stdlib/public/core/Misc.swift @@ -55,7 +55,6 @@ public func _autorelease(_ x: AnyObject) { /// This function is primarily useful to call various runtime functions /// written in C++. @inlinable // FIXME(sil-serialize-all) -@usableFromInline // FIXME(sil-serialize-all) internal func _withUninitializedString( _ body: (UnsafeMutablePointer) -> R ) -> (R, String) { diff --git a/stdlib/public/core/MutableCollection.swift b/stdlib/public/core/MutableCollection.swift index b55c1c02f87c5..dc1f10bf46ff8 100644 --- a/stdlib/public/core/MutableCollection.swift +++ b/stdlib/public/core/MutableCollection.swift @@ -25,7 +25,7 @@ public typealias MutableIndexable = MutableCollection /// modify one of the names in an array of students. /// /// var students = ["Ben", "Ivy", "Jordell", "Maxime"] -/// if let i = students.index(of: "Maxime") { +/// if let i = students.firstIndex(of: "Maxime") { /// students[i] = "Max" /// } /// print(students) @@ -112,7 +112,7 @@ where SubSequence: MutableCollection /// print(streetsSlice) /// // Prints "["Channing", "Douglas", "Evarts"]" /// - /// let index = streetsSlice.index(of: "Evarts") // 4 + /// let index = streetsSlice.firstIndex(of: "Evarts") // 4 /// streets[index!] = "Eustace" /// print(streets[index!]) /// // Prints "Eustace" @@ -211,7 +211,7 @@ extension MutableCollection { /// print(streetsSlice) /// // Prints "["Channing", "Douglas", "Evarts"]" /// - /// let index = streetsSlice.index(of: "Evarts") // 4 + /// let index = streetsSlice.firstIndex(of: "Evarts") // 4 /// streets[index!] = "Eustace" /// print(streets[index!]) /// // Prints "Eustace" diff --git a/stdlib/public/core/NewtypeWrapper.swift b/stdlib/public/core/NewtypeWrapper.swift index fe74178b3342b..b5353109032a0 100644 --- a/stdlib/public/core/NewtypeWrapper.swift +++ b/stdlib/public/core/NewtypeWrapper.swift @@ -15,11 +15,22 @@ /// attribute. public protocol _SwiftNewtypeWrapper : RawRepresentable { } -extension _SwiftNewtypeWrapper where Self.RawValue : Hashable { +extension _SwiftNewtypeWrapper where Self: Hashable, Self.RawValue : Hashable { + /// The hash value. @inlinable // FIXME(sil-serialize-all) public var hashValue: Int { return rawValue.hashValue } + + /// Hashes the essential components of this value by feeding them into the + /// given hasher. + /// + /// - Parameter hasher: The hasher to use when combining the components + /// of this instance. + @inlinable // FIXME(sil-serialize-all) + public func hash(into hasher: inout Hasher) { + hasher.combine(rawValue) + } } #if _runtime(_ObjC) diff --git a/stdlib/public/core/ObjectIdentifier.swift b/stdlib/public/core/ObjectIdentifier.swift index 903e49fb221f4..d049a1fb550e3 100644 --- a/stdlib/public/core/ObjectIdentifier.swift +++ b/stdlib/public/core/ObjectIdentifier.swift @@ -21,8 +21,8 @@ public struct ObjectIdentifier { /// Creates an instance that uniquely identifies the given class instance. /// - /// The following example creates an example class `A` and compares instances - /// of the class using their object identifiers and the identical-to + /// The following example creates an example class `IntegerRef` and compares + /// instances of the class using their object identifiers and the identical-to /// operator (`===`): /// /// class IntegerRef { @@ -84,15 +84,14 @@ extension ObjectIdentifier: Comparable { } extension ObjectIdentifier: Hashable { - // FIXME: Better hashing algorithm - /// The identifier's hash value. + /// Hashes the essential components of this value by feeding them into the + /// given hasher. /// - /// The hash value is not guaranteed to be stable across different - /// invocations of the same program. Do not persist the hash value across - /// program runs. + /// - Parameter hasher: The hasher to use when combining the components + /// of this instance. @inlinable // FIXME(sil-serialize-all) - public var hashValue: Int { - return Int(Builtin.ptrtoint_Word(_value)) + public func hash(into hasher: inout Hasher) { + hasher.combine(Int(Builtin.ptrtoint_Word(_value))) } } diff --git a/stdlib/public/core/Optional.swift b/stdlib/public/core/Optional.swift index 516010cabb053..66c42efe051ce 100644 --- a/stdlib/public/core/Optional.swift +++ b/stdlib/public/core/Optional.swift @@ -72,7 +72,7 @@ /// optional chaining to access the `hasSuffix(_:)` method on a `String?` /// instance. /// -/// if let isPNG = imagePaths["star"]?.hasSuffix(".png") { +/// if imagePaths["star"]?.hasSuffix(".png") == true { /// print("The star image is in PNG format") /// } /// // Prints "The star image is in PNG format" @@ -410,25 +410,19 @@ extension Optional : Equatable where Wrapped : Equatable { } extension Optional: Hashable where Wrapped: Hashable { - /// The hash value for the optional instance. + /// Hashes the essential components of this value by feeding them into the + /// given hasher. /// - /// Two optionals that are equal will always have equal hash values. - /// - /// Hash values are not guaranteed to be equal across different executions of - /// your program. Do not save hash values to use during a future execution. - @inlinable // FIXME(sil-serialize-all) - public var hashValue: Int { - return _hashValue(for: self) - } - + /// - Parameter hasher: The hasher to use when combining the components + /// of this instance. @inlinable // FIXME(sil-serialize-all) - public func _hash(into hasher: inout _Hasher) { + public func hash(into hasher: inout Hasher) { switch self { case .none: - hasher.append(0 as UInt8) + hasher.combine(0 as UInt8) case .some(let wrapped): - hasher.append(1 as UInt8) - hasher.append(wrapped) + hasher.combine(1 as UInt8) + hasher.combine(wrapped) } } } @@ -724,7 +718,6 @@ public func ?? (optional: T?, defaultValue: @autoclosure () throws -> T?) extension Optional : _ObjectiveCBridgeable { // The object that represents `none` for an Optional of this type. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static var _nilSentinel : AnyObject { @_silgen_name("_swift_Foundation_getOptionalNilSentinelObject") get diff --git a/stdlib/public/core/OutputStream.swift b/stdlib/public/core/OutputStream.swift index 9131342332de7..c7f060ca78fca 100644 --- a/stdlib/public/core/OutputStream.swift +++ b/stdlib/public/core/OutputStream.swift @@ -279,7 +279,6 @@ internal func _opaqueSummary(_ metadata: Any.Type) -> UnsafePointer? /// Do our best to print a value that cannot be printed directly. @inlinable // FIXME(sil-serialize-all) -@usableFromInline // FIXME(sil-serialize-all) @_semantics("optimize.sil.specialize.generic.never") internal func _adHocPrint_unlocked( _ value: T, _ mirror: Mirror, _ target: inout TargetStream, @@ -416,9 +415,8 @@ internal func _print_unlocked( /// /// This function is forbidden from being inlined because when building the /// standard library inlining makes us drop the special semantics. -@inlinable -@usableFromInline @inline(never) @effects(readonly) +@usableFromInline internal func _toStringReadOnlyStreamable< T : TextOutputStreamable >(_ x: T) -> String { @@ -427,9 +425,8 @@ internal func _toStringReadOnlyStreamable< return result } -@inlinable -@usableFromInline @inline(never) @effects(readonly) +@usableFromInline internal func _toStringReadOnlyPrintable< T : CustomStringConvertible >(_ x: T) -> String { @@ -440,7 +437,6 @@ internal func _toStringReadOnlyPrintable< // `debugPrint` //===----------------------------------------------------------------------===// -@inlinable // FIXME(sil-serialize-all) @_semantics("optimize.sil.specialize.generic.never") @inline(never) public func _debugPrint_unlocked( @@ -466,7 +462,6 @@ public func _debugPrint_unlocked( } @inlinable // FIXME(sil-serialize-all) -@usableFromInline // FIXME(sil-serialize-all) @_semantics("optimize.sil.specialize.generic.never") internal func _dumpPrint_unlocked( _ value: T, _ mirror: Mirror, _ target: inout TargetStream @@ -542,23 +537,19 @@ internal func _dumpPrint_unlocked( @usableFromInline // FIXME(sil-serialize-all) internal struct _Stdout : TextOutputStream { @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init() {} @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal mutating func _lock() { _swift_stdlib_flockfile_stdout() } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal mutating func _unlock() { _swift_stdlib_funlockfile_stdout() } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal mutating func write(_ string: String) { if string.isEmpty { return } @@ -631,7 +622,6 @@ internal struct _TeeStream< > : TextOutputStream { @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(left: L, right: R) { self.left = left self.right = right @@ -644,16 +634,13 @@ internal struct _TeeStream< /// Append the given `string` to this stream. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal mutating func write(_ string: String) { left.write(string); right.write(string) } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal mutating func _lock() { left._lock(); right._lock() } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal mutating func _unlock() { right._unlock(); left._unlock() } } diff --git a/stdlib/public/core/PrefixWhile.swift b/stdlib/public/core/PrefixWhile.swift index 524505f24420a..cc2288ba239c8 100644 --- a/stdlib/public/core/PrefixWhile.swift +++ b/stdlib/public/core/PrefixWhile.swift @@ -18,7 +18,6 @@ public struct LazyPrefixWhileSequence { public typealias Element = Base.Element @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_base: Base, predicate: @escaping (Element) -> Bool) { self._base = _base self._predicate = predicate @@ -50,7 +49,6 @@ extension LazyPrefixWhileSequence { internal let _predicate: (Element) -> Bool @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_base: Base.Iterator, predicate: @escaping (Element) -> Bool) { self._base = _base self._predicate = predicate @@ -117,7 +115,6 @@ public struct LazyPrefixWhileCollection { public typealias SubSequence = Slice> @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_base: Base, predicate: @escaping (Element) -> Bool) { self._base = _base self._predicate = predicate @@ -158,7 +155,6 @@ extension LazyPrefixWhileCollection { /// Creates a new index wrapper for `i`. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_ i: Base.Index) { self._value = .index(i) } @@ -167,7 +163,6 @@ extension LazyPrefixWhileCollection { /// `LazyPrefixWhileCollection`. This is not the same as a wrapper /// around `Base.endIndex`. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(endOf: Base) { self._value = .pastEnd } @@ -207,18 +202,18 @@ extension LazyPrefixWhileCollection.Index: Comparable { } extension LazyPrefixWhileCollection.Index: Hashable where Base.Index: Hashable { + /// Hashes the essential components of this value by feeding them into the + /// given hasher. + /// + /// - Parameter hasher: The hasher to use when combining the components + /// of this instance. @inlinable // FIXME(sil-serialize-all) - public var hashValue: Int { - return _hashValue(for: self) - } - - @inlinable // FIXME(sil-serialize-all) - public func _hash(into hasher: inout _Hasher) { + public func hash(into hasher: inout Hasher) { switch _value { case .index(let value): - hasher.append(value) + hasher.combine(value) case .pastEnd: - hasher.append(Int.max) + hasher.combine(Int.max) } } } diff --git a/stdlib/public/core/Random.swift b/stdlib/public/core/Random.swift new file mode 100644 index 0000000000000..1716a0e1e4b1d --- /dev/null +++ b/stdlib/public/core/Random.swift @@ -0,0 +1,159 @@ +//===--- Random.swift -----------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 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 +// +//===----------------------------------------------------------------------===// + +import SwiftShims + +/// A type that provides uniformly distributed random data. +/// +/// When you call methods that use random data, such as creating new random +/// values or shuffling a collection, you can pass a `RandomNumberGenerator` +/// type to be used as the source for randomness. When you don't pass a +/// generator, the default `Random` type is used. +/// +/// When providing new APIs that use randomness, provide a version that accepts +/// a generator conforming to the `RandomNumberGenerator` protocol as well as a +/// version that uses the default generator. For example, this `Weekday` +/// enumeration provides static methods that return a random day of the week: +/// +/// enum Weekday: CaseIterable { +/// case sunday, monday, tuesday, wednesday, thursday, friday, saturday +/// +/// static func random(using generator: inout G) -> Weekday { +/// return Weekday.allCases.randomElement(using: &generator)! +/// } +/// +/// static func random() -> Weekday { +/// return Weekday.randomWeekday(using: &Random.default) +/// } +/// } +/// +/// Conforming to the RandomNumberGenerator Protocol +/// ================================================ +/// +/// A custom `RandomNumberGenerator` type can have different characteristics +/// than the default `Random` type. For example, a seedable generator can be +/// used to generate the same sequence of random values for testing purposes. +/// +/// To make a custom type conform to the `RandomNumberGenerator` protocol, +/// implement the required `next()` method. Each call to `next()` must produce +/// a uniform and independent random value. +/// +/// Types that conform to `RandomNumberGenerator` should specifically document +/// the thread safety and quality of the generator. +public protocol RandomNumberGenerator { + /// Returns a value from a uniform, independent distribution of binary data. + /// + /// - Returns: An unsigned 64-bit random value. + mutating func next() -> UInt64 + + // FIXME: De-underscore after swift-evolution amendment + mutating func _fill(bytes buffer: UnsafeMutableRawBufferPointer) +} + +extension RandomNumberGenerator { + public mutating func _fill(bytes buffer: UnsafeMutableRawBufferPointer) { + // FIXME: Optimize + var chunk: UInt64 = 0 + var chunkBytes = 0 + for i in 0..>= UInt8.bitWidth + chunkBytes -= 1 + } + } +} + +extension RandomNumberGenerator { + /// Returns a value from a uniform, independent distribution of binary data. + /// + /// - Returns: A random value of `T`. Bits are randomly distributed so that + /// every value of `T` is equally likely to be returned. + @inlinable + public mutating func next() -> T { + return T._random(using: &self) + } + + /// Returns a random value that is less than the given upper bound. + /// + /// - Parameter upperBound: The upper bound for the randomly generated value. + /// - Returns: A random value of `T` in the range `0..( + upperBound: T + ) -> T { + guard upperBound != 0 else { return 0 } + let tmp = (T.max % upperBound) + 1 + let range = tmp == upperBound ? 0 : tmp + var random: T = 0 + + repeat { + random = next() + } while random < range + + return random % upperBound + } +} + +/// The default source of random data. +/// +/// When you generate random values, shuffle a collection, or perform another +/// operation that depends on random data, this type's `default` property is +/// the generator used by default. For example, the two method calls in this +/// example are equivalent: +/// +/// let x = Int.random(in: 1...100) +/// let y = Int.random(in: 1...100, using: &Random.default) +/// +/// `Random.default` is automatically seeded, is safe to use in multiple +/// threads, and uses a cryptographically secure algorithm whenever possible. +/// +/// Platform Implementation of `Random` +/// =================================== +/// +/// While the `Random.default` generator is automatically seeded and +/// thread-safe on every platform, the cryptographic quality of the stream of +/// random data produced by the generator may vary. For more detail, see the +/// documentation for the APIs used by each platform. +/// +/// - Apple platforms use `arc4random_buf(3)`. +/// - Linux platforms use `getrandom(2)` when available; otherwise, they read +/// from `/dev/urandom`. +public struct Random : RandomNumberGenerator { + /// The default instance of the `Random` random number generator. + public static var `default`: Random { + get { return Random() } + set { /* Discard */ } + } + + private init() {} + + /// Returns a value from a uniform, independent distribution of binary data. + /// + /// - Returns: An unsigned 64-bit random value. + @effects(releasenone) + public mutating func next() -> UInt64 { + var random: UInt64 = 0 + _stdlib_random(&random, MemoryLayout.size) + return random + } + + public mutating func _fill(bytes buffer: UnsafeMutableRawBufferPointer) { + if !buffer.isEmpty { + _stdlib_random(buffer.baseAddress!, buffer.count) + } + } +} diff --git a/stdlib/public/core/RandomAccessCollection.swift b/stdlib/public/core/RandomAccessCollection.swift index 0130e9a1fca53..8e9f34edba0cd 100644 --- a/stdlib/public/core/RandomAccessCollection.swift +++ b/stdlib/public/core/RandomAccessCollection.swift @@ -87,7 +87,7 @@ where SubSequence: RandomAccessCollection, Indices: RandomAccessCollection /// print(streetsSlice) /// // Prints "["Channing", "Douglas", "Evarts"]" /// - /// let index = streetsSlice.index(of: "Evarts") // 4 + /// let index = streetsSlice.firstIndex(of: "Evarts") // 4 /// print(streets[index!]) /// // Prints "Evarts" /// diff --git a/stdlib/public/core/Range.swift b/stdlib/public/core/Range.swift index 0b0a9de2d1613..5c79478b5c7c2 100644 --- a/stdlib/public/core/Range.swift +++ b/stdlib/public/core/Range.swift @@ -255,6 +255,12 @@ where Bound : Strideable, Bound.Stride : SignedInteger return lowerBound <= element && element < upperBound ? element : nil } + @inlinable + public func _customLastIndexOfEquatableElement(_ element: Bound) -> Index?? { + // The first and last elements are the same because each element is unique. + return _customIndexOfEquatableElement(element) + } + /// Accesses the element at specified position. /// /// You can subscript a collection with any valid index other than the @@ -399,15 +405,15 @@ extension Range: Equatable { } extension Range: Hashable where Bound: Hashable { + /// Hashes the essential components of this value by feeding them into the + /// given hasher. + /// + /// - Parameter hasher: The hasher to use when combining the components + /// of this instance. @inlinable // FIXME(sil-serialize-all) - public var hashValue: Int { - return _hashValue(for: self) - } - - @inlinable // FIXME(sil-serialize-all) - public func _hash(into hasher: inout _Hasher) { - hasher.append(lowerBound) - hasher.append(upperBound) + public func hash(into hasher: inout Hasher) { + hasher.combine(lowerBound) + hasher.combine(upperBound) } } @@ -606,18 +612,29 @@ extension PartialRangeFrom: Sequence { public typealias Element = Bound + /// The iterator for a `PartialRangeFrom` instance. @_fixed_layout public struct Iterator: IteratorProtocol { @usableFromInline internal var _current: Bound @inlinable public init(_current: Bound) { self._current = _current } + + /// Advances to the next element and returns it, or `nil` if no next + /// element exists. + /// + /// Once `nil` has been returned, all subsequent calls return `nil`. + /// + /// - Returns: The next element in the underlying sequence, if a next + /// element exists; otherwise, `nil`. @inlinable public mutating func next() -> Bound? { defer { _current = _current.advanced(by: 1) } return _current } } + + /// Returns an iterator for this sequence. @inlinable public func makeIterator() -> Iterator { return Iterator(_current: lowerBound) @@ -739,30 +756,33 @@ extension Comparable { /// unbounded range is essentially a conversion of a collection instance into /// its slice type. /// -/// For example, the following code declares `levenshteinDistance(_:_:)`, a -/// function that calculates the number of changes required to convert one -/// string into another. `levenshteinDistance(_:_:)` uses `Substring`, a -/// string's slice type, for its parameters. +/// For example, the following code declares `countLetterChanges(_:_:)`, a +/// function that finds the number of changes required to change one +/// word or phrase into another. The function uses a recursive approach to +/// perform the same comparisons on smaller and smaller pieces of the original +/// strings. In order to use recursion without making copies of the strings at +/// each step, `countLetterChanges(_:_:)` uses `Substring`, a string's slice +/// type, for its parameters. /// -/// func levenshteinDistance(_ s1: Substring, _ s2: Substring) -> Int { +/// func countLetterChanges(_ s1: Substring, _ s2: Substring) -> Int { /// if s1.isEmpty { return s2.count } /// if s2.isEmpty { return s1.count } /// /// let cost = s1.first == s2.first ? 0 : 1 /// /// return min( -/// levenshteinDistance(s1.dropFirst(), s2) + 1, -/// levenshteinDistance(s1, s2.dropFirst()) + 1, -/// levenshteinDistance(s1.dropFirst(), s2.dropFirst()) + cost) +/// countLetterChanges(s1.dropFirst(), s2) + 1, +/// countLetterChanges(s1, s2.dropFirst()) + 1, +/// countLetterChanges(s1.dropFirst(), s2.dropFirst()) + cost) /// } /// -/// To call `levenshteinDistance(_:_:)` with two strings, use an unbounded -/// range in each string's subscript to convert it to a `Substring`. +/// To call `countLetterChanges(_:_:)` with two strings, use an unbounded +/// range in each string's subscript. /// /// let word1 = "grizzly" /// let word2 = "grisly" -/// let distance = levenshteinDistance(word1[...], word2[...]) -/// // distance == 2 +/// let changes = countLetterChanges(word1[...], word2[...]) +/// // changes == 2 @_frozen // FIXME(sil-serialize-all) public enum UnboundedRange_ { // FIXME: replace this with a computed var named `...` when the language makes @@ -800,7 +820,7 @@ extension Collection { /// of the strings in the slice, and then uses that index in the original /// array. /// - /// let index = streetsSlice.index(of: "Evarts") // 4 + /// let index = streetsSlice.firstIndex(of: "Evarts") // 4 /// print(streets[index!]) /// // "Evarts" /// @@ -890,6 +910,8 @@ extension Range { } } -@available(*, deprecated, renamed: "Range") public typealias CountableRange = Range where Bound.Stride : SignedInteger + +public typealias CountablePartialRangeFrom = PartialRangeFrom + where Bound.Stride : SignedInteger diff --git a/stdlib/public/core/RangeReplaceableCollection.swift b/stdlib/public/core/RangeReplaceableCollection.swift index 002d2717e0e6b..ba754af40e045 100644 --- a/stdlib/public/core/RangeReplaceableCollection.swift +++ b/stdlib/public/core/RangeReplaceableCollection.swift @@ -353,6 +353,16 @@ public protocol RangeReplaceableCollection : Collection /// - Complexity: O(*n*), where *n* is the length of the collection. mutating func removeAll(keepingCapacity keepCapacity: Bool /*= false*/) + /// Removes from the collection all elements that satisfy the given predicate. + /// + /// - Parameter shouldBeRemoved: A closure that takes an element of the + /// sequence as its argument and returns a Boolean value indicating + /// whether the element should be removed from the collection. + /// + /// - Complexity: O(*n*), where *n* is the length of the collection. + mutating func removeAll( + where shouldBeRemoved: (Element) throws -> Bool) rethrows + // FIXME(ABI): Associated type inference requires this. subscript(bounds: Index) -> Element { get } @@ -544,7 +554,7 @@ extension RangeReplaceableCollection { /// Removes the elements in the specified subrange from the collection. /// /// All the elements following the specified position are moved to close the - /// gap. This example removes two elements from the middle of an array of + /// gap. This example removes three elements from the middle of an array of /// measurements. /// /// var measurements = [1.2, 1.5, 2.9, 1.2, 1.5] @@ -745,7 +755,7 @@ extension RangeReplaceableCollection { /// Removes the elements in the specified subrange from the collection. /// /// All the elements following the specified position are moved to close the - /// gap. This example removes two elements from the middle of an array of + /// gap. This example removes three elements from the middle of an array of /// measurements. /// /// var measurements = [1.2, 1.5, 2.9, 1.2, 1.5] @@ -1075,3 +1085,63 @@ extension RangeReplaceableCollection { return try Self(self.lazy.filter(isIncluded)) } } + +extension RangeReplaceableCollection where Self: MutableCollection { + /// Removes all the elements that satisfy the given predicate. + /// + /// Use this method to remove every element in a collection that meets + /// particular criteria. This example removes all the odd values from an + /// array of numbers: + /// + /// var numbers = [5, 6, 7, 8, 9, 10, 11] + /// numbers.removeAll(where: { $0 % 2 == 1 }) + /// // numbers == [6, 8, 10] + /// + /// - Parameter shouldBeRemoved: A closure that takes an element of the + /// sequence as its argument and returns a Boolean value indicating + /// whether the element should be removed from the collection. + /// + /// - Complexity: O(*n*), where *n* is the length of the collection. + @inlinable + public mutating func removeAll( + where shouldBeRemoved: (Element) throws -> Bool + ) rethrows { + guard var i = try firstIndex(where: shouldBeRemoved) else { return } + var j = index(after: i) + while j != endIndex { + if try !shouldBeRemoved(self[j]) { + swapAt(i, j) + formIndex(after: &i) + } + formIndex(after: &j) + } + removeSubrange(i...) + } +} + +extension RangeReplaceableCollection { + /// Removes all the elements that satisfy the given predicate. + /// + /// Use this method to remove every element in a collection that meets + /// particular criteria. This example removes all the vowels from a string: + /// + /// var phrase = "The rain in Spain stays mainly in the plain." + /// + /// let vowels: Set = ["a", "e", "i", "o", "u"] + /// phrase.removeAll(where: { vowels.contains($0) }) + /// // phrase == "Th rn n Spn stys mnly n th pln." + /// + /// - Parameter shouldBeRemoved: A closure that takes an element of the + /// sequence as its argument and returns a Boolean value indicating + /// whether the element should be removed from the collection. + /// + /// - Complexity: O(*n*), where *n* is the length of the collection. + @inlinable + public mutating func removeAll( + where shouldBeRemoved: (Element) throws -> Bool + ) rethrows { + // FIXME: Switch to using RRC.filter once stdlib is compiled for 4.0 + // self = try filter { try !predicate($0) } + self = try Self(self.lazy.filter { try !shouldBeRemoved($0) }) + } +} diff --git a/stdlib/public/core/ReflectionMirror.swift b/stdlib/public/core/ReflectionMirror.swift index 489f82bb6f258..a92685d1d2686 100644 --- a/stdlib/public/core/ReflectionMirror.swift +++ b/stdlib/public/core/ReflectionMirror.swift @@ -10,19 +10,16 @@ // //===----------------------------------------------------------------------===// -@inlinable // FIXME(sil-serialize-all) @usableFromInline // FIXME(sil-serialize-all) @_silgen_name("swift_reflectionMirror_normalizedType") internal func _getNormalizedType(_: T, type: Any.Type) -> Any.Type -@inlinable // FIXME(sil-serialize-all) @usableFromInline // FIXME(sil-serialize-all) @_silgen_name("swift_reflectionMirror_count") internal func _getChildCount(_: T, type: Any.Type) -> Int internal typealias NameFreeFunc = @convention(c) (UnsafePointer?) -> Void -@inlinable // FIXME(sil-serialize-all) @usableFromInline // FIXME(sil-serialize-all) @_silgen_name("swift_reflectionMirror_subscript") internal func _getChild( @@ -34,13 +31,11 @@ internal func _getChild( ) -> Any // Returns 'c' (class), 'e' (enum), 's' (struct), 't' (tuple), or '\0' (none) -@inlinable // FIXME(sil-serialize-all) @usableFromInline // FIXME(sil-serialize-all) @_silgen_name("swift_reflectionMirror_displayStyle") internal func _getDisplayStyle(_: T) -> CChar @inlinable // FIXME(sil-serialize-all) -@usableFromInline // FIXME(sil-serialize-all) internal func getChild(of value: T, type: Any.Type, index: Int) -> (label: String?, value: Any) { var nameC: UnsafePointer? = nil var freeFunc: NameFreeFunc? = nil @@ -53,24 +48,20 @@ internal func getChild(of value: T, type: Any.Type, index: Int) -> (label: St } #if _runtime(_ObjC) -@inlinable // FIXME(sil-serialize-all) @usableFromInline // FIXME(sil-serialize-all) @_silgen_name("swift_reflectionMirror_quickLookObject") internal func _getQuickLookObject(_: T) -> AnyObject? -@inlinable // FIXME(sil-serialize-all) @usableFromInline // FIXME(sil-serialize-all) @_silgen_name("_swift_stdlib_NSObject_isKindOfClass") internal func _isImpl(_ object: AnyObject, kindOf: AnyObject) -> Bool @inlinable // FIXME(sil-serialize-all) -@usableFromInline // FIXME(sil-serialize-all) internal func _is(_ object: AnyObject, kindOf `class`: String) -> Bool { return _isImpl(object, kindOf: `class` as AnyObject) } @inlinable // FIXME(sil-serialize-all) -@usableFromInline // FIXME(sil-serialize-all) internal func _getClassPlaygroundQuickLook( _ object: AnyObject ) -> PlaygroundQuickLook? { @@ -121,7 +112,6 @@ internal func _getClassPlaygroundQuickLook( extension Mirror { @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal init(internalReflecting subject: Any, subjectType: Any.Type? = nil, customAncestor: Mirror? = nil) @@ -170,7 +160,6 @@ extension Mirror { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal static func quickLookObject(_ subject: Any) -> PlaygroundQuickLook? { #if _runtime(_ObjC) let object = _getQuickLookObject(subject) diff --git a/stdlib/public/core/Repeat.swift b/stdlib/public/core/Repeat.swift index 07875b21fdc94..33948cec2e7e2 100644 --- a/stdlib/public/core/Repeat.swift +++ b/stdlib/public/core/Repeat.swift @@ -45,7 +45,6 @@ extension Repeated: RandomAccessCollection { /// Creates an instance that contains `count` elements having the /// value `repeatedValue`. - @usableFromInline @inlinable internal init(_repeating repeatedValue: Element, count: Int) { _precondition(count >= 0, "Repetition count should be non-negative") diff --git a/stdlib/public/core/Reverse.swift b/stdlib/public/core/Reverse.swift index fa5db9589f665..ae7f315bed08f 100644 --- a/stdlib/public/core/Reverse.swift +++ b/stdlib/public/core/Reverse.swift @@ -49,8 +49,6 @@ extension MutableCollection where Self: BidirectionalCollection { /// * `c.reversed()` does not create new storage /// * `c.reversed().map(f)` maps eagerly and returns a new array /// * `c.lazy.reversed().map(f)` maps lazily and returns a `LazyMapCollection` -/// -/// - See also: `ReversedRandomAccessCollection` @_fixed_layout public struct ReversedCollection { public let _base: Base @@ -59,7 +57,6 @@ public struct ReversedCollection { /// reverse order. /// /// - Complexity: O(1) - @usableFromInline @inlinable internal init(_base: Base) { self._base = _base @@ -85,7 +82,7 @@ extension ReversedCollection { } } } - + extension ReversedCollection.Iterator: IteratorProtocol, Sequence { public typealias Element = Base.Element @@ -132,7 +129,7 @@ extension ReversedCollection { /// /// func indexOfLastEven(_ numbers: [Int]) -> Int? { /// let reversedNumbers = numbers.reversed() - /// guard let i = reversedNumbers.index(where: { $0 % 2 == 0 }) + /// guard let i = reversedNumbers.firstIndex(where: { $0 % 2 == 0 }) /// else { return nil } /// /// return numbers.index(before: i.base) @@ -155,7 +152,7 @@ extension ReversedCollection { /// `"a"` character in a string's character view. /// /// let name = "Horatio" - /// let aIndex = name.index(of: "a")! + /// let aIndex = name.firstIndex(of: "a")! /// // name[aIndex] == "a" /// /// let reversedName = name.reversed() @@ -194,14 +191,14 @@ extension ReversedCollection.Index: Comparable { } extension ReversedCollection.Index: Hashable where Base.Index: Hashable { + /// Hashes the essential components of this value by feeding them into the + /// given hasher. + /// + /// - Parameter hasher: The hasher to use when combining the components + /// of this instance. @inlinable // FIXME(sil-serialize-all) - public var hashValue: Int { - return base.hashValue - } - - @inlinable // FIXME(sil-serialize-all) - public func _hash(into hasher: inout _Hasher) { - hasher.append(base) + public func hash(into hasher: inout Hasher) { + hasher.combine(base) } } @@ -254,6 +251,17 @@ extension ReversedCollection: BidirectionalCollection { extension ReversedCollection: RandomAccessCollection where Base: RandomAccessCollection { } +extension ReversedCollection { + /// Reversing a reversed collection returns the original collection. + /// + /// - Complexity: O(1) + @inlinable + @available(swift, introduced: 4.2) + public func reversed() -> Base { + return _base + } +} + extension BidirectionalCollection { /// Returns a view presenting the elements of the collection in reverse /// order. diff --git a/stdlib/public/core/Runtime.swift.gyb b/stdlib/public/core/Runtime.swift.gyb index 56b963707beb3..ebfffa8f0d43d 100644 --- a/stdlib/public/core/Runtime.swift.gyb +++ b/stdlib/public/core/Runtime.swift.gyb @@ -146,7 +146,6 @@ func _swift_stdlib_atomicStoreUInt${bits}( } @inlinable // FIXME(sil-serialize-all) -@usableFromInline // FIXME(sil-serialize-all) internal func _swift_stdlib_atomicStoreInt${bits}( object target: UnsafeMutablePointer, desired: Int${bits}) { @@ -164,7 +163,6 @@ func _swift_stdlib_atomicLoadUInt${bits}( } @inlinable // FIXME(sil-serialize-all) -@usableFromInline // FIXME(sil-serialize-all) internal func _swift_stdlib_atomicLoadInt${bits}( object target: UnsafeMutablePointer) -> Int${bits} { @@ -189,7 +187,6 @@ func _swift_stdlib_atomicFetch${operation}UInt${bits}( // Warning: no overflow checking. @inlinable // FIXME(sil-serialize-all) -@usableFromInline // FIXME(sil-serialize-all) internal func _swift_stdlib_atomicFetch${operation}Int${bits}( object target: UnsafeMutablePointer, operand: Int${bits}) -> Int${bits} { @@ -204,7 +201,6 @@ internal func _swift_stdlib_atomicFetch${operation}Int${bits}( % end @inlinable // FIXME(sil-serialize-all) -@usableFromInline // FIXME(sil-serialize-all) internal func _stdlib_atomicCompareExchangeStrongInt( object target: UnsafeMutablePointer, expected: UnsafeMutablePointer, @@ -221,7 +217,6 @@ internal func _stdlib_atomicCompareExchangeStrongInt( } @inlinable // FIXME(sil-serialize-all) -@usableFromInline // FIXME(sil-serialize-all) internal func _swift_stdlib_atomicStoreInt( object target: UnsafeMutablePointer, desired: Int) { @@ -284,7 +279,6 @@ public final class _stdlib_AtomicInt { internal var _value: Int @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var _valuePtr: UnsafeMutablePointer { return _getUnsafePointerToStoredProperties(self).assumingMemoryBound( to: Int.self) @@ -344,7 +338,6 @@ public final class _stdlib_AtomicInt { @usableFromInline // FIXME(sil-serialize-all) internal struct _Buffer32 { @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init() {} % for i in range(32): @usableFromInline // FIXME(sil-serialize-all) @@ -352,7 +345,6 @@ internal struct _Buffer32 { % end @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal mutating func withBytes( _ body: (UnsafeMutablePointer) throws -> Result ) rethrows -> Result @@ -368,7 +360,6 @@ internal struct _Buffer32 { @usableFromInline // FIXME(sil-serialize-all) internal struct _Buffer72 { @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init() {} % for i in range(72): @usableFromInline // FIXME(sil-serialize-all) @@ -376,7 +367,6 @@ internal struct _Buffer72 { % end @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal mutating func withBytes( _ body: (UnsafeMutablePointer) throws -> Result ) rethrows -> Result @@ -393,7 +383,6 @@ internal struct _Buffer72 { #if !os(Windows) && (arch(i386) || arch(x86_64)) % end -@inlinable // FIXME(sil-serialize-all) @usableFromInline // FIXME(sil-serialize-all) @_silgen_name("swift_float${bits}ToString") internal func _float${bits}ToStringImpl( @@ -403,7 +392,6 @@ internal func _float${bits}ToStringImpl( ) -> UInt @inlinable // FIXME(sil-serialize-all) -@usableFromInline // FIXME(sil-serialize-all) internal func _float${bits}ToString( _ value: Float${bits}, debug: Bool ) -> String { @@ -422,7 +410,6 @@ internal func _float${bits}ToString( % end -@inlinable // FIXME(sil-serialize-all) @usableFromInline // FIXME(sil-serialize-all) @_silgen_name("swift_int64ToString") internal func _int64ToStringImpl( @@ -432,7 +419,6 @@ internal func _int64ToStringImpl( ) -> UInt @inlinable // FIXME(sil-serialize-all) -@usableFromInline // FIXME(sil-serialize-all) internal func _int64ToString( _ value: Int64, radix: Int64 = 10, uppercase: Bool = false ) -> String { @@ -455,7 +441,6 @@ internal func _int64ToString( } } -@inlinable // FIXME(sil-serialize-all) @usableFromInline // FIXME(sil-serialize-all) @_silgen_name("swift_uint64ToString") internal func _uint64ToStringImpl( @@ -488,7 +473,6 @@ func _uint64ToString( } @inlinable -@usableFromInline internal func _rawPointerToString(_ value: Builtin.RawPointer) -> String { var result = _uint64ToString( UInt64( @@ -515,11 +499,9 @@ internal func _rawPointerToString(_ value: Builtin.RawPointer) -> String { @objc @_swift_native_objc_runtime_base(_SwiftNativeNSArrayBase) internal class _SwiftNativeNSArray { @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @nonobjc internal init() {} @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) deinit {} } @@ -528,11 +510,9 @@ internal class _SwiftNativeNSArray { @objc @_swift_native_objc_runtime_base(_SwiftNativeNSDictionaryBase) internal class _SwiftNativeNSDictionary { @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @nonobjc internal init() {} @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) deinit {} } @@ -541,11 +521,9 @@ internal class _SwiftNativeNSDictionary { @objc @_swift_native_objc_runtime_base(_SwiftNativeNSSetBase) internal class _SwiftNativeNSSet { @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @nonobjc internal init() {} @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) deinit {} } @@ -554,11 +532,9 @@ internal class _SwiftNativeNSSet { @objc @_swift_native_objc_runtime_base(_SwiftNativeNSEnumeratorBase) internal class _SwiftNativeNSEnumerator { @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @nonobjc internal init() {} @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) deinit {} } @@ -581,12 +557,10 @@ open class _SwiftNativeNSData { @objc internal class _stdlib_ReturnAutoreleasedDummy { @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @objc internal init() {} @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) deinit {} // Use 'dynamic' to force Objective-C dispatch, which uses the @@ -630,30 +604,24 @@ public func _stdlib_initializeReturnAutoreleased() { @usableFromInline // FIXME(sil-serialize-all) internal class _SwiftNativeNSArray { @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init() {} @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) deinit {} } @_fixed_layout // FIXME(sil-serialize-all) @usableFromInline // FIXME(sil-serialize-all) internal class _SwiftNativeNSDictionary { @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init() {} @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) deinit {} } @_fixed_layout // FIXME(sil-serialize-all) @usableFromInline // FIXME(sil-serialize-all) internal class _SwiftNativeNSSet { @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init() {} @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) deinit {} } diff --git a/stdlib/public/core/SentinelCollection.swift b/stdlib/public/core/SentinelCollection.swift index 6efff9a8ffe1d..65b9bbbef6724 100644 --- a/stdlib/public/core/SentinelCollection.swift +++ b/stdlib/public/core/SentinelCollection.swift @@ -33,14 +33,12 @@ where IsSentinel.Input == Base.Element { internal var _expired: Bool = false @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_ base: Base, until condition: IsSentinel) { _base = base _isSentinel = condition } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal mutating func next() -> Base.Element? { guard _fastPath(!_expired) else { return nil } let x = _base.next() @@ -65,7 +63,6 @@ where IsSentinel.Input == Base.Iterator.Element { internal var _base : Base @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func makeIterator() -> _SentinelIterator { return _SentinelIterator(_base.makeIterator(), until: _isSentinel) } @@ -74,7 +71,6 @@ where IsSentinel.Input == Base.Iterator.Element { @usableFromInline // FIXME(sil-serialize-all) internal struct Index : Comparable { @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init( _impl: (position: Base.Index, element: Base.Iterator.Element)? ) { @@ -85,14 +81,12 @@ where IsSentinel.Input == Base.Iterator.Element { internal var _impl: (position: Base.Index, element: Base.Iterator.Element)? @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static func == (lhs: Index, rhs: Index) -> Bool { if rhs._impl == nil { return lhs._impl == nil } return lhs._impl != nil && rhs._impl!.position == lhs._impl!.position } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static func < (lhs: Index, rhs: Index) -> Bool { if rhs._impl == nil { return lhs._impl != nil } return lhs._impl != nil && rhs._impl!.position < lhs._impl!.position @@ -100,31 +94,26 @@ where IsSentinel.Input == Base.Iterator.Element { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var startIndex : Index { return _index(at: _base.startIndex) } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var endIndex : Index { return Index(_impl: nil) } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal subscript(i: Index) -> Base.Iterator.Element { return i._impl!.element } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func index(after i: Index) -> Index { return _index(at: _base.index(after: i._impl!.position)) } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func _index(at i: Base.Index) -> Index { // We don't need this check if it's a precondition that the sentinel will be // found @@ -135,7 +124,6 @@ where IsSentinel.Input == Base.Iterator.Element { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_ base: Base, until condition: IsSentinel) { _base = base _isSentinel = condition @@ -146,11 +134,9 @@ where IsSentinel.Input == Base.Iterator.Element { @usableFromInline // FIXME(sil-serialize-all) internal struct _IsZero : _Predicate { @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init() {} @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func apply(_ x: T) -> Bool { return x == 0 } diff --git a/stdlib/public/core/Sequence.swift b/stdlib/public/core/Sequence.swift index ebac78a32bd6d..457979638ab63 100644 --- a/stdlib/public/core/Sequence.swift +++ b/stdlib/public/core/Sequence.swift @@ -649,7 +649,6 @@ internal struct _DropFirstSequence @usableFromInline internal var _dropped: Int - @usableFromInline @inlinable internal init(_iterator: Base, limit: Int, dropped: Int = 0) { self._iterator = _iterator @@ -657,13 +656,11 @@ internal struct _DropFirstSequence self._dropped = dropped } - @usableFromInline @inlinable internal func makeIterator() -> _DropFirstSequence { return self } - @usableFromInline @inlinable internal mutating func next() -> Base.Element? { while _dropped < _limit { @@ -676,7 +673,6 @@ internal struct _DropFirstSequence return _iterator.next() } - @usableFromInline @inlinable internal func dropFirst(_ n: Int) -> AnySequence { // If this is already a _DropFirstSequence, we need to fold in @@ -705,7 +701,6 @@ internal struct _PrefixSequence @usableFromInline internal var _taken: Int - @usableFromInline @inlinable internal init(_iterator: Base, maxLength: Int, taken: Int = 0) { self._iterator = _iterator @@ -713,13 +708,11 @@ internal struct _PrefixSequence self._taken = taken } - @usableFromInline @inlinable internal func makeIterator() -> _PrefixSequence { return self } - @usableFromInline @inlinable internal mutating func next() -> Base.Element? { if _taken >= _maxLength { return nil } @@ -733,7 +726,6 @@ internal struct _PrefixSequence return nil } - @usableFromInline @inlinable internal func prefix(_ maxLength: Int) -> AnySequence { return AnySequence( @@ -760,7 +752,6 @@ internal struct _DropWhileSequence @usableFromInline internal var _nextElement: Base.Element? - @usableFromInline @inlinable internal init( iterator: Base, @@ -775,13 +766,11 @@ internal struct _DropWhileSequence } } - @usableFromInline @inlinable internal func makeIterator() -> _DropWhileSequence { return self } - @usableFromInline @inlinable internal mutating func next() -> Element? { guard _nextElement != nil else { @@ -793,7 +782,6 @@ internal struct _DropWhileSequence return next } - @usableFromInline @inlinable internal func drop( while predicate: (Element) throws -> Bool diff --git a/stdlib/public/core/SequenceAlgorithms.swift b/stdlib/public/core/SequenceAlgorithms.swift index 9c9b98be04545..9850b26913f51 100644 --- a/stdlib/public/core/SequenceAlgorithms.swift +++ b/stdlib/public/core/SequenceAlgorithms.swift @@ -488,6 +488,21 @@ extension Sequence { } return false } + + /// Returns a Boolean value indicating whether every element of a sequence + /// satisfies a given predicate. + /// + /// - Parameter predicate: A closure that takes an element of the sequence + /// as its argument and returns a Boolean value that indicates whether + /// the passed element satisfies a condition. + /// - Returns: `true` if the sequence contains only elements that satisfy + /// `predicate`; otherwise, `false`. + @inlinable + public func allSatisfy( + _ predicate: (Element) throws -> Bool + ) rethrows -> Bool { + return try !contains { try !predicate($0) } + } } extension Sequence where Element : Equatable { @@ -675,10 +690,10 @@ extension Sequence { /// /// let numbers = [1, 2, 3, 4] /// - /// let mapped = numbers.map { Array(count: $0, repeatedValue: $0) } + /// let mapped = numbers.map { Array(repeating: $0, count: $0) } /// // [[1], [2, 2], [3, 3, 3], [4, 4, 4, 4]] /// - /// let flatMapped = numbers.flatMap { Array(count: $0, repeatedValue: $0) } + /// let flatMapped = numbers.flatMap { Array(repeating: $0, count: $0) } /// // [1, 2, 2, 3, 3, 3, 4, 4, 4, 4] /// /// In fact, `s.flatMap(transform)` is equivalent to diff --git a/stdlib/public/core/Set.swift b/stdlib/public/core/Set.swift index e230127d20a2e..1cbbadcd78f1a 100644 --- a/stdlib/public/core/Set.swift +++ b/stdlib/public/core/Set.swift @@ -160,17 +160,16 @@ public struct Set { } extension Set { - /// Creates a new, empty set with at least the specified number of elements' - /// worth of buffer. + /// Creates an empty set with preallocated space for at least the specified + /// number of elements. /// - /// Use this initializer to avoid repeated reallocations of a set's buffer - /// if you know you'll be adding elements to the set after creation. The - /// actual capacity of the created set will be the smallest power of 2 that - /// is greater than or equal to `minimumCapacity`. + /// Use this initializer to avoid intermediate reallocations of a set's + /// storage buffer when you know how many elements you'll insert into the set + /// after creation. /// /// - Parameter minimumCapacity: The minimum number of elements that the /// newly created set should be able to store without reallocating its - /// buffer. + /// storage buffer. @inlinable // FIXME(sil-serialize-all) public init(minimumCapacity: Int) { _variantBuffer = @@ -180,7 +179,6 @@ extension Set { /// Private initializer. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_nativeBuffer: _NativeSetBuffer) { _variantBuffer = _VariantBuffer.native(_nativeBuffer) } @@ -351,7 +349,7 @@ extension Set: Collection { /// /// - Complexity: O(1) @inlinable // FIXME(sil-serialize-all) - public func index(of member: Element) -> Index? { + public func firstIndex(of member: Element) -> Index? { return _variantBuffer.index(forKey: member) } @@ -359,7 +357,15 @@ extension Set: Collection { public func _customIndexOfEquatableElement( _ member: Element ) -> Index?? { - return Optional(index(of: member)) + return Optional(firstIndex(of: member)) + } + + @inlinable // FIXME(sil-serialize-all) + public func _customLastIndexOfEquatableElement( + _ member: Element + ) -> Index?? { + // The first and last elements are the same because each element is unique. + return _customIndexOfEquatableElement(member) } /// The number of elements in the set. @@ -393,7 +399,6 @@ extension Set: Collection { /// /// (isSubset: lhs ⊂ rhs, isEqual: lhs ⊂ rhs and |lhs| = |rhs|) @inlinable -@usableFromInline internal func _compareSets(_ lhs: Set, _ rhs: Set) -> (isSubset: Bool, isEqual: Bool) { // FIXME(performance): performance could be better if we start by comparing @@ -479,25 +484,20 @@ extension Set: Equatable { } extension Set: Hashable { - /// The hash value for the set. - /// - /// Two sets that are equal will always have equal hash values. + /// Hashes the essential components of this value by feeding them into the + /// given hasher. /// - /// Hash values are not guaranteed to be equal across different executions of - /// your program. Do not save hash values to use during a future execution. + /// - Parameter hasher: The hasher to use when combining the components + /// of this instance. @inlinable // FIXME(sil-serialize-all) - public var hashValue: Int { + public func hash(into hasher: inout Hasher) { // FIXME(ABI)#177: Cache Set hashValue - return _hashValue(for: self) - } - - @inlinable // FIXME(sil-serialize-all) - public func _hash(into hasher: inout _Hasher) { var hash = 0 + let seed = hasher._generateSeed() for member in self { - hash ^= _hashValue(for: member) + hash ^= member._rawHashValue(seed: seed) } - hasher.append(hash) + hasher.combine(hash) } } @@ -891,7 +891,6 @@ extension Set: SetAlgebra { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal func _subtracting(_ other: S) -> Set where S.Element == Element { var newSet = self @@ -919,7 +918,6 @@ extension Set: SetAlgebra { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal mutating func _subtract(_ other: S) where S.Element == Element { for item in other { @@ -1046,7 +1044,6 @@ extension Set: CustomStringConvertible, CustomDebugStringConvertible { #if _runtime(_ObjC) @_silgen_name("swift_stdlib_CFSetGetValues") -@inlinable // FIXME(sil-serialize-all) @usableFromInline // FIXME(sil-serialize-all) internal func _stdlib_CFSetGetValues(_ nss: _NSSet, _: UnsafeMutablePointer) @@ -1054,7 +1051,6 @@ func _stdlib_CFSetGetValues(_ nss: _NSSet, _: UnsafeMutablePointer) /// Equivalent to `NSSet.allObjects`, but does not leave objects on the /// autorelease pool. @inlinable // FIXME(sil-serialize-all) -@usableFromInline // FIXME(sil-serialize-all) internal func _stdlib_NSSet_allObjects(_ nss: _NSSet) -> _HeapBuffer { let count = nss.count @@ -1483,7 +1479,6 @@ internal class _RawNativeSetStorage: // This API is unsafe and needs a `_fixLifetime` in the caller. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @nonobjc internal final var _initializedHashtableEntriesBitMapBuffer: UnsafeMutablePointer { @@ -1494,7 +1489,6 @@ internal class _RawNativeSetStorage: /// created without any elements. The contents of the storage should never /// be mutated. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @nonobjc internal static var empty: RawStorage { return Builtin.bridgeFromRawPointer( @@ -1504,7 +1498,6 @@ internal class _RawNativeSetStorage: // This type is made with allocWithTailElems, so no init is ever called. // But we still need to have an init to satisfy the compiler. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @nonobjc internal init(_doNotCallMe: ()) { _sanityCheckFailure("Only create this by using the `empty` singleton") @@ -1519,7 +1512,6 @@ internal class _RawNativeSetStorage: /// _HashableTypedNativeSetStorage overloads this to give /// _NativeSelfNSEnumerator proper type parameters. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @objc internal func enumerator() -> _NSEnumerator { return _NativeSetNSEnumerator( @@ -1527,14 +1519,12 @@ internal class _RawNativeSetStorage: } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @objc(copyWithZone:) internal func copy(with zone: _SwiftNSZone?) -> AnyObject { return self } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @objc(countByEnumeratingWithState:objects:count:) internal func countByEnumerating( with state: UnsafeMutablePointer<_SwiftNSFastEnumerationState>, @@ -1555,21 +1545,18 @@ internal class _RawNativeSetStorage: } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @objc internal required init(objects: UnsafePointer, count: Int) { _sanityCheckFailure("don't call this designated initializer") } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @objc internal func member(_ object: AnyObject) -> AnyObject? { return nil } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @objc internal func objectEnumerator() -> _NSEnumerator { return enumerator() @@ -1602,7 +1589,6 @@ internal class _TypedNativeSetStorage: _RawNativeSetStorage { // This type is made with allocWithTailElems, so no init is ever called. // But we still need to have an init to satisfy the compiler. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @nonobjc override internal init(_doNotCallMe: ()) { _sanityCheckFailure("Only create this by calling Buffer's inits") @@ -1610,7 +1596,6 @@ internal class _TypedNativeSetStorage: _RawNativeSetStorage { #if _runtime(_ObjC) @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @objc internal required init(objects: UnsafePointer, count: Int) { _sanityCheckFailure("don't call this designated initializer") @@ -1630,7 +1615,6 @@ final internal class _HashableTypedNativeSetStorage // This type is made with allocWithTailElems, so no init is ever called. // But we still need to have an init to satisfy the compiler. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @nonobjc override internal init(_doNotCallMe: ()) { _sanityCheckFailure("Only create this by calling Buffer's inits'") @@ -1643,26 +1627,22 @@ final internal class _HashableTypedNativeSetStorage // just wrappers around a RawNativeSetStorage. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var buffer: Buffer { return Buffer(_storage: self) } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var full: FullContainer { return FullContainer(_nativeBuffer: buffer) } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal override func enumerator() -> _NSEnumerator { return _NativeSetNSEnumerator( Buffer(_storage: self)) } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @objc(countByEnumeratingWithState:objects:count:) internal override func countByEnumerating( with state: UnsafeMutablePointer<_SwiftNSFastEnumerationState>, @@ -1704,7 +1684,6 @@ final internal class _HashableTypedNativeSetStorage } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @nonobjc internal func getObjectFor(_ aKey: AnyObject) -> AnyObject? { guard let nativeKey = _conditionallyBridgeFromObjectiveC(aKey, Key.self) @@ -1720,14 +1699,12 @@ final internal class _HashableTypedNativeSetStorage } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @objc internal required init(objects: UnsafePointer, count: Int) { _sanityCheckFailure("don't call this designated initializer") } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @objc override internal func member(_ object: AnyObject) -> AnyObject? { return getObjectFor(object) @@ -1763,7 +1740,6 @@ internal struct _NativeSetBuffer { /// Creates a Buffer with a storage that is typed, but doesn't understand /// Hashing. Mostly for bridging; prefer `init(minimumCapacity:)`. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_exactBucketCount bucketCount: Int, unhashable: ()) { let bitmapWordCount = _UnsafeBitMap.sizeInWords(forSizeInBits: bucketCount) let storage = Builtin.allocWithTailElems_2(TypedStorage.self, @@ -1775,7 +1751,6 @@ internal struct _NativeSetBuffer { /// Given a bucket count and uninitialized RawStorage, completes the /// initialization and returns a Buffer. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_exactBucketCount bucketCount: Int, storage: RawStorage) { storage.bucketCount = bucketCount storage.count = 0 @@ -1806,7 +1781,7 @@ internal struct _NativeSetBuffer { // // FIXME: Use an approximation of true per-instance seeding. We can't just // use the base address, because COW copies need to share the same seed. - let seed = _Hasher._seed + let seed = Hasher._seed let perturbation = bucketCount _storage.seed = (seed.0 ^ UInt64(truncatingIfNeeded: perturbation), seed.1) } @@ -1814,13 +1789,11 @@ internal struct _NativeSetBuffer { // Forwarding the individual fields of the storage in various forms @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal var bucketCount: Int { return _assumeNonNegative(_storage.bucketCount) } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal var count: Int { set { _storage.count = newValue @@ -1831,7 +1804,6 @@ internal struct _NativeSetBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var _initializedHashtableEntriesBitMapBuffer: UnsafeMutablePointer { return _storage._initializedHashtableEntriesBitMapBuffer @@ -1839,21 +1811,18 @@ internal struct _NativeSetBuffer { // This API is unsafe and needs a `_fixLifetime` in the caller. @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal var keys: UnsafeMutablePointer { return _storage.keys.assumingMemoryBound(to: Key.self) } /// Constructs a buffer adopting the given storage. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_storage: RawStorage) { self._storage = _storage } /// Constructs an instance from the empty singleton. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init() { self._storage = RawStorage.empty } @@ -1862,7 +1831,6 @@ internal struct _NativeSetBuffer { // but only the parts that don't actually rely on hashing. @inlinable // FIXME(sil-serialize-all) - @usableFromInline @inline(__always) internal func key(at i: Int) -> Key { _sanityCheck(i >= 0 && i < bucketCount) @@ -1878,7 +1846,6 @@ internal struct _NativeSetBuffer { /// /// Intended for use with verbatim bridgeable keys. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func bridgedKey(at index: Index) -> AnyObject { let k = key(at: index.offset) return _bridgeAnythingToObjectiveC(k) @@ -1888,7 +1855,6 @@ internal struct _NativeSetBuffer { /// /// Intended for use with verbatim bridgeable keys. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func bridgedValue(at index: Index) -> AnyObject { let v = value(at: index.offset) return _bridgeAnythingToObjectiveC(v) @@ -1896,7 +1862,6 @@ internal struct _NativeSetBuffer { #endif @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal func isInitializedEntry(at i: Int) -> Bool { _sanityCheck(i >= 0 && i < bucketCount) defer { _fixLifetime(self) } @@ -1905,7 +1870,6 @@ internal struct _NativeSetBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @_transparent internal func destroyEntry(at i: Int) { _sanityCheck(isInitializedEntry(at: i)) @@ -1916,7 +1880,6 @@ internal struct _NativeSetBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @_transparent internal func initializeKey(_ k: Key, at i: Int) { _sanityCheck(!isInitializedEntry(at: i)) @@ -1927,7 +1890,6 @@ internal struct _NativeSetBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @_transparent internal func moveInitializeEntry(from: Buffer, at: Int, toEntryAt: Int) { _sanityCheck(!isInitializedEntry(at: toEntryAt)) @@ -1941,14 +1903,12 @@ internal struct _NativeSetBuffer { /// Alias for key(at:) in Sets for better code reuse @inlinable // FIXME(sil-serialize-all) - @usableFromInline @_transparent internal func value(at i: Int) -> Value { return key(at: i) } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func setKey(_ key: Key, at i: Int) { _sanityCheck(i >= 0 && i < bucketCount) _sanityCheck(isInitializedEntry(at: i)) @@ -1958,7 +1918,6 @@ internal struct _NativeSetBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal var startIndex: Index { // We start at "index after -1" instead of "0" because we need to find the // first occupied slot. @@ -1966,13 +1925,11 @@ internal struct _NativeSetBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal var endIndex: Index { return Index(offset: bucketCount) } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal func index(after i: Index) -> Index { _precondition(i != endIndex) var idx = i.offset + 1 @@ -1984,13 +1941,11 @@ internal struct _NativeSetBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal func formIndex(after i: inout Index) { i = index(after: i) } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func assertingGet(_ i: Index) -> SequenceElement { _precondition(i.offset >= 0 && i.offset < bucketCount) _precondition( @@ -2009,7 +1964,6 @@ extension _NativeSetBuffer where Element: Hashable internal typealias SequenceElement = Element @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @inline(__always) internal init(minimumCapacity: Int) { let bucketCount = _NativeSetBuffer.bucketCount( @@ -2019,7 +1973,6 @@ extension _NativeSetBuffer where Element: Hashable } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @inline(__always) internal init(bucketCount: Int) { // Actual bucket count is the next power of 2 greater than or equal to @@ -2032,7 +1985,6 @@ extension _NativeSetBuffer where Element: Hashable /// Create a buffer instance with room for at least 'bucketCount' entries, /// marking all entries invalid. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_exactBucketCount bucketCount: Int) { let bitmapWordCount = _UnsafeBitMap.sizeInWords(forSizeInBits: bucketCount) let storage = Builtin.allocWithTailElems_2(HashTypedStorage.self, @@ -2043,7 +1995,6 @@ extension _NativeSetBuffer where Element: Hashable #if _runtime(_ObjC) @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func bridged() -> _NSSet { // We can zero-cost bridge if our keys are verbatim // or if we're the empty singleton. @@ -2068,7 +2019,6 @@ extension _NativeSetBuffer where Element: Hashable /// A textual representation of `self`. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var description: String { var result = "" #if INTERNAL_CHECKS_ENABLED @@ -2085,7 +2035,6 @@ extension _NativeSetBuffer where Element: Hashable } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var _bucketMask: Int { // The bucket count is not negative, therefore subtracting 1 will not // overflow. @@ -2093,23 +2042,18 @@ extension _NativeSetBuffer where Element: Hashable } @inlinable // FIXME(sil-serialize-all) - @usableFromInline @inline(__always) // For performance reasons. internal func _bucket(_ k: Key) -> Int { - var hasher = _Hasher(seed: _storage.seed) - hasher.append(k) - return hasher.finalize() & _bucketMask + return k._rawHashValue(seed: _storage.seed) & _bucketMask } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal func _index(after bucket: Int) -> Int { // Bucket is within 0 and bucketCount. Therefore adding 1 does not overflow. return (bucket &+ 1) & _bucketMask } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func _prev(_ bucket: Int) -> Int { // Bucket is not negative. Therefore subtracting 1 does not overflow. return (bucket &- 1) & _bucketMask @@ -2120,7 +2064,6 @@ extension _NativeSetBuffer where Element: Hashable /// If the key is not present, returns the position where it could be /// inserted. @inlinable // FIXME(sil-serialize-all) - @usableFromInline @inline(__always) internal func _find(_ key: Key, startBucket: Int) -> (pos: Index, found: Bool) { @@ -2142,7 +2085,6 @@ extension _NativeSetBuffer where Element: Hashable } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @_transparent internal static func bucketCount( forCapacity capacity: Int, @@ -2158,7 +2100,6 @@ extension _NativeSetBuffer where Element: Hashable /// This function does *not* update `count`. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func unsafeAddNew(key newKey: Element) { let (i, found) = _find(newKey, startBucket: _bucket(newKey)) _precondition( @@ -2171,7 +2112,6 @@ extension _NativeSetBuffer where Element: Hashable // @inlinable // FIXME(sil-serialize-all) - @usableFromInline @inline(__always) internal func index(forKey key: Key) -> Index? { if count == 0 { @@ -2183,7 +2123,6 @@ extension _NativeSetBuffer where Element: Hashable } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func assertingGet(_ key: Key) -> Value { let (i, found) = _find(key, startBucket: _bucket(key)) _precondition(found, "Key not found") @@ -2191,7 +2130,6 @@ extension _NativeSetBuffer where Element: Hashable } @inlinable // FIXME(sil-serialize-all) - @usableFromInline @inline(__always) internal func maybeGet(_ key: Key) -> Value? { if count == 0 { @@ -2207,7 +2145,6 @@ extension _NativeSetBuffer where Element: Hashable } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @discardableResult internal func updateValue(_ value: Value, forKey key: Key) -> Value? { _sanityCheckFailure( @@ -2215,7 +2152,6 @@ extension _NativeSetBuffer where Element: Hashable } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @discardableResult internal func insert( _ value: Value, forKey key: Key @@ -2225,7 +2161,6 @@ extension _NativeSetBuffer where Element: Hashable } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @discardableResult internal func remove(at index: Index) -> SequenceElement { _sanityCheckFailure( @@ -2233,7 +2168,6 @@ extension _NativeSetBuffer where Element: Hashable } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @discardableResult internal func removeValue(forKey key: Key) -> Value? { _sanityCheckFailure( @@ -2241,14 +2175,12 @@ extension _NativeSetBuffer where Element: Hashable } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func removeAll(keepingCapacity keepCapacity: Bool) { _sanityCheckFailure( "don't call mutating methods on _NativeSetBuffer") } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal static func fromArray(_ elements: [SequenceElementWithoutLabels]) -> Buffer { @@ -2286,13 +2218,11 @@ final internal class _NativeSetNSEnumerator internal typealias Index = _NativeSetIndex @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal override required init() { _sanityCheckFailure("don't call this designated initializer") } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_ buffer: Buffer) { self.buffer = buffer nextIndex = buffer.startIndex @@ -2313,7 +2243,6 @@ final internal class _NativeSetNSEnumerator // @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @objc internal func nextObject() -> AnyObject? { if nextIndex == endIndex { @@ -2325,7 +2254,6 @@ final internal class _NativeSetNSEnumerator } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @objc(countByEnumeratingWithState:objects:count:) internal func countByEnumerating( with state: UnsafeMutablePointer<_SwiftNSFastEnumerationState>, @@ -2377,7 +2305,6 @@ final internal class _SwiftDeferredNSSet internal typealias Value = Element @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @nonobjc internal init(bucketCount: Int = 2) { nativeBuffer = NativeBuffer(bucketCount: bucketCount) @@ -2385,7 +2312,6 @@ final internal class _SwiftDeferredNSSet } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(nativeBuffer: NativeBuffer) { self.nativeBuffer = nativeBuffer super.init() @@ -2404,7 +2330,6 @@ final internal class _SwiftDeferredNSSet internal var nativeBuffer: NativeBuffer @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @objc(copyWithZone:) internal func copy(with zone: _SwiftNSZone?) -> AnyObject { // Instances of this class should be visible outside of standard library as @@ -2420,21 +2345,18 @@ final internal class _SwiftDeferredNSSet // @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @objc internal required init(objects: UnsafePointer, count: Int) { _sanityCheckFailure("don't call this designated initializer") } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @objc internal func member(_ object: AnyObject) -> AnyObject? { return bridgingObjectForKey(object) } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @objc internal func objectEnumerator() -> _NSEnumerator { return enumerator() @@ -2443,7 +2365,6 @@ final internal class _SwiftDeferredNSSet /// Returns the pointer to the stored property, which contains bridged /// Set elements. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @nonobjc internal var _heapStorageBridgedPtr: UnsafeMutablePointer { return _getUnsafePointerToStoredProperties(self).assumingMemoryBound( @@ -2452,7 +2373,6 @@ final internal class _SwiftDeferredNSSet /// The buffer for bridged Set elements, if present. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @nonobjc internal var _bridgedStorage: BridgedBuffer.RawStorage? { @@ -2466,7 +2386,6 @@ final internal class _SwiftDeferredNSSet /// Attach a buffer for bridged Set elements. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @nonobjc internal func _initializeHeapStorageBridged(_ newStorage: AnyObject) { _stdlib_atomicInitializeARCRef( @@ -2475,13 +2394,11 @@ final internal class _SwiftDeferredNSSet /// Returns the bridged Set values. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var bridgedBuffer: BridgedBuffer { return BridgedBuffer(_storage: _bridgedStorage!) } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @nonobjc internal func bridgeEverything() { if _fastPath(_bridgedStorage != nil) { @@ -2511,14 +2428,12 @@ final internal class _SwiftDeferredNSSet } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @objc internal var count: Int { return nativeBuffer.count } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @nonobjc internal func bridgingObjectForKey(_ aKey: AnyObject) -> AnyObject? { @@ -2535,7 +2450,6 @@ final internal class _SwiftDeferredNSSet } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @objc internal func enumerator() -> _NSEnumerator { bridgeEverything() @@ -2543,7 +2457,6 @@ final internal class _SwiftDeferredNSSet } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @objc(countByEnumeratingWithState:objects:count:) internal func countByEnumerating( with state: UnsafeMutablePointer<_SwiftNSFastEnumerationState>, @@ -2605,7 +2518,6 @@ internal struct _CocoaSetBuffer: _HashBuffer { internal var cocoaSet: _NSSet @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(cocoaSet: _NSSet) { self.cocoaSet = cocoaSet } @@ -2618,32 +2530,27 @@ internal struct _CocoaSetBuffer: _HashBuffer { internal typealias Value = AnyObject @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var startIndex: Index { return Index(cocoaSet, startIndex: ()) } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var endIndex: Index { return Index(cocoaSet, endIndex: ()) } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal func index(after i: Index) -> Index { return i.successor() } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func formIndex(after i: inout Index) { // FIXME: swift-3-indexing-model: optimize if possible. i = i.successor() } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal func index(forKey key: Key) -> Index? { // Fast path that does not involve creating an array of all keys. In case // the key is present, this lookup is a penalty for the slow path, but the @@ -2667,7 +2574,6 @@ internal struct _CocoaSetBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func assertingGet(_ i: Index) -> SequenceElement { let value: Value? = i.allKeys[i.currentKeyIndex] _sanityCheck(value != nil, "Item not found in underlying NSSet") @@ -2676,7 +2582,6 @@ internal struct _CocoaSetBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func assertingGet(_ key: Key) -> Value { let value: Value? = cocoaSet.member(key) _precondition(value != nil, "Member not found in underlying NSSet") @@ -2684,7 +2589,6 @@ internal struct _CocoaSetBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @inline(__always) internal func maybeGet(_ key: Key) -> Value? { @@ -2693,14 +2597,12 @@ internal struct _CocoaSetBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @discardableResult internal mutating func updateValue(_ value: Value, forKey key: Key) -> Value? { _sanityCheckFailure("cannot mutate NSSet") } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @discardableResult internal mutating func insert( _ value: Value, forKey key: Key @@ -2709,33 +2611,28 @@ internal struct _CocoaSetBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @discardableResult internal mutating func remove(at index: Index) -> SequenceElement { _sanityCheckFailure("cannot mutate NSSet") } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @discardableResult internal mutating func removeValue(forKey key: Key) -> Value? { _sanityCheckFailure("cannot mutate NSSet") } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal mutating func removeAll(keepingCapacity keepCapacity: Bool) { _sanityCheckFailure("cannot mutate NSSet") } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var count: Int { return cocoaSet.count } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static func fromArray(_ elements: [SequenceElementWithoutLabels]) -> _CocoaSetBuffer { @@ -2766,14 +2663,12 @@ internal enum _VariantSetBuffer: _HashBuffer { #endif @inlinable // FIXME(sil-serialize-all) - @usableFromInline @_transparent internal var guaranteedNative: Bool { return _canBeClass(Key.self) == 0 || _canBeClass(Value.self) == 0 } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal mutating func isUniquelyReferenced() -> Bool { // Note that &self drills down through .native(NativeBuffer) to the first // property in NativeBuffer, which is the reference to the storage. @@ -2794,7 +2689,6 @@ internal enum _VariantSetBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal var asNative: NativeBuffer { get { switch self { @@ -2812,7 +2706,6 @@ internal enum _VariantSetBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal mutating func ensureNativeBuffer() { #if _runtime(_ObjC) if _fastPath(guaranteedNative) { return } @@ -2824,7 +2717,6 @@ internal enum _VariantSetBuffer: _HashBuffer { #if _runtime(_ObjC) @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var asCocoa: CocoaBuffer { switch self { case .native: @@ -2837,7 +2729,6 @@ internal enum _VariantSetBuffer: _HashBuffer { /// Return true if self is native. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var _isNative: Bool { #if _runtime(_ObjC) switch self { @@ -2853,7 +2744,6 @@ internal enum _VariantSetBuffer: _HashBuffer { @inline(__always) @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal mutating func ensureUniqueNativeBufferNative( withBucketCount desiredBucketCount: Int ) -> (reallocated: Bool, capacityChanged: Bool) { @@ -2885,7 +2775,6 @@ internal enum _VariantSetBuffer: _HashBuffer { @inline(__always) @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal mutating func ensureUniqueNativeBuffer( withCapacity minimumCapacity: Int ) -> (reallocated: Bool, capacityChanged: Bool) { @@ -2898,7 +2787,6 @@ internal enum _VariantSetBuffer: _HashBuffer { /// Ensure this we hold a unique reference to a native buffer /// having at least `minimumCapacity` elements. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal mutating func ensureUniqueNativeBuffer( withBucketCount desiredBucketCount: Int ) -> (reallocated: Bool, capacityChanged: Bool) { @@ -2941,9 +2829,8 @@ internal enum _VariantSetBuffer: _HashBuffer { } #if _runtime(_ObjC) - @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @inline(never) + @usableFromInline internal mutating func migrateDataToNativeBuffer( _ cocoaBuffer: _CocoaSetBuffer ) { @@ -2956,7 +2843,6 @@ internal enum _VariantSetBuffer: _HashBuffer { /// Reserves enough space for the specified number of elements to be stored /// without reallocating additional storage. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal mutating func reserveCapacity(_ capacity: Int) { _ = ensureUniqueNativeBuffer(withCapacity: capacity) } @@ -2969,7 +2855,6 @@ internal enum _VariantSetBuffer: _HashBuffer { /// newly allocated storage. For native storage, this is the element count /// at which adding any more elements will exceed the load factor. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var capacity: Int { switch self { case .native: @@ -2989,7 +2874,6 @@ internal enum _VariantSetBuffer: _HashBuffer { internal typealias Index = SetIndex @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var startIndex: Index { if _fastPath(guaranteedNative) { return ._native(asNative.startIndex) @@ -3006,7 +2890,6 @@ internal enum _VariantSetBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var endIndex: Index { if _fastPath(guaranteedNative) { return ._native(asNative.endIndex) @@ -3023,7 +2906,6 @@ internal enum _VariantSetBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal func index(after i: Index) -> Index { if _fastPath(guaranteedNative) { return ._native(asNative.index(after: i._nativeIndex)) @@ -3040,14 +2922,12 @@ internal enum _VariantSetBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func formIndex(after i: inout Index) { // FIXME: swift-3-indexing-model: optimize if possible. i = index(after: i) } @inlinable // FIXME(sil-serialize-all) - @usableFromInline @inline(__always) internal func index(forKey key: Key) -> Index? { if _fastPath(guaranteedNative) { @@ -3075,7 +2955,6 @@ internal enum _VariantSetBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func assertingGet(_ i: Index) -> SequenceElement { if _fastPath(guaranteedNative) { return asNative.assertingGet(i._nativeIndex) @@ -3094,7 +2973,6 @@ internal enum _VariantSetBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func assertingGet(_ key: Key) -> Value { if _fastPath(guaranteedNative) { return asNative.assertingGet(key) @@ -3114,9 +2992,8 @@ internal enum _VariantSetBuffer: _HashBuffer { } #if _runtime(_ObjC) - @inlinable // FIXME(sil-serialize-all) - @usableFromInline @inline(never) + @usableFromInline internal static func maybeGetFromCocoaBuffer( _ cocoaBuffer: CocoaBuffer, forKey key: Key ) -> Value? { @@ -3129,7 +3006,6 @@ internal enum _VariantSetBuffer: _HashBuffer { #endif @inlinable // FIXME(sil-serialize-all) - @usableFromInline @inline(__always) internal func maybeGet(_ key: Key) -> Value? { if _fastPath(guaranteedNative) { @@ -3147,7 +3023,6 @@ internal enum _VariantSetBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal mutating func nativeUpdateValue( _ value: Value, forKey key: Key ) -> Value? { @@ -3177,7 +3052,6 @@ internal enum _VariantSetBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @discardableResult internal mutating func updateValue( _ value: Value, forKey key: Key @@ -3199,7 +3073,6 @@ internal enum _VariantSetBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal mutating func nativeInsert( _ value: Value, forKey key: Key ) -> (inserted: Bool, memberAfterInsert: Value) { @@ -3224,7 +3097,6 @@ internal enum _VariantSetBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @discardableResult internal mutating func insert( _ value: Value, forKey key: Key @@ -3237,7 +3109,6 @@ internal enum _VariantSetBuffer: _HashBuffer { /// - parameter offset: The offset of the element that will be deleted. /// Precondition: there should be an initialized entry at offset. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal mutating func nativeDelete( _ nativeBuffer: NativeBuffer, idealBucket: Int, offset: Int ) { @@ -3302,7 +3173,6 @@ internal enum _VariantSetBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal mutating func nativeRemoveObject(forKey key: Key) -> Value? { var idealBucket = asNative._bucket(key) var (index, found) = asNative._find(key, startBucket: idealBucket) @@ -3337,7 +3207,6 @@ internal enum _VariantSetBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal mutating func nativeRemove( at nativeIndex: NativeIndex ) -> SequenceElement { @@ -3364,7 +3233,6 @@ internal enum _VariantSetBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @discardableResult internal mutating func remove(at index: Index) -> SequenceElement { if _fastPath(guaranteedNative) { @@ -3394,7 +3262,6 @@ internal enum _VariantSetBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @discardableResult internal mutating func removeValue(forKey key: Key) -> Value? { if _fastPath(guaranteedNative) { @@ -3417,7 +3284,6 @@ internal enum _VariantSetBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal mutating func nativeRemoveAll() { if !isUniquelyReferenced() { asNative = NativeBuffer(_exactBucketCount: asNative.bucketCount) @@ -3437,7 +3303,6 @@ internal enum _VariantSetBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal mutating func removeAll(keepingCapacity keepCapacity: Bool) { if count == 0 { return @@ -3464,7 +3329,6 @@ internal enum _VariantSetBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var count: Int { if _fastPath(guaranteedNative) { return asNative.count @@ -3484,7 +3348,6 @@ internal enum _VariantSetBuffer: _HashBuffer { /// /// - Complexity: O(1). @inlinable // FIXME(sil-serialize-all) - @usableFromInline @inline(__always) internal func makeIterator() -> SetIterator { switch self { @@ -3499,7 +3362,6 @@ internal enum _VariantSetBuffer: _HashBuffer { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static func fromArray(_ elements: [SequenceElement]) -> _VariantSetBuffer { @@ -3514,7 +3376,6 @@ internal struct _NativeSetIndex: Comparable { internal var offset: Int @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal init(offset: Int) { self.offset = offset } @@ -3523,7 +3384,6 @@ internal struct _NativeSetIndex: Comparable { extension _NativeSetIndex { @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static func < ( lhs: _NativeSetIndex, rhs: _NativeSetIndex @@ -3531,7 +3391,6 @@ extension _NativeSetIndex { return lhs.offset < rhs.offset } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static func <= ( lhs: _NativeSetIndex, rhs: _NativeSetIndex @@ -3539,7 +3398,6 @@ extension _NativeSetIndex { return lhs.offset <= rhs.offset } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static func > ( lhs: _NativeSetIndex, rhs: _NativeSetIndex @@ -3547,7 +3405,6 @@ extension _NativeSetIndex { return lhs.offset > rhs.offset } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static func >= ( lhs: _NativeSetIndex, rhs: _NativeSetIndex @@ -3555,7 +3412,6 @@ extension _NativeSetIndex { return lhs.offset >= rhs.offset } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static func == ( lhs: _NativeSetIndex, rhs: _NativeSetIndex @@ -3590,7 +3446,6 @@ internal struct _CocoaSetIndex: Comparable { internal var currentKeyIndex: Int @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_ cocoaSet: _NSSet, startIndex: ()) { self.cocoaSet = cocoaSet self.allKeys = _stdlib_NSSet_allObjects(cocoaSet) @@ -3598,7 +3453,6 @@ internal struct _CocoaSetIndex: Comparable { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_ cocoaSet: _NSSet, endIndex: ()) { self.cocoaSet = cocoaSet self.allKeys = _stdlib_NSSet_allObjects(cocoaSet) @@ -3606,7 +3460,6 @@ internal struct _CocoaSetIndex: Comparable { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_ cocoaSet: _NSSet, _ allKeys: _HeapBuffer, _ currentKeyIndex: Int @@ -3620,7 +3473,6 @@ internal struct _CocoaSetIndex: Comparable { /// /// - Precondition: The next value is representable. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func successor() -> _CocoaSetIndex { // FIXME: swift-3-indexing-model: remove this method. _precondition( @@ -3632,7 +3484,6 @@ internal struct _CocoaSetIndex: Comparable { extension _CocoaSetIndex { @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static func < ( lhs: _CocoaSetIndex, rhs: _CocoaSetIndex @@ -3640,7 +3491,6 @@ extension _CocoaSetIndex { return lhs.currentKeyIndex < rhs.currentKeyIndex } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static func <= ( lhs: _CocoaSetIndex, rhs: _CocoaSetIndex @@ -3648,7 +3498,6 @@ extension _CocoaSetIndex { return lhs.currentKeyIndex <= rhs.currentKeyIndex } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static func > ( lhs: _CocoaSetIndex, rhs: _CocoaSetIndex @@ -3656,7 +3505,6 @@ extension _CocoaSetIndex { return lhs.currentKeyIndex > rhs.currentKeyIndex } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static func >= ( lhs: _CocoaSetIndex, rhs: _CocoaSetIndex @@ -3664,7 +3512,6 @@ extension _CocoaSetIndex { return lhs.currentKeyIndex >= rhs.currentKeyIndex } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static func == ( lhs: _CocoaSetIndex, rhs: _CocoaSetIndex @@ -3709,7 +3556,6 @@ extension Set { internal typealias Value = Element @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_value: SetIndexRepresentation) { self._value = _value } @@ -3718,26 +3564,23 @@ extension Set { internal var _value: SetIndexRepresentation @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal static func _native(_ index: _NativeIndex) -> Index { return SetIndex(_value: ._native(index)) } #if _runtime(_ObjC) @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal static func _cocoa(_ index: _CocoaIndex) -> Index { return SetIndex(_value: ._cocoa(index)) } #endif @inlinable // FIXME(sil-serialize-all) - @usableFromInline @_transparent internal var _guaranteedNative: Bool { return _canBeClass(Key.self) == 0 && _canBeClass(Value.self) == 0 } - @usableFromInline // FIXME(sil-serialize-all) + @inlinable // FIXME(sil-serialize-all) @_transparent internal var _nativeIndex: _NativeIndex { switch _value { @@ -3752,7 +3595,6 @@ extension Set { #if _runtime(_ObjC) @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @_transparent internal var _cocoaIndex: _CocoaIndex { switch _value { @@ -3811,20 +3653,30 @@ extension Set.Index { } } + /// Hashes the essential components of this value by feeding them into the + /// given hasher. + /// + /// - Parameter hasher: The hasher to use when combining the components + /// of this instance. @inlinable // FIXME(sil-serialize-all) - public var hashValue: Int { + public func hash(into hasher: inout Hasher) { + #if _runtime(_ObjC) if _fastPath(_guaranteedNative) { - return _nativeIndex.offset + hasher.combine(0 as UInt8) + hasher.combine(_nativeIndex.offset) + return } - switch _value { case ._native(let nativeIndex): - return nativeIndex.offset - #if _runtime(_ObjC) + hasher.combine(0 as UInt8) + hasher.combine(nativeIndex.offset) case ._cocoa(let cocoaIndex): - return cocoaIndex.currentKeyIndex - #endif + hasher.combine(1 as UInt8) + hasher.combine(cocoaIndex.currentKeyIndex) } + #else + hasher.combine(_nativeIndex.offset) + #endif } } @@ -3853,7 +3705,6 @@ final internal class _CocoaSetIterator: IteratorProtocol { internal let cocoaSet: _NSSet @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var _fastEnumerationStatePtr: UnsafeMutablePointer<_SwiftNSFastEnumerationState> { return _getUnsafePointerToStoredProperties(self).assumingMemoryBound( @@ -3861,7 +3712,6 @@ final internal class _CocoaSetIterator: IteratorProtocol { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var _fastEnumerationStackBufPtr: UnsafeMutablePointer<_CocoaFastEnumerationStackBuf> { return UnsafeMutableRawPointer(_fastEnumerationStatePtr + 1) @@ -3878,13 +3728,11 @@ final internal class _CocoaSetIterator: IteratorProtocol { internal var itemCount: Int = 0 @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal init(_ cocoaSet: _NSSet) { self.cocoaSet = cocoaSet } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal func next() -> Element? { if itemIndex < 0 { return nil @@ -3960,13 +3808,11 @@ public struct SetIterator: IteratorProtocol { internal var _state: SetIteratorRepresentation @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_state: SetIteratorRepresentation) { self._state = _state } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal static func _native( start: _NativeIndex, end: _NativeIndex, buffer: _NativeBuffer ) -> SetIterator { @@ -3975,7 +3821,6 @@ public struct SetIterator: IteratorProtocol { } #if _runtime(_ObjC) @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal static func _cocoa( _ iterator: _CocoaSetIterator ) -> SetIterator{ @@ -3984,14 +3829,12 @@ public struct SetIterator: IteratorProtocol { #endif @inlinable // FIXME(sil-serialize-all) - @usableFromInline @_transparent internal var _guaranteedNative: Bool { return _canBeClass(Element.self) == 0 } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal mutating func _nativeNext() -> Element? { switch _state { case ._native(let startIndex, let endIndex, let buffer): diff --git a/stdlib/public/core/Shims.swift b/stdlib/public/core/Shims.swift index 9ae0ec088738d..5fce53cd39928 100644 --- a/stdlib/public/core/Shims.swift +++ b/stdlib/public/core/Shims.swift @@ -18,7 +18,6 @@ import SwiftShims @inlinable -@usableFromInline internal func _makeSwiftNSFastEnumerationState() -> _SwiftNSFastEnumerationState { return _SwiftNSFastEnumerationState( @@ -29,17 +28,11 @@ internal func _makeSwiftNSFastEnumerationState() /// A dummy value to be used as the target for `mutationsPtr` in fast /// enumeration implementations. @usableFromInline // FIXME(sil-serialize-all) -@_fixed_layout internal var _fastEnumerationStorageMutationsTarget: CUnsignedLong = 0 /// A dummy pointer to be used as `mutationsPtr` in fast enumeration /// implementations. -@inlinable // FIXME(sil-serialize-all) -public // SPI(Foundation) -var _fastEnumerationStorageMutationsPtr: UnsafeMutablePointer { - // Note that either _fastEnumerationStorageMutationsPtr should be - // @_fixed_layout, or this function should not be @inlinable. - return UnsafeMutablePointer( - Builtin.addressof(&_fastEnumerationStorageMutationsTarget)) -} +@usableFromInline // FIXME(sil-serialize-all) +internal let _fastEnumerationStorageMutationsPtr = + UnsafeMutablePointer(Builtin.addressof(&_fastEnumerationStorageMutationsTarget)) #endif diff --git a/stdlib/public/core/SipHash.swift b/stdlib/public/core/SipHash.swift new file mode 100644 index 0000000000000..04130a0f41301 --- /dev/null +++ b/stdlib/public/core/SipHash.swift @@ -0,0 +1,204 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// This file implements SipHash-2-4 and SipHash-1-3 +/// (https://131002.net/siphash/). +/// +/// This file is based on the reference C implementation, which was released +/// to public domain by: +/// +/// * Jean-Philippe Aumasson +/// * Daniel J. Bernstein +//===----------------------------------------------------------------------===// + +private struct _SipHashState { + // "somepseudorandomlygeneratedbytes" + fileprivate var v0: UInt64 = 0x736f6d6570736575 + fileprivate var v1: UInt64 = 0x646f72616e646f6d + fileprivate var v2: UInt64 = 0x6c7967656e657261 + fileprivate var v3: UInt64 = 0x7465646279746573 + + @inline(__always) + fileprivate init(seed: (UInt64, UInt64)) { + v3 ^= seed.1 + v2 ^= seed.0 + v1 ^= seed.1 + v0 ^= seed.0 + } + + @inline(__always) + fileprivate + static func _rotateLeft(_ x: UInt64, by amount: UInt64) -> UInt64 { + return (x &<< amount) | (x &>> (64 - amount)) + } + + @inline(__always) + fileprivate mutating func _round() { + v0 = v0 &+ v1 + v1 = _SipHashState._rotateLeft(v1, by: 13) + v1 ^= v0 + v0 = _SipHashState._rotateLeft(v0, by: 32) + v2 = v2 &+ v3 + v3 = _SipHashState._rotateLeft(v3, by: 16) + v3 ^= v2 + v0 = v0 &+ v3 + v3 = _SipHashState._rotateLeft(v3, by: 21) + v3 ^= v0 + v2 = v2 &+ v1 + v1 = _SipHashState._rotateLeft(v1, by: 17) + v1 ^= v2 + v2 = _SipHashState._rotateLeft(v2, by: 32) + } + + @inline(__always) + fileprivate func _extract() -> UInt64 { + return v0 ^ v1 ^ v2 ^ v3 + } + + @inline(__always) + fileprivate func _generateSeed() -> (UInt64, UInt64) { + return (v0 &+ v1, v2 ^ v3) + } +} + +internal struct _SipHash13Core: _HasherCore { + private var _state: _SipHashState + + @inline(__always) + internal init(seed: (UInt64, UInt64)) { + _state = _SipHashState(seed: seed) + } + + @inline(__always) + internal mutating func compress(_ m: UInt64) { + _state.v3 ^= m + _state._round() + _state.v0 ^= m + } + + @inline(__always) + internal mutating func finalize(tailAndByteCount: UInt64) -> UInt64 { + compress(tailAndByteCount) + _state.v2 ^= 0xff + for _ in 0..<3 { + _state._round() + } + return _state._extract() + } + + @inline(__always) + internal func _generateSeed() -> (UInt64, UInt64) { + return _state._generateSeed() + } +} + +internal struct _SipHash24Core: _HasherCore { + private var _state: _SipHashState + + @inline(__always) + internal init(seed: (UInt64, UInt64)) { + _state = _SipHashState(seed: seed) + } + + @inline(__always) + internal mutating func compress(_ m: UInt64) { + _state.v3 ^= m + _state._round() + _state._round() + _state.v0 ^= m + } + + @inline(__always) + internal mutating func finalize(tailAndByteCount: UInt64) -> UInt64 { + compress(tailAndByteCount) + + _state.v2 ^= 0xff + for _ in 0..<4 { + _state._round() + } + return _state._extract() + } + + @inline(__always) + internal func _generateSeed() -> (UInt64, UInt64) { + return _state._generateSeed() + } +} + +// FIXME: This type only exists to facilitate testing, and should not exist in +// production builds. +@usableFromInline // @testable +internal struct _SipHash13 { + internal typealias Core = _BufferingHasher<_SipHash13Core> + + internal var _core: Core + + @usableFromInline // @testable + internal init(_seed: (UInt64, UInt64)) { _core = Core(seed: _seed) } + @usableFromInline // @testable + internal mutating func _combine(_ v: UInt) { _core.combine(v) } + @usableFromInline // @testable + internal mutating func _combine(_ v: UInt64) { _core.combine(v) } + @usableFromInline // @testable + internal mutating func _combine(_ v: UInt32) { _core.combine(v) } + @usableFromInline // @testable + internal mutating func _combine(_ v: UInt16) { _core.combine(v) } + @usableFromInline // @testable + internal mutating func _combine(_ v: UInt8) { _core.combine(v) } + @usableFromInline // @testable + internal mutating func _combine(bytes v: UInt64, count: Int) { + _core.combine(bytes: v, count: count) + } + @usableFromInline // @testable + internal mutating func combine(bytes: UnsafeRawBufferPointer) { + _core.combine(bytes: bytes) + } + @usableFromInline // @testable + internal __consuming func finalize() -> UInt64 { + var core = _core + return core.finalize() + } +} + +// FIXME: This type only exists to facilitate testing, and should not exist in +// production builds. +@usableFromInline // @testable +internal struct _SipHash24 { + internal typealias Core = _BufferingHasher<_SipHash24Core> + + internal var _core: Core + + @usableFromInline // @testable + internal init(_seed: (UInt64, UInt64)) { _core = Core(seed: _seed) } + @usableFromInline // @testable + internal mutating func _combine(_ v: UInt) { _core.combine(v) } + @usableFromInline // @testable + internal mutating func _combine(_ v: UInt64) { _core.combine(v) } + @usableFromInline // @testable + internal mutating func _combine(_ v: UInt32) { _core.combine(v) } + @usableFromInline // @testable + internal mutating func _combine(_ v: UInt16) { _core.combine(v) } + @usableFromInline // @testable + internal mutating func _combine(_ v: UInt8) { _core.combine(v) } + @usableFromInline // @testable + internal mutating func _combine(bytes v: UInt64, count: Int) { + _core.combine(bytes: v, count: count) + } + @usableFromInline // @testable + internal mutating func combine(bytes: UnsafeRawBufferPointer) { + _core.combine(bytes: bytes) + } + @usableFromInline // @testable + internal __consuming func finalize() -> UInt64 { + var core = _core + return core.finalize() + } +} diff --git a/stdlib/public/core/SipHash.swift.gyb b/stdlib/public/core/SipHash.swift.gyb deleted file mode 100644 index 7f812c3f85993..0000000000000 --- a/stdlib/public/core/SipHash.swift.gyb +++ /dev/null @@ -1,201 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// -/// This file implements SipHash-2-4 and SipHash-1-3 -/// (https://131002.net/siphash/). -/// -/// This file is based on the reference C implementation, which was released -/// to public domain by: -/// -/// * Jean-Philippe Aumasson -/// * Daniel J. Bernstein -//===----------------------------------------------------------------------===// - -%{ -# Number of bits in the Builtin.Word type -word_bits = int(CMAKE_SIZEOF_VOID_P) * 8 -}% - -@_frozen // FIXME(sil-serialize-all) -@usableFromInline -internal enum _SipHashDetail { - @usableFromInline - @inline(__always) - internal static func _rotate(_ x: UInt64, leftBy amount: UInt64) -> UInt64 { - return (x &<< amount) | (x &>> (64 - amount)) - } - - @usableFromInline - @inline(__always) - internal static func _sipRound( - v0: inout UInt64, - v1: inout UInt64, - v2: inout UInt64, - v3: inout UInt64 - ) { - v0 = v0 &+ v1 - v1 = _rotate(v1, leftBy: 13) - v1 ^= v0 - v0 = _rotate(v0, leftBy: 32) - v2 = v2 &+ v3 - v3 = _rotate(v3, leftBy: 16) - v3 ^= v2 - v0 = v0 &+ v3 - v3 = _rotate(v3, leftBy: 21) - v3 ^= v0 - v2 = v2 &+ v1 - v1 = _rotate(v1, leftBy: 17) - v1 ^= v2 - v2 = _rotate(v2, leftBy: 32) - } -} - -% for (c_rounds, d_rounds) in [(2, 4), (1, 3)]: -% Self = '_SipHash{}{}'.format(c_rounds, d_rounds) - -@_fixed_layout // FIXME(sil-serialize-all) -public // @testable -struct ${Self} { - // "somepseudorandomlygeneratedbytes" - @usableFromInline - internal var v0: UInt64 = 0x736f6d6570736575 - - @usableFromInline - internal var v1: UInt64 = 0x646f72616e646f6d - - @usableFromInline - internal var v2: UInt64 = 0x6c7967656e657261 - - @usableFromInline - internal var v3: UInt64 = 0x7465646279746573 - - /// This value holds the byte count and the pending bytes that haven't been - /// compressed yet, in the format that the finalization step needs. (The least - /// significant 56 bits hold the trailing bytes, while the most significant 8 - /// bits hold the count of bytes appended so far, mod 256.) - @usableFromInline - internal var tailAndByteCount: UInt64 = 0 - - @inline(__always) - public init(key: (UInt64, UInt64)) { - v3 ^= key.1 - v2 ^= key.0 - v1 ^= key.1 - v0 ^= key.0 - } - - @usableFromInline - internal var byteCount: UInt64 { - @inline(__always) - get { - return tailAndByteCount &>> 56 - } - } - - @usableFromInline - internal var tail: UInt64 { - @inline(__always) - get { - return tailAndByteCount & ~(0xFF &<< 56) - } - } - - @inline(__always) - @usableFromInline - internal mutating func _compress(_ m: UInt64) { - v3 ^= m - for _ in 0..<${c_rounds} { - _SipHashDetail._sipRound(v0: &v0, v1: &v1, v2: &v2, v3: &v3) - } - v0 ^= m - } - - @inline(__always) - public mutating func append(_ value: Int) { - append(UInt(bitPattern: value)) - } - - @inline(__always) - public mutating func append(_ value: UInt) { - % if word_bits == 64: - append(UInt64(_truncatingBits: value._lowWord)) - % elif word_bits == 32: - append(UInt32(_truncatingBits: value._lowWord)) - % else: - fatalError("Unsupported word width") - % end - } - - @inline(__always) - public mutating func append(_ value: Int32) { - append(UInt32(bitPattern: value)) - } - - @inline(__always) - public mutating func append(_ value: UInt32) { - let m = UInt64(_truncatingBits: value._lowWord) - if byteCount & 4 == 0 { - _sanityCheck(byteCount & 7 == 0 && tail == 0) - tailAndByteCount = (tailAndByteCount | m) &+ (4 &<< 56) - } else { - _sanityCheck(byteCount & 3 == 0) - _compress((m &<< 32) | tail) - tailAndByteCount = (byteCount &+ 4) &<< 56 - } - } - - @inline(__always) - public mutating func append(_ value: Int64) { - append(UInt64(bitPattern: value)) - } - - @inline(__always) - public mutating func append(_ m: UInt64) { - if byteCount & 4 == 0 { - _sanityCheck(byteCount & 7 == 0 && tail == 0) - _compress(m) - tailAndByteCount = tailAndByteCount &+ (8 &<< 56) - } else { - _sanityCheck(byteCount & 3 == 0) - _compress((m &<< 32) | tail) - tailAndByteCount = ((byteCount &+ 8) &<< 56) | (m &>> 32) - } - } - - @inline(__always) - public mutating func finalize( - tailBytes: UInt64, - tailByteCount: Int - ) -> UInt64 { - _sanityCheck(tailByteCount >= 0) - _sanityCheck(tailByteCount < 8 - (byteCount & 7)) - _sanityCheck(tailBytes >> (tailByteCount << 3) == 0) - let count = UInt64(_truncatingBits: tailByteCount._lowWord) - let currentByteCount = byteCount & 7 - tailAndByteCount |= (tailBytes &<< (currentByteCount &<< 3)) - tailAndByteCount = tailAndByteCount &+ (count &<< 56) - return finalize() - } - - @inline(__always) - public mutating func finalize() -> UInt64 { - _compress(tailAndByteCount) - - v2 ^= 0xff - - for _ in 0..<${d_rounds} { - _SipHashDetail._sipRound(v0: &v0, v1: &v1, v2: &v2, v3: &v3) - } - - return (v0 ^ v1 ^ v2 ^ v3) - } -} -% end diff --git a/stdlib/public/core/SliceBuffer.swift b/stdlib/public/core/SliceBuffer.swift index 4b51cec353fc5..165e7100d0979 100644 --- a/stdlib/public/core/SliceBuffer.swift +++ b/stdlib/public/core/SliceBuffer.swift @@ -21,7 +21,6 @@ internal struct _SliceBuffer internal typealias NativeBuffer = _ContiguousArrayBuffer @inlinable - @usableFromInline internal init( owner: AnyObject, subscriptBaseAddress: UnsafeMutablePointer, indices: Range, hasNativeBuffer: Bool @@ -35,7 +34,6 @@ internal struct _SliceBuffer } @inlinable - @usableFromInline internal init() { let empty = _ContiguousArrayBuffer() self.owner = empty.owner @@ -46,7 +44,6 @@ internal struct _SliceBuffer } @inlinable - @usableFromInline internal init(_buffer buffer: NativeBuffer, shiftedToStartIndex: Int) { let shift = buffer.startIndex - shiftedToStartIndex self.init( @@ -57,7 +54,6 @@ internal struct _SliceBuffer } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal func _invariantCheck() { let isNative = _hasNativeBuffer let isNativeStorage: Bool = owner is _ContiguousArrayStorageBase @@ -68,13 +64,11 @@ internal struct _SliceBuffer } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal var _hasNativeBuffer: Bool { return (endIndexAndFlags & 1) != 0 } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal var nativeBuffer: NativeBuffer { _sanityCheck(_hasNativeBuffer) return NativeBuffer( @@ -82,7 +76,6 @@ internal struct _SliceBuffer } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal var nativeOwner: AnyObject { _sanityCheck(_hasNativeBuffer, "Expect a native array") return owner @@ -95,7 +88,6 @@ internal struct _SliceBuffer /// `_ContiguousArrayBuffer` and /// `insertCount <= numericCast(newValues.count)`. @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal mutating func replaceSubrange( _ subrange: Range, with insertCount: Int, @@ -133,7 +125,6 @@ internal struct _SliceBuffer /// buffers address the same elements when they have the same /// identity and count. @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal var identity: UnsafeRawPointer { return UnsafeRawPointer(firstElementAddress) } @@ -145,13 +136,11 @@ internal struct _SliceBuffer internal let subscriptBaseAddress: UnsafeMutablePointer @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal var firstElementAddress: UnsafeMutablePointer { return subscriptBaseAddress + startIndex } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal var firstElementAddressIfContiguous: UnsafeMutablePointer? { return firstElementAddress } @@ -163,7 +152,6 @@ internal struct _SliceBuffer //===--- Non-essential bits ---------------------------------------------===// @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal mutating func requestUniqueMutableBackingBuffer( minimumCapacity: Int ) -> NativeBuffer? { @@ -204,7 +192,6 @@ internal struct _SliceBuffer } @inlinable - @usableFromInline internal mutating func isMutableAndUniquelyReferenced() -> Bool { // This is a performance optimization that ensures that the copy of self // that occurs at -Onone is destroyed before we call @@ -220,7 +207,6 @@ internal struct _SliceBuffer } @inlinable - @usableFromInline internal mutating func isMutableAndUniquelyReferencedOrPinned() -> Bool { // This is a performance optimization that ensures that the copy of self // that occurs at -Onone is destroyed before we call @@ -239,7 +225,6 @@ internal struct _SliceBuffer /// containing the same number of elements as `self`, return it. /// Otherwise, return `nil`. @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal func requestNativeBuffer() -> _ContiguousArrayBuffer? { _invariantCheck() if _fastPath(_hasNativeBuffer && nativeBuffer.count == count) { @@ -249,7 +234,6 @@ internal struct _SliceBuffer } @inlinable // FIXME(sil-serialize-all) - @usableFromInline @discardableResult internal func _copyContents( subRange bounds: Range, @@ -266,13 +250,11 @@ internal struct _SliceBuffer /// True, if the array is native and does not need a deferred type check. @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal var arrayPropertyIsNativeTypeChecked: Bool { return _hasNativeBuffer } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal var count: Int { get { return endIndex - startIndex @@ -290,14 +272,12 @@ internal struct _SliceBuffer /// Traps unless the given `index` is valid for subscripting, i.e. /// `startIndex ≤ index < endIndex` @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal func _checkValidSubscript(_ index : Int) { _precondition( index >= startIndex && index < endIndex, "Index out of bounds") } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal var capacity: Int { let count = self.count if _slowPath(!_hasNativeBuffer) { @@ -312,19 +292,16 @@ internal struct _SliceBuffer } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal mutating func isUniquelyReferenced() -> Bool { return isKnownUniquelyReferenced(&owner) } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal mutating func isUniquelyReferencedOrPinned() -> Bool { return _isKnownUniquelyReferencedOrPinned(&owner) } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal func getElement(_ i: Int) -> Element { _sanityCheck(i >= startIndex, "slice index is out of range (before startIndex)") _sanityCheck(i < endIndex, "slice index is out of range") @@ -336,7 +313,6 @@ internal struct _SliceBuffer /// - Precondition: `position` is a valid position in `self` and /// `position != endIndex`. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal subscript(position: Int) -> Element { get { return getElement(position) @@ -349,7 +325,6 @@ internal struct _SliceBuffer } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal subscript(bounds: Range) -> _SliceBuffer { get { _sanityCheck(bounds.lowerBound >= startIndex) @@ -379,7 +354,6 @@ internal struct _SliceBuffer /// `endIndex` is always reachable from `startIndex` by zero or more /// applications of `index(after:)`. @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal var endIndex: Int { get { return Int(endIndexAndFlags >> 1) @@ -395,7 +369,6 @@ internal struct _SliceBuffer /// Call `body(p)`, where `p` is an `UnsafeBufferPointer` over the /// underlying contiguous storage. @inlinable - @usableFromInline internal func withUnsafeBufferPointer( _ body: (UnsafeBufferPointer) throws -> R ) rethrows -> R { @@ -407,7 +380,6 @@ internal struct _SliceBuffer /// Call `body(p)`, where `p` is an `UnsafeMutableBufferPointer` /// over the underlying contiguous storage. @inlinable - @usableFromInline internal mutating func withUnsafeMutableBufferPointer( _ body: (UnsafeMutableBufferPointer) throws -> R ) rethrows -> R { @@ -419,7 +391,6 @@ internal struct _SliceBuffer extension _SliceBuffer { @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func _copyToContiguousArray() -> ContiguousArray { if _hasNativeBuffer { let n = nativeBuffer diff --git a/stdlib/public/core/SmallString.swift b/stdlib/public/core/SmallString.swift index 353fead19c0f0..88281641eba28 100644 --- a/stdlib/public/core/SmallString.swift +++ b/stdlib/public/core/SmallString.swift @@ -22,7 +22,7 @@ typealias _SmallUTF16StringBuffer = _FixedArray16 // Helper method for declaring something as not supported in 32-bit. Use inside // a function body inside a #if block so that callers don't have to be // conditional. -@_transparent @usableFromInline +@_transparent @inlinable func unsupportedOn32bit() -> Never { _conditionallyUnreachable() } // Trivial type declaration for type checking. Never present at runtime. @@ -46,7 +46,6 @@ struct _SmallUTF8String { // @usableFromInline var _storage: _RawBitPattern = (0,0) - @usableFromInline @inlinable @inline(__always) init() { @@ -130,7 +129,7 @@ extension _SmallUTF8String { @inlinable public // @testable - init?(_ codeUnits: C) where C.Element == UInt8 { + init?(_ codeUnits: UnsafeBufferPointer) { #if arch(i386) || arch(arm) return nil // Never form small strings on 32-bit #else @@ -138,13 +137,12 @@ extension _SmallUTF8String { guard count <= _SmallUTF8String.capacity else { return nil } self.init() self._withAllUnsafeMutableBytes { rawBufPtr in - let bufPtr = UnsafeMutableBufferPointer( - start: rawBufPtr.baseAddress.unsafelyUnwrapped.assumingMemoryBound( - to: UInt8.self), - count: rawBufPtr.count) - var (itr, written) = codeUnits._copyContents(initializing: bufPtr) - _sanityCheck(itr.next() == nil) - _sanityCheck(count == written) + let rawDst = rawBufPtr.baseAddress._unsafelyUnwrappedUnchecked + memcpy_( + dst: rawDst.assumingMemoryBound(to: UInt8.self), + src: codeUnits.baseAddress._unsafelyUnwrappedUnchecked, + count: count + ) } _sanityCheck(self.count == 0, "overwrote count early?") self.count = count @@ -153,6 +151,20 @@ extension _SmallUTF8String { if !self.isASCII { return nil } _invariantCheck() +#endif + } + + @inlinable + public // @testable + init?(_ scalar: Unicode.Scalar) { +#if arch(i386) || arch(arm) + return nil // Never form small strings on 32-bit +#else + // FIXME: support transcoding + guard scalar.value <= 0x7F else { return nil } + self.init() + self.count = 1 + self[0] = UInt8(truncatingIfNeeded: scalar.value) #endif } } @@ -161,7 +173,6 @@ extension _SmallUTF8String { // Small string read interface // extension _SmallUTF8String { - @usableFromInline @inlinable @inline(__always) func withUTF8CodeUnits( @@ -197,7 +208,6 @@ extension _SmallUTF8String { #endif } - @usableFromInline @inlinable @inline(__always) func withUnmanagedUTF16( @@ -212,7 +222,6 @@ extension _SmallUTF8String { #endif } - @usableFromInline @inlinable @inline(__always) func withUnmanagedASCII( @@ -275,7 +284,6 @@ extension _SmallUTF8String { } } - @usableFromInline @inlinable func _invariantCheck() { #if arch(i386) || arch(arm) @@ -304,7 +312,6 @@ extension _SmallUTF8String { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func _copy( into target: UnsafeMutableBufferPointer ) where TargetCodeUnit : FixedWidthInteger & UnsignedInteger { @@ -426,7 +433,6 @@ extension _SmallUTF8String { #endif } - @usableFromInline @inlinable func _appending(_ other: C) -> _SmallUTF8String? where C.Element == UInt16 { @@ -458,10 +464,7 @@ extension _SmallUTF8String { // NOTE: This exists to facilitate _fromCodeUnits, which is awful for this use // case. Please don't call this from anywhere else. - @usableFromInline - @inline(never) // @outlined - // @_specialize(where Encoding == UTF16) - // @_specialize(where Encoding == UTF8) + @inlinable init?( _fromCodeUnits codeUnits: S, utf16Length: Int, @@ -508,10 +511,10 @@ extension _SmallUTF8String { extension _SmallUTF8String { #if arch(i386) || arch(arm) @_fixed_layout @usableFromInline struct UnicodeScalarIterator { - @usableFromInline @inlinable @inline(__always) + @inlinable @inline(__always) func next() -> Unicode.Scalar? { unsupportedOn32bit() } } - @usableFromInline @inlinable @inline(__always) + @inlinable @inline(__always) func makeUnicodeScalarIterator() -> UnicodeScalarIterator { unsupportedOn32bit() } @@ -528,14 +531,12 @@ extension _SmallUTF8String { var _offset: Int @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) init(_ base: _SmallUTF8String) { (self.buffer, self.count) = base.transcoded self._offset = 0 } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) mutating func next() -> Unicode.Scalar? { if _slowPath(_offset == count) { return nil } let u0 = buffer[_offset] @@ -555,7 +556,6 @@ extension _SmallUTF8String { } } - @usableFromInline // FIXME(sil-serialize-all) @inlinable func makeUnicodeScalarIterator() -> UnicodeScalarIterator { return UnicodeScalarIterator(self) @@ -567,7 +567,6 @@ extension _SmallUTF8String { #else extension _SmallUTF8String { @inlinable - @usableFromInline @inline(__always) init(_rawBits: _RawBitPattern) { self._storage.low = _rawBits.low @@ -576,7 +575,6 @@ extension _SmallUTF8String { } @inlinable - @usableFromInline @inline(__always) init(low: UInt, high: UInt, count: Int) { self.init() @@ -587,24 +585,20 @@ extension _SmallUTF8String { } @inlinable - @usableFromInline internal var _rawBits: _RawBitPattern { @inline(__always) get { return _storage } } @inlinable - @usableFromInline internal var lowUnpackedBits: UInt { @inline(__always) get { return _storage.low } } @inlinable - @usableFromInline internal var highUnpackedBits: UInt { @inline(__always) get { return _storage.high & 0x00FF_FFFF_FFFF_FFFF } } @inlinable - @usableFromInline internal var unpackedBits: (low: UInt, high: UInt, count: Int) { @inline(__always) get { return (lowUnpackedBits, highUnpackedBits, count) } @@ -613,7 +607,6 @@ extension _SmallUTF8String { extension _SmallUTF8String { // Operate with a pointer to the entire struct, including unused capacity // and inline count. You should almost never call this directly. - @usableFromInline @inlinable @inline(__always) mutating func _withAllUnsafeMutableBytes( @@ -623,7 +616,6 @@ extension _SmallUTF8String { defer { self = copy } return try Swift.withUnsafeMutableBytes(of: ©._storage) { try body($0) } } - @usableFromInline @inlinable @inline(__always) func _withAllUnsafeBytes( @@ -632,7 +624,6 @@ extension _SmallUTF8String { var copy = self return try Swift.withUnsafeBytes(of: ©._storage) { try body($0) } } - @usableFromInline @inlinable @inline(__always) mutating func _withMutableExcessCapacityBytes( @@ -649,7 +640,6 @@ extension _SmallUTF8String { } extension _SmallUTF8String { - @usableFromInline @inlinable @inline(__always) func _uncheckedCodeUnit(at i: Int) -> UInt8 { @@ -660,7 +650,6 @@ extension _SmallUTF8String { return _storage.high._uncheckedGetByte(at: i &- 8) } } - @usableFromInline @inlinable @inline(__always) mutating func _uncheckedSetCodeUnit(at i: Int, to: UInt8) { @@ -671,7 +660,6 @@ extension _SmallUTF8String { extension _SmallUTF8String { @inlinable - @usableFromInline @inline(__always) internal func _uncheckedClamp(upperBound: Int) -> _SmallUTF8String { _sanityCheck(upperBound <= self.count) @@ -692,7 +680,6 @@ extension _SmallUTF8String { } @inlinable - @usableFromInline @inline(__always) internal func _uncheckedClamp(lowerBound: Int) -> _SmallUTF8String { _sanityCheck(lowerBound < self.count) @@ -713,7 +700,6 @@ extension _SmallUTF8String { } @inlinable - @usableFromInline @inline(__always) internal func _uncheckedClamp( lowerBound: Int, upperBound: Int @@ -731,7 +717,6 @@ extension _SmallUTF8String {//}: _StringVariant { typealias TranscodedBuffer = _SmallUTF16StringBuffer @inlinable - @usableFromInline @discardableResult func transcode( _uncheckedInto buffer: UnsafeMutableBufferPointer @@ -753,7 +738,6 @@ extension _SmallUTF8String {//}: _StringVariant { } @inlinable - @usableFromInline @inline(__always) func transcode(into buffer: UnsafeMutablePointer) -> Int { let ptr = UnsafeMutableRawPointer(buffer).assumingMemoryBound( @@ -764,7 +748,6 @@ extension _SmallUTF8String {//}: _StringVariant { } @inlinable - @usableFromInline var transcoded: (TranscodedBuffer, count: Int) { @inline(__always) get { // TODO: in-register zero-extension for ascii @@ -810,7 +793,6 @@ extension _SmallUTF8String {//}: _StringVariant { } } -@usableFromInline @inlinable @inline(__always) internal @@ -834,7 +816,6 @@ extension UInt { // // TODO: endianess awareness day @inlinable - @usableFromInline @inline(__always) func _uncheckedGetByte(at i: Int) -> UInt8 { _sanityCheck(i >= 0 && i < MemoryLayout.stride) diff --git a/stdlib/public/core/Sort.swift.gyb b/stdlib/public/core/Sort.swift.gyb index 3f610fd036535..6249649700fe2 100644 --- a/stdlib/public/core/Sort.swift.gyb +++ b/stdlib/public/core/Sort.swift.gyb @@ -34,7 +34,6 @@ else: }% @inlinable -@usableFromInline internal func _insertionSort( _ elements: inout C, subRange range: Range @@ -177,7 +176,6 @@ func _sort3( /// - Precondition: The count of `range` must be >= 3: /// `elements.distance(from: range.lowerBound, to: range.upperBound) >= 3` @inlinable -@usableFromInline internal func _partition( _ elements: inout C, subRange range: Range @@ -254,7 +252,6 @@ func _introSort( } @inlinable -@usableFromInline internal func _introSortImpl( _ elements: inout C, subRange range: Range @@ -301,7 +298,6 @@ internal func _introSortImpl( } @inlinable -@usableFromInline internal func _siftDown( _ elements: inout C, index: C.Index, @@ -344,7 +340,6 @@ internal func _siftDown( } @inlinable -@usableFromInline internal func _heapify( _ elements: inout C, subRange range: Range @@ -376,7 +371,6 @@ internal func _heapify( } @inlinable -@usableFromInline internal func _heapSort( _ elements: inout C, subRange range: Range diff --git a/stdlib/public/core/StaticString.swift b/stdlib/public/core/StaticString.swift index bfd33259020bb..afa995a50539d 100644 --- a/stdlib/public/core/StaticString.swift +++ b/stdlib/public/core/StaticString.swift @@ -167,7 +167,6 @@ public struct StaticString } @inlinable // FIXME(sil-serialize-all) - @usableFromInline @_transparent internal init( _start: Builtin.RawPointer, @@ -185,7 +184,6 @@ public struct StaticString } @inlinable // FIXME(sil-serialize-all) - @usableFromInline @_transparent internal init( unicodeScalar: Builtin.Int32 diff --git a/stdlib/public/core/Stride.swift.gyb b/stdlib/public/core/Stride.swift.gyb index db1a754fb1529..49cb8cfc050bc 100644 --- a/stdlib/public/core/Stride.swift.gyb +++ b/stdlib/public/core/Stride.swift.gyb @@ -282,7 +282,6 @@ public struct StrideToIterator { internal var _current: (index: Int?, value: Element) @inlinable - @usableFromInline internal init(_start: Element, end: Element, stride: Element.Stride) { self._start = _start _end = end @@ -323,7 +322,6 @@ public struct StrideTo { internal let _stride: Element.Stride @inlinable - @usableFromInline internal init(_start: Element, end: Element, stride: Element.Stride) { _precondition(stride != 0, "Stride size must not be zero") // At start, striding away from end is allowed; it just makes for an @@ -496,7 +494,6 @@ public struct StrideThroughIterator { internal var _didReturnEnd: Bool = false @inlinable - @usableFromInline internal init(_start: Element, end: Element, stride: Element.Stride) { self._start = _start _end = end @@ -543,7 +540,6 @@ public struct StrideThrough { internal let _stride: Element.Stride @inlinable - @usableFromInline internal init(_start: Element, end: Element, stride: Element.Stride) { _precondition(stride != 0, "Stride size must not be zero") self._start = _start diff --git a/stdlib/public/core/String.swift b/stdlib/public/core/String.swift index f34c1f6e44c27..4401e162f74fc 100644 --- a/stdlib/public/core/String.swift +++ b/stdlib/public/core/String.swift @@ -13,7 +13,6 @@ import SwiftShims @inlinable // FIXME(sil-serialize-all) -@usableFromInline // FIXME(sil-serialize-all) @_semantics("optimize.sil.specialize.generic.partial.never") internal func _withCStringAndLength< Source : Collection, @@ -54,7 +53,6 @@ extension _StringGuts { /// Invokes `body` on a null-terminated sequence of code units in the given /// encoding corresponding to the substring in `bounds`. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func _withCSubstring( in bounds: Range, encoding targetEncoding: TargetEncoding.Type, @@ -66,7 +64,6 @@ extension _StringGuts { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @_semantics("optimize.sil.specialize.generic.partial.never") internal func _withCSubstringAndLength< Result, TargetEncoding: Unicode.Encoding @@ -135,7 +132,6 @@ extension _StringGuts { extension String { @inlinable - @usableFromInline internal static func _fromCodeUnits< Input: Collection, Encoding: Unicode.Encoding @@ -366,7 +362,7 @@ extension String { /// that point: /// /// let name = "Marie Curie" -/// let firstSpace = name.index(of: " ") ?? name.endIndex +/// let firstSpace = name.firstIndex(of: " ") ?? name.endIndex /// let firstName = name[..) -> Bool { + for byte in input { + guard byte <= 0x7F else { return false } + } + return true +} + extension String { - @inlinable - @usableFromInline - static func _fromUTF8CodeUnitSequence( - _ input: C, repair: Bool - ) -> String? where C.Element == UInt8 { + static func _fromUTF8CodeUnitSequence( + _ input: UnsafeBufferPointer, repair: Bool + ) -> String? { + if _isAllASCII(input) { + return _fromASCII(input) + } + if let smol = _SmallUTF8String(input) { return String(_StringGuts(smol)) } + return String._fromCodeUnits( input, encoding: UTF8.self, repairIllFormedSequences: repair) } - @inlinable @usableFromInline - static func _fromASCII( - _ input: C - ) -> String where C.Element == UInt8 { + static func _fromASCII(_ input: UnsafeBufferPointer) -> String { if let smol = _SmallUTF8String(input) { return String(_StringGuts(smol)) } let storage = _SwiftStringStorage.create( capacity: input.count, count: input.count) - var (itr, end) = input._copyContents(initializing: storage.usedBuffer) - _sanityCheck(itr.next() == nil) - _sanityCheck(end == storage.usedBuffer.endIndex) + _sanityCheck(storage.count == input.count) + storage.start.initialize( + from: input.baseAddress._unsafelyUnwrappedUnchecked, count: input.count) return String(_StringGuts(_large: storage)) } - @inlinable // FIXME(sil-serialize-all) - public // FIXME: @usableFromInline, currently public because testing... - static func _fromWellFormedUTF8CodeUnitSequence( - _ input: C, repair: Bool = false - ) -> String where C.Element == UTF8.CodeUnit { + @usableFromInline + static func _fromWellFormedUTF8CodeUnitSequence( + _ input: UnsafeBufferPointer, repair: Bool = false + ) -> String { return String._fromUTF8CodeUnitSequence(input, repair: repair)! } } @@ -680,9 +682,7 @@ extension String : _ExpressibleByBuiltinUnicodeScalarLiteral { // // TODO: All scalars are small if scalar.value <= 0x7f { - if let small = _SmallUTF8String( - Unicode.UTF8.encode(scalar)._unsafelyUnwrappedUnchecked - ) { + if let small = _SmallUTF8String(scalar) { self = String(_StringGuts(small)) return } else { @@ -798,7 +798,6 @@ extension String { /// Returns the number of code units occupied by this string /// in the given encoding. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func _encodedLength< Encoding: Unicode.Encoding >(_ encoding: Encoding.Type) -> Int { @@ -821,7 +820,6 @@ extension String { // Related: Please document how NSString interacts // with unpaired surrogates @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func _encode( _ encoding: Encoding.Type, into processCodeUnit: (Encoding.CodeUnit) -> Void @@ -901,7 +899,6 @@ extension String { /// // Prints "Hello, friend" /// /// - Parameter other: Another string. - @inlinable // FIXME(sil-serialize-all) public mutating func append(_ other: String) { self._guts.append(other._guts) } @@ -919,7 +916,6 @@ extension String { // TODO(SSO): Consider small-checking version @inlinable // FIXME(sil-serialize-all) - public init(_largeStorage storage: _SwiftStringStorage) where CodeUnit : FixedWidthInteger & UnsignedInteger { _guts = _StringGuts(_large: storage) @@ -978,13 +974,12 @@ extension Sequence where Element: StringProtocol { /// - Parameter separator: A string to insert between each of the elements /// in this sequence. The default separator is an empty string. /// - Returns: A single, concatenated string. - @inlinable // FIXME(sil-serialize-all) + @_specialize(where Self == Array) + @_specialize(where Self == Array) public func joined(separator: String = "") -> String { return _joined(separator: separator) } - @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func _joined(separator: String = "") -> String { let separatorSize = separator._guts.count var width = separator._guts.byteWidth @@ -1040,25 +1035,21 @@ extension BidirectionalCollection where Iterator.Element == String { /// - Parameter separator: A string to insert between each of the elements /// in this sequence. The default separator is an empty string. /// - Returns: A single, concatenated string. - @inlinable // FIXME(sil-serialize-all) + @_specialize(where Self == Array) public func joined(separator: String = "") -> String { return _joined(separator: separator) } } #if _runtime(_ObjC) -@inlinable // FIXME(sil-serialize-all) @usableFromInline // FIXME(sil-serialize-all) @_silgen_name("swift_stdlib_NSStringLowercaseString") internal func _stdlib_NSStringLowercaseString(_ str: AnyObject) -> _CocoaString -@inlinable // FIXME(sil-serialize-all) @usableFromInline // FIXME(sil-serialize-all) @_silgen_name("swift_stdlib_NSStringUppercaseString") internal func _stdlib_NSStringUppercaseString(_ str: AnyObject) -> _CocoaString #else -@inlinable // FIXME(sil-serialize-all) -@usableFromInline // FIXME(sil-serialize-all) internal func _nativeUnicodeLowercaseString(_ str: String) -> String { // TODO (TODO: JIRA): check for small @@ -1089,7 +1080,6 @@ internal func _nativeUnicodeLowercaseString(_ str: String) -> String { return String(_largeStorage: storage) } -@inlinable // FIXME(sil-serialize-all) @usableFromInline // FIXME(sil-serialize-all) internal func _nativeUnicodeUppercaseString(_ str: String) -> String { @@ -1132,7 +1122,6 @@ extension String { /// from the ASCII value of that character and divide by 2. The bit is set iff /// that character is a lower case character. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var _asciiLowerCaseTable: UInt64 { @inline(__always) get { @@ -1142,7 +1131,6 @@ extension String { /// The same table for upper case characters. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var _asciiUpperCaseTable: UInt64 { @inline(__always) get { @@ -1161,7 +1149,6 @@ extension String { /// - Returns: A lowercase copy of the string. /// /// - Complexity: O(*n*) - @inlinable // FIXME(sil-serialize-all) public func lowercased() -> String { if _guts.isASCII { var guts = _guts @@ -1209,7 +1196,6 @@ extension String { /// - Returns: An uppercase copy of the string. /// /// - Complexity: O(*n*) - @inlinable // FIXME(sil-serialize-all) public func uppercased() -> String { if _guts.isASCII { var guts = _guts diff --git a/stdlib/public/core/StringBridge.swift b/stdlib/public/core/StringBridge.swift index 19c0973e7372f..405cc83c361a4 100644 --- a/stdlib/public/core/StringBridge.swift +++ b/stdlib/public/core/StringBridge.swift @@ -113,12 +113,10 @@ internal func _cocoaStringSubscript( // @inlinable // FIXME(sil-serialize-all) -@usableFromInline // FIXME(sil-serialize-all) internal var kCFStringEncodingASCII : _swift_shims_CFStringEncoding { @inline(__always) get { return 0x0600 } } @inlinable // FIXME(sil-serialize-all) -@usableFromInline // FIXME(sil-serialize-all) internal var kCFStringEncodingUTF8 : _swift_shims_CFStringEncoding { @inline(__always) get { return 0x8000100 } } @@ -205,22 +203,6 @@ func _makeCocoaStringGuts(_ cocoaString: _CocoaString) -> _StringGuts { let length = _StringGuts.getCocoaLength( _unsafeBitPattern: Builtin.reinterpretCast(immutableCopy)) - - // TODO(SSO): And also for UTF-16 strings and non-contiguous strings - if let ptr = start, !isUTF16 && length <= _SmallUTF8String.capacity { - if let small = _SmallUTF8String( - UnsafeBufferPointer( - start: ptr.assumingMemoryBound(to: UInt8.self), count: length) - ) { - return _StringGuts(small) - } else { -#if arch(i386) || arch(arm) -#else - _sanityCheckFailure("Couldn't fit 15-char ASCII small string?") -#endif - } - } - return _StringGuts( _largeNonTaggedCocoaObject: immutableCopy, count: length, @@ -373,7 +355,6 @@ public final class _NSContiguousString : _SwiftNativeNSString, _NSStringCore { /// anything transitively reachable form this object. Doing so /// will result in undefined behavior. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @_semantics("self_no_escaping_closure") func _unsafeWithNotEscapedSelfPointer( _ body: (OpaquePointer) throws -> Result @@ -390,7 +371,6 @@ public final class _NSContiguousString : _SwiftNativeNSString, _NSStringCore { /// transitively reachable objects. Doing so will result in undefined /// behavior. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @_semantics("pair_no_escaping_closure") func _unsafeWithNotEscapedSelfPointerPair( _ rhs: _NSContiguousString, diff --git a/stdlib/public/core/StringCharacterView.swift b/stdlib/public/core/StringCharacterView.swift index 2422d9b24f6ef..db48e4aff86f6 100644 --- a/stdlib/public/core/StringCharacterView.swift +++ b/stdlib/public/core/StringCharacterView.swift @@ -52,7 +52,7 @@ extension String { /// using the `String` type's `init(_:)` initializer. /// /// let name = "Marie Curie" - /// if let firstSpace = name.characters.index(of: " ") { + /// if let firstSpace = name.characters.firstIndex(of: " ") { /// let firstName = String(name.characters[.. String.CharacterView in - /// if let i = chars.index(of: " ") { + /// if let i = chars.firstIndex(of: " ") { /// let result = chars[chars.index(after: i)...] /// chars.removeSubrange(i...) /// return result @@ -181,19 +181,16 @@ extension String { extension String._CharacterView : _SwiftStringView { @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var _persistentContent : String { return _base } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var _wholeString : String { return _base } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var _encodedOffsetRange : Range { return _base._encodedOffsetRange } @@ -201,7 +198,6 @@ extension String._CharacterView : _SwiftStringView { extension String._CharacterView { @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal var _guts: _StringGuts { return _base._guts } @@ -211,7 +207,6 @@ extension String._CharacterView { internal typealias UnicodeScalarView = String.UnicodeScalarView @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal var unicodeScalars: UnicodeScalarView { return UnicodeScalarView(_base._guts, coreOffset: _baseOffset) } @@ -225,7 +220,6 @@ extension String._CharacterView : BidirectionalCollection { /// Translates a view index into an index in the underlying base string using /// this view's `_baseOffset`. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func _toBaseIndex(_ index: Index) -> Index { return Index( encodedOffset: index.encodedOffset - _baseOffset, @@ -235,7 +229,6 @@ extension String._CharacterView : BidirectionalCollection { /// Translates an index in the underlying base string into a view index using /// this view's `_baseOffset`. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func _toViewIndex(_ index: Index) -> Index { return Index( encodedOffset: index.encodedOffset + _baseOffset, @@ -281,7 +274,7 @@ extension String._CharacterView : BidirectionalCollection { /// letter and then prints the character at the found index: /// /// let greeting = "Hello, friend!" - /// if let i = greeting.characters.index(where: { "A"..."Z" ~= $0 }) { + /// if let i = greeting.characters.firstIndex(where: { "A"..."Z" ~= $0 }) { /// print("First capital letter: \(greeting.characters[i])") /// } /// // Prints "First capital letter: H" @@ -337,7 +330,6 @@ extension String._CharacterView : RangeReplaceableCollection { /// to allocate. /// /// - Complexity: O(*n*), where *n* is the capacity being reserved. - @inlinable // FIXME(sil-serialize-all) public mutating func reserveCapacity(_ n: Int) { _base.reserveCapacity(n) } @@ -345,7 +337,6 @@ extension String._CharacterView : RangeReplaceableCollection { /// Appends the given character to the character view. /// /// - Parameter c: The character to append to the character view. - @inlinable // FIXME(sil-serialize-all) public mutating func append(_ c: Character) { _base.append(c) } @@ -368,14 +359,13 @@ extension String._CharacterView { /// not including, the first comma (`","`) in the string. /// /// let str = "All this happened, more or less." - /// let i = str.characters.index(of: ",")! + /// let i = str.characters.firstIndex(of: ",")! /// let substring = str.characters[str.characters.startIndex ..< i] /// print(String(substring)) /// // Prints "All this happened" /// /// - Complexity: O(*n*) if the underlying string is bridged from /// Objective-C, where *n* is the length of the string; otherwise, O(1). - @inlinable // FIXME(sil-serialize-all) @available(swift, deprecated: 3.2, message: "Please use String or Substring directly") public subscript(bounds: Range) -> String.CharacterView { diff --git a/stdlib/public/core/StringComparable.swift b/stdlib/public/core/StringComparable.swift index aa50ef72b5190..8a11f2710516a 100644 --- a/stdlib/public/core/StringComparable.swift +++ b/stdlib/public/core/StringComparable.swift @@ -20,7 +20,6 @@ extension _StringGuts { } @inlinable - @usableFromInline internal static func isEqual( _ left: _StringGuts, to right: _StringGuts ) -> Bool { @@ -32,7 +31,6 @@ extension _StringGuts { } @inlinable - @usableFromInline internal static func isEqual( _ left: _StringGuts, _ leftRange: Range, to right: _StringGuts, _ rightRange: Range @@ -45,7 +43,6 @@ extension _StringGuts { } @inlinable - @usableFromInline internal static func isLess( _ left: _StringGuts, than right: _StringGuts ) -> Bool { @@ -57,7 +54,6 @@ extension _StringGuts { } @inlinable - @usableFromInline internal static func isLess( _ left: _StringGuts, _ leftRange: Range, than right: _StringGuts, _ rightRange: Range @@ -70,7 +66,6 @@ extension _StringGuts { } @inlinable - @usableFromInline internal static func compare( _ left: _StringGuts, _ leftRange: Range, to right: _StringGuts, _ rightRange: Range @@ -92,7 +87,6 @@ extension _StringGuts { } @inlinable - @usableFromInline internal static func compare( _ left: _StringGuts, to right: _StringGuts ) -> Int { diff --git a/stdlib/public/core/StringComparison.swift b/stdlib/public/core/StringComparison.swift index d1fe8d84256f3..33ea1f667653e 100644 --- a/stdlib/public/core/StringComparison.swift +++ b/stdlib/public/core/StringComparison.swift @@ -22,7 +22,6 @@ enum _GutsClassification: UInt { } extension _StringGuts { - @usableFromInline @inlinable var classification: _GutsClassification { if _isSmall { return .smallUTF8 } @@ -684,7 +683,6 @@ internal func _tryNormalize( extension _UnmanagedString where CodeUnit == UInt8 { @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal func compareASCII(to other: _UnmanagedString) -> Int { // FIXME Results should be the same across all platforms. if self.start == other.start { @@ -939,26 +937,29 @@ extension _UnmanagedString where CodeUnit == UInt8 { if _fastPath( other._parseRawScalar(startingFrom: idx).0._isNormalizedSuperASCII ) { - return .less - } - } else { - let selfASCIIChar = UInt16(self[idx]) - _sanityCheck(selfASCIIChar != otherCU, "should be different") - if idx+1 == other.count { - return _lexicographicalCompare(selfASCIIChar, otherCU) - } - if _fastPath(other.hasNormalizationBoundary(after: idx)) { - return _lexicographicalCompare(selfASCIIChar, otherCU) + return .less } + + // Rare pathological case, e.g. Kelvin symbol + var selfIterator = _NormalizedCodeUnitIterator(self) + return selfIterator.compare(with: _NormalizedCodeUnitIterator(other)) + } + + let selfASCIIChar = UInt16(self[idx]) + _sanityCheck(selfASCIIChar != otherCU, "should be different") + if idx+1 == other.count { + return _lexicographicalCompare(selfASCIIChar, otherCU) + } + if _fastPath(other.hasNormalizationBoundary(after: idx)) { + return _lexicographicalCompare(selfASCIIChar, otherCU) } // // Otherwise, need to normalize the segment and then compare // - let selfASCIIChar = UInt16(self[idx]) return _compareStringsPostSuffix( - selfASCIIChar: selfASCIIChar, otherUTF16: other[idx...] - ) + selfASCIIChar: selfASCIIChar, otherUTF16WithLeadingASCII: other[idx...] + ) } } @@ -1010,15 +1011,17 @@ extension BidirectionalCollection where Element == UInt16, SubSequence == Self { } } +@inline(never) // @outlined private func _compareStringsPostSuffix( selfASCIIChar: UInt16, - otherUTF16: _UnmanagedString + otherUTF16WithLeadingASCII: _UnmanagedString ) -> _Ordering { - let otherCU = otherUTF16[0] + let otherCU = otherUTF16WithLeadingASCII[0] _sanityCheck(otherCU <= 0x7F, "should be ASCII, otherwise no need to call") - let segmentEndIdx = otherUTF16._findNormalizationSegmentEnd(startingFrom: 0) - let segment = otherUTF16[.. UInt16.max) { // Don't store a 0 stride for the endIndex @@ -37,7 +34,6 @@ extension String.Index { } extension _StringVariant { - @usableFromInline @inlinable internal func _stride(at i: String.Index) -> Int { if case .character(let stride) = i._cache { @@ -48,21 +44,18 @@ extension _StringVariant { return characterStride(atOffset: i.encodedOffset) } - @usableFromInline @inlinable internal func characterStride(atOffset offset: Int) -> Int { let slice = self.checkedSlice(from: offset) return slice.measureFirstExtendedGraphemeCluster() } - @usableFromInline @inlinable internal func characterIndex(atOffset offset: Int) -> String.Index { let stride = self.characterStride(atOffset: offset) return String.Index(encodedOffset: offset, characterStride: stride) } - @usableFromInline @inlinable internal func characterIndex(after i: String.Index) -> String.Index { let offset = i.encodedOffset @@ -78,7 +71,6 @@ extension _StringVariant { characterStride: stride2) } - @usableFromInline @inlinable internal func characterIndex(before i: String.Index) -> String.Index { let offset = i.encodedOffset @@ -92,7 +84,6 @@ extension _StringVariant { characterStride: stride) } - @usableFromInline @inlinable internal func characterIndex( _ i: String.Index, @@ -111,7 +102,6 @@ extension _StringVariant { return i } - @usableFromInline @inlinable internal func characterIndex( _ i: String.Index, @@ -160,7 +150,6 @@ extension _StringVariant { } @inlinable - @usableFromInline internal func character(at i: String.Index) -> Character { let stride = _stride(at: i) let offset = i.encodedOffset @@ -180,7 +169,6 @@ extension _StringVariant { // paths of grapheme breaking that we have high confidence won't change. /// Returns the length of the first extended grapheme cluster in UTF-16 /// code units. - @usableFromInline @inlinable internal func measureFirstExtendedGraphemeCluster() -> Int { @@ -211,7 +199,6 @@ extension _StringVariant { // /// Returns the length of the last extended grapheme cluster in UTF-16 /// code units. - @usableFromInline @inlinable internal func measureLastExtendedGraphemeCluster() -> Int { @@ -324,7 +311,6 @@ extension _UnmanagedOpaqueString { extension Unicode.UTF16 { /// Fast check for a (stable) grapheme break between two UInt16 code units @inlinable // Safe to inline - @usableFromInline internal static func _quickCheckGraphemeBreakBetween( _ lhs: UInt16, _ rhs: UInt16 ) -> Bool { diff --git a/stdlib/public/core/StringGuts.swift b/stdlib/public/core/StringGuts.swift index 185acf87bc6b3..203feceeee3c5 100644 --- a/stdlib/public/core/StringGuts.swift +++ b/stdlib/public/core/StringGuts.swift @@ -49,7 +49,6 @@ struct _StringGuts { public typealias _RawBitPattern = (_StringObject._RawBitPattern, UInt) - @usableFromInline @inlinable internal var rawBits: _RawBitPattern { @inline(__always) @@ -67,7 +66,6 @@ struct _StringGuts { extension _StringGuts { @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func _invariantCheck() { #if INTERNAL_CHECKS_ENABLED _object._invariantCheck() @@ -103,7 +101,7 @@ extension _StringGuts { @inlinable @inline(__always) public // @testable - mutating func isUniqueNative() -> Bool { + mutating func _isUniqueNative() -> Bool { guard _isNative else { return false } // Note that the isUnique test must be in a separate statement; // `isNative && _isUnique` always evaluates to false in debug builds, @@ -125,8 +123,15 @@ extension _StringGuts { @inlinable public // @testable var isASCII: Bool { - // FIXME: Currently used to sometimes mean contiguous ASCII - return _object.isContiguousASCII + @inline(__always) get { return _object.isContiguousASCII } + } + + @inlinable + internal + var _isASCIIOrSmallASCII: Bool { + @inline(__always) get { + return isASCII || _isSmall && _smallUTF8String.isASCII + } } @inlinable @@ -168,7 +173,6 @@ extension _StringGuts { return _object.isSingleByte } - @usableFromInline @inlinable internal var _isEmptySingleton: Bool { @@ -181,7 +185,6 @@ extension _StringGuts { return _object.byteWidth } - @usableFromInline @inlinable internal var _nativeCount: Int { @@ -197,7 +200,7 @@ extension _StringGuts { } // TODO(SSO): consider a small-checking variant - @usableFromInline + @inlinable @inline(__always) internal init(_large storage: _SwiftStringStorage) @@ -231,7 +234,6 @@ extension _StringGuts { Builtin.reinterpretCast(_unsafeBitPattern)) } - @usableFromInline @inlinable var _cocoaCount: Int { @inline(__always) @@ -244,7 +246,6 @@ extension _StringGuts { } } - @usableFromInline @inlinable var _cocoaRawStart: UnsafeRawPointer { @inline(__always) @@ -257,7 +258,6 @@ extension _StringGuts { } } - @usableFromInline @inlinable func _asContiguousCocoa( of codeUnit: CodeUnit.Type = CodeUnit.self @@ -311,7 +311,6 @@ extension _StringGuts { #endif // _runtime(_ObjC) extension _StringGuts { - @usableFromInline @inlinable internal var _unmanagedRawStart: UnsafeRawPointer { @inline(__always) get { @@ -320,7 +319,6 @@ extension _StringGuts { } } - @usableFromInline @inlinable internal var _unmanagedCount: Int { @inline(__always) get { @@ -329,7 +327,6 @@ extension _StringGuts { } } - @usableFromInline @inlinable @inline(__always) internal @@ -346,7 +343,6 @@ extension _StringGuts { } // TODO(SSO): consider a small-checking variant - @usableFromInline @inlinable init(_large s: _UnmanagedString) where CodeUnit : FixedWidthInteger & UnsignedInteger { @@ -363,7 +359,6 @@ extension _StringGuts { // Small strings extension _StringGuts { - @usableFromInline @inlinable internal var _smallUTF8Count: Int { @inline(__always) get { @@ -375,7 +370,6 @@ extension _StringGuts { } } - @usableFromInline @inlinable internal var _smallUTF8String: _SmallUTF8String { @inline(__always) get { @@ -388,7 +382,6 @@ extension _StringGuts { } } - @usableFromInline @inlinable @inline(__always) internal init(_ small: _SmallUTF8String) { @@ -403,7 +396,6 @@ extension _StringGuts { } extension _StringGuts { - @usableFromInline @inlinable internal var _unmanagedASCIIView: _UnmanagedString { @@ -425,7 +417,6 @@ extension _StringGuts { } } - @usableFromInline @inlinable internal var _unmanagedUTF16View: _UnmanagedString { @@ -449,7 +440,6 @@ extension _StringGuts { } extension _StringGuts { - @usableFromInline @inlinable internal var _isOpaque: Bool { @@ -457,7 +447,6 @@ extension _StringGuts { get { return _object.isOpaque } } - @usableFromInline @inlinable internal var _isContiguous: Bool { @@ -468,7 +457,7 @@ extension _StringGuts { #if _runtime(_ObjC) extension _StringGuts { - public // @testable + @usableFromInline var _underlyingCocoaString: _CocoaString? { if _object.isNative { return _object.nativeRawStorage @@ -643,6 +632,8 @@ extension _StringGuts { // Use the existing buffer if possible; otherwise copy the string into a // new buffer. @usableFromInline + @_specialize(where CodeUnit == UInt8) + @_specialize(where CodeUnit == UInt16) internal func _extractNativeStorage( of codeUnit: CodeUnit.Type = CodeUnit.self @@ -657,9 +648,6 @@ extension _StringGuts { @_specialize(where CodeUnit == UInt8) @_specialize(where CodeUnit == UInt16) - @_specialize(where CodeUnit == UTF16.CodeUnit) - @usableFromInline - @inlinable internal func _copyToNativeStorage( of codeUnit: CodeUnit.Type = CodeUnit.self, @@ -675,8 +663,7 @@ extension _StringGuts { return storage } - @inlinable - public // @testable + @usableFromInline // @testable func _extractSlice(_ range: Range) -> _StringGuts { if range.isEmpty { return _StringGuts() } if range == 0..( of type: CodeUnit.Type, unusedCapacity: Int @@ -728,7 +713,7 @@ extension _StringGuts { } // We have enough space; check if it's unique and of the correct width. if _fastPath(_object.bitWidth == CodeUnit.bitWidth) { - if _fastPath(isUniqueNative()) { + if _fastPath(_isUniqueNative()) { return nil } } @@ -738,8 +723,6 @@ extension _StringGuts { // Convert ourselves (if needed) to a native string with the specified storage // parameters and call `body` on the resulting native storage. - @usableFromInline - @inlinable internal mutating func withMutableStorage( of type: CodeUnit.Type = CodeUnit.self, @@ -769,8 +752,6 @@ extension _StringGuts { return result } - @usableFromInline - @inlinable @inline(__always) internal mutating func withMutableASCIIStorage( @@ -781,8 +762,6 @@ extension _StringGuts { of: UInt8.self, unusedCapacity: unusedCapacity, body) } - @usableFromInline - @inlinable @inline(__always) internal mutating func withMutableUTF16Storage( @@ -798,19 +777,16 @@ extension _StringGuts { // String API // extension _StringGuts { - @usableFromInline @inlinable internal var _hasStoredCount: Bool { @inline(__always) get { return !_object.isSmallOrCocoa } } - @usableFromInline @inlinable internal var startIndex: Int { return 0 } - @usableFromInline @inlinable internal var endIndex: Int { @inline(__always) get { return count } @@ -831,16 +807,19 @@ extension _StringGuts { @usableFromInline internal var _nonStoredCount: Int { - if _object.isSmall { - return _object.smallUTF8Count - } + @effects(readonly) + get { + if _object.isSmall { + return _object.smallUTF8Count + } #if _runtime(_ObjC) - _sanityCheck(_object.isCocoa) - return _cocoaCount + _sanityCheck(_object.isCocoa) + return _cocoaCount #else - _sanityCheck(_object.isOpaque) - return _opaqueCount + _sanityCheck(_object.isOpaque) + return _opaqueCount #endif + } } @inlinable @@ -888,7 +867,6 @@ extension _StringGuts { /// Get the UTF-16 code unit stored at the specified position in this string. @inlinable // FIXME(sil-serialize-all) - public // @testable func codeUnit(atCheckedOffset offset: Int) -> UTF16.CodeUnit { if _slowPath(_isOpaque) { return _opaqueCodeUnit(atCheckedOffset: offset) @@ -915,8 +893,6 @@ extension _StringGuts { // Copy code units from a slice of this string into a buffer. - @usableFromInline - @inlinable // FIXME(sil-serialize-all) internal func _copy( range: Range, into dest: UnsafeMutableBufferPointer) @@ -936,7 +912,6 @@ extension _StringGuts { } } - @usableFromInline // @opaque internal func _opaqueCopy( range: Range, into dest: UnsafeMutableBufferPointer) @@ -952,13 +927,12 @@ extension _StringGuts { _asOpaque()[range]._copy(into: dest) } - @inlinable - public // TODO(StringGuts): for testing + @usableFromInline mutating func reserveUnusedCapacity( _ unusedCapacity: Int, ascii: Bool = false ) { - if _fastPath(isUniqueNative()) { + if _fastPath(_isUniqueNative()) { if _fastPath( ascii == (_object.bitWidth == 8) && _object.nativeRawStorage.unusedCapacity >= unusedCapacity) { @@ -984,35 +958,36 @@ extension _StringGuts { _invariantCheck() } - @inlinable - public // TODO(StringGuts): for testing + @usableFromInline // @testable mutating func reserveCapacity(_ capacity: Int) { - if _fastPath(isUniqueNative()) { + if _fastPath(_isUniqueNative()) { if _fastPath(_object.nativeRawStorage.capacity >= capacity) { return } } - // TODO (TODO: JIRA): check if we're small and still within capacity + // Small strings can accomodate small capacities + if capacity <= _SmallUTF8String.capacity { + return + } + let selfCount = self.count if isASCII { let storage = _copyToNativeStorage( of: UInt8.self, - from: 0.. 0 else { return } @@ -1034,8 +1009,6 @@ extension _StringGuts { } } - @usableFromInline - @inlinable internal mutating func append(_ other: _UnmanagedUTF16String) { guard other.count > 0 else { return } @@ -1044,8 +1017,6 @@ extension _StringGuts { } } - @usableFromInline - @inlinable internal mutating func append(_ other: _UnmanagedOpaqueString) { guard other.count > 0 else { return } @@ -1054,15 +1025,13 @@ extension _StringGuts { } } - @inlinable - @usableFromInline internal mutating func append(_ other: S) { self.append(other._wholeString._guts, range: other._encodedOffsetRange) } - @inlinable - public // TODO(StringGuts): for testing only + @usableFromInline // @testable + internal mutating func append(_ other: _StringGuts) { // FIXME(TODO: JIRA): shouldn't _isEmptySingleton be sufficient? if _isEmptySingleton || self.count == 0 && !_object.isNative { @@ -1087,7 +1056,6 @@ extension _StringGuts { } } - @usableFromInline // @opaque mutating func _opaqueAppend(opaqueOther other: _StringGuts) { if other._isSmall { // TODO: Fix the visitation pattern for append here. For now, we funnel @@ -1103,8 +1071,8 @@ extension _StringGuts { self.append(other._asOpaque()) } - @inlinable - public // TODO(StringGuts): for testing only + @usableFromInline + internal mutating func append(_ other: _StringGuts, range: Range) { _sanityCheck(range.lowerBound >= 0 && range.upperBound <= other.count) guard range.count > 0 else { return } @@ -1125,7 +1093,6 @@ extension _StringGuts { } } - @usableFromInline // @opaque mutating func _opaqueAppend(opaqueOther other: _StringGuts, range: Range) { if other._isSmall { other._smallUTF8String.withUnmanagedASCII { @@ -1143,9 +1110,8 @@ extension _StringGuts { // FIXME (TODO JIRA): Appending a character onto the end of a string should // really have a less generic implementation, then we can drop @specialize. // - @_specialize(where C == Character._SmallUTF16) - @inlinable @usableFromInline + @_specialize(where C == Character._SmallUTF16) mutating func append(contentsOf other: C) where C.Element == UInt16 { if self._isSmall { @@ -1247,7 +1213,8 @@ extension _StringGuts { _invariantCheck() } - public mutating func replaceSubrange( + @usableFromInline + mutating func replaceSubrange( _ bounds: Range, with newElements: C ) where C : Collection, C.Element == UTF16.CodeUnit { @@ -1259,90 +1226,15 @@ extension _StringGuts { } } -extension _StringGuts : Sequence { - public typealias Element = UTF16.CodeUnit - - @_fixed_layout - public struct Iterator : IteratorProtocol { - public typealias Element = UTF16.CodeUnit - - @usableFromInline - internal let _guts: _StringGuts - @usableFromInline - internal let _endOffset: Int - @usableFromInline - internal var _nextOffset: Int - @usableFromInline - internal var _buffer = _FixedArray16() - @usableFromInline - internal var _bufferIndex: Int = 0 - - @inlinable - @usableFromInline - internal init(_ guts: _StringGuts, range: Range) { - self._guts = guts - self._endOffset = range.upperBound - self._nextOffset = range.lowerBound - if _fastPath(!range.isEmpty) { - _fillBuffer() - } - } - - @inlinable - public mutating func next() -> Element? { - if _fastPath(_bufferIndex < _buffer.count) { - let result = _buffer[_bufferIndex] - _bufferIndex += 1 - return result - } - if _nextOffset == _endOffset { - return nil - } - _fillBuffer() - _bufferIndex = 1 - return _buffer[0] - } - - @usableFromInline - @inline(never) - internal mutating func _fillBuffer() { - _sanityCheck(_buffer.count == 0) - _buffer.count = Swift.min(_buffer.capacity, _endOffset - _nextOffset) - _sanityCheck(_buffer.count > 0) - let guts = _guts // Make a copy to prevent overlapping access to self - _buffer.withUnsafeMutableBufferPointer { buffer in - let range: Range = _nextOffset ..< _nextOffset + buffer.count - guts._copy(range: range, into: buffer) - } - _nextOffset += _buffer.count - } - } - - @inlinable - public func makeIterator() -> Iterator { - return Iterator(self, range: 0..) -> Iterator { - return Iterator(self, range: range) - } -} - extension _StringGuts { // TODO: Drop or unify with String._fromCodeUnits - // - @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal - static func fromCodeUnits( - _ input: Input, + static func fromCodeUnits( + _ input: UnsafeBufferPointer, encoding: Encoding.Type, repairIllFormedSequences: Bool, minimumCapacity: Int = 0 - ) -> (_StringGuts?, hadError: Bool) - where Input.Element == Encoding.CodeUnit { + ) -> (_StringGuts?, hadError: Bool) { // Determine how many UTF-16 code units we'll need guard let (utf16Count, isASCII) = UTF16.transcodedLength( of: input.makeIterator(), @@ -1381,10 +1273,9 @@ extension _StringGuts { extension _StringGuts { // For testing purposes only. Might be both inefficient and too low-level. // There should be an eventual API on String to accomplish something similar. - public // @_testable - static - func _createStringFromUTF16(_ cus: C) -> String - where C.Element == UInt16 { + @usableFromInline // @_testable + static internal + func _createStringFromUTF16(_ cus: UnsafeBufferPointer) -> String { let storage = _SwiftStringStorage.create( capacity: cus.count, count: cus.count) _ = storage._initialize(fromCodeUnits: cus, encoding: UTF16.self) @@ -1400,14 +1291,11 @@ extension _SwiftStringStorage { /// Returns true iff `input` was found to contain invalid code units in the /// specified encoding. If any invalid sequences are found, they are replaced /// with REPLACEMENT CHARACTER (U+FFFD). - @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal - func _initialize( - fromCodeUnits input: Input, + func _initialize( + fromCodeUnits input: UnsafeBufferPointer, encoding: Encoding.Type - ) -> Bool - where Input.Element == Encoding.CodeUnit { + ) -> Bool { var p = self.start let hadError = transcode( input.makeIterator(), diff --git a/stdlib/public/core/StringGutsVisitor.swift b/stdlib/public/core/StringGutsVisitor.swift index 7cc2183d948e2..41fb9ee7f0e09 100644 --- a/stdlib/public/core/StringGutsVisitor.swift +++ b/stdlib/public/core/StringGutsVisitor.swift @@ -18,7 +18,6 @@ // else ARC. // extension String { - @usableFromInline @inlinable @inline(__always) func _visit( @@ -105,7 +104,6 @@ extension String { return opaque(view) } - @usableFromInline @inlinable @inline(__always) func _visit( @@ -195,7 +193,6 @@ extension String { } } -@usableFromInline @inlinable @inline(__always) internal @@ -210,7 +207,6 @@ func _visitGuts( range: range, ascii: ascii, utf16: utf16, opaque: opaque) } -@usableFromInline @inlinable @inline(__always) internal diff --git a/stdlib/public/core/StringHashable.swift b/stdlib/public/core/StringHashable.swift index cc4ab2792da60..d9ffbfab485c4 100644 --- a/stdlib/public/core/StringHashable.swift +++ b/stdlib/public/core/StringHashable.swift @@ -12,196 +12,209 @@ import SwiftShims -func _emptyASCIIHashBuffer() -> _UIntBuffer { - var buffer = _UIntBuffer() - // We don't want the unused bits of a partially filled buffer to collide - // with trailing nuls when hashing - buffer._storage = UInt64.max - return buffer -} - -internal struct ASCIIHasher { - private var buffer = _emptyASCIIHashBuffer() - - internal mutating func consume() -> UInt64? { - if !buffer.isEmpty { - defer { resetBuffer() } - return buffer._storage - } - return nil - } - - private mutating func resetBuffer() { - buffer = _emptyASCIIHashBuffer() - } - - internal mutating func append(_ c: UInt8) -> UInt64? { - if buffer.count < buffer.capacity { - buffer.append(c) - } - - if buffer.count == buffer.capacity { - defer { resetBuffer() } - return buffer._storage - } - return nil - } -} - extension _UnmanagedString where CodeUnit == UInt8 { - // NOT @usableFromInline - @effects(releasenone) - internal func hashASCII(into hasher: inout _Hasher) { - var asciiHasher = ASCIIHasher() - for c in self { - if let combined = asciiHasher.append(UInt8(truncatingIfNeeded: c)) { - hasher.append(combined) - } - } - - if let combined = asciiHasher.consume() { - hasher.append(combined) - } + internal func hashASCII(into core: inout Hasher.Core) { + core.combine(bytes: rawBuffer) } } extension BidirectionalCollection where Element == UInt16, SubSequence == Self { - // NOT @usableFromInline - internal func hashUTF16(into hasher: inout _Hasher) { - var asciiHasher = ASCIIHasher() - + internal func hashUTF16(into core: inout Hasher.Core) { for i in self.indices { let cu = self[i] let cuIsASCII = cu <= 0x7F let isSingleSegmentScalar = self.hasNormalizationBoundary(after: i) - guard cuIsASCII && isSingleSegmentScalar else { - if let combined = asciiHasher.consume() { - hasher.append(combined) - } - - let codeUnitSequence = IteratorSequence( - _NormalizedCodeUnitIterator(self[i.. Int { + return Hasher._hash(seed: seed, bytes: rawBuffer) } } extension _UnmanagedString where CodeUnit == UInt16 { - @effects(releasenone) - @usableFromInline - internal func computeHashValue(into hasher: inout _Hasher) { - self.hashUTF16(into: &hasher) + internal func hash(into hasher: inout Hasher) { + self.hashUTF16(into: &hasher._core) + hasher._core.combine(0xFF as UInt8) // terminator + } + + internal func _rawHashValue(seed: (UInt64, UInt64)) -> Int { + var core = Hasher.Core(seed: seed) + self.hashUTF16(into: &core) + return Int(truncatingIfNeeded: core.finalize()) } } extension _UnmanagedOpaqueString { - @usableFromInline - internal func computeHashValue(into hasher: inout _Hasher) { - self.hashUTF16(into: &hasher) + internal func hash(into hasher: inout Hasher) { + self.hashUTF16(into: &hasher._core) + hasher._core.combine(0xFF as UInt8) // terminator + } + + internal func _rawHashValue(seed: (UInt64, UInt64)) -> Int { + var core = Hasher.Core(seed: seed) + self.hashUTF16(into: &core) + return Int(truncatingIfNeeded: core.finalize()) } } extension _SmallUTF8String { - @usableFromInline - @inlinable - internal func computeHashValue(into hasher: inout _Hasher) { + internal func hash(into hasher: inout Hasher) { +#if arch(i386) || arch(arm) + unsupportedOn32bit() +#else + if isASCII { + self.withUnmanagedASCII { $0.hash(into: &hasher) } + return + } + self.withUnmanagedUTF16 { $0.hash(into: &hasher) } +#endif // 64-bit + } + + internal func _rawHashValue(seed: (UInt64, UInt64)) -> Int { #if arch(i386) || arch(arm) unsupportedOn32bit() #else if isASCII { - return self.withUnmanagedASCII { $0.computeHashValue(into: &hasher) } + return self.withUnmanagedASCII { $0._rawHashValue(seed: seed) } } - return self.withUnmanagedUTF16 { $0.computeHashValue(into: &hasher) } + return self.withUnmanagedUTF16 { $0._rawHashValue(seed: seed) } #endif // 64-bit } } extension _StringGuts { + @effects(releasenone) // FIXME: Is this valid in the opaque case? @usableFromInline - @effects(releasenone) // FIXME: Is this guaranteed in the opaque case? - internal func _hash(into hasher: inout _Hasher) { + internal func hash(into hasher: inout Hasher) { if _isSmall { - return _smallUTF8String.computeHashValue(into: &hasher) + _smallUTF8String.hash(into: &hasher) + return } defer { _fixLifetime(self) } if _slowPath(_isOpaque) { - _asOpaque().computeHashValue(into: &hasher) + _asOpaque().hash(into: &hasher) return } if isASCII { - _unmanagedASCIIView.computeHashValue(into: &hasher) + _unmanagedASCIIView.hash(into: &hasher) return } - _unmanagedUTF16View.computeHashValue(into: &hasher) + _unmanagedUTF16View.hash(into: &hasher) } + @effects(releasenone) // FIXME: Is this valid in the opaque case? @usableFromInline - @effects(releasenone) // FIXME: Is this guaranteed in the opaque case? - internal func _hash(_ range: Range, into hasher: inout _Hasher) { + internal func hash(_ range: Range, into hasher: inout Hasher) { if _isSmall { - return _smallUTF8String[range].computeHashValue(into: &hasher) + _smallUTF8String[range].hash(into: &hasher) + return } defer { _fixLifetime(self) } if _slowPath(_isOpaque) { - _asOpaque()[range].computeHashValue(into: &hasher) + _asOpaque()[range].hash(into: &hasher) return } if isASCII { - _unmanagedASCIIView[range].computeHashValue(into: &hasher) + _unmanagedASCIIView[range].hash(into: &hasher) return } - _unmanagedUTF16View[range].computeHashValue(into: &hasher) + _unmanagedUTF16View[range].hash(into: &hasher) + } + + @effects(releasenone) // FIXME: Is this valid in the opaque case? + @usableFromInline + internal func _rawHashValue(seed: (UInt64, UInt64)) -> Int { + if _isSmall { + return _smallUTF8String._rawHashValue(seed: seed) + } + + defer { _fixLifetime(self) } + if _slowPath(_isOpaque) { + return _asOpaque()._rawHashValue(seed: seed) + } + if isASCII { + return _unmanagedASCIIView._rawHashValue(seed: seed) + } + return _unmanagedUTF16View._rawHashValue(seed: seed) + } + + @effects(releasenone) // FIXME: Is this valid in the opaque case? + @usableFromInline + internal func _rawHashValue( + _ range: Range, + seed: (UInt64, UInt64) + ) -> Int { + if _isSmall { + return _smallUTF8String[range]._rawHashValue(seed: seed) + } + + defer { _fixLifetime(self) } + if _slowPath(_isOpaque) { + return _asOpaque()[range]._rawHashValue(seed: seed) + } + if isASCII { + return _unmanagedASCIIView[range]._rawHashValue(seed: seed) + } + return _unmanagedUTF16View[range]._rawHashValue(seed: seed) } } extension String : Hashable { - /// The string's hash value. + /// Hashes the essential components of this value by feeding them into the + /// given hasher. /// - /// Hash values are not guaranteed to be equal across different executions of - /// your program. Do not save hash values to use during a future execution. + /// - Parameter hasher: The hasher to use when combining the components + /// of this instance. @inlinable - public var hashValue: Int { - return _hashValue(for: self) + public func hash(into hasher: inout Hasher) { + _guts.hash(into: &hasher) } @inlinable - public func _hash(into hasher: inout _Hasher) { - _guts._hash(into: &hasher) + public func _rawHashValue(seed: (UInt64, UInt64)) -> Int { + return _guts._rawHashValue(seed: seed) } } extension StringProtocol { + /// Hashes the essential components of this value by feeding them into the + /// given hasher. + /// + /// - Parameter hasher: The hasher to use when combining the components + /// of this instance. @inlinable - public var hashValue : Int { - return _hashValue(for: self) + public func hash(into hasher: inout Hasher) { + _wholeString._guts.hash(_encodedOffsetRange, into: &hasher) } @inlinable - public func _hash(into hasher: inout _Hasher) { - _wholeString._guts._hash(_encodedOffsetRange, into: &hasher) + public func _rawHashValue(seed: (UInt64, UInt64)) -> Int { + return _wholeString._guts._rawHashValue(_encodedOffsetRange, seed: seed) } } diff --git a/stdlib/public/core/StringIndex.swift b/stdlib/public/core/StringIndex.swift index 44605155acce5..dd4e624c4b024 100644 --- a/stdlib/public/core/StringIndex.swift +++ b/stdlib/public/core/StringIndex.swift @@ -33,22 +33,18 @@ extension String { /// Convenience accessors extension String.Index._Cache { @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal var utf16: Void? { if case .utf16 = self { return () } else { return nil } } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal var utf8: String.Index._UTF8Buffer? { if case .utf8(let r) = self { return r } else { return nil } } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal var character: UInt16? { if case .character(let r) = self { return r } else { return nil } } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal var unicodeScalar: UnicodeScalar? { if case .unicodeScalar(let r) = self { return r } else { return nil } } @@ -69,9 +65,14 @@ extension String.Index : Comparable { } extension String.Index : Hashable { + /// Hashes the essential components of this value by feeding them into the + /// given hasher. + /// + /// - Parameter hasher: The hasher to use when combining the components + /// of this instance. @inlinable // FIXME(sil-serialize-all) - public var hashValue: Int { - return _compoundOffset.hashValue + public func hash(into hasher: inout Hasher) { + hasher.combine(_compoundOffset) } } @@ -88,21 +89,17 @@ extension String.Index { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal init(encodedOffset o: Int, transcodedOffset: Int = 0, _ c: _Cache) { _compoundOffset = UInt64(o << _Self._strideBits | transcodedOffset) _cache = c } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static var _strideBits : Int { return 2 } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static var _mask : UInt64 { return (1 &<< _Self._strideBits) &- 1 } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal mutating func _setEncodedOffset(_ x: Int) { _compoundOffset = UInt64(x << _Self._strideBits) } @@ -115,7 +112,6 @@ extension String.Index { /// The offset of this index within whatever encoding this is being viewed as @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal var _transcodedOffset : Int { get { return Int(_compoundOffset & _Self._mask) diff --git a/stdlib/public/core/StringIndexConversions.swift b/stdlib/public/core/StringIndexConversions.swift index abba2a7d9f176..c97cd873bfda0 100644 --- a/stdlib/public/core/StringIndexConversions.swift +++ b/stdlib/public/core/StringIndexConversions.swift @@ -26,7 +26,7 @@ extension String.Index { /// print(cafe) /// // Prints "Café" /// - /// let scalarsIndex = cafe.unicodeScalars.index(of: "e")! + /// let scalarsIndex = cafe.unicodeScalars.firstIndex(of: "e")! /// let stringIndex = String.Index(scalarsIndex, within: cafe)! /// /// print(cafe[...stringIndex]) @@ -49,7 +49,6 @@ extension String.Index { /// `sourcePosition` must be a valid index of at least one of the views /// of `target`. /// - target: The string referenced by the resulting index. - @inlinable // FIXME(sil-serialize-all) public init?( _ sourcePosition: String.Index, within target: String @@ -67,7 +66,7 @@ extension String.Index { /// uses this method find the same position in the string's `utf8` view. /// /// let cafe = "Café" - /// if let i = cafe.index(of: "é") { + /// if let i = cafe.firstIndex(of: "é") { /// let j = i.samePosition(in: cafe.utf8)! /// print(Array(cafe.utf8[j...])) /// } @@ -96,7 +95,7 @@ extension String.Index { /// uses this method find the same position in the string's `utf16` view. /// /// let cafe = "Café" - /// if let i = cafe.index(of: "é") { + /// if let i = cafe.firstIndex(of: "é") { /// let j = i.samePosition(in: cafe.utf16)! /// print(cafe.utf16[j]) /// } diff --git a/stdlib/public/core/StringLegacy.swift b/stdlib/public/core/StringLegacy.swift index 150e001d88528..fc365f79bc8b3 100644 --- a/stdlib/public/core/StringLegacy.swift +++ b/stdlib/public/core/StringLegacy.swift @@ -66,7 +66,6 @@ extension String { // TODO: since this is generally useful, make public via evolution proposal. extension BidirectionalCollection { @inlinable - @usableFromInline internal func _ends( with suffix: Suffix, by areEquivalent: (Element,Element) -> Bool ) -> Bool where Suffix.Element == Element { @@ -82,7 +81,6 @@ extension BidirectionalCollection { extension BidirectionalCollection where Element: Equatable { @inlinable - @usableFromInline internal func _ends( with suffix: Suffix ) -> Bool where Suffix.Element == Element { @@ -162,7 +160,6 @@ extension StringProtocol { } extension String { - @inlinable // FIXME(sil-serialize-all) public func hasPrefix(_ prefix: String) -> Bool { let prefixCount = prefix._guts.count if prefixCount == 0 { return true } @@ -210,7 +207,6 @@ extension String { return self.starts(with: prefix) } - @inlinable // FIXME(sil-serialize-all) public func hasSuffix(_ suffix: String) -> Bool { let suffixCount = suffix._guts.count if suffixCount == 0 { return true } @@ -297,7 +293,6 @@ extension String { extension _StringGuts { @inlinable - @usableFromInline func _repeated(_ n: Int) -> _StringGuts { _sanityCheck(n > 1) if self._isSmall { diff --git a/stdlib/public/core/StringObject.swift b/stdlib/public/core/StringObject.swift index ee4edfae4986c..5220f7d8ba562 100644 --- a/stdlib/public/core/StringObject.swift +++ b/stdlib/public/core/StringObject.swift @@ -52,7 +52,6 @@ struct _StringObject { #endif #if arch(i386) || arch(arm) - @usableFromInline @inlinable @inline(__always) internal @@ -62,7 +61,6 @@ struct _StringObject { _invariantCheck() } #else - @usableFromInline @inlinable @inline(__always) internal @@ -80,7 +78,6 @@ extension _StringObject { public typealias _RawBitPattern = UInt #endif - @usableFromInline @inlinable internal var rawBits: _RawBitPattern { @@ -95,7 +92,6 @@ extension _StringObject { } } - @usableFromInline @inlinable @inline(__always) // TODO: private @@ -111,7 +107,6 @@ extension _StringObject { #endif } - @usableFromInline @inlinable @inline(__always) // TODO: private @@ -131,7 +126,6 @@ extension _StringObject { // we are giving up on compile-time constant folding of ARC of values. Thus, // this should only be called from the callee of a non-inlineable function // that has no knowledge of the value-ness of the object. - @usableFromInline @inlinable @inline(__always) // TODO: private @@ -181,7 +175,6 @@ extension _StringObject { // extension _StringObject { #if arch(i386) || arch(arm) - @usableFromInline @inlinable internal static var _isCocoaBit: UInt { @@ -191,7 +184,6 @@ extension _StringObject { } } - @usableFromInline @inlinable internal static var _isOpaqueBit: UInt { @@ -201,7 +193,6 @@ extension _StringObject { } } - @usableFromInline @inlinable internal static var _twoByteBit: UInt { @@ -211,7 +202,6 @@ extension _StringObject { } } #else // !(arch(i386) || arch(arm)) - @usableFromInline @inlinable internal static var _isValueBit: UInt { @@ -225,7 +215,6 @@ extension _StringObject { // After deciding isValue, which of the two variants (on both sides) are we. // That is, native vs objc or unsafe vs small. - @usableFromInline @inlinable internal static var _subVariantBit: UInt { @@ -235,7 +224,6 @@ extension _StringObject { } } - @usableFromInline @inlinable internal static var _isOpaqueBit: UInt { @@ -245,7 +233,6 @@ extension _StringObject { } } - @usableFromInline @inlinable internal static var _twoByteBit: UInt { @@ -256,7 +243,6 @@ extension _StringObject { } // There are 4 sub-variants depending on the isValue and subVariant bits - @usableFromInline @inlinable internal static var _variantMask: UInt { @@ -265,7 +251,6 @@ extension _StringObject { _isValueBit._value, _subVariantBit._value)) } } - @usableFromInline @inlinable internal static var _payloadMask: UInt { @@ -275,7 +260,6 @@ extension _StringObject { } } - @usableFromInline @inlinable internal var _variantBits: UInt { @@ -286,7 +270,6 @@ extension _StringObject { } #endif // arch(i386) || arch(arm) - @usableFromInline @inlinable internal var referenceBits: UInt { @@ -304,7 +287,6 @@ extension _StringObject { } } - @usableFromInline @inlinable internal var payloadBits: UInt { @@ -344,7 +326,6 @@ internal var _emptyStringAddressBits: UInt { extension _StringObject { #if arch(i386) || arch(arm) - @usableFromInline @inlinable internal var isEmptySingleton: Bool { @@ -357,7 +338,6 @@ extension _StringObject { } } - @usableFromInline @inlinable @inline(__always) internal @@ -365,7 +345,6 @@ extension _StringObject { self.init(.unmanagedSingleByte, _emptyStringAddressBits) } #else - @usableFromInline @inlinable internal static var _emptyStringBitPattern: UInt { @@ -373,7 +352,6 @@ extension _StringObject { get { return _smallUTF8TopNibble } } - @usableFromInline @inlinable internal var isEmptySingleton: Bool { @@ -381,7 +359,6 @@ extension _StringObject { get { return rawBits == _StringObject._emptyStringBitPattern } } - @usableFromInline @inlinable @inline(__always) internal @@ -405,24 +382,23 @@ extension _StringObject { #if arch(i386) || arch(arm) #else - @usableFromInline @inlinable internal + @inlinable internal static var _topNibbleMask: UInt { @inline(__always) get { return 0xF000_0000_0000_0000 } } - @usableFromInline @inlinable internal + @inlinable internal static var _smallUTF8TopNibble: UInt { @inline(__always) get { return 0xE000_0000_0000_0000 } } - @usableFromInline @inlinable internal + @inlinable internal static var _smallUTF8CountMask: UInt { @inline(__always) get { return 0x0F00_0000_0000_0000 } } #endif - @usableFromInline @inlinable internal var _isSmallUTF8: Bool { @@ -443,7 +419,6 @@ extension _StringObject { // keep the count. The StringObject represents the second word of a // SmallUTF8String. // - @usableFromInline @inlinable internal var asSmallUTF8SecondWord: UInt { @@ -458,7 +433,6 @@ extension _StringObject { } } - @usableFromInline @inlinable internal var smallUTF8Count: Int { @@ -475,7 +449,6 @@ extension _StringObject { } } - @usableFromInline @inlinable internal init(_smallUTF8SecondWord bits: UInt) { @@ -494,7 +467,6 @@ extension _StringObject { // TODO: private! // extension _StringObject { - @usableFromInline @inlinable internal // TODO: private! var asNativeObject: AnyObject { @@ -520,7 +492,6 @@ extension _StringObject { } #if _runtime(_ObjC) - @usableFromInline @inlinable internal // TODO: private! var asCocoaObject: _CocoaString { @@ -546,7 +517,6 @@ extension _StringObject { } #endif - @usableFromInline @inlinable internal // TODO: private! var asOpaqueObject: _OpaqueString { @@ -558,7 +528,6 @@ extension _StringObject { } } - @usableFromInline @inlinable internal var asUnmanagedRawStart: UnsafeRawPointer { @@ -583,7 +552,6 @@ extension _StringObject { // // Determine which of the 4 major variants we are // - @usableFromInline @inlinable internal var isNative: Bool { @@ -598,7 +566,6 @@ extension _StringObject { } } - @usableFromInline @inlinable internal var isCocoa: Bool { @@ -626,7 +593,6 @@ extension _StringObject { #endif } - @usableFromInline @inlinable internal var isValue: Bool { @@ -644,7 +610,6 @@ extension _StringObject { } } - @usableFromInline @inlinable internal var isUnmanaged: Bool { @@ -663,7 +628,6 @@ extension _StringObject { } } - @usableFromInline @inlinable internal var isSmall: Bool { @@ -682,7 +646,6 @@ extension _StringObject { } } - @usableFromInline @inlinable internal var isSmallOrCocoa: Bool { @@ -704,7 +667,6 @@ extension _StringObject { // // Frequently queried properties // - @usableFromInline @inlinable internal var isContiguous: Bool { @@ -725,7 +687,6 @@ extension _StringObject { } } - @usableFromInline @inlinable internal var isOpaque: Bool { @@ -733,7 +694,6 @@ extension _StringObject { get { return !isContiguous } } - @usableFromInline @inlinable internal var isContiguousCocoa: Bool { @@ -741,7 +701,6 @@ extension _StringObject { get { return isContiguous && isCocoa } } - @usableFromInline @inlinable internal var isNoncontiguousCocoa: Bool { @@ -750,7 +709,6 @@ extension _StringObject { } @inlinable - public // @testable var isSingleByte: Bool { @inline(__always) get { @@ -770,13 +728,11 @@ extension _StringObject { } @inlinable - public // @testable var byteWidth: Int { @inline(__always) get { return isSingleByte ? 1 : 2 } } - @usableFromInline @inlinable var bitWidth: Int { @inline(__always) @@ -784,20 +740,17 @@ extension _StringObject { } @inlinable - public // @testable var isContiguousASCII: Bool { @inline(__always) get { return isContiguous && isSingleByte } } @inlinable - public // @testable var isContiguousUTF16: Bool { @inline(__always) get { return isContiguous && !isSingleByte } } - @usableFromInline @inlinable @inline(__always) internal @@ -813,7 +766,6 @@ extension _StringObject { } @inlinable - public // @testable var nativeRawStorage: _SwiftRawStringStorage { @inline(__always) get { _sanityCheck(isNative) @@ -825,7 +777,6 @@ extension _StringObject { extension _StringObject { @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func _invariantCheck() { #if INTERNAL_CHECKS_ENABLED _sanityCheck(MemoryLayout<_StringObject>.size == 8) @@ -865,7 +816,6 @@ extension _StringObject { // Conveniently construct, tag, flag, etc. StringObjects // extension _StringObject { - @usableFromInline @inlinable @inline(__always) internal @@ -944,7 +894,6 @@ extension _StringObject { #endif } - @usableFromInline @inlinable @inline(__always) internal @@ -963,7 +912,6 @@ extension _StringObject { isTwoByte: !isSingleByte) } - @usableFromInline @inlinable @inline(__always) internal @@ -976,7 +924,6 @@ extension _StringObject { } #if _runtime(_ObjC) - @usableFromInline @inlinable @inline(__always) internal @@ -989,7 +936,6 @@ extension _StringObject { isSingleByte: isSingleByte) } #else - @usableFromInline @inlinable @inline(__always) internal @@ -1002,7 +948,6 @@ extension _StringObject { } #endif - @usableFromInline @inlinable @inline(__always) internal @@ -1018,7 +963,6 @@ extension _StringObject { _sanityCheck(isSingleByte == (CodeUnit.bitWidth == 8)) } - @usableFromInline @inlinable @inline(__always) internal diff --git a/stdlib/public/core/StringProtocol.swift b/stdlib/public/core/StringProtocol.swift index 1b5a87c5da60c..0dacf5a8fa8b0 100644 --- a/stdlib/public/core/StringProtocol.swift +++ b/stdlib/public/core/StringProtocol.swift @@ -162,7 +162,6 @@ internal protocol _SwiftStringView { extension _SwiftStringView { @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var _ephemeralContent : String { return _persistentContent } } @@ -179,7 +178,6 @@ extension StringProtocol { extension String : _SwiftStringView { @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var _persistentContent : String { return self } diff --git a/stdlib/public/core/StringRangeReplaceableCollection.swift b/stdlib/public/core/StringRangeReplaceableCollection.swift index 8ec331e0b64e6..fe404b2b493ed 100644 --- a/stdlib/public/core/StringRangeReplaceableCollection.swift +++ b/stdlib/public/core/StringRangeReplaceableCollection.swift @@ -89,8 +89,12 @@ extension String : StringProtocol, RangeReplaceableCollection { @inlinable // FIXME(sil-serialize-all) public var endIndex: Index { return Index(encodedOffset: _guts.count) } + /// The number of characters in a string. + public var count: Int { + return distance(from: startIndex, to: endIndex) + } + @inlinable - @usableFromInline @inline(__always) internal func _boundsCheck(_ index: Index) { _precondition(index.encodedOffset >= 0 && index.encodedOffset < _guts.count, @@ -98,7 +102,6 @@ extension String : StringProtocol, RangeReplaceableCollection { } @inlinable - @usableFromInline @inline(__always) internal func _boundsCheck(_ range: Range) { _precondition( @@ -108,7 +111,6 @@ extension String : StringProtocol, RangeReplaceableCollection { } @inlinable - @usableFromInline @inline(__always) internal func _boundsCheck(_ range: ClosedRange) { _precondition( @@ -117,8 +119,6 @@ extension String : StringProtocol, RangeReplaceableCollection { "String index range is out of bounds") } - @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func _index(atEncodedOffset offset: Int) -> Index { return _visitGuts(_guts, args: offset, ascii: { ascii, offset in return ascii.characterIndex(atOffset: offset) }, @@ -132,7 +132,6 @@ extension String : StringProtocol, RangeReplaceableCollection { /// - Parameter i: A valid index of the collection. `i` must be less than /// `endIndex`. /// - Returns: The index value immediately after `i`. - @inlinable // FIXME(sil-serialize-all) public func index(after i: Index) -> Index { return _visitGuts(_guts, args: i, ascii: { ascii, i in ascii.characterIndex(after: i) }, @@ -145,7 +144,6 @@ extension String : StringProtocol, RangeReplaceableCollection { /// - Parameter i: A valid index of the collection. `i` must be greater than /// `startIndex`. /// - Returns: The index value immediately before `i`. - @inlinable // FIXME(sil-serialize-all) public func index(before i: Index) -> Index { return _visitGuts(_guts, args: i, ascii: { ascii, i in ascii.characterIndex(before: i) }, @@ -175,7 +173,6 @@ extension String : StringProtocol, RangeReplaceableCollection { /// to `index(before:)`. /// /// - Complexity: O(*n*), where *n* is the absolute value of `n`. - @inlinable // FIXME(sil-serialize-all) public func index(_ i: Index, offsetBy n: IndexDistance) -> Index { return _visitGuts(_guts, args: (i, n), ascii: { ascii, args in let (i, n) = args @@ -223,7 +220,6 @@ extension String : StringProtocol, RangeReplaceableCollection { /// the method returns `nil`. /// /// - Complexity: O(*n*), where *n* is the absolute value of `n`. - @inlinable // FIXME(sil-serialize-all) public func index( _ i: Index, offsetBy n: IndexDistance, limitedBy limit: Index ) -> Index? { @@ -245,7 +241,6 @@ extension String : StringProtocol, RangeReplaceableCollection { /// - Returns: The distance between `start` and `end`. /// /// - Complexity: O(*n*), where *n* is the resulting distance. - @inlinable // FIXME(sil-serialize-all) public func distance(from start: Index, to end: Index) -> IndexDistance { return _visitGuts(_guts, args: (start, end), ascii: { ascii, args in let (start, end) = args @@ -262,16 +257,15 @@ extension String : StringProtocol, RangeReplaceableCollection { /// For example, this code finds the first letter after the first space: /// /// let str = "Greetings, friend! How are you?" - /// let firstSpace = str.index(of: " ") ?? str.endIndex + /// let firstSpace = str.firstIndex(of: " ") ?? str.endIndex /// let substr = str[firstSpace...] - /// if let nextCapital = substr.index(where: { $0 >= "A" && $0 <= "Z" }) { + /// if let nextCapital = substr.firstIndex(where: { $0 >= "A" && $0 <= "Z" }) { /// print("Capital after a space: \(str[nextCapital])") /// } /// // Prints "Capital after a space: H" /// /// - Parameter i: A valid index of the string. `i` must be less than the /// string's end index. - @inlinable // FIXME(sil-serialize-all) public subscript(i: Index) -> Character { return _visitGuts(_guts, args: i, ascii: { ascii, i in return ascii.character(at: i) }, @@ -315,7 +309,6 @@ extension String { /// to allocate. /// /// - Complexity: O(*n*) - @inlinable // FIXME(sil-serialize-all) public mutating func reserveCapacity(_ n: Int) { _guts.reserveCapacity(n) } @@ -330,7 +323,6 @@ extension String { /// // Prints "Globe 🌍" /// /// - Parameter c: The character to append to the string. - @inlinable // FIXME(sil-serialize-all) public mutating func append(_ c: Character) { if let small = c._smallUTF16 { _guts.append(contentsOf: small) @@ -340,12 +332,10 @@ extension String { } } - @inlinable // FIXME(sil-serialize-all) public mutating func append(contentsOf newElements: String) { append(newElements) } - @inlinable // FIXME(sil-serialize-all) public mutating func append(contentsOf newElements: Substring) { _guts.append( newElements._wholeString._guts, @@ -440,7 +430,7 @@ extension String { /// removes the hyphen from the middle of a string. /// /// var nonempty = "non-empty" - /// if let i = nonempty.index(of: "-") { + /// if let i = nonempty.firstIndex(of: "-") { /// nonempty.remove(at: i) /// } /// print(nonempty) @@ -464,7 +454,6 @@ extension String { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func _stride(of i: Index) -> Int { if case .character(let stride) = i._cache { // TODO: should _fastPath the case somehow diff --git a/stdlib/public/core/StringStorage.swift b/stdlib/public/core/StringStorage.swift index 02a9a8eade54a..568440749030e 100644 --- a/stdlib/public/core/StringStorage.swift +++ b/stdlib/public/core/StringStorage.swift @@ -13,14 +13,14 @@ import SwiftShims @_fixed_layout -public +@usableFromInline class _SwiftRawStringStorage : _SwiftNativeNSString { @nonobjc - public // @testable + @usableFromInline final var capacity: Int @nonobjc - public // @testable + @usableFromInline final var count: Int @nonobjc @@ -29,7 +29,6 @@ class _SwiftRawStringStorage : _SwiftNativeNSString { } @inlinable - @usableFromInline @nonobjc internal var rawStart: UnsafeMutableRawPointer { _abstract() @@ -37,7 +36,6 @@ class _SwiftRawStringStorage : _SwiftNativeNSString { @inlinable @nonobjc - public // @testable final var unusedCapacity: Int { _sanityCheck(capacity >= count) return capacity - count @@ -48,14 +46,16 @@ internal typealias _ASCIIStringStorage = _SwiftStringStorage internal typealias _UTF16StringStorage = _SwiftStringStorage @_fixed_layout -public final class _SwiftStringStorage +@usableFromInline +final class _SwiftStringStorage : _SwiftRawStringStorage, _NSStringCore where CodeUnit : UnsignedInteger & FixedWidthInteger { /// Create uninitialized storage of at least the specified capacity. - @inlinable @usableFromInline @nonobjc + @_specialize(where CodeUnit == UInt8) + @_specialize(where CodeUnit == UInt16) internal static func create( capacity: Int, count: Int = 0 @@ -64,8 +64,10 @@ where CodeUnit : UnsignedInteger & FixedWidthInteger { #if arch(i386) || arch(arm) #else - _sanityCheck((CodeUnit.self != UInt8.self || capacity > 15), - "Should prefer a small representation") + // TODO(SR-7594): Restore below invariant + // _sanityCheck( + // CodeUnit.self != UInt8.self || capacity > _SmallUTF8String.capacity, + // "Should prefer a small representation") #endif // 64-bit let storage = Builtin.allocWithTailElems_1( @@ -84,7 +86,6 @@ where CodeUnit : UnsignedInteger & FixedWidthInteger { } @inlinable - @usableFromInline @nonobjc internal override final var rawStart: UnsafeMutableRawPointer { return UnsafeMutableRawPointer(start) @@ -94,24 +95,28 @@ where CodeUnit : UnsignedInteger & FixedWidthInteger { // NSString API @objc(initWithCoder:) - public convenience init(coder aDecoder: AnyObject) { + @usableFromInline + convenience init(coder aDecoder: AnyObject) { _sanityCheckFailure("init(coder:) not implemented for _SwiftStringStorage") } @objc(length) - public var length: Int { + @usableFromInline + var length: Int { return count } @objc(characterAtIndex:) - public func character(at index: Int) -> UInt16 { + @usableFromInline + func character(at index: Int) -> UInt16 { defer { _fixLifetime(self) } precondition(index >= 0 && index < count, "Index out of bounds") return UInt16(start[index]) } @objc(getCharacters:range:) - public func getCharacters( + @usableFromInline + func getCharacters( _ buffer: UnsafeMutablePointer, range aRange: _SwiftNSRange ) { @@ -129,13 +134,15 @@ where CodeUnit : UnsignedInteger & FixedWidthInteger { } @objc(_fastCharacterContents) - public func _fastCharacterContents() -> UnsafePointer? { + @usableFromInline + func _fastCharacterContents() -> UnsafePointer? { guard CodeUnit.self == UInt16.self else { return nil } return UnsafePointer(rawStart.assumingMemoryBound(to: UInt16.self)) } @objc(copyWithZone:) - public func copy(with zone: _SwiftNSZone?) -> AnyObject { + @usableFromInline + func copy(with zone: _SwiftNSZone?) -> AnyObject { // While _SwiftStringStorage instances aren't immutable in general, // mutations may only occur when instances are uniquely referenced. // Therefore, it is safe to return self here; any outstanding Objective-C @@ -149,42 +156,39 @@ extension _SwiftStringStorage { // Basic properties @inlinable - @usableFromInline @nonobjc internal final var start: UnsafeMutablePointer { return UnsafeMutablePointer(Builtin.projectTailElems(self, CodeUnit.self)) } @inlinable - @usableFromInline @nonobjc internal final var end: UnsafeMutablePointer { return start + count } @inlinable - @usableFromInline @nonobjc internal final var capacityEnd: UnsafeMutablePointer { return start + capacity } @inlinable - @usableFromInline @nonobjc var usedBuffer: UnsafeMutableBufferPointer { return UnsafeMutableBufferPointer(start: start, count: count) } @inlinable - @usableFromInline @nonobjc var unusedBuffer: UnsafeMutableBufferPointer { - return UnsafeMutableBufferPointer(start: end, count: capacity - count) + @inline(__always) + get { + return UnsafeMutableBufferPointer(start: end, count: capacity - count) + } } @inlinable - @usableFromInline @nonobjc var unmanagedView: _UnmanagedString { return _UnmanagedString(start: self.start, count: self.count) @@ -194,8 +198,6 @@ extension _SwiftStringStorage { extension _SwiftStringStorage { // Append operations - @inlinable - @usableFromInline @nonobjc internal final func _appendInPlace( _ other: _UnmanagedString @@ -207,8 +209,6 @@ extension _SwiftStringStorage { self.count += otherCount } - @inlinable - @usableFromInline @nonobjc internal final func _appendInPlace(_ other: _UnmanagedOpaqueString) { let otherCount = Int(other.count) @@ -217,8 +217,6 @@ extension _SwiftStringStorage { self.count += otherCount } - @inlinable - @usableFromInline @nonobjc internal final func _appendInPlace(contentsOf other: C) where C.Element == CodeUnit { @@ -231,8 +229,6 @@ extension _SwiftStringStorage { count += otherCount } - @inlinable - @usableFromInline @_specialize(where C == Character._SmallUTF16, CodeUnit == UInt8) @nonobjc internal final func _appendInPlaceUTF16(contentsOf other: C) @@ -250,8 +246,6 @@ extension _SwiftStringStorage { } extension _SwiftStringStorage { - @inlinable - @usableFromInline @nonobjc internal final func _appendInPlace(_ other: _StringGuts, range: Range) { if _slowPath(other._isOpaque) { @@ -282,8 +276,6 @@ extension _SwiftStringStorage { _appendInPlace(other._asOpaque()[range]) } - @inlinable - @usableFromInline @nonobjc internal final func _appendInPlace(_ other: _StringGuts) { if _slowPath(other._isOpaque) { @@ -312,15 +304,11 @@ extension _SwiftStringStorage { _appendInPlace(other._asOpaque()) } - @inlinable - @usableFromInline @nonobjc internal final func _appendInPlace(_ other: String) { self._appendInPlace(other._guts) } - @inlinable - @usableFromInline @nonobjc internal final func _appendInPlace(_ other: S) { self._appendInPlace( diff --git a/stdlib/public/core/StringSwitch.swift b/stdlib/public/core/StringSwitch.swift index bd0e70a301f87..f708165d1d03a 100644 --- a/stdlib/public/core/StringSwitch.swift +++ b/stdlib/public/core/StringSwitch.swift @@ -45,7 +45,6 @@ internal typealias _StringSwitchCache = Dictionary @usableFromInline // FIXME(sil-serialize-all) internal struct _StringSwitchContext { @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init( cases: [StaticString], cachePtr: UnsafeMutablePointer<_StringSwitchCache> @@ -95,7 +94,6 @@ func _findStringSwitchCaseWithCache( /// Builds the string switch case. @inlinable // FIXME(sil-serialize-all) -@usableFromInline // FIXME(sil-serialize-all) internal func _createStringTableCache(_ cacheRawPtr: Builtin.RawPointer) { let context = UnsafePointer<_StringSwitchContext>(cacheRawPtr).pointee var cache = _StringSwitchCache() diff --git a/stdlib/public/core/StringUTF16.swift b/stdlib/public/core/StringUTF16.swift index 413b1596222a3..5a97108a52981 100644 --- a/stdlib/public/core/StringUTF16.swift +++ b/stdlib/public/core/StringUTF16.swift @@ -55,7 +55,7 @@ extension String { /// `String` type's `init(_:)` initializer. /// /// let favemoji = "My favorite emoji is 🎉" - /// if let i = favemoji.utf16.index(where: { $0 >= 128 }) { + /// if let i = favemoji.utf16.firstIndex(where: { $0 >= 128 }) { /// let asciiPrefix = String(favemoji.utf16[.. Int { return _guts.startIndex + i } @@ -256,20 +254,17 @@ extension String { #endif @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_ _guts: _StringGuts) { self.init(_guts, offset: 0, length: _guts.count) } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_ _guts: _StringGuts, offset: Int, length: Int) { self._offset = offset self._length = length self._guts = _guts } - @inlinable // FIXME(sil-serialize-all) public var description: String { return String(_guts._extractSlice(_encodedOffsetRange)) } @@ -307,7 +302,7 @@ extension String { /// another string's `utf16` view. /// /// let picnicGuest = "Deserving porcupine" - /// if let i = picnicGuest.utf16.index(of: 32) { + /// if let i = picnicGuest.utf16.firstIndex(of: 32) { /// let adjective = String(picnicGuest.utf16[.. { return _offset..<_offset+_length } @@ -387,7 +378,7 @@ extension String.UTF16View.Index { /// /// let cafe = "Café 🍵" /// - /// let stringIndex = cafe.index(of: "é")! + /// let stringIndex = cafe.firstIndex(of: "é")! /// let utf16Index = String.Index(stringIndex, within: cafe.utf16)! /// /// print(cafe.utf16[...utf16Index]) @@ -415,7 +406,7 @@ extension String.UTF16View.Index { /// position in the string's `unicodeScalars` view. /// /// let cafe = "Café 🍵" - /// let i = cafe.utf16.index(of: 32)! + /// let i = cafe.utf16.firstIndex(of: 32)! /// let j = i.samePosition(in: cafe.unicodeScalars)! /// print(cafe.unicodeScalars[.. Index { - if _fastPath(_guts.isASCII) { return Index(encodedOffset: n) } + internal func _nonASCIIIndex(atEncodedOffset n: Int) -> Index { + _sanityCheck(!_guts._isASCIIOrSmallASCII) let count = _guts.count if n == count { return endIndex } + let buffer: Index._UTF8Buffer = _visitGuts( + _guts, range: (n..( + from i: inout Iter + ) -> Index._UTF8Buffer where Iter.Element == UInt16 { var p = UTF16.ForwardParser() - var i = _guts.makeIterator(in: n.. buffer.capacity { break Loop } + if buffer.count + u8.count > buffer.capacity { + return buffer + } buffer.append(contentsOf: u8) case .error: let u8 = Unicode.UTF8.encodedReplacementCharacter - if buffer.count + u8.count > buffer.capacity { break Loop } + if buffer.count + u8.count > buffer.capacity { + return buffer + } buffer.append(contentsOf: u8) case .emptyInput: - break Loop + return buffer } } - return Index(encodedOffset: n, .utf8(buffer: buffer)) } /// Returns the next consecutive position after `i`. @@ -196,16 +224,25 @@ extension String { @inlinable // FIXME(sil-serialize-all) @inline(__always) public func index(after i: Index) -> Index { - if _fastPath(_guts.isASCII) { + if _fastPath(_guts._isASCIIOrSmallASCII) { precondition(i.encodedOffset < _guts.count) return Index(encodedOffset: i.encodedOffset + 1) } + return _nonASCIIIndex(after: i) + } + + @inline(never) + @effects(releasenone) + @usableFromInline + internal func _nonASCIIIndex(after i: Index) -> Index { + _sanityCheck(!_guts._isASCIIOrSmallASCII) + var j = i // Ensure j's cache is utf8 if _slowPath(j._cache.utf8 == nil) { - j = _index(atEncodedOffset: j.encodedOffset) + j = _nonASCIIIndex(atEncodedOffset: j.encodedOffset) precondition(j != endIndex, "Index out of bounds") } @@ -241,16 +278,24 @@ extension String { .utf8(buffer: nextBuffer)) } // If nothing left in the buffer, refill it. - return _index(atEncodedOffset: j.encodedOffset + scalarLength16) + return _nonASCIIIndex(atEncodedOffset: j.encodedOffset + scalarLength16) } @inlinable // FIXME(sil-serialize-all) public func index(before i: Index) -> Index { - if _fastPath(_guts.isASCII) { + if _fastPath(_guts._isASCIIOrSmallASCII) { precondition(i.encodedOffset > 0) return Index(encodedOffset: i.encodedOffset - 1) } + return _nonASCIIIndex(before: i) + } + + @inline(never) + @effects(releasenone) + @usableFromInline + internal func _nonASCIIIndex(before i: Index) -> Index { + _sanityCheck(!_guts._isASCIIOrSmallASCII) if i._transcodedOffset != 0 { _sanityCheck(i._cache.utf8 != nil) var r = i @@ -273,20 +318,29 @@ extension String { @inlinable // FIXME(sil-serialize-all) public func distance(from i: Index, to j: Index) -> Int { - if _fastPath(_guts.isASCII) { + if _fastPath(_guts._isASCIIOrSmallASCII) { return j.encodedOffset - i.encodedOffset } - return j >= i - ? _forwardDistance(from: i, to: j) : -_forwardDistance(from: j, to: i) + return _nonASCIIDistance(from: i, to: j) } - @inlinable // FIXME(sil-serialize-all) + @inline(never) + @effects(releasenone) @usableFromInline - @inline(__always) - internal func _forwardDistance(from i: Index, to j: Index) -> Int { - return j._transcodedOffset - i._transcodedOffset + - String.UTF8View._count(fromUTF16: IteratorSequence(_guts.makeIterator( - in: i.encodedOffset.. Int { + let forwards = j >= i + + let start, end: Index + if forwards { + start = i + end = j + } else { + start = j + end = i + } + let countAbs = end._transcodedOffset - start._transcodedOffset + + _gutsNonASCIIUTF8Count(start.encodedOffset.. UTF8.CodeUnit { @inline(__always) get { - if _fastPath(_guts.isASCII) { - let ascii = _guts._unmanagedASCIIView + if _fastPath(_guts._isASCIIOrSmallASCII) { let offset = position.encodedOffset - _precondition(offset < ascii.count, "Index out of bounds") - return ascii.buffer[position.encodedOffset] - } - var j = position - while true { - if case .utf8(let buffer) = j._cache { - _onFastPath() - return buffer[ - buffer.index(buffer.startIndex, offsetBy: j._transcodedOffset)] + _precondition(offset < _guts.count, "Index out of bounds") + + if _guts._isSmall { + return _guts._smallUTF8String[offset] } - j = _index(atEncodedOffset: j.encodedOffset) - precondition(j < endIndex, "Index out of bounds") + return _guts._unmanagedASCIIView.buffer[offset] + } + + return _nonASCIISubscript(position: position) + } + } + + @inline(never) + @effects(releasenone) + @usableFromInline + internal func _nonASCIISubscript(position: Index) -> UTF8.CodeUnit { + _sanityCheck(!_guts._isASCIIOrSmallASCII) + var j = position + while true { + if case .utf8(let buffer) = j._cache { + _onFastPath() + return buffer[ + buffer.index(buffer.startIndex, offsetBy: j._transcodedOffset)] } + j = _nonASCIIIndex(atEncodedOffset: j.encodedOffset) + precondition(j < endIndex, "Index out of bounds") } } @@ -360,7 +426,6 @@ extension String { /// print(strlen(ptr.baseAddress!)) /// } /// // Prints "6" - @inlinable // FIXME(sil-serialize-all) public var utf8CString: ContiguousArray { var result = ContiguousArray() result.reserveCapacity(utf8.count + 1) @@ -372,7 +437,6 @@ extension String { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func _withUnsafeBufferPointerToUTF8( _ body: (UnsafeBufferPointer) throws -> R ) rethrows -> R { @@ -394,7 +458,7 @@ extension String { /// another string's `utf8` view. /// /// let picnicGuest = "Deserving porcupine" - /// if let i = picnicGuest.utf8.index(of: 32) { + /// if let i = picnicGuest.utf8.firstIndex(of: 32) { /// let adjective = String(picnicGuest.utf8[.. { return 0..<_guts.count } @@ -474,7 +534,6 @@ extension String.UTF8View.Iterator : IteratorProtocol { public typealias Element = String.UTF8View.Element @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_ utf8: String.UTF8View) { self._guts = utf8._guts self._nextOffset = 0 @@ -482,6 +541,11 @@ extension String.UTF8View.Iterator : IteratorProtocol { self._endOffset = utf8._guts.count } + internal mutating func _clear() { + self._nextOffset = self._endOffset + self._buffer = _OutputBuffer() + } + @inlinable // FIXME(sil-serialize-all) public mutating func next() -> Unicode.UTF8.CodeUnit? { if _slowPath(_nextOffset == _endOffset) { @@ -550,22 +614,27 @@ extension String.UTF8View.Iterator : IteratorProtocol { } } +// Used to calculate a running count. For non-BMP scalars, it's important if the +// prior code unit was a leading surrogate (validity). +internal func _utf8Count(_ utf16CU: UInt16, prev: UInt16) -> Int { + switch utf16CU { + case 0..<0x80: return 1 + case 0x80..<0x800: return 2 + case 0x800..<0xDC00: return 3 + case 0xDC00..<0xE000: return UTF16.isLeadSurrogate(prev) ? 1 : 3 + default: return 3 + } +} + extension String.UTF8View { - @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) + @usableFromInline internal static func _count(fromUTF16 source: Source) -> Int where Source.Element == Unicode.UTF16.CodeUnit { var result = 0 var prev: Unicode.UTF16.CodeUnit = 0 for u in source { - switch u { - case 0..<0x80: result += 1 - case 0x80..<0x800: result += 2 - case 0x800..<0xDC00: result += 3 - case 0xDC00..<0xE000: result += UTF16.isLeadSurrogate(prev) ? 1 : 3 - default: result += 3 - } + result += _utf8Count(u, prev: prev) prev = u } return result @@ -573,11 +642,23 @@ extension String.UTF8View { @inlinable // FIXME(sil-serialize-all) public var count: Int { - if _fastPath(_guts.isASCII) { return _guts.count } - return _visitGuts(_guts, + let gutsCount = _guts.count + if _fastPath(_guts._isASCIIOrSmallASCII) { return gutsCount } + return _gutsNonASCIIUTF8Count(0.. + ) -> Int { + _sanityCheck(!_guts._isASCIIOrSmallASCII) + return _visitGuts(_guts, range: (range, performBoundsCheck: true), ascii: { ascii in return ascii.count }, utf16: { utf16 in return String.UTF8View._count(fromUTF16: utf16) }, - opaque: { opaque in return String.UTF8View._count(fromUTF16: opaque) }) + opaque: { opaque in return String.UTF8View._count(fromUTF16: opaque) } + ) } } @@ -592,7 +673,7 @@ extension String.UTF8View.Index { /// /// let cafe = "Café 🍵" /// - /// let utf16Index = cafe.utf16.index(of: 32)! + /// let utf16Index = cafe.utf16.firstIndex(of: 32)! /// let utf8Index = String.UTF8View.Index(utf16Index, within: cafe.utf8)! /// /// print(Array(cafe.utf8[..) -> String.UTF8View { let wholeString = String(_guts) @@ -734,9 +814,47 @@ extension String.UTF8View { legacyPartialCharacters: legacyPartialCharacters) } - @inlinable // FIXME(sil-serialize-all) @available(swift, obsoleted: 4) public subscript(bounds: ClosedRange) -> String.UTF8View { return self[bounds.relative(to: self)] } } + +extension String.UTF8View { + /// Copies `self` into the supplied buffer. + /// + /// - Precondition: The memory in `self` is uninitialized. The buffer must + /// contain sufficient uninitialized memory to accommodate `source.underestimatedCount`. + /// + /// - Postcondition: The `Pointee`s at `buffer[startIndex.. + ) -> (Iterator,UnsafeMutableBufferPointer.Index) { + guard var ptr = buffer.baseAddress else { + _preconditionFailure( + "Attempt to copy string contents into nil buffer pointer") + } + var it = self.makeIterator() + + if _guts.isASCII { + defer { _fixLifetime(_guts) } + let asciiView = _guts._unmanagedASCIIView + _precondition(asciiView.count <= buffer.count, + "Insufficient space allocated to copy string contents") + ptr.initialize(from: asciiView.start, count: asciiView.count) + it._clear() + return (it, buffer.index(buffer.startIndex, offsetBy: asciiView.count)) + } + else { + for idx in buffer.startIndex..= 128 }) { + /// if let i = favemoji.unicodeScalars.firstIndex(where: { $0.value >= 128 }) { /// let asciiPrefix = String(favemoji.unicodeScalars[.. Index { return Index(encodedOffset: i + _coreOffset) } @@ -97,7 +95,6 @@ extension String { /// Translates a `UnicodeScalarIndex` into a `_guts` index using this /// view's `_coreOffset`. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func _toCoreIndex(_ i: Index) -> Int { return i.encodedOffset - _coreOffset } @@ -159,7 +156,7 @@ extension String { /// at the found index: /// /// let greeting = "Hello, friend!" - /// if let i = greeting.unicodeScalars.index(where: { "A"..."Z" ~= $0 }) { + /// if let i = greeting.unicodeScalars.firstIndex(where: { "A"..."Z" ~= $0 }) { /// print("First capital letter: \(greeting.unicodeScalars[i])") /// print("Unicode scalar value: \(greeting.unicodeScalars[i].value)") /// } @@ -195,7 +192,6 @@ extension String { internal var _smallIterator: _SmallUTF8String.UnicodeScalarIterator? @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_ guts: _StringGuts) { if _slowPath(guts._isOpaque) { self.init(_opaque: guts) @@ -205,7 +201,6 @@ extension String { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @inline(__always) internal init(_concrete guts: _StringGuts) { _sanityCheck(!guts._isOpaque) @@ -269,7 +264,6 @@ extension String { return String(_guts) } - @inlinable // FIXME(sil-serialize-all) public var debugDescription: String { return "StringUnicodeScalarView(\(self.description.debugDescription))" } @@ -282,7 +276,7 @@ extension String { /// another string's `unicodeScalars` view. /// /// let picnicGuest = "Deserving porcupine" - /// if let i = picnicGuest.unicodeScalars.index(of: " ") { + /// if let i = picnicGuest.unicodeScalars.firstIndex(of: " ") { /// let adjective = String(picnicGuest.unicodeScalars[.. Unicode.Scalar { return _visitGuts(self, args: offset, ascii: { ascii, offset in @@ -316,7 +309,6 @@ extension _StringGuts { } @inlinable - @usableFromInline internal func unicodeScalar(endingAt offset: Int) -> Unicode.Scalar { return _visitGuts(self, args: offset, ascii: { ascii, offset in @@ -331,17 +323,14 @@ extension _StringGuts { extension String.UnicodeScalarView : _SwiftStringView { @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var _persistentContent : String { return String(_guts) } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) var _wholeString : String { return String(_guts) } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) var _encodedOffsetRange : Range { return 0..<_guts.count } @@ -379,7 +368,6 @@ extension String.UnicodeScalarView : RangeReplaceableCollection { /// to allocate. /// /// - Complexity: O(*n*), where *n* is the capacity being reserved. - @inlinable // FIXME(sil-serialize-all) public mutating func reserveCapacity(_ n: Int) { _guts.reserveCapacity(n) } @@ -387,7 +375,6 @@ extension String.UnicodeScalarView : RangeReplaceableCollection { /// Appends the given Unicode scalar to the view. /// /// - Parameter c: The character to append to the string. - @inlinable // FIXME(sil-serialize-all) public mutating func append(_ c: Unicode.Scalar) { if _fastPath(_guts.isASCII && c.value <= 0x7f) { _guts.withMutableASCIIStorage(unusedCapacity: 1) { storage in @@ -417,7 +404,6 @@ extension String.UnicodeScalarView : RangeReplaceableCollection { /// - Parameter newElements: A sequence of Unicode scalar values. /// /// - Complexity: O(*n*), where *n* is the length of the resulting view. - @inlinable // FIXME(sil-serialize-all) public mutating func append(contentsOf newElements: S) where S.Element == Unicode.Scalar { // FIXME: Keep ASCII storage if possible @@ -461,7 +447,6 @@ extension String.UnicodeScalarView : RangeReplaceableCollection { /// `newElements`. If the call to `replaceSubrange(_:with:)` simply /// removes elements at the end of the string, the complexity is O(*n*), /// where *n* is equal to `bounds.count`. - @inlinable // FIXME(sil-serialize-all) public mutating func replaceSubrange( _ bounds: Range, with newElements: C @@ -484,7 +469,7 @@ extension String.UnicodeScalarIndex { /// /// let cafe = "Café 🍵" /// - /// let utf16Index = cafe.utf16.index(of: 32)! + /// let utf16Index = cafe.utf16.firstIndex(of: 32)! /// let scalarIndex = String.Index(utf16Index, within: cafe.unicodeScalars)! /// /// print(String(cafe.unicodeScalars[.. Bool { if _fastPath(_guts.isASCII) { return true } if i == startIndex || i == endIndex { @@ -552,7 +536,6 @@ extension String.UnicodeScalarView { // NOTE: Don't make this function inlineable. Grapheme cluster // segmentation uses a completely different algorithm in Unicode 9.0. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func _isOnGraphemeClusterBoundary(_ i: Index) -> Bool { if i == startIndex || i == endIndex { return true @@ -637,14 +620,13 @@ extension String.UnicodeScalarView { /// to, but not including, the first comma (`","`) in the string. /// /// let str = "All this happened, more or less." - /// let i = str.unicodeScalars.index(of: ",")! + /// let i = str.unicodeScalars.firstIndex(of: ",")! /// let substring = str.unicodeScalars[str.unicodeScalars.startIndex ..< i] /// print(String(substring)) /// // Prints "All this happened" /// /// - Complexity: O(*n*) if the underlying string is bridged from /// Objective-C, where *n* is the length of the string; otherwise, O(1). - @inlinable // FIXME(sil-serialize-all) @available(swift, obsoleted: 4) public subscript(r: Range) -> String.UnicodeScalarView { let rawSubRange: Range = diff --git a/stdlib/public/core/StringVariant.swift b/stdlib/public/core/StringVariant.swift index d79617c6e526c..0a954e3e88b72 100644 --- a/stdlib/public/core/StringVariant.swift +++ b/stdlib/public/core/StringVariant.swift @@ -52,7 +52,6 @@ where extension _StringVariant { @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func _copyToNativeStorage( of codeUnit: TargetCodeUnit.Type = TargetCodeUnit.self, unusedCapacity: Int = 0 @@ -66,7 +65,6 @@ extension _StringVariant { } @inlinable - @usableFromInline @inline(__always) func _boundsCheck(_ i: Index) { _precondition(i >= startIndex && i < endIndex, @@ -74,7 +72,6 @@ extension _StringVariant { } @inlinable - @usableFromInline @inline(__always) func _boundsCheck(_ range: Range) { _precondition(range.lowerBound >= startIndex, @@ -84,7 +81,6 @@ extension _StringVariant { } @inlinable - @usableFromInline @inline(__always) func _boundsCheck(offset i: Int) { _precondition(i >= 0 && i < count, @@ -92,7 +88,6 @@ extension _StringVariant { } @inlinable - @usableFromInline @inline(__always) func _boundsCheck(offsetRange range: Range) { _precondition(range.lowerBound >= 0 && range.upperBound <= count, @@ -100,28 +95,24 @@ extension _StringVariant { } @inlinable - @usableFromInline internal func codeUnit(atCheckedIndex index: Index) -> Element { _boundsCheck(index) return self[index] } @inlinable - @usableFromInline internal func codeUnit(atCheckedOffset offset: Int) -> Element { _boundsCheck(offset: offset) return self[offset] } @inlinable - @usableFromInline internal func checkedSlice(_ range: Range) -> Self { _boundsCheck(offsetRange: range) return self[range] } @inlinable - @usableFromInline internal func checkedSlice(from startOffset: Int) -> Self { let r: Range = startOffset.. Self { let r: Range = 0.. Int { _boundsCheck(offset: offset) if _slowPath(UTF16.isLeadSurrogate(self[offset])) { @@ -152,7 +141,6 @@ extension _StringVariant { } @inlinable - @usableFromInline func unicodeScalarWidth(endingAt offset: Int) -> Int { _boundsCheck(offset: offset - 1) if _slowPath(UTF16.isTrailSurrogate(self[offset - 1])) { @@ -164,7 +152,6 @@ extension _StringVariant { } @inlinable - @usableFromInline func unicodeScalar(startingAt offset: Int) -> Unicode.Scalar { let u0 = self.codeUnit(atCheckedOffset: offset) if _fastPath(UTF16._isScalar(u0)) { @@ -180,7 +167,6 @@ extension _StringVariant { } @inlinable - @usableFromInline func unicodeScalar(endingAt offset: Int) -> Unicode.Scalar { let u1 = self.codeUnit(atCheckedOffset: offset - 1) if _fastPath(UTF16._isScalar(u1)) { diff --git a/stdlib/public/core/Substring.swift.gyb b/stdlib/public/core/Substring.swift.gyb index 789afd86afb5e..875c3e5820151 100644 --- a/stdlib/public/core/Substring.swift.gyb +++ b/stdlib/public/core/Substring.swift.gyb @@ -18,7 +18,6 @@ extension String { /// instance. /// /// - Complexity: O(*n*), where *n* is the length of `substring`. - @inlinable // FIXME(sil-serialize-all) public init(_ substring: Substring) { let wholeGuts = substring._wholeString._guts self.init(wholeGuts._extractSlice(substring._encodedOffsetRange)) @@ -37,7 +36,7 @@ extension String { /// substring of the first sentence: /// /// let greeting = "Hi there! It's nice to meet you! 👋" -/// let endOfSentence = greeting.index(of: "!")! +/// let endOfSentence = greeting.firstIndex(of: "!")! /// let firstSentence = greeting[...endOfSentence] /// // firstSentence == "Hi there!" /// @@ -108,13 +107,11 @@ public struct Substring : StringProtocol { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_slice: Slice) { self._slice = _slice } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_ guts: _StringGuts, _ offsetRange: Range) { self.init( _base: String(guts), @@ -134,7 +131,6 @@ public struct Substring : StringProtocol { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init( _base base: String, _ bounds: R ) where R.Bound == Index { @@ -307,7 +303,6 @@ public struct Substring : StringProtocol { extension Substring : _SwiftStringView { @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var _persistentContent: String { return String(self) } @@ -395,7 +390,6 @@ extension Substring.${_View} : BidirectionalCollection { /// Creates an instance that slices `base` at `_bounds`. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_ base: String.${_View}, _bounds: Range) { _slice = Slice( base: String(base._guts).${_property}, @@ -404,13 +398,11 @@ extension Substring.${_View} : BidirectionalCollection { /// The entire String onto whose slice this view is a projection. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var _wholeString: String { return String(_slice._base._guts) } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var _encodedOffsetRange: Range { return startIndex.encodedOffset.. Bool { return (index >= 0) && (index <= count) @@ -33,7 +32,6 @@ internal func _isValidArrayIndex(_ index: Int, count: Int) -> Bool { /// Returns `true` iff the given `index` is valid for subscripting, i.e. /// `0 ≤ index < count`. @inlinable // FIXME(sil-serialize-all) -@usableFromInline @_transparent internal func _isValidArraySubscript(_ index: Int, count: Int) -> Bool { return (index >= 0) && (index < count) @@ -47,16 +45,13 @@ internal class _SwiftNativeNSArrayWithContiguousStorage : _SwiftNativeNSArray { // Provides NSArray inheritance and native refcounting @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @nonobjc internal override init() {} @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) deinit {} // Operate on our contiguous storage @inlinable - @usableFromInline internal func withUnsafeBufferOfObjects( _ body: (UnsafeBufferPointer) throws -> R ) rethrows -> R { @@ -160,7 +155,6 @@ extension _SwiftNativeNSArrayWithContiguousStorage : _NSArrayCore { internal let _nativeStorage: _ContiguousArrayStorageBase @inlinable - @usableFromInline @nonobjc internal var _heapBufferBridgedPtr: UnsafeMutablePointer { return _getUnsafePointerToStoredProperties(self).assumingMemoryBound( @@ -170,7 +164,6 @@ extension _SwiftNativeNSArrayWithContiguousStorage : _NSArrayCore { internal typealias HeapBufferStorage = _HeapBufferStorage @inlinable - @usableFromInline internal var _heapBufferBridged: HeapBufferStorage? { if let ref = _stdlib_atomicLoadARCRef(object: _heapBufferBridgedPtr) { @@ -180,14 +173,12 @@ extension _SwiftNativeNSArrayWithContiguousStorage : _NSArrayCore { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline @nonobjc internal init(_nativeStorage: _ContiguousArrayStorageBase) { self._nativeStorage = _nativeStorage } @inlinable - @usableFromInline internal func _destroyBridgedStorage(_ hb: HeapBufferStorage?) { if let bridgedStorage = hb { let heapBuffer = _HeapBuffer(bridgedStorage) @@ -197,13 +188,11 @@ extension _SwiftNativeNSArrayWithContiguousStorage : _NSArrayCore { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) deinit { _destroyBridgedStorage(_heapBufferBridged) } @inlinable - @usableFromInline internal override func withUnsafeBufferOfObjects( _ body: (UnsafeBufferPointer) throws -> R ) rethrows -> R { @@ -265,11 +254,9 @@ extension _SwiftNativeNSArrayWithContiguousStorage : _NSArrayCore { @_fixed_layout internal class _SwiftNativeNSArrayWithContiguousStorage { @inlinable - @usableFromInline internal init() {} @inlinable - @usableFromInline deinit {} } #endif @@ -284,7 +271,6 @@ internal class _ContiguousArrayStorageBase final var countAndCapacity: _ArrayBody @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @nonobjc internal init(_doNotCallMeBase: ()) { _sanityCheckFailure("creating instance of _ContiguousArrayStorageBase") @@ -292,7 +278,6 @@ internal class _ContiguousArrayStorageBase #if _runtime(_ObjC) @inlinable - @usableFromInline internal override func withUnsafeBufferOfObjects( _ body: (UnsafeBufferPointer) throws -> R ) rethrows -> R { @@ -307,7 +292,6 @@ internal class _ContiguousArrayStorageBase /// `UnsafeBufferPointer` to the elements and return the result. /// Otherwise, return `nil`. @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal func _withVerbatimBridgedUnsafeBuffer( _ body: (UnsafeBufferPointer) throws -> R ) rethrows -> R? { @@ -316,7 +300,6 @@ internal class _ContiguousArrayStorageBase } @inlinable // FIXME(sil-serialize-all) - @usableFromInline @nonobjc internal func _getNonVerbatimBridgedCount() -> Int { _sanityCheckFailure( @@ -324,7 +307,6 @@ internal class _ContiguousArrayStorageBase } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal func _getNonVerbatimBridgedHeapBuffer() -> _HeapBuffer { _sanityCheckFailure( @@ -333,7 +315,6 @@ internal class _ContiguousArrayStorageBase #endif @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal func canStoreElements(ofDynamicType _: Any.Type) -> Bool { _sanityCheckFailure( "Concrete subclasses must implement canStoreElements(ofDynamicType:)") @@ -341,14 +322,12 @@ internal class _ContiguousArrayStorageBase /// A type that every element in the array is. @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal var staticElementType: Any.Type { _sanityCheckFailure( "Concrete subclasses must implement staticElementType") } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) deinit { _sanityCheck( self !== _emptyArrayStorage, "Deallocating empty array storage?!") diff --git a/stdlib/public/core/ThreadLocalStorage.swift b/stdlib/public/core/ThreadLocalStorage.swift index 3f29b75e00c2e..d9f5fd08428f0 100644 --- a/stdlib/public/core/ThreadLocalStorage.swift +++ b/stdlib/public/core/ThreadLocalStorage.swift @@ -59,7 +59,6 @@ internal struct _ThreadLocalStorage { // private: Should only be called by _initializeThreadLocalStorage @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_uBreakIterator: OpaquePointer) { self.uBreakIterator = _uBreakIterator } @@ -67,7 +66,6 @@ internal struct _ThreadLocalStorage { // Get the current thread's TLS pointer. On first call for a given thread, // creates and initializes a new one. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static func getPointer() -> UnsafeMutablePointer<_ThreadLocalStorage> { @@ -81,7 +79,6 @@ internal struct _ThreadLocalStorage { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static func getUBreakIterator( start: UnsafePointer, count: Int32 @@ -99,7 +96,6 @@ internal struct _ThreadLocalStorage { // Destructor to register with pthreads. Responsible for deallocating any memory // owned. -@inlinable // FIXME(sil-serialize-all) @usableFromInline // FIXME(sil-serialize-all) @_silgen_name("_stdlib_destroyTLS") internal func _destroyTLS(_ ptr: UnsafeMutableRawPointer?) { @@ -127,9 +123,8 @@ internal let _tlsKey: __swift_thread_key_t = { return key }() -@inlinable // FIXME(sil-serialize-all) -@usableFromInline // FIXME(sil-serialize-all) @inline(never) +@usableFromInline internal func _initializeThreadLocalStorage() -> UnsafeMutablePointer<_ThreadLocalStorage> { diff --git a/stdlib/public/core/UIntBuffer.swift b/stdlib/public/core/UIntBuffer.swift index 3ac57e5be88af..6d02dfda16153 100644 --- a/stdlib/public/core/UIntBuffer.swift +++ b/stdlib/public/core/UIntBuffer.swift @@ -74,7 +74,6 @@ extension _UIntBuffer : Collection { internal var bitOffset: UInt8 @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal init(bitOffset: UInt8) { self.bitOffset = bitOffset } @inlinable // FIXME(sil-serialize-all) @@ -106,7 +105,6 @@ extension _UIntBuffer : Collection { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal var _elementWidth : UInt8 { return UInt8(truncatingIfNeeded: Element.bitWidth) } @@ -148,19 +146,16 @@ extension _UIntBuffer : RandomAccessCollection { extension FixedWidthInteger { @inline(__always) @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal func _fullShiftLeft(_ n: N) -> Self { return (self &<< ((n &+ 1) &>> 1)) &<< (n &>> 1) } @inline(__always) @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal func _fullShiftRight(_ n: N) -> Self { return (self &>> ((n &+ 1) &>> 1)) &>> (n &>> 1) } @inline(__always) @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal static func _lowBits(_ n: N) -> Self { return ~((~0 as Self)._fullShiftLeft(n)) } @@ -169,7 +164,6 @@ extension FixedWidthInteger { extension Range { @inline(__always) @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal func _contains_(_ other: Range) -> Bool { return other.clamped(to: self) == other } diff --git a/stdlib/public/core/UTF16.swift b/stdlib/public/core/UTF16.swift index 457df38a3540d..0be7f3c805dc1 100644 --- a/stdlib/public/core/UTF16.swift +++ b/stdlib/public/core/UTF16.swift @@ -21,7 +21,6 @@ extension Unicode.UTF16 : Unicode.Encoding { public typealias EncodedScalar = _UIntBuffer @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static var _replacementCodeUnit: CodeUnit { @inline(__always) get { return 0xfffd } } @@ -42,7 +41,6 @@ extension Unicode.UTF16 : Unicode.Encoding { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @inline(__always) internal static func _decodeSurrogates( _ lead: CodeUnit, diff --git a/stdlib/public/core/UTF32.swift b/stdlib/public/core/UTF32.swift index 4c8ef008da9e5..f6ada0636f1a3 100644 --- a/stdlib/public/core/UTF32.swift +++ b/stdlib/public/core/UTF32.swift @@ -21,7 +21,6 @@ extension Unicode.UTF32 : Unicode.Encoding { public typealias EncodedScalar = CollectionOfOne @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static var _replacementCodeUnit: CodeUnit { @inline(__always) get { return 0xFFFD } } diff --git a/stdlib/public/core/UTF8.swift b/stdlib/public/core/UTF8.swift index 4d02e9f3207e1..1552e2305b0ca 100644 --- a/stdlib/public/core/UTF8.swift +++ b/stdlib/public/core/UTF8.swift @@ -177,7 +177,6 @@ extension UTF8.ReverseParser : Unicode.Parser, _UTFParser { /// Returns the length of the invalid sequence that ends with the LSB of /// buffer. @inline(never) - @inlinable // FIXME(sil-serialize-all) @usableFromInline internal func _invalidLength() -> UInt8 { if _buffer._storage & 0b0__1111_0000__1100_0000 @@ -255,7 +254,6 @@ extension Unicode.UTF8.ForwardParser : Unicode.Parser, _UTFParser { /// Returns the length of the invalid sequence that starts with the LSB of /// buffer. @inline(never) - @inlinable // FIXME(sil-serialize-all) @usableFromInline internal func _invalidLength() -> UInt8 { if _buffer._storage & 0b0__1100_0000__1111_0000 diff --git a/stdlib/public/core/UnfoldSequence.swift b/stdlib/public/core/UnfoldSequence.swift index 9bf3f6b266862..9d9a8fedca25a 100644 --- a/stdlib/public/core/UnfoldSequence.swift +++ b/stdlib/public/core/UnfoldSequence.swift @@ -114,7 +114,6 @@ public struct UnfoldSequence : Sequence, IteratorProtocol { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init(_state: State, _next: @escaping (inout State) -> Element?) { self._state = _state self._next = _next diff --git a/stdlib/public/core/Unicode.swift b/stdlib/public/core/Unicode.swift index 3106ce8d05a32..64ca5d12a0f08 100644 --- a/stdlib/public/core/Unicode.swift +++ b/stdlib/public/core/Unicode.swift @@ -384,7 +384,6 @@ extension Unicode.UTF16 : UnicodeCodec { /// units it spanned in the input. This function may consume more code /// units than required for this scalar. @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal mutating func _decodeOne( _ input: inout I ) -> (UnicodeDecodingResult, Int) where I.Element == CodeUnit { @@ -488,7 +487,6 @@ extension Unicode.UTF32 : UnicodeCodec { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static func _decode( _ input: inout I ) -> UnicodeDecodingResult where I.Element == CodeUnit { @@ -886,7 +884,6 @@ extension Unicode.Scalar { /// Create an instance with numeric value `value`, bypassing the regular /// precondition checks for code point validity. @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal init(_unchecked value: UInt32) { _sanityCheck(value < 0xD800 || value > 0xDFFF, "high- and low-surrogate code points are not valid Unicode scalar values") diff --git a/stdlib/public/core/UnicodeEncoding.swift b/stdlib/public/core/UnicodeEncoding.swift index 7d6bfc3b7d2c4..161ec464a0968 100644 --- a/stdlib/public/core/UnicodeEncoding.swift +++ b/stdlib/public/core/UnicodeEncoding.swift @@ -76,7 +76,6 @@ extension _UnicodeEncoding { /// `encodedReplacementCharacter` if the scalar can't be represented in this /// encoding. @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal static func _encode(_ content: Unicode.Scalar) -> EncodedScalar { return encode(content) ?? encodedReplacementCharacter } @@ -85,7 +84,6 @@ extension _UnicodeEncoding { /// `encodedReplacementCharacter` if the scalar can't be represented in this /// encoding. @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal static func _transcode( _ content: FromEncoding.EncodedScalar, from _: FromEncoding.Type ) -> EncodedScalar { @@ -94,7 +92,6 @@ extension _UnicodeEncoding { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal static func _transcode< Source: Sequence, SourceEncoding: Unicode.Encoding>( _ source: Source, diff --git a/stdlib/public/core/UnicodeParser.swift b/stdlib/public/core/UnicodeParser.swift index 653e1918f475e..444a633cd8594 100644 --- a/stdlib/public/core/UnicodeParser.swift +++ b/stdlib/public/core/UnicodeParser.swift @@ -27,14 +27,12 @@ extension Unicode { case error(length: Int) @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal var _valid: T? { if case .valid(let result) = self { return result } return nil } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal var _error: Int? { if case .error(let result) = self { return result } return nil @@ -61,7 +59,6 @@ public protocol _UnicodeParser { extension _UnicodeParser { @inlinable // FIXME(sil-serialize-all) - @usableFromInline @inline(__always) @discardableResult internal static func _parse( @@ -136,51 +133,3 @@ extension Unicode._ParsingIterator : IteratorProtocol, Sequence { } } } - -/* -extension Unicode { - @_fixed_layout - @usableFromInline - internal struct _TranscodingIterator< - SourceCodeUnits : IteratorProtocol, - Parser : Unicode.Parser, - TargetEncoding : Unicode.Encoding - > where Parser.Encoding.CodeUnit == SourceCodeUnits.Element { - - @inline(__always) - @inlinable - public init(source: SourceCodeUnits, parser: Parser) { - _scalars = _ParsingIterator(codeUnits: source, parser: parser) - let firstScalar_ = _scalars.next() - if _fastPath(firstScalar_ != nil), let firstScalar = firstScalar_ { - _codeUnits = TargetEncoding._transcode( - firstScalar, from: Parser.Encoding.self).makeIterator() - } - else { - _codeUnits = TargetEncoding._encode( - UnicodeScalar(_unchecked: 0)).makeIterator() - while _codeUnits.next() != nil { } - return - } - } - - internal var _scalars: _ParsingIterator - internal var _codeUnits: TargetEncoding.EncodedScalar.Iterator - } -} - - -extension Unicode._TranscodingIterator : IteratorProtocol, Sequence { - @inline(__always) - @inlinable - mutating public func next() -> TargetEncoding.CodeUnit? { - if let x = _codeUnits.next() { return x } - let nextScalar_ = _scalars.next() - if _fastPath(nextScalar_ != nil), let nextScalar = nextScalar_ { - _codeUnits = TargetEncoding._transcode( - nextScalar, from: Parser.Encoding.self).makeIterator() - } - return _codeUnits.next() - } -} -*/ diff --git a/stdlib/public/core/UnicodeScalar.swift b/stdlib/public/core/UnicodeScalar.swift index 8851f7083a2d8..3a729a1fdf25c 100644 --- a/stdlib/public/core/UnicodeScalar.swift +++ b/stdlib/public/core/UnicodeScalar.swift @@ -35,7 +35,6 @@ extension Unicode { @_fixed_layout public struct Scalar { @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal init(_value: UInt32) { self._value = _value } @@ -280,7 +279,6 @@ extension Unicode.Scalar : // FIXME: Unicode makes this interesting. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var _isPrintableASCII: Bool { return (self >= Unicode.Scalar(0o040) && self <= Unicode.Scalar(0o176)) } @@ -313,13 +311,14 @@ extension Unicode.Scalar : LosslessStringConvertible { } extension Unicode.Scalar : Hashable { - /// The Unicode scalar's hash value. + /// Hashes the essential components of this value by feeding them into the + /// given hasher. /// - /// Hash values are not guaranteed to be equal across different executions of - /// your program. Do not save hash values to use during a future execution. + /// - Parameter hasher: The hasher to use when combining the components + /// of this instance. @inlinable // FIXME(sil-serialize-all) - public var hashValue: Int { - return Int(self.value) + public func hash(into hasher: inout Hasher) { + hasher.combine(self.value) } } @@ -400,8 +399,7 @@ extension Unicode.Scalar : Comparable { extension Unicode.Scalar { @_fixed_layout // FIXME(sil-serialize-all) public struct UTF16View { - @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) + @inlinable // FIXME(sil-serialize-all) internal init(value: Unicode.Scalar) { self.value = value } @@ -457,7 +455,6 @@ func _ascii16(_ c: Unicode.Scalar) -> UTF16.CodeUnit { extension Unicode.Scalar { @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal static var _replacementCharacter: Unicode.Scalar { return Unicode.Scalar(_value: UTF32._replacementCodeUnit) } diff --git a/stdlib/public/core/Unmanaged.swift b/stdlib/public/core/Unmanaged.swift index 517a1f7e02d8a..a11b0e157e052 100644 --- a/stdlib/public/core/Unmanaged.swift +++ b/stdlib/public/core/Unmanaged.swift @@ -20,7 +20,6 @@ public struct Unmanaged { internal unowned(unsafe) var _value: Instance @inlinable // FIXME(sil-serialize-all) - @usableFromInline @_transparent internal init(_private: Instance) { _value = _private } diff --git a/stdlib/public/core/UnmanagedOpaqueString.swift b/stdlib/public/core/UnmanagedOpaqueString.swift index a39dd1dca545e..764d23a33bc13 100644 --- a/stdlib/public/core/UnmanagedOpaqueString.swift +++ b/stdlib/public/core/UnmanagedOpaqueString.swift @@ -41,7 +41,6 @@ internal struct _UnmanagedOpaqueString { #if _runtime(_ObjC) // FIXME unify @inlinable - @usableFromInline init(_ object: _CocoaString, range: Range, isSlice: Bool) { self.object = object self.range = range @@ -55,13 +54,11 @@ internal struct _UnmanagedOpaqueString { } @inlinable - @usableFromInline init(_ object: _CocoaString, count: Int) { self.init(object, range: 0.., isSlice: Bool) { self.object = object self.range = range @@ -74,7 +71,6 @@ internal struct _UnmanagedOpaqueString { } @inlinable - @usableFromInline init(_ object: _OpaqueString, count: Int) { self.init(object, range: 0.. Iterator { return Iterator(self, startingAt: range.lowerBound) } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func makeIterator(startingAt position: Int) -> Iterator { return Iterator(self, startingAt: position) } @@ -119,14 +113,12 @@ extension _UnmanagedOpaqueString : Sequence { internal var _bufferIndex: Int8 = 0 @inlinable - @usableFromInline init(_ string: _UnmanagedOpaqueString, startingAt start: Int) { self._object = string.object self._range = start.. Element? { if _fastPath(_bufferIndex < _buffer.count) { @@ -172,21 +164,18 @@ extension _UnmanagedOpaqueString : RandomAccessCollection { @usableFromInline internal var _value: Int - @usableFromInline @inlinable @inline(__always) init(_ value: Int) { self._value = value } - @usableFromInline @inlinable @inline(__always) func distance(to other: Index) -> Int { return other._value - self._value } - @usableFromInline @inlinable @inline(__always) func advanced(by n: Int) -> Index { @@ -194,25 +183,21 @@ extension _UnmanagedOpaqueString : RandomAccessCollection { } } - @usableFromInline @inlinable var startIndex: Index { return Index(range.lowerBound) } - @usableFromInline @inlinable var endIndex: Index { return Index(range.upperBound) } - @usableFromInline @inlinable var count: Int { return range.count } - @usableFromInline @inlinable // FIXME(sil-serialize-all) subscript(position: Index) -> UTF16.CodeUnit { _sanityCheck(position._value >= range.lowerBound) @@ -224,7 +209,6 @@ extension _UnmanagedOpaqueString : RandomAccessCollection { #endif } - @usableFromInline @inlinable // FIXME(sil-serialize-all) subscript(bounds: Range) -> _UnmanagedOpaqueString { _sanityCheck(bounds.lowerBound._value >= range.lowerBound) @@ -240,13 +224,11 @@ extension _UnmanagedOpaqueString : _StringVariant { internal typealias CodeUnit = Encoding.CodeUnit @inlinable - @usableFromInline var isASCII: Bool { @inline(__always) get { return false } } @inlinable - @usableFromInline @inline(__always) func _boundsCheck(_ i: Index) { _precondition(i._value >= range.lowerBound && i._value < range.upperBound, @@ -254,7 +236,6 @@ extension _UnmanagedOpaqueString : _StringVariant { } @inlinable - @usableFromInline @inline(__always) func _boundsCheck(_ range: Range) { _precondition( @@ -264,7 +245,6 @@ extension _UnmanagedOpaqueString : _StringVariant { } @inlinable - @usableFromInline @inline(__always) func _boundsCheck(offset: Int) { _precondition(offset >= 0 && offset < range.count, @@ -272,14 +252,12 @@ extension _UnmanagedOpaqueString : _StringVariant { } @inlinable - @usableFromInline @inline(__always) func _boundsCheck(offsetRange range: Range) { _precondition(range.lowerBound >= 0 && range.upperBound <= count, "String index range is out of bounds") } - @usableFromInline @inlinable // FIXME(sil-serialize-all) subscript(offset: Int) -> UTF16.CodeUnit { _sanityCheck(offset >= 0 && offset < count) @@ -290,7 +268,6 @@ extension _UnmanagedOpaqueString : _StringVariant { #endif } - @usableFromInline @inlinable // FIXME(sil-serialize-all) subscript(offsetRange: Range) -> _UnmanagedOpaqueString { _sanityCheck(offsetRange.lowerBound >= 0) @@ -303,7 +280,6 @@ extension _UnmanagedOpaqueString : _StringVariant { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal subscript(offsetRange: PartialRangeUpTo) -> SubSequence { _sanityCheck(offsetRange.upperBound <= range.count) let b: Range = @@ -314,7 +290,6 @@ extension _UnmanagedOpaqueString : _StringVariant { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal subscript(offsetRange: PartialRangeThrough) -> SubSequence { _sanityCheck(offsetRange.upperBound <= range.count) let b: Range = @@ -325,7 +300,6 @@ extension _UnmanagedOpaqueString : _StringVariant { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal subscript(offsetRange: PartialRangeFrom) -> SubSequence { _sanityCheck(offsetRange.lowerBound < range.count) let b: Range = @@ -336,7 +310,6 @@ extension _UnmanagedOpaqueString : _StringVariant { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func _copy( into dest: UnsafeMutableBufferPointer ) { @@ -353,7 +326,6 @@ extension _UnmanagedOpaqueString : _StringVariant { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func _copy( into dest: UnsafeMutableBufferPointer ) diff --git a/stdlib/public/core/UnmanagedString.swift b/stdlib/public/core/UnmanagedString.swift index bde7218b0ce0e..df7eaa2b0ecb3 100644 --- a/stdlib/public/core/UnmanagedString.swift +++ b/stdlib/public/core/UnmanagedString.swift @@ -15,8 +15,8 @@ import SwiftShims internal typealias _UnmanagedASCIIString = _UnmanagedString internal typealias _UnmanagedUTF16String = _UnmanagedString -@usableFromInline @inlinable +@inline(__always) internal func memcpy_zext< Target: FixedWidthInteger & UnsignedInteger, @@ -25,13 +25,18 @@ func memcpy_zext< dst: UnsafeMutablePointer, src: UnsafePointer, count: Int ) { _sanityCheck(Source.bitWidth < Target.bitWidth) - for i in 0..= 0) + // Don't use the for-in-range syntax to avoid precondition checking in Range. + // This enables vectorization of the memcpy loop. + var i = 0 + while i < count { dst[i] = Target(src[i]) + i = i &+ 1 } } -@usableFromInline @inlinable +@inline(__always) internal func memcpy_trunc< Target: FixedWidthInteger & UnsignedInteger, @@ -40,8 +45,30 @@ func memcpy_trunc< dst: UnsafeMutablePointer, src: UnsafePointer, count: Int ) { _sanityCheck(Source.bitWidth > Target.bitWidth) - for i in 0..= 0) + // Don't use the for-in-range syntax to avoid precondition checking in Range. + // This enables vectorization of the memcpy loop. + var i = 0 + while i < count { dst[i] = Target(truncatingIfNeeded: src[i]) + i = i &+ 1 + } +} + +@inlinable +@inline(__always) +internal +func memcpy_< + Source: FixedWidthInteger & UnsignedInteger +>( + dst: UnsafeMutablePointer, src: UnsafePointer, count: Int +) { + // Don't use the for-in-range syntax to avoid precondition checking in Range. + // This enables vectorization of the memcpy loop. + var i = 0 + while i < count { + dst[i] = src[i] + i = i &+ 1 } } @@ -68,7 +95,6 @@ struct _UnmanagedString internal var count: Int @inlinable - @usableFromInline init(start: UnsafePointer, count: Int) { _sanityCheck(CodeUnit.self == UInt8.self || CodeUnit.self == UInt16.self) self.start = start @@ -76,7 +102,6 @@ struct _UnmanagedString } @inlinable - @usableFromInline init(_ bufPtr: UnsafeBufferPointer) { self.init( start: bufPtr.baseAddress._unsafelyUnwrappedUnchecked, @@ -86,31 +111,26 @@ struct _UnmanagedString extension _UnmanagedString { @inlinable - @usableFromInline internal var end: UnsafePointer { return start + count } @inlinable - @usableFromInline internal var rawStart: UnsafeRawPointer { return UnsafeRawPointer(start) } @inlinable - @usableFromInline internal var rawEnd: UnsafeRawPointer { return UnsafeRawPointer(end) } @inlinable - @usableFromInline internal var buffer: UnsafeBufferPointer { return .init(start: start, count: count) } @inlinable - @usableFromInline internal var rawBuffer: UnsafeRawBufferPointer { return .init(start: rawStart, count: rawEnd - rawStart) } @@ -128,17 +148,14 @@ extension _UnmanagedString : RandomAccessCollection { internal typealias SubSequence = _UnmanagedString @inlinable - @usableFromInline internal var startIndex: Index { return start } @inlinable - @usableFromInline internal var endIndex: Index { return end } @inlinable - @usableFromInline internal subscript(position: Index) -> UTF16.CodeUnit { @inline(__always) get { @@ -148,7 +165,6 @@ extension _UnmanagedString : RandomAccessCollection { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal subscript(_ bounds: Range) -> SubSequence { _sanityCheck(bounds.lowerBound >= start && bounds.upperBound <= end) return _UnmanagedString(start: bounds.lowerBound, count: bounds.count) @@ -157,14 +173,12 @@ extension _UnmanagedString : RandomAccessCollection { extension _UnmanagedString : _StringVariant { @inlinable - @usableFromInline internal var isASCII: Bool { // NOTE: For now, single byte means ASCII. Might change in future return CodeUnit.bitWidth == 8 } @inlinable - @usableFromInline internal subscript(offset: Int) -> UTF16.CodeUnit { @inline(__always) get { @@ -174,7 +188,6 @@ extension _UnmanagedString : _StringVariant { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal subscript(offsetRange: Range) -> _UnmanagedString { _sanityCheck(offsetRange.lowerBound >= 0 && offsetRange.upperBound <= count) return _UnmanagedString( @@ -183,7 +196,6 @@ extension _UnmanagedString : _StringVariant { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal subscript(offsetRange: PartialRangeFrom) -> SubSequence { _sanityCheck(offsetRange.lowerBound >= 0) return _UnmanagedString( @@ -193,7 +205,6 @@ extension _UnmanagedString : _StringVariant { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal subscript(offsetRange: PartialRangeUpTo) -> SubSequence { _sanityCheck(offsetRange.upperBound <= count) return _UnmanagedString( @@ -203,7 +214,6 @@ extension _UnmanagedString : _StringVariant { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal subscript(offsetRange: PartialRangeThrough) -> SubSequence { _sanityCheck(offsetRange.upperBound < count) return _UnmanagedString( @@ -213,7 +223,7 @@ extension _UnmanagedString : _StringVariant { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) + @inline(__always) internal func _copy( into target: UnsafeMutableBufferPointer ) where TargetCodeUnit : FixedWidthInteger & UnsignedInteger { @@ -251,14 +261,12 @@ extension _UnmanagedString : _StringVariant { var _offset: Int @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) init(_ base: _UnmanagedString) { self._base = base self._offset = 0 } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) mutating func next() -> Unicode.Scalar? { if _slowPath(_offset == _base.count) { return nil } let u0 = _base[_offset] @@ -278,7 +286,6 @@ extension _UnmanagedString : _StringVariant { } } - @usableFromInline // FIXME(sil-serialize-all) @inlinable func makeUnicodeScalarIterator() -> UnicodeScalarIterator { return UnicodeScalarIterator(self) diff --git a/stdlib/public/core/UnsafeBufferPointer.swift.gyb b/stdlib/public/core/UnsafeBufferPointer.swift.gyb index bc4fe72d16b39..c240c0730fbf2 100644 --- a/stdlib/public/core/UnsafeBufferPointer.swift.gyb +++ b/stdlib/public/core/UnsafeBufferPointer.swift.gyb @@ -295,7 +295,7 @@ extension Unsafe${Mutable}BufferPointer: ${Mutable}Collection, RandomAccessColle /// let streetSlice = buffer[2.. Int { + return Hasher._hash(seed: seed, UInt(bitPattern: self)) } } @@ -1003,7 +1003,6 @@ extension ${Self} : CustomReflectable { extension ${Self} : CustomPlaygroundQuickLookable { @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal var summary: String { let selfType = "${Self}" let ptrValue = UInt64(bitPattern: Int64(Int(Builtin.ptrtoint_Word(_rawValue)))) @@ -1055,7 +1054,6 @@ extension UInt { extension ${Self} { @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal static var _max : ${Self} { return ${Self}( bitPattern: 0 as Int &- MemoryLayout.stride diff --git a/stdlib/public/core/UnsafeRawBufferPointer.swift.gyb b/stdlib/public/core/UnsafeRawBufferPointer.swift.gyb index d5e2824df8818..4d4e932daf464 100644 --- a/stdlib/public/core/UnsafeRawBufferPointer.swift.gyb +++ b/stdlib/public/core/UnsafeRawBufferPointer.swift.gyb @@ -106,7 +106,6 @@ extension UnsafeRawBufferPointer { @usableFromInline internal var _position, _end: UnsafeRawPointer? - @usableFromInline @inlinable internal init(_position: UnsafeRawPointer?, _end: UnsafeRawPointer?) { self._position = _position @@ -725,13 +724,17 @@ extension ${Self} { /// bytes of the given argument. /// /// The buffer pointer argument to the `body` closure provides a collection -/// interface to the raw bytes of `arg`. The buffer is the size of the -/// instance passed as `arg` and does not include any remote storage. +/// interface to the raw bytes of `value`. The buffer is the size of the +/// instance passed as `value` and does not include any remote storage. /// /// - Parameters: -/// - arg: An instance to temporarily access through a mutable raw buffer +/// - value: An instance to temporarily access through a mutable raw buffer /// pointer. -/// - body: A closure that takes a raw buffer pointer to the bytes of `arg` +/// Note that the `inout` exclusivity rules mean that, like any other +/// `inout` argument, `value` cannot be directly accessed by other code +/// for the duration of `body`. Access must only occur through the pointer +/// argument to `body` until `body` returns. +/// - body: A closure that takes a raw buffer pointer to the bytes of `value` /// as its sole argument. If the closure has a return value, that value is /// also used as the return value of the `withUnsafeMutableBytes(of:_:)` /// function. The buffer pointer argument is valid only for the duration @@ -739,11 +742,11 @@ extension ${Self} { /// - Returns: The return value, if any, of the `body` closure. @inlinable public func withUnsafeMutableBytes( - of arg: inout T, + of value: inout T, _ body: (UnsafeMutableRawBufferPointer) throws -> Result ) rethrows -> Result { - return try withUnsafeMutablePointer(to: &arg) { + return try withUnsafeMutablePointer(to: &value) { return try body(UnsafeMutableRawBufferPointer( start: $0, count: MemoryLayout.size)) } @@ -753,28 +756,65 @@ public func withUnsafeMutableBytes( /// the given argument. /// /// The buffer pointer argument to the `body` closure provides a collection -/// interface to the raw bytes of `arg`. The buffer is the size of the -/// instance passed as `arg` and does not include any remote storage. +/// interface to the raw bytes of `value`. The buffer is the size of the +/// instance passed as `value` and does not include any remote storage. /// /// - Parameters: -/// - arg: An instance to temporarily access through a raw buffer pointer. -/// - body: A closure that takes a raw buffer pointer to the bytes of `arg` +/// - value: An instance to temporarily access through a raw buffer pointer. +/// Note that the `inout` exclusivity rules mean that, like any other +/// `inout` argument, `value` cannot be directly accessed by other code +/// for the duration of `body`. Access must only occur through the pointer +/// argument to `body` until `body` returns. +/// - body: A closure that takes a raw buffer pointer to the bytes of `value` /// as its sole argument. If the closure has a return value, that value is /// also used as the return value of the `withUnsafeBytes(of:_:)` /// function. The buffer pointer argument is valid only for the duration -/// of the closure's execution. +/// of the closure's execution. It is undefined behavior to attempt to +/// mutate through the pointer by conversion to +/// `UnsafeMutableRawBufferPointer` or any other mutable pointer type. +/// If you want to mutate a value by writing through a pointer, use +/// `withUnsafeMutableBytes(of:_:)` instead. /// - Returns: The return value, if any, of the `body` closure. @inlinable public func withUnsafeBytes( - of arg: inout T, + of value: inout T, _ body: (UnsafeRawBufferPointer) throws -> Result ) rethrows -> Result { - return try withUnsafePointer(to: &arg) { + return try withUnsafePointer(to: &value) { try body(UnsafeRawBufferPointer(start: $0, count: MemoryLayout.size)) } } +/// Invokes the given closure with a buffer pointer covering the raw bytes of +/// the given argument. +/// +/// The buffer pointer argument to the `body` closure provides a collection +/// interface to the raw bytes of `value`. The buffer is the size of the +/// instance passed as `value` and does not include any remote storage. +/// +/// - Parameters: +/// - value: An instance to temporarily access through a raw buffer pointer. +/// - body: A closure that takes a raw buffer pointer to the bytes of `value` +/// as its sole argument. If the closure has a return value, that value is +/// also used as the return value of the `withUnsafeBytes(of:_:)` +/// function. The buffer pointer argument is valid only for the duration +/// of the closure's execution. It is undefined behavior to attempt to +/// mutate through the pointer by conversion to +/// `UnsafeMutableRawBufferPointer` or any other mutable pointer type. +/// If you want to mutate a value by writing through a pointer, use +/// `withUnsafeMutableBytes(of:_:)` instead. +/// - Returns: The return value, if any, of the `body` closure. +@inlinable +public func withUnsafeBytes( + of value: T, + _ body: (UnsafeRawBufferPointer) throws -> Result +) rethrows -> Result { + let addr = UnsafeRawPointer(Builtin.addressOfBorrow(value)) + let buffer = UnsafeRawBufferPointer(start: addr, count: MemoryLayout.size) + return try body(buffer) +} + // @available(*, deprecated, renamed: "UnsafeRawBufferPointer.Iterator") public typealias UnsafeRawBufferPointerIterator = UnsafeBufferPointer.Iterator diff --git a/stdlib/public/core/UnsafeRawPointer.swift.gyb b/stdlib/public/core/UnsafeRawPointer.swift.gyb index 4d914f3a7fbd1..f235e0f290306 100644 --- a/stdlib/public/core/UnsafeRawPointer.swift.gyb +++ b/stdlib/public/core/UnsafeRawPointer.swift.gyb @@ -965,14 +965,14 @@ extension ${Self}: Comparable { } extension ${Self}: Hashable { - /// The pointer's hash value. + /// Hashes the essential components of this value by feeding them into the + /// given hasher. /// - /// The hash value is not guaranteed to be stable across different - /// invocations of the same program. Do not persist the hash value across - /// program runs. - @inlinable - public var hashValue: Int { - return Int(bitPattern: self) + /// - Parameter hasher: The hasher to use when combining the components + /// of this instance. + @inlinable // FIXME(sil-serialize-all) + public func hash(into hasher: inout Hasher) { + hasher.combine(Int(bitPattern: self)) } } @@ -995,7 +995,6 @@ extension Unsafe${Mutable}RawPointer : CustomReflectable { extension Unsafe${Mutable}RawPointer : CustomPlaygroundQuickLookable { @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) @available(*, deprecated, message: "Unsafe${Mutable}RawPointer.customPlaygroundQuickLook will be removed in a future Swift version") internal var summary: String { let selfType = "${Self}" diff --git a/stdlib/public/core/ValidUTF8Buffer.swift b/stdlib/public/core/ValidUTF8Buffer.swift index 04687102005fe..ffa7816d57a5c 100644 --- a/stdlib/public/core/ValidUTF8Buffer.swift +++ b/stdlib/public/core/ValidUTF8Buffer.swift @@ -25,13 +25,11 @@ public struct _ValidUTF8Buffer { internal var _biasedBits: Storage @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal init(_biasedBits: Storage) { self._biasedBits = _biasedBits } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal init(_containing e: Element) { _sanityCheck( e != 192 && e != 193 && !(245...255).contains(e), "invalid UTF8 byte") @@ -72,7 +70,6 @@ extension _ValidUTF8Buffer : Collection { internal var _biasedBits: Storage @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal init(_biasedBits: Storage) { self._biasedBits = _biasedBits } @inlinable // FIXME(sil-serialize-all) @@ -186,7 +183,6 @@ extension _ValidUTF8Buffer : RangeReplaceableCollection { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline internal func _isValid(_ i: Index) -> Bool { return i == endIndex || indices.contains(i) } @@ -221,4 +217,12 @@ extension _ValidUTF8Buffer { public static var encodedReplacementCharacter : _ValidUTF8Buffer { return _ValidUTF8Buffer(_biasedBits: 0xBD_BF_EF &+ 0x01_01_01) } + + @inlinable + internal var _bytes: (bytes: UInt64, count: Int) { + let count = self.count + let mask: UInt64 = 1 &<< (UInt64(truncatingIfNeeded: count) &<< 3) &- 1 + let unbiased = UInt64(truncatingIfNeeded: _biasedBits) &- 0x0101010101010101 + return (unbiased & mask, count) + } } diff --git a/stdlib/public/core/VarArgs.swift b/stdlib/public/core/VarArgs.swift index 46b792f333237..07a6662813f0e 100644 --- a/stdlib/public/core/VarArgs.swift +++ b/stdlib/public/core/VarArgs.swift @@ -32,6 +32,11 @@ /// arguments. C functions that use the `...` syntax for variadic arguments /// are not imported, and therefore can't be called using `CVarArg` arguments. /// +/// If you need to pass an optional pointer as a `CVarArg` argument, use the +/// `Int(bitPattern:)` initializer to interpret the optional pointer as an +/// `Int` value, which has the same C variadic calling conventions as a pointer +/// on all supported platforms. +/// /// - Note: Declaring conformance to the `CVarArg` protocol for types defined /// outside the standard library is not supported. public protocol CVarArg { @@ -102,6 +107,11 @@ internal typealias _VAInt = Int32 /// execution of `withVaList(_:_:)`. Do not store or return the pointer for /// later use. /// +/// If you need to pass an optional pointer as a `CVarArg` argument, use the +/// `Int(bitPattern:)` initializer to interpret the optional pointer as an +/// `Int` value, which has the same C variadic calling conventions as a pointer +/// on all supported platforms. +/// /// - Parameters: /// - args: An array of arguments to convert to a C `va_list` pointer. /// - body: A closure with a `CVaListPointer` parameter that references the @@ -122,7 +132,6 @@ public func withVaList(_ args: [CVarArg], /// Invoke `body` with a C `va_list` argument derived from `builder`. @inlinable // FIXME(sil-serialize-all) -@usableFromInline // FIXME(sil-serialize-all) internal func _withVaList( _ builder: _VaListBuilder, _ body: (CVaListPointer) -> R @@ -144,6 +153,11 @@ internal func _withVaList( /// uses, such as in a `class` initializer, you may find that the language /// rules do not allow you to use `withVaList(_:_:)` as intended. /// +/// If you need to pass an optional pointer as a `CVarArg` argument, use the +/// `Int(bitPattern:)` initializer to interpret the optional pointer as an +/// `Int` value, which has the same C variadic calling conventions as a pointer +/// on all supported platforms. +/// /// - Parameter args: An array of arguments to convert to a C `va_list` /// pointer. /// - Returns: A pointer that can be used with C functions that take a @@ -377,7 +391,6 @@ final internal class _VaListBuilder { @usableFromInline internal struct Header { @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init() {} @usableFromInline // FIXME(sil-serialize-all) @@ -392,18 +405,15 @@ final internal class _VaListBuilder { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init() { // prepare the register save area storage = ContiguousArray(repeating: 0, count: _registerSaveWords) } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) deinit {} @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func append(_ arg: CVarArg) { var encoded = arg._cVarArgEncoding @@ -445,7 +455,6 @@ final internal class _VaListBuilder { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func va_list() -> CVaListPointer { header.reg_save_area = storage._baseAddress header.overflow_arg_area @@ -476,11 +485,9 @@ final internal class _VaListBuilder { final internal class _VaListBuilder { @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init() {} @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func append(_ arg: CVarArg) { // Write alignment padding if necessary. // This is needed on architectures where the ABI alignment of some @@ -521,7 +528,6 @@ final internal class _VaListBuilder { // FIXME: this should be packaged into a better storage type @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func appendWords(_ words: [Int]) { let newCount = count + words.count if newCount > allocated { @@ -548,7 +554,6 @@ final internal class _VaListBuilder { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func rawSizeAndAlignment( _ wordCount: Int ) -> (Builtin.Word, Builtin.Word) { @@ -557,7 +562,6 @@ final internal class _VaListBuilder { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal func allocStorage(wordCount: Int) -> UnsafeMutablePointer { let (rawSize, rawAlignment) = rawSizeAndAlignment(wordCount) let rawStorage = Builtin.allocRaw(rawSize, rawAlignment) @@ -574,7 +578,6 @@ final internal class _VaListBuilder { } @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) deinit { if let allocatedStorage = storage { deallocStorage(wordCount: allocated, storage: allocatedStorage) diff --git a/stdlib/public/core/WriteBackMutableSlice.swift b/stdlib/public/core/WriteBackMutableSlice.swift index b7ca91f2056c4..5c7453c4c4e11 100644 --- a/stdlib/public/core/WriteBackMutableSlice.swift +++ b/stdlib/public/core/WriteBackMutableSlice.swift @@ -11,7 +11,6 @@ //===----------------------------------------------------------------------===// @inlinable -@usableFromInline internal func _writeBackMutableSlice( _ self_: inout C, bounds: Range, slice: Slice_ ) where diff --git a/stdlib/public/core/Zip.swift b/stdlib/public/core/Zip.swift index f5b5e98e2d329..6b05fd5aff77a 100644 --- a/stdlib/public/core/Zip.swift +++ b/stdlib/public/core/Zip.swift @@ -101,7 +101,6 @@ extension Zip2Sequence { /// Creates an instance around a pair of underlying iterators. @inlinable // FIXME(sil-serialize-all) - @usableFromInline // FIXME(sil-serialize-all) internal init( _ iterator1: Sequence1.Iterator, _ iterator2: Sequence2.Iterator diff --git a/stdlib/public/runtime/AnyHashableSupport.cpp b/stdlib/public/runtime/AnyHashableSupport.cpp index f5cd207b28f34..2e9035f8979de 100644 --- a/stdlib/public/runtime/AnyHashableSupport.cpp +++ b/stdlib/public/runtime/AnyHashableSupport.cpp @@ -160,7 +160,7 @@ void _swift_makeAnyHashableUpcastingToHashableBaseType( getValueFromSwiftValue(srcSwiftValue); if (auto unboxedHashableWT = - swift_conformsToProtocol(type, &HashableProtocolDescriptor)) { + swift_conformsToProtocol(unboxedType, &HashableProtocolDescriptor)) { #ifndef SWIFT_RUNTIME_ENABLE_GUARANTEED_NORMAL_ARGUMENTS ValueBuffer unboxedCopyBuf; // Allocate buffer. @@ -194,32 +194,10 @@ void _swift_makeAnyHashableUpcastingToHashableBaseType( return; } - case MetadataKind::Struct: - case MetadataKind::Enum: - case MetadataKind::Optional: + default: _swift_makeAnyHashableUsingDefaultRepresentation( value, anyHashableResultPointer, type, hashableWT); return; - - case MetadataKind::ErrorObject: - // ErrorObject metadata is not used for any Swift-level values, so - // this case is unreachable. - _failCorruptType(type); - - case MetadataKind::Opaque: - case MetadataKind::Tuple: - case MetadataKind::Function: - case MetadataKind::Existential: - case MetadataKind::Metatype: - case MetadataKind::ExistentialMetatype: - case MetadataKind::HeapLocalVariable: - case MetadataKind::HeapGenericLocalVariable: - // We assume that the value can not be an existential, - // because existentials can't conform to Hashable today. - // - // FIXME: handle generalized existentials when Swift has them. - _failCorruptType(type); } - _failCorruptType(type); } diff --git a/stdlib/public/runtime/CMakeLists.txt b/stdlib/public/runtime/CMakeLists.txt index c83e4714070ed..d53c72531c790 100644 --- a/stdlib/public/runtime/CMakeLists.txt +++ b/stdlib/public/runtime/CMakeLists.txt @@ -73,6 +73,12 @@ set(swift_runtime_sources "${SWIFT_SOURCE_DIR}/lib/Demangling/ManglingUtils.cpp" "${SWIFT_SOURCE_DIR}/lib/Demangling/Punycode.cpp") +# When we're building with assertions, include the demangle node dumper to aid +# in debugging. +if (LLVM_ENABLE_ASSERTIONS) + list(APPEND swift_runtime_sources "${SWIFT_SOURCE_DIR}/lib/Demangling/NodeDumper.cpp") +endif(LLVM_ENABLE_ASSERTIONS) + # Acknowledge that the following sources are known. set(LLVM_OPTIONAL_SOURCES MutexPThread.cpp @@ -97,16 +103,39 @@ if(SWIFT_BUILD_STATIC_STDLIB AND "${sdk}" STREQUAL "LINUX") string(TOLOWER "${sdk}" lowercase_sdk) # These two libraries are only used with the static swiftcore - add_swift_library(swiftImageInspectionShared STATIC + add_swift_library(swiftImageInspectionShared TARGET_LIBRARY STATIC ImageInspectionELF.cpp C_COMPILE_FLAGS ${swift_runtime_library_compile_flags} - LINK_FLAGS ${swift_runtime_linker_flags}) - set_target_properties(swiftImageInspectionShared PROPERTIES - ARCHIVE_OUTPUT_DIRECTORY "${SWIFTSTATICLIB_DIR}/${lowercase_sdk}") + LINK_FLAGS ${swift_runtime_linker_flags} + INSTALL_IN_COMPONENT stdlib) + + foreach(arch IN LISTS SWIFT_SDK_${sdk}_ARCHITECTURES) + set(FragileSupportLibrary swiftImageInspectionShared-${SWIFT_SDK_${sdk}_LIB_SUBDIR}-${arch}) + set(LibraryLocation ${SWIFTSTATICLIB_DIR}/${lowercase_sdk}/${arch}) + add_custom_command_target(swift_image_inspection_${arch}_static + COMMAND + "${CMAKE_COMMAND}" -E copy $ ${LibraryLocation} + OUTPUT + "${LibraryLocation}/${CMAKE_STATIC_LIBRARY_PREFIX}swiftImageInspectionShared${CMAKE_STATIC_LIBRARY_SUFFIX}" + DEPENDS + ${FragileSupportLibrary}) + swift_install_in_component(stdlib + FILES $ + DESTINATION "lib/swift_static/${lowercase_sdk}/${arch}") + endforeach() - swift_install_in_component(stdlib - TARGETS swiftImageInspectionShared - DESTINATION "lib/swift_static/${lowercase_sdk}") + set(FragileSupportLibraryPrimary swiftImageInspectionShared-${SWIFT_SDK_${sdk}_LIB_SUBDIR}-${SWIFT_PRIMARY_VARIANT_ARCH}) + set(LibraryLocationPrimary ${SWIFTSTATICLIB_DIR}/${lowercase_sdk}) + add_custom_command_target(swift_image_inspection_static_primary_arch + COMMAND + "${CMAKE_COMMAND}" -E copy $ ${LibraryLocationPrimary} + OUTPUT + "${LibraryLocationPrimary}/${CMAKE_STATIC_LIBRARY_PREFIX}swiftImageInspectionShared${CMAKE_STATIC_LIBRARY_SUFFIX}" + DEPENDS + ${FragileSupportLibraryPrimary}) + swift_install_in_component(stdlib + FILES $ + DESTINATION "lib/swift_static/${lowercase_sdk}") # Generate the static-executable-args.lnk file used for ELF systems (eg linux) set(linkfile "${lowercase_sdk}/static-executable-args.lnk") @@ -125,8 +154,12 @@ if(SWIFT_BUILD_STATIC_STDLIB AND "${sdk}" STREQUAL "LINUX") FILES "${SWIFTSTATICLIB_DIR}/${linkfile}" DESTINATION "lib/swift_static/${lowercase_sdk}") add_custom_target(static_binary_magic ALL DEPENDS ${static_binary_lnk_file_list}) + foreach(arch IN LISTS SWIFT_SDK_LINUX_ARCHITECTURES) + add_dependencies(static_binary_magic ${swift_image_inspection_${arch}_static}) + endforeach() + add_dependencies(static_binary_magic ${swift_image_inspection_static_primary_arch}) - add_swift_library(swiftImageInspectionShared OBJECT_LIBRARY TARGET_LIBRARY + add_swift_library(swiftImageInspectionSharedObject OBJECT_LIBRARY TARGET_LIBRARY ImageInspectionELF.cpp C_COMPILE_FLAGS ${swift_runtime_library_compile_flags} LINK_FLAGS ${swift_runtime_linker_flags} diff --git a/stdlib/public/runtime/Casting.cpp b/stdlib/public/runtime/Casting.cpp index a73b8dc6b3051..98634688c84ab 100644 --- a/stdlib/public/runtime/Casting.cpp +++ b/stdlib/public/runtime/Casting.cpp @@ -352,21 +352,13 @@ bool swift::_conformsToProtocol(const OpaqueValue *value, return _unknownClassConformsToObjCProtocol(value, protocol); return false; #else - _failCorruptType(type); + return false; #endif + case MetadataKind::Existential: // FIXME case MetadataKind::ExistentialMetatype: // FIXME - case MetadataKind::Function: - case MetadataKind::HeapLocalVariable: - case MetadataKind::HeapGenericLocalVariable: - case MetadataKind::ErrorObject: - case MetadataKind::Metatype: - case MetadataKind::Enum: - case MetadataKind::Optional: - case MetadataKind::Opaque: - case MetadataKind::Struct: - case MetadataKind::Tuple: + default: return false; } @@ -490,20 +482,11 @@ findDynamicValueAndType(OpaqueValue *value, const Metadata *type, } // Non-polymorphic types. - case MetadataKind::Function: - case MetadataKind::HeapLocalVariable: - case MetadataKind::HeapGenericLocalVariable: - case MetadataKind::ErrorObject: - case MetadataKind::Enum: - case MetadataKind::Optional: - case MetadataKind::Opaque: - case MetadataKind::Struct: - case MetadataKind::Tuple: + default: outValue = value; outType = type; return; } - _failCorruptType(type); } extern "C" const Metadata * @@ -544,23 +527,9 @@ static void deallocateDynamicValue(OpaqueValue *value, const Metadata *type) { } // None of the rest of these require deallocation. - case MetadataKind::Class: - case MetadataKind::ForeignClass: - case MetadataKind::ObjCClassWrapper: - case MetadataKind::Metatype: - case MetadataKind::ExistentialMetatype: - case MetadataKind::Function: - case MetadataKind::HeapLocalVariable: - case MetadataKind::HeapGenericLocalVariable: - case MetadataKind::ErrorObject: - case MetadataKind::Enum: - case MetadataKind::Optional: - case MetadataKind::Opaque: - case MetadataKind::Struct: - case MetadataKind::Tuple: + default: return; } - _failCorruptType(type); } #if SWIFT_OBJC_INTEROP @@ -575,19 +544,7 @@ swift_dynamicCastMetatypeToObjectConditional(const Metadata *metatype) { return (id)metatype->getObjCClassObject(); // Other kinds of metadata don't cast to AnyObject. - case MetadataKind::Struct: - case MetadataKind::Enum: - case MetadataKind::Optional: - case MetadataKind::Opaque: - case MetadataKind::Tuple: - case MetadataKind::Function: - case MetadataKind::Existential: - case MetadataKind::Metatype: - case MetadataKind::ExistentialMetatype: - case MetadataKind::ForeignClass: - case MetadataKind::HeapLocalVariable: - case MetadataKind::HeapGenericLocalVariable: - case MetadataKind::ErrorObject: + default: return nullptr; } } @@ -603,19 +560,7 @@ swift_dynamicCastMetatypeToObjectUnconditional(const Metadata *metatype) { return (id)metatype->getObjCClassObject(); // Other kinds of metadata don't cast to AnyObject. - case MetadataKind::Struct: - case MetadataKind::Enum: - case MetadataKind::Optional: - case MetadataKind::Opaque: - case MetadataKind::Tuple: - case MetadataKind::Function: - case MetadataKind::Existential: - case MetadataKind::Metatype: - case MetadataKind::ExistentialMetatype: - case MetadataKind::ForeignClass: - case MetadataKind::HeapLocalVariable: - case MetadataKind::HeapGenericLocalVariable: - case MetadataKind::ErrorObject: { + default: { std::string sourceName = nameForMetadata(metatype); swift_dynamicCastFailure(metatype, sourceName.c_str(), nullptr, "AnyObject", @@ -876,12 +821,7 @@ static bool _dynamicCastToExistential(OpaqueValue *dest, #endif LLVM_FALLTHROUGH; - case MetadataKind::Function: - case MetadataKind::HeapLocalVariable: - case MetadataKind::HeapGenericLocalVariable: - case MetadataKind::ErrorObject: - case MetadataKind::Opaque: - case MetadataKind::Tuple: + default: return fallbackForNonClass(); } @@ -1010,7 +950,7 @@ swift_dynamicCastUnknownClassImpl(const void *object, = static_cast(targetType)->Class; return swift_dynamicCastObjCClass(object, targetClassType); #else - _failCorruptType(targetType); + return nullptr; #endif } @@ -1019,7 +959,7 @@ swift_dynamicCastUnknownClassImpl(const void *object, auto targetClassType = static_cast(targetType); return swift_dynamicCastForeignClass(object, targetClassType); #else - _failCorruptType(targetType); + return nullptr; #endif } @@ -1027,20 +967,9 @@ swift_dynamicCastUnknownClassImpl(const void *object, return _dynamicCastUnknownClassToExistential(object, static_cast(targetType)); } - case MetadataKind::ExistentialMetatype: - case MetadataKind::Function: - case MetadataKind::HeapLocalVariable: - case MetadataKind::HeapGenericLocalVariable: - case MetadataKind::ErrorObject: - case MetadataKind::Metatype: - case MetadataKind::Enum: - case MetadataKind::Optional: - case MetadataKind::Opaque: - case MetadataKind::Struct: - case MetadataKind::Tuple: + default: return nullptr; } - _failCorruptType(targetType); } /// Perform a dynamic class of some sort of class instance to some @@ -1060,7 +989,7 @@ swift_dynamicCastUnknownClassUnconditionalImpl(const void *object, = static_cast(targetType)->Class; return swift_dynamicCastObjCClassUnconditional(object, targetClassType); #else - _failCorruptType(targetType); + swift_dynamicCastFailure(_swift_getClass(object), targetType); #endif } @@ -1069,7 +998,7 @@ swift_dynamicCastUnknownClassUnconditionalImpl(const void *object, auto targetClassType = static_cast(targetType); return swift_dynamicCastForeignClassUnconditional(object, targetClassType); #else - _failCorruptType(targetType); + swift_dynamicCastFailure(_swift_getClass(object), targetType); #endif } @@ -1082,21 +1011,10 @@ swift_dynamicCastUnknownClassUnconditionalImpl(const void *object, swift_dynamicCastFailure(_swift_getClass(object), targetType); } - - case MetadataKind::ExistentialMetatype: - case MetadataKind::Function: - case MetadataKind::HeapLocalVariable: - case MetadataKind::HeapGenericLocalVariable: - case MetadataKind::ErrorObject: - case MetadataKind::Metatype: - case MetadataKind::Enum: - case MetadataKind::Optional: - case MetadataKind::Opaque: - case MetadataKind::Struct: - case MetadataKind::Tuple: + + default: swift_dynamicCastFailure(_swift_getClass(object), targetType); } - _failCorruptType(targetType); } /******************************************************************************/ @@ -1146,18 +1064,7 @@ swift_dynamicCastMetatypeImpl(const Metadata *sourceType, return nullptr; } - case MetadataKind::Existential: - case MetadataKind::ExistentialMetatype: - case MetadataKind::Function: - case MetadataKind::HeapLocalVariable: - case MetadataKind::HeapGenericLocalVariable: - case MetadataKind::ErrorObject: - case MetadataKind::Metatype: - case MetadataKind::Enum: - case MetadataKind::Optional: - case MetadataKind::Opaque: - case MetadataKind::Struct: - case MetadataKind::Tuple: + default: return nullptr; } break; @@ -1177,34 +1084,12 @@ swift_dynamicCastMetatypeImpl(const Metadata *sourceType, (const ClassMetadata*)targetType)) return origSourceType; return nullptr; - case MetadataKind::Existential: - case MetadataKind::ExistentialMetatype: - case MetadataKind::Function: - case MetadataKind::HeapLocalVariable: - case MetadataKind::HeapGenericLocalVariable: - case MetadataKind::ErrorObject: - case MetadataKind::Metatype: - case MetadataKind::Enum: - case MetadataKind::Optional: - case MetadataKind::Opaque: - case MetadataKind::Struct: - case MetadataKind::Tuple: + default: return nullptr; } break; - case MetadataKind::Existential: - case MetadataKind::ExistentialMetatype: - case MetadataKind::Function: - case MetadataKind::HeapLocalVariable: - case MetadataKind::HeapGenericLocalVariable: - case MetadataKind::ErrorObject: - case MetadataKind::Metatype: - case MetadataKind::Enum: - case MetadataKind::Optional: - case MetadataKind::Opaque: - case MetadataKind::Struct: - case MetadataKind::Tuple: + default: // The cast succeeds only if the metadata pointers are statically // equivalent. if (sourceType != targetType) @@ -1258,18 +1143,7 @@ swift_dynamicCastMetatypeUnconditionalImpl(const Metadata *sourceType, // If we returned, then the cast succeeded. return origSourceType; } - case MetadataKind::Existential: - case MetadataKind::ExistentialMetatype: - case MetadataKind::Function: - case MetadataKind::HeapLocalVariable: - case MetadataKind::HeapGenericLocalVariable: - case MetadataKind::ErrorObject: - case MetadataKind::Metatype: - case MetadataKind::Enum: - case MetadataKind::Optional: - case MetadataKind::Opaque: - case MetadataKind::Struct: - case MetadataKind::Tuple: + default: swift_dynamicCastFailure(sourceType, targetType); } break; @@ -1290,41 +1164,17 @@ swift_dynamicCastMetatypeUnconditionalImpl(const Metadata *sourceType, (const ClassMetadata*)targetType); // If we returned, then the cast succeeded. return origSourceType; - case MetadataKind::Existential: - case MetadataKind::ExistentialMetatype: - case MetadataKind::Function: - case MetadataKind::HeapLocalVariable: - case MetadataKind::HeapGenericLocalVariable: - case MetadataKind::ErrorObject: - case MetadataKind::Metatype: - case MetadataKind::Enum: - case MetadataKind::Optional: - case MetadataKind::Opaque: - case MetadataKind::Struct: - case MetadataKind::Tuple: + default: swift_dynamicCastFailure(sourceType, targetType); } break; - case MetadataKind::Existential: - case MetadataKind::ExistentialMetatype: - case MetadataKind::Function: - case MetadataKind::HeapLocalVariable: - case MetadataKind::HeapGenericLocalVariable: - case MetadataKind::ErrorObject: - case MetadataKind::Metatype: - case MetadataKind::Enum: - case MetadataKind::Optional: - case MetadataKind::Opaque: - case MetadataKind::Struct: - case MetadataKind::Tuple: + default: // The cast succeeds only if the metadata pointers are statically // equivalent. if (sourceType != targetType) swift_dynamicCastFailure(sourceType, targetType); return origSourceType; } - - swift_runtime_unreachable("Unhandled MetadataKind in switch."); } /******************************************************************************/ @@ -1688,18 +1538,10 @@ static bool _dynamicCastToMetatype(OpaqueValue *dest, return _dynamicCastUnknownClassToMetatype(dest, object, targetType, flags); } - case MetadataKind::Function: - case MetadataKind::HeapLocalVariable: - case MetadataKind::HeapGenericLocalVariable: - case MetadataKind::ErrorObject: - case MetadataKind::Enum: - case MetadataKind::Optional: - case MetadataKind::Opaque: case MetadataKind::Struct: // AnyHashable, if metatypes implement Hashable - case MetadataKind::Tuple: + default: return _fail(src, srcType, targetType, flags); } - _failCorruptType(srcType); } /// Perform a dynamic cast of a metatype to an existential metatype type. @@ -1863,18 +1705,10 @@ static bool _dynamicCastToExistentialMetatype(OpaqueValue *dest, targetType, flags); } - case MetadataKind::Function: - case MetadataKind::HeapLocalVariable: - case MetadataKind::HeapGenericLocalVariable: - case MetadataKind::ErrorObject: - case MetadataKind::Enum: - case MetadataKind::Optional: - case MetadataKind::Opaque: case MetadataKind::Struct: // AnyHashable, if metatypes implement Hashable - case MetadataKind::Tuple: + default: return _fail(src, srcType, targetType, flags); } - _failCorruptType(srcType); } /******************************************************************************/ @@ -1935,23 +1769,9 @@ static bool _dynamicCastToFunction(OpaqueValue *dest, static_cast(srcType), targetType, flags); - case MetadataKind::Class: - case MetadataKind::Struct: - case MetadataKind::Enum: - case MetadataKind::Optional: - case MetadataKind::ObjCClassWrapper: - case MetadataKind::ForeignClass: - case MetadataKind::ExistentialMetatype: - case MetadataKind::HeapLocalVariable: - case MetadataKind::HeapGenericLocalVariable: - case MetadataKind::ErrorObject: - case MetadataKind::Metatype: - case MetadataKind::Opaque: - case MetadataKind::Tuple: + default: return _fail(src, srcType, targetType, flags); } - - swift_runtime_unreachable("Unhandled MetadataKind in switch."); } /******************************************************************************/ @@ -1975,10 +1795,7 @@ static id dynamicCastValueToNSError(OpaqueValue *src, BoxPair errorBox = swift_allocError(srcType, srcErrorWitness, src, /*isTake*/ flags & DynamicCastFlags::TakeOnSuccess); auto *error = (SwiftError *)errorBox.object; - id result = _swift_stdlib_bridgeErrorToNSError(error); - // Now that we have bridged the error to nserror, release the error. - SWIFT_CC_PLUSZERO_GUARD(swift_errorRelease(error)); - return result; + return _swift_stdlib_bridgeErrorToNSError(error); } #endif @@ -2049,7 +1866,7 @@ checkDynamicCastFromOptional(OpaqueValue *dest, // .Some // Single payload enums are guaranteed layout compatible with their // payload. Only the source's payload needs to be taken or destroyed. - return {false, payloadType}; + return checkDynamicCastFromOptional(dest, src, payloadType, targetType, flags); } /******************************************************************************/ @@ -2522,14 +2339,7 @@ static bool swift_dynamicCastImpl(OpaqueValue *dest, OpaqueValue *src, return _fail(src, srcType, targetType, flags); } - case MetadataKind::ExistentialMetatype: - case MetadataKind::Function: - case MetadataKind::HeapLocalVariable: - case MetadataKind::HeapGenericLocalVariable: - case MetadataKind::ErrorObject: - case MetadataKind::Metatype: - case MetadataKind::Opaque: - case MetadataKind::Tuple: + default: return _fail(src, srcType, targetType, flags); } break; @@ -2604,26 +2414,14 @@ static bool swift_dynamicCastImpl(OpaqueValue *dest, OpaqueValue *src, } break; - case MetadataKind::Existential: - case MetadataKind::ExistentialMetatype: - case MetadataKind::Function: - case MetadataKind::HeapLocalVariable: - case MetadataKind::HeapGenericLocalVariable: - case MetadataKind::ErrorObject: - case MetadataKind::Metatype: - case MetadataKind::Opaque: - case MetadataKind::Tuple: + default: break; } LLVM_FALLTHROUGH; // The non-polymorphic types. - case MetadataKind::HeapLocalVariable: - case MetadataKind::HeapGenericLocalVariable: - case MetadataKind::ErrorObject: - case MetadataKind::Opaque: - case MetadataKind::Tuple: + default: // If there's an exact type match, we're done. if (srcType == targetType) return _succeed(dest, src, srcType, flags); @@ -2646,7 +2444,6 @@ static bool swift_dynamicCastImpl(OpaqueValue *dest, OpaqueValue *src, // Otherwise, we have a failure. return _fail(src, srcType, targetType, flags); } - _failCorruptType(srcType); } static inline bool swift_isClassOrObjCExistentialTypeImpl(const Metadata *T) { @@ -2959,6 +2756,7 @@ static id bridgeAnythingNonVerbatimToObjectiveC(OpaqueValue *src, return objc_retain(protocolObj); } } + // Handle bridgable types. } else if (auto srcBridgeWitness = findBridgeWitness(srcType)) { // Bridge the source value to an object. auto srcBridgedObject = @@ -2969,21 +2767,28 @@ static id bridgeAnythingNonVerbatimToObjectiveC(OpaqueValue *src, srcType->vw_destroy(src); return (id)srcBridgedObject; + // Handle Errors. + } else if (auto srcErrorWitness = findErrorWitness(srcType)) { + // Bridge the source value to an NSError. + auto box = swift_allocError(srcType, srcErrorWitness, src, consume) + .object; + return _swift_stdlib_bridgeErrorToNSError((SwiftError*)box); } // Fall back to boxing. return (id)bridgeAnythingToSwiftValueObject(src, srcType, consume); } -/// public func _bridgeAnythingNonVerbatimToObjectiveC(_ x: T) -> AnyObject +/// public +/// func _bridgeAnythingNonVerbatimToObjectiveC(_ x: __owned T) -> AnyObject +/// /// Called by inlined stdlib code. -#define _bridgeAnythingNonVerbatimToObjectiveC \ - MANGLE_SYM(s38_bridgeAnythingNonVerbatimToObjectiveCyyXlxlF) +#define _bridgeAnythingNonVerbatimToObjectiveC \ + MANGLE_SYM(s38_bridgeAnythingNonVerbatimToObjectiveCyyXlxnlF) SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_API id _bridgeAnythingNonVerbatimToObjectiveC(OpaqueValue *src, const Metadata *srcType) { bool shouldConsume = true; - SWIFT_CC_PLUSZERO_GUARD(shouldConsume = false); return bridgeAnythingNonVerbatimToObjectiveC(src, srcType, /*consume*/shouldConsume); } @@ -3023,19 +2828,7 @@ findBridgeWitness(const Metadata *T) { break; } - case MetadataKind::Class: - case MetadataKind::Struct: - case MetadataKind::Enum: - case MetadataKind::Optional: - case MetadataKind::Opaque: - case MetadataKind::Tuple: - case MetadataKind::Function: - case MetadataKind::Existential: - case MetadataKind::ObjCClassWrapper: - case MetadataKind::ForeignClass: - case MetadataKind::HeapLocalVariable: - case MetadataKind::HeapGenericLocalVariable: - case MetadataKind::ErrorObject: + default: break; } return nullptr; diff --git a/stdlib/public/runtime/Demangle.cpp b/stdlib/public/runtime/Demangle.cpp index 4a3f522c128db..07fb3fe40adc3 100644 --- a/stdlib/public/runtime/Demangle.cpp +++ b/stdlib/public/runtime/Demangle.cpp @@ -631,18 +631,19 @@ swift::_swift_buildDemanglingForMetadata(const Metadata *type, } return tupleNode; } - case MetadataKind::Opaque: { + case MetadataKind::HeapLocalVariable: + case MetadataKind::HeapGenericLocalVariable: + case MetadataKind::ErrorObject: + break; + case MetadataKind::Opaque: + default: { if (auto builtinType = _buildDemanglerForBuiltinType(type, Dem)) return builtinType; - + // FIXME: Some opaque types do have manglings, but we don't have enough info // to figure them out. break; } - case MetadataKind::HeapLocalVariable: - case MetadataKind::HeapGenericLocalVariable: - case MetadataKind::ErrorObject: - break; } // Not a type. return nullptr; diff --git a/stdlib/public/runtime/Enum.cpp b/stdlib/public/runtime/Enum.cpp index b30a40482144a..bcdf669d7d994 100644 --- a/stdlib/public/runtime/Enum.cpp +++ b/stdlib/public/runtime/Enum.cpp @@ -89,12 +89,14 @@ swift::swift_initEnumMetadataSinglePayload(EnumMetadata *self, auto vwtable = getMutableVWTableForInit(self, layoutFlags); size_t align = payloadLayout->flags.getAlignment(); + bool isBT = payloadLayout->flags.isBitwiseTakable(); TypeLayout layout; layout.size = size; - layout.flags = payloadLayout->flags - .withExtraInhabitants(unusedExtraInhabitants > 0) - .withEnumWitnesses(true) - .withInlineStorage(ValueWitnessTable::isValueInline(size, align)); + layout.flags = + payloadLayout->flags.withExtraInhabitants(unusedExtraInhabitants > 0) + .withEnumWitnesses(true) + .withInlineStorage( + ValueWitnessTable::isValueInline(isBT, size, align)); auto rawStride = llvm::alignTo(size, align); layout.stride = rawStride == 0 ? 1 : rawStride; @@ -200,14 +202,14 @@ swift::swift_initEnumMetadataMultiPayload(EnumMetadata *enumType, TypeLayout layout; layout.size = totalSize; layout.flags = ValueWitnessFlags() - .withAlignmentMask(alignMask) - .withPOD(isPOD) - .withBitwiseTakable(isBT) - // TODO: Extra inhabitants - .withExtraInhabitants(false) - .withEnumWitnesses(true) - .withInlineStorage(ValueWitnessTable::isValueInline(totalSize, alignMask+1)) - ; + .withAlignmentMask(alignMask) + .withPOD(isPOD) + .withBitwiseTakable(isBT) + // TODO: Extra inhabitants + .withExtraInhabitants(false) + .withEnumWitnesses(true) + .withInlineStorage(ValueWitnessTable::isValueInline( + isBT, totalSize, alignMask + 1)); auto rawStride = (totalSize + alignMask) & ~alignMask; layout.stride = rawStride == 0 ? 1 : rawStride; diff --git a/stdlib/public/runtime/ErrorDefaultImpls.cpp b/stdlib/public/runtime/ErrorDefaultImpls.cpp index 2599da39e346e..f4ad4a026e1fe 100644 --- a/stdlib/public/runtime/ErrorDefaultImpls.cpp +++ b/stdlib/public/runtime/ErrorDefaultImpls.cpp @@ -32,20 +32,7 @@ intptr_t _swift_stdlib_getDefaultErrorCode(OpaqueValue *error, result = T->vw_getEnumTag(error); break; - case MetadataKind::Class: - case MetadataKind::ObjCClassWrapper: - case MetadataKind::ForeignClass: - case MetadataKind::Function: - case MetadataKind::Struct: - case MetadataKind::Optional: - case MetadataKind::Opaque: - case MetadataKind::Tuple: - case MetadataKind::Existential: - case MetadataKind::Metatype: - case MetadataKind::ExistentialMetatype: - case MetadataKind::HeapLocalVariable: - case MetadataKind::HeapGenericLocalVariable: - case MetadataKind::ErrorObject: + default: result = 1; break; } diff --git a/stdlib/public/runtime/ErrorObject.h b/stdlib/public/runtime/ErrorObject.h index e748dbeb661df..43358f0f7b0cd 100644 --- a/stdlib/public/runtime/ErrorObject.h +++ b/stdlib/public/runtime/ErrorObject.h @@ -218,6 +218,8 @@ void swift_unexpectedError(SwiftError *object); #if SWIFT_OBJC_INTEROP /// Initialize an Error box to make it usable as an NSError instance. +/// +/// errorObject is assumed to be passed at +1 and consumed in this function. SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_SPI id _swift_stdlib_bridgeErrorToNSError(SwiftError *errorObject); diff --git a/stdlib/public/runtime/ErrorObject.mm b/stdlib/public/runtime/ErrorObject.mm index c1e109bc16bf6..d67b4cd78f517 100644 --- a/stdlib/public/runtime/ErrorObject.mm +++ b/stdlib/public/runtime/ErrorObject.mm @@ -37,6 +37,7 @@ #include #include #include +#include "../SDK/Foundation/NSError.h" using namespace swift; using namespace swift::hashable_support; @@ -220,31 +221,40 @@ static Class getSwiftNativeNSErrorClass() { object_dispose((id)error); } +/// Get the error bridging info from the Foundation overlay. If it can't +/// be loaded, return all NULLs. +static ErrorBridgingInfo getErrorBridgingInfo() { + auto *info = SWIFT_LAZY_CONSTANT( + reinterpret_cast( + dlsym(RTLD_DEFAULT, ERROR_BRIDGING_SYMBOL_NAME_STRING))); + if (!info) { + ErrorBridgingInfo nulls = {}; + return nulls; + } + return *info; +} + static const WitnessTable *getNSErrorConformanceToError() { // CFError and NSError are toll-free-bridged, so we can use either type's // witness table interchangeably. CFError's is potentially slightly more // efficient since it doesn't need to dispatch for an unsubclassed NSCFError. - // The witness table lives in the Foundation overlay, but it should be safe - // to assume that that's been linked in if a user is using NSError in their - // Swift source. + // The error bridging info lives in the Foundation overlay, but it should be + // safe to assume that that's been linked in if a user is using NSError in + // their Swift source. - auto TheWitnessTable = SWIFT_LAZY_CONSTANT(dlsym(RTLD_DEFAULT, - MANGLE_AS_STRING(MANGLE_SYM(So10CFErrorRefas5Error10FoundationWa)))); - assert(TheWitnessTable && + auto getter = getErrorBridgingInfo().GetCFErrorErrorConformance; + assert(getter && "Foundation overlay not loaded, or 'CFError : Error' conformance " "not available"); - - return reinterpret_cast(TheWitnessTable)(); + return getter(); } static const HashableWitnessTable *getNSErrorConformanceToHashable() { - auto TheWitnessTable = SWIFT_LAZY_CONSTANT(dlsym(RTLD_DEFAULT, - MANGLE_AS_STRING(MANGLE_SYM(So8NSObjectCs8Hashable10ObjectiveCWa)))); - assert(TheWitnessTable && + auto getter = getErrorBridgingInfo().GetNSObjectHashableConformance; + assert(getter && "ObjectiveC overlay not loaded, or 'NSObject : Hashable' conformance " "not available"); - - return reinterpret_cast(TheWitnessTable)(); + return getter(); } bool SwiftError::isPureNSError() const { @@ -371,16 +381,9 @@ NSInteger getErrorCode(const OpaqueValue *error, NSDictionary *_swift_stdlib_getErrorDefaultUserInfo(OpaqueValue *error, const Metadata *T, const WitnessTable *Error) { - typedef SWIFT_CC(swift) - NSDictionary *GetDefaultFn(const OpaqueValue *error, - const Metadata *T, - const WitnessTable *Error); - // public func Foundation._getErrorDefaultUserInfo(_ error: T) // -> AnyObject? - auto foundationGetDefaultUserInfo = SWIFT_LAZY_CONSTANT( - reinterpret_cast (dlsym(RTLD_DEFAULT, - MANGLE_AS_STRING(MANGLE_SYM(10Foundation24_getErrorDefaultUserInfoyyXlSgxs0C0RzlF))))); + auto foundationGetDefaultUserInfo = getErrorBridgingInfo().GetErrorDefaultUserInfo; if (!foundationGetDefaultUserInfo) { SWIFT_CC_PLUSONE_GUARD(T->vw_destroy(error)); return nullptr; @@ -392,7 +395,8 @@ typedef SWIFT_CC(swift) return foundationGetDefaultUserInfo(error, T, Error); } -/// Take an Error box and turn it into a valid NSError instance. +/// Take an Error box and turn it into a valid NSError instance. Error is passed +/// at +1. id swift::_swift_stdlib_bridgeErrorToNSError(SwiftError *errorObject) { auto ns = reinterpret_cast(errorObject); @@ -410,7 +414,6 @@ typedef SWIFT_CC(swift) // that the domain can be used alone as a flag for the initialization of the // object. if (errorObject->domain.load(std::memory_order_acquire)) { - SWIFT_CC_PLUSZERO_GUARD([ns retain]); return ns; } @@ -453,10 +456,11 @@ typedef SWIFT_CC(swift) std::memory_order_acq_rel)) objc_release(domain); - SWIFT_CC_PLUSZERO_GUARD([ns retain]); return ns; } +extern "C" const ProtocolDescriptor PROTOCOL_DESCR_SYM(s5Error); + bool swift::tryDynamicCastNSErrorToValue(OpaqueValue *dest, OpaqueValue *src, @@ -464,25 +468,9 @@ typedef SWIFT_CC(swift) const Metadata *destType, DynamicCastFlags flags) { Class NSErrorClass = getNSErrorClass(); - auto CFErrorTypeID = SWIFT_LAZY_CONSTANT(CFErrorGetTypeID()); - // public func Foundation._bridgeNSErrorToError< - // T : _ObjectiveCBridgeableError - // >(error: NSError, out: UnsafeMutablePointer) -> Bool { - typedef SWIFT_CC(swift) - bool BridgeFn(NSError *, OpaqueValue*, const Metadata *, - const WitnessTable *); - auto bridgeNSErrorToError = SWIFT_LAZY_CONSTANT( - reinterpret_cast(dlsym(RTLD_DEFAULT, - MANGLE_AS_STRING(MANGLE_SYM(10Foundation21_bridgeNSErrorToError_3outSbSo0C0C_SpyxGtAA021_ObjectiveCBridgeableE0RzlF))))); - // protocol _ObjectiveCBridgeableError - auto TheObjectiveCBridgeableError = SWIFT_LAZY_CONSTANT( - reinterpret_cast(dlsym(RTLD_DEFAULT, - MANGLE_AS_STRING(MANGLE_SYM(10Foundation26_ObjectiveCBridgeableErrorMp))))); - // If the Foundation overlay isn't loaded, then NSErrors can't be bridged. - if (!bridgeNSErrorToError || !TheObjectiveCBridgeableError) - return false; + NSError *srcInstance; // Is the input type an NSError? switch (srcType->getKind()) { @@ -491,6 +479,19 @@ bool BridgeFn(NSError *, OpaqueValue*, const Metadata *, // Native class or ObjC class should be an NSError subclass. if (![srcType->getObjCClassObject() isSubclassOfClass: NSErrorClass]) return false; + + srcInstance = *reinterpret_cast(src); + + // A _SwiftNativeNSError box can always be unwrapped to cast the value back + // out as an Error existential. + if (!reinterpret_cast(srcInstance)->isPureNSError()) { + auto theErrorProtocol = &PROTOCOL_DESCR_SYM(s5Error); + auto theErrorTy = + swift_getExistentialTypeMetadata(ProtocolClassConstraint::Any, + nullptr, 1, &theErrorProtocol); + return swift_dynamicCast(dest, src, theErrorTy, destType, flags); + } + break; case MetadataKind::ForeignClass: { // Foreign class should be CFError. @@ -500,21 +501,22 @@ bool BridgeFn(NSError *, OpaqueValue*, const Metadata *, break; } // Not a class. - case MetadataKind::Enum: - case MetadataKind::Optional: - case MetadataKind::Existential: - case MetadataKind::ExistentialMetatype: - case MetadataKind::Function: - case MetadataKind::HeapLocalVariable: - case MetadataKind::HeapGenericLocalVariable: - case MetadataKind::ErrorObject: - case MetadataKind::Metatype: - case MetadataKind::Opaque: - case MetadataKind::Struct: - case MetadataKind::Tuple: + default: return false; } + // public func Foundation._bridgeNSErrorToError< + // T : _ObjectiveCBridgeableError + // >(error: NSError, out: UnsafeMutablePointer) -> Bool { + auto bridgeNSErrorToError = getErrorBridgingInfo().BridgeErrorToNSError; + // protocol _ObjectiveCBridgeableError + auto TheObjectiveCBridgeableError = getErrorBridgingInfo().ObjectiveCBridgeableError; + + // If the Foundation overlay isn't loaded, then arbitrary NSErrors can't be + // bridged. + if (!bridgeNSErrorToError || !TheObjectiveCBridgeableError) + return false; + // Is the target type a bridgeable error? auto witness = swift_conformsToProtocol(destType, TheObjectiveCBridgeableError); @@ -523,7 +525,6 @@ bool BridgeFn(NSError *, OpaqueValue*, const Metadata *, return false; // If so, attempt the bridge. - NSError *srcInstance = *reinterpret_cast(src); SWIFT_CC_PLUSONE_GUARD(objc_retain(srcInstance)); if (bridgeNSErrorToError(srcInstance, dest, destType, witness)) { if (flags & DynamicCastFlags::TakeOnSuccess) diff --git a/stdlib/public/runtime/Exclusivity.cpp b/stdlib/public/runtime/Exclusivity.cpp index 0c7dc3e833d6d..c496f9a3a4090 100644 --- a/stdlib/public/runtime/Exclusivity.cpp +++ b/stdlib/public/runtime/Exclusivity.cpp @@ -260,6 +260,16 @@ class AccessSet { swift_runtime_unreachable("access not found in set"); } + +#ifndef NDEBUG + /// Only available with asserts. Intended to be used with + /// swift_dumpTrackedAccess(). + void forEach(std::function action) { + for (auto *iter = Head; iter != nullptr; iter = iter->getNext()) { + action(iter); + } + } +#endif }; } // end anonymous namespace @@ -348,3 +358,17 @@ void swift::swift_endAccess(ValueBuffer *buffer) { getAccessSet().remove(access); } + +#ifndef NDEBUG + +// Dump the accesses that are currently being tracked by the runtime. +// +// This is only intended to be used in the debugger. +void swift::swift_dumpTrackedAccesses() { + getAccessSet().forEach([](Access *a) { + fprintf(stderr, "Access. Pointer: %p. PC: %p. AccessAction: %s\n", + a->Pointer, a->PC, getAccessName(a->getAccessAction())); + }); +} + +#endif diff --git a/stdlib/public/runtime/ExistentialMetadataImpl.h b/stdlib/public/runtime/ExistentialMetadataImpl.h index 3d849e3fd5d94..3183b8aabb6d0 100644 --- a/stdlib/public/runtime/ExistentialMetadataImpl.h +++ b/stdlib/public/runtime/ExistentialMetadataImpl.h @@ -107,20 +107,9 @@ struct LLVM_LIBRARY_VISIBILITY OpaqueExistentialBoxBase static Container *initializeWithTake(Container *dest, Container *src, A... args) { src->copyTypeInto(dest, args...); - auto *type = src->getType(); - auto *vwt = type->getValueWitnesses(); - - if (vwt->isValueInline()) { - auto *destValue = - reinterpret_cast(dest->getBuffer(args...)); - auto *srcValue = - reinterpret_cast(src->getBuffer(args...)); - - type->vw_initializeWithTake(destValue, srcValue); - } else { - // initWithTake of the reference to the cow box. - copyReference(dest, src, Dest::Init, Source::Take, args...); - } + auto from = src->getBuffer(args...); + auto to = dest->getBuffer(args...); + memcpy(to, from, sizeof(ValueBuffer)); return dest; } @@ -338,7 +327,7 @@ struct LLVM_LIBRARY_VISIBILITY OpaqueExistentialBox static constexpr size_t alignment = alignof(Container); static constexpr size_t stride = sizeof(Container); static constexpr size_t isPOD = false; - static constexpr bool isBitwiseTakable = false; + static constexpr bool isBitwiseTakable = true; static constexpr unsigned numExtraInhabitants = 0; }; diff --git a/stdlib/public/runtime/HeapObject.cpp b/stdlib/public/runtime/HeapObject.cpp index c4a78d2f858b1..6f70e4c916b26 100644 --- a/stdlib/public/runtime/HeapObject.cpp +++ b/stdlib/public/runtime/HeapObject.cpp @@ -372,6 +372,10 @@ size_t swift::swift_unownedRetainCount(HeapObject *object) { return object->refCounts.getUnownedCount(); } +size_t swift::swift_weakRetainCount(HeapObject *object) { + return object->refCounts.getWeakCount(); +} + HeapObject *swift::swift_unownedRetain(HeapObject *object) { SWIFT_RT_TRACK_INVOCATION(object, swift_unownedRetain); if (!isValidPointerForNativeRetain(object)) diff --git a/stdlib/public/runtime/ImageInspectionCOFF.cpp b/stdlib/public/runtime/ImageInspectionCOFF.cpp index 915f827bbe813..29fec32dd2126 100644 --- a/stdlib/public/runtime/ImageInspectionCOFF.cpp +++ b/stdlib/public/runtime/ImageInspectionCOFF.cpp @@ -41,7 +41,7 @@ void swift::initializeProtocolLookup() { const swift::MetadataSections *sections = registered; while (true) { const swift::MetadataSections::Range &protocols = - sections->swift4_protocols; + sections->swift5_protocols; if (protocols.length) addImageProtocolsBlockCallback(reinterpret_cast(protocols.start), protocols.length); @@ -56,7 +56,7 @@ void swift::initializeProtocolConformanceLookup() { const swift::MetadataSections *sections = registered; while (true) { const swift::MetadataSections::Range &conformances = - sections->swift4_protocol_conformances; + sections->swift5_protocol_conformances; if (conformances.length) addImageProtocolConformanceBlockCallback(reinterpret_cast(conformances.start), conformances.length); @@ -71,7 +71,7 @@ void swift::initializeTypeMetadataRecordLookup() { const swift::MetadataSections *sections = registered; while (true) { const swift::MetadataSections::Range &type_metadata = - sections->swift4_type_metadata; + sections->swift5_type_metadata; if (type_metadata.length) addImageTypeMetadataRecordBlockCallback(reinterpret_cast(type_metadata.start), type_metadata.length); @@ -85,7 +85,7 @@ void swift::initializeTypeMetadataRecordLookup() { void swift::initializeTypeFieldLookup() { const swift::MetadataSections *sections = registered; while (true) { - const swift::MetadataSections::Range &fields = sections->swift4_fieldmd; + const swift::MetadataSections::Range &fields = sections->swift5_fieldmd; if (fields.length) addImageTypeFieldDescriptorBlockCallback( reinterpret_cast(fields.start), fields.length); @@ -103,20 +103,20 @@ void swift_addNewDSOImage(const void *addr) { record(sections); - const auto &protocols_section = sections->swift4_protocols; + const auto &protocols_section = sections->swift5_protocols; const void *protocols = reinterpret_cast(protocols_section.start); if (protocols_section.length) addImageProtocolsBlockCallback(protocols, protocols_section.length); - const auto &protocol_conformances = sections->swift4_protocol_conformances; + const auto &protocol_conformances = sections->swift5_protocol_conformances; const void *conformances = reinterpret_cast(protocol_conformances.start); if (protocol_conformances.length) addImageProtocolConformanceBlockCallback(conformances, protocol_conformances.length); - const auto &type_metadata = sections->swift4_type_metadata; + const auto &type_metadata = sections->swift5_type_metadata; const void *metadata = reinterpret_cast(type_metadata.start); if (type_metadata.length) addImageTypeMetadataRecordBlockCallback(metadata, type_metadata.length); diff --git a/stdlib/public/runtime/ImageInspectionCOFF.h b/stdlib/public/runtime/ImageInspectionCOFF.h index 2ce5fa6ade7cb..75e0266cf73bf 100644 --- a/stdlib/public/runtime/ImageInspectionCOFF.h +++ b/stdlib/public/runtime/ImageInspectionCOFF.h @@ -45,13 +45,13 @@ struct MetadataSections { size_t length; }; - Range swift4_protocols; - Range swift4_protocol_conformances; - Range swift4_type_metadata; - Range swift4_typeref; - Range swift4_reflstr; - Range swift4_fieldmd; - Range swift4_assocty; + Range swift5_protocols; + Range swift5_protocol_conformances; + Range swift5_type_metadata; + Range swift5_typeref; + Range swift5_reflstr; + Range swift5_fieldmd; + Range swift5_assocty; }; } // namespace swift diff --git a/stdlib/public/runtime/ImageInspectionELF.cpp b/stdlib/public/runtime/ImageInspectionELF.cpp index dc564891b12c1..3dfe95fc67aa9 100644 --- a/stdlib/public/runtime/ImageInspectionELF.cpp +++ b/stdlib/public/runtime/ImageInspectionELF.cpp @@ -46,7 +46,7 @@ void swift::initializeProtocolLookup() { const swift::MetadataSections *sections = registered; while (true) { const swift::MetadataSections::Range &protocols = - sections->swift4_protocols; + sections->swift5_protocols; if (protocols.length) addImageProtocolsBlockCallback(reinterpret_cast(protocols.start), protocols.length); @@ -60,7 +60,7 @@ void swift::initializeProtocolConformanceLookup() { const swift::MetadataSections *sections = registered; while (true) { const swift::MetadataSections::Range &conformances = - sections->swift4_protocol_conformances; + sections->swift5_protocol_conformances; if (conformances.length) addImageProtocolConformanceBlockCallback(reinterpret_cast(conformances.start), conformances.length); @@ -75,7 +75,7 @@ void swift::initializeTypeMetadataRecordLookup() { const swift::MetadataSections *sections = registered; while (true) { const swift::MetadataSections::Range &type_metadata = - sections->swift4_type_metadata; + sections->swift5_type_metadata; if (type_metadata.length) addImageTypeMetadataRecordBlockCallback(reinterpret_cast(type_metadata.start), type_metadata.length); @@ -89,7 +89,7 @@ void swift::initializeTypeMetadataRecordLookup() { void swift::initializeTypeFieldLookup() { const swift::MetadataSections *sections = registered; while (true) { - const swift::MetadataSections::Range &fields = sections->swift4_fieldmd; + const swift::MetadataSections::Range &fields = sections->swift5_fieldmd; if (fields.length) addImageTypeFieldDescriptorBlockCallback( reinterpret_cast(fields.start), fields.length); @@ -111,20 +111,20 @@ void swift_addNewDSOImage(const void *addr) { record(sections); - const auto &protocols_section = sections->swift4_protocol_conformances; + const auto &protocols_section = sections->swift5_protocols; const void *protocols = reinterpret_cast(protocols_section.start); if (protocols_section.length) addImageProtocolsBlockCallback(protocols, protocols_section.length); - const auto &protocol_conformances = sections->swift4_protocol_conformances; + const auto &protocol_conformances = sections->swift5_protocol_conformances; const void *conformances = reinterpret_cast(protocol_conformances.start); if (protocol_conformances.length) addImageProtocolConformanceBlockCallback(conformances, protocol_conformances.length); - const auto &type_metadata = sections->swift4_type_metadata; + const auto &type_metadata = sections->swift5_type_metadata; const void *metadata = reinterpret_cast(type_metadata.start); if (type_metadata.length) addImageTypeMetadataRecordBlockCallback(metadata, type_metadata.length); diff --git a/stdlib/public/runtime/ImageInspectionELF.h b/stdlib/public/runtime/ImageInspectionELF.h index b88be2297c4df..950536000412f 100644 --- a/stdlib/public/runtime/ImageInspectionELF.h +++ b/stdlib/public/runtime/ImageInspectionELF.h @@ -45,13 +45,13 @@ struct MetadataSections { size_t length; }; - Range swift4_protocols; - Range swift4_protocol_conformances; - Range swift4_type_metadata; - Range swift4_typeref; - Range swift4_reflstr; - Range swift4_fieldmd; - Range swift4_assocty; + Range swift5_protocols; + Range swift5_protocol_conformances; + Range swift5_type_metadata; + Range swift5_typeref; + Range swift5_reflstr; + Range swift5_fieldmd; + Range swift5_assocty; }; } // namespace swift diff --git a/stdlib/public/runtime/ImageInspectionMachO.cpp b/stdlib/public/runtime/ImageInspectionMachO.cpp index 2972a8d5fda49..f8c86d2629755 100644 --- a/stdlib/public/runtime/ImageInspectionMachO.cpp +++ b/stdlib/public/runtime/ImageInspectionMachO.cpp @@ -31,16 +31,16 @@ using namespace swift; namespace { /// The Mach-O section name for the section containing protocol descriptor /// references. This lives within SEG_TEXT. -constexpr const char ProtocolsSection[] = "__swift4_protos"; +constexpr const char ProtocolsSection[] = "__swift5_protos"; /// The Mach-O section name for the section containing protocol conformances. /// This lives within SEG_TEXT. -constexpr const char ProtocolConformancesSection[] = "__swift4_proto"; +constexpr const char ProtocolConformancesSection[] = "__swift5_proto"; /// The Mach-O section name for the section containing type references. /// This lives within SEG_TEXT. -constexpr const char TypeMetadataRecordSection[] = "__swift4_types"; +constexpr const char TypeMetadataRecordSection[] = "__swift5_types"; /// The Mach-O section name for the section containing type field references. /// This lives within SEG_TEXT. -constexpr const char TypeFieldRecordSection[] = "__swift4_fieldmd"; +constexpr const char TypeFieldRecordSection[] = "__swift5_fieldmd"; #if __POINTER_WIDTH__ == 64 using mach_header_platform = mach_header_64; @@ -57,7 +57,7 @@ void addImageCallback(const mach_header *mh, intptr_t vmaddr_slide) { assert(mh->magic == MH_MAGIC_64 && "loaded non-64-bit image?!"); #endif - // Look for a __swift4_proto section. + // Look for a __swift5_proto section. unsigned long size; const uint8_t *section = getsectiondata(reinterpret_cast(mh), diff --git a/stdlib/public/runtime/Leaks.mm b/stdlib/public/runtime/Leaks.mm index 1c15aec0ac775..440115517cead 100644 --- a/stdlib/public/runtime/Leaks.mm +++ b/stdlib/public/runtime/Leaks.mm @@ -131,6 +131,9 @@ static void dumpSwiftHeapObjects() { kindDescriptor = #name; \ break; #include "swift/ABI/MetadataKind.def" + default: + kindDescriptor = "unknown"; + break; } if (auto *NTD = diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index 756dbf2852c1b..83c06223270d2 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.cpp @@ -876,6 +876,9 @@ class TupleCacheEntry class TupleCache : public MetadataCache { public: +// FIXME: https://bugs.swift.org/browse/SR-1155 +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Winvalid-offsetof" static TupleCacheEntry * resolveExistingEntry(const TupleTypeMetadata *metadata) { // The correctness of this arithmetic is verified by an assertion in @@ -885,6 +888,7 @@ class TupleCache : public MetadataCache { auto entry = reinterpret_cast(bytes); return const_cast(entry); } +#pragma clang diagnostic pop }; } // end anonymous namespace @@ -1053,23 +1057,6 @@ static OpaqueValue *tuple_initializeBufferWithCopyOfBuffer(ValueBuffer *dest, return tuple_projectBuffer(dest, metatype); } -/// Generic tuple value witness for 'initializeBufferWithTakeOfBuffer'. -template -static OpaqueValue *tuple_initializeBufferWithTakeOfBuffer(ValueBuffer *dest, - ValueBuffer *src, - const Metadata *metatype) { - assert(IsPOD == tuple_getValueWitnesses(metatype)->isPOD()); - assert(IsInline == tuple_getValueWitnesses(metatype)->isValueInline()); - if (IsInline) { - return tuple_initializeWithTake( - tuple_projectBuffer(dest, metatype), - tuple_projectBuffer(src, metatype), metatype); - } - auto *srcReference = *reinterpret_cast(src); - *reinterpret_cast(dest) = srcReference; - return tuple_projectBuffer(dest, metatype); -} - template static unsigned tuple_getEnumTagSinglePayload(const OpaqueValue *enumAddr, unsigned numEmptyCases, @@ -1211,13 +1198,15 @@ static void performBasicLayout(TypeLayout &layout, if (!eltLayout->flags.isPOD()) isPOD = false; if (!eltLayout->flags.isBitwiseTakable()) isBitwiseTakable = false; } - bool isInline = ValueWitnessTable::isValueInline(size, alignMask + 1); + bool isInline = + ValueWitnessTable::isValueInline(isBitwiseTakable, size, alignMask + 1); layout.size = size; - layout.flags = ValueWitnessFlags().withAlignmentMask(alignMask) - .withPOD(isPOD) - .withBitwiseTakable(isBitwiseTakable) - .withInlineStorage(isInline); + layout.flags = ValueWitnessFlags() + .withAlignmentMask(alignMask) + .withPOD(isPOD) + .withBitwiseTakable(isBitwiseTakable) + .withInlineStorage(isInline); layout.stride = std::max(size_t(1), roundUpToAlignMask(size, alignMask)); } @@ -1554,20 +1543,6 @@ static OpaqueValue *pod_indirect_initializeBufferWithCopyOfBuffer( return reinterpret_cast(bytePtr + byteOffset); } -static OpaqueValue *pod_indirect_initializeBufferWithTakeOfBuffer( - ValueBuffer *dest, ValueBuffer *src, const Metadata *self) { - auto wtable = self->getValueWitnesses(); - auto *srcReference = *reinterpret_cast(src); - *reinterpret_cast(dest) = srcReference; - - // Project the address of the value in the buffer. - unsigned alignMask = wtable->getAlignmentMask(); - // Compute the byte offset of the object in the box. - unsigned byteOffset = (sizeof(HeapObject) + alignMask) & ~alignMask; - auto *bytePtr = reinterpret_cast(srcReference); - return reinterpret_cast(bytePtr + byteOffset); -} - static void pod_noop(void *object, const Metadata *self) { } #define pod_direct_destroy \ @@ -1584,9 +1559,6 @@ static OpaqueValue *pod_direct_initializeWithCopy(OpaqueValue *dest, #define pod_direct_initializeBufferWithCopyOfBuffer \ pointer_function_cast \ (pod_direct_initializeWithCopy) -#define pod_direct_initializeBufferWithTakeOfBuffer \ - pointer_function_cast \ - (pod_direct_initializeWithCopy) #define pod_direct_assignWithCopy pod_direct_initializeWithCopy #define pod_indirect_assignWithCopy pod_direct_initializeWithCopy #define pod_direct_initializeWithTake pod_direct_initializeWithCopy @@ -1699,23 +1671,12 @@ void swift::installCommonValueWitnesses(const TypeLayout &layout, // Use POD value witnesses for operations that do an initializeWithTake. if (flags.isInlineStorage()) { vwtable->initializeWithTake = pod_direct_initializeWithTake; - vwtable->initializeBufferWithTakeOfBuffer - = pod_direct_initializeBufferWithTakeOfBuffer; } else { vwtable->initializeWithTake = pod_indirect_initializeWithTake; - vwtable->initializeBufferWithTakeOfBuffer - = pod_indirect_initializeBufferWithTakeOfBuffer; } return; } - if (!flags.isInlineStorage()) { - // For values stored out-of-line, initializeBufferWithTakeOfBuffer is - // always a memcpy. - vwtable->initializeBufferWithTakeOfBuffer - = pod_indirect_initializeBufferWithTakeOfBuffer; - return; - } } /***************************************************************************/ @@ -1758,12 +1719,12 @@ static ValueWitnessTable *getMutableVWTableForInit(StructMetadata *self, void swift::swift_initStructMetadata(StructMetadata *structType, StructLayoutFlags layoutFlags, size_t numFields, - const TypeLayout * const *fieldTypes, - size_t *fieldOffsets) { + const TypeLayout *const *fieldTypes, + uint32_t *fieldOffsets) { auto layout = getInitialLayoutForValueType(); performBasicLayout(layout, fieldTypes, numFields, [&](const TypeLayout *fieldType) { return fieldType; }, - [&](size_t i, const TypeLayout *fieldType, size_t offset) { + [&](size_t i, const TypeLayout *fieldType, uint32_t offset) { assignUnlessEqual(fieldOffsets[i], offset); }); @@ -2565,7 +2526,7 @@ OpaqueExistentialValueWitnessTableCacheEntry(unsigned numWitnessTables) { Data.flags = ValueWitnessFlags() .withAlignment(Box::Container::getAlignment(numWitnessTables)) .withPOD(false) - .withBitwiseTakable(false) + .withBitwiseTakable(true) .withInlineStorage(false) .withExtraInhabitants(false); Data.stride = Box::Container::getStride(numWitnessTables); @@ -3096,6 +3057,58 @@ swift::swift_getForeignTypeMetadata(ForeignTypeMetadata *nonUnique) { return uniqueMetadata; } +/// Unique-ing of foreign types' witness tables. +namespace { + class ForeignWitnessTableCacheEntry { + public: + struct Key { + const TypeContextDescriptor *type; + const ProtocolDescriptor *protocol; + }; + + const Key key; + const WitnessTable *data; + + ForeignWitnessTableCacheEntry(const ForeignWitnessTableCacheEntry::Key k, + const WitnessTable *d) + : key(k), data(d) {} + + intptr_t getKeyIntValueForDump() { + return reinterpret_cast(key.type); + } + + int compareWithKey(const Key other) const { + if (auto r = comparePointers(other.protocol, key.protocol)) + return r; + return strcmp(other.type->Name.get(), key.type->Name.get()); + } + + static size_t getExtraAllocationSize(const Key, + const WitnessTable *) { + return 0; + } + + size_t getExtraAllocationSize() const { + return 0; + } + }; +} + +static SimpleGlobalCache ForeignWitnessTables; + +const WitnessTable *swift::swift_getForeignWitnessTable( + const WitnessTable *witnessTableCandidate, + const TypeContextDescriptor *contextDescriptor, + const ProtocolDescriptor *protocol) { + auto result = + ForeignWitnessTables + .getOrInsert( + ForeignWitnessTableCacheEntry::Key{contextDescriptor, protocol}, + witnessTableCandidate) + .first->data; + return result; +} + /***************************************************************************/ /*** Other metadata routines ***********************************************/ /***************************************************************************/ @@ -3113,23 +3126,9 @@ Metadata::getClassObject() const { return wrapper->Class; } // Other kinds of types don't have class objects. - case MetadataKind::Struct: - case MetadataKind::Enum: - case MetadataKind::Optional: - case MetadataKind::ForeignClass: - case MetadataKind::Opaque: - case MetadataKind::Tuple: - case MetadataKind::Function: - case MetadataKind::Existential: - case MetadataKind::ExistentialMetatype: - case MetadataKind::Metatype: - case MetadataKind::HeapLocalVariable: - case MetadataKind::HeapGenericLocalVariable: - case MetadataKind::ErrorObject: + default: return nullptr; } - - swift_runtime_unreachable("Unhandled MetadataKind in switch."); } template <> OpaqueValue *Metadata::allocateBoxForExistentialIn(ValueBuffer *buffer) const { @@ -3203,9 +3202,10 @@ StringRef swift::getStringForMetadataKind(MetadataKind kind) { case MetadataKind::NAME: \ return #NAME; #include "swift/ABI/MetadataKind.def" + + default: + return ""; } - - swift_runtime_unreachable("Unhandled metadata kind?!"); } #ifndef NDEBUG @@ -3914,3 +3914,29 @@ bool Metadata::satisfiesClassConstraint() const { // or it's a class. return isAnyClass(); } + +#if !NDEBUG +void swift::verifyMangledNameRoundtrip(const Metadata *metadata) { + // Enable verification when a special environment variable is set. + // Some metatypes crash when going through the mangler or demangler. A + // lot of tests currently trigger those crashes, resulting in failing + // tests which are still usefully testing something else. This + // variable lets us easily turn on verification to find and fix these + // bugs. Remove this and leave it permanently on once everything works + // with it enabled. + bool verificationEnabled = + SWIFT_LAZY_CONSTANT((bool)getenv("SWIFT_ENABLE_MANGLED_NAME_VERIFICATION")); + + if (!verificationEnabled) return; + + Demangle::Demangler Dem; + auto node = _swift_buildDemanglingForMetadata(metadata, Dem); + auto mangledName = Demangle::mangleNode(node); + auto result = _getTypeByMangledName(mangledName, + [](unsigned, unsigned){ return nullptr; }); + if (metadata != result) + swift::warning(RuntimeErrorFlagNone, + "Metadata mangled name failed to roundtrip: %p -> %s -> %p", + metadata, mangledName.c_str(), (const Metadata *)result); +} +#endif diff --git a/stdlib/public/runtime/MetadataCache.h b/stdlib/public/runtime/MetadataCache.h index 8222d058901e5..1f764ab7fbfa6 100644 --- a/stdlib/public/runtime/MetadataCache.h +++ b/stdlib/public/runtime/MetadataCache.h @@ -925,6 +925,10 @@ class MetadataCacheEntryBase awaitSatisfyingState(concurrency, request, curTrackingInfo); } +#if !NDEBUG + verifyMangledNameRoundtrip(value); +#endif + return { value, curTrackingInfo.getAccomplishedRequestState() }; } diff --git a/stdlib/public/runtime/MetadataImpl.h b/stdlib/public/runtime/MetadataImpl.h index bcd562fc10fb8..a52a8f49f83fa 100644 --- a/stdlib/public/runtime/MetadataImpl.h +++ b/stdlib/public/runtime/MetadataImpl.h @@ -620,31 +620,28 @@ enum class FixedPacking { Allocate, OffsetZero }; -constexpr FixedPacking getFixedPacking(size_t size, size_t alignment) { - return (canBeInline(size, alignment) ? FixedPacking::OffsetZero - : FixedPacking::Allocate); +constexpr FixedPacking getFixedPacking(bool isBitwiseTakable, size_t size, + size_t alignment) { + return (canBeInline(isBitwiseTakable, size, alignment) + ? FixedPacking::OffsetZero + : FixedPacking::Allocate); } /// A CRTP base class which provides default implementations of a /// number of value witnesses. -template +template struct BufferValueWitnesses; /// An implementation of ValueBase suitable for classes that can be /// allocated inline. -template -struct BufferValueWitnesses +template +struct BufferValueWitnesses : BufferValueWitnessesBase { static constexpr bool isInline = true; - static OpaqueValue *initializeBufferWithTakeOfBuffer(ValueBuffer *dest, - ValueBuffer *src, - const Metadata *self) { - return Impl::initializeWithTake(reinterpret_cast(dest), - reinterpret_cast(src), - self); - } static OpaqueValue *initializeBufferWithCopyOfBuffer(ValueBuffer *dest, ValueBuffer *src, const Metadata *self) { @@ -655,26 +652,12 @@ struct BufferValueWitnesses /// An implementation of BufferValueWitnesses suitable for types that /// cannot be allocated inline. -template -struct BufferValueWitnesses +template +struct BufferValueWitnesses : BufferValueWitnessesBase { static constexpr bool isInline = false; - static OpaqueValue *initializeBufferWithTakeOfBuffer(ValueBuffer *dest, - ValueBuffer *src, - const Metadata *self) { - auto wtable = self->getValueWitnesses(); - auto *srcReference = *reinterpret_cast(src); - *reinterpret_cast(dest) = srcReference; - - // Project the address of the value in the buffer. - unsigned alignMask = wtable->getAlignmentMask(); - // Compute the byte offset of the object in the box. - unsigned byteOffset = (sizeof(HeapObject) + alignMask) & ~alignMask; - auto *bytePtr = reinterpret_cast(srcReference); - return reinterpret_cast(bytePtr + byteOffset); - } - static OpaqueValue *initializeBufferWithCopyOfBuffer(ValueBuffer *dest, ValueBuffer *src, const Metadata *self) { @@ -695,26 +678,6 @@ struct BufferValueWitnesses /// fixed in size. template struct NonFixedBufferValueWitnesses : BufferValueWitnessesBase { - static OpaqueValue *initializeBufferWithTakeOfBuffer(ValueBuffer *dest, - ValueBuffer *src, - const Metadata *self) { - auto vwtable = self->getValueWitnesses(); - (void)vwtable; - if (!IsKnownAllocated && vwtable->isValueInline()) { - return Impl::initializeWithTake(reinterpret_cast(dest), - reinterpret_cast(src), - self); - } else { - auto reference = src->PrivateData[0]; - dest->PrivateData[0] = reference; - // Project the address of the value in the buffer. - unsigned alignMask = vwtable->getAlignmentMask(); - // Compute the byte offset of the object in the box. - unsigned byteOffset = (sizeof(HeapObject) + alignMask) & ~alignMask; - auto *bytePtr = reinterpret_cast(reference); - return reinterpret_cast(bytePtr + byteOffset); - } - } static OpaqueValue *initializeBufferWithCopyOfBuffer(ValueBuffer *dest, ValueBuffer *src, @@ -741,15 +704,16 @@ struct NonFixedBufferValueWitnesses : BufferValueWitnessesBase { /// Provides implementations for /// getEnumTagSinglePayload/storeEnumTagSinglePayload. -template +template struct FixedSizeBufferValueWitnesses; /// A fixed size buffer value witness that can rely on the presents of the extra /// inhabitant functions. -template -struct FixedSizeBufferValueWitnesses +struct FixedSizeBufferValueWitnesses - : BufferValueWitnesses { + : BufferValueWitnesses { static unsigned getEnumTagSinglePayload(const OpaqueValue *enumAddr, unsigned numEmptyCases, @@ -771,10 +735,10 @@ struct FixedSizeBufferValueWitnesses -struct FixedSizeBufferValueWitnesses +struct FixedSizeBufferValueWitnesses - : BufferValueWitnesses { + : BufferValueWitnesses { static unsigned getEnumTagSinglePayload(const OpaqueValue *enumAddr, unsigned numEmptyCases, @@ -801,11 +765,12 @@ static constexpr bool hasExtraInhabitants(unsigned numExtraInhabitants) { /// The box type has to provide a numExtraInhabitants member, but as /// long as it's zero, the rest is fine. template -struct ValueWitnesses : FixedSizeBufferValueWitnesses< - ValueWitnesses, Box::size, Box::alignment, - hasExtraInhabitants(Box::numExtraInhabitants)> { +struct ValueWitnesses + : FixedSizeBufferValueWitnesses< + ValueWitnesses, Box::isBitwiseTakable, Box::size, Box::alignment, + hasExtraInhabitants(Box::numExtraInhabitants)> { using Base = FixedSizeBufferValueWitnesses< - ValueWitnesses, Box::size, Box::alignment, + ValueWitnesses, Box::isBitwiseTakable, Box::size, Box::alignment, hasExtraInhabitants(Box::numExtraInhabitants)>; static constexpr size_t size = Box::size; @@ -817,7 +782,7 @@ struct ValueWitnesses : FixedSizeBufferValueWitnesses< static constexpr bool hasExtraInhabitants = (numExtraInhabitants != 0); static constexpr ValueWitnessFlags flags = ValueWitnessFlags().withAlignmentMask(alignment - 1) - .withInlineStorage(Base::isInline) + .withInlineStorage(Base::isInline && isBitwiseTakable) .withPOD(isPOD) .withBitwiseTakable(isBitwiseTakable) .withExtraInhabitants(hasExtraInhabitants); diff --git a/stdlib/public/runtime/MetadataLookup.cpp b/stdlib/public/runtime/MetadataLookup.cpp index 7693994d61579..07ce6c8c76b01 100644 --- a/stdlib/public/runtime/MetadataLookup.cpp +++ b/stdlib/public/runtime/MetadataLookup.cpp @@ -23,7 +23,6 @@ #include "swift/Runtime/Concurrent.h" #include "swift/Runtime/HeapObject.h" #include "swift/Runtime/Metadata.h" -#include "swift/Runtime/Mutex.h" #include "swift/Strings.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/Optional.h" @@ -106,11 +105,9 @@ namespace { struct TypeMetadataPrivateState { ConcurrentMap NominalCache; - std::vector SectionsToScan; - Mutex SectionsToScanLock; + ConcurrentReadableArray SectionsToScan; TypeMetadataPrivateState() { - SectionsToScan.reserve(16); initializeTypeMetadataRecordLookup(); } @@ -122,7 +119,6 @@ static void _registerTypeMetadataRecords(TypeMetadataPrivateState &T, const TypeMetadataRecord *begin, const TypeMetadataRecord *end) { - ScopedLock guard(T.SectionsToScanLock); T.SectionsToScan.push_back(TypeMetadataSection{begin, end}); } @@ -153,13 +149,17 @@ swift::swift_registerTypeMetadataRecords(const TypeMetadataRecord *begin, _registerTypeMetadataRecords(T, begin, end); } +static const TypeContextDescriptor * +_findNominalTypeDescriptor(Demangle::NodePointer node, + Demangle::Demangler &Dem); + bool swift::_contextDescriptorMatchesMangling(const ContextDescriptor *context, Demangle::NodePointer node) { - if (node->getKind() == Demangle::Node::Kind::Type) - node = node->getChild(0); - while (context) { + if (node->getKind() == Demangle::Node::Kind::Type) + node = node->getChild(0); + // We can directly match symbolic references to the current context. if (node && node->getKind() == Demangle::Node::Kind::SymbolicReference) { if (equalContexts(context, reinterpret_cast( @@ -182,8 +182,56 @@ swift::_contextDescriptorMatchesMangling(const ContextDescriptor *context, } case ContextDescriptorKind::Extension: { - // TODO: Check whether the extension context constraints match. - return false; + auto extension = cast(context); + + // Check whether the extension context matches the mangled context. + if (node->getKind() != Demangle::Node::Kind::Extension) + return false; + if (node->getNumChildren() < 2) + return false; + + // Check that the context being extended matches as well. + auto extendedContextNode = node->getChild(1); + auto extendedContextMangledName = extension->getMangledExtendedContext(); + auto demangler = getDemanglerForRuntimeTypeResolution(); + auto extendedContextDemangled = + demangler.demangleType(extendedContextMangledName); + if (!extendedContextDemangled) + return false; + if (extendedContextDemangled->getKind() == Node::Kind::Type) { + if (extendedContextDemangled->getNumChildren() < 1) + return false; + extendedContextDemangled = extendedContextDemangled->getChild(0); + } + extendedContextDemangled = + stripGenericArgsFromContextNode(extendedContextDemangled, demangler); + + auto extendedDescriptorFromNode = + _findNominalTypeDescriptor(extendedContextNode, demangler); + auto extendedDescriptorFromDemangled = + _findNominalTypeDescriptor(extendedContextDemangled, demangler); + + if (!extendedDescriptorFromNode || !extendedDescriptorFromDemangled || + !equalContexts(extendedDescriptorFromNode, + extendedDescriptorFromDemangled)) + return false; + + // Check whether the generic signature of the extension matches the + // mangled constraints, if any. + + if (node->getNumChildren() >= 3) { + // NB: If we ever support extensions with independent generic arguments + // like `extension Array where Element == Optional`, we'd need + // to look at the mangled context name to match up generic arguments. + // That would probably need a new extension mangling form, though. + + // TODO + } + + // The parent context of the extension should match in the mangling and + // context descriptor. + node = node->getChild(0); + break; } default: @@ -244,12 +292,9 @@ swift::_contextDescriptorMatchesMangling(const ContextDescriptor *context, // returns the nominal type descriptor for the type named by typeName static const TypeContextDescriptor * -_searchTypeMetadataRecords(const TypeMetadataPrivateState &T, +_searchTypeMetadataRecords(TypeMetadataPrivateState &T, Demangle::NodePointer node) { - unsigned sectionIdx = 0; - unsigned endSectionIdx = T.SectionsToScan.size(); - for (; sectionIdx < endSectionIdx; ++sectionIdx) { - auto §ion = T.SectionsToScan[sectionIdx]; + for (auto §ion : T.SectionsToScan.snapshot()) { for (const auto &record : section) { if (auto ntd = record.getTypeContextDescriptor()) { if (_contextDescriptorMatchesMangling(ntd, node)) { @@ -263,7 +308,8 @@ _searchTypeMetadataRecords(const TypeMetadataPrivateState &T, } static const TypeContextDescriptor * -_findNominalTypeDescriptor(Demangle::NodePointer node) { +_findNominalTypeDescriptor(Demangle::NodePointer node, + Demangle::Demangler &Dem) { const TypeContextDescriptor *foundNominal = nullptr; auto &T = TypeMetadataRecords.get(); @@ -275,7 +321,13 @@ _findNominalTypeDescriptor(Demangle::NodePointer node) { return cast( (const ContextDescriptor *)symbolicNode->getIndex()); - auto mangledName = Demangle::mangleNode(node); + auto mangledName = + Demangle::mangleNode(node, + [&](const void *context) -> NodePointer { + return _buildDemanglingForContext( + (const ContextDescriptor *) context, + {}, false, Dem); + }); // Look for an existing entry. // Find the bucket for the metadata entry. @@ -283,9 +335,7 @@ _findNominalTypeDescriptor(Demangle::NodePointer node) { return Value->getDescription(); // Check type metadata records - T.SectionsToScanLock.withLock([&] { - foundNominal = _searchTypeMetadataRecords(T, node); - }); + foundNominal = _searchTypeMetadataRecords(T, node); // Check protocol conformances table. Note that this has no support for // resolving generic types yet. @@ -336,11 +386,9 @@ namespace { struct ProtocolMetadataPrivateState { ConcurrentMap ProtocolCache; - std::vector SectionsToScan; - Mutex SectionsToScanLock; + ConcurrentReadableArray SectionsToScan; ProtocolMetadataPrivateState() { - SectionsToScan.reserve(16); initializeProtocolLookup(); } }; @@ -352,7 +400,6 @@ static void _registerProtocols(ProtocolMetadataPrivateState &C, const ProtocolRecord *begin, const ProtocolRecord *end) { - ScopedLock guard(C.SectionsToScanLock); C.SectionsToScan.push_back(ProtocolSection{begin, end}); } @@ -380,12 +427,9 @@ void swift::swift_registerProtocols(const ProtocolRecord *begin, } static const ProtocolDescriptor * -_searchProtocolRecords(const ProtocolMetadataPrivateState &C, +_searchProtocolRecords(ProtocolMetadataPrivateState &C, const llvm::StringRef protocolName){ - unsigned sectionIdx = 0; - unsigned endSectionIdx = C.SectionsToScan.size(); - for (; sectionIdx < endSectionIdx; ++sectionIdx) { - auto §ion = C.SectionsToScan[sectionIdx]; + for (auto §ion : C.SectionsToScan.snapshot()) { for (const auto &record : section) { if (auto protocol = record.Protocol.getPointer()) { // Drop the "S$" prefix from the protocol record. It's not used in @@ -413,9 +457,7 @@ _findProtocolDescriptor(llvm::StringRef mangledName) { return Value->getDescription(); // Check type metadata records - T.SectionsToScanLock.withLock([&] { - foundProtocol = _searchProtocolRecords(T, mangledName); - }); + foundProtocol = _searchProtocolRecords(T, mangledName); if (foundProtocol) { T.ProtocolCache.getOrInsert(mangledName, foundProtocol); @@ -475,7 +517,7 @@ class DynamicFieldSection { DynamicFieldSection(const FieldDescriptor **fields, size_t size) : Begin(fields), End(fields + size) {} - const FieldDescriptor **begin() { return Begin; } + const FieldDescriptor **begin() const { return Begin; } const FieldDescriptor **end() const { return End; } }; @@ -483,13 +525,10 @@ class DynamicFieldSection { struct FieldCacheState { ConcurrentMap FieldCache; - Mutex SectionsLock; - std::vector StaticSections; - std::vector DynamicSections; + ConcurrentReadableArray StaticSections; + ConcurrentReadableArray DynamicSections; FieldCacheState() { - StaticSections.reserve(16); - DynamicSections.reserve(8); initializeTypeFieldLookup(); } }; @@ -500,7 +539,6 @@ static Lazy FieldCache; void swift::swift_registerFieldDescriptors(const FieldDescriptor **records, size_t size) { auto &cache = FieldCache.get(); - ScopedLock guard(cache.SectionsLock); cache.DynamicSections.push_back({records, size}); } @@ -511,7 +549,6 @@ void swift::addImageTypeFieldDescriptorBlockCallback(const void *recordsBegin, // Field cache should always be sufficiently initialized by this point. auto &cache = FieldCache.unsafeGetAlreadyInitialized(); - ScopedLock guard(cache.SectionsLock); cache.StaticSections.push_back({recordsBegin, recordsEnd}); } @@ -696,7 +733,7 @@ class DecodedMetadataBuilder { #endif // Look for a nominal type descriptor based on its mangled name. - return _findNominalTypeDescriptor(node); + return _findNominalTypeDescriptor(node, demangler); } BuiltProtocolDecl createProtocolDecl( @@ -747,8 +784,20 @@ class DecodedMetadataBuilder { // Figure out the various levels of generic parameters we have in // this type. std::vector genericParamCounts; - bool innermostIsGeneric = - _gatherGenericParameterCounts(typeDecl, genericParamCounts); + bool innermostIsGeneric; + + // If we have no parent given, try to form the whole type in one go. + if (!parent) { + innermostIsGeneric = !genericArgs.empty(); + if (innermostIsGeneric) { + genericParamCounts.push_back(genericArgs.size()); + } + // Otherwise, we'll need to steal the generic arguments from the parent + // type to build a nested type. + } else { + innermostIsGeneric = _gatherGenericParameterCounts(typeDecl, + genericParamCounts); + } bool isGeneric = !genericParamCounts.empty(); // Gather the generic arguments. @@ -1019,7 +1068,7 @@ swift::_getTypeByMangledName(StringRef typeName, // Call the associated type access function. // TODO: can we just request abstract metadata? If so, do we have // a responsibility to try to finish it later? - return ((const AssociatedTypeAccessFunction * const *)witnessTable)[*assocTypeReqIndex] + return ((AssociatedTypeAccessFunction * const *)witnessTable)[*assocTypeReqIndex] (MetadataState::Complete, base, witnessTable).Value; }); @@ -1079,8 +1128,10 @@ void swift::swift_getFieldAt( } } + auto typeName = field.getMangledTypeName(0); + auto typeInfo = _getTypeByMangledName( - field.getMangledTypeName(0), + typeName, [&](unsigned depth, unsigned index) -> const Metadata * { if (depth >= descriptorPath.size()) return nullptr; @@ -1104,6 +1155,16 @@ void swift::swift_getFieldAt( return base->getGenericArgs()[flatIndex]; }); + // If demangling the type failed, pretend it's an empty type instead with + // a log message. + if (typeInfo == nullptr) { + typeInfo = TypeInfo(&METADATA_SYM(EMPTY_TUPLE_MANGLING), {}); + warning(0, "SWIFT RUNTIME BUG: unable to demangle type of field '%*s'. " + "mangled type name is '%*s'", + (int)name.size(), name.data(), + (int)typeName.size(), typeName.data()); + } + callback(name, FieldType() .withType(typeInfo) .withIndirect(field.isIndirectCase()) @@ -1133,16 +1194,15 @@ void swift::swift_getFieldAt( return; } - ScopedLock guard(cache.SectionsLock); // Otherwise let's try to find it in one of the sections. - for (auto §ion : cache.DynamicSections) { + for (auto §ion : cache.DynamicSections.snapshot()) { for (const auto *descriptor : section) { if (isRequestedDescriptor(*descriptor)) return; } } - for (const auto §ion : cache.StaticSections) { + for (const auto §ion : cache.StaticSections.snapshot()) { for (auto &descriptor : section) { if (isRequestedDescriptor(descriptor)) return; diff --git a/stdlib/public/runtime/Private.h b/stdlib/public/runtime/Private.h index 43b39d832ef5e..3bcfcaa2d6a8c 100644 --- a/stdlib/public/runtime/Private.h +++ b/stdlib/public/runtime/Private.h @@ -60,7 +60,7 @@ class TypeReferenceOwnership { /// itself related info has to be bundled with it. class TypeInfo { const Metadata *Type; - const TypeReferenceOwnership ReferenceOwnership; + TypeReferenceOwnership ReferenceOwnership; public: TypeInfo() : Type(nullptr), ReferenceOwnership() {} diff --git a/stdlib/public/runtime/ProtocolConformance.cpp b/stdlib/public/runtime/ProtocolConformance.cpp index b2a2b485aaa7c..0877082c27b5f 100644 --- a/stdlib/public/runtime/ProtocolConformance.cpp +++ b/stdlib/public/runtime/ProtocolConformance.cpp @@ -21,7 +21,6 @@ #include "swift/Runtime/Concurrent.h" #include "swift/Runtime/HeapObject.h" #include "swift/Runtime/Metadata.h" -#include "swift/Runtime/Mutex.h" #include "swift/Runtime/Unreachable.h" #include "CompatibilityOverride.h" #include "ImageInspection.h" @@ -228,12 +227,12 @@ namespace { const void *Type; const ProtocolDescriptor *Proto; std::atomic Table; - std::atomic FailureGeneration; + std::atomic FailureGeneration; public: ConformanceCacheEntry(ConformanceCacheKey key, const WitnessTable *table, - uintptr_t failureGeneration) + size_t failureGeneration) : Type(key.Type), Proto(key.Proto), Table(table), FailureGeneration(failureGeneration) { } @@ -261,7 +260,7 @@ namespace { Table.store(table, std::memory_order_release); } - void updateFailureGeneration(uintptr_t failureGeneration) { + void updateFailureGeneration(size_t failureGeneration) { assert(!isSuccessful()); FailureGeneration.store(failureGeneration, std::memory_order_relaxed); } @@ -272,8 +271,8 @@ namespace { return Table.load(std::memory_order_acquire); } - /// Get the generation number under which this lookup failed. - unsigned getFailureGeneration() const { + /// Get the generation in which this lookup failed. + size_t getFailureGeneration() const { assert(!isSuccessful()); return FailureGeneration.load(std::memory_order_relaxed); } @@ -283,18 +282,16 @@ namespace { // Conformance Cache. struct ConformanceState { ConcurrentMap Cache; - std::vector SectionsToScan; - Mutex SectionsToScanLock; + ConcurrentReadableArray SectionsToScan; ConformanceState() { - SectionsToScan.reserve(16); initializeProtocolConformanceLookup(); } void cacheSuccess(const void *type, const ProtocolDescriptor *proto, const WitnessTable *witness) { auto result = Cache.getOrInsert(ConformanceCacheKey(type, proto), - witness, uintptr_t(0)); + witness, 0); // If the entry was already present, we may need to update it. if (!result.second) { @@ -302,8 +299,8 @@ struct ConformanceState { } } - void cacheFailure(const void *type, const ProtocolDescriptor *proto) { - uintptr_t failureGeneration = SectionsToScan.size(); + void cacheFailure(const void *type, const ProtocolDescriptor *proto, + size_t failureGeneration) { auto result = Cache.getOrInsert(ConformanceCacheKey(type, proto), (const WitnessTable *) nullptr, failureGeneration); @@ -329,9 +326,7 @@ void ConformanceState::verify() const { // Iterate over all of the sections and verify all of the protocol // descriptors. auto &Self = const_cast(*this); - ScopedLock guard(Self.SectionsToScanLock); - - for (const auto &Section : SectionsToScan) { + for (const auto &Section : Self.SectionsToScan.snapshot()) { for (const auto &Record : Section) { Record.get()->verify(); } @@ -345,7 +340,6 @@ static void _registerProtocolConformances(ConformanceState &C, const ProtocolConformanceRecord *begin, const ProtocolConformanceRecord *end) { - ScopedLock guard(C.SectionsToScanLock); C.SectionsToScan.push_back(ConformanceSection{begin, end}); } @@ -448,9 +442,7 @@ searchInConformanceCache(const Metadata *type, } // Check if the negative cache entry is up-to-date. - // FIXME: Using SectionsToScan.size() outside SectionsToScanLock - // is undefined. - if (Value->getFailureGeneration() == C.SectionsToScan.size()) { + if (Value->getFailureGeneration() == C.SectionsToScan.snapshot().count()) { // Negative cache entry is up-to-date. Return failure along with // the original query type's own cache entry, if we found one. // (That entry may be out of date but the caller still has use for it.) @@ -543,8 +535,6 @@ swift_conformsToProtocolImpl(const Metadata * const type, // See if we have a cached conformance. The ConcurrentMap data structure // allows us to insert and search the map concurrently without locking. - // We do lock the slow path because the SectionsToScan data structure is not - // concurrent. auto FoundConformance = searchInConformanceCache(type, protocol); // If the result (positive or negative) is authoritative, return it. if (FoundConformance.isAuthoritative) @@ -552,48 +542,19 @@ swift_conformsToProtocolImpl(const Metadata * const type, auto failureEntry = FoundConformance.failureEntry; - // No up-to-date cache entry found. - // Acquire the lock so we can scan conformance records. - ScopedLock guard(C.SectionsToScanLock); - - // The world may have changed while we waited for the lock. - // If we found an out-of-date negative cache entry before - // acquiring the lock, make sure the entry is still negative and out of date. - // If we found no entry before acquiring the lock, search the cache again. - if (failureEntry) { - if (failureEntry->isSuccessful()) { - // Somebody else found a conformance. - return failureEntry->getWitnessTable(); - } - if (failureEntry->getFailureGeneration() == C.SectionsToScan.size()) { - // Somebody else brought the negative cache entry up to date. - return nullptr; - } - } - else { - FoundConformance = searchInConformanceCache(type, protocol); - if (FoundConformance.isAuthoritative) { - // Somebody else found a conformance or cached an up-to-date failure. - return FoundConformance.witnessTable; - } - failureEntry = FoundConformance.failureEntry; - } - - // We are now caught up after acquiring the lock. // Prepare to scan conformance records. - + auto snapshot = C.SectionsToScan.snapshot(); + // Scan only sections that were not scanned yet. // If we found an out-of-date negative cache entry, // we need not to re-scan the sections that it covers. - unsigned startSectionIdx = - failureEntry ? failureEntry->getFailureGeneration() : 0; - - unsigned endSectionIdx = C.SectionsToScan.size(); + auto startIndex = failureEntry ? failureEntry->getFailureGeneration() : 0; + auto endIndex = snapshot.count(); // If there are no unscanned sections outstanding // then we can cache failure and give up now. - if (startSectionIdx == endSectionIdx) { - C.cacheFailure(type, protocol); + if (startIndex == endIndex) { + C.cacheFailure(type, protocol, snapshot.count()); return nullptr; } @@ -614,31 +575,23 @@ swift_conformsToProtocolImpl(const Metadata * const type, return; case ConformanceFlags::ConformanceKind::ConditionalWitnessTableAccessor: { - // Note: we might end up doing more scanning for other conformances - // when checking the conditional requirements, so do a gross unlock/lock. - // FIXME: Don't do this :) - C.SectionsToScanLock.unlock(); auto witnessTable = descriptor.getWitnessTable(type); - C.SectionsToScanLock.lock(); if (witnessTable) C.cacheSuccess(type, protocol, witnessTable); else - C.cacheFailure(type, protocol); + C.cacheFailure(type, protocol, snapshot.count()); return; } } // Always fail, because we cannot interpret a future conformance // kind. - C.cacheFailure(type, protocol); + C.cacheFailure(type, protocol, snapshot.count()); }; // Really scan conformance records. - - for (unsigned sectionIdx = startSectionIdx; - sectionIdx < endSectionIdx; - ++sectionIdx) { - auto §ion = C.SectionsToScan[sectionIdx]; + for (size_t i = startIndex; i < endIndex; i++) { + auto §ion = snapshot.Start[i]; // Eagerly pull records for nondependent witnesses into our cache. for (const auto &record : section) { auto &descriptor = *record.get(); @@ -678,7 +631,7 @@ swift_conformsToProtocolImpl(const Metadata * const type, } } } - + // Conformance scan is complete. // Search the cache once more, and this time update the cache if necessary. @@ -686,7 +639,7 @@ swift_conformsToProtocolImpl(const Metadata * const type, if (FoundConformance.isAuthoritative) { return FoundConformance.witnessTable; } else { - C.cacheFailure(type, protocol); + C.cacheFailure(type, protocol, snapshot.count()); return nullptr; } } @@ -695,13 +648,7 @@ const TypeContextDescriptor * swift::_searchConformancesByMangledTypeName(Demangle::NodePointer node) { auto &C = Conformances.get(); - ScopedLock guard(C.SectionsToScanLock); - - unsigned sectionIdx = 0; - unsigned endSectionIdx = C.SectionsToScan.size(); - - for (; sectionIdx < endSectionIdx; ++sectionIdx) { - auto §ion = C.SectionsToScan[sectionIdx]; + for (auto §ion : C.SectionsToScan.snapshot()) { for (const auto &record : section) { if (auto ntd = record->getTypeContextDescriptor()) { if (_contextDescriptorMatchesMangling(ntd, node)) @@ -709,7 +656,6 @@ swift::_searchConformancesByMangledTypeName(Demangle::NodePointer node) { } } } - return nullptr; } diff --git a/stdlib/public/runtime/ReflectionMirror.mm b/stdlib/public/runtime/ReflectionMirror.mm index 361c5b744edf0..69aa2851052f5 100644 --- a/stdlib/public/runtime/ReflectionMirror.mm +++ b/stdlib/public/runtime/ReflectionMirror.mm @@ -622,8 +622,7 @@ auto call(OpaqueValue *passedValue, const Metadata *T, const Metadata *passedTyp } /// TODO: Implement specialized mirror witnesses for all kinds. - case MetadataKind::Function: - case MetadataKind::Existential: + default: break; // Types can't have these kinds. @@ -725,9 +724,9 @@ char swift_reflectionMirror_displayStyle(OpaqueValue *value, const Metadata *T) return "(Heap Generic Local Variable)"; case MetadataKind::ErrorObject: return "(ErrorType Object)"; + default: + return "(Unknown)"; } - - return "(Unknown)"; } #if SWIFT_OBJC_INTEROP diff --git a/stdlib/public/runtime/SwiftDtoa.cpp b/stdlib/public/runtime/SwiftDtoa.cpp index 4b253cff87607..8c827e9e1d302 100644 --- a/stdlib/public/runtime/SwiftDtoa.cpp +++ b/stdlib/public/runtime/SwiftDtoa.cpp @@ -30,21 +30,18 @@ /// /// Loitsch' original Grisu2 implementation guarantees round-trip /// accuracy but only generates the shortest decimal expansion about 99% -/// of the time. Grisu3 addresses this by essentially running two -/// copies of Grisu2 in parallel: one copy biased to avoid inaccuracy, -/// the other biased to avoid generating excess digits. If they agree, -/// then the result must be both accurate and short. But if they -/// disagree, then Grisu3 gives up and must defer to another algorithm. +/// of the time. Grisu3 is similar, but fails rather than producing +/// a result that is not the shortest possible. /// /// The Errol paper provides a deeper analysis of the cases where /// Grisu2 fails to find the shortest decimal expansion. There /// are two root causes of such failures: /// /// * Insufficient precision leads to scattered failures across the -/// entire range. Theorem 7 in the Errol paper shows a way to -/// construct a superset of the numbers subject to such failures. -/// With this list, we can simply test whether we have sufficient -/// precision. +/// entire range. The enumeration technique described in the Errol +/// paper shows a way to construct a superset of the numbers subject +/// to such failures. With this list, we can simply test whether we +/// have sufficient precision. /// /// For Double, the Errol3 algorithm uses double-double arithmetic /// with about 106 bits precision. This turns out to be not quite @@ -59,19 +56,16 @@ /// that for all three cases, an n-bit significand can be formatted /// optimally with no more than 2n+7 bits of intermediate precision. /// -/// * For numbers with even significands and a specific range of -/// exponents, the shortest value might occur exactly at the midpoint -/// between two adjacent binary floating-point values. Theorem 6 of -/// the Errol paper characterizes this sufficiently to allow us to -/// vary our strategy. Errol3 uses a separate formatter for this -/// case. This implementation instead widens the interval (as in -/// Grisu3), which allows us to use the same fast digit generation in -/// all cases, providing uniform performance across the entire range. +/// * Sometimes, the shortest value might occur exactly at the +/// midpoint between two adjacent binary floating-point values. +/// When converted back to binary, this will round to the adjacent +/// even significand. We handle this by widening the interval +/// whenever the significand is even in order to allow these +/// exact midpoints to be considered. /// -/// In addition to addressing the shortness failures characterized in -/// the Errol paper, the implementation here also incorporates -/// final-digit corrections from Grisu3, allowing it to produce the -/// optimal decimal decomposition in all cases. +/// In addition to addressing the shortness failures characterized in the Errol +/// paper, the implementation here also incorporates final-digit corrections +/// that allow it to produce the optimal decimal decomposition in all cases. /// /// In summary, this implementation is: /// @@ -80,7 +74,8 @@ /// /// * Simple. It is only a little more complex than Loitsch' original /// implementation of Grisu2. The full digit decomposition for double -/// is less than 300 lines of standard C. +/// is less than 300 lines of standard C, including routine arithmetic +/// helper functions. /// /// * Always Accurate. Converting the decimal form back to binary /// will always yield exactly the same value. For the IEEE 754 @@ -94,16 +89,17 @@ /// chooses the result that is closest to the exact floating-point /// value. (In case of an exact tie, it rounds the last digit even.) /// -/// For single-precision Float, these claims have been exhaustively -/// tested -- all 2^32 values can be checked in under an hour on a -/// mid-range modern laptop. The Double and Float80 formatters rely on -/// results from the Errol paper to ensure correctness. In addition, -/// we have verified more than 10^13 randomly-chosen values by comparing -/// the results to the output of Errol4. +/// For single-precision Float, we can simply test all 2^32 values. +/// This requires only a few minutes on a mid-range modern laptop. The +/// Double and Float80 formatters rely on results from the Errol paper +/// to ensure correctness. In addition, we have verified more than +/// 10^14 randomly-chosen Double values by comparing the results to the +/// output of Grisu3 (where Grisu3 fails, we've compared to Errol4). /// // ---------------------------------------------------------------------------- #include +#include #include #include #include @@ -162,19 +158,6 @@ // #if SWIFT_DTOA_FLOAT_SUPPORT || SWIFT_DTOA_DOUBLE_SUPPORT || SWIFT_DTOA_FLOAT80_SUPPORT -#if HAVE_UINT128_T -typedef __uint128_t swift_uint128_t; -#define initialize128WithHighLow64(dest, high64, low64) ((dest) = ((__uint128_t)(high64) << 64) | (low64)) -#else -typedef struct { - uint32_t low, b, c, high; -} swift_uint128_t; -#define initialize128WithHighLow64(dest, high64, low64) \ - ((dest).low = (uint32_t)(low64), \ - (dest).b = (uint32_t)((low64) >> 32), \ - (dest).c = (uint32_t)(high64), \ - (dest).high = (uint32_t)((high64) >> 32)) -#endif static int binaryExponentFor10ToThe(int p); static int decimalExponentFor2ToThe(int e); #endif @@ -188,10 +171,30 @@ static uint64_t multiply64x32RoundingDown(uint64_t lhs, uint32_t rhs); static uint64_t multiply64x32RoundingUp(uint64_t lhs, uint32_t rhs); static uint64_t multiply64x64RoundingDown(uint64_t lhs, uint64_t rhs); static uint64_t multiply64x64RoundingUp(uint64_t lhs, uint64_t rhs); -static uint64_t shiftRightRoundingUp64(uint64_t lhs, int shift); static void intervalContainingPowerOf10_Float(int p, uint64_t *lower, uint64_t *upper, int *exponent); #endif +// +// Helpers used by both the double-precision and float80 formatters +// + +#if SWIFT_DTOA_DOUBLE_SUPPORT || SWIFT_DTOA_FLOAT80_SUPPORT +#if HAVE_UINT128_T +typedef __uint128_t swift_uint128_t; +#define initialize128WithHighLow64(dest, high64, low64) ((dest) = ((__uint128_t)(high64) << 64) | (low64)) +#else +typedef struct { + uint32_t low, b, c, high; +} swift_uint128_t; +#define initialize128WithHighLow64(dest, high64, low64) \ + ((dest).low = (uint32_t)(low64), \ + (dest).b = (uint32_t)((low64) >> 32), \ + (dest).c = (uint32_t)(high64), \ + (dest).high = (uint32_t)((high64) >> 32)) +#endif +#endif + + // // Helper functions used only by the double-precision formatter // @@ -415,16 +418,12 @@ int swift_decompose_double(double d, static const int integerBits = 14; static const int fractionBits = 128 - integerBits; - // We scale the interval in one of two different ways... + // We scale the interval in one of two different ways, + // depending on whether the significand is even or odd... - // This value comes from the Errol paper, Theorem 6 - static const int maxIntegerMidpointExponent = 131; swift_uint128_t u, l; - if (((significandBitPattern & 1) != 0) - || (binaryExponent < significandBitCount + 3) - || (binaryExponent > maxIntegerMidpointExponent)) { - - // Case A: Narrow the interval + if (significandBitPattern & 1) { + // Case A: Narrow the interval (odd significand) // Loitsch' original Grisu2 always narrows the interval. // Since our digit generation will select a value within @@ -438,9 +437,12 @@ int swift_decompose_double(double d, // miss. This risk obviously gets lower with increased // precision, but it wasn't until the Errol paper that anyone // had a good way to test whether a particular implementation - // had sufficient precision. Using Errol Theorem 7 and tinkering - // with the `fractionBits` parameter above, you can show that - // 110 fraction bits is sufficient for Double. + // had sufficient precision. That paper shows a way to enumerate + // the worst-case numbers; those numbers that are extremely close + // to the mid-points between adjacent floating-point values. + // These are the values that might sit just outside of the + // narrowed interval. By testing these values, we can verify + // the correctness of our implementation. // Multiply out the upper midpoint, rounding down... swift_uint128_t u1 = multiply128x64RoundingDown(powerOfTenRoundedDown, @@ -455,38 +457,22 @@ int swift_decompose_double(double d, l = shiftRightRoundingUp128(l1, integerBits - extraBits); } else { + // Case B: Widen the interval (even significand) - // Case B: Widen the interval - - // As explained in Errol Theorem 6, in certain cases the true - // shortest decimal result is at one of the exact scaled - // midpoints. Any narrowing will exclude the exact midpoints, - // so we must widen here to ensure we consider those cases. - // Errol Theorem 6 explains that this can only happen if the - // exact midpoints are integers with even significands and - // exponents less than a particular bound. (See the `if` - // condition above.) - - // Widening the interval in this case ensures that we find a - // short result but carries a risk of selecting a result - // outside of the exact scaled interval (which would be - // inaccurate). For Float and Float80, it's easy to see this - // never happens: they use more fraction bits here than the - // maximum exponent for this case so any number outside the - // exact interval will not be an integer. Since any - // non-integer will always have more digits than the adjacent - // integers, the digit generation will never select it. For - // double, we've cut things a bit finer (114 bit fraction here - // is not higher than the exponent limit of 131 above), so - // we've had to rely on Errol Theorem 7 to enumerate possible - // failures to test those cases. - - // Note: Grisu3 converts every number twice: once with the - // narrowed interval to ensure accuracy and once with the - // wider interval to ensure shortness. If both agree, the - // result must meet both conditions. In essence, the Errol - // paper provides a way to select narrowing or widening - // appropriately so we can avoid Grisu3's double conversion. + // As explained in Errol Theorem 6, in certain cases there is + // a short decimal representation at the exact boundary of the + // scaled interval. When such a number is converted back to + // binary, it will get rounded to the adjacent even + // significand. + + // So when the significand is even, we widen the interval in + // order to ensure that the exact midpoints are considered. + // Of couse, this ensures that we find a short result but + // carries a risk of selecting a result outside of the exact + // scaled interval (which would be inaccurate). + + // The same testing approach described above also applies + // to this case. swift_uint128_t u1 = multiply128x64RoundingUp(powerOfTenRoundedUp, upperMidpointExact); @@ -584,9 +570,8 @@ int swift_decompose_double(double d, clearIntegerPart128(&t, fractionBits); } - // Adjust the final digit to be closer to the original value. - // This is basically the same as Grisu3. It accounts for the - // fact that sometimes there is more than one shortest digit + // Adjust the final digit to be closer to the original value. It accounts + // for the fact that sometimes there is more than one shortest digit // sequence. // For example, consider how the above would work if you had the @@ -695,11 +680,11 @@ int swift_decompose_float(float f, } // Step 2: Determine the exact unscaled target interval - uint32_t halfUlp = (uint32_t)1 << (32 - significandBitCount - 2); - uint32_t quarterUlp = halfUlp >> 1; + static const uint32_t halfUlp = (uint32_t)1 << (32 - significandBitCount - 2); uint32_t upperMidpointExact = significand + halfUlp; int isBoundary = significandBitPattern == 0; + static const uint32_t quarterUlp = halfUlp >> 1; uint32_t lowerMidpointExact = significand - (isBoundary ? quarterUlp : halfUlp); @@ -718,31 +703,28 @@ int swift_decompose_float(float f, // Step 5: Scale the interval (with rounding) static const int integerBits = 5; + const int shift = integerBits - extraBits; + const int roundUpBias = (1 << shift) - 1; static const int fractionBits = 64 - integerBits; - // This value comes from the Errol paper, Theorem 6 - static const int maxIntegerMidpointExponent = 57; uint64_t u, l; - if (((significandBitPattern & 1) != 0) - || (binaryExponent < significandBitCount + 3) - || (binaryExponent > maxIntegerMidpointExponent)) { - - // Narrow the interval + if (significandBitPattern & 1) { + // Narrow the interval (odd significand) uint64_t u1 = multiply64x32RoundingDown(powerOfTenRoundedDown, upperMidpointExact); - u = u1 >> (integerBits - extraBits); // Rounding down + u = u1 >> shift; // Rounding down uint64_t l1 = multiply64x32RoundingUp(powerOfTenRoundedUp, lowerMidpointExact); - l = shiftRightRoundingUp64(l1, integerBits - extraBits); + l = (l1 + roundUpBias) >> shift; // Rounding Up } else { - // Widen the interval + // Widen the interval (even significand) uint64_t u1 = multiply64x32RoundingUp(powerOfTenRoundedUp, upperMidpointExact); - u = shiftRightRoundingUp64(u1, integerBits - extraBits); + u = (u1 + roundUpBias) >> shift; // Rounding Up uint64_t l1 = multiply64x32RoundingDown(powerOfTenRoundedDown, lowerMidpointExact); - l = l1 >> (integerBits - extraBits); // Rounding down + l = l1 >> shift; // Rounding down } // Step 6: Align first digit, adjust exponent @@ -761,8 +743,6 @@ int swift_decompose_float(float f, // Step 7: Generate digits int8_t *digit_p = digits; - - // Adjustment above already set up the first digit: int nextDigit = (int)(t >> fractionBits); t &= fixedPointMask; @@ -784,12 +764,13 @@ int swift_decompose_float(float f, skew = delta / 2 - t; } uint64_t one = (uint64_t)(1) << (64 - integerBits); - uint64_t fractionMask = one - 1; + uint64_t lastAccurateBit = 1ULL << 24; + uint64_t fractionMask = (one - 1) & ~(lastAccurateBit - 1); uint64_t oneHalf = one >> 1; - if ((skew & fractionMask) == oneHalf) { + if (((skew + (lastAccurateBit >> 1)) & fractionMask) == oneHalf) { + // If the skew is exactly integer + 1/2, round the last + // digit even after adjustment int adjust = (int)(skew >> (64 - integerBits)); - // If the skew is integer + 1/2, round the last digit even - // after adjustment nextDigit = (nextDigit - adjust) & ~1; } else { // Else round to nearest... @@ -809,8 +790,6 @@ int swift_decompose_float(float f, int swift_decompose_float80(long double d, int8_t *digits, size_t digits_length, int *decimalExponent) { - // Omit leading bit, as if we were an IEEE 754 format - static const int significandBitCount = LDBL_MANT_DIG - 1; static const int exponentBitCount = 15; static const int exponentMask = (1 << exponentBitCount) - 1; // See comments in swift_decompose_double to understand @@ -881,14 +860,9 @@ int swift_decompose_float80(long double d, #else static const int highFractionBits = fractionBits % 32; #endif - // This value comes from the Errol paper, Theorem 6 - static const int maxIntegerMidpointExponent = 153; swift_uint192_t u, l; - if (((significandBitPattern & 1) != 0) - || (binaryExponent < significandBitCount + 3) - || (binaryExponent > maxIntegerMidpointExponent)) { - - // Narrow the interval + if (significandBitPattern & 1) { + // Narrow the interval (odd significand) u = powerOfTenRoundedDown; multiply192x128RoundingDown(&u, upperMidpointExact); shiftRightRoundingDown192(&u, integerBits - extraBits); @@ -897,7 +871,7 @@ int swift_decompose_float80(long double d, multiply192x128RoundingUp(&l, lowerMidpointExact); shiftRightRoundingUp192(&l, integerBits - extraBits); } else { - // Widen the interval + // Widen the interval (even significand) u = powerOfTenRoundedUp; multiply192x128RoundingUp(&u, upperMidpointExact); shiftRightRoundingUp192(&u, integerBits - extraBits); @@ -1063,7 +1037,9 @@ size_t swift_format_float(float d, char *dest, size_t length) int8_t digits[9]; int digitCount = swift_decompose_float(d, digits, sizeof(digits), &decimalExponent); - if (decimalExponent < -3 || decimalExponent > 6) { + // People use float to model integers <= 2^24, so we use that + // as a cutoff for decimal vs. exponential format. + if (decimalExponent < -3 || fabsf(d) > 0x1.0p24F) { return swift_format_exponential(dest, length, signbit(d), digits, digitCount, decimalExponent); } else { @@ -1090,10 +1066,10 @@ size_t swift_format_double(double d, char *dest, size_t length) uint64_t raw = bitPatternForDouble(d); const char *sign = signbit(d) ? "-" : ""; const char *signaling = ((raw >> (significandBitCount - 1)) & 1) ? "" : "s"; - uint64_t payload = raw & ((1L << (significandBitCount - 2)) - 1); + uint64_t payload = raw & ((1ull << (significandBitCount - 2)) - 1); char buff[32]; if (payload != 0) { - snprintf(buff, sizeof(buff), "%s%snan(0x%llx)", + snprintf(buff, sizeof(buff), "%s%snan(0x%" PRIx64 ")", sign, signaling, payload); } else { snprintf(buff, sizeof(buff), "%s%snan", @@ -1117,7 +1093,9 @@ size_t swift_format_double(double d, char *dest, size_t length) int8_t digits[17]; int digitCount = swift_decompose_double(d, digits, sizeof(digits), &decimalExponent); - if (decimalExponent < -3 || decimalExponent > 15) { + // People use double to model integers <= 2^53, so we use that + // as a cutoff for decimal vs. exponential format. + if (decimalExponent < -3 || fabs(d) > 0x1.0p53) { return swift_format_exponential(dest, length, signbit(d), digits, digitCount, decimalExponent); } else { @@ -1147,7 +1125,7 @@ size_t swift_format_float80(long double d, char *dest, size_t length) uint64_t payload = significandBitPattern & (((uint64_t)1 << 61) - 1); char buff[32]; if (payload != 0) { - snprintf(buff, sizeof(buff), "%s%snan(0x%llx)", + snprintf(buff, sizeof(buff), "%s%snan(0x%" PRIx64 ")", sign, signaling, payload); } else { snprintf(buff, sizeof(buff), "%s%snan", @@ -1166,13 +1144,16 @@ size_t swift_format_float80(long double d, char *dest, size_t length) } } - // Decimal numeric formatting // Decimal numeric formatting int decimalExponent; int8_t digits[21]; int digitCount = swift_decompose_float80(d, digits, sizeof(digits), &decimalExponent); - if (decimalExponent < -3 || decimalExponent > 18) { + // People use long double to model integers <= 2^64, so we use that + // as a cutoff for decimal vs. exponential format. + // The constant is written out as a float80 (aka "long double") literal + // here since it can't be expressed as a 64-bit integer. + if (decimalExponent < -3 || fabsl(d) > 0x1.0p64L) { return swift_format_exponential(dest, length, signbit(d), digits, digitCount, decimalExponent); } else { @@ -1355,35 +1336,23 @@ size_t swift_format_decimal(char *dest, size_t length, // low-order part (rounding). So most of the arithmetic helpers here // are for multiplication. -// Note: With 64-bit GCC and Clang, we get a lot of performance gain -// and code simplification by using `__uint128_t`. Otherwise, we have -// to break things down into 32-bit chunks so we don't overflow 64-bit -// temporaries. +// Note: With 64-bit GCC and Clang, we get a noticable performance +// gain by using `__uint128_t`. Otherwise, we have to break things +// down into 32-bit chunks so we don't overflow 64-bit temporaries. #if SWIFT_DTOA_FLOAT_SUPPORT // Multiply a 64-bit fraction by a 32-bit fraction, rounding down. static uint64_t multiply64x32RoundingDown(uint64_t lhs, uint32_t rhs) { -#if HAVE_UINT128_T - __uint128_t full = (__uint128_t)lhs * rhs; - return (uint64_t)(full >> 32); -#else static const uint64_t mask32 = UINT32_MAX; uint64_t t = ((lhs & mask32) * rhs) >> 32; return t + (lhs >> 32) * rhs; -#endif } // Multiply a 64-bit fraction by a 32-bit fraction, rounding up. static uint64_t multiply64x32RoundingUp(uint64_t lhs, uint32_t rhs) { -#if HAVE_UINT128_T - static const __uint128_t roundingFactor = UINT32_MAX; - __uint128_t full = (__uint128_t)lhs * rhs; - return (uint64_t)((full + roundingFactor) >> 32); -#else static const uint64_t mask32 = UINT32_MAX; uint64_t t = (((lhs & mask32) * rhs) + mask32) >> 32; return t + (lhs >> 32) * rhs; -#endif } // Multiply a 64-bit fraction by a 64-bit fraction, rounding down. @@ -1397,9 +1366,19 @@ static uint64_t multiply64x64RoundingDown(uint64_t lhs, uint64_t rhs) { t >>= 32; uint64_t a = (lhs >> 32) * (rhs & mask32); uint64_t b = (lhs & mask32) * (rhs >> 32); - t += (a & mask32) + (b & mask32); + // Useful: If w,x,y,z are all 32-bit values, then: + // w * x + y + z + // <= (2^64 - 2^33 + 1) + (2^32 - 1) + (2^32 - 1) + // <= 2^64 - 1 + // + // That is, a product of two 32-bit values plus two more 32-bit + // values can't overflow 64 bits. (But "three more" can, so be + // careful!) + // + // Here: t + a + (b & mask32) <= 2^64 - 1 + t += a + (b & mask32); t >>= 32; - t += (a >> 32) + (b >> 32); + t += (b >> 32); return t + (lhs >> 32) * (rhs >> 32); #endif } @@ -1407,28 +1386,22 @@ static uint64_t multiply64x64RoundingDown(uint64_t lhs, uint64_t rhs) { // Multiply a 64-bit fraction by a 64-bit fraction, rounding up. static uint64_t multiply64x64RoundingUp(uint64_t lhs, uint64_t rhs) { #if HAVE_UINT128_T - static const __uint128_t roundingFactor = ((__uint128_t)1 << 64) - 1; + static const __uint128_t roundingUpBias = ((__uint128_t)1 << 64) - 1; __uint128_t full = (__uint128_t)lhs * rhs; - return (uint64_t)((full + roundingFactor) >> 64); + return (uint64_t)((full + roundingUpBias) >> 64); #else static const uint64_t mask32 = UINT32_MAX; uint64_t t = (lhs & mask32) * (rhs & mask32); t = (t + mask32) >> 32; uint64_t a = (lhs >> 32) * (rhs & mask32); uint64_t b = (lhs & mask32) * (rhs >> 32); - t += (a & mask32) + (b & mask32); - t = (t + mask32) >> 32; + t += (a & mask32) + (b & mask32) + mask32; + t >>= 32; t += (a >> 32) + (b >> 32); return t + (lhs >> 32) * (rhs >> 32); #endif } -// Shift a 64-bit integer right, rounding up. -static uint64_t shiftRightRoundingUp64(uint64_t lhs, int shift) { - uint64_t mask = ((uint64_t)1 << shift) - 1; - uint64_t round = ((lhs & mask) == 0) ? 0 : 1; - return (lhs >> shift) + round; -} #endif #if SWIFT_DTOA_DOUBLE_SUPPORT @@ -1449,9 +1422,9 @@ static swift_uint128_t multiply128x64RoundingDown(swift_uint128_t lhs, uint64_t t >>= 32; uint64_t a = (lhs.b) * rhs0; uint64_t b = (lhs.low) * rhs1; - t += (a & mask32) + (b & mask32); + t += a + (b & mask32); t >>= 32; - t += (a >> 32) + (b >> 32); + t += (b >> 32); a = lhs.c * rhs0; b = lhs.b * rhs1; t += (a & mask32) + (b & mask32); @@ -1478,9 +1451,8 @@ static swift_uint128_t multiply128x64RoundingUp(swift_uint128_t lhs, uint64_t rh uint64_t lhsh = (uint64_t)(lhs >> 64); swift_uint128_t h = (swift_uint128_t)lhsh * rhs; swift_uint128_t l = (swift_uint128_t)lhsl * rhs; - uint64_t remainder = (uint64_t)l; - uint64_t round = (remainder != 0) ? 1 : 0; - return h + (l >> 64) + round; + const static __uint128_t bias = ((__uint128_t)1 << 64) - 1; + return h + ((l + bias) >> 64); #else swift_uint128_t result; static const uint64_t mask32 = UINT32_MAX; @@ -1620,9 +1592,9 @@ static void multiply192x64RoundingDown(swift_uint192_t *lhs, uint64_t rhs) { t >>= 32; uint64_t a = lhs->low * rhs1; uint64_t b = lhs->b * rhs0; - t += (a & mask32) + (b & mask32); + t += a + (b & mask32); t >>= 32; - t += (a >> 32) + (b >> 32); + t += (b >> 32); a = lhs->b * rhs1; b = lhs->c * rhs0; t += (a & mask32) + (b & mask32); @@ -1775,9 +1747,9 @@ static void multiply192x128RoundingDown(swift_uint192_t *lhs, swift_uint128_t rh t >>= 32; a = (uint64_t)lhs->low * rhs.b; b = (uint64_t)lhs->b * rhs.low; - t += (a & mask32) + (b & mask32); + t += a + (b & mask32); t >>= 32; - t += (a >> 32) + (b >> 32); + t += (b >> 32); a = (uint64_t)lhs->low * rhs.c; b = (uint64_t)lhs->b * rhs.b; c = (uint64_t)lhs->c * rhs.low; @@ -2319,26 +2291,19 @@ static int decimalExponentFor2ToThe(int e) { // lower * 2^exponent <= 10^p <= upper * 2^exponent // ``` // -// In particular, if `10^p` can be exactly represented, this routine -// may return the same value for `lower` and `upper`. -// +// Note: Max(*upper - *lower) = 3 static void intervalContainingPowerOf10_Float(int p, uint64_t *lower, uint64_t *upper, int *exponent) { if (p < 0) { uint64_t base = powersOf10_Float[p + 40]; int baseExponent = binaryExponentFor10ToThe(p + 40); uint64_t tenToTheMinus40 = 0x8b61313bbabce2c6; // x 2^-132 ~= 10^-40 - *lower = multiply64x64RoundingDown(base, tenToTheMinus40); *upper = multiply64x64RoundingUp(base + 1, tenToTheMinus40 + 1); + *lower = multiply64x64RoundingDown(base, tenToTheMinus40); *exponent = baseExponent - 132; - } else if (p <= 27) { - uint64_t exact = powersOf10_Float[p]; - *upper = exact; - *lower = exact; - *exponent = binaryExponentFor10ToThe(p); } else { - uint64_t exact = powersOf10_Float[p]; - *upper = exact + 1; - *lower = exact; + uint64_t base = powersOf10_Float[p]; + *upper = base + 1; + *lower = base; *exponent = binaryExponentFor10ToThe(p); } } diff --git a/stdlib/public/runtime/SwiftObject.mm b/stdlib/public/runtime/SwiftObject.mm index e277a1c14a1bf..d2380e75856f9 100644 --- a/stdlib/public/runtime/SwiftObject.mm +++ b/stdlib/public/runtime/SwiftObject.mm @@ -96,6 +96,34 @@ static Class _swift_getObjCClassOfAllocated(const void *object) { return class_const_cast(_swift_getClassOfAllocated(object)); } +/// \brief Fetch the ObjC class object associated with the formal dynamic +/// type of the given (possibly Objective-C) object. The formal +/// dynamic type ignores dynamic subclasses such as those introduced +/// by KVO. +/// +/// The object pointer may be a tagged pointer, but cannot be null. +const ClassMetadata *swift::swift_getObjCClassFromObject(HeapObject *object) { + auto classAsMetadata = _swift_getClass(object); + + // Walk up the superclass chain skipping over artifical Swift classes. + // If we find a non-Swift class use the result of [object class] instead. + + while (classAsMetadata && classAsMetadata->isTypeMetadata()) { + if (!classAsMetadata->isArtificialSubclass()) + return classAsMetadata; + classAsMetadata = classAsMetadata->Superclass; + } + + id objcObject = reinterpret_cast(object); + Class objcClass = [objcObject class]; + if (objcObjectIsClass(objcObject)) { + // Original object is a class. We want a + // metaclass but +class doesn't give that to us. + objcClass = object_getClass(objcClass); + } + classAsMetadata = reinterpret_cast(objcClass); + return classAsMetadata; +} #endif /// \brief Fetch the type metadata associated with the formal dynamic @@ -575,12 +603,13 @@ static bool isNonNative_unTagged_bridgeObject(void *object) { #if SWIFT_OBJC_INTEROP if (!isNonNative_unTagged_bridgeObject(object)) { swift_retain(static_cast(objectRef)); - return static_cast(objectRef); + return object; } - return objc_retain(static_cast(objectRef)); + objc_retain(static_cast(objectRef)); + return object; #else swift_retain(static_cast(objectRef)); - return static_cast(objectRef); + return object; #endif } @@ -596,12 +625,13 @@ static bool isNonNative_unTagged_bridgeObject(void *object) { #if SWIFT_OBJC_INTEROP if (!isNonNative_unTagged_bridgeObject(object)) { swift_nonatomic_retain(static_cast(objectRef)); - return static_cast(objectRef); + return object; } - return objc_retain(static_cast(objectRef)); + objc_retain(static_cast(objectRef)); + return object; #else swift_nonatomic_retain(static_cast(objectRef)); - return static_cast(objectRef); + return object; #endif } @@ -652,14 +682,14 @@ static bool isNonNative_unTagged_bridgeObject(void *object) { void *objc_ret = nullptr; if (!isNonNative_unTagged_bridgeObject(object)) { swift_retain_n(static_cast(objectRef), n); - return static_cast(objectRef); + return object; } for (int i = 0;i < n; ++i) objc_ret = objc_retain(static_cast(objectRef)); - return objc_ret; + return object; #else swift_retain_n(static_cast(objectRef), n); - return static_cast(objectRef); + return object; #endif } @@ -693,14 +723,14 @@ static bool isNonNative_unTagged_bridgeObject(void *object) { void *objc_ret = nullptr; if (!isNonNative_unTagged_bridgeObject(object)) { swift_nonatomic_retain_n(static_cast(objectRef), n); - return static_cast(objectRef); + return object; } for (int i = 0;i < n; ++i) objc_ret = objc_retain(static_cast(objectRef)); - return objc_ret; + return object; #else swift_nonatomic_retain_n(static_cast(objectRef), n); - return static_cast(objectRef); + return object; #endif } @@ -1112,16 +1142,7 @@ static bool isObjCForUnownedReference(void *value) { break; // Other kinds of type can never conform to ObjC protocols. - case MetadataKind::Struct: - case MetadataKind::Enum: - case MetadataKind::Optional: - case MetadataKind::Opaque: - case MetadataKind::Tuple: - case MetadataKind::Function: - case MetadataKind::Existential: - case MetadataKind::Metatype: - case MetadataKind::ExistentialMetatype: - case MetadataKind::ForeignClass: + default: swift_dynamicCastFailure(type, nameForMetadata(type).c_str(), protocols[0], protocol_getName(protocols[0])); @@ -1158,16 +1179,7 @@ static bool isObjCForUnownedReference(void *value) { break; // Other kinds of type can never conform to ObjC protocols. - case MetadataKind::Struct: - case MetadataKind::Enum: - case MetadataKind::Optional: - case MetadataKind::Opaque: - case MetadataKind::Tuple: - case MetadataKind::Function: - case MetadataKind::Existential: - case MetadataKind::Metatype: - case MetadataKind::ExistentialMetatype: - case MetadataKind::ForeignClass: + default: return nullptr; case MetadataKind::HeapLocalVariable: @@ -1389,22 +1401,39 @@ static bool usesNativeSwiftReferenceCounting_nonNull( bool swift::swift_isEscapingClosureAtFileLocation(const HeapObject *object, const unsigned char *filename, int32_t filenameLength, - int32_t line) { + int32_t line, int32_t column, + unsigned verifcationType) { + assert((verifcationType == 0 || verifcationType == 1) && + "Unknown verifcation type"); + bool isEscaping = object != nullptr && !object->refCounts.isUniquelyReferenced(); // Print a message if the closure escaped. if (isEscaping) { - auto *message = "Fatal error: closure argument was escaped in " - "withoutActuallyEscaping block"; + auto *message = (verifcationType == 0) + ? "closure argument was escaped in " + "withoutActuallyEscaping block" + : "closure argument passed as @noescape " + "to Objective-C has escaped"; auto messageLength = strlen(message); - if (_swift_shouldReportFatalErrorsToDebugger()) - _swift_reportToDebugger(RuntimeErrorFlagFatal, message); - char *log; - swift_asprintf(&log, "%.*s: file %.*s, line %" PRIu32 "\n", messageLength, - message, filenameLength, filename, line); + swift_asprintf( + &log, "%.*s: file %.*s, line %" PRIu32 ", column %" PRIu32 " \n", + messageLength, message, filenameLength, filename, line, column); + + printCurrentBacktrace(2/*framesToSkip*/); + + if (_swift_shouldReportFatalErrorsToDebugger()) { + RuntimeErrorDetails details = { + .version = RuntimeErrorDetails::currentVersion, + .errorType = "escaping-closure-violation", + .currentStackDescription = "Closure has escaped", + .framesToSkip = 1, + }; + _swift_reportToDebugger(RuntimeErrorFlagFatal, log, &details); + } swift_reportError(RuntimeErrorFlagFatal, log); free(log); diff --git a/stdlib/public/runtime/SwiftRT-ELF.cpp b/stdlib/public/runtime/SwiftRT-ELF.cpp index 47ba9a7ffa574..56074d2c58083 100644 --- a/stdlib/public/runtime/SwiftRT-ELF.cpp +++ b/stdlib/public/runtime/SwiftRT-ELF.cpp @@ -24,14 +24,14 @@ __attribute__((__visibility__("hidden"))) extern const char __stop_##name; extern "C" { -DECLARE_SWIFT_SECTION(swift4_protocols) -DECLARE_SWIFT_SECTION(swift4_protocol_conformances) -DECLARE_SWIFT_SECTION(swift4_type_metadata) - -DECLARE_SWIFT_SECTION(swift4_typeref) -DECLARE_SWIFT_SECTION(swift4_reflstr) -DECLARE_SWIFT_SECTION(swift4_fieldmd) -DECLARE_SWIFT_SECTION(swift4_assocty) +DECLARE_SWIFT_SECTION(swift5_protocols) +DECLARE_SWIFT_SECTION(swift5_protocol_conformances) +DECLARE_SWIFT_SECTION(swift5_type_metadata) + +DECLARE_SWIFT_SECTION(swift5_typeref) +DECLARE_SWIFT_SECTION(swift5_reflstr) +DECLARE_SWIFT_SECTION(swift5_fieldmd) +DECLARE_SWIFT_SECTION(swift5_assocty) } #undef DECLARE_SWIFT_SECTION @@ -53,14 +53,14 @@ static void swift_image_constructor() { nullptr, nullptr, - SWIFT_SECTION_RANGE(swift4_protocols), - SWIFT_SECTION_RANGE(swift4_protocol_conformances), - SWIFT_SECTION_RANGE(swift4_type_metadata), + SWIFT_SECTION_RANGE(swift5_protocols), + SWIFT_SECTION_RANGE(swift5_protocol_conformances), + SWIFT_SECTION_RANGE(swift5_type_metadata), - SWIFT_SECTION_RANGE(swift4_typeref), - SWIFT_SECTION_RANGE(swift4_reflstr), - SWIFT_SECTION_RANGE(swift4_fieldmd), - SWIFT_SECTION_RANGE(swift4_assocty), + SWIFT_SECTION_RANGE(swift5_typeref), + SWIFT_SECTION_RANGE(swift5_reflstr), + SWIFT_SECTION_RANGE(swift5_fieldmd), + SWIFT_SECTION_RANGE(swift5_assocty), }; #undef SWIFT_SECTION_RANGE diff --git a/stdlib/public/stubs/CMakeLists.txt b/stdlib/public/stubs/CMakeLists.txt index c311b20cc886f..61f45eed90af9 100644 --- a/stdlib/public/stubs/CMakeLists.txt +++ b/stdlib/public/stubs/CMakeLists.txt @@ -5,6 +5,7 @@ set(swift_stubs_sources KeyPaths.cpp LibcShims.cpp Stubs.cpp + MathStubs.cpp ) set(swift_stubs_objc_sources Availability.mm @@ -23,15 +24,21 @@ set(swift_stubs_c_compile_flags ${SWIFT_RUNTIME_CORE_CXX_FLAGS}) list(APPEND swift_stubs_c_compile_flags -DswiftCore_EXPORTS) list(APPEND swift_stubs_c_compile_flags -I${SWIFT_SOURCE_DIR}/include) -add_swift_library(swiftStdlibStubs OBJECT_LIBRARY TARGET_LIBRARY - ${swift_stubs_sources} - ${swift_stubs_objc_sources} - ${swift_stubs_unicode_normalization_sources} - C_COMPILE_FLAGS ${swift_stubs_c_compile_flags} - LINK_FLAGS ${SWIFT_RUNTIME_CORE_LINK_FLAGS} - INSTALL_IN_COMPONENT stdlib) +add_swift_library(swiftStdlibStubs + OBJECT_LIBRARY TARGET_LIBRARY + ${swift_stubs_sources} + ${swift_stubs_objc_sources} + ${swift_stubs_unicode_normalization_sources} + C_COMPILE_FLAGS + ${swift_stubs_c_compile_flags} + LINK_FLAGS + ${SWIFT_RUNTIME_CORE_LINK_FLAGS} + INSTALL_IN_COMPONENT + stdlib) if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") - set_property(SOURCE SwiftNativeNSXXXBaseARC.m APPEND_STRING PROPERTY COMPILE_FLAGS - "-fobjc-arc") + set_property(SOURCE + SwiftNativeNSXXXBaseARC.m + APPEND_STRING PROPERTY COMPILE_FLAGS + "-fobjc-arc") endif() diff --git a/stdlib/public/stubs/GlobalObjects.cpp b/stdlib/public/stubs/GlobalObjects.cpp index bdf2c94fd3e80..323ff9a15fab7 100644 --- a/stdlib/public/stubs/GlobalObjects.cpp +++ b/stdlib/public/stubs/GlobalObjects.cpp @@ -17,10 +17,10 @@ //===----------------------------------------------------------------------===// #include "../SwiftShims/GlobalObjects.h" +#include "../SwiftShims/LibcShims.h" #include "swift/Runtime/Metadata.h" #include "swift/Runtime/Debug.h" #include -#include namespace swift { // FIXME(ABI)#76 : does this declaration need SWIFT_RUNTIME_STDLIB_INTERFACE? @@ -116,18 +116,10 @@ static swift::_SwiftHashingParameters initializeHashingParameters() { if (determinism && 0 == strcmp(determinism, "1")) { return { 0, 0, true }; } -#if defined(__APPLE__) - // Use arc4random if available. __swift_uint64_t seed0 = 0, seed1 = 0; - arc4random_buf(&seed0, sizeof(seed0)); - arc4random_buf(&seed1, sizeof(seed1)); + swift::_stdlib_random(&seed0, sizeof(seed0)); + swift::_stdlib_random(&seed1, sizeof(seed1)); return { seed0, seed1, false }; -#else - std::random_device randomDevice; - std::mt19937_64 engine(randomDevice()); - std::uniform_int_distribution<__swift_uint64_t> distribution; - return { distribution(engine), distribution(engine), false }; -#endif } SWIFT_ALLOWED_RUNTIME_GLOBAL_CTOR_BEGIN diff --git a/stdlib/public/stubs/LibcShims.cpp b/stdlib/public/stubs/LibcShims.cpp index 7d66992c3e5e7..d0892da66b75c 100644 --- a/stdlib/public/stubs/LibcShims.cpp +++ b/stdlib/public/stubs/LibcShims.cpp @@ -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 - 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 @@ -14,31 +14,41 @@ #define _REENTRANT #include #endif -#include -#include -#include -#if defined(_WIN32) + +#if defined(_WIN32) && !defined(__CYGWIN__) #include #define WIN32_LEAN_AND_MEAN #include +#include +#pragma comment(lib, "Bcrypt.lib") #else -#include #include #include #include +#include #endif -#include +#include +#include +#include +#include +#include #include +#include #include -#include -#include -#include +#if __has_include() +#include +#endif #include +#include +#include + +#include "llvm/Support/DataTypes.h" #include "swift/Basic/Lazy.h" #include "swift/Runtime/Config.h" +#include "swift/Runtime/Debug.h" +#include "swift/Runtime/Mutex.h" #include "../SwiftShims/LibcShims.h" -#include "llvm/Support/DataTypes.h" using namespace swift; @@ -171,7 +181,7 @@ int swift::_stdlib_ioctlPtr(int fd, unsigned long int request, void* ptr) { #if defined(__FreeBSD__) SWIFT_RUNTIME_STDLIB_INTERNAL -char * _Nullable *swift::_stdlib_getEnviron() { +char * _Nullable * _Null_unspecified swift::_stdlib_getEnviron() { extern char **environ; return environ; } @@ -308,3 +318,76 @@ swift::_stdlib_cxx11_mt19937_uniform(__swift_uint32_t upper_bound) { std::uniform_int_distribution<__swift_uint32_t> RandomUniform(0, upper_bound); return RandomUniform(getGlobalMT19937()); } + +// _stdlib_random +// +// Should the implementation of this function add a new platform/change for a +// platform, make sure to also update the documentation regarding platform +// implementation of this function. +// This can be found at: /docs/Random.md + +#if defined(__APPLE__) + +SWIFT_RUNTIME_STDLIB_INTERNAL +void swift::_stdlib_random(void *buf, __swift_size_t nbytes) { + arc4random_buf(buf, nbytes); +} + +#elif defined(_WIN32) && !defined(__CYGWIN__) +#warning TODO: Test _stdlib_random on Windows + +SWIFT_RUNTIME_STDLIB_INTERNAL +void swift::_stdlib_random(void *buf, __swift_size_t nbytes) { + NTSTATUS status = BCryptGenRandom(nullptr, + static_cast(buf), + static_cast(nbytes), + BCRYPT_USE_SYSTEM_PREFERRED_RNG); + if (!BCRYPT_SUCCESS(status)) { + fatalError(0, "Fatal error: 0x%.8X in '%s'\n", status, __func__); + } +} + +#else + +#undef WHILE_EINTR +#define WHILE_EINTR(expression) ({ \ + decltype(expression) result = -1; \ + do { result = (expression); } while (result == -1 && errno == EINTR); \ + result; \ +}) + +SWIFT_RUNTIME_STDLIB_INTERNAL +void swift::_stdlib_random(void *buf, __swift_size_t nbytes) { + while (nbytes > 0) { + __swift_ssize_t actual_nbytes = -1; +#if defined(GRND_RANDOM) + static const bool getrandom_available = + !(getrandom(nullptr, 0, 0) == -1 && errno == ENOSYS); + if (getrandom_available) { + actual_nbytes = WHILE_EINTR(getrandom(buf, nbytes, 0)); + } +#elif defined(__Fuchsia__) + __swift_size_t getentropy_nbytes = std::min(nbytes, __swift_size_t{256}); + if (0 == getentropy(buf, getentropy_nbytes)) { + actual_nbytes = getentropy_nbytes; + } +#endif + if (actual_nbytes == -1) { + static const int fd = + WHILE_EINTR(_stdlib_open("/dev/urandom", O_RDONLY | O_CLOEXEC, 0)); + if (fd != -1) { + static StaticMutex mutex; + mutex.withLock([&] { + actual_nbytes = WHILE_EINTR(_stdlib_read(fd, buf, nbytes)); + }); + } + } + if (actual_nbytes == -1) { + fatalError(0, "Fatal error: %d in '%s'\n", errno, __func__); + } + buf = static_cast(buf) + actual_nbytes; + nbytes -= actual_nbytes; + } +} + +#endif diff --git a/stdlib/public/stubs/MathStubs.cpp b/stdlib/public/stubs/MathStubs.cpp new file mode 100644 index 0000000000000..9f8de5571ac17 --- /dev/null +++ b/stdlib/public/stubs/MathStubs.cpp @@ -0,0 +1,426 @@ +//===--- MathStubs.cpp - Swift Language Runtime Stubs ---------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Math stubs for functions which should be defined in the core standard +// library, but are difficult or impossible to write in Swift at the +// moment. +// +//===----------------------------------------------------------------------===// + +#include "../SwiftShims/Visibility.h" + +#include +#include + +#if __has_attribute(__mode__) +#define SWIFT_MODE_DI __attribute__((__mode__(DI))) +#define SWIFT_MODE_TI __attribute__((__mode__(TI))) +#else +#define SWIFT_MODE_DI +#define SWIFT_MODE_TI +#endif + +typedef int si_int; +typedef int di_int SWIFT_MODE_DI; +typedef int ti_int SWIFT_MODE_TI; + +typedef unsigned su_int; +typedef unsigned du_int SWIFT_MODE_DI; +typedef unsigned tu_int SWIFT_MODE_TI; + +typedef union +{ + tu_int all; + struct + { +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + du_int low; + du_int high; +#else + du_int high; + du_int low; +#endif /* __BYTE_ORDER__ == __LITTLE_ENDIAN__ */ + }s; +} utwords; + +extern "C" { + +// Although this builtin is provided by clang rt builtins, +// it isn't provided by libgcc, which is the default +// runtime library on Linux, even when compiling with clang. +// This implementation is copied here to avoid a new dependency +// on compiler-rt on Linux. +// FIXME: rdar://14883575 Libcompiler_rt omits muloti4 +#if (defined(__linux__) && defined(__x86_64__)) || \ + (defined(__linux__) && defined(__aarch64__)) || \ + (defined(__linux__) && defined(__powerpc64__)) || \ + (defined(__linux__) && defined(__s390x__)) || \ + (defined(__ANDROID__) && defined(__arm64__)) + +SWIFT_RUNTIME_STDLIB_INTERFACE +ti_int +__muloti4(ti_int a, ti_int b, int* overflow) +{ + const int N = (int)(sizeof(ti_int) * CHAR_BIT); + const ti_int MIN = (ti_int)1 << (N-1); + const ti_int MAX = ~MIN; + *overflow = 0; + ti_int result = a * b; + if (a == MIN) + { + if (b != 0 && b != 1) + *overflow = 1; + return result; + } + if (b == MIN) + { + if (a != 0 && a != 1) + *overflow = 1; + return result; + } + ti_int sa = a >> (N - 1); + ti_int abs_a = (a ^ sa) - sa; + ti_int sb = b >> (N - 1); + ti_int abs_b = (b ^ sb) - sb; + if (abs_a < 2 || abs_b < 2) + return result; + if (sa == sb) + { + if (abs_a > MAX / abs_b) + *overflow = 1; + } + else + { + if (abs_a > MIN / -abs_b) + *overflow = 1; + } + return result; +} + +#endif + +// FIXME: ideally we would have a slow path here for Windows which would be +// lowered to instructions as though MSVC had generated. There does not seem to +// be a MSVC provided multiply with overflow detection that I can see, but this +// avoids an unnecessary dependency on compiler-rt for a single function. +#if (defined(__linux__) && defined(__arm__)) || defined(_WIN32) + +// Similar to above, but with mulodi4. Perhaps this is +// something that shouldn't be done, and is a bandaid over +// some other lower-level architecture issue that I'm +// missing. Perhaps relevant bug report: +// FIXME: https://llvm.org/bugs/show_bug.cgi?id=14469 + +SWIFT_RUNTIME_STDLIB_INTERFACE +di_int +__mulodi4(di_int a, di_int b, int* overflow) +{ + const int N = (int)(sizeof(di_int) * CHAR_BIT); + const di_int MIN = (di_int)1 << (N-1); + const di_int MAX = ~MIN; + *overflow = 0; + di_int result = a * b; + if (a == MIN) + { + if (b != 0 && b != 1) + *overflow = 1; + return result; + } + if (b == MIN) + { + if (a != 0 && a != 1) + *overflow = 1; + return result; + } + di_int sa = a >> (N - 1); + di_int abs_a = (a ^ sa) - sa; + di_int sb = b >> (N - 1); + di_int abs_b = (b ^ sb) - sb; + if (abs_a < 2 || abs_b < 2) + return result; + if (sa == sb) + { + if (abs_a > MAX / abs_b) + *overflow = 1; + } + else + { + if (abs_a > MIN / -abs_b) + *overflow = 1; + } + return result; +} + +#endif + +#if defined(_WIN32) + +tu_int +__udivmodti4(tu_int a, tu_int b, tu_int* rem) +{ + const unsigned n_udword_bits = sizeof(du_int) * CHAR_BIT; + const unsigned n_utword_bits = sizeof(tu_int) * CHAR_BIT; + utwords n; + n.all = a; + utwords d; + d.all = b; + utwords q; + utwords r; + unsigned sr; + /* special cases, X is unknown, K != 0 */ + if (n.s.high == 0) + { + if (d.s.high == 0) + { + /* 0 X + * --- + * 0 X + */ + if (rem) + *rem = n.s.low % d.s.low; + return n.s.low / d.s.low; + } + /* 0 X + * --- + * K X + */ + if (rem) + *rem = n.s.low; + return 0; + } + /* n.s.high != 0 */ + if (d.s.low == 0) + { + if (d.s.high == 0) + { + /* K X + * --- + * 0 0 + */ + if (rem) + *rem = n.s.high % d.s.low; + return n.s.high / d.s.low; + } + /* d.s.high != 0 */ + if (n.s.low == 0) + { + /* K 0 + * --- + * K 0 + */ + if (rem) + { + r.s.high = n.s.high % d.s.high; + r.s.low = 0; + *rem = r.all; + } + return n.s.high / d.s.high; + } + /* K K + * --- + * K 0 + */ + if ((d.s.high & (d.s.high - 1)) == 0) /* if d is a power of 2 */ + { + if (rem) + { + r.s.low = n.s.low; + r.s.high = n.s.high & (d.s.high - 1); + *rem = r.all; + } + return n.s.high >> __builtin_ctzll(d.s.high); + } + /* K K + * --- + * K 0 + */ + sr = __builtin_clzll(d.s.high) - __builtin_clzll(n.s.high); + /* 0 <= sr <= n_udword_bits - 2 or sr large */ + if (sr > n_udword_bits - 2) + { + if (rem) + *rem = n.all; + return 0; + } + ++sr; + /* 1 <= sr <= n_udword_bits - 1 */ + /* q.all = n.all << (n_utword_bits - sr); */ + q.s.low = 0; + q.s.high = n.s.low << (n_udword_bits - sr); + /* r.all = n.all >> sr; */ + r.s.high = n.s.high >> sr; + r.s.low = (n.s.high << (n_udword_bits - sr)) | (n.s.low >> sr); + } + else /* d.s.low != 0 */ + { + if (d.s.high == 0) + { + /* K X + * --- + * 0 K + */ + if ((d.s.low & (d.s.low - 1)) == 0) /* if d is a power of 2 */ + { + if (rem) + *rem = n.s.low & (d.s.low - 1); + if (d.s.low == 1) + return n.all; + sr = __builtin_ctzll(d.s.low); + q.s.high = n.s.high >> sr; + q.s.low = (n.s.high << (n_udword_bits - sr)) | (n.s.low >> sr); + return q.all; + } + /* K X + * --- + * 0 K + */ + sr = 1 + n_udword_bits + __builtin_clzll(d.s.low) + - __builtin_clzll(n.s.high); + /* 2 <= sr <= n_utword_bits - 1 + * q.all = n.all << (n_utword_bits - sr); + * r.all = n.all >> sr; + */ + if (sr == n_udword_bits) + { + q.s.low = 0; + q.s.high = n.s.low; + r.s.high = 0; + r.s.low = n.s.high; + } + else if (sr < n_udword_bits) // 2 <= sr <= n_udword_bits - 1 + { + q.s.low = 0; + q.s.high = n.s.low << (n_udword_bits - sr); + r.s.high = n.s.high >> sr; + r.s.low = (n.s.high << (n_udword_bits - sr)) | (n.s.low >> sr); + } + else // n_udword_bits + 1 <= sr <= n_utword_bits - 1 + { + q.s.low = n.s.low << (n_utword_bits - sr); + q.s.high = (n.s.high << (n_utword_bits - sr)) | + (n.s.low >> (sr - n_udword_bits)); + r.s.high = 0; + r.s.low = n.s.high >> (sr - n_udword_bits); + } + } + else + { + /* K X + * --- + * K K + */ + sr = __builtin_clzll(d.s.high) - __builtin_clzll(n.s.high); + /*0 <= sr <= n_udword_bits - 1 or sr large */ + if (sr > n_udword_bits - 1) + { + if (rem) + *rem = n.all; + return 0; + } + ++sr; + /* 1 <= sr <= n_udword_bits + * q.all = n.all << (n_utword_bits - sr); + * r.all = n.all >> sr; + */ + q.s.low = 0; + if (sr == n_udword_bits) + { + q.s.high = n.s.low; + r.s.high = 0; + r.s.low = n.s.high; + } + else + { + r.s.high = n.s.high >> sr; + r.s.low = (n.s.high << (n_udword_bits - sr)) | (n.s.low >> sr); + q.s.high = n.s.low << (n_udword_bits - sr); + } + } + } + /* Not a special case + * q and r are initialized with: + * q.all = n.all << (n_utword_bits - sr); + * r.all = n.all >> sr; + * 1 <= sr <= n_utword_bits - 1 + */ + su_int carry = 0; + for (; sr > 0; --sr) + { + /* r:q = ((r:q) << 1) | carry */ + r.s.high = (r.s.high << 1) | (r.s.low >> (n_udword_bits - 1)); + r.s.low = (r.s.low << 1) | (q.s.high >> (n_udword_bits - 1)); + q.s.high = (q.s.high << 1) | (q.s.low >> (n_udword_bits - 1)); + q.s.low = (q.s.low << 1) | carry; + /* carry = 0; + * if (r.all >= d.all) + * { + * r.all -= d.all; + * carry = 1; + * } + */ + const ti_int s = (ti_int)(d.all - r.all - 1) >> (n_utword_bits - 1); + carry = s & 1; + r.all -= d.all & s; + } + q.all = (q.all << 1) | carry; + if (rem) + *rem = r.all; + return q.all; +} + +SWIFT_RUNTIME_STDLIB_INTERFACE +tu_int +__udivti3(tu_int a, tu_int b) +{ + return __udivmodti4(a, b, NULL); +} + +SWIFT_RUNTIME_STDLIB_INTERFACE +tu_int +__umodti3(tu_int a, tu_int b) +{ + tu_int r; + __udivmodti4(a, b, &r); + return r; +} + +SWIFT_RUNTIME_STDLIB_INTERFACE +ti_int +__divti3(ti_int a, ti_int b) +{ + const int bits_in_tword_m1 = (int)(sizeof(ti_int) * CHAR_BIT) - 1; + ti_int s_a = a >> bits_in_tword_m1; /* s_a = a < 0 ? -1 : 0 */ + ti_int s_b = b >> bits_in_tword_m1; /* s_b = b < 0 ? -1 : 0 */ + a = (a ^ s_a) - s_a; /* negate if s_a == -1 */ + b = (b ^ s_b) - s_b; /* negate if s_b == -1 */ + s_a ^= s_b; /* sign of quotient */ + return (__udivmodti4(a, b, (tu_int*)0) ^ s_a) - s_a; /* negate if s_a == -1 */ +} + +SWIFT_RUNTIME_STDLIB_INTERFACE +ti_int +__modti3(ti_int a, ti_int b) +{ + const int bits_in_tword_m1 = (int)(sizeof(ti_int) * CHAR_BIT) - 1; + ti_int s = b >> bits_in_tword_m1; /* s = b < 0 ? -1 : 0 */ + b = (b ^ s) - s; /* negate if s == -1 */ + s = a >> bits_in_tword_m1; /* s = a < 0 ? -1 : 0 */ + a = (a ^ s) - s; /* negate if s == -1 */ + tu_int r; + __udivmodti4(a, b, &r); + return ((ti_int)r ^ s) - s; /* negate if s == -1 */ +} + +#endif + +} + diff --git a/stdlib/public/stubs/Stubs.cpp b/stdlib/public/stubs/Stubs.cpp index 19acbce0bbd5a..262409804bcb4 100644 --- a/stdlib/public/stubs/Stubs.cpp +++ b/stdlib/public/stubs/Stubs.cpp @@ -45,25 +45,28 @@ #include #include #elif defined(__ANDROID__) -// Android's libc implementation Bionic currently only supports the "C" locale -// (https://android.googlesource.com/platform/bionic/+/ndk-r11c/libc/bionic/locale.cpp#40). -// As such, we have no choice but to map functions like strtod_l, which should -// respect the given locale_t parameter, to functions like strtod, which do not. #include + +#include + +#if __ANDROID_API__ < 21 // Introduced in Android API 21 - L +static inline long double swift_strtold_l(const char *nptr, char **endptr, + locale_t) { + return strtod(nptr, endptr); +} +#define strtold_l swift_strtold_l +#endif + +#if __ANDROID_API__ < 26 // Introduced in Android API 26 - O static double swift_strtod_l(const char *nptr, char **endptr, locale_t loc) { return strtod(nptr, endptr); } static float swift_strtof_l(const char *nptr, char **endptr, locale_t loc) { return strtof(nptr, endptr); } -static long double swift_strtold_l(const char *nptr, - char **endptr, - locale_t loc) { - return strtod(nptr, endptr); -} #define strtod_l swift_strtod_l #define strtof_l swift_strtof_l -#define strtold_l swift_strtold_l +#endif #elif defined(__linux__) #include #else @@ -168,10 +171,11 @@ static locale_t getCLocale() { } #endif +#if !SWIFT_DTOA_FLOAT80_SUPPORT #if defined(__APPLE__) #define swift_snprintf_l snprintf_l #elif defined(__CYGWIN__) || defined(_WIN32) || defined(__HAIKU__) -// In Cygwin, swift_snprintf_l() is not used. +// swift_snprintf_l() is not used. #else static int swift_snprintf_l(char *Str, size_t StrSize, locale_t Locale, const char *Format, ...) { @@ -191,7 +195,6 @@ static int swift_snprintf_l(char *Str, size_t StrSize, locale_t Locale, } #endif -#if !SWIFT_DTOA_FLOAT80_SUPPORT template static uint64_t swift_floatingPointToString(char *Buffer, size_t BufferLength, T Value, const char *Format, @@ -330,120 +333,6 @@ swift::swift_stdlib_readLine_stdin(unsigned char **LinePtr) { #endif } - -// Although this builtin is provided by clang rt builtins, -// it isn't provided by libgcc, which is the default -// runtime library on Linux, even when compiling with clang. -// This implementation is copied here to avoid a new dependency -// on compiler-rt on Linux. -// FIXME: rdar://14883575 Libcompiler_rt omits muloti4 -#if (defined(__linux__) && defined(__x86_64__)) || \ - (defined(__linux__) && defined(__aarch64__)) || \ - (defined(__linux__) && defined(__powerpc64__)) || \ - (defined(__linux__) && defined(__s390x__)) || \ - (defined(__ANDROID__) && defined(__arm64__)) - -typedef int ti_int __attribute__((__mode__(TI))); -SWIFT_RUNTIME_STDLIB_INTERFACE -ti_int -__muloti4(ti_int a, ti_int b, int* overflow) -{ - const int N = (int)(sizeof(ti_int) * CHAR_BIT); - const ti_int MIN = (ti_int)1 << (N-1); - const ti_int MAX = ~MIN; - *overflow = 0; - ti_int result = a * b; - if (a == MIN) - { - if (b != 0 && b != 1) - *overflow = 1; - return result; - } - if (b == MIN) - { - if (a != 0 && a != 1) - *overflow = 1; - return result; - } - ti_int sa = a >> (N - 1); - ti_int abs_a = (a ^ sa) - sa; - ti_int sb = b >> (N - 1); - ti_int abs_b = (b ^ sb) - sb; - if (abs_a < 2 || abs_b < 2) - return result; - if (sa == sb) - { - if (abs_a > MAX / abs_b) - *overflow = 1; - } - else - { - if (abs_a > MIN / -abs_b) - *overflow = 1; - } - return result; -} - -#endif - -// FIXME: ideally we would have a slow path here for Windows which would be -// lowered to instructions as though MSVC had generated. There does not seem to -// be a MSVC provided multiply with overflow detection that I can see, but this -// avoids an unnecessary dependency on compiler-rt for a single function. -#if (defined(__linux__) && defined(__arm__)) || defined(_WIN32) -// Similar to above, but with mulodi4. Perhaps this is -// something that shouldn't be done, and is a bandaid over -// some other lower-level architecture issue that I'm -// missing. Perhaps relevant bug report: -// FIXME: https://llvm.org/bugs/show_bug.cgi?id=14469 -#if __has_attribute(__mode__) -#define SWIFT_MODE_DI __attribute__((__mode__(DI))) -#else -#define SWIFT_MODE_DI -#endif - -typedef int di_int SWIFT_MODE_DI; -SWIFT_RUNTIME_STDLIB_INTERFACE -di_int -__mulodi4(di_int a, di_int b, int* overflow) -{ - const int N = (int)(sizeof(di_int) * CHAR_BIT); - const di_int MIN = (di_int)1 << (N-1); - const di_int MAX = ~MIN; - *overflow = 0; - di_int result = a * b; - if (a == MIN) - { - if (b != 0 && b != 1) - *overflow = 1; - return result; - } - if (b == MIN) - { - if (a != 0 && a != 1) - *overflow = 1; - return result; - } - di_int sa = a >> (N - 1); - di_int abs_a = (a ^ sa) - sa; - di_int sb = b >> (N - 1); - di_int abs_b = (b ^ sb) - sb; - if (abs_a < 2 || abs_b < 2) - return result; - if (sa == sb) - { - if (abs_a > MAX / abs_b) - *overflow = 1; - } - else - { - if (abs_a > MIN / -abs_b) - *overflow = 1; - } - return result; -} -#endif - #if defined(__CYGWIN__) || defined(_WIN32) #define strcasecmp _stricmp #endif @@ -473,9 +362,9 @@ static const char *_swift_stdlib_strtoX_clocale_impl( ValueStream >> ParsedValue; *outResult = ParsedValue; - int pos = ValueStream.tellg(); + std::streamoff pos = ValueStream.tellg(); if (ValueStream.eof()) - pos = strlen(nptr); + pos = static_cast(strlen(nptr)); if (pos <= 0) return nullptr; diff --git a/stdlib/public/stubs/SwiftNativeNSXXXBaseARC.m b/stdlib/public/stubs/SwiftNativeNSXXXBaseARC.m index c33dce43d86bf..f92d837bce7f7 100644 --- a/stdlib/public/stubs/SwiftNativeNSXXXBaseARC.m +++ b/stdlib/public/stubs/SwiftNativeNSXXXBaseARC.m @@ -82,4 +82,8 @@ size_t swift_stdlib_NSStringHashValue(NSString *SWIFT_NS_RELEASES_ARGUMENT str, } } +#else + +extern char ignore_pedantic_warning; + #endif diff --git a/test/APINotes/Inputs/custom-modules/ObsoletedAPINotesTest.apinotes b/test/APINotes/Inputs/custom-modules/ObsoletedAPINotesTest.apinotes new file mode 100644 index 0000000000000..85798d425017e --- /dev/null +++ b/test/APINotes/Inputs/custom-modules/ObsoletedAPINotesTest.apinotes @@ -0,0 +1,14 @@ +Name: M +Classes: +- Name: FooID + SwiftName: Foo_ID +- Name: BarSub + SwiftName: BarContainerCanonical.Sub + +SwiftVersions: +- Version: 4 + Classes: + - Name: FooID + SwiftName: FooID + - Name: BarSub + SwiftName: BarContainerOld.Sub diff --git a/test/APINotes/Inputs/custom-modules/ObsoletedAPINotesTest.h b/test/APINotes/Inputs/custom-modules/ObsoletedAPINotesTest.h new file mode 100644 index 0000000000000..7cd99d5d12b69 --- /dev/null +++ b/test/APINotes/Inputs/custom-modules/ObsoletedAPINotesTest.h @@ -0,0 +1,10 @@ +@import Foundation; +@interface FooID: NSObject +@end + +@interface BarContainerOld +@end +@interface BarContainerCanonical +@end +@interface BarSub +@end diff --git a/test/APINotes/Inputs/custom-modules/module.modulemap b/test/APINotes/Inputs/custom-modules/module.modulemap index afd875a4681fd..82aa8ccf3f54f 100644 --- a/test/APINotes/Inputs/custom-modules/module.modulemap +++ b/test/APINotes/Inputs/custom-modules/module.modulemap @@ -1,3 +1,6 @@ module APINotesTest { header "APINotesTest.h" } +module ObsoletedAPINotesTest { + header "ObsoletedAPINotesTest.h" +} diff --git a/test/APINotes/obsoleted.swift b/test/APINotes/obsoleted.swift new file mode 100644 index 0000000000000..e081c5c8737c5 --- /dev/null +++ b/test/APINotes/obsoleted.swift @@ -0,0 +1,11 @@ +// RUN: not %target-swift-frontend -typecheck -verify -I %S/Inputs/custom-modules -F %S/Inputs/custom-frameworks -swift-version 3 %s +// RUN: %target-swift-frontend -typecheck -verify -I %S/Inputs/custom-modules -F %S/Inputs/custom-frameworks -swift-version 4.2 %s +// RUN: %target-swift-frontend -typecheck -verify -I %S/Inputs/custom-modules -F %S/Inputs/custom-frameworks -swift-version 5 %s +// REQUIRES: objc_interop +import ObsoletedAPINotesTest + +let _: FooID // expected-error{{'FooID' has been renamed to 'Foo_ID'}} +let _: Foo_ID + +let _: BarContainerOld.Sub // expected-error{{'Sub' has been renamed to 'BarContainerCanonical.Sub'}} +let _: BarContainerCanonical.Sub diff --git a/test/APINotes/versioned-multi.swift b/test/APINotes/versioned-multi.swift index 07bb138ceefcc..c7e79f6e8c1ee 100644 --- a/test/APINotes/versioned-multi.swift +++ b/test/APINotes/versioned-multi.swift @@ -308,69 +308,69 @@ // CHECK-SWIFT-5: var multiVersionedGlobal4: Int32 -// CHECK-SWIFT-5: @available(swift, obsoleted: 5, renamed: "multiVersionedGlobal4") +// CHECK-SWIFT-5: @available(swift, obsoleted: 4.2, renamed: "multiVersionedGlobal4") // CHECK-SWIFT-5: var multiVersionedGlobal4_4: Int32 // CHECK-SWIFT-5: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal4Notes_NEW") // CHECK-SWIFT-5: var multiVersionedGlobal4Notes: Int32 -// CHECK-SWIFT-5: @available(swift, obsoleted: 5, renamed: "multiVersionedGlobal4Notes_NEW") +// CHECK-SWIFT-5: @available(swift, obsoleted: 4.2, renamed: "multiVersionedGlobal4Notes_NEW") // CHECK-SWIFT-5: var multiVersionedGlobal4Notes_4: Int32 // CHECK-SWIFT-5: var multiVersionedGlobal4Notes_NEW: Int32 // CHECK-SWIFT-5: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal4Header_NEW") // CHECK-SWIFT-5: var multiVersionedGlobal4Header: Int32 -// CHECK-SWIFT-5: @available(swift, obsoleted: 5, renamed: "multiVersionedGlobal4Header_NEW") +// CHECK-SWIFT-5: @available(swift, obsoleted: 4.2, renamed: "multiVersionedGlobal4Header_NEW") // CHECK-SWIFT-5: var multiVersionedGlobal4Header_4: Int32 // CHECK-SWIFT-5: var multiVersionedGlobal4Header_NEW: Int32 // CHECK-SWIFT-5: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal4Both_NEW") // CHECK-SWIFT-5: var multiVersionedGlobal4Both: Int32 -// CHECK-SWIFT-5: @available(swift, obsoleted: 5, renamed: "multiVersionedGlobal4Both_NEW") +// CHECK-SWIFT-5: @available(swift, obsoleted: 4.2, renamed: "multiVersionedGlobal4Both_NEW") // CHECK-SWIFT-5: var multiVersionedGlobal4Both_4: Int32 // CHECK-SWIFT-5: var multiVersionedGlobal4Both_NEW: Int32 // CHECK-SWIFT-5: var multiVersionedGlobal34: Int32 // CHECK-SWIFT-5: @available(swift, obsoleted: 4, renamed: "multiVersionedGlobal34") // CHECK-SWIFT-5: var multiVersionedGlobal34_3: Int32 -// CHECK-SWIFT-5: @available(swift, obsoleted: 5, renamed: "multiVersionedGlobal34") +// CHECK-SWIFT-5: @available(swift, obsoleted: 4.2, renamed: "multiVersionedGlobal34") // CHECK-SWIFT-5: var multiVersionedGlobal34_4: Int32 // CHECK-SWIFT-5: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal34Notes_NEW") // CHECK-SWIFT-5: var multiVersionedGlobal34Notes: Int32 // CHECK-SWIFT-5: @available(swift, obsoleted: 4, renamed: "multiVersionedGlobal34Notes_NEW") // CHECK-SWIFT-5: var multiVersionedGlobal34Notes_3: Int32 -// CHECK-SWIFT-5: @available(swift, obsoleted: 5, renamed: "multiVersionedGlobal34Notes_NEW") +// CHECK-SWIFT-5: @available(swift, obsoleted: 4.2, renamed: "multiVersionedGlobal34Notes_NEW") // CHECK-SWIFT-5: var multiVersionedGlobal34Notes_4: Int32 // CHECK-SWIFT-5: var multiVersionedGlobal34Notes_NEW: Int32 // CHECK-SWIFT-5: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal34Header_NEW") // CHECK-SWIFT-5: var multiVersionedGlobal34Header: Int32 // CHECK-SWIFT-5: @available(swift, obsoleted: 4, renamed: "multiVersionedGlobal34Header_NEW") // CHECK-SWIFT-5: var multiVersionedGlobal34Header_3: Int32 -// CHECK-SWIFT-5: @available(swift, obsoleted: 5, renamed: "multiVersionedGlobal34Header_NEW") +// CHECK-SWIFT-5: @available(swift, obsoleted: 4.2, renamed: "multiVersionedGlobal34Header_NEW") // CHECK-SWIFT-5: var multiVersionedGlobal34Header_4: Int32 // CHECK-SWIFT-5: var multiVersionedGlobal34Header_NEW: Int32 // CHECK-SWIFT-5: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal34Both_NEW") // CHECK-SWIFT-5: var multiVersionedGlobal34Both: Int32 // CHECK-SWIFT-5: @available(swift, obsoleted: 4, renamed: "multiVersionedGlobal34Both_NEW") // CHECK-SWIFT-5: var multiVersionedGlobal34Both_3: Int32 -// CHECK-SWIFT-5: @available(swift, obsoleted: 5, renamed: "multiVersionedGlobal34Both_NEW") +// CHECK-SWIFT-5: @available(swift, obsoleted: 4.2, renamed: "multiVersionedGlobal34Both_NEW") // CHECK-SWIFT-5: var multiVersionedGlobal34Both_4: Int32 // CHECK-SWIFT-5: var multiVersionedGlobal34Both_NEW: Int32 // CHECK-SWIFT-5: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal45_5") // CHECK-SWIFT-5: var multiVersionedGlobal45: Int32 -// CHECK-SWIFT-5: @available(swift, obsoleted: 5, renamed: "multiVersionedGlobal45_5") +// CHECK-SWIFT-5: @available(swift, obsoleted: 4.2, renamed: "multiVersionedGlobal45_5") // CHECK-SWIFT-5: var multiVersionedGlobal45_4: Int32 // CHECK-SWIFT-5: var multiVersionedGlobal45_5: Int32 // CHECK-SWIFT-5: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal45Notes_5") // CHECK-SWIFT-5: var multiVersionedGlobal45Notes: Int32 -// CHECK-SWIFT-5: @available(swift, obsoleted: 5, renamed: "multiVersionedGlobal45Notes_5") +// CHECK-SWIFT-5: @available(swift, obsoleted: 4.2, renamed: "multiVersionedGlobal45Notes_5") // CHECK-SWIFT-5: var multiVersionedGlobal45Notes_4: Int32 // CHECK-SWIFT-5: var multiVersionedGlobal45Notes_5: Int32 // CHECK-SWIFT-5: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal45Header_5") // CHECK-SWIFT-5: var multiVersionedGlobal45Header: Int32 -// CHECK-SWIFT-5: @available(swift, obsoleted: 5, renamed: "multiVersionedGlobal45Header_5") +// CHECK-SWIFT-5: @available(swift, obsoleted: 4.2, renamed: "multiVersionedGlobal45Header_5") // CHECK-SWIFT-5: var multiVersionedGlobal45Header_4: Int32 // CHECK-SWIFT-5: var multiVersionedGlobal45Header_5: Int32 // CHECK-SWIFT-5: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal45Both_5") // CHECK-SWIFT-5: var multiVersionedGlobal45Both: Int32 -// CHECK-SWIFT-5: @available(swift, obsoleted: 5, renamed: "multiVersionedGlobal45Both_5") +// CHECK-SWIFT-5: @available(swift, obsoleted: 4.2, renamed: "multiVersionedGlobal45Both_5") // CHECK-SWIFT-5: var multiVersionedGlobal45Both_4: Int32 // CHECK-SWIFT-5: var multiVersionedGlobal45Both_5: Int32 @@ -378,28 +378,28 @@ // CHECK-SWIFT-5: var multiVersionedGlobal345: Int32 // CHECK-SWIFT-5: @available(swift, obsoleted: 4, renamed: "multiVersionedGlobal345_5") // CHECK-SWIFT-5: var multiVersionedGlobal345_3: Int32 -// CHECK-SWIFT-5: @available(swift, obsoleted: 5, renamed: "multiVersionedGlobal345_5") +// CHECK-SWIFT-5: @available(swift, obsoleted: 4.2, renamed: "multiVersionedGlobal345_5") // CHECK-SWIFT-5: var multiVersionedGlobal345_4: Int32 // CHECK-SWIFT-5: var multiVersionedGlobal345_5: Int32 // CHECK-SWIFT-5: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal345Notes_5") // CHECK-SWIFT-5: var multiVersionedGlobal345Notes: Int32 // CHECK-SWIFT-5: @available(swift, obsoleted: 4, renamed: "multiVersionedGlobal345Notes_5") // CHECK-SWIFT-5: var multiVersionedGlobal345Notes_3: Int32 -// CHECK-SWIFT-5: @available(swift, obsoleted: 5, renamed: "multiVersionedGlobal345Notes_5") +// CHECK-SWIFT-5: @available(swift, obsoleted: 4.2, renamed: "multiVersionedGlobal345Notes_5") // CHECK-SWIFT-5: var multiVersionedGlobal345Notes_4: Int32 // CHECK-SWIFT-5: var multiVersionedGlobal345Notes_5: Int32 // CHECK-SWIFT-5: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal345Header_5") // CHECK-SWIFT-5: var multiVersionedGlobal345Header: Int32 // CHECK-SWIFT-5: @available(swift, obsoleted: 4, renamed: "multiVersionedGlobal345Header_5") // CHECK-SWIFT-5: var multiVersionedGlobal345Header_3: Int32 -// CHECK-SWIFT-5: @available(swift, obsoleted: 5, renamed: "multiVersionedGlobal345Header_5") +// CHECK-SWIFT-5: @available(swift, obsoleted: 4.2, renamed: "multiVersionedGlobal345Header_5") // CHECK-SWIFT-5: var multiVersionedGlobal345Header_4: Int32 // CHECK-SWIFT-5: var multiVersionedGlobal345Header_5: Int32 // CHECK-SWIFT-5: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal345Both_5") // CHECK-SWIFT-5: var multiVersionedGlobal345Both: Int32 // CHECK-SWIFT-5: @available(swift, obsoleted: 4, renamed: "multiVersionedGlobal345Both_5") // CHECK-SWIFT-5: var multiVersionedGlobal345Both_3: Int32 -// CHECK-SWIFT-5: @available(swift, obsoleted: 5, renamed: "multiVersionedGlobal345Both_5") +// CHECK-SWIFT-5: @available(swift, obsoleted: 4.2, renamed: "multiVersionedGlobal345Both_5") // CHECK-SWIFT-5: var multiVersionedGlobal345Both_4: Int32 // CHECK-SWIFT-5: var multiVersionedGlobal345Both_5: Int32 // CHECK-SWIFT-5: @available(swift, obsoleted: 5, renamed: "multiVersionedGlobal34_4_2") diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 0aa6eb9153645..d0a1f250bbf6a 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -299,7 +299,9 @@ foreach(SDK ${SWIFT_SDKS}) set(test_mode_target_suffix "-${test_mode}") endif() - add_custom_target("check-swift${test_subset_target_suffix}${test_mode_target_suffix}${VARIANT_SUFFIX}" + set(test_target_name + "check-swift${test_subset_target_suffix}${test_mode_target_suffix}${VARIANT_SUFFIX}") + add_custom_target("${test_target_name}" ${command_upload_stdlib} ${command_upload_swift_reflection_test} ${command_clean_test_results_dir} @@ -313,7 +315,7 @@ foreach(SDK ${SWIFT_SDKS}) COMMENT "Running ${test_subset} Swift tests for ${VARIANT_TRIPLE}" USES_TERMINAL) - add_custom_target("check-swift${test_subset_target_suffix}${test_mode_target_suffix}${VARIANT_SUFFIX}-custom" + add_custom_target("${test_target_name}-custom" ${command_upload_stdlib} ${command_upload_swift_reflection_test} ${command_clean_test_results_dir} @@ -325,6 +327,8 @@ foreach(SDK ${SWIFT_SDKS}) DEPENDS ${dependencies} COMMENT "Running ${test_subset} Swift tests for ${VARIANT_TRIPLE} from custom test locations" USES_TERMINAL) + set_property(TARGET "${test_target_name}" "${test_target_name}-custom" + PROPERTY FOLDER "Tests/check-swift") endforeach() endforeach() endforeach() @@ -342,7 +346,11 @@ foreach(test_mode ${TEST_MODES}) set(test_subset_target_suffix "") endif() - add_custom_target(check-swift${test_subset_target_suffix}${test_mode_target_suffix} - DEPENDS "check-swift${test_subset_target_suffix}${test_mode_target_suffix}${SWIFT_PRIMARY_VARIANT_SUFFIX}") + set(test_target_name + "check-swift${test_subset_target_suffix}${test_mode_target_suffix}") + add_custom_target("${test_target_name}" + DEPENDS "${test_target_name}${SWIFT_PRIMARY_VARIANT_SUFFIX}") + set_property(TARGET "${test_target_name}" + PROPERTY FOLDER "Tests/check-swift") endforeach() endforeach() diff --git a/test/ClangImporter/CoreGraphics_test.swift b/test/ClangImporter/CoreGraphics_test.swift index b80cb60f1b5cd..e68d316d96af9 100644 --- a/test/ClangImporter/CoreGraphics_test.swift +++ b/test/ClangImporter/CoreGraphics_test.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -target x86_64-apple-macosx10.11 -module-name=cgtest -emit-ir -O %s | %FileCheck %s +// RUN: %target-swift-frontend -module-name=cgtest -emit-ir -O %s | %FileCheck %s // Test some imported CG APIs import CoreGraphics @@ -75,6 +75,7 @@ public func pdfOperations(_ context: CGContext) { // Test some more recently renamed APIs // CHECK-LABEL: define swiftcc void {{.*}}testColorRenames{{.*}} { +@available(macOS 10.11, *) public func testColorRenames(color: CGColor, intent: CGColorRenderingIntent) { let colorSpace = CGColorSpace(name: CGColorSpace.sRGB)! diff --git a/test/ClangImporter/CoreMIDI_test.swift b/test/ClangImporter/CoreMIDI_test.swift index da4aeafd20442..0d3cdcc044159 100644 --- a/test/ClangImporter/CoreMIDI_test.swift +++ b/test/ClangImporter/CoreMIDI_test.swift @@ -1,6 +1,4 @@ -// RUN: %target-typecheck-verify-swift %clang-importer-sdk - -// REQUIRES: objc_interop +// RUN: %target-typecheck-verify-swift %clang-importer-sdk -enable-objc-interop import CoreMIDI diff --git a/test/ClangImporter/Inputs/ImportAsMemberSwiftVersioned_a.swift b/test/ClangImporter/Inputs/ImportAsMemberSwiftVersioned_a.swift new file mode 100644 index 0000000000000..b801f3242b17c --- /dev/null +++ b/test/ClangImporter/Inputs/ImportAsMemberSwiftVersioned_a.swift @@ -0,0 +1,6 @@ +import Foundation +import ImportAsMember.Class + +@_versioned +internal func foo(_ x: IncompleteImportTargetName) -> Any { return x } + diff --git a/test/ClangImporter/Inputs/ImportAsMemberSwiftVersioned_b.swift b/test/ClangImporter/Inputs/ImportAsMemberSwiftVersioned_b.swift new file mode 100644 index 0000000000000..4ce24e2db58db --- /dev/null +++ b/test/ClangImporter/Inputs/ImportAsMemberSwiftVersioned_b.swift @@ -0,0 +1,6 @@ +import Foundation +import ImportAsMember.Class + +@inline(__always) public func call_foo() -> Any { + return foo("hello" as IncompleteImportTargetName) +} diff --git a/test/ClangImporter/Inputs/custom-modules/AvailabilityExtras.h b/test/ClangImporter/Inputs/custom-modules/AvailabilityExtras.h index b151a3b843fdc..eb2a7f21714a1 100644 --- a/test/ClangImporter/Inputs/custom-modules/AvailabilityExtras.h +++ b/test/ClangImporter/Inputs/custom-modules/AvailabilityExtras.h @@ -107,3 +107,18 @@ typedef NS_ENUM(NSInteger, NSEnumAddedCasesIn2017) { @property (class) int setterDeprecatedClass; + (void)setSetterDeprecatedClass:(int)setterDeprecated __attribute__((deprecated)); @end + + +@interface UnavailableAccessors: NSObject +@property NSInteger fullyUnavailable __attribute__((unavailable)); + +@property NSInteger getterUnavailable; +- (NSInteger)getterUnavailable __attribute__((unavailable)); +@property (class) NSInteger getterUnavailableClass; ++ (NSInteger)getterUnavailableClass __attribute__((unavailable)); + +@property NSInteger setterUnavailable; +- (void)setSetterUnavailable:(NSInteger)setterUnavailable __attribute__((unavailable)); +@property (class) NSInteger setterUnavailableClass; ++ (void)setSetterUnavailableClass:(NSInteger)setterUnavailable __attribute__((unavailable)); +@end diff --git a/test/ClangImporter/Inputs/custom-modules/ConditionallyFoo.h b/test/ClangImporter/Inputs/custom-modules/ConditionallyFoo.h new file mode 100644 index 0000000000000..598a0ba0adfef --- /dev/null +++ b/test/ClangImporter/Inputs/custom-modules/ConditionallyFoo.h @@ -0,0 +1,3 @@ +#ifdef WANT_FOO +int foo(); +#endif diff --git a/test/ClangImporter/Inputs/custom-modules/EnumExhaustivity.h b/test/ClangImporter/Inputs/custom-modules/EnumExhaustivity.h index 80e8912fbc0b0..b7d0f2271a940 100644 --- a/test/ClangImporter/Inputs/custom-modules/EnumExhaustivity.h +++ b/test/ClangImporter/Inputs/custom-modules/EnumExhaustivity.h @@ -41,3 +41,9 @@ enum ForwardDeclaredOnly { ForwardDeclaredOnlyA, ForwardDeclaredOnlyB }; + +enum __attribute__((enum_extensibility(closed))) UnavailableCases { + UnavailableCasesA, + UnavailableCasesB, + UnavailableCasesThisIsTheUnavailableOne __attribute__((availability(swift, unavailable))) +}; diff --git a/test/ClangImporter/Inputs/custom-modules/module.map b/test/ClangImporter/Inputs/custom-modules/module.map index f7c389a667db2..1f4c987202b11 100644 --- a/test/ClangImporter/Inputs/custom-modules/module.map +++ b/test/ClangImporter/Inputs/custom-modules/module.map @@ -212,3 +212,8 @@ module Warnings6 { header "Warnings6.h" } module Warnings7 { header "Warnings7.h" } module Warnings8 { header "Warnings8.h" } module Warnings9 { header "Warnings9.h" } + +module ConditionallyFoo { + header "ConditionallyFoo.h" + config_macros [exhaustive] WANT_FOO +} \ No newline at end of file diff --git a/test/ClangImporter/Inputs/diags_from_header.h b/test/ClangImporter/Inputs/diags_from_header.h new file mode 100644 index 0000000000000..04a7d4a4dab86 --- /dev/null +++ b/test/ClangImporter/Inputs/diags_from_header.h @@ -0,0 +1,2 @@ +#warning "here is some warning about something" +#error "but this one is an error" diff --git a/test/ClangImporter/Inputs/no-fake-source-buffer-excerpts.h b/test/ClangImporter/Inputs/no-fake-source-buffer-excerpts.h new file mode 100644 index 0000000000000..5254c4049a456 --- /dev/null +++ b/test/ClangImporter/Inputs/no-fake-source-buffer-excerpts.h @@ -0,0 +1,2 @@ +#define WANT_FOO +@import ConditionallyFoo; diff --git a/test/ClangImporter/Inputs/pch-bridging-header-with-another-bridging-header/app.h b/test/ClangImporter/Inputs/pch-bridging-header-with-another-bridging-header/app.h new file mode 100644 index 0000000000000..154f4efc33dbe --- /dev/null +++ b/test/ClangImporter/Inputs/pch-bridging-header-with-another-bridging-header/app.h @@ -0,0 +1,5 @@ +#include "has_warning.h" + +static inline int app_function(int x) { + return x + 27; +} diff --git a/test/ClangImporter/Inputs/pch-bridging-header-with-another-bridging-header/has_warning.h b/test/ClangImporter/Inputs/pch-bridging-header-with-another-bridging-header/has_warning.h new file mode 100644 index 0000000000000..b516ee9787875 --- /dev/null +++ b/test/ClangImporter/Inputs/pch-bridging-header-with-another-bridging-header/has_warning.h @@ -0,0 +1,6 @@ +#ifndef HAS_WARNING_H +#define HAS_WARNING_H + +#warning "warning in bridging header" + +#endif diff --git a/test/ClangImporter/Inputs/pch-bridging-header-with-another-bridging-header/unit-tests.h b/test/ClangImporter/Inputs/pch-bridging-header-with-another-bridging-header/unit-tests.h new file mode 100644 index 0000000000000..a78913e98ad8e --- /dev/null +++ b/test/ClangImporter/Inputs/pch-bridging-header-with-another-bridging-header/unit-tests.h @@ -0,0 +1 @@ +#include "has_warning.h" diff --git a/test/ClangImporter/Inputs/privateframeworks/withprivate-autolink/SomeKit.framework/Headers/SKWidget.h b/test/ClangImporter/Inputs/privateframeworks/withprivate-autolink/SomeKit.framework/Headers/SKWidget.h new file mode 100644 index 0000000000000..dd99ff7ed2fc3 --- /dev/null +++ b/test/ClangImporter/Inputs/privateframeworks/withprivate-autolink/SomeKit.framework/Headers/SKWidget.h @@ -0,0 +1 @@ +#import diff --git a/test/ClangImporter/Inputs/privateframeworks/withprivate-autolink/SomeKit.framework/Headers/SomeKit.h b/test/ClangImporter/Inputs/privateframeworks/withprivate-autolink/SomeKit.framework/Headers/SomeKit.h new file mode 100644 index 0000000000000..17a4cc83fdf0f --- /dev/null +++ b/test/ClangImporter/Inputs/privateframeworks/withprivate-autolink/SomeKit.framework/Headers/SomeKit.h @@ -0,0 +1 @@ +#import diff --git a/test/ClangImporter/Inputs/privateframeworks/withprivate-autolink/SomeKit.framework/Modules/module.modulemap b/test/ClangImporter/Inputs/privateframeworks/withprivate-autolink/SomeKit.framework/Modules/module.modulemap new file mode 100644 index 0000000000000..ae4b276d47b35 --- /dev/null +++ b/test/ClangImporter/Inputs/privateframeworks/withprivate-autolink/SomeKit.framework/Modules/module.modulemap @@ -0,0 +1,6 @@ +framework module SomeKit { + umbrella header "SomeKit.h" + module * { + export * + } +} diff --git a/test/ClangImporter/Inputs/privateframeworks/withprivate-autolink/SomeKit.framework/SomeKit.tbd b/test/ClangImporter/Inputs/privateframeworks/withprivate-autolink/SomeKit.framework/SomeKit.tbd new file mode 100644 index 0000000000000..fcb8b1047f348 --- /dev/null +++ b/test/ClangImporter/Inputs/privateframeworks/withprivate-autolink/SomeKit.framework/SomeKit.tbd @@ -0,0 +1 @@ +// dummy file to trigger autolink diff --git a/test/ClangImporter/Inputs/privateframeworks/withprivate-autolink/SomeKitCore.framework/Headers/SKWidget.h b/test/ClangImporter/Inputs/privateframeworks/withprivate-autolink/SomeKitCore.framework/Headers/SKWidget.h new file mode 100644 index 0000000000000..0370e85acd381 --- /dev/null +++ b/test/ClangImporter/Inputs/privateframeworks/withprivate-autolink/SomeKitCore.framework/Headers/SKWidget.h @@ -0,0 +1,27 @@ +@import ObjectiveC; +@import Foundation; + +@interface SKWidget : NSObject +- (void)someObjCMethod; +@end + +@interface SKWidget(ObjCAPI) +- (void)someObjCExtensionMethod; +@property (readwrite,strong,nonnull) NSObject *anObject; +@end + +@interface NSObject (SKWidget) +- (void)doSomethingWithWidget:(nonnull SKWidget *)widget; +@end + +extern NSString * _Nonnull const SKWidgetErrorDomain; +typedef enum __attribute__((ns_error_domain(SKWidgetErrorDomain))) __attribute__((swift_name("SKWidget.Error"))) SKWidgetErrorCode : NSInteger { + SKWidgetErrorNone = 0, + SKWidgetErrorBoom = 1 +} SKWidgetErrorCode; + +@interface SKWidget(Erroneous) +- (SKWidgetErrorCode)getCurrentError; +@end + +extern void someKitGlobalFunc(void); diff --git a/test/ClangImporter/Inputs/privateframeworks/withprivate-autolink/SomeKitCore.framework/Headers/SomeKitCore.h b/test/ClangImporter/Inputs/privateframeworks/withprivate-autolink/SomeKitCore.framework/Headers/SomeKitCore.h new file mode 100644 index 0000000000000..dd99ff7ed2fc3 --- /dev/null +++ b/test/ClangImporter/Inputs/privateframeworks/withprivate-autolink/SomeKitCore.framework/Headers/SomeKitCore.h @@ -0,0 +1 @@ +#import diff --git a/test/ClangImporter/Inputs/privateframeworks/withprivate-autolink/SomeKitCore.framework/Modules/module.modulemap b/test/ClangImporter/Inputs/privateframeworks/withprivate-autolink/SomeKitCore.framework/Modules/module.modulemap new file mode 100644 index 0000000000000..30f9770c97c61 --- /dev/null +++ b/test/ClangImporter/Inputs/privateframeworks/withprivate-autolink/SomeKitCore.framework/Modules/module.modulemap @@ -0,0 +1,7 @@ +framework module SomeKitCore { + umbrella header "SomeKitCore.h" + export_as SomeKit + module * { + export * + } +} diff --git a/test/ClangImporter/Inputs/privateframeworks/withprivate-autolink/SomeKitCore.framework/SomeKitCore.tbd b/test/ClangImporter/Inputs/privateframeworks/withprivate-autolink/SomeKitCore.framework/SomeKitCore.tbd new file mode 100644 index 0000000000000..fcb8b1047f348 --- /dev/null +++ b/test/ClangImporter/Inputs/privateframeworks/withprivate-autolink/SomeKitCore.framework/SomeKitCore.tbd @@ -0,0 +1 @@ +// dummy file to trigger autolink diff --git a/test/ClangImporter/MixedSource/Xcc_include.swift b/test/ClangImporter/MixedSource/Xcc_include.swift index d7d7538fa9c70..df091500df42a 100644 --- a/test/ClangImporter/MixedSource/Xcc_include.swift +++ b/test/ClangImporter/MixedSource/Xcc_include.swift @@ -1,13 +1,8 @@ // RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -Xcc -include -Xcc %S/Inputs/Xcc_include.h -typecheck %s 2>&1 | %FileCheck -check-prefix=CHECK-INCLUDE-ONLY %s - -// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -Xcc -include -Xcc %S/Inputs/Xcc_include.h -import-objc-header %S/../../Inputs/empty.swift -typecheck %s 2>&1 | %FileCheck -check-prefix=CHECK-INCLUDE-PLUS-BRIDGING %s - -// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -Xcc -include -Xcc %S/Inputs/Xcc_include.h -F %S/Inputs/mixed-target/ -module-name Mixed -import-underlying-module -typecheck %s -disable-objc-attr-requires-foundation-module 2>&1 | %FileCheck -check-prefix=CHECK-INCLUDE-FRAMEWORK %s - +// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -Xcc -include -Xcc %S/Inputs/Xcc_include.h -enable-objc-interop -import-objc-header %S/../../Inputs/empty.swift -typecheck %s 2>&1 | %FileCheck -check-prefix=CHECK-INCLUDE-PLUS-BRIDGING %s +// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -Xcc -include -Xcc %S/Inputs/Xcc_include.h -F %S/Inputs/mixed-target/ -module-name Mixed -import-underlying-module -typecheck %s -enable-objc-interop -disable-objc-attr-requires-foundation-module 2>&1 | %FileCheck -check-prefix=CHECK-INCLUDE-FRAMEWORK %s // RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -Xcc -include -Xcc %S/Inputs/this_header_does_not_exist.h -typecheck %s 2>&1 | %FileCheck -check-prefix=CHECK-INCLUDE-MISSING %s -// REQUIRES: objc_interop - // CHECK-INCLUDE-MISSING: error: '{{.*}}this_header_does_not_exist.h' file not found func test() { diff --git a/test/ClangImporter/MixedSource/broken-bridging-header.swift b/test/ClangImporter/MixedSource/broken-bridging-header.swift index fe50fff694d51..0a883e6538026 100644 --- a/test/ClangImporter/MixedSource/broken-bridging-header.swift +++ b/test/ClangImporter/MixedSource/broken-bridging-header.swift @@ -1,14 +1,14 @@ // RUN: %empty-directory(%t) -// RUN: not %target-swift-frontend -typecheck %S/../../Inputs/empty.swift -import-objc-header %t/fake.h 2>&1 | %FileCheck -check-prefix=MISSING-HEADER %s +// RUN: not %target-swift-frontend -typecheck %S/../../Inputs/empty.swift -enable-objc-interop -import-objc-header %t/fake.h 2>&1 | %FileCheck -check-prefix=MISSING-HEADER %s // RUN: cp %S/Inputs/error-on-define.h %t -// RUN: not %target-swift-frontend -typecheck %S/../../Inputs/empty.swift -import-objc-header %t/error-on-define.h 2>&1 | %FileCheck -check-prefix=MISSING-OTHER-HEADER %s +// RUN: not %target-swift-frontend -typecheck %S/../../Inputs/empty.swift -enable-objc-interop -import-objc-header %t/error-on-define.h 2>&1 | %FileCheck -check-prefix=MISSING-OTHER-HEADER %s // RUN: cp %S/Inputs/error-on-define-impl.h %t -// RUN: not %target-swift-frontend -typecheck %S/../../Inputs/empty.swift -import-objc-header %t/error-on-define.h -Xcc -DERROR 2>&1 | %FileCheck -check-prefix=HEADER-ERROR %s +// RUN: not %target-swift-frontend -typecheck %S/../../Inputs/empty.swift -enable-objc-interop -import-objc-header %t/error-on-define.h -Xcc -DERROR 2>&1 | %FileCheck -check-prefix=HEADER-ERROR %s -// RUN: %target-swift-frontend -emit-module -o %t -module-name HasBridgingHeader %S/../../Inputs/empty.swift -import-objc-header %t/error-on-define.h +// RUN: %target-swift-frontend -emit-module -o %t -module-name HasBridgingHeader %S/../../Inputs/empty.swift -enable-objc-interop -import-objc-header %t/error-on-define.h // RUN: %target-swift-frontend -typecheck %s -I %t -Xcc -DERROR -verify -show-diagnostics-after-fatal // RUN: not %target-swift-frontend -typecheck %s -I %t -Xcc -DERROR 2>&1 | %FileCheck -check-prefix=HEADER-ERROR %s @@ -17,8 +17,6 @@ // RUN: %target-swift-frontend -typecheck %s -I %t -verify -show-diagnostics-after-fatal // RUN: not %target-swift-frontend -typecheck %s -I %t 2>&1 | %FileCheck -check-prefix=MISSING-OTHER-HEADER %s -// REQUIRES: objc_interop - import HasBridgingHeader // expected-error {{failed to import bridging header}} expected-error {{failed to load module 'HasBridgingHeader'}} // MISSING-HEADER: error: bridging header '{{.*}}/fake.h' does not exist diff --git a/test/ClangImporter/MixedSource/broken-modules.swift b/test/ClangImporter/MixedSource/broken-modules.swift index 21e6ef3030508..115ca3ca6136e 100644 --- a/test/ClangImporter/MixedSource/broken-modules.swift +++ b/test/ClangImporter/MixedSource/broken-modules.swift @@ -1,13 +1,9 @@ // RUN: %empty-directory(%t) -// RUN: not %target-swift-frontend -typecheck %s -I %S/Inputs/broken-modules/ -enable-source-import -show-diagnostics-after-fatal 2> %t/err.txt -// RUN: %FileCheck -check-prefix CHECK -check-prefix CLANG-CHECK %s < %t/err.txt +// RUN: not %target-swift-frontend -enable-objc-interop -typecheck %s -I %S/Inputs/broken-modules/ -enable-source-import -show-diagnostics-after-fatal -o /dev/null 2>&1 | %FileCheck -check-prefix CHECK -check-prefix CLANG-CHECK %s -// RUN: not %target-swift-frontend -typecheck %s -import-objc-header %S/Inputs/broken-modules/BrokenClangModule.h -enable-source-import 2> %t/err.bridging-header.txt -// RUN: %FileCheck -check-prefix CHECK-BRIDGING-HEADER -check-prefix CLANG-CHECK %s < %t/err.bridging-header.txt +// RUN: not %target-swift-frontend -typecheck %s -enable-objc-interop -import-objc-header %S/Inputs/broken-modules/BrokenClangModule.h -enable-source-import -o /dev/null 2>&1 | %FileCheck -check-prefix CHECK-BRIDGING-HEADER -check-prefix CLANG-CHECK %s -// RUN: not %target-swift-frontend -typecheck %s -import-objc-header %S/../../Inputs/empty.swift 2>&1 | %FileCheck -check-prefix=EMPTY-HEADER %s - -// REQUIRES: objc_interop +// RUN: not %target-swift-frontend -typecheck %s -enable-objc-interop -import-objc-header %S/../../Inputs/empty.swift 2>&1 | %FileCheck -check-prefix=EMPTY-HEADER %s // EMPTY-HEADER-NOT: header diff --git a/test/ClangImporter/MixedSource/can_import_objc_idempotent.swift b/test/ClangImporter/MixedSource/can_import_objc_idempotent.swift index c9f85e5071203..1948d5f198027 100644 --- a/test/ClangImporter/MixedSource/can_import_objc_idempotent.swift +++ b/test/ClangImporter/MixedSource/can_import_objc_idempotent.swift @@ -2,11 +2,11 @@ // RUN: cp -R %S/Inputs/mixed-target %t // FIXME: BEGIN -enable-source-import hackaround -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-module -o %t %clang-importer-sdk-path/swift-modules/CoreGraphics.swift -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-module -o %t %clang-importer-sdk-path/swift-modules/Foundation.swift +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -emit-module -o %t %clang-importer-sdk-path/swift-modules/CoreGraphics.swift +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -emit-module -o %t %clang-importer-sdk-path/swift-modules/Foundation.swift // FIXME: END -enable-source-import hackaround -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk-nosource -I %t) -I %S/../Inputs/custom-modules -import-objc-header %t/mixed-target/header.h -emit-module-path %t/MixedWithHeader.swiftmodule %S/Inputs/mixed-with-header.swift %S/../../Inputs/empty.swift -module-name MixedWithHeader -disable-objc-attr-requires-foundation-module +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk-nosource -I %t) -I %S/../Inputs/custom-modules -enable-objc-interop -import-objc-header %t/mixed-target/header.h -emit-module-path %t/MixedWithHeader.swiftmodule %S/Inputs/mixed-with-header.swift %S/../../Inputs/empty.swift -module-name MixedWithHeader -disable-objc-attr-requires-foundation-module // RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk-nosource -I %t) -I %S/../Inputs/custom-modules -typecheck %s -verify // RUN: rm -rf %t/mixed-target/ @@ -26,15 +26,15 @@ #endif #if canImport(CoreGraphics) - let square = CGRect(x: 100, y: 100, width: 100, height: 100) // expected-error {{use of unresolved identifier 'CGRect'}} - // expected-error@-1 {{'square' used within its own type}} - // expected-error@-2 {{could not infer type for 'square'}} + let square = CGRect(x: 100, y: 100, width: 100, height: 100) + // expected-error@-1 {{use of unresolved identifier 'CGRect'}} + // expected-note@-2 {{'square' declared here}} + let (r, s) = square.divided(atDistance: 50, from: .minXEdge) + // expected-error@-1 {{ambiguous use of 'square'}} #endif #if canImport(MixedWithHeader) let object = NSObject() // expected-error {{use of unresolved identifier 'NSObject'}} - // expected-error@-1 {{'object' used within its own type}} - // expected-error@-2 {{could not infer type for 'object'}} let someAPI = Derived() // expected-error {{use of unresolved identifier 'Derived'}} #endif diff --git a/test/ClangImporter/MixedSource/defer-supermodule-import.swift b/test/ClangImporter/MixedSource/defer-supermodule-import.swift index 0e6d5050115eb..2813089817621 100644 --- a/test/ClangImporter/MixedSource/defer-supermodule-import.swift +++ b/test/ClangImporter/MixedSource/defer-supermodule-import.swift @@ -1,7 +1,6 @@ -// RUN: not %target-swift-frontend -F %S/Inputs/defer-supermodule-import -import-objc-header %S/Inputs/defer-supermodule-import/Some-Bridging-Header.h -typecheck %s 2>&1 | %FileCheck -check-prefix=HEADER-ERROR %s +// RUN: not %target-swift-frontend -F %S/Inputs/defer-supermodule-import -enable-objc-interop -import-objc-header %S/Inputs/defer-supermodule-import/Some-Bridging-Header.h -typecheck %s 2>&1 | %FileCheck -check-prefix=HEADER-ERROR %s // HEADER-ERROR: Some-Bridging-Header.h:4:13: error: expected a type // HEADER-ERROR: Some-Bridging-Header.h:7:10: error: declaration of 'TYPE' must be imported from module 'Some' before it is required -// REQUIRES: objc_interop // The bug we're testing here is that: // diff --git a/test/ClangImporter/MixedSource/import-mixed-framework-with-forward.swift b/test/ClangImporter/MixedSource/import-mixed-framework-with-forward.swift index 006d623d67ee2..d554c5bcfb7b3 100644 --- a/test/ClangImporter/MixedSource/import-mixed-framework-with-forward.swift +++ b/test/ClangImporter/MixedSource/import-mixed-framework-with-forward.swift @@ -1,17 +1,15 @@ // RUN: %empty-directory(%t) // RUN: cp -r %S/Inputs/mixed-framework/Mixed.framework %t -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-module -o %t/Mixed.framework/Modules/Mixed.swiftmodule/%target-swiftmodule-name %S/Inputs/mixed-framework/Mixed.swift -import-underlying-module -F %t -module-name Mixed -disable-objc-attr-requires-foundation-module +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-module -o %t/Mixed.framework/Modules/Mixed.swiftmodule/%target-swiftmodule-name %S/Inputs/mixed-framework/Mixed.swift -import-underlying-module -F %t -module-name Mixed -enable-objc-interop -disable-objc-attr-requires-foundation-module -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -F %t -import-objc-header %S/Inputs/import-mixed-framework-with-forward.h %s -verify +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -F %t -enable-objc-interop -import-objc-header %S/Inputs/import-mixed-framework-with-forward.h %s -verify -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-pch -F %t %S/Inputs/import-mixed-framework-with-forward.h -o %t/bridge.pch -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -F %t -import-objc-header %t/bridge.pch %s -verify -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -F %t -import-objc-header %S/Inputs/import-mixed-framework-with-forward.h -pch-output-dir %t/pch %s -verify +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -emit-pch -F %t %S/Inputs/import-mixed-framework-with-forward.h -o %t/bridge.pch +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -F %t -enable-objc-interop -import-objc-header %t/bridge.pch %s -verify +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -F %t -enable-objc-interop -import-objc-header %S/Inputs/import-mixed-framework-with-forward.h -pch-output-dir %t/pch %s -verify -// REQUIRES: objc_interop - import Mixed BridgingHeader.takeForward(SwiftClass(x: 42)) diff --git a/test/ClangImporter/MixedSource/import-mixed-framework.swift b/test/ClangImporter/MixedSource/import-mixed-framework.swift index d61456825287d..004ded545121d 100644 --- a/test/ClangImporter/MixedSource/import-mixed-framework.swift +++ b/test/ClangImporter/MixedSource/import-mixed-framework.swift @@ -4,10 +4,8 @@ // Don't crash if a generated header is present but the swiftmodule is missing. // RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -F %t -typecheck %s -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-module -o %t/Mixed.framework/Modules/Mixed.swiftmodule/%target-swiftmodule-name %S/Inputs/mixed-framework/Mixed.swift -import-underlying-module -F %t -module-name Mixed -disable-objc-attr-requires-foundation-module -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -F %t -typecheck %s -verify - -// XFAIL: linux +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -emit-module -o %t/Mixed.framework/Modules/Mixed.swiftmodule/%target-swiftmodule-name %S/Inputs/mixed-framework/Mixed.swift -import-underlying-module -F %t -module-name Mixed -disable-objc-attr-requires-foundation-module +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -F %t -typecheck %s -verify import Mixed diff --git a/test/ClangImporter/MixedSource/import-mixed-with-header-twice.swift b/test/ClangImporter/MixedSource/import-mixed-with-header-twice.swift index 9776ab905460f..3997642719f78 100644 --- a/test/ClangImporter/MixedSource/import-mixed-with-header-twice.swift +++ b/test/ClangImporter/MixedSource/import-mixed-with-header-twice.swift @@ -1,14 +1,12 @@ // RUN: %empty-directory(%t) // RUN: cp -R %S/Inputs/mixed-target %t -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -I %S/../Inputs/custom-modules -import-objc-header %t/mixed-target/header.h -emit-module-path %t/MixedWithHeader.swiftmodule %S/Inputs/mixed-with-header.swift %S/../../Inputs/empty.swift -module-name MixedWithHeader -disable-objc-attr-requires-foundation-module -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -I %t -I %S/../Inputs/custom-modules -import-objc-header %t/mixed-target/header-again.h -emit-module-path %t/MixedWithHeaderAgain.swiftmodule %S/Inputs/mixed-with-header-again.swift %S/../../Inputs/empty.swift -module-name MixedWithHeaderAgain -disable-objc-attr-requires-foundation-module -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -I %S/../Inputs/custom-modules -I %t -typecheck %s -verify +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -I %S/../Inputs/custom-modules -enable-objc-interop -import-objc-header %t/mixed-target/header.h -emit-module-path %t/MixedWithHeader.swiftmodule %S/Inputs/mixed-with-header.swift %S/../../Inputs/empty.swift -module-name MixedWithHeader -disable-objc-attr-requires-foundation-module +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -I %t -I %S/../Inputs/custom-modules -enable-objc-interop -import-objc-header %t/mixed-target/header-again.h -emit-module-path %t/MixedWithHeaderAgain.swiftmodule %S/Inputs/mixed-with-header-again.swift %S/../../Inputs/empty.swift -module-name MixedWithHeaderAgain -disable-objc-attr-requires-foundation-module +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -I %S/../Inputs/custom-modules -I %t -enable-objc-interop -typecheck %s -verify // RUN: rm %t/mixed-target/header.h -// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -I %t -I %S/../Inputs/custom-modules -typecheck %s 2>&1 | %FileCheck %s -check-prefix=USE-SERIALIZED-HEADER - -// XFAIL: linux +// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -I %t -I %S/../Inputs/custom-modules -enable-objc-interop -typecheck %s 2>&1 | %FileCheck %s -check-prefix=USE-SERIALIZED-HEADER // USE-SERIALIZED-HEADER: redefinition of 'Point2D' // USE-SERIALIZED-HEADER: previous definition is here diff --git a/test/ClangImporter/MixedSource/import-mixed-with-header.swift b/test/ClangImporter/MixedSource/import-mixed-with-header.swift index d15957f8b48ef..113f339358b3f 100644 --- a/test/ClangImporter/MixedSource/import-mixed-with-header.swift +++ b/test/ClangImporter/MixedSource/import-mixed-with-header.swift @@ -2,15 +2,15 @@ // RUN: cp -R %S/Inputs/mixed-target %t // FIXME: BEGIN -enable-source-import hackaround -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-module -o %t %clang-importer-sdk-path/swift-modules/CoreGraphics.swift -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-module -o %t %clang-importer-sdk-path/swift-modules/Foundation.swift +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -emit-module -o %t %clang-importer-sdk-path/swift-modules/CoreGraphics.swift +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -emit-module -o %t %clang-importer-sdk-path/swift-modules/Foundation.swift // FIXME: END -enable-source-import hackaround -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk-nosource -I %t) -I %S/../Inputs/custom-modules -import-objc-header %t/mixed-target/header.h -emit-module-path %t/MixedWithHeader.swiftmodule %S/Inputs/mixed-with-header.swift %S/../../Inputs/empty.swift -module-name MixedWithHeader -disable-objc-attr-requires-foundation-module -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk-nosource -I %t) -I %S/../Inputs/custom-modules -typecheck %s -verify +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk-nosource -I %t) -I %S/../Inputs/custom-modules -import-objc-header %t/mixed-target/header.h -emit-module-path %t/MixedWithHeader.swiftmodule %S/Inputs/mixed-with-header.swift %S/../../Inputs/empty.swift -module-name MixedWithHeader -enable-objc-interop -disable-objc-attr-requires-foundation-module +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk-nosource -I %t) -I %S/../Inputs/custom-modules -enable-objc-interop -typecheck %s -verify // RUN: rm -rf %t/mixed-target/ -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk-nosource -I %t) -I %S/../Inputs/custom-modules -typecheck %s -verify +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk-nosource -I %t) -I %S/../Inputs/custom-modules -enable-objc-interop -typecheck %s -verify // XFAIL: linux diff --git a/test/ClangImporter/MixedSource/mixed-target-using-header-swift4.swift b/test/ClangImporter/MixedSource/mixed-target-using-header-swift4.swift index 67ec230b129a0..2ec8a513730fa 100644 --- a/test/ClangImporter/MixedSource/mixed-target-using-header-swift4.swift +++ b/test/ClangImporter/MixedSource/mixed-target-using-header-swift4.swift @@ -1,4 +1,3 @@ -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -I %S/../Inputs/custom-modules -import-objc-header %S/Inputs/mixed-target/header.h -typecheck -primary-file %S/mixed-target-using-header.swift %S/Inputs/mixed-target/other-file.swift -disable-objc-attr-requires-foundation-module -verify -swift-version 4 -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -I %S/../Inputs/custom-modules -import-objc-header %S/Inputs/mixed-target/header.h -emit-sil -primary-file %S/mixed-target-using-header.swift %S/Inputs/mixed-target/other-file.swift -disable-objc-attr-requires-foundation-module -o /dev/null -D SILGEN -swift-version 4 +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -I %S/../Inputs/custom-modules -enable-objc-interop -import-objc-header %S/Inputs/mixed-target/header.h -typecheck -primary-file %S/mixed-target-using-header.swift %S/Inputs/mixed-target/other-file.swift -disable-objc-attr-requires-foundation-module -verify -swift-version 4 +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -I %S/../Inputs/custom-modules -enable-objc-interop -import-objc-header %S/Inputs/mixed-target/header.h -emit-sil -primary-file %S/mixed-target-using-header.swift %S/Inputs/mixed-target/other-file.swift -disable-objc-attr-requires-foundation-module -o /dev/null -D SILGEN -swift-version 4 -// REQUIRES: objc_interop diff --git a/test/ClangImporter/MixedSource/mixed-target-using-header.swift b/test/ClangImporter/MixedSource/mixed-target-using-header.swift index cd506d6ec04c1..f172deb0d62e1 100644 --- a/test/ClangImporter/MixedSource/mixed-target-using-header.swift +++ b/test/ClangImporter/MixedSource/mixed-target-using-header.swift @@ -1,7 +1,5 @@ -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -I %S/../Inputs/custom-modules -import-objc-header %S/Inputs/mixed-target/header.h -typecheck -primary-file %s %S/Inputs/mixed-target/other-file.swift -disable-objc-attr-requires-foundation-module -verify -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -I %S/../Inputs/custom-modules -import-objc-header %S/Inputs/mixed-target/header.h -emit-sil -primary-file %s %S/Inputs/mixed-target/other-file.swift -disable-objc-attr-requires-foundation-module -o /dev/null -D SILGEN - -// REQUIRES: objc_interop +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -I %S/../Inputs/custom-modules -enable-objc-interop -import-objc-header %S/Inputs/mixed-target/header.h -typecheck -primary-file %s %S/Inputs/mixed-target/other-file.swift -disable-objc-attr-requires-foundation-module -verify +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -I %S/../Inputs/custom-modules -enable-objc-interop -import-objc-header %S/Inputs/mixed-target/header.h -emit-sil -primary-file %s %S/Inputs/mixed-target/other-file.swift -disable-objc-attr-requires-foundation-module -o /dev/null -D SILGEN func test(_ foo : FooProto) { _ = foo.bar as CInt diff --git a/test/ClangImporter/MixedSource/mixed-target-using-module.swift b/test/ClangImporter/MixedSource/mixed-target-using-module.swift index d1661865adf48..72a483ff9047c 100644 --- a/test/ClangImporter/MixedSource/mixed-target-using-module.swift +++ b/test/ClangImporter/MixedSource/mixed-target-using-module.swift @@ -1,8 +1,6 @@ -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -F %S/Inputs/mixed-target/ -module-name Mixed -import-underlying-module -typecheck %s -verify -disable-objc-attr-requires-foundation-module -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -F %S/Inputs/mixed-target/ -module-name Mixed -import-underlying-module -emit-ir %S/../../Inputs/empty.swift - | %FileCheck -check-prefix=CHECK-AUTOLINK %s -// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -F %S/Inputs/mixed-target/ -module-name WrongName -import-underlying-module -typecheck %s -disable-objc-attr-requires-foundation-module 2>&1 | %FileCheck -check-prefix=CHECK-WRONG-NAME %s - -// REQUIRES: objc_interop +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -F %S/Inputs/mixed-target/ -module-name Mixed -import-underlying-module -typecheck %s -verify -enable-objc-interop -disable-objc-attr-requires-foundation-module +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -F %S/Inputs/mixed-target/ -module-name Mixed -import-underlying-module -enable-objc-interop -emit-ir %S/../../Inputs/empty.swift - | %FileCheck -check-prefix=CHECK-AUTOLINK %s +// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -F %S/Inputs/mixed-target/ -module-name WrongName -import-underlying-module -typecheck %s -enable-objc-interop -disable-objc-attr-requires-foundation-module 2>&1 | %FileCheck -check-prefix=CHECK-WRONG-NAME %s // CHECK-AUTOLINK: !llvm.linker.options = !{ // CHECK-AUTOLINK-NOT: metadata !"-framework", metadata !"Mixed" diff --git a/test/ClangImporter/MixedSource/resolve-cross-language.swift b/test/ClangImporter/MixedSource/resolve-cross-language.swift index e3f840806f8e5..066c600b6a8fb 100644 --- a/test/ClangImporter/MixedSource/resolve-cross-language.swift +++ b/test/ClangImporter/MixedSource/resolve-cross-language.swift @@ -1,10 +1,8 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk-nosource) -emit-module -emit-objc-header -o %t %S/Inputs/resolve-cross-language/Base.swift -disable-objc-attr-requires-foundation-module +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk-nosource) -emit-module -enable-objc-interop -emit-objc-header -o %t %S/Inputs/resolve-cross-language/Base.swift -disable-objc-attr-requires-foundation-module // RUN: cp %S/Inputs/resolve-cross-language/Base-module.map %t/module.map -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk-nosource) -typecheck -I %t -F %S/Inputs/resolve-cross-language %s -verify - -// REQUIRES: objc_interop +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk-nosource) -enable-objc-interop -typecheck -I %t -F %S/Inputs/resolve-cross-language %s -verify import Base import BaseUser diff --git a/test/ClangImporter/attr-objc_subclassing_restricted.swift b/test/ClangImporter/attr-objc_subclassing_restricted.swift index dd9b4ac66d9c1..45e63a2c21e0f 100644 --- a/test/ClangImporter/attr-objc_subclassing_restricted.swift +++ b/test/ClangImporter/attr-objc_subclassing_restricted.swift @@ -1,9 +1,7 @@ -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -import-objc-header %S/Inputs/attr-objc_subclassing_restricted.h %s -swift-version 5 -verify +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -enable-objc-interop -import-objc-header %S/Inputs/attr-objc_subclassing_restricted.h %s -swift-version 5 -verify // No errors in Swift 3 and 4 modes. -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -import-objc-header %S/Inputs/attr-objc_subclassing_restricted.h %s -swift-version 4 - -// REQUIRES: objc_interop +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -enable-objc-interop -import-objc-header %S/Inputs/attr-objc_subclassing_restricted.h %s -swift-version 4 class Sub: Restricted { // expected-error {{cannot inherit from non-open class 'Restricted' outside of its defining module}} } diff --git a/test/ClangImporter/attr-swift_name_renaming-objc.swift b/test/ClangImporter/attr-swift_name_renaming-objc.swift index baf9dbd98d0ba..9e796af2e4340 100644 --- a/test/ClangImporter/attr-swift_name_renaming-objc.swift +++ b/test/ClangImporter/attr-swift_name_renaming-objc.swift @@ -1,6 +1,4 @@ -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -I %S/Inputs/custom-modules -Xcc -w -typecheck -verify %s - -// REQUIRES: objc_interop +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -I %S/Inputs/custom-modules -Xcc -w -typecheck -verify %s import SwiftName diff --git a/test/ClangImporter/attr-swift_name_renaming.swift b/test/ClangImporter/attr-swift_name_renaming.swift index 16c13d0c886af..edfc111354efd 100644 --- a/test/ClangImporter/attr-swift_name_renaming.swift +++ b/test/ClangImporter/attr-swift_name_renaming.swift @@ -13,9 +13,9 @@ func test() { // Enumerator remapping. var excuse: HomeworkExcuse = .dogAteIt - excuse = .overslept // FIXME: should provide Fix-It expected-error{{type 'HomeworkExcuse' has no member 'overslept'}} {{none}} + excuse = .overslept // expected-error{{type 'HomeworkExcuse' has no member 'overslept'; did you mean 'Overslept'?}} {{13-22=Overslept}} excuse = .tired - excuse = .tooHard // FIXME: should provide Fix-It expected-error{{type 'HomeworkExcuse' has no member 'tooHard'}} {{none}} + excuse = .tooHard // expected-error{{type 'HomeworkExcuse' has no member 'tooHard'; did you mean 'TooHard'?}} {{13-20=TooHard}} excuse = .challenging // Typedef-of-anonymous-type-name renaming diff --git a/test/ClangImporter/autolinking.swift b/test/ClangImporter/autolinking.swift index 73febd120b094..694a52c51e1f5 100644 --- a/test/ClangImporter/autolinking.swift +++ b/test/ClangImporter/autolinking.swift @@ -1,14 +1,15 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend %s -sdk %S/Inputs -I %S/Inputs/custom-modules -emit-ir -o %t/without-adapter.ll -// RUN: %FileCheck %s < %t/without-adapter.ll +// RUN: %target-swift-frontend %s -sdk %S/Inputs -I %S/Inputs/custom-modules -emit-ir -o - | %FileCheck %s // RUN: %target-swift-frontend -emit-module %S/Inputs/adapter.swift -sdk %S/Inputs -module-link-name SwiftAdapter -module-name ClangModuleWithAdapter -I %S/Inputs/custom-modules -o %t + // RUN: %target-swift-frontend %s -sdk %S/Inputs -I %S/Inputs/custom-modules -I %t -emit-ir -o %t/with-adapter.ll // RUN: %FileCheck %s < %t/with-adapter.ll -// RUN: %FileCheck --check-prefix=CHECK-WITH-SWIFT %s < %t/with-adapter.ll +// RUN: %FileCheck -check-prefix CHECK-WITH-SWIFT %s < %t/with-adapter.ll -// RUN: %target-swift-frontend %s -sdk %S/Inputs -I %S/Inputs/custom-modules -emit-ir -disable-autolink-framework LinkFramework -o %t/with-disabled.ll -// RUN: %FileCheck --check-prefix=CHECK-WITH-DISABLED %s < %t/with-disabled.ll +// RUN: %target-swift-frontend %s -sdk %S/Inputs -I %S/Inputs/custom-modules -emit-ir -disable-autolink-framework LinkFramework -o - > %t/with-disabled.ll +// RUN: %FileCheck -check-prefix CHECK-WITH-DISABLED %s < %t/with-disabled.ll +// RUN: %FileCheck -check-prefix NEGATIVE-WITH-DISABLED %s < %t/with-disabled.ll // Linux uses a different autolinking mechanism, based on // swift-autolink-extract. This file tests the Darwin mechanism. @@ -38,6 +39,6 @@ UsesSubmodule.useSomethingFromSubmodule() // CHECK-WITH-SWIFT: !{{[0-9]+}} = !{!"-lSwiftAdapter"} -// CHECK-WITH-DISABLED: !{!"-framework", !"Barrel"} -// CHECK-WITH-DISABLED-NOT: !{!"-framework", !"LinkFramework"} -// CHECK-WITH-DISABLED: !{!"-framework", !"Indirect"} +// CHECK-WITH-DISABLED-DAG: !{!"-framework", !"Barrel"} +// CHECK-WITH-DISABLED-DAG: !{!"-framework", !"Indirect"} +// NEGATIVE-WITH-DISABLED-NOT: !"LinkFramework" diff --git a/test/ClangImporter/availability.swift b/test/ClangImporter/availability.swift index cc14dd57fcccf..9e744f49ebd0d 100644 --- a/test/ClangImporter/availability.swift +++ b/test/ClangImporter/availability.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -verify -I %S/Inputs/custom-modules %s +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -verify -I %S/Inputs/custom-modules %s -verify-ignore-unknown // REQUIRES: objc_interop @@ -25,6 +25,28 @@ func test_unavailable_func(_ x : NSObject) { NSDeallocateObject(x) // expected-error {{'NSDeallocateObject' is unavailable}} } +func test_unavailable_accessors(_ obj: UnavailableAccessors) { + _ = obj.fullyUnavailable // expected-error {{'fullyUnavailable' is unavailable}} + obj.fullyUnavailable = 0 // expected-error {{'fullyUnavailable' is unavailable}} + obj.fullyUnavailable += 1 // expected-error {{'fullyUnavailable' is unavailable}} + + _ = obj.getterUnavailable // expected-error {{getter for 'getterUnavailable' is unavailable}} + obj.getterUnavailable = 0 + obj.getterUnavailable += 1 // expected-error {{getter for 'getterUnavailable' is unavailable}} + + _ = UnavailableAccessors.getterUnavailableClass // expected-error {{getter for 'getterUnavailableClass' is unavailable}} + UnavailableAccessors.getterUnavailableClass = 0 + UnavailableAccessors.getterUnavailableClass += 1 // expected-error {{getter for 'getterUnavailableClass' is unavailable}} + + _ = obj.setterUnavailable + obj.setterUnavailable = 0 // expected-error {{setter for 'setterUnavailable' is unavailable}} + obj.setterUnavailable += 1 // expected-error {{setter for 'setterUnavailable' is unavailable}} + + _ = UnavailableAccessors.setterUnavailableClass + UnavailableAccessors.setterUnavailableClass = 0 // expected-error {{setter for 'setterUnavailableClass' is unavailable}} + UnavailableAccessors.setterUnavailableClass += 1 // expected-error {{setter for 'setterUnavailableClass' is unavailable}} +} + func test_deprecated(_ s:UnsafeMutablePointer, _ obj: AccessorDeprecations) { _ = tmpnam(s) // expected-warning {{'tmpnam' is deprecated: Due to security concerns inherent in the design of tmpnam(3), it is highly recommended that you use mkstemp(3) instead.}} diff --git a/test/ClangImporter/availability_open_enums.swift b/test/ClangImporter/availability_open_enums.swift index 402c14d9955a9..68156feeaf649 100644 --- a/test/ClangImporter/availability_open_enums.swift +++ b/test/ClangImporter/availability_open_enums.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -verify -I %S/Inputs/custom-modules -swift-version 4 %s +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -verify -I %S/Inputs/custom-modules -swift-version 4 -enable-nonfrozen-enum-exhaustivity-diagnostics %s // REQUIRES: objc_interop @@ -8,6 +8,7 @@ import AvailabilityExtras func exhaustiveSwitch(e: NSEnumAddedCasesIn2017) { switch e { // expected-error{{switch must be exhaustive}} // expected-note@-1{{add missing case: '.newCaseOne'}} + // expected-note@-2{{handle unknown values using "@unknown default"}} case .existingCaseOne: return case .existingCaseTwo: diff --git a/test/ClangImporter/availability_open_enums_swift3.swift b/test/ClangImporter/availability_open_enums_swift3.swift index 4ff7402b37da0..a9cb2f5e8354a 100644 --- a/test/ClangImporter/availability_open_enums_swift3.swift +++ b/test/ClangImporter/availability_open_enums_swift3.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -verify -I %S/Inputs/custom-modules -swift-version 3 %s +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -verify -I %S/Inputs/custom-modules -swift-version 3 -enable-nonfrozen-enum-exhaustivity-diagnostics %s // REQUIRES: objc_interop @@ -8,6 +8,7 @@ import AvailabilityExtras func exhaustiveSwitch(e: NSEnumAddedCasesIn2017) { switch e { // expected-warning{{switch must be exhaustive}} // expected-note@-1{{add missing case: '.newCaseOne'}} + // expected-note@-2{{handle unknown values using "@unknown default"}} case .existingCaseOne: return case .existingCaseTwo: diff --git a/test/ClangImporter/availability_returns_twice.swift b/test/ClangImporter/availability_returns_twice.swift index 537c1846c8588..2ddc5624d1823 100644 --- a/test/ClangImporter/availability_returns_twice.swift +++ b/test/ClangImporter/availability_returns_twice.swift @@ -3,9 +3,12 @@ #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) import Darwin typealias JumpBuffer = Int32 -#else +#elseif os(Android) || os(Cygwin) || os(FreeBSD) || os(Linux) import Glibc typealias JumpBuffer = jmp_buf +#elseif os(Windows) + import MSVCRT + typealias JumpBuffer = jmp_buf #endif func test_unavailable_returns_twice_function() { diff --git a/test/ClangImporter/bad-ns-extensible-string-enum.swift b/test/ClangImporter/bad-ns-extensible-string-enum.swift index 7e2537b72460e..e0bb97080466a 100644 --- a/test/ClangImporter/bad-ns-extensible-string-enum.swift +++ b/test/ClangImporter/bad-ns-extensible-string-enum.swift @@ -1,4 +1,3 @@ -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck %s -import-objc-header %S/Inputs/bad-ns-extensible-string-enum.h -verify -// REQUIRES: objc_interop +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck %s -enable-objc-interop -import-objc-header %S/Inputs/bad-ns-extensible-string-enum.h -verify let string = MyString.MyStringOne // expected-error {{use of unresolved identifier 'MyString'}} diff --git a/test/ClangImporter/broken-modules.swift b/test/ClangImporter/broken-modules.swift index ade6d5a25cedc..b7fc791457d64 100644 --- a/test/ClangImporter/broken-modules.swift +++ b/test/ClangImporter/broken-modules.swift @@ -1,18 +1,8 @@ -// RUN: %empty-directory(%t) +// RUN: not %target-swift-frontend -typecheck %s -I %S/Inputs/custom-modules/ -enable-objc-interop -show-diagnostics-after-fatal -D MISSING_FROM_MODULE -o /dev/null 2>&1 | %FileCheck -check-prefix CHECK-MODULE-MAP %s +// RUN: not %target-swift-frontend -typecheck %s -I %S/Inputs/custom-modules/ -enable-objc-interop -show-diagnostics-after-fatal -o /dev/null 2>&1 | %FileCheck -check-prefix CHECK -check-prefix CHECK-DIRECT %s +// RUN: not %target-swift-frontend -typecheck %s -I %S/Inputs/custom-modules/ -enable-objc-interop -show-diagnostics-after-fatal -D INDIRECT -o /dev/null 2>&1 | %FileCheck -check-prefix CHECK -check-prefix CHECK-INDIRECT %s -// RUN: not %target-swift-frontend -typecheck %s -I %S/Inputs/custom-modules/ -show-diagnostics-after-fatal -D MISSING_FROM_MODULE 2> %t/err.txt -// RUN: %FileCheck -check-prefix CHECK-MODULE-MAP %s < %t/err.txt - -// RUN: not %target-swift-frontend -typecheck %s -I %S/Inputs/custom-modules/ -show-diagnostics-after-fatal 2> %t/err.txt -// RUN: %FileCheck -check-prefix CHECK -check-prefix CHECK-DIRECT %s < %t/err.txt - -// RUN: not %target-swift-frontend -typecheck %s -I %S/Inputs/custom-modules/ -show-diagnostics-after-fatal -D INDIRECT 2> %t/err.txt -// RUN: %FileCheck -check-prefix CHECK -check-prefix CHECK-INDIRECT %s < %t/err.txt - -// FIXME: not every test here depends on Objective-C syntax, this test can be -// split. -// -// REQUIRES: objc_interop +// FIXME: not every test here depends on Objective-C syntax, this test can be split. #if MISSING_FROM_MODULE import MissingHeader diff --git a/test/ClangImporter/c_inside_objc.swift b/test/ClangImporter/c_inside_objc.swift index c4b0d6e695890..14dcc7cdfc7d2 100644 --- a/test/ClangImporter/c_inside_objc.swift +++ b/test/ClangImporter/c_inside_objc.swift @@ -1,12 +1,10 @@ -// RUN: %target-swift-ide-test -print-module -module-to-print CInsideObjC -I %S/Inputs/custom-modules -source-filename %s -Xcc -DCLASS | %FileCheck %s -// RUN: %target-swift-ide-test -print-module -module-to-print CInsideObjC -I %S/Inputs/custom-modules -source-filename %s -Xcc -DCATEGORY | %FileCheck %s -// RUN: %target-swift-ide-test -print-module -module-to-print CInsideObjC -I %S/Inputs/custom-modules -source-filename %s -Xcc -DPROTOCOL | %FileCheck %s +// RUN: %target-swift-ide-test -enable-objc-interop -print-module -module-to-print CInsideObjC -I %S/Inputs/custom-modules -source-filename %s -Xcc -DCLASS | %FileCheck %s +// RUN: %target-swift-ide-test -enable-objc-interop -print-module -module-to-print CInsideObjC -I %S/Inputs/custom-modules -source-filename %s -Xcc -DCATEGORY | %FileCheck %s +// RUN: %target-swift-ide-test -enable-objc-interop -print-module -module-to-print CInsideObjC -I %S/Inputs/custom-modules -source-filename %s -Xcc -DPROTOCOL | %FileCheck %s -// RUN: %target-swift-frontend -typecheck %s -I %S/Inputs/custom-modules -verify -Xcc -DCLASS -// RUN: %target-swift-frontend -typecheck %s -I %S/Inputs/custom-modules -verify -Xcc -DCATEGORY -// RUN: %target-swift-frontend -typecheck %s -I %S/Inputs/custom-modules -verify -Xcc -DPROTOCOL - -// REQUIRES: objc_interop +// RUN: %target-swift-frontend -enable-objc-interop -typecheck %s -I %S/Inputs/custom-modules -verify -Xcc -DCLASS +// RUN: %target-swift-frontend -enable-objc-interop -typecheck %s -I %S/Inputs/custom-modules -verify -Xcc -DCATEGORY +// RUN: %target-swift-frontend -enable-objc-interop -typecheck %s -I %S/Inputs/custom-modules -verify -Xcc -DPROTOCOL // CHECK-LABEL: struct AlreadyDeclaredStruct { diff --git a/test/ClangImporter/can_import_missing_requirement.swift b/test/ClangImporter/can_import_missing_requirement.swift index cd7a80a945387..ed6d619bfe5cc 100644 --- a/test/ClangImporter/can_import_missing_requirement.swift +++ b/test/ClangImporter/can_import_missing_requirement.swift @@ -1,7 +1,5 @@ // RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -I %S/Inputs/missing-requirement %s -verify -// REQUIRES: objc_interop - class Unique {} #if canImport(MissingRequirement) diff --git a/test/ClangImporter/cfuncs_parse.swift b/test/ClangImporter/cfuncs_parse.swift index 26089cec21dd8..e565174356439 100644 --- a/test/ClangImporter/cfuncs_parse.swift +++ b/test/ClangImporter/cfuncs_parse.swift @@ -153,7 +153,7 @@ func test_decay() { func test_nested_pointers() { nested_pointer(nil) nested_pointer_audited(nil) - nested_pointer_audited2(nil) // expected-error {{nil is not compatible with expected argument type 'UnsafePointer?>'}} + nested_pointer_audited2(nil) // expected-error {{'nil' is not compatible with expected argument type 'UnsafePointer?>'}} nested_pointer(0) // expected-error {{expected argument type 'UnsafePointer?>?'}} nested_pointer_audited(0) // expected-error {{expected argument type 'UnsafePointer>?'}} diff --git a/test/ClangImporter/clang_builtins.swift b/test/ClangImporter/clang_builtins.swift index b9a72eb50405e..6d046f8849ace 100644 --- a/test/ClangImporter/clang_builtins.swift +++ b/test/ClangImporter/clang_builtins.swift @@ -5,8 +5,10 @@ #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) import Darwin -#else +#elseif os(Android) || os(Cygwin) || os(FreeBSD) || os(Linux) import Glibc +#elseif os(Windows) + import MSVCRT #endif func test() { diff --git a/test/ClangImporter/const_and_pure.swift b/test/ClangImporter/const_and_pure.swift index 1574c688e1b80..cc9abdfae5a25 100644 --- a/test/ClangImporter/const_and_pure.swift +++ b/test/ClangImporter/const_and_pure.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -emit-sil %s -import-objc-header %S/Inputs/const_and_pure.h | %FileCheck %s +// RUN: %target-swift-frontend -emit-sil %s -enable-objc-interop -import-objc-header %S/Inputs/const_and_pure.h | %FileCheck %s func testit() { const_function() diff --git a/test/ClangImporter/ctypes_parse.swift b/test/ClangImporter/ctypes_parse.swift index a3a25246c06c9..b77c0f1742066 100644 --- a/test/ClangImporter/ctypes_parse.swift +++ b/test/ClangImporter/ctypes_parse.swift @@ -217,9 +217,9 @@ func testStructDefaultInit() { func testArrays() { nonnullArrayParameters([], [], []) - nonnullArrayParameters(nil, [], []) // expected-error {{nil is not compatible with expected argument type 'UnsafePointer'}} - nonnullArrayParameters([], nil, []) // expected-error {{nil is not compatible with expected argument type 'UnsafePointer'}} - nonnullArrayParameters([], [], nil) // expected-error {{nil is not compatible with expected argument type 'UnsafePointer'}} + nonnullArrayParameters(nil, [], []) // expected-error {{'nil' is not compatible with expected argument type 'UnsafePointer'}} + nonnullArrayParameters([], nil, []) // expected-error {{'nil' is not compatible with expected argument type 'UnsafePointer'}} + nonnullArrayParameters([], [], nil) // expected-error {{'nil' is not compatible with expected argument type 'UnsafePointer'}} nullableArrayParameters([], [], []) nullableArrayParameters(nil, nil, nil) @@ -234,7 +234,7 @@ func testVaList() { withVaList([]) { hasVaList($0) // okay } - hasVaList(nil) // expected-error {{nil is not compatible with expected argument type 'CVaListPointer'}} + hasVaList(nil) // expected-error {{'nil' is not compatible with expected argument type 'CVaListPointer'}} } func testNestedForwardDeclaredStructs() { diff --git a/test/ClangImporter/ctypes_parse_objc.swift b/test/ClangImporter/ctypes_parse_objc.swift index 507a29fa3ba05..415eb495873b9 100644 --- a/test/ClangImporter/ctypes_parse_objc.swift +++ b/test/ClangImporter/ctypes_parse_objc.swift @@ -1,6 +1,4 @@ -// RUN: %target-typecheck-verify-swift %clang-importer-sdk - -// REQUIRES: objc_interop +// RUN: %target-typecheck-verify-swift %clang-importer-sdk -enable-objc-interop import ctypes import CoreGraphics diff --git a/test/ClangImporter/ctypes_parse_swift4.swift b/test/ClangImporter/ctypes_parse_swift4.swift index bc9278f539f55..2743deac443e0 100644 --- a/test/ClangImporter/ctypes_parse_swift4.swift +++ b/test/ClangImporter/ctypes_parse_swift4.swift @@ -6,5 +6,5 @@ func testArrays() { // It would also be nice to warn here about the arrays being too short, but // that's probably beyond us for a while. staticBoundsArray([]) - staticBoundsArray(nil) // expected-error {{nil is not compatible with expected argument type 'UnsafePointer'}} + staticBoundsArray(nil) // expected-error {{'nil' is not compatible with expected argument type 'UnsafePointer'}} } diff --git a/test/ClangImporter/diags-with-many-imports.swift b/test/ClangImporter/diags-with-many-imports.swift index 0b2cf0011b119..db98a69c4bd49 100644 --- a/test/ClangImporter/diags-with-many-imports.swift +++ b/test/ClangImporter/diags-with-many-imports.swift @@ -1,7 +1,7 @@ -// RUN: not %target-swift-frontend -typecheck %s -I %S/Inputs/many-imports -import-objc-header %S/Inputs/many-imports/obsoleted.h 2>&1 | %FileCheck %s -// RUN: %target-swift-frontend -emit-pch %S/Inputs/many-imports/obsoleted.h -o %t.pch -// RUN: not %target-swift-frontend -typecheck %s -I %S/Inputs/many-imports -import-objc-header %t.pch 2>&1 | %FileCheck %s -// RUN: not %target-swift-frontend -typecheck %s -I %S/Inputs/many-imports -import-objc-header %S/Inputs/many-imports/obsoleted.h -pch-output-dir %t/pch 2>&1 | %FileCheck %s +// RUN: not %target-swift-frontend -typecheck %s -I %S/Inputs/many-imports -enable-objc-interop -import-objc-header %S/Inputs/many-imports/obsoleted.h 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend -enable-objc-interop -emit-pch %S/Inputs/many-imports/obsoleted.h -o %t.pch +// RUN: not %target-swift-frontend -typecheck %s -I %S/Inputs/many-imports -enable-objc-interop -import-objc-header %t.pch 2>&1 | %FileCheck %s +// RUN: not %target-swift-frontend -typecheck %s -I %S/Inputs/many-imports -enable-objc-interop -import-objc-header %S/Inputs/many-imports/obsoleted.h -pch-output-dir %t/pch 2>&1 | %FileCheck %s import Module1 import Module2 diff --git a/test/ClangImporter/diags_from_header.swift b/test/ClangImporter/diags_from_header.swift new file mode 100644 index 0000000000000..786cd40564c3c --- /dev/null +++ b/test/ClangImporter/diags_from_header.swift @@ -0,0 +1,7 @@ +// RUN: not %target-swift-frontend -typecheck %s -enable-objc-interop -import-objc-header %S/Inputs/diags_from_header.h 2>&1 | %FileCheck %s -check-prefix=CHECK -check-prefix=CHECK-WARN + +// RUN: not %target-swift-frontend -typecheck %s -enable-objc-interop -import-objc-header %S/Inputs/diags_from_header.h -Xcc -Wno-#warnings 2>&1 | %FileCheck %s -check-prefix=CHECK -check-prefix=CHECK-NO-WARN + +// CHECK-WARN: diags_from_header.h:{{.*}}:2: warning: "here is some warning about something" +// CHECK-NO-WARN-NOT: warning about something +// CHECK: diags_from_header.h:{{.*}}:2: error: "but this one is an error" diff --git a/test/ClangImporter/diags_from_module.swift b/test/ClangImporter/diags_from_module.swift index 10e4ea5c8285d..b761ed9dbd0e2 100644 --- a/test/ClangImporter/diags_from_module.swift +++ b/test/ClangImporter/diags_from_module.swift @@ -1,7 +1,11 @@ -// RUN: not %target-swift-frontend -typecheck %s -F %S/Inputs/frameworks -Xcc -D -Xcc FOO 2> %t.err.txt -// RUN: %FileCheck -input-file=%t.err.txt %s +// RUN: %empty-directory(%t) +// RUN: not %target-swift-frontend -module-cache-path %t -enable-objc-interop -typecheck %s -F %S/Inputs/frameworks -Xcc -D -Xcc FOO -o /dev/null 2>&1 | %FileCheck %s -// XFAIL: linux +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -module-cache-path %t -enable-objc-interop -typecheck %s -F %S/Inputs/frameworks -o /dev/null 2>&1 | %FileCheck %s -check-prefix CHECK-WARN + +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -module-cache-path %t -enable-objc-interop -typecheck %s -F %S/Inputs/frameworks -Xcc -Wno-#warnings -o /dev/null 2>&1 | %FileCheck -check-prefix CHECK-NO-WARN -allow-empty %s import Module @@ -9,8 +13,8 @@ import Module // CHECK: Sub2.h:2:9: error: could not build module 'Another' // CHECK: diags_from_module.swift:[[@LINE-4]]:8: error: could not build Objective-C module 'Module' -// RUN: %target-swift-frontend -typecheck %s -F %S/Inputs/frameworks 2> %tw.err.txt -// RUN: %FileCheck -input-file=%tw.err.txt %s -check-prefix=CHECK-WARN - // CHECK-WARN: Sub2.h:7:2: warning: here is some warning about something -// FIXME: show the clang warning: :1:1: warning: umbrella header for module 'Module' does not include header 'NotInModule.h' [-Wincomplete-umbrella] +// CHECK-WARN: :1:1: warning: umbrella header for module 'Module' does not include header 'NotInModule.h' +// FIXME: show [-Wincomplete-umbrella] + +// CHECK-NO-WARN-NOT: warning about something diff --git a/test/ClangImporter/enum-anon.swift b/test/ClangImporter/enum-anon.swift index f4cdb25ec31eb..8718e481304ad 100644 --- a/test/ClangImporter/enum-anon.swift +++ b/test/ClangImporter/enum-anon.swift @@ -1,5 +1,5 @@ -// RUN: %target-swift-frontend -typecheck %s -import-objc-header %S/Inputs/enum-anon.h -DDIAGS -verify -// RUN: %target-swift-frontend -emit-ir %s -import-objc-header %S/Inputs/enum-anon.h | %FileCheck -check-prefix=CHECK -check-prefix=CHECK-%target-runtime %s +// RUN: %target-swift-frontend -typecheck %s -enable-objc-interop -import-objc-header %S/Inputs/enum-anon.h -DDIAGS -verify +// RUN: %target-swift-frontend -emit-ir %s -enable-objc-interop -import-objc-header %S/Inputs/enum-anon.h | %FileCheck -check-prefix=CHECK -check-prefix=CHECK-%target-runtime %s #if DIAGS func testDiags() { diff --git a/test/ClangImporter/enum-dataflow.swift b/test/ClangImporter/enum-dataflow.swift index 9b9b023ec1079..2b1618b624419 100644 --- a/test/ClangImporter/enum-dataflow.swift +++ b/test/ClangImporter/enum-dataflow.swift @@ -1,6 +1,4 @@ -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck %s -verify - -// REQUIRES: objc_interop +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck %s -verify -enable-objc-interop import Foundation import user_objc diff --git a/test/ClangImporter/enum-error.swift b/test/ClangImporter/enum-error.swift index eccf391ec9e50..91c5e6585e367 100644 --- a/test/ClangImporter/enum-error.swift +++ b/test/ClangImporter/enum-error.swift @@ -88,13 +88,13 @@ func testError() { let terr = getErr() switch (terr) { case .TENone, .TEOne, .TETwo: break } // EXHAUSTIVE: [[@LINE-1]]:{{.+}}: warning: switch must be exhaustive - // EXHAUSTIVE: [[@LINE-2]]:{{.+}}: note: do you want to add a default clause? + // EXHAUSTIVE: [[@LINE-2]]:{{.+}}: note: handle unknown values using "@unknown default" // FIXME: This should still be an error because there are /known/ cases that // aren't covered. switch (terr) { case .TENone, .TEOne: break } - // EXHAUSTIVE: [[@LINE-1]]:{{.+}}: warning: switch must be exhaustive - // EXHAUSTIVE: [[@LINE-2]]:{{.+}}: note: do you want to add a default clause? + // EXHAUSTIVE: [[@LINE-1]]:{{.+}}: error: switch must be exhaustive + // EXHAUSTIVE: [[@LINE-2]]:{{.+}}: note: add missing case: '.TETwo' let _ = TestError.Code(rawValue: 2)! @@ -105,7 +105,8 @@ func testError() { // Allow exhaustive error codes as well. let eerr = getExhaustiveErr() - switch eerr { case .EENone, .EEOne, .EETwo: break } // ok + switch eerr { case .EENone, .EEOne, .EETwo: break } + // EXHAUSTIVE-NOT: [[@LINE-1]]:{{.+}}: {{error|warning|note}} switch eerr { case .EENone, .EEOne: break } // EXHAUSTIVE: [[@LINE-1]]:{{.+}}: error: switch must be exhaustive diff --git a/test/ClangImporter/enum-exhaustivity.swift b/test/ClangImporter/enum-exhaustivity.swift index e6052c37e0f74..285c6169c1081 100644 --- a/test/ClangImporter/enum-exhaustivity.swift +++ b/test/ClangImporter/enum-exhaustivity.swift @@ -17,7 +17,7 @@ import EnumExhaustivity func test(_ value: RegularEnum, _ exhaustiveValue: ExhaustiveEnum) { - switch value { // expected-error {{switch must be exhaustive}} expected-note {{do you want to add a default clause?}} + switch value { // expected-error {{switch must be exhaustive}} expected-note {{handle unknown values using "@unknown default"}} case .A: break case .B: break } @@ -43,7 +43,7 @@ func testAttributes( case .A, .B: break } - switch retetb { // expected-error {{switch must be exhaustive}} expected-note {{do you want to add a default clause?}} + switch retetb { // expected-error {{switch must be exhaustive}} expected-note {{handle unknown values using "@unknown default"}} case .A, .B: break } @@ -55,3 +55,10 @@ func testAttributes( case .A, .B: break } } + +func testUnavailableCases(_ value: UnavailableCases) { + switch value { // okay + case .A: break + case .B: break + } +} diff --git a/test/ClangImporter/enum-inferred-exhaustivity.swift b/test/ClangImporter/enum-inferred-exhaustivity.swift index 0fee03ef55d59..2e4b650c245c0 100644 --- a/test/ClangImporter/enum-inferred-exhaustivity.swift +++ b/test/ClangImporter/enum-inferred-exhaustivity.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -typecheck %s -import-objc-header %S/Inputs/enum-inferred-exhaustivity.h -verify -enable-nonfrozen-enum-exhaustivity-diagnostics -warnings-as-errors +// RUN: %target-swift-frontend -typecheck %s -enable-objc-interop -import-objc-header %S/Inputs/enum-inferred-exhaustivity.h -verify -enable-nonfrozen-enum-exhaustivity-diagnostics -warnings-as-errors // This is testing what happens with a CF_ENUM definition that doesn't include // any enum_extensibility attributes. As such, the test deliberately avoids @@ -6,14 +6,14 @@ func test(_ value: EnumWithDefaultExhaustivity) { // We want to assume such enums are non-frozen. - switch value { // expected-error {{switch must be exhaustive}} expected-note {{do you want to add a default clause?}} + switch value { // expected-error {{switch must be exhaustive}} expected-note {{handle unknown values using "@unknown default"}} case .loneCase: break } } func test(_ value: EnumWithSpecialAttributes) { // Same, but with the attributes macro shipped in the Xcode 9 SDKs. - switch value { // expected-error {{switch must be exhaustive}} expected-note {{do you want to add a default clause?}} + switch value { // expected-error {{switch must be exhaustive}} expected-note {{handle unknown values using "@unknown default"}} case .loneCase: break } } diff --git a/test/ClangImporter/enum-new.swift b/test/ClangImporter/enum-new.swift index b07723a196d60..abfaced2e3fb2 100644 --- a/test/ClangImporter/enum-new.swift +++ b/test/ClangImporter/enum-new.swift @@ -1,17 +1,17 @@ -// RUN: %target-swift-frontend -typecheck %s -import-objc-header %S/Inputs/enum-new.h -verify +// RUN: %target-swift-frontend -typecheck %s -import-objc-header %S/Inputs/enum-new.h -verify -enable-nonfrozen-enum-exhaustivity-diagnostics // REQUIRES: OS=macosx _ = .Red as Color _ = .Cyan as MoreColor func test() { - switch getColor() { + switch getColor() { // expected-warning {{switch must be exhaustive}} expected-note{{handle unknown values using "@unknown default"}} case .Red, .Blue, .Green: break - } // no-error + } - switch getMoreColor() { + switch getMoreColor() { // expected-warning {{switch must be exhaustive}} expected-note{{handle unknown values using "@unknown default"}} case .Yellow, .Magenta, .Black, .Cyan: break - } // no-error + } switch getColorOptions() { // expected-error {{switch must be exhaustive}} expected-note{{do you want to add a default clause?}} case ColorOptions.Pastel: break diff --git a/test/ClangImporter/enum-objc.swift b/test/ClangImporter/enum-objc.swift index e04f9ad48127e..78d63ffc2cf8f 100644 --- a/test/ClangImporter/enum-objc.swift +++ b/test/ClangImporter/enum-objc.swift @@ -1,13 +1,19 @@ -// RUN: %target-swift-frontend -emit-sil %s -import-objc-header %S/Inputs/enum-objc.h -verify +// RUN: %target-swift-frontend -emit-sil %s -enable-objc-interop -import-objc-header %S/Inputs/enum-objc.h -verify -enable-nonfrozen-enum-exhaustivity-diagnostics // REQUIRES: objc_interop -func test(_ value: SwiftEnum) { - switch value { - case .one: break - case .two: break - case .three: break - } // no error +func test(_ value: SwiftEnum, _ exhaustiveValue: ExhaustiveEnum) { + switch value { // expected-warning {{switch must be exhaustive}} expected-note {{handle unknown values using "@unknown default"}} + case .one: break + case .two: break + case .three: break + } + + switch exhaustiveValue { // ok + case .one: break + case .two: break + case .three: break + } } let _: Int = forwardBarePointer // expected-error {{cannot convert value of type '(OpaquePointer) -> Void' to specified type 'Int'}} diff --git a/test/ClangImporter/import-as-member-objc.swift b/test/ClangImporter/import-as-member-objc.swift index 78f93e6431262..792d91b17cfef 100644 --- a/test/ClangImporter/import-as-member-objc.swift +++ b/test/ClangImporter/import-as-member-objc.swift @@ -1,5 +1,4 @@ -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -I %S/../IDE/Inputs/custom-modules %s -verify -// REQUIRES: objc_interop +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -I %S/../IDE/Inputs/custom-modules %s -verify -enable-objc-interop import ImportAsMember.Class diff --git a/test/ClangImporter/import-as-member-versioned.swift b/test/ClangImporter/import-as-member-versioned.swift new file mode 100644 index 0000000000000..a953752a32884 --- /dev/null +++ b/test/ClangImporter/import-as-member-versioned.swift @@ -0,0 +1,20 @@ +// RUN: %empty-directory(%t) +// RUN: mkdir -p %t/modules + +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-module -module-name ImportAsMemberSwiftVersioned -o %t/modules/ImportAsMemberSwiftVersioned_a.partial.swiftmodule -swift-version 3 -I %S/../IDE/Inputs/custom-modules -primary-file %S/Inputs/ImportAsMemberSwiftVersioned_a.swift %S/Inputs/ImportAsMemberSwiftVersioned_b.swift + +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-module -module-name ImportAsMemberSwiftVersioned -o %t/modules/ImportAsMemberSwiftVersioned_b.partial.swiftmodule -swift-version 3 -I %S/../IDE/Inputs/custom-modules -primary-file %S/Inputs/ImportAsMemberSwiftVersioned_b.swift %S/Inputs/ImportAsMemberSwiftVersioned_a.swift + +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -merge-modules -swift-version 3 -emit-module -module-name ImportAsMemberSwiftVersioned -I %S/../IDE/Inputs/custom-modules -o %t/modules/ImportAsMemberSwiftVersioned.swiftmodule %t/modules/ImportAsMemberSwiftVersioned_a.partial.swiftmodule %t/modules/ImportAsMemberSwiftVersioned_b.partial.swiftmodule + +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-sil -swift-version 4 -O -I %S/../IDE/Inputs/custom-modules -o - %s -I %t/modules | %FileCheck %s + +// REQUIRES: objc_interop +import Foundation +import ImportAsMember.Class +import ImportAsMemberSwiftVersioned + +// CHECK: function_ref {{.*}}call_foo +public func callFoo() -> Any { + return call_foo() +} diff --git a/test/ClangImporter/import-as-member.swift b/test/ClangImporter/import-as-member.swift index 85e6c02551e09..cc848625e3398 100644 --- a/test/ClangImporter/import-as-member.swift +++ b/test/ClangImporter/import-as-member.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -F %S/Inputs/frameworks -I %S/Inputs/custom-modules %s -verify +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -typecheck -F %S/Inputs/frameworks -I %S/Inputs/custom-modules %s -verify import ImportAsMember import ImportAsMemberSubmodules diff --git a/test/ClangImporter/inherited-protocols-sil.swift b/test/ClangImporter/inherited-protocols-sil.swift index f7327d7c0bfd8..5895cf61331c3 100644 --- a/test/ClangImporter/inherited-protocols-sil.swift +++ b/test/ClangImporter/inherited-protocols-sil.swift @@ -1,6 +1,4 @@ -// RUN: %target-swift-frontend -sdk "" -emit-sil %s -import-objc-header %S/Inputs/inherited-protocols-sil.h -O - -// REQUIRES: objc_interop +// RUN: %target-swift-frontend -sdk "" -emit-sil %s -enable-objc-interop -import-objc-header %S/Inputs/inherited-protocols-sil.h -O // Protocol Extensions May Crash Swift Compiler when Whole-Module Optimization is Enabled diff --git a/test/ClangImporter/invalid_bridging_header.swift b/test/ClangImporter/invalid_bridging_header.swift index 904ba35c3a540..3bf3b7b1dbaba 100644 --- a/test/ClangImporter/invalid_bridging_header.swift +++ b/test/ClangImporter/invalid_bridging_header.swift @@ -1,7 +1,4 @@ -// RUN: not %target-swift-frontend -typecheck -import-objc-header %S/Inputs/invalid_bridging_header.h %s > %t.out 2>&1 -// RUN: %FileCheck %s < %t.out - -// REQUIRES: objc_interop +// RUN: not %target-swift-frontend -typecheck -enable-objc-interop -import-objc-header %S/Inputs/invalid_bridging_header.h %s -o - 2>&1 | %FileCheck %s // CHECK: 1:12: error: cannot find interface declaration for 'UndeclaredError' // CHECK: error: failed to import bridging header diff --git a/test/ClangImporter/macros.swift b/test/ClangImporter/macros.swift index 73202a0b1193a..577179cb5f79c 100644 --- a/test/ClangImporter/macros.swift +++ b/test/ClangImporter/macros.swift @@ -1,6 +1,4 @@ -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -verify %s - -// XFAIL: linux +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -typecheck -verify %s @_exported import macros diff --git a/test/ClangImporter/macros_redef.swift b/test/ClangImporter/macros_redef.swift index 7c8ae8316863d..319b46046a342 100644 --- a/test/ClangImporter/macros_redef.swift +++ b/test/ClangImporter/macros_redef.swift @@ -1,6 +1,5 @@ -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -I %S/Inputs/custom-modules -import-objc-header %S/Inputs/macros_redef.h -emit-silgen %s | %FileCheck -check-prefix=NEGATIVE %s - -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -I %S/Inputs/custom-modules -import-objc-header %S/Inputs/macros_redef.h -DCONFLICT -typecheck -verify %s +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -I %S/Inputs/custom-modules -enable-objc-interop -import-objc-header %S/Inputs/macros_redef.h -emit-silgen %s | %FileCheck -check-prefix=NEGATIVE %s +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -I %S/Inputs/custom-modules -enable-objc-interop -import-objc-header %S/Inputs/macros_redef.h -DCONFLICT -typecheck -verify %s // NEGATIVE-NOT: OLDTAG diff --git a/test/ClangImporter/mirror_import_overrides.swift b/test/ClangImporter/mirror_import_overrides.swift index 10658bc849b70..8f03a89d33abc 100644 --- a/test/ClangImporter/mirror_import_overrides.swift +++ b/test/ClangImporter/mirror_import_overrides.swift @@ -1,7 +1,5 @@ -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -import-objc-header %S/Inputs/mirror_import_overrides_1.h -typecheck -verify %s -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -import-objc-header %S/Inputs/mirror_import_overrides_2.h -typecheck -verify %s - -// REQUIRES: objc_interop +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -import-objc-header %S/Inputs/mirror_import_overrides_1.h -typecheck -verify %s +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -import-objc-header %S/Inputs/mirror_import_overrides_2.h -typecheck -verify %s // rdar://31471034 diff --git a/test/ClangImporter/no-fake-source-buffer-excerpts.swift b/test/ClangImporter/no-fake-source-buffer-excerpts.swift new file mode 100644 index 0000000000000..7a88953f18ef5 --- /dev/null +++ b/test/ClangImporter/no-fake-source-buffer-excerpts.swift @@ -0,0 +1,11 @@ +// REQUIRES: OS=macosx +// +// This triggers a warning about ignored configuration macros; the warning then +// attempts to emit an excerpt from one of the clang importer's fake buffers +// () which is full of 250kb of nulls. We want to check +// that we do not emit a gigantic block of nulls to stderr. +// +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -typecheck -I %S/Inputs/custom-modules -import-objc-header %S/Inputs/no-fake-source-buffer-excerpts.h %s 2>%t/errors +// RUN: od -a < %t/errors | %FileCheck %s +// CHECK-NOT: nul nul nul nul diff --git a/test/ClangImporter/no-import-objc.swift b/test/ClangImporter/no-import-objc.swift index ca0645d6e05de..ec380ca18866e 100644 --- a/test/ClangImporter/no-import-objc.swift +++ b/test/ClangImporter/no-import-objc.swift @@ -1,6 +1,4 @@ -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -I %S/Inputs/no-import-objc %s -verify - -// REQUIRES: objc_interop +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -enable-objc-interop -I %S/Inputs/no-import-objc %s -verify // Note that we don't import ObjectiveC. import People diff --git a/test/ClangImporter/non-modular-include.swift b/test/ClangImporter/non-modular-include.swift index 9db78b320fc54..7b67e4d7ef3ca 100644 --- a/test/ClangImporter/non-modular-include.swift +++ b/test/ClangImporter/non-modular-include.swift @@ -1,5 +1,6 @@ // RUN: %empty-directory(%t) -// RUN: not %target-swift-frontend -typecheck %s -I %S/Inputs/non-modular -F %S/Inputs/non-modular 2>&1 | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-%target-runtime %s +// RUN: not %target-swift-frontend -enable-objc-interop -typecheck %s -I %S/Inputs/non-modular -F %S/Inputs/non-modular 2>&1 | %FileCheck --check-prefix=CHECK -check-prefix CHECK-objc %s +// RUN: not %target-swift-frontend -disable-objc-interop -typecheck %s -I %S/Inputs/non-modular -F %S/Inputs/non-modular 2>&1 | %FileCheck --check-prefix=CHECK -check-prefix CHECK-native %s // CHECK: {{.+}}/non-modular/Foo.framework/Headers/Foo.h:1:9: error: include of non-modular header inside framework module 'Foo' // CHECK-objc: error: could not build Objective-C module 'Foo' diff --git a/test/ClangImporter/nullability.swift b/test/ClangImporter/nullability.swift index cb7e8e007b95e..82ed922953b61 100644 --- a/test/ClangImporter/nullability.swift +++ b/test/ClangImporter/nullability.swift @@ -1,17 +1,15 @@ -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -I %S/Inputs/custom-modules %s -import-underlying-module -verify - -// REQUIRES: objc_interop +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -typecheck -I %S/Inputs/custom-modules %s -import-underlying-module -verify import CoreCooling func testSomeClass(_ sc: SomeClass, osc: SomeClass?) { let ao1: Any = sc.methodA(osc) _ = ao1 - if sc.methodA(osc) == nil { } // expected-warning {{comparing non-optional value of type 'Any' to nil always returns false}} + if sc.methodA(osc) == nil { } // expected-warning {{comparing non-optional value of type 'Any' to 'nil' always returns false}} let ao2: Any = sc.methodB(nil) _ = ao2 - if sc.methodA(osc) == nil { }// expected-warning {{comparing non-optional value of type 'Any' to nil always returns false}} + if sc.methodA(osc) == nil { }// expected-warning {{comparing non-optional value of type 'Any' to 'nil' always returns false}} let ao3: Any? = sc.property.flatMap { .some($0) } _ = ao3 @@ -20,7 +18,7 @@ func testSomeClass(_ sc: SomeClass, osc: SomeClass?) { let ao4: Any = sc.methodD() _ = ao4 - if sc.methodD() == nil { } // expected-warning {{comparing non-optional value of type 'Any' to nil always returns false}} + if sc.methodD() == nil { } // expected-warning {{comparing non-optional value of type 'Any' to 'nil' always returns false}} sc.methodE(sc) sc.methodE(osc) // expected-error{{value of optional type 'SomeClass?' not unwrapped; did you mean to use '!' or '?'?}} {{17-17=!}} @@ -37,7 +35,7 @@ func testSomeClass(_ sc: SomeClass, osc: SomeClass?) { let sc2 = SomeClass(int: ci) let sc2a: SomeClass = sc2 _ = sc2a - if sc2 == nil { } // expected-warning {{comparing non-optional value of type 'SomeClass' to nil always returns false}} + if sc2 == nil { } // expected-warning {{comparing non-optional value of type 'SomeClass' to 'nil' always returns false}} let sc3 = SomeClass(double: 1.5) if sc3 == nil { } // okay @@ -47,13 +45,13 @@ func testSomeClass(_ sc: SomeClass, osc: SomeClass?) { let sc4 = sc.returnMe() let sc4a: SomeClass = sc4 _ = sc4a - if sc4 == nil { } // expected-warning {{comparing non-optional value of type 'SomeClass' to nil always returns false}} + if sc4 == nil { } // expected-warning {{comparing non-optional value of type 'SomeClass' to 'nil' always returns false}} } // Nullability with CF types. func testCF(_ fridge: CCRefrigerator) { CCRefrigeratorOpenDoSomething(fridge) // okay - CCRefrigeratorOpenDoSomething(nil) // expected-error{{nil is not compatible with expected argument type 'CCRefrigerator'}} + CCRefrigeratorOpenDoSomething(nil) // expected-error{{'nil' is not compatible with expected argument type 'CCRefrigerator'}} CCRefrigeratorOpenMaybeDoSomething(fridge) // okay CCRefrigeratorOpenMaybeDoSomething(nil) // okay diff --git a/test/ClangImporter/objc_curried_method.swift b/test/ClangImporter/objc_curried_method.swift index b0f32b55329ea..7093de29cddf9 100644 --- a/test/ClangImporter/objc_curried_method.swift +++ b/test/ClangImporter/objc_curried_method.swift @@ -1,7 +1,5 @@ -// RUN: %target-swift-frontend -swift-version 3 -typecheck %s -import-objc-header %S/Inputs/objc_curried_method.h -// RUN: %target-swift-frontend -swift-version 4 -typecheck %s -import-objc-header %S/Inputs/objc_curried_method.h - -// REQUIRES: objc_interop +// RUN: %target-swift-frontend -swift-version 3 -typecheck %s -enable-objc-interop -import-objc-header %S/Inputs/objc_curried_method.h +// RUN: %target-swift-frontend -swift-version 4 -typecheck %s -enable-objc-interop -import-objc-header %S/Inputs/objc_curried_method.h // rdar://problem/32588152 diff --git a/test/ClangImporter/objc_diags.swift b/test/ClangImporter/objc_diags.swift index 815c39ce61547..bbda2d87b4638 100644 --- a/test/ClangImporter/objc_diags.swift +++ b/test/ClangImporter/objc_diags.swift @@ -1,6 +1,4 @@ -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck %s -verify - -// REQUIRES: objc_interop +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -typecheck %s -verify import ObjectiveC diff --git a/test/ClangImporter/objc_generics_conformance.swift b/test/ClangImporter/objc_generics_conformance.swift index cfc598dcd1448..0662299c2a0dd 100644 --- a/test/ClangImporter/objc_generics_conformance.swift +++ b/test/ClangImporter/objc_generics_conformance.swift @@ -24,6 +24,16 @@ extension GenericClass : WithAssocOther { typealias Other = [T] // expected-error{{type 'GenericClass.Other' involving Objective-C type parameter 'T' cannot be used for associated type 'Other' of protocol 'WithAssocOther'}} } +protocol WithAssocSeparate { + associatedtype Separate +} + +extension GenericClass { + typealias Separate = T // expected-note {{'Separate' declared here}} +} +extension GenericClass : WithAssocSeparate { // expected-error {{type 'GenericClass.Separate' involving Objective-C type parameter 'T' cannot be used for associated type 'Separate' of protocol 'WithAssocSeparate'}} +} + protocol WithAssocElement { associatedtype Element } diff --git a/test/ClangImporter/objc_missing_designated_init.swift b/test/ClangImporter/objc_missing_designated_init.swift index 2c498fc192345..403589891c18b 100644 --- a/test/ClangImporter/objc_missing_designated_init.swift +++ b/test/ClangImporter/objc_missing_designated_init.swift @@ -1,6 +1,4 @@ -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -I %S/Inputs/custom-modules %s -swift-version 4 -verify - -// REQUIRES: objc_interop +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -typecheck -I %S/Inputs/custom-modules %s -swift-version 4 -verify import UnimportableMembers import UnimportableMembersUser diff --git a/test/ClangImporter/objc_parse.swift b/test/ClangImporter/objc_parse.swift index e348fe8d6697a..79a1ceb45b978 100644 --- a/test/ClangImporter/objc_parse.swift +++ b/test/ClangImporter/objc_parse.swift @@ -121,7 +121,7 @@ func properties(_ b: B) { // An informal property cannot be made formal in a subclass. The // formal property is simply ignored. b.informalMadeFormal() - b.informalMadeFormal = i // expected-error{{cannot assign to property: 'informalMadeFormal' is a method}} + b.informalMadeFormal = i // expected-error{{cannot assign to value: 'informalMadeFormal' is a method}} b.setInformalMadeFormal(5) b.overriddenProp = 17 @@ -215,7 +215,7 @@ func testProtocolMethods(_ b: B, p2m: P2.Type) { func testId(_ x: AnyObject) { x.perform!("foo:", with: x) // expected-warning{{no method declared with Objective-C selector 'foo:'}} - // expected-warning @-1 {{result of call is unused, but produces 'Unmanaged?'}} + // expected-warning @-1 {{result of call to function returning 'Unmanaged?' is unused}} _ = x.performAdd(1, withValue: 2, withValue: 3, withValue2: 4) _ = x.performAdd!(1, withValue: 2, withValue: 3, withValue2: 4) @@ -247,27 +247,18 @@ func almostSubscriptableValueMismatch(_ as1: AlmostSubscriptable, a: A) { func almostSubscriptableKeyMismatch(_ bc: BadCollection, key: NSString) { // FIXME: We end up importing this as read-only due to the mismatch between // getter/setter element types. - var _ : Any = bc[key] // expected-warning {{expression implicitly coerced from 'Any?' to 'Any'}} - // expected-note@-1 {{force-unwrap the value to avoid this warning}} - // expected-note@-2 {{provide a default value to avoid this warning}} - // expected-note@-3 {{explicitly cast to 'Any' with 'as Any' to silence this warning}} + var _ : Any = bc[key] } func almostSubscriptableKeyMismatchInherited(_ bc: BadCollectionChild, key: String) { - var value : Any = bc[key] // expected-warning {{expression implicitly coerced from 'Any?' to 'Any'}} - // expected-note@-1 {{force-unwrap the value to avoid this warning}} - // expected-note@-2 {{provide a default value to avoid this warning}} - // expected-note@-3 {{explicitly cast to 'Any' with 'as Any' to silence this warning}} + var value : Any = bc[key] bc[key] = value // expected-error{{cannot assign through subscript: subscript is get-only}} } func almostSubscriptableKeyMismatchInherited(_ roc: ReadOnlyCollectionChild, key: String) { - var value : Any = roc[key] // expected-warning {{expression implicitly coerced from 'Any?' to 'Any'}} - // expected-note@-1 {{force-unwrap the value to avoid this warning}} - // expected-note@-2 {{provide a default value to avoid this warning}} - // expected-note@-3 {{explicitly cast to 'Any' with 'as Any' to silence this warning}} + var value : Any = roc[key] roc[key] = value // expected-error{{cannot assign through subscript: subscript is get-only}} } @@ -407,22 +398,10 @@ func testPropertyAndMethodCollision(_ obj: PropertyAndMethodCollision, type(of: rev).classRef(rev, doSomething:#selector(getter: NSMenuItem.action)) var value: Any - value = obj.protoProp() // expected-warning {{expression implicitly coerced from 'Any?' to 'Any'}} - // expected-note@-1 {{force-unwrap the value to avoid this warning}} - // expected-note@-2 {{provide a default value to avoid this warning}} - // expected-note@-3 {{explicitly cast to 'Any' with 'as Any' to silence this warning}} - value = obj.protoPropRO() // expected-warning {{expression implicitly coerced from 'Any?' to 'Any'}} - // expected-note@-1 {{force-unwrap the value to avoid this warning}} - // expected-note@-2 {{provide a default value to avoid this warning}} - // expected-note@-3 {{explicitly cast to 'Any' with 'as Any' to silence this warning}} - value = type(of: obj).protoClassProp() // expected-warning {{expression implicitly coerced from 'Any?' to 'Any'}} - // expected-note@-1 {{force-unwrap the value to avoid this warning}} - // expected-note@-2 {{provide a default value to avoid this warning}} - // expected-note@-3 {{explicitly cast to 'Any' with 'as Any' to silence this warning}} - value = type(of: obj).protoClassPropRO() // expected-warning {{expression implicitly coerced from 'Any?' to 'Any'}} - // expected-note@-1 {{force-unwrap the value to avoid this warning}} - // expected-note@-2 {{provide a default value to avoid this warning}} - // expected-note@-3 {{explicitly cast to 'Any' with 'as Any' to silence this warning}} + value = obj.protoProp() + value = obj.protoPropRO() + value = type(of: obj).protoClassProp() + value = type(of: obj).protoClassPropRO() _ = value } @@ -509,8 +488,7 @@ func testWeakVariable() { } class IncompleteProtocolAdopter : Incomplete, IncompleteOptional { // expected-error {{type 'IncompleteProtocolAdopter' cannot conform to protocol 'Incomplete' because it has requirements that cannot be satisfied}} - // expected-error@-1{{type 'IncompleteProtocolAdopter' does not conform to protocol 'Incomplete'}} - @objc func getObject() -> AnyObject { return self } // expected-note{{candidate has non-matching type '() -> AnyObject'}} + @objc func getObject() -> AnyObject { return self } } func testNullarySelectorPieces(_ obj: AnyObject) { diff --git a/test/ClangImporter/objc_parse_verifier.swift b/test/ClangImporter/objc_parse_verifier.swift index d068379f5c267..3ad28b6f677e8 100644 --- a/test/ClangImporter/objc_parse_verifier.swift +++ b/test/ClangImporter/objc_parse_verifier.swift @@ -1,6 +1,5 @@ -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-sil -I %S/Inputs/custom-modules %s -verify > /dev/null +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -emit-sil -I %S/Inputs/custom-modules %s -verify > /dev/null -// REQUIRES: objc_interop // expected-no-diagnostics // This file tests the AST verifier, which performs extra checks when there are diff --git a/test/ClangImporter/objc_redeclared_properties.swift b/test/ClangImporter/objc_redeclared_properties.swift index 54bc8aea664b2..cdb928f37293a 100644 --- a/test/ClangImporter/objc_redeclared_properties.swift +++ b/test/ClangImporter/objc_redeclared_properties.swift @@ -1,8 +1,6 @@ -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -I %S/Inputs/custom-modules -D ONE_MODULE %s -verify -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -I %S/Inputs/custom-modules -D SUB_MODULE %s -verify -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -I %S/Inputs/custom-modules -D TWO_MODULES %s -verify - -// REQUIRES: objc_interop +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -typecheck -I %S/Inputs/custom-modules -D ONE_MODULE %s -verify +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -typecheck -I %S/Inputs/custom-modules -D SUB_MODULE %s -verify +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -typecheck -I %S/Inputs/custom-modules -D TWO_MODULES %s -verify #if ONE_MODULE import RedeclaredProperties diff --git a/test/ClangImporter/objc_redeclared_properties_incompatible.swift b/test/ClangImporter/objc_redeclared_properties_incompatible.swift index f9c479f168bb0..bdcd2c7483d5e 100644 --- a/test/ClangImporter/objc_redeclared_properties_incompatible.swift +++ b/test/ClangImporter/objc_redeclared_properties_incompatible.swift @@ -1,13 +1,11 @@ -// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -F %S/Inputs/frameworks %s 2>&1 | %FileCheck -check-prefix=CHECK -check-prefix=CHECK-PUBLIC %s +// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -typecheck -F %S/Inputs/frameworks %s 2>&1 | %FileCheck -check-prefix=CHECK -check-prefix=CHECK-PUBLIC %s // RUN: echo '#import ' > %t.h -// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -F %S/Inputs/frameworks -import-objc-header %t.h %s 2>&1 | %FileCheck -check-prefix=CHECK -check-prefix=CHECK-PRIVATE %s +// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -F %S/Inputs/frameworks -enable-objc-interop -import-objc-header %t.h %s 2>&1 | %FileCheck -check-prefix=CHECK -check-prefix=CHECK-PRIVATE %s -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-pch -F %S/Inputs/frameworks -o %t.pch %t.h -// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -F %S/Inputs/frameworks -import-objc-header %t.pch %s 2>&1 | %FileCheck -check-prefix=CHECK -check-prefix=CHECK-PRIVATE %s -// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -F %S/Inputs/frameworks -import-objc-header %t.h -pch-output-dir %t/pch %s 2>&1 | %FileCheck -check-prefix=CHECK -check-prefix=CHECK-PRIVATE %s - -// REQUIRES: objc_interop +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -emit-pch -F %S/Inputs/frameworks -o %t.pch %t.h +// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -F %S/Inputs/frameworks -enable-objc-interop -import-objc-header %t.pch %s 2>&1 | %FileCheck -check-prefix=CHECK -check-prefix=CHECK-PRIVATE %s +// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -F %S/Inputs/frameworks -enable-objc-interop -import-objc-header %t.h -pch-output-dir %t/pch %s 2>&1 | %FileCheck -check-prefix=CHECK -check-prefix=CHECK-PRIVATE %s import PrivatelyReadwrite diff --git a/test/ClangImporter/objc_runtime_visible.swift b/test/ClangImporter/objc_runtime_visible.swift index 76389a5175a24..cd347c26bcc4d 100644 --- a/test/ClangImporter/objc_runtime_visible.swift +++ b/test/ClangImporter/objc_runtime_visible.swift @@ -1,6 +1,4 @@ -// RUN: %target-swift-frontend -typecheck -disable-objc-attr-requires-foundation-module -I %S/../Inputs/custom-modules %s -verify - -// REQUIRES: objc_interop +// RUN: %target-swift-frontend -typecheck -enable-objc-interop -disable-objc-attr-requires-foundation-module -I %S/../Inputs/custom-modules %s -verify import ObjCRuntimeVisible diff --git a/test/ClangImporter/overlay_with_submodule.swift b/test/ClangImporter/overlay_with_submodule.swift index dcc902c212058..d9ddae03bcf8d 100644 --- a/test/ClangImporter/overlay_with_submodule.swift +++ b/test/ClangImporter/overlay_with_submodule.swift @@ -1,6 +1,4 @@ -// RUN: %target-swift-frontend -emit-module %s -sdk %S/Inputs -module-name HasSubmodule -I %S/Inputs/custom-modules -o %t - -// REQUIRES: objc_interop +// RUN: %target-swift-frontend -enable-objc-interop -emit-module %s -sdk %S/Inputs -module-name HasSubmodule -I %S/Inputs/custom-modules -o %t @_exported import HasSubmodule @_exported import HasSubmodule.Submodule diff --git a/test/ClangImporter/pch-bridging-header-deps.swift b/test/ClangImporter/pch-bridging-header-deps.swift index 18846207d6897..d94eb1a8b623d 100644 --- a/test/ClangImporter/pch-bridging-header-deps.swift +++ b/test/ClangImporter/pch-bridging-header-deps.swift @@ -1,4 +1,3 @@ -// REQUIRES: objc_interop // RUN: rm -f %t.* // // Generate a bridging PCH, use it in a swift file, and check that the swift file's .swiftdeps diff --git a/test/ClangImporter/pch-bridging-header-vs-modular-interface-defn.swift b/test/ClangImporter/pch-bridging-header-vs-modular-interface-defn.swift index 01e96a529dd52..70241e23006ee 100644 --- a/test/ClangImporter/pch-bridging-header-vs-modular-interface-defn.swift +++ b/test/ClangImporter/pch-bridging-header-vs-modular-interface-defn.swift @@ -77,8 +77,7 @@ // RUN: rm -rf %t // RUN: mkdir -p %t/Headers/Simple // RUN: ln -s %S/Inputs/frameworks/Simple.framework/Headers/Simple.h %t/Headers/Simple/Simple.h -// RUN: %target-build-swift -emit-module -module-name test -Xfrontend -disable-deserialization-recovery -v -F %S/Inputs/frameworks -Xcc "-I%t/Headers" -module-cache-path %t/clang-module-cache -import-objc-header %S/Inputs/pch-bridging-header-with-non-modular-import.h %S/Inputs/other.swift %s -// REQUIRES: objc_interop +// RUN: %target-build-swift -emit-module -module-name test -Xfrontend -enable-objc-interop -Xfrontend -disable-deserialization-recovery -v -F %S/Inputs/frameworks -Xcc "-I%t/Headers" -module-cache-path %t/clang-module-cache -import-objc-header %S/Inputs/pch-bridging-header-with-non-modular-import.h %S/Inputs/other.swift %s import Simple class Foo: SimpleProtocol { diff --git a/test/ClangImporter/pch-bridging-header-with-another-bridging-header.swift b/test/ClangImporter/pch-bridging-header-with-another-bridging-header.swift new file mode 100644 index 0000000000000..63d232667208f --- /dev/null +++ b/test/ClangImporter/pch-bridging-header-with-another-bridging-header.swift @@ -0,0 +1,21 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -emit-module -import-objc-header %S/Inputs/pch-bridging-header-with-another-bridging-header/app.h -I %S/Inputs/pch-bridging-header-with-another-bridging-header -module-name App -emit-module-path %t/App.swiftmodule %S/../Inputs/empty.swift +// RUN: llvm-bcanalyzer -dump %t/App.swiftmodule | %FileCheck %s + +// CHECK: IMPORTED_HEADER{{.*}}Inputs/pch-bridging-header-with-another-bridging-header/app.h + +// Now load the app-module-with-bridging-header along with another bridging +// header that we precompile. This is going to the frontend directly to make +// sure we validate PCH inputs (because -pch-disable-validation wasn't passed). +// This is deliberately run twice to test what happens when the PCH is already +// there. (It used to crash.) + +// RUN: cp %S/Inputs/pch-bridging-header-with-another-bridging-header/unit-tests.h %t/unit-tests.h +// RUN: %target-swift-frontend -typecheck -pch-output-dir %t -import-objc-header %t/unit-tests.h -I %S/Inputs/pch-bridging-header-with-another-bridging-header -I %t %s +// RUN: %target-swift-frontend -typecheck -pch-output-dir %t -import-objc-header %t/unit-tests.h -I %S/Inputs/pch-bridging-header-with-another-bridging-header -I %t %s +// RUN: echo >> %t/unit-tests.h +// RUN: %target-swift-frontend -typecheck -pch-output-dir %t -import-objc-header %t/unit-tests.h -I %S/Inputs/pch-bridging-header-with-another-bridging-header -I %t %s + +import App + +_ = app_function(2) \ No newline at end of file diff --git a/test/ClangImporter/private_frameworks_autolink.swift b/test/ClangImporter/private_frameworks_autolink.swift new file mode 100644 index 0000000000000..545b180d422f1 --- /dev/null +++ b/test/ClangImporter/private_frameworks_autolink.swift @@ -0,0 +1,16 @@ +// RUN: %empty-directory(%t) + +// FIXME: BEGIN -enable-source-import hackaround +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk-nosource) -emit-module -o %t %clang-importer-sdk-path/swift-modules/CoreGraphics.swift +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk-nosource -I %t) -emit-module -o %t %clang-importer-sdk-path/swift-modules/Foundation.swift +// FIXME: END -enable-source-import hackaround + +// Check that the autolink information is appropriate (do not link against SomeKitCore). +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk-nosource -I %t) -emit-ir -o %t/private_frameworks_autolink.ll -F %S/Inputs/privateframeworks/withprivate-autolink %s +// RUN: %FileCheck %s < %t/private_frameworks_autolink.ll +// CHECK-NOT: !{!"-framework", !"SomeKitCore"} + +// REQUIRES: objc_interop + +import SomeKitCore +import SomeKit diff --git a/test/ClangImporter/private_frameworks_autolink2.swift b/test/ClangImporter/private_frameworks_autolink2.swift new file mode 100644 index 0000000000000..7d09ba9580bf8 --- /dev/null +++ b/test/ClangImporter/private_frameworks_autolink2.swift @@ -0,0 +1,16 @@ +// RUN: %empty-directory(%t) + +// FIXME: BEGIN -enable-source-import hackaround +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk-nosource) -emit-module -o %t %clang-importer-sdk-path/swift-modules/CoreGraphics.swift +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk-nosource -I %t) -emit-module -o %t %clang-importer-sdk-path/swift-modules/Foundation.swift +// FIXME: END -enable-source-import hackaround + +// Check that the autolink information is appropriate (do not link against SomeKitCore). +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk-nosource -I %t) -emit-ir -o %t/private_frameworks_autolink2.ll -F %S/Inputs/privateframeworks/withprivate-autolink %s +// RUN: %FileCheck %s < %t/private_frameworks_autolink2.ll +// CHECK-NOT: !{!"-framework", !"SomeKit"} +// CHECK: !{!"-framework", !"SomeKitCore"} + +// REQUIRES: objc_interop + +import SomeKitCore diff --git a/test/ClangImporter/protocol-conformance-in-extension.swift b/test/ClangImporter/protocol-conformance-in-extension.swift index 69a4c832d5595..b391f288ba5a5 100644 --- a/test/ClangImporter/protocol-conformance-in-extension.swift +++ b/test/ClangImporter/protocol-conformance-in-extension.swift @@ -1,8 +1,6 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -emit-module -o %t/a~partial.swiftmodule -I %S/Inputs/custom-modules -module-name TEST -primary-file %s -// RUN: %target-swift-frontend -emit-module -o %t/test.swiftmodule -I %S/Inputs/custom-modules -module-name TEST %t/a~partial.swiftmodule - -// REQUIRES: objc_interop +// RUN: %target-swift-frontend -enable-objc-interop -emit-module -o %t/a~partial.swiftmodule -I %S/Inputs/custom-modules -module-name TEST -primary-file %s +// RUN: %target-swift-frontend -enable-objc-interop -emit-module -o %t/test.swiftmodule -I %S/Inputs/custom-modules -module-name TEST %t/a~partial.swiftmodule import TestProtocols diff --git a/test/ClangImporter/serialization-search-paths.swift b/test/ClangImporter/serialization-search-paths.swift index d13e44647774e..af6b3394eae80 100644 --- a/test/ClangImporter/serialization-search-paths.swift +++ b/test/ClangImporter/serialization-search-paths.swift @@ -1,8 +1,6 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -emit-module-path %t/SerializationHelper.swiftmodule -I %S/Inputs/custom-modules -F %S/Inputs/frameworks -sdk "" -disable-objc-attr-requires-foundation-module %S/Inputs/SerializationHelper.swift -// RUN: %target-swift-frontend -typecheck -I %t %s -sdk "" -verify - -// XFAIL: linux +// RUN: %target-swift-frontend -emit-module-path %t/SerializationHelper.swiftmodule -I %S/Inputs/custom-modules -F %S/Inputs/frameworks -sdk "" -enable-objc-interop -disable-objc-attr-requires-foundation-module %S/Inputs/SerializationHelper.swift +// RUN: %target-swift-frontend -enable-objc-interop -typecheck -I %t %s -sdk "" -verify import SerializationHelper import Module diff --git a/test/ClangImporter/serialization.swift b/test/ClangImporter/serialization.swift index 9e4a3291f7a0e..ca628e3e27169 100644 --- a/test/ClangImporter/serialization.swift +++ b/test/ClangImporter/serialization.swift @@ -1,8 +1,6 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -emit-module-path %t/SerializationHelper.swiftmodule -I %S/Inputs/custom-modules %S/Inputs/SerializationHelper.swift -sdk "" -disable-objc-attr-requires-foundation-module -// RUN: %target-swift-frontend -typecheck -sdk "" -I %t -I %S/Inputs/custom-modules %s -verify - -// XFAIL: linux +// RUN: %target-swift-frontend -emit-module-path %t/SerializationHelper.swiftmodule -I %S/Inputs/custom-modules %S/Inputs/SerializationHelper.swift -sdk "" -enable-objc-interop -disable-objc-attr-requires-foundation-module +// RUN: %target-swift-frontend -enable-objc-interop -typecheck -sdk "" -I %t -I %S/Inputs/custom-modules %s -verify import SerializationHelper diff --git a/test/ClangImporter/static_inline.swift b/test/ClangImporter/static_inline.swift index a18d5a03fb796..d946dc59fdca4 100644 --- a/test/ClangImporter/static_inline.swift +++ b/test/ClangImporter/static_inline.swift @@ -2,9 +2,9 @@ // Check if SIL printing+parsing of a clang imported function works. -// RUN: %target-swift-frontend -parse-as-library -module-name=static_inline -emit-sil %S/Inputs/static_inline.swift -import-objc-header %S/Inputs/static_inline.h -o %t/static_inline.sil +// RUN: %target-swift-frontend -parse-as-library -module-name=static_inline -emit-sil %S/Inputs/static_inline.swift -enable-objc-interop -import-objc-header %S/Inputs/static_inline.h -o %t/static_inline.sil // RUN: %FileCheck < %t/static_inline.sil %s -// RUN: %target-swift-frontend -parse-as-library -module-name=static_inline -O -emit-ir %t/static_inline.sil -import-objc-header %S/Inputs/static_inline.h | %FileCheck --check-prefix=CHECK-IR %s +// RUN: %target-swift-frontend -parse-as-library -module-name=static_inline -O -emit-ir %t/static_inline.sil -enable-objc-interop -import-objc-header %S/Inputs/static_inline.h | %FileCheck --check-prefix=CHECK-IR %s // CHECK: sil shared [serializable] [clang c_inline_func] @c_inline_func : $@convention(c) (Int32) -> Int32 diff --git a/test/ClangImporter/static_inline_serialize.swift b/test/ClangImporter/static_inline_serialize.swift index 087282cb52a20..2af3cf677f0fb 100644 --- a/test/ClangImporter/static_inline_serialize.swift +++ b/test/ClangImporter/static_inline_serialize.swift @@ -2,9 +2,9 @@ // Try serialization of clang-generated functions. -// RUN: %target-swift-frontend -parse-as-library -module-name=static_inline -emit-module %S/Inputs/static_inline.swift -import-objc-header %S/Inputs/static_inline.h -o %t/static_inline.swiftmodule -// RUN: %target-swift-frontend -module-name test -O -emit-sil %s -I %t | %FileCheck %s -// RUN: %target-swift-frontend -module-name test -O -emit-ir %s -I %t | %FileCheck --check-prefix=CHECK-IR %s +// RUN: %target-swift-frontend -parse-as-library -module-name=static_inline -emit-module %S/Inputs/static_inline.swift -enable-objc-interop -import-objc-header %S/Inputs/static_inline.h -o %t/static_inline.swiftmodule +// RUN: %target-swift-frontend -module-name test -O -emit-sil %s -I %t -enable-objc-interop | %FileCheck %s +// RUN: %target-swift-frontend -module-name test -O -emit-ir %s -I %t -enable-objc-interop | %FileCheck --check-prefix=CHECK-IR %s // CHECK: sil shared_external [clang c_inline_func] @c_inline_func : $@convention(c) (Int32) -> Int32 diff --git a/test/ClangImporter/submodules-bridging-header.swift b/test/ClangImporter/submodules-bridging-header.swift index 4da9a8f2ff857..e640a1f1581ec 100644 --- a/test/ClangImporter/submodules-bridging-header.swift +++ b/test/ClangImporter/submodules-bridging-header.swift @@ -1,8 +1,8 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -verify -I %S/Inputs/custom-modules/ -import-objc-header %S/Inputs/submodules-bridging-header.h %s +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -verify -I %S/Inputs/custom-modules/ -enable-objc-interop -import-objc-header %S/Inputs/submodules-bridging-header.h %s -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-pch -o %t/submodules-bridging-header.pch %S/Inputs/submodules-bridging-header.h -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -verify -I %S/Inputs/custom-modules/ -import-objc-header %t/submodules-bridging-header.pch %s +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -emit-pch -o %t/submodules-bridging-header.pch %S/Inputs/submodules-bridging-header.h +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -verify -I %S/Inputs/custom-modules/ -enable-objc-interop -import-objc-header %t/submodules-bridging-header.pch %s // From ctypes.bits submodule public var x : DWORD = MY_INT diff --git a/test/ClangImporter/submodules_indirect.swift b/test/ClangImporter/submodules_indirect.swift index 2711b7b3e7565..0ddcd93b4aacd 100644 --- a/test/ClangImporter/submodules_indirect.swift +++ b/test/ClangImporter/submodules_indirect.swift @@ -1,10 +1,8 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -verify -I %S/Inputs/custom-modules/ %s -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -o %t -emit-module -I %S/Inputs/custom-modules/ %s -module-name submodules -// RUN: echo 'import submodules; let s = "\(x), \(y)"' | %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck - -I %t -I %S/Inputs/custom-modules/ -// RUN: echo 'import submodules; let s = "\(x), \(y)"' | not %target-swift-frontend -typecheck - -I %t -I %S/Inputs/custom-modules/ 2>&1 | %FileCheck -check-prefix=MISSING %s - -// XFAIL: linux +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -typecheck -verify -I %S/Inputs/custom-modules/ %s +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -o %t -emit-module -I %S/Inputs/custom-modules/ %s -module-name submodules +// RUN: echo 'import submodules; let s = "\(x), \(y)"' | %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -typecheck - -I %t -I %S/Inputs/custom-modules/ +// RUN: echo 'import submodules; let s = "\(x), \(y)"' | not %target-swift-frontend -enable-objc-interop -typecheck - -I %t -I %S/Inputs/custom-modules/ 2>&1 | %FileCheck -check-prefix=MISSING %s import ctypes_bits_exported // MISSING: could not build Objective-C module 'ctypes_bits_exported' diff --git a/test/Compatibility/accessibility.swift b/test/Compatibility/accessibility.swift index a6eed5479da22..09a90c083d544 100644 --- a/test/Compatibility/accessibility.swift +++ b/test/Compatibility/accessibility.swift @@ -19,40 +19,40 @@ private protocol PrivateProto { } public struct PublicStruct: PublicProto, InternalProto, FilePrivateProto, PrivateProto { - private func publicReq() {} // expected-error {{method 'publicReq()' must be declared public because it matches a requirement in public protocol 'PublicProto'}} {{3-10=public}} - private func internalReq() {} // expected-error {{method 'internalReq()' must be declared internal because it matches a requirement in internal protocol 'InternalProto'}} {{3-10=internal}} - private func filePrivateReq() {} // expected-error {{method 'filePrivateReq()' must be declared fileprivate because it matches a requirement in fileprivate protocol 'FilePrivateProto'}} {{3-10=fileprivate}} - private func privateReq() {} // expected-error {{method 'privateReq()' must be declared fileprivate because it matches a requirement in private protocol 'PrivateProto'}} {{3-10=fileprivate}} + private func publicReq() {} // expected-error {{method 'publicReq()' must be declared public because it matches a requirement in public protocol 'PublicProto'}} {{none}} expected-note {{mark the instance method as 'public' to satisfy the requirement}} {{3-10=public}} + private func internalReq() {} // expected-error {{method 'internalReq()' must be declared internal because it matches a requirement in internal protocol 'InternalProto'}} {{none}} expected-note {{mark the instance method as 'internal' to satisfy the requirement}} {{3-10=internal}} + private func filePrivateReq() {} // expected-error {{method 'filePrivateReq()' must be declared fileprivate because it matches a requirement in fileprivate protocol 'FilePrivateProto'}} {{none}} expected-note {{mark the instance method as 'fileprivate' to satisfy the requirement}} {{3-10=fileprivate}} + private func privateReq() {} // expected-error {{method 'privateReq()' must be declared fileprivate because it matches a requirement in private protocol 'PrivateProto'}} {{none}} expected-note {{mark the instance method as 'fileprivate' to satisfy the requirement}} {{3-10=fileprivate}} public var publicVar = 0 } // expected-note@+1 * {{type declared here}} internal struct InternalStruct: PublicProto, InternalProto, FilePrivateProto, PrivateProto { - private func publicReq() {} // expected-error {{method 'publicReq()' must be as accessible as its enclosing type because it matches a requirement in protocol 'PublicProto'}} {{3-10=internal}} - private func internalReq() {} // expected-error {{method 'internalReq()' must be declared internal because it matches a requirement in internal protocol 'InternalProto'}} {{3-10=internal}} - private func filePrivateReq() {} // expected-error {{method 'filePrivateReq()' must be declared fileprivate because it matches a requirement in fileprivate protocol 'FilePrivateProto'}} {{3-10=fileprivate}} - private func privateReq() {} // expected-error {{method 'privateReq()' must be declared fileprivate because it matches a requirement in private protocol 'PrivateProto'}} {{3-10=fileprivate}} + private func publicReq() {} // expected-error {{method 'publicReq()' must be as accessible as its enclosing type because it matches a requirement in protocol 'PublicProto'}} {{none}} expected-note {{mark the instance method as 'internal' to satisfy the requirement}} {{3-10=internal}} + private func internalReq() {} // expected-error {{method 'internalReq()' must be declared internal because it matches a requirement in internal protocol 'InternalProto'}} {{none}} expected-note {{mark the instance method as 'internal' to satisfy the requirement}} {{3-10=internal}} + private func filePrivateReq() {} // expected-error {{method 'filePrivateReq()' must be declared fileprivate because it matches a requirement in fileprivate protocol 'FilePrivateProto'}} {{none}} expected-note {{mark the instance method as 'fileprivate' to satisfy the requirement}} {{3-10=fileprivate}} + private func privateReq() {} // expected-error {{method 'privateReq()' must be declared fileprivate because it matches a requirement in private protocol 'PrivateProto'}} {{none}} expected-note {{mark the instance method as 'fileprivate' to satisfy the requirement}} {{3-10=fileprivate}} public var publicVar = 0 } // expected-note@+1 * {{type declared here}} fileprivate struct FilePrivateStruct: PublicProto, InternalProto, FilePrivateProto, PrivateProto { - private func publicReq() {} // expected-error {{method 'publicReq()' must be as accessible as its enclosing type because it matches a requirement in protocol 'PublicProto'}} {{3-10=fileprivate}} - private func internalReq() {} // expected-error {{method 'internalReq()' must be as accessible as its enclosing type because it matches a requirement in protocol 'InternalProto'}} {{3-10=fileprivate}} - private func filePrivateReq() {} // expected-error {{method 'filePrivateReq()' must be declared fileprivate because it matches a requirement in fileprivate protocol 'FilePrivateProto'}} {{3-10=fileprivate}} - private func privateReq() {} // expected-error {{method 'privateReq()' must be declared fileprivate because it matches a requirement in private protocol 'PrivateProto'}} {{3-10=fileprivate}} + private func publicReq() {} // expected-error {{method 'publicReq()' must be as accessible as its enclosing type because it matches a requirement in protocol 'PublicProto'}} {{none}} expected-note {{mark the instance method as 'fileprivate' to satisfy the requirement}} {{3-10=fileprivate}} + private func internalReq() {} // expected-error {{method 'internalReq()' must be as accessible as its enclosing type because it matches a requirement in protocol 'InternalProto'}} {{none}} expected-note {{mark the instance method as 'fileprivate' to satisfy the requirement}} {{3-10=fileprivate}} + private func filePrivateReq() {} // expected-error {{method 'filePrivateReq()' must be declared fileprivate because it matches a requirement in fileprivate protocol 'FilePrivateProto'}} {{none}} expected-note {{mark the instance method as 'fileprivate' to satisfy the requirement}} {{3-10=fileprivate}} + private func privateReq() {} // expected-error {{method 'privateReq()' must be declared fileprivate because it matches a requirement in private protocol 'PrivateProto'}} {{none}} expected-note {{mark the instance method as 'fileprivate' to satisfy the requirement}} {{3-10=fileprivate}} public var publicVar = 0 } // expected-note@+1 * {{type declared here}} private struct PrivateStruct: PublicProto, InternalProto, FilePrivateProto, PrivateProto { - private func publicReq() {} // expected-error {{method 'publicReq()' must be as accessible as its enclosing type because it matches a requirement in protocol 'PublicProto'}} {{3-10=fileprivate}} - private func internalReq() {} // expected-error {{method 'internalReq()' must be as accessible as its enclosing type because it matches a requirement in protocol 'InternalProto'}} {{3-10=fileprivate}} - private func filePrivateReq() {} // expected-error {{method 'filePrivateReq()' must be declared fileprivate because it matches a requirement in fileprivate protocol 'FilePrivateProto'}} {{3-10=fileprivate}} - private func privateReq() {} // expected-error {{method 'privateReq()' must be declared fileprivate because it matches a requirement in private protocol 'PrivateProto'}} {{3-10=fileprivate}} + private func publicReq() {} // expected-error {{method 'publicReq()' must be as accessible as its enclosing type because it matches a requirement in protocol 'PublicProto'}} {{none}} expected-note {{mark the instance method as 'fileprivate' to satisfy the requirement}} {{3-10=fileprivate}} + private func internalReq() {} // expected-error {{method 'internalReq()' must be as accessible as its enclosing type because it matches a requirement in protocol 'InternalProto'}} {{none}} expected-note {{mark the instance method as 'fileprivate' to satisfy the requirement}} {{3-10=fileprivate}} + private func filePrivateReq() {} // expected-error {{method 'filePrivateReq()' must be declared fileprivate because it matches a requirement in fileprivate protocol 'FilePrivateProto'}} {{none}} expected-note {{mark the instance method as 'fileprivate' to satisfy the requirement}} {{3-10=fileprivate}} + private func privateReq() {} // expected-error {{method 'privateReq()' must be declared fileprivate because it matches a requirement in private protocol 'PrivateProto'}} {{none}} expected-note {{mark the instance method as 'fileprivate' to satisfy the requirement}} {{3-10=fileprivate}} public var publicVar = 0 } @@ -140,7 +140,7 @@ private extension PrivateStruct { public struct PublicStructDefaultMethods: PublicProto, InternalProto, PrivateProto { - func publicReq() {} // expected-error {{method 'publicReq()' must be declared public because it matches a requirement in public protocol 'PublicProto'}} {{3-3=public }} + func publicReq() {} // expected-error {{method 'publicReq()' must be declared public because it matches a requirement in public protocol 'PublicProto'}} {{none}} expected-note {{mark the instance method as 'public' to satisfy the requirement}} {{3-3=public }} func internalReq() {} func privateReq() {} } @@ -430,6 +430,7 @@ class DefaultSubclassPublic : PublicClass {} class DefaultSubclassInternal : InternalClass {} class DefaultSubclassPrivate : PrivateClass {} // expected-error {{class must be declared private or fileprivate because its superclass is private}} +// expected-note@+1 * {{superclass is declared here}} public class PublicGenericClass {} // expected-note@+2 * {{type declared here}} // expected-note@+1 * {{superclass is declared here}} @@ -441,7 +442,10 @@ open class OpenConcreteSubclassInternal : InternalGenericClass {} // expect public class PublicConcreteSubclassPublic : PublicGenericClass {} public class PublicConcreteSubclassInternal : InternalGenericClass {} // expected-warning {{class should not be declared public because its superclass is internal}} public class PublicConcreteSubclassPrivate : PrivateGenericClass {} // expected-warning {{class should not be declared public because its superclass is private}} -public class PublicConcreteSubclassPublicPrivateArg : PublicGenericClass {} // expected-warning {{class should not be declared public because its superclass is private}} +public class PublicConcreteSubclassPublicPrivateArg : PublicGenericClass {} // expected-warning {{class should not be declared public because its superclass uses a private type as a generic parameter}} +public class PublicConcreteSubclassPublicInternalArg : PublicGenericClass {} // expected-warning {{class should not be declared public because its superclass uses an internal type as a generic parameter}} +open class OpenConcreteSubclassPublicFilePrivateArg : PublicGenericClass {} // expected-warning {{class should not be declared open because its superclass uses a fileprivate type as a generic parameter}} expected-error {{superclass 'PublicGenericClass' of open class must be open}} +internal class InternalConcreteSubclassPublicFilePrivateArg : InternalGenericClass {} // expected-warning {{class should not be declared internal because its superclass uses a private type as a generic parameter}} open class OpenGenericSubclassInternal : InternalGenericClass {} // expected-warning {{class should not be declared open because its superclass is internal}} expected-error {{superclass 'InternalGenericClass' of open class must be open}} public class PublicGenericSubclassPublic : PublicGenericClass {} @@ -502,8 +506,8 @@ internal protocol InternalMutationOperations { } public struct AccessorsControl : InternalMutationOperations { - private var size = 0 // expected-error {{property 'size' must be declared internal because it matches a requirement in internal protocol 'InternalMutationOperations'}} {{3-10=internal}} - private subscript (_: Int) -> Int { // expected-error {{subscript must be declared internal because it matches a requirement in internal protocol 'InternalMutationOperations'}} {{3-10=internal}} + private var size = 0 // expected-error {{property 'size' must be declared internal because it matches a requirement in internal protocol 'InternalMutationOperations'}} {{none}} expected-note {{mark the var as 'internal' to satisfy the requirement}} {{3-10=internal}} + private subscript (_: Int) -> Int { // expected-error {{subscript must be declared internal because it matches a requirement in internal protocol 'InternalMutationOperations'}} {{none}} expected-note {{mark the subscript as 'internal' to satisfy the requirement}} {{3-10=internal}} get { return 42 } set {} } @@ -511,8 +515,8 @@ public struct AccessorsControl : InternalMutationOperations { public struct PrivateSettersPublic : InternalMutationOperations { // Please don't change the formatting here; it's a precise fix-it test. - public private(set) var size = 0 // expected-error {{setter for property 'size' must be declared internal because it matches a requirement in internal protocol 'InternalMutationOperations'}} {{10-17=internal}} - public private(set) subscript (_: Int) -> Int { // expected-error {{subscript setter must be declared internal because it matches a requirement in internal protocol 'InternalMutationOperations'}} {{10-17=internal}} + public private(set) var size = 0 // expected-error {{setter for property 'size' must be declared internal because it matches a requirement in internal protocol 'InternalMutationOperations'}} {{none}} expected-note {{mark the var as 'internal' to satisfy the requirement}} {{10-17=internal}} + public private(set) subscript (_: Int) -> Int { // expected-error {{subscript setter must be declared internal because it matches a requirement in internal protocol 'InternalMutationOperations'}} {{none}} expected-note {{mark the subscript as 'internal' to satisfy the requirement}} {{10-17=internal}} get { return 42 } set {} } @@ -520,9 +524,9 @@ public struct PrivateSettersPublic : InternalMutationOperations { internal struct PrivateSettersInternal : PublicMutationOperations { // Please don't change the formatting here; it's a precise fix-it test. - private(set)var size = 0 // expected-error {{setter for property 'size' must be as accessible as its enclosing type because it matches a requirement in protocol 'PublicMutationOperations'}} {{3-15=}} + private(set)var size = 0 // expected-error {{setter for property 'size' must be as accessible as its enclosing type because it matches a requirement in protocol 'PublicMutationOperations'}} {{none}} expected-note {{mark the var as 'internal' to satisfy the requirement}} {{3-15=}} - internal private(set)subscript (_: Int) -> Int { // expected-error {{subscript setter must be as accessible as its enclosing type because it matches a requirement in protocol 'PublicMutationOperations'}} {{12-24=}} + internal private(set)subscript (_: Int) -> Int { // expected-error {{subscript setter must be as accessible as its enclosing type because it matches a requirement in protocol 'PublicMutationOperations'}} {{none}} expected-note {{mark the subscript as 'internal' to satisfy the requirement}} {{12-24=}} get { return 42 } set {} } @@ -543,7 +547,7 @@ internal struct PrivateSettersForReadOnlyInternal : PublicReadOnlyOperations { public struct PrivateSettersForReadOnlyPublic : PublicReadOnlyOperations { public private(set) var size = 0 // no-warning - internal private(set) subscript (_: Int) -> Int { // expected-error {{subscript must be declared public because it matches a requirement in public protocol 'PublicReadOnlyOperations'}} {{3-11=public}} + internal private(set) subscript (_: Int) -> Int { // expected-error {{subscript must be declared public because it matches a requirement in public protocol 'PublicReadOnlyOperations'}} {{none}} expected-note {{mark the subscript as 'public' to satisfy the requirement}} {{3-11=public}} get { return 42 } set {} } @@ -567,10 +571,11 @@ private protocol PrivateOperatorProto { } public struct PublicOperatorAdopter : PublicOperatorProto { + // expected-error@-1 {{method '!' must be declared public because it matches a requirement in public protocol 'PublicOperatorProto'}} {{none}} fileprivate struct Inner : PublicOperatorProto { } } -private prefix func !(input: PublicOperatorAdopter) -> PublicOperatorAdopter { // expected-error {{method '!' must be declared public because it matches a requirement in public protocol 'PublicOperatorProto'}} {{1-8=public}} +private prefix func !(input: PublicOperatorAdopter) -> PublicOperatorAdopter { // expected-note {{mark the operator function as 'public' to satisfy the requirement}} {{1-8=public}} return input } private prefix func !(input: PublicOperatorAdopter.Inner) -> PublicOperatorAdopter.Inner { @@ -578,10 +583,11 @@ private prefix func !(input: PublicOperatorAdopter.Inner) -> PublicOperatorAdopt } public struct InternalOperatorAdopter : InternalOperatorProto { + // expected-error@-1 {{method '!' must be declared internal because it matches a requirement in internal protocol 'InternalOperatorProto'}} {{none}} fileprivate struct Inner : InternalOperatorProto { } } -private prefix func !(input: InternalOperatorAdopter) -> InternalOperatorAdopter { // expected-error {{method '!' must be declared internal because it matches a requirement in internal protocol 'InternalOperatorProto'}} {{1-8=internal}} +private prefix func !(input: InternalOperatorAdopter) -> InternalOperatorAdopter { // expected-note {{mark the operator function as 'internal' to satisfy the requirement}} {{1-8=internal}} return input } private prefix func !(input: InternalOperatorAdopter.Inner) -> InternalOperatorAdopter.Inner { @@ -636,17 +642,20 @@ fileprivate struct EquatablishOuterProblem { internal struct EquatablishOuterProblem2 { public struct Inner : Equatablish { - fileprivate static func ==(lhs: Inner, rhs: Inner) {} // expected-error {{method '==' must be as accessible as its enclosing type because it matches a requirement in protocol 'Equatablish'}} {{5-16=internal}} - // expected-note@-1 {{candidate has non-matching type}} + fileprivate static func ==(lhs: Inner, rhs: Inner) {} // expected-error {{method '==' must be as accessible as its enclosing type because it matches a requirement in protocol 'Equatablish'}} {{none}} + // expected-note@-1 {{mark the operator function as 'internal' to satisfy the requirement}} {{5-16=internal}} + // expected-note@-2 {{candidate has non-matching type}} } } internal struct EquatablishOuterProblem3 { public struct Inner : Equatablish { + // expected-error@-1 {{method '==' must be as accessible as its enclosing type because it matches a requirement in protocol 'Equatablish'}} {{none}} } } -private func ==(lhs: EquatablishOuterProblem3.Inner, rhs: EquatablishOuterProblem3.Inner) {} // expected-error {{method '==' must be as accessible as its enclosing type because it matches a requirement in protocol 'Equatablish'}} {{1-8=internal}} -// expected-note@-1 {{candidate has non-matching type}} +private func ==(lhs: EquatablishOuterProblem3.Inner, rhs: EquatablishOuterProblem3.Inner) {} +// expected-note@-1 {{mark the operator function as 'internal' to satisfy the requirement}} {{1-8=internal}} +// expected-note@-2 {{candidate has non-matching type}} public protocol AssocTypeProto { @@ -661,13 +670,13 @@ fileprivate struct AssocTypeOuter { fileprivate struct AssocTypeOuterProblem { internal struct Inner : AssocTypeProto { - private typealias Assoc = Int // expected-error {{type alias 'Assoc' must be as accessible as its enclosing type because it matches a requirement in protocol 'AssocTypeProto'}} {{5-12=fileprivate}} + private typealias Assoc = Int // expected-error {{type alias 'Assoc' must be as accessible as its enclosing type because it matches a requirement in protocol 'AssocTypeProto'}} {{none}} expected-note {{mark the type alias as 'fileprivate' to satisfy the requirement}} {{5-12=fileprivate}} } } internal struct AssocTypeOuterProblem2 { public struct Inner : AssocTypeProto { - fileprivate typealias Assoc = Int // expected-error {{type alias 'Assoc' must be as accessible as its enclosing type because it matches a requirement in protocol 'AssocTypeProto'}} {{5-16=internal}} + fileprivate typealias Assoc = Int // expected-error {{type alias 'Assoc' must be as accessible as its enclosing type because it matches a requirement in protocol 'AssocTypeProto'}} {{none}} expected-note {{mark the type alias as 'internal' to satisfy the requirement}} {{5-16=internal}} } } diff --git a/test/Compatibility/accessibility_private.swift b/test/Compatibility/accessibility_private.swift index 457a33343da04..1d26677c98eea 100644 --- a/test/Compatibility/accessibility_private.swift +++ b/test/Compatibility/accessibility_private.swift @@ -118,18 +118,21 @@ protocol VeryImportantProto { } private struct VIPPrivateType : VeryImportantProto { - private typealias Assoc = Int // expected-error {{type alias 'Assoc' must be as accessible as its enclosing type because it matches a requirement in protocol 'VeryImportantProto'}} + private typealias Assoc = Int // expected-error {{type alias 'Assoc' must be as accessible as its enclosing type because it matches a requirement in protocol 'VeryImportantProto'}} {{none}} + // expected-note@-1 {{mark the type alias as 'fileprivate' to satisfy the requirement}} {{3-10=fileprivate}} var value: Int } private struct VIPPrivateProp : VeryImportantProto { typealias Assoc = Int - private var value: Int // expected-error {{property 'value' must be as accessible as its enclosing type because it matches a requirement in protocol 'VeryImportantProto'}} {{3-10=fileprivate}} + private var value: Int // expected-error {{property 'value' must be as accessible as its enclosing type because it matches a requirement in protocol 'VeryImportantProto'}} {{none}} + // expected-note@-1 {{mark the var as 'fileprivate' to satisfy the requirement}} {{3-10=fileprivate}} } private struct VIPPrivateSetProp : VeryImportantProto { typealias Assoc = Int - private(set) var value: Int // expected-error {{setter for property 'value' must be as accessible as its enclosing type because it matches a requirement in protocol 'VeryImportantProto'}} {{3-10=fileprivate}} + private(set) var value: Int // expected-error {{setter for property 'value' must be as accessible as its enclosing type because it matches a requirement in protocol 'VeryImportantProto'}} {{none}} + // expected-note@-1 {{mark the var as 'fileprivate' to satisfy the requirement}} {{3-10=fileprivate}} } private class VIPPrivateSetBase { @@ -140,9 +143,11 @@ private class VIPPrivateSetSub : VIPPrivateSetBase, VeryImportantProto { // expe } private class VIPPrivateSetPropBase { - private(set) var value: Int = 0 // expected-error {{setter for property 'value' must be as accessible as its enclosing type because it matches a requirement in protocol 'VeryImportantProto'}} {{3-10=fileprivate}} + private(set) var value: Int = 0 + // expected-note@-1 {{mark the var as 'fileprivate' to satisfy the requirement}} {{3-10=fileprivate}} } private class VIPPrivateSetPropSub : VIPPrivateSetPropBase, VeryImportantProto { + // expected-error@-1 {{setter for property 'value' must be as accessible as its enclosing type because it matches a requirement in protocol 'VeryImportantProto'}} {{none}} typealias Assoc = Int } @@ -166,8 +171,10 @@ extension Container { extension Container { private struct VeryPrivateStruct { // expected-note * {{type declared here}} private typealias VeryPrivateType = Int // expected-note * {{type declared here}} + private struct VeryPrivateInnerStruct {} var privateVar: VeryPrivateType { fatalError() } // expected-warning {{property should be declared private because its type uses a private type}} var privateVar2 = VeryPrivateType() // expected-warning {{property should be declared private because its type 'Container.VeryPrivateStruct.VeryPrivateType' (aka 'Int') uses a private type}} + var privateVar3 = VeryPrivateInnerStruct() // expected-warning {{property should be declared private because its type 'Container.VeryPrivateStruct.VeryPrivateInnerStruct' uses a private type}} typealias PrivateAlias = VeryPrivateType // expected-warning {{type alias should be declared private because its underlying type uses a private type}} subscript(_: VeryPrivateType) -> Void { return () } // expected-warning {{subscript should be declared private because its index uses a private type}} func privateMethod(_: VeryPrivateType) -> Void {} // expected-warning {{method should be declared private because its parameter uses a private type}} {{none}} diff --git a/test/Compatibility/attr_objc.swift b/test/Compatibility/attr_objc.swift index a5ce19020479a..b1da854c709b6 100644 --- a/test/Compatibility/attr_objc.swift +++ b/test/Compatibility/attr_objc.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -disable-objc-attr-requires-foundation-module -typecheck -verify %s -swift-version 3 +// RUN: %target-swift-frontend -enable-objc-interop -disable-objc-attr-requires-foundation-module -typecheck -verify %s -swift-version 3 class Load1 { class func load() { } diff --git a/test/Compatibility/exhaustive_switch.swift b/test/Compatibility/exhaustive_switch.swift index 91919fd220eaa..b315f0e5990ca 100644 --- a/test/Compatibility/exhaustive_switch.swift +++ b/test/Compatibility/exhaustive_switch.swift @@ -1,5 +1,5 @@ -// RUN: %target-typecheck-verify-swift -swift-version 3 -enable-resilience -enable-nonfrozen-enum-exhaustivity-diagnostics -// RUN: %target-typecheck-verify-swift -swift-version 4 -enable-resilience -enable-nonfrozen-enum-exhaustivity-diagnostics +// RUN: %target-typecheck-verify-swift -swift-version 3 -enable-resilience +// RUN: %target-typecheck-verify-swift -swift-version 4 -enable-resilience func foo(a: Int?, b: Int?) -> Int { switch (a, b) { @@ -368,8 +368,7 @@ func checkDiagnosticMinimality(x: Runcible?) { switch (x!, x!) { // expected-error {{switch must be exhaustive}} // expected-note@-1 {{add missing case: '(.fork, _)'}} // expected-note@-2 {{add missing case: '(.hat, .hat)'}} - // expected-note@-3 {{add missing case: '(.hat, .fork)'}} - // expected-note@-4 {{add missing case: '(_, .fork)'}} + // expected-note@-3 {{add missing case: '(_, .fork)'}} case (.spoon, .spoon): break case (.spoon, .hat): @@ -381,10 +380,8 @@ func checkDiagnosticMinimality(x: Runcible?) { switch (x!, x!) { // expected-error {{switch must be exhaustive}} // expected-note@-1 {{add missing case: '(.fork, _)'}} // expected-note@-2 {{add missing case: '(.hat, .spoon)'}} - // expected-note@-3 {{add missing case: '(.hat, .fork)'}} - // expected-note@-4 {{add missing case: '(.spoon, .hat)'}} - // expected-note@-5 {{add missing case: '(.spoon, .fork)'}} - // expected-note@-6 {{add missing case: '(_, .fork)'}} + // expected-note@-3 {{add missing case: '(.spoon, .hat)'}} + // expected-note@-4 {{add missing case: '(_, .fork)'}} case (.spoon, .spoon): break case (.hat, .hat): @@ -445,7 +442,7 @@ enum ContainsOverlyLargeEnum { } func quiteBigEnough() -> Bool { - switch (OverlyLargeSpaceEnum.case1, OverlyLargeSpaceEnum.case2) { // expected-error {{switch must be exhaustive}} + switch (OverlyLargeSpaceEnum.case1, OverlyLargeSpaceEnum.case2) { // expected-error {{the compiler is unable to check that this switch is exhaustive in reasonable time}} // expected-note@-1 {{do you want to add a default clause?}} case (.case0, .case0): return true case (.case1, .case1): return true @@ -461,7 +458,7 @@ func quiteBigEnough() -> Bool { case (.case11, .case11): return true } - switch (OverlyLargeSpaceEnum.case1, OverlyLargeSpaceEnum.case2) { // expected-error {{switch must be exhaustive}} + switch (OverlyLargeSpaceEnum.case1, OverlyLargeSpaceEnum.case2) { // expected-error {{the compiler is unable to check that this switch is exhaustive in reasonable time}} // expected-note@-1 {{do you want to add a default clause?}} case (.case0, _): return true case (.case1, _): return true @@ -476,6 +473,21 @@ func quiteBigEnough() -> Bool { case (.case10, _): return true } + switch (OverlyLargeSpaceEnum.case1, OverlyLargeSpaceEnum.case2) { // expected-error {{the compiler is unable to check that this switch is exhaustive in reasonable time}} + case (.case0, _): return true + case (.case1, _): return true + case (.case2, _): return true + case (.case3, _): return true + case (.case4, _): return true + case (.case5, _): return true + case (.case6, _): return true + case (.case7, _): return true + case (.case8, _): return true + case (.case9, _): return true + case (.case10, _): return true + @unknown default: return false // expected-note {{remove '@unknown' to handle remaining values}} {{3-12=}} + } + // No diagnostic switch (OverlyLargeSpaceEnum.case1, OverlyLargeSpaceEnum.case2) { @@ -529,6 +541,10 @@ func quiteBigEnough() -> Bool { case .two: return true case .three: return true } + + // Make sure we haven't just stopped emitting diagnostics. + switch OverlyLargeSpaceEnum.case1 { // expected-error {{switch must be exhaustive}} expected-note 12 {{add missing case}} + } } indirect enum InfinitelySized { @@ -547,13 +563,13 @@ indirect enum MutuallyRecursive { func infinitelySized() -> Bool { switch (InfinitelySized.one, InfinitelySized.one) { // expected-error {{switch must be exhaustive}} - // expected-note@-1 10 {{add missing case:}} + // expected-note@-1 8 {{add missing case:}} case (.one, .one): return true case (.two, .two): return true } switch (MutuallyRecursive.one, MutuallyRecursive.one) { // expected-error {{switch must be exhaustive}} - // expected-note@-1 10 {{add missing case:}} + // expected-note@-1 8 {{add missing case:}} case (.one, .one): return true case (.two, .two): return true } @@ -807,6 +823,10 @@ public enum NonExhaustive { case a, b } +public enum NonExhaustivePayload { + case a(Int), b(Bool) +} + @_frozen public enum TemporalProxy { case seconds(Int) case milliseconds(Int) @@ -819,12 +839,12 @@ public enum NonExhaustive { // Inlinable code is considered "outside" the module and must include a default // case. @inlinable -public func testNonExhaustive(_ value: NonExhaustive, for interval: TemporalProxy, flag: Bool) { - switch value { // expected-warning {{switch must be exhaustive}} {{none}} expected-note {{do you want to add a default clause?}} {{3-3=default:\n<#code#>\n}} +public func testNonExhaustive(_ value: NonExhaustive, _ payload: NonExhaustivePayload, for interval: TemporalProxy, flag: Bool) { + switch value { // expected-error {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.b'}} {{none}} case .a: break } - switch value { // expected-warning {{switch must be exhaustive}} {{none}} expected-note {{do you want to add a default clause?}} {{3-3=default:\n<#code#>\n}} + switch value { // no-warning case .a: break case .b: break } @@ -834,9 +854,29 @@ public func testNonExhaustive(_ value: NonExhaustive, for interval: TemporalProx case .b: break default: break // no-warning } + + switch value { + case .a: break + case .b: break + @unknown case _: break // no-warning + } + + switch value { // expected-warning {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.b'}} {{none}} + case .a: break + @unknown case _: break + } + + switch value { // expected-warning {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.a'}} {{none}} expected-note {{add missing case: '.b'}} {{none}} + @unknown case _: break + } + + switch value { + case _: break + @unknown case _: break // expected-warning {{case is already handled by previous patterns; consider removing it}} + } // Test being part of other spaces. - switch value as Optional { // expected-warning {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.some(_)'}} + switch value as Optional { // no-warning case .a?: break case .b?: break case nil: break @@ -847,41 +887,129 @@ public func testNonExhaustive(_ value: NonExhaustive, for interval: TemporalProx case nil: break } // no-warning - switch (value, flag) { // expected-warning {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '(_, false)'}} + switch value as Optional { + case .a?: break + case .b?: break + case nil: break + @unknown case _: break + } // no-warning + + switch (value, flag) { // no-warning + case (.a, _): break + case (.b, false): break + case (_, true): break + } + + switch (value, flag) { case (.a, _): break case (.b, false): break case (_, true): break + @unknown case _: break + } // no-warning + + switch (flag, value) { // no-warning + case (_, .a): break + case (false, .b): break + case (true, _): break } - switch (flag, value) { // expected-warning {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '(false, _)'}} + switch (flag, value) { case (_, .a): break case (false, .b): break case (true, _): break + @unknown case _: break + } // no-warning + + switch (value, value) { // no-warning + case (.a, _), (_, .a): break + case (.b, _), (_, .b): break } + switch (value, value) { + case (.a, _), (_, .a): break + case (.b, _), (_, .b): break + @unknown case _: break + } // no-warning + // Test interaction with @_downgrade_exhaustivity_check. - switch (value, interval) { // expected-warning {{switch must be exhaustive}} {{none}} - // expected-note@-1 {{add missing case: '(_, .milliseconds(_))'}} - // expected-note@-2 {{add missing case: '(_, .microseconds(_))'}} - // expected-note@-3 {{add missing case: '(_, .nanoseconds(_))'}} - // expected-note@-4 {{add missing case: '(_, .never)'}} + switch (value, interval) { // no-warning case (_, .seconds): break case (.a, _): break case (.b, _): break } - switch (value, interval) { // expected-warning {{switch must be exhaustive}} {{none}} - // expected-note@-1 {{add missing case: '(_, .seconds(_))'}} - // expected-note@-2 {{add missing case: '(_, .milliseconds(_))'}} - // expected-note@-3 {{add missing case: '(_, .microseconds(_))'}} - // expected-note@-4 {{add missing case: '(_, .nanoseconds(_))'}} + switch (value, interval) { // no-warning case (_, .never): break case (.a, _): break case (.b, _): break } + + + // Test payloaded enums. + switch payload { // expected-error {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.b(_)'}} {{none}} + case .a: break + } + + switch payload { // no-warning + case .a: break + case .b: break + } + + switch payload { + case .a: break + case .b: break + default: break // no-warning + } + + switch payload { + case .a: break + case .b: break + @unknown case _: break // no-warning + } + + switch payload { // expected-warning {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.b(_)'}} {{none}} + case .a: break + @unknown case _: break + } + + switch payload { // expected-error {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.b(true)'}} {{none}} + case .a: break + case .b(false): break + } + + switch payload { // expected-warning {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.b(true)'}} {{none}} + case .a: break + case .b(false): break + @unknown case _: break + } + + // Test fully-covered switches. + switch interval { + case .seconds, .milliseconds, .microseconds, .nanoseconds: break + case .never: break + @unknown case _: break // expected-warning {{case is already handled by previous patterns; consider removing it}} + } + + switch flag { + case true: break + case false: break + @unknown case _: break // expected-warning {{case is already handled by previous patterns; consider removing it}} + } + + switch flag as Optional { + case _?: break + case nil: break + @unknown case _: break // expected-warning {{case is already handled by previous patterns; consider removing it}} + } + + switch (flag, value) { + case (true, _): break + case (false, _): break + @unknown case _: break // expected-warning {{case is already handled by previous patterns; consider removing it}} + } } -public func testNonExhaustiveWithinModule(_ value: NonExhaustive, for interval: TemporalProxy, flag: Bool) { +public func testNonExhaustiveWithinModule(_ value: NonExhaustive, _ payload: NonExhaustivePayload, for interval: TemporalProxy, flag: Bool) { switch value { // expected-error {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.b'}} case .a: break } @@ -897,6 +1025,26 @@ public func testNonExhaustiveWithinModule(_ value: NonExhaustive, for interval: default: break // no-warning } + switch value { + case .a: break + case .b: break + @unknown case _: break // no-warning + } + + switch value { // expected-warning {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.b'}} {{none}} + case .a: break + @unknown case _: break + } + + switch value { // expected-warning {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.a'}} {{none}} expected-note {{add missing case: '.b'}} {{none}} + @unknown case _: break + } + + switch value { + case _: break + @unknown case _: break // expected-warning {{case is already handled by previous patterns; consider removing it}} + } + // Test being part of other spaces. switch value as Optional { // no-warning case .a?: break @@ -921,6 +1069,21 @@ public func testNonExhaustiveWithinModule(_ value: NonExhaustive, for interval: case (true, _): break } + switch (value, value) { // no-warning + case (.a, _): break + case (.b, _): break + case (_, .a): break + case (_, .b): break + } + + switch (value, value) { // no-warning + case (.a, _): break + case (.b, _): break + case (_, .a): break + case (_, .b): break + @unknown case _: break + } + // Test interaction with @_downgrade_exhaustivity_check. switch (value, interval) { // no-warning case (_, .seconds): break @@ -933,4 +1096,91 @@ public func testNonExhaustiveWithinModule(_ value: NonExhaustive, for interval: case (.a, _): break case (.b, _): break } + + // Test payloaded enums. + switch payload { // expected-error {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.b(_)'}} {{none}} + case .a: break + } + + switch payload { // no-warning + case .a: break + case .b: break + } + + switch payload { + case .a: break + case .b: break + default: break // no-warning + } + + switch payload { + case .a: break + case .b: break + @unknown case _: break // no-warning + } + + switch payload { // expected-warning {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.b(_)'}} {{none}} + case .a: break + @unknown case _: break + } + + switch payload { // expected-error {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.b(true)'}} {{none}} + case .a: break + case .b(false): break + } + + switch payload { // expected-warning {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.b(true)'}} {{none}} + case .a: break + case .b(false): break + @unknown case _: break + } +} + +enum UnavailableCase { + case a + case b + @available(*, unavailable) + case oopsThisWasABadIdea +} + +enum UnavailableCaseOSSpecific { + case a + case b + +#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + @available(macOS, unavailable) + @available(iOS, unavailable) + @available(tvOS, unavailable) + @available(watchOS, unavailable) + case unavailableOnAllTheseApplePlatforms +#else + @available(*, unavailable) + case dummyCaseForOtherPlatforms +#endif +} + +enum UnavailableCaseOSIntroduced { + case a + case b + + @available(macOS 50, iOS 50, tvOS 50, watchOS 50, *) + case notYetIntroduced +} + +func testUnavailableCases(_ x: UnavailableCase, _ y: UnavailableCaseOSSpecific, _ z: UnavailableCaseOSIntroduced) { + switch x { + case .a: break + case .b: break + } // no-error + + switch y { + case .a: break + case .b: break + } // no-error + + switch z { + case .a: break + case .b: break + case .notYetIntroduced: break + } // no-error } diff --git a/test/Compatibility/override.swift b/test/Compatibility/override.swift index ddfde0ef79f63..f6a94b4fe694a 100644 --- a/test/Compatibility/override.swift +++ b/test/Compatibility/override.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift -parse-as-library -swift-version 3 +// RUN: %target-typecheck-verify-swift -parse-as-library -swift-version 3 -enable-objc-interop class A { @objc func objcVirtualFunction() { } // expected-note{{overridden declaration is here}} diff --git a/test/Compatibility/ownership_protocol.swift b/test/Compatibility/ownership_protocol.swift index 4fbfa6c55b07b..c8f62b7bf9d69 100644 --- a/test/Compatibility/ownership_protocol.swift +++ b/test/Compatibility/ownership_protocol.swift @@ -4,9 +4,15 @@ class SomeClass {} protocol P { - weak var foo: SomeClass? { get set } // expected-warning {{'weak' should not be applied to a property declaration in a protocol and will be disallowed in future versions}} - unowned var foo2: SomeClass { get set } // expected-warning {{'unowned' should not be applied to a property declaration in a protocol and will be disallowed in future versions}} - weak var foo3: Int? { get set } // expected-error {{'weak' may only be applied to class and class-bound protocol types, not 'Int'}} - unowned var foo4: Int { get set } // expected-error {{'unowned' may only be applied to class and class-bound protocol types, not 'Int'}} + // expected-warning@+1 {{'weak' should not be applied to a property declaration in a protocol and will be disallowed in future versions}} + weak var foo: SomeClass? { get set } + // expected-warning@+1 {{'unowned' should not be applied to a property declaration in a protocol and will be disallowed in future versions}} + unowned var foo2: SomeClass { get set } + // expected-warning@+2 {{'weak' should not be applied to a property declaration in a protocol and will be disallowed in future versions}} + // expected-error@+1 {{'weak' may only be applied to class and class-bound protocol types, not 'Int'}} + weak var foo3: Int? { get set } + // expected-warning@+2 {{'unowned' should not be applied to a property declaration in a protocol and will be disallowed in future versions}} + // expected-error@+1 {{'unowned' may only be applied to class and class-bound protocol types, not 'Int'}} + unowned var foo4: Int { get set } } diff --git a/test/Compatibility/protocol_composition.swift b/test/Compatibility/protocol_composition.swift index 21ea3ddd76a40..ec60815dd91df 100644 --- a/test/Compatibility/protocol_composition.swift +++ b/test/Compatibility/protocol_composition.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %utils/split_file.py -o %t %s +// RUN: %{python} %utils/split_file.py -o %t %s // RUN: %target-swift-frontend -typecheck -primary-file %t/swift3.swift %t/common.swift -verify -swift-version 3 // RUN: %target-swift-frontend -typecheck -primary-file %t/swift4.swift %t/common.swift -verify -swift-version 4 diff --git a/test/Compatibility/stdlib_generic_typealiases.swift b/test/Compatibility/stdlib_generic_typealiases.swift index eed311043a2dc..75574a3e920ac 100644 --- a/test/Compatibility/stdlib_generic_typealiases.swift +++ b/test/Compatibility/stdlib_generic_typealiases.swift @@ -2,8 +2,7 @@ struct RequiresStrideable { } -extension CountableRange { // expected-warning{{'CountableRange' is deprecated: renamed to 'Range'}} - // expected-note@-1{{use 'Range' instead}}{{11-25=Range}} +extension CountableRange { func testStrideable() { _ = RequiresStrideable() } @@ -24,7 +23,5 @@ extension DictionaryIndex { } extension CountableRange where Element == Int { - // expected-warning@-1{{'CountableRange' is deprecated: renamed to 'Range'}} - // expected-note@-2{{use 'Range' instead}} func getLowerBoundAsInt() -> Int { return lowerBound } } diff --git a/test/Compatibility/throws_identifier.swift b/test/Compatibility/throws_identifier.swift index 9b840c45c1bf2..9def511068ed3 100644 --- a/test/Compatibility/throws_identifier.swift +++ b/test/Compatibility/throws_identifier.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %utils/split_file.py -o %t %s +// RUN: %{python} %utils/split_file.py -o %t %s // RUN: %target-swift-frontend -parse -primary-file %t/swift3.swift -verify -swift-version 3 // RUN: %target-swift-frontend -parse -primary-file %t/swift4.swift -verify -swift-version 4 diff --git a/test/Compatibility/tuple_arguments_3.swift b/test/Compatibility/tuple_arguments_3.swift index 6537f717e5c71..28eab95050505 100644 --- a/test/Compatibility/tuple_arguments_3.swift +++ b/test/Compatibility/tuple_arguments_3.swift @@ -547,7 +547,7 @@ do { _ = InitTuple(3, 4) // expected-error {{initializer expects a single parameter of type '(Int, Int)'}} {{17-17=(}} {{21-21=)}} _ = InitTuple((3, 4)) - _ = InitLabeledTuple(x: 3, 4) // expected-error {{extra argument in call}} + _ = InitLabeledTuple(x: 3, 4) // expected-error {{initializer expects a single parameter of type '(Int, Int)'}} _ = InitLabeledTuple(x: (3, 4)) } @@ -601,7 +601,7 @@ do { _ = s2[(3, 4)] let s3 = SubscriptLabeledTuple() - _ = s3[x: 3, 4] // expected-error {{extra argument in call}} + _ = s3[x: 3, 4] // expected-error {{subscript expects a single parameter of type '(Int, Int)'}} _ = s3[x: (3, 4)] } @@ -651,7 +651,7 @@ do { _ = Enum.tuple(3, 4) // expected-error {{enum element 'tuple' expects a single parameter of type '(Int, Int)'}} {{18-18=(}} {{22-22=)}} _ = Enum.tuple((3, 4)) - _ = Enum.labeledTuple(x: 3, 4) // expected-error {{extra argument in call}} + _ = Enum.labeledTuple(x: 3, 4) // expected-error {{enum element 'labeledTuple' expects a single parameter of type '(Int, Int)'}} _ = Enum.labeledTuple(x: (3, 4)) } @@ -712,7 +712,7 @@ do { sTwo.generic(3.0, 4.0) // expected-error {{instance method 'generic' expects a single parameter of type '(Double, Double)'}} {{16-16=(}} {{24-24=)}} sTwo.generic((3.0, 4.0)) - sTwo.genericLabeled(x: 3.0, 4.0) // expected-error {{extra argument in call}} + sTwo.genericLabeled(x: 3.0, 4.0) // expected-error {{instance method 'genericLabeled' expects a single parameter of type '(Double, Double)'}} sTwo.genericLabeled(x: (3.0, 4.0)) } @@ -793,7 +793,7 @@ do { sTwo.mutatingGeneric(3.0, 4.0) // expected-error {{instance method 'mutatingGeneric' expects a single parameter of type '(Double, Double)'}} {{24-24=(}} {{32-32=)}} sTwo.mutatingGeneric((3.0, 4.0)) - sTwo.mutatingGenericLabeled(x: 3.0, 4.0) // expected-error {{extra argument in call}} + sTwo.mutatingGenericLabeled(x: 3.0, 4.0) // expected-error {{instance method 'mutatingGenericLabeled' expects a single parameter of type '(Double, Double)'}} sTwo.mutatingGenericLabeled(x: (3.0, 4.0)) } @@ -954,7 +954,7 @@ do { _ = GenericInitTuple(3, 4) // expected-error {{initializer expects a single parameter of type '(T, T)'}} {{24-24=(}} {{28-28=)}} _ = GenericInitTuple((3, 4)) - _ = GenericInitLabeledTuple(x: 3, 4) // expected-error {{extra argument in call}} + _ = GenericInitLabeledTuple(x: 3, 4) // expected-error {{initializer expects a single parameter of type '(T, T)'}} _ = GenericInitLabeledTuple(x: (3, 4)) } @@ -971,7 +971,7 @@ do { _ = GenericInitTuple(3, 4) // expected-error {{initializer expects a single parameter of type '(T, T)'}} {{29-29=(}} {{33-33=)}} _ = GenericInitTuple((3, 4)) - _ = GenericInitLabeledTuple(x: 3, 4) // expected-error {{extra argument in call}} + _ = GenericInitLabeledTuple(x: 3, 4) // expected-error {{initializer expects a single parameter of type '(T, T)'}} _ = GenericInitLabeledTuple(x: (3, 4)) } @@ -1087,7 +1087,7 @@ do { _ = s3[(3.0, 4.0)] let s3a = GenericSubscriptLabeledTuple() - _ = s3a[x: 3.0, 4.0] // expected-error {{extra argument in call}} + _ = s3a[x: 3.0, 4.0] // expected-error {{subscript expects a single parameter of type '(T, T)'}} _ = s3a[x: (3.0, 4.0)] } @@ -1163,9 +1163,9 @@ do { _ = GenericEnum<(Int, Int)>.one(3, 4) _ = GenericEnum<(Int, Int)>.one((3, 4)) // Does not diagnose in Swift 3 mode - _ = GenericEnum<(Int, Int)>.labeled(x: 3, 4) // expected-error {{extra argument in call}} + _ = GenericEnum<(Int, Int)>.labeled(x: 3, 4) // expected-error {{enum element 'labeled' expects a single parameter of type '(Int, Int)'}} _ = GenericEnum<(Int, Int)>.labeled(x: (3, 4)) - _ = GenericEnum<(Int, Int)>.labeled(3, 4) // expected-error {{extra argument in call}} + _ = GenericEnum<(Int, Int)>.labeled(3, 4) // expected-error {{enum element 'labeled' expects a single parameter of type '(Int, Int)'}} _ = GenericEnum<(Int, Int)>.labeled((3, 4)) // expected-error {{missing argument label 'x:' in call}} _ = GenericEnum.two(3, 4) @@ -1282,7 +1282,7 @@ do { sTwo.requirement(3.0, 4.0) // expected-error {{instance method 'requirement' expects a single parameter of type '(Double, Double)'}} {{20-20=(}} {{28-28=)}} sTwo.requirement((3.0, 4.0)) - sTwo.requirementLabeled(x: 3.0, 4.0) // expected-error {{extra argument in call}} + sTwo.requirementLabeled(x: 3.0, 4.0) // expected-error {{instance method 'requirementLabeled' expects a single parameter of type '(Double, Double)'}} sTwo.requirementLabeled(x: (3.0, 4.0)) } diff --git a/test/Compatibility/tuple_arguments_4.swift b/test/Compatibility/tuple_arguments_4.swift index c9941261ff242..9920533b5ca2b 100644 --- a/test/Compatibility/tuple_arguments_4.swift +++ b/test/Compatibility/tuple_arguments_4.swift @@ -531,7 +531,7 @@ do { _ = InitTuple(3, 4) // expected-error {{initializer expects a single parameter of type '(Int, Int)'}} {{17-17=(}} {{21-21=)}} _ = InitTuple((3, 4)) - _ = InitLabeledTuple(x: 3, 4) // expected-error {{extra argument in call}} + _ = InitLabeledTuple(x: 3, 4) // expected-error {{initializer expects a single parameter of type '(Int, Int)'}} _ = InitLabeledTuple(x: (3, 4)) } @@ -601,7 +601,7 @@ do { _ = s2[d] let s3 = SubscriptLabeledTuple() - _ = s3[x: 3, 4] // expected-error {{extra argument in call}} + _ = s3[x: 3, 4] // expected-error {{subscript expects a single parameter of type '(Int, Int)'}} _ = s3[x: (3, 4)] } @@ -636,7 +636,7 @@ do { _ = Enum.tuple(3, 4) // expected-error {{enum element 'tuple' expects a single parameter of type '(Int, Int)'}} {{18-18=(}} {{22-22=)}} _ = Enum.tuple((3, 4)) - _ = Enum.labeledTuple(x: 3, 4) // expected-error {{extra argument in call}} + _ = Enum.labeledTuple(x: 3, 4) // expected-error {{enum element 'labeledTuple' expects a single parameter of type '(Int, Int)'}} _ = Enum.labeledTuple(x: (3, 4)) } @@ -697,7 +697,7 @@ do { sTwo.generic(3.0, 4.0) // expected-error {{instance method 'generic' expects a single parameter of type '(Double, Double)'}} {{16-16=(}} {{24-24=)}} sTwo.generic((3.0, 4.0)) - sTwo.genericLabeled(x: 3.0, 4.0) // expected-error {{extra argument in call}} + sTwo.genericLabeled(x: 3.0, 4.0) // expected-error {{instance method 'genericLabeled' expects a single parameter of type '(Double, Double)'}} sTwo.genericLabeled(x: (3.0, 4.0)) } @@ -778,7 +778,7 @@ do { sTwo.mutatingGeneric(3.0, 4.0) // expected-error {{instance method 'mutatingGeneric' expects a single parameter of type '(Double, Double)'}} {{24-24=(}} {{32-32=)}} sTwo.mutatingGeneric((3.0, 4.0)) - sTwo.mutatingGenericLabeled(x: 3.0, 4.0) // expected-error {{extra argument in call}} + sTwo.mutatingGenericLabeled(x: 3.0, 4.0) // expected-error {{instance method 'mutatingGenericLabeled' expects a single parameter of type '(Double, Double)'}} sTwo.mutatingGenericLabeled(x: (3.0, 4.0)) } @@ -939,7 +939,7 @@ do { _ = GenericInitTuple(3, 4) // expected-error {{initializer expects a single parameter of type '(T, T)'}} {{24-24=(}} {{28-28=)}} _ = GenericInitTuple((3, 4)) - _ = GenericInitLabeledTuple(x: 3, 4) // expected-error {{extra argument in call}} + _ = GenericInitLabeledTuple(x: 3, 4) // expected-error {{initializer expects a single parameter of type '(T, T)'}} _ = GenericInitLabeledTuple(x: (3, 4)) } @@ -956,7 +956,7 @@ do { _ = GenericInitTuple(3, 4) // expected-error {{initializer expects a single parameter of type '(T, T)'}} {{29-29=(}} {{33-33=)}} _ = GenericInitTuple((3, 4)) - _ = GenericInitLabeledTuple(x: 3, 4) // expected-error {{extra argument in call}} + _ = GenericInitLabeledTuple(x: 3, 4) // expected-error {{initializer expects a single parameter of type '(T, T)'}} _ = GenericInitLabeledTuple(x: (3, 4)) } @@ -1070,7 +1070,7 @@ do { _ = s3[(3.0, 4.0)] let s3a = GenericSubscriptLabeledTuple() - _ = s3a[x: 3.0, 4.0] // expected-error {{extra argument in call}} + _ = s3a[x: 3.0, 4.0] // expected-error {{subscript expects a single parameter of type '(T, T)'}} _ = s3a[x: (3.0, 4.0)] } @@ -1144,9 +1144,9 @@ do { _ = GenericEnum<(Int, Int)>.one(3, 4) // expected-error {{enum element 'one' expects a single parameter of type '(Int, Int)'}} {{35-35=(}} {{39-39=)}} _ = GenericEnum<(Int, Int)>.one((3, 4)) - _ = GenericEnum<(Int, Int)>.labeled(x: 3, 4) // expected-error {{extra argument in call}} + _ = GenericEnum<(Int, Int)>.labeled(x: 3, 4) // expected-error {{enum element 'labeled' expects a single parameter of type '(Int, Int)'}} _ = GenericEnum<(Int, Int)>.labeled(x: (3, 4)) - _ = GenericEnum<(Int, Int)>.labeled(3, 4) // expected-error {{extra argument in call}} + _ = GenericEnum<(Int, Int)>.labeled(3, 4) // expected-error {{enum element 'labeled' expects a single parameter of type '(Int, Int)'}} _ = GenericEnum<(Int, Int)>.labeled((3, 4)) // expected-error {{missing argument label 'x:' in call}} _ = GenericEnum.two(3, 4) @@ -1263,7 +1263,7 @@ do { sTwo.requirement(3.0, 4.0) // expected-error {{instance method 'requirement' expects a single parameter of type '(Double, Double)'}} {{20-20=(}} {{28-28=)}} sTwo.requirement((3.0, 4.0)) - sTwo.requirementLabeled(x: 3.0, 4.0) // expected-error {{extra argument in call}} + sTwo.requirementLabeled(x: 3.0, 4.0) // expected-error {{instance method 'requirementLabeled' expects a single parameter of type '(Double, Double)'}} sTwo.requirementLabeled(x: (3.0, 4.0)) } @@ -1670,3 +1670,13 @@ do { func h(_: ()) {} // expected-note {{'h' declared here}} h() // expected-error {{missing argument for parameter #1 in call}} } + + +// https://bugs.swift.org/browse/SR-7191 +class Mappable { + init(_: T) { } + func map(_ body: (T) -> U) -> U { fatalError() } +} + +let x = Mappable(()) +_ = x.map { (_: Void) in return () } diff --git a/test/Constraints/Inputs/keypath.swift b/test/Constraints/Inputs/keypath.swift new file mode 100644 index 0000000000000..44fa4a5ded8f5 --- /dev/null +++ b/test/Constraints/Inputs/keypath.swift @@ -0,0 +1,3 @@ +class C { + fileprivate(set) var i = 0 +} diff --git a/test/Constraints/array_literal.swift b/test/Constraints/array_literal.swift index 63faa1741fd29..a23fb0ab714fc 100644 --- a/test/Constraints/array_literal.swift +++ b/test/Constraints/array_literal.swift @@ -44,7 +44,7 @@ func useDict(_ d: Dict) {} useIntList([1,2,3]) useIntList([1.0,2,3]) // expected-error{{cannot convert value of type 'Double' to expected element type 'Int'}} -useIntList([nil]) // expected-error {{nil is not compatible with expected element type 'Int'}} +useIntList([nil]) // expected-error {{'nil' is not compatible with expected element type 'Int'}} useDoubleList([1.0,2,3]) useDoubleList([1.0,2.0,3.0]) diff --git a/test/Constraints/casts.swift b/test/Constraints/casts.swift index d7be38e83ef95..2483a5e5b6e8a 100644 --- a/test/Constraints/casts.swift +++ b/test/Constraints/casts.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift +// RUN: %target-typecheck-verify-swift -enable-objc-interop class B { init() {} diff --git a/test/Constraints/casts_objc.swift b/test/Constraints/casts_objc.swift index 89c12ac903093..d988aa147b3d3 100644 --- a/test/Constraints/casts_objc.swift +++ b/test/Constraints/casts_objc.swift @@ -34,12 +34,7 @@ func nsobject_as_class_cast(_ x: NSObject, _: T) { func test(_ a : CFString!, b : CFString) { let dict = NSMutableDictionary() let object = NSObject() - dict[a] = object // expected-warning {{expression implicitly coerced from 'CFString?' to 'Any'}} - // expected-note@-1 {{force-unwrap the value to avoid this warning}} - // expected-note@-2 {{provide a default value to avoid this warning}} - // expected-note@-3 {{explicitly cast to 'Any' with 'as Any' to silence this warning}} - - + dict[a] = object dict[b] = object } diff --git a/test/Constraints/closures.swift b/test/Constraints/closures.swift index 5e20b6ba99fa6..e2d31a2a48adf 100644 --- a/test/Constraints/closures.swift +++ b/test/Constraints/closures.swift @@ -299,7 +299,7 @@ struct Thing { init?() {} } // This throws a compiler error -let things = Thing().map { thing in // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{34-34=-> (Thing) }} +let things = Thing().map { thing in // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{34-34=-> Thing }} // Commenting out this makes it compile _ = thing return thing @@ -358,7 +358,7 @@ func someGeneric19997471(_ x: T) { func rdar21078316() { var foo : [String : String]? var bar : [(String, String)]? - bar = foo.map { ($0, $1) } // expected-error {{contextual closure type '([String : String]) -> [(String, String)]' expects 1 argument, but 2 were used in closure body}} + bar = foo.map { ($0, $1) } // expected-error {{contextual closure type '([String : String]) throws -> [(String, String)]' expects 1 argument, but 2 were used in closure body}} } @@ -552,7 +552,7 @@ extension A_SR_5030 { func foo() -> B_SR_5030 { let tt : B_SR_5030 = sr5030_exFalso() return tt.map { x in (idx: x) } - // expected-error@-1 {{cannot convert value of type '(idx: (Int))' to closure result type 'Int'}} + // expected-error@-1 {{cannot convert value of type '(idx: Int)' to closure result type 'Int'}} } } @@ -690,3 +690,24 @@ func rdar37790062() { _ = S({ bzz(C1()) }, { bar() }) // expected-warning {{result of call to 'bzz' is unused}} _ = S({ faz(C2()) }, { bar() }) // expected-warning {{result of call to 'faz' is unused}} } + +// +typealias KeyedItem = (key: K, value: T) + +protocol Node { + associatedtype T + associatedtype E + associatedtype K + var item: E {get set} + var children: [(key: K, value: T)] {get set} +} + +extension Node { + func getChild(for key:K)->(key: K, value: T) { + return children.first(where: { (item:KeyedItem) -> Bool in + return item.key == key + // expected-error@-1 {{binary operator '==' cannot be applied to operands of type '_' and 'Self.K'}} + // expected-note@-2 {{overloads for '==' exist with these partially matching parameter lists:}} + }) + } +} diff --git a/test/Constraints/diag_ambiguities.swift b/test/Constraints/diag_ambiguities.swift index 4de29e1189379..3e399f80e673f 100644 --- a/test/Constraints/diag_ambiguities.swift +++ b/test/Constraints/diag_ambiguities.swift @@ -51,10 +51,7 @@ struct SR3715 { func take(_ a: [Any]) {} func test() { - take([overloaded]) // expected-warning {{expression implicitly coerced from 'Int?' to 'Any'}} - // expected-note@-1 {{force-unwrap the value to avoid this warning}} - // expected-note@-2 {{provide a default value to avoid this warning}} - // expected-note@-3 {{explicitly cast to 'Any' with 'as Any' to silence this warning}} + take([overloaded]) } } diff --git a/test/Constraints/diagnostics.swift b/test/Constraints/diagnostics.swift index 86ce2e7c64843..1ba0fa727e053 100644 --- a/test/Constraints/diagnostics.swift +++ b/test/Constraints/diagnostics.swift @@ -161,7 +161,8 @@ func rdar20142523() { // Bad diagnostic for invalid method call in boolean expression: (_, ExpressibleByIntegerLiteral)' is not convertible to 'ExpressibleByIntegerLiteral func rdar21080030() { var s = "Hello" - if s.count() == 0 {} // expected-error{{cannot call value of non-function type 'Int'}}{{13-15=}} + // SR-7599: This should be `cannot_call_non_function_value` + if s.count() == 0 {} // expected-error{{cannot invoke 'count' with no arguments}} } // QoI: problem with return type inference mis-diagnosed as invalid arguments @@ -210,7 +211,7 @@ class r20201968C { // QoI: Poor compilation error calling assert func r21459429(_ a : Int) { assert(a != nil, "ASSERT COMPILATION ERROR") - // expected-warning @-1 {{comparing non-optional value of type 'Int' to nil always returns true}} + // expected-warning @-1 {{comparing non-optional value of type 'Int' to 'nil' always returns true}} } @@ -335,7 +336,7 @@ f7(1)(b: 1.0) // expected-error{{extraneous argument label 'b:' in call}} let f8 = f7(2) _ = f8(1) -f8(10) // expected-warning {{result of call is unused, but produces 'Int'}} +f8(10) // expected-warning {{result of call to function returning 'Int' is unused}} f8(1.0) // expected-error {{cannot convert value of type 'Double' to expected argument type 'Int'}} f8(b: 1.0) // expected-error {{extraneous argument label 'b:' in call}} @@ -486,12 +487,44 @@ class B { static func f1(_ a : AOpts) {} } +class GenClass {} +struct GenStruct {} +enum GenEnum {} func test(_ a : B) { - B.f1(nil) // expected-error {{nil is not compatible with expected argument type 'AOpts'}} - a.function(42, a: nil) //expected-error {{nil is not compatible with expected argument type 'AOpts'}} - a.function(42, nil) //expected-error {{missing argument label 'a:' in call}} - a.f2(nil) // expected-error {{nil is not compatible with expected argument type 'AOpts'}} + B.f1(nil) // expected-error {{'nil' is not compatible with expected argument type 'AOpts'}} + a.function(42, a: nil) // expected-error {{'nil' is not compatible with expected argument type 'AOpts'}} + a.function(42, nil) // expected-error {{missing argument label 'a:' in call}} + a.f2(nil) // expected-error {{'nil' is not compatible with expected argument type 'AOpts'}} + + func foo1(_ arg: Bool) -> Int {return nil} + func foo2(_ arg: T) -> GenClass {return nil} + func foo3(_ arg: T) -> GenStruct {return nil} + func foo4(_ arg: T) -> GenEnum {return nil} + // expected-error@-4 {{'nil' is incompatible with return type 'Int'}} + // expected-error@-4 {{'nil' is incompatible with return type 'GenClass'}} + // expected-error@-4 {{'nil' is incompatible with return type 'GenStruct'}} + // expected-error@-4 {{'nil' is incompatible with return type 'GenEnum'}} + + let clsr1: () -> Int = {return nil} + let clsr2: () -> GenClass = {return nil} + let clsr3: () -> GenStruct = {return nil} + let clsr4: () -> GenEnum = {return nil} + // expected-error@-4 {{'nil' is not compatible with closure result type 'Int'}} + // expected-error@-4 {{'nil' is not compatible with closure result type 'GenClass'}} + // expected-error@-4 {{'nil' is not compatible with closure result type 'GenStruct'}} + // expected-error@-4 {{'nil' is not compatible with closure result type 'GenEnum'}} + + var number = 0 + var genClassBool = GenClass() + var funcFoo1 = foo1 + + number = nil + genClassBool = nil + funcFoo1 = nil + // expected-error@-3 {{'nil' cannot be assigned to type 'Int'}} + // expected-error@-3 {{'nil' cannot be assigned to type 'GenClass'}} + // expected-error@-3 {{'nil' cannot be assigned to type '(Bool) -> Int'}} } // QoI: invalid operator use inside a closure reported as a problem with the closure @@ -500,14 +533,14 @@ func r21684487() { var closures = Array() let testClosure = {(list: [Int]) -> Bool in return true} - let closureIndex = closures.index{$0 === testClosure} // expected-error {{cannot check reference equality of functions; operands here have types '_' and '([Int]) -> Bool'}} + let closureIndex = closures.index{$0 === testClosure} // expected-error {{cannot check reference equality of functions;}} } // QoI: special case comparisons with nil func r18397777(_ d : r21447318?) { let c = r21447318() - if c != nil { // expected-warning {{comparing non-optional value of type 'r21447318' to nil always returns true}} + if c != nil { // expected-warning {{comparing non-optional value of type 'r21447318' to 'nil' always returns true}} } if d { // expected-error {{optional type 'r21447318?' cannot be used as a boolean; test for '!= nil' instead}} {{6-6=(}} {{7-7= != nil)}} @@ -687,18 +720,18 @@ class SR1594 { func sr1594(bytes : UnsafeMutablePointer, _ i : Int?) { _ = (i === nil) // expected-error {{value of type 'Int?' cannot be compared by reference; did you mean to compare by value?}} {{12-15===}} _ = (bytes === nil) // expected-error {{type 'UnsafeMutablePointer' is not optional, value can never be nil}} - _ = (self === nil) // expected-warning {{comparing non-optional value of type 'AnyObject' to nil always returns false}} + _ = (self === nil) // expected-warning {{comparing non-optional value of type 'AnyObject' to 'nil' always returns false}} _ = (i !== nil) // expected-error {{value of type 'Int?' cannot be compared by reference; did you mean to compare by value?}} {{12-15=!=}} _ = (bytes !== nil) // expected-error {{type 'UnsafeMutablePointer' is not optional, value can never be nil}} - _ = (self !== nil) // expected-warning {{comparing non-optional value of type 'AnyObject' to nil always returns true}} + _ = (self !== nil) // expected-warning {{comparing non-optional value of type 'AnyObject' to 'nil' always returns true}} } } func nilComparison(i: Int, o: AnyObject) { - _ = i == nil // expected-warning {{comparing non-optional value of type 'Int' to nil always returns false}} - _ = nil == i // expected-warning {{comparing non-optional value of type 'Int' to nil always returns false}} - _ = i != nil // expected-warning {{comparing non-optional value of type 'Int' to nil always returns true}} - _ = nil != i // expected-warning {{comparing non-optional value of type 'Int' to nil always returns true}} + _ = i == nil // expected-warning {{comparing non-optional value of type 'Int' to 'nil' always returns false}} + _ = nil == i // expected-warning {{comparing non-optional value of type 'Int' to 'nil' always returns false}} + _ = i != nil // expected-warning {{comparing non-optional value of type 'Int' to 'nil' always returns true}} + _ = nil != i // expected-warning {{comparing non-optional value of type 'Int' to 'nil' always returns true}} // FIXME(integers): uncomment these tests once the < is no longer ambiguous // _ = i < nil // _xpected-error {{type 'Int' is not optional, value can never be nil}} @@ -710,8 +743,8 @@ func nilComparison(i: Int, o: AnyObject) { // _ = i >= nil // _xpected-error {{type 'Int' is not optional, value can never be nil}} // _ = nil >= i // _xpected-error {{type 'Int' is not optional, value can never be nil}} - _ = o === nil // expected-warning {{comparing non-optional value of type 'AnyObject' to nil always returns false}} - _ = o !== nil // expected-warning {{comparing non-optional value of type 'AnyObject' to nil always returns true}} + _ = o === nil // expected-warning {{comparing non-optional value of type 'AnyObject' to 'nil' always returns false}} + _ = o !== nil // expected-warning {{comparing non-optional value of type 'AnyObject' to 'nil' always returns true}} } func secondArgumentNotLabeled(a: Int, _ b: Int) { } @@ -750,7 +783,7 @@ func rdar27391581(_ a : Int, b : Int) -> Int { func read2(_ p: UnsafeMutableRawPointer, maxLength: Int) {} func read() -> T? { var buffer : T - let n = withUnsafePointer(to: &buffer) { (p) in + let n = withUnsafeMutablePointer(to: &buffer) { (p) in read2(UnsafePointer(p), maxLength: MemoryLayout.size) // expected-error {{cannot convert value of type 'UnsafePointer<_>' to expected argument type 'UnsafeMutableRawPointer'}} } } diff --git a/test/Constraints/dictionary_literal.swift b/test/Constraints/dictionary_literal.swift index 47fbc6dc3d0bc..afdd205e3dc91 100644 --- a/test/Constraints/dictionary_literal.swift +++ b/test/Constraints/dictionary_literal.swift @@ -18,11 +18,20 @@ func useDict(_ d: Dictionary) {} // Concrete dictionary literals. useDictStringInt(["Hello" : 1]) useDictStringInt(["Hello" : 1, "World" : 2]) -useDictStringInt(["Hello" : 1, "World" : 2.5]) // expected-error{{cannot convert value of type 'Double' to expected dictionary value type 'Int'}} -useDictStringInt([4.5 : 2]) // expected-error{{cannot convert value of type 'Double' to expected dictionary key type 'String'}} -useDictStringInt([nil : 2]) // expected-error{{nil is not compatible with expected dictionary key type 'String'}} - -useDictStringInt([7 : 1, "World" : 2]) // expected-error{{cannot convert value of type 'Int' to expected dictionary key type 'String'}} +useDictStringInt(["Hello" : 1, "World" : 2.5]) +// expected-error@-1 {{cannot convert value of type 'Double' to expected dictionary value type 'Int'}} +useDictStringInt([4.5 : 2]) +// expected-error@-1 {{cannot convert value of type 'Double' to expected dictionary key type 'String'}} +useDictStringInt([nil : 2]) +// expected-error@-1 {{'nil' is not compatible with expected dictionary key type 'String'}} +useDictStringInt([7 : 1, "World" : 2]) +// expected-error@-1 {{cannot convert value of type 'Int' to expected dictionary key type 'String'}} +useDictStringInt(["Hello" : nil]) +// expected-error@-1 {{'nil' is not compatible with expected dictionary value type 'Int'}} + +typealias FuncBoolToInt = (Bool) -> Int +let dict1: Dictionary = ["Hello": nil] +// expected-error@-1 {{'nil' is not compatible with expected dictionary value type '(Bool) -> Int'}} // Generic dictionary literals. useDict(["Hello" : 1]) diff --git a/test/Constraints/dynamic_lookup.swift b/test/Constraints/dynamic_lookup.swift index 4f5f998f8a319..a12a9cc0f74fc 100644 --- a/test/Constraints/dynamic_lookup.swift +++ b/test/Constraints/dynamic_lookup.swift @@ -117,7 +117,7 @@ obj.foo!(5) obj.foo!("hello") obj.wibble!() obj.wobble!() // expected-error{{value of type 'Id' (aka 'AnyObject') has no member 'wobble'}} -obj.ext1!() // expected-warning {{result of call is unused}} +obj.ext1!() // expected-warning {{result of call to function returning 'A' is unused}} obj.wonka!() // Same as above but without the '!' @@ -126,7 +126,7 @@ obj.foo(5) obj.foo("hello") obj.wibble() obj.wobble() // expected-error{{value of type 'Id' (aka 'AnyObject') has no member 'wobble'}} -obj.ext1() // expected-warning {{result of call is unused}} +obj.ext1() // expected-warning {{result of call to function returning 'A' is unused}} obj.wonka() // Find class methods via dynamic method lookup. @@ -144,7 +144,7 @@ var ovl1Result = obj.ovl1!() ovl1Result = A() // verify that we got an A, not a B // Same as above but without the '!' -obj.ovl1() // expected-warning {{result of call is unused}} +obj.ovl1() // expected-warning {{result of call to function returning 'A' is unused}} // Don't allow overload resolution between declarations from different // classes. @@ -163,7 +163,7 @@ var ovl4Result = obj.ovl4!() var ovl5Result = obj.ovl5!() // expected-error{{ambiguous use of 'ovl5()'}} // Same as above but without the '!' -obj.ovl4() // expected-warning {{result of call is unused}} +obj.ovl4() // expected-warning {{result of call to function returning 'B' is unused}} // Generics @@ -312,3 +312,32 @@ let _: DynamicIUO? = o[dyn_iuo] o[dyn_iuo] = dyn_iuo // expected-error {{cannot assign to immutable expression of type 'DynamicIUO??'}} o[dyn_iuo]! = dyn_iuo // expected-error {{cannot assign to immutable expression of type 'DynamicIUO?'}} o[dyn_iuo]!! = dyn_iuo // expected-error {{cannot assign to immutable expression of type 'DynamicIUO'}} + + +// Check that we avoid picking an unavailable overload if there's an +// alternative. +class OverloadedWithUnavailable1 { + @objc func overloadedWithUnavailableA() { } + + @objc + @available(swift, obsoleted: 3) + func overloadedWithUnavailableB() { } +} + +class OverloadedWithUnavailable2 { + @available(swift, obsoleted: 3) + @objc func overloadedWithUnavailableA() { } + + @objc func overloadedWithUnavailableB() { } +} + +func testOverloadedWithUnavailable(ao: AnyObject) { + ao.overloadedWithUnavailableA() + ao.overloadedWithUnavailableB() +} + +func dynamicInitCrash(ao: AnyObject.Type) { + let sdk = ao.init(blahblah: ()) + // expected-error@-1 {{argument labels '(blahblah:)' do not match any available overloads}} + // expected-note@-2 {{overloads for 'AnyObject.Type.init' exist with these partially matching parameter lists}} +} diff --git a/test/Constraints/existential_metatypes.swift b/test/Constraints/existential_metatypes.swift index 17e8ed71c918f..3b03e5ef13d0b 100644 --- a/test/Constraints/existential_metatypes.swift +++ b/test/Constraints/existential_metatypes.swift @@ -87,8 +87,5 @@ func testP3(_ p: P3, something: Something) { } func testIUOToAny(_ t: AnyObject.Type!) { - let _: Any = t // expected-warning {{expression implicitly coerced from 'AnyObject.Type?' to 'Any'}} - // expected-note@-1 {{force-unwrap the value to avoid this warning}} - // expected-note@-2 {{provide a default value to avoid this warning}} - // expected-note@-3 {{explicitly cast to 'Any' with 'as Any' to silence this warning}} + let _: Any = t } diff --git a/test/Constraints/generics.swift b/test/Constraints/generics.swift index 151ce31be16c0..2d556d05c95cb 100644 --- a/test/Constraints/generics.swift +++ b/test/Constraints/generics.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift +// RUN: %target-typecheck-verify-swift -enable-objc-interop infix operator +++ @@ -561,3 +561,20 @@ do { func rdar35890334(_ arr: inout [Int]) { _ = arr.popFirst() // expected-error {{'[Int]' requires the types '[Int]' and 'ArraySlice' be equivalent to use 'popFirst'}} } + +// rdar://problem/39616039 + +func rdar39616039() { + func foo(default: V, _ values: [String: V]) -> V { + return values["foo"] ?? `default` + } + + var a = foo(default: 42, ["foo": 0]) + a += 1 // ok + + var b = foo(default: 42.0, ["foo": 0]) + b += 1 // ok + + var c = foo(default: 42.0, ["foo": Float(0)]) + c += 1 // ok +} diff --git a/test/Constraints/if_expr.swift b/test/Constraints/if_expr.swift index 0a5681810363d..0f80ec812fbc3 100644 --- a/test/Constraints/if_expr.swift +++ b/test/Constraints/if_expr.swift @@ -63,3 +63,25 @@ let ib: Bool! = false let eb: Bool? = .some(false) let conditional = ib ? "Broken" : "Heart" // should infer Bool! let conditional = eb ? "Broken" : "Heart" // expected-error {{value of optional type 'Bool?' not unwrapped; did you mean to use '!' or '?'?}} + +// - crash when IfExpr has UnresolvedType in condition +struct Delegate { + var shellTasks: [ShellTask] +} + +extension Array { + subscript(safe safe: Int) -> Element? { // expected-note {{found this candidate}} + get { } + set { } + } +} + +struct ShellTask { + var commandLine: [String] +} + +let delegate = Delegate(shellTasks: []) +_ = delegate.shellTasks[safe: 0]?.commandLine.compactMap({ $0.asString.hasPrefix("") ? $0 : nil }).count ?? 0 +// expected-error@-1 {{ambiguous reference to member 'subscript'}} + +// FIXME: Horrible diagnostic, but at least we no longer crash diff --git a/test/Constraints/invalid_implicit_conversions.swift b/test/Constraints/invalid_implicit_conversions.swift new file mode 100644 index 0000000000000..b4723694c6ddc --- /dev/null +++ b/test/Constraints/invalid_implicit_conversions.swift @@ -0,0 +1,40 @@ +// RUN: %target-typecheck-verify-swift + +func takesAutoclosure(_ lhs: T, _ rhs: @autoclosure () throws -> T) {} + +func test( + _ rawPtr: UnsafeRawPointer, + _ mutRawPtr: UnsafeMutableRawPointer, + _ mutPtr: UnsafeMutablePointer, + _ ptr: UnsafePointer, + _ ptrI8: UnsafePointer, + _ ptrU8: UnsafePointer, + _ ptrVoid: UnsafePointer // expected-warning {{UnsafePointer has been replaced by UnsafeRawPointer}} +) { + var i: Int = 0 + var a: [Int] = [0] + let s = "string" + + takesAutoclosure(rawPtr, &i) // expected-error {{'&' used with non-inout argument of type 'Int'}} + takesAutoclosure(mutRawPtr, &i) // expected-error {{'&' used with non-inout argument of type 'Int'}} + takesAutoclosure(mutPtr, &i) // expected-error {{'&' used with non-inout argument of type 'Int'}} + takesAutoclosure(ptr, &i) // expected-error {{'&' used with non-inout argument of type 'Int'}} + takesAutoclosure(rawPtr, &a) // expected-error {{'&' used with non-inout argument of type '[Int]'}} + takesAutoclosure(mutRawPtr, &a) // expected-error {{'&' used with non-inout argument of type '[Int]'}} + takesAutoclosure(mutPtr, &a) // expected-error {{'&' used with non-inout argument of type '[Int]'}} + takesAutoclosure(ptr, &a) // expected-error {{'&' used with non-inout argument of type '[Int]'}} + + takesAutoclosure(rawPtr, a) // expected-error {{cannot invoke 'takesAutoclosure' with an argument list of type '(UnsafeRawPointer, [Int])'}} + // expected-note@-1 {{expected an argument list of type '(T, @autoclosure () throws -> T)'}} + takesAutoclosure(ptr, a) // expected-error {{cannot invoke 'takesAutoclosure' with an argument list of type '(UnsafePointer, [Int])'}} + // expected-note@-1 {{expected an argument list of type '(T, @autoclosure () throws -> T)'}} + + takesAutoclosure(rawPtr, s) // expected-error {{cannot invoke 'takesAutoclosure' with an argument list of type '(UnsafeRawPointer, String)'}} + // expected-note@-1 {{expected an argument list of type '(T, @autoclosure () throws -> T)'}} + takesAutoclosure(ptrI8, s) // expected-error {{cannot invoke 'takesAutoclosure' with an argument list of type '(UnsafePointer, String)'}} + // expected-note@-1 {{expected an argument list of type '(T, @autoclosure () throws -> T)'}} + takesAutoclosure(ptrU8, s) // expected-error {{cannot invoke 'takesAutoclosure' with an argument list of type '(UnsafePointer, String)'}} + // expected-note@-1 {{expected an argument list of type '(T, @autoclosure () throws -> T)'}} + takesAutoclosure(ptrVoid, s) // expected-error {{cannot invoke 'takesAutoclosure' with an argument list of type '(UnsafePointer, String)'}} + // expected-note@-1 {{expected an argument list of type '(T, @autoclosure () throws -> T)'}} +} diff --git a/test/Constraints/iuo.swift b/test/Constraints/iuo.swift index 554adfab84f36..50641d3958506 100644 --- a/test/Constraints/iuo.swift +++ b/test/Constraints/iuo.swift @@ -213,3 +213,13 @@ func conditionalDowncastToObject(b: B?) -> D { return b as? D! // expected-error {{value of optional type 'D?' not unwrapped; did you mean to use '!' or '?'?}} // expected-warning@-1 {{using '!' here is deprecated and will be removed in a future release}} } + +// Ensure that we select the overload that does *not* involve forcing an IUO. +func sr6988(x: Int?, y: Int?) -> Int { return x! } +func sr6988(x: Int, y: Int) -> Float { return Float(x) } + +var x: Int! = nil +var y: Int = 2 + +let r = sr6988(x: x, y: y) +let _: Int = r diff --git a/test/Constraints/keypath.swift b/test/Constraints/keypath.swift new file mode 100644 index 0000000000000..647041a87d298 --- /dev/null +++ b/test/Constraints/keypath.swift @@ -0,0 +1,13 @@ +// RUN: %target-typecheck-verify-swift %S/Inputs/keypath.swift -primary-file %s + +struct S { + let i: Int + + init() { + let _: WritableKeyPath = \.i // no error for Swift 3/4 + } +} + +func test() { + let _: WritableKeyPath = \.i // no error for Swift 3/4 +} diff --git a/test/Constraints/keypath_swift_5.swift b/test/Constraints/keypath_swift_5.swift new file mode 100644 index 0000000000000..4325a664fb031 --- /dev/null +++ b/test/Constraints/keypath_swift_5.swift @@ -0,0 +1,13 @@ +// RUN: %target-typecheck-verify-swift %S/Inputs/keypath.swift -primary-file %s -swift-version 5 + +struct S { + let i: Int + + init() { + let _: WritableKeyPath = \.i // expected-error {{type of expression is ambiguous without more context}} + } +} + +func test() { + let _: WritableKeyPath = \.i // expected-error {{type of expression is ambiguous without more context}} +} diff --git a/test/Constraints/members.swift b/test/Constraints/members.swift index 50347d9de7f9d..752e33a77a267 100644 --- a/test/Constraints/members.swift +++ b/test/Constraints/members.swift @@ -433,8 +433,7 @@ struct Aardvark { func rdar33914444() { struct A { enum R { - case e(E) - // expected-note@-1 {{did you mean 'e'}} + case e(E) // expected-note {{'e' declared here}} } struct S { @@ -447,7 +446,7 @@ func rdar33914444() { } _ = A.S(e: .e1) - // expected-error@-1 {{type 'A.R' has no member 'e1'}} + // expected-error@-1 {{type 'A.R' has no member 'e1'; did you mean 'e'?}} } // SR-5324: Better diagnostic when instance member of outer type is referenced from nested type @@ -464,3 +463,6 @@ struct Outer { } } } + +// rdar://problem/39514009 - don't crash when trying to diagnose members with special names +print("hello")[0] // expected-error {{value of tuple type '()' has no member 'subscript'}} diff --git a/test/Constraints/members_objc.swift b/test/Constraints/members_objc.swift index 28b367a6fb009..32d6d491f6047 100644 --- a/test/Constraints/members_objc.swift +++ b/test/Constraints/members_objc.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift +// RUN: %target-typecheck-verify-swift -enable-objc-interop import Swift diff --git a/test/Constraints/rdar38625824.swift b/test/Constraints/rdar38625824.swift new file mode 100644 index 0000000000000..c167302ff379b --- /dev/null +++ b/test/Constraints/rdar38625824.swift @@ -0,0 +1,11 @@ +// RUN: %target-swift-frontend -emit-sil -verify %s | %FileCheck %s +func foo(_: Any) -> T { + fatalError() +} + +func foo(_: Any?) -> T { + fatalError() +} + +// CHECK: function_ref @$S12rdar386258243fooyxyplF : $@convention(thin) <τ_0_0> (@in_guaranteed Any) -> @out τ_0_0 +var _: String = foo("hello") diff --git a/test/Constraints/rdar39209245.swift b/test/Constraints/rdar39209245.swift new file mode 100644 index 0000000000000..b7e15d58b0ccf --- /dev/null +++ b/test/Constraints/rdar39209245.swift @@ -0,0 +1,15 @@ +// RUN: %target-typecheck-verify-swift + +struct S: Hashable { + let e: E? +} + +enum E: Hashable { + case foo + case bar +} + +let a = S(e: .foo) +let b = S(e: .bar) + +_ = a == b diff --git a/test/Constraints/rdar39401774.swift b/test/Constraints/rdar39401774.swift new file mode 100644 index 0000000000000..1f79311af3356 --- /dev/null +++ b/test/Constraints/rdar39401774.swift @@ -0,0 +1,26 @@ +// RUN: %target-typecheck-verify-swift + +class A { + var foo: Int? { return 42 } // expected-note {{found this candidate}} + func baz() -> T { fatalError() } // expected-note {{found this candidate}} + func fiz() -> Int { return 42 } // expected-note {{found this candidate}} +} + +protocol P1 { + associatedtype T + var foo: Int? { get } // expected-note {{found this candidate}} + func baz() -> T // expected-note {{found this candidate}} + func fiz() -> Int // expected-note {{found this candidate}} +} + +protocol P2 : P1 { + var bar: Int? { get } +} + +extension P2 where Self: A { + var bar: Int? { + guard let foo = foo else { return 0 } // expected-error {{ambiguous use of 'foo'}} + let _ = baz() // expected-error {{ambiguous use of 'baz()'}} + return fiz() // expected-error {{ambiguous use of 'fiz()'}} + } +} diff --git a/test/Constraints/rdar39931475.swift b/test/Constraints/rdar39931475.swift new file mode 100644 index 0000000000000..5962726dfb081 --- /dev/null +++ b/test/Constraints/rdar39931475.swift @@ -0,0 +1,9 @@ +// RUN: %target-typecheck-verify-swift + +protocol P { + func b(i: @escaping (inout Int) -> Double) +} + +func foo(_ bar: T) { + _ = bar.b { a in Double((a, a += 1).0) } +} diff --git a/test/Constraints/rdar40002266.swift b/test/Constraints/rdar40002266.swift new file mode 100644 index 0000000000000..a6039d311bae8 --- /dev/null +++ b/test/Constraints/rdar40002266.swift @@ -0,0 +1,11 @@ +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -verify %s +// REQUIRES: objc_interop + +import Foundation + +struct S { + init(_ num: T) { + self.init(num != 0) // expected-error {{binary operator '!=' cannot be applied to operands of type 'T' and 'Int'}} + // expected-note@-1 {{expected an argument list of type '(Int, Int)'}} + } +} diff --git a/test/Constraints/sr7098.swift b/test/Constraints/sr7098.swift new file mode 100644 index 0000000000000..90005f93b1810 --- /dev/null +++ b/test/Constraints/sr7098.swift @@ -0,0 +1,13 @@ +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck %s +// REQUIRES: objc_interop + +import Foundation + +class C : NSObject, NSWobbling { + func wobble() {} + func returnMyself() -> Self { return self } +} + +func testDynamicOptionalRequirement(_ a: AnyObject) { + a.optionalRequirement?() +} diff --git a/test/Constraints/super_constructor.swift b/test/Constraints/super_constructor.swift index 70c533ff08db7..d4922e1b5c4ab 100644 --- a/test/Constraints/super_constructor.swift +++ b/test/Constraints/super_constructor.swift @@ -64,3 +64,11 @@ class Impl_2484 : SR_2484 { super.init() // expected-error {{'init' is inaccessible due to 'private' protection level}} } } + +class A_Priv { + private init(_ foo: T) {} +} + +class B_Override : A_Priv<[U]> { + init(_ foo: [U]) { fatalError() } // Ok, because effectively overrides init from parent which is invisible +} diff --git a/test/Constraints/tuple_arguments.swift b/test/Constraints/tuple_arguments.swift index 54402bdca02fc..d0dc6424fec3e 100644 --- a/test/Constraints/tuple_arguments.swift +++ b/test/Constraints/tuple_arguments.swift @@ -532,7 +532,7 @@ do { _ = InitTuple(3, 4) // expected-error {{initializer expects a single parameter of type '(Int, Int)'}} {{17-17=(}} {{21-21=)}} _ = InitTuple((3, 4)) - _ = InitLabeledTuple(x: 3, 4) // expected-error {{extra argument in call}} + _ = InitLabeledTuple(x: 3, 4) // expected-error {{initializer expects a single parameter of type '(Int, Int)'}} _ = InitLabeledTuple(x: (3, 4)) } @@ -602,7 +602,7 @@ do { _ = s2[d] let s3 = SubscriptLabeledTuple() - _ = s3[x: 3, 4] // expected-error {{extra argument in call}} + _ = s3[x: 3, 4] // expected-error {{subscript expects a single parameter of type '(Int, Int)'}} _ = s3[x: (3, 4)] } @@ -637,7 +637,7 @@ do { _ = Enum.tuple(3, 4) // expected-error {{enum element 'tuple' expects a single parameter of type '(Int, Int)'}} {{18-18=(}} {{22-22=)}} _ = Enum.tuple((3, 4)) - _ = Enum.labeledTuple(x: 3, 4) // expected-error {{extra argument in call}} + _ = Enum.labeledTuple(x: 3, 4) // expected-error {{enum element 'labeledTuple' expects a single parameter of type '(Int, Int)'}} _ = Enum.labeledTuple(x: (3, 4)) } @@ -698,7 +698,7 @@ do { sTwo.generic(3.0, 4.0) // expected-error {{instance method 'generic' expects a single parameter of type '(Double, Double)'}} {{16-16=(}} {{24-24=)}} sTwo.generic((3.0, 4.0)) - sTwo.genericLabeled(x: 3.0, 4.0) // expected-error {{extra argument in call}} + sTwo.genericLabeled(x: 3.0, 4.0) // expected-error {{instance method 'genericLabeled' expects a single parameter of type '(Double, Double)'}} sTwo.genericLabeled(x: (3.0, 4.0)) } @@ -779,7 +779,7 @@ do { sTwo.mutatingGeneric(3.0, 4.0) // expected-error {{instance method 'mutatingGeneric' expects a single parameter of type '(Double, Double)'}} {{24-24=(}} {{32-32=)}} sTwo.mutatingGeneric((3.0, 4.0)) - sTwo.mutatingGenericLabeled(x: 3.0, 4.0) // expected-error {{extra argument in call}} + sTwo.mutatingGenericLabeled(x: 3.0, 4.0) // expected-error {{instance method 'mutatingGenericLabeled' expects a single parameter of type '(Double, Double)'}} sTwo.mutatingGenericLabeled(x: (3.0, 4.0)) } @@ -940,7 +940,7 @@ do { _ = GenericInitTuple(3, 4) // expected-error {{initializer expects a single parameter of type '(T, T)'}} {{24-24=(}} {{28-28=)}} _ = GenericInitTuple((3, 4)) - _ = GenericInitLabeledTuple(x: 3, 4) // expected-error {{extra argument in call}} + _ = GenericInitLabeledTuple(x: 3, 4) // expected-error {{initializer expects a single parameter of type '(T, T)'}} _ = GenericInitLabeledTuple(x: (3, 4)) } @@ -957,7 +957,7 @@ do { _ = GenericInitTuple(3, 4) // expected-error {{initializer expects a single parameter of type '(T, T)'}} {{29-29=(}} {{33-33=)}} _ = GenericInitTuple((3, 4)) - _ = GenericInitLabeledTuple(x: 3, 4) // expected-error {{extra argument in call}} + _ = GenericInitLabeledTuple(x: 3, 4) // expected-error {{initializer expects a single parameter of type '(T, T)'}} _ = GenericInitLabeledTuple(x: (3, 4)) } @@ -1071,7 +1071,7 @@ do { _ = s3[(3.0, 4.0)] let s3a = GenericSubscriptLabeledTuple() - _ = s3a[x: 3.0, 4.0] // expected-error {{extra argument in call}} + _ = s3a[x: 3.0, 4.0] // expected-error {{subscript expects a single parameter of type '(T, T)'}} _ = s3a[x: (3.0, 4.0)] } @@ -1145,9 +1145,9 @@ do { _ = GenericEnum<(Int, Int)>.one(3, 4) // expected-error {{enum element 'one' expects a single parameter of type '(Int, Int)'}} {{35-35=(}} {{39-39=)}} _ = GenericEnum<(Int, Int)>.one((3, 4)) - _ = GenericEnum<(Int, Int)>.labeled(x: 3, 4) // expected-error {{extra argument in call}} + _ = GenericEnum<(Int, Int)>.labeled(x: 3, 4) // expected-error {{enum element 'labeled' expects a single parameter of type '(Int, Int)'}} _ = GenericEnum<(Int, Int)>.labeled(x: (3, 4)) - _ = GenericEnum<(Int, Int)>.labeled(3, 4) // expected-error {{extra argument in call}} + _ = GenericEnum<(Int, Int)>.labeled(3, 4) // expected-error {{enum element 'labeled' expects a single parameter of type '(Int, Int)'}} _ = GenericEnum<(Int, Int)>.labeled((3, 4)) // expected-error {{missing argument label 'x:' in call}} _ = GenericEnum.two(3, 4) @@ -1264,7 +1264,7 @@ do { sTwo.requirement(3.0, 4.0) // expected-error {{instance method 'requirement' expects a single parameter of type '(Double, Double)'}} {{20-20=(}} {{28-28=)}} sTwo.requirement((3.0, 4.0)) - sTwo.requirementLabeled(x: 3.0, 4.0) // expected-error {{extra argument in call}} + sTwo.requirementLabeled(x: 3.0, 4.0) // expected-error {{instance method 'requirementLabeled' expects a single parameter of type '(Double, Double)'}} sTwo.requirementLabeled(x: (3.0, 4.0)) } diff --git a/test/Constraints/valid_implicit_conversions.swift b/test/Constraints/valid_implicit_conversions.swift new file mode 100644 index 0000000000000..a0c7fea577c6c --- /dev/null +++ b/test/Constraints/valid_implicit_conversions.swift @@ -0,0 +1,49 @@ +// RUN: %target-typecheck-verify-swift + +func takesAutoclosure(_ lhs: T, _ rhs: @autoclosure () throws -> T) {} +func takesUnsafeRawPointer(_ ptr: UnsafeRawPointer) {} +func takesUnsafeMutableRawPointer(_ ptr: UnsafeMutableRawPointer) {} +func takesUnsafePointer(_ ptr: UnsafePointer) {} +func takesUnsafeMutablePointer(_ ptr: UnsafeMutablePointer) {} +func takesUnsafePointerInt8(_ ptr: UnsafePointer) {} +func takesUnsafePointerUInt8(_ ptr: UnsafePointer) {} +func takesUnsafePointerVoid(_ ptr: UnsafePointer) {} // expected-warning {{UnsafePointer has been replaced by UnsafeRawPointer}} + +func test( + _ rawPtr: UnsafeRawPointer, + _ mutRawPtr: UnsafeMutableRawPointer, + _ mutPtr: UnsafeMutablePointer, + _ ptr: UnsafePointer +) { + var i: Int = 0 + var a: [Int] = [0] + let s = "string" + + takesUnsafeRawPointer(&i) + takesUnsafeMutableRawPointer(&i) + takesUnsafeMutablePointer(&i) + takesUnsafePointer(&i) + takesUnsafeRawPointer(&a) + takesUnsafeMutableRawPointer(&a) + takesUnsafeMutablePointer(&a) + takesUnsafePointer(&a) + + takesUnsafeRawPointer(mutPtr) + takesUnsafeMutableRawPointer(mutPtr) + takesUnsafePointer(mutPtr) + + takesUnsafeRawPointer(mutRawPtr) + + takesUnsafeRawPointer(a) + takesUnsafePointer(a) + + takesAutoclosure(rawPtr, mutPtr) + takesAutoclosure(mutRawPtr, mutPtr) + takesAutoclosure(ptr, mutPtr) + takesAutoclosure(rawPtr, mutRawPtr) + + takesUnsafeRawPointer(s) + takesUnsafePointerInt8(s) + takesUnsafePointerUInt8(s) + takesUnsafePointerVoid(s) +} diff --git a/test/DebugInfo/DumpDeclFromMangledName.swift b/test/DebugInfo/DumpDeclFromMangledName.swift index 154afd3290fcd..c13e950d7893f 100644 --- a/test/DebugInfo/DumpDeclFromMangledName.swift +++ b/test/DebugInfo/DumpDeclFromMangledName.swift @@ -7,6 +7,22 @@ // RUN: sed -ne '/--->/s/^.*---> *//p' < %S/Inputs/decl-reconstr-names.txt > %t.check // RUN: %target-build-swift -emit-executable %s -g -o %t/DeclReconstr -emit-module + +// Input validation tests. +// RUN: not %lldb-moduleimport-test patatino 2>&1 | %FileCheck %s \ +// RUN: --check-prefix=INVALID-INPUT +// INVALID-INPUT: patatino No such file or directory + +// RUN: not %lldb-moduleimport-test %t/DeclReconstr \ +// RUN: --decl-from-mangled=patatino 2>&1 | \ +// RUN: %FileCheck %s --check-prefix=INVALID-DECL +// INVALID-DECL: patatino does not exists, exiting. + +// RUN: not %lldb-moduleimport-test %t/DeclReconstr \ +// RUN: --type-from-mangled=patatino 2>&1 | \ +// RUN: %FileCheck %s --check-prefix=INVALID-TYPE +// INVALID-TYPE: patatino does not exists, exiting. + // RUN: %lldb-moduleimport-test %t/DeclReconstr \ // RUN: -decl-from-mangled=%t.input > %t.output 2>&1 // RUN: diff %t.check %t.output @@ -23,3 +39,19 @@ func patatino() -> Int { } patatino() + +class Foo { + var x : T + init(_ x : T) { + self.x = x + } +} + +typealias Patatino = Foo + +func main() -> Int { + var p : Patatino = Patatino(23); + return 0 +} + +let _ = main() diff --git a/test/DebugInfo/DumpTypeFromMangledName.swift b/test/DebugInfo/DumpTypeFromMangledName.swift index e3602d1de213e..b763334397f8f 100644 --- a/test/DebugInfo/DumpTypeFromMangledName.swift +++ b/test/DebugInfo/DumpTypeFromMangledName.swift @@ -12,10 +12,6 @@ // RUN: diff %t.check %t.output // REQUIRES: executable_test -func main() { - struct Patatino {} -} - extension Collection where Element: Equatable { func split(separatedBy separator: C) -> [SubSequence] where C.Element == Element { @@ -23,3 +19,20 @@ extension Collection where Element: Equatable { return results } } + +class Foo { + var x : T + init(_ x : T) { + self.x = x + } +} + +typealias Patatino = Foo + +func main() -> Int { + struct patatino {} + var p : Patatino = Patatino(23); + return 0 +} + +let _ = main() diff --git a/test/DebugInfo/Inputs/decl-reconstr-names.txt b/test/DebugInfo/Inputs/decl-reconstr-names.txt index 988e2ade68533..c8357fd7108f1 100644 --- a/test/DebugInfo/Inputs/decl-reconstr-names.txt +++ b/test/DebugInfo/Inputs/decl-reconstr-names.txt @@ -1,2 +1,3 @@ $S12DeclReconstr8patatinoSiyF ---> func patatino() -> Int $S12DeclReconstr1SVACycfC ---> init() +$S12DeclReconstr8PatatinoaySiGD ---> typealias Patatino = Foo diff --git a/test/DebugInfo/Inputs/type-reconstr-names.txt b/test/DebugInfo/Inputs/type-reconstr-names.txt index 1579b264f3d8f..aae344ab377fe 100644 --- a/test/DebugInfo/Inputs/type-reconstr-names.txt +++ b/test/DebugInfo/Inputs/type-reconstr-names.txt @@ -3,3 +3,6 @@ $SytD ---> () $Ss5Int32VD ---> Int32 $S4blah4mainyyF8PatatinoL_VMa ---> Can't resolve type of $S4blah4mainyyF8PatatinoL_VMa $Ss10CollectionP7Element ---> Can't resolve type of $Ss10CollectionP7Element +$Ss15ContiguousArrayV9formIndex5afterySiz_tFSS_Tg5 ---> (inout Int) -> () +$S12TypeReconstr8PatatinoaySiGD ---> Patatino +$S13EyeCandySwift21_previousUniqueNumber33_ADC08935D64EA4F796440E7335798735LLs6UInt64Vvp -> UInt64 diff --git a/test/DebugInfo/LinetableArtificialFn.swift b/test/DebugInfo/LinetableArtificialFn.swift deleted file mode 100644 index 4f985ff0ccd24..0000000000000 --- a/test/DebugInfo/LinetableArtificialFn.swift +++ /dev/null @@ -1,34 +0,0 @@ -// RUN: %target-swift-frontend %s -emit-ir -g -o - -disable-sil-linking | %FileCheck %s - -// Verify that a helper function that is generated on-the-fly does -// not mess up the linetable of the calling function. -// CHECK: store i2048 10, i2048* [[STKLOC:%.*]], align -// CHECK: call swiftcc {{(i32|i64)}} @"$SSi22_builtinIntegerLiteralSiBi2048__tcfC"(i2048* {{.*}} [[STKLOC]] -// CHECK: store {{(i32|i64)}} {{.*}}getelementptr -// CHECK: store {{(i32|i64)}} {{.*}}getelementptr{{.*}}, !dbg ![[DBG:[0-9]+]] -// CHECK-NOT: ![[DBG]] = !{i32 0, i32 0, - -class TurnBasedPolicy { - - typealias Rules = (onWin : Int, onHint : Int, onLose : Int, onWrongGuess: Int, numAttempts : Int, numHints : Int, secretRange : (Int, Int)) - - init (r : Rules) { - self.rules = r - } - var rules : Rules - var secret = 0 - var attempts = 0 - var hints = 0 - var hintedRange = (0, 0) -} - -var easyPolicy : TurnBasedPolicy.Rules = ( - onWin : 2, - onHint : -1, - onLose : -2, - onWrongGuess : 0, - numAttempts : 7, - numHints : 7, - secretRange : (1,10) -) - diff --git a/test/DebugInfo/autoclosure.swift b/test/DebugInfo/autoclosure.swift index de1dfb5e033ea..e99462f1f678a 100644 --- a/test/DebugInfo/autoclosure.swift +++ b/test/DebugInfo/autoclosure.swift @@ -21,7 +21,7 @@ func &&&&&(lhs: Bool, rhs: @autoclosure () -> Bool) -> Bool { func call_me(_ input: Int64) -> Void { // rdar://problem/14627460 // An autoclosure should have a line number in the debug info and a scope line of 0. -// CHECK-DAG: !DISubprogram({{.*}}linkageName: "$S11autoclosure7call_meyys5Int64VFSbyXKfu_",{{.*}} line: [[@LINE+3]],{{.*}} isLocal: true, isDefinition: true +// CHECK-DAG: !DISubprogram({{.*}}linkageName: "$S11autoclosure7call_meyys5Int64VFSbyXKfu_",{{.*}} isLocal: true, isDefinition: true // But not in the line table. // CHECK-DAG: ![[DBG]] = !DILocation(line: [[@LINE+1]], if input != 0 &&&&& ( get_truth (input * 2 + 1) > 0 ) { diff --git a/test/DebugInfo/basic.swift b/test/DebugInfo/basic.swift index d9b6bbbb30eaf..4755a0f7f86bc 100644 --- a/test/DebugInfo/basic.swift +++ b/test/DebugInfo/basic.swift @@ -20,8 +20,6 @@ // -------------------------------------------------------------------- // Now check that we do generate line+scope info with -g. // RUN: %target-swift-frontend %s -emit-ir -g -o - | %FileCheck %s -// RUN: %target-swift-frontend %s -emit-ir -g -o - -disable-sil-linking \ -// RUN: | %FileCheck %s --check-prefix=CHECK-NOSIL // -------------------------------------------------------------------- // Currently -gdwarf-types should give the same results as -g. // RUN: %target-swift-frontend %s -emit-ir -gdwarf-types -o - | %FileCheck %s @@ -49,10 +47,7 @@ func foo(_ a: Int64, _ b: Int64) -> Int64 { // CHECK-DAG: !DILexicalBlock({{.*}} line: [[@LINE-1]] // Transparent inlined multiply: // CHECK-DAG: smul{{.*}}, !dbg ![[MUL:[0-9]+]] - // CHECK-DAG: [[MUL]] = !DILocation(line: [[@LINE+4]], column: 16, - // Runtime call to multiply function: - // CHECK-NOSIL: @"$Ss5Int64V1moiyA2B_ABtFZ{{.*}}, !dbg ![[MUL:[0-9]+]] - // CHECK-NOSIL: [[MUL]] = !DILocation(line: [[@LINE+1]], column: 16, + // CHECK-DAG: [[MUL]] = !DILocation(line: [[@LINE+1]], column: 16, return a*b } else { // CHECK-DAG: ![[PARENT:[0-9]+]] = distinct !DILexicalBlock({{.*}} line: [[@LINE-1]], column: 13) diff --git a/test/DebugInfo/byref-capture.swift b/test/DebugInfo/byref-capture.swift index 91b0af62c2efa..5b1fbdbcfbfe6 100644 --- a/test/DebugInfo/byref-capture.swift +++ b/test/DebugInfo/byref-capture.swift @@ -7,11 +7,10 @@ func makeIncrementor(_ inc : Int64) -> () -> Int64 func inner() -> Int64 { // CHECK: call void @llvm.dbg.declare(metadata %Ts5Int64V** // CHECK-SAME: metadata ![[SUM_CAPTURE:[0-9]+]], - // CHECK-SAME: metadata !DIExpression()) - // CHECK: ![[INOUTTY:[0-9]+]] = !DICompositeType({{.*}}identifier: "$Ss5Int64VzD" - // ^ inout type. + // CHECK-SAME: metadata !DIExpression(DW_OP_deref)) + // CHECK: ![[INOUTTY:[0-9]+]] = !DICompositeType({{.*}}identifier: "$Ss5Int64VD" // CHECK: ![[SUM_CAPTURE]] = !DILocalVariable(name: "sum", arg: 1, - // CHECK-SAME: line: [[@LINE-9]], type: ![[INOUTTY]] + // CHECK-SAME: line: [[@LINE-8]], type: ![[INOUTTY]] sum += inc return sum } diff --git a/test/DebugInfo/generic_enum_closure.swift b/test/DebugInfo/generic_enum_closure.swift index ffa6b19101402..16164a4bc938b 100644 --- a/test/DebugInfo/generic_enum_closure.swift +++ b/test/DebugInfo/generic_enum_closure.swift @@ -15,9 +15,8 @@ struct CErrorOr // CHECK-SAME: !DIExpression(DW_OP_deref)) // CHECK-DAG: store i8* %[[DYN:.*]], i8** %[[SHADOW]] // CHECK-DAG: %[[DYN]] = alloca i8, i{{32|64}} % - // CHECK: ![[T1:.*]] = !DICompositeType({{.*}}, identifier: "$S20generic_enum_closure8CErrorOrVyACQq_GD") - // CHECK: ![[SELF]] = !DILocalVariable(name: "self", scope: - // CHECK-SAME: type: ![[T1]]) + // CHECK-DAG: ![[T1:.*]] = !DICompositeType({{.*}}, identifier: "$S20generic_enum_closure8CErrorOrVyACQq_GD") + // CHECK-DAG: ![[SELF]] = !DILocalVariable(name: "self", scope:{{.*}}, type: ![[T1]]) value = .none } } diff --git a/test/DebugInfo/guard-let.swift b/test/DebugInfo/guard-let.swift index d013a5806b505..2db51a45c75e1 100644 --- a/test/DebugInfo/guard-let.swift +++ b/test/DebugInfo/guard-let.swift @@ -32,9 +32,9 @@ public func g(_ s : String?) // CHECK2: @llvm.dbg.declare(metadata %TSSSg* // CHECK2: %debug.copy1 = alloca %TSS // CHECK2: @llvm.dbg.declare(metadata %TSS* - // CHECK2: %4 = bitcast %TSSSg* %debug.copy to { i64, i64 }*, !dbg - // CHECK2: %5 = getelementptr inbounds { i64, i64 }, { i64, i64 }* %4, i32 0, i32 0, !dbg - // CHECK2: store i64 %0, i64* %5, align 8, !dbg + // CHECK2: %4 = bitcast %TSSSg* %debug.copy to {{.*}}*, !dbg + // CHECK2: %5 = getelementptr inbounds {{.*}}, {{.*}}* %4, i32 0, i32 0, !dbg + // CHECK2: store {{.*}} %0, {{.*}}* %5, align 8, !dbg // CHECK2: ![[G:.*]] = distinct !DISubprogram(name: "g" guard let val = s else { return } use(val) diff --git a/test/DebugInfo/inout.swift b/test/DebugInfo/inout.swift index 436175591db86..1b87c607a8873 100644 --- a/test/DebugInfo/inout.swift +++ b/test/DebugInfo/inout.swift @@ -16,15 +16,14 @@ typealias MyFloat = Float // Closure with promoted capture. // PROMO-CHECK: define {{.*}}@"$S5inout13modifyFooHeapyys5Int64Vz_SftFADyXEfU_" // PROMO-CHECK: call void @llvm.dbg.declare(metadata %Ts5Int64V** % -// PROMO-CHECK-SAME: metadata ![[A1:[0-9]+]], metadata !DIExpression()) +// PROMO-CHECK-SAME: metadata ![[A1:[0-9]+]], metadata !DIExpression(DW_OP_deref)) // PROMO-CHECK-DAG: ![[INT:.*]] = !DICompositeType({{.*}}identifier: "$Ss5Int64VD" -// PROMO-CHECK-DAG: ![[INT:.*]] = !DICompositeType({{.*}}identifier: "$Ss5Int64VzD" // PROMO-CHECK: ![[A1]] = !DILocalVariable(name: "a", arg: 1 // PROMO-CHECK-SAME: type: ![[INT]] func modifyFooHeap(_ a: inout Int64, // CHECK-DAG: ![[A]] = !DILocalVariable(name: "a", arg: 1{{.*}} line: [[@LINE-1]],{{.*}} type: ![[RINT:[0-9]+]] - // CHECK-DAG: ![[RINT]] = !DICompositeType({{.*}}identifier: "$Ss5Int64VzD" + // CHECK-DAG: ![[RINT]] = !DICompositeType({{.*}}identifier: "$Ss5Int64VD" _ b: MyFloat) { let b = b @@ -39,11 +38,11 @@ func modifyFooHeap(_ a: inout Int64, // Inout reference type. // FOO-CHECK: define {{.*}}@"$S5inout9modifyFooyys5Int64Vz_SftF" // FOO-CHECK: call void @llvm.dbg.declare(metadata %Ts5Int64V** % -// FOO-CHECK-SAME: metadata ![[U:[0-9]+]], metadata !DIExpression()) +// FOO-CHECK-SAME: metadata ![[U:[0-9]+]], metadata !DIExpression(DW_OP_deref)) func modifyFoo(_ u: inout Int64, // FOO-CHECK-DAG: !DILocalVariable(name: "v", arg: 2{{.*}} line: [[@LINE+3]],{{.*}} type: ![[MYFLOAT:[0-9]+]] // FOO-CHECK-DAG: [[U]] = !DILocalVariable(name: "u", arg: 1{{.*}} line: [[@LINE-2]],{{.*}} type: ![[RINT:[0-9]+]] - // FOO-CHECK-DAG: ![[RINT]] = !DICompositeType({{.*}}identifier: "$Ss5Int64VzD" + // FOO-CHECK-DAG: ![[RINT]] = !DICompositeType({{.*}}identifier: "$Ss5Int64VD" _ v: MyFloat) // FOO-CHECK-DAG: ![[MYFLOAT]] = !DIDerivedType(tag: DW_TAG_typedef, name: "$S5inout7MyFloataD",{{.*}} baseType: ![[FLOAT:[0-9]+]] // FOO-CHECK-DAG: ![[FLOAT]] = !DICompositeType({{.*}}identifier: "$SSfD" diff --git a/test/DebugInfo/resilience.swift b/test/DebugInfo/resilience.swift index c013afe179a0a..25a07b2e32b15 100644 --- a/test/DebugInfo/resilience.swift +++ b/test/DebugInfo/resilience.swift @@ -9,31 +9,58 @@ // RUN: %target-swift-frontend -g -I %t -emit-ir -enable-resilience %s -o - \ // RUN: | %FileCheck %s // -// RUN: %target-swift-frontend -g -I %t -emit-sil -enable-resilience %s -o - \ -// RUN: | %FileCheck %s --check-prefix=CHECK-SIL -// RUN: %target-swift-frontend -g -I %t -emit-sil -enable-resilience %s -o - \ -// RUN: -debugger-support | %FileCheck %s --check-prefix=CHECK-LLDB +// RUN: %target-swift-frontend -g -I %t -emit-ir -enable-resilience %s -o - \ +// RUN: -enable-resilience-bypass | %FileCheck %s --check-prefix=CHECK-LLDB import resilient_struct -func use(_ t: T) {} +let fixed = Point(x: 1, y: 2) +let non_fixed = Size(w: 1, h: 2) +let int = ResilientInt(i: 1) +// CHECK: @"$S10resilience5fixed16resilient_struct5PointVvp" = +// CHECK-SAME: !dbg ![[FIXED:[0-9]+]] +// CHECK: @"$S10resilience9non_fixed16resilient_struct4SizeVvp" = +// CHECK-SAME: !dbg ![[NON_FIXED:[0-9]+]] +// CHECK: @"$S10resilience3int16resilient_struct12ResilientIntVvp" = +// CHECK-SAME: !dbg ![[INT:[0-9]+]] +// CHECK-LABEL: define{{.*}}main -public func f() { +// CHECK-LABEL: define{{.*}} swiftcc void @"$S10resilience9takesSizeyy16resilient_struct0C0VF"(%swift.opaque* noalias nocapture) +// CHECK-LLDB-LABEL: define{{.*}} swiftcc void @"$S10resilience9takesSizeyy16resilient_struct0C0VF"(%T16resilient_struct4SizeV* noalias nocapture dereferenceable({{8|16}})) +public func takesSize(_ s: Size) {} + + +// CHECK-LABEL: define{{.*}} swiftcc void @"$S10resilience1fyyF"() +// CHECK-LLDB-LABEL: define{{.*}} swiftcc void @"$S10resilience1fyyF"() +func f() { let s1 = Size(w: 1, h: 2) - use(s1) + takesSize(s1) // CHECK: %[[ADDR:.*]] = alloca i8* // CHECK: call void @llvm.dbg.declare(metadata i8** %[[ADDR]], // CHECK-SAME: metadata ![[V1:[0-9]+]], // CHECK-SAME: metadata !DIExpression(DW_OP_deref)) // CHECK: %[[S1:.*]] = alloca i8, // CHECK: store i8* %[[S1]], i8** %[[ADDR]] - // CHECK: ![[V1]] = !DILocalVariable(name: "s1", {{.*}}type: ![[TY:[0-9]+]]) - // CHECK: ![[TY]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Size", - // FIXME-NOT: size: - // CHECK: = + + // CHECK-LLDB: %[[ADDR:.*]] = alloca %T16resilient_struct4SizeV + // CHECK-LLDB: call void @llvm.dbg.declare(metadata %T16resilient_struct4SizeV* %[[ADDR]], + // CHECK-LLDB-SAME: metadata ![[V1:[0-9]+]], + // CHECK-LLDB-SAME: metadata !DIExpression()) } +f() + +// Note that these DW_OP_deref are not necessarily correct, but it's the best +// approxmiation we have until LLDB can query the runtime for whether a relient +// type's storage is inline or not. +// CHECK: ![[FIXED]] = !DIGlobalVariableExpression( +// CHECK-SAME: expr: !DIExpression()) +// CHECK: ![[NON_FIXED]] = !DIGlobalVariableExpression( +// CHECK-SAME: expr: !DIExpression(DW_OP_deref)) +// CHECK: ![[TY:[0-9]+]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Size", +// CHECK: ![[INT]] = !DIGlobalVariableExpression( +// CHECK-SAME: expr: !DIExpression(DW_OP_deref)) + +// CHECK: ![[V1]] = !DILocalVariable(name: "s1", {{.*}}type: ![[TY]]) + +// CHECK-LLDB: ![[TY:[0-9]+]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Size", +// CHECK-LLDB: ![[V1]] = !DILocalVariable(name: "s1", {{.*}}type: ![[TY]]) -// CHECK-SIL: // f() -// CHECK-LLDB: // f() -// CHECK-SIL: %0 = alloc_stack $Size, let, name "s1" -// CHECK-LLDB: %0 = metatype $@thin Size.Type -// CHECK-LLDB: debug_value %{{.*}} : $Size, let, name "s1" diff --git a/test/DebugInfo/returnlocation.swift b/test/DebugInfo/returnlocation.swift index c21e2cf70e727..75a61373cb477 100644 --- a/test/DebugInfo/returnlocation.swift +++ b/test/DebugInfo/returnlocation.swift @@ -12,8 +12,7 @@ import Foundation // CHECK_NONE: define{{( protected)?}} {{.*}}void {{.*}}none public func none(_ a: inout Int64) { // CHECK_NONE: call void @llvm.dbg{{.*}}, !dbg - // CHECK_NONE: store{{.*}}, !dbg - // CHECK_NONE: !dbg ![[NONE_INIT:.*]] + // CHECK_NONE: store i64{{.*}}, !dbg ![[NONE_INIT:.*]] a -= 2 // CHECK_NONE: ret {{.*}}, !dbg ![[NONE_RET:.*]] // CHECK_NONE: ![[NONE_INIT]] = !DILocation(line: [[@LINE-2]], column: @@ -178,3 +177,17 @@ public func cleanup_simple_complex(_ a: NSString) -> Int64 { } // --------------------------------------------------------------------- + +// RUN: %FileCheck %s --check-prefix=CHECK_INIT < %t.ll +// CHECK_INIT: define {{.*}}$S4main6Class1CACSgycfc +public class Class1 { + public required init?() { + print("hello") + // CHECK_INIT: call {{.*}}@"$Ss5print_9separator10terminatoryypd_S2StF"{{.*}}, !dbg [[printLoc:![0-9]+]] + // CHECK_INIT: br label {{.*}}, !dbg [[retnLoc:![0-9]+]] + + // CHECK_INIT: [[printLoc]] = !DILocation(line: [[@LINE-4]] + // CHECK_INIT: [[retnLoc]] = !DILocation(line: [[@LINE+1]] + return nil + } +} diff --git a/test/DebugInfo/shadowcopy-linetable.swift b/test/DebugInfo/shadowcopy-linetable.swift index 92699bbe2cbc1..b3e1bb599a97d 100644 --- a/test/DebugInfo/shadowcopy-linetable.swift +++ b/test/DebugInfo/shadowcopy-linetable.swift @@ -8,6 +8,8 @@ func foo(_ x: inout Int64) { // not. // CHECK: %[[X:.*]] = alloca %Ts5Int64V*, align {{(4|8)}} // CHECK-NEXT: call void @llvm.dbg.declare + // CHECK-NEXT: [[ZEROED:%[0-9]+]] = bitcast %Ts5Int64V** %[[X]] to %swift.opaque** + // CHECK-NEXT: store %swift.opaque* null, %swift.opaque** [[ZEROED]], align {{(4|8)}} // CHECK: store %Ts5Int64V* %0, %Ts5Int64V** %[[X]], align {{(4|8)}} // CHECK-SAME: !dbg ![[LOC0:.*]] // CHECK-NEXT: getelementptr inbounds %Ts5Int64V, %Ts5Int64V* %0, i32 0, i32 0, diff --git a/test/DebugInfo/transparent.swift b/test/DebugInfo/transparent.swift new file mode 100644 index 0000000000000..3b8c7e9a7754f --- /dev/null +++ b/test/DebugInfo/transparent.swift @@ -0,0 +1,29 @@ +// RUN: %target-swift-frontend %s -O -I %t -emit-ir -g -o - | %FileCheck %s + +func use(_ t: T) {} + +@inline(never) +public func noinline(_ x: Int64) -> Int64 { return x } + +@_transparent +public func transparent(_ y: Int64) -> Int64 { + var local = y + return noinline(local) +} + +let z = transparent(0) +use(z) + +// Check that a transparent function has no debug information. +// CHECK: define {{.*}}$S11transparentAA +// CHECK-SAME: !dbg ![[SP:[0-9]+]] +// CHECK-NEXT: entry: +// CHECK-NEXT: !dbg ![[ZERO:[0-9]+]] +// CHECK-NEXT: !dbg ![[ZERO]] +// CHECK-NEXT: } + +// CHECK: ![[SP]] = {{.*}}name: "transparent" +// CHECK-SAME: file: ![[FILE:[0-9]+]] +// CHECK-NOT: line: +// CHECK: ![[FILE]] = {{.*}}"" +// CHECK: ![[ZERO]] = !DILocation(line: 0, diff --git a/test/DebugInfo/uninitialized.swift b/test/DebugInfo/uninitialized.swift index 2fbeb9f4b2856..ae01ece06d2ad 100644 --- a/test/DebugInfo/uninitialized.swift +++ b/test/DebugInfo/uninitialized.swift @@ -1,11 +1,27 @@ // RUN: %target-swift-frontend %s -emit-ir -g -o - | %FileCheck %s +// RUN: %target-swift-frontend %s -O -emit-ir -g -o - | %FileCheck %s --check-prefix=OPT class MyClass {} -// CHECK: define {{.*}} @"$S13uninitialized1fyyF" +// CHECK-LABEL: define {{.*}} @"$S13uninitialized1fyyF" +// OPT-LABEL: define {{.*}} @"$S13uninitialized1fyyF" public func f() { var object: MyClass - // CHECK: %[[OBJ:.*]] = alloca %[[T:.*]]*, align - // CHECK: call void @llvm.dbg.declare(metadata %[[T]]** %[[OBJ]], - // CHECK: %[[BC:.*]] = bitcast %[[T]]** %[[OBJ]] to %swift.opaque**, !dbg - // CHECK: store %swift.opaque* null, %swift.opaque** %[[BC]], align {{.*}}, !dbg + // CHECK: %[[OBJ:.*]] = alloca %[[T1:.*]]*, align + // CHECK: call void @llvm.dbg.declare(metadata %[[T1]]** %[[OBJ]], + // CHECK: %[[BC1:.*]] = bitcast %[[T1]]** %[[OBJ]] to %swift.opaque** + // CHECK: store %swift.opaque* null, %swift.opaque** %[[BC1]], align {{.*}} + // OPT-NOT: store + // OPT: ret +} + +// CHECK-LABEL: define {{.*}} @"$S13uninitialized1gyyF" +// OPT-LABEL: define {{.*}} @"$S13uninitialized1gyyF" +public func g() { + var dict: Dictionary + // CHECK: %[[DICT:.*]] = alloca %[[T2:.*]], align + // CHECK: call void @llvm.dbg.declare(metadata %[[T2]]* %[[DICT]], + // CHECK: %[[BC2:.*]] = bitcast %[[T2]]* %[[DICT]] to %swift.opaque** + // CHECK: store %swift.opaque* null, %swift.opaque** %[[BC2]], align {{.*}} + // OPT-NOT: store + // OPT: ret } diff --git a/test/DebugInfo/weak-self-capture.swift b/test/DebugInfo/weak-self-capture.swift new file mode 100644 index 0000000000000..58528f5075d72 --- /dev/null +++ b/test/DebugInfo/weak-self-capture.swift @@ -0,0 +1,19 @@ +// RUN: %target-swift-frontend %s -emit-ir -g -o - | %FileCheck %s +public class ClosureMaker { + var a : Int + + init (a : Int) { self.a = a } + + public func getClosure() -> (() -> Int) { + return { [weak self] () -> Int in + if let _self = self { + return _self.a + } else { + return 0 + } + } + } +} + +// CHECK: define {{.*}} @"$S4main12ClosureMakerC03getB0SiycyFSiycfU_" +// CHECK: call void @llvm.dbg.declare(metadata %swift.weak** %{{.*}} !DIExpression(DW_OP_deref)), diff --git a/test/DebuggerTestingTransform/class.swift b/test/DebuggerTestingTransform/class.swift new file mode 100644 index 0000000000000..9b320d00987b7 --- /dev/null +++ b/test/DebuggerTestingTransform/class.swift @@ -0,0 +1,13 @@ +// RUN: %target-swift-frontend -debugger-testing-transform -Xllvm -sil-full-demangle -emit-sil -module-name M %s | %FileCheck %s -check-prefix=CHECK-SIL + +class C1 { + var x: Int = 0 + init() { + // CHECK-SIL: // closure #1 () -> () in M.C1.init() -> M.C1 + // CHECK-SIL: string_literal utf8 "x" + // CHECK-SIL: // function_ref Swift._stringForPrintObject(Any) -> Swift.String + // CHECK-SIL: // function_ref Swift._debuggerTestingCheckExpect(Swift.String, Swift.String) -> () + // CHECK-SIL: // end sil function + self.x = 1 + } +} diff --git a/test/Demangle/Inputs/manglings.txt b/test/Demangle/Inputs/manglings.txt index d4d409516e359..37d070a46d081 100644 --- a/test/Demangle/Inputs/manglings.txt +++ b/test/Demangle/Inputs/manglings.txt @@ -310,3 +310,14 @@ $SSC9SomeErrorLeVD ---> __C_Synthesized.related decl 'e' for SomeError $S20mangling_retroactive5test0yyAA1ZVy12RetroactiveB1XVSiAE1YVAG0D1A1PAAg_AiJ1QAAg1_GF -> mangling_retroactive.test0(mangling_retroactive.Z) -> () _T0LiteralAByxGxd_tcfC ---> _T0LiteralAByxGxd_tcfC _T0XZ ---> _T0XZ +_TTSf0os___TFVs17_LegacyStringCore15_invariantCheckfT_T_ ---> function signature specialization of Swift._LegacyStringCore._invariantCheck() -> () +_TTSf2o___TTSf2s_d___TFVs17_LegacyStringCoreCfVs13_StringBufferS_ ---> function signature specialization of function signature specialization of Swift._LegacyStringCore.init(Swift._StringBuffer) -> Swift._LegacyStringCore +_TTSf2do___TTSf2s_d___TFVs17_LegacyStringCoreCfVs13_StringBufferS_ ---> function signature specialization of function signature specialization of Swift._LegacyStringCore.init(Swift._StringBuffer) -> Swift._LegacyStringCore +_TTSf2dos___TTSf2s_d___TFVs17_LegacyStringCoreCfVs13_StringBufferS_ ---> function signature specialization of function signature specialization of Swift._LegacyStringCore.init(Swift._StringBuffer) -> Swift._LegacyStringCore +_TTSf ---> _TTSf +_$S3BBBBf0602365061_ ---> _$S3BBBBf0602365061_ +_$S3BBBBi0602365061_ ---> _$S3BBBBi0602365061_ +_$S3BBBBv0602365061_ ---> _$S3BBBBv0602365061_ +_T0lxxxmmmTk ---> _T0lxxxmmmTk +$S4blah8PatatinoaySiGD -> blah.Patatino + diff --git a/test/Demangle/Inputs/simplified-manglings.txt b/test/Demangle/Inputs/simplified-manglings.txt index 74803720bec29..fa125fae635ed 100644 --- a/test/Demangle/Inputs/simplified-manglings.txt +++ b/test/Demangle/Inputs/simplified-manglings.txt @@ -207,3 +207,7 @@ _T03abc6testitySiFTm ---> testit(_:) _T04main4TestCACSi1x_tc6_PRIV_Llfc ---> Test.init(x:) _$S3abc6testityySiFTm ---> testit(_:) _$S4main4TestC1xACSi_tc6_PRIV_Llfc ---> Test.init(x:) +_TTSf0os___TFVs17_LegacyStringCore15_invariantCheckfT_T_ ---> specialized _LegacyStringCore._invariantCheck() +_TTSf2o___TTSf2s_d___TFVs17_LegacyStringCoreCfVs13_StringBufferS_ ---> specialized _LegacyStringCore.init(_:) +_TTSf2do___TTSf2s_d___TFVs17_LegacyStringCoreCfVs13_StringBufferS_ ---> specialized _LegacyStringCore.init(_:) +_TTSf2dos___TTSf2s_d___TFVs17_LegacyStringCoreCfVs13_StringBufferS_ ---> specialized _LegacyStringCore.init(_:) diff --git a/test/Driver/Dependencies/Inputs/moduleonly/bar.swift b/test/Driver/Dependencies/Inputs/moduleonly/bar.swift new file mode 100644 index 0000000000000..dd3d6dee17e0a --- /dev/null +++ b/test/Driver/Dependencies/Inputs/moduleonly/bar.swift @@ -0,0 +1,28 @@ +public func bar() { } + +public class Bar { + @usableFromInline internal class Inner { + + /// makeX is declared in foo.swift + var x = makeX([1,2,3,4]) + + @usableFromInline internal func f() { + print("Bar.Inner.f") + } + } +} + +#if _runtime(_ObjC) && canImport(Foundation) + +import Foundation + +public class ObjCBar : NSObject { + + @objc(foo) + public func bar() -> String { + return "" + } + +} + +#endif diff --git a/test/Driver/Dependencies/Inputs/moduleonly/baz.swift b/test/Driver/Dependencies/Inputs/moduleonly/baz.swift new file mode 100644 index 0000000000000..d50c4318edf42 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/moduleonly/baz.swift @@ -0,0 +1,20 @@ +public func baz() {} + +public protocol BazProtocol { + associatedtype Ret + func method1() -> Ret? +} + +public enum Baz : BazProtocol { + case collection(T) + case other + + public func method1() -> T.SubSequence? { + switch self { + case .collection(let collection): + return makeX(collection) + default: + return nil + } + } +} diff --git a/test/Driver/Dependencies/Inputs/moduleonly/foo.swift b/test/Driver/Dependencies/Inputs/moduleonly/foo.swift new file mode 100644 index 0000000000000..4fda8eea4859d --- /dev/null +++ b/test/Driver/Dependencies/Inputs/moduleonly/foo.swift @@ -0,0 +1,28 @@ +@inlinable +public func foo(x: Int) -> Int { + return x + 1 +} + +/// something +func makeX(_ seed: T) -> T.SubSequence { + return seed.dropFirst(1) +} + +public struct Foo : BazProtocol { + /// varx + public var x = makeX("foobar") + + /// method + public func method1() -> Int? { return 1 } + + /* Foo Bar */ + @_transparent + public func method2(x: Int) -> Int { + return x * 12 + } + + @inlinable + public func method3(x: T, y: T) -> T { + return x == y ? x : y + } +} diff --git a/test/Driver/Dependencies/Inputs/moduleonly/output.json b/test/Driver/Dependencies/Inputs/moduleonly/output.json new file mode 100644 index 0000000000000..940a7ab5db703 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/moduleonly/output.json @@ -0,0 +1,21 @@ +{ + "./foo.swift": { + "object": "./foo.o", + "swift-dependencies": "./foo.swiftdeps", + "swiftmodule": "./foo~partial.swiftmodule" + }, + "./bar.swift": { + "object": "./bar.o", + "swift-dependencies": "./bar.swiftdeps", + "swiftmodule": "./bar~partial.swiftmodule" + }, + "./baz.swift": { + "object": "./baz.o", + "swift-dependencies": "./baz.swiftdeps", + "swiftmodule": "./baz~partial.swiftmodule" + }, + "": { + "swift-dependencies": "./buildrecord.swiftdeps" + } +} + diff --git a/test/Driver/Dependencies/moduleonly.swift b/test/Driver/Dependencies/moduleonly.swift new file mode 100644 index 0000000000000..b06864c06f636 --- /dev/null +++ b/test/Driver/Dependencies/moduleonly.swift @@ -0,0 +1,80 @@ +// RUN: rm -rf %t && cp -r %S/Inputs/moduleonly/ %t +// RUN: touch -t 201801230045 %t/*.swift + +// RUN: cd %t && %target-build-swift -emit-module -output-file-map ./output.json -incremental ./foo.swift ./bar.swift ./baz.swift -module-name testmodule -v 2>&1 | %FileCheck -check-prefix=CHECK1 %s +// RUN: test ! -f %t/buildrecord.swiftdeps +// RUN: test -f %t/buildrecord.swiftdeps~moduleonly + +// CHECK1-DAG: -primary-file ./foo.swift +// CHECK1-DAG: -primary-file ./bar.swift +// CHECK1-DAG: -primary-file ./baz.swift + +// RUN: cd %t && %target-build-swift -c -emit-module -output-file-map ./output.json -incremental ./foo.swift ./bar.swift ./baz.swift -module-name testmodule -v 2>&1 | %FileCheck -check-prefix=CHECK2 %s +// RUN: test -f %t/buildrecord.swiftdeps +// RUN: test -f %t/buildrecord.swiftdeps~moduleonly + +// CHECK2-DAG: -primary-file ./foo.swift +// CHECK2-DAG: -primary-file ./bar.swift +// CHECK2-DAG: -primary-file ./baz.swift + +// RUN: cd %t && %target-build-swift -emit-module -output-file-map ./output.json -incremental ./foo.swift ./bar.swift ./baz.swift -module-name testmodule -v 2>&1 | %FileCheck -check-prefix=CHECK3 %s +// RUN: test -f %t/buildrecord.swiftdeps~moduleonly +// RUN: test -f %t/buildrecord.swiftdeps + +// CHECK3-NOT: -primary-file ./foo.swift +// CHECK3-NOT: -primary-file ./bar.swift +// CHECK3-NOT: -primary-file ./baz.swift + +// RUN: touch -t 201801230123 %t/bar.swift +// RUN: cd %t && %target-build-swift -emit-module -output-file-map ./output.json -incremental ./foo.swift ./bar.swift ./baz.swift -module-name testmodule -v 2>&1 | %FileCheck -check-prefix=CHECK4 %s + +// CHECK4-NOT: -primary-file ./foo.swift +// CHECK4-NOT: -primary-file ./baz.swift +// CHECK4-DAG: -primary-file ./bar.swift + +// RUN: touch -t 201801230145 %t/baz.swift +// RUN: cd %t && %target-build-swift -c -emit-module -output-file-map ./output.json -incremental ./foo.swift ./bar.swift ./baz.swift -module-name testmodule -v 2>&1 | %FileCheck -check-prefix=CHECK5 %s + +// CHECK5-NOT: -primary-file ./foo.swift +// CHECK5-DAG: -primary-file ./bar.swift +// CHECK5-DAG: -primary-file ./baz.swift + +// RUN: cd %t && %target-build-swift -emit-module -output-file-map ./output.json -incremental ./foo.swift ./bar.swift ./baz.swift -module-name testmodule -v 2>&1 | %FileCheck -check-prefix=CHECK6 %s + +// CHECK6-NOT: -primary-file ./foo.swift +// CHECK6-NOT: -primary-file ./bar.swift +// CHECK6-NOT: -primary-file ./baz.swift + + +// '-c' (without '-emit-module') from clean environment. +// +// RUN: rm -rf %t && cp -r %S/Inputs/moduleonly/ %t +// RUN: touch -t 201801230045 %t/*.swift +// RUN: cd %t && %target-build-swift -c -g -output-file-map ./output.json -incremental ./foo.swift ./bar.swift ./baz.swift -module-name testmodule -v 2>&1 +// RUN: test ! -f %t/buildrecord.swiftdeps~moduleonly +// RUN: test -f %t/buildrecord.swiftdeps +// RUN: test ! -f %t/foo~partial.swiftmodule + +// '-emit-library -g' (without '-emit-module') from clean environment. +// +// RUN: rm -rf %t && cp -r %S/Inputs/moduleonly/ %t +// RUN: touch -t 201801230045 %t/*.swift +// RUN: cd %t && %target-build-swift -emit-library -g -output-file-map ./output.json -incremental ./foo.swift ./bar.swift ./baz.swift -module-name testmodule -v 2>&1 +// RUN: test ! -f %t/buildrecord.swiftdeps~moduleonly +// RUN: test -f %t/buildrecord.swiftdeps +// RUN: test -f %t/foo~partial.swiftmodule + +// Ensure '-emit-module' and '-c -emit-module' emits identical 'swiftmodule' and 'swiftdoc' file. +// +// RUN: rm -f %t-moduleonly.swiftmodule +// RUN: rm -f %t-moduleonly.swiftdoc +// RUN: rm -rf %t && cp -r %S/Inputs/moduleonly/ %t +// RUN: touch -t 201801230045 %t/*.swift +// RUN: cd %t && %target-build-swift -emit-module -output-file-map ./output.json -incremental ./foo.swift ./bar.swift ./baz.swift -module-name testmodule -v 2>&1 +// RUN: cp -f %t/testmodule.swiftmodule %t-moduleonly.swiftmodule +// RUN: cp -f %t/testmodule.swiftdoc %t-moduleonly.swiftdoc +// RUN: rm -rf %t && cp -r %S/Inputs/moduleonly/ %t +// RUN: touch -t 201801230045 %t/*.swift +// RUN: cd %t && %target-build-swift -c -emit-module -output-file-map ./output.json -incremental ./foo.swift ./bar.swift ./baz.swift -module-name testmodule -v 2>&1 +// RUN: diff %t/testmodule.swiftmodule %t-moduleonly.swiftmodule +// RUN: diff %t/testmodule.swiftdoc %t-moduleonly.swiftdoc diff --git a/test/Driver/Inputs/fake-resource-dir/lib/swift/clang/lib/windows/clang_rt.asan-x86_64.lib b/test/Driver/Inputs/fake-resource-dir/lib/swift/clang/lib/windows/clang_rt.asan-x86_64.lib new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/test/Driver/Inputs/imported_modules/imported_modules.importedmodules b/test/Driver/Inputs/imported_modules/imported_modules.importedmodules index be6e34200b621..24ea7a79a842f 100644 --- a/test/Driver/Inputs/imported_modules/imported_modules.importedmodules +++ b/test/Driver/Inputs/imported_modules/imported_modules.importedmodules @@ -3,5 +3,6 @@ X Y Foo InvalidOverlay +Swift Z Bar diff --git a/test/Driver/batch_mode_force_one_batch_repartition.swift b/test/Driver/batch_mode_force_one_batch_repartition.swift index 6941666b333e0..ceda9ecf7675b 100644 --- a/test/Driver/batch_mode_force_one_batch_repartition.swift +++ b/test/Driver/batch_mode_force_one_batch_repartition.swift @@ -2,10 +2,8 @@ // RUN: touch %t/file-01.swift %t/file-02.swift %t/file-03.swift // RUN: echo 'public func main() {}' >%t/main.swift // -// RUN: %swiftc_driver -enable-batch-mode -c -emit-module -module-name main -j 2 -driver-force-one-batch-repartition %t/file-01.swift %t/file-02.swift %t/file-03.swift %t/main.swift -### 2>%t/shouldBeEmpty2 | %FileCheck %s -check-prefix=CHECK-COMBINED -// RUN: test -z "`cat %t/shouldBeEmpty1`" -// RUN: test -z "`cat %t/shouldBeEmpty2`" -// +// RUN: %swiftc_driver -enable-batch-mode -c -emit-module -module-name main -j 2 -driver-force-one-batch-repartition %t/file-01.swift %t/file-02.swift %t/file-03.swift %t/main.swift -### 2>%t/stderr | %FileCheck %s -check-prefix=CHECK-COMBINED + // CHECK-COMBINED: -primary-file {{.*}}/file-01.swift // CHECK-COMBINED-NEXT: -primary-file {{.*}}/file-02.swift // CHECK-COMBINED-NEXT: -primary-file {{.*}}/file-03.swift diff --git a/test/Driver/batch_mode_parseable_output.swift b/test/Driver/batch_mode_parseable_output.swift index f9717f03297d0..5fc5f38e42e9f 100644 --- a/test/Driver/batch_mode_parseable_output.swift +++ b/test/Driver/batch_mode_parseable_output.swift @@ -9,35 +9,27 @@ // CHECK-NEXT: { // CHECK-NEXT: "kind": "began", // CHECK-NEXT: "name": "compile", -// CHECK-NEXT: "command": "{{.*}}/swift{{c?}} -frontend -c -primary-file {{.*}}/file-01.swift -primary-file {{.*}}/file-02.swift {{.*}}/file-03.swift {{.*}}/main.swift -emit-module-path {{.*}}/file-01-[[MODULE01:[a-z0-9]+]].swiftmodule -emit-module-path {{.*}}/file-02-[[MODULE02:[a-z0-9]+]].swiftmodule -emit-module-doc-path {{.*}}/file-01-[[SWIFTDOC01:[a-z0-9]+]].swiftdoc -emit-module-doc-path {{.*}}/file-02-[[SWIFTDOC02:[a-z0-9]+]].swiftdoc {{.*}} -module-name main -o {{.*}}/file-01-[[OBJ01:[a-z0-9]+]].o -o {{.*}}/file-02-[[OBJ02:[a-z0-9]+]].o", +// CHECK-NEXT: "command": "{{.*}}/swift{{c?}} -frontend -c -primary-file {{.*}}/file-01.swift {{.*}}/file-02.swift {{.*}}/file-03.swift {{.*}}/main.swift -emit-module-path {{.*}}/file-01-[[MODULE01:[a-z0-9]+]].swiftmodule -emit-module-doc-path {{.*}}/file-01-[[SWIFTDOC01:[a-z0-9]+]].swiftdoc {{.*}} -module-name main -o {{.*}}/file-01-[[OBJ01:[a-z0-9]+]].o", // CHECK-NEXT: "command_executable": "{{.*}}/swift{{c?}}", // CHECK-NEXT: "command_arguments": [ // CHECK-NEXT: "-frontend", // CHECK-NEXT: "-c", // CHECK-NEXT: "-primary-file", // CHECK-NEXT: "{{.*}}/file-01.swift", -// CHECK-NEXT: "-primary-file", // CHECK-NEXT: "{{.*}}/file-02.swift", // CHECK-NEXT: "{{.*}}/file-03.swift", // CHECK-NEXT: "{{.*}}/main.swift", // CHECK-NEXT: "-emit-module-path", // CHECK-NEXT: "{{.*}}/file-01-[[MODULE01:[a-z0-9]+]].swiftmodule", -// CHECK-NEXT: "-emit-module-path", -// CHECK-NEXT: "{{.*}}/file-02-[[MODULE02:[a-z0-9]+]].swiftmodule", // CHECK-NEXT: "-emit-module-doc-path", // CHECK-NEXT: "{{.*}}/file-01-[[SWIFTDOC01:[a-z0-9]+]].swiftdoc", -// CHECK-NEXT: "-emit-module-doc-path", -// CHECK-NEXT: "{{.*}}/file-02-[[SWIFTDOC02:[a-z0-9]+]].swiftdoc", // CHECK: "-module-name", // CHECK-NEXT: "main", // CHECK-NEXT: "-o", -// CHECK-NEXT: "{{.*}}/file-01-[[OBJ01:[a-z0-9]+]].o", -// CHECK-NEXT: "-o", -// CHECK-NEXT: "{{.*}}/file-02-[[OBJ02:[a-z0-9]+]].o" +// CHECK-NEXT: "{{.*}}/file-01-[[OBJ01:[a-z0-9]+]].o" // CHECK-NEXT: ], // CHECK-NEXT: "inputs": [ -// CHECK-NEXT: "{{.*}}/file-01.swift", -// CHECK-NEXT: "{{.*}}/file-02.swift" +// CHECK-NEXT: "{{.*}}/file-01.swift" // CHECK-NEXT: ], // CHECK-NEXT: "outputs": [ // CHECK-NEXT: { @@ -45,33 +37,63 @@ // CHECK-NEXT: "path": "{{.*}}/file-01-[[OBJ01]].o" // CHECK-NEXT: }, // CHECK-NEXT: { -// CHECK-NEXT: "type": "object", -// CHECK-NEXT: "path": "{{.*}}/file-02-[[OBJ02]].o" -// CHECK-NEXT: }, -// CHECK-NEXT: { // CHECK-NEXT: "type": "swiftmodule", // CHECK-NEXT: "path": "{{.*}}/file-01-[[MODULE01]].swiftmodule" // CHECK-NEXT: }, // CHECK-NEXT: { -// CHECK-NEXT: "type": "swiftmodule", -// CHECK-NEXT: "path": "{{.*}}/file-02-[[MODULE02]].swiftmodule" -// CHECK-NEXT: }, -// CHECK-NEXT: { // CHECK-NEXT: "type": "swiftdoc", // CHECK-NEXT: "path": "{{.*}}/file-01-[[SWIFTDOC01]].swiftdoc" +// CHECK-NEXT: } +// CHECK-NEXT: ], +// CHECK-NEXT: "pid": -{{[1-9][0-9]*}} +// CHECK-NEXT: } +// CHECK: {{[1-9][0-9]*}} +// CHECK-NEXT: { +// CHECK-NEXT: "kind": "began", +// CHECK-NEXT: "name": "compile", +// CHECK-NEXT: "command": "{{.*}}/swift{{c?}} -frontend -c {{.*}}/file-01.swift -primary-file {{.*}}/file-02.swift {{.*}}/file-03.swift {{.*}}/main.swift -emit-module-path {{.*}}/file-02-[[MODULE02:[a-z0-9]+]].swiftmodule -emit-module-doc-path {{.*}}/file-02-[[SWIFTDOC02:[a-z0-9]+]].swiftdoc {{.*}} -module-name main -o {{.*}}/file-02-[[OBJ02:[a-z0-9]+]].o", +// CHECK-NEXT: "command_executable": "{{.*}}/swift{{c?}}", +// CHECK-NEXT: "command_arguments": [ +// CHECK-NEXT: "-frontend", +// CHECK-NEXT: "-c", +// CHECK-NEXT: "{{.*}}/file-01.swift", +// CHECK-NEXT: "-primary-file", +// CHECK-NEXT: "{{.*}}/file-02.swift", +// CHECK-NEXT: "{{.*}}/file-03.swift", +// CHECK-NEXT: "{{.*}}/main.swift", +// CHECK-NEXT: "-emit-module-path", +// CHECK-NEXT: "{{.*}}/file-02-[[MODULE02:[a-z0-9]+]].swiftmodule", +// CHECK-NEXT: "-emit-module-doc-path", +// CHECK-NEXT: "{{.*}}/file-02-[[SWIFTDOC02:[a-z0-9]+]].swiftdoc", +// CHECK: "-module-name", +// CHECK-NEXT: "main", +// CHECK-NEXT: "-o", +// CHECK-NEXT: "{{.*}}/file-02-[[OBJ02:[a-z0-9]+]].o" +// CHECK-NEXT: ], +// CHECK-NEXT: "inputs": [ +// CHECK-NEXT: "{{.*}}/file-02.swift" +// CHECK-NEXT: ], +// CHECK-NEXT: "outputs": [ +// CHECK-NEXT: { +// CHECK-NEXT: "type": "object", +// CHECK-NEXT: "path": "{{.*}}/file-02-[[OBJ02]].o" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "type": "swiftmodule", +// CHECK-NEXT: "path": "{{.*}}/file-02-[[MODULE02]].swiftmodule" // CHECK-NEXT: }, // CHECK-NEXT: { // CHECK-NEXT: "type": "swiftdoc", // CHECK-NEXT: "path": "{{.*}}/file-02-[[SWIFTDOC02]].swiftdoc" // CHECK-NEXT: } // CHECK-NEXT: ], -// CHECK-NEXT: "pid": {{[1-9][0-9]*}} +// CHECK-NEXT: "pid": -{{[1-9][0-9]*}} // CHECK-NEXT: } -// CHECK-NEXT: {{[1-9][0-9]*}} +// CHECK: {{[1-9][0-9]*}} // CHECK-NEXT: { // CHECK-NEXT: "kind": "began", // CHECK-NEXT: "name": "compile", -// CHECK-NEXT: "command": "{{.*}}/swift{{c?}} -frontend -c {{.*}}/file-01.swift {{.*}}/file-02.swift -primary-file {{.*}}/file-03.swift -primary-file {{.*}}/main.swift -emit-module-path {{.*}}/file-03-[[MODULE03:[a-z0-9]+]].swiftmodule -emit-module-path {{.*}}/main-[[MODULEMAIN:[a-z0-9]+]].swiftmodule -emit-module-doc-path {{.*}}/file-03-[[SWIFTDOC03:[a-z0-9]+]].swiftdoc -emit-module-doc-path {{.*}}/main-[[SWIFTDOCMAIN:[a-z0-9]+]].swiftdoc {{.*}} -module-name main -o {{.*}}/file-03-[[OBJ03:[a-z0-9]+]].o -o {{.*}}/main-[[OBJMAIN:[a-z0-9]+]].o", +// CHECK-NEXT: "command": "{{.*}}/swift{{c?}} -frontend -c {{.*}}/file-01.swift {{.*}}/file-02.swift -primary-file {{.*}}/file-03.swift {{.*}}/main.swift -emit-module-path {{.*}}/file-03-[[MODULE03:[a-z0-9]+]].swiftmodule -emit-module-doc-path {{.*}}/file-03-[[SWIFTDOC03:[a-z0-9]+]].swiftdoc {{.*}} -module-name main -o {{.*}}/file-03-[[OBJ03:[a-z0-9]+]].o", // CHECK-NEXT: "command_executable": "{{.*}}/swift{{c?}}", // CHECK-NEXT: "command_arguments": [ // CHECK-NEXT: "-frontend", @@ -80,26 +102,18 @@ // CHECK-NEXT: "{{.*}}/file-02.swift", // CHECK-NEXT: "-primary-file", // CHECK-NEXT: "{{.*}}/file-03.swift", -// CHECK-NEXT: "-primary-file", // CHECK-NEXT: "{{.*}}/main.swift", // CHECK-NEXT: "-emit-module-path", // CHECK-NEXT: "{{.*}}/file-03-[[MODULE03:[a-z0-9]+]].swiftmodule", -// CHECK-NEXT: "-emit-module-path", -// CHECK-NEXT: "{{.*}}/main-[[MODULEMAIN:[a-z0-9]+]].swiftmodule", // CHECK-NEXT: "-emit-module-doc-path", // CHECK-NEXT: "{{.*}}/file-03-[[SWIFTDOC03:[a-z0-9]+]].swiftdoc", -// CHECK-NEXT: "-emit-module-doc-path", -// CHECK-NEXT: "{{.*}}/main-[[SWIFTDOCMAIN:[a-z0-9]+]].swiftdoc", // CHECK: "-module-name", // CHECK-NEXT: "main", // CHECK-NEXT: "-o", -// CHECK-NEXT: "{{.*}}/file-03-[[OBJ03:[a-z0-9]+]].o", -// CHECK-NEXT: "-o", -// CHECK-NEXT: "{{.*}}/main-[[OBJMAIN:[a-z0-9]+]].o" +// CHECK-NEXT: "{{.*}}/file-03-[[OBJ03:[a-z0-9]+]].o" // CHECK-NEXT: ], // CHECK-NEXT: "inputs": [ -// CHECK-NEXT: "{{.*}}/file-03.swift", -// CHECK-NEXT: "{{.*}}/main.swift" +// CHECK-NEXT: "{{.*}}/file-03.swift" // CHECK-NEXT: ], // CHECK-NEXT: "outputs": [ // CHECK-NEXT: { @@ -107,40 +121,84 @@ // CHECK-NEXT: "path": "{{.*}}/file-03-[[OBJ03]].o" // CHECK-NEXT: }, // CHECK-NEXT: { -// CHECK-NEXT: "type": "object", -// CHECK-NEXT: "path": "{{.*}}/main-[[OBJMAIN]].o" -// CHECK-NEXT: }, -// CHECK-NEXT: { // CHECK-NEXT: "type": "swiftmodule", // CHECK-NEXT: "path": "{{.*}}/file-03-[[MODULE03]].swiftmodule" // CHECK-NEXT: }, // CHECK-NEXT: { -// CHECK-NEXT: "type": "swiftmodule", -// CHECK-NEXT: "path": "{{.*}}/main-[[MODULEMAIN]].swiftmodule" -// CHECK-NEXT: }, -// CHECK-NEXT: { // CHECK-NEXT: "type": "swiftdoc", // CHECK-NEXT: "path": "{{.*}}/file-03-[[SWIFTDOC03]].swiftdoc" +// CHECK-NEXT: } +// CHECK-NEXT: ], +// CHECK-NEXT: "pid": -{{[1-9][0-9]*}} +// CHECK-NEXT: } +// CHECK: {{[1-9][0-9]*}} +// CHECK-NEXT: { +// CHECK-NEXT: "kind": "began", +// CHECK-NEXT: "name": "compile", +// CHECK-NEXT: "command": "{{.*}}/swift{{c?}} -frontend -c {{.*}}/file-01.swift {{.*}}/file-02.swift {{.*}}/file-03.swift -primary-file {{.*}}/main.swift -emit-module-path {{.*}}/main-[[MODULEMAIN:[a-z0-9]+]].swiftmodule -emit-module-doc-path {{.*}}/main-[[SWIFTDOCMAIN:[a-z0-9]+]].swiftdoc {{.*}} -module-name main -o {{.*}}/main-[[OBJMAIN:[a-z0-9]+]].o", +// CHECK-NEXT: "command_executable": "{{.*}}/swift{{c?}}", +// CHECK-NEXT: "command_arguments": [ +// CHECK-NEXT: "-frontend", +// CHECK-NEXT: "-c", +// CHECK-NEXT: "{{.*}}/file-01.swift", +// CHECK-NEXT: "{{.*}}/file-02.swift", +// CHECK-NEXT: "{{.*}}/file-03.swift", +// CHECK-NEXT: "-primary-file", +// CHECK-NEXT: "{{.*}}/main.swift", +// CHECK-NEXT: "-emit-module-path", +// CHECK-NEXT: "{{.*}}/main-[[MODULEMAIN:[a-z0-9]+]].swiftmodule", +// CHECK-NEXT: "-emit-module-doc-path", +// CHECK-NEXT: "{{.*}}/main-[[SWIFTDOCMAIN:[a-z0-9]+]].swiftdoc", +// CHECK: "-module-name", +// CHECK-NEXT: "main", +// CHECK-NEXT: "-o", +// CHECK-NEXT: "{{.*}}/main-[[OBJMAIN:[a-z0-9]+]].o" +// CHECK-NEXT: ], +// CHECK-NEXT: "inputs": [ +// CHECK-NEXT: "{{.*}}/main.swift" +// CHECK-NEXT: ], +// CHECK-NEXT: "outputs": [ +// CHECK-NEXT: { +// CHECK-NEXT: "type": "object", +// CHECK-NEXT: "path": "{{.*}}/main-[[OBJMAIN]].o" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "type": "swiftmodule", +// CHECK-NEXT: "path": "{{.*}}/main-[[MODULEMAIN]].swiftmodule" // CHECK-NEXT: }, // CHECK-NEXT: { // CHECK-NEXT: "type": "swiftdoc", // CHECK-NEXT: "path": "{{.*}}/main-[[SWIFTDOCMAIN]].swiftdoc" // CHECK-NEXT: } // CHECK-NEXT: ], -// CHECK-NEXT: "pid": {{[1-9][0-9]*}} +// CHECK-NEXT: "pid": -{{[1-9][0-9]*}} // CHECK-NEXT: } // CHECK-NEXT: {{[1-9][0-9]*}} // CHECK-NEXT: { // CHECK-NEXT: "kind": "finished", // CHECK-NEXT: "name": "compile", -// CHECK-NEXT: "pid": {{[1-9][0-9]*}}, +// CHECK-NEXT: "pid": -{{[1-9][0-9]*}}, // CHECK-NEXT: "exit-status": 0 // CHECK-NEXT: } // CHECK-NEXT: {{[1-9][0-9]*}} // CHECK-NEXT: { // CHECK-NEXT: "kind": "finished", // CHECK-NEXT: "name": "compile", -// CHECK-NEXT: "pid": {{[1-9][0-9]*}}, +// CHECK-NEXT: "pid": -{{[1-9][0-9]*}}, +// CHECK-NEXT: "exit-status": 0 +// CHECK-NEXT: } +// CHECK-NEXT: {{[1-9][0-9]*}} +// CHECK-NEXT: { +// CHECK-NEXT: "kind": "finished", +// CHECK-NEXT: "name": "compile", +// CHECK-NEXT: "pid": -{{[1-9][0-9]*}}, +// CHECK-NEXT: "exit-status": 0 +// CHECK-NEXT: } +// CHECK-NEXT: {{[1-9][0-9]*}} +// CHECK-NEXT: { +// CHECK-NEXT: "kind": "finished", +// CHECK-NEXT: "name": "compile", +// CHECK-NEXT: "pid": -{{[1-9][0-9]*}}, // CHECK-NEXT: "exit-status": 0 // CHECK-NEXT: } // CHECK-NEXT: {{[1-9][0-9]*}} diff --git a/test/Driver/batch_mode_print_jobs.swift b/test/Driver/batch_mode_print_jobs.swift index 3ad2eee86b6aa..c385e31c62279 100644 --- a/test/Driver/batch_mode_print_jobs.swift +++ b/test/Driver/batch_mode_print_jobs.swift @@ -10,9 +10,10 @@ // RUN: touch %t/file-01.swift %t/file-02.swift %t/file-03.swift // RUN: echo 'public func main() {}' >%t/main.swift // -// RUN: %swiftc_driver -enable-batch-mode -c -emit-module -module-name main -j 2 %t/file-01.swift %t/file-02.swift %t/file-03.swift %t/main.swift -driver-print-jobs 2>%t/shouldBeEmpty1 | %FileCheck %s -check-prefix=CHECK-COMBINED -// RUN: %swiftc_driver -enable-batch-mode -c -emit-module -module-name main -j 2 %t/file-01.swift %t/file-02.swift %t/file-03.swift %t/main.swift -### 2>%t/shouldBeEmpty2 | %FileCheck %s -check-prefix=CHECK-COMBINED -// RUN: test -z "`cat %t/shouldBeEmpty1`" -// RUN: test -z "`cat %t/shouldBeEmpty2`" +// RUN: %swiftc_driver -enable-batch-mode -c -emit-module -module-name main -j 2 %t/file-01.swift %t/file-02.swift %t/file-03.swift %t/main.swift -driver-print-jobs 2>%t/stderr1 | %FileCheck %s -check-prefix=CHECK-COMBINED +// RUN: %swiftc_driver -enable-batch-mode -c -emit-module -module-name main -j 2 %t/file-01.swift %t/file-02.swift %t/file-03.swift %t/main.swift -### 2>%t/stderr2 | %FileCheck %s -check-prefix=CHECK-COMBINED +// RUN: %FileCheck %s -check-prefix=NEGATIVE-CHECK-COMBINED <%t/stderr1 +// RUN: %FileCheck %s -check-prefix=NEGATIVE-CHECK-COMBINED <%t/stderr2 // // CHECK-COMBINED: -primary-file {{.*}}/file-01.swift -primary-file {{.*}}/file-02.swift {{.*}}/file-03.swift {{.*}}/main.swift +// NEGATIVE-CHECK-COMBINED-NOT: -primary-file {{.*}}/file-01.swift -primary-file {{.*}}/file-02.swift {{.*}}/file-03.swift {{.*}}/main.swift diff --git a/test/Driver/batch_mode_remark.swift b/test/Driver/batch_mode_remark.swift new file mode 100644 index 0000000000000..24a2b24d7cbc3 --- /dev/null +++ b/test/Driver/batch_mode_remark.swift @@ -0,0 +1,12 @@ +// Ensure that driver issues a remark iff in batch mode. +// +// RUN: %swiftc_driver -whole-module-optimization -enable-batch-mode %S/../Inputs/empty.swift -### 2>&1 >/dev/null | %FileCheck %s +// RUN: %swiftc_driver %S/../Inputs/empty.swift -### 2>&1 >/dev/null | %FileCheck -allow-empty %s +// RUN: %swiftc_driver -enable-batch-mode -disable-batch-mode %S/../Inputs/empty.swift -### 2>&1 >/dev/null | %FileCheck -allow-empty %s +// +// CHECK-NOT: remark: using batch mode +// +// +// RUN: %swiftc_driver -enable-batch-mode %S/../Inputs/empty.swift -### 2>&1 | %FileCheck %s -check-prefix USING-BATCH-MODE +// +// USING-BATCH-MODE: remark: using batch mode diff --git a/test/Driver/batch_mode_with_WMO_or_index.swift b/test/Driver/batch_mode_with_WMO_or_index.swift index 49b496c214040..92ec9e9ef6c7c 100644 --- a/test/Driver/batch_mode_with_WMO_or_index.swift +++ b/test/Driver/batch_mode_with_WMO_or_index.swift @@ -12,3 +12,27 @@ // RUN: %FileCheck -check-prefix CHECK-INDEX %s <%t/stderr_index_batch // RUN: %FileCheck -check-prefix CHECK-INDEX %s <%t/stderr_batch_index // CHECK-INDEX: warning: ignoring '-enable-batch-mode' because '-index-file' was also specified +// +// The following test is verifying that -disable-batch-mode overrides an earlier +// -enable-batch-mode and silences the warning about mixing batch mode with +// -index-file. Tools that take an existing command line and add -index-file can +// thus add -disable-batch-mode without having to otherwise interpret the +// arguments. +// +// RUN: %swiftc_driver -disable-batch-mode -index-file %S/../Inputs/empty.swift -### 2>%t/stderr_nobatch_index | %FileCheck %s +// RUN: %swiftc_driver -enable-batch-mode -index-file %S/../Inputs/empty.swift -disable-batch-mode -### 2>%t/stderr_batch_nobatch_index | %FileCheck %s +// RUN: %FileCheck -allow-empty -check-prefix CHECK-INDEX-DISABLED %s <%t/stderr_nobatch_index +// RUN: %FileCheck -allow-empty -check-prefix CHECK-INDEX-DISABLED %s <%t/stderr_batch_nobatch_index +// CHECK-INDEX-DISABLED-NOT: warning +// +// This next one is a regression test for a specific failure in the past: wmo + +// batch mode should not just result in wmo, but also preserve the num-threads +// argument and (crucially) the resulting fact that the single wmo subprocess +// generates multiple output files. The build system that invokes swiftc expects +// multiple outputs. +// +// RUN: touch %t/a.swift %t/b.swift %t/c.swift +// RUN: %swiftc_driver %t/a.swift %t/b.swift %t/c.swift -num-threads 4 -whole-module-optimization -enable-batch-mode -### >%t/stdout_mt_wmo 2>%t/stderr_mt_wmo +// RUN: %FileCheck --check-prefix CHECK-WMO %s <%t/stderr_mt_wmo +// RUN: %FileCheck --check-prefix CHECK-MULTITHREADED-WMO-ARGS %s <%t/stdout_mt_wmo +// CHECK-MULTITHREADED-WMO-ARGS: -num-threads 4 {{.*}}-o {{.*}}/a-{{[a-z0-9]+}}.o -o {{.*}}/b-{{[a-z0-9]+}}.o -o {{.*}}/c-{{[a-z0-9]+}}.o diff --git a/test/Driver/batch_mode_with_pch.swift b/test/Driver/batch_mode_with_pch.swift index a61905754325e..f3eb15939ccda 100644 --- a/test/Driver/batch_mode_with_pch.swift +++ b/test/Driver/batch_mode_with_pch.swift @@ -2,4 +2,4 @@ // RUN: echo 'print("Hello, World!")' >%t/main.swift // RUN: touch %t/bridgingHeader.h // -// RUN: %swiftc_driver -driver-use-filelists -enable-batch-mode -num-threads 2 %t/main.swift -import-objc-header %t/bridgingHeader.h +// RUN: %swiftc_driver -driver-filelist-threshold=0 -enable-batch-mode -num-threads 2 %t/main.swift -import-objc-header %t/bridgingHeader.h diff --git a/test/Driver/batch_mode_with_supplementary_filelist.swift b/test/Driver/batch_mode_with_supplementary_filelist.swift index 5f5733f6c7062..b01fecf7130b5 100644 --- a/test/Driver/batch_mode_with_supplementary_filelist.swift +++ b/test/Driver/batch_mode_with_supplementary_filelist.swift @@ -4,6 +4,6 @@ // // Ensure that the supplementary output filelist argument is passed to the frontend: // -// RUN: %swiftc_driver -enable-batch-mode -driver-use-filelists -j2 %t/main.swift %t/file-01.swift %t/file-02.swift %t/file-03.swift -o %t/file-01.o -o %t/file-02.o -o %t/file-03.o -### | %FileCheck %s -check-prefix=CHECK-SUPPLEMENTARY-OUTPUT-FILELIST +// RUN: %swiftc_driver -enable-batch-mode -driver-filelist-threshold=0 -j2 %t/main.swift %t/file-01.swift %t/file-02.swift %t/file-03.swift -o %t/file-01.o -o %t/file-02.o -o %t/file-03.o -### | %FileCheck %s -check-prefix=CHECK-SUPPLEMENTARY-OUTPUT-FILELIST // // CHECK-SUPPLEMENTARY-OUTPUT-FILELIST: -supplementary-output-file-map {{.*}}/supplementaryOutputs- diff --git a/test/Driver/batch_mode_with_supplementary_filelist_execution.swift b/test/Driver/batch_mode_with_supplementary_filelist_execution.swift index 9c77918917a55..5c2b595955430 100644 --- a/test/Driver/batch_mode_with_supplementary_filelist_execution.swift +++ b/test/Driver/batch_mode_with_supplementary_filelist_execution.swift @@ -6,6 +6,6 @@ // Ensure that the supplementary output filelist argument is passed to the frontend. // Also use some characters outside the BMP. // -// RUN: %target-build-swift -emit-dependencies -serialize-diagnostics -driver-use-filelists -j2 %t/main.swift %t/𝔼-file-01.swift %t/😂-file-02.swift %t/Ω-file-03.swift -o %t/a.out +// RUN: %target-build-swift -emit-dependencies -serialize-diagnostics -driver-filelist-threshold=0 -j2 %t/main.swift %t/𝔼-file-01.swift %t/😂-file-02.swift %t/Ω-file-03.swift -o %t/a.out // RUN: %target-run %t/a.out | %FileCheck %s -check-prefix=CHECK-HELLO-WORLD // CHECK-HELLO-WORLD: Hello, World! diff --git a/test/Driver/batch_mode_with_supplementary_filelist_unicode.swift b/test/Driver/batch_mode_with_supplementary_filelist_unicode.swift index 330edd954f5eb..ff334adaa6c6f 100644 --- a/test/Driver/batch_mode_with_supplementary_filelist_unicode.swift +++ b/test/Driver/batch_mode_with_supplementary_filelist_unicode.swift @@ -5,5 +5,5 @@ // If the supplementary output file map does not escape the characters in the // source files, the frontend won't recognize the desired outputs. // -// RUN: (cd %t && %target-build-swift -c -emit-dependencies -serialize-diagnostics -driver-use-filelists -j2 main.swift 𝔼-file-01.swift 😂-file-02.swift Ω-file-03.swift -module-name mod) +// RUN: (cd %t && %target-build-swift -c -emit-dependencies -serialize-diagnostics -driver-filelist-threshold=0 -j2 main.swift 𝔼-file-01.swift 😂-file-02.swift Ω-file-03.swift -module-name mod) // RUN: (cd %t && test -e 😂-file-02.d -a -e 😂-file-02.dia -a -e 😂-file-02.o -a -e 😂-file-02.swift -a -e 𝔼-file-01.d -a -e 𝔼-file-01.dia -a -e 𝔼-file-01.o -a -e 𝔼-file-01.swift -a -e main.d -a -e main.dia -a -e Ω-file-03.d -a -e Ω-file-03.dia -a -e Ω-file-03.o -a -e Ω-file-03.swift) diff --git a/test/Driver/continue-building-after-errors.swift b/test/Driver/continue-building-after-errors.swift index 7f84318664d16..4744e14ee7562 100644 --- a/test/Driver/continue-building-after-errors.swift +++ b/test/Driver/continue-building-after-errors.swift @@ -1,10 +1,16 @@ // RUN: not %target-build-swift %S/Inputs/error.swift %s 2>&1 | %FileCheck %s // RUN: not %target-build-swift -continue-building-after-errors %S/Inputs/error.swift %s 2>&1 | %FileCheck -check-prefix=CHECK-CONTINUE %s +// Check that batch mode implies -continue-building-after-errors. +// RUN: touch %t.empty.swift +// RUN: not %target-build-swift -enable-batch-mode -j2 %S/Inputs/error.swift %S/../Inputs/empty.swift %s %t.empty.swift 2>&1 | %FileCheck -check-prefix=CHECK-BATCH %s + // CHECK: self.bar = self.bar // CHECK-NOT: self.baz = self.baz // CHECK-CONTINUE: self.bar = self.bar // CHECK-CONTINUE: self.baz = self.baz +// CHECK-BATCH-DAG: self.bar = self.bar +// CHECK-BATCH-DAG: self.baz = self.baz struct Bar { let baz: Int init() { diff --git a/test/Driver/createCompilerInvocation.swift b/test/Driver/createCompilerInvocation.swift index 5dc6fcbb4f3e3..0151cd7ea6cfa 100644 --- a/test/Driver/createCompilerInvocation.swift +++ b/test/Driver/createCompilerInvocation.swift @@ -3,5 +3,7 @@ // RUN: %swift-ide-test_plain -test-createCompilerInvocation -c %s %S/Input/main.swift %S/Input/lib.swift -module-name createCompilerInvocation -emit-module -emit-objc-header 2>&1 // RUN: not %swift-ide-test_plain -test-createCompilerInvocation -typecheck %s -emit-module-path %t.swiftmodule 2>&1 | %FileCheck --check-prefix=CHECK-FAIL %s // RUN: not %swift-ide-test_plain -test-createCompilerInvocation -v 2>&1 | %FileCheck --check-prefix=CHECK-FAIL %s +// RUN: %swift-ide-test_plain -test-createCompilerInvocation %s -enable-batch-mode 2>&1 | %FileCheck -allow-empty -check-prefix=CHECK-NOWARN %s // CHECK-FAIL: error: unable to create a CompilerInvocation +// CHECK-NOWARN-NOT: warning diff --git a/test/Driver/debug-generic-signatures.swift b/test/Driver/debug-generic-signatures.swift new file mode 100644 index 0000000000000..d20e9b4660d62 --- /dev/null +++ b/test/Driver/debug-generic-signatures.swift @@ -0,0 +1,156 @@ +// RUN: %target-swift-frontend -typecheck -debug-generic-signatures %s 2>&1 | %FileCheck %s + +// CHECK: Generic signature: +// CHECK-NEXT: Canonical generic signature: <τ_0_0 where τ_0_0 : P1> +// CHECK-LABEL: main.(file).P1@ +// CHECK: Requirement signature: +// CHECK-NEXT: Canonical requirement signature: <τ_0_0> +protocol P1 { + associatedtype A + func f() -> A +} + +// Recursion, and where clauses. +// CHECK: Generic signature: +// CHECK-NEXT: Canonical generic signature: <τ_0_0 where τ_0_0 : P2> +// CHECK-LABEL: main.(file).P2@ +// CHECK: Requirement signature: +// CHECK-NEXT: Canonical requirement signature: <τ_0_0 where τ_0_0.A : P2, τ_0_0.B : P2, τ_0_0.A.A == τ_0_0.B.A> +protocol P2 { + associatedtype A: P2 + associatedtype B: P2 where Self.A.A == Self.B.A +} + +// Simpler recursion +// CHECK: Generic signature: +// CHECK-NEXT: Canonical generic signature: <τ_0_0 where τ_0_0 : P3> +// CHECK-LABEL: main.(file).P3@ +// CHECK: Requirement signature: +// CHECK-NEXT: Canonical requirement signature: <τ_0_0 where τ_0_0.A : P3> +protocol P3 { + associatedtype A: P3 +} + +// CHECK-LABEL: StructDecl name=Basic +// CHECK: (normal_conformance type=Basic protocol=P1 +// CHECK-NEXT: (assoc_type req=A type=Int) +// CHECK-NEXT: (value req=f() witness=main.(file).Basic.f()@{{.*}})) +struct Basic: P1 { + typealias A = Int + func f() -> Int { fatalError() } +} + +// Recursive conformances should have finite output. + +// CHECK-LABEL: StructDecl name=Recur +// CHECK-NEXT: (normal_conformance type=Recur protocol=P2 +// CHECK-NEXT: (assoc_type req=A type=Recur) +// CHECK-NEXT: (assoc_type req=B type=Recur) +// CHECK-NEXT: (normal_conformance type=Recur protocol=P2 (details printed above)) +// CHECK-NEXT: (normal_conformance type=Recur protocol=P2 (details printed above))) +struct Recur: P2 { + typealias A = Recur + typealias B = Recur +} + +// The full information about a conformance doesn't need to be printed twice. + +// CHECK-LABEL: StructDecl name=NonRecur +// CHECK-NEXT: (normal_conformance type=NonRecur protocol=P2 +// CHECK-NEXT: (assoc_type req=A type=Recur) +// CHECK-NEXT: (assoc_type req=B type=Recur) +// CHECK-NEXT: (normal_conformance type=Recur protocol=P2 +// CHECK-NEXT: (assoc_type req=A type=Recur) +// CHECK-NEXT: (assoc_type req=B type=Recur) +// CHECK-NEXT: (normal_conformance type=Recur protocol=P2 (details printed above)) +// CHECK-NEXT: (normal_conformance type=Recur protocol=P2 (details printed above))) +// CHECK-NEXT: (normal_conformance type=Recur protocol=P2 (details printed above))) +struct NonRecur: P2 { + typealias A = Recur + typealias B = Recur +} + +// Conditional conformance. + +struct Generic {} +// CHECK-LABEL: ExtensionDecl line={{.*}} base=Generic +// CHECK-NEXT: (normal_conformance type=Generic protocol=P1 +// CHECK-NEXT: (assoc_type req=A type=T) +// CHECK-NEXT: (value req=f() witness=main.(file).Generic.f()@{{.*}}) +// CHECK-NEXT: conforms_to: T P1) +extension Generic: P1 where T: P1 { + typealias A = T + func f() -> T { fatalError() } +} + + +// Satisfying associated types with requirements with generic params +class Super {} +// CHECK-LABEL: ExtensionDecl line={{.*}} base=Super +// CHECK-NEXT: (normal_conformance type=Super protocol=P2 +// CHECK-NEXT: (assoc_type req=A type=T) +// CHECK-NEXT: (assoc_type req=B type=T) +// CHECK-NEXT: (abstract_conformance protocol=P2) +// CHECK-NEXT: (abstract_conformance protocol=P2) +// CHECK-NEXT: conforms_to: T P2 +// CHECK-NEXT: conforms_to: U P2) +extension Super: P2 where T: P2, U: P2 { + typealias A = T + typealias B = T +} + +// Inherited/specialized conformances. +// CHECK-LABEL: ClassDecl name=Sub +// CHECK-NEXT: (inherited_conformance type=Sub protocol=P2 +// CHECK-NEXT: (specialized_conformance type=Super protocol=P2 +// CHECK-NEXT: (substitution_map generic_signature= +// CHECK-NEXT: (substitution T -> NonRecur) +// CHECK-NEXT: (substitution U -> Recur) +// CHECK-NEXT: (conformance type=T +// CHECK-NEXT: (normal_conformance type=NonRecur protocol=P2 +// CHECK-NEXT: (assoc_type req=A type=Recur) +// CHECK-NEXT: (assoc_type req=B type=Recur) +// CHECK-NEXT: (normal_conformance type=Recur protocol=P2 +// CHECK-NEXT: (assoc_type req=A type=Recur) +// CHECK-NEXT: (assoc_type req=B type=Recur) +// CHECK-NEXT: (normal_conformance type=Recur protocol=P2 (details printed above)) +// CHECK-NEXT: (normal_conformance type=Recur protocol=P2 (details printed above))) +// CHECK-NEXT: (normal_conformance type=Recur protocol=P2 (details printed above)))) +// CHECK-NEXT: (conformance type=U +// CHECK-NEXT: (normal_conformance type=Recur protocol=P2 (details printed above)))) +// CHECK-NEXT: conforms_to: NonRecur P2 +// CHECK-NEXT: conforms_to: Recur P2 +// CHECK-NEXT: (normal_conformance type=Super protocol=P2 +// CHECK-NEXT: (assoc_type req=A type=T) +// CHECK-NEXT: (assoc_type req=B type=T) +// CHECK-NEXT: (abstract_conformance protocol=P2) +// CHECK-NEXT: (abstract_conformance protocol=P2) +// CHECK-NEXT: conforms_to: T P2 +// CHECK-NEXT: conforms_to: U P2))) +class Sub: Super {} + +// Specialization of a recursive conformance should be okay: recursion detection +// should work through SubstitutionMaps. + +// CHECK-LABEL: StructDecl name=RecurGeneric +// CHECK-NEXT: (normal_conformance type=RecurGeneric protocol=P3 +// CHECK-NEXT: (assoc_type req=A type=RecurGeneric) +// CHECK-NEXT: (normal_conformance type=RecurGeneric protocol=P3 (details printed above))) +struct RecurGeneric: P3 { + typealias A = RecurGeneric +} + +// CHECK-LABEL: StructDecl name=Specialize +// CHECK-NEXT: (normal_conformance type=Specialize protocol=P3 +// CHECK-NEXT: (assoc_type req=A type=RecurGeneric) +// CHECK-NEXT: (specialized_conformance type=Specialize.A protocol=P3 +// CHECK-NEXT: (substitution_map generic_signature= +// CHECK-NEXT: (substitution T -> Specialize) +// CHECK-NEXT: (conformance type=T +// CHECK-NEXT: (normal_conformance type=Specialize protocol=P3 (details printed above)))) +// CHECK-NEXT: (normal_conformance type=RecurGeneric protocol=P3 +// CHECK-NEXT: (assoc_type req=A type=RecurGeneric) +// CHECK-NEXT: (normal_conformance type=RecurGeneric protocol=P3 (details printed above))))) +struct Specialize: P3 { + typealias A = RecurGeneric +} diff --git a/test/Driver/driver-compile.swift b/test/Driver/driver-compile.swift index fcf097dd23ae2..62f1dbe97b4ec 100644 --- a/test/Driver/driver-compile.swift +++ b/test/Driver/driver-compile.swift @@ -35,7 +35,7 @@ // RUN: cp %s %t // RUN: not %swiftc_driver -driver-print-jobs -c -target x86_64-apple-macosx10.9 %s %t/driver-compile.swift 2>&1 | %FileCheck -check-prefix DUPLICATE-NAME %s -// RUN: %swiftc_driver -driver-print-jobs -c -target x86_64-apple-macosx10.9 %s %S/../Inputs/empty.swift -module-name main -driver-use-filelists 2>&1 | %FileCheck -check-prefix=FILELIST %s +// RUN: %swiftc_driver -driver-print-jobs -c -target x86_64-apple-macosx10.9 %s %S/../Inputs/empty.swift -module-name main -driver-filelist-threshold=0 2>&1 | %FileCheck -check-prefix=FILELIST %s // RUN: %empty-directory(%t)/DISTINCTIVE-PATH/usr/bin/ // RUN: %hardlink-or-copy(from: %swift_driver_plain, to: %t/DISTINCTIVE-PATH/usr/bin/swiftc) diff --git a/test/Driver/dump-parse.swift b/test/Driver/dump-parse.swift index 94ebd3b6ba78e..30cdf5cfc175d 100644 --- a/test/Driver/dump-parse.swift +++ b/test/Driver/dump-parse.swift @@ -1,8 +1,8 @@ // RUN: not %target-swift-frontend -dump-parse %s 2>&1 | %FileCheck %s // RUN: not %target-swift-frontend -dump-ast %s 2>&1 | %FileCheck %s -check-prefix=CHECK-AST -// CHECK-LABEL: (func_decl "foo(_:)" -// CHECK-AST-LABEL: (func_decl "foo(_:)" +// CHECK-LABEL: (func_decl{{.*}}"foo(_:)" +// CHECK-AST-LABEL: (func_decl{{.*}}"foo(_:)" func foo(_ n: Int) -> Int { // CHECK: (brace_stmt // CHECK: (return_stmt @@ -15,8 +15,8 @@ func foo(_ n: Int) -> Int { } // -dump-parse should print an AST even though this code is invalid. -// CHECK-LABEL: (func_decl "bar()" -// CHECK-AST-LABEL: (func_decl "bar()" +// CHECK-LABEL: (func_decl{{.*}}"bar()" +// CHECK-AST-LABEL: (func_decl{{.*}}"bar()" func bar() { // CHECK: (brace_stmt // CHECK-NEXT: (unresolved_decl_ref_expr type='{{[^']+}}' name=foo @@ -29,28 +29,35 @@ func bar() { foo foo foo } -// CHECK-LABEL: (enum_decl trailing_semi "TrailingSemi" +// CHECK-LABEL: (enum_decl{{.*}}trailing_semi "TrailingSemi" enum TrailingSemi { - // CHECK-LABEL: (enum_case_decl trailing_semi + // CHECK-LABEL: (enum_case_decl{{.*}}trailing_semi // CHECK-NOT: (enum_element_decl{{.*}}trailing_semi - // CHECK: (enum_element_decl "A") - // CHECK: (enum_element_decl "B") + // CHECK: (enum_element_decl{{.*}}"A") + // CHECK: (enum_element_decl{{.*}}"B") case A,B; - // CHECK-LABEL: (subscript_decl trailing_semi - // CHECK-NOT: (func_decl trailing_semi 'anonname={{.*}}' getter_for=subscript(_:) - // CHECK: (accessor_decl 'anonname={{.*}}' getter_for=subscript(_:) + // CHECK-LABEL: (subscript_decl{{.*}}trailing_semi + // CHECK-NOT: (func_decl{{.*}}trailing_semi 'anonname={{.*}}' getter_for=subscript(_:) + // CHECK: (accessor_decl{{.*}}'anonname={{.*}}' getter_for=subscript(_:) subscript(x: Int) -> Int { - // CHECK-LABEL: (pattern_binding_decl trailing_semi - // CHECK-NOT: (var_decl trailing_semi "y" - // CHECK: (var_decl "y" + // CHECK-LABEL: (pattern_binding_decl{{.*}}trailing_semi + // CHECK-NOT: (var_decl{{.*}}trailing_semi "y" + // CHECK: (var_decl{{.*}}"y" var y = 1; // CHECK-LABEL: (sequence_expr {{.*}} trailing_semi y += 1; - // CHECK-LABEL: (return_stmt trailing_semi + // CHECK-LABEL: (return_stmt{{.*}}trailing_semi return y; }; }; + +// The substitution map for a declref should be relatively unobtrustive. +// CHECK-AST-LABEL: (func_decl{{.*}}"generic(_:)" interface type=' (T) -> ()' access=internal captures=( ) +func generic(_: T) {} +// CHECK-AST: (pattern_binding_decl +// CHECK-AST: (declref_expr type='(Int) -> ()' location={{.*}} range={{.*}} decl=main.(file).generic@{{.*}} [with (substitution_map generic_signature= (substitution T -> Int))] function_ref=unapplied)) +let _: (Int) -> () = generic diff --git a/test/Driver/emit_public_type_metadata_accessors.swift b/test/Driver/emit_public_type_metadata_accessors.swift new file mode 100644 index 0000000000000..123c13636a8e1 --- /dev/null +++ b/test/Driver/emit_public_type_metadata_accessors.swift @@ -0,0 +1,3 @@ +// RUN: %target-build-swift %s -emit-public-type-metadata-accessors 2>&1 | %FileCheck %s + +// CHECK: the option '-emit-public-type-metadata-accessors' is no longer needed and is deprecated; consider removing it diff --git a/test/Driver/filelist-threshold.swift b/test/Driver/filelist-threshold.swift new file mode 100644 index 0000000000000..32e0dc49b1b50 --- /dev/null +++ b/test/Driver/filelist-threshold.swift @@ -0,0 +1,44 @@ +// RUN: %empty-directory(%t) +// RUN: touch %t/a.swift %t/b.swift + +// RUN: %swiftc_driver -driver-print-jobs -c -target x86_64-apple-macosx10.9 %t/a.swift %t/b.swift %S/../Inputs/empty.swift -module-name main -driver-filelist-threshold=99 2>&1 | %FileCheck -check-prefix=UNDERTHRESHOLD %s + +// UNDERTHRESHOLD-NOT: -filelist +// UNDERTHRESHOLD-NOT: -primary-filelist +// UNDERTHRESHOLD-NOT: -supplementary-output-file-map +// UNDERTHRESHOLD-NOT: -output-filelist + + +// RUN: %swiftc_driver -driver-print-jobs -c -target x86_64-apple-macosx10.9 %t/a.swift %t/b.swift %S/../Inputs/empty.swift -module-name main -driver-filelist-threshold=0 | %FileCheck -check-prefix=OVERTHRESHOLD %s +// RUN: %swiftc_driver -driver-print-jobs -c -target x86_64-apple-macosx10.9 %t/a.swift %t/b.swift %S/../Inputs/empty.swift -module-name main -driver-use-filelists > %t/out.txt 2>&1 +// RUN: %FileCheck -check-prefixes=OVERTHRESHOLD,DEPRECATED --input-file %t/out.txt %s + +// DEPRECATED: warning: the option '-driver-use-filelists' is deprecated; use '-driver-filelist-threshold=0' instead +// OVERTHRESHOLD: -filelist +// OVERTHRESHOLD: -primary-filelist +// OVERTHRESHOLD: -supplementary-output-file-map +// OVERTHRESHOLD: -output-filelist +// OVERTHRESHOLD: -filelist +// OVERTHRESHOLD: -primary-filelist +// OVERTHRESHOLD: -supplementary-output-file-map +// OVERTHRESHOLD: -output-filelist +// OVERTHRESHOLD: -filelist +// OVERTHRESHOLD: -primary-filelist +// OVERTHRESHOLD: -supplementary-output-file-map +// OVERTHRESHOLD: -output-filelist + + +// RUN: %swiftc_driver -driver-print-jobs -c -target x86_64-apple-macosx10.9 %t/a.swift %t/b.swift %S/../Inputs/empty.swift -module-name main -driver-filelist-threshold=1 | %FileCheck -check-prefix=MIXEDTHRESHOLD %s + +// MIXEDTHRESHOLD: -filelist +// MIXEDTHRESHOLD-NOT: -primary-filelist +// MIXEDTHRESHOLD: -supplementary-output-file-map +// MIXEDTHRESHOLD-NOT: -output-filelist +// MIXEDTHRESHOLD: -filelist +// MIXEDTHRESHOLD-NOT: -primary-filelist +// MIXEDTHRESHOLD: -supplementary-output-file-map +// MIXEDTHRESHOLD-NOT: -output-filelist +// MIXEDTHRESHOLD: -filelist +// MIXEDTHRESHOLD-NOT: -primary-filelist +// MIXEDTHRESHOLD: -supplementary-output-file-map +// MIXEDTHRESHOLD-NOT: -output-filelist diff --git a/test/Driver/filelists.swift b/test/Driver/filelists.swift index 70ff7b0e85501..38bba22b8ab7a 100644 --- a/test/Driver/filelists.swift +++ b/test/Driver/filelists.swift @@ -1,7 +1,7 @@ // RUN: %empty-directory(%t) // RUN: touch %t/a.swift %t/b.swift %t/c.swift -// RUN: (cd %t && %swiftc_driver_plain -driver-use-frontend-path %S/Inputs/filelists/check-filelist-abc.py -emit-module ./a.swift ./b.swift ./c.swift -module-name main -target x86_64-apple-macosx10.9 -driver-use-filelists -output-file-map=%S/Inputs/filelists/output.json 2>&1 | %FileCheck %s) +// RUN: (cd %t && %swiftc_driver_plain -driver-use-frontend-path %S/Inputs/filelists/check-filelist-abc.py -emit-module ./a.swift ./b.swift ./c.swift -module-name main -target x86_64-apple-macosx10.9 -driver-filelist-threshold=0 -output-file-map=%S/Inputs/filelists/output.json 2>&1 | %FileCheck %s) // CHECK-NOT: Handled // CHECK: Handled a.swift @@ -21,7 +21,7 @@ -// RUN: %swiftc_driver_plain -driver-use-frontend-path %S/Inputs/filelists/check-filelist-abc.py -c %t/a.swift %t/b.swift %t/c.swift -module-name main -target x86_64-apple-macosx10.9 -driver-use-filelists -force-single-frontend-invocation 2>&1 | %FileCheck -check-prefix=CHECK-WMO %s +// RUN: %swiftc_driver_plain -driver-use-frontend-path %S/Inputs/filelists/check-filelist-abc.py -c %t/a.swift %t/b.swift %t/c.swift -module-name main -target x86_64-apple-macosx10.9 -driver-filelist-threshold=0 -force-single-frontend-invocation 2>&1 | %FileCheck -check-prefix=CHECK-WMO %s // CHECK-WMO-NOT: Handled // CHECK-WMO: Handled all @@ -34,10 +34,10 @@ // RUN: %empty-directory(%t/bin) // RUN: ln -s %S/Inputs/filelists/fake-ld.py %t/bin/ld -// RUN: (cd %t && %swiftc_driver_plain -driver-use-frontend-path %S/Inputs/filelists/check-filelist-abc.py -c ./a.swift ./b.swift ./c.swift -module-name main -target x86_64-apple-macosx10.9 -driver-use-filelists -output-file-map=%S/Inputs/filelists/output.json -force-single-frontend-invocation -num-threads 1 2>&1 | %FileCheck -check-prefix=CHECK-WMO-THREADED %s) -// RUN: (cd %t && %swiftc_driver_plain -driver-use-frontend-path %S/Inputs/filelists/check-filelist-abc.py -c ./a.swift ./b.swift ./c.swift -module-name main -target x86_64-apple-macosx10.9 -driver-use-filelists -output-file-map=%S/Inputs/filelists/output.json -force-single-frontend-invocation -num-threads 1 -embed-bitcode 2>&1 | %FileCheck -check-prefix=CHECK-WMO-THREADED %s) +// RUN: (cd %t && %swiftc_driver_plain -driver-use-frontend-path %S/Inputs/filelists/check-filelist-abc.py -c ./a.swift ./b.swift ./c.swift -module-name main -target x86_64-apple-macosx10.9 -driver-filelist-threshold=0 -output-file-map=%S/Inputs/filelists/output.json -force-single-frontend-invocation -num-threads 1 2>&1 | %FileCheck -check-prefix=CHECK-WMO-THREADED %s) +// RUN: (cd %t && %swiftc_driver_plain -driver-use-frontend-path %S/Inputs/filelists/check-filelist-abc.py -c ./a.swift ./b.swift ./c.swift -module-name main -target x86_64-apple-macosx10.9 -driver-filelist-threshold=0 -output-file-map=%S/Inputs/filelists/output.json -force-single-frontend-invocation -num-threads 1 -embed-bitcode 2>&1 | %FileCheck -check-prefix=CHECK-WMO-THREADED %s) // RUN: %empty-directory(%t/tmp) -// RUN: (cd %t && env TMPDIR="%t/tmp/" %swiftc_driver_plain -driver-use-frontend-path %S/Inputs/filelists/check-filelist-abc.py -c ./a.swift ./b.swift ./c.swift -module-name main -target x86_64-apple-macosx10.9 -driver-use-filelists -output-file-map=%S/Inputs/filelists/output.json -force-single-frontend-invocation -num-threads 1 -save-temps 2>&1 | %FileCheck -check-prefix=CHECK-WMO-THREADED %s) +// RUN: (cd %t && env TMPDIR="%t/tmp/" %swiftc_driver_plain -driver-use-frontend-path %S/Inputs/filelists/check-filelist-abc.py -c ./a.swift ./b.swift ./c.swift -module-name main -target x86_64-apple-macosx10.9 -driver-filelist-threshold=0 -output-file-map=%S/Inputs/filelists/output.json -force-single-frontend-invocation -num-threads 1 -save-temps 2>&1 | %FileCheck -check-prefix=CHECK-WMO-THREADED %s) // RUN: ls %t/tmp/sources-* %t/tmp/outputs-* // CHECK-WMO-THREADED-NOT: Handled @@ -52,17 +52,17 @@ // CHECK-WMO-THREADED-NOT: Handled // RUN: mkdir -p %t/tmp-fail/ -// RUN: (cd %t && not env TMPDIR="%t/tmp-fail/" %swiftc_driver_plain -driver-use-frontend-path %S/Inputs/fail.py -c ./a.swift ./b.swift ./c.swift -module-name main -target x86_64-apple-macosx10.9 -driver-use-filelists -output-file-map=%S/Inputs/filelists/output.json -force-single-frontend-invocation -num-threads 1) +// RUN: (cd %t && not env TMPDIR="%t/tmp-fail/" %swiftc_driver_plain -driver-use-frontend-path %S/Inputs/fail.py -c ./a.swift ./b.swift ./c.swift -module-name main -target x86_64-apple-macosx10.9 -driver-filelist-threshold=0 -output-file-map=%S/Inputs/filelists/output.json -force-single-frontend-invocation -num-threads 1) // RUN: not ls %t/tmp-fail/sources-* // RUN: not ls %t/tmp-fail/outputs-* // RUN: mkdir -p %t/tmp-crash/ -// RUN: (cd %t && not env TMPDIR="%t/tmp-crash/" %swiftc_driver_plain -driver-use-frontend-path %S/Inputs/crash.py -c ./a.swift ./b.swift ./c.swift -module-name main -target x86_64-apple-macosx10.9 -driver-use-filelists -output-file-map=%S/Inputs/filelists/output.json -force-single-frontend-invocation -num-threads 1) +// RUN: (cd %t && not env TMPDIR="%t/tmp-crash/" %swiftc_driver_plain -driver-use-frontend-path %S/Inputs/crash.py -c ./a.swift ./b.swift ./c.swift -module-name main -target x86_64-apple-macosx10.9 -driver-filelist-threshold=0 -output-file-map=%S/Inputs/filelists/output.json -force-single-frontend-invocation -num-threads 1) // RUN: ls %t/tmp-crash/sources-* %t/tmp-crash/outputs-* -// RUN: (cd %t && env PATH="%t/bin/:$PATH" %swiftc_driver_plain -driver-use-frontend-path %S/Inputs/filelists/check-filelist-abc.py -emit-library ./a.swift ./b.swift ./c.swift -module-name main -target x86_64-apple-macosx10.9 -driver-use-filelists -output-file-map=%S/Inputs/filelists/output.json 2>&1 | %FileCheck -check-prefix=CHECK-LINK %s) -// RUN: (cd %t && env PATH="%t/bin/:$PATH" %swiftc_driver_plain -driver-use-frontend-path %S/Inputs/filelists/check-filelist-abc.py -emit-library ./a.swift ./b.swift ./c.swift -module-name main -target x86_64-apple-macosx10.9 -driver-use-filelists -output-file-map=%S/Inputs/filelists/output.json -force-single-frontend-invocation -num-threads 1 2>&1 | %FileCheck -check-prefix=CHECK-LINK %s) +// RUN: (cd %t && env PATH="%t/bin/:$PATH" %swiftc_driver_plain -driver-use-frontend-path %S/Inputs/filelists/check-filelist-abc.py -emit-library ./a.swift ./b.swift ./c.swift -module-name main -target x86_64-apple-macosx10.9 -driver-filelist-threshold=0 -output-file-map=%S/Inputs/filelists/output.json 2>&1 | %FileCheck -check-prefix=CHECK-LINK %s) +// RUN: (cd %t && env PATH="%t/bin/:$PATH" %swiftc_driver_plain -driver-use-frontend-path %S/Inputs/filelists/check-filelist-abc.py -emit-library ./a.swift ./b.swift ./c.swift -module-name main -target x86_64-apple-macosx10.9 -driver-filelist-threshold=0 -output-file-map=%S/Inputs/filelists/output.json -force-single-frontend-invocation -num-threads 1 2>&1 | %FileCheck -check-prefix=CHECK-LINK %s) // CHECK-LINK: Handled link diff --git a/test/Driver/help.swift b/test/Driver/help.swift new file mode 100644 index 0000000000000..9fb89d421aefb --- /dev/null +++ b/test/Driver/help.swift @@ -0,0 +1,23 @@ +// Check that options printed with -help respect whether the driver is invoked +// as 'swift' or as 'swiftc'. + +// RUN: %swiftc_driver -help | %FileCheck -check-prefix CHECK -check-prefix CHECK-SWIFTC %s +// RUN: %swiftc_driver -help | %FileCheck -check-prefix NEGATIVE -check-prefix NEGATIVE-SWIFTC %s + +// RUN: %swift_driver -help | %FileCheck -check-prefix CHECK -check-prefix CHECK-SWIFT %s +// RUN: %swift_driver -help | %FileCheck -check-prefix NEGATIVE -check-prefix NEGATIVE-SWIFT %s + +// Options that work with both 'swiftc' and 'swift': +// CHECK-DAG: -swift-version + +// swiftc-only options: +// CHECK-SWIFTC-DAG: -typecheck +// NEGATIVE-SWIFT-NOT: -typecheck + +// There are currently no interpreter-only options. + +// Frontend options should not show up here. +// NEGATIVE-NOT: -merge-modules + +// Options marked "help-hidden" should not show up here. +// NEGATIVE-NOT: -parse-stdlib diff --git a/test/Driver/imported_modules.swift b/test/Driver/imported_modules.swift index 0b40d22c55516..88bce0d07209e 100644 --- a/test/Driver/imported_modules.swift +++ b/test/Driver/imported_modules.swift @@ -7,3 +7,13 @@ import Y import enum Foo.Member // The overlaying Swift module should not be loaded. import InvalidOverlay + +#if canImport(Swift) // To wit, true +import Swift +#else +import Garbage +#endif + +#if !canImport(Swift) // To wit, false +import Garbage +#endif diff --git a/test/Driver/linker.swift b/test/Driver/linker.swift index 075e6f238194d..08a387a2221a4 100644 --- a/test/Driver/linker.swift +++ b/test/Driver/linker.swift @@ -33,6 +33,9 @@ // RUN: %swiftc_driver -driver-print-jobs -target x86_64-unknown-windows-cygnus -Ffoo -Fsystem car -F cdr -framework bar -Lbaz -lboo -Xlinker -undefined %s 2>&1 > %t.cygwin.txt // RUN: %FileCheck -check-prefix CYGWIN-x86_64 %s < %t.cygwin.txt +// RUN: %swiftc_driver -driver-print-jobs -target x86_64-unknown-windows-msvc -Ffoo -Fsystem car -F cdr -framework bar -Lbaz -lboo -Xlinker -undefined %s 2>&1 > %t.windows.txt +// RUN: %FileCheck -check-prefix WINDOWS-x86_64 %s < %t.windows.txt + // RUN: %swiftc_driver -driver-print-jobs -emit-library -target x86_64-unknown-linux-gnu %s -Lbar -o dynlib.out 2>&1 > %t.linux.dynlib.txt // RUN: %FileCheck -check-prefix LINUX_DYNLIB-x86_64 %s < %t.linux.dynlib.txt @@ -56,11 +59,12 @@ // RUN: %empty-directory(%t) // RUN: touch %t/a.o // RUN: %swiftc_driver -driver-print-jobs -target x86_64-apple-macosx10.9 %s %t/a.o -o linker 2>&1 | %FileCheck -check-prefix COMPILE_AND_LINK %s -// RUN: %swiftc_driver -driver-print-jobs -target x86_64-apple-macosx10.9 %s %t/a.o -driver-use-filelists -o linker 2>&1 | %FileCheck -check-prefix FILELIST %s +// RUN: %swiftc_driver -driver-print-jobs -target x86_64-apple-macosx10.9 %s %t/a.o -driver-filelist-threshold=0 -o linker 2>&1 | %FileCheck -check-prefix FILELIST %s // RUN: %swiftc_driver -driver-print-jobs -target x86_64-apple-macosx10.9 -emit-library %s -module-name LINKER | %FileCheck -check-prefix INFERRED_NAME_DARWIN %s // RUN: %swiftc_driver -driver-print-jobs -target x86_64-unknown-linux-gnu -emit-library %s -module-name LINKER | %FileCheck -check-prefix INFERRED_NAME_LINUX %s // RUN: %swiftc_driver -driver-print-jobs -target x86_64-unknown-windows-cygnus -emit-library %s -module-name LINKER | %FileCheck -check-prefix INFERRED_NAME_WINDOWS %s +// RUN: %swiftc_driver -driver-print-jobs -target x86_64-unknown-windows-msvc -emit-library %s -module-name LINKER | %FileCheck -check-prefix INFERRED_NAME_WINDOWS %s // Here we specify an output file name using '-o'. For ease of writing these // tests, we happen to specify the same file name as is inferred in the @@ -240,6 +244,20 @@ // CYGWIN-x86_64-DAG: -Xlinker -undefined // CYGWIN-x86_64: -o linker +// WINDOWS-x86_64: swift +// WINDOWS-x86_64: -o [[OBJECTFILE:.*]] + +// WINDOWS-x86_64: clang++{{"? }} +// WINDOWS-x86_64-DAG: [[OBJECTFILE]] +// WINDOWS-x86_64-DAG: -L [[STDLIB_PATH:[^ ]+/lib/swift/windows/x86_64]] +// WINDOWS-x86_64-DAG: -F foo -iframework car -F cdr +// WINDOWS-x86_64-DAG: -framework bar +// WINDOWS-x86_64-DAG: -L baz +// WINDOWS-x86_64-DAG: -lboo +// WINDOWS-x86_64-DAG: -Xlinker -undefined +// WINDOWS-x86_64: -o linker + + // COMPLEX: bin/ld{{"? }} // COMPLEX-DAG: -dylib // COMPLEX-DAG: -syslibroot {{.*}}/Inputs/clang-importer-sdk diff --git a/test/Driver/loaded_module_trace_append.swift b/test/Driver/loaded_module_trace_append.swift index 80ddef9babcb1..0d1e274ad46b4 100644 --- a/test/Driver/loaded_module_trace_append.swift +++ b/test/Driver/loaded_module_trace_append.swift @@ -1,3 +1,4 @@ +// RUN: rm -f %t // RUN: env SWIFT_LOADED_MODULE_TRACE_FILE=%t %target-build-swift -module-name loaded_module_trace_append %s -o- > /dev/null // RUN: env SWIFT_LOADED_MODULE_TRACE_FILE=%t %target-build-swift -module-name loaded_module_trace_append2 %s -o- > /dev/null // RUN: %FileCheck %s < %t diff --git a/test/Driver/loaded_module_trace_env.swift b/test/Driver/loaded_module_trace_env.swift index e95c9acc91087..e7d9ef7586890 100644 --- a/test/Driver/loaded_module_trace_env.swift +++ b/test/Driver/loaded_module_trace_env.swift @@ -1,3 +1,4 @@ +// RUN: rm -f %t // RUN: env SWIFT_LOADED_MODULE_TRACE_FILE=%t %target-build-swift -module-name loaded_module_trace_env %s -o- > /dev/null // RUN: %FileCheck %s < %t diff --git a/test/Driver/loaded_module_trace_header.swift b/test/Driver/loaded_module_trace_header.swift index 242f7c38971d2..7c427fc5b7f47 100644 --- a/test/Driver/loaded_module_trace_header.swift +++ b/test/Driver/loaded_module_trace_header.swift @@ -1,3 +1,4 @@ +// RUN: rm -f %t // RUN: env SWIFT_LOADED_MODULE_TRACE_FILE=%t %target-build-swift -module-name loaded_module_trace_header %s -o- -import-objc-header %S/Inputs/loaded_module_trace_header.h > /dev/null // RUN: %FileCheck %s < %t diff --git a/test/Driver/merge-module.swift b/test/Driver/merge-module.swift index 81f570104966d..c4929be909a7f 100644 --- a/test/Driver/merge-module.swift +++ b/test/Driver/merge-module.swift @@ -14,7 +14,7 @@ // RUN: %FileCheck %s < %t.complex.txt // RUN: %FileCheck -check-prefix THREE-OUTPUTS %s < %t.complex.txt -// RUN: %swiftc_driver -emit-module -driver-print-jobs -driver-use-filelists %s %S/../Inputs/empty.swift -module-name main 2>&1 | %FileCheck -check-prefix FILELISTS %s +// RUN: %swiftc_driver -emit-module -driver-print-jobs -driver-filelist-threshold=0 %s %S/../Inputs/empty.swift -module-name main 2>&1 | %FileCheck -check-prefix FILELISTS %s // CHECK: bin/swift{{c?}} -frontend // CHECK: -module-name {{[^ ]+}} diff --git a/test/Driver/missing-ofm.swift b/test/Driver/missing-ofm.swift new file mode 100644 index 0000000000000..3266a7e61e0e7 --- /dev/null +++ b/test/Driver/missing-ofm.swift @@ -0,0 +1,8 @@ +// Ensure that a bogus output-file-map path does not crash the driver, +// but instead outputs a nice diagnostic. +// +// RUN: %empty-directory(%t) +// RUN: not %swiftc_driver -c %S/../Inputs/empty.swift -output-file-map %t/something-which-should-not-exist.json 2>&1 | %FileCheck %s +// +// CHECK: error: unable to load output file map '{{.*}}/something-which-should-not-exist.json': No such file or directory +// CHECK-NOT: Assertion failed diff --git a/test/Driver/options.swift b/test/Driver/options.swift index e9807c404d68e..91d5d4c69d1ad 100644 --- a/test/Driver/options.swift +++ b/test/Driver/options.swift @@ -129,3 +129,7 @@ // RUN: %swiftc_driver -driver-print-jobs -assume-single-threaded %s | %FileCheck -check-prefix=ASSUME_SINGLE_THREADED %s // ASSUME_SINGLE_THREADED: swift // ASSUME_SINGLE_THREADED: -frontend {{.*}} -assume-single-threaded + +// RUN: not %swiftc_driver -incremental -autolink-force-load %s 2>&1 | %FileCheck -check-prefix=AUTOLINK_FORCE_LOAD %s +// RUN: not %swiftc_driver -autolink-force-load -incremental %s 2>&1 | %FileCheck -check-prefix=AUTOLINK_FORCE_LOAD %s +// AUTOLINK_FORCE_LOAD: error: '-autolink-force-load' is not supported with '-incremental' diff --git a/test/Driver/pipe_round_robin.swift.gyb b/test/Driver/pipe_round_robin.swift.gyb new file mode 100644 index 0000000000000..d706a345e1a9e --- /dev/null +++ b/test/Driver/pipe_round_robin.swift.gyb @@ -0,0 +1,33 @@ +// RUN: %empty-directory(%t/manyfuncs) +// RUN: %empty-directory(%t/stats) +// +// This test is looking at behaviour of the driver's task queue, checking +// to make sure that it drains the output streams of multiple subprocesses +// evenly, rather than one subprocess all-at-once before the next +// all-at-once. This isn't batch-mode specific but it emerges somewhat +// vividly there when combined with lots of files that do +// -debug-time-function-bodies. +// +// Here we do a non-batch-mode variant that has lots of functions in each +// of 4 files. +// +// RUN: %gyb -D N=1 %s -o %t/manyfuncs/file1.swift +// RUN: %gyb -D N=2 %s -o %t/manyfuncs/file2.swift +// RUN: %gyb -D N=3 %s -o %t/manyfuncs/file3.swift +// RUN: %gyb -D N=4 %s -o %t/manyfuncs/file4.swift +// +// We calculate the ratio of poll() calls to read() calls; these should be +// nearly equal (we test abs(read/poll) < 3.0) if we're doing interleaved +// reading. If we're doing non-interleaved reading, they become radically +// different (eg. thousands of reads per poll). +// +// RUN: %target-build-swift -j 4 -module-name manyfuncs -typecheck -stats-output-dir %t/stats -Xfrontend -debug-time-function-bodies %t/manyfuncs/*.swift +// RUN: %utils/process-stats-dir.py --evaluate 'abs(float(NumDriverPipeReads) / float(NumDriverPipePolls)) < 3.0' %t/stats + +% for i in range(1,1000): +func process_${N}_function_${i}(_ x: Int) -> Int { + let v = (1 + 2 * 3 + x * 5 + x + 6) + let a = [v, 1, ${i}, 3, ${N}, 4] + return a.reduce(0, {$0 + $1}) +} +% end diff --git a/test/Driver/profiling.swift b/test/Driver/profiling.swift index 0ef41d7373cc4..32c252aa072dc 100644 --- a/test/Driver/profiling.swift +++ b/test/Driver/profiling.swift @@ -18,6 +18,7 @@ // RUN: %swiftc_driver -driver-print-jobs -profile-generate -target armv7k-apple-watchos2.0 -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ %s | %FileCheck -check-prefix=CHECK -check-prefix=watchOS %s // RUN: %swiftc_driver -driver-print-jobs -profile-generate -target x86_64-unknown-linux-gnu %s | %FileCheck -check-prefix=CHECK -check-prefix=LINUX %s +// RUN: %swiftc_driver -driver-print-jobs -profile-generate -target x86_64-unknown-windows-msvc %s | %FileCheck -check-prefix=CHECK -check-prefix=WINDOWS %s // CHECK: swift // CHECK: -profile-generate @@ -47,6 +48,10 @@ // LINUX: lib/swift/clang/lib/linux/libclang_rt.profile-x86_64.a // LINUX: -u__llvm_profile_runtime +// WINDOWS: clang++{{"? }} +// WINDOWS: lib/swift/clang/lib/windows/clang_rt.profile-x86_64.lib +// WINDOWS: -u__llvm_profile_runtime + // RUN: not %swiftc_driver -driver-print-jobs -profile-generate -profile-use=/dev/null %s 2>&1 | %FileCheck -check-prefix=MIX_GEN_USE %s // MIX_GEN_USE: conflicting options '-profile-generate' and '-profile-use' diff --git a/test/Driver/response-file.swift b/test/Driver/response-file.swift new file mode 100644 index 0000000000000..8ffcb8c0c2cba --- /dev/null +++ b/test/Driver/response-file.swift @@ -0,0 +1,45 @@ +// RUN: echo "-DTEST0" > %t.0.resp +// RUN: %target-build-swift -typecheck @%t.0.resp %s 2>&1 | %FileCheck %s -check-prefix=SHORT +// SHORT: warning: result of call to 'abs' is unused + +// RUN: python -c 'for a in ["A", "B", "C", "D"]: print "-DTEST1" + a' > %t.1.resp +// RUN: %target-build-swift -typecheck @%t.1.resp %s 2>&1 | %FileCheck %s -check-prefix=MULTIPLE +// MULTIPLE: warning: result of call to 'abs' is unused + +// RUN: echo "-DTEST2A -DTEST2B" > %t.2.resp +// RUN: echo "-DTEST2C -DTEST2D" > %t.3.resp +// RUN: %target-build-swift -typecheck @%t.2.resp %s @%t.3.resp 2>&1 | %FileCheck %s -check-prefix=MIXED +// MIXED: warning: result of call to 'abs' is unused + +// RUN: echo "-DTEST3A" > %t.4.resp +// RUN: echo "%s" >> %t.4.resp +// RUN: echo "-DTEST3B" >> %t.4.resp +// RUN: %target-build-swift -typecheck @%t.4.resp 2>&1 | %FileCheck %s -check-prefix=RESPONLY +// RESPONLY: warning: result of call to 'abs' is unused + +// RUN: echo "-DTEST4A" > %t.5.resp +// RUN: echo "%s" >> %t.5.resp +// RUN: echo "@%t.5.resp" > %t.6.resp +// RUN: echo "-DTEST4B" >> %t.6.resp +// RUN: %target-build-swift -typecheck @%t.6.resp 2>&1 | %FileCheck %s -check-prefix=RECURSIVE +// RECURSIVE: warning: result of call to 'abs' is unused + +#if TEST0 +abs(-5) +#endif + +#if TEST1A && TEST1B && TEST1C && TEST1D +abs(-5) +#endif + +#if TEST2A && TEST2B && TEST2C && TEST2D +abs(-5) +#endif + +#if TEST3A && TEST3B +abs(-5) +#endif + +#if TEST4A && TEST4B +abs(-5) +#endif diff --git a/test/Driver/sanitizers.swift b/test/Driver/sanitizers.swift index d143c70fea600..894c5d6ea1b7d 100644 --- a/test/Driver/sanitizers.swift +++ b/test/Driver/sanitizers.swift @@ -7,6 +7,7 @@ // RUN: %swiftc_driver -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=address -target i386-apple-watchos2.0 %s | %FileCheck -check-prefix=ASAN -check-prefix=ASAN_watchOS_SIM %s // RUN: %swiftc_driver -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=address -target armv7k-apple-watchos2.0 %s | %FileCheck -check-prefix=ASAN -check-prefix=ASAN_watchOS %s // RUN: %swiftc_driver -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=address -target x86_64-unknown-linux-gnu %s 2>&1 | %FileCheck -check-prefix=ASAN_LINUX %s +// RUN: %swiftc_driver -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=address -target x86_64-unknown-windows-msvc %s 2>&1 | %FileCheck -check-prefix=ASAN_WINDOWS %s // RUN: %swiftc_driver -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=thread -target x86_64-apple-macosx10.9 %s | %FileCheck -check-prefix=TSAN -check-prefix=TSAN_OSX %s // RUN: not %swiftc_driver -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=thread -target x86-apple-macosx10.9 %s 2>&1 | %FileCheck -check-prefix=TSAN_OSX_32 %s @@ -16,6 +17,7 @@ // RUN: not %swiftc_driver -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=thread -target arm64-apple-tvos9.0 %s 2>&1 | %FileCheck -check-prefix=TSAN_tvOS %s // RUN: not %swiftc_driver -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=thread -target i386-apple-watchos2.0 %s 2>&1 | %FileCheck -check-prefix=TSAN_watchOS_SIM %s // RUN: not %swiftc_driver -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=thread -target armv7k-apple-watchos2.0 %s 2>&1 | %FileCheck -check-prefix=TSAN_watchOS %s +// RUN: not %swiftc_driver -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=thread -target x86_64-unknown-windows-msvc %s 2>&1 | %FileCheck -check-prefix=TSAN_WINDOWS %s // RUN: %swiftc_driver -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=thread -target x86_64-unknown-linux-gnu %s 2>&1 | %FileCheck -check-prefix=TSAN_LINUX %s // RUN: not %swiftc_driver -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=address,unknown %s 2>&1 | %FileCheck -check-prefix=BADARG %s @@ -41,6 +43,7 @@ // ASAN_watchOS_SIM: lib/swift/clang/lib/darwin/libclang_rt.asan_watchossim_dynamic.dylib // ASAN_watchOS: lib/swift/clang/lib/darwin/libclang_rt.asan_watchos_dynamic.dylib // ASAN_LINUX: lib/swift/clang/lib/linux/libclang_rt.asan-x86_64.a +// ASAN_WINDOWS: lib/swift/clang/lib/windows/clang_rt.asan-x86_64.lib // ASAN: -rpath @executable_path @@ -57,6 +60,7 @@ // TSAN_watchOS: unsupported option '-sanitize=thread' for target 'armv7k-apple-watchos2.0' // FUZZER_NONEXISTENT: unsupported option '-sanitize=fuzzer' for target 'x86_64-apple-macosx10.9' // TSAN_LINUX: lib/swift/clang/lib/linux/libclang_rt.tsan-x86_64.a +// TSAN_WINDOWS: unsupported option '-sanitize=thread' for target 'x86_64-unknown-windows-msvc' // TSAN: -rpath @executable_path diff --git a/test/Driver/sdk.swift b/test/Driver/sdk.swift index 63f9c74649e68..4aa0909d7318a 100644 --- a/test/Driver/sdk.swift +++ b/test/Driver/sdk.swift @@ -1,6 +1,7 @@ -// RUN: %swiftc_driver -driver-print-jobs -target x86_64-apple-macosx10.9 -g -sdk %S/../Inputs/clang-importer-sdk %s 2>&1 | %FileCheck %s --check-prefix OSX -// RUN: %swiftc_driver -driver-print-jobs -target x86_64-unknown-linux-gnu -g -sdk %S/../Inputs/clang-importer-sdk %s 2>&1 | %FileCheck %s --check-prefix LINUX -// RUN: %swiftc_driver -driver-print-jobs -target x86_64-unknown-freebsd -g -sdk %S/../Inputs/clang-importer-sdk %s 2>&1 | %FileCheck %s --check-prefix FREEBSD +// RUN: %swiftc_driver -driver-print-jobs -target x86_64-apple-macosx10.9 -g -sdk %S/../Inputs/clang-importer-sdk %s 2>&1 | %FileCheck %s --check-prefix OSX +// RUN: %swiftc_driver -driver-print-jobs -target x86_64-unknown-linux-gnu -g -sdk %S/../Inputs/clang-importer-sdk %s 2>&1 | %FileCheck %s --check-prefix LINUX +// RUN: %swiftc_driver -driver-print-jobs -target x86_64-unknown-freebsd -g -sdk %S/../Inputs/clang-importer-sdk %s 2>&1 | %FileCheck %s --check-prefix FREEBSD +// RUN: %swiftc_driver -driver-print-jobs -target x86_64-unknown-windows-msvc -g -sdk %S/../Inputs/clang-importer-sdk %s 2>&1 | %FileCheck %s --check-prefix WINDOWS // RUN: env SDKROOT=%S/../Inputs/clang-importer-sdk %swiftc_driver_plain -target x86_64-apple-macosx10.9 -g -driver-print-jobs %s 2>&1 | %FileCheck %s --check-prefix OSX // RUN: env SDKROOT=%S/../Inputs/clang-importer-sdk %swiftc_driver_plain -target x86_64-unknown-linux-gnu -g -driver-print-jobs %s 2>&1 | %FileCheck %s --check-prefix LINUX @@ -33,6 +34,15 @@ // FREEBSD: bin/{{.+}} {{.*}}swiftrt.o // FREEBSD: {{-syslibroot|--sysroot}} {{.*}}/Inputs/clang-importer-sdk +// WINDOWS-NOT: warning: no such SDK: +// WINDOWS: bin/swift +// WINDOWS: Driver/sdk.swift +// WINDOWS: -sdk {{.*}}/Inputs/clang-importer-sdk +// WINDOWS-NEXT: bin/swift +// WINDOWS: -sdk {{.*}}/Inputs/clang-importer-sdk +// WINDOWS: bin/{{.+}} {{.*}}swiftrt.o +// WINDOWS: {{-I}} {{.*}}/Inputs/clang-importer-sdk + // RUN: %swift_driver -driver-print-jobs -repl -sdk %S/Inputs/nonexistent-sdk 2>&1 | %FileCheck %s --check-prefix=SDKWARNING // RUN: %swift_driver -driver-print-jobs -sdk %S/Inputs/nonexistent-sdk 2>&1 | %FileCheck %s --check-prefix=SDKWARNING // RUN: env SDKROOT=%S/Inputs/nonexistent-sdk %swift_driver_plain -driver-print-jobs -repl 2>&1 | %FileCheck %s --check-prefix=SDKWARNING diff --git a/test/FixCode/fixits-switch-nonfrozen.swift b/test/FixCode/fixits-switch-nonfrozen.swift new file mode 100644 index 0000000000000..eb3c082c3594a --- /dev/null +++ b/test/FixCode/fixits-switch-nonfrozen.swift @@ -0,0 +1,293 @@ +// RUN: not %target-swift-frontend -emit-sil %s -emit-fixits-path %t.remap -enable-resilience -enable-nonfrozen-enum-exhaustivity-diagnostics -diagnostics-editor-mode +// RUN: c-arcmt-test %t.remap | arcmt-test -verify-transformed-files %s.result + +enum Runcible { + case spoon + case hat + case fork +} + +func checkDiagnosticMinimality(x: Runcible?) { + switch (x!, x!) { + case (.spoon, .spoon): + break + case (.spoon, .hat): + break + case (.hat, .spoon): + break + } + + switch (x!, x!) { + case (.spoon, .spoon): + break + case (.hat, .hat): + break + } +} + +enum LargeSpaceEnum { + case case0 + case case1 + case case2 + case case3 + case case4 + case case5 + case case6 + case case7 + case case8 + case case9 + case case10 +} + +func notQuiteBigEnough() -> Bool { + switch (LargeSpaceEnum.case1, LargeSpaceEnum.case2) { + case (.case0, .case0): return true + case (.case1, .case1): return true + case (.case2, .case2): return true + case (.case3, .case3): return true + case (.case4, .case4): return true + case (.case5, .case5): return true + case (.case6, .case6): return true + case (.case7, .case7): return true + case (.case8, .case8): return true + case (.case9, .case9): return true + case (.case10, .case10): return true + } +} + +enum OverlyLargeSpaceEnum { + case case0 + case case1 + case case2 + case case3 + case case4 + case case5 + case case6 + case case7 + case case8 + case case9 + case case10 + case case11 +} + +enum ContainsOverlyLargeEnum { + case one(OverlyLargeSpaceEnum) + case two(OverlyLargeSpaceEnum) + case three(OverlyLargeSpaceEnum, OverlyLargeSpaceEnum) +} + +func quiteBigEnough() -> Bool { + switch (OverlyLargeSpaceEnum.case1, OverlyLargeSpaceEnum.case2) { + case (.case0, .case0): return true + case (.case1, .case1): return true + case (.case2, .case2): return true + case (.case3, .case3): return true + case (.case4, .case4): return true + case (.case5, .case5): return true + case (.case6, .case6): return true + case (.case7, .case7): return true + case (.case8, .case8): return true + case (.case9, .case9): return true + case (.case10, .case10): return true + case (.case11, .case11): return true + } + + switch (OverlyLargeSpaceEnum.case1, OverlyLargeSpaceEnum.case2) { + case (.case0, _): return true + case (.case1, _): return true + case (.case2, _): return true + case (.case3, _): return true + case (.case4, _): return true + case (.case5, _): return true + case (.case6, _): return true + case (.case7, _): return true + case (.case8, _): return true + case (.case9, _): return true + case (.case10, _): return true + } +} + +indirect enum InfinitelySized { + case one + case two + case recur(InfinitelySized) + case mutualRecur(MutuallyRecursive, InfinitelySized) +} + +indirect enum MutuallyRecursive { + case one + case two + case recur(MutuallyRecursive) + case mutualRecur(InfinitelySized, MutuallyRecursive) +} + +func infinitelySized() -> Bool { + switch (InfinitelySized.one, InfinitelySized.one) { + case (.one, .one): return true + case (.two, .two): return true + } + + switch (MutuallyRecursive.one, MutuallyRecursive.one) { + case (.one, .one): return true + case (.two, .two): return true + } +} + +public enum NonExhaustive { + case a, b +} + +public enum NonExhaustivePayload { + case a(Int), b(Bool) +} + +@_frozen public enum TemporalProxy { + case seconds(Int) + case milliseconds(Int) + case microseconds(Int) + case nanoseconds(Int) + @_downgrade_exhaustivity_check + case never +} + +// Inlineable code is considered "outside" the module and must include a default +// case. +@inlinable +public func testNonExhaustive(_ value: NonExhaustive, _ payload: NonExhaustivePayload, for interval: TemporalProxy, flag: Bool) { + switch value { + case .a: break + } + + switch value { + case .a: break + case .b: break + } + + switch value { + case .a: break + case .b: break + default: break // no-warning + } + + switch value { + case .a: break + case .b: break + @unknown case _: break // no-warning + } + + switch value { + case .a: break + @unknown case _: break + } + + switch value { + @unknown case _: break + } + + // Test being part of other spaces. + switch value as Optional { + case .a?: break + case .b?: break + case nil: break + } + + switch value as Optional { + case .a?: break + case .b?: break + case nil: break + @unknown case _: break + } // no-warning + + switch value as Optional { + case _?: break + case nil: break + } // no-warning + + switch (value, flag) { + case (.a, _): break + case (.b, false): break + case (_, true): break + } + + switch (value, flag) { + case (.a, _): break + case (.b, false): break + case (_, true): break + @unknown case _: break + } // no-warning + + switch (flag, value) { + case (_, .a): break + case (false, .b): break + case (true, _): break + } + + switch (flag, value) { + case (_, .a): break + case (false, .b): break + case (true, _): break + @unknown case _: break + } // no-warning + + switch (value, value) { + case (.a, _), (_, .a): break + case (.b, _), (_, .b): break + } + + switch (value, value) { + case (.a, _), (_, .a): break + case (.b, _), (_, .b): break + @unknown case _: break + } // no-warning + + // Test interaction with @_downgrade_exhaustivity_check. + switch (value, interval) { + case (_, .seconds): break + case (.a, _): break + case (.b, _): break + } + + switch (value, interval) { + case (_, .never): break + case (.a, _): break + case (.b, _): break + } + + // Test payloaded enums. + switch payload { + case .a: break + } + + switch payload { + case .a: break + case .b: break + } + + switch payload { + case .a: break + case .b: break + default: break // no-warning + } + + switch payload { + case .a: break + case .b: break + @unknown case _: break // no-warning + } + + switch payload { + case .a: break + @unknown case _: break + } + + switch payload { + case .a: break + case .b(false): break + } + + switch payload { + case .a: break + case .b(false): break + @unknown case _: break + } +} diff --git a/test/FixCode/fixits-switch-nonfrozen.swift.result b/test/FixCode/fixits-switch-nonfrozen.swift.result new file mode 100644 index 0000000000000..07cee6e0cdfca --- /dev/null +++ b/test/FixCode/fixits-switch-nonfrozen.swift.result @@ -0,0 +1,613 @@ +// RUN: not %target-swift-frontend -emit-sil %s -emit-fixits-path %t.remap -enable-resilience -enable-nonfrozen-enum-exhaustivity-diagnostics -diagnostics-editor-mode +// RUN: c-arcmt-test %t.remap | arcmt-test -verify-transformed-files %s.result + +enum Runcible { + case spoon + case hat + case fork +} + +func checkDiagnosticMinimality(x: Runcible?) { + switch (x!, x!) { + case (.spoon, .spoon): + break + case (.spoon, .hat): + break + case (.hat, .spoon): + break + case (.fork, _): +<#code#> +case (.hat, .hat): +<#code#> +case (_, .fork): +<#code#> +} + + switch (x!, x!) { + case (.spoon, .spoon): + break + case (.hat, .hat): + break + case (.fork, _): +<#code#> +case (.hat, .spoon): +<#code#> +case (.spoon, .hat): +<#code#> +case (_, .fork): +<#code#> +} +} + +enum LargeSpaceEnum { + case case0 + case case1 + case case2 + case case3 + case case4 + case case5 + case case6 + case case7 + case case8 + case case9 + case case10 +} + +func notQuiteBigEnough() -> Bool { + switch (LargeSpaceEnum.case1, LargeSpaceEnum.case2) { + case (.case0, .case0): return true + case (.case1, .case1): return true + case (.case2, .case2): return true + case (.case3, .case3): return true + case (.case4, .case4): return true + case (.case5, .case5): return true + case (.case6, .case6): return true + case (.case7, .case7): return true + case (.case8, .case8): return true + case (.case9, .case9): return true + case (.case10, .case10): return true + case (.case10, .case0): +<#code#> +case (.case10, .case1): +<#code#> +case (.case10, .case2): +<#code#> +case (.case10, .case3): +<#code#> +case (.case10, .case4): +<#code#> +case (.case10, .case5): +<#code#> +case (.case10, .case6): +<#code#> +case (.case10, .case7): +<#code#> +case (.case10, .case8): +<#code#> +case (.case10, .case9): +<#code#> +case (.case9, .case0): +<#code#> +case (.case9, .case1): +<#code#> +case (.case9, .case2): +<#code#> +case (.case9, .case3): +<#code#> +case (.case9, .case4): +<#code#> +case (.case9, .case5): +<#code#> +case (.case9, .case6): +<#code#> +case (.case9, .case7): +<#code#> +case (.case9, .case8): +<#code#> +case (.case9, .case10): +<#code#> +case (.case8, .case0): +<#code#> +case (.case8, .case1): +<#code#> +case (.case8, .case2): +<#code#> +case (.case8, .case3): +<#code#> +case (.case8, .case4): +<#code#> +case (.case8, .case5): +<#code#> +case (.case8, .case6): +<#code#> +case (.case8, .case7): +<#code#> +case (.case8, .case9): +<#code#> +case (.case8, .case10): +<#code#> +case (.case7, .case0): +<#code#> +case (.case7, .case1): +<#code#> +case (.case7, .case2): +<#code#> +case (.case7, .case3): +<#code#> +case (.case7, .case4): +<#code#> +case (.case7, .case5): +<#code#> +case (.case7, .case6): +<#code#> +case (.case7, .case8): +<#code#> +case (.case7, .case9): +<#code#> +case (.case7, .case10): +<#code#> +case (.case6, .case0): +<#code#> +case (.case6, .case1): +<#code#> +case (.case6, .case2): +<#code#> +case (.case6, .case3): +<#code#> +case (.case6, .case4): +<#code#> +case (.case6, .case5): +<#code#> +case (.case6, .case7): +<#code#> +case (.case6, .case8): +<#code#> +case (.case6, .case9): +<#code#> +case (.case6, .case10): +<#code#> +case (.case5, .case0): +<#code#> +case (.case5, .case1): +<#code#> +case (.case5, .case2): +<#code#> +case (.case5, .case3): +<#code#> +case (.case5, .case4): +<#code#> +case (.case5, .case6): +<#code#> +case (.case5, .case7): +<#code#> +case (.case5, .case8): +<#code#> +case (.case5, .case9): +<#code#> +case (.case5, .case10): +<#code#> +case (.case4, .case0): +<#code#> +case (.case4, .case1): +<#code#> +case (.case4, .case2): +<#code#> +case (.case4, .case3): +<#code#> +case (.case4, .case5): +<#code#> +case (.case4, .case6): +<#code#> +case (.case4, .case7): +<#code#> +case (.case4, .case8): +<#code#> +case (.case4, .case9): +<#code#> +case (.case4, .case10): +<#code#> +case (.case3, .case0): +<#code#> +case (.case3, .case1): +<#code#> +case (.case3, .case2): +<#code#> +case (.case3, .case4): +<#code#> +case (.case3, .case5): +<#code#> +case (.case3, .case6): +<#code#> +case (.case3, .case7): +<#code#> +case (.case3, .case8): +<#code#> +case (.case3, .case9): +<#code#> +case (.case3, .case10): +<#code#> +case (.case2, .case0): +<#code#> +case (.case2, .case1): +<#code#> +case (.case2, .case3): +<#code#> +case (.case2, .case4): +<#code#> +case (.case2, .case5): +<#code#> +case (.case2, .case6): +<#code#> +case (.case2, .case7): +<#code#> +case (.case2, .case8): +<#code#> +case (.case2, .case9): +<#code#> +case (.case2, .case10): +<#code#> +case (.case1, .case0): +<#code#> +case (.case1, .case2): +<#code#> +case (.case1, .case3): +<#code#> +case (.case1, .case4): +<#code#> +case (.case1, .case5): +<#code#> +case (.case1, .case6): +<#code#> +case (.case1, .case7): +<#code#> +case (.case1, .case8): +<#code#> +case (.case1, .case9): +<#code#> +case (.case1, .case10): +<#code#> +case (.case0, .case1): +<#code#> +case (.case0, .case2): +<#code#> +case (.case0, .case3): +<#code#> +case (.case0, .case4): +<#code#> +case (.case0, .case5): +<#code#> +case (.case0, .case6): +<#code#> +case (.case0, .case7): +<#code#> +case (.case0, .case8): +<#code#> +case (.case0, .case9): +<#code#> +case (.case0, .case10): +<#code#> +} +} + +enum OverlyLargeSpaceEnum { + case case0 + case case1 + case case2 + case case3 + case case4 + case case5 + case case6 + case case7 + case case8 + case case9 + case case10 + case case11 +} + +enum ContainsOverlyLargeEnum { + case one(OverlyLargeSpaceEnum) + case two(OverlyLargeSpaceEnum) + case three(OverlyLargeSpaceEnum, OverlyLargeSpaceEnum) +} + +func quiteBigEnough() -> Bool { + switch (OverlyLargeSpaceEnum.case1, OverlyLargeSpaceEnum.case2) { + case (.case0, .case0): return true + case (.case1, .case1): return true + case (.case2, .case2): return true + case (.case3, .case3): return true + case (.case4, .case4): return true + case (.case5, .case5): return true + case (.case6, .case6): return true + case (.case7, .case7): return true + case (.case8, .case8): return true + case (.case9, .case9): return true + case (.case10, .case10): return true + case (.case11, .case11): return true + default: +<#fatalError()#> +} + + switch (OverlyLargeSpaceEnum.case1, OverlyLargeSpaceEnum.case2) { + case (.case0, _): return true + case (.case1, _): return true + case (.case2, _): return true + case (.case3, _): return true + case (.case4, _): return true + case (.case5, _): return true + case (.case6, _): return true + case (.case7, _): return true + case (.case8, _): return true + case (.case9, _): return true + case (.case10, _): return true + default: +<#fatalError()#> +} +} + +indirect enum InfinitelySized { + case one + case two + case recur(InfinitelySized) + case mutualRecur(MutuallyRecursive, InfinitelySized) +} + +indirect enum MutuallyRecursive { + case one + case two + case recur(MutuallyRecursive) + case mutualRecur(InfinitelySized, MutuallyRecursive) +} + +func infinitelySized() -> Bool { + switch (InfinitelySized.one, InfinitelySized.one) { + case (.one, .one): return true + case (.two, .two): return true + case (.recur(_), _): +<#code#> +case (.mutualRecur(_, _), _): +<#code#> +case (.two, .one): +<#code#> +case (.recur(_), .mutualRecur(_, _)): +<#code#> +case (.mutualRecur(_, _), .mutualRecur(_, _)): +<#code#> +case (.one, .two): +<#code#> +case (_, .recur(_)): +<#code#> +case (_, .mutualRecur(_, _)): +<#code#> +} + + switch (MutuallyRecursive.one, MutuallyRecursive.one) { + case (.one, .one): return true + case (.two, .two): return true + case (.recur(_), _): +<#code#> +case (.mutualRecur(_, _), _): +<#code#> +case (.two, .one): +<#code#> +case (.recur(_), .mutualRecur(_, _)): +<#code#> +case (.mutualRecur(_, _), .mutualRecur(_, _)): +<#code#> +case (.one, .two): +<#code#> +case (_, .recur(_)): +<#code#> +case (_, .mutualRecur(_, _)): +<#code#> +} +} + +public enum NonExhaustive { + case a, b +} + +public enum NonExhaustivePayload { + case a(Int), b(Bool) +} + +@_frozen public enum TemporalProxy { + case seconds(Int) + case milliseconds(Int) + case microseconds(Int) + case nanoseconds(Int) + @_downgrade_exhaustivity_check + case never +} + +// Inlineable code is considered "outside" the module and must include a default +// case. +@inlinable +public func testNonExhaustive(_ value: NonExhaustive, _ payload: NonExhaustivePayload, for interval: TemporalProxy, flag: Bool) { + switch value { + case .a: break + case .b: +<#code#> +@unknown default: +<#code#> +} + + switch value { + case .a: break + case .b: break + @unknown default: +<#fatalError()#> +} + + switch value { + case .a: break + case .b: break + default: break // no-warning + } + + switch value { + case .a: break + case .b: break + @unknown case _: break // no-warning + } + + switch value { + case .a: break + case .b: +<#code#> +@unknown case _: break + } + + switch value { + case .a: +<#code#> +case .b: +<#code#> +@unknown case _: break + } + + // Test being part of other spaces. + switch value as Optional { + case .a?: break + case .b?: break + case nil: break + case .some(_): +<#code#> +} + + switch value as Optional { + case .a?: break + case .b?: break + case nil: break + @unknown case _: break + } // no-warning + + switch value as Optional { + case _?: break + case nil: break + } // no-warning + + switch (value, flag) { + case (.a, _): break + case (.b, false): break + case (_, true): break + case (_, false): +<#code#> +} + + switch (value, flag) { + case (.a, _): break + case (.b, false): break + case (_, true): break + @unknown case _: break + } // no-warning + + switch (flag, value) { + case (_, .a): break + case (false, .b): break + case (true, _): break + case (false, _): +<#code#> +} + + switch (flag, value) { + case (_, .a): break + case (false, .b): break + case (true, _): break + @unknown case _: break + } // no-warning + + switch (value, value) { + case (.a, _), (_, .a): break + case (.b, _), (_, .b): break + case (_, _): +<#code#> +} + + switch (value, value) { + case (.a, _), (_, .a): break + case (.b, _), (_, .b): break + @unknown case _: break + } // no-warning + + // Test interaction with @_downgrade_exhaustivity_check. + switch (value, interval) { + case (_, .seconds): break + case (.a, _): break + case (.b, _): break + case (_, .milliseconds(_)): +<#code#> +case (_, .microseconds(_)): +<#code#> +case (_, .nanoseconds(_)): +<#code#> +case (_, .never): +<#code#> +} + + switch (value, interval) { + case (_, .never): break + case (.a, _): break + case (.b, _): break + case (_, .seconds(_)): +<#code#> +case (_, .milliseconds(_)): +<#code#> +case (_, .microseconds(_)): +<#code#> +case (_, .nanoseconds(_)): +<#code#> +} + + // Test payloaded enums. + switch payload { + case .a: break + case .b(_): +<#code#> +@unknown default: +<#code#> +} + + switch payload { + case .a: break + case .b: break + @unknown default: +<#fatalError()#> +} + + switch payload { + case .a: break + case .b: break + default: break // no-warning + } + + switch payload { + case .a: break + case .b: break + @unknown case _: break // no-warning + } + + switch payload { + case .a: break + case .b(_): +<#code#> +@unknown case _: break + } + + switch payload { + case .a: break + case .b(false): break + case .b(true): +<#code#> +@unknown default: +<#code#> +} + + switch payload { + case .a: break + case .b(false): break + case .b(true): +<#code#> +@unknown case _: break + } +} diff --git a/test/Generics/Inputs/conditional_conformances_objc.h b/test/Generics/Inputs/conditional_conformances_objc.h index a3d91a2cfbb6a..ac3fa57403248 100644 --- a/test/Generics/Inputs/conditional_conformances_objc.h +++ b/test/Generics/Inputs/conditional_conformances_objc.h @@ -5,3 +5,6 @@ @interface ObjC2: NSObject @end + +@protocol ObjCProtocol +@end diff --git a/test/Generics/associated_type_where_clause.swift b/test/Generics/associated_type_where_clause.swift index 04435c4c6c65e..daf580d446213 100644 --- a/test/Generics/associated_type_where_clause.swift +++ b/test/Generics/associated_type_where_clause.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift -typecheck %s -verify -swift-version 4 +// RUN: %target-typecheck-verify-swift -verify -swift-version 4 func needsSameType(_: T.Type, _: T.Type) {} diff --git a/test/Generics/conditional_conformances.swift b/test/Generics/conditional_conformances.swift index 0c3c2fef16095..786cc5cfe26c5 100644 --- a/test/Generics/conditional_conformances.swift +++ b/test/Generics/conditional_conformances.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift -typecheck %s -verify +// RUN: %target-typecheck-verify-swift -typecheck -verify // RUN: %target-typecheck-verify-swift -typecheck -debug-generic-signatures %s > %t.dump 2>&1 // RUN: %FileCheck %s < %t.dump @@ -7,10 +7,7 @@ protocol P2 {} protocol P3 {} protocol P4: P1 {} protocol P5: P2 {} -// expected-note@-1{{type 'InheritImplicitGood' does not conform to inherited protocol 'P2'}} -// expected-note@-2{{type 'InheritImplicitBad' does not conform to inherited protocol 'P2'}} protocol P6: P2 {} -// expected-note@-1{{type 'InheritImplicitBad' does not conform to inherited protocol 'P2'}} protocol Assoc { associatedtype AT } @@ -220,6 +217,8 @@ struct InheritEqual {} extension InheritEqual: P2 where T: P1 {} // CHECK-LABEL: ExtensionDecl line={{.*}} base=InheritEqual // CHECK-NEXT: (normal_conformance type=InheritEqual protocol=P5 +// CHECK-NEXT: (normal_conformance type=InheritEqual protocol=P2 +// CHECK-NEXT: conforms_to: T P1) // CHECK-NEXT: conforms_to: T P1) extension InheritEqual: P5 where T: P1 {} func inheritequal_good(_: U) { @@ -252,6 +251,8 @@ struct InheritMore {} extension InheritMore: P2 where T: P1 {} // CHECK-LABEL: ExtensionDecl line={{.*}} base=InheritMore // CHECK-NEXT: (normal_conformance type=InheritMore protocol=P5 +// CHECK-NEXT: (normal_conformance type=InheritMore protocol=P2 +// CHECK-NEXT: conforms_to: T P1) // CHECK-NEXT: conforms_to: T P4) extension InheritMore: P5 where T: P4 {} func inheritequal_good_good(_: U) { @@ -276,35 +277,62 @@ func inheritequal_bad_bad(_: U) { // expected-note@-3{{requirement from conditional conformance of 'InheritMore' to 'P5'}} } -struct InheritImplicitGood {} -// FIXME: per SE-0143, this should result in an implicit conformance -// InheritImplicitGood: P2. -extension InheritImplicitGood: P5 where T: P1 {} -// expected-error@-1{{type 'InheritImplicitGood' does not conform to protocol 'P2'}} +struct InheritImplicitOne {} +// This shouldn't give anything implicit since we disallow implication for +// conditional conformances (in many cases, the implied bounds are +// incorrect/insufficiently general). +extension InheritImplicitOne: P5 where T: P1 {} +// expected-error@-1{{conditional conformance of type 'InheritImplicitOne' to protocol 'P5' does not imply conformance to inherited protocol 'P2'}} +// expected-note@-2{{did you mean to explicitly state the conformance like 'extension InheritImplicitOne: P2 where ...'?}} + +struct InheritImplicitTwo {} +// Even if we relax the rule about implication, this double-up should still be +// an error, because either conformance could imply InheritImplicitTwo: P2. +extension InheritImplicitTwo: P5 where T: P1 {} +// expected-error@-1{{conditional conformance of type 'InheritImplicitTwo' to protocol 'P5' does not imply conformance to inherited protocol 'P2'}} +// expected-note@-2{{did you mean to explicitly state the conformance like 'extension InheritImplicitTwo: P2 where ...'?}} +extension InheritImplicitTwo: P6 where T: P1 {} + +// However, if there's a non-conditional conformance that implies something, we +// can imply from that one. +struct InheritImplicitGood1 {} +extension InheritImplicitGood1: P5 {} +extension InheritImplicitGood1: P6 where T: P1 {} + +func inheritimplicitgood1(_ : T) { + takes_P2(InheritImplicitGood1()) // unconstrained! + takes_P2(InheritImplicitGood1()) +} +struct InheritImplicitGood2 {} +extension InheritImplicitGood2: P6 where T: P1 {} +extension InheritImplicitGood2: P5 {} -struct InheritImplicitBad {} -// This shouldn't give anything implicit since either conformance could imply -// InheritImplicitBad: P2. -extension InheritImplicitBad: P5 where T: P1 {} -// expected-error@-1{{type 'InheritImplicitBad' does not conform to protocol 'P2'}} -extension InheritImplicitBad: P6 where T: P1 {} -// expected-error@-1{{type 'InheritImplicitBad' does not conform to protocol 'P2'}} +func inheritimplicitgood2(_: T) { + takes_P2(InheritImplicitGood2()) // unconstrained! + takes_P2(InheritImplicitGood2()) +} +struct InheritImplicitGood3: P5 {} +extension InheritImplicitGood3: P6 where T: P1 {} +func inheritimplicitgood3(_: T) { + takes_P2(InheritImplicitGood3()) // unconstrained! + takes_P2(InheritImplicitGood3()) +} // "Multiple conformances" from SE0143 struct TwoConformances {} extension TwoConformances: P2 where T: P1 {} -// expected-error@-1{{redundant conformance of 'TwoConformances' to protocol 'P2'}} -extension TwoConformances: P2 where T: P3 {} // expected-note@-1{{'TwoConformances' declares conformance to protocol 'P2' here}} +extension TwoConformances: P2 where T: P3 {} +// expected-error@-1{{conflicting conformance of 'TwoConformances' to protocol 'P2'; there cannot be more than one conformance, even with different conditional bounds}} struct TwoDisjointConformances {} extension TwoDisjointConformances: P2 where T == Int {} -// expected-error@-1{{redundant conformance of 'TwoDisjointConformances' to protocol 'P2'}} -extension TwoDisjointConformances: P2 where T == String {} // expected-note@-1{{'TwoDisjointConformances' declares conformance to protocol 'P2' here}} +extension TwoDisjointConformances: P2 where T == String {} +// expected-error@-1{{conflicting conformance of 'TwoDisjointConformances' to protocol 'P2'; there cannot be more than one conformance, even with different conditional bounds}} // FIXME: these cases should be equivalent (and both with the same output as the diff --git a/test/Generics/conditional_conformances_fixit.swift b/test/Generics/conditional_conformances_fixit.swift new file mode 100644 index 0000000000000..192655aaf1dfc --- /dev/null +++ b/test/Generics/conditional_conformances_fixit.swift @@ -0,0 +1,41 @@ +// RUN: %target-typecheck-verify-swift -emit-fixits-path %t.remap -fixit-all -diagnostics-editor-mode +// RUN: c-arcmt-test %t.remap | arcmt-test -verify-transformed-files %s.result + +protocol P1 {} +protocol P2: P1 {} + + +struct S1 {} + +extension S1: P2 where T: P1 {} +// expected-error@-1 {{conditional conformance of type 'S1' to protocol 'P2' does not imply conformance to inherited protocol 'P1'}} +// expected-note@-2 {{did you mean to explicitly state the conformance with the same bounds?}} +// expected-note@-3 {{did you mean to explicitly state the conformance with different bounds?}} + +protocol P3 { + associatedtype X +} +struct S2 {} + +extension S2: P2 where T: P2, U: P2, V.X: P2 {} +// expected-error@-1 {{conditional conformance of type 'S2' to protocol 'P2' does not imply conformance to inherited protocol 'P1'}} +// expected-note@-2 {{did you mean to explicitly state the conformance with relaxed bounds?}} +// expected-note@-3 {{did you mean to explicitly state the conformance with the same bounds?}} +// expected-note@-4 {{did you mean to explicitly state the conformance with different bounds?}} + + +struct S3 {} + +extension S3: P2 where T: P2, U: P2, V.X == Int {} +// expected-error@-1 {{conditional conformance of type 'S3' to protocol 'P2' does not imply conformance to inherited protocol 'P1'}} +// expected-note@-2 {{did you mean to explicitly state the conformance with the same bounds?}} +// expected-note@-3 {{did you mean to explicitly state the conformance with different bounds?}} + + +struct S4 {} + +extension S4: P2 where T: P2, U: P3, V.X: P2 {} +// expected-error@-1 {{conditional conformance of type 'S4' to protocol 'P2' does not imply conformance to inherited protocol 'P1'}} +// expected-note@-2 {{did you mean to explicitly state the conformance with the same bounds?}} +// expected-note@-3 {{did you mean to explicitly state the conformance with different bounds?}} + diff --git a/test/Generics/conditional_conformances_fixit.swift.result b/test/Generics/conditional_conformances_fixit.swift.result new file mode 100644 index 0000000000000..ed81ed912578b --- /dev/null +++ b/test/Generics/conditional_conformances_fixit.swift.result @@ -0,0 +1,77 @@ +// RUN: %target-typecheck-verify-swift -emit-fixits-path %t.remap -fixit-all -diagnostics-editor-mode +// RUN: c-arcmt-test %t.remap | arcmt-test -verify-transformed-files %s.result + +protocol P1 {} +protocol P2: P1 {} + + +struct S1 {} + +extension S1: P1 where T: P1 { + <#witnesses#> +} + +extension S1: P1 where <#requirements#> { + <#witnesses#> +} + +extension S1: P2 where T: P1 {} +// expected-error@-1 {{conditional conformance of type 'S1' to protocol 'P2' does not imply conformance to inherited protocol 'P1'}} +// expected-note@-2 {{did you mean to explicitly state the conformance with the same bounds?}} +// expected-note@-3 {{did you mean to explicitly state the conformance with different bounds?}} + +protocol P3 { + associatedtype X +} +struct S2 {} + +extension S2: P1 where T: P1, U: P1, V.X: P1 { + <#witnesses#> +} + +extension S2: P1 where T: P2, U: P2, V.X: P2 { + <#witnesses#> +} + +extension S2: P1 where <#requirements#> { + <#witnesses#> +} + +extension S2: P2 where T: P2, U: P2, V.X: P2 {} +// expected-error@-1 {{conditional conformance of type 'S2' to protocol 'P2' does not imply conformance to inherited protocol 'P1'}} +// expected-note@-2 {{did you mean to explicitly state the conformance with relaxed bounds?}} +// expected-note@-3 {{did you mean to explicitly state the conformance with the same bounds?}} +// expected-note@-4 {{did you mean to explicitly state the conformance with different bounds?}} + + +struct S3 {} + +extension S3: P1 where T: P2, U: P2, V.X == Int { + <#witnesses#> +} + +extension S3: P1 where <#requirements#> { + <#witnesses#> +} + +extension S3: P2 where T: P2, U: P2, V.X == Int {} +// expected-error@-1 {{conditional conformance of type 'S3' to protocol 'P2' does not imply conformance to inherited protocol 'P1'}} +// expected-note@-2 {{did you mean to explicitly state the conformance with the same bounds?}} +// expected-note@-3 {{did you mean to explicitly state the conformance with different bounds?}} + + +struct S4 {} + +extension S4: P1 where T: P2, U: P3, V.X: P2 { + <#witnesses#> +} + +extension S4: P1 where <#requirements#> { + <#witnesses#> +} + +extension S4: P2 where T: P2, U: P3, V.X: P2 {} +// expected-error@-1 {{conditional conformance of type 'S4' to protocol 'P2' does not imply conformance to inherited protocol 'P1'}} +// expected-note@-2 {{did you mean to explicitly state the conformance with the same bounds?}} +// expected-note@-3 {{did you mean to explicitly state the conformance with different bounds?}} + diff --git a/test/Generics/conditional_conformances_objc.swift b/test/Generics/conditional_conformances_objc.swift index 7f5874c1c64be..c74915af8729c 100644 --- a/test/Generics/conditional_conformances_objc.swift +++ b/test/Generics/conditional_conformances_objc.swift @@ -32,3 +32,11 @@ extension ObjC.Class: Foo where T == ObjC2 {} extension ObjC.Class: Bar where T: Bar {} // expected-error@-1{{type 'ObjC.Class' cannot conditionally conform to protocol 'Bar' because the type uses the Objective-C generics model}} +@objc protocol AtObjCProtocol {} + +class X {} + +extension X: ObjCProtocol where T: Foo {} +// expected-error@-1{{type 'X' cannot conditionally conform to @objc protocol 'ObjCProtocol' because Objective-C does not support conditional conformances}} +extension X: AtObjCProtocol where T: Foo {} +// expected-error@-1{{type 'X' cannot conditionally conform to @objc protocol 'AtObjCProtocol' because Objective-C does not support conditional conformances}} diff --git a/test/Generics/existential_restrictions.swift b/test/Generics/existential_restrictions.swift index b13ca76763d4c..72af6d66147b2 100644 --- a/test/Generics/existential_restrictions.swift +++ b/test/Generics/existential_restrictions.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift +// RUN: %target-typecheck-verify-swift -enable-objc-interop protocol P { } @objc protocol OP { } diff --git a/test/Generics/same_type_constraints.swift b/test/Generics/same_type_constraints.swift index 9dca6b04ce954..2d8b5cdb4cf01 100644 --- a/test/Generics/same_type_constraints.swift +++ b/test/Generics/same_type_constraints.swift @@ -378,3 +378,19 @@ typealias NotAnInt = Double extension X11 where NotAnInt == Int { } // expected-warning@-1{{neither type in same-type constraint ('NotAnInt' (aka 'Double') or 'Int') refers to a generic parameter or associated type}} // expected-error@-2{{generic signature requires types 'NotAnInt' (aka 'Double') and 'Int' to be the same}} + + +struct X12 { } + +protocol P12 { + associatedtype A + associatedtype B +} + +func testP12a(_: T) where T.A == X12, T.A == X12, T.B == Int { } +// expected-warning@-1{{redundant same-type constraint 'T.B' == 'Int'}} +// expected-note@-2{{same-type constraint 'T.B' == 'Int' written here}} + +func testP12b(_: T) where T.B == Int, T.A == X12, X12 == T.A { } +// expected-warning@-1{{redundant same-type constraint 'T.A' == 'X12'}} +// expected-note@-2{{same-type constraint 'T.A' == 'X12' written here}} diff --git a/test/Generics/sr7275.swift b/test/Generics/sr7275.swift new file mode 100644 index 0000000000000..05e164c425650 --- /dev/null +++ b/test/Generics/sr7275.swift @@ -0,0 +1,14 @@ +// RUN: %target-typecheck-verify-swift -swift-version 3 +// RUN: %target-typecheck-verify-swift -swift-version 4 + +extension Sequence where Element: AnyObject { + public func f1(to object: AnyObject) -> Bool { + return contains { $0 === object } + } +} + +extension Sequence where Iterator.Element: AnyObject { + public func f2(to object: AnyObject) -> Bool { + return contains { $0 === object } + } +} diff --git a/test/IDE/Inputs/custom-modules/ImportAsMember.apinotes b/test/IDE/Inputs/custom-modules/ImportAsMember.apinotes index e6f05c5bc0a83..50c29ffb7a416 100644 --- a/test/IDE/Inputs/custom-modules/ImportAsMember.apinotes +++ b/test/IDE/Inputs/custom-modules/ImportAsMember.apinotes @@ -12,6 +12,8 @@ Functions: Typedefs: - Name: IAMStruct1APINoteType SwiftName: Struct1.NewApiNoteType +- Name: IncompleteImportTargetName + SwiftName: IncompleteImportTarget.Name SwiftVersions: - Version: 3 Globals: @@ -27,3 +29,5 @@ SwiftVersions: Typedefs: - Name: IAMStruct1APINoteType SwiftName: Struct1.OldApiNoteType + - Name: IncompleteImportTargetName + SwiftWrapper: none diff --git a/test/IDE/Inputs/custom-modules/ImportAsMemberClass.h b/test/IDE/Inputs/custom-modules/ImportAsMemberClass.h index cc23e81bfb1f5..d4aeb7b679ef1 100644 --- a/test/IDE/Inputs/custom-modules/ImportAsMemberClass.h +++ b/test/IDE/Inputs/custom-modules/ImportAsMemberClass.h @@ -44,6 +44,11 @@ __attribute__((swift_name("Panda"))) @interface PKPanda : NSObject @end +typedef NSString *IncompleteImportTargetName __attribute__((swift_wrapper(struct))); + +@interface IncompleteImportTarget : NSObject +@end + #pragma clang assume_nonnull end #endif diff --git a/test/IDE/comment_to_xml.swift b/test/IDE/comment_to_xml.swift index 25504cc25f189..352b7b5d65e78 100644 --- a/test/IDE/comment_to_xml.swift +++ b/test/IDE/comment_to_xml.swift @@ -1,7 +1,7 @@ //===--- Check that we convert comments to XML correctly. -// RUN: %target-swift-frontend -typecheck -verify -disable-objc-attr-requires-foundation-module %S/../Inputs/comment_to_something_conversion.swift -// RUN: %target-swift-ide-test -module-name comment_to_xml -print-comments -source-filename %S/../Inputs/comment_to_something_conversion.swift -comments-xml-schema %S/../../bindings/xml/comment-xml-schema.rng > %t.txt +// RUN: %target-swift-frontend -typecheck -verify -disable-objc-attr-requires-foundation-module -enable-objc-interop %S/../Inputs/comment_to_something_conversion.swift +// RUN: %target-swift-ide-test -enable-objc-interop -module-name comment_to_xml -print-comments -source-filename %S/../Inputs/comment_to_something_conversion.swift -comments-xml-schema %S/../../bindings/xml/comment-xml-schema.rng > %t.txt // RUN: %FileCheck %S/../Inputs/comment_to_something_conversion.swift < %t.txt // RUN: %FileCheck %s -check-prefix=WRONG < %t.txt diff --git a/test/IDE/complete_after_self.swift b/test/IDE/complete_after_self.swift index fe4afbe89cf07..b5a37447c6063 100644 --- a/test/IDE/complete_after_self.swift +++ b/test/IDE/complete_after_self.swift @@ -166,7 +166,7 @@ class ThisDerived1 : ThisBase1 { init() { self#^CONSTRUCTOR_SELF_NO_DOT_1^# -// CONSTRUCTOR_SELF_NO_DOT_1: Begin completions, 23 items +// CONSTRUCTOR_SELF_NO_DOT_1: Begin completions, 24 items // CONSTRUCTOR_SELF_NO_DOT_1-DAG: Decl[Constructor]/CurrNominal: .init()[#ThisDerived1#]; // CONSTRUCTOR_SELF_NO_DOT_1-DAG: Decl[Constructor]/CurrNominal: .init({#a: Int#})[#ThisDerived1#]; // CONSTRUCTOR_SELF_NO_DOT_1: End completions @@ -177,9 +177,10 @@ class ThisDerived1 : ThisBase1 { init(a : Int) { self.#^CONSTRUCTOR_SELF_DOT_1^# -// CONSTRUCTOR_SELF_DOT_1: Begin completions, 18 items +// CONSTRUCTOR_SELF_DOT_1: Begin completions, 19 items // CONSTRUCTOR_SELF_DOT_1-DAG: Decl[Constructor]/CurrNominal: init()[#ThisDerived1#]; // CONSTRUCTOR_SELF_DOT_1-DAG: Decl[Constructor]/CurrNominal: init({#a: Int#})[#ThisDerived1#]; + // CONSTRUCTOR_SELF_DOT_1: End completions let d: ThisDerived1 d.#^CONSTRUCTOR_NONSELF_DOT_1^# @@ -187,23 +188,23 @@ class ThisDerived1 : ThisBase1 { deinit { self#^DESTRUCTOR_SELF_NO_DOT_1^# -// DESTRUCTOR_SELF_NO_DOT_1: Begin completions, 20 items +// DESTRUCTOR_SELF_NO_DOT_1: Begin completions, 21 items // DESTRUCTOR_SELF_NO_DOT_1: End completions self.#^DESTRUCTOR_SELF_DOT_1^# -// DESTRUCTOR_SELF_DOT_1: Begin completions, 15 items +// DESTRUCTOR_SELF_DOT_1: Begin completions, 16 items // DESTRUCTOR_SELF_DOT_1: End completions } func test1() { self#^FUNC_SELF_NO_DOT_1^# -// FUNC_SELF_NO_DOT_1: Begin completions, 20 items +// FUNC_SELF_NO_DOT_1: Begin completions, 21 items // FUNC_SELF_NO_DOT_1: End completions } func test2() { self.#^FUNC_SELF_DOT_1^# -// FUNC_SELF_DOT_1: Begin completions, 15 items +// FUNC_SELF_DOT_1: Begin completions, 16 items // FUNC_SELF_DOT_1: End completions } @@ -236,12 +237,14 @@ class ThisDerived1 : ThisBase1 { // FUNC_STATIC_SELF_NO_DOT_1-NEXT: Decl[Class]/Super: .BaseExtNestedClass[#ThisBase1.BaseExtNestedClass#] // FUNC_STATIC_SELF_NO_DOT_1-NEXT: Decl[Enum]/Super: .BaseExtNestedEnum[#ThisBase1.BaseExtNestedEnum#] // FUNC_STATIC_SELF_NO_DOT_1-NEXT: Decl[TypeAlias]/Super: .BaseExtNestedTypealias[#Int#] +// FUNC_STATIC_SELF_NO_DOT_1-NEXT: Keyword[self]/CurrNominal: .self[#ThisDerived1.Type#]; name=self // FUNC_STATIC_SELF_NO_DOT_1-NEXT: End completions } class func staticTest2() { self.#^FUNC_STATIC_SELF_DOT_1^# // FUNC_STATIC_SELF_DOT_1: Begin completions +// FUNC_STATIC_SELF_DOT_1-NEXT: Keyword[self]/CurrNominal: self[#ThisDerived1.Type#]; name=self // FUNC_STATIC_SELF_DOT_1-NEXT: Decl[InstanceMethod]/CurrNominal: derivedFunc0({#self: ThisDerived1#})[#() -> Void#] // FUNC_STATIC_SELF_DOT_1-NEXT: Decl[StaticVar]/CurrNominal: derivedStaticVar[#Int#] // FUNC_STATIC_SELF_DOT_1-NEXT: Decl[StaticMethod]/CurrNominal: derivedStaticFunc0()[#Void#] @@ -307,7 +310,8 @@ struct S1 { init() {} init(x: Int) { self.#^STRUCT_CONSTRUCTOR_SELF_DOT_1^# -// STRUCT_CONSTRUCTOR_SELF_DOT_1: Begin completions, 3 items +// STRUCT_CONSTRUCTOR_SELF_DOT_1: Begin completions, 4 items +// STRUCT_CONSTRUCTOR_SELF_DOT_1-DAG: Keyword[self]/CurrNominal: self[#S1#]; name=self // STRUCT_CONSTRUCTOR_SELF_DOT_1-DAG: Decl[Constructor]/CurrNominal: init()[#S1#]; // STRUCT_CONSTRUCTOR_SELF_DOT_1-DAG: Decl[Constructor]/CurrNominal: init({#x: Int#})[#S1#]; // STRUCT_CONSTRUCTOR_SELF_DOT_1-DAG: Decl[InstanceMethod]/CurrNominal: f()[#Void#]; diff --git a/test/IDE/complete_associated_types.swift b/test/IDE/complete_associated_types.swift index fad21e7cd5b35..dfa0eee5db46b 100644 --- a/test/IDE/complete_associated_types.swift +++ b/test/IDE/complete_associated_types.swift @@ -179,7 +179,7 @@ func testStruct3(a: StructWithAssociatedTypes) { } // STRUCT_TYPE_COUNT: Begin completions, 26 items -// STRUCT_INSTANCE: Begin completions, 14 items +// STRUCT_INSTANCE: Begin completions, 15 items // STRUCT_INSTANCE-DAG: Decl[InstanceMethod]/CurrNominal: deduceCommonA()[#Int#] // STRUCT_INSTANCE-DAG: Decl[InstanceMethod]/CurrNominal: deduceCommonB()[#Int#] // STRUCT_INSTANCE-DAG: Decl[InstanceMethod]/CurrNominal: deduceCommonC()[#Int#] @@ -194,6 +194,7 @@ func testStruct3(a: StructWithAssociatedTypes) { // STRUCT_INSTANCE-DAG: Decl[InstanceMethod]/CurrNominal: deduceBarBaseC()[#Int#] // STRUCT_INSTANCE-DAG: Decl[InstanceMethod]/CurrNominal: deduceBarBaseD()[#Int#] // STRUCT_INSTANCE-DAG: Decl[InstanceMethod]/CurrNominal: deduceBarD()[#Int#] +// STRUCT_INSTANCE-DAG: Keyword[self]/CurrNominal: self[#StructWithAssociatedTypes#]; name=self // STRUCT_INSTANCE: End completions // STRUCT_TYPES: Begin completions @@ -266,7 +267,7 @@ struct StructWithBrokenConformance : FooProtocolWithAssociatedTypes { func testBrokenConformances1() { StructWithBrokenConformance.#^BROKEN_CONFORMANCE_1^# } -// BROKEN_CONFORMANCE_1: Begin completions, 34 items +// BROKEN_CONFORMANCE_1: Begin completions, 35 items // BROKEN_CONFORMANCE_1-DAG: Decl[TypeAlias]/CurrNominal: DefaultedTypeCommonA[#StructWithBrokenConformance.DefaultedTypeCommonA#]; name=DefaultedTypeCommonA // BROKEN_CONFORMANCE_1-DAG: Decl[TypeAlias]/CurrNominal: DefaultedTypeCommonB[#StructWithBrokenConformance.DefaultedTypeCommonB#]; name=DefaultedTypeCommonB // BROKEN_CONFORMANCE_1-DAG: Decl[TypeAlias]/CurrNominal: FooBaseDefaultedTypeB[#StructWithBrokenConformance.FooBaseDefaultedTypeB#]; name=FooBaseDefaultedTypeB diff --git a/test/IDE/complete_at_top_level.swift b/test/IDE/complete_at_top_level.swift index 0ec1446cb4a6b..6879375c0b81f 100644 --- a/test/IDE/complete_at_top_level.swift +++ b/test/IDE/complete_at_top_level.swift @@ -178,7 +178,8 @@ fooObject#^TYPE_CHECKED_EXPR_1^# // TYPE_CHECKED_EXPR_1: Begin completions // TYPE_CHECKED_EXPR_1-NEXT: Decl[InstanceVar]/CurrNominal: .instanceVar[#Int#]{{; name=.+$}} // TYPE_CHECKED_EXPR_1-NEXT: Decl[InstanceMethod]/CurrNominal: .instanceFunc({#(a): Int#})[#Void#]{{; name=.+$}} -// TYPE_CHECKED_EXPR_1-NEXT:BuiltinOperator/None: = {#FooStruct#}[#Void#]; name== FooStruct +// TYPE_CHECKED_EXPR_1-NEXT: BuiltinOperator/None: = {#FooStruct#}[#Void#]; name== FooStruct +// TYPE_CHECKED_EXPR_1-NEXT: Keyword[self]/CurrNominal: .self[#FooStruct#]; name=self // TYPE_CHECKED_EXPR_1-NEXT: End completions func resyncParser2() {} @@ -191,6 +192,7 @@ fooObject#^TYPE_CHECKED_EXPR_2^# // TYPE_CHECKED_EXPR_2-NEXT: Decl[InstanceVar]/CurrNominal: .instanceVar[#Int#]{{; name=.+$}} // TYPE_CHECKED_EXPR_2-NEXT: Decl[InstanceMethod]/CurrNominal: .instanceFunc({#(a): Int#})[#Void#]{{; name=.+$}} // TYPE_CHECKED_EXPR_2-NEXT: BuiltinOperator/None: = {#FooStruct#}[#Void#]; name== FooStruct +// TYPE_CHECKED_EXPR_2-NEXT: Keyword[self]/CurrNominal: .self[#FooStruct#]; name=self // TYPE_CHECKED_EXPR_2-NEXT: End completions func resyncParser3() {} @@ -200,12 +202,14 @@ fooObject#^TYPE_CHECKED_EXPR_3^#.bar // TYPE_CHECKED_EXPR_3-NEXT: Decl[InstanceVar]/CurrNominal: .instanceVar[#Int#]{{; name=.+$}} // TYPE_CHECKED_EXPR_3-NEXT: Decl[InstanceMethod]/CurrNominal: .instanceFunc({#(a): Int#})[#Void#]{{; name=.+$}} // TYPE_CHECKED_EXPR_3-NEXT: BuiltinOperator/None: = {#FooStruct#}[#Void#]; name== FooStruct +// TYPE_CHECKED_EXPR_3-NEXT: Keyword[self]/CurrNominal: .self[#FooStruct#]; name=self // TYPE_CHECKED_EXPR_3-NEXT: End completions func resyncParser4() {} fooObject.#^TYPE_CHECKED_EXPR_4^# // TYPE_CHECKED_EXPR_4: Begin completions +// TYPE_CHECKED_EXPR_4-NEXT: Keyword[self]/CurrNominal: self[#FooStruct#]; name=self // TYPE_CHECKED_EXPR_4-NEXT: Decl[InstanceVar]/CurrNominal: instanceVar[#Int#]{{; name=.+$}} // TYPE_CHECKED_EXPR_4-NEXT: Decl[InstanceMethod]/CurrNominal: instanceFunc({#(a): Int#})[#Void#]{{; name=.+$}} // TYPE_CHECKED_EXPR_4-NEXT: End completions @@ -214,6 +218,7 @@ func resyncParser5() {} fooObject.#^TYPE_CHECKED_EXPR_5^#.bar // TYPE_CHECKED_EXPR_5: Begin completions +// TYPE_CHECKED_EXPR_5-NEXT: Keyword[self]/CurrNominal: self[#FooStruct#]; name=self // TYPE_CHECKED_EXPR_5-NEXT: Decl[InstanceVar]/CurrNominal: instanceVar[#Int#]{{; name=.+$}} // TYPE_CHECKED_EXPR_5-NEXT: Decl[InstanceMethod]/CurrNominal: instanceFunc({#(a): Int#})[#Void#]{{; name=.+$}} // TYPE_CHECKED_EXPR_5-NEXT: End completions @@ -236,6 +241,7 @@ var fooObjectWithErrorInInit : FooStruct = unknown_var fooObjectWithErrorInInit.#^TYPE_CHECKED_EXPR_WITH_ERROR_IN_INIT_1^# // TYPE_CHECKED_EXPR_WITH_ERROR_IN_INIT_1: Begin completions +// TYPE_CHECKED_EXPR_WITH_ERROR_IN_INIT_1-NEXT: Keyword[self]/CurrNominal: self[#FooStruct#]; name=self // TYPE_CHECKED_EXPR_WITH_ERROR_IN_INIT_1-NEXT: Decl[InstanceVar]/CurrNominal: instanceVar[#Int#]{{; name=.+$}} // TYPE_CHECKED_EXPR_WITH_ERROR_IN_INIT_1-NEXT: Decl[InstanceMethod]/CurrNominal: instanceFunc({#(a): Int#})[#Void#]{{; name=.+$}} // TYPE_CHECKED_EXPR_WITH_ERROR_IN_INIT_1-NEXT: End completions @@ -259,6 +265,7 @@ var topLevelVar2 = FooStruct#^TOP_LEVEL_VAR_INIT_2^# // TOP_LEVEL_VAR_INIT_2-NEXT: Decl[InstanceMethod]/CurrNominal: .instanceFunc({#self: FooStruct#})[#(Int) -> Void#]{{; name=.+$}} // TOP_LEVEL_VAR_INIT_2-NEXT: Decl[Constructor]/CurrNominal: ({#instanceVar: Int#})[#FooStruct#]{{; name=.+$}} // TOP_LEVEL_VAR_INIT_2-NEXT: Decl[Constructor]/CurrNominal: ()[#FooStruct#]{{; name=.+$}} +// TOP_LEVEL_VAR_INIT_2-NEXT: Keyword[self]/CurrNominal: .self[#FooStruct.Type#]; name=self // TOP_LEVEL_VAR_INIT_2-NEXT: End completions func resyncParser8() {} diff --git a/test/IDE/complete_call_arg.swift b/test/IDE/complete_call_arg.swift index 126efdab0d5a4..2bfe9ff9897a1 100644 --- a/test/IDE/complete_call_arg.swift +++ b/test/IDE/complete_call_arg.swift @@ -45,6 +45,8 @@ // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=BOUND_IUO | %FileCheck %s -check-prefix=MEMBEROF_IUO // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=FORCED_IUO | %FileCheck %s -check-prefix=MEMBEROF_IUO +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GENERIC_TO_GENERIC | %FileCheck %s -check-prefix=GENERIC_TO_GENERIC + var i1 = 1 var i2 = 2 var oi1 : Int? @@ -373,6 +375,14 @@ struct TestBoundGeneric1 { // BOUND_GENERIC_1: Decl[InstanceVar]/CurrNominal: y[#[Int]#]; } +func whereConvertible(lhs: T, rhs: T) where T: Collection { + _ = zip(lhs, #^GENERIC_TO_GENERIC^#) +} +// GENERIC_TO_GENERIC: Begin completions +// GENERIC_TO_GENERIC: Decl[LocalVar]/Local: lhs[#Collection#]; name=lhs +// GENERIC_TO_GENERIC: Decl[LocalVar]/Local: rhs[#Collection#]; name=rhs +// GENERIC_TO_GENERIC: End completions + func emptyOverload() {} func emptyOverload(foo foo: Int) {} emptyOverload(foo: #^EMPTY_OVERLOAD_1^#) @@ -403,7 +413,8 @@ class Bar { self.collectionView? .#^BOUND_IUO^#x self.collectionView! .#^FORCED_IUO^#x } - // MEMBEROF_IUO: Begin completions, 1 items + // MEMBEROF_IUO: Begin completions, 2 items + // MEMBEROF_IUO: Keyword[self]/CurrNominal: self[#Foo#]; name=self // MEMBEROF_IUO: Decl[InstanceVar]/CurrNominal: x[#Int#]; name=x // MEMBEROF_IUO: End completions } diff --git a/test/IDE/complete_constructor.swift b/test/IDE/complete_constructor.swift index b84176e5ca329..f0de365de742d 100644 --- a/test/IDE/complete_constructor.swift +++ b/test/IDE/complete_constructor.swift @@ -43,8 +43,9 @@ struct ImplicitConstructors1 { func testImplicitConstructors1() { ImplicitConstructors1#^IMPLICIT_CONSTRUCTORS_1^# -// IMPLICIT_CONSTRUCTORS_1: Begin completions, 1 items +// IMPLICIT_CONSTRUCTORS_1: Begin completions, 2 items // IMPLICIT_CONSTRUCTORS_1-DAG: Decl[Constructor]/CurrNominal: ()[#ImplicitConstructors1#]{{; name=.+$}} +// IMPLICIT_CONSTRUCTORS_1-DAG: Keyword[self]/CurrNominal: .self[#ImplicitConstructors1.Type#]; name=self // IMPLICIT_CONSTRUCTORS_1: End completions } func testImplicitConstructors1P() { @@ -58,9 +59,10 @@ struct ImplicitConstructors2 { func testImplicitConstructors2() { ImplicitConstructors2#^IMPLICIT_CONSTRUCTORS_2^# -// IMPLICIT_CONSTRUCTORS_2: Begin completions, 2 items +// IMPLICIT_CONSTRUCTORS_2: Begin completions, 3 items // IMPLICIT_CONSTRUCTORS_2-DAG: Decl[Constructor]/CurrNominal: ({#instanceVar: Int#})[#ImplicitConstructors2#]{{; name=.+$}} // IMPLICIT_CONSTRUCTORS_2-DAG: Decl[Constructor]/CurrNominal: ()[#ImplicitConstructors2#]{{; name=.+$}} +// IMPLICIT_CONSTRUCTORS_2-DAG: Keyword[self]/CurrNominal: .self[#ImplicitConstructors2.Type#]; name=self // IMPLICIT_CONSTRUCTORS_2: End completions } func testImplicitConstructors2P() { @@ -78,10 +80,11 @@ struct ExplicitConstructors1 { func testExplicitConstructors1() { ExplicitConstructors1#^EXPLICIT_CONSTRUCTORS_1^# -// EXPLICIT_CONSTRUCTORS_1: Begin completions, 3 items +// EXPLICIT_CONSTRUCTORS_1: Begin completions, 4 items // EXPLICIT_CONSTRUCTORS_1-DAG: Decl[Constructor]/CurrNominal: ()[#ExplicitConstructors1#]{{; name=.+$}} // EXPLICIT_CONSTRUCTORS_1-DAG: Decl[Constructor]/CurrNominal: ({#a: Int#})[#ExplicitConstructors1#]{{; name=.+$}} // EXPLICIT_CONSTRUCTORS_1-DAG: Decl[Constructor]/CurrNominal: ({#a: Int#}, {#b: Float#})[#ExplicitConstructors1#]{{; name=.+$}} +// EXPLICIT_CONSTRUCTORS_1-DAG: Keyword[self]/CurrNominal: .self[#ExplicitConstructors1.Type#]; name=self // EXPLICIT_CONSTRUCTORS_1: End completions } func testExplicitConstructors1P() { @@ -94,10 +97,11 @@ func testExplicitConstructors1P() { ExplicitConstructors1#^EXPLICIT_CONSTRUCTORS_2^# -// EXPLICIT_CONSTRUCTORS_2: Begin completions, 3 items +// EXPLICIT_CONSTRUCTORS_2: Begin completions, 4 items // EXPLICIT_CONSTRUCTORS_2-DAG: Decl[Constructor]/CurrNominal: ()[#ExplicitConstructors1#] // EXPLICIT_CONSTRUCTORS_2-DAG: Decl[Constructor]/CurrNominal: ({#a: Int#})[#ExplicitConstructors1#] // EXPLICIT_CONSTRUCTORS_2-DAG: Decl[Constructor]/CurrNominal: ({#a: Int#}, {#b: Float#})[#ExplicitConstructors1#] +// EXPLICIT_CONSTRUCTORS_2-DAG: Keyword[self]/CurrNominal: .self[#ExplicitConstructors1.Type#]; name=self // EXPLICIT_CONSTRUCTORS_2: End completions ExplicitConstructors1(#^EXPLICIT_CONSTRUCTORS_2P^# @@ -130,9 +134,10 @@ struct ExplicitConstructorsSelector1 { func testExplicitConstructorsSelector1() { ExplicitConstructorsSelector1#^EXPLICIT_CONSTRUCTORS_SELECTOR_1^# -// EXPLICIT_CONSTRUCTORS_SELECTOR_1: Begin completions, 2 items +// EXPLICIT_CONSTRUCTORS_SELECTOR_1: Begin completions, 3 items // EXPLICIT_CONSTRUCTORS_SELECTOR_1-DAG: Decl[Constructor]/CurrNominal: ({#int: Int#})[#ExplicitConstructorsSelector1#]{{; name=.+$}} // EXPLICIT_CONSTRUCTORS_SELECTOR_1-DAG: Decl[Constructor]/CurrNominal: ({#int: Int#}, {#andFloat: Float#})[#ExplicitConstructorsSelector1#]{{; name=.+$}} +// EXPLICIT_CONSTRUCTORS_SELECTOR_1-DAG: Keyword[self]/CurrNominal: .self[#ExplicitConstructorsSelector1.Type#]; name=self // EXPLICIT_CONSTRUCTORS_SELECTOR_1: End completions } @@ -145,11 +150,12 @@ struct ExplicitConstructorsSelector2 { func testExplicitConstructorsSelector2() { ExplicitConstructorsSelector2#^EXPLICIT_CONSTRUCTORS_SELECTOR_2^# -// EXPLICIT_CONSTRUCTORS_SELECTOR_2: Begin completions, 4 items +// EXPLICIT_CONSTRUCTORS_SELECTOR_2: Begin completions, 5 items // EXPLICIT_CONSTRUCTORS_SELECTOR_2-DAG: Decl[Constructor]/CurrNominal: ({#noArgs: ()#})[#ExplicitConstructorsSelector2#]{{; name=.+$}} // EXPLICIT_CONSTRUCTORS_SELECTOR_2-DAG: Decl[Constructor]/CurrNominal: ({#Int#})[#ExplicitConstructorsSelector2#]{{; name=.+$}} // EXPLICIT_CONSTRUCTORS_SELECTOR_2-DAG: Decl[Constructor]/CurrNominal: ({#Int#}, {#withFloat: Float#})[#ExplicitConstructorsSelector2#]{{; name=.+$}} // EXPLICIT_CONSTRUCTORS_SELECTOR_2-DAG: Decl[Constructor]/CurrNominal: ({#int: Int#}, {#Float#})[#ExplicitConstructorsSelector2#]{{; name=.+$}} +// EXPLICIT_CONSTRUCTORS_SELECTOR_2-DAG: Keyword[self]/CurrNominal: .self[#ExplicitConstructorsSelector2.Type#]; name=self // EXPLICIT_CONSTRUCTORS_SELECTOR_2: End completions } @@ -174,9 +180,10 @@ class ExplicitConstructorsDerived2 : ExplicitConstructorsBase1 { func testExplicitConstructorsBaseDerived1() { ExplicitConstructorsDerived1#^EXPLICIT_CONSTRUCTORS_BASE_DERIVED_1^# } -// EXPLICIT_CONSTRUCTORS_BASE_DERIVED_1: Begin completions, 2 items +// EXPLICIT_CONSTRUCTORS_BASE_DERIVED_1: Begin completions, 3 items // EXPLICIT_CONSTRUCTORS_BASE_DERIVED_1-DAG: Decl[Constructor]/CurrNominal: ()[#ExplicitConstructorsDerived1#]{{; name=.+$}} // EXPLICIT_CONSTRUCTORS_BASE_DERIVED_1-DAG: Decl[Constructor]/CurrNominal: ({#a: Int#})[#ExplicitConstructorsDerived1#]{{; name=.+$}} +// EXPLICIT_CONSTRUCTORS_BASE_DERIVED_1-DAG: Keyword[self]/CurrNominal: .self[#ExplicitConstructorsDerived1.Type#]; name=self // EXPLICIT_CONSTRUCTORS_BASE_DERIVED_1: End completions func testGetInitFromMetatype1() { @@ -184,6 +191,7 @@ func testGetInitFromMetatype1() { } // INIT_FROM_METATYPE1: Begin completions +// INIT_FROM_METATYPE1-NEXT: Keyword[self]/CurrNominal: self[#ExplicitConstructorsBase1.Type#]; name=self // INIT_FROM_METATYPE1-NEXT: Decl[Constructor]/CurrNominal: init()[#ExplicitConstructorsBase1#]{{; name=.+$}} // INIT_FROM_METATYPE1-NEXT: Decl[Constructor]/CurrNominal: init({#a: Int#})[#ExplicitConstructorsBase1#]{{; name=.+$}} // INIT_FROM_METATYPE1-NEXT: End completions diff --git a/test/IDE/complete_crashes.swift b/test/IDE/complete_crashes.swift index 2acd4a517362a..4cfcfa959a161 100644 --- a/test/IDE/complete_crashes.swift +++ b/test/IDE/complete_crashes.swift @@ -30,9 +30,10 @@ protocol BadMembers2 { func badMembers2(_ a: BadMembers2) { a#^BAD_MEMBERS_2^# } -// BAD_MEMBERS_2: Begin completions, 2 items +// BAD_MEMBERS_2: Begin completions, 3 items // BAD_MEMBERS_2-NEXT: Decl[InstanceVar]/CurrNominal: .prop[#Int#]{{; name=.+$}} // BAD_MEMBERS_2-NEXT: Decl[Subscript]/CurrNominal: [{#Int#}][#Double#]{{; name=.+$}} +// BAD_MEMBERS_2-NEXT: Keyword[self]/CurrNominal: .self[#BadMembers2#]; name=self // BAD_MEMBERS_2-NEXT: End completions func globalFunc() {} @@ -208,3 +209,42 @@ S_RDAR_28991372(x: #^RDAR_28991372^#, y: <#T##Int#>) protocol P where #^RDAR_31981486^# // RDAR_31981486: Begin completions + +// rdar://problem/38149042 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=RDAR_38149042 | %FileCheck %s -check-prefix=RDAR_38149042 +class Baz_38149042 { + let x: Int = 0 +} +protocol Bar_38149042 { + var foo: Baz_38149042? {get} +} +func foo_38149042(bar: Bar_38149042) { + _ = bar.foo? #^RDAR_38149042^# .x +} +// RDAR_38149042: Begin completions, 4 items +// RDAR_38149042-DAG: Decl[InstanceVar]/CurrNominal: .x[#Int#]; name=x +// RDAR_38149042-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: [' ']=== {#AnyObject?#}[#Bool#]; name==== AnyObject? +// RDAR_38149042-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: [' ']!== {#AnyObject?#}[#Bool#]; name=!== AnyObject? +// RDAR_38149042-DAG: Keyword[self]/CurrNominal: .self[#Baz_38149042#]; name=self +// RDAR_38149042: End completions + +// rdar://problem/38272904 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=RDAR_38272904 | %FileCheck %s -check-prefix=RDAR_38272904 +public protocol P_38272904 { + associatedtype A = Error + associatedtype B +} + +public func ?? (lhs: T, rhs: @autoclosure() throws -> T.B) rethrows -> T.B { + fatalError() +} + +class A_38272904 { + open class func foo() -> A_38272904 { fatalError() } +} + +func bar_38272904(a: A_38272904) {} +func foo_38272904(a: A_38272904) { + bar_38272904(a: .foo() #^RDAR_38272904^#) +} +// RDAR_38272904: Begin completions diff --git a/test/IDE/complete_decl_attribute.swift b/test/IDE/complete_decl_attribute.swift index abd1bb10d7c69..eb0e55b8a5422 100644 --- a/test/IDE/complete_decl_attribute.swift +++ b/test/IDE/complete_decl_attribute.swift @@ -33,7 +33,7 @@ @#^KEYWORD2^# func method(){} -// KEYWORD2: Begin completions, 9 items +// KEYWORD2: Begin completions, 11 items // KEYWORD2-NEXT: Keyword/None: available[#Func Attribute#]; name=available{{$}} // KEYWORD2-NEXT: Keyword/None: objc[#Func Attribute#]; name=objc{{$}} // KEYWORD2-NEXT: Keyword/None: noreturn[#Func Attribute#]; name=noreturn{{$}} @@ -41,14 +41,16 @@ func method(){} // KEYWORD2-NEXT: Keyword/None: NSManaged[#Func Attribute#]; name=NSManaged{{$}} // KEYWORD2-NEXT: Keyword/None: inline[#Func Attribute#]; name=inline{{$}} // KEYWORD2-NEXT: Keyword/None: nonobjc[#Func Attribute#]; name=nonobjc{{$}} +// KEYWORD2-NEXT: Keyword/None: inlinable[#Func Attribute#]; name=inlinable{{$}} // KEYWORD2-NEXT: Keyword/None: warn_unqualified_access[#Func Attribute#]; name=warn_unqualified_access{{$}} +// KEYWORD2-NEXT: Keyword/None: usableFromInline[#Func Attribute#]; name=usableFromInline // KEYWORD2-NEXT: Keyword/None: discardableResult[#Func Attribute#]; name=discardableResult // KEYWORD2-NEXT: End completions @#^KEYWORD3^# class C {} -// KEYWORD3: Begin completions, 8 items +// KEYWORD3: Begin completions, 9 items // KEYWORD3-NEXT: Keyword/None: available[#Class Attribute#]; name=available{{$}} // KEYWORD3-NEXT: Keyword/None: objc[#Class Attribute#]; name=objc{{$}} // KEYWORD3-NEXT: Keyword/None: dynamicMemberLookup[#Class Attribute#]; name=dynamicMemberLookup{{$}} @@ -57,28 +59,31 @@ class C {} // KEYWORD3-NEXT: Keyword/None: requires_stored_property_inits[#Class Attribute#]; name=requires_stored_property_inits{{$}} // KEYWORD3-NEXT: Keyword/None: objcMembers[#Class Attribute#]; name=objcMembers{{$}} // KEYWORD3-NEXT: Keyword/None: NSApplicationMain[#Class Attribute#]; name=NSApplicationMain{{$}} +// KEYWORD3-NEXT: Keyword/None: usableFromInline[#Class Attribute#]; name=usableFromInline // KEYWORD3-NEXT: End completions @#^KEYWORD4^# enum E {} -// KEYWORD4: Begin completions, 3 items +// KEYWORD4: Begin completions, 4 items // KEYWORD4-NEXT: Keyword/None: available[#Enum Attribute#]; name=available{{$}} // KEYWORD4-NEXT: Keyword/None: objc[#Enum Attribute#]; name=objc{{$}} // KEYWORD4-NEXT: Keyword/None: dynamicMemberLookup[#Enum Attribute#]; name=dynamicMemberLookup +// KEYWORD4-NEXT: Keyword/None: usableFromInline[#Enum Attribute#]; name=usableFromInline // KEYWORD4-NEXT: End completions @#^KEYWORD5^# struct S{} -// KEYWORD5: Begin completions, 2 item +// KEYWORD5: Begin completions, 3 items // KEYWORD5-NEXT: Keyword/None: available[#Struct Attribute#]; name=available{{$}} // KEYWORD5-NEXT: Keyword/None: dynamicMemberLookup[#Struct Attribute#]; name=dynamicMemberLookup +// KEYWORD5-NEXT: Keyword/None: usableFromInline[#Struct Attribute#]; name=usableFromInline // KEYWORD5-NEXT: End completions @#^KEYWORD_LAST^# -// KEYWORD_LAST: Begin completions, 19 items +// KEYWORD_LAST: Begin completions, 21 items // KEYWORD_LAST-NEXT: Keyword/None: available[#Declaration Attribute#]; name=available{{$}} // KEYWORD_LAST-NEXT: Keyword/None: objc[#Declaration Attribute#]; name=objc{{$}} // KEYWORD_LAST-NEXT: Keyword/None: noreturn[#Declaration Attribute#]; name=noreturn{{$}} @@ -93,9 +98,11 @@ struct S{} // KEYWORD_LAST-NEXT: Keyword/None: inline[#Declaration Attribute#]; name=inline{{$}} // KEYWORD_LAST-NEXT: Keyword/None: requires_stored_property_inits[#Declaration Attribute#]; name=requires_stored_property_inits{{$}} // KEYWORD_LAST-NEXT: Keyword/None: nonobjc[#Declaration Attribute#]; name=nonobjc{{$}} +// KEYWORD_LAST-NEXT: Keyword/None: inlinable[#Declaration Attribute#]; name=inlinable{{$}} // KEYWORD_LAST-NEXT: Keyword/None: objcMembers[#Declaration Attribute#]; name=objcMembers{{$}} // KEYWORD_LAST-NEXT: Keyword/None: NSApplicationMain[#Declaration Attribute#]; name=NSApplicationMain{{$}} // KEYWORD_LAST-NEXT: Keyword/None: warn_unqualified_access[#Declaration Attribute#]; name=warn_unqualified_access +// KEYWORD_LAST-NEXT: Keyword/None: usableFromInline[#Declaration Attribute#]; name=usableFromInline{{$}} // KEYWORD_LAST-NEXT: Keyword/None: discardableResult[#Declaration Attribute#]; name=discardableResult // KEYWORD_LAST-NEXT: Keyword/None: GKInspectable[#Declaration Attribute#]; name=GKInspectable{{$}} // KEYWORD_LAST-NEXT: End completions diff --git a/test/IDE/complete_doc_keyword.swift b/test/IDE/complete_doc_keyword.swift index 7ed141cabba56..318a461632752 100644 --- a/test/IDE/complete_doc_keyword.swift +++ b/test/IDE/complete_doc_keyword.swift @@ -93,6 +93,7 @@ func foo2() { let c = C1() c.#^MEMBER1^# // MEMBER1: Begin completions +// MEMBER1-NEXT: Keyword[self]/CurrNominal: self[#C1#]; name=self // MEMBER1-NEXT: Decl[InstanceVar]/CurrNominal/keyword[v1, Int]/recommended[v2]: v1[#Int#] // MEMBER1-NEXT: Decl[InstanceVar]/CurrNominal/keyword[v2, Int]/recommendedover[v1]: v2[#Int#] // MEMBER1-NEXT: Decl[InstanceMethod]/CurrNominal/keyword[f1, func]/recommended[f2]: f1()[#Void#] @@ -103,6 +104,7 @@ func foo3() { let s = S3() s.#^MEMBER2^# // MEMBER2: Begin completions +// MEMBER2-NEXT: Keyword[self]/CurrNominal: self[#S3#]; name=self // MEMBER2-NEXT: Decl[InstanceMethod]/CurrNominal/nonmutatingvariant[fooing]: foo()[#Void#] // MEMBER2-NEXT: Decl[InstanceMethod]/CurrNominal/mutatingvariant[foo]: fooing()[#S3#] } diff --git a/test/IDE/complete_dynamic_lookup.swift b/test/IDE/complete_dynamic_lookup.swift index 9d6a0608a46ec..d0419febfa0d3 100644 --- a/test/IDE/complete_dynamic_lookup.swift +++ b/test/IDE/complete_dynamic_lookup.swift @@ -234,9 +234,11 @@ protocol Bar { func bar() } // TLOC_MEMBERS_NO_DOT-NEXT: Decl[InstanceVar]/CurrNominal: .topLevelObjcClass_Property1[#Int#]{{; name=.+$}} // TLOC_MEMBERS_NO_DOT-NEXT: Decl[InfixOperatorFunction]/OtherModule[Swift]: === {#AnyObject?#}[#Bool#]; // TLOC_MEMBERS_NO_DOT-NEXT: Decl[InfixOperatorFunction]/OtherModule[Swift]: !== {#AnyObject?#}[#Bool#]; +// TLOC_MEMBERS_NO_DOT-NEXT: Keyword[self]/CurrNominal: .self[#TopLevelObjcClass#]; name=self // TLOC_MEMBERS_NO_DOT-NEXT: End completions // TLOC_MEMBERS_DOT: Begin completions +// TLOC_MEMBERS_DOT-NEXT: Keyword[self]/CurrNominal: self[#TopLevelObjcClass#]; name=self // TLOC_MEMBERS_DOT-NEXT: Decl[InstanceMethod]/CurrNominal: returnsObjcClass({#(i): Int#})[#TopLevelObjcClass#]{{; name=.+$}} // TLOC_MEMBERS_DOT-NEXT: Decl[InstanceMethod]/CurrNominal: topLevelObjcClass_InstanceFunc1()[#Void#]{{; name=.+$}} // TLOC_MEMBERS_DOT-NEXT: Decl[InstanceVar]/CurrNominal: topLevelObjcClass_Property1[#Int#]{{; name=.+$}} @@ -474,6 +476,7 @@ func testAnyObject13(_ dl: AnyObject) { } // DL_FUNC_NAME_BANG_1: Begin completions // DL_FUNC_NAME_BANG_1-NEXT: Pattern/CurrModule: ({#Int#})[#TopLevelObjcClass#] +// DL_FUNC_NAME_BANG_1-NEXT: Keyword[self]/CurrNominal: .self[#(Int) -> TopLevelObjcClass#]; name=self // DL_FUNC_NAME_BANG_1-NEXT: End completions func testAnyObject14() { diff --git a/test/IDE/complete_enum_elements.swift b/test/IDE/complete_enum_elements.swift index 0b58d44e4f354..b8e015221494c 100644 --- a/test/IDE/complete_enum_elements.swift +++ b/test/IDE/complete_enum_elements.swift @@ -50,7 +50,7 @@ // RUN: %FileCheck %s -check-prefix=QUX_ENUM_NO_DOT < %t.enum.txt // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=ENUM_QUAL_DOT_1 > %t.enum.txt -// RUN: %FileCheck %s -check-prefix=FOO_ENUM_DOT < %t.enum.txt +// RUN: %FileCheck %s -check-prefix=FOO_ENUM_DOT_INVALID < %t.enum.txt // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=ENUM_QUAL_DOT_2 > %t.enum.txt // RUN: %FileCheck %s -check-prefix=BAR_ENUM_DOT < %t.enum.txt @@ -88,17 +88,30 @@ enum FooEnum: CaseIterable { // FOO_ENUM_NO_DOT: Begin completions // FOO_ENUM_NO_DOT-NEXT: Decl[EnumElement]/CurrNominal: .Foo1[#FooEnum#]{{; name=.+$}} // FOO_ENUM_NO_DOT-NEXT: Decl[EnumElement]/CurrNominal: .Foo2[#FooEnum#]{{; name=.+$}} +// FOO_ENUM_NO_DOT-NEXT: Decl[InstanceMethod]/CurrNominal: .hash({#self: FooEnum#})[#(into: inout Hasher) -> Void#]; name=hash(FooEnum) // FOO_ENUM_NO_DOT-NEXT: Decl[TypeAlias]/CurrNominal: .AllCases[#[FooEnum]#]{{; name=.+$}} // FOO_ENUM_NO_DOT-NEXT: Decl[StaticVar]/CurrNominal: .allCases[#[FooEnum]#]{{; name=.+$}} +// FOO_ENUM_NO_DOT-NEXT: Keyword[self]/CurrNominal: .self[#FooEnum.Type#]; name=self // FOO_ENUM_NO_DOT-NEXT: End completions // FOO_ENUM_DOT: Begin completions +// FOO_ENUM_DOT-NEXT: Keyword[self]/CurrNominal: self[#FooEnum.Type#]; name=self // FOO_ENUM_DOT-NEXT: Decl[EnumElement]/CurrNominal: Foo1[#FooEnum#]{{; name=.+$}} // FOO_ENUM_DOT-NEXT: Decl[EnumElement]/CurrNominal: Foo2[#FooEnum#]{{; name=.+$}} +// FOO_ENUM_DOT-NEXT: Decl[InstanceMethod]/CurrNominal: hash({#self: FooEnum#})[#(into: inout Hasher) -> Void#]; name=hash(FooEnum) // FOO_ENUM_DOT-NEXT: Decl[TypeAlias]/CurrNominal: AllCases[#[FooEnum]#]{{; name=.+$}} // FOO_ENUM_DOT-NEXT: Decl[StaticVar]/CurrNominal: allCases[#[FooEnum]#]{{; name=.+$}} // FOO_ENUM_DOT-NEXT: End completions +// FOO_ENUM_DOT_INVALID: Begin completions +// FOO_ENUM_DOT_INVALID-NEXT: Keyword[self]/CurrNominal: self[#FooEnum.Type#]; name=self +// FOO_ENUM_DOT_INVALID-NEXT: Decl[EnumElement]/CurrNominal: Foo1[#FooEnum#]{{; name=.+$}} +// FOO_ENUM_DOT_INVALID-NEXT: Decl[EnumElement]/CurrNominal: Foo2[#FooEnum#]{{; name=.+$}} +// FOO_ENUM_DOT_INVALID-NEXT: Decl[InstanceMethod]/CurrNominal/NotRecommended/TypeRelation[Invalid]: hash({#self: FooEnum#})[#(into: inout Hasher) -> Void#]; name=hash(FooEnum) +// FOO_ENUM_DOT_INVALID-NEXT: Decl[TypeAlias]/CurrNominal: AllCases[#[FooEnum]#]{{; name=.+$}} +// FOO_ENUM_DOT_INVALID-NEXT: Decl[StaticVar]/CurrNominal: allCases[#[FooEnum]#]{{; name=.+$}} +// FOO_ENUM_DOT_INVALID-NEXT: End completions + // FOO_ENUM_DOT_ELEMENTS: Begin completions, 2 items // FOO_ENUM_DOT_ELEMENTS-NEXT: Decl[EnumElement]/ExprSpecific: Foo1[#FooEnum#]{{; name=.+$}} // FOO_ENUM_DOT_ELEMENTS-NEXT: Decl[EnumElement]/ExprSpecific: Foo2[#FooEnum#]{{; name=.+$}} @@ -155,9 +168,11 @@ enum BarEnum { // BAR_ENUM_NO_DOT-NEXT: Decl[InstanceMethod]/CurrNominal: .barInstanceFunc({#self: &BarEnum#})[#() -> Void#]{{; name=.+$}} // BAR_ENUM_NO_DOT-NEXT: Decl[StaticVar]/CurrNominal: .staticVar[#Int#]{{; name=.+$}} // BAR_ENUM_NO_DOT-NEXT: Decl[StaticMethod]/CurrNominal: .barStaticFunc()[#Void#]{{; name=.+$}} +// BAR_ENUM_NO_DOT-NEXT: Keyword[self]/CurrNominal: .self[#BarEnum.Type#]; name=self // BAR_ENUM_NO_DOT-NEXT: End completions // BAR_ENUM_DOT: Begin completions +// BAR_ENUM_DOT-NEXT: Keyword[self]/CurrNominal: self[#BarEnum.Type#]; name=self // BAR_ENUM_DOT-NEXT: Decl[EnumElement]/CurrNominal: Bar1[#BarEnum#]{{; name=.+$}} // BAR_ENUM_DOT-NEXT: Decl[EnumElement]/CurrNominal: Bar2()[#() -> BarEnum#]{{; name=.+$}} // BAR_ENUM_DOT-NEXT: Decl[EnumElement]/CurrNominal: Bar3({#Int#})[#(Int) -> BarEnum#]{{; name=.+$}} @@ -191,13 +206,14 @@ enum BazEnum { // BAZ_ENUM_TYPE_CONTEXT-DAG: Decl[EnumElement]/ExprSpecific: .Baz2({#T#})[#(T) -> BazEnum#]{{; name=.+$}} // BAZ_ENUM_TYPE_CONTEXT: End completions -// BAZ_INT_ENUM_NO_DOT: Begin completions, 6 items +// BAZ_INT_ENUM_NO_DOT: Begin completions, 7 items // BAZ_INT_ENUM_NO_DOT-NEXT: Decl[EnumElement]/CurrNominal: .Baz1[#BazEnum#]{{; name=.+$}} // BAZ_INT_ENUM_NO_DOT-NEXT: Decl[EnumElement]/CurrNominal: .Baz2({#T#})[#(T) -> BazEnum#]{{; name=.+$}} // BAZ_INT_ENUM_NO_DOT-NEXT: Decl[InstanceMethod]/CurrNominal: .bazInstanceFunc({#self: &BazEnum#})[#() -> Void#]{{; name=.+$}} // BAZ_INT_ENUM_NO_DOT-NEXT: Decl[StaticVar]/CurrNominal: .staticVar[#Int#]{{; name=.+$}} // BAZ_INT_ENUM_NO_DOT-NEXT: Decl[StaticVar]/CurrNominal: .staticVarT[#Int#]{{; name=.+$}} // BAZ_INT_ENUM_NO_DOT-NEXT: Decl[StaticMethod]/CurrNominal: .bazStaticFunc()[#Void#]{{; name=.+$}} +// BAZ_INT_ENUM_NO_DOT-NEXT: Keyword[self]/CurrNominal: .self[#BazEnum.Type#]; name=self // BAZ_INT_ENUM_NO_DOT-NEXT: End completions // BAZ_T_ENUM_NO_DOT: Begin completions @@ -207,9 +223,11 @@ enum BazEnum { // BAZ_T_ENUM_NO_DOT-NEXT: Decl[StaticVar]/CurrNominal: .staticVar[#Int#]{{; name=.+$}} // BAZ_T_ENUM_NO_DOT-NEXT: Decl[StaticVar]/CurrNominal: .staticVarT[#_#]{{; name=.+$}} // BAZ_T_ENUM_NO_DOT-NEXT: Decl[StaticMethod]/CurrNominal: .bazStaticFunc()[#Void#]{{; name=.+$}} +// BAZ_T_ENUM_NO_DOT-NEXT: Keyword[self]/CurrNominal: .self[#BazEnum<_>.Type#]; name=self // BAZ_T_ENUM_NO_DOT-NEXT: End completions -// BAZ_INT_ENUM_DOT: Begin completions, 6 items +// BAZ_INT_ENUM_DOT: Begin completions, 7 items +// BAZ_INT_ENUM_DOT-NEXT: Keyword[self]/CurrNominal: self[#BazEnum.Type#]; name=self // BAZ_INT_ENUM_DOT-NEXT: Decl[EnumElement]/CurrNominal: Baz1[#BazEnum#]{{; name=.+$}} // BAZ_INT_ENUM_DOT-NEXT: Decl[EnumElement]/CurrNominal: Baz2({#T#})[#(T) -> BazEnum#]{{; name=.+$}} // BAZ_INT_ENUM_DOT-NEXT: Decl[InstanceMethod]/CurrNominal/NotRecommended/TypeRelation[Invalid]: bazInstanceFunc({#self: &BazEnum#})[#() -> Void#]{{; name=.+$}} @@ -218,7 +236,8 @@ enum BazEnum { // BAZ_INT_ENUM_DOT-NEXT: Decl[StaticMethod]/CurrNominal/NotRecommended/TypeRelation[Invalid]: bazStaticFunc()[#Void#]{{; name=.+$}} // BAZ_INT_ENUM_DOT-NEXT: End completions -// BAZ_T_ENUM_DOT: Begin completions, 6 items +// BAZ_T_ENUM_DOT: Begin completions, 7 items +// BAZ_T_ENUM_DOT-NEXT: Keyword[self]/CurrNominal: self[#BazEnum<_>.Type#]; name=self // BAZ_T_ENUM_DOT-NEXT: Decl[EnumElement]/CurrNominal: Baz1[#BazEnum#]{{; name=.+$}} // BAZ_T_ENUM_DOT-NEXT: Decl[EnumElement]/CurrNominal: Baz2({#T#})[#(T) -> BazEnum#]{{; name=.+$}} // BAZ_T_ENUM_DOT-NEXT: Decl[InstanceMethod]/CurrNominal: bazInstanceFunc({#self: &BazEnum<_>#})[#() -> Void#]{{; name=.+$}} @@ -237,17 +256,21 @@ enum QuxEnum : Int { // QUX_ENUM_TYPE_CONTEXT-DAG: Decl[EnumElement]/ExprSpecific: .Qux2[#QuxEnum#]{{; name=.+$}} // QUX_ENUM_TYPE_CONTEXT: End completions -// QUX_ENUM_NO_DOT: Begin completions, 4 items +// QUX_ENUM_NO_DOT: Begin completions, 6 items // QUX_ENUM_NO_DOT-NEXT: Decl[EnumElement]/CurrNominal: .Qux1[#QuxEnum#]{{; name=.+$}} // QUX_ENUM_NO_DOT-NEXT: Decl[EnumElement]/CurrNominal: .Qux2[#QuxEnum#]{{; name=.+$}} // QUX_ENUM_NO_DOT-NEXT: Decl[TypeAlias]/CurrNominal: .RawValue[#Int#]{{; name=.+$}} +// QUX_ENUM_NO_DOT-NEXT: Decl[InstanceMethod]/CurrNominal: .hash({#self: QuxEnum#})[#(into: inout Hasher) -> Void#]; name=hash(QuxEnum) // QUX_ENUM_NO_DOT-NEXT: Decl[Constructor]/CurrNominal: ({#rawValue: Int#})[#QuxEnum?#]{{; name=.+$}} +// QUX_ENUM_NO_DOT-NEXT: Keyword[self]/CurrNominal: .self[#QuxEnum.Type#]; name=self // QUX_ENUM_NO_DOT-NEXT: End completions -// QUX_ENUM_DOT: Begin completions, 4 items +// QUX_ENUM_DOT: Begin completions, 6 items +// QUX_ENUM_DOT-NEXT: Keyword[self]/CurrNominal: self[#QuxEnum.Type#]; name=self // QUX_ENUM_DOT-NEXT: Decl[EnumElement]/CurrNominal: Qux1[#QuxEnum#]{{; name=.+$}} // QUX_ENUM_DOT-NEXT: Decl[EnumElement]/CurrNominal: Qux2[#QuxEnum#]{{; name=.+$}} // QUX_ENUM_DOT-NEXT: Decl[TypeAlias]/CurrNominal: RawValue[#Int#]{{; name=.+$}} +// QUX_ENUM_DOT-NEXT: Decl[InstanceMethod]/CurrNominal/NotRecommended/TypeRelation[Invalid]: hash({#self: QuxEnum#})[#(into: inout Hasher) -> Void#]; name=hash(QuxEnum) // QUX_ENUM_DOT-NEXT: Decl[Constructor]/CurrNominal: init({#rawValue: Int#})[#QuxEnum?#]{{; name=.+$}} // QUX_ENUM_DOT-NEXT: End completions diff --git a/test/IDE/complete_exception.swift b/test/IDE/complete_exception.swift index f810cf1e22905..e3aadfbc257c8 100644 --- a/test/IDE/complete_exception.swift +++ b/test/IDE/complete_exception.swift @@ -196,8 +196,9 @@ func test012() { } catch { error.#^INSIDE_CATCH_ERR_DOT1^# } -// ERROR_DOT-NOT: Begin completions } +// ERROR_DOT: Begin completions +// ERROR_DOT: Keyword[self]/CurrNominal: self[#Error#]; name=self func test013() { do { } catch let e { diff --git a/test/IDE/complete_expr_postfix_begin.swift b/test/IDE/complete_expr_postfix_begin.swift index d05ac2e0297fc..90a000d885681 100644 --- a/test/IDE/complete_expr_postfix_begin.swift +++ b/test/IDE/complete_expr_postfix_begin.swift @@ -123,8 +123,7 @@ typealias FooTypealias = Int // COMMON-DAG: Keyword[#column]/None: #column[#Int#]{{; name=.+$}} // COMMON: End completions -// NO_SELF-NOT: Self -// NO_SELF-NOT: self +// NO_SELF-NOT: {{[[:<:]][Ss]elf[[:>:]]}} //===--- Test that we can code complete at the beginning of expr-postfix. diff --git a/test/IDE/complete_expr_tuple.swift b/test/IDE/complete_expr_tuple.swift index b2bfb9f7b9fc5..c5d712d649836 100644 --- a/test/IDE/complete_expr_tuple.swift +++ b/test/IDE/complete_expr_tuple.swift @@ -27,7 +27,7 @@ func testTupleNoDot1() { var t = (1, 2.0) t#^TUPLE_NO_DOT_1^# } -// TUPLE_NO_DOT_1: Begin completions, 9 items +// TUPLE_NO_DOT_1: Begin completions, 10 items // TUPLE_NO_DOT_1-DAG: Pattern/CurrNominal: .0[#Int#]{{; name=.+$}} // TUPLE_NO_DOT_1-DAG: Pattern/CurrNominal: .1[#Double#]{{; name=.+$}} // TUPLE_NO_DOT_1-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: == {#(Int, Double)#}[#Bool#]{{; name=.+$}} @@ -37,13 +37,14 @@ func testTupleNoDot1() { // TUPLE_NO_DOT_1-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: != {#(Int, Double)#}[#Bool#]{{; name=.+$}} // TUPLE_NO_DOT_1-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: > {#(Int, Double)#}[#Bool#]{{; name=.+$}} // TUPLE_NO_DOT_1-DAG: BuiltinOperator/None: = {#(Int, Double)#}[#Void#]{{; name=.+$}} +// TUPLE_NO_DOT_1-NEXT: Keyword[self]/CurrNominal: .self[#(Int, Double)#]; name=self // TUPLE_NO_DOT_1-NEXT: End completions func testTupleNoDot2() { var t = (foo: 1, bar: 2.0) t#^TUPLE_NO_DOT_2^# } -// TUPLE_NO_DOT_2: Begin completions, 9 items +// TUPLE_NO_DOT_2: Begin completions, 10 items // TUPLE_NO_DOT_2-DAG: Pattern/CurrNominal: .foo[#Int#]{{; name=.+$}} // TUPLE_NO_DOT_2-DAG: Pattern/CurrNominal: .bar[#Double#]{{; name=.+$}} // TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: == {#(Int, Double)#}[#Bool#]{{; name=.+$}} @@ -53,13 +54,14 @@ func testTupleNoDot2() { // TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: != {#(Int, Double)#}[#Bool#]{{; name=.+$}} // TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: > {#(Int, Double)#}[#Bool#]{{; name=.+$}} // TUPLE_NO_DOT_2-DAG: BuiltinOperator/None: = {#(foo: Int, bar: Double)#}[#Void#]{{; name=.+$}} +// TUPLE_NO_DOT_2-DAG: Keyword[self]/CurrNominal: .self[#(foo: Int, bar: Double)#]; name=self // TUPLE_NO_DOT_2-NEXT: End completions func testTupleNoDot3() { var t = (foo: 1, 2.0) t#^TUPLE_NO_DOT_3^# } -// TUPLE_NO_DOT_3: Begin completions, 9 items +// TUPLE_NO_DOT_3: Begin completions, 10 items // TUPLE_NO_DOT_3-DAG: Pattern/CurrNominal: .foo[#Int#]{{; name=.+$}} // TUPLE_NO_DOT_3-DAG: Pattern/CurrNominal: .1[#Double#]{{; name=.+$}} // TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: == {#(Int, Double)#}[#Bool#]{{; name=.+$}} @@ -69,13 +71,15 @@ func testTupleNoDot3() { // TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: != {#(Int, Double)#}[#Bool#]{{; name=.+$}} // TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: > {#(Int, Double)#}[#Bool#]{{; name=.+$}} // TUPLE_NO_DOT_3-DAG: BuiltinOperator/None: = {#(foo: Int, Double)#}[#Void#]{{; name=.+$}} +// TUPLE_NO_DOT_3-DAG: Keyword[self]/CurrNominal: .self[#(foo: Int, Double)#]; name=self // TUPLE_NO_DOT_3-NEXT: End completions func testTupleDot1() { var t = (1, 2.0) t.#^TUPLE_DOT_1^# } -// TUPLE_DOT_1: Begin completions, 2 items +// TUPLE_DOT_1: Begin completions, 3 items +// TUPLE_DOT_1-NEXT: Keyword[self]/CurrNominal: self[#(Int, Double)#]; name=self // TUPLE_DOT_1-NEXT: Pattern/CurrNominal: 0[#Int#]{{; name=.+$}} // TUPLE_DOT_1-NEXT: Pattern/CurrNominal: 1[#Double#]{{; name=.+$}} // TUPLE_DOT_1-NEXT: End completions @@ -84,7 +88,8 @@ func testTupleDot2() { var t = (foo: 1, bar: 2.0) t.#^TUPLE_DOT_2^# } -// TUPLE_DOT_2: Begin completions, 2 items +// TUPLE_DOT_2: Begin completions, 3 items +// TUPLE_DOT_2-NEXT: Keyword[self]/CurrNominal: self[#(foo: Int, bar: Double)#]; name=self // TUPLE_DOT_2-NEXT: Pattern/CurrNominal: foo[#Int#]{{; name=.+$}} // TUPLE_DOT_2-NEXT: Pattern/CurrNominal: bar[#Double#]{{; name=.+$}} // TUPLE_DOT_2-NEXT: End completions @@ -93,7 +98,8 @@ func testTupleDot3() { var t = (foo: 1, 2.0) t.#^TUPLE_DOT_3^# } -// TUPLE_DOT_3: Begin completions, 2 items +// TUPLE_DOT_3: Begin completions, 3 items +// TUPLE_DOT_3-NEXT: Keyword[self]/CurrNominal: self[#(foo: Int, Double)#]; name=self // TUPLE_DOT_3-NEXT: Pattern/CurrNominal: foo[#Int#]{{; name=.+$}} // TUPLE_DOT_3-NEXT: Pattern/CurrNominal: 1[#Double#]{{; name=.+$}} // TUPLE_DOT_3-NEXT: End completions @@ -107,7 +113,8 @@ func testTupleNested1() { var t = (foo: FooStruct(), i: Int) t.foo.#^TUPLE_NESTED_1^# } -// TUPLE_NESTED_1: Begin completions, 2 items +// TUPLE_NESTED_1: Begin completions, 3 items +// TUPLE_NESTED_1-NEXT: Keyword[self]/CurrNominal: self[#FooStruct#]; name=self // TUPLE_NESTED_1-NEXT: Decl[InstanceVar]/CurrNominal: fooInstanceVar[#Int#]{{; name=.+$}} // TUPLE_NESTED_1-NEXT: Decl[InstanceVar]/CurrNominal: barInstanceVar[#Double#]{{; name=.+$}} // TUPLE_NESTED_1-NEXT: End completions diff --git a/test/IDE/complete_from_clang_framework.swift b/test/IDE/complete_from_clang_framework.swift index 0ff5e6a09100b..e580884035b20 100644 --- a/test/IDE/complete_from_clang_framework.swift +++ b/test/IDE/complete_from_clang_framework.swift @@ -44,6 +44,7 @@ struct SwiftStruct { func testSwiftCompletions(foo: SwiftStruct) { foo.#^SWIFT_COMPLETIONS^# // SWIFT_COMPLETIONS: Begin completions +// SWIFT_COMPLETIONS-NEXT: Keyword[self]/CurrNominal: self[#SwiftStruct#]; name=self // SWIFT_COMPLETIONS-NEXT: Decl[InstanceVar]/CurrNominal: instanceVar[#Int#]{{; name=.+$}} // SWIFT_COMPLETIONS-NEXT: End completions } @@ -227,6 +228,7 @@ func testCompleteFunctionCall1() { fooFunc1#^FUNCTION_CALL_1^# // FUNCTION_CALL_1: Begin completions // FUNCTION_CALL_1-NEXT: Pattern/CurrModule: ({#(a): Int32#})[#Int32#]{{; name=.+$}} +// FUNCTION_CALL_1-NEXT: Keyword[self]/CurrNominal: .self[#(Int32) -> Int32#]; name=self // FUNCTION_CALL_1-NEXT: End completions } @@ -234,6 +236,7 @@ func testCompleteFunctionCall2() { fooFunc1AnonymousParam#^FUNCTION_CALL_2^# // FUNCTION_CALL_2: Begin completions // FUNCTION_CALL_2-NEXT: Pattern/CurrModule: ({#Int32#})[#Int32#]{{; name=.+$}} +// FUNCTION_CALL_2-NEXT: Keyword[self]/CurrNominal: .self[#(Int32) -> Int32#]; name=self // FUNCTION_CALL_2-NEXT: End completions } @@ -242,6 +245,7 @@ func testCompleteStructMembers1() { // CLANG_STRUCT_MEMBERS_1: Begin completions // CLANG_STRUCT_MEMBERS_1-NEXT: Decl[Constructor]/CurrNominal: ()[#FooStruct1#]{{; name=.+$}} // CLANG_STRUCT_MEMBERS_1-NEXT: Decl[Constructor]/CurrNominal: ({#x: Int32#}, {#y: Double#})[#FooStruct1#]{{; name=.+$}} +// CLANG_STRUCT_MEMBERS_1-NEXT: Keyword[self]/CurrNominal: .self[#FooStruct1.Type#]; name=self // CLANG_STRUCT_MEMBERS_1-NEXT: End completions } @@ -267,6 +271,7 @@ func testCompleteClassMembers1() { // CLANG_CLASS_MEMBERS_1-NEXT: Decl[InstanceMethod]/CurrNominal: .nonInternalMeth({#self: FooClassBase#})[#() -> Any?#] // CLANG_CLASS_MEMBERS_1-NEXT: Decl[StaticMethod]/CurrNominal: ._internalMeth1()[#Any!#] // CLANG_CLASS_MEMBERS_1-NEXT: Decl[InstanceMethod]/CurrNominal: ._internalMeth1({#self: FooClassBase#})[#() -> Any?#] +// CLANG_CLASS_MEMBERS_1-NEXT: Keyword[self]/CurrNominal: .self[#FooClassBase.Type#]; name=self // CLANG_CLASS_MEMBERS_1-NEXT: End completions } @@ -299,6 +304,7 @@ func testCompleteClassMembers2() { // CLANG_CLASS_MEMBERS_2-NEXT: Decl[InstanceMethod]/Super: .nonInternalMeth({#self: FooClassBase#})[#() -> Any?#] // CLANG_CLASS_MEMBERS_2-NEXT: Decl[StaticMethod]/Super: ._internalMeth1()[#Any!#] // CLANG_CLASS_MEMBERS_2-NEXT: Decl[InstanceMethod]/Super: ._internalMeth1({#self: FooClassBase#})[#() -> Any?#] +// CLANG_CLASS_MEMBERS_2-NEXT: Keyword[self]/CurrNominal: .self[#FooClassDerived.Type#]; name=self // CLANG_CLASS_MEMBERS_2-NEXT: End completions } diff --git a/test/IDE/complete_from_clang_framework_typechecker.swift b/test/IDE/complete_from_clang_framework_typechecker.swift index 149811a0a066d..4bce3edbd66ed 100644 --- a/test/IDE/complete_from_clang_framework_typechecker.swift +++ b/test/IDE/complete_from_clang_framework_typechecker.swift @@ -1,6 +1,4 @@ -// RUN: %target-typecheck-verify-swift -F %S/Inputs/mock-sdk - -// XFAIL: linux, freebsd +// RUN: %target-typecheck-verify-swift -enable-objc-interop -F %S/Inputs/mock-sdk import Foo // Don't import 'FooHelper'. diff --git a/test/IDE/complete_from_clang_importer_framework.swift b/test/IDE/complete_from_clang_importer_framework.swift index 3979d7bb09379..1a4fe14b2984a 100644 --- a/test/IDE/complete_from_clang_importer_framework.swift +++ b/test/IDE/complete_from_clang_importer_framework.swift @@ -59,7 +59,8 @@ func testCompleteModuleQualifiedMacros1() { func testClangMember1() { var FS = FooStruct1() FS.#^CLANG_MEMBER1^# -// CLANG_MEMBERS1: Begin completions, 2 items +// CLANG_MEMBERS1: Begin completions, 3 items // CLANG_MEMBERS1-DAG: Decl[InstanceVar]/CurrNominal/keyword[x, Struct1]/recommended[y]: x[#Int32#]{{; name=.+$}} // CLANG_MEMBERS1-DAG: Decl[InstanceVar]/CurrNominal/keyword[y, Struct1]/recommendedover[x]: y[#Double#]{{; name=.+$}} +// CLANG_MEMBERS1-DAG: Keyword[self]/CurrNominal: self[#FooStruct1#]; name=self } diff --git a/test/IDE/complete_from_constraint_extensions.swift b/test/IDE/complete_from_constraint_extensions.swift index 1b91f54ae1fbf..52b91f917ae60 100644 --- a/test/IDE/complete_from_constraint_extensions.swift +++ b/test/IDE/complete_from_constraint_extensions.swift @@ -22,7 +22,8 @@ func foo1() { I1.#^CONSTRAINT1^# } -// CONSTRAINT1: Begin completions, 1 items +// CONSTRAINT1: Begin completions, 2 items +// CONSTRAINT1-NEXT: Keyword[self]/CurrNominal: self[#Example#]; name=self // CONSTRAINT1-NEXT: Decl[InstanceMethod]/CurrNominal: P1Method()[#Void#]; name=P1Method() // CONSTRAINT1-NEXT: End completions @@ -31,7 +32,8 @@ func foo2() { I2.#^CONSTRAINT2^# } -// CONSTRAINT2: Begin completions, 1 items +// CONSTRAINT2: Begin completions, 2 items +// CONSTRAINT2-NEXT: Keyword[self]/CurrNominal: self[#Example#]; name=self // CONSTRAINT2-NEXT: Decl[InstanceMethod]/CurrNominal: P2Method()[#Void#]; name=P2Method() // CONSTRAINT2-NEXT: End completions @@ -48,6 +50,7 @@ struct ConcreteCollection : MyCollection {} func foo3() { ConcreteCollection().#^CONSTRAINT3^# } -// CONSTRAINT3: Begin completions, 1 items +// CONSTRAINT3: Begin completions, 2 items +// CONSTRAINT3-NEXT: Keyword[self]/CurrNominal: self[#ConcreteCollection#]; name=self // CONSTRAINT3-NEXT: Decl[InstanceVar]/Super: indices[#MyDefaultIndices>#]; name=indices // CONSTRAINT3-NEXT: End completions diff --git a/test/IDE/complete_from_stdlib.swift b/test/IDE/complete_from_stdlib.swift index 561fbdefd2a37..ecaea90c6dec2 100644 --- a/test/IDE/complete_from_stdlib.swift +++ b/test/IDE/complete_from_stdlib.swift @@ -93,7 +93,7 @@ func protocolExtCollection1a(_ a: C) { // PRIVATE_NOMINAL_MEMBERS_2A: Begin completions // PRIVATE_NOMINAL_MEMBERS_2A-DAG: map({#(transform): (C.Element) throws -> T##(C.Element) throws -> T#})[' rethrows'][#[T]#]{{; name=.+}} // PRIVATE_NOMINAL_MEMBERS_2A: End completions -// NEGATIVE_PRIVATE_NOMINAL_MEMBERS_2A-NOT: Decl{{.*}}: last +// NEGATIVE_PRIVATE_NOMINAL_MEMBERS_2A-NOT: Decl{{.*}}: index({#before: Comparable#}) func protocolExtCollection1b(_ a: Collection) { a.#^PRIVATE_NOMINAL_MEMBERS_2B^# @@ -102,7 +102,7 @@ func protocolExtCollection1b(_ a: Collection) { // PRIVATE_NOMINAL_MEMBERS_2B: Begin completions // PRIVATE_NOMINAL_MEMBERS_2B-DAG: map({#(transform): (Collection.Element) throws -> T##(Collection.Element) throws -> T#})[' rethrows'][#[T]#]{{; name=.+}} // PRIVATE_NOMINAL_MEMBERS_2B: End completions -// NEGATIVE_PRIVATE_NOMINAL_MEMBERS_2B-NOT: Decl{{.*}}: last +// NEGATIVE_PRIVATE_NOMINAL_MEMBERS_2B-NOT: Decl{{.*}}: index({#before: Comparable#}) func protocolExtCollection2(_ a: C) { a.#^PRIVATE_NOMINAL_MEMBERS_3^# @@ -111,9 +111,9 @@ func protocolExtCollection2(_ // PRIVATE_NOMINAL_MEMBERS_3: Begin completions // PRIVATE_NOMINAL_MEMBERS_3-DAG: Decl[InstanceMethod]/Super: map({#(transform): (C.Element) throws -> T##(C.Element) throws -> T#})[' rethrows'][#[T]#]{{; name=.+}} // PRIVATE_NOMINAL_MEMBERS_3-DAG: Decl[InstanceVar]/Super: lazy[#LazySequence#]{{; name=.+}} -// PRIVATE_NOMINAL_MEMBERS_3-DAG: index({#where: (C.Element) throws -> Bool##(C.Element) throws -> Bool#})[' rethrows'][#Comparable?#]{{; name=.+}} +// PRIVATE_NOMINAL_MEMBERS_3-DAG: firstIndex({#where: (C.Element) throws -> Bool##(C.Element) throws -> Bool#})[' rethrows'][#Comparable?#]{{; name=.+}} // PRIVATE_NOMINAL_MEMBERS_3: End completions -// NEGATIVE_PRIVATE_NOMINAL_MEMBERS_3-NOT: Decl{{.*}}: index({#({{.*}}): Self.Iterator.Element +// NEGATIVE_PRIVATE_NOMINAL_MEMBERS_3-NOT: Decl{{.*}}: firstIndex({#({{.*}}): Self.Iterator.Element func protocolExtArray(_ a: [T]) { a.#^PRIVATE_NOMINAL_MEMBERS_4^# @@ -121,8 +121,8 @@ func protocolExtArray(_ a: [T]) { // PRIVATE_NOMINAL_MEMBERS_4: Begin completions // PRIVATE_NOMINAL_MEMBERS_4-DAG: Decl[InstanceMethod]/Super: map({#(transform): (Equatable) throws -> T##(Equatable) throws -> T#})[' rethrows'][#[T]#]{{; name=.+}} // PRIVATE_NOMINAL_MEMBERS_4-DAG: Decl[InstanceVar]/Super: last[#Equatable?#]{{; name=.+}} -// PRIVATE_NOMINAL_MEMBERS_4-DAG: Decl[InstanceMethod]/Super: index({#of: Equatable#})[#Int?#]{{; name=.+}} -// PRIVATE_NOMINAL_MEMBERS_4-DAG: Decl[InstanceMethod]/Super: index({#where: (Equatable) throws -> Bool##(Equatable) throws -> Bool#})[' rethrows'][#Int?#]{{; name=.+}} +// PRIVATE_NOMINAL_MEMBERS_4-DAG: Decl[InstanceMethod]/Super: firstIndex({#of: Equatable#})[#Int?#]{{; name=.+}} +// PRIVATE_NOMINAL_MEMBERS_4-DAG: Decl[InstanceMethod]/Super: firstIndex({#where: (Equatable) throws -> Bool##(Equatable) throws -> Bool#})[' rethrows'][#Int?#]{{; name=.+}} // PRIVATE_NOMINAL_MEMBERS_4: End completions func testArchetypeReplacement1(_ a: [FOO]) { diff --git a/test/IDE/complete_from_swift_module.swift b/test/IDE/complete_from_swift_module.swift index 77794ed248b18..576361061cebf 100644 --- a/test/IDE/complete_from_swift_module.swift +++ b/test/IDE/complete_from_swift_module.swift @@ -54,6 +54,7 @@ func testCompleteModuleQualified2() { foo_swift_module.FooSwiftStruct.#^MODULE_QUALIFIED_2^# } // MODULE_QUALIFIED_2: Begin completions +// MODULE_QUALIFIED_2-NEXT: Keyword[self]/CurrNominal: self[#FooSwiftStruct.Type#]; name=self // MODULE_QUALIFIED_2-NEXT: Decl[InstanceMethod]/CurrNominal: fooInstanceFunc({#self: FooSwiftStruct#})[#() -> Void#]{{; name=.+$}} // MODULE_QUALIFIED_2-NEXT: Decl[Constructor]/CurrNominal: init()[#FooSwiftStruct#]{{; name=.+$}} // MODULE_QUALIFIED_2-NEXT: End completions @@ -72,6 +73,7 @@ func testCompleteModuleQualified4() { // MODULE_QUALIFIED_4: Begin completions // MODULE_QUALIFIED_4-NEXT: Decl[Constructor]/CurrNominal: ({#t: _#}, {#u: _#})[#BarGenericSwiftStruct2<_, _>#]; name=(t: _, u: _) // MODULE_QUALIFIED_4-NEXT: Decl[InstanceMethod]/CurrNominal: .bar2InstanceFunc({#self: BarGenericSwiftStruct2<_, _>#})[#() -> Void#]; name=bar2InstanceFunc(BarGenericSwiftStruct2<_, _>) +// MODULE_QUALIFIED_4-NEXT: Keyword[self]/CurrNominal: .self[#BarGenericSwiftStruct2<_, _>.Type#]; name=self // MODULE_QUALIFIED_4-NEXT: End completions func testCompleteModuleQualified5() { diff --git a/test/IDE/complete_func_body_typechecking.swift b/test/IDE/complete_func_body_typechecking.swift index 7c4d1d1d17a6d..8856a9dfb9a3c 100644 --- a/test/IDE/complete_func_body_typechecking.swift +++ b/test/IDE/complete_func_body_typechecking.swift @@ -40,6 +40,7 @@ struct FooStruct { } // FOO_STRUCT_COMMON: Begin completions +// FOO_STRUCT_COMMON-NEXT: Keyword[self]/CurrNominal: self[#FooStruct#]; name=self // FOO_STRUCT_COMMON-NEXT: Decl[InstanceVar]/CurrNominal: instanceVar[#Int#]{{; name=.+$}} // FOO_STRUCT_COMMON-NEXT: Decl[InstanceMethod]/CurrNominal: instanceFunc0()[#Void#]{{; name=.+$}} // FOO_STRUCT_COMMON-NEXT: Decl[InstanceMethod]/CurrNominal: builderFunc1()[#FooStruct#]{{; name=.+$}} diff --git a/test/IDE/complete_generic_optional.swift b/test/IDE/complete_generic_optional.swift index 588760c9b7e8b..b2f7f47a21f19 100644 --- a/test/IDE/complete_generic_optional.swift +++ b/test/IDE/complete_generic_optional.swift @@ -11,6 +11,7 @@ struct Foo { // SR-642 Code completion does not instantiate generic arguments of a type wrapped in an optional let x: Foo? = Foo() x.#^FOO_OPTIONAL_1^# -// FOO_OPTIONAL_1: Begin completions, 6 items +// FOO_OPTIONAL_1: Begin completions, 7 items // FOO_OPTIONAL_1-DAG: Decl[InstanceMethod]/CurrNominal/Erase[1]: ?.myFunction({#(foobar): Bar#})[#Void#]; name=myFunction(foobar: Bar) +// FOO_OPTIONAL_1-DAG: Keyword[self]/CurrNominal: self[#Foo?#]; name=self // FOO_OPTIONAL_1: End completions diff --git a/test/IDE/complete_in_accessors.swift b/test/IDE/complete_in_accessors.swift index 99ea62a9b7385..dd08693d43ad8 100644 --- a/test/IDE/complete_in_accessors.swift +++ b/test/IDE/complete_in_accessors.swift @@ -125,6 +125,7 @@ struct FooStruct { func returnsInt() -> Int {} // FOO_OBJECT_DOT: Begin completions +// FOO_OBJECT_DOT-NEXT: Keyword[self]/CurrNominal: self[#FooStruct#]; name=self // FOO_OBJECT_DOT-NEXT: Decl[InstanceVar]/CurrNominal: instanceVar[#Int#]{{; name=.+$}} // FOO_OBJECT_DOT-NEXT: Decl[InstanceMethod]/CurrNominal: instanceFunc0()[#Void#]{{; name=.+$}} // FOO_OBJECT_DOT-NEXT: End completions diff --git a/test/IDE/complete_in_closures.swift b/test/IDE/complete_in_closures.swift index 964f27a85747b..e41f910e99136 100644 --- a/test/IDE/complete_in_closures.swift +++ b/test/IDE/complete_in_closures.swift @@ -64,6 +64,7 @@ struct FooStruct { } // FOO_OBJECT_DOT: Begin completions +// FOO_OBJECT_DOT-NEXT: Keyword[self]/CurrNominal: self[#FooStruct#]; name=self // FOO_OBJECT_DOT-NEXT: Decl[InstanceVar]/CurrNominal: instanceVar[#Int#]{{; name=.+$}} // FOO_OBJECT_DOT-NEXT: Decl[InstanceMethod]/CurrNominal: instanceFunc0()[#Void#]{{; name=.+$}} // FOO_OBJECT_DOT-NEXT: End completions @@ -320,7 +321,7 @@ func testClosureParam1() { } } // CLOSURE_PARAM_1: Begin completions -// CLOSURE_PARAM_1-DAG: Decl[LocalVar]/Local: theValue[#(Int)#]{{; name=.+$}} +// CLOSURE_PARAM_1-DAG: Decl[LocalVar]/Local: theValue[#Int#]{{; name=.+$}} func testClosureParam2() { closureTaker2 { (Value1, Value2) -> () in #^CLOSURE_PARAM_2^# diff --git a/test/IDE/complete_init_inherited.swift b/test/IDE/complete_init_inherited.swift index f661695465d78..62d650c3f1cec 100644 --- a/test/IDE/complete_init_inherited.swift +++ b/test/IDE/complete_init_inherited.swift @@ -15,6 +15,7 @@ class A { // TEST_A-NEXT: Decl[Constructor]/CurrNominal: ({#int: Int#})[#A#]{{; name=.+$}} // TEST_A-NEXT: Decl[Constructor]/CurrNominal: ({#double: Double#})[#A#]{{; name=.+$}} // TEST_A-NEXT: Decl[Constructor]/CurrNominal: ({#float: Float#})[#A#]{{; name=.+$}} +// TEST_A-NEXT: Keyword[self]/CurrNominal: .self[#A.Type#]; name=self // TEST_A-NEXT: End completions class B : A { @@ -27,6 +28,7 @@ class B : A { // TEST_B-NEXT: Decl[Constructor]/CurrNominal: ({#int: Int#})[#B#]{{; name=.+$}} // TEST_B-NEXT: Decl[Constructor]/CurrNominal: ({#double: Double#})[#B#]{{; name=.+$}} // TEST_B-NEXT: Decl[Constructor]/Super: ({#float: Float#})[#A#]{{; name=.+$}} +// TEST_B-NEXT: Keyword[self]/CurrNominal: .self[#B.Type#]; name=self // TEST_B-NEXT: End completions class C : B { @@ -42,6 +44,7 @@ class C : B { // TEST_C: Begin completions // TEST_C-NEXT: Decl[Constructor]/CurrNominal: ({#int: Int#})[#C#]{{; name=.+$}} // TEST_C-NEXT: Decl[Constructor]/CurrNominal: ({#c: C#})[#C#]{{; name=.+$}} +// TEST_C-NEXT: Keyword[self]/CurrNominal: .self[#C.Type#]; name=self // TEST_C-NEXT: End completions class D : C { @@ -59,6 +62,7 @@ class D : C { // TEST_D-NEXT: Decl[Constructor]/CurrNominal: ({#d: D#})[#D#]{{; name=.+$}} // TEST_D-NEXT: Decl[Constructor]/CurrNominal: ({#int: Int#})[#D#]{{; name=.+$}} // TEST_D-NEXT: Decl[Constructor]/Super: ({#c: C#})[#C#]{{; name=.+$}} +// TEST_D-NEXT: Keyword[self]/CurrNominal: .self[#D.Type#]; name=self // TEST_D-NEXT: End completions func testA() { diff --git a/test/IDE/complete_lazy_initialized_var.swift b/test/IDE/complete_lazy_initialized_var.swift index b287a40d0f690..d77b6f662e5f4 100644 --- a/test/IDE/complete_lazy_initialized_var.swift +++ b/test/IDE/complete_lazy_initialized_var.swift @@ -8,6 +8,7 @@ func lazyInClass1(a: FooClass1) { } // This test checks that we don't include extra hidden declarations into code completion results. If you add more declarations to the type, update this test properly. -// LAZYVAR1: Begin completions, 1 items +// LAZYVAR1: Begin completions, 2 items +// LAZYVAR1-NEXT: Keyword[self]/CurrNominal: self[#FooClass1#]; name=self // LAZYVAR1-NEXT: Decl[InstanceVar]/CurrNominal: lazyVar1[#Int#]{{; name=.+$}} // LAZYVAR1-NEXT: End completions diff --git a/test/IDE/complete_member_decls_from_parent_decl_context.swift b/test/IDE/complete_member_decls_from_parent_decl_context.swift index d5e87bde07c7e..fa70353f5f9be 100644 --- a/test/IDE/complete_member_decls_from_parent_decl_context.swift +++ b/test/IDE/complete_member_decls_from_parent_decl_context.swift @@ -281,10 +281,10 @@ struct NestedOuter1 { typealias ATestTypealias = #^NESTED_NOMINAL_DECL_A_3^# // NESTED_NOMINAL_DECL_A_3: Begin completions -// NESTED_NOMINAL_DECL_A_3-DAG: Decl[Struct]/CurrNominal: NestedInnerAStruct[#NestedInnerA.NestedInnerAStruct#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_A_3-DAG: Decl[Class]/CurrNominal: NestedInnerAClass[#NestedInnerA.NestedInnerAClass#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_A_3-DAG: Decl[Enum]/CurrNominal: NestedInnerAEnum[#NestedInnerA.NestedInnerAEnum#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_A_3-DAG: Decl[TypeAlias]/CurrNominal: NestedInnerATypealias[#Int#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_A_3-DAG: Decl[Struct]/OutNominal: NestedInnerAStruct[#NestedInnerA.NestedInnerAStruct#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_A_3-DAG: Decl[Class]/OutNominal: NestedInnerAClass[#NestedInnerA.NestedInnerAClass#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_A_3-DAG: Decl[Enum]/OutNominal: NestedInnerAEnum[#NestedInnerA.NestedInnerAEnum#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_A_3-DAG: Decl[TypeAlias]/OutNominal: NestedInnerATypealias[#Int#]{{; name=.+$}} // FIXME: should this really come as Local? // NESTED_NOMINAL_DECL_A_3-DAG: Decl[Struct]/Local: NestedInnerA[#NestedInnerA#]{{; name=.+$}} // NESTED_NOMINAL_DECL_A_3-DAG: Decl[TypeAlias]/OutNominal: OuterTypealias[#Int#]{{; name=.+$}} @@ -324,11 +324,12 @@ struct NestedOuter1 { // NESTED_NOMINAL_DECL_A_4: End completions NestedInnerA(aInstanceVar: 42)#^NESTED_NOMINAL_DECL_A_5^# -// NESTED_NOMINAL_DECL_A_5: Begin completions, 4 items +// NESTED_NOMINAL_DECL_A_5: Begin completions, 5 items // NESTED_NOMINAL_DECL_A_5-NEXT: Decl[InstanceMethod]/CurrNominal: .aTestInstanceFunc()[#Void#]{{; name=.+$}} // NESTED_NOMINAL_DECL_A_5-NEXT: Decl[InstanceVar]/CurrNominal: .aInstanceVar[#Int#]{{; name=.+$}} // NESTED_NOMINAL_DECL_A_5-NEXT: Decl[InstanceMethod]/CurrNominal: .aInstanceFunc()[#Void#]{{; name=.+$}} // NESTED_NOMINAL_DECL_A_5-NEXT: Decl[Subscript]/CurrNominal: [{#Int#}][#Double#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_A_5-NEXT: Keyword[self]/CurrNominal: .self[#NestedInnerA#]; name=self // NESTED_NOMINAL_DECL_A_5-NEXT: End completions } @@ -388,10 +389,10 @@ struct NestedOuter1 { typealias BTestTypealias = #^NESTED_NOMINAL_DECL_B_3^# // NESTED_NOMINAL_DECL_B_3: Begin completions -// NESTED_NOMINAL_DECL_B_3-DAG: Decl[Struct]/CurrNominal: NestedInnerBStruct[#NestedInnerB.NestedInnerBStruct#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_B_3-DAG: Decl[Class]/CurrNominal: NestedInnerBClass[#NestedInnerB.NestedInnerBClass#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_B_3-DAG: Decl[Enum]/CurrNominal: NestedInnerBEnum[#NestedInnerB.NestedInnerBEnum#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_B_3-DAG: Decl[TypeAlias]/CurrNominal: NestedInnerBTypealias[#Int#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_B_3-DAG: Decl[Struct]/OutNominal: NestedInnerBStruct[#NestedInnerB.NestedInnerBStruct#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_B_3-DAG: Decl[Class]/OutNominal: NestedInnerBClass[#NestedInnerB.NestedInnerBClass#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_B_3-DAG: Decl[Enum]/OutNominal: NestedInnerBEnum[#NestedInnerB.NestedInnerBEnum#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_B_3-DAG: Decl[TypeAlias]/OutNominal: NestedInnerBTypealias[#Int#]{{; name=.+$}} // FIXME: should this really come as Local? // NESTED_NOMINAL_DECL_B_3-DAG: Decl[Struct]/Local: NestedInnerB[#NestedInnerB#]{{; name=.+$}} // NESTED_NOMINAL_DECL_B_3-DAG: Decl[TypeAlias]/OutNominal: OuterTypealias[#Int#]{{; name=.+$}} @@ -433,11 +434,12 @@ struct NestedOuter1 { // NESTED_NOMINAL_DECL_B_4: End completions NestedInnerB(bInstanceVar: 42)#^NESTED_NOMINAL_DECL_B_5^# -// NESTED_NOMINAL_DECL_B_5: Begin completions, 4 items +// NESTED_NOMINAL_DECL_B_5: Begin completions, 5 items // NESTED_NOMINAL_DECL_B_5-DAG: Decl[InstanceMethod]/CurrNominal: .bTestInstanceFunc()[#Void#]{{; name=.+$}} // NESTED_NOMINAL_DECL_B_5-DAG: Decl[InstanceVar]/CurrNominal: .bInstanceVar[#Int#]{{; name=.+$}} // NESTED_NOMINAL_DECL_B_5-DAG: Decl[InstanceMethod]/CurrNominal: .bInstanceFunc()[#Void#]{{; name=.+$}} // NESTED_NOMINAL_DECL_B_5-DAG: Decl[Subscript]/CurrNominal: [{#Int#}][#Double#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_B_5-DAG: Keyword[self]/CurrNominal: .self[#NestedInnerB#]; name=self // NESTED_NOMINAL_DECL_B_5: End completions } @@ -489,10 +491,10 @@ func testOuterC() { typealias CTestTypealias = #^NESTED_NOMINAL_DECL_C_3^# // NESTED_NOMINAL_DECL_C_3: Begin completions -// NESTED_NOMINAL_DECL_C_3-DAG: Decl[Struct]/CurrNominal: NestedInnerCStruct[#NestedInnerC.NestedInnerCStruct#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_C_3-DAG: Decl[Class]/CurrNominal: NestedInnerCClass[#NestedInnerC.NestedInnerCClass#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_C_3-DAG: Decl[Enum]/CurrNominal: NestedInnerCEnum[#NestedInnerC.NestedInnerCEnum#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_C_3-DAG: Decl[TypeAlias]/CurrNominal: NestedInnerCTypealias[#Int#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_C_3-DAG: Decl[Struct]/OutNominal: NestedInnerCStruct[#NestedInnerC.NestedInnerCStruct#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_C_3-DAG: Decl[Class]/OutNominal: NestedInnerCClass[#NestedInnerC.NestedInnerCClass#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_C_3-DAG: Decl[Enum]/OutNominal: NestedInnerCEnum[#NestedInnerC.NestedInnerCEnum#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_C_3-DAG: Decl[TypeAlias]/OutNominal: NestedInnerCTypealias[#Int#]{{; name=.+$}} // FIXME: should this really come as Local? // NESTED_NOMINAL_DECL_C_3-DAG: Decl[Struct]/Local: NestedInnerC[#NestedInnerC#]{{; name=.+$}} // NESTED_NOMINAL_DECL_C_3: End completions @@ -524,11 +526,12 @@ func testOuterC() { // NESTED_NOMINAL_DECL_C_4: End completions NestedInnerC(cInstanceVar: 42)#^NESTED_NOMINAL_DECL_C_5^# -// NESTED_NOMINAL_DECL_C_5: Begin completions, 4 items +// NESTED_NOMINAL_DECL_C_5: Begin completions, 5 items // NESTED_NOMINAL_DECL_C_5-NEXT: Decl[InstanceMethod]/CurrNominal: .cTestInstanceFunc()[#Void#]{{; name=.+$}} // NESTED_NOMINAL_DECL_C_5-NEXT: Decl[InstanceVar]/CurrNominal: .cInstanceVar[#Int#]{{; name=.+$}} // NESTED_NOMINAL_DECL_C_5-NEXT: Decl[InstanceMethod]/CurrNominal: .cInstanceFunc()[#Void#]{{; name=.+$}} // NESTED_NOMINAL_DECL_C_5-NEXT: Decl[Subscript]/CurrNominal: [{#Int#}][#Double#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_C_5-NEXT: Keyword[self]/CurrNominal: .self[#NestedInnerC#]; name=self // NESTED_NOMINAL_DECL_C_5-NEXT: End completions } diff --git a/test/IDE/complete_members_optional.swift b/test/IDE/complete_members_optional.swift index ebed13dec40d7..2cf8ba4591b36 100644 --- a/test/IDE/complete_members_optional.swift +++ b/test/IDE/complete_members_optional.swift @@ -1,8 +1,8 @@ // RUN: sed -n -e '1,/NO_ERRORS_UP_TO_HERE$/ p' %s > %t_no_errors.swift -// RUN: %target-swift-frontend -typecheck -verify -disable-objc-attr-requires-foundation-module %t_no_errors.swift +// RUN: %target-swift-frontend -typecheck -verify -disable-objc-attr-requires-foundation-module -enable-objc-interop %t_no_errors.swift -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OPTIONAL_MEMBERS_1 | %FileCheck %s -check-prefix=OPTIONAL_MEMBERS_1 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OPTIONAL_MEMBERS_2 | %FileCheck %s -check-prefix=OPTIONAL_MEMBERS_2 +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=OPTIONAL_MEMBERS_1 | %FileCheck %s -check-prefix=OPTIONAL_MEMBERS_1 +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=OPTIONAL_MEMBERS_2 | %FileCheck %s -check-prefix=OPTIONAL_MEMBERS_2 @objc protocol HasOptionalMembers1 { @@ -28,16 +28,18 @@ func sanityCheck1(_ a: HasOptionalMembers1) { func optionalMembers1(_ a: HasOptionalMembers1) { a.#^OPTIONAL_MEMBERS_1^# } -// OPTIONAL_MEMBERS_1: Begin completions, 2 items +// OPTIONAL_MEMBERS_1: Begin completions, 3 items // OPTIONAL_MEMBERS_1-DAG: Decl[InstanceMethod]/CurrNominal: optionalInstanceFunc!()[#Int#]{{; name=.+$}} // OPTIONAL_MEMBERS_1-DAG: Decl[InstanceVar]/CurrNominal: optionalInstanceProperty[#Int?#]{{; name=.+$}} +// OPTIONAL_MEMBERS_1-DAG: Keyword[self]/CurrNominal: self[#HasOptionalMembers1#]; name=self // OPTIONAL_MEMBERS_1: End completions func optionalMembers2(_ a: T) { T.#^OPTIONAL_MEMBERS_2^# } -// OPTIONAL_MEMBERS_2: Begin completions, 3 items +// OPTIONAL_MEMBERS_2: Begin completions, 4 items // OPTIONAL_MEMBERS_2-DAG: Decl[InstanceMethod]/Super: optionalInstanceFunc!({#self: HasOptionalMembers1#})[#() -> Int#]{{; name=.+$}} // OPTIONAL_MEMBERS_2-DAG: Decl[StaticMethod]/Super: optionalClassFunc!()[#Int#]{{; name=.+$}} // OPTIONAL_MEMBERS_2-DAG: Decl[StaticVar]/Super: optionalClassProperty[#Int?#]{{; name=.+$}} +// OPTIONAL_MEMBERS_2-DAG: Keyword[self]/CurrNominal: self[#T.Type#]; name=self // OPTIONAL_MEMBERS_2: End completions diff --git a/test/IDE/complete_multiple_files.swift b/test/IDE/complete_multiple_files.swift index 484edc70e5b7f..8ee8999172d01 100644 --- a/test/IDE/complete_multiple_files.swift +++ b/test/IDE/complete_multiple_files.swift @@ -13,6 +13,7 @@ func testObjectExpr() { fooObject.#^T1^# } // T1: Begin completions +// T1-NEXT: Keyword[self]/CurrNominal: self[#FooStruct#]; name=self // T1-NEXT: Decl[InstanceVar]/CurrNominal: instanceVar[#Int#]{{; name=.+$}} // T1-NEXT: Decl[InstanceMethod]/CurrNominal: instanceFunc0()[#Void#]{{; name=.+$}} // @@ -24,6 +25,7 @@ func testGenericObjectExpr() { genericFooObject.#^T2^# } // T2: Begin completions +// T2-NEXT: Keyword[self]/CurrNominal: self[#GenericFooStruct#]; name=self // T2-NEXT: Decl[InstanceVar]/CurrNominal: instanceVar[#Int#]{{; name=.+$}} // T2-NEXT: Decl[InstanceMethod]/CurrNominal: instanceFunc0()[#Void#]{{; name=.+$}} // diff --git a/test/IDE/complete_name_lookup.swift b/test/IDE/complete_name_lookup.swift index 3319439022e6e..c900379e767f6 100644 --- a/test/IDE/complete_name_lookup.swift +++ b/test/IDE/complete_name_lookup.swift @@ -15,6 +15,7 @@ struct FooStruct : FooMoreRefinedProtocol { var instanceProperty: Int { return 0 } } // FOO_OBJECT_DOT: Begin completions +// FOO_OBJECT_DOT-NEXT: Keyword[self]/CurrNominal: self[#FooStruct#]; name=self // FOO_OBJECT_DOT-NEXT: Decl[InstanceVar]/CurrNominal: instanceProperty[#Int#] // FOO_OBJECT_DOT-NEXT: End completions @@ -22,6 +23,7 @@ struct BarStruct : FooEvenMoreRefinedProtocol { var instanceProperty: Int { return 0 } } // BAR_OBJECT_DOT: Begin completions +// BAR_OBJECT_DOT-NEXT: Keyword[self]/CurrNominal: self[#BarStruct#]; name=self // BAR_OBJECT_DOT-NEXT: Decl[InstanceVar]/CurrNominal: instanceProperty[#Int#] // BAR_OBJECT_DOT-NEXT: End completions diff --git a/test/IDE/complete_not_recommended.swift b/test/IDE/complete_not_recommended.swift index e90b7e4e19bcb..a8d0697d0f2f7 100644 --- a/test/IDE/complete_not_recommended.swift +++ b/test/IDE/complete_not_recommended.swift @@ -1,5 +1,6 @@ // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=MEMBER | %FileCheck %s -check-prefix=CHECK1 -// CHECK1: Begin completions, 2 items +// CHECK1: Begin completions, 3 items +// CHECK1: Keyword[self]/CurrNominal: self[#A.Type#]; name=self // CHECK1: Decl[InstanceMethod]/CurrNominal: foo({#self: A#})[#() -> Void#] // CHECK1: Decl[Constructor]/CurrNominal: init()[#A#]; name=init(){{$}} // CHECK1: End completions diff --git a/test/IDE/complete_operators.swift b/test/IDE/complete_operators.swift index 0aec8db059449..a4c2159d47502 100644 --- a/test/IDE/complete_operators.swift +++ b/test/IDE/complete_operators.swift @@ -119,7 +119,8 @@ func testPostfix7() { func testPostfix8(x: S) { x#^POSTFIX_8^# } -// POSTFIX_8-NOT: *** +// POSTFIX_8: Begin completions +// POSTFIX_8: Keyword[self]/CurrNominal: .self[#S#]; name=self protocol P { associatedtype T @@ -250,16 +251,18 @@ func testInfix11() { S2#^INFIX_11^# } -// INFIX_11: Begin completions, 1 items +// INFIX_11: Begin completions, 2 items // INFIX_11-DAG: Decl[Constructor]/CurrNominal: ()[#S2#]; name=() +// INFIX_11-DAG: Keyword[self]/CurrNominal: .self[#S2.Type#]; name=self // INFIX_11: End completions func testInfix12() { P#^INFIX_12^# } -// INFIX_12: Begin completions, 2 items +// INFIX_12: Begin completions, 3 items // INFIX_12-NEXT: Decl[AssociatedType]/CurrNominal: .T; name=T // INFIX_12-NEXT: Decl[InstanceMethod]/CurrNominal: .foo({#self: P#})[#() -> P.T#]; name=foo(P) +// INFIX_12-NEXT: Keyword[self]/CurrNominal: .self[#P.Protocol#]; name=self // INFIX_12: End completions func testInfix13() { @@ -273,17 +276,19 @@ func testInfix14() { func testInfix15() { T#^INFIX_15^# } -// INFIX_15: Begin completions, 2 items +// INFIX_15: Begin completions, 3 items // INFIX_15-NEXT: Decl[AssociatedType]/Super: .T; name=T // INFIX_15-NEXT: Decl[InstanceMethod]/Super: .foo({#self: P#})[#() -> S2#]; name=foo(P) +// INFIX_15-NEXT: Keyword[self]/CurrNominal: .self[#T.Type#]; name=self // INFIX_15: End completions func testInfix16() { T.foo#^INFIX_16^# } -// INFIX_16: Begin completions, 1 items +// INFIX_16: Begin completions, 2 items // INFIX_16-NEXT: Pattern/CurrModule: ({#(self): T#})[#() -> S2#]; name=(self: T) +// INFIX_16-NEXT: Keyword[self]/CurrNominal: .self[#(T) -> () -> S2#]; name=self // INFIX_16: End completions func testInfix17(x: Void) { @@ -325,7 +330,7 @@ func testInfix21() { func testInfix22() { E.B#^INFIX_22^# } -// INFIX_22: Begin completions, 1 items +// INFIX_22: Begin completions, 2 items // INFIX_22-NEXT: Pattern/CurrModule: ({#S2#})[#E#]; name=(S2) // INFIX_22: End completions diff --git a/test/IDE/complete_overridden_decls.swift b/test/IDE/complete_overridden_decls.swift index 6927e4cc4c38a..1decf28fea21e 100644 --- a/test/IDE/complete_overridden_decls.swift +++ b/test/IDE/complete_overridden_decls.swift @@ -54,6 +54,7 @@ func test1(_ b: TestABase) { b.#^OVER_BASE_1^# } // OVER_BASE_1: Begin completions +// OVER_BASE_1-NEXT: Keyword[self]/CurrNominal: self[#TestABase#]; name=self // OVER_BASE_1-NEXT: Decl[InstanceVar]/CurrNominal: baseInstanceVar[#FooBase#]{{; name=.+$}} // OVER_BASE_1-NEXT: Decl[InstanceVar]/CurrNominal: baseOverInstanceVar[#FooBase#]{{; name=.+$}} // OVER_BASE_1-NEXT: Decl[InstanceMethod]/CurrNominal: baseOverFunc()[#Void#]{{; name=.+$}} @@ -65,6 +66,7 @@ func test2(_ d: TestADerived) { d.#^OVER_DERIVED_1^# } // OVER_DERIVED_1: Begin completions +// OVER_DERIVED_1-NEXT: Keyword[self]/CurrNominal: self[#TestADerived#]; name=self // OVER_DERIVED_1-NEXT: Decl[InstanceVar]/CurrNominal: derivedInstanceVar[#FooBase#]{{; name=.+$}} // OVER_DERIVED_1-NEXT: Decl[InstanceVar]/CurrNominal: baseOverInstanceVar[#FooDerived#]{{; name=.+$}} // OVER_DERIVED_1-NEXT: Decl[InstanceVar]/CurrNominal: derivedOverInstanceVar[#FooBase#]{{; name=.+$}} @@ -78,6 +80,7 @@ func test3(_ md: TestAMoreDerived) { md.#^OVER_MORE_DERIVED_1^# } // OVER_MORE_DERIVED_1: Begin completions +// OVER_MORE_DERIVED_1-NEXT: Keyword[self]/CurrNominal: self[#TestAMoreDerived#]; name=self // OVER_MORE_DERIVED_1-NEXT: Decl[InstanceVar]/CurrNominal: moreDerivedInstanceVar[#FooBase#]{{; name=.+$}} // OVER_MORE_DERIVED_1-NEXT: Decl[InstanceVar]/CurrNominal: baseOverInstanceVar[#FooMoreDerived#]{{; name=.+$}} // OVER_MORE_DERIVED_1-NEXT: Decl[InstanceVar]/CurrNominal: derivedOverInstanceVar[#FooDerived#]{{; name=.+$}} diff --git a/test/IDE/complete_override.swift b/test/IDE/complete_override.swift index a9be1e73cd32b..0d455a9973506 100644 --- a/test/IDE/complete_override.swift +++ b/test/IDE/complete_override.swift @@ -1,141 +1,141 @@ // RUN: sed -n -e '1,/NO_ERRORS_UP_TO_HERE$/ p' %s > %t_no_errors.swift -// RUN: %target-swift-frontend -typecheck -verify -disable-objc-attr-requires-foundation-module %t_no_errors.swift +// RUN: %target-swift-frontend -typecheck -verify -disable-objc-attr-requires-foundation-module -enable-objc-interop %t_no_errors.swift -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=CLASS_PA -code-completion-keywords=false > %t.txt +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=CLASS_PA -code-completion-keywords=false > %t.txt // RUN: %FileCheck %s -check-prefix=CLASS_PA < %t.txt // RUN: %FileCheck %s -check-prefix=WITH_PA < %t.txt -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=CLASS_PA_EXT_1 -code-completion-keywords=false > %t.txt +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=CLASS_PA_EXT_1 -code-completion-keywords=false > %t.txt // RUN: %FileCheck %s -check-prefix=CLASS_PA < %t.txt // RUN: %FileCheck %s -check-prefix=WITH_PA < %t.txt -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=CLASS_PA_EXT_2 -code-completion-keywords=false > %t.txt +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=CLASS_PA_EXT_2 -code-completion-keywords=false > %t.txt // RUN: %FileCheck %s -check-prefix=CLASS_PA < %t.txt // RUN: %FileCheck %s -check-prefix=WITH_PA < %t.txt -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=CLASS_PB -code-completion-keywords=false > %t.txt +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=CLASS_PB -code-completion-keywords=false > %t.txt // RUN: %FileCheck %s -check-prefix=CLASS_PB < %t.txt // RUN: %FileCheck %s -check-prefix=WITH_PB < %t.txt -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=CLASS_PA_PB -code-completion-keywords=false > %t.txt +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=CLASS_PA_PB -code-completion-keywords=false > %t.txt // RUN: %FileCheck %s -check-prefix=CLASS_PA_PB < %t.txt // RUN: %FileCheck %s -check-prefix=WITH_PA < %t.txt // RUN: %FileCheck %s -check-prefix=WITH_PB < %t.txt -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=CLASS_BA -code-completion-keywords=false > %t.txt +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=CLASS_BA -code-completion-keywords=false > %t.txt // RUN: %FileCheck %s -check-prefix=CLASS_BA < %t.txt // RUN: %FileCheck %s -check-prefix=WITH_BA < %t.txt -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=CLASS_BA_PA -code-completion-keywords=false > %t.txt +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=CLASS_BA_PA -code-completion-keywords=false > %t.txt // RUN: %FileCheck %s -check-prefix=CLASS_BA_PA < %t.txt // RUN: %FileCheck %s -check-prefix=WITH_BA < %t.txt // RUN: %FileCheck %s -check-prefix=WITH_PA < %t.txt -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=CLASS_BA_PA_EXT1 -code-completion-keywords=false > %t.txt +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=CLASS_BA_PA_EXT1 -code-completion-keywords=false > %t.txt // RUN: %FileCheck %s -check-prefix=CLASS_BA_PA < %t.txt // RUN: %FileCheck %s -check-prefix=WITH_BA < %t.txt // RUN: %FileCheck %s -check-prefix=WITH_PA < %t.txt -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=CLASS_BA_PA_EXT2 -code-completion-keywords=false > %t.txt +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=CLASS_BA_PA_EXT2 -code-completion-keywords=false > %t.txt // RUN: %FileCheck %s -check-prefix=CLASS_BA_PA < %t.txt // RUN: %FileCheck %s -check-prefix=WITH_BA < %t.txt // RUN: %FileCheck %s -check-prefix=WITH_PA < %t.txt -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=CLASS_BA_PB -code-completion-keywords=false > %t.txt +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=CLASS_BA_PB -code-completion-keywords=false > %t.txt // RUN: %FileCheck %s -check-prefix=CLASS_BA_PB < %t.txt // RUN: %FileCheck %s -check-prefix=WITH_BA < %t.txt // RUN: %FileCheck %s -check-prefix=WITH_PB < %t.txt -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=CLASS_BB -code-completion-keywords=false > %t.txt +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=CLASS_BB -code-completion-keywords=false > %t.txt // RUN: %FileCheck %s -check-prefix=CLASS_BB < %t.txt // RUN: %FileCheck %s -check-prefix=WITH_BB < %t.txt -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=CLASS_BE -code-completion-keywords=false > %t.txt +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=CLASS_BE -code-completion-keywords=false > %t.txt // RUN: %FileCheck %s -check-prefix=CLASS_BE < %t.txt // RUN: %FileCheck %s -check-prefix=WITH_BE < %t.txt -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=CLASS_BE_PA -code-completion-keywords=false > %t.txt +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=CLASS_BE_PA -code-completion-keywords=false > %t.txt // RUN: %FileCheck %s -check-prefix=CLASS_BE_PA < %t.txt // RUN: %FileCheck %s -check-prefix=WITH_BE < %t.txt // RUN: %FileCheck %s -check-prefix=WITH_PA < %t.txt -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=CLASS_BE_PA_PE -code-completion-keywords=false > %t.txt +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=CLASS_BE_PA_PE -code-completion-keywords=false > %t.txt // RUN: %FileCheck %s -check-prefix=CLASS_BE_PA_PE < %t.txt // RUN: %FileCheck %s -check-prefix=WITH_BE < %t.txt // RUN: %FileCheck %s -check-prefix=WITH_PA < %t.txt -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=CLASS_BE_PA_PE_EXT1 -code-completion-keywords=false > %t.txt +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=CLASS_BE_PA_PE_EXT1 -code-completion-keywords=false > %t.txt // RUN: %FileCheck %s -check-prefix=CLASS_BE_PA_PE < %t.txt // RUN: %FileCheck %s -check-prefix=WITH_BE < %t.txt // RUN: %FileCheck %s -check-prefix=WITH_PA < %t.txt -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=CLASS_BE_PA_PE_EXT2 -code-completion-keywords=false > %t.txt +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=CLASS_BE_PA_PE_EXT2 -code-completion-keywords=false > %t.txt // RUN: %FileCheck %s -check-prefix=CLASS_BE_PA_PE < %t.txt // RUN: %FileCheck %s -check-prefix=WITH_BE < %t.txt // RUN: %FileCheck %s -check-prefix=WITH_PA < %t.txt -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=CLASS_PEI_PE -code-completion-keywords=false > %t.txt +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=CLASS_PEI_PE -code-completion-keywords=false > %t.txt // RUN: %FileCheck %s -check-prefix=CLASS_PEI_PE < %t.txt // RUN: %FileCheck %s -check-prefix=WITH_PEI < %t.txt -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=PROTOCOL_PA -code-completion-keywords=false > %t.txt +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=PROTOCOL_PA -code-completion-keywords=false > %t.txt // RUN: %FileCheck %s -check-prefix=PROTOCOL_PA < %t.txt -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=PROTOCOL_PA_EXT -code-completion-keywords=false > %t.txt +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=PROTOCOL_PA_EXT -code-completion-keywords=false > %t.txt // RUN: %FileCheck %s -check-prefix=PROTOCOL_PA_EXT < %t.txt -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=NESTED_NOMINAL -code-completion-keywords=false > %t.txt +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=NESTED_NOMINAL -code-completion-keywords=false > %t.txt // RUN: %FileCheck %s -check-prefix=NESTED_NOMINAL < %t.txt -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=NESTED_CLOSURE_1 -code-completion-keywords=false | %FileCheck %s -check-prefix=NESTED_CLOSURE_1 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=NESTED_CLOSURE_2 -code-completion-keywords=false | %FileCheck %s -check-prefix=NESTED_CLOSURE_2 +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=NESTED_CLOSURE_1 -code-completion-keywords=false | %FileCheck %s -check-prefix=NESTED_CLOSURE_1 +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=NESTED_CLOSURE_2 -code-completion-keywords=false | %FileCheck %s -check-prefix=NESTED_CLOSURE_2 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OMIT_KEYWORD1 -code-completion-keywords=false > %t.txt +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=OMIT_KEYWORD1 -code-completion-keywords=false > %t.txt // RUN: %FileCheck %s -check-prefix=OMIT_KEYWORD1< %t.txt -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OMIT_KEYWORD2 -code-completion-keywords=false > %t.txt +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=OMIT_KEYWORD2 -code-completion-keywords=false > %t.txt // RUN: %FileCheck %s -check-prefix=OMIT_KEYWORD2< %t.txt -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OMIT_KEYWORD3 -code-completion-keywords=false > %t.txt +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=OMIT_KEYWORD3 -code-completion-keywords=false > %t.txt // RUN: %FileCheck %s -check-prefix=OMIT_KEYWORD3< %t.txt -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OMIT_KEYWORD4 -code-completion-keywords=false | %FileCheck %s -check-prefix=OMIT_KEYWORD4 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OMIT_KEYWORD4_LET -code-completion-keywords=false | %FileCheck %s -check-prefix=OMIT_KEYWORD4 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OMIT_KEYWORD5 -code-completion-keywords=false | %FileCheck %s -check-prefix=OMIT_KEYWORD1 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OMIT_KEYWORD6 -code-completion-keywords=false | %FileCheck %s -check-prefix=OMIT_KEYWORD2 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OMIT_KEYWORD7 -code-completion-keywords=false | %FileCheck %s -check-prefix=OMIT_KEYWORD3 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OMIT_KEYWORD8 -code-completion-keywords=false | %FileCheck %s -check-prefix=OMIT_KEYWORD4 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OMIT_KEYWORD8_LET -code-completion-keywords=false | %FileCheck %s -check-prefix=OMIT_KEYWORD4 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OMIT_KEYWORD9 -code-completion-keywords=false | %FileCheck %s -check-prefix=OMIT_KEYWORD4 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OMIT_KEYWORD9_LET -code-completion-keywords=false | %FileCheck %s -check-prefix=OMIT_KEYWORD4 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OMIT_KEYWORD10 -code-completion-keywords=false | %FileCheck %s -check-prefix=WITH_PA_NO_PROTOFUNCA - -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=HAS_THROWING -code-completion-keywords=false | %FileCheck %s -check-prefix=HAS_THROWING -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=ASSOC_TYPE1 -code-completion-keywords=false | %FileCheck %s -check-prefix=ASSOC_TYPE1 - -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=DEPRECATED_1 -code-completion-keywords=false | %FileCheck %s -check-prefix=DEPRECATED_1 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=ESCAPING_1 -code-completion-keywords=false | %FileCheck %s -check-prefix=ESCAPING_1 - -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=MODIFIER1 -code-completion-keywords=false | %FileCheck %s -check-prefix=MODIFIER1 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=MODIFIER2 -code-completion-keywords=false | %FileCheck %s -check-prefix=MODIFIER2 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=MODIFIER3 -code-completion-keywords=false | %FileCheck %s -check-prefix=MODIFIER2 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=MODIFIER4 -code-completion-keywords=false | %FileCheck %s -check-prefix=MODIFIER4 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=MODIFIER5 -code-completion-keywords=false | %FileCheck %s -check-prefix=MODIFIER5 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=MODIFIER6 -code-completion-keywords=false | %FileCheck %s -check-prefix=MODIFIER6 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=MODIFIER7 -code-completion-keywords=false | %FileCheck %s -check-prefix=MODIFIER7 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=MODIFIER8 -code-completion-keywords=false | %FileCheck %s -check-prefix=MODIFIER8 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=MODIFIER9 -code-completion-keywords=false | %FileCheck %s -check-prefix=MODIFIER9 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=MODIFIER10 -code-completion-keywords=false | %FileCheck %s -check-prefix=MODIFIER6 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=MODIFIER11 -code-completion-keywords=false | %FileCheck %s -check-prefix=MODIFIER9 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=MODIFIER12 -code-completion-keywords=false | %FileCheck %s -check-prefix=MODIFIER9 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=MODIFIER13 -code-completion-keywords=false | %FileCheck %s -check-prefix=MODIFIER13 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=MODIFIER14 -code-completion-keywords=false | %FileCheck %s -check-prefix=MODIFIER9 - -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=PROTOINIT_NORM -code-completion-keywords=false | %FileCheck %s -check-prefix=PROTOINIT_NORM -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=PROTOINIT_FINAL -code-completion-keywords=false | %FileCheck %s -check-prefix=PROTOINIT_FINAL -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=PROTOINIT_STRUCT -code-completion-keywords=false | %FileCheck %s -check-prefix=PROTOINIT_STRUCT - -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=MISSING_ASSOC_1 -code-completion-keywords=false | %FileCheck %s -check-prefix=MISSING_ASSOC_1 +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=OMIT_KEYWORD4 -code-completion-keywords=false | %FileCheck %s -check-prefix=OMIT_KEYWORD4 +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=OMIT_KEYWORD4_LET -code-completion-keywords=false | %FileCheck %s -check-prefix=OMIT_KEYWORD4 +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=OMIT_KEYWORD5 -code-completion-keywords=false | %FileCheck %s -check-prefix=OMIT_KEYWORD1 +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=OMIT_KEYWORD6 -code-completion-keywords=false | %FileCheck %s -check-prefix=OMIT_KEYWORD2 +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=OMIT_KEYWORD7 -code-completion-keywords=false | %FileCheck %s -check-prefix=OMIT_KEYWORD3 +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=OMIT_KEYWORD8 -code-completion-keywords=false | %FileCheck %s -check-prefix=OMIT_KEYWORD4 +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=OMIT_KEYWORD8_LET -code-completion-keywords=false | %FileCheck %s -check-prefix=OMIT_KEYWORD4 +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=OMIT_KEYWORD9 -code-completion-keywords=false | %FileCheck %s -check-prefix=OMIT_KEYWORD4 +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=OMIT_KEYWORD9_LET -code-completion-keywords=false | %FileCheck %s -check-prefix=OMIT_KEYWORD4 +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=OMIT_KEYWORD10 -code-completion-keywords=false | %FileCheck %s -check-prefix=WITH_PA_NO_PROTOFUNCA + +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=HAS_THROWING -code-completion-keywords=false | %FileCheck %s -check-prefix=HAS_THROWING +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=ASSOC_TYPE1 -code-completion-keywords=false | %FileCheck %s -check-prefix=ASSOC_TYPE1 + +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=DEPRECATED_1 -code-completion-keywords=false | %FileCheck %s -check-prefix=DEPRECATED_1 +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=ESCAPING_1 -code-completion-keywords=false | %FileCheck %s -check-prefix=ESCAPING_1 + +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=MODIFIER1 -code-completion-keywords=false | %FileCheck %s -check-prefix=MODIFIER1 +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=MODIFIER2 -code-completion-keywords=false | %FileCheck %s -check-prefix=MODIFIER2 +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=MODIFIER3 -code-completion-keywords=false | %FileCheck %s -check-prefix=MODIFIER2 +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=MODIFIER4 -code-completion-keywords=false | %FileCheck %s -check-prefix=MODIFIER4 +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=MODIFIER5 -code-completion-keywords=false | %FileCheck %s -check-prefix=MODIFIER5 +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=MODIFIER6 -code-completion-keywords=false | %FileCheck %s -check-prefix=MODIFIER6 +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=MODIFIER7 -code-completion-keywords=false | %FileCheck %s -check-prefix=MODIFIER7 +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=MODIFIER8 -code-completion-keywords=false | %FileCheck %s -check-prefix=MODIFIER8 +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=MODIFIER9 -code-completion-keywords=false | %FileCheck %s -check-prefix=MODIFIER9 +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=MODIFIER10 -code-completion-keywords=false | %FileCheck %s -check-prefix=MODIFIER6 +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=MODIFIER11 -code-completion-keywords=false | %FileCheck %s -check-prefix=MODIFIER9 +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=MODIFIER12 -code-completion-keywords=false | %FileCheck %s -check-prefix=MODIFIER9 +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=MODIFIER13 -code-completion-keywords=false | %FileCheck %s -check-prefix=MODIFIER13 +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=MODIFIER14 -code-completion-keywords=false | %FileCheck %s -check-prefix=MODIFIER9 + +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=PROTOINIT_NORM -code-completion-keywords=false | %FileCheck %s -check-prefix=PROTOINIT_NORM +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=PROTOINIT_FINAL -code-completion-keywords=false | %FileCheck %s -check-prefix=PROTOINIT_FINAL +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=PROTOINIT_STRUCT -code-completion-keywords=false | %FileCheck %s -check-prefix=PROTOINIT_STRUCT + +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=MISSING_ASSOC_1 -code-completion-keywords=false | %FileCheck %s -check-prefix=MISSING_ASSOC_1 @objc diff --git a/test/IDE/complete_override_access_control_class.swift b/test/IDE/complete_override_access_control_class.swift index 7d50295cb7d0e..92a7f740374da 100644 --- a/test/IDE/complete_override_access_control_class.swift +++ b/test/IDE/complete_override_access_control_class.swift @@ -1,20 +1,20 @@ // RUN: sed -n -e '1,/NO_ERRORS_UP_TO_HERE$/ p' %s > %t_no_errors.swift -// RUN: %target-swift-frontend -typecheck -verify -disable-objc-attr-requires-foundation-module %t_no_errors.swift +// RUN: %target-swift-frontend -typecheck -verify -disable-objc-attr-requires-foundation-module -enable-objc-interop %t_no_errors.swift // -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TEST_PRIVATE_AD -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_PRIVATE_AD -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TEST_FILEPRIVATE_AD -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_PRIVATE_AD -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TEST_INTERNAL_AD -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_INTERNAL_AD -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TEST_PUBLIC_AD -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_PUBLIC_AD +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=TEST_PRIVATE_AD -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_PRIVATE_AD +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=TEST_FILEPRIVATE_AD -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_PRIVATE_AD +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=TEST_INTERNAL_AD -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_INTERNAL_AD +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=TEST_PUBLIC_AD -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_PUBLIC_AD // -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TEST_PRIVATE_BD -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_PRIVATE_BD -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TEST_FILEPRIVATE_BD -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_PRIVATE_BD -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TEST_INTERNAL_BD -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_INTERNAL_BD -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TEST_PUBLIC_BD -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_PUBLIC_BD +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=TEST_PRIVATE_BD -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_PRIVATE_BD +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=TEST_FILEPRIVATE_BD -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_PRIVATE_BD +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=TEST_INTERNAL_BD -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_INTERNAL_BD +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=TEST_PUBLIC_BD -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_PUBLIC_BD // -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TEST_PRIVATE_CD -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_PRIVATE_CD -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TEST_FILEPRIVATE_CD -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_PRIVATE_CD -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TEST_INTERNAL_CD -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_INTERNAL_CD -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TEST_PUBLIC_CD -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_PUBLIC_CD +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=TEST_PRIVATE_CD -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_PRIVATE_CD +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=TEST_FILEPRIVATE_CD -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_PRIVATE_CD +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=TEST_INTERNAL_CD -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_INTERNAL_CD +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=TEST_PUBLIC_CD -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_PUBLIC_CD @objc private class TagPA {} diff --git a/test/IDE/complete_override_access_control_protocol.swift b/test/IDE/complete_override_access_control_protocol.swift index 3a0ab9d0ec8d2..0cc1856b7921e 100644 --- a/test/IDE/complete_override_access_control_protocol.swift +++ b/test/IDE/complete_override_access_control_protocol.swift @@ -1,25 +1,25 @@ // RUN: sed -n -e '1,/NO_ERRORS_UP_TO_HERE$/ p' %s > %t_no_errors.swift -// RUN: %target-swift-frontend -typecheck -verify -disable-objc-attr-requires-foundation-module %t_no_errors.swift +// RUN: %target-swift-frontend -typecheck -verify -disable-objc-attr-requires-foundation-module -enable-objc-interop %t_no_errors.swift // -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TEST_PRIVATE_ABC -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_PRIVATE_ABC -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TEST_FILEPRIVATE_ABC -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_PRIVATE_ABC -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TEST_INTERNAL_ABC -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_INTERNAL_ABC -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TEST_PUBLIC_ABC -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_PUBLIC_ABC +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=TEST_PRIVATE_ABC -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_PRIVATE_ABC +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=TEST_FILEPRIVATE_ABC -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_PRIVATE_ABC +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=TEST_INTERNAL_ABC -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_INTERNAL_ABC +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=TEST_PUBLIC_ABC -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_PUBLIC_ABC // -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TEST_PRIVATE_DE -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_PRIVATE_DE -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TEST_FILEPRIVATE_DE -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_PRIVATE_DE -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TEST_INTERNAL_DE -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_INTERNAL_DE -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TEST_PUBLIC_DE -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_PUBLIC_DE +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=TEST_PRIVATE_DE -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_PRIVATE_DE +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=TEST_FILEPRIVATE_DE -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_PRIVATE_DE +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=TEST_INTERNAL_DE -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_INTERNAL_DE +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=TEST_PUBLIC_DE -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_PUBLIC_DE // -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TEST_PRIVATE_ED -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_PRIVATE_ED -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TEST_FILEPRIVATE_ED -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_PRIVATE_ED -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TEST_INTERNAL_ED -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_INTERNAL_ED -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TEST_PUBLIC_ED -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_PUBLIC_ED +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=TEST_PRIVATE_ED -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_PRIVATE_ED +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=TEST_FILEPRIVATE_ED -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_PRIVATE_ED +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=TEST_INTERNAL_ED -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_INTERNAL_ED +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=TEST_PUBLIC_ED -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_PUBLIC_ED // -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TEST_PRIVATE_EF -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_PRIVATE_EF -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TEST_FILEPRIVATE_EF -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_PRIVATE_EF -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TEST_INTERNAL_EF -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_INTERNAL_EF -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TEST_PUBLIC_EF -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_PUBLIC_EF +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=TEST_PRIVATE_EF -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_PRIVATE_EF +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=TEST_FILEPRIVATE_EF -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_PRIVATE_EF +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=TEST_INTERNAL_EF -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_INTERNAL_EF +// RUN: %target-swift-ide-test -enable-objc-interop -code-completion -source-filename %s -code-completion-token=TEST_PUBLIC_EF -code-completion-keywords=false | %FileCheck %s -check-prefix=TEST_PUBLIC_EF @objc private class TagPA {} diff --git a/test/IDE/complete_return.swift b/test/IDE/complete_return.swift index 82520862a952e..bc33e3a91adf5 100644 --- a/test/IDE/complete_return.swift +++ b/test/IDE/complete_return.swift @@ -54,6 +54,7 @@ func testReturnInt1() { func testReturnInt2(_ fooObject: FooStruct) { return fooObject.#^RETURN_INT_2^# // RETURN_INT_2: Begin completions +// RETURN_INT_2-NEXT: Keyword[self]/CurrNominal: self[#FooStruct#]; name=self // RETURN_INT_2-NEXT: Decl[InstanceVar]/CurrNominal: instanceVar[#Int#]{{; name=.+$}} // RETURN_INT_2-NEXT: End completions } diff --git a/test/IDE/complete_stdlib_optional.swift b/test/IDE/complete_stdlib_optional.swift index 6712c749c8ce8..22c12aaa1a003 100644 --- a/test/IDE/complete_stdlib_optional.swift +++ b/test/IDE/complete_stdlib_optional.swift @@ -135,9 +135,11 @@ class ObjcClass { // OBJCCLASS_MEMBERS_NO_DOT-NEXT: Decl[InstanceMethod]/CurrNominal: .instanceFunc()[#ObjcClass#] // OBJCCLASS_MEMBERS_NO_DOT-NEXT: Decl[InfixOperatorFunction]/OtherModule[Swift]: === {#AnyObject?#}[#Bool#] // OBJCCLASS_MEMBERS_NO_DOT-NEXT: Decl[InfixOperatorFunction]/OtherModule[Swift]: !== {#AnyObject?#}[#Bool#] +// OBJCCLASS_MEMBERS_NO_DOT-NEXT: Keyword[self]/CurrNominal: .self[#ObjcClass#]; name=self // OBJCCLASS_MEMBERS_NO_DOT-NEXT: End completions // OBJCCLASS_MEMBERS_DOT: Begin completions +// OBJCCLASS_MEMBERS_DOT-NEXT: Keyword[self]/CurrNominal: self[#ObjcClass#]; name=self // OBJCCLASS_MEMBERS_DOT-NEXT: Decl[InstanceVar]/CurrNominal: instanceVar[#Int#] // OBJCCLASS_MEMBERS_DOT-NEXT: Decl[InstanceMethod]/CurrNominal: instanceFunc()[#ObjcClass#] // OBJCCLASS_MEMBERS_DOT-NEXT: End completions diff --git a/test/IDE/complete_type.swift b/test/IDE/complete_type.swift index 1d652d581becf..b9a6677fd5f64 100644 --- a/test/IDE/complete_type.swift +++ b/test/IDE/complete_type.swift @@ -45,6 +45,14 @@ // RUN: %FileCheck %s -check-prefix=WITH_GLOBAL_TYPES < %t.types.txt // RUN: %FileCheck %s -check-prefix=GLOBAL_NEGATIVE < %t.types.txt +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TYPE_IN_RETURN_GEN_PARAM_NO_DUP > %t.types.txt +// RUN: %FileCheck %s -check-prefix=TYPE_IN_RETURN_GEN_PARAM_NO_DUP < %t.types.txt + +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TYPE_IVAR_GEN_PARAM_NO_DUP > %t.types.txt +// RUN: %FileCheck %s -check-prefix=TYPE_IVAR_GEN_PARAM_NO_DUP < %t.types.txt + +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TYPE_IN_SUBSCR_GEN_PARAM_NO_DUP > %t.types.txt +// RUN: %FileCheck %s -check-prefix=TYPE_IN_SUBSCR_GEN_PARAM_NO_DUP < %t.types.txt // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TYPE_IN_LOCAL_VAR_IN_FREE_FUNC_1 > %t.types.txt // RUN: %FileCheck %s -check-prefix=WITH_GLOBAL_TYPES < %t.types.txt @@ -573,6 +581,34 @@ struct TestTypeInConstructorParamGeneric3< // No tests for destructors: destructors don't have parameters. +//===--- +//===--- Test that we don't duplicate generic parameters. +//===--- + +struct GenericStruct { + func foo() -> #^TYPE_IN_RETURN_GEN_PARAM_NO_DUP^# +} +class A { + var foo: #^TYPE_IVAR_GEN_PARAM_NO_DUP^# + + subscript(_ arg: Int) -> #^TYPE_IN_SUBSCR_GEN_PARAM_NO_DUP^# +} + +// TYPE_IN_RETURN_GEN_PARAM_NO_DUP: Begin completions +// TYPE_IN_RETURN_GEN_PARAM_NO_DUP-DAG: Decl[GenericTypeParam]/CurrNominal: T[#T#]; name=T +// TYPE_IN_RETURN_GEN_PARAM_NO_DUP-NOT: Decl[GenericTypeParam]/Local: T[#T#]; name=T +// TYPE_IN_RETURN_GEN_PARAM_NO_DUP: End completions + +// TYPE_IVAR_GEN_PARAM_NO_DUP: Begin completions +// TYPE_IVAR_GEN_PARAM_NO_DUP-DAG: Decl[GenericTypeParam]/CurrNominal: T[#T#]; name=T +// TYPE_IVAR_GEN_PARAM_NO_DUP-NOT: Decl[GenericTypeParam]/Local: T[#T#]; name=T +// TYPE_IVAR_GEN_PARAM_NO_DUP: End completions + +// TYPE_IN_SUBSCR_GEN_PARAM_NO_DUP: Begin completions +// TYPE_IN_SUBSCR_GEN_PARAM_NO_DUP-DAG: Decl[GenericTypeParam]/CurrNominal: T[#T#]; name=T +// TYPE_IN_SUBSCR_GEN_PARAM_NO_DUP-NOT: Decl[GenericTypeParam]/Local: T[#T#]; name=T +// TYPE_IN_SUBSCR_GEN_PARAM_NO_DUP: End completions + //===--- //===--- Test that we can complete types in variable declarations. //===--- diff --git a/test/IDE/complete_type_subscript.swift b/test/IDE/complete_type_subscript.swift index 6a547f7c251eb..cb550d2cb275c 100644 --- a/test/IDE/complete_type_subscript.swift +++ b/test/IDE/complete_type_subscript.swift @@ -37,7 +37,7 @@ struct G0 { subscript(x: T) -> #^GEN_RETURN_0^# { return 0 } } // GEN_TOP_LEVEL_0: Keyword/None: Any[#Any#]; -// GEN_TOP_LEVEL_0: Decl[GenericTypeParam]/Local: T[#T#]; +// GEN_TOP_LEVEL_0: Decl[GenericTypeParam]/CurrNominal: T[#T#]; name=T // GEN_TOP_LEVEL_0: Decl[Struct]/CurrModule: S0[#S0#]; // GEN_TOP_LEVEL_0: Decl[Struct]/OtherModule[Swift]: Int[#Int#]; diff --git a/test/IDE/complete_unicode.swift b/test/IDE/complete_unicode.swift index c5228d555dd26..560dbee19b770 100644 --- a/test/IDE/complete_unicode.swift +++ b/test/IDE/complete_unicode.swift @@ -15,6 +15,7 @@ func unicode_test_1() { Unicode1().#^UNICODE_1^# } // UNICODE_1: Begin completions +// UNICODE_1-NEXT: Keyword[self]/CurrNominal: self[#Unicode1#]; name=self // UNICODE_1-NEXT: Decl[InstanceMethod]/CurrNominal: Идентификаторы_с_кириллицей_допустимы()[#Void#]{{; name=.+$}} // UNICODE_1-NEXT: Decl[InstanceMethod]/CurrNominal: Ідентіфікатори_українською_також_працюють()[#Void#]{{; name=.+$}} // UNICODE_1-NEXT: Decl[InstanceMethod]/CurrNominal: 識別子は()[#Void#]{{; name=.+$}} diff --git a/test/IDE/complete_value_expr.swift b/test/IDE/complete_value_expr.swift index 329537173017d..fbfbbdba49f2e 100644 --- a/test/IDE/complete_value_expr.swift +++ b/test/IDE/complete_value_expr.swift @@ -295,6 +295,7 @@ extension FooStruct { var fooObject: FooStruct // FOO_OBJECT_DOT: Begin completions +// FOO_OBJECT_DOT-NEXT: Keyword[self]/CurrNominal: self[#FooStruct#]; name=self // FOO_OBJECT_DOT-NEXT: Decl[InstanceVar]/CurrNominal: lazyInstanceVar[#Int#]{{; name=.+$}} // FOO_OBJECT_DOT-NEXT: Decl[InstanceVar]/CurrNominal: instanceVar[#Int#]{{; name=.+$}} // FOO_OBJECT_DOT-NEXT: Decl[InstanceMethod]/CurrNominal: instanceFunc0()[#Void#]{{; name=.+$}} @@ -361,9 +362,11 @@ var fooObject: FooStruct // FOO_OBJECT_NO_DOT-NEXT: Decl[InstanceVar]/CurrNominal: .extProp[#Int#]{{; name=.+$}} // FOO_OBJECT_NO_DOT-NEXT: Decl[InstanceMethod]/CurrNominal: .extFunc0()[#Void#]{{; name=.+$}} // FOO_OBJECT_NO_DOT-NEXT: BuiltinOperator/None: = {#Foo +// FOO_OBJECT_NO_DOT-NEXT: Keyword[self]/CurrNominal: .self[#FooStruct#]; name=self // FOO_OBJECT_NO_DOT-NEXT: End completions // FOO_STRUCT_DOT: Begin completions +// FOO_STRUCT_DOT-NEXT: Keyword[self]/CurrNominal: self[#FooStruct.Type#]; name=self // FOO_STRUCT_DOT-NEXT: Decl[InstanceMethod]/CurrNominal: instanceFunc0({#self: &FooStruct#})[#() -> Void#]{{; name=.+$}} // FOO_STRUCT_DOT-NEXT: Decl[InstanceMethod]/CurrNominal: instanceFunc1({#self: &FooStruct#})[#(Int) -> Void#]{{; name=.+$}} // FOO_STRUCT_DOT-NEXT: Decl[InstanceMethod]/CurrNominal: instanceFunc2({#self: &FooStruct#})[#(Int, b: inout Double) -> Void#]{{; name=.+$}} @@ -455,6 +458,7 @@ var fooObject: FooStruct // FOO_STRUCT_NO_DOT-NEXT: Decl[Class]/CurrNominal: .ExtNestedClass[#FooStruct.ExtNestedClass#]{{; name=.+$}} // FOO_STRUCT_NO_DOT-NEXT: Decl[Enum]/CurrNominal: .ExtNestedEnum[#FooStruct.ExtNestedEnum#]{{; name=.+$}} // FOO_STRUCT_NO_DOT-NEXT: Decl[TypeAlias]/CurrNominal: .ExtNestedTypealias[#Int#]{{; name=.+$}} +// FOO_STRUCT_NO_DOT-NEXT: Keyword[self]/CurrNominal: .self[#FooStruct.Type#]; name=self // FOO_STRUCT_NO_DOT-NEXT: End completions func testObjectExpr() { @@ -501,31 +505,37 @@ func testImplicitlyCurriedFunc(_ fs: inout FooStruct) { FooStruct.instanceFunc0(&fs)#^IMPLICITLY_CURRIED_FUNC_0^# // IMPLICITLY_CURRIED_FUNC_0: Begin completions // IMPLICITLY_CURRIED_FUNC_0-NEXT: Pattern/CurrModule: ()[#Void#]{{; name=.+$}} +// IMPLICITLY_CURRIED_FUNC_0-NEXT: Keyword[self]/CurrNominal: .self[#() -> ()#]; name=self // IMPLICITLY_CURRIED_FUNC_0-NEXT: End completions FooStruct.instanceFunc1(&fs)#^IMPLICITLY_CURRIED_FUNC_1^# // IMPLICITLY_CURRIED_FUNC_1: Begin completions // IMPLICITLY_CURRIED_FUNC_1-NEXT: Pattern/CurrModule: ({#(a): Int#})[#Void#]{{; name=.+$}} +// IMPLICITLY_CURRIED_FUNC_1-NEXT: Keyword[self]/CurrNominal: .self[#(Int) -> ()#]; name=self // IMPLICITLY_CURRIED_FUNC_1-NEXT: End completions FooStruct.instanceFunc2(&fs)#^IMPLICITLY_CURRIED_FUNC_2^# // IMPLICITLY_CURRIED_FUNC_2: Begin completions // IMPLICITLY_CURRIED_FUNC_2-NEXT: Pattern/CurrModule: ({#(a): Int#}, {#b: &Double#})[#Void#]{{; name=.+$}} +// IMPLICITLY_CURRIED_FUNC_2-NEXT: Keyword[self]/CurrNominal: .self[#(Int, inout Double) -> ()#]; name=self // IMPLICITLY_CURRIED_FUNC_2-NEXT: End completions FooStruct.varargInstanceFunc0(&fs)#^IMPLICITLY_CURRIED_VARARG_FUNC_0^# // IMPLICITLY_CURRIED_VARARG_FUNC_0: Begin completions // IMPLICITLY_CURRIED_VARARG_FUNC_0-NEXT: Pattern/CurrModule: ({#(v): Int...#})[#Void#]{{; name=.+$}} +// IMPLICITLY_CURRIED_VARARG_FUNC_0-NEXT: Keyword[self]/CurrNominal: .self[#(Int...) -> ()#]; name=self // IMPLICITLY_CURRIED_VARARG_FUNC_0-NEXT: End completions FooStruct.varargInstanceFunc1(&fs)#^IMPLICITLY_CURRIED_VARARG_FUNC_1^# // IMPLICITLY_CURRIED_VARARG_FUNC_1: Begin completions // IMPLICITLY_CURRIED_VARARG_FUNC_1-NEXT: Pattern/CurrModule: ({#(a): Float#}, {#v: Int...#})[#Void#]{{; name=.+$}} +// IMPLICITLY_CURRIED_VARARG_FUNC_1-NEXT: Keyword[self]/CurrNominal: .self[#(Float, Int...) -> ()#]; name=self // IMPLICITLY_CURRIED_VARARG_FUNC_1-NEXT: End completions FooStruct.varargInstanceFunc2(&fs)#^IMPLICITLY_CURRIED_VARARG_FUNC_2^# // IMPLICITLY_CURRIED_VARARG_FUNC_2: Begin completions // IMPLICITLY_CURRIED_VARARG_FUNC_2-NEXT: Pattern/CurrModule: ({#(a): Float#}, {#b: Double#}, {#v: Int...#})[#Void#]{{; name=.+$}} +// IMPLICITLY_CURRIED_VARARG_FUNC_2-NEXT: Keyword[self]/CurrNominal: .self[#(Float, Double, Int...) -> ()#]; name=self // IMPLICITLY_CURRIED_VARARG_FUNC_2-NEXT: End completions // This call is ambiguous, and the expression is invalid. @@ -798,12 +808,14 @@ func testFuncTypeVars() { // VF1: Begin completions // VF1-NEXT: Pattern/CurrModule: ()[#Double#]{{; name=.+$}} // VF1-NEXT: BuiltinOperator/None: = {#() -> Double##() -> Double#}[#Void#] +// VF1-NEXT: Keyword[self]/CurrNominal: .self[#() -> Double#]; name=self // VF1-NEXT: End completions funcTypeVarsObject.funcVar2#^VF2^# // VF2: Begin completions // VF2-NEXT: Pattern/CurrModule: ({#Int#})[#Double#]{{; name=.+$}} // VF2-NEXT: BuiltinOperator/None: = {#(Int) -> Double##(Int) -> Double#}[#Void#] +// VF2-NEXT: Keyword[self]/CurrNominal: .self[#(Int) -> Double#]; name=self // VF2-NEXT: End completions } @@ -825,6 +837,7 @@ var membersDerived: MembersDerived func testLookInBase() { membersDerived.#^BASE_MEMBERS^# // BASE_MEMBERS: Begin completions +// BASE_MEMBERS-DAG: Keyword[self]/CurrNominal: self[#MembersDerived#]; name=self // BASE_MEMBERS-NEXT: Decl[InstanceVar]/CurrNominal: derivedVar[#Int#]{{; name=.+$}} // BASE_MEMBERS-NEXT: Decl[InstanceMethod]/CurrNominal: derivedInstanceFunc()[#Void#]{{; name=.+$}} // BASE_MEMBERS-NEXT: Decl[InstanceVar]/Super: baseVar[#Int#]{{; name=.+$}} @@ -835,6 +848,7 @@ func testLookInBase() { func testLookInBaseStatic() { MembersDerived.#^BASE_MEMBERS_STATIC^# // BASE_MEMBERS_STATIC: Begin completions +// BASE_MEMBERS_STATIC-NEXT: Keyword[self]/CurrNominal: self[#MembersDerived.Type#]; name=self // BASE_MEMBERS_STATIC-NEXT: Decl[InstanceMethod]/CurrNominal: derivedInstanceFunc({#self: MembersDerived#})[#() -> Void#]{{; name=.+$}} // BASE_MEMBERS_STATIC-NEXT: Decl[StaticMethod]/CurrNominal: derivedStaticFunc()[#Void#]{{; name=.+$}} // BASE_MEMBERS_STATIC-NEXT: Decl[Constructor]/CurrNominal: init()[#MembersDerived#]; name=init(){{$}} @@ -853,6 +867,7 @@ func testLookInProtoNoDot1() { // PROTO_MEMBERS_NO_DOT_1-NEXT: Decl[InstanceMethod]/CurrNominal: .fooInstanceFunc0()[#Double#]{{; name=.+$}} // PROTO_MEMBERS_NO_DOT_1-NEXT: Decl[InstanceMethod]/CurrNominal: .fooInstanceFunc1({#(a): Int#})[#Double#]{{; name=.+$}} // PROTO_MEMBERS_NO_DOT_1-NEXT: Decl[Subscript]/CurrNominal: [{#Int#}][#Double#]{{; name=.+$}} +// PROTO_MEMBERS_NO_DOT_1-NEXT: Keyword[self]/CurrNominal: .self[#FooProtocol#]; name=self // PROTO_MEMBERS_NO_DOT_1-NEXT: End completions } @@ -867,6 +882,7 @@ func testLookInProtoNoDot2() { // PROTO_MEMBERS_NO_DOT_2-NEXT: Decl[InstanceMethod]/CurrNominal: .fooInstanceFunc0()[#Double#]{{; name=.+$}} // PROTO_MEMBERS_NO_DOT_2-NEXT: Decl[InstanceMethod]/CurrNominal: .fooInstanceFunc1({#(a): Int#})[#Double#]{{; name=.+$}} // PROTO_MEMBERS_NO_DOT_2-NEXT: Decl[Subscript]/CurrNominal: [{#Int#}][#Double#]{{; name=.+$}} +// PROTO_MEMBERS_NO_DOT_2-NEXT: Keyword[self]/CurrNominal: .self[#BarProtocol & FooProtocol#]; name=self // PROTO_MEMBERS_NO_DOT_2-NEXT: End completions } @@ -883,12 +899,14 @@ func testLookInProtoNoDot3() { // PROTO_MEMBERS_NO_DOT_3-NEXT: Decl[InstanceMethod]/Super: .fooInstanceFunc1({#(a): Int#})[#Double#]{{; name=.+$}} // PROTO_MEMBERS_NO_DOT_3-NEXT: Decl[Subscript]/Super: [{#Int#}][#Double#]{{; name=.+$}} // PROTO_MEMBERS_NO_DOT_3-NEXT: Decl[InstanceMethod]/CurrNominal: .fooExInstanceFunc0()[#Double#]{{; name=.+$}} +// PROTO_MEMBERS_NO_DOT_3-NEXT: Keyword[self]/CurrNominal: .self[#BarExProtocol & FooExProtocol#]; name=self // PROTO_MEMBERS_NO_DOT_3-NEXT: End completions } func testLookInProto1() { fooProtocolInstance.#^PROTO_MEMBERS_1^# // PROTO_MEMBERS_1: Begin completions +// PROTO_MEMBERS_1-NEXT: Keyword[self]/CurrNominal: self[#FooProtocol#]; name=self // PROTO_MEMBERS_1-NEXT: Decl[InstanceVar]/CurrNominal: fooInstanceVar1[#Int#]{{; name=.+$}} // PROTO_MEMBERS_1-NEXT: Decl[InstanceVar]/CurrNominal: fooInstanceVar2[#Int#]{{; name=.+$}} // PROTO_MEMBERS_1-NEXT: Decl[InstanceMethod]/CurrNominal: fooInstanceFunc0()[#Double#]{{; name=.+$}} @@ -899,6 +917,7 @@ func testLookInProto1() { func testLookInProto2() { fooBarProtocolInstance.#^PROTO_MEMBERS_2^# // PROTO_MEMBERS_2: Begin completions +// PROTO_MEMBERS_2-NEXT: Keyword[self]/CurrNominal: self[#BarProtocol & FooProtocol#]; name=self // PROTO_MEMBERS_2-NEXT: Decl[InstanceVar]/CurrNominal: barInstanceVar[#Int#]{{; name=.+$}} // PROTO_MEMBERS_2-NEXT: Decl[InstanceMethod]/CurrNominal: barInstanceFunc0()[#Double#]{{; name=.+$}} // PROTO_MEMBERS_2-NEXT: Decl[InstanceMethod]/CurrNominal: barInstanceFunc1({#(a): Int#})[#Double#]{{; name=.+$}} @@ -912,6 +931,7 @@ func testLookInProto2() { func testLookInProto3() { fooExBarExProtocolInstance.#^PROTO_MEMBERS_3^# // PROTO_MEMBERS_3: Begin completions +// PROTO_MEMBERS_3-NEXT: Keyword[self]/CurrNominal: self[#BarExProtocol & FooExProtocol#]; name=self // PROTO_MEMBERS_3-NEXT: Decl[InstanceVar]/Super: barInstanceVar[#Int#]{{; name=.+$}} // PROTO_MEMBERS_3-NEXT: Decl[InstanceMethod]/Super: barInstanceFunc0()[#Double#]{{; name=.+$}} // PROTO_MEMBERS_3-NEXT: Decl[InstanceMethod]/Super: barInstanceFunc1({#(a): Int#})[#Double#]{{; name=.+$}} @@ -948,6 +968,7 @@ class TestResolveFuncParam2 { func testResolveFuncParam3(_ foo: Foo) { foo.#^RESOLVE_FUNC_PARAM_3^# // RESOLVE_FUNC_PARAM_3: Begin completions +// RESOLVE_FUNC_PARAM_3-NEXT: Keyword[self]/CurrNominal: self[#Foo#]; name=self // RESOLVE_FUNC_PARAM_3-NEXT: Decl[InstanceVar]/Super: fooInstanceVar1[#Int#]{{; name=.+$}} // RESOLVE_FUNC_PARAM_3-NEXT: Decl[InstanceVar]/Super: fooInstanceVar2[#Int#]{{; name=.+$}} // RESOLVE_FUNC_PARAM_3-NEXT: Decl[InstanceMethod]/Super: fooInstanceFunc0()[#Double#]{{; name=.+$}} @@ -958,6 +979,7 @@ func testResolveFuncParam3(_ foo: Foo) { func testResolveFuncParam4(_ fooBar: FooBar) { fooBar.#^RESOLVE_FUNC_PARAM_4^# // RESOLVE_FUNC_PARAM_4: Begin completions +// RESOLVE_FUNC_PARAM_4-NEXT: Keyword[self]/CurrNominal: self[#FooBar#]; name=self // RESOLVE_FUNC_PARAM_4-NEXT: Decl[InstanceVar]/Super: barInstanceVar[#Int#]{{; name=.+$}} // RESOLVE_FUNC_PARAM_4-NEXT: Decl[InstanceMethod]/Super: barInstanceFunc0()[#Double#]{{; name=.+$}} // RESOLVE_FUNC_PARAM_4-NEXT: Decl[InstanceMethod]/Super: barInstanceFunc1({#(a): Int#})[#Double#]{{; name=.+$}} @@ -971,6 +993,7 @@ func testResolveFuncParam4(_ fooBar: FooBar) func testResolveFuncParam5(_ a: FooExBarEx) { a.#^RESOLVE_FUNC_PARAM_5^# // RESOLVE_FUNC_PARAM_5: Begin completions +// RESOLVE_FUNC_PARAM_5-NEXT: Keyword[self]/CurrNominal: self[#FooExBarEx#]; name=self // RESOLVE_FUNC_PARAM_5-NEXT: Decl[InstanceVar]/Super: barInstanceVar[#Int#]{{; name=.+$}} // RESOLVE_FUNC_PARAM_5-NEXT: Decl[InstanceMethod]/Super: barInstanceFunc0()[#Double#]{{; name=.+$}} // RESOLVE_FUNC_PARAM_5-NEXT: Decl[InstanceMethod]/Super: barInstanceFunc1({#(a): Int#})[#Double#]{{; name=.+$}} @@ -986,6 +1009,7 @@ func testResolveFuncParam5(_ a: FooE func testResolveFuncParam6(_ foo: Foo) { foo.#^RESOLVE_FUNC_PARAM_6^# // RESOLVE_FUNC_PARAM_6: Begin completions +// RESOLVE_FUNC_PARAM_6-NEXT: Keyword[self]/CurrNominal: self[#Foo#]; name=self // RESOLVE_FUNC_PARAM_6-NEXT: Decl[InstanceVar]/Super: fooInstanceVar1[#Int#]{{; name=.+$}} // RESOLVE_FUNC_PARAM_6-NEXT: Decl[InstanceVar]/Super: fooInstanceVar2[#Int#]{{; name=.+$}} // RESOLVE_FUNC_PARAM_6-NEXT: Decl[InstanceMethod]/Super: fooInstanceFunc0()[#Double#]{{; name=.+$}} @@ -1006,6 +1030,7 @@ class TestResolveConstructorParam2 { init(foo: Foo) { foo.#^RESOLVE_CONSTRUCTOR_PARAM_2^# // RESOLVE_CONSTRUCTOR_PARAM_2: Begin completions +// RESOLVE_CONSTRUCTOR_PARAM_2-NEXT: Keyword[self]/CurrNominal: self[#Foo#]; name=self // RESOLVE_CONSTRUCTOR_PARAM_2-NEXT: Decl[InstanceVar]/Super: fooInstanceVar1[#Int#]{{; name=.+$}} // RESOLVE_CONSTRUCTOR_PARAM_2-NEXT: Decl[InstanceVar]/Super: fooInstanceVar2[#Int#]{{; name=.+$}} // RESOLVE_CONSTRUCTOR_PARAM_2-NEXT: Decl[InstanceMethod]/Super: fooInstanceFunc0()[#Double#]{{; name=.+$}} @@ -1018,6 +1043,7 @@ class TestResolveConstructorParam3 { init(foo: Foo) { foo.#^RESOLVE_CONSTRUCTOR_PARAM_3^# // RESOLVE_CONSTRUCTOR_PARAM_3: Begin completions +// RESOLVE_CONSTRUCTOR_PARAM_3-NEXT: Keyword[self]/CurrNominal: self[#Foo#]; name=self // RESOLVE_CONSTRUCTOR_PARAM_3-NEXT: Decl[InstanceVar]/Super: fooInstanceVar1[#Int#]{{; name=.+$}} // RESOLVE_CONSTRUCTOR_PARAM_3-NEXT: Decl[InstanceVar]/Super: fooInstanceVar2[#Int#]{{; name=.+$}} // RESOLVE_CONSTRUCTOR_PARAM_3-NEXT: Decl[InstanceMethod]/Super: fooInstanceFunc0()[#Double#]{{; name=.+$}} @@ -1047,6 +1073,7 @@ func testFuncParenPattern1(_ fpp: FuncParenPattern) { // FUNC_PAREN_PATTERN_1: Begin completions // FUNC_PAREN_PATTERN_1-NEXT: Decl[InstanceMethod]/CurrNominal: .instanceFunc({#Int#})[#Void#]{{; name=.+$}} // FUNC_PAREN_PATTERN_1-NEXT: Decl[Subscript]/CurrNominal: [{#Int#}][#Int#]{{; name=.+$}} +// FUNC_PAREN_PATTERN_1-NEXT: Keyword[self]/CurrNominal: .self[#FuncParenPattern#]; name=self // FUNC_PAREN_PATTERN_1-NEXT: End completions } @@ -1056,6 +1083,7 @@ func testFuncParenPattern2(_ fpp: FuncParenPattern) { // FUNC_PAREN_PATTERN_2-NEXT: Decl[Constructor]/CurrNominal: ({#Int#})[#FuncParenPattern#]{{; name=.+$}} // FUNC_PAREN_PATTERN_2-NEXT: Decl[Constructor]/CurrNominal: ({#(Int, Int)#})[#FuncParenPattern#]{{; name=.+$}} // FUNC_PAREN_PATTERN_2-NEXT: Decl[InstanceMethod]/CurrNominal: .instanceFunc({#self: &FuncParenPattern#})[#(Int) -> Void#]{{; name=.+$}} +// FUNC_PAREN_PATTERN_2-NEXT: Keyword[self]/CurrNominal: .self[#FuncParenPattern.Type#]; name=self // FUNC_PAREN_PATTERN_2-NEXT: End completions } @@ -1063,6 +1091,7 @@ func testFuncParenPattern3(_ fpp: inout FuncParenPattern) { fpp.instanceFunc#^FUNC_PAREN_PATTERN_3^# // FUNC_PAREN_PATTERN_3: Begin completions // FUNC_PAREN_PATTERN_3-NEXT: Pattern/CurrModule: ({#Int#})[#Void#]{{; name=.+$}} +// FUNC_PAREN_PATTERN_3-NEXT: Keyword[self]/CurrNominal: .self[#(Int) -> ()#]; name=self // FUNC_PAREN_PATTERN_3-NEXT: End completions } @@ -1081,6 +1110,7 @@ func testChainedCalls1() { // CHAINED_CALLS_1-DAG: Decl[InstanceMethod]/CurrNominal: .doFoo()[#SomeBuilder#]{{; name=.+$}} // CHAINED_CALLS_1-DAG: Decl[InstanceMethod]/CurrNominal: .doBar()[#SomeBuilder#]{{; name=.+$}} // CHAINED_CALLS_1-DAG: Decl[InstanceMethod]/CurrNominal: .doBaz({#(z): Double#})[#SomeBuilder#]{{; name=.+$}} +// CHAINED_CALLS_1-DAG: Keyword[self]/CurrNominal: .self[#SomeBuilder#]; name=self // CHAINED_CALLS_1: End completions } @@ -1090,6 +1120,7 @@ func testChainedCalls2() { // CHAINED_CALLS_2-DAG: Decl[InstanceMethod]/CurrNominal: .doFoo()[#SomeBuilder#]{{; name=.+$}} // CHAINED_CALLS_2-DAG: Decl[InstanceMethod]/CurrNominal: .doBar()[#SomeBuilder#]{{; name=.+$}} // CHAINED_CALLS_2-DAG: Decl[InstanceMethod]/CurrNominal: .doBaz({#(z): Double#})[#SomeBuilder#]{{; name=.+$}} +// CHAINED_CALLS_2-DAG: Keyword[self]/CurrNominal: .self[#SomeBuilder#]; name=self // CHAINED_CALLS_2: End completions } @@ -1100,6 +1131,7 @@ func testChainedCalls3() { // CHAINED_CALLS_3-DAG: Decl[InstanceMethod]/CurrNominal: .doFoo()[#SomeBuilder#]{{; name=.+$}} // CHAINED_CALLS_3-DAG: Decl[InstanceMethod]/CurrNominal: .doBar()[#SomeBuilder#]{{; name=.+$}} // CHAINED_CALLS_3-DAG: Decl[InstanceMethod]/CurrNominal: .doBaz({#z: Double#})[#SomeBuilder#]{{; name=.+$}} +// CHAINED_CALLS_3-DAG: Keyword[self]/CurrNominal: .self[#SomeBuilder#]; name=self // CHAINED_CALLS_3: End completions } @@ -1113,6 +1145,7 @@ func testResolveGenericParams1() { // RESOLVE_GENERIC_PARAMS_1-NEXT: Decl[InstanceMethod]/CurrNominal: .fooVoidInstanceFunc1({#(a): FooStruct#})[#Void#]{{; name=.+$}} // RESOLVE_GENERIC_PARAMS_1-NEXT: Decl[InstanceMethod]/CurrNominal: .fooTInstanceFunc1({#(a): FooStruct#})[#FooStruct#]{{; name=.+$}} // RESOLVE_GENERIC_PARAMS_1-NEXT: Decl[InstanceMethod]/CurrNominal: .fooUInstanceFunc1({#(a): U#})[#U#]{{; name=.+$}} +// RESOLVE_GENERIC_PARAMS_1-NEXT: Keyword[self]/CurrNominal: .self[#FooGenericStruct#]; name=self // RESOLVE_GENERIC_PARAMS_1-NEXT: End completions FooGenericStruct#^RESOLVE_GENERIC_PARAMS_1_STATIC^# @@ -1127,6 +1160,7 @@ func testResolveGenericParams1() { // RESOLVE_GENERIC_PARAMS_1_STATIC-NEXT: Decl[StaticMethod]/CurrNominal: .fooVoidStaticFunc1({#(a): FooStruct#})[#Void#]{{; name=.+$}} // RESOLVE_GENERIC_PARAMS_1_STATIC-NEXT: Decl[StaticMethod]/CurrNominal: .fooTStaticFunc1({#(a): FooStruct#})[#FooStruct#]{{; name=.+$}} // RESOLVE_GENERIC_PARAMS_1_STATIC-NEXT: Decl[StaticMethod]/CurrNominal: .fooUInstanceFunc1({#(a): U#})[#U#]{{; name=.+$}} +// RESOLVE_GENERIC_PARAMS_1_STATIC-NEXT: Keyword[self]/CurrNominal: .self[#FooGenericStruct.Type#]; name=self // RESOLVE_GENERIC_PARAMS_1_STATIC-NEXT: End completions } @@ -1138,6 +1172,7 @@ func testResolveGenericParams2(_ foo: Foo) { // RESOLVE_GENERIC_PARAMS_2-NEXT: Decl[InstanceMethod]/CurrNominal: .fooVoidInstanceFunc1({#(a): FooProtocol#})[#Void#]{{; name=.+$}} // RESOLVE_GENERIC_PARAMS_2-NEXT: Decl[InstanceMethod]/CurrNominal: .fooTInstanceFunc1({#(a): FooProtocol#})[#FooProtocol#]{{; name=.+$}} // RESOLVE_GENERIC_PARAMS_2-NEXT: Decl[InstanceMethod]/CurrNominal: .fooUInstanceFunc1({#(a): U#})[#U#]{{; name=.+$}} +// RESOLVE_GENERIC_PARAMS_2-NEXT: Keyword[self]/CurrNominal: .self[#FooGenericStruct#]; name=self // RESOLVE_GENERIC_PARAMS_2-NEXT: End completions FooGenericStruct#^RESOLVE_GENERIC_PARAMS_2_STATIC^# @@ -1152,6 +1187,7 @@ func testResolveGenericParams2(_ foo: Foo) { // RESOLVE_GENERIC_PARAMS_2_STATIC-NEXT: Decl[StaticMethod]/CurrNominal: .fooVoidStaticFunc1({#(a): FooProtocol#})[#Void#]{{; name=.+$}} // RESOLVE_GENERIC_PARAMS_2_STATIC-NEXT: Decl[StaticMethod]/CurrNominal: .fooTStaticFunc1({#(a): FooProtocol#})[#FooProtocol#]{{; name=.+$}} // RESOLVE_GENERIC_PARAMS_2_STATIC-NEXT: Decl[StaticMethod]/CurrNominal: .fooUInstanceFunc1({#(a): U#})[#U#]{{; name=.+$}} +// RESOLVE_GENERIC_PARAMS_2_STATIC-NEXT: Keyword[self]/CurrNominal: .self[#FooGenericStruct.Type#]; name=self // RESOLVE_GENERIC_PARAMS_2_STATIC-NEXT: End completions } @@ -1164,10 +1200,11 @@ struct TestResolveGenericParams3_4 { // RESOLVE_GENERIC_PARAMS_3-NEXT: Decl[InstanceMethod]/CurrNominal: .fooVoidInstanceFunc1({#(a): FooStruct#})[#Void#]{{; name=.+$}} // RESOLVE_GENERIC_PARAMS_3-NEXT: Decl[InstanceMethod]/CurrNominal: .fooTInstanceFunc1({#(a): FooStruct#})[#FooStruct#]{{; name=.+$}} // RESOLVE_GENERIC_PARAMS_3-NEXT: Decl[InstanceMethod]/CurrNominal: .fooUInstanceFunc1({#(a): U#})[#U#]{{; name=.+$}} +// RESOLVE_GENERIC_PARAMS_3-NEXT: Keyword[self]/CurrNominal: .self[#FooGenericStruct#]; name=self // RESOLVE_GENERIC_PARAMS_3-NEXT: End completions FooGenericStruct#^RESOLVE_GENERIC_PARAMS_3_STATIC^# -// RESOLVE_GENERIC_PARAMS_3_STATIC: Begin completions, 10 items +// RESOLVE_GENERIC_PARAMS_3_STATIC: Begin completions, 11 items // RESOLVE_GENERIC_PARAMS_3_STATIC-NEXT: Decl[Constructor]/CurrNominal: ()[#FooGenericStruct#]; name=() // RESOLVE_GENERIC_PARAMS_3_STATIC-NEXT: Decl[Constructor]/CurrNominal: ({#t: FooStruct#})[#FooGenericStruct#]{{; name=.+$}} // RESOLVE_GENERIC_PARAMS_3_STATIC-NEXT: Decl[InstanceMethod]/CurrNominal: .fooVoidInstanceFunc1({#self: &FooGenericStruct#})[#(FooStruct) -> Void#]{{; name=.+$}} @@ -1178,6 +1215,7 @@ struct TestResolveGenericParams3_4 { // RESOLVE_GENERIC_PARAMS_3_STATIC-NEXT: Decl[StaticMethod]/CurrNominal: .fooVoidStaticFunc1({#(a): FooStruct#})[#Void#]{{; name=.+$}} // RESOLVE_GENERIC_PARAMS_3_STATIC-NEXT: Decl[StaticMethod]/CurrNominal: .fooTStaticFunc1({#(a): FooStruct#})[#FooStruct#]{{; name=.+$}} // RESOLVE_GENERIC_PARAMS_3_STATIC-NEXT: Decl[StaticMethod]/CurrNominal: .fooUInstanceFunc1({#(a): U#})[#U#]{{; name=.+$}} +// RESOLVE_GENERIC_PARAMS_3_STATIC-NEXT: Keyword[self]/CurrNominal: .self[#FooGenericStruct.Type#]; name=self // RESOLVE_GENERIC_PARAMS_3_STATIC-NEXT: End completions } @@ -1189,6 +1227,7 @@ struct TestResolveGenericParams3_4 { // RESOLVE_GENERIC_PARAMS_4-NEXT: Decl[InstanceMethod]/CurrNominal: .fooVoidInstanceFunc1({#(a): T#})[#Void#]{{; name=.+$}} // RESOLVE_GENERIC_PARAMS_4-NEXT: Decl[InstanceMethod]/CurrNominal: .fooTInstanceFunc1({#(a): T#})[#T#]{{; name=.+$}} // RESOLVE_GENERIC_PARAMS_4-NEXT: Decl[InstanceMethod]/CurrNominal: .fooUInstanceFunc1({#(a): U#})[#U#]{{; name=.+$}} +// RESOLVE_GENERIC_PARAMS_4-NEXT: Keyword[self]/CurrNominal: .self[#FooGenericStruct#]; name=self // RESOLVE_GENERIC_PARAMS_4-NEXT: End completions FooGenericStruct#^RESOLVE_GENERIC_PARAMS_4_STATIC^# @@ -1203,6 +1242,7 @@ struct TestResolveGenericParams3_4 { // RESOLVE_GENERIC_PARAMS_4_STATIC-NEXT: Decl[StaticMethod]/CurrNominal: .fooVoidStaticFunc1({#(a): T#})[#Void#]{{; name=.+$}} // RESOLVE_GENERIC_PARAMS_4_STATIC-NEXT: Decl[StaticMethod]/CurrNominal: .fooTStaticFunc1({#(a): T#})[#T#]{{; name=.+$}} // RESOLVE_GENERIC_PARAMS_4_STATIC-NEXT: Decl[StaticMethod]/CurrNominal: .fooUInstanceFunc1({#(a): U#})[#U#]{{; name=.+$}} +// RESOLVE_GENERIC_PARAMS_4_STATIC-NEXT: Keyword[self]/CurrNominal: .self[#FooGenericStruct.Type#]; name=self // RESOLVE_GENERIC_PARAMS_4_STATIC-NEXT: End completions } @@ -1214,6 +1254,7 @@ struct TestResolveGenericParams3_4 { // RESOLVE_GENERIC_PARAMS_5-NEXT: Decl[InstanceMethod]/CurrNominal: .fooVoidInstanceFunc1({#(a): U#})[#Void#]{{; name=.+$}} // RESOLVE_GENERIC_PARAMS_5-NEXT: Decl[InstanceMethod]/CurrNominal: .fooTInstanceFunc1({#(a): U#})[#U#]{{; name=.+$}} // RESOLVE_GENERIC_PARAMS_5-NEXT: Decl[InstanceMethod]/CurrNominal: .fooUInstanceFunc1({#(a): U#})[#U#]{{; name=.+$}} +// RESOLVE_GENERIC_PARAMS_5-NEXT: Keyword[self]/CurrNominal: .self[#FooGenericStruct#]; name=self // RESOLVE_GENERIC_PARAMS_5-NEXT: End completions FooGenericStruct#^RESOLVE_GENERIC_PARAMS_5_STATIC^# @@ -1228,6 +1269,7 @@ struct TestResolveGenericParams3_4 { // RESOLVE_GENERIC_PARAMS_5_STATIC-NEXT: Decl[StaticMethod]/CurrNominal: .fooVoidStaticFunc1({#(a): U#})[#Void#]{{; name=.+$}} // RESOLVE_GENERIC_PARAMS_5_STATIC-NEXT: Decl[StaticMethod]/CurrNominal: .fooTStaticFunc1({#(a): U#})[#U#]{{; name=.+$}} // RESOLVE_GENERIC_PARAMS_5_STATIC-NEXT: Decl[StaticMethod]/CurrNominal: .fooUInstanceFunc1({#(a): U#})[#U#]{{; name=.+$}} +// RESOLVE_GENERIC_PARAMS_5_STATIC-NEXT: Keyword[self]/CurrNominal: .self[#FooGenericStruct.Type#]; name=self // RESOLVE_GENERIC_PARAMS_5_STATIC-NEXT: End completions } } @@ -1263,6 +1305,7 @@ func testTypeCheckWithUnsolvedVariables1() { BuilderStyle().#^TC_UNSOLVED_VARIABLES_1^# } // TC_UNSOLVED_VARIABLES_1: Begin completions +// TC_UNSOLVED_VARIABLES_1-NEXT: Keyword[self]/CurrNominal: self[#BuilderStyle<_>#]; name=self // TC_UNSOLVED_VARIABLES_1-NEXT: Decl[InstanceVar]/CurrNominal: count[#Int#]{{; name=.+$}} // TC_UNSOLVED_VARIABLES_1-NEXT: Decl[InstanceMethod]/CurrNominal: addString({#(s): String#})[#BuilderStyle<_>#]{{; name=.+$}} // TC_UNSOLVED_VARIABLES_1-NEXT: Decl[InstanceMethod]/CurrNominal: add({#(t): _#})[#BuilderStyle<_>#]{{; name=.+$}} @@ -1273,6 +1316,7 @@ func testTypeCheckWithUnsolvedVariables2() { BuilderStyle().addString("abc").#^TC_UNSOLVED_VARIABLES_2^# } // TC_UNSOLVED_VARIABLES_2: Begin completions +// TC_UNSOLVED_VARIABLES_2-NEXT: Keyword[self]/CurrNominal: self[#BuilderStyle<_>#]; name=self // TC_UNSOLVED_VARIABLES_2-NEXT: Decl[InstanceVar]/CurrNominal: count[#Int#]{{; name=.+$}} // TC_UNSOLVED_VARIABLES_2-NEXT: Decl[InstanceMethod]/CurrNominal: addString({#(s): String#})[#BuilderStyle<_>#]{{; name=.+$}} // TC_UNSOLVED_VARIABLES_2-NEXT: Decl[InstanceMethod]/CurrNominal: add({#(t): _#})[#BuilderStyle<_>#]{{; name=.+$}} @@ -1283,6 +1327,7 @@ func testTypeCheckWithUnsolvedVariables3() { BuilderStyle().addString("abc").add(42).#^TC_UNSOLVED_VARIABLES_3^# } // TC_UNSOLVED_VARIABLES_3: Begin completions +// TC_UNSOLVED_VARIABLES_3-NEXT: Keyword[self]/CurrNominal: self[#BuilderStyle#]; name=self // TC_UNSOLVED_VARIABLES_3-NEXT: Decl[InstanceVar]/CurrNominal: count[#Int#]{{; name=.+$}} // TC_UNSOLVED_VARIABLES_3-NEXT: Decl[InstanceMethod]/CurrNominal: addString({#(s): String#})[#BuilderStyle#]{{; name=.+$}} // TC_UNSOLVED_VARIABLES_3-NEXT: Decl[InstanceMethod]/CurrNominal: add({#(t): Int#})[#BuilderStyle#]{{; name=.+$}} @@ -1648,26 +1693,29 @@ struct dedupS : dedupP { func testDeDuped(_ x: dedupS) { x#^PROTOCOL_EXT_DEDUP_1^# // FIXME: Should produce 3 items (?) -// PROTOCOL_EXT_DEDUP_1: Begin completions, 6 items +// PROTOCOL_EXT_DEDUP_1: Begin completions, 7 items // PROTOCOL_EXT_DEDUP_1: Decl[InstanceMethod]/CurrNominal: .foo()[#Int#]; name=foo() // PROTOCOL_EXT_DEDUP_1: Decl[InstanceVar]/CurrNominal: .bar[#Int#]; name=bar // PROTOCOL_EXT_DEDUP_1: Decl[Subscript]/CurrNominal: [{#Int#}][#Int#]; name=[Int] +// PROTOCOL_EXT_DEDUP_1: Keyword[self]/CurrNominal: .self[#dedupS#]; name=self // PROTOCOL_EXT_DEDUP_1: End completions } func testDeDuped2(_ x: dedupP) { x#^PROTOCOL_EXT_DEDUP_2^# -// PROTOCOL_EXT_DEDUP_2: Begin completions, 3 items +// PROTOCOL_EXT_DEDUP_2: Begin completions, 4 items // PROTOCOL_EXT_DEDUP_2: Decl[InstanceMethod]/CurrNominal: .foo()[#dedupP.T#]; name=foo() // PROTOCOL_EXT_DEDUP_2: Decl[InstanceVar]/CurrNominal: .bar[#dedupP.T#]; name=bar // PROTOCOL_EXT_DEDUP_2: Decl[Subscript]/CurrNominal: [{#Self.T#}][#Self.T#]; name=[Self.T] +// PROTOCOL_EXT_DEDUP_2: Keyword[self]/CurrNominal: .self[#dedupP#]; name=self // PROTOCOL_EXT_DEDUP_2: End completions } func testDeDuped3(_ x: T) { x#^PROTOCOL_EXT_DEDUP_3^# -// PROTOCOL_EXT_DEDUP_3: Begin completions, 3 items +// PROTOCOL_EXT_DEDUP_3: Begin completions, 4 items // PROTOCOL_EXT_DEDUP_3: Decl[InstanceMethod]/Super: .foo()[#Int#]; name=foo() // PROTOCOL_EXT_DEDUP_3: Decl[InstanceVar]/Super: .bar[#Int#]; name=bar // PROTOCOL_EXT_DEDUP_3: Decl[Subscript]/Super: [{#Self.T#}][#Self.T#]; name=[Self.T] +// PROTOCOL_EXT_DEDUP_3: Keyword[self]/CurrNominal: .self[#T#]; name=self // PROTOCOL_EXT_DEDUP_3: End completions } @@ -1778,6 +1826,7 @@ class TestDotExprWithNonNominal { func test1() { let person = Person(firstName: otherField.#^DOT_EXPR_NON_NOMINAL_1^#) // DOT_EXPR_NON_NOMINAL_1-NOT: Instance +// DOT_EXPR_NON_NOMINAL_1: Keyword[self]/CurrNominal: self[#Other#]; name=self // DOT_EXPR_NON_NOMINAL_1: Decl[InstanceVar]/CurrNominal: nameFromOther[#Int#]; // DOT_EXPR_NON_NOMINAL_1-NOT: Instance } @@ -1785,6 +1834,7 @@ class TestDotExprWithNonNominal { let person = Person(firstName: 1.#^DOT_EXPR_NON_NOMINAL_2^#) // DOT_EXPR_NON_NOMINAL_2-NOT: otherField // DOT_EXPR_NON_NOMINAL_2-NOT: firstName +// DOT_EXPR_NON_NOMINAL_2: Keyword[self]/CurrNominal: self[#Int#]; name=self // DOT_EXPR_NON_NOMINAL_2: Decl[InstanceVar]/CurrNominal: hashValue[#Int#]; // DOT_EXPR_NON_NOMINAL_2-NOT: otherField // DOT_EXPR_NON_NOMINAL_2-NOT: firstName diff --git a/test/IDE/complete_where_clause.swift b/test/IDE/complete_where_clause.swift index e10077a8a1ffb..499e84dafbef2 100644 --- a/test/IDE/complete_where_clause.swift +++ b/test/IDE/complete_where_clause.swift @@ -17,13 +17,13 @@ // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=INIT_2 | %FileCheck %s -check-prefix=GEN_T_DOT // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=ALIAS_1 | %FileCheck %s -check-prefix=GEN_T // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=ALIAS_2 | %FileCheck %s -check-prefix=GEN_T_DOT -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=STRUCT_1 | %FileCheck %s -check-prefix=GEN_T +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=STRUCT_1 | %FileCheck %s -check-prefix=GEN_T_NOMINAL // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=STRUCT_2 | %FileCheck %s -check-prefix=GEN_T_DOT -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=STRUCT_3 | %FileCheck %s -check-prefix=GEN_T +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=STRUCT_3 | %FileCheck %s -check-prefix=GEN_T_NOMINAL // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=STRUCT_4 | %FileCheck %s -check-prefix=GEN_T_DOT -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=CLASS_1 | %FileCheck %s -check-prefix=GEN_T +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=CLASS_1 | %FileCheck %s -check-prefix=GEN_T_NOMINAL // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=CLASS_2 | %FileCheck %s -check-prefix=GEN_T_DOT -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=ENUM_1 | %FileCheck %s -check-prefix=GEN_T +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=ENUM_1 | %FileCheck %s -check-prefix=GEN_T_NOMINAL // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=ENUM_2 | %FileCheck %s -check-prefix=GEN_T_DOT // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=ASSOC_1 | %FileCheck %s -check-prefix=P2 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=ASSOC_2 | %FileCheck %s -check-prefix=U_DOT @@ -68,7 +68,7 @@ protocol Assoc { } func f1(_: T) where #^FUNC_1^# {} -// GEN_T: Decl[GenericTypeParam]/Local: T[#T#]; +// GEN_T: Decl[GenericTypeParam]/Local: T[#T#]; name=T func f2(_: T) where T.#^FUNC_2^# {} // GEN_T_DOT: Begin completions // GEN_T_DOT-DAG: Keyword/None: Type[#T.Type#]; @@ -101,6 +101,7 @@ class C1 where #^CLASS_1^# {} class C2 where T.#^CLASS_2^# {} enum E1 where #^ENUM_1^# {} enum E2 where T.#^ENUM_2^# {} +// GEN_T_NOMINAL: Decl[GenericTypeParam]/CurrNominal: T[#T#]; name=T protocol P2 { associatedtype T where #^ASSOC_1^# diff --git a/test/IDE/complete_with_closure_param.swift b/test/IDE/complete_with_closure_param.swift index 43f86bdbc2451..4e144b9e5ec5f 100644 --- a/test/IDE/complete_with_closure_param.swift +++ b/test/IDE/complete_with_closure_param.swift @@ -15,7 +15,7 @@ struct FooStruct { FooStruct().#^COMPLETE^# -// CHECK: Begin completions, 7 items +// CHECK: Begin completions, 8 items // CHECK-DAG: Decl[InstanceMethod]/CurrNominal: instanceMethod1({#(callback): (Int, Int) -> Void##(Int, Int) -> Void#})[#Void#]; name=instanceMethod1(callback: (Int, Int) -> Void){{$}} // CHECK-DAG: Decl[InstanceMethod]/CurrNominal: instanceMethod2({#(callback): ((Int, Int) -> Void)?##(Int, Int) -> Void#})[#Void#]{{; name=.+$}} // CHECK-DAG: Decl[InstanceMethod]/CurrNominal: instanceMethod3({#(callback): ((Int, Int) -> Void)??##(Int, Int) -> Void#})[#Void#]{{; name=.+$}} @@ -23,4 +23,5 @@ FooStruct().#^COMPLETE^# // CHECK-DAG: Decl[InstanceMethod]/CurrNominal: instanceMethod5({#(callback): (Int, Int) -> Bool##(Int, Int) -> Bool#})[#Void#]{{; name=.+$}} // CHECK-DAG: Decl[InstanceMethod]/CurrNominal: instanceMethod6({#(callback): FunctionTypealias?##(Int, Int) -> Bool#})[#Void#]{{; name=.+$}} // CHECK-DAG: Decl[InstanceMethod]/CurrNominal: instanceMethod7({#(callback): OptionalFunctionTypealias##(Int, Int) -> Bool#})[#Void#]{{; name=.+$}} +// CHECK-DAG: Keyword[self]/CurrNominal: self[#FooStruct#]; name=self // CHECK: End completions diff --git a/test/IDE/print_ast_tc_decls.swift b/test/IDE/print_ast_tc_decls.swift index 11b19f17bd11b..8d031d24b52ee 100644 --- a/test/IDE/print_ast_tc_decls.swift +++ b/test/IDE/print_ast_tc_decls.swift @@ -192,8 +192,8 @@ struct d0100_FooStruct { class NestedClass {} // PASS_COMMON-NEXT: {{^}} class NestedClass {{{$}} -// PASS_COMMON-NEXT: {{^}} @objc deinit{{$}} // PASS_COMMON-NEXT: {{^}} init(){{$}} +// PASS_COMMON-NEXT: {{^}} @objc deinit{{$}} // PASS_COMMON-NEXT: {{^}} }{{$}} enum NestedEnum {} @@ -268,8 +268,8 @@ extension d0100_FooStruct { class ExtNestedClass {} // PASS_COMMON-NEXT: {{^}} class ExtNestedClass {{{$}} -// PASS_COMMON-NEXT: {{^}} @objc deinit{{$}} // PASS_COMMON-NEXT: {{^}} init(){{$}} +// PASS_COMMON-NEXT: {{^}} @objc deinit{{$}} // PASS_COMMON-NEXT: {{^}} }{{$}} enum ExtNestedEnum { @@ -587,12 +587,13 @@ struct d0200_EscapedIdentifiers { // PASS_COMMON-NEXT: {{^}} case `case`{{$}} // PASS_COMMON-NEXT: {{^}} {{.*}}static func __derived_enum_equals(_ a: d0200_EscapedIdentifiers.`enum`, _ b: d0200_EscapedIdentifiers.`enum`) -> Bool // PASS_COMMON-NEXT: {{^}} var hashValue: Int { get }{{$}} +// PASS_COMMON-NEXT: {{^}} func hash(into hasher: inout Hasher) // PASS_COMMON-NEXT: {{^}} }{{$}} class `class` {} // PASS_COMMON-NEXT: {{^}} class `class` {{{$}} -// PASS_COMMON-NEXT: {{^}} @objc deinit{{$}} // PASS_COMMON-NEXT: {{^}} init(){{$}} +// PASS_COMMON-NEXT: {{^}} @objc deinit{{$}} // PASS_COMMON-NEXT: {{^}} }{{$}} typealias `protocol` = `class` @@ -602,8 +603,8 @@ struct d0200_EscapedIdentifiers { class `extension` : `class` {} // PASS_ONE_LINE_TYPE-DAG: {{^}} class `extension` : d0200_EscapedIdentifiers.`class` {{{$}} // PASS_ONE_LINE_TYPEREPR-DAG: {{^}} class `extension` : `class` {{{$}} -// PASS_COMMON: {{^}} @objc deinit{{$}} -// PASS_COMMON-NEXT: {{^}} {{(override )?}}init(){{$}} +// PASS_COMMON: {{^}} {{(override )?}}init(){{$}} +// PASS_COMMON-NEXT: {{^}} @objc deinit{{$}} // PASS_COMMON-NEXT: {{^}} }{{$}} func `func`<`let`: `protocol`, `where`>( @@ -1015,6 +1016,7 @@ enum d2000_EnumDecl1 { // PASS_COMMON-NEXT: {{^}} case ED1_Second{{$}} // PASS_COMMON-NEXT: {{^}} {{.*}}static func __derived_enum_equals(_ a: d2000_EnumDecl1, _ b: d2000_EnumDecl1) -> Bool // PASS_COMMON-NEXT: {{^}} var hashValue: Int { get }{{$}} +// PASS_COMMON-NEXT: {{^}} func hash(into hasher: inout Hasher) // PASS_COMMON-NEXT: {{^}}}{{$}} enum d2100_EnumDecl2 { @@ -1072,6 +1074,7 @@ enum d2300_EnumDeclWithValues1 : Int { // PASS_COMMON-NEXT: {{^}} case EDV2_Second{{$}} // PASS_COMMON-NEXT: {{^}} typealias RawValue = Int // PASS_COMMON-NEXT: {{^}} var hashValue: Int { get }{{$}} +// PASS_COMMON-NEXT: {{^}} func hash(into hasher: inout Hasher) // PASS_COMMON-NEXT: {{^}} init?(rawValue: Int){{$}} // PASS_COMMON-NEXT: {{^}} var rawValue: Int { get }{{$}} // PASS_COMMON-NEXT: {{^}}}{{$}} @@ -1085,6 +1088,7 @@ enum d2400_EnumDeclWithValues2 : Double { // PASS_COMMON-NEXT: {{^}} case EDV3_Second{{$}} // PASS_COMMON-NEXT: {{^}} typealias RawValue = Double // PASS_COMMON-NEXT: {{^}} var hashValue: Int { get }{{$}} +// PASS_COMMON-NEXT: {{^}} func hash(into hasher: inout Hasher) // PASS_COMMON-NEXT: {{^}} init?(rawValue: Double){{$}} // PASS_COMMON-NEXT: {{^}} var rawValue: Double { get }{{$}} // PASS_COMMON-NEXT: {{^}}}{{$}} @@ -1375,3 +1379,5 @@ public typealias MyPairI = MyPair // PASS_PRINT_AST: public typealias MyPairI = MyPair public typealias MyPairAlias = MyPair // PASS_PRINT_AST: public typealias MyPairAlias = MyPair +public typealias MyPairAlias2 = MyPair where U: BarProtocol +// PASS_PRINT_AST: public typealias MyPairAlias2 = MyPair where T : FooProtocol, U : BarProtocol diff --git a/test/IDE/print_clang_decls.swift b/test/IDE/print_clang_decls.swift index 1b602902c6d10..e8ce5b923ff12 100644 --- a/test/IDE/print_clang_decls.swift +++ b/test/IDE/print_clang_decls.swift @@ -22,6 +22,9 @@ // RUN: %target-swift-ide-test(mock-sdk: -sdk %S/../Inputs/clang-importer-sdk -I %t) -print-module -source-filename %s -module-to-print=nullability -function-definitions=false -prefer-type-repr=true > %t.printed.txt // RUN: %FileCheck %s -check-prefix=CHECK-NULLABILITY -strict-whitespace < %t.printed.txt +// RUN: %target-swift-ide-test(mock-sdk: -sdk %S/../Inputs/clang-importer-sdk -I %t) -print-module -source-filename %s -module-to-print=bridged_typedef -function-definitions=false -prefer-type-repr=true > %t.printed.txt +// RUN: %FileCheck %s -check-prefix=CHECK-BRIDGED-TYPEDEF -strict-whitespace < %t.printed.txt + // TAG_DECLS_AND_TYPEDEFS: {{^}}struct FooStruct1 {{{$}} // TAG_DECLS_AND_TYPEDEFS: {{^}} var x: Int32{{$}} // TAG_DECLS_AND_TYPEDEFS: {{^}} var y: Double{{$}} @@ -153,3 +156,8 @@ // CHECK-NULLABILITY: func optArrayMethod() -> [Any]? // CHECK-NULLABILITY: } // CHECK-NULLABILITY: func compare_classes(_ sc1: SomeClass, _ sc2: SomeClass, _ sc3: SomeClass!) + +// CHECK-BRIDGED-TYPEDEF: typealias NSMyAmazingStringAlias = String +// CHECK-BRIDGED-TYPEDEF: func acceptNSMyAmazingStringAlias(_ param: NSMyAmazingStringAlias?) +// CHECK-BRIDGED-TYPEDEF: func acceptNSMyAmazingStringAliasArray(_ param: [NSMyAmazingStringAlias]) +// CHECK-BRIDGED-TYPEDEF: func acceptIndirectedAmazingAlias(_ param: AutoreleasingUnsafeMutablePointer?) diff --git a/test/IDE/print_usrs.swift b/test/IDE/print_usrs.swift index c58ec4981d6b6..cae8f95b237b7 100644 --- a/test/IDE/print_usrs.swift +++ b/test/IDE/print_usrs.swift @@ -1,5 +1,5 @@ -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -verify -disable-objc-attr-requires-foundation-module %s -// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -print-usrs -source-filename %s | %FileCheck %s -strict-whitespace +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -verify -disable-objc-attr-requires-foundation-module -enable-objc-interop %s +// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -enable-objc-interop -print-usrs -source-filename %s | %FileCheck %s -strict-whitespace import macros diff --git a/test/IDE/range_info_basics.swift b/test/IDE/range_info_basics.swift index d11fe25216af0..137bcf16f66f8 100644 --- a/test/IDE/range_info_basics.swift +++ b/test/IDE/range_info_basics.swift @@ -144,6 +144,11 @@ func testExpressionVariables() -> Int { return bar() } +func testDefer() { + defer { + } +} + // RUN: %target-swift-ide-test -range -pos=8:1 -end-pos 8:32 -source-filename %s | %FileCheck %s -check-prefix=CHECK1 // RUN: %target-swift-ide-test -range -pos=9:1 -end-pos 9:26 -source-filename %s | %FileCheck %s -check-prefix=CHECK2 // RUN: %target-swift-ide-test -range -pos=10:1 -end-pos 10:27 -source-filename %s | %FileCheck %s -check-prefix=CHECK3 @@ -181,6 +186,7 @@ func testExpressionVariables() -> Int { // RUN: %target-swift-ide-test -range -pos=118:1 -end-pos=120:22 -source-filename %s | %FileCheck %s -check-prefix=CHECK-INT // RUN: %target-swift-ide-test -range -pos=133:1 -end-pos=135:65 -source-filename %s | %FileCheck %s -check-prefix=CHECK-NO-PATTERN // RUN: %target-swift-ide-test -range -pos=142:12 -end-pos=142:17 -source-filename %s | %FileCheck %s -check-prefix=CHECK-X-Y +// RUN: %target-swift-ide-test -range -pos=147:1 -end-pos=150:1 -source-filename %s | %FileCheck %s -check-prefix=CHECK-INVALID // CHECK-NO-PATTERN: MultiStatement // CHECK-NO-PATTERN-NEXT: for key in parameters.keys.sorted(by: <) { diff --git a/test/IDE/structure.swift b/test/IDE/structure.swift index bc89ae1f57b57..8e4e62c18ba86 100644 --- a/test/IDE/structure.swift +++ b/test/IDE/structure.swift @@ -142,7 +142,7 @@ func test1() { // CHECK: enum SomeEnum { // CHECK: case North // CHECK: case South, East -// CHECK: case QRCode(String) +// CHECK: case QRCode(String) // CHECK: case // CHECK: } enum SomeEnum { diff --git a/test/IRGen/Inputs/OtherModule.swift b/test/IRGen/Inputs/OtherModule.swift new file mode 100644 index 0000000000000..ef16b8f6ff55d --- /dev/null +++ b/test/IRGen/Inputs/OtherModule.swift @@ -0,0 +1,24 @@ +import resilient_struct + +public struct First {} +public struct Second { + public let resilientData: Size +} + +private enum PrivateEnum { + case first(First?) + case second(Second?) +} + +public struct Foo { + private var _property = PrivateEnum.first(nil) +} + +internal enum InternalEnum { + case first(First?) + case second(Second?) +} + +public struct Bar { + private var _property = InternalEnum.first(nil) +} diff --git a/test/IRGen/Inputs/deserialize-clang-importer-witness-tables/regex.swift b/test/IRGen/Inputs/deserialize-clang-importer-witness-tables/regex.swift new file mode 100644 index 0000000000000..ce8c9290e3a50 --- /dev/null +++ b/test/IRGen/Inputs/deserialize-clang-importer-witness-tables/regex.swift @@ -0,0 +1,25 @@ + +import Foundation + +public struct RegEx { + public let pattern: String + fileprivate let regex: NSRegularExpression + public typealias Options = NSRegularExpression.Options + + public init(pattern: String, options: Options = []) throws { + self.pattern = pattern + self.regex = try NSRegularExpression(pattern: pattern, options: options) + } + + /// Returns a match group for the first match, or nil if there was no match. + public func firstMatch(in string: String) -> [String]? { + let nsString = string as NSString + + return regex.firstMatch(in: string, range: NSMakeRange(0, nsString.length)).map { match -> [String] in + return (1 ..< match.numberOfRanges).map { idx -> String in + let range = match.range(at: idx) + return range.location == NSNotFound ? "" : nsString.substring(with: range) + } + } + } +} \ No newline at end of file diff --git a/test/IRGen/Inputs/resilience_bypass/first.swift b/test/IRGen/Inputs/resilience_bypass/first.swift new file mode 100644 index 0000000000000..8f9771e50def1 --- /dev/null +++ b/test/IRGen/Inputs/resilience_bypass/first.swift @@ -0,0 +1,9 @@ +public class C {} + +public struct S { + public let c: C + + public init() { + self.c = C() + } +} diff --git a/test/IRGen/Inputs/resilience_bypass/second.swift b/test/IRGen/Inputs/resilience_bypass/second.swift new file mode 100644 index 0000000000000..019596bfafe5d --- /dev/null +++ b/test/IRGen/Inputs/resilience_bypass/second.swift @@ -0,0 +1,6 @@ +import first + +public enum E { + case a(S) + case b(S) +} diff --git a/test/IRGen/Inputs/usr/include/Gizmo.h b/test/IRGen/Inputs/usr/include/Gizmo.h index 6859d7e8efbdf..d741d042ca29a 100644 --- a/test/IRGen/Inputs/usr/include/Gizmo.h +++ b/test/IRGen/Inputs/usr/include/Gizmo.h @@ -129,11 +129,19 @@ typedef NS_ENUM(unsigned, NeverActuallyMentionedByName) { - (NeverActuallyMentionedByName)getValue; @end +#if defined(_WIN32) +enum RawEnumInGizmo : unsigned { + InGizmoOne=0x7FFFFFFF, + InGizmoTwo, + InGizmoThree +}; +#else enum RawEnumInGizmo { InGizmoOne=0x7FFFFFFF, InGizmoTwo, InGizmoThree }; +#endif struct StructOfNSStrings { __unsafe_unretained NSString *a; diff --git a/test/IRGen/abitypes.swift b/test/IRGen/abitypes.swift index 27050af0b2610..39b83651614d2 100644 --- a/test/IRGen/abitypes.swift +++ b/test/IRGen/abitypes.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -I %S/Inputs/abi %s -emit-ir | %FileCheck -check-prefix=%target-cpu-%target-os %s +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -I %S/Inputs/abi %s -emit-ir -enable-objc-interop | %FileCheck -check-prefix=%target-cpu-%target-os %s // FIXME: rdar://problem/19648117 Needs splitting objc parts out // XFAIL: linux diff --git a/test/IRGen/access_control.sil b/test/IRGen/access_control.sil index e1f094705c49a..c52e479d4e5f3 100644 --- a/test/IRGen/access_control.sil +++ b/test/IRGen/access_control.sil @@ -4,7 +4,7 @@ import Builtin import Swift public struct PublicStruct { var x: Int } -// CHECK: @"$S14access_control12PublicStructVMn" = {{(protected )?}}constant +// CHECK: @"$S14access_control12PublicStructVMn" = {{(dllexport )?}}{{(protected )?}}constant // CHECK: @"$S14access_control12PublicStructVMf" = internal constant internal struct InternalStruct { var x: Int } @@ -21,7 +21,7 @@ func local() { // CHECK: @"$S14access_control5localyyF11LocalStructL_VMf" = internal constant } -// CHECK: @"$S14access_control12PublicStructVN" = {{(protected )?}}alias +// CHECK: @"$S14access_control12PublicStructVN" = {{(dllexport )?}}{{(protected )?}}alias // CHECK: @"$S14access_control14InternalStructVN" = hidden alias // CHECK: @"$S14access_control13PrivateStruct33_8F630B0A1EEF3ED34B761E3ED76C95A8LLVN" = internal alias // CHECK: @"$S14access_control5localyyF11LocalStructL_VN" = internal alias diff --git a/test/IRGen/alloc_stack.swift b/test/IRGen/alloc_stack.swift new file mode 100644 index 0000000000000..a94df3a1d0b79 --- /dev/null +++ b/test/IRGen/alloc_stack.swift @@ -0,0 +1,16 @@ +// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -primary-file %s -emit-ir | %FileCheck %s + +// REQUIRES: CPU=x86_64 + +class Foobar { + init() { + var a : Bool = true + } +} + +// Make sure we are mis-initializing the alloca. +// CHECK-LABEL: define {{.*}}swiftcc %T11alloc_stack6FoobarC* @"$S11alloc_stack6FoobarCACycfc"(%T11alloc_stack6FoobarC* swiftself) +// CHECK-NOT: store{{.*}}opaque +// CHECK: ret {{.*}}%0 +// CHECK:} + diff --git a/test/IRGen/argument_attrs.sil b/test/IRGen/argument_attrs.sil index 80ea045393b16..76023dbf39dae 100644 --- a/test/IRGen/argument_attrs.sil +++ b/test/IRGen/argument_attrs.sil @@ -4,7 +4,7 @@ import Builtin struct Huge { var x, y, z, w, a, b, c, d, e, f: Builtin.Int32 } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @arguments_in_def(i32* nocapture dereferenceable(4), i32* noalias nocapture dereferenceable(4), i32* noalias nocapture dereferenceable(4), %T14argument_attrs4HugeV* noalias nocapture dereferenceable(40), %swift.opaque* noalias nocapture, %swift.opaque* noalias nocapture, %swift.type* %T) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @arguments_in_def(i32* nocapture dereferenceable(4), i32* noalias nocapture dereferenceable(4), i32* noalias nocapture dereferenceable(4), %T14argument_attrs4HugeV* noalias nocapture dereferenceable(40), %swift.opaque* noalias nocapture, %swift.opaque* noalias nocapture, %swift.type* %T) sil @arguments_in_def : $@convention(thin) (@inout Builtin.Int32, @in Builtin.Int32, @in_guaranteed Builtin.Int32, Huge, @in T, @in ()) -> () { entry(%1 : $*Builtin.Int32, %2 : $*Builtin.Int32, %3 : $*Builtin.Int32, %4 : $Huge, %5 : $*T, %6 : $*()): // CHECK: call swiftcc void @arguments_in_decl(i32* nocapture dereferenceable(4) {{%.*}}, i32* noalias nocapture dereferenceable(4) {{%.*}}, i32* noalias nocapture dereferenceable(4) {{%.*}}, %T14argument_attrs4HugeV* noalias nocapture dereferenceable(40) {{%.*}}, %swift.opaque* noalias nocapture {{%.*}}, %swift.opaque* noalias nocapture {{%.*}}, %swift.type* %T) @@ -16,10 +16,10 @@ entry(%1 : $*Builtin.Int32, %2 : $*Builtin.Int32, %3 : $*Builtin.Int32, %4 : $Hu return undef : $() } -// CHECK-LABEL: declare swiftcc void @arguments_in_decl(i32* nocapture dereferenceable(4), i32* noalias nocapture dereferenceable(4), i32* noalias nocapture dereferenceable(4), %T14argument_attrs4HugeV* noalias nocapture dereferenceable(40), %swift.opaque* noalias nocapture, %swift.opaque* noalias nocapture, %swift.type*) +// CHECK-LABEL: declare{{( dllimport)?}} swiftcc void @arguments_in_decl(i32* nocapture dereferenceable(4), i32* noalias nocapture dereferenceable(4), i32* noalias nocapture dereferenceable(4), %T14argument_attrs4HugeV* noalias nocapture dereferenceable(40), %swift.opaque* noalias nocapture, %swift.opaque* noalias nocapture, %swift.type*) sil @arguments_in_decl : $@convention(thin) (@inout Builtin.Int32, @in Builtin.Int32, @in_guaranteed Builtin.Int32, Huge, @in T, @in ()) -> () -// CHECK-LABEL: define{{( protected)?}} swiftcc void @arguments_in_def_out(i32* noalias nocapture sret, i32* nocapture dereferenceable(4), i32* noalias nocapture dereferenceable(4), i32* noalias nocapture dereferenceable(4), %T14argument_attrs4HugeV* noalias nocapture dereferenceable(40), %swift.opaque* noalias nocapture, %swift.opaque* noalias nocapture, %swift.type* %T) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @arguments_in_def_out(i32* noalias nocapture sret, i32* nocapture dereferenceable(4), i32* noalias nocapture dereferenceable(4), i32* noalias nocapture dereferenceable(4), %T14argument_attrs4HugeV* noalias nocapture dereferenceable(40), %swift.opaque* noalias nocapture, %swift.opaque* noalias nocapture, %swift.type* %T) sil @arguments_in_def_out : $@convention(thin) (@inout Builtin.Int32, @in Builtin.Int32, @in_guaranteed Builtin.Int32, Huge, @in T, @in ()) -> @out Builtin.Int32 { entry(%0 : $*Builtin.Int32, %1 : $*Builtin.Int32, %2 : $*Builtin.Int32, %3 : $*Builtin.Int32, %4 : $Huge, %5 : $*T, %6 : $*()): // CHECK: call swiftcc void @arguments_in_decl_out(i32* noalias nocapture sret {{%.*}}, i32* nocapture dereferenceable(4) {{%.*}}, i32* noalias nocapture dereferenceable(4) {{%.*}}, i32* noalias nocapture dereferenceable(4) {{%.*}}, %T14argument_attrs4HugeV* noalias nocapture dereferenceable(40) {{%.*}}, %swift.opaque* noalias nocapture {{%.*}}, %swift.opaque* noalias nocapture {{%.*}}, %swift.type* {{%.*}}) @@ -31,10 +31,10 @@ entry(%0 : $*Builtin.Int32, %1 : $*Builtin.Int32, %2 : $*Builtin.Int32, %3 : $*B return undef : $() } -// CHECK-LABEL: declare swiftcc void @arguments_in_decl_out(i32* noalias nocapture sret, i32* nocapture dereferenceable(4), i32* noalias nocapture dereferenceable(4), i32* noalias nocapture dereferenceable(4), %T14argument_attrs4HugeV* noalias nocapture dereferenceable(40), %swift.opaque* noalias nocapture, %swift.opaque* noalias nocapture, %swift.type*) +// CHECK-LABEL: declare{{( dllimport)?}} swiftcc void @arguments_in_decl_out(i32* noalias nocapture sret, i32* nocapture dereferenceable(4), i32* noalias nocapture dereferenceable(4), i32* noalias nocapture dereferenceable(4), %T14argument_attrs4HugeV* noalias nocapture dereferenceable(40), %swift.opaque* noalias nocapture, %swift.opaque* noalias nocapture, %swift.type*) sil @arguments_in_decl_out : $@convention(thin) (@inout Builtin.Int32, @in Builtin.Int32, @in_guaranteed Builtin.Int32, Huge, @in T, @in ()) -> @out Builtin.Int32 -// CHECK-LABEL: define{{( protected)?}} swiftcc void @arguments_in_def_huge_ret(%T14argument_attrs4HugeV* noalias nocapture sret, i32* nocapture dereferenceable(4), i32* noalias nocapture dereferenceable(4), i32* noalias nocapture dereferenceable(4), %T14argument_attrs4HugeV* noalias nocapture dereferenceable(40), %swift.opaque* noalias nocapture, %swift.opaque* noalias nocapture, %swift.type* %T) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @arguments_in_def_huge_ret(%T14argument_attrs4HugeV* noalias nocapture sret, i32* nocapture dereferenceable(4), i32* noalias nocapture dereferenceable(4), i32* noalias nocapture dereferenceable(4), %T14argument_attrs4HugeV* noalias nocapture dereferenceable(40), %swift.opaque* noalias nocapture, %swift.opaque* noalias nocapture, %swift.type* %T) sil @arguments_in_def_huge_ret : $@convention(thin) (@inout Builtin.Int32, @in Builtin.Int32, @in_guaranteed Builtin.Int32, Huge, @in T, @in ()) -> Huge { entry(%1 : $*Builtin.Int32, %2 : $*Builtin.Int32, %3 : $*Builtin.Int32, %4 : $Huge, %5 : $*T, %6 : $*()): %f = function_ref @arguments_in_decl_huge_ret : $@convention(thin) (@inout Builtin.Int32, @in Builtin.Int32, @in_guaranteed Builtin.Int32, Huge, @in T, @in ()) -> Huge @@ -46,7 +46,7 @@ entry(%1 : $*Builtin.Int32, %2 : $*Builtin.Int32, %3 : $*Builtin.Int32, %4 : $Hu return %y : $Huge } -// CHECK-LABEL: declare swiftcc void @arguments_in_decl_huge_ret(%T14argument_attrs4HugeV* noalias nocapture sret, i32* nocapture dereferenceable(4), i32* noalias nocapture dereferenceable(4), i32* noalias nocapture dereferenceable(4), %T14argument_attrs4HugeV* noalias nocapture dereferenceable(40), %swift.opaque* noalias nocapture, %swift.opaque* noalias nocapture, %swift.type*) +// CHECK-LABEL: declare{{( dllimport)?}} swiftcc void @arguments_in_decl_huge_ret(%T14argument_attrs4HugeV* noalias nocapture sret, i32* nocapture dereferenceable(4), i32* noalias nocapture dereferenceable(4), i32* noalias nocapture dereferenceable(4), %T14argument_attrs4HugeV* noalias nocapture dereferenceable(40), %swift.opaque* noalias nocapture, %swift.opaque* noalias nocapture, %swift.type*) sil @arguments_in_decl_huge_ret : $@convention(thin) (@inout Builtin.Int32, @in Builtin.Int32, @in_guaranteed Builtin.Int32, Huge, @in T, @in ()) -> Huge diff --git a/test/IRGen/asmname.swift b/test/IRGen/asmname.swift index e285837350b73..0239932ff60b5 100644 --- a/test/IRGen/asmname.swift +++ b/test/IRGen/asmname.swift @@ -16,7 +16,7 @@ _ = atan2test(0.0, 0.0) public func PlainPublic() { } internal func PlainInternal() { } private func PlainPrivate() { } -// CHECK: define{{( protected)?}} swiftcc void @"$S7asmname11PlainPublic +// CHECK: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$S7asmname11PlainPublic // CHECK-NOT: PlainInternal // CHECK-NOT: PlainPrivate @@ -29,7 +29,7 @@ private func PlainPrivate() { } @_silgen_name("silgen_name_public") public func SilgenNamePublic() { } @_silgen_name("silgen_name_internal") internal func SilgenNameInternal() { } @_silgen_name("silgen_name_private") private func SilgenNamePrivate() { } -// CHECK: define{{( protected)?}} swiftcc void @silgen_name_public +// CHECK: define{{( dllexport)?}}{{( protected)?}} swiftcc void @silgen_name_public // CHECK: define hidden swiftcc void @silgen_name_internal // CHECK-NOT: silgen_name_private // CHECK-NOT: SilgenName @@ -43,8 +43,8 @@ private func PlainPrivate() { } @_cdecl("cdecl_public") public func CDeclPublic() { } @_cdecl("cdecl_internal") internal func CDeclInternal() { } @_cdecl("cdecl_private") private func CDeclPrivate() { } -// CHECK: define{{( protected)?}} void @cdecl_public -// CHECK: define{{( protected)?}} swiftcc void @"$S7asmname11CDeclPublic +// CHECK: define{{( dllexport)?}}{{( protected)?}} void @cdecl_public +// CHECK: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$S7asmname11CDeclPublic // CHECK: define hidden void @cdecl_internal // CHECK: define hidden swiftcc void @"$S7asmname13CDeclInternal // CHECK-NOT: cdecl_private diff --git a/test/IRGen/associated_type_witness.swift b/test/IRGen/associated_type_witness.swift index ecfbe4c81fb5d..72a62d4b320a2 100644 --- a/test/IRGen/associated_type_witness.swift +++ b/test/IRGen/associated_type_witness.swift @@ -12,7 +12,7 @@ protocol Assocked { struct Universal : P, Q {} -// CHECK: [[ASSOC_TYPE_NAMES:@.*]] = private constant [29 x i8] c"OneAssoc TwoAssoc ThreeAssoc\00" +// CHECK: [[ASSOC_TYPE_NAMES:@.*]] = private constant [29 x i8] c"OneAssoc ThreeAssoc TwoAssoc\00" // CHECK: @"$S23associated_type_witness18HasThreeAssocTypesMp" = // CHECK-SAME: [[ASSOC_TYPE_NAMES]] to i64 diff --git a/test/IRGen/associated_types.swift b/test/IRGen/associated_types.swift index 131bb7de337cf..c14c76a0ce007 100644 --- a/test/IRGen/associated_types.swift +++ b/test/IRGen/associated_types.swift @@ -75,7 +75,7 @@ func testFastRuncible(_ t: T, u: U) // 1. Get the type metadata for U.RuncerType.Runcee. // 1a. Get the type metadata for U.RuncerType. // Note that we actually look things up in T, which is going to prove unfortunate. -// CHECK: [[T0_GEP:%.*]] = getelementptr inbounds i8*, i8** %T.Runcible, i32 1 +// CHECK: [[T0_GEP:%.*]] = getelementptr inbounds i8*, i8** %T.Runcible, i32 2 // CHECK: [[T0:%.*]] = load i8*, i8** [[T0_GEP]] // CHECK-NEXT: [[T1:%.*]] = bitcast i8* [[T0]] to %swift.metadata_response ([[INT]], %swift.type*, i8**)* // CHECK-NEXT: [[T2:%.*]] = call swiftcc %swift.metadata_response [[T1]]([[INT]] 0, %swift.type* %T, i8** %T.Runcible) diff --git a/test/IRGen/autolink-coff-force-link.swift b/test/IRGen/autolink-coff-force-link.swift index 29df394004ea4..71422e1c143e4 100644 --- a/test/IRGen/autolink-coff-force-link.swift +++ b/test/IRGen/autolink-coff-force-link.swift @@ -2,20 +2,20 @@ // RUN: %swift -target i686--windows-msvc -parse-stdlib -autolink-force-load -module-name swiftMSVCRT -module-link-name swiftMSVCRT -emit-module -o %t/swiftMSVCRT.swiftmodule %S/../Inputs/empty.swift -// RUN: %swift -target i686--windows-msvc -parse-as-library -parse-stdlib -autolink-force-load -module-name autolink -module-link-name autolink -emit-ir -o - %s | %FileCheck %s -// RUN: %swift -target i686--windows-msvc -parse-as-library -parse-stdlib -autolink-force-load -module-name autolink -module-link-name autolink -S -o - %s | %FileCheck %s -check-prefix CHECK-ASM-MSC +// RUN: %swift -target i686--windows-msvc -parse-as-library -parse-stdlib -autolink-force-load -module-name autolink -module-link-name autolink -emit-ir -o - %s -I%t | %FileCheck %s +// RUN: %swift -target i686--windows-msvc -parse-as-library -parse-stdlib -autolink-force-load -module-name autolink -module-link-name autolink -S -o - %s -I%t | %FileCheck %s -check-prefix CHECK-ASM-MSC // RUN: %swift -target i686--windows-itanium -parse-stdlib -autolink-force-load -module-name swiftMSVCRT -module-link-name swiftMSVCRT -emit-module -o %t/swiftMSVCRT.swiftmodule %S/../Inputs/empty.swift -// RUN: %swift -target i686--windows-itanium -parse-as-library -parse-stdlib -autolink-force-load -module-name autolink -module-link-name autolink -emit-ir -o - %s | %FileCheck %s -// RUN: %swift -target i686--windows-itanium -parse-as-library -parse-stdlib -autolink-force-load -module-name autolink -module-link-name autolink -S -o - %s | %FileCheck %s -check-prefix CHECK-ASM-GNU +// RUN: %swift -target i686--windows-itanium -parse-as-library -parse-stdlib -autolink-force-load -module-name autolink -module-link-name autolink -emit-ir -o - %s -I%t | %FileCheck %s +// RUN: %swift -target i686--windows-itanium -parse-as-library -parse-stdlib -autolink-force-load -module-name autolink -module-link-name autolink -S -o - %s -I%t | %FileCheck %s -check-prefix CHECK-ASM-GNU // REQUIRES: OS=windows-msvc import swiftMSVCRT // CHECK: @"_swift_FORCE_LOAD_$_swiftMSVCRT_$_autolink" = weak hidden constant void ()* @"_swift_FORCE_LOAD_$_swiftMSVCRT" -// CHECK: weak_odr dllexport void @"_swift_FORCE_LOAD_$_autolink"() +// CHECK: define dllexport void @"_swift_FORCE_LOAD_$_autolink"() // CHECK-ASM-GNU: .ascii " -export:__swift_FORCE_LOAD_$_autolink" // CHECK-ASM-MSC: .ascii " /EXPORT:__swift_FORCE_LOAD_$_autolink" diff --git a/test/IRGen/autorelease.sil b/test/IRGen/autorelease.sil index 74f7b6e3d3e9a..3cdf4fd254cbf 100644 --- a/test/IRGen/autorelease.sil +++ b/test/IRGen/autorelease.sil @@ -1,5 +1,4 @@ // RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) %s -emit-ir | %FileCheck -check-prefix=%target-cpu %s -// REQUIRES: objc_interop // rdar://16565958 diff --git a/test/IRGen/big_types_corner_cases.sil b/test/IRGen/big_types_corner_cases.sil index da498e4ed3949..38c4c4ea9352c 100644 --- a/test/IRGen/big_types_corner_cases.sil +++ b/test/IRGen/big_types_corner_cases.sil @@ -36,7 +36,7 @@ public struct BigBigStruct { var s : BigStruct } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @testBitfieldInBlock +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @testBitfieldInBlock // CHECK: call void {{%.*}}(%TSo11BitfieldOneV* noalias nocapture sret {{%.*}}, %objc_block* {{%.*}}, %TSo11BitfieldOneV* byval align 8 {{%.*}}) sil @testBitfieldInBlock : $@convention(thin) (@owned @convention(block) (BitfieldOne) -> BitfieldOne, BitfieldOne) -> BitfieldOne { entry(%b : $@convention(block) (BitfieldOne) -> BitfieldOne, %x : $BitfieldOne): @@ -44,7 +44,7 @@ entry(%b : $@convention(block) (BitfieldOne) -> BitfieldOne, %x : $BitfieldOne): return %r : $BitfieldOne } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @testTupleExtract +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @testTupleExtract // CHECK: call void {{%.*}}(%TSo11BitfieldOneV* noalias nocapture sret {{%.*}}, %objc_block* {{%.*}}, %TSo11BitfieldOneV* byval align 8 {{%.*}}) sil @testTupleExtract : $@convention(thin) (@owned (BitfieldOne, @convention(block) (BitfieldOne) -> BitfieldOne), BitfieldOne) -> BitfieldOne { entry(%b : $(BitfieldOne, @convention(block) (BitfieldOne) -> (BitfieldOne)), %x : $BitfieldOne): @@ -53,7 +53,7 @@ entry(%b : $(BitfieldOne, @convention(block) (BitfieldOne) -> (BitfieldOne)), %x return %r : $BitfieldOne } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @testBigTempStruct(%T22big_types_corner_cases13BigTempStructV* noalias nocapture sret, %swift.bridge*, %swift.type* %Element) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @testBigTempStruct(%T22big_types_corner_cases13BigTempStructV* noalias nocapture sret, %swift.bridge*, %swift.type* %Element) // CHECK: [[ALLOC:%.*]] = alloca %T22big_types_corner_cases13BigTempStructV // CHECK: call swiftcc void @testBigTempStruct(%T22big_types_corner_cases13BigTempStructV* noalias nocapture sret [[ALLOC]], %swift.bridge* %1, %swift.type* %Element) // CHECK: ret void @@ -65,7 +65,7 @@ bb0(%0 : $_ArrayBuffer): return %9 : $BigTempStruct } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @testTryApply(%T22big_types_corner_cases9BigStructV* noalias nocapture sret, i8*, %swift.refcounted*, %swift.refcounted* swiftself, %swift.error** swifterror) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @testTryApply(%T22big_types_corner_cases9BigStructV* noalias nocapture sret, i8*, %swift.refcounted*, %swift.refcounted* swiftself, %swift.error** swifterror) // CHECK: [[ALLOC:%.*]] = alloca %T22big_types_corner_cases9BigStructV // CHECK: call swiftcc void {{.*}}(%T22big_types_corner_cases9BigStructV* noalias nocapture sret [[ALLOC]] // CHECK: ret void @@ -81,7 +81,7 @@ bb2(%err : $Error): throw %err : $Error } -// CHECK-LABEL: define{{( protected)?}} swiftcc i8* @testThinFuncPointer(%swift.type* %"BigTempStruct", %T22big_types_corner_cases13BigTempStructV* noalias nocapture swiftself dereferenceable({{.*}}) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc i8* @testThinFuncPointer(%swift.type* %"BigTempStruct", %T22big_types_corner_cases13BigTempStructV* noalias nocapture swiftself dereferenceable({{.*}}) // CHECK-NEXT: entry // CHECK-NEXT: ret i8* bitcast (i8* (%swift.type*, %T22big_types_corner_cases13BigTempStructV*)* @testThinFuncPointer to i8*) sil @testThinFuncPointer : $@convention(method) (@guaranteed BigTempStruct) -> @owned Builtin.RawPointer { @@ -92,7 +92,7 @@ bb0(%0 : $BigTempStruct): return %ret : $Builtin.RawPointer } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @testFuncWithModBlockStorageApply({ %objc_block, %swift.function }* nocapture dereferenceable({{.*}}), %T22big_types_corner_cases9BigStructV* noalias nocapture dereferenceable({{.*}}) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @testFuncWithModBlockStorageApply({ %objc_block, %swift.function }* nocapture dereferenceable({{.*}}), %T22big_types_corner_cases9BigStructV* noalias nocapture dereferenceable({{.*}}) // CHECK: call swiftcc void {{.*}}(%T22big_types_corner_cases9BigStructV* noalias nocapture dereferenceable({{.*}}) %1 // CHECK: ret void sil @testFuncWithModBlockStorageApply : $@convention(thin) (@inout_aliasable @block_storage @callee_owned (@owned BigStruct) -> (), BigStruct) -> () { @@ -112,7 +112,7 @@ bb0(%0 : $*@block_storage @callee_owned (@owned BigStruct) -> (), %1 : $BigStruc sil public_external @c_return_func : $@convention(thin) () -> () -> @owned BigStruct -// CHECK-LABEL: define{{( protected)?}} swiftcc { i8*, %swift.refcounted* } @part_apply_caller() +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc { i8*, %swift.refcounted* } @part_apply_caller() // CHECK: [[CALLEXT:%.*]] = call swiftcc { i8*, %swift.refcounted* } @c_return_func() // CHECK: [[VALEXT:%.*]] = extractvalue { i8*, %swift.refcounted* } [[CALLEXT]], 1 // CHECK: store %swift.refcounted* [[VALEXT]], %swift.refcounted** @@ -186,7 +186,7 @@ sil @get_optional_none : $@convention(method) <τ_0_0> (@thin Optional<τ_0_0>.T sil @short_circuit_operation : $@convention(thin) <τ_0_0> (@in Optional<τ_0_0>, @owned @callee_owned () -> (@out τ_0_0, @error Error)) -> (@out τ_0_0, @error Error) sil @autoclosure_partialapply : $@convention(thin) (@owned @callee_owned () -> (BigStruct, @error Error)) -> (@out BigStruct, @error Error) -// CHECK-LABEL: define{{( protected)?}} swiftcc void @closure(%T22big_types_corner_cases9BigStructV* noalias nocapture sret, %T22big_types_corner_cases8SuperSubC*) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @closure(%T22big_types_corner_cases9BigStructV* noalias nocapture sret, %T22big_types_corner_cases8SuperSubC*) // CHECK-64: [[ALLOC1:%.*]] = alloca %T22big_types_corner_cases9BigStructV // CHECK-64: [[ALLOC2:%.*]] = alloca %T22big_types_corner_cases9BigStructV // CHECK-64: [[ALLOC3:%.*]] = alloca %T22big_types_corner_cases9BigStructVSg @@ -227,7 +227,7 @@ bb2(%24 : $Error): sil @returnBigStruct : $@convention(thin) () -> @owned BigStruct -// CHECK-LABEL: define{{( protected)?}} swiftcc void @closureToConvert() +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @closureToConvert() // CHECK: entry: // CHECK: [[ALLOC:%.*]] = alloca %T22big_types_corner_cases9BigStructV // CHECK: call swiftcc void @returnBigStruct(%T22big_types_corner_cases9BigStructV* noalias nocapture sret [[ALLOC]]) @@ -241,7 +241,7 @@ bb0: return %99 : $() } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @testConvertFunc() +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @testConvertFunc() // CHECK: entry: // CHECK: call swiftcc void bitcast (void ()* @closureToConvert to void (%swift.refcounted*)*)(%swift.refcounted* swiftself null) // CHECK: ret void @@ -282,3 +282,64 @@ sil_vtable SuperBase { sil_vtable SuperSub { } +class X { + @objc func foo() -> BitfieldOne +} +sil_vtable X {} + +sil @$S22big_types_corner_cases1XC3fooSo11BitfieldOneVyFTo : $@convention(objc_method) (X) -> BitfieldOne { +bb0(%1 : $X): + %4 = function_ref @$getLargeObjCType : $@convention(thin) () -> BitfieldOne + %7 = apply %4() : $@convention(thin) () -> BitfieldOne + return %7 : $BitfieldOne +} + +sil @$getLargeObjCType : $@convention(thin) () -> BitfieldOne + +// CHECK-LABAL: define {{.*}} swiftcc void @"$crash_on_objc_apply"(%objc_object*) +// CHECK: entry: +// CHECK: [[LOADS:%.*]] = load i8*, i8** @"\01L_selector(foo)" +// CHECK: [[RESS:%.*]] = load i8*, i8** @"\01L_selector(respondsToSelector:)" +// CHECK: call i1 bitcast (void ()* @objc_msgSend to i1 (%objc_object*, i8*, i8*)*)(%objc_object* %0, i8* [[RESS]], i8* [[LOADS]]) +sil @$crash_on_objc_apply : $@convention(thin) (@guaranteed AnyObject) -> () { +// %0 // users: %2, %1 +bb0(%0 : $AnyObject): + debug_value %0 : $AnyObject, let, name "object", argno 1 + %2 = open_existential_ref %0 : $AnyObject to $@opened("E5D03528-36AD-11E8-A0AB-D0817AD47398") AnyObject + strong_retain %2 : $@opened("E5D03528-36AD-11E8-A0AB-D0817AD47398") AnyObject + %4 = alloc_stack $Optional + dynamic_method_br %2 : $@opened("E5D03528-36AD-11E8-A0AB-D0817AD47398") AnyObject, #X.foo!1.foreign, bb1, bb2 + +bb1(%6 : $@convention(objc_method) (@opened("E5D03528-36AD-11E8-A0AB-D0817AD47398") AnyObject) -> BitfieldOne): // Preds: bb0 + strong_retain %2 : $@opened("E5D03528-36AD-11E8-A0AB-D0817AD47398") AnyObject + %8 = partial_apply [callee_guaranteed] %6(%2) : $@convention(objc_method) (@opened("E5D03528-36AD-11E8-A0AB-D0817AD47398") AnyObject) -> BitfieldOne + %9 = apply %8() : $@callee_guaranteed () -> BitfieldOne + %10 = init_enum_data_addr %4 : $*Optional, #Optional.some!enumelt.1 + store %9 to %10 : $*BitfieldOne + inject_enum_addr %4 : $*Optional, #Optional.some!enumelt.1 + strong_release %8 : $@callee_guaranteed () -> BitfieldOne + br bb3 + +bb2: // Preds: bb0 + inject_enum_addr %4 : $*Optional, #Optional.none!enumelt + br bb3 + +bb3: // Preds: bb2 bb1 + %17 = load %4 : $*Optional + dealloc_stack %4 : $*Optional + strong_release %2 : $@opened("E5D03528-36AD-11E8-A0AB-D0817AD47398") AnyObject + %20 = tuple () + return %20 : $() +} // end sil function '$crash_on_objc_apply' + +sil @slowCallee : $@convention(thin) (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed Array) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned BigStruct + +// CHECK-LABAL: define {{.*}} swiftcc void @slowCaller(%swift.bridge* +// CHECK: call swiftcc { i8*, %swift.refcounted* } @slowCallee(%swift.bridge* %0 +sil @slowCaller : $@convention(thin) (@guaranteed String) -> () { +bb0(%0 : $String): + %1 = function_ref @slowCallee : $@convention(thin) (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed Array) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned BigStruct + %2 = apply %1(%0) : $@convention(thin) (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed Array) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned @callee_guaranteed (@guaranteed String) -> @owned BigStruct + %ret = tuple () + return %ret : $() +} diff --git a/test/IRGen/big_types_corner_cases.swift b/test/IRGen/big_types_corner_cases.swift index c40d341aecd92..fb4171d7eca0a 100644 --- a/test/IRGen/big_types_corner_cases.swift +++ b/test/IRGen/big_types_corner_cases.swift @@ -35,7 +35,7 @@ class OptionalInoutFuncType { } } -// CHECK-LABEL: define{{( protected)?}} i32 @main(i32, i8**) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} i32 @main(i32, i8**) // CHECK: call void @llvm.lifetime.start // CHECK: call void @llvm.memcpy // CHECK: call void @llvm.lifetime.end @@ -44,7 +44,7 @@ let bigStructGlobalArray : [BigStruct] = [ BigStruct() ] -// CHECK-LABEL: define{{( protected)?}} internal swiftcc void @"$S22big_types_corner_cases21OptionalInoutFuncTypeC7executeyys5Error_pSgFyyXEfU_"(%T22big_types_corner_cases9BigStructVSg* nocapture dereferenceable({{.*}}), %T22big_types_corner_cases21OptionalInoutFuncTypeC*, %T22big_types_corner_cases9BigStructVSgs5Error_pSgIegcg_Sg* nocapture dereferenceable({{.*}}) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} internal swiftcc void @"$S22big_types_corner_cases21OptionalInoutFuncTypeC7executeyys5Error_pSgFyyXEfU_"(%T22big_types_corner_cases9BigStructVSg* nocapture dereferenceable({{.*}}), %T22big_types_corner_cases21OptionalInoutFuncTypeC*, %T22big_types_corner_cases9BigStructVSgs5Error_pSgIegcg_Sg* nocapture dereferenceable({{.*}}) // CHECK: call void @"$S22big_types_corner_cases9BigStructVSgs5Error_pSgIegcg_SgWOe // CHECK: call void @"$S22big_types_corner_cases9BigStructVSgs5Error_pSgIegcg_SgWOy // CHECK: ret void @@ -63,7 +63,7 @@ public func f3_uses_f2() { let _ = useOfF2(x) } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @"$S22big_types_corner_cases10f3_uses_f2yyF"() +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$S22big_types_corner_cases10f3_uses_f2yyF"() // CHECK: call swiftcc void @"$S22big_types_corner_cases9BigStructVACycfC"(%T22big_types_corner_cases9BigStructV* noalias nocapture sret // CHECK: call swiftcc { i8*, %swift.refcounted* } @"$S22big_types_corner_cases13f2_returns_f1AA9BigStructVADcyF"() // CHECK: call swiftcc void {{.*}}(%T22big_types_corner_cases9BigStructV* noalias nocapture sret {{.*}}, %T22big_types_corner_cases9BigStructV* noalias nocapture dereferenceable({{.*}}) {{.*}}, %swift.refcounted* swiftself {{.*}}) @@ -76,7 +76,7 @@ public func f4_tuple_use_of_f2() { let _ = useOfF2(x) } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @"$S22big_types_corner_cases18f4_tuple_use_of_f2yyF"() +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$S22big_types_corner_cases18f4_tuple_use_of_f2yyF"() // CHECK: [[TUPLE:%.*]] = call swiftcc { i8*, %swift.refcounted* } @"$S22big_types_corner_cases13f2_returns_f1AA9BigStructVADcyF"() // CHECK: [[TUPLE_EXTRACT:%.*]] = extractvalue { i8*, %swift.refcounted* } [[TUPLE]], 0 // CHECK: [[CAST_EXTRACT:%.*]] = bitcast i8* [[TUPLE_EXTRACT]] to void (%T22big_types_corner_cases9BigStructV*, %T22big_types_corner_cases9BigStructV*, %swift.refcounted*)* @@ -94,7 +94,7 @@ public class BigClass { } } -// CHECK-LABEL: define{{( protected)?}} hidden swiftcc void @"$S22big_types_corner_cases8BigClassC03useE6Struct0aH0yAA0eH0V_tF"(%T22big_types_corner_cases9BigStructV* noalias nocapture dereferenceable({{.*}}), %T22big_types_corner_cases8BigClassC* swiftself) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} hidden swiftcc void @"$S22big_types_corner_cases8BigClassC03useE6Struct0aH0yAA0eH0V_tF"(%T22big_types_corner_cases9BigStructV* noalias nocapture dereferenceable({{.*}}), %T22big_types_corner_cases8BigClassC* swiftself) // CHECK: [[BITCAST:%.*]] = bitcast i8* {{.*}} to void (%T22big_types_corner_cases9BigStructV*, %swift.refcounted*)* // CHECK: call swiftcc void [[BITCAST]](%T22big_types_corner_cases9BigStructV* noalias nocapture dereferenceable({{.*}}) %0, %swift.refcounted* swiftself // CHECK: ret void @@ -115,7 +115,7 @@ class Foo { func myMethod(_ callback: (MyStruct, Int) -> Void) -> Void { } } -// CHECK-LABEL: define{{( protected)?}} linkonce_odr hidden swiftcc { i8*, %swift.refcounted* } @"$S22big_types_corner_cases3FooC8myMethodyyyAA8MyStructV_SitXEFTc"(%T22big_types_corner_cases3FooC*) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} linkonce_odr hidden swiftcc { i8*, %swift.refcounted* } @"$S22big_types_corner_cases3FooC8myMethodyyyAA8MyStructV_SitXEFTc"(%T22big_types_corner_cases3FooC*) // CHECK: getelementptr inbounds %T22big_types_corner_cases3FooC, %T22big_types_corner_cases3FooC* // CHECK: getelementptr inbounds void (i8*, %swift.opaque*, %T22big_types_corner_cases3FooC*)*, void (i8*, %swift.opaque*, %T22big_types_corner_cases3FooC*)** // CHECK: call noalias %swift.refcounted* @swift_allocObject(%swift.type* getelementptr inbounds (%swift.full_boxmetadata, %swift.full_boxmetadata* @@ -138,7 +138,7 @@ public func enumCallee(_ x: LargeEnum) { case .Empty2: break } } -// CHECK-LABEL-64: define{{( protected)?}} swiftcc void @"$S22big_types_corner_cases10enumCalleeyAA9LargeEnumOF"(%T22big_types_corner_cases9LargeEnumO* noalias nocapture dereferenceable({{.*}})) #0 { +// CHECK-LABEL-64: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$S22big_types_corner_cases10enumCalleeyAA9LargeEnumOF"(%T22big_types_corner_cases9LargeEnumO* noalias nocapture dereferenceable({{.*}})) #0 { // CHECK-64: alloca %T22big_types_corner_cases9LargeEnumO05InnerF0O // CHECK-64: alloca %T22big_types_corner_cases9LargeEnumO // CHECK-64: call void @llvm.memcpy.p0i8.p0i8.i64 @@ -146,7 +146,7 @@ public func enumCallee(_ x: LargeEnum) { // CHECK-64: $Ss5print_9separator10terminatoryypd_S2StF // CHECK-64: ret void -// CHECK-LABEL: define{{( protected)?}} internal swiftcc void @"$S22big_types_corner_cases8SuperSubC1fyyFAA9BigStructVycfU_"(%T22big_types_corner_cases9BigStructV* noalias nocapture sret, %T22big_types_corner_cases8SuperSubC*) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} internal swiftcc void @"$S22big_types_corner_cases8SuperSubC1fyyFAA9BigStructVycfU_"(%T22big_types_corner_cases9BigStructV* noalias nocapture sret, %T22big_types_corner_cases8SuperSubC*) // CHECK-64: [[ALLOC1:%.*]] = alloca %T22big_types_corner_cases9BigStructV // CHECK-64: [[ALLOC2:%.*]] = alloca %T22big_types_corner_cases9BigStructV // CHECK-64: [[ALLOC3:%.*]] = alloca %T22big_types_corner_cases9BigStructVSg @@ -169,7 +169,7 @@ class SuperSub : SuperBase { } } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @"$S22big_types_corner_cases10MUseStructV16superclassMirrorAA03BigF0VSgvg"(%T22big_types_corner_cases9BigStructVSg* noalias nocapture sret, %T22big_types_corner_cases10MUseStructV* noalias nocapture swiftself dereferenceable({{.*}})) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$S22big_types_corner_cases10MUseStructV16superclassMirrorAA03BigF0VSgvg"(%T22big_types_corner_cases9BigStructVSg* noalias nocapture sret, %T22big_types_corner_cases10MUseStructV* noalias nocapture swiftself dereferenceable({{.*}})) // CHECK: [[ALLOC:%.*]] = alloca %T22big_types_corner_cases9BigStructVSg // CHECK: [[LOAD:%.*]] = load %swift.refcounted*, %swift.refcounted** %.callInternalLet.data // CHECK: call swiftcc void %7(%T22big_types_corner_cases9BigStructVSg* noalias nocapture sret [[ALLOC]], %swift.refcounted* swiftself [[LOAD]]) @@ -183,8 +183,8 @@ public struct MUseStruct { internal let callInternalLet: () -> BigStruct? } -// CHECK-LABEL-64: define{{( protected)?}} swiftcc void @"$S22big_types_corner_cases18stringAndSubstringSS_s0G0VtyF"(<{ %TSS, %Ts9SubstringV }>* noalias nocapture sret) #0 { -// CHECK-LABEL-32: define{{( protected)?}} swiftcc void @"$S22big_types_corner_cases18stringAndSubstringSS_s0G0VtyF"(<{ %TSS, [4 x i8], %Ts9SubstringV }>* noalias nocapture sret) #0 { +// CHECK-LABEL-64: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$S22big_types_corner_cases18stringAndSubstringSS_s0G0VtyF"(<{ %TSS, %Ts9SubstringV }>* noalias nocapture sret) #0 { +// CHECK-LABEL-32: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$S22big_types_corner_cases18stringAndSubstringSS_s0G0VtyF"(<{ %TSS, [4 x i8], %Ts9SubstringV }>* noalias nocapture sret) #0 { // CHECK: alloca %Ts9SubstringV // CHECK: alloca %Ts9SubstringV // CHECK: ret void @@ -198,20 +198,20 @@ func bigStructGet() -> BigStruct { return BigStruct() } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @"$S22big_types_corner_cases11testGetFuncyyF"() +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$S22big_types_corner_cases11testGetFuncyyF"() // CHECK: ret void public func testGetFunc() { let testGetPtr: @convention(thin) () -> BigStruct = bigStructGet } -// CHECK-LABEL: define{{( protected)?}} hidden swiftcc void @"$S22big_types_corner_cases7TestBigC4testyyF"(%T22big_types_corner_cases7TestBigC* swiftself) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} hidden swiftcc void @"$S22big_types_corner_cases7TestBigC4testyyF"(%T22big_types_corner_cases7TestBigC* swiftself) // CHECK: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$SSayy22big_types_corner_cases9BigStructVcSgGMa" // CHECK: [[CALL1:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 // CHECK: [[CALL2:%.*]] = call i8** @"$SSayy22big_types_corner_cases9BigStructVcSgGSayxGs10CollectionsWl -// CHECK: call swiftcc void @"$Ss10CollectionPsE5index5where5IndexQzSgSb7ElementQzKXE_tKF"(%TSq.{{.*}}* noalias nocapture sret {{.*}}, i8* bitcast (i1 (%T22big_types_corner_cases9BigStructVytIegnr_Sg*, %swift.refcounted*, %swift.error**)* @"$S22big_types_corner_cases9BigStructVIegy_SgSbs5Error_pIggdzo_ACytIegnr_SgSbsAE_pIegndzo_TRTA" to i8*), %swift.opaque* {{.*}}, %swift.type* [[CALL1]], i8** [[CALL2]], %swift.opaque* noalias nocapture swiftself +// CHECK: call swiftcc void @"$Ss10CollectionPsE10firstIndex5where0C0QzSgSb7ElementQzKXE_tKF"(%TSq.{{.*}}* noalias nocapture sret {{.*}}, i8* bitcast (i1 (%T22big_types_corner_cases9BigStructVytIegnr_Sg*, %swift.refcounted*, %swift.error**)* @"$S22big_types_corner_cases9BigStructVIegy_SgSbs5Error_pIggdzo_ACytIegnr_SgSbsAE_pIegndzo_TRTA" to i8*), %swift.opaque* {{.*}}, %swift.type* [[CALL1]], i8** [[CALL2]], %swift.opaque* noalias nocapture swiftself // CHECK: ret void -// CHECK-LABEL: define{{( protected)?}} hidden swiftcc void @"$S22big_types_corner_cases7TestBigC5test2yyF"(%T22big_types_corner_cases7TestBigC* swiftself) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} hidden swiftcc void @"$S22big_types_corner_cases7TestBigC5test2yyF"(%T22big_types_corner_cases7TestBigC* swiftself) // CHECK: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$SSaySS2ID_y22big_types_corner_cases9BigStructVcSg7handlertGMa" // CHECK: [[CALL1:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 // CHECK: [[CALL2:%.*]] = call i8** @"$SSaySS2ID_y22big_types_corner_cases9BigStructVcSg7handlertGSayxGs10CollectionsWl" @@ -222,7 +222,7 @@ class TestBig { func test() { let arr = [Handler?]() - let d = arr.index(where: { _ in true }) + let d = arr.firstIndex(where: { _ in true }) } func test2() { @@ -234,3 +234,23 @@ class TestBig { } } } + +struct BigStructWithFunc { + var incSize : BigStruct + var foo: ((BigStruct) -> Void)? +} + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} hidden swiftcc void @"$S22big_types_corner_cases20UseBigStructWithFuncC5crashyyF"(%T22big_types_corner_cases20UseBigStructWithFuncC* swiftself) +// CHECK: call swiftcc void @"$S22big_types_corner_cases20UseBigStructWithFuncC10callMethod +// CHECK: ret void +class UseBigStructWithFunc { + var optBigStructWithFunc: BigStructWithFunc? + + func crash() { + guard let bigStr = optBigStructWithFunc else { return } + callMethod(ptr: bigStr.foo) + } + + private func callMethod(ptr: ((BigStruct) -> Void)?) -> () { + } +} diff --git a/test/IRGen/big_types_corner_cases_as_library.swift b/test/IRGen/big_types_corner_cases_as_library.swift index c0491918e8738..b6f690c9cd97d 100644 --- a/test/IRGen/big_types_corner_cases_as_library.swift +++ b/test/IRGen/big_types_corner_cases_as_library.swift @@ -12,7 +12,7 @@ public struct BigStruct { var i8 : Int32 = 8 } -// CHECK-LABEL: define{{( protected)?}} linkonce_odr hidden %swift.opaque* @"$S33big_types_corner_cases_as_library9BigStructVwCP" +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} linkonce_odr hidden %swift.opaque* @"$S33big_types_corner_cases_as_library9BigStructVwCP" // CHECK: [[RETVAL:%.*]] = bitcast %T33big_types_corner_cases_as_library9BigStructV* {{.*}} to %swift.opaque* // CHECK: ret %swift.opaque* [[RETVAL]] let bigStructGlobalArray : [BigStruct] = [ diff --git a/test/IRGen/big_types_tests.sil b/test/IRGen/big_types_tests.sil index 81f4452c243f6..020302f926c87 100644 --- a/test/IRGen/big_types_tests.sil +++ b/test/IRGen/big_types_tests.sil @@ -19,7 +19,7 @@ public struct BigStruct { var i8 : Int32 = 8 } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @testDestroyValue(%T15big_types_tests9BigStructV* noalias nocapture dereferenceable({{.*}}) #0 { +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @testDestroyValue(%T15big_types_tests9BigStructV* noalias nocapture dereferenceable({{.*}}) #0 { // CHECK-NEXT: entry // CHECK-NEXT: call %T15big_types_tests9BigStructV* @"$S15big_types_tests9BigStructVWOh"(%T15big_types_tests9BigStructV* %0) // CHECK-NEXT: ret void diff --git a/test/IRGen/bitcast.sil b/test/IRGen/bitcast.sil index a54ab3e3a43f7..aebc3bb7028f4 100644 --- a/test/IRGen/bitcast.sil +++ b/test/IRGen/bitcast.sil @@ -16,7 +16,7 @@ sil_vtable C {} protocol CP: class {} -// CHECK-i386-LABEL: define{{( protected)?}} swiftcc i32 @bitcast_trivial(%T7bitcast1CC*) {{.*}} { +// CHECK-i386-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc i32 @bitcast_trivial(%T7bitcast1CC*) {{.*}} { // CHECK-i386: [[BUF:%.*]] = alloca %T7bitcast1CC*, align 4 // CHECK-i386: store %T7bitcast1CC* %0, %T7bitcast1CC** [[BUF]] // CHECK-i386: [[OUT_BUF:%.*]] = bitcast %T7bitcast1CC** [[BUF]] to %TSi* @@ -25,7 +25,7 @@ protocol CP: class {} // CHECK-i386: ret i32 [[VALUE]] // CHECK-i386: } -// CHECK-x86_64-LABEL: define{{( protected)?}} swiftcc i64 @bitcast_trivial(%T7bitcast1CC*) {{.*}} { +// CHECK-x86_64-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc i64 @bitcast_trivial(%T7bitcast1CC*) {{.*}} { // CHECK-x86_64: [[BUF:%.*]] = alloca %T7bitcast1CC*, align 8 // CHECK-x86_64: store %T7bitcast1CC* %0, %T7bitcast1CC** [[BUF]] // CHECK-x86_64: [[OUT_BUF:%.*]] = bitcast %T7bitcast1CC** [[BUF]] to %TSi* @@ -40,7 +40,7 @@ entry(%c : $C): } -// CHECK-x86_64-LABEL: define{{( protected)?}} swiftcc { i64, i8 } @bitcast_trivial_optional(i64, i8) {{.*}} { +// CHECK-x86_64-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc { i64, i8 } @bitcast_trivial_optional(i64, i8) {{.*}} { // CHECK-x86_64-NEXT: entry: // CHECK-x86_64-NEXT: %2 = trunc i8 %1 to i1 // CHECK-x86_64-NEXT: %3 = zext i1 %2 to i8 @@ -54,13 +54,13 @@ entry(%c : $Optional): return %i : $Optional } -// CHECK-i386-LABEL: define{{( protected)?}} swiftcc i32 @bitcast_ref(%T7bitcast1CC*) {{.*}} { +// CHECK-i386-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc i32 @bitcast_ref(%T7bitcast1CC*) {{.*}} { // CHECK-i386-NEXT: entry: // CHECK-i386-NEXT: [[VALUE:%.*]] = ptrtoint %T7bitcast1CC* %0 to i32 // CHECK-i386-NEXT: ret i32 [[VALUE]] // CHECK-i386-NEXT: } -// CHECK-x86_64-LABEL: define{{( protected)?}} swiftcc i64 @bitcast_ref(%T7bitcast1CC*) {{.*}} { +// CHECK-x86_64-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc i64 @bitcast_ref(%T7bitcast1CC*) {{.*}} { // CHECK-x86_64-NEXT: entry: // CHECK-x86_64-NEXT: [[VALUE:%.*]] = ptrtoint %T7bitcast1CC* %0 to i64 // CHECK-x86_64-NEXT: ret i64 [[VALUE]] @@ -116,7 +116,7 @@ bb0(%0 : $Int, %1 : $Int): return %i3 : $Int } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @unchecked_ref_cast_addr +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @unchecked_ref_cast_addr // CHECK-i386: call i1 @swift_dynamicCast(%swift.opaque* %0, %swift.opaque* %{{.*}}, %swift.type* %T, %swift.type* %U, i32 7) // CHECK-x86_64: call i1 @swift_dynamicCast(%swift.opaque* %0, %swift.opaque* %{{.*}}, %swift.type* %T, %swift.type* %U, i64 7) sil @unchecked_ref_cast_addr : $@convention(thin) (@in T) -> @out U { @@ -130,11 +130,11 @@ bb0(%0 : $*U, %1 : $*T): return %r : $() } -// CHECK-i386-LABEL: define{{( protected)?}} swiftcc i32 @unchecked_ref_cast_class_optional +// CHECK-i386-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc i32 @unchecked_ref_cast_class_optional // CHECK-i386: ptrtoint %T7bitcast1AC* %0 to i32 // CHECK-i386-NEXT: ret i32 -// CHECK-x86_64-LABEL: define{{( protected)?}} swiftcc i64 @unchecked_ref_cast_class_optional +// CHECK-x86_64-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc i64 @unchecked_ref_cast_class_optional // CHECK-x86_64: ptrtoint %T7bitcast1AC* %0 to i64 // CHECK-x86_64-NEXT: ret i64 sil @unchecked_ref_cast_class_optional : $@convention(thin) (@owned A) -> @owned Optional { @@ -143,10 +143,10 @@ bb0(%0 : $A): return %2 : $Optional } -// CHECK-i386-LABEL: define{{( protected)?}} swiftcc i32 @unchecked_ref_cast_optional_optional +// CHECK-i386-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc i32 @unchecked_ref_cast_optional_optional // CHECK-i386: ret i32 %0 -// CHECK-x86_64-LABEL: define{{( protected)?}} swiftcc i64 @unchecked_ref_cast_optional_optional +// CHECK-x86_64-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc i64 @unchecked_ref_cast_optional_optional // CHECK-x86_64: ret i64 %0 sil @unchecked_ref_cast_optional_optional : $@convention(thin) (@owned Optional) -> @owned Optional { bb0(%0 : $Optional): @@ -154,11 +154,11 @@ bb0(%0 : $Optional): return %2 : $Optional } -// CHECK-i386-LABEL: define{{( protected)?}} swiftcc i32 @unchecked_ref_cast_proto_optional +// CHECK-i386-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc i32 @unchecked_ref_cast_proto_optional // CHECK-i386: ptrtoint {{%objc_object|%swift.refcounted}}* %0 to i32 // CHECK-i386-NEXT: ret i32 -// CHECK-x86_64-LABEL: define{{( protected)?}} swiftcc i64 @unchecked_ref_cast_proto_optional +// CHECK-x86_64-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc i64 @unchecked_ref_cast_proto_optional // CHECK-x86_64: ptrtoint {{%objc_object|%swift.refcounted}}* %0 to i64 // CHECK-x86_64-NEXT: ret i64 sil @unchecked_ref_cast_proto_optional : $@convention(thin) (@owned CP) -> @owned Optional { @@ -167,10 +167,10 @@ bb0(%0 : $CP): return %2 : $Optional } -// CHECK-i386-LABEL: define{{( protected)?}} swiftcc i32 @unchecked_ref_cast_optionalproto_optional +// CHECK-i386-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc i32 @unchecked_ref_cast_optionalproto_optional // CHECK-i386: ret i32 %0 -// CHECK-x86_64-LABEL: define{{( protected)?}} swiftcc i64 @unchecked_ref_cast_optionalproto_optional +// CHECK-x86_64-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc i64 @unchecked_ref_cast_optionalproto_optional // CHECK-x86_64: ret i64 %0 sil @unchecked_ref_cast_optionalproto_optional : $@convention(thin) (@owned Optional) -> @owned Optional { bb0(%0 : $Optional): diff --git a/test/IRGen/bitcast_different_size.sil b/test/IRGen/bitcast_different_size.sil index a4a87ea79dfea..eed9fd30fd32d 100644 --- a/test/IRGen/bitcast_different_size.sil +++ b/test/IRGen/bitcast_different_size.sil @@ -7,7 +7,7 @@ sil_stage canonical import Swift -// CHECK-LABEL: define{{( protected)?}} swiftcc i64 @bitcast_different_size1 +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc i64 @bitcast_different_size1 // OPT-LABEL: define{{.*}}@bitcast_different_size1(i32) // OPT: tail call void asm sideeffect "", "n"(i32 0) diff --git a/test/IRGen/boxed_existential.sil b/test/IRGen/boxed_existential.sil index 48155fd727a9b..240f6105c6363 100644 --- a/test/IRGen/boxed_existential.sil +++ b/test/IRGen/boxed_existential.sil @@ -4,7 +4,7 @@ import Swift sil @error_user : $@convention(thin) (@owned Error) -> () -// CHECK-LABEL: define{{( protected)?}} swiftcc void @retain_release_boxed_existential(%swift.error*) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @retain_release_boxed_existential(%swift.error*) sil @retain_release_boxed_existential : $@convention(thin) (@owned Error) -> () { entry(%e : $Error): // CHECK-objc: @swift_errorRetain @@ -18,7 +18,7 @@ entry(%e : $Error): return undef : $() } -// CHECK-LABEL: define{{( protected)?}} swiftcc %swift.error* @alloc_boxed_existential(%swift.opaque* noalias nocapture, %swift.type* %T, i8** %T.Error) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc %swift.error* @alloc_boxed_existential(%swift.opaque* noalias nocapture, %swift.type* %T, i8** %T.Error) sil @alloc_boxed_existential : $@convention(thin) (@in T) -> @owned Error { entry(%x : $*T): // CHECK: [[BOX_PAIR:%.*]] = call swiftcc { %swift.error*, %swift.opaque* } @swift_allocError(%swift.type* %T, i8** %T.Error, %swift.opaque* null, i1 false) @@ -37,7 +37,7 @@ struct SomeError: Error { let _code: Int } -// CHECK-LABEL: define{{( protected)?}} swiftcc %swift.error* @alloc_boxed_existential_concrete +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc %swift.error* @alloc_boxed_existential_concrete sil @alloc_boxed_existential_concrete : $@convention(thin) (@owned SomeError) -> @owned Error { entry(%x : $SomeError): // CHECK: [[BOX_PAIR:%.*]] = call swiftcc { %swift.error*, %swift.opaque* } @swift_allocError(%swift.type* {{.*}} @"$S17boxed_existential9SomeErrorVMf", {{.*}}, i8** {{%.*|@"\$S17boxed_existential9SomeErrorVs0D0AAWP"}}, %swift.opaque* null, i1 false) @@ -51,7 +51,7 @@ entry(%x : $SomeError): return %b : $Error } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @dealloc_boxed_existential(%swift.error*, %swift.type* %T, i8** %T.Error) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @dealloc_boxed_existential(%swift.error*, %swift.type* %T, i8** %T.Error) sil @dealloc_boxed_existential : $@convention(thin) (@owned Error) -> () { entry(%b : $Error): // CHECK: call void @swift_deallocError(%swift.error* %0, %swift.type* %T) @@ -61,7 +61,7 @@ entry(%b : $Error): struct Str : Error { } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @alloc_dealloc_box_with_concrete_type +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @alloc_dealloc_box_with_concrete_type sil @alloc_dealloc_box_with_concrete_type : $@convention(thin) () -> () { bb0: // CHECK: call {{.*}} @swift_allocError @@ -72,7 +72,7 @@ bb0: return %r : $() } -// CHECK-LABEL: define{{( protected)?}} swiftcc {{i[0-9]+}} @project_boxed_existential(%swift.error*) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc {{i[0-9]+}} @project_boxed_existential(%swift.error*) sil @project_boxed_existential : $@convention(thin) (@owned Error) -> Int { entry(%b : $Error): // CHECK: call void @swift_getErrorValue(%swift.error* %0, i8** {{%.*}}, [[TRIPLE:{ %swift.opaque\*, %swift.type\*, i8\*\* }]]* [[OUT:%.*]]) diff --git a/test/IRGen/bridge_object_arm64.sil b/test/IRGen/bridge_object_arm64.sil index 3473c2e71d5e1..1bd3a3d12cc70 100644 --- a/test/IRGen/bridge_object_arm64.sil +++ b/test/IRGen/bridge_object_arm64.sil @@ -25,7 +25,7 @@ entry(%c : $C, %w : $Builtin.Word): } -// CHECK-LABEL: define{{( protected)?}} swiftcc { %objc_object*, i64 } @convert_from_bridge_object +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc { %objc_object*, i64 } @convert_from_bridge_object // CHECK: [[BOBITS:%.*]] = ptrtoint [[BRIDGE:%swift.bridge\*]] %0 to i64 // -- 0x8000_0000_0000_0000 // CHECK: [[TAGBITS:%.*]] = and i64 [[BOBITS]], -9223372036854775808 diff --git a/test/IRGen/bridge_object_armv7.sil b/test/IRGen/bridge_object_armv7.sil index 6139d4e7d7913..1438c8a1e654a 100644 --- a/test/IRGen/bridge_object_armv7.sil +++ b/test/IRGen/bridge_object_armv7.sil @@ -24,7 +24,7 @@ entry(%c : $C, %w : $Builtin.Word): return %b : $Builtin.BridgeObject } -// CHECK-LABEL: define{{( protected)?}} swiftcc { %objc_object*, i32 } @convert_from_bridge_object +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc { %objc_object*, i32 } @convert_from_bridge_object // CHECK: [[BOBITS:%.*]] = ptrtoint [[BRIDGE:%swift.bridge\*]] %0 to i32 // CHECK: [[MASKED_BITS:%.*]] = and i32 [[BOBITS]], -4 // CHECK: inttoptr i32 [[MASKED_BITS]] to [[C:%objc_object\*]] diff --git a/test/IRGen/bridge_object_x86_64.sil b/test/IRGen/bridge_object_x86_64.sil index 49433b006242d..737f32acc4c81 100644 --- a/test/IRGen/bridge_object_x86_64.sil +++ b/test/IRGen/bridge_object_x86_64.sil @@ -11,8 +11,8 @@ sil_vtable C {} @objc protocol ObjC {} -// CHECK-LABEL: define{{( protected)?}} swiftcc void @retain_release_bridge_object -// CHECK: call [[BRIDGE:%swift.bridge\*]] @swift_bridgeObjectRetain +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @retain_release_bridge_object +// CHECK: call [[BRIDGE:%swift.bridge\*]] @swift_bridgeObjectRetain{{.*}}returned // CHECK: call void @swift_bridgeObjectRelease sil @retain_release_bridge_object : $(Builtin.BridgeObject) -> () { entry(%b : $Builtin.BridgeObject): @@ -21,7 +21,7 @@ entry(%b : $Builtin.BridgeObject): return undef : $() } -// CHECK-LABEL: define{{( protected)?}} swiftcc %swift.bridge* @convert_to_bridge_object +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc %swift.bridge* @convert_to_bridge_object // CHECK: [[REFBITS:%.*]] = ptrtoint [[C:%T13bridge_object1CC\*]] %0 to i64 // CHECK: [[OR:%.*]] = or i64 [[REFBITS]], %1 // CHECK: inttoptr i64 [[OR]] to [[BRIDGE]] @@ -31,7 +31,7 @@ entry(%c : $C, %w : $Builtin.Word): return %b : $Builtin.BridgeObject } -// CHECK-LABEL: define{{( protected)?}} swiftcc { %objc_object*, i64 } @convert_from_bridge_object +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc { %objc_object*, i64 } @convert_from_bridge_object // CHECK: [[BOBITS:%.*]] = ptrtoint [[BRIDGE]] %0 to i64 // -- 0x8000_0000_0000_0001 // CHECK: [[TAGBITS:%.*]] = and i64 [[BOBITS]], -9223372036854775807 @@ -56,7 +56,7 @@ entry(%b : $Builtin.BridgeObject): return %t : $(ObjC, Builtin.Word) } -// CHECK-LABEL: define{{( protected)?}} swiftcc %T13bridge_object1CC* @convert_from_native_bridge_object +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc %T13bridge_object1CC* @convert_from_native_bridge_object // CHECK: [[BOBITS:%.*]] = ptrtoint %swift.bridge* %0 to i64 // CHECK: [[MASKED_BITS:%.*]] = and i64 [[BOBITS]], 72057594037927928 // CHECK: [[RESULT:%.*]] = inttoptr i64 [[MASKED_BITS]] to [[C:%T13bridge_object1CC\*]] diff --git a/test/IRGen/builtin_math.swift b/test/IRGen/builtin_math.swift index 04a3a39727ea0..e3a7b1befb0f8 100644 --- a/test/IRGen/builtin_math.swift +++ b/test/IRGen/builtin_math.swift @@ -1,13 +1,23 @@ -// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -emit-ir -O %s | %FileCheck %s - -// XFAIL: linux +// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -emit-ir -O %s | %FileCheck %s -check-prefix CHECK -check-prefix CHECK-%target-os +#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS) import Darwin +#elseif os(Android) || os(Cygwin) || os(FreeBSD) || os(Linux) +import Glibc +#elseif os(Windows) +import MSVCRT +#endif // Make sure we use an intrinsic for functions such as exp. // CHECK-LABEL: define {{.*}}test1 -// CHECK: call float @llvm.exp.f32 +// CHECK-ios: call float @llvm.exp.f32 +// CHECK-macosx: call float @llvm.exp.f32 +// CHECK-tvos: call float @llvm.exp.f32 +// CHECK-watchos: call float @llvm.exp.f32 +// CHECK-darwin: call float @llvm.exp.f32 +// CHECK-linux-gnu: call float @expf +// CHECK-windows: call float @expf public func test1(f : Float) -> Float { return exp(f) @@ -29,7 +39,8 @@ public func test3(d : Double) -> Double { } // CHECK-LABEL: define {{.*}}test4 -// CHECK: call float @llvm.sqrt.f32 +// CHECK-LINUX: call float @llvm.sqrt.f32 +// CHECK-WINDOWS: call float @llvm.sqrt.f32 public func test4(f : Float) -> Float { // This call does not match the signature for the C sqrt function diff --git a/test/IRGen/builtins.swift b/test/IRGen/builtins.swift index 6b05c9d796688..bf70c20d99421 100644 --- a/test/IRGen/builtins.swift +++ b/test/IRGen/builtins.swift @@ -225,12 +225,12 @@ func sizeof_alignof_test() { // CHECK: define hidden {{.*}}void @"$S8builtins27generic_sizeof_alignof_testyyxlF" func generic_sizeof_alignof_test(_: T) { - // CHECK: [[T0:%.*]] = getelementptr inbounds i8*, i8** [[T:%.*]], i32 9 + // CHECK: [[T0:%.*]] = getelementptr inbounds i8*, i8** [[T:%.*]], i32 8 // CHECK-NEXT: [[T1:%.*]] = load i8*, i8** [[T0]] // CHECK-NEXT: [[SIZE:%.*]] = ptrtoint i8* [[T1]] to i64 // CHECK-NEXT: store i64 [[SIZE]], i64* [[S:%.*]] var s = Builtin.sizeof(T.self) - // CHECK: [[T0:%.*]] = getelementptr inbounds i8*, i8** [[T:%.*]], i32 10 + // CHECK: [[T0:%.*]] = getelementptr inbounds i8*, i8** [[T:%.*]], i32 9 // CHECK-NEXT: [[T1:%.*]] = load i8*, i8** [[T0]] // CHECK-NEXT: [[T2:%.*]] = ptrtoint i8* [[T1]] to i64 // CHECK-NEXT: [[T3:%.*]] = and i64 [[T2]], 65535 @@ -241,7 +241,7 @@ func generic_sizeof_alignof_test(_: T) { // CHECK: define hidden {{.*}}void @"$S8builtins21generic_strideof_testyyxlF" func generic_strideof_test(_: T) { - // CHECK: [[T0:%.*]] = getelementptr inbounds i8*, i8** [[T:%.*]], i32 11 + // CHECK: [[T0:%.*]] = getelementptr inbounds i8*, i8** [[T:%.*]], i32 10 // CHECK-NEXT: [[T1:%.*]] = load i8*, i8** [[T0]] // CHECK-NEXT: [[STRIDE:%.*]] = ptrtoint i8* [[T1]] to i64 // CHECK-NEXT: store i64 [[STRIDE]], i64* [[S:%.*]] @@ -410,20 +410,24 @@ func testOnceWithContext(_ p: Builtin.RawPointer, f: @escaping @convention(c) (B class C {} struct S {} +#if _runtime(_ObjC) @objc class O {} @objc protocol OP1 {} @objc protocol OP2 {} +#endif protocol P {} // CHECK-LABEL: define hidden {{.*}}void @"$S8builtins10canBeClass{{[_0-9a-zA-Z]*}}F" func canBeClass(_ f: @escaping (Builtin.Int8) -> (), _: T) { - // CHECK: call {{.*}}void {{%.*}}(i8 1 +#if _runtime(_ObjC) + // CHECK-objc: call {{.*}}void {{%.*}}(i8 1 f(Builtin.canBeClass(O.self)) - // CHECK: call {{.*}}void {{%.*}}(i8 1 + // CHECK-objc: call {{.*}}void {{%.*}}(i8 1 f(Builtin.canBeClass(OP1.self)) typealias ObjCCompo = OP1 & OP2 - // CHECK: call {{.*}}void {{%.*}}(i8 1 + // CHECK-objc: call {{.*}}void {{%.*}}(i8 1 f(Builtin.canBeClass(ObjCCompo.self)) +#endif // CHECK: call {{.*}}void {{%.*}}(i8 0 f(Builtin.canBeClass(S.self)) @@ -431,9 +435,11 @@ func canBeClass(_ f: @escaping (Builtin.Int8) -> (), _: T) { f(Builtin.canBeClass(C.self)) // CHECK: call {{.*}}void {{%.*}}(i8 0 f(Builtin.canBeClass(P.self)) +#if _runtime(_ObjC) typealias MixedCompo = OP1 & P - // CHECK: call {{.*}}void {{%.*}}(i8 0 + // CHECK-objc: call {{.*}}void {{%.*}}(i8 0 f(Builtin.canBeClass(MixedCompo.self)) +#endif // CHECK: call {{.*}}void {{%.*}}(i8 2 f(Builtin.canBeClass(T.self)) @@ -765,7 +771,7 @@ func isUniqueIUO(_ ref: inout Builtin.NativeObject?) -> Bool { // CHECK-LABEL: define {{.*}} @{{.*}}generic_ispod_test func generic_ispod_test(_: T) { - // CHECK: [[T0:%.*]] = getelementptr inbounds i8*, i8** [[T:%.*]], i32 10 + // CHECK: [[T0:%.*]] = getelementptr inbounds i8*, i8** [[T:%.*]], i32 9 // CHECK-NEXT: [[T1:%.*]] = load i8*, i8** [[T0]] // CHECK-NEXT: [[FLAGS:%.*]] = ptrtoint i8* [[T1]] to i64 // CHECK-NEXT: [[ISNOTPOD:%.*]] = and i64 [[FLAGS]], 65536 @@ -858,7 +864,7 @@ func testForceTry(_ fn: () -> ()) { try! createInt(fn) } -// CHECK-LABEL: declare swiftcc void @swift_unexpectedError(%swift.error* +// CHECK-LABEL: declare{{( dllimport)?}} swiftcc void @swift_unexpectedError(%swift.error* enum MyError : Error { case A, B diff --git a/test/IRGen/c_function_pointer.sil b/test/IRGen/c_function_pointer.sil index 6c8d6b5251a51..5cfded0f5b2e1 100644 --- a/test/IRGen/c_function_pointer.sil +++ b/test/IRGen/c_function_pointer.sil @@ -2,13 +2,13 @@ import Swift -// CHECK-LABEL: define{{( protected)?}} void @c_native_function_pointer(void ()*) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} void @c_native_function_pointer(void ()*) sil @c_native_function_pointer : $@convention(c) (@convention(c) () -> ()) -> () { entry(%f : $@convention(c) () -> ()): return undef : $() } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @call_with_native_c_function_pointer(i8*) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @call_with_native_c_function_pointer(i8*) sil @call_with_native_c_function_pointer : $@convention(thin) (@convention(c) () -> ()) -> () { entry(%f : $@convention(c) () -> ()): %c = function_ref @c_native_function_pointer : $@convention(c) (@convention(c) () -> ()) -> () @@ -16,7 +16,7 @@ entry(%f : $@convention(c) () -> ()): return %z : $() } -// CHECK-LABEL: define{{( protected)?}} swiftcc %swift.type* @c_function_pointer_metadata() +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc %swift.type* @c_function_pointer_metadata() sil @c_function_pointer_metadata : $@convention(thin) () -> @thick Any.Type { entry: // CHECK: call swiftcc %swift.metadata_response @"$SyyXCMa"([[INT]] 0) diff --git a/test/IRGen/c_functions.swift b/test/IRGen/c_functions.swift index 6ea6e2f54d137..5f15f3e4b33f9 100644 --- a/test/IRGen/c_functions.swift +++ b/test/IRGen/c_functions.swift @@ -1,6 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -import-objc-header %S/Inputs/c_functions.h -primary-file %s -emit-ir | %FileCheck %s -// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -import-objc-header %S/Inputs/c_functions.h -primary-file %s -emit-ir | %FileCheck %s --check-prefix=%target-cpu +// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -import-objc-header %S/Inputs/c_functions.h -primary-file %s -emit-ir | %FileCheck %s -check-prefix CHECK -check-prefix %target-cpu // This is deliberately not a SIL test so that we can test SILGen too. @@ -19,14 +18,13 @@ func test_indirect_by_val_alignment() { log_a_thing(x) } +// We only want to test x86_64. // x86_64-LABEL: define hidden swiftcc void @"$S11c_functions30test_indirect_by_val_alignmentyyF"() // x86_64: %indirect-temporary = alloca %TSo7a_thinga, align [[ALIGN:[0-9]+]] // x86_64: [[CAST:%.*]] = bitcast %TSo7a_thinga* %indirect-temporary to %struct.a_thing* // x86_64: call void @log_a_thing(%struct.a_thing* byval align [[ALIGN]] [[CAST]]) // x86_64: define internal void @log_a_thing(%struct.a_thing* byval align [[ALIGN]] - -// We only want to test x86_64. // aarch64: define hidden swiftcc void @"$S11c_functions30test_indirect_by_val_alignmentyyF"() // arm64: define hidden swiftcc void @"$S11c_functions30test_indirect_by_val_alignmentyyF"() // armv7k: define hidden swiftcc void @"$S11c_functions30test_indirect_by_val_alignmentyyF"() diff --git a/test/IRGen/c_layout.sil b/test/IRGen/c_layout.sil index aeaf0d657a40d..8ea8a7287deab 100644 --- a/test/IRGen/c_layout.sil +++ b/test/IRGen/c_layout.sil @@ -106,7 +106,7 @@ bb0: sil public_external @createSIMDStruct : $@convention(c) () -> SIMDStruct sil public_external @consumeSIMDStruct : $@convention(c) SIMDStruct -> () -// CHECK-x86_64-LABEL: define{{( protected)?}} swiftcc void @testSIMDStruct() +// CHECK-x86_64-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @testSIMDStruct() // CHECK-x86_64: call <3 x float> @createSIMDStruct // CHECK-x86_64: call void @consumeSIMDStruct(<3 x float> sil @testSIMDStruct : $() -> () { @@ -125,7 +125,7 @@ bb0: return %s : $Builtin.Word } -// CHECK-x86_64-LABEL: define{{( protected)?}} swiftcc void @testIntegerExtension +// CHECK-x86_64-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @testIntegerExtension // CHECK-x86_64: call signext i8 @chareth(i8 signext // CHECK-x86_64: call signext i8 @signedChareth(i8 signext // CHECK-x86_64: call zeroext i8 @unsignedChareth(i8 zeroext @@ -133,15 +133,15 @@ bb0: // CHECK-x86_64: call zeroext i16 @eatMyUnsignedShorts(i16 zeroext // CHECK-x86_64: call i32 @ints(i32 %5) // CHECK-x86_64: call i32 @unsigneds(i32 %6) -// CHECK-x86_64-LABEL: declare signext i8 @chareth(i8 signext) -// CHECK-x86_64-LABEL: declare signext i8 @signedChareth(i8 signext) -// CHECK-x86_64-LABEL: declare zeroext i8 @unsignedChareth(i8 zeroext) -// CHECK-x86_64-LABEL: declare signext i16 @eatMyShorts(i16 signext) -// CHECK-x86_64-LABEL: declare zeroext i16 @eatMyUnsignedShorts(i16 zeroext) -// CHECK-x86_64-LABEL: declare i32 @ints(i32) -// CHECK-x86_64-LABEL: declare i32 @unsigneds(i32) - -// CHECK-i386-LABEL: define{{( protected)?}} swiftcc void @testIntegerExtension +// CHECK-x86_64-LABEL: declare{{( dllimport)?}} signext i8 @chareth(i8 signext) +// CHECK-x86_64-LABEL: declare{{( dllimport)?}} signext i8 @signedChareth(i8 signext) +// CHECK-x86_64-LABEL: declare{{( dllimport)?}} zeroext i8 @unsignedChareth(i8 zeroext) +// CHECK-x86_64-LABEL: declare{{( dllimport)?}} signext i16 @eatMyShorts(i16 signext) +// CHECK-x86_64-LABEL: declare{{( dllimport)?}} zeroext i16 @eatMyUnsignedShorts(i16 zeroext) +// CHECK-x86_64-LABEL: declare{{( dllimport)?}} i32 @ints(i32) +// CHECK-x86_64-LABEL: declare{{( dllimport)?}} i32 @unsigneds(i32) + +// CHECK-i386-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @testIntegerExtension // CHECK-i386: call signext i8 @chareth(i8 signext // CHECK-i386: call signext i8 @signedChareth(i8 signext // CHECK-i386: call zeroext i8 @unsignedChareth(i8 zeroext @@ -149,15 +149,15 @@ bb0: // CHECK-i386: call zeroext i16 @eatMyUnsignedShorts(i16 zeroext // CHECK-i386: call i32 @ints(i32 %5) // CHECK-i386: call i32 @unsigneds(i32 %6) -// CHECK-i386-LABEL: declare signext i8 @chareth(i8 signext) -// CHECK-i386-LABEL: declare signext i8 @signedChareth(i8 signext) -// CHECK-i386-LABEL: declare zeroext i8 @unsignedChareth(i8 zeroext) -// CHECK-i386-LABEL: declare signext i16 @eatMyShorts(i16 signext) -// CHECK-i386-LABEL: declare zeroext i16 @eatMyUnsignedShorts(i16 zeroext) -// CHECK-i386-LABEL: declare i32 @ints(i32) -// CHECK-i386-LABEL: declare i32 @unsigneds(i32) - -// CHECK-armv7-LABEL: define{{( protected)?}} swiftcc void @testIntegerExtension +// CHECK-i386-LABEL: declare{{( dllimport)?}} signext i8 @chareth(i8 signext) +// CHECK-i386-LABEL: declare{{( dllimport)?}} signext i8 @signedChareth(i8 signext) +// CHECK-i386-LABEL: declare{{( dllimport)?}} zeroext i8 @unsignedChareth(i8 zeroext) +// CHECK-i386-LABEL: declare{{( dllimport)?}} signext i16 @eatMyShorts(i16 signext) +// CHECK-i386-LABEL: declare{{( dllimport)?}} zeroext i16 @eatMyUnsignedShorts(i16 zeroext) +// CHECK-i386-LABEL: declare{{( dllimport)?}} i32 @ints(i32) +// CHECK-i386-LABEL: declare{{( dllimport)?}} i32 @unsigneds(i32) + +// CHECK-armv7-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @testIntegerExtension // CHECK-armv7: call signext i8 @chareth(i8 signext // CHECK-armv7: call signext i8 @signedChareth(i8 signext // CHECK-armv7: call zeroext i8 @unsignedChareth(i8 zeroext @@ -165,15 +165,15 @@ bb0: // CHECK-armv7: call zeroext i16 @eatMyUnsignedShorts(i16 zeroext // CHECK-armv7: call i32 @ints(i32 %5) // CHECK-armv7: call i32 @unsigneds(i32 %6) -// CHECK-armv7-LABEL: declare signext i8 @chareth(i8 signext) -// CHECK-armv7-LABEL: declare signext i8 @signedChareth(i8 signext) -// CHECK-armv7-LABEL: declare zeroext i8 @unsignedChareth(i8 zeroext) -// CHECK-armv7-LABEL: declare signext i16 @eatMyShorts(i16 signext) -// CHECK-armv7-LABEL: declare zeroext i16 @eatMyUnsignedShorts(i16 zeroext) -// CHECK-armv7-LABEL: declare i32 @ints(i32) -// CHECK-armv7-LABEL: declare i32 @unsigneds(i32) - -// CHECK-armv7s-LABEL: define{{( protected)?}} swiftcc void @testIntegerExtension +// CHECK-armv7-LABEL: declare{{( dllimport)?}} signext i8 @chareth(i8 signext) +// CHECK-armv7-LABEL: declare{{( dllimport)?}} signext i8 @signedChareth(i8 signext) +// CHECK-armv7-LABEL: declare{{( dllimport)?}} zeroext i8 @unsignedChareth(i8 zeroext) +// CHECK-armv7-LABEL: declare{{( dllimport)?}} signext i16 @eatMyShorts(i16 signext) +// CHECK-armv7-LABEL: declare{{( dllimport)?}} zeroext i16 @eatMyUnsignedShorts(i16 zeroext) +// CHECK-armv7-LABEL: declare{{( dllimport)?}} i32 @ints(i32) +// CHECK-armv7-LABEL: declare{{( dllimport)?}} i32 @unsigneds(i32) + +// CHECK-armv7s-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @testIntegerExtension // CHECK-armv7s: call signext i8 @chareth(i8 signext // CHECK-armv7s: call signext i8 @signedChareth(i8 signext // CHECK-armv7s: call zeroext i8 @unsignedChareth(i8 zeroext @@ -181,15 +181,15 @@ bb0: // CHECK-armv7s: call zeroext i16 @eatMyUnsignedShorts(i16 zeroext // CHECK-armv7s: call i32 @ints(i32 %5) // CHECK-armv7s: call i32 @unsigneds(i32 %6) -// CHECK-armv7s-LABEL: declare signext i8 @chareth(i8 signext) -// CHECK-armv7s-LABEL: declare signext i8 @signedChareth(i8 signext) -// CHECK-armv7s-LABEL: declare zeroext i8 @unsignedChareth(i8 zeroext) -// CHECK-armv7s-LABEL: declare signext i16 @eatMyShorts(i16 signext) -// CHECK-armv7s-LABEL: declare zeroext i16 @eatMyUnsignedShorts(i16 zeroext) -// CHECK-armv7s-LABEL: declare i32 @ints(i32) -// CHECK-armv7s-LABEL: declare i32 @unsigneds(i32) - -// CHECK-armv7k-LABEL: define{{( protected)?}} swiftcc void @testIntegerExtension +// CHECK-armv7s-LABEL: declare{{( dllimport)?}} signext i8 @chareth(i8 signext) +// CHECK-armv7s-LABEL: declare{{( dllimport)?}} signext i8 @signedChareth(i8 signext) +// CHECK-armv7s-LABEL: declare{{( dllimport)?}} zeroext i8 @unsignedChareth(i8 zeroext) +// CHECK-armv7s-LABEL: declare{{( dllimport)?}} signext i16 @eatMyShorts(i16 signext) +// CHECK-armv7s-LABEL: declare{{( dllimport)?}} zeroext i16 @eatMyUnsignedShorts(i16 zeroext) +// CHECK-armv7s-LABEL: declare{{( dllimport)?}} i32 @ints(i32) +// CHECK-armv7s-LABEL: declare{{( dllimport)?}} i32 @unsigneds(i32) + +// CHECK-armv7k-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @testIntegerExtension // CHECK-armv7k: call signext i8 @chareth(i8 signext // CHECK-armv7k: call signext i8 @signedChareth(i8 signext // CHECK-armv7k: call zeroext i8 @unsignedChareth(i8 zeroext @@ -197,15 +197,15 @@ bb0: // CHECK-armv7k: call zeroext i16 @eatMyUnsignedShorts(i16 zeroext // CHECK-armv7k: call i32 @ints(i32 %5) // CHECK-armv7k: call i32 @unsigneds(i32 %6) -// CHECK-armv7k-LABEL: declare signext i8 @chareth(i8 signext) -// CHECK-armv7k-LABEL: declare signext i8 @signedChareth(i8 signext) -// CHECK-armv7k-LABEL: declare zeroext i8 @unsignedChareth(i8 zeroext) -// CHECK-armv7k-LABEL: declare signext i16 @eatMyShorts(i16 signext) -// CHECK-armv7k-LABEL: declare zeroext i16 @eatMyUnsignedShorts(i16 zeroext) -// CHECK-armv7k-LABEL: declare i32 @ints(i32) -// CHECK-armv7k-LABEL: declare i32 @unsigneds(i32) - -// CHECK-arm64-LABEL: define{{( protected)?}} swiftcc void @testIntegerExtension +// CHECK-armv7k-LABEL: declare{{( dllimport)?}} signext i8 @chareth(i8 signext) +// CHECK-armv7k-LABEL: declare{{( dllimport)?}} signext i8 @signedChareth(i8 signext) +// CHECK-armv7k-LABEL: declare{{( dllimport)?}} zeroext i8 @unsignedChareth(i8 zeroext) +// CHECK-armv7k-LABEL: declare{{( dllimport)?}} signext i16 @eatMyShorts(i16 signext) +// CHECK-armv7k-LABEL: declare{{( dllimport)?}} zeroext i16 @eatMyUnsignedShorts(i16 zeroext) +// CHECK-armv7k-LABEL: declare{{( dllimport)?}} i32 @ints(i32) +// CHECK-armv7k-LABEL: declare{{( dllimport)?}} i32 @unsigneds(i32) + +// CHECK-arm64-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @testIntegerExtension // CHECK-arm64: call signext i8 @chareth(i8 signext // CHECK-arm64: call signext i8 @signedChareth(i8 signext // CHECK-arm64: call zeroext i8 @unsignedChareth(i8 zeroext @@ -213,15 +213,15 @@ bb0: // CHECK-arm64: call zeroext i16 @eatMyUnsignedShorts(i16 zeroext // CHECK-arm64: call i32 @ints(i32 %5) // CHECK-arm64: call i32 @unsigneds(i32 %6) -// CHECK-arm64-LABEL: declare signext i8 @chareth(i8 signext) -// CHECK-arm64-LABEL: declare signext i8 @signedChareth(i8 signext) -// CHECK-arm64-LABEL: declare zeroext i8 @unsignedChareth(i8 zeroext) -// CHECK-arm64-LABEL: declare signext i16 @eatMyShorts(i16 signext) -// CHECK-arm64-LABEL: declare zeroext i16 @eatMyUnsignedShorts(i16 zeroext) -// CHECK-arm64-LABEL: declare i32 @ints(i32) -// CHECK-arm64-LABEL: declare i32 @unsigneds(i32) - -// CHECK-aarch64-LABEL: define{{( protected)?}} swiftcc void @testIntegerExtension +// CHECK-arm64-LABEL: declare{{( dllimport)?}} signext i8 @chareth(i8 signext) +// CHECK-arm64-LABEL: declare{{( dllimport)?}} signext i8 @signedChareth(i8 signext) +// CHECK-arm64-LABEL: declare{{( dllimport)?}} zeroext i8 @unsignedChareth(i8 zeroext) +// CHECK-arm64-LABEL: declare{{( dllimport)?}} signext i16 @eatMyShorts(i16 signext) +// CHECK-arm64-LABEL: declare{{( dllimport)?}} zeroext i16 @eatMyUnsignedShorts(i16 zeroext) +// CHECK-arm64-LABEL: declare{{( dllimport)?}} i32 @ints(i32) +// CHECK-arm64-LABEL: declare{{( dllimport)?}} i32 @unsigneds(i32) + +// CHECK-aarch64-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @testIntegerExtension // CHECK-aarch64: call i8 @chareth(i8 %0) // CHECK-aarch64: call i8 @signedChareth(i8 %1) // CHECK-aarch64: call i8 @unsignedChareth(i8 %2) @@ -229,15 +229,15 @@ bb0: // CHECK-aarch64: call i16 @eatMyUnsignedShorts(i16 %4) // CHECK-aarch64: call i32 @ints(i32 %5) // CHECK-aarch64: call i32 @unsigneds(i32 %6) -// CHECK-aarch64-LABEL: declare i8 @chareth(i8) -// CHECK-aarch64-LABEL: declare i8 @signedChareth(i8) -// CHECK-aarch64-LABEL: declare i8 @unsignedChareth(i8) -// CHECK-aarch64-LABEL: declare i16 @eatMyShorts(i16) -// CHECK-aarch64-LABEL: declare i16 @eatMyUnsignedShorts(i16) -// CHECK-aarch64-LABEL: declare i32 @ints(i32) -// CHECK-aarch64-LABEL: declare i32 @unsigneds(i32) - -// CHECK-powerpc64-LABEL: define{{( protected)?}} swiftcc void @testIntegerExtension +// CHECK-aarch64-LABEL: declare{{( dllimport)?}} i8 @chareth(i8) +// CHECK-aarch64-LABEL: declare{{( dllimport)?}} i8 @signedChareth(i8) +// CHECK-aarch64-LABEL: declare{{( dllimport)?}} i8 @unsignedChareth(i8) +// CHECK-aarch64-LABEL: declare{{( dllimport)?}} i16 @eatMyShorts(i16) +// CHECK-aarch64-LABEL: declare{{( dllimport)?}} i16 @eatMyUnsignedShorts(i16) +// CHECK-aarch64-LABEL: declare{{( dllimport)?}} i32 @ints(i32) +// CHECK-aarch64-LABEL: declare{{( dllimport)?}} i32 @unsigneds(i32) + +// CHECK-powerpc64-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @testIntegerExtension // CHECK-powerpc64: call zeroext i8 @chareth(i8 zeroext // CHECK-powerpc64: call zeroext i8 @signedChareth(i8 zeroext // CHECK-powerpc64: call zeroext i8 @unsignedChareth(i8 zeroext @@ -245,15 +245,15 @@ bb0: // CHECK-powerpc64: call zeroext i16 @eatMyUnsignedShorts(i16 zeroext // CHECK-powerpc64: call signext i32 @ints(i32 signext %5) // CHECK-powerpc64: call zeroext i32 @unsigneds(i32 zeroext %6) -// CHECK-powerpc64-LABEL: declare zeroext i8 @chareth(i8 zeroext) -// CHECK-powerpc64-LABEL: declare zeroext i8 @signedChareth(i8 zeroext) -// CHECK-powerpc64-LABEL: declare zeroext i8 @unsignedChareth(i8 zeroext) -// CHECK-powerpc64-LABEL: declare signext i16 @eatMyShorts(i16 signext) -// CHECK-powerpc64-LABEL: declare zeroext i16 @eatMyUnsignedShorts(i16 zeroext) -// CHECK-powerpc64-LABEL: declare signext i32 @ints(i32 signext) -// CHECK-powerpc64-LABEL: declare zeroext i32 @unsigneds(i32 zeroext) - -// CHECK-powerpc64le-LABEL: define{{( protected)?}} swiftcc void @testIntegerExtension +// CHECK-powerpc64-LABEL: declare{{( dllimport)?}} zeroext i8 @chareth(i8 zeroext) +// CHECK-powerpc64-LABEL: declare{{( dllimport)?}} zeroext i8 @signedChareth(i8 zeroext) +// CHECK-powerpc64-LABEL: declare{{( dllimport)?}} zeroext i8 @unsignedChareth(i8 zeroext) +// CHECK-powerpc64-LABEL: declare{{( dllimport)?}} signext i16 @eatMyShorts(i16 signext) +// CHECK-powerpc64-LABEL: declare{{( dllimport)?}} zeroext i16 @eatMyUnsignedShorts(i16 zeroext) +// CHECK-powerpc64-LABEL: declare{{( dllimport)?}} signext i32 @ints(i32 signext) +// CHECK-powerpc64-LABEL: declare{{( dllimport)?}} zeroext i32 @unsigneds(i32 zeroext) + +// CHECK-powerpc64le-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @testIntegerExtension // CHECK-powerpc64le: call zeroext i8 @chareth(i8 zeroext // CHECK-powerpc64le: call zeroext i8 @signedChareth(i8 zeroext // CHECK-powerpc64le: call zeroext i8 @unsignedChareth(i8 zeroext @@ -261,15 +261,15 @@ bb0: // CHECK-powerpc64le: call zeroext i16 @eatMyUnsignedShorts(i16 zeroext // CHECK-powerpc64le: call signext i32 @ints(i32 signext %5) // CHECK-powerpc64le: call zeroext i32 @unsigneds(i32 zeroext %6) -// CHECK-powerpc64le-LABEL: declare zeroext i8 @chareth(i8 zeroext) -// CHECK-powerpc64le-LABEL: declare zeroext i8 @signedChareth(i8 zeroext) -// CHECK-powerpc64le-LABEL: declare zeroext i8 @unsignedChareth(i8 zeroext) -// CHECK-powerpc64le-LABEL: declare signext i16 @eatMyShorts(i16 signext) -// CHECK-powerpc64le-LABEL: declare zeroext i16 @eatMyUnsignedShorts(i16 zeroext) -// CHECK-powerpc64le-LABEL: declare signext i32 @ints(i32 signext) -// CHECK-powerpc64le-LABEL: declare zeroext i32 @unsigneds(i32 zeroext) - -// CHECK-s390x-LABEL: define{{( protected)?}} swiftcc void @testIntegerExtension +// CHECK-powerpc64le-LABEL: declare{{( dllimport)?}} zeroext i8 @chareth(i8 zeroext) +// CHECK-powerpc64le-LABEL: declare{{( dllimport)?}} zeroext i8 @signedChareth(i8 zeroext) +// CHECK-powerpc64le-LABEL: declare{{( dllimport)?}} zeroext i8 @unsignedChareth(i8 zeroext) +// CHECK-powerpc64le-LABEL: declare{{( dllimport)?}} signext i16 @eatMyShorts(i16 signext) +// CHECK-powerpc64le-LABEL: declare{{( dllimport)?}} zeroext i16 @eatMyUnsignedShorts(i16 zeroext) +// CHECK-powerpc64le-LABEL: declare{{( dllimport)?}} signext i32 @ints(i32 signext) +// CHECK-powerpc64le-LABEL: declare{{( dllimport)?}} zeroext i32 @unsigneds(i32 zeroext) + +// CHECK-s390x-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @testIntegerExtension // CHECK-s390x: call zeroext i8 @chareth(i8 zeroext // CHECK-s390x: call zeroext i8 @signedChareth(i8 zeroext // CHECK-s390x: call zeroext i8 @unsignedChareth(i8 zeroext @@ -277,13 +277,13 @@ bb0: // CHECK-s390x: call zeroext i16 @eatMyUnsignedShorts(i16 zeroext // CHECK-s390x: call signext i32 @ints(i32 signext %5) // CHECK-s390x: call zeroext i32 @unsigneds(i32 zeroext %6) -// CHECK-s390x-LABEL: declare zeroext i8 @chareth(i8 zeroext) -// CHECK-s390x-LABEL: declare zeroext i8 @signedChareth(i8 zeroext) -// CHECK-s390x-LABEL: declare zeroext i8 @unsignedChareth(i8 zeroext) -// CHECK-s390x-LABEL: declare signext i16 @eatMyShorts(i16 signext) -// CHECK-s390x-LABEL: declare zeroext i16 @eatMyUnsignedShorts(i16 zeroext) -// CHECK-s390x-LABEL: declare signext i32 @ints(i32 signext) -// CHECK-s390x-LABEL: declare zeroext i32 @unsigneds(i32 zeroext) +// CHECK-s390x-LABEL: declare{{( dllimport)?}} zeroext i8 @chareth(i8 zeroext) +// CHECK-s390x-LABEL: declare{{( dllimport)?}} zeroext i8 @signedChareth(i8 zeroext) +// CHECK-s390x-LABEL: declare{{( dllimport)?}} zeroext i8 @unsignedChareth(i8 zeroext) +// CHECK-s390x-LABEL: declare{{( dllimport)?}} signext i16 @eatMyShorts(i16 signext) +// CHECK-s390x-LABEL: declare{{( dllimport)?}} zeroext i16 @eatMyUnsignedShorts(i16 zeroext) +// CHECK-s390x-LABEL: declare{{( dllimport)?}} signext i32 @ints(i32 signext) +// CHECK-s390x-LABEL: declare{{( dllimport)?}} zeroext i32 @unsigneds(i32 zeroext) sil @testIntegerExtension : $@convention(thin) (CChar, CSignedChar, CUnsignedChar, CShort, CUnsignedShort, CInt, CUnsignedInt) -> () { entry(%a : $CChar, %b : $CSignedChar, %c : $CUnsignedChar, %d : $CShort, %e : $CUnsignedShort, %f : $CInt, %g : $CUnsignedInt): @@ -311,7 +311,7 @@ entry(%a : $CChar, %b : $CSignedChar, %c : $CUnsignedChar, %d : $CShort, %e : $C return undef : $() } -// CHECK-x86_64-LABEL: define{{( protected)?}} swiftcc i8 @testIntegerExtensionInBlock(%objc_block*, i8) +// CHECK-x86_64-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc i8 @testIntegerExtensionInBlock(%objc_block*, i8) sil @testIntegerExtensionInBlock : $@convention(thin) (@owned @convention(block) (CChar) -> CChar, CChar) -> CChar { entry(%b : $@convention(block) (CChar) -> CChar, %c : $CChar): // CHECK-x86_64: call signext i8 {{%.*}}(%objc_block* {{%.*}}, i8 signext {{%.*}}) @@ -319,7 +319,7 @@ entry(%b : $@convention(block) (CChar) -> CChar, %c : $CChar): return %r : $CChar } -// CHECK-x86_64-LABEL: define{{( protected)?}} swiftcc void @testBitfieldInBlock +// CHECK-x86_64-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @testBitfieldInBlock // CHECK-x86_64: call void {{%.*}}(%TSo11BitfieldOneV* noalias nocapture sret {{%.*}}, %objc_block* {{%.*}}, %TSo11BitfieldOneV* byval align 8 {{%.*}}) sil @testBitfieldInBlock : $@convention(thin) (@owned @convention(block) (BitfieldOne) -> BitfieldOne, BitfieldOne) -> BitfieldOne { entry(%b : $@convention(block) (BitfieldOne) -> BitfieldOne, %x : $BitfieldOne): diff --git a/test/IRGen/casts.sil b/test/IRGen/casts.sil index 1d8b0de90a438..bdcb63370850e 100644 --- a/test/IRGen/casts.sil +++ b/test/IRGen/casts.sil @@ -1,7 +1,6 @@ -// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil %s -emit-ir -disable-objc-attr-requires-foundation-module | %FileCheck %s -DINT=i%target-ptrsize +// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil %s -emit-ir -enable-objc-interop -disable-objc-attr-requires-foundation-module | %FileCheck %s -DINT=i%target-ptrsize // REQUIRES: CPU=i386 || CPU=x86_64 -// XFAIL: linux sil_stage canonical @@ -16,7 +15,7 @@ class B: A {} sil_vtable A {} sil_vtable B {} -// CHECK-LABEL: define{{( protected)?}} swiftcc %T5casts1BC* @unchecked_addr_cast(%T5casts1AC** noalias nocapture dereferenceable({{.*}})) {{.*}} { +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc %T5casts1BC* @unchecked_addr_cast(%T5casts1AC** noalias nocapture dereferenceable({{.*}})) {{.*}} { // CHECK: bitcast %T5casts1AC** %0 to %T5casts1BC** sil @unchecked_addr_cast : $(@in A) -> B { entry(%a : $*A): @@ -29,7 +28,7 @@ protocol CP: class {} protocol CP2: class {} @objc protocol OP: class {} -// CHECK-LABEL: define{{( protected)?}} swiftcc i8* @ref_to_raw_pointer_existential(%objc_object*, i8**) {{.*}} { +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc i8* @ref_to_raw_pointer_existential(%objc_object*, i8**) {{.*}} { // CHECK: [[CAST:%.*]] = bitcast %objc_object* %0 to i8* // CHECK: ret i8* [[CAST]] sil @ref_to_raw_pointer_existential : $@convention(thin) (@owned CP) -> Builtin.RawPointer { @@ -38,7 +37,7 @@ entry(%p : $CP): return %r : $Builtin.RawPointer } -// CHECK-LABEL: define{{( protected)?}} swiftcc %objc_object* @raw_pointer_to_ref_existential(i8*) {{.*}} { +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc %objc_object* @raw_pointer_to_ref_existential(i8*) {{.*}} { // CHECK: [[CAST:%.*]] = bitcast i8* %0 to %objc_object* // CHECK: ret %objc_object* [[CAST]] sil @raw_pointer_to_ref_existential : $@convention(thin) (@owned Builtin.RawPointer) -> AnyObject { @@ -53,9 +52,9 @@ entry(%n : $Builtin.NativeObject): return %r : $AnyObject } -// CHECK-LABEL: define{{( protected)?}} swiftcc { %objc_object*, i8** } @u_cast_to_class_existential(%objc_object*) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc { %objc_object*, i8** } @u_cast_to_class_existential(%objc_object*) // CHECK: call { i8*, i8** } @dynamic_cast_existential_1_unconditional(i8* {{%.*}}, %swift.type* {{%.*}}, %swift.protocol* @"$S5casts2CPMp") -// CHECK-LABEL: define{{( protected)?}} private { i8*, i8** } @dynamic_cast_existential_1_unconditional(i8*, %swift.type*, %swift.protocol*) {{.*}} { +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} private { i8*, i8** } @dynamic_cast_existential_1_unconditional(i8*, %swift.type*, %swift.protocol*) {{.*}} { // CHECK: [[WITNESS:%.*]] = call i8** @swift_conformsToProtocol(%swift.type* %1, %swift.protocol* %2) // CHECK: [[IS_NULL:%.*]] = icmp eq i8** [[WITNESS]], null // CHECK: br i1 [[IS_NULL]], label %fail, label %cont @@ -71,7 +70,7 @@ entry(%a : $AnyObject): return %p : $CP } -// CHECK-LABEL: define{{( protected)?}} swiftcc { %swift.type*, i8** } @u_cast_to_existential_metatype(%swift.type*) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc { %swift.type*, i8** } @u_cast_to_existential_metatype(%swift.type*) // CHECK: call { i8*, i8** } @dynamic_cast_existential_1_unconditional(i8* %1, %swift.type* %0, %swift.protocol* @"$S5casts2CPMp") sil @u_cast_to_existential_metatype : $@convention(thin) (@owned @thick Any.Type) -> @owned @thick CP.Type { entry(%a : $@thick Any.Type): @@ -79,9 +78,9 @@ entry(%a : $@thick Any.Type): return %p : $@thick CP.Type } -// CHECK-LABEL: define{{( protected)?}} swiftcc { %objc_object*, i8**, i8** } @u_cast_to_class_existential_2(%objc_object*) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc { %objc_object*, i8**, i8** } @u_cast_to_class_existential_2(%objc_object*) // CHECK: call { i8*, i8**, i8** } @dynamic_cast_existential_2_unconditional(i8* {{%.*}}, %swift.type* {{%.*}}, %swift.protocol* @"$S5casts2CPMp", %swift.protocol* @"$S5casts3CP2Mp") -// CHECK-LABEL: define{{( protected)?}} private { i8*, i8**, i8** } @dynamic_cast_existential_2_unconditional(i8*, %swift.type*, %swift.protocol*, %swift.protocol*) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} private { i8*, i8**, i8** } @dynamic_cast_existential_2_unconditional(i8*, %swift.type*, %swift.protocol*, %swift.protocol*) // CHECK: [[WITNESS:%.*]] = call i8** @swift_conformsToProtocol(%swift.type* %1, %swift.protocol* %2) // CHECK: [[IS_NULL:%.*]] = icmp eq i8** [[WITNESS]], null // CHECK: br i1 [[IS_NULL]], label %fail, label %cont @@ -99,7 +98,7 @@ entry(%a : $AnyObject): return %p : $CP & CP2 } -// CHECK-LABEL: define{{( protected)?}} swiftcc { %objc_object*, i8**, i8** } @u_cast_to_class_existential_mixed(%objc_object*) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc { %objc_object*, i8**, i8** } @u_cast_to_class_existential_mixed(%objc_object*) // CHECK: call %objc_object* @swift_dynamicCastObjCProtocolUnconditional // CHECK: call { i8*, i8**, i8** } @dynamic_cast_existential_2_unconditional(i8* {{%.*}}, %swift.type* {{%.*}}, %swift.protocol* @"$S5casts2CPMp", %swift.protocol* @"$S5casts3CP2Mp") sil @u_cast_to_class_existential_mixed : $@convention(thin) (@owned AnyObject) -> @owned CP & OP & CP2 { @@ -108,7 +107,7 @@ entry(%a : $AnyObject): return %p : $CP & OP & CP2 } -// CHECK-LABEL: define{{( protected)?}} swiftcc { %swift.type*, i8**, i8** } @u_cast_to_existential_metatype_mixed(%swift.type*) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc { %swift.type*, i8**, i8** } @u_cast_to_existential_metatype_mixed(%swift.type*) // CHECK: call %swift.type* @swift_dynamicCastTypeToObjCProtocolUnconditional(%swift.type* %0, {{(i32|i64)}} 1, i8** {{%.*}}) // CHECK: [[CAST:%.*]] = call { i8*, i8**, i8** } @dynamic_cast_existential_2_unconditional(i8* {{.*}}, %swift.type* %0, %swift.protocol* @"$S5casts2CPMp", %swift.protocol* @"$S5casts3CP2Mp") // CHECK: [[OBJPTR:%.*]] = extractvalue { i8*, i8**, i8** } [[CAST]], 0 @@ -121,9 +120,9 @@ entry(%a : $@thick Any.Type): } -// CHECK-LABEL: define{{( protected)?}} swiftcc { %objc_object*, i8** } @c_cast_to_class_existential(%objc_object*) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc { %objc_object*, i8** } @c_cast_to_class_existential(%objc_object*) // CHECK: call { i8*, i8** } @dynamic_cast_existential_1_conditional(i8* {{.*}}, %swift.type* %.Type, %swift.protocol* @"$S5casts2CPMp") -// CHECK-LABEL: define{{( protected)?}} private { i8*, i8** } @dynamic_cast_existential_1_conditional(i8*, %swift.type*, %swift.protocol*) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} private { i8*, i8** } @dynamic_cast_existential_1_conditional(i8*, %swift.type*, %swift.protocol*) // CHECK: [[WITNESS:%.*]] = call i8** @swift_conformsToProtocol(%swift.type* %1, %swift.protocol* %2) // CHECK: [[IS_NULL:%.*]] = icmp eq i8** [[WITNESS]], null // CHECK: br i1 [[IS_NULL]], label %fail, label %cont @@ -140,7 +139,7 @@ nay: unreachable } -// CHECK-LABEL: define{{( protected)?}} swiftcc { %swift.type*, i8** } @c_cast_to_existential_metatype(%swift.type*) {{.*}} { +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc { %swift.type*, i8** } @c_cast_to_existential_metatype(%swift.type*) {{.*}} { // CHECK: call { i8*, i8** } @dynamic_cast_existential_1_conditional(i8* %1, %swift.type* %0, %swift.protocol* @"$S5casts2CPMp") sil @c_cast_to_existential_metatype : $@convention(thin) (@owned @thick Any.Type) -> @owned @thick CP.Type { entry(%a : $@thick Any.Type): @@ -151,9 +150,9 @@ nay: unreachable } -// CHECK-LABEL: define{{( protected)?}} swiftcc { %objc_object*, i8**, i8** } @c_cast_to_class_existential_2(%objc_object*) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc { %objc_object*, i8**, i8** } @c_cast_to_class_existential_2(%objc_object*) // CHECK: call { i8*, i8**, i8** } @dynamic_cast_existential_2_conditional(i8* {{%.*}}, %swift.type* {{%.*}}, %swift.protocol* @"$S5casts2CPMp", %swift.protocol* @"$S5casts3CP2Mp") -// CHECK-LABEL: define{{( protected)?}} private { i8*, i8**, i8** } @dynamic_cast_existential_2_conditional(i8*, %swift.type*, %swift.protocol*, %swift.protocol*) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} private { i8*, i8**, i8** } @dynamic_cast_existential_2_conditional(i8*, %swift.type*, %swift.protocol*, %swift.protocol*) // CHECK: [[WITNESS:%.*]] = call i8** @swift_conformsToProtocol(%swift.type* %1, %swift.protocol* %2) // CHECK: [[IS_NULL:%.*]] = icmp eq i8** [[WITNESS]], null // CHECK: br i1 [[IS_NULL]], label %fail, label %cont @@ -174,7 +173,7 @@ nay: unreachable } -// CHECK-LABEL: define{{( protected)?}} swiftcc { %objc_object*, i8**, i8** } @c_cast_to_class_existential_mixed(%objc_object*) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc { %objc_object*, i8**, i8** } @c_cast_to_class_existential_mixed(%objc_object*) // CHECK: [[CAST:%.*]] = call %objc_object* @swift_dynamicCastObjCProtocolConditional // CHECK: [[IS_NULL:%.*]] = icmp eq %objc_object* [[CAST]], null // CHECK: br i1 [[IS_NULL]], label %cont, label %success @@ -192,7 +191,7 @@ nay: unreachable } -// CHECK-LABEL: define{{( protected)?}} swiftcc { %swift.type*, i8**, i8** } @c_cast_to_existential_metatype_mixed(%swift.type*) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc { %swift.type*, i8**, i8** } @c_cast_to_existential_metatype_mixed(%swift.type*) // CHECK: [[OBJC_CAST:%.*]] = call %swift.type* @swift_dynamicCastTypeToObjCProtocolConditional(%swift.type* %0, {{(i32|i64)}} 1, i8** {{%.*}}) // CHECK: [[IS_NULL:%.*]] = icmp eq %swift.type* [[OBJC_CAST]], null // CHECK: br i1 [[IS_NULL]], label %cont, label %success @@ -207,7 +206,7 @@ nay: unreachable } -// CHECK-LABEL: define{{( protected)?}} swiftcc %objc_object* @checked_upcast(%T5casts1AC*) {{.*}} { +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc %objc_object* @checked_upcast(%T5casts1AC*) {{.*}} { // -- Don't need to check conformance of an object to AnyObject. // CHECK-NOT: call %objc_object* @swift_dynamicCastObjCProtocolConditional // CHECK: phi %objc_object* @@ -220,7 +219,7 @@ nay: unreachable } -// CHECK-LABEL: define{{( protected)?}} swiftcc %T5casts1AC* @checked_downcast_optional({{(i32|i64)}}) {{.*}} { +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc %T5casts1AC* @checked_downcast_optional({{(i32|i64)}}) {{.*}} { // CHECK: [[T0:%.*]] = inttoptr {{(i32|i64)}} %0 to %T5casts1AC* // CHECK: [[OBJ_PTR:%.*]] = bitcast %T5casts1AC* [[T0]] to i8* // CHECK: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$S5casts1ACMa"([[INT]] 0) @@ -239,7 +238,7 @@ nay: unreachable } -// CHECK-LABEL: define{{( protected)?}} swiftcc %swift.type* @checked_downcast_optional_metatype({{(i32|i64)}}) {{.*}} { +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc %swift.type* @checked_downcast_optional_metatype({{(i32|i64)}}) {{.*}} { // CHECK: [[VALUE:%.*]] = inttoptr {{(i32|i64)}} %0 to %swift.type* // CHECK: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$S5casts1BCMa"([[INT]] 0) // CHECK: [[METADATA:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 @@ -255,7 +254,7 @@ nay: unreachable } -// CHECK-LABEL: define{{( protected)?}} swiftcc %swift.type* @checked_downcast_optional_exmetatype({{(i32, i32|i64, i64)}}) {{.*}} { +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc %swift.type* @checked_downcast_optional_exmetatype({{(i32, i32|i64, i64)}}) {{.*}} { // CHECK: [[VALUE:%.*]] = inttoptr {{(i32|i64)}} %0 to %swift.type* // CHECK: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$S5casts1BCMa"([[INT]] 0) // CHECK: [[METADATA:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 @@ -271,7 +270,32 @@ nay: unreachable } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @checked_metatype_to_object_casts +// CHECK: define {{(dllexport )?}}{{(protected )?}}swiftcc {{.*}} @checked_downcast_optional_class_to_ex([[INT]]) +// CHECK: entry: +// CHECK: [[V1:%.*]] = inttoptr [[INT]] %0 to %T5casts1AC* +// CHECK: [[V2:%.*]] = icmp ne %T5casts1AC* [[V1]], null +// CHECK: br i1 [[V2]], label %[[LBL:.*]], label +// CHECK: [[LBL]]: +// CHECK: [[V4:%.*]] = bitcast %T5casts1AC* [[V1]] to %swift.type** +// CHECK: load %swift.type*, %swift.type** [[V4]] +sil @checked_downcast_optional_class_to_ex : $@convention(thin) (@guaranteed Optional) -> @owned Optional { +bb0(%0 : $Optional): + checked_cast_br %0 : $Optional to $CP, bb1, bb2 + +bb1(%3 : $CP): + %4 = enum $Optional, #Optional.some!enumelt.1, %3 : $CP + retain_value %0 : $Optional + br bb3(%4 : $Optional) + +bb2: + %7 = enum $Optional, #Optional.none!enumelt + br bb3(%7 : $Optional) + +bb3(%9 : $Optional): + return %9 : $Optional +} + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @checked_metatype_to_object_casts sil @checked_metatype_to_object_casts : $@convention(thin) (@thick Any.Type) -> () { entry(%e : $@thick Any.Type): %a = metatype $@thin NotClass.Type @@ -320,7 +344,7 @@ entry(%x : $OB): return %x : $OB } -// CHECK-LABEL: define{{( protected)?}} swiftcc %T5casts1BC* @checked_object_to_object_casts(%T5casts1AC*) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc %T5casts1BC* @checked_object_to_object_casts(%T5casts1AC*) // CHECK: @swift_dynamicCastClassUnconditional sil @checked_object_to_object_casts : $@convention(thin) (A) -> B { entry(%a : $A): @@ -328,7 +352,7 @@ entry(%a : $A): return %b : $B } -// CHECK-LABEL: define{{( protected)?}} swiftcc %T5casts2OBC* @checked_objc_object_to_object_casts(%T5casts2OAC*) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc %T5casts2OBC* @checked_objc_object_to_object_casts(%T5casts2OAC*) // CHECK: @swift_dynamicCastClassUnconditional sil @checked_objc_object_to_object_casts : $@convention(thin) (OA) -> OB { entry(%a : $OA): diff --git a/test/IRGen/cf.sil b/test/IRGen/cf.sil index d3234db804c19..7916e58514bd4 100644 --- a/test/IRGen/cf.sil +++ b/test/IRGen/cf.sil @@ -1,9 +1,8 @@ // RUN: %empty-directory(%t) -// RUN: %utils/chex.py < %s > %t/cf.sil -// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -sdk %S/Inputs %t/cf.sil -emit-ir -import-cf-types | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize %t/cf.sil -DINT=i%target-ptrsize +// RUN: %{python} %utils/chex.py < %s > %t/cf.sil +// RUN: %target-swift-frontend -enable-objc-interop -assume-parsing-unqualified-ownership-sil -sdk %S/Inputs %t/cf.sil -emit-ir -import-cf-types | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize %t/cf.sil -DINT=i%target-ptrsize // REQUIRES: CPU=i386 || CPU=x86_64 -// REQUIRES: objc_interop // CHECK: [[TYPE:%swift.type]] = type // CHECK: [[REFRIGERATOR:%TSo17CCRefrigeratorRefa]] = type @@ -47,7 +46,7 @@ bb0(%0 : $CCRefrigerator, %1: $CCMutableRefrigerator): return %5 : $() } -// CHECK: define{{( protected)?}} swiftcc void @call_generic([[REFRIGERATOR]]*, [[MUTABLE_REFRIGERATOR]]*) {{.*}} { +// CHECK: define{{( dllexport)?}}{{( protected)?}} swiftcc void @call_generic([[REFRIGERATOR]]*, [[MUTABLE_REFRIGERATOR]]*) {{.*}} { // CHECK: [[T0:%.*]] = bitcast [[REFRIGERATOR]]* %0 to [[OBJC]]* // CHECK-NEXT: [[T1:%.*]] = call swiftcc %swift.metadata_response @"$SSo17CCRefrigeratorRefaMa"([[INT]] 0) // CHECK-NEXT: [[T2:%.*]] = extractvalue %swift.metadata_response [[T1]], 0 diff --git a/test/IRGen/cf_members.sil b/test/IRGen/cf_members.sil index 5140197390732..ddc3129795f90 100644 --- a/test/IRGen/cf_members.sil +++ b/test/IRGen/cf_members.sil @@ -1,5 +1,4 @@ // RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -emit-ir -verify -I %S/../IDE/Inputs/custom-modules %s -// REQUIRES: objc_interop sil_stage canonical diff --git a/test/IRGen/clang_inline.swift b/test/IRGen/clang_inline.swift index 6212769efdab6..875bf9c006358 100644 --- a/test/IRGen/clang_inline.swift +++ b/test/IRGen/clang_inline.swift @@ -1,12 +1,13 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -sdk %S/Inputs -primary-file %s -O -disable-sil-perf-optzns -disable-llvm-optzns -emit-ir | %FileCheck %s +// RUN: %build-irgen-test-overlays +// RUN: %target-swift-frontend -enable-objc-interop -assume-parsing-unqualified-ownership-sil -sdk %S/Inputs -primary-file %s -O -disable-sil-perf-optzns -disable-llvm-optzns -emit-ir -Xcc -fstack-protector -I %t | %FileCheck %s // RUN: %empty-directory(%t/Empty.framework/Modules/Empty.swiftmodule) -// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -emit-module-path %t/Empty.framework/Modules/Empty.swiftmodule/%target-swiftmodule-name %S/../Inputs/empty.swift -module-name Empty -// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -sdk %S/Inputs -primary-file %s -F %t -DIMPORT_EMPTY -O -disable-sil-perf-optzns -disable-llvm-optzns -emit-ir | %FileCheck %s +// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -emit-module-path %t/Empty.framework/Modules/Empty.swiftmodule/%target-swiftmodule-name %S/../Inputs/empty.swift -module-name Empty -I %t +// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -sdk %S/Inputs -primary-file %s -I %t -F %t -DIMPORT_EMPTY -O -disable-sil-perf-optzns -disable-llvm-optzns -emit-ir -Xcc -fstack-protector -enable-objc-interop | %FileCheck %s // REQUIRES: CPU=i386 || CPU=x86_64 -// XFAIL: linux +// REQUIRES: objc_interop #if IMPORT_EMPTY import Empty diff --git a/test/IRGen/clang_inline_reverse.swift b/test/IRGen/clang_inline_reverse.swift index d1968841b96a1..98611dd429a89 100644 --- a/test/IRGen/clang_inline_reverse.swift +++ b/test/IRGen/clang_inline_reverse.swift @@ -1,9 +1,11 @@ // Same test as clang_inline.swift, but with the order swapped. -// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -sdk %S/Inputs -primary-file %s -emit-ir -module-name clang_inline | %FileCheck %s +// RUN: %empty-directory(%t) +// RUN: %build-irgen-test-overlays +// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -sdk %S/Inputs -primary-file %s -enable-objc-interop -emit-ir -module-name clang_inline -I %t | %FileCheck %s // REQUIRES: CPU=i386 || CPU=x86_64 -// XFAIL: linux +// REQUIRES: objc_interop import gizmo diff --git a/test/IRGen/class.sil b/test/IRGen/class.sil index 703761d0fbd30..81d8ba31732a8 100644 --- a/test/IRGen/class.sil +++ b/test/IRGen/class.sil @@ -1,7 +1,6 @@ -// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -emit-ir %s | %FileCheck %s +// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -enable-objc-interop -emit-ir %s | %FileCheck %s // REQUIRES: CPU=x86_64 -// REQUIRES: objc_interop import Builtin import Swift @@ -44,7 +43,7 @@ sil_vtable C {} // \ CHECK: }> // Destroying destructor -// CHECK: define{{( protected)?}} swiftcc [[REF]]* @"$S5class1CCfd"([[C_CLASS]]* swiftself) {{.*}} { +// CHECK: define{{( dllexport)?}}{{( protected)?}} swiftcc [[REF]]* @"$S5class1CCfd"([[C_CLASS]]* swiftself) {{.*}} { // CHECK-NEXT: entry: // CHECK-NEXT: [[OBJ_PTR:%[a-zA-Z0-9]+]] = bitcast [[C_CLASS]]* %0 to [[REF]]* // CHECK-NEXT: ret [[REF]]* [[OBJ_PTR]] @@ -55,7 +54,7 @@ bb0(%0 : $C): } // Deallocating destructor -// CHECK: define{{( protected)?}} swiftcc void @"$S5class1CCfD"([[C_CLASS]]* swiftself) +// CHECK: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$S5class1CCfD"([[C_CLASS]]* swiftself) sil @$S5class1CCfD : $@convention(method) (@owned C) -> () { bb0(%0 : $C): // CHECK-NEXT: entry @@ -72,7 +71,7 @@ bb0(%0 : $C): return %5 : $() // id: %6 } -// CHECK: define{{( protected)?}} swiftcc [[REF]]* @unchecked_ref_cast_cast([[C_CLASS]]*) +// CHECK: define{{( dllexport)?}}{{( protected)?}} swiftcc [[REF]]* @unchecked_ref_cast_cast([[C_CLASS]]*) // CHECK: bitcast [[C_CLASS]]* {{%.*}} to [[REF]]* sil @unchecked_ref_cast_cast : $@convention(thin) C -> Builtin.NativeObject { entry(%c : $C): @@ -80,7 +79,7 @@ entry(%c : $C): return %r : $Builtin.NativeObject } -// CHECK: define{{( protected)?}} swiftcc [[OBJCOBJ]]* @ref_to_objc_pointer_cast([[C_CLASS]]*) +// CHECK: define{{( dllexport)?}}{{( protected)?}} swiftcc [[OBJCOBJ]]* @ref_to_objc_pointer_cast([[C_CLASS]]*) // CHECK: bitcast [[C_CLASS]]* %0 to [[OBJCOBJ]]* sil @ref_to_objc_pointer_cast : $@convention(thin) C -> Builtin.UnknownObject { entry(%c : $C): @@ -88,7 +87,7 @@ entry(%c : $C): return %r : $Builtin.UnknownObject } -// CHECK-LABEL: define{{( protected)?}} swiftcc %T5class1CC* @alloc_ref_dynamic(%swift.type*) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc %T5class1CC* @alloc_ref_dynamic(%swift.type*) sil @alloc_ref_dynamic : $@convention(thin) (@thick C.Type) -> @owned C { bb0(%0 : $@thick C.Type): // CHECK: [[META_PTR:%[0-9]+]] = bitcast %swift.type* %0 to i8* @@ -105,7 +104,7 @@ bb0(%0 : $@thick C.Type): return %1 : $C } -// CHECK-LABEL: define{{( protected)?}} swiftcc %T5class1CC* @autorelease(%T5class1CC*) {{.*}} { +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc %T5class1CC* @autorelease(%T5class1CC*) {{.*}} { // CHECK: %1 = bitcast %T5class1CC* %0 to %objc_object* // CHECK: call %objc_object* @objc_autorelease(%objc_object* %1) // CHECK: ret %T5class1CC* %0 @@ -115,7 +114,7 @@ entry(%c : $C): return %c : $C } -// CHECK-LABEL: define{{( protected)?}} swiftcc i64 @autorelease_optional(i64) {{.*}} { +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc i64 @autorelease_optional(i64) {{.*}} { // CHECK: %1 = inttoptr i64 %0 to %objc_object* // CHECK: call %objc_object* @objc_autorelease(%objc_object* %1) // CHECK: ret i64 %0 @@ -163,7 +162,7 @@ entry(%x : $ClassConstrainedGenericField): %b = load %a : $*ClassConstraintConformance return %b : $ClassConstraintConformance } -// CHECK-LABEL: define{{( protected)?}} swiftcc %T5class26ClassConstraintConformanceC* @fixed_class_generic_field(%T5class28ClassConstrainedGenericFieldCyAA0B21ConstraintConformanceCG*) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc %T5class26ClassConstraintConformanceC* @fixed_class_generic_field(%T5class28ClassConstrainedGenericFieldCyAA0B21ConstraintConformanceCG*) // CHECK: [[FIELD_ADDR_GENERIC:%.*]] = getelementptr inbounds %T5class28ClassConstrainedGenericFieldCyAA0B21ConstraintConformanceCG, %T5class28ClassConstrainedGenericFieldCyAA0B21ConstraintConformanceCG* %0, i32 0, i32 1 // CHECK: load %T5class26ClassConstraintConformanceC*, %T5class26ClassConstraintConformanceC** [[FIELD_ADDR_GENERIC]] diff --git a/test/IRGen/class_bounded_generics.swift b/test/IRGen/class_bounded_generics.swift index 8edf2424728c0..f1952830e8459 100644 --- a/test/IRGen/class_bounded_generics.swift +++ b/test/IRGen/class_bounded_generics.swift @@ -1,8 +1,6 @@ - -// RUN: %target-swift-frontend -module-name class_bounded_generics -emit-ir -primary-file %s -disable-objc-attr-requires-foundation-module | %FileCheck %s -DINT=i%target-ptrsize +// RUN: %target-swift-frontend -module-name class_bounded_generics -enable-objc-interop -emit-ir -primary-file %s -disable-objc-attr-requires-foundation-module | %FileCheck %s -DINT=i%target-ptrsize // REQUIRES: CPU=x86_64 -// XFAIL: linux protocol ClassBound : class { func classBoundMethod() diff --git a/test/IRGen/class_isa_pointers.sil b/test/IRGen/class_isa_pointers.sil index 934bfb0cb4737..abffbb7b92830 100644 --- a/test/IRGen/class_isa_pointers.sil +++ b/test/IRGen/class_isa_pointers.sil @@ -17,7 +17,7 @@ class Purebred { } sil_vtable Purebred {} -// CHECK-LABEL: define{{( protected)?}} swiftcc void @purebred_method(%T18class_isa_pointers8PurebredC*) {{.*}} { +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @purebred_method(%T18class_isa_pointers8PurebredC*) {{.*}} { // CHECK: [[ISA_PTR:%.*]] = bitcast %T18class_isa_pointers8PurebredC* %0 to %swift.type** // CHECK: [[ISA:%.*]] = load %swift.type*, %swift.type** [[ISA_PTR]] // CHECK: [[VTABLE:%.*]] = bitcast %swift.type* [[ISA]] @@ -38,7 +38,7 @@ class Mongrel: Gizmo { } sil_vtable Mongrel {} -// CHECK-LABEL: define{{( protected)?}} swiftcc void @mongrel_method(%T18class_isa_pointers7MongrelC*) {{.*}} { +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @mongrel_method(%T18class_isa_pointers7MongrelC*) {{.*}} { // CHECK: [[T0:%.*]] = bitcast {{.*}} %0 to i64* // CHECK-NEXT: [[T1:%.*]] = load i64, i64* [[T0]], align 8 // CHECK-NEXT: [[T2:%.*]] = load i64, i64* @swift_isaMask, align 8 diff --git a/test/IRGen/class_isa_pointers_armv7k_watchos.sil b/test/IRGen/class_isa_pointers_armv7k_watchos.sil index 71ffe077b6b10..023ccdc00fea4 100644 --- a/test/IRGen/class_isa_pointers_armv7k_watchos.sil +++ b/test/IRGen/class_isa_pointers_armv7k_watchos.sil @@ -17,7 +17,7 @@ class Purebred { } sil_vtable Purebred {} -// CHECK-LABEL: define{{( protected)?}} swiftcc void @purebred_method(%T33class_isa_pointers_armv7k_watchos8PurebredC*) {{.*}} { +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @purebred_method(%T33class_isa_pointers_armv7k_watchos8PurebredC*) {{.*}} { // CHECK: [[ISA_PTR:%.*]] = bitcast %T33class_isa_pointers_armv7k_watchos8PurebredC* %0 to %swift.type** // CHECK: [[ISA:%.*]] = load %swift.type*, %swift.type** [[ISA_PTR]] // CHECK: [[VTABLE:%.*]] = bitcast %swift.type* [[ISA]] @@ -38,7 +38,7 @@ class Mongrel: Gizmo { } sil_vtable Mongrel {} -// CHECK-LABEL: define{{( protected)?}} swiftcc void @mongrel_method(%T33class_isa_pointers_armv7k_watchos7MongrelC*) {{.*}} { +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @mongrel_method(%T33class_isa_pointers_armv7k_watchos7MongrelC*) {{.*}} { // CHECK: [[T0:%.*]] = bitcast {{.*}} %0 to %objc_object* // CHECK: [[T1:%.*]] = call %objc_class* @object_getClass(%objc_object* [[T0]]) // CHECK: [[ISA:%.*]] = bitcast %objc_class* [[T1]] to %swift.type* diff --git a/test/IRGen/class_resilience.sil b/test/IRGen/class_resilience.sil index 186be230a63f2..b1c6d8b749048 100644 --- a/test/IRGen/class_resilience.sil +++ b/test/IRGen/class_resilience.sil @@ -20,7 +20,7 @@ import resilient_class // Perhaps we should not serialize allocating initializers, then this would not // be an issue. -// CHECK-LABEL: define {{(protected )?}}swiftcc void @allocResilientOutsideParent() +// CHECK-LABEL: define {{(dllexport )?}}{{(protected )?}}swiftcc void @allocResilientOutsideParent() // CHECK: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$S15resilient_class22ResilientOutsideParentCMa"(i64 0) // CHECK-NEXT: [[META:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 // CHECK-NEXT: [[META_ADDR:%.*]] = bitcast %swift.type* [[META]] to i8* diff --git a/test/IRGen/class_resilience.swift b/test/IRGen/class_resilience.swift index 31ec787dee552..e10b90ae285e2 100644 --- a/test/IRGen/class_resilience.swift +++ b/test/IRGen/class_resilience.swift @@ -1,10 +1,10 @@ // RUN: %empty-directory(%t) -// RUN: %utils/chex.py < %s > %t/class_resilience.swift -// RUN: %target-swift-frontend -emit-module -enable-resilience -emit-module-path=%t/resilient_struct.swiftmodule -module-name=resilient_struct %S/../Inputs/resilient_struct.swift -// RUN: %target-swift-frontend -emit-module -enable-resilience -emit-module-path=%t/resilient_enum.swiftmodule -module-name=resilient_enum -I %t %S/../Inputs/resilient_enum.swift -// RUN: %target-swift-frontend -emit-module -enable-resilience -emit-module-path=%t/resilient_class.swiftmodule -module-name=resilient_class -I %t %S/../Inputs/resilient_class.swift -// RUN: %target-swift-frontend -I %t -emit-ir -enable-resilience %t/class_resilience.swift | %FileCheck %t/class_resilience.swift --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize -DINT=i%target-ptrsize -// RUN: %target-swift-frontend -I %t -emit-ir -enable-resilience -O %t/class_resilience.swift +// RUN: %{python} %utils/chex.py < %s > %t/class_resilience.swift +// RUN: %target-swift-frontend -emit-module -enable-resilience -enable-class-resilience -emit-module-path=%t/resilient_struct.swiftmodule -module-name=resilient_struct %S/../Inputs/resilient_struct.swift +// RUN: %target-swift-frontend -emit-module -enable-resilience -enable-class-resilience -emit-module-path=%t/resilient_enum.swiftmodule -module-name=resilient_enum -I %t %S/../Inputs/resilient_enum.swift +// RUN: %target-swift-frontend -emit-module -enable-resilience -enable-class-resilience -emit-module-path=%t/resilient_class.swiftmodule -module-name=resilient_class -I %t %S/../Inputs/resilient_class.swift +// RUN: %target-swift-frontend -I %t -emit-ir -enable-resilience -enable-class-resilience %t/class_resilience.swift | %FileCheck %t/class_resilience.swift --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize -DINT=i%target-ptrsize +// RUN: %target-swift-frontend -I %t -emit-ir -enable-resilience -enable-class-resilience -O %t/class_resilience.swift // CHECK: @"$S16class_resilience26ClassWithResilientPropertyC1s16resilient_struct4SizeVvpWvd" = hidden global [[INT]] 0 // CHECK: @"$S16class_resilience26ClassWithResilientPropertyC5colors5Int32VvpWvd" = hidden global [[INT]] 0 @@ -14,9 +14,9 @@ // CHECK: @"$S16class_resilience14ResilientChildC5fields5Int32VvpWvd" = hidden global [[INT]] {{8|16}} -// CHECK: @"$S16class_resilience21ResilientGenericChildCMo" = {{(protected )?}}global [[BOUNDS:{ (i32|i64), i32, i32 }]] zeroinitializer +// CHECK: @"$S16class_resilience21ResilientGenericChildCMo" = {{(protected )?}}{{(dllexport )?}}global [[BOUNDS:{ (i32|i64), i32, i32 }]] zeroinitializer -// CHECK: @"$S16class_resilience26ClassWithResilientPropertyCMo" = {{(protected )?}}constant [[BOUNDS]] +// CHECK: @"$S16class_resilience26ClassWithResilientPropertyCMo" = {{(protected )?}}{{(dllexport )?}}constant [[BOUNDS]] // CHECK-SAME-32: { [[INT]] 52, i32 2, i32 13 } // CHECK-SAME-64: { [[INT]] 80, i32 2, i32 10 } @@ -28,9 +28,9 @@ // CHECK: [[RESILIENTCHILD_NAME:@.*]] = private constant [15 x i8] c"ResilientChild\00" -// CHECK: @"$S16class_resilience14ResilientChildCMo" = {{(protected )?}}global [[BOUNDS]] zeroinitializer +// CHECK: @"$S16class_resilience14ResilientChildCMo" = {{(protected )?}}{{(dllexport )?}}global [[BOUNDS]] zeroinitializer -// CHECK: @"$S16class_resilience14ResilientChildCMn" = {{(protected )?}}constant <{{.*}}> <{ +// CHECK: @"$S16class_resilience14ResilientChildCMn" = {{(protected )?}}{{(dllexport )?}}constant <{{.*}}> <{ // -- flags: class, unique, reflectable, has vtable, has resilient superclass // CHECK-SAME: // -- name: @@ -41,21 +41,21 @@ // CHECK-SAME: i32 3, // CHECK-SAME: }> -// CHECK: @"$S16class_resilience16FixedLayoutChildCMo" = {{(protected )?}}global [[BOUNDS]] zeroinitializer +// CHECK: @"$S16class_resilience16FixedLayoutChildCMo" = {{(protected )?}}{{(dllexport )?}}global [[BOUNDS]] zeroinitializer -// CHECK: @"$S16class_resilience17MyResilientParentCMo" = {{(protected )?}}constant [[BOUNDS]] +// CHECK: @"$S16class_resilience17MyResilientParentCMo" = {{(protected )?}}{{(dllexport )?}}constant [[BOUNDS]] // CHECK-SAME-32: { [[INT]] 52, i32 2, i32 13 } // CHECK-SAME-64: { [[INT]] 80, i32 2, i32 10 } -// CHECK: @"$S16class_resilience16MyResilientChildCMo" = {{(protected )?}}constant [[BOUNDS]] +// CHECK: @"$S16class_resilience16MyResilientChildCMo" = {{(protected )?}}{{(dllexport )?}}constant [[BOUNDS]] // CHECK-SAME-32: { [[INT]] 60, i32 2, i32 15 } // CHECK-SAME-64: { [[INT]] 96, i32 2, i32 12 } -// CHECK: @"$S16class_resilience24MyResilientGenericParentCMo" = {{(protected )?}}constant [[BOUNDS]] +// CHECK: @"$S16class_resilience24MyResilientGenericParentCMo" = {{(protected )?}}{{(dllexport )?}}constant [[BOUNDS]] // CHECK-SAME-32: { [[INT]] 52, i32 2, i32 13 } // CHECK-SAME-64: { [[INT]] 80, i32 2, i32 10 } -// CHECK: @"$S16class_resilience24MyResilientConcreteChildCMo" = {{(protected )?}}constant [[BOUNDS]] +// CHECK: @"$S16class_resilience24MyResilientConcreteChildCMo" = {{(protected )?}}{{(dllexport )?}}constant [[BOUNDS]] // CHECK-SAME-32: { [[INT]] 64, i32 2, i32 16 } // CHECK-SAME-64: { [[INT]] 104, i32 2, i32 13 } @@ -189,7 +189,7 @@ extension ResilientGenericOutsideParent { // ClassWithResilientProperty.color getter -// CHECK-LABEL: define{{( protected)?}} swiftcc i32 @"$S16class_resilience26ClassWithResilientPropertyC5colors5Int32Vvg"(%T16class_resilience26ClassWithResilientPropertyC* swiftself) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc i32 @"$S16class_resilience26ClassWithResilientPropertyC5colors5Int32Vvg"(%T16class_resilience26ClassWithResilientPropertyC* swiftself) // CHECK: [[OFFSET:%.*]] = load [[INT]], [[INT]]* @"$S16class_resilience26ClassWithResilientPropertyC5colors5Int32VvpWvd" // CHECK-NEXT: [[PTR:%.*]] = bitcast %T16class_resilience26ClassWithResilientPropertyC* %0 to i8* // CHECK-NEXT: [[FIELD_ADDR:%.*]] = getelementptr inbounds i8, i8* [[PTR]], [[INT]] [[OFFSET]] @@ -200,7 +200,7 @@ extension ResilientGenericOutsideParent { // ClassWithResilientProperty metadata accessor -// CHECK-LABEL: define{{( protected)?}} swiftcc %swift.metadata_response @"$S16class_resilience26ClassWithResilientPropertyCMa"( +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc %swift.metadata_response @"$S16class_resilience26ClassWithResilientPropertyCMa"( // CHECK: [[CACHE:%.*]] = load %swift.type*, %swift.type** @"$S16class_resilience26ClassWithResilientPropertyCML" // CHECK-NEXT: [[COND:%.*]] = icmp eq %swift.type* [[CACHE]], null // CHECK-NEXT: br i1 [[COND]], label %cacheIsNull, label %cont @@ -218,7 +218,7 @@ extension ResilientGenericOutsideParent { // ClassWithResilientlySizedProperty.color getter -// CHECK-LABEL: define{{( protected)?}} swiftcc i32 @"$S16class_resilience33ClassWithResilientlySizedPropertyC5colors5Int32Vvg"(%T16class_resilience33ClassWithResilientlySizedPropertyC* swiftself) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc i32 @"$S16class_resilience33ClassWithResilientlySizedPropertyC5colors5Int32Vvg"(%T16class_resilience33ClassWithResilientlySizedPropertyC* swiftself) // CHECK: [[OFFSET:%.*]] = load [[INT]], [[INT]]* @"$S16class_resilience33ClassWithResilientlySizedPropertyC5colors5Int32VvpWvd" // CHECK-NEXT: [[PTR:%.*]] = bitcast %T16class_resilience33ClassWithResilientlySizedPropertyC* %0 to i8* // CHECK-NEXT: [[FIELD_ADDR:%.*]] = getelementptr inbounds i8, i8* [[PTR]], [[INT]] [[OFFSET]] @@ -229,7 +229,7 @@ extension ResilientGenericOutsideParent { // ClassWithResilientlySizedProperty metadata accessor -// CHECK-LABEL: define{{( protected)?}} swiftcc %swift.metadata_response @"$S16class_resilience33ClassWithResilientlySizedPropertyCMa"( +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc %swift.metadata_response @"$S16class_resilience33ClassWithResilientlySizedPropertyCMa"( // CHECK: [[CACHE:%.*]] = load %swift.type*, %swift.type** @"$S16class_resilience33ClassWithResilientlySizedPropertyCML" // CHECK-NEXT: [[COND:%.*]] = icmp eq %swift.type* [[CACHE]], null // CHECK-NEXT: br i1 [[COND]], label %cacheIsNull, label %cont @@ -247,7 +247,7 @@ extension ResilientGenericOutsideParent { // ClassWithIndirectResilientEnum.color getter -// CHECK-LABEL: define{{( protected)?}} swiftcc i32 @"$S16class_resilience30ClassWithIndirectResilientEnumC5colors5Int32Vvg"(%T16class_resilience30ClassWithIndirectResilientEnumC* swiftself) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc i32 @"$S16class_resilience30ClassWithIndirectResilientEnumC5colors5Int32Vvg"(%T16class_resilience30ClassWithIndirectResilientEnumC* swiftself) // CHECK: [[FIELD_PTR:%.*]] = getelementptr inbounds %T16class_resilience30ClassWithIndirectResilientEnumC, %T16class_resilience30ClassWithIndirectResilientEnumC* %0, i32 0, i32 2 // CHECK-NEXT: [[FIELD_PAYLOAD:%.*]] = getelementptr inbounds %Ts5Int32V, %Ts5Int32V* [[FIELD_PTR]], i32 0, i32 0 // CHECK-NEXT: [[FIELD_VALUE:%.*]] = load i32, i32* [[FIELD_PAYLOAD]] @@ -256,7 +256,7 @@ extension ResilientGenericOutsideParent { // ResilientChild.field getter -// CHECK-LABEL: define{{( protected)?}} swiftcc i32 @"$S16class_resilience14ResilientChildC5fields5Int32Vvg"(%T16class_resilience14ResilientChildC* swiftself) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc i32 @"$S16class_resilience14ResilientChildC5fields5Int32Vvg"(%T16class_resilience14ResilientChildC* swiftself) // CHECK: [[OFFSET:%.*]] = load [[INT]], [[INT]]* @"$S16class_resilience14ResilientChildC5fields5Int32VvpWvd" // CHECK-NEXT: [[PTR:%.*]] = bitcast %T16class_resilience14ResilientChildC* %0 to i8* // CHECK-NEXT: [[FIELD_ADDR:%.*]] = getelementptr inbounds i8, i8* [[PTR]], [[INT]] [[OFFSET]] @@ -269,7 +269,7 @@ extension ResilientGenericOutsideParent { // ResilientGenericChild.field getter -// CHECK-LABEL: define{{( protected)?}} swiftcc i32 @"$S16class_resilience21ResilientGenericChildC5fields5Int32Vvg"(%T16class_resilience21ResilientGenericChildC* swiftself) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc i32 @"$S16class_resilience21ResilientGenericChildC5fields5Int32Vvg"(%T16class_resilience21ResilientGenericChildC* swiftself) // FIXME: we could eliminate the unnecessary isa load by lazily emitting // metadata sources in EmitPolymorphicParameters @@ -296,7 +296,7 @@ extension ResilientGenericOutsideParent { // MyResilientChild.field getter -// CHECK-LABEL: define{{( protected)?}} swiftcc i32 @"$S16class_resilience16MyResilientChildC5fields5Int32Vvg"(%T16class_resilience16MyResilientChildC* swiftself) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc i32 @"$S16class_resilience16MyResilientChildC5fields5Int32Vvg"(%T16class_resilience16MyResilientChildC* swiftself) // CHECK: [[FIELD_ADDR:%.*]] = getelementptr inbounds %T16class_resilience16MyResilientChildC, %T16class_resilience16MyResilientChildC* %0, i32 0, i32 2 // CHECK-NEXT: [[PAYLOAD_ADDR:%.*]] = getelementptr inbounds %Ts5Int32V, %Ts5Int32V* [[FIELD_ADDR]], i32 0, i32 0 // CHECK-NEXT: [[RESULT:%.*]] = load i32, i32* [[PAYLOAD_ADDR]] @@ -305,7 +305,7 @@ extension ResilientGenericOutsideParent { // ResilientGenericOutsideParent.genericExtensionMethod() -// CHECK-LABEL: define{{( protected)?}} swiftcc %swift.type* @"$S15resilient_class29ResilientGenericOutsideParentC0B11_resilienceE22genericExtensionMethodxmyF"(%T15resilient_class29ResilientGenericOutsideParentC* swiftself) #0 { +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc %swift.type* @"$S15resilient_class29ResilientGenericOutsideParentC0B11_resilienceE22genericExtensionMethodxmyF"(%T15resilient_class29ResilientGenericOutsideParentC* swiftself) #0 { // CHECK: [[ISA_ADDR:%.*]] = bitcast %T15resilient_class29ResilientGenericOutsideParentC* %0 to %swift.type** // CHECK-NEXT: [[ISA:%.*]] = load %swift.type*, %swift.type** [[ISA_ADDR]] // CHECK-NEXT: [[BASE:%.*]] = load [[INT]], [[INT]]* getelementptr inbounds ([[BOUNDS]], [[BOUNDS]]* @"$S15resilient_class29ResilientGenericOutsideParentCMo", i32 0, i32 0) @@ -395,7 +395,7 @@ extension ResilientGenericOutsideParent { // ResilientChild.field getter dispatch thunk -// CHECK-LABEL: define{{( protected)?}} swiftcc i32 @"$S16class_resilience14ResilientChildC5fields5Int32VvgTj"(%T16class_resilience14ResilientChildC* swiftself) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc i32 @"$S16class_resilience14ResilientChildC5fields5Int32VvgTj"(%T16class_resilience14ResilientChildC* swiftself) // CHECK: [[ISA_ADDR:%.*]] = getelementptr inbounds %T16class_resilience14ResilientChildC, %T16class_resilience14ResilientChildC* %0, i32 0, i32 0, i32 0 // CHECK-NEXT: [[ISA:%.*]] = load %swift.type*, %swift.type** [[ISA_ADDR]] // CHECK-NEXT: [[BASE:%.*]] = load [[INT]], [[INT]]* getelementptr inbounds ([[BOUNDS]], [[BOUNDS]]* @"$S16class_resilience14ResilientChildCMo", i32 0, i32 0) @@ -408,7 +408,7 @@ extension ResilientGenericOutsideParent { // ResilientChild.field setter dispatch thunk -// CHECK-LABEL: define{{( protected)?}} swiftcc void @"$S16class_resilience14ResilientChildC5fields5Int32VvsTj"(i32, %T16class_resilience14ResilientChildC* swiftself) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$S16class_resilience14ResilientChildC5fields5Int32VvsTj"(i32, %T16class_resilience14ResilientChildC* swiftself) // CHECK: [[ISA_ADDR:%.*]] = getelementptr inbounds %T16class_resilience14ResilientChildC, %T16class_resilience14ResilientChildC* %1, i32 0, i32 0, i32 0 // CHECK-NEXT: [[ISA:%.*]] = load %swift.type*, %swift.type** [[ISA_ADDR]] // CHECK-NEXT: [[BASE:%.*]] = load [[INT]], [[INT]]* getelementptr inbounds ([[BOUNDS]], [[BOUNDS]]* @"$S16class_resilience14ResilientChildCMo", i32 0, i32 0) diff --git a/test/IRGen/class_resilience_objc.swift b/test/IRGen/class_resilience_objc.swift index 277eba629f18c..c7c78820e4555 100644 --- a/test/IRGen/class_resilience_objc.swift +++ b/test/IRGen/class_resilience_objc.swift @@ -1,6 +1,5 @@ -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-ir -o - -primary-file %s | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -emit-ir -o - -primary-file %s | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize -// REQUIRES: objc_interop // XFAIL: CPU=armv7k // CHECK: %swift.type = type { [[INT:i32|i64]] } diff --git a/test/IRGen/class_resilience_objc_armv7k.swift b/test/IRGen/class_resilience_objc_armv7k.swift index 559ad9737ccb6..f906f7dd2be4c 100644 --- a/test/IRGen/class_resilience_objc_armv7k.swift +++ b/test/IRGen/class_resilience_objc_armv7k.swift @@ -1,6 +1,5 @@ // RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-ir -o - -primary-file %s | %FileCheck %s --check-prefix=CHECK -// REQUIRES: objc_interop // REQUIRES: CPU=armv7k // CHECK: %swift.type = type { [[INT:i32|i64]] } diff --git a/test/IRGen/class_resilience_thunks.swift b/test/IRGen/class_resilience_thunks.swift index 0cd90c89ec6ed..541f2fd3c6f90 100644 --- a/test/IRGen/class_resilience_thunks.swift +++ b/test/IRGen/class_resilience_thunks.swift @@ -8,7 +8,7 @@ import resilient_class_thunks -// CHECK-LABEL: define{{( protected)?}} swiftcc void @"$S23class_resilience_thunks21testDispatchThunkBase1b1ty010resilient_a1_C00G0CyxG_xtlF"(%T22resilient_class_thunks4BaseC*, %swift.opaque* noalias nocapture) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$S23class_resilience_thunks21testDispatchThunkBase1b1ty010resilient_a1_C00G0CyxG_xtlF"(%T22resilient_class_thunks4BaseC*, %swift.opaque* noalias nocapture) public func testDispatchThunkBase(b: Base, t: T) { // CHECK: call swiftcc void @"$S22resilient_class_thunks4BaseC6takesTyyxFTj"(%swift.opaque* noalias nocapture {{%.*}}, %T22resilient_class_thunks4BaseC* swiftself %0) @@ -23,7 +23,7 @@ public func testDispatchThunkBase(b: Base, t: T) { // CHECK: ret void } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @"$S23class_resilience_thunks24testDispatchThunkDerived1dy010resilient_a1_C00G0C_tF"(%T22resilient_class_thunks7DerivedC*) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$S23class_resilience_thunks24testDispatchThunkDerived1dy010resilient_a1_C00G0C_tF"(%T22resilient_class_thunks7DerivedC*) public func testDispatchThunkDerived(d: Derived) { // CHECK: call swiftcc void @"$S22resilient_class_thunks4BaseC6takesTyyxFTj"(%swift.opaque* noalias nocapture dereferenceable({{4|8}}) {{%.*}}, %T22resilient_class_thunks4BaseC* swiftself {{%.*}}) diff --git a/test/IRGen/class_stack_alloc.sil b/test/IRGen/class_stack_alloc.sil index cfc9d39b9550f..0ca7540244b21 100644 --- a/test/IRGen/class_stack_alloc.sil +++ b/test/IRGen/class_stack_alloc.sil @@ -28,7 +28,7 @@ class BigClass { sil_vtable TestClass {} sil_vtable BigClass {} -// CHECK-LABEL: define{{( protected)?}} swiftcc void @simple_promote +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @simple_promote // CHECK: %reference.raw = alloca %[[C:[a-zA-Z0-9_]+]], align 8 // CHECK-NEXT: [[MR:%[0-9]+]] = call swiftcc %swift.metadata_response @"$S17class_stack_alloc9TestClassCMa"([[INT]] 0) // CHECK-NEXT: [[M:%.*]] = extractvalue %swift.metadata_response [[MR]], 0 @@ -52,7 +52,7 @@ bb0: // A stack promotion limit of 48 bytes allows that one of the two alloc_refs // can be allocated on the stack. -// CHECK-LABEL: define{{( protected)?}} swiftcc void @exceed_limit +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @exceed_limit // CHECK: alloca {{.*}}TestClass // CHECK: alloca {{.*}}TestStruct // CHECK-NOT: alloca @@ -80,7 +80,7 @@ bb0: return %r : $() } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @promoted_with_devirtualized_release +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @promoted_with_devirtualized_release // CHECK: %reference.raw = alloca %[[C:[a-zA-Z0-9_]+]], align 8 // CHECK-NEXT: [[MR:%[0-9]+]] = call swiftcc %swift.metadata_response @"$S17class_stack_alloc9TestClassCMa"([[INT]] 0) // CHECK-NEXT: [[M:%.*]] = extractvalue %swift.metadata_response [[MR]], 0 @@ -104,7 +104,7 @@ bb0: return %r : $() } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @promoted_with_inlined_devirtualized_release +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @promoted_with_inlined_devirtualized_release // CHECK: %reference.raw = alloca %[[C:[a-zA-Z0-9_]+]], align 8 // CHECK-NEXT: [[MR:%[0-9]+]] = call swiftcc %swift.metadata_response @"$S17class_stack_alloc9TestClassCMa"([[INT]] 0) // CHECK-NEXT: [[M:%.*]] = extractvalue %swift.metadata_response [[MR]], 0 @@ -126,7 +126,7 @@ bb0: return %r : $() } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @not_promoted_with_inlined_devirtualized_release +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @not_promoted_with_inlined_devirtualized_release // CHECK: [[O:%[0-9]+]] = call {{.*}} @swift_allocObject // CHECK-NEXT: [[BC:%[0-9]+]] = bitcast %swift.refcounted* [[O]] to // CHECK-NEXT: call {{.*}} @swift_setDeallocating {{.*}}({{.*}} [[BC]]) diff --git a/test/IRGen/closure.swift b/test/IRGen/closure.swift index 826f3548d3e62..ddc1fa7efc206 100644 --- a/test/IRGen/closure.swift +++ b/test/IRGen/closure.swift @@ -4,7 +4,7 @@ // -- partial_apply context metadata -// CHECK: [[METADATA:@.*]] = private constant %swift.full_boxmetadata { void (%swift.refcounted*)* @objectdestroy, i8** null, %swift.type { i64 64 }, i32 16, i8* bitcast ({ i32, i32, i32, i32 }* @"\01l__swift4_reflection_descriptor" to i8*) } +// CHECK: [[METADATA:@.*]] = private constant %swift.full_boxmetadata { void (%swift.refcounted*)* @objectdestroy, i8** null, %swift.type { i64 64 }, i32 16, i8* bitcast ({ i32, i32, i32, i32 }* @"\01l__swift5_reflection_descriptor" to i8*) } func a(i i: Int) -> (Int) -> Int { return { x in i } diff --git a/test/IRGen/concrete_inherits_generic_base.swift b/test/IRGen/concrete_inherits_generic_base.swift index 6af1ef23c79b3..d26fd549178d7 100644 --- a/test/IRGen/concrete_inherits_generic_base.swift +++ b/test/IRGen/concrete_inherits_generic_base.swift @@ -75,7 +75,7 @@ presentBase(Derived(x: "two")) presentBase(Base(x: "two")) presentBase(Base(x: 2)) -// CHECK-LABEL: define{{( protected)?}} private void @initialize_metadata_SuperDerived(i8*) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} private void @initialize_metadata_SuperDerived(i8*) // CHECK: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$S3foo7DerivedCMa"([[INT]] 1) // CHECK: [[TMP:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 // CHECK-NEXT: extractvalue %swift.metadata_response [[T0]], 1 diff --git a/test/IRGen/conditional_conformances_class_with_defaulted_method.swift b/test/IRGen/conditional_conformances_class_with_defaulted_method.swift new file mode 100644 index 0000000000000..060c43722cb6d --- /dev/null +++ b/test/IRGen/conditional_conformances_class_with_defaulted_method.swift @@ -0,0 +1,15 @@ +// RUN: %target-swift-frontend -emit-ir %s -module-name x | %FileCheck %s + +// rdar://problem/40078863 - witness signatures get adjusted and the +// ProtocolConformances accompanying them did not, resulting in an extra witness +// table parameter. + +protocol Foo { + func bar() +} +extension Foo { + func bar() {} +} +class Box {} +extension Box: Foo where T: Foo {} +// CHECK-LABEL: define internal swiftcc void @"$S1x3BoxCyqd__GAA3FooA2aEP3baryyFTW"(%T1x3BoxC.0** noalias nocapture swiftself dereferenceable({{[48]}}), %swift.type* %Self, i8** %SelfWitnessTable) diff --git a/test/IRGen/coverage.swift b/test/IRGen/coverage.swift deleted file mode 100644 index aaac44a766128..0000000000000 --- a/test/IRGen/coverage.swift +++ /dev/null @@ -1,20 +0,0 @@ -// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil %s -profile-generate -profile-coverage-mapping -emit-sil -o - | %FileCheck %s --check-prefix=SIL -// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil %s -profile-generate -profile-coverage-mapping -emit-ir -o - | %FileCheck %s --check-prefix=IR - -// IR-NOT: __llvm_coverage_names -// IR-NOT: __profn - -// SIL-DAG: sil hidden @$S8coverage2f1yyF -// SIL-DAG: string_literal utf8 "{{.*}}coverage.swift:{{.*}}$S8coverage2f1yyF" -internal func f1() {} - -// SIL-DAG: sil private @$S8coverage2f233_[[F2HASH:[_a-zA-Z0-9]+]] -// SIL-DAG: string_literal utf8 "{{.*}}coverage.swift:$S8coverage2f233_[[F2HASH]]" -private func f2() {} - -// SIL-DAG: sil @$S8coverage2f3yyF -// SIL-DAG: string_literal utf8 "$S8coverage2f3yyF" -public func f3() { - f1(); - f2(); -} diff --git a/test/IRGen/coverage_ignored.swift b/test/IRGen/coverage_ignored.swift deleted file mode 100644 index c2c5193f5eed7..0000000000000 --- a/test/IRGen/coverage_ignored.swift +++ /dev/null @@ -1,7 +0,0 @@ -// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil %s -profile-generate -emit-sil -o %t.sil -// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil %s -emit-ir -o - | %FileCheck %s - -// CHECK-NOT: llvm.instrprof -// CHECK-NOT: profc -func foo() {} -foo() diff --git a/test/IRGen/deallocate.swift b/test/IRGen/deallocate.swift index 4fc5fe91d8aa3..95ae04cc27225 100644 --- a/test/IRGen/deallocate.swift +++ b/test/IRGen/deallocate.swift @@ -1,7 +1,6 @@ // RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -emit-ir %s | %FileCheck %s // REQUIRES: CPU=x86_64 -// REQUIRES: objc_interop // rdar://16979846 diff --git a/test/IRGen/deserialize-clang-importer-witness-tables.swift b/test/IRGen/deserialize-clang-importer-witness-tables.swift new file mode 100644 index 0000000000000..af60c07f16270 --- /dev/null +++ b/test/IRGen/deserialize-clang-importer-witness-tables.swift @@ -0,0 +1,16 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -swift-version 4 -emit-module -o %t/regex.swiftmodule %S/Inputs/deserialize-clang-importer-witness-tables/regex.swift +// RUN: %target-swift-frontend -swift-version 4 -emit-ir %s -I %t | %FileCheck %s +// REQUIRES: objc_interop +import regex + +public func foo(line: String) { + // The NSRegularExpressionOptions: SetAlgebra conformance is used indirectly + // from the default argument expression passed to `RegEx(pattern:options:)` + // below. Ensure that a local copy of the definition was deserialized + // and lowered to IR. + // CHECK-LABEL: define {{.*}} i8** @"$SSo26NSRegularExpressionOptionsVs10SetAlgebraSCWa" + // CHECK-LABEL: define {{.*}} void @"$SSo26NSRegularExpressionOptionsVs10SetAlgebraSCsACPxycfCTW" + let versionRegex = try! RegEx(pattern: "Apple") + _ = versionRegex.firstMatch(in: line) +} diff --git a/test/IRGen/dynamic_cast.sil b/test/IRGen/dynamic_cast.sil index 2e55f28497e10..1061dc4c46ca6 100644 --- a/test/IRGen/dynamic_cast.sil +++ b/test/IRGen/dynamic_cast.sil @@ -17,7 +17,7 @@ struct S { var v: Int } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @testUnconditional0( +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @testUnconditional0( sil @testUnconditional0 : $@convention(thin) (@in P) -> () { bb0(%0 : $*P): // CHECK: [[T0:%.*]] = alloca [[S:%.*]], align @@ -37,7 +37,7 @@ bb0(%0 : $*P): // CHECK-LABEL: define linkonce_odr hidden swiftcc %swift.metadata_response @"$S12dynamic_cast1P_pMa"( // CHECK: call %swift.type* @swift_getExistentialTypeMetadata( -// CHECK-LABEL: define{{( protected)?}} swiftcc void @testUnconditional1( +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @testUnconditional1( sil @testUnconditional1 : $@convention(thin) (@in P) -> () { bb0(%0 : $*P): // CHECK: [[T0:%.*]] = alloca [[S:%.*]], align @@ -54,7 +54,7 @@ bb0(%0 : $*P): return %2 : $() } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @testConditional0( +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @testConditional0( sil @testConditional0 : $@convention(thin) (@in P) -> () { bb0(%0 : $*P): // CHECK: [[T0:%.*]] = alloca [[S:%.*]], align @@ -75,7 +75,7 @@ bb2: return %2 : $() } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @testConditional1( +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @testConditional1( sil @testConditional1 : $@convention(thin) (@in P) -> () { bb0(%0 : $*P): // CHECK: [[T0:%.*]] = alloca [[S:%.*]], align @@ -96,7 +96,7 @@ bb2: return %2 : $() } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @testConditional2( +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @testConditional2( sil @testConditional2 : $@convention(thin) (@in P) -> () { bb0(%0 : $*P): // CHECK: [[T0:%.*]] = alloca [[S:%.*]], align diff --git a/test/IRGen/dynamic_init.sil b/test/IRGen/dynamic_init.sil index e272afbe99a6b..1aa1570f2d473 100644 --- a/test/IRGen/dynamic_init.sil +++ b/test/IRGen/dynamic_init.sil @@ -16,7 +16,7 @@ sil @$S12dynamic_init1CCACycACmcfc : $@convention(method) (@owned C) -> @owned C sil @$S12dynamic_init1CCfd : $@convention(method) (@owned C) -> @owned Builtin.NativeObject sil @$S12dynamic_init1CCfD : $@convention(method) (@owned C) -> () -// CHECK-LABEL: define{{( protected)?}} swiftcc void @"$S12dynamic_init15testDynamicInityAA1CCm2cm_tF" +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$S12dynamic_init15testDynamicInityAA1CCm2cm_tF" sil @$S12dynamic_init15testDynamicInityAA1CCm2cm_tF : $@convention(method) (@thick C.Type) -> () { bb0(%0 : $@thick C.Type): // CHECK: [[META:%[0-9]+]] = bitcast %swift.type* %0 to %T12dynamic_init1CC* (%swift.type*)** diff --git a/test/IRGen/dynamic_lookup.sil b/test/IRGen/dynamic_lookup.sil index a318ea9b4e6cf..d28e6902fc04e 100644 --- a/test/IRGen/dynamic_lookup.sil +++ b/test/IRGen/dynamic_lookup.sil @@ -1,7 +1,6 @@ -// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -emit-ir %s | %FileCheck %s +// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -enable-objc-interop -emit-ir %s | %FileCheck %s // REQUIRES: CPU=i386 || CPU=x86_64 -// REQUIRES: objc_interop import Swift import Builtin @@ -51,7 +50,7 @@ bb0(%0 : $X): return %7 : $Int } -// CHECK: define{{( protected)?}} swiftcc void @dynamic_lookup_br(%objc_object*) +// CHECK: define{{( dllexport)?}}{{( protected)?}} swiftcc void @dynamic_lookup_br(%objc_object*) sil @dynamic_lookup_br : $@convention(thin) (AnyObject) -> () { bb0(%0 : $AnyObject): %1 = alloc_box $<τ_0_0> { var τ_0_0 } @@ -82,7 +81,7 @@ bb3: return %43 : $() } -// CHECK: define{{( protected)?}} swiftcc void @dynamic_lookup_static_br(%swift.type*) +// CHECK: define{{( dllexport)?}}{{( protected)?}} swiftcc void @dynamic_lookup_static_br(%swift.type*) sil @dynamic_lookup_static_br : $@convention(thin) (@thick AnyObject.Type) -> () { bb0(%0 : $@thick AnyObject.Type): // CHECK: [[SEL:%[0-9]+]] = load i8*, i8** @"\01L_selector(g)", align {{(4|8)}} @@ -126,7 +125,7 @@ bb3: return %43 : $() } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @_T1t16opt_to_subscriptFT3objPSo13AnyObject_1iSi_T_(%objc_object*, {{(i32|i64)}}) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @_T1t16opt_to_subscriptFT3objPSo13AnyObject_1iSi_T_(%objc_object*, {{(i32|i64)}}) sil @_T1t16opt_to_subscriptFT3objPSo13AnyObject_1iSi_T_ : $@convention(thin) (AnyObject, Int) -> () { bb0(%0 : $AnyObject, %1 : $Int): %2 = alloc_box $<τ_0_0> { var τ_0_0 } diff --git a/test/IRGen/dynamic_self.sil b/test/IRGen/dynamic_self.sil index c8cae595138d8..816735b9fe52a 100644 --- a/test/IRGen/dynamic_self.sil +++ b/test/IRGen/dynamic_self.sil @@ -11,7 +11,7 @@ protocol P { func f() -> Self } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @_TF12dynamic_self23testExistentialDispatchFT1pPS_1P__T_ +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @_TF12dynamic_self23testExistentialDispatchFT1pPS_1P__T_ sil @_TF12dynamic_self23testExistentialDispatchFT1pPS_1P__T_ : $@convention(thin) (@in P) -> () { bb0(%0 : $*P): debug_value_addr %0 : $*P, let, name "p" // id: %1 diff --git a/test/IRGen/enum.sil b/test/IRGen/enum.sil index b42e5295ee304..83cc36da3ab9e 100644 --- a/test/IRGen/enum.sil +++ b/test/IRGen/enum.sil @@ -1,4 +1,5 @@ -// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil %s -gnone -emit-ir -disable-objc-attr-requires-foundation-module | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize --check-prefix=CHECK-%target-runtime-%target-ptrsize +// Please put -enable-objc-interop tests in enum_objc.sil +// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil %s -gnone -emit-ir -disable-objc-interop | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize --check-prefix=CHECK-%target-runtime-%target-ptrsize // REQUIRES: CPU=i386 || CPU=x86_64 @@ -126,11 +127,11 @@ import Swift // CHECK: @"$S4enum16DynamicSingletonOMP" = internal constant <{ {{.*}} }> <{ // CHECK-SAME: @"$S4enum16DynamicSingletonOMi" -// CHECK-SAME: [18 x i8*]* @"$S4enum16DynamicSingletonOWV" +// CHECK-SAME: [17 x i8*]* @"$S4enum16DynamicSingletonOWV" // -- No-payload enums have extra inhabitants in // their value witness table. -// CHECK: @"$S4enum10NoPayloadsOWV" = internal constant [18 x i8*] [ +// CHECK: @"$S4enum10NoPayloadsOWV" = internal constant [17 x i8*] [ // -- ... // -- size // CHECK-SAME: i8* inttoptr ([[WORD:i32|i64]] 1 to i8*), @@ -148,7 +149,7 @@ import Swift // -- Single-payload enums take unused extra inhabitants from their payload // as their own. -// CHECK: @"$S4enum19SinglePayloadNestedOWV" = internal constant [18 x i8*] [ +// CHECK: @"$S4enum19SinglePayloadNestedOWV" = internal constant [17 x i8*] [ // -- ... // -- size // CHECK-SAME: i8* inttoptr ([[WORD]] 1 to i8*), @@ -164,16 +165,16 @@ import Swift // CHECK-SAME: i8* bitcast (i32 (%swift.opaque*, %swift.type*)* @"$S4enum19SinglePayloadNestedOwxg" to i8*) // CHECK-SAME: ] -// CHECK: @"$S4enum20DynamicSinglePayloadOWV" = internal constant [18 x i8*] [ +// CHECK: @"$S4enum20DynamicSinglePayloadOWV" = internal constant [17 x i8*] [ // CHECK-SAME: i8* null // CHECK-SAME: i8* bitcast (void (%swift.opaque*, i32, %swift.type*)* @"$S4enum20DynamicSinglePayloadOwxs" to i8*) // CHECK-SAME: i8* bitcast (i32 (%swift.opaque*, %swift.type*)* @"$S4enum20DynamicSinglePayloadOwxg" to i8*) // CHECK: @"$S4enum20DynamicSinglePayloadOMP" = internal constant <{ {{.*}} }> <{ // CHECK-SAME: @"$S4enum20DynamicSinglePayloadOMi" -// CHECK-SAME: [18 x i8*]* @"$S4enum20DynamicSinglePayloadOWV" +// CHECK-SAME: [17 x i8*]* @"$S4enum20DynamicSinglePayloadOWV" -// CHECK: @"$S4enum18MultiPayloadNestedOWV" = internal constant [18 x i8*] [ +// CHECK: @"$S4enum18MultiPayloadNestedOWV" = internal constant [17 x i8*] [ // -- size // CHECK-32-SAME: i8* inttoptr ([[WORD]] 5 to i8*), // CHECK-64-SAME: i8* inttoptr ([[WORD]] 9 to i8*), @@ -201,8 +202,8 @@ enum DynamicSingleton { case value(T) } -// CHECK-64: define{{( protected)?}} swiftcc void @singleton_switch(i64, i64) {{.*}} { -// CHECK-32: define{{( protected)?}} swiftcc void @singleton_switch(%T4enum9SingletonO* noalias nocapture dereferenceable(16)) {{.*}} { +// CHECK-64: define{{( dllexport)?}}{{( protected)?}} swiftcc void @singleton_switch(i64, i64) {{.*}} { +// CHECK-32: define{{( dllexport)?}}{{( protected)?}} swiftcc void @singleton_switch(%T4enum9SingletonO* noalias nocapture dereferenceable(16)) {{.*}} { sil @singleton_switch : $(Singleton) -> () { // CHECK-64: entry: // CHECK-32: entry: @@ -220,8 +221,8 @@ dest: return %x : $() } -// CHECK-64: define{{( protected)?}} swiftcc void @singleton_switch_arg(i64, i64) {{.*}} { -// CHECK-32: define{{( protected)?}} swiftcc void @singleton_switch_arg(%T4enum9SingletonO* noalias nocapture dereferenceable(16)) {{.*}} { +// CHECK-64: define{{( dllexport)?}}{{( protected)?}} swiftcc void @singleton_switch_arg(i64, i64) {{.*}} { +// CHECK-32: define{{( dllexport)?}}{{( protected)?}} swiftcc void @singleton_switch_arg(%T4enum9SingletonO* noalias nocapture dereferenceable(16)) {{.*}} { sil @singleton_switch_arg : $(Singleton) -> () { // CHECK-64: entry: // CHECK-32: entry: @@ -242,7 +243,7 @@ dest(%u2 : $(Builtin.Int64, Builtin.Int64)): return %x : $() } -// CHECK: define{{( protected)?}} swiftcc void @singleton_switch_indirect(%T4enum9SingletonO* nocapture dereferenceable({{.*}})) {{.*}} { +// CHECK: define{{( dllexport)?}}{{( protected)?}} swiftcc void @singleton_switch_indirect(%T4enum9SingletonO* nocapture dereferenceable({{.*}})) {{.*}} { // CHECK: entry: // CHECK: br label %[[DEST:[0-9]+]] // CHECK: ;

) { + testIt(f) +} diff --git a/test/IRGen/partial_apply.sil b/test/IRGen/partial_apply.sil index f99b4cd62c7ae..67c8e6f178a22 100644 --- a/test/IRGen/partial_apply.sil +++ b/test/IRGen/partial_apply.sil @@ -11,7 +11,7 @@ sil @$S13partial_apply10SwiftClassCfD : $@convention(method) (SwiftClass) -> () sil @partially_applyable_to_class : $@convention(thin) (@owned SwiftClass) -> () -// CHECK: define{{( protected)?}} swiftcc { i8*, %swift.refcounted* } @partial_apply_class(%T13partial_apply10SwiftClassC*) {{.*}} { +// CHECK: define{{( dllexport)?}}{{( protected)?}} swiftcc { i8*, %swift.refcounted* } @partial_apply_class(%T13partial_apply10SwiftClassC*) {{.*}} { // CHECK: entry: // CHECK: %1 = bitcast %T13partial_apply10SwiftClassC* %0 to %swift.refcounted* // CHECK: %2 = insertvalue { i8*, %swift.refcounted* } { i8* bitcast (void (%swift.refcounted*)* @"$S28partially_applyable_to_classTA" to i8*), %swift.refcounted* undef }, %swift.refcounted* %1, 1 @@ -66,7 +66,7 @@ entry(%a : $*T): sil public_external @guaranteed_captured_class_param : $@convention(thin) (Int, @guaranteed SwiftClass) -> Int -// CHECK-LABEL: define{{( protected)?}} swiftcc { i8*, %swift.refcounted* } @partial_apply_guaranteed_class_param(%T13partial_apply10SwiftClassC*) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc { i8*, %swift.refcounted* } @partial_apply_guaranteed_class_param(%T13partial_apply10SwiftClassC*) // CHECK: [[CONTEXT_OBJ:%.*]] = bitcast %T13partial_apply10SwiftClassC* {{%.*}} to %swift.refcounted* // CHECK: [[T0:%.*]] = insertvalue {{.*}} [[PARTIAL_APPLY_FORWARDER:@"\$S[A-Za-z0-9_]+TA"]] {{.*}} [[CONTEXT_OBJ]] // CHECK: ret {{.*}} [[T0]] @@ -87,7 +87,7 @@ bb0(%x : $SwiftClass): sil public_external @indirect_guaranteed_captured_class_param : $@convention(thin) (Int, @in_guaranteed SwiftClass) -> Int -// CHECK-LABEL: define{{( protected)?}} swiftcc { i8*, %swift.refcounted* } @partial_apply_indirect_guaranteed_class_param(%T13partial_apply10SwiftClassC** noalias nocapture dereferenceable({{.*}})) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc { i8*, %swift.refcounted* } @partial_apply_indirect_guaranteed_class_param(%T13partial_apply10SwiftClassC** noalias nocapture dereferenceable({{.*}})) // CHECK-NOT: {{retain|release}} // CHECK: [[X:%.*]] = load %T13partial_apply10SwiftClassC*, %T13partial_apply10SwiftClassC** %0 // CHECK-NEXT: [[CONTEXT_OBJ:%.*]] = bitcast %T13partial_apply10SwiftClassC* [[X]] to %swift.refcounted* @@ -116,7 +116,7 @@ bb0(%x : $*SwiftClass): sil public_external @indirect_consumed_captured_class_param : $@convention(thin) (Int, @in SwiftClass) -> Int -// CHECK-LABEL: define{{( protected)?}} swiftcc { i8*, %swift.refcounted* } @partial_apply_indirect_consumed_class_param(%T13partial_apply10SwiftClassC** noalias nocapture dereferenceable({{.*}})) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc { i8*, %swift.refcounted* } @partial_apply_indirect_consumed_class_param(%T13partial_apply10SwiftClassC** noalias nocapture dereferenceable({{.*}})) // CHECK-NOT: {{retain|release}} // CHECK: [[X:%.*]] = load %T13partial_apply10SwiftClassC*, %T13partial_apply10SwiftClassC** %0 // CHECK-NEXT: [[CONTEXT_OBJ:%.*]] = bitcast %T13partial_apply10SwiftClassC* [[X]] to %swift.refcounted* @@ -131,7 +131,7 @@ sil public_external @indirect_consumed_captured_class_param : $@convention(thin) // CHECK-NOT: load // CHECK-NOT: retain // CHECK-NOT: release -// CHECK: [[RESULT:%.*]] = tail call swiftcc i64 @indirect_consumed_captured_class_param(i64 %0, %T13partial_apply10SwiftClassC** noalias nocapture dereferenceable({{.*}}) [[X_CAST]]) +// CHECK: [[RESULT:%.*]] = call swiftcc i64 @indirect_consumed_captured_class_param(i64 %0, %T13partial_apply10SwiftClassC** noalias nocapture dereferenceable({{.*}}) [[X_CAST]]) // CHECK-NOT: retain // CHECK-NOT: release // CHECK: ret i64 [[RESULT]] @@ -152,7 +152,7 @@ struct SwiftClassPair { var x: SwiftClass, y: SwiftClass } sil public_external @guaranteed_captured_class_pair_param : $@convention(thin) (Int, @guaranteed SwiftClassPair) -> Int -// CHECK-LABEL: define{{( protected)?}} swiftcc { i8*, %swift.refcounted* } @partial_apply_guaranteed_class_pair_param(%T13partial_apply10SwiftClassC*, %T13partial_apply10SwiftClassC*) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc { i8*, %swift.refcounted* } @partial_apply_guaranteed_class_pair_param(%T13partial_apply10SwiftClassC*, %T13partial_apply10SwiftClassC*) // CHECK: [[CONTEXT_OBJ:%.*]] = call noalias %swift.refcounted* @swift_allocObject // CHECK: [[PAIR_ADDR:%.*]] = getelementptr // CHECK-NEXT: [[X_ADDR:%.*]] = getelementptr inbounds %T13partial_apply14SwiftClassPairV, %T13partial_apply14SwiftClassPairV* [[PAIR_ADDR]], i32 0, i32 0 @@ -183,7 +183,7 @@ bb0(%x : $SwiftClassPair): sil public_external @indirect_guaranteed_captured_class_pair_param : $@convention(thin) (Int, @in_guaranteed SwiftClassPair) -> Int -// CHECK-LABEL: define{{( protected)?}} swiftcc { i8*, %swift.refcounted* } @partial_apply_indirect_guaranteed_class_pair_param(%T13partial_apply14SwiftClassPairV* noalias nocapture dereferenceable({{.*}})) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc { i8*, %swift.refcounted* } @partial_apply_indirect_guaranteed_class_pair_param(%T13partial_apply14SwiftClassPairV* noalias nocapture dereferenceable({{.*}})) // CHECK: [[CONTEXT_OBJ:%.*]] = call noalias %swift.refcounted* @swift_allocObject // CHECK-NOT: {{retain|release}} // CHECK: [[T0:%.*]] = insertvalue {{.*}} [[PARTIAL_APPLY_FORWARDER:@"\$S[A-Za-z0-9_]+TA"]] {{.*}} [[CONTEXT_OBJ]] @@ -206,7 +206,7 @@ bb0(%x : $*SwiftClassPair): sil public_external @indirect_consumed_captured_class_pair_param : $@convention(thin) (Int, @in SwiftClassPair) -> Int -// CHECK-LABEL: define{{( protected)?}} swiftcc { i8*, %swift.refcounted* } @partial_apply_indirect_consumed_class_pair_param(%T13partial_apply14SwiftClassPairV* noalias nocapture dereferenceable({{.*}})) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc { i8*, %swift.refcounted* } @partial_apply_indirect_consumed_class_pair_param(%T13partial_apply14SwiftClassPairV* noalias nocapture dereferenceable({{.*}})) // CHECK: [[CONTEXT_OBJ:%.*]] = call noalias %swift.refcounted* @swift_allocObject // CHECK-NOT: {{retain|release}} // CHECK: [[T0:%.*]] = insertvalue {{.*}} [[PARTIAL_APPLY_FORWARDER:@"\$S[A-Za-z0-9_]+TA"]] {{.*}} [[CONTEXT_OBJ]] @@ -228,12 +228,12 @@ bb0(%x : $*SwiftClassPair): sil public_external @captured_fixed_and_dependent_params : $@convention(thin) (@owned SwiftClass, @in A, Int) -> () -// CHECK-LABEL: define{{( protected)?}} swiftcc { i8*, %swift.refcounted* } @partial_apply_indirect_non_fixed_layout(%T13partial_apply10SwiftClassC*, %swift.opaque* noalias nocapture, i64, %swift.type* %T) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc { i8*, %swift.refcounted* } @partial_apply_indirect_non_fixed_layout(%T13partial_apply10SwiftClassC*, %swift.opaque* noalias nocapture, i64, %swift.type* %T) // -- Round the base offset for the T field up to T's alignment. // CHECK: [[T_METADATA_BASE:%.*]] = bitcast %swift.type* %T to i8*** // CHECK: [[T_VWTABLE_ADDR:%.*]] = getelementptr {{.*}} [[T_METADATA_BASE]], [[WORD:i[0-9]+]] -1 // CHECK: [[T_VWTABLE:%.*]] = load {{.*}} [[T_VWTABLE_ADDR]] -// CHECK: [[T_FLAGS_ADDR:%.*]] = getelementptr {{.*}} [[T_VWTABLE]], i32 10 +// CHECK: [[T_FLAGS_ADDR:%.*]] = getelementptr {{.*}} [[T_VWTABLE]], i32 9 // CHECK: [[T_FLAGS_PTR:%.*]] = load {{.*}} [[T_FLAGS_ADDR]] // CHECK: [[T_FLAGS:%.*]] = ptrtoint {{.*}} [[T_FLAGS_PTR]] to [[WORD]] // CHECK: [[T_ALIGN_MASK:%.*]] = and [[WORD]] [[T_FLAGS]], 65535 @@ -245,7 +245,7 @@ sil public_external @captured_fixed_and_dependent_params : $@convention(thin) @out T): return %p : $@callee_owned () -> @out T } -// CHECK-LABEL: define{{( protected)?}} swiftcc { i8*, %swift.refcounted* } @partial_apply_dynamic_with_out_param +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc { i8*, %swift.refcounted* } @partial_apply_dynamic_with_out_param // CHECK: insertvalue {{.*}} [[FORWARDER:@"\$STA[A-Za-z0-9_]*"]] // CHECK: define internal swiftcc void [[FORWARDER]] // CHECK: call swiftcc void {{%.*}}(%swift.opaque* noalias nocapture sret @@ -329,7 +329,7 @@ bb0(%0 : $Base): sil public_external @receive_closure : $@convention(thin) (@owned @callee_owned () -> (@owned C)) -> () -// CHECK-LABEL: define{{( protected)?}} swiftcc void @test_partial_apply(%T13partial_apply4BaseC*) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @test_partial_apply(%T13partial_apply4BaseC*) // CHECK: [[CTX:%.*]] = bitcast %T13partial_apply4BaseC* %0 to %swift.refcounted* // CHECK: [[TMP:%.*]] = call swiftcc %swift.metadata_response @"$S13partial_apply3SubCMa"(i64 0) // CHECK: [[TYPE:%.*]] = extractvalue %swift.metadata_response [[TMP]], 0 @@ -362,7 +362,7 @@ bb0(%0 : $Base): sil public_external @partial_empty_box : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } <()>, @inout ()) -> () -// CHECK-LABEL: define{{( protected)?}} swiftcc void @empty_box() +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @empty_box() sil @empty_box : $@convention(thin) () -> () { entry: // CHECK: [[BOX:%.*]] = call {{.*}}swift_allocEmptyBox @@ -384,11 +384,11 @@ sil hidden_external @complex_generic_function : $@convention(thin) (Int) -> () { bb0(%0 : $Int): %fn = function_ref @complex_generic_function : $@convention(thin) (Int) -> () - %pa = partial_apply %fn (%0) : $@convention(thin) (Int) -> () + %pa = partial_apply %fn (%0) : $@convention(thin) (Int) -> () %result = tuple () return %result : $() } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @partial_apply_complex_generic_function(i64, %swift.type* %T, i8** %T.P2, i8** %T.Y.P2) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @partial_apply_complex_generic_function(i64, %swift.type* %T, i8** %T.P2, i8** %T.Y.P2) // CHECK: [[T0:%.*]] = call noalias %swift.refcounted* @swift_allocObject(%swift.type* {{.*}}, i64 48, i64 7) // CHECK-NEXT: [[T1:%.*]] = bitcast %swift.refcounted* [[T0]] to <{ %swift.refcounted, [24 x i8], %TSi }>* // CHECK-NEXT: [[T2:%.*]] = getelementptr inbounds <{ %swift.refcounted, [24 x i8], %TSi }>, <{ %swift.refcounted, [24 x i8], %TSi }>* [[T1]], i32 0, i32 1 diff --git a/test/IRGen/partial_apply_forwarder.sil b/test/IRGen/partial_apply_forwarder.sil index 81f8dd905aade..f3440bd117952 100644 --- a/test/IRGen/partial_apply_forwarder.sil +++ b/test/IRGen/partial_apply_forwarder.sil @@ -78,7 +78,7 @@ sil hidden_external @takingQ : $@convention(thin) <τ_0_0 where τ_0_0 : Q> (@o sil hidden_external @takingQAndEmpty : $@convention(thin) <τ_0_0 where τ_0_0 : Q> (@owned WeakBox<τ_0_0>, EmptyType) -> () sil hidden_external @takingEmptyAndQ : $@convention(thin) <τ_0_0 where τ_0_0 : Q> (EmptyType, @owned WeakBox<τ_0_0>) -> () -// CHECK-LABEL: define{{( protected)?}} swiftcc { i8*, %swift.refcounted* } @bind_polymorphic_param_from_context(%swift.opaque* noalias nocapture, %swift.type* %"\CF\84_0_1") +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc { i8*, %swift.refcounted* } @bind_polymorphic_param_from_context(%swift.opaque* noalias nocapture, %swift.type* %"\CF\84_0_1") // CHECK: entry: // CHECK: [[TMP:%.*]] = call swiftcc %swift.metadata_response @"$S23partial_apply_forwarder12BaseProducerVMa"([[INT]] 255, %swift.type* %"\CF\84_0_1") // CHECK: [[BPTYPE:%.*]] = extractvalue %swift.metadata_response [[TMP]], 0 @@ -111,7 +111,7 @@ bb0(%0 : $*τ_0_1): return %9 : $@callee_owned () -> () } -// CHECK-LABEL: define{{( protected)?}} swiftcc { i8*, %swift.refcounted* } @bind_polymorphic_param_from_context_2(%swift.opaque* noalias nocapture, %swift.type* %"\CF\84_0_1") +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc { i8*, %swift.refcounted* } @bind_polymorphic_param_from_context_2(%swift.opaque* noalias nocapture, %swift.type* %"\CF\84_0_1") // CHECK: [[OBJ:%.*]] = call {{.*}} @swift_allocObject // CHECK: [[WB:%.*]] = bitcast %swift.refcounted* [[OBJ]] // CHECK: [[REF:%.*]] = bitcast {{.*}}* [[WB]] to %swift.refcounted* @@ -125,7 +125,7 @@ bb0(%0 : $*τ_0_1, %2: $EmptyType): return %9 : $@callee_owned () -> () } -// CHECK-LABEL: define{{( protected)?}} swiftcc { i8*, %swift.refcounted* } @bind_polymorphic_param_from_context_3(%swift.opaque* noalias nocapture, %swift.type* %"\CF\84_0_1") +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc { i8*, %swift.refcounted* } @bind_polymorphic_param_from_context_3(%swift.opaque* noalias nocapture, %swift.type* %"\CF\84_0_1") // CHECK: [[OBJ:%.*]] = call {{.*}} @swift_allocObject // CHECK: [[WB:%.*]] = bitcast %swift.refcounted* [[OBJ]] // CHECK: [[REF:%.*]] = bitcast {{.*}}* [[WB]] to %swift.refcounted* @@ -140,7 +140,7 @@ bb0(%0 : $*τ_0_1, %2: $EmptyType): return %9 : $@callee_owned () -> () } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @bind_polymorphic_param_from_forwarder_parameter(%swift.opaque* noalias nocapture, %swift.type* %"\CF\84_0_1") +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @bind_polymorphic_param_from_forwarder_parameter(%swift.opaque* noalias nocapture, %swift.type* %"\CF\84_0_1") // CHECK: entry: // CHECK: [[TMP:%.*]] = call swiftcc %swift.metadata_response @"$S23partial_apply_forwarder12BaseProducerVMa"([[INT]] 255, %swift.type* %"\CF\84_0_1") // CHECK: [[BPTY:%.*]] = extractvalue %swift.metadata_response [[TMP]], 0 @@ -176,7 +176,7 @@ struct S { sil hidden_external @takingQAndS : $@convention(thin) <τ_0_0 where τ_0_0 : Q> (S, @owned WeakBox<τ_0_0>) -> () -// CHECK-LABEL: define{{( protected)?}} swiftcc { i8*, %swift.refcounted* } @bind_polymorphic_param_from_context_with_layout(%swift.opaque* noalias nocapture, i64, %swift.type* %"\CF\84_0_1") +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc { i8*, %swift.refcounted* } @bind_polymorphic_param_from_context_with_layout(%swift.opaque* noalias nocapture, i64, %swift.type* %"\CF\84_0_1") // CHECK: entry: // CHECK: [[TMP:%.*]] = call swiftcc %swift.metadata_response @"$S23partial_apply_forwarder12BaseProducerVMa"([[INT]] 255, %swift.type* %"\CF\84_0_1") // CHECK: [[BPTY:%.*]] = extractvalue %swift.metadata_response [[TMP]], 0 diff --git a/test/IRGen/partial_apply_objc.sil b/test/IRGen/partial_apply_objc.sil index 7be814a40a181..6908d374b4ff6 100644 --- a/test/IRGen/partial_apply_objc.sil +++ b/test/IRGen/partial_apply_objc.sil @@ -48,7 +48,7 @@ bb0(%0 : $ObjCClass): return %0 : $ObjCClass } -// CHECK-LABEL: define{{( protected)?}} swiftcc { i8*, %swift.refcounted* } @indirect_partial_apply(i8*, %swift.refcounted*, i64) {{.*}} { +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc { i8*, %swift.refcounted* } @indirect_partial_apply(i8*, %swift.refcounted*, i64) {{.*}} { // CHECK: entry: // CHECK: [[T0:%.*]] = bitcast i8* %0 to void (i64, %swift.refcounted*)* // CHECK: [[OBJ:%.*]] = call noalias %swift.refcounted* @swift_allocObject(%swift.type* getelementptr inbounds (%swift.full_boxmetadata, %swift.full_boxmetadata* @metadata, i32 0, i32 2), i64 40, i64 7) @@ -83,7 +83,7 @@ entry(%f : $@callee_owned (Builtin.Word) -> (), %x : $Builtin.Word): return %p : $@callee_owned () -> () } -// CHECK-LABEL: define{{( protected)?}} swiftcc { i8*, %swift.refcounted* } @objc_partial_apply(%T18partial_apply_objc9ObjCClassC*) {{.*}} { +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc { i8*, %swift.refcounted* } @objc_partial_apply(%T18partial_apply_objc9ObjCClassC*) {{.*}} { // CHECK: entry: // CHECK: [[OBJ:%.*]] = call noalias %swift.refcounted* @swift_allocObject(%swift.type* getelementptr inbounds (%swift.full_boxmetadata, %swift.full_boxmetadata* @metadata.2, i32 0, i32 2), i64 24, i64 7) // CHECK: [[DATA_ADDR:%.*]] = bitcast %swift.refcounted* [[OBJ]] to [[DATA_TYPE:<{ %swift.refcounted, %T18partial_apply_objc9ObjCClassC\* }>]]* @@ -136,7 +136,7 @@ entry(%c : $ObjCClass): return %p : $@callee_owned NSRect -> () } -// CHECK-LABEL: define{{( protected)?}} swiftcc { i8*, %swift.refcounted* } @objc_partial_apply_consumes_self(%T18partial_apply_objc9ObjCClassC*) {{.*}} { +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc { i8*, %swift.refcounted* } @objc_partial_apply_consumes_self(%T18partial_apply_objc9ObjCClassC*) {{.*}} { // CHECK: bitcast %swift.refcounted* {{%.*}} to [[DATA_TYPE:<{ %swift.refcounted, %T18partial_apply_objc9ObjCClassC\* }>]]* // CHECK: insertvalue {{.*}} [[OBJC_CONSUMES_SELF_PARTIAL_APPLY_STUB:@"\$STa[A-Za-z0-9_.]*"]] // CHECK: define internal swiftcc %T18partial_apply_objc9ObjCClassC* [[OBJC_CONSUMES_SELF_PARTIAL_APPLY_STUB]](%swift.refcounted* swiftself) {{.*}} { @@ -166,7 +166,7 @@ entry(%x : $Int): return %v : $() } -// CHECK-LABEL: define{{( protected)?}} swiftcc { i8*, %swift.refcounted* } @dynamic_lookup_br_partial_apply(%objc_object*) {{.*}} { +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc { i8*, %swift.refcounted* } @dynamic_lookup_br_partial_apply(%objc_object*) {{.*}} { // CHECK: phi i8* [ bitcast (void (i64)* @dummy to i8*), {{%.*}} ], [ bitcast (void (i64, %swift.refcounted*)* [[DYNAMIC_LOOKUP_BR_PARTIAL_APPLY_STUB:@"\$STa[A-Za-z0-9_.]*"]] to i8*), {{%.*}} ] // CHECK: define internal swiftcc void [[DYNAMIC_LOOKUP_BR_PARTIAL_APPLY_STUB]](i64, %swift.refcounted* swiftself) {{.*}} { // CHECK: load i8*, i8** @"\01L_selector(methodWithX:)", align 8 @@ -190,7 +190,7 @@ done(%f : $@callee_owned Int -> ()): sil @partially_applyable_to_pure_objc : $@convention(thin) Gizmo -> () -// CHECK: define{{( protected)?}} swiftcc { i8*, %swift.refcounted* } @partial_apply_pure_objc +// CHECK: define{{( dllexport)?}}{{( protected)?}} swiftcc { i8*, %swift.refcounted* } @partial_apply_pure_objc // CHECK: @swift_allocObject sil @partial_apply_pure_objc : $@convention(thin) Gizmo -> @callee_owned () -> () { entry(%c : $Gizmo): diff --git a/test/IRGen/pic.swift b/test/IRGen/pic.swift index 8534f86770fc8..4242288e8d2bf 100644 --- a/test/IRGen/pic.swift +++ b/test/IRGen/pic.swift @@ -3,23 +3,21 @@ // RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil %s -module-name main -S -o - | %FileCheck -check-prefix=%target-cpu %s -// XFAIL: linux - var global: Int = 0 public func use_global() -> Int { return global } -// x86_64-LABEL: _$S4main10use_globalSiyF: -// x86_64: movq _$S4main6globalSivp(%rip), %rax +// x86_64-LABEL: {{_?}}$S4main10use_globalSiyF: +// x86_64: movq {{_?}}{{[(]?}}$S4main6globalSivp{{[)]?}}(%rip), %rax -// i386-LABEL: _$S4main10use_globalSiyF: +// i386-LABEL: {{_?}}$S4main10use_globalSiyF: // i386: [[PIC_BLOCK:^L.*\$pb]]:{{$}} // i386: popl [[PIC_REG:%[a-z]+]] -// i386: movl _$S4main6globalSivp-[[PIC_BLOCK]]([[PIC_REG]]), {{%[a-z]+}} +// i386: movl {{_?}}$S4main6globalSivp-[[PIC_BLOCK]]([[PIC_REG]]), {{%[a-z]+}} -// armv7-LABEL: _$S4main10use_globalSiyF: +// armv7-LABEL: {{_?}}$S4main10use_globalSiyF: // Check for the runtime memory enforcement call. The global address may be // materialized in a different register prior to that call. // armv7: bl _swift_beginAccess @@ -29,7 +27,7 @@ public func use_global() -> Int { // armv7: add [[R_ADR]], pc // armv7: ldr [[R_ADR]], {{\[}}[[R_ADR]]{{\]}} -// armv7s-LABEL: _$S4main10use_globalSiyF: +// armv7s-LABEL: {{_?}}$S4main10use_globalSiyF: // armv7s: bl _swift_beginAccess // armv7s: movw [[R_ADR:r.*]], :lower16:(_$S4main6globalSivp-([[PIC_0:L.*]]+4)) // armv7s: movt [[R_ADR]], :upper16:(_$S4main6globalSivp-([[PIC_0]]+4)) @@ -37,7 +35,7 @@ public func use_global() -> Int { // armv7s: add [[R_ADR]], pc // armv7s: ldr [[R_ADR]], {{\[}}[[R_ADR]]{{\]}} -// armv7k-LABEL: _$S4main10use_globalSiyF: +// armv7k-LABEL: {{_?}}$S4main10use_globalSiyF: // armv7k: bl _swift_beginAccess // armv7k: movw [[R_ADR:r.*]], :lower16:(_$S4main6globalSivp-([[PIC_0:L.*]]+4)) // armv7k: movt [[R_ADR]], :upper16:(_$S4main6globalSivp-([[PIC_0]]+4)) @@ -45,7 +43,7 @@ public func use_global() -> Int { // armv7k: add [[R_ADR]], pc // armv7k: ldr [[R_ADR]], {{\[}}[[R_ADR]]{{\]}} -// arm64-LABEL: _$S4main10use_globalSiyF: +// arm64-LABEL: {{_?}}$S4main10use_globalSiyF: // arm64: bl _swift_beginAccess // arm64: adrp [[REG1:x[0-9]+]], _$S4main6globalSivp@PAGE // arm64: add [[REG2:x[0-9]+]], [[REG1]], _$S4main6globalSivp@PAGEOFF diff --git a/test/IRGen/playground.swift b/test/IRGen/playground.swift index 86866f6cb5d59..c6d7616fdfdbc 100644 --- a/test/IRGen/playground.swift +++ b/test/IRGen/playground.swift @@ -13,7 +13,7 @@ public func anchor() {} anchor() -// CHECK-LABEL: define{{( protected)?}} i32 @main +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} i32 @main // CHECK: call void @runtime_registration // CHECK: call swiftcc void @"$S10playground6anchoryyF" // CHECK: ret void diff --git a/test/IRGen/preserve_exclusivity.swift b/test/IRGen/preserve_exclusivity.swift new file mode 100644 index 0000000000000..14e1a87160e7a --- /dev/null +++ b/test/IRGen/preserve_exclusivity.swift @@ -0,0 +1,55 @@ +// RUN: %target-swift-frontend -swift-version 3 -parse-stdlib -Xllvm -sil-disable-pass=FunctionSignatureOpts -Xllvm -sil-disable-pass=GenericSpecializer -emit-ir -O %s | %FileCheck %s +// RUN: %target-swift-frontend -swift-version 4 -parse-stdlib -Xllvm -sil-disable-pass=FunctionSignatureOpts -Xllvm -sil-disable-pass=GenericSpecializer -emit-ir -O %s | %FileCheck %s +// +// Check that the -O pipeline always preserves the runtime calls for Builtin access markers and that the KeyPath implementation is fully inlined. + +@_silgen_name("marker1") +func marker1() -> () + +@_silgen_name("marker2") +func marker2() -> () + +@_silgen_name("marker3") +func marker3() -> () + +// IR-LABEL: define {{.*}}swiftcc void @"$S20preserve_exclusivity11beginAccessyyBp_BpxmtlF"(i8*, i8*, %swift.type*{{.*}}, %swift.type*{{.*}} %T1) +// IR: call void @swift_beginAccess +// IR-NEXT: ret void + +public func beginAccess(_ address: Builtin.RawPointer, _ scratch: Builtin.RawPointer, _ ty1: T1.Type) { + marker1() + Builtin.beginUnpairedModifyAccess(address, scratch, ty1); +} + +// CHECK-LABEL: define {{.*}}swiftcc void @"$S20preserve_exclusivity9endAccessyyBpF"(i8*{{.*}}) +// CHECK: call void @swift_endAccess +// CHECK-NEXT: ret void +public func endAccess(_ address: Builtin.RawPointer) { + marker2() + Builtin.endUnpairedAccess(address) +} + +// CHECK-LABEL: define {{.*}}swiftcc void @"$S20preserve_exclusivity10readAccessyyBp_xmtlF"(i8*, %swift.type*{{.*}}, %swift.type*{{.*}} %T1) +// CHECK: call void @swift_beginAccess +// CHECK: ret void +@_semantics("optimize.sil.preserve_exclusivity") +public func readAccess(_ address: Builtin.RawPointer, _ ty1: T1.Type) { + marker3() + Builtin.performInstantaneousReadAccess(address, ty1); +} + +// Make sure testAccess properly inlines in our functions. +// +// CHECK-LABEL: define {{.*}}swiftcc void @"$S20preserve_exclusivity10testAccessyyBpF"(i8*) +// CHECK: call swiftcc void @marker1 +// CHECK: call void @swift_beginAccess +// CHECK: call swiftcc void @marker2 +// CHECK: call void @swift_endAccess +// CHECK: call swiftcc void @marker3 +// CHECK: call void @swift_beginAccess +// CHECK: ret void +public func testAccess(_ k1: Builtin.RawPointer) { + beginAccess(k1, k1, Builtin.RawPointer.self) + endAccess(k1) + readAccess(k1, Builtin.RawPointer.self) +} diff --git a/test/IRGen/property_descriptor.sil b/test/IRGen/property_descriptor.sil index 75c25a3afd7ef..51adf08a60417 100644 --- a/test/IRGen/property_descriptor.sil +++ b/test/IRGen/property_descriptor.sil @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %utils/chex.py < %s > %t/property_descriptor.sil +// RUN: %{python} %utils/chex.py < %s > %t/property_descriptor.sil // RUN: %target-swift-frontend -emit-ir %t/property_descriptor.sil | %FileCheck --check-prefix=CHECK-%target-ptrsize --check-prefix=CHECK %t/property_descriptor.sil sil_stage canonical @@ -36,20 +36,20 @@ public struct ExternalReabstractions { } // -- 0xff_fffe - struct property, offset resolved from field offset vector in metadata -// CHECK-64: @"$S19property_descriptor15ExternalGenericV2roxvpMV" ={{( protected)?}}{{( protected)?}} constant +// CHECK-64: @"$S19property_descriptor15ExternalGenericV2roxvpMV" ={{( dllexport)?}}{{( protected)?}} constant // CHECK-64-SAME: <{ , i32 32 }>, align 8 -// CHECK-32: @"$S19property_descriptor15ExternalGenericV2roxvpMV" ={{( protected)?}}{{( protected)?}} constant +// CHECK-32: @"$S19property_descriptor15ExternalGenericV2roxvpMV" ={{( dllexport)?}}{{( protected)?}} constant // CHECK-32-SAME: <{ , i32 16 }>, align 4 sil_property #ExternalGeneric.ro ( stored_property #ExternalGeneric.ro : $T) -// CHECK-64: @"$S19property_descriptor15ExternalGenericV2rwxvpMV" ={{( protected)?}}{{( protected)?}} constant -// CHECK-64-SAME: <{ , i32 40 }>, align 8 -// CHECK-32: @"$S19property_descriptor15ExternalGenericV2rwxvpMV" ={{( protected)?}} constant +// CHECK-64: @"$S19property_descriptor15ExternalGenericV2rwxvpMV" ={{( dllexport)?}}{{( protected)?}} constant +// CHECK-64-SAME: <{ , i32 36 }>, align 8 +// CHECK-32: @"$S19property_descriptor15ExternalGenericV2rwxvpMV" ={{( dllexport)?}}{{( protected)?}} constant // CHECK-32-SAME: <{ , i32 20 }>, align 4 sil_property #ExternalGeneric.rw ( stored_property #ExternalGeneric.rw : $T) -// CHECK: @"$S19property_descriptor15ExternalGenericV10computedROxvpMV" ={{( protected)?}} constant +// CHECK: @"$S19property_descriptor15ExternalGenericV10computedROxvpMV" ={{( dllexport)?}}{{( protected)?}} constant // -- 0x0108_0000 - computed, readonly, has arguments // CHECK-SAME: <{ , // CHECK-SAME: @id_computed, @@ -62,7 +62,7 @@ sil_property #ExternalGeneric.computedRO ( id @id_computed : $@convention(thin) () -> (), getter @get_computed_generic : $@convention(thin) (@in_guaranteed ExternalGeneric) -> @out T) -// CHECK: @"$S19property_descriptor15ExternalGenericV10computedRWxvpMV" ={{( protected)?}} constant +// CHECK: @"$S19property_descriptor15ExternalGenericV10computedRWxvpMV" ={{( dllexport)?}}{{( protected)?}} constant // -- 0x01c8_0000 - computed, settable, mutating, has arguments // CHECK-SAME: <{ , // CHECK-SAME: @id_computed, @@ -77,7 +77,7 @@ sil_property #ExternalGeneric.computedRW ( getter @get_computed_generic : $@convention(thin) (@in_guaranteed ExternalGeneric) -> @out T, setter @set_computed_generic : $@convention(thin) (@in_guaranteed T, @inout ExternalGeneric) -> ()) -// CHECK: @"$S19property_descriptor15ExternalGenericVyxqd__cs8HashableRd__luipMV" ={{( protected)?}} constant +// CHECK: @"$S19property_descriptor15ExternalGenericVyxqd__cs8HashableRd__luipMV" ={{( dllexport)?}}{{( protected)?}} constant // -- 0x01c8_0000 - computed, settable, mutating, has arguments // CHECK-SAME: <{ , // CHECK-SAME: @id_computed, @@ -92,9 +92,9 @@ sil_property #ExternalGeneric.subscript ( getter @get_computed_generic_subscript : $@convention(thin) (@in_guaranteed ExternalGeneric, UnsafeRawPointer) -> @out T, setter @set_computed_generic_subscript : $@convention(thin) (@in_guaranteed T, @inout ExternalGeneric, UnsafeRawPointer) -> ()) -// CHECK: @"$S19property_descriptor8ExternalV2roSivpMV" ={{( protected)?}} constant <{ i32 }> zeroinitializer, align 4 -// CHECK-64: @"$S19property_descriptor8ExternalV2rwSivpMV" ={{( protected)?}} constant <{ i32 }> <{ i32 8 }>, align 4 -// CHECK-32: @"$S19property_descriptor8ExternalV2rwSivpMV" ={{( protected)?}} constant <{ i32 }> <{ i32 4 }>, align 4 +// CHECK: @"$S19property_descriptor8ExternalV2roSivpMV" ={{( dllexport)?}}{{( protected)?}} constant <{ i32 }> zeroinitializer, align 4 +// CHECK-64: @"$S19property_descriptor8ExternalV2rwSivpMV" ={{( dllexport)?}}{{( protected)?}} constant <{ i32 }> <{ i32 8 }>, align 4 +// CHECK-32: @"$S19property_descriptor8ExternalV2rwSivpMV" ={{( dllexport)?}}{{( protected)?}} constant <{ i32 }> <{ i32 4 }>, align 4 sil_property #External.ro (stored_property #External.ro : $Int) sil_property #External.rw (stored_property #External.rw : $Int) sil_property #External.computedRO ( diff --git a/test/IRGen/protocol_conformance_records.swift b/test/IRGen/protocol_conformance_records.swift index c0c7d4b9b9886..79baebe8173fe 100644 --- a/test/IRGen/protocol_conformance_records.swift +++ b/test/IRGen/protocol_conformance_records.swift @@ -9,7 +9,7 @@ public protocol Associate { } // Dependent conformance -// CHECK-LABEL: @"$S28protocol_conformance_records9DependentVyxGAA9AssociateAAMc" ={{ protected | }}constant +// CHECK-LABEL: @"$S28protocol_conformance_records9DependentVyxGAA9AssociateAAMc" ={{ dllexport | protected | }}constant // -- protocol descriptor // CHECK-SAME: @"$S28protocol_conformance_records9AssociateMp" // -- nominal type descriptor @@ -29,7 +29,7 @@ public protocol Runcible { func runce() } -// CHECK-LABEL: @"$S28protocol_conformance_records15NativeValueTypeVAA8RuncibleAAMc" ={{ protected | }}constant %swift.protocol_conformance_descriptor { +// CHECK-LABEL: @"$S28protocol_conformance_records15NativeValueTypeVAA8RuncibleAAMc" ={{ dllexport | protected | }}constant %swift.protocol_conformance_descriptor { // -- protocol descriptor // CHECK-SAME: [[RUNCIBLE:@"\$S28protocol_conformance_records8RuncibleMp"]] // -- type metadata @@ -43,7 +43,7 @@ public struct NativeValueType: Runcible { public func runce() {} } -// CHECK-LABEL: @"$S28protocol_conformance_records15NativeClassTypeCAA8RuncibleAAMc" ={{ protected | }}constant %swift.protocol_conformance_descriptor { +// CHECK-LABEL: @"$S28protocol_conformance_records15NativeClassTypeCAA8RuncibleAAMc" ={{ dllexport | protected | }}constant %swift.protocol_conformance_descriptor { // -- protocol descriptor // CHECK-SAME: [[RUNCIBLE]] // -- class metadata @@ -57,7 +57,7 @@ public class NativeClassType: Runcible { public func runce() {} } -// CHECK-LABEL: @"$S28protocol_conformance_records17NativeGenericTypeVyxGAA8RuncibleAAMc" ={{ protected | }}constant %swift.protocol_conformance_descriptor { +// CHECK-LABEL: @"$S28protocol_conformance_records17NativeGenericTypeVyxGAA8RuncibleAAMc" ={{ dllexport | protected | }}constant %swift.protocol_conformance_descriptor { // -- protocol descriptor // CHECK-SAME: [[RUNCIBLE]] // -- nominal type descriptor @@ -71,7 +71,7 @@ public struct NativeGenericType: Runcible { public func runce() {} } -// CHECK-LABEL: @"$SSi28protocol_conformance_records8RuncibleAAMc" ={{ protected | }}constant %swift.protocol_conformance_descriptor { +// CHECK-LABEL: @"$SSi28protocol_conformance_records8RuncibleAAMc" ={{ dllexport | protected | }}constant %swift.protocol_conformance_descriptor { // -- protocol descriptor // CHECK-SAME: [[RUNCIBLE]] // -- type metadata @@ -87,7 +87,7 @@ extension Int: Runcible { // For a resilient struct, reference the NominalTypeDescriptor -// CHECK-LABEL: @"$S16resilient_struct4SizeV28protocol_conformance_records8RuncibleADMc" ={{ protected | }}constant %swift.protocol_conformance_descriptor { +// CHECK-LABEL: @"$S16resilient_struct4SizeV28protocol_conformance_records8RuncibleADMc" ={{ dllexport | protected | }}constant %swift.protocol_conformance_descriptor { // -- protocol descriptor // CHECK-SAME: [[RUNCIBLE]] // -- nominal type descriptor @@ -109,7 +109,7 @@ extension Size: Runcible { public protocol Spoon { } // Conditional conformances -// CHECK-LABEL: {{^}}@"$S28protocol_conformance_records17NativeGenericTypeVyxGAA5SpoonA2aERzlMc" ={{ protected | }}constant +// CHECK-LABEL: {{^}}@"$S28protocol_conformance_records17NativeGenericTypeVyxGAA5SpoonA2aERzlMc" ={{ dllexport | protected | }}constant // -- protocol descriptor // CHECK-SAME: @"$S28protocol_conformance_records5SpoonMp" // -- nominal type descriptor @@ -119,7 +119,7 @@ public protocol Spoon { } // -- flags // CHECK-SAME: i32 258 // -- conditional requirement #1 -// CHECK-SAME: i32 64, +// CHECK-SAME: i32 128, // CHECK-SAME: i32 0, // CHECK-SAME: @"$S28protocol_conformance_records5SpoonMp" // CHECK-SAME: } @@ -128,7 +128,7 @@ extension NativeGenericType : Spoon where T: Spoon { } // Retroactive conformance -// CHECK-LABEL: @"$SSi18resilient_protocol22OtherResilientProtocol0B20_conformance_recordsMc" ={{ protected | }}constant +// CHECK-LABEL: @"$SSi18resilient_protocol22OtherResilientProtocol0B20_conformance_recordsMc" ={{ dllexport | protected | }}constant // -- protocol descriptor // CHECK-SAME: @"got.$S18resilient_protocol22OtherResilientProtocolMp" // -- nominal type descriptor diff --git a/test/IRGen/protocol_extensions.sil b/test/IRGen/protocol_extensions.sil index af6a41e174a5d..0bbc1f15ba46e 100644 --- a/test/IRGen/protocol_extensions.sil +++ b/test/IRGen/protocol_extensions.sil @@ -24,7 +24,7 @@ bb0(%0 : $*Self): return %5 : $() // id: %6 } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @_TFP19protocol_extensions2P16extP1bUS0___fQPS0_FT_T_ +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @_TFP19protocol_extensions2P16extP1bUS0___fQPS0_FT_T_ sil @_TFP19protocol_extensions2P16extP1bUS0___fQPS0_FT_T_ : $@convention(method) (@in Self) -> () { bb0(%0 : $*Self): debug_value_addr %0 : $*Self, let, name "self" // id: %1 diff --git a/test/IRGen/protocol_metadata.swift b/test/IRGen/protocol_metadata.swift index 78aafb470ae5c..5b9639b90080c 100644 --- a/test/IRGen/protocol_metadata.swift +++ b/test/IRGen/protocol_metadata.swift @@ -1,7 +1,6 @@ -// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -primary-file %s -emit-ir -disable-objc-attr-requires-foundation-module | %FileCheck %s +// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -primary-file %s -emit-ir -disable-objc-attr-requires-foundation-module -enable-objc-interop | %FileCheck %s // REQUIRES: CPU=x86_64 -// REQUIRES: objc_interop protocol A { func a() } protocol B { func b() } diff --git a/test/IRGen/protocol_resilience.sil b/test/IRGen/protocol_resilience.sil index 7dc3b7a83cf45..335537fd909b6 100644 --- a/test/IRGen/protocol_resilience.sil +++ b/test/IRGen/protocol_resilience.sil @@ -78,7 +78,7 @@ import resilient_protocol // Protocol descriptor for ResilientProtocol -// CHECK: @"$S19protocol_resilience17ResilientProtocolMp" = {{(protected )?}}constant %swift.protocol { +// CHECK: @"$S19protocol_resilience17ResilientProtocolMp" = {{(dllexport )?}}{{(protected )?}}constant %swift.protocol { // CHECK-SAME: i32 1031, // CHECK-SAME: i32 8, // CHECK-SAME: i32{{ | trunc \(i64 }}sub ([[INT]] ptrtoint ([8 x %swift.protocol_requirement]* [[RP_REQTS]] to [[INT]]), [[INT]] ptrtoint (i32* getelementptr inbounds (%swift.protocol, %swift.protocol* @"$S19protocol_resilience17ResilientProtocolMp", i32 0 @@ -167,7 +167,7 @@ protocol InternalProtocol { // CHECK-SAME: i8* bitcast (i8** ()* @"$S19protocol_resilience23ResilientConformingTypeV010resilient_A005OtherC8ProtocolAAWa" to i8*) // CHECK-SAME: ] -// CHECK-LABEL: define{{( protected)?}} swiftcc void @defaultC(%swift.opaque* noalias nocapture swiftself, %swift.type* %Self, i8** %SelfWitnessTable) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @defaultC(%swift.opaque* noalias nocapture swiftself, %swift.type* %Self, i8** %SelfWitnessTable) // CHECK-NEXT: entry: sil @defaultC : $@convention(witness_method: ResilientProtocol) (@in_guaranteed Self) -> () { @@ -178,7 +178,7 @@ bb0(%0 : $*Self): } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @defaultD(%swift.opaque* noalias nocapture swiftself, %swift.type* %Self, i8** %SelfWitnessTable) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @defaultD(%swift.opaque* noalias nocapture swiftself, %swift.type* %Self, i8** %SelfWitnessTable) // CHECK-NEXT: entry: sil @defaultD : $@convention(witness_method: ResilientProtocol) (@in_guaranteed Self) -> () { @@ -218,7 +218,7 @@ bb0(%0 : $*Self): } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @defaultE(%swift.type* swiftself, %swift.type* %Self, i8** %SelfWitnessTable) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @defaultE(%swift.type* swiftself, %swift.type* %Self, i8** %SelfWitnessTable) // CHECK-NEXT: entry: sil @defaultE : $@convention(witness_method: ResilientProtocol) (@thick Self.Type) -> () { @@ -258,7 +258,7 @@ bb0(%0 : $@thick Self.Type): } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @defaultF(%swift.type* swiftself, %swift.type* %Self, i8** %SelfWitnessTable) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @defaultF(%swift.type* swiftself, %swift.type* %Self, i8** %SelfWitnessTable) // CHECK-NEXT: entry: sil @defaultF : $@convention(witness_method: ResilientProtocol) (@thick Self.Type) -> () { @@ -296,7 +296,7 @@ struct ConformingStruct : ResilientProtocol { static func defaultF() } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @noDefaultA(%T19protocol_resilience16ConformingStructV* noalias nocapture swiftself, %swift.type* %Self, i8** %SelfWitnessTable) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @noDefaultA(%T19protocol_resilience16ConformingStructV* noalias nocapture swiftself, %swift.type* %Self, i8** %SelfWitnessTable) // CHECK-NEXT: entry: sil @noDefaultA : $@convention(witness_method: ResilientProtocol) (@in_guaranteed ConformingStruct) -> () { @@ -316,7 +316,7 @@ bb0(%0 : $*ConformingStruct): } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @noDefaultB(%T19protocol_resilience16ConformingStructV* noalias nocapture swiftself, %swift.type* %Self, i8** %SelfWitnessTable) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @noDefaultB(%T19protocol_resilience16ConformingStructV* noalias nocapture swiftself, %swift.type* %Self, i8** %SelfWitnessTable) // CHECK-NEXT: entry: sil @noDefaultB : $@convention(witness_method: ResilientProtocol) (@in_guaranteed ConformingStruct) -> () { @@ -354,14 +354,14 @@ sil_witness_table ConformingStruct : ResilientProtocol module protocol_resilienc // Make sure resilient conformances are accessed with an accessor function // -// CHECK-LABEL: define{{( protected)?}} swiftcc void @doSomething(%swift.opaque* noalias nocapture, %swift.type* %T, i8** %T.OtherResilientProtocol) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @doSomething(%swift.opaque* noalias nocapture, %swift.type* %T, i8** %T.OtherResilientProtocol) sil @doSomething : $@convention(thin) (@in T) -> () { bb0(%0 : $*T): %result = tuple () return %result : $() } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @passConformingType(%T19protocol_resilience23ResilientConformingTypeV* noalias nocapture) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @passConformingType(%T19protocol_resilience23ResilientConformingTypeV* noalias nocapture) sil @passConformingType : $@convention(thin) (@in ResilientConformingType) -> () { bb0(%0 : $*ResilientConformingType): @@ -380,7 +380,7 @@ bb0(%0 : $*ResilientConformingType): // Caching witness table accessor -// CHECK-LABEL: define{{( protected)?}} linkonce_odr hidden i8** @"$S19protocol_resilience23ResilientConformingTypeVAC010resilient_A005OtherC8ProtocolAAWl"() +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} linkonce_odr hidden i8** @"$S19protocol_resilience23ResilientConformingTypeVAC010resilient_A005OtherC8ProtocolAAWl"() // CHECK-NEXT: entry: // CHECK-NEXT: [[CACHE:%.*]] = load i8**, i8*** @"$S19protocol_resilience23ResilientConformingTypeVAC010resilient_A005OtherC8ProtocolAAWL" // CHECK-NEXT: [[COND:%.*]] = icmp eq i8** [[CACHE]], null @@ -397,7 +397,7 @@ bb0(%0 : $*ResilientConformingType): // Resilient conformance -- must call a runtime function -// CHECK-LABEL: define{{( protected)?}} i8** @"$S19protocol_resilience23ResilientConformingTypeV010resilient_A005OtherC8ProtocolAAWa"() +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} i8** @"$S19protocol_resilience23ResilientConformingTypeV010resilient_A005OtherC8ProtocolAAWa"() // CHECK-NEXT: entry: // CHECK-NEXT: [[WTABLE:%.*]] = call i8** @swift_getGenericWitnessTable(%swift.generic_witness_table_cache* @"$S19protocol_resilience23ResilientConformingTypeV010resilient_A005OtherC8ProtocolAAWG", %swift.type* null, i8*** null) // CHECK-NEXT: ret i8** [[WTABLE]] @@ -419,14 +419,14 @@ sil_witness_table AnotherConformingStruct : RefinesOtherResilientProtocol module sil_witness_table hidden AnotherConformingStruct: OtherResilientProtocol module protocol_resilience { } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @doSomethingRefined(%swift.opaque* noalias nocapture, %swift.type* %T, i8** %T.RefinesOtherResilientProtocol) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @doSomethingRefined(%swift.opaque* noalias nocapture, %swift.type* %T, i8** %T.RefinesOtherResilientProtocol) sil @doSomethingRefined : $@convention(thin) (@in T) -> () { bb0(%0 : $*T): %result = tuple () return %result : $() } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @passConformingTypeRefined(%T19protocol_resilience23AnotherConformingStructV* noalias nocapture) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @passConformingTypeRefined(%T19protocol_resilience23AnotherConformingStructV* noalias nocapture) sil @passConformingTypeRefined : $@convention(thin) (@in AnotherConformingStruct) -> () { bb0(%0 : $*AnotherConformingStruct): @@ -463,14 +463,14 @@ sil_witness_table ConformsWithResilientAssoc : HasResilientAssoc module protocol associated_type_protocol (T: OtherResilientProtocol): ResilientConformingType: OtherResilientProtocol module protocol_resilience } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @doSomethingAssoc(%swift.opaque* noalias nocapture, %swift.type* %T, i8** %T.HasResilientAssoc) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @doSomethingAssoc(%swift.opaque* noalias nocapture, %swift.type* %T, i8** %T.HasResilientAssoc) sil @doSomethingAssoc : $@convention(thin) (@in T) -> () { bb0(%0 : $*T): %result = tuple () return %result : $() } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @passConformingTypeAssoc(%T19protocol_resilience26ConformsWithResilientAssocV* noalias nocapture) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @passConformingTypeAssoc(%T19protocol_resilience26ConformsWithResilientAssocV* noalias nocapture) sil @passConformingTypeAssoc : $@convention(thin) (@in ConformsWithResilientAssoc) -> () { bb0(%0 : $*ConformsWithResilientAssoc): @@ -515,6 +515,6 @@ sil_witness_table ConformsWithRequirements : ProtocolWithRequirements module pro // Fragile conformance -- no runtime calls needed -// CHECK-LABEL: define{{( protected)?}} hidden i8** @"$S19protocol_resilience16ConformingStructVAA17ResilientProtocolAAWa"() +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} hidden i8** @"$S19protocol_resilience16ConformingStructVAA17ResilientProtocolAAWa"() // CHECK-NEXT: entry: // CHECK-NEXT: ret i8** getelementptr inbounds ([9 x i8*], [9 x i8*]* @"$S19protocol_resilience16ConformingStructVAA17ResilientProtocolAAWP", i32 0, i32 0) diff --git a/test/IRGen/protocol_resilience_thunks.swift b/test/IRGen/protocol_resilience_thunks.swift index d4417c50fecd8..d69fde682a2b0 100644 --- a/test/IRGen/protocol_resilience_thunks.swift +++ b/test/IRGen/protocol_resilience_thunks.swift @@ -7,7 +7,7 @@ import resilient_protocol -// CHECK-LABEL: define{{( protected)?}} swiftcc void @"$S26protocol_resilience_thunks26callResilientWitnessMethodyyx010resilient_A00E12BaseProtocolRzlF"(%swift.opaque* noalias nocapture, %swift.type* %T, i8** %T.ResilientBaseProtocol) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$S26protocol_resilience_thunks26callResilientWitnessMethodyyx010resilient_A00E12BaseProtocolRzlF"(%swift.opaque* noalias nocapture, %swift.type* %T, i8** %T.ResilientBaseProtocol) // CHECK: call swiftcc [[INT]] @"$S18resilient_protocol21ResilientBaseProtocolP11requirementSiyFTj"(%swift.opaque* noalias nocapture swiftself %0, %swift.type* %T, i8** %T.ResilientBaseProtocol) // CHECK: ret void public func callResilientWitnessMethod(_ value: T) { @@ -27,56 +27,56 @@ public protocol MyResilientProtocol { var property: Bool { get set } } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @"$S26protocol_resilience_thunks19MyResilientProtocolP11returnsVoid1xySb_tFTj"(i1, %swift.opaque* noalias nocapture swiftself, %swift.type*, i8**) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$S26protocol_resilience_thunks19MyResilientProtocolP11returnsVoid1xySb_tFTj"(i1, %swift.opaque* noalias nocapture swiftself, %swift.type*, i8**) // CHECK: [[WITNESS_GEP:%.*]] = getelementptr inbounds i8*, i8** %3, i32 1 // CHECK: [[WITNESS:%.*]] = load i8*, i8** [[WITNESS_GEP]] // CHECK-NEXT: [[FN:%.*]] = bitcast i8* [[WITNESS]] to void (i1, %swift.opaque*, %swift.type*, i8**)* // CHECK-NEXT: call swiftcc void [[FN]](i1 %0, %swift.opaque* noalias nocapture swiftself %1, %swift.type* %2, i8** %3) // CHECK-NEXT: ret void -// CHECK-LABEL: define{{( protected)?}} swiftcc i1 @"$S26protocol_resilience_thunks19MyResilientProtocolP11returnsBoolSbyFTj"(%swift.opaque* noalias nocapture swiftself, %swift.type*, i8**) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc i1 @"$S26protocol_resilience_thunks19MyResilientProtocolP11returnsBoolSbyFTj"(%swift.opaque* noalias nocapture swiftself, %swift.type*, i8**) // CHECK: [[WITNESS_ADDR:%.*]] = getelementptr inbounds i8*, i8** %2, i32 2 // CHECK-NEXT: [[WITNESS:%.*]] = load i8*, i8** [[WITNESS_ADDR]] // CHECK-NEXT: [[FN:%.*]] = bitcast i8* [[WITNESS]] to i1 (%swift.opaque*, %swift.type*, i8**)* // CHECK-NEXT: [[RESULT:%.*]] = call swiftcc i1 [[FN]](%swift.opaque* noalias nocapture swiftself %0, %swift.type* %1, i8** %2) // CHECK-NEXT: ret i1 [[RESULT]] -// CHECK-LABEL: define{{( protected)?}} swiftcc void @"$S26protocol_resilience_thunks19MyResilientProtocolP10returnsAnyypyFTj"(%Any* noalias nocapture sret, %swift.opaque* noalias nocapture swiftself, %swift.type*, i8**) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$S26protocol_resilience_thunks19MyResilientProtocolP10returnsAnyypyFTj"(%Any* noalias nocapture sret, %swift.opaque* noalias nocapture swiftself, %swift.type*, i8**) // CHECK: [[WITNESS_ADDR:%.*]] = getelementptr inbounds i8*, i8** %3, i32 3 // CHECK-NEXT: [[WITNESS:%.*]] = load i8*, i8** [[WITNESS_ADDR]] // CHECK-NEXT: [[FN:%.*]] = bitcast i8* [[WITNESS]] to void (%Any*, %swift.opaque*, %swift.type*, i8**)* // CHECK-NEXT: call swiftcc void [[FN]](%Any* noalias nocapture sret %0, %swift.opaque* noalias nocapture swiftself %1, %swift.type* %2, i8** %3) // CHECK-NEXT: ret void -// CHECK-LABEL: define{{( protected)?}} swiftcc void @"$S26protocol_resilience_thunks19MyResilientProtocolP12throwingFuncyyKFTj"(%swift.opaque* noalias nocapture swiftself, %swift.error**{{( swifterror)?}}, %swift.type*, i8**) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$S26protocol_resilience_thunks19MyResilientProtocolP12throwingFuncyyKFTj"(%swift.opaque* noalias nocapture swiftself, %swift.error**{{( swifterror)?}}, %swift.type*, i8**) // CHECK: [[WITNESS_ADDR:%.*]] = getelementptr inbounds i8*, i8** %3, i32 4 // CHECK-NEXT: [[WITNESS:%.*]] = load i8*, i8** [[WITNESS_ADDR]] // CHECK-NEXT: [[FN:%.*]] = bitcast i8* [[WITNESS]] to void (%swift.opaque*, %swift.error**, %swift.type*, i8**)* // CHECK-NEXT: call swiftcc void [[FN]](%swift.opaque* noalias nocapture swiftself %0, %swift.error**{{( swifterror)?}} %1, %swift.type* %2, i8** %3) // CHECK-NEXT: ret void -// CHECK-LABEL: define{{( protected)?}} swiftcc void @"$S26protocol_resilience_thunks19MyResilientProtocolP11genericFuncyqd__qd__lFTj"(%swift.opaque* noalias nocapture sret, %swift.opaque* noalias nocapture, %swift.type*, %swift.opaque* noalias nocapture swiftself, %swift.type*, i8**) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$S26protocol_resilience_thunks19MyResilientProtocolP11genericFuncyqd__qd__lFTj"(%swift.opaque* noalias nocapture sret, %swift.opaque* noalias nocapture, %swift.type*, %swift.opaque* noalias nocapture swiftself, %swift.type*, i8**) // CHECK: [[WITNESS_ADDR:%.*]] = getelementptr inbounds i8*, i8** %5, i32 5 // CHECK-NEXT: [[WITNESS:%.*]] = load i8*, i8** [[WITNESS_ADDR]] // CHECK-NEXT: [[FN:%.*]] = bitcast i8* [[WITNESS]] to void (%swift.opaque*, %swift.opaque*, %swift.type*, %swift.opaque*, %swift.type*, i8**)* // CHECK-NEXT: call swiftcc void [[FN]](%swift.opaque* noalias nocapture sret %0, %swift.opaque* noalias nocapture %1, %swift.type* %2, %swift.opaque* noalias nocapture swiftself %3, %swift.type* %4, i8** %5) // CHECK-NEXT: ret void -// CHECK-LABEL: define{{( protected)?}} swiftcc i1 @"$S26protocol_resilience_thunks19MyResilientProtocolP8propertySbvgTj"(%swift.opaque* noalias nocapture swiftself, %swift.type*, i8**) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc i1 @"$S26protocol_resilience_thunks19MyResilientProtocolP8propertySbvgTj"(%swift.opaque* noalias nocapture swiftself, %swift.type*, i8**) // CHECK: [[WITNESS_ADDR:%.*]] = getelementptr inbounds i8*, i8** %2, i32 6 // CHECK-NEXT: [[WITNESS:%.*]] = load i8*, i8** [[WITNESS_ADDR]] // CHECK-NEXT: [[FN:%.*]] = bitcast i8* [[WITNESS]] to i1 (%swift.opaque*, %swift.type*, i8**)* // CHECK-NEXT: [[RESULT:%.*]] = call swiftcc i1 %5(%swift.opaque* noalias nocapture swiftself %0, %swift.type* %1, i8** %2) // CHECK-NEXT: ret i1 [[RESULT]] -// CHECK-LABEL: define{{( protected)?}} swiftcc void @"$S26protocol_resilience_thunks19MyResilientProtocolP8propertySbvsTj"(i1, %swift.opaque* nocapture swiftself, %swift.type*, i8**) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$S26protocol_resilience_thunks19MyResilientProtocolP8propertySbvsTj"(i1, %swift.opaque* nocapture swiftself, %swift.type*, i8**) // CHECK: [[WITNESS_ADDR:%.*]] = getelementptr inbounds i8*, i8** %3, i32 7 // CHECK-NEXT: [[WITNESS:%.*]] = load i8*, i8** [[WITNESS_ADDR]] // CHECK-NEXT: [[FN:%.*]] = bitcast i8* [[WITNESS]] to void (i1, %swift.opaque*, %swift.type*, i8**)* // CHECK-NEXT: call swiftcc void [[FN]](i1 %0, %swift.opaque* nocapture swiftself %1, %swift.type* %2, i8** %3) // CHECK-NEXT: ret void -// CHECK-LABEL: define{{( protected)?}} swiftcc { i8*, {{i32|i64}} } @"$S26protocol_resilience_thunks19MyResilientProtocolP8propertySbvmTj"(i8*, [{{12|24}} x i8]* nocapture dereferenceable({{12|24}}), %swift.opaque* nocapture swiftself, %swift.type*, i8**) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc { i8*, {{i32|i64}} } @"$S26protocol_resilience_thunks19MyResilientProtocolP8propertySbvmTj"(i8*, [{{12|24}} x i8]* nocapture dereferenceable({{12|24}}), %swift.opaque* nocapture swiftself, %swift.type*, i8**) // CHECK: [[WITNESS_ADDR:%.*]] = getelementptr inbounds i8*, i8** %4, i32 8 // CHECK-NEXT: [[WITNESS:%.*]] = load i8*, i8** [[WITNESS_ADDR]] // CHECK-NEXT: [[FN:%.*]] = bitcast i8* [[WITNESS]] to { i8*, [[INT]] } (i8*, [{{12|24}} x i8]*, %swift.opaque*, %swift.type*, i8**)* diff --git a/test/IRGen/readonly.sil b/test/IRGen/readonly.sil index c7d553cde2f02..32b54e464f227 100644 --- a/test/IRGen/readonly.sil +++ b/test/IRGen/readonly.sil @@ -18,7 +18,7 @@ sil @XXX_ctor : $@convention(method) (@owned XXX) -> @owned XXX // the function body that this function is really read only. // CHECK-NOT: ; Function Attrs: readonly -// CHECK: define{{( protected)?}} swiftcc void @function_foo( +// CHECK: define{{( dllexport)?}}{{( protected)?}} swiftcc void @function_foo( sil [readonly] @function_foo : $@convention(thin) (Int) -> () { bb0(%0 : $Int): %1 = tuple () @@ -31,7 +31,7 @@ bb0(%0 : $Int): // the function body that this function is really read only or read none. // CHECK-NOT: ; Function Attrs: readonly -// CHECK: define{{( protected)?}} swiftcc void @function_foo2( +// CHECK: define{{( dllexport)?}}{{( protected)?}} swiftcc void @function_foo2( sil [readnone] @function_foo2 : $@convention(thin) (Int) -> () { bb0(%0 : $Int): %1 = tuple () @@ -42,7 +42,7 @@ bb0(%0 : $Int): // -- function is not read only at LLVM level // CHECK-NOT: ; Function Attrs: readonly -// CHECK: define{{( protected)?}} swiftcc void @function_bar( +// CHECK: define{{( dllexport)?}}{{( protected)?}} swiftcc void @function_bar( sil [readonly] @function_bar : $@convention(thin) (@owned XXX) -> () { bb0(%0 : $XXX): strong_release %0 : $XXX @@ -53,7 +53,7 @@ bb0(%0 : $XXX): // There's an @out parameter -- function is not read only at LLVM level // CHECK-NOT: ; Function Attrs: readonly -// CHECK: define{{( protected)?}} swiftcc void @function_baz( +// CHECK: define{{( dllexport)?}}{{( protected)?}} swiftcc void @function_baz( sil [readonly] @function_baz : $@convention(thin) () -> (@out Any) { bb0(%0 : $*Any): init_existential_addr %0 : $*Any, $() diff --git a/test/IRGen/reflection_metadata.swift b/test/IRGen/reflection_metadata.swift index 0065eae739202..0664561e4ef64 100644 --- a/test/IRGen/reflection_metadata.swift +++ b/test/IRGen/reflection_metadata.swift @@ -2,51 +2,51 @@ // RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -disable-reflection-names -emit-ir %s | %FileCheck %s --check-prefix=STRIP_REFLECTION_NAMES // RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -disable-reflection-metadata -emit-ir %s | %FileCheck %s --check-prefix=STRIP_REFLECTION_METADATA -// STRIP_REFLECTION_NAMES_DAG: {{.*}}swift4_reflect -// STRIP_REFLECTION_NAMES_DAG: {{.*}}swift4_fieldmd -// STRIP_REFLECTION_NAMES_DAG: {{.*}}swift4_assocty -// STRIP_REFLECTION_NAMES-DAG: {{.*}}swift4_capture -// STRIP_REFLECTION_NAMES-DAG: {{.*}}swift4_typeref -// STRIP_REFLECTION_NAMES-NOT: {{.*}}swift4_reflstr -// STRIP_REFLECTION_NAMES-NOT: {{.*}}swift4_builtin - -// STRIP_REFLECTION_NAMES-DAG: @"$S19reflection_metadata10MyProtocol_pMF" = internal constant {{.*}}swift4_fieldmd - -// STRIP_REFLECTION_METADATA-NOT: {{.*}}swift4_reflect -// STRIP_REFLECTION_METADATA-NOT: {{.*}}swift4_fieldmd -// STRIP_REFLECTION_METADATA-NOT: {{.*}}swift4_assocty -// STRIP_REFLECTION_METADATA-NOT: {{.*}}swift4_capture -// STRIP_REFLECTION_METADATA-NOT: {{.*}}swift4_typeref -// STRIP_REFLECTION_METADATA-NOT: {{.*}}swift4_reflstr -// STRIP_REFLECTION_METADATA-NOT: {{.*}}swift4_builtin +// STRIP_REFLECTION_NAMES_DAG: {{.*}}swift5_reflect +// STRIP_REFLECTION_NAMES_DAG: {{.*}}swift5_fieldmd +// STRIP_REFLECTION_NAMES_DAG: {{.*}}swift5_assocty +// STRIP_REFLECTION_NAMES-DAG: {{.*}}swift5_capture +// STRIP_REFLECTION_NAMES-DAG: {{.*}}swift5_typeref +// STRIP_REFLECTION_NAMES-NOT: {{.*}}swift5_reflstr +// STRIP_REFLECTION_NAMES-NOT: {{.*}}swift5_builtin + +// STRIP_REFLECTION_NAMES-DAG: @"$S19reflection_metadata10MyProtocol_pMF" = internal constant {{.*}}swift5_fieldmd + +// STRIP_REFLECTION_METADATA-NOT: {{.*}}swift5_reflect +// STRIP_REFLECTION_METADATA-NOT: {{.*}}swift5_fieldmd +// STRIP_REFLECTION_METADATA-NOT: {{.*}}swift5_assocty +// STRIP_REFLECTION_METADATA-NOT: {{.*}}swift5_capture +// STRIP_REFLECTION_METADATA-NOT: {{.*}}swift5_typeref +// STRIP_REFLECTION_METADATA-NOT: {{.*}}swift5_reflstr +// STRIP_REFLECTION_METADATA-NOT: {{.*}}swift5_builtin // CHECK-DAG: @__swift_reflection_version = linkonce_odr hidden constant i16 {{[0-9]+}} -// CHECK-DAG: private constant [2 x i8] c"i\00", section "{{[^"]*}}swift4_reflstr{{[^"]*}}" -// CHECK-DAG: private constant [3 x i8] c"ms\00", section "{{[^"]*}}swift4_reflstr{{[^"]*}}" -// CHECK-DAG: private constant [3 x i8] c"me\00", section "{{[^"]*}}swift4_reflstr{{[^"]*}}" -// CHECK-DAG: private constant [3 x i8] c"mc\00", section "{{[^"]*}}swift4_reflstr{{[^"]*}}" -// CHECK-DAG: private constant [2 x i8] c"C\00", section "{{[^"]*}}swift4_reflstr{{[^"]*}}" -// CHECK-DAG: private constant [2 x i8] c"S\00", section "{{[^"]*}}swift4_reflstr{{[^"]*}}" -// CHECK-DAG: private constant [2 x i8] c"E\00", section "{{[^"]*}}swift4_reflstr{{[^"]*}}" -// CHECK-DAG: private constant [2 x i8] c"I\00", section "{{[^"]*}}swift4_reflstr{{[^"]*}}" -// CHECK-DAG: private constant [2 x i8] c"t\00", section "{{[^"]*}}swift4_reflstr{{[^"]*}}" -// CHECK-DAG: private constant [4 x i8] c"mgs\00", section "{{[^"]*}}swift4_reflstr{{[^"]*}}" -// CHECK-DAG: private constant [4 x i8] c"mge\00", section "{{[^"]*}}swift4_reflstr{{[^"]*}}" -// CHECK-DAG: private constant [4 x i8] c"mgc\00", section "{{[^"]*}}swift4_reflstr{{[^"]*}}" -// CHECK-DAG: private constant [3 x i8] c"GC\00", section "{{[^"]*}}swift4_reflstr{{[^"]*}}" -// CHECK-DAG: private constant [3 x i8] c"GS\00", section "{{[^"]*}}swift4_reflstr{{[^"]*}}" -// CHECK-DAG: private constant [3 x i8] c"GE\00", section "{{[^"]*}}swift4_reflstr{{[^"]*}}" - -// CHECK-DAG: @"\01l__swift4_reflection_descriptor" = private constant { {{.*}} } { i32 1, i32 1, i32 2, {{.*}} } - -// CHECK-DAG: @"$S19reflection_metadata10MyProtocol_pMF" = internal constant {{.*}}swift4_fieldmd -// CHECK-DAG: @"$S19reflection_metadata7MyClassCMF" = internal constant {{.*}}swift4_fieldmd -// CHECK-DAG: @"$S19reflection_metadata11ConformanceVAA10MyProtocolAAMA" = internal constant {{.*}}swift4_assocty -// CHECK-DAG: @"$S19reflection_metadata8MyStructVMF" = internal constant {{.*}}swift4_fieldmd -// CHECK-DAG: @"$S19reflection_metadata6MyEnumOMF" = internal constant {{.*}}swift4_fieldmd -// CHECK-DAG: @"$S19reflection_metadata14MyGenericClassCMF" = internal constant {{.*}}swift4_fieldmd -// CHECK-DAG: @"$S19reflection_metadata15MyGenericStructVMF" = internal constant {{.*}}swift4_fieldmd -// CHECK-DAG: @"$S19reflection_metadata13MyGenericEnumOMF" = internal constant {{.*}}swift4_fieldmd +// CHECK-DAG: private constant [2 x i8] c"i\00", section "{{[^"]*}}swift5_reflstr{{[^"]*}}" +// CHECK-DAG: private constant [3 x i8] c"ms\00", section "{{[^"]*}}swift5_reflstr{{[^"]*}}" +// CHECK-DAG: private constant [3 x i8] c"me\00", section "{{[^"]*}}swift5_reflstr{{[^"]*}}" +// CHECK-DAG: private constant [3 x i8] c"mc\00", section "{{[^"]*}}swift5_reflstr{{[^"]*}}" +// CHECK-DAG: private constant [2 x i8] c"C\00", section "{{[^"]*}}swift5_reflstr{{[^"]*}}" +// CHECK-DAG: private constant [2 x i8] c"S\00", section "{{[^"]*}}swift5_reflstr{{[^"]*}}" +// CHECK-DAG: private constant [2 x i8] c"E\00", section "{{[^"]*}}swift5_reflstr{{[^"]*}}" +// CHECK-DAG: private constant [2 x i8] c"I\00", section "{{[^"]*}}swift5_reflstr{{[^"]*}}" +// CHECK-DAG: private constant [2 x i8] c"t\00", section "{{[^"]*}}swift5_reflstr{{[^"]*}}" +// CHECK-DAG: private constant [4 x i8] c"mgs\00", section "{{[^"]*}}swift5_reflstr{{[^"]*}}" +// CHECK-DAG: private constant [4 x i8] c"mge\00", section "{{[^"]*}}swift5_reflstr{{[^"]*}}" +// CHECK-DAG: private constant [4 x i8] c"mgc\00", section "{{[^"]*}}swift5_reflstr{{[^"]*}}" +// CHECK-DAG: private constant [3 x i8] c"GC\00", section "{{[^"]*}}swift5_reflstr{{[^"]*}}" +// CHECK-DAG: private constant [3 x i8] c"GS\00", section "{{[^"]*}}swift5_reflstr{{[^"]*}}" +// CHECK-DAG: private constant [3 x i8] c"GE\00", section "{{[^"]*}}swift5_reflstr{{[^"]*}}" + +// CHECK-DAG: @"\01l__swift5_reflection_descriptor" = private constant { {{.*}} } { i32 1, i32 1, i32 2, {{.*}} } + +// CHECK-DAG: @"$S19reflection_metadata10MyProtocol_pMF" = internal constant {{.*}}swift5_fieldmd +// CHECK-DAG: @"$S19reflection_metadata7MyClassCMF" = internal constant {{.*}}swift5_fieldmd +// CHECK-DAG: @"$S19reflection_metadata11ConformanceVAA10MyProtocolAAMA" = internal constant {{.*}}swift5_assocty +// CHECK-DAG: @"$S19reflection_metadata8MyStructVMF" = internal constant {{.*}}swift5_fieldmd +// CHECK-DAG: @"$S19reflection_metadata6MyEnumOMF" = internal constant {{.*}}swift5_fieldmd +// CHECK-DAG: @"$S19reflection_metadata14MyGenericClassCMF" = internal constant {{.*}}swift5_fieldmd +// CHECK-DAG: @"$S19reflection_metadata15MyGenericStructVMF" = internal constant {{.*}}swift5_fieldmd +// CHECK-DAG: @"$S19reflection_metadata13MyGenericEnumOMF" = internal constant {{.*}}swift5_fieldmd public protocol MyProtocol { associatedtype Inner diff --git a/test/IRGen/reflection_metadata_imported.swift b/test/IRGen/reflection_metadata_imported.swift index 4af3fd7511f00..f8a61b140b61a 100644 --- a/test/IRGen/reflection_metadata_imported.swift +++ b/test/IRGen/reflection_metadata_imported.swift @@ -4,10 +4,10 @@ import c_layout // CHECK-DAG: @__swift_reflection_version = linkonce_odr hidden constant i16 {{[0-9]+}} -// CHECK-DAG: @"$S28reflection_metadata_imported15HasImportedTypeVMF" = internal constant {{.*}}swift4_fieldmd -// CHECK-DAG: @"$SSo1AVMB" = linkonce_odr hidden constant {{.*}}swift4_builtin -// CHECK-DAG: @"$SSo11CrappyColorVMB" = linkonce_odr hidden constant {{.*}}swift4_builtin -// CHECK-DAG: @"$SSo11CrappyColorVs16RawRepresentableSCMA" = linkonce_odr hidden constant {{.*}}swift4_assocty +// CHECK-DAG: @"$S28reflection_metadata_imported15HasImportedTypeVMF" = internal constant {{.*}}swift5_fieldmd +// CHECK-DAG: @"$SSo1AVMB" = linkonce_odr hidden constant {{.*}}swift5_builtin +// CHECK-DAG: @"$SSo11CrappyColorVMB" = linkonce_odr hidden constant {{.*}}swift5_builtin +// CHECK-DAG: @"$SSo11CrappyColorVs16RawRepresentableSCMA" = linkonce_odr hidden constant {{.*}}swift5_assocty struct HasImportedType { let a: A diff --git a/test/IRGen/resilience_bypass.swift b/test/IRGen/resilience_bypass.swift new file mode 100644 index 0000000000000..95b59e292e800 --- /dev/null +++ b/test/IRGen/resilience_bypass.swift @@ -0,0 +1,16 @@ +// RUN: %empty-directory(%t) + +// RUN: %target-swift-frontend -emit-module -enable-resilience -emit-module-path=%t/first.swiftmodule -module-name=first %S/Inputs/resilience_bypass/first.swift +// RUN: %target-swift-frontend -emit-module -emit-module-path=%t/second.swiftmodule -module-name=second %S/Inputs/resilience_bypass/second.swift -I %t +// RUN: %target-swift-frontend -emit-ir -enable-resilience-bypass %s -I %t | %FileCheck %s -DINT=i%target-ptrsize + +import second + +// CHECK: define{{( protected)?}} swiftcc [[INT]] @"$S17resilience_bypass7getSizeSiyF"() {{.*}} { +// CHECK-NEXT: entry: +// CHECK-NEXT: ret [[INT]] {{5|9}} +// CHECK-NEXT: } + +public func getSize() -> Int { + return MemoryLayout.size +} diff --git a/test/IRGen/runtime_calling_conventions.swift b/test/IRGen/runtime_calling_conventions.swift index 6b6e1e3fdc956..415e244e8b4b2 100644 --- a/test/IRGen/runtime_calling_conventions.swift +++ b/test/IRGen/runtime_calling_conventions.swift @@ -7,11 +7,11 @@ public class C { } -// CHECK-LABEL: define {{(protected )?}}swiftcc void @"$S27runtime_calling_conventions3fooyyAA1CCF"(%T27runtime_calling_conventions1CC*) +// CHECK-LABEL: define {{(dllexport )?}}{{(protected )?}}swiftcc void @"$S27runtime_calling_conventions3fooyyAA1CCF"(%T27runtime_calling_conventions1CC*) // Check that runtime functions use a proper calling convention. // CHECK-NOT: call void {{.*}} @swift_release -// OPT-CHECK-LABEL: define {{(protected )?}}swiftcc void @"$S27runtime_calling_conventions3fooyyAA1CCF"(%T27runtime_calling_conventions1CC* nocapture) +// OPT-CHECK-LABEL: define {{(dllexport )?}}{{(protected )?}}swiftcc void @"$S27runtime_calling_conventions3fooyyAA1CCF"(%T27runtime_calling_conventions1CC* nocapture) // Check that runtime functions use a proper calling convention. // OPT-CHECK-NOT: tail call void @swift_release diff --git a/test/IRGen/same_type_constraints.swift b/test/IRGen/same_type_constraints.swift index a8ca7fe861818..1db1f19b11900 100644 --- a/test/IRGen/same_type_constraints.swift +++ b/test/IRGen/same_type_constraints.swift @@ -1,6 +1,12 @@ // RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -emit-ir -primary-file %s -disable-objc-attr-requires-foundation-module | %FileCheck %s // RUN: %target-swift-frontend -Osize -assume-parsing-unqualified-ownership-sil -emit-ir -primary-file %s -disable-objc-attr-requires-foundation-module | %FileCheck %s --check-prefix=OSIZE +// Ensure that same-type constraints between generic arguments get reflected +// correctly in the type context descriptor. +// CHECK-LABEL: @"$S21same_type_constraints4SG11VA2A2P2Rzq_RszrlE13InnerTEqualsUVMn" = +// T U(==T) V padding +// CHECK-SAME: , i8 -128, i8 0, i8 -128, i8 0, + // IRGen crash with protocol extension involving same-type constraint to X public struct DefaultFoo { var t: T? @@ -16,13 +22,14 @@ public extension P where Foo == DefaultFoo { } } -// CHECK: define{{( protected)?}} swiftcc void @"$S21same_type_constraints1PPA2A10DefaultFooVyxG0E0RtzrlE3fooAFyF" + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$S21same_type_constraints1PPA2A10DefaultFooVyxG0E0RtzrlE3fooAFyF" // IRGen crash with derived class declaring same-type constraint on constrained associatedtype. public class C1 { } public class C2: C1 where T == U.Foo {} -// CHECK: define{{( protected)?}} swiftcc void @"$S21same_type_constraints2C1CfD" +// CHECK: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$S21same_type_constraints2C1CfD" public protocol MyHashable {} public protocol DataType : MyHashable {} @@ -62,3 +69,40 @@ where Self : CodingType, // OSIZE: define internal swiftcc i8** @"$S21same_type_constraints12GenericKlazzCyxq_GAA1EAA4Data_AA0F4TypePWT"(%swift.type* %"GenericKlazz.Data", %swift.type* nocapture readonly %"GenericKlazz", i8** nocapture readnone %"GenericKlazz.E") [[ATTRS:#[0-9]+]] { // OSIZE: [[ATTRS]] = {{{.*}}noinline + +// Check that same-typing two generic parameters together lowers correctly. + +protocol P1 {} +protocol P2 {} +protocol P3 {} +struct ConformsToP1: P1 {} +struct ConformsToP2: P2 {} +struct ConformsToP3: P3 {} + +struct SG11 {} + +struct ConformsToP1AndP2 : P1, P2 { } + +extension SG11 where U == T { + struct InnerTEqualsU { } +} + +extension SG11 where T == ConformsToP1 { + struct InnerTEqualsConformsToP1 { } +} + +extension SG11 where U == ConformsToP2 { + struct InnerUEqualsConformsToP2 { } +} + +func inner1() -> Any.Type { + return SG11.InnerTEqualsU.self +} + +func inner2() -> Any.Type { + return SG11.InnerTEqualsConformsToP1.self +} + +func inner3() -> Any.Type { + return SG11.InnerTEqualsConformsToP1.self +} diff --git a/test/IRGen/sanitize_coverage.swift b/test/IRGen/sanitize_coverage.swift index 3020afc17129b..930d438876698 100644 --- a/test/IRGen/sanitize_coverage.swift +++ b/test/IRGen/sanitize_coverage.swift @@ -1,4 +1,3 @@ -// XFAIL: linux // RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -emit-ir -sanitize=address -sanitize-coverage=func %s | %FileCheck %s -check-prefix=SANCOV // RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -emit-ir -sanitize=address -sanitize-coverage=bb %s | %FileCheck %s -check-prefix=SANCOV // RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -emit-ir -sanitize=address -sanitize-coverage=edge %s | %FileCheck %s -check-prefix=SANCOV @@ -9,14 +8,25 @@ // RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -emit-ir -sanitize=address -sanitize-coverage=edge,8bit-counters %s | %FileCheck %s -check-prefix=SANCOV -check-prefix=SANCOV_8BIT_COUNTERS // RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -emit-ir -sanitize=fuzzer %s | %FileCheck %s -check-prefix=SANCOV -check-prefix=SANCOV_TRACE_CMP +#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS) import Darwin +#elseif os(Android) || os(Cygwin) || os(FreeBSD) || os(Linux) +import Glibc +#elseif os(Windows) +import MSVCRT +#endif // FIXME: We should have a reliable way of triggering an indirect call in the // LLVM IR generated from this code. func test() { // Use random numbers so the compiler can't constant fold +#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS) let x = arc4random() let y = arc4random() +#else + let x = rand() + let y = rand() +#endif // Comparison is to trigger insertion of __sanitizer_cov_trace_cmp let z = x == y print("\(z)") diff --git a/test/IRGen/select_enum.sil b/test/IRGen/select_enum.sil index bddf9f72e43ba..a199647b5fbee 100644 --- a/test/IRGen/select_enum.sil +++ b/test/IRGen/select_enum.sil @@ -7,7 +7,7 @@ enum SinglePayloadSingleEmpty { case DataCase(Builtin.Word) } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @select_enum_SinglePayloadSingleEmpty(%T11select_enum013SinglePayloadC5EmptyO* noalias nocapture dereferenceable({{.*}})) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @select_enum_SinglePayloadSingleEmpty(%T11select_enum013SinglePayloadC5EmptyO* noalias nocapture dereferenceable({{.*}})) sil @select_enum_SinglePayloadSingleEmpty : $@convention(thin) (@in SinglePayloadSingleEmpty) -> () { bb0(%0 : $*SinglePayloadSingleEmpty): %1 = load %0 : $*SinglePayloadSingleEmpty @@ -79,7 +79,7 @@ enum NoPayloadSingleton { case One } -// CHECK-LABEL: define{{( protected)?}} swiftcc i1 @testOptionalOfNoPayloadSingleton(i8) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc i1 @testOptionalOfNoPayloadSingleton(i8) sil @testOptionalOfNoPayloadSingleton : $@convention(thin) (MyOptional) -> Builtin.Int1 { bb0(%0 : $MyOptional): // CHECK: [[NATIVECCTRUNC:%.*]] = trunc i8 %0 to i1 diff --git a/test/IRGen/sil_generic_witness_methods_objc.swift b/test/IRGen/sil_generic_witness_methods_objc.swift index 23d39e0c34839..5e1d835a425e9 100644 --- a/test/IRGen/sil_generic_witness_methods_objc.swift +++ b/test/IRGen/sil_generic_witness_methods_objc.swift @@ -1,7 +1,6 @@ -// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -primary-file %s -emit-ir -disable-objc-attr-requires-foundation-module | %FileCheck %s +// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -primary-file %s -emit-ir -enable-objc-interop -disable-objc-attr-requires-foundation-module | %FileCheck %s // REQUIRES: CPU=x86_64 -// REQUIRES: objc_interop // FIXME: These should be SIL tests, but we can't parse generic types in SIL // yet. diff --git a/test/IRGen/sil_linkage.sil b/test/IRGen/sil_linkage.sil index d5256e11594bf..e53594e62c1a7 100644 --- a/test/IRGen/sil_linkage.sil +++ b/test/IRGen/sil_linkage.sil @@ -2,25 +2,25 @@ sil_stage canonical -// CHECK: define{{( protected)?}} swiftcc void @public_fragile_function_test() {{.*}} { -// CHECK: define{{( protected)?}} swiftcc void @public_transparent_fragile_function_test() {{.*}} { -// CHECK: define{{( protected)?}} swiftcc void @public_transparent_function_test() {{.*}} { +// CHECK: define{{( dllexport)?}}{{( protected)?}} swiftcc void @public_fragile_function_test() {{.*}} { +// CHECK: define{{( dllexport)?}}{{( protected)?}} swiftcc void @public_transparent_fragile_function_test() {{.*}} { +// CHECK: define{{( dllexport)?}}{{( protected)?}} swiftcc void @public_transparent_function_test() {{.*}} { // CHECK: define hidden swiftcc void @public_non_abi_function_test() {{.*}} { // CHECK: define hidden swiftcc void @hidden_fragile_function_test() {{.*}} { // CHECK: define linkonce_odr hidden swiftcc void @shared_fragile_function_test() {{.*}} { // CHECK: define{{( internal)?}} swiftcc void @private_fragile_function_test() {{.*}} { // Public external functions are not emitted into clients. -// CHECK: declare swiftcc void @public_external_fragile_function_def_test() -// CHECK: define{{( protected)?}} available_externally{{ (hidden)?}} swiftcc void @hidden_external_fragile_function_def_test() {{.*}} { +// CHECK: declare{{( dllimport)?}} swiftcc void @public_external_fragile_function_def_test() +// CHECK: define{{( protected)?}} available_externally{{ (hidden)?}}{{( dllimport)?}} swiftcc void @hidden_external_fragile_function_def_test() {{.*}} { // CHECK: define linkonce_odr hidden swiftcc void @shared_external_fragile_function_def_test() {{.*}} { -// CHECK: define{{( protected)?}} available_externally{{ (hidden)?}} swiftcc void @private_external_fragile_function_def_test() {{.*}} { -// CHECK: define{{( protected)?}} swiftcc void @public_resilient_function_test() {{.*}} { +// CHECK: define{{( protected)?}} available_externally{{ (hidden)?}}{{( dllimport)?}} swiftcc void @private_external_fragile_function_def_test() {{.*}} { +// CHECK: define{{( protected)?}}{{( dllexport)?}} swiftcc void @public_resilient_function_test() {{.*}} { // CHECK: define hidden swiftcc void @hidden_resilient_function_test() {{.*}} { // CHECK: define linkonce_odr hidden swiftcc void @shared_resilient_function_test() {{.*}} { // CHECK: define internal swiftcc void @private_resilient_function_test() {{.*}}{ // Public external functions are not emitted into clients. -// CHECK: declare swiftcc void @public_external_resilient_function_def_test() -// CHECK: define{{( protected)?}} available_externally hidden swiftcc void @hidden_external_resilient_function_def_test() {{.*}} { +// CHECK: declare{{( dllimport)?}} swiftcc void @public_external_resilient_function_def_test() +// CHECK: define{{( protected)?}} available_externally hidden{{( dllimport)?}} swiftcc void @hidden_external_resilient_function_def_test() {{.*}} { // CHECK: define linkonce_odr hidden swiftcc void @shared_external_resilient_function_def_test() {{.*}} { sil public [serialized] @public_fragile_function_test : $@convention(thin) () -> () { diff --git a/test/IRGen/sil_witness_methods.sil b/test/IRGen/sil_witness_methods.sil index 0e540d2dc65ec..c76a510cb6117 100644 --- a/test/IRGen/sil_witness_methods.sil +++ b/test/IRGen/sil_witness_methods.sil @@ -27,14 +27,14 @@ struct X {} struct Y {} struct Z {} -// CHECK-LABEL: define{{( protected)?}} swiftcc %swift.type* @concrete_type_concrete_method_witness(%T19sil_witness_methods3FooV* noalias nocapture swiftself, %swift.type* %Self, i8** %SelfWitnessTable) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc %swift.type* @concrete_type_concrete_method_witness(%T19sil_witness_methods3FooV* noalias nocapture swiftself, %swift.type* %Self, i8** %SelfWitnessTable) sil @concrete_type_concrete_method_witness : $@convention(witness_method: P) (@in Foo) -> @thick Foo.Type { entry(%x : $*Foo): %m = metatype $@thick Foo.Type return %m : $@thick Foo.Type } -// CHECK-LABEL: define{{( protected)?}} swiftcc %swift.type* @generic_type_concrete_method_witness(%T19sil_witness_methods3BarC** noalias nocapture swiftself dereferenceable({{.*}}), %swift.type* %Self, i8** %SelfWitnessTable) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc %swift.type* @generic_type_concrete_method_witness(%T19sil_witness_methods3BarC** noalias nocapture swiftself dereferenceable({{.*}}), %swift.type* %Self, i8** %SelfWitnessTable) // CHECK: [[T0:%.*]] = bitcast %swift.type* %Self to %swift.type** // CHECK: [[T1:%.*]] = getelementptr inbounds %swift.type*, %swift.type** [[T0]], i64 10 // CHECK: %T = load %swift.type*, %swift.type** [[T1]], align 8 @@ -56,7 +56,7 @@ entry(%x : $*Bar): // TODO: %Self Type arg is redundant for static method witness -// CHECK-LABEL: define{{( protected)?}} swiftcc %swift.type* @concrete_type_concrete_static_method_witness(%swift.type* swiftself, %swift.type* %Self, i8** %SelfWitnessTable) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc %swift.type* @concrete_type_concrete_static_method_witness(%swift.type* swiftself, %swift.type* %Self, i8** %SelfWitnessTable) sil @concrete_type_concrete_static_method_witness : $@convention(witness_method: P) (@thick Foo.Type) -> @thick Foo.Type { entry(%x : $@thick Foo.Type): %m = metatype $@thick Foo.Type @@ -64,7 +64,7 @@ entry(%x : $@thick Foo.Type): } // The use of %0 or %Self here is irrelevant. -// CHECK-LABEL: define{{( protected)?}} swiftcc %swift.type* @generic_type_concrete_static_method_witness(%swift.type* swiftself, %swift.type* %Self, i8** %SelfWitnessTable) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc %swift.type* @generic_type_concrete_static_method_witness(%swift.type* swiftself, %swift.type* %Self, i8** %SelfWitnessTable) // CHECK: [[T0:%.*]] = bitcast %swift.type* %Self to %swift.type** // CHECK: [[T1:%.*]] = getelementptr inbounds %swift.type*, %swift.type** [[T0]], i64 10 // CHECK: %T = load %swift.type*, %swift.type** [[T1]], align 8 @@ -86,14 +86,14 @@ entry(%x : $@thick Bar.Type): // TODO: %Self Type arg is redundant for class method witness -// CHECK-LABEL: define{{( protected)?}} swiftcc %swift.type* @concrete_type_generic_method_witness(%swift.opaque* noalias nocapture, %swift.type* %Z, %T19sil_witness_methods3FooV* noalias nocapture swiftself, %swift.type* %Self, i8** %SelfWitnessTable) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc %swift.type* @concrete_type_generic_method_witness(%swift.opaque* noalias nocapture, %swift.type* %Z, %T19sil_witness_methods3FooV* noalias nocapture swiftself, %swift.type* %Self, i8** %SelfWitnessTable) sil @concrete_type_generic_method_witness : $@convention(witness_method: P) (@in Z, @in Foo) -> @thick Foo.Type { entry(%z : $*Z, %x : $*Foo): %m = metatype $@thick Foo.Type return %m : $@thick Foo.Type } -// CHECK-LABEL: define{{( protected)?}} swiftcc %swift.type* @generic_type_generic_method_witness(%swift.opaque* noalias nocapture, %swift.type* %Z, %T19sil_witness_methods3BarC{{.*}}** noalias nocapture swiftself dereferenceable(8), %swift.type* %Self, i8** %SelfWitnessTable) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc %swift.type* @generic_type_generic_method_witness(%swift.opaque* noalias nocapture, %swift.type* %Z, %T19sil_witness_methods3BarC{{.*}}** noalias nocapture swiftself dereferenceable(8), %swift.type* %Self, i8** %SelfWitnessTable) sil @generic_type_generic_method_witness : $@convention(witness_method: P) (@in Z, @in Bar) -> @thick Bar.Type { entry(%z : $*Z, %x : $*Bar): %t = metatype $@thick T.Type @@ -104,14 +104,14 @@ entry(%z : $*Z, %x : $*Bar): return %m : $@thick Bar.Type } -// CHECK-LABEL: define{{( protected)?}} swiftcc %swift.type* @concrete_type_generic_static_method_witness(%swift.opaque* noalias nocapture, %swift.type* %Z, %swift.type* swiftself, %swift.type* %Self, i8** %SelfWitnessTable) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc %swift.type* @concrete_type_generic_static_method_witness(%swift.opaque* noalias nocapture, %swift.type* %Z, %swift.type* swiftself, %swift.type* %Self, i8** %SelfWitnessTable) sil @concrete_type_generic_static_method_witness : $@convention(witness_method: P) (@in Z, @thick Foo.Type) -> @thick Foo.Type { entry(%z : $*Z, %x : $@thick Foo.Type): %m = metatype $@thick Foo.Type return %m : $@thick Foo.Type } -// CHECK-LABEL: define{{( protected)?}} swiftcc %swift.type* @generic_type_generic_static_method_witness(%swift.opaque* noalias nocapture, %swift.type* %Z, %swift.type* swiftself, %swift.type* %Self, i8** %SelfWitnessTable) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc %swift.type* @generic_type_generic_static_method_witness(%swift.opaque* noalias nocapture, %swift.type* %Z, %swift.type* swiftself, %swift.type* %Self, i8** %SelfWitnessTable) sil @generic_type_generic_static_method_witness : $@convention(witness_method: P) (@in Z, @thick Bar.Type) -> @thick Bar.Type { entry(%z : $*Z, %x : $@thick Bar.Type): %t = metatype $@thick T.Type @@ -122,7 +122,7 @@ entry(%z : $*Z, %x : $@thick Bar.Type): return %m : $@thick Bar.Type } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @call_concrete_witness() {{.*}} { +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @call_concrete_witness() {{.*}} { // CHECK: call swiftcc %swift.type* @concrete_type_concrete_method_witness(%T19sil_witness_methods3FooV* {{.*}}, %swift.type* {{.*}} @"$S19sil_witness_methods3FooVMf", {{.*}}) sil @call_concrete_witness : $(Foo) -> () { entry(%x : $Foo): @@ -134,7 +134,7 @@ entry(%x : $Foo): return undef : $() } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @call_concrete_static_witness() {{.*}} { +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @call_concrete_static_witness() {{.*}} { // CHECK: call swiftcc %swift.type* @concrete_type_concrete_static_method_witness(%swift.type* {{.*}} @"$S19sil_witness_methods3FooVMf", {{.*}} %swift.type* {{.*}} @"$S19sil_witness_methods3FooVMf", {{.*}}) sil @call_concrete_static_witness : $() -> () { entry: @@ -144,7 +144,7 @@ entry: return undef : $() } -// CHECK-LABEL: define{{( protected)?}} swiftcc { i8*, %swift.refcounted* } @partial_apply_concrete_witness() {{.*}} { +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc { i8*, %swift.refcounted* } @partial_apply_concrete_witness() {{.*}} { // CHECK: [[CONTEXT:%.*]] = call noalias %swift.refcounted* @swift_allocObject({{.*}}) // CHECK: [[LAYOUT:%.*]] = bitcast %swift.refcounted* [[CONTEXT]] to <{ %swift.refcounted, i8* }>* // CHECK: [[WTABLE:%.*]] = getelementptr inbounds <{ %swift.refcounted, i8* }>, <{ %swift.refcounted, i8* }>* [[LAYOUT]], i32 0, i32 1 diff --git a/test/IRGen/sil_witness_tables.swift b/test/IRGen/sil_witness_tables.swift index 066dbbadb9286..4433cebf61a9b 100644 --- a/test/IRGen/sil_witness_tables.swift +++ b/test/IRGen/sil_witness_tables.swift @@ -36,7 +36,7 @@ struct Conformer: Q, QQ { func qMethod() {} } -// CHECK: [[EXTERNAL_CONFORMER_EXTERNAL_P_WITNESS_TABLE:@"\$S39sil_witness_tables_external_conformance17ExternalConformerVAA0F1PAAWP"]] = external global i8*, align 8 +// CHECK: [[EXTERNAL_CONFORMER_EXTERNAL_P_WITNESS_TABLE:@"\$S39sil_witness_tables_external_conformance17ExternalConformerVAA0F1PAAWP"]] = external{{( dllimport)?}} global i8*, align 8 // CHECK: [[CONFORMER_Q_WITNESS_TABLE:@"\$S18sil_witness_tables9ConformerVAA1QAAWP"]] = hidden constant [3 x i8*] [ // CHECK: i8* bitcast ([5 x i8*]* [[CONFORMER_P_WITNESS_TABLE:@"\$S18sil_witness_tables9ConformerVAA1PAAWP"]] to i8*), // CHECK: i8* bitcast (void (%T18sil_witness_tables9ConformerV*, %swift.type*, i8**)* @"$S18sil_witness_tables9ConformerVAA1QA2aDP7qMethod{{[_0-9a-zA-Z]*}}FTW" to i8*) diff --git a/test/IRGen/sil_witness_tables_external_witnesstable.swift b/test/IRGen/sil_witness_tables_external_witnesstable.swift index db0df376ac58b..993667de89224 100644 --- a/test/IRGen/sil_witness_tables_external_witnesstable.swift +++ b/test/IRGen/sil_witness_tables_external_witnesstable.swift @@ -4,8 +4,8 @@ import Swift -// CHECK: @"$Ss1XVN" = external global %swift.type -// CHECK: @"$Ss1XVs1PsWP" = external global i8* +// CHECK: @"$Ss1XVN" = external {{(dllimport )?}}global %swift.type +// CHECK: @"$Ss1XVs1PsWP" = external {{(dllimport )?}}global i8* func doSomething(_ t : T) -> Y { return t.doSomething() diff --git a/test/IRGen/special_protocols.sil b/test/IRGen/special_protocols.sil index 0709db5c8a6da..4c21eac3415a7 100644 --- a/test/IRGen/special_protocols.sil +++ b/test/IRGen/special_protocols.sil @@ -1,14 +1,14 @@ // RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil %s -emit-ir -parse-stdlib -module-name Swift | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize public protocol Error {} -// CHECK-LABEL: @"$Ss5ErrorMp" = {{(protected )?}}constant %swift.protocol { +// CHECK-LABEL: @"$Ss5ErrorMp" = {{(dllexport )?}}{{(protected )?}}constant %swift.protocol { // -- 0x0000_0047: special protocol 01, non-class, witness, swift // CHECK: i32 71, // CHECK: i32 0, // CHECK: i32 0 } public protocol PlainOldProtocol {} -// CHECK-LABEL: @"$Ss16PlainOldProtocolMp" = {{(protected )?}}constant %swift.protocol { +// CHECK-LABEL: @"$Ss16PlainOldProtocolMp" = {{(dllexport )?}}{{(protected )?}}constant %swift.protocol { // -- 0x0000_0007: no special protocol, non-class, witness, swift // CHECK: i32 7, // CHECK: i32 0, diff --git a/test/IRGen/static_initializer.sil b/test/IRGen/static_initializer.sil index e6ec2222491d2..0189f2d87ec8b 100644 --- a/test/IRGen/static_initializer.sil +++ b/test/IRGen/static_initializer.sil @@ -32,7 +32,7 @@ sil_global @$S2ch1xSiv : $Int32 = { %1 = integer_literal $Builtin.Int32, 2 %initval = struct $Int32 (%1 : $Builtin.Int32) } -// CHECK: @"$S2ch1xSiv" = {{(protected )?}}global %Ts5Int32V <{ i32 2 }>, align 4 +// CHECK: @"$S2ch1xSiv" = {{(dllexport )?}}{{(protected )?}}global %Ts5Int32V <{ i32 2 }>, align 4 sil_global @$S6nested1xAA2S2Vv : $S2 = { %1 = integer_literal $Builtin.Int32, 2 @@ -66,7 +66,7 @@ sil_global @static_array : $TestArrayStorage = { %5 = struct $Int64 (%2 : $Builtin.Int64) %initval = object $TestArrayStorage (%3 : $Int32, [tail_elems] %4 : $Int64, %5 : $Int64) } -// CHECK: @static_array = {{(protected )?}}global %T18static_initializer16TestArrayStorageC_tailelems0c { [1 x i64] zeroinitializer, %T18static_initializer16TestArrayStorageC_tailelems0 <{ %swift.refcounted zeroinitializer, %Ts5Int32V <{ i32 2 }>, [4 x i8] undef, %Ts5Int64V <{ i64 10 }>, %Ts5Int64V <{ i64 20 }> }> }, align 8 +// CHECK: @static_array = {{(dllexport )?}}{{(protected )?}}global %T18static_initializer16TestArrayStorageC_tailelems0c { [1 x i64] zeroinitializer, %T18static_initializer16TestArrayStorageC_tailelems0 <{ %swift.refcounted zeroinitializer, %Ts5Int32V <{ i32 2 }>, [4 x i8] undef, %Ts5Int64V <{ i64 10 }>, %Ts5Int64V <{ i64 20 }> }> }, align 8 sil_global @static_aligned_array : $TestArrayStorage = { %0 = integer_literal $Builtin.Int32, 2 @@ -101,9 +101,9 @@ sil_global @static_array_with_empty_element : $TestArrayStorage = { %2 = struct $Int32 (%1 : $Builtin.Int32) %initval = object $TestArrayStorage (%2 : $Int32, [tail_elems] %0 : $Empty, %0 : $Empty) } -// CHECK: @static_array_with_empty_element = {{(protected )?}}global %T18static_initializer16TestArrayStorageC_tailelems3c { [1 x i64] zeroinitializer, %T18static_initializer16TestArrayStorageC_tailelems3 <{ %swift.refcounted zeroinitializer, %Ts5Int32V <{ i32 2 }>, [1 x i8] undef, [1 x i8] undef }> }, align 8 +// CHECK: @static_array_with_empty_element = {{( dllexport)?}}{{(protected )?}}global %T18static_initializer16TestArrayStorageC_tailelems3c { [1 x i64] zeroinitializer, %T18static_initializer16TestArrayStorageC_tailelems3 <{ %swift.refcounted zeroinitializer, %Ts5Int32V <{ i32 2 }>, [1 x i8] undef, [1 x i8] undef }> }, align 8 -// CHECK-LABEL: define{{( protected)?}} swiftcc i8* @_TF2cha1xSi() {{.*}} { +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc i8* @_TF2cha1xSi() {{.*}} { // CHECK-NEXT: entry: // CHECK-NEXT: ret i8* bitcast (%Ts5Int32V* @"$S2ch1xSiv" to i8*) sil [global_init] @_TF2cha1xSi : $@convention(thin) () -> Builtin.RawPointer { @@ -113,7 +113,7 @@ bb0: return %1 : $Builtin.RawPointer } -// CHECK-LABEL: define{{( protected)?}} swiftcc i32 @"$S2ch1fSiyF"() {{.*}} { +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc i32 @"$S2ch1fSiyF"() {{.*}} { // CHECK-NEXT: entry: // CHECK-NEXT: load i32, i32* getelementptr inbounds (%Ts5Int32V, %Ts5Int32V* @"$S2ch1xSiv", i32 0, i32 0) // CHECK-NEXT: ret @@ -124,7 +124,7 @@ bb0: return %1 : $Int32 } -// CHECK-LABEL: define{{( protected)?}} swiftcc i8* @_TF6nesteda1xVS_2S2() {{.*}} { +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc i8* @_TF6nesteda1xVS_2S2() {{.*}} { // CHECK-NEXT: entry: // CHECK-NEXT: ret i8* bitcast (%T18static_initializer2S2V* @"$S6nested1xAA2S2Vv" to i8*) sil [global_init] @_TF6nesteda1xVS_2S2 : $@convention(thin) () -> Builtin.RawPointer { @@ -134,7 +134,7 @@ bb0: return %1 : $Builtin.RawPointer } -// CHECK-LABEL: define{{( protected)?}} swiftcc { i64, i32 } @"$S6nested1fAA2S2VyF"() {{.*}} { +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc { i64, i32 } @"$S6nested1fAA2S2VyF"() {{.*}} { // CHECK-NEXT: entry: // CHECK: load i32, i32* getelementptr inbounds (%T18static_initializer2S2V, %T18static_initializer2S2V* @"$S6nested1xAA2S2Vv", i32 0, i32 0, i32 0) // CHECK-NEXT: load i32, i32* getelementptr inbounds (%T18static_initializer2S2V, %T18static_initializer2S2V* @"$S6nested1xAA2S2Vv", i32 0, i32 1, i32 0) @@ -146,7 +146,7 @@ bb0: return %1 : $S2 } -// CHECK-LABEL: define{{( protected)?}} swiftcc %T18static_initializer16TestArrayStorageC* @return_static_array() {{.*}} { +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc %T18static_initializer16TestArrayStorageC* @return_static_array() {{.*}} { sil @return_static_array : $@convention(thin) () -> TestArray { bb0: // CHECK: [[TMP:%.*]] = call swiftcc %swift.metadata_response @"$S18static_initializer16TestArrayStorageCMa"(i64 0) diff --git a/test/IRGen/struct_resilience.swift b/test/IRGen/struct_resilience.swift index 4de30b4b962b5..34c081dc87c1b 100644 --- a/test/IRGen/struct_resilience.swift +++ b/test/IRGen/struct_resilience.swift @@ -15,7 +15,7 @@ import resilient_enum // Resilient structs from outside our resilience domain are manipulated via // value witnesses -// CHECK-LABEL: define{{( protected)?}} swiftcc void @"$S17struct_resilience30functionWithResilientTypesSize_1f010resilient_A00G0VAFn_A2FnXEtF"(%swift.opaque* noalias nocapture sret, %swift.opaque* noalias nocapture, i8*, %swift.opaque*) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$S17struct_resilience30functionWithResilientTypesSize_1f010resilient_A00G0VAFn_A2FnXEtF"(%swift.opaque* noalias nocapture sret, %swift.opaque* noalias nocapture, i8*, %swift.opaque*) public func functionWithResilientTypesSize(_ s: __owned Size, f: (__owned Size) -> Size) -> Size { // CHECK: entry: @@ -25,7 +25,7 @@ public func functionWithResilientTypesSize(_ s: __owned Size, f: (__owned Size) // CHECK: [[VWT_ADDR:%.*]] = getelementptr inbounds i8**, i8*** [[METADATA_ADDR]], [[INT]] -1 // CHECK: [[VWT:%.*]] = load i8**, i8*** [[VWT_ADDR]] -// CHECK-NEXT: [[WITNESS_ADDR:%.*]] = getelementptr inbounds i8*, i8** [[VWT]], i32 9 +// CHECK-NEXT: [[WITNESS_ADDR:%.*]] = getelementptr inbounds i8*, i8** [[VWT]], i32 8 // CHECK: [[WITNESS:%.*]] = load i8*, i8** [[WITNESS_ADDR]] // CHECK: [[WITNESS_FOR_SIZE:%.*]] = ptrtoint i8* [[WITNESS]] // CHECK: [[ALLOCA:%.*]] = alloca i8, {{.*}} [[WITNESS_FOR_SIZE]], align 16 @@ -51,7 +51,7 @@ public func functionWithResilientTypesSize(_ s: __owned Size, f: (__owned Size) return f(s) } -// CHECK-LABEL: declare swiftcc %swift.metadata_response @"$S16resilient_struct4SizeVMa" +// CHECK-LABEL: declare{{( dllimport)?}} swiftcc %swift.metadata_response @"$S16resilient_struct4SizeVMa" // CHECK-SAME: ([[INT]]) // Rectangle has fixed layout inside its resilience domain, and dynamic @@ -60,17 +60,17 @@ public func functionWithResilientTypesSize(_ s: __owned Size, f: (__owned Size) // Make sure we use a type metadata accessor function, and load indirect // field offsets from it. -// CHECK-LABEL: define{{( protected)?}} swiftcc void @"$S17struct_resilience35functionWithResilientTypesRectangleyy010resilient_A00G0VF"(%T16resilient_struct9RectangleV* noalias nocapture) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$S17struct_resilience35functionWithResilientTypesRectangleyy010resilient_A00G0VF"(%T16resilient_struct9RectangleV* noalias nocapture) public func functionWithResilientTypesRectangle(_ r: Rectangle) { // CHECK: entry: // CHECK-NEXT: [[TMP:%.*]] = call swiftcc %swift.metadata_response @"$S16resilient_struct9RectangleVMa"([[INT]] 0) // CHECK-NEXT: [[METADATA:%.*]] = extractvalue %swift.metadata_response [[TMP]], 0 -// CHECK-NEXT: [[METADATA_ADDR:%.*]] = bitcast %swift.type* [[METADATA]] to [[INT]]* -// CHECK-NEXT: [[FIELD_OFFSET_VECTOR:%.*]] = getelementptr inbounds [[INT]], [[INT]]* [[METADATA_ADDR]], [[INT]] 2 -// CHECK-NEXT: [[FIELD_OFFSET_PTR:%.*]] = getelementptr inbounds [[INT]], [[INT]]* [[FIELD_OFFSET_VECTOR]], i32 2 -// CHECK-NEXT: [[FIELD_OFFSET:%.*]] = load [[INT]], [[INT]]* [[FIELD_OFFSET_PTR]] +// CHECK-NEXT: [[METADATA_ADDR:%.*]] = bitcast %swift.type* [[METADATA]] to i32* +// CHECK-NEXT: [[FIELD_OFFSET_VECTOR:%.*]] = getelementptr inbounds i32, i32* [[METADATA_ADDR]], [[INT]] [[IDX:2|4]] +// CHECK-NEXT: [[FIELD_OFFSET_PTR:%.*]] = getelementptr inbounds i32, i32* [[FIELD_OFFSET_VECTOR]], i32 2 +// CHECK-NEXT: [[FIELD_OFFSET:%.*]] = load i32, i32* [[FIELD_OFFSET_PTR]] // CHECK-NEXT: [[STRUCT_ADDR:%.*]] = bitcast %T16resilient_struct9RectangleV* %0 to i8* -// CHECK-NEXT: [[FIELD_ADDR:%.*]] = getelementptr inbounds i8, i8* [[STRUCT_ADDR]], [[INT]] [[FIELD_OFFSET]] +// CHECK-NEXT: [[FIELD_ADDR:%.*]] = getelementptr inbounds i8, i8* [[STRUCT_ADDR]], i32 [[FIELD_OFFSET]] // CHECK-NEXT: [[FIELD_PTR:%.*]] = bitcast i8* [[FIELD_ADDR]] to %TSi* // CHECK-NEXT: [[FIELD_PAYLOAD_PTR:%.*]] = getelementptr inbounds %TSi, %TSi* [[FIELD_PTR]], i32 0, i32 0 // CHECK-NEXT: [[FIELD_PAYLOAD:%.*]] = load [[INT]], [[INT]]* [[FIELD_PAYLOAD_PTR]] @@ -90,7 +90,7 @@ public struct MySize { public let h: Int } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @"$S17struct_resilience32functionWithMyResilientTypesSize_1fAA0eH0VAEn_A2EnXEtF"(%T17struct_resilience6MySizeV* noalias nocapture sret, %T17struct_resilience6MySizeV* noalias nocapture dereferenceable({{8|(16)}}), i8*, %swift.opaque*) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$S17struct_resilience32functionWithMyResilientTypesSize_1fAA0eH0VAEn_A2EnXEtF"(%T17struct_resilience6MySizeV* noalias nocapture sret, %T17struct_resilience6MySizeV* noalias nocapture dereferenceable({{8|(16)}}), i8*, %swift.opaque*) public func functionWithMyResilientTypesSize(_ s: __owned MySize, f: (__owned MySize) -> MySize) -> MySize { // CHECK: [[TEMP:%.*]] = alloca %T17struct_resilience6MySizeV @@ -120,15 +120,15 @@ public struct StructWithResilientStorage { // resilient layout, and go through the field offset vector in the // metadata when accessing stored properties. -// CHECK-LABEL: define{{( protected)?}} swiftcc {{i32|i64}} @"$S17struct_resilience26StructWithResilientStorageV1nSivg"(%T17struct_resilience26StructWithResilientStorageV* {{.*}}) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc {{i32|i64}} @"$S17struct_resilience26StructWithResilientStorageV1nSivg"(%T17struct_resilience26StructWithResilientStorageV* {{.*}}) // CHECK: [[TMP:%.*]] = call swiftcc %swift.metadata_response @"$S17struct_resilience26StructWithResilientStorageVMa"([[INT]] 0) // CHECK: [[METADATA:%.*]] = extractvalue %swift.metadata_response [[TMP]], 0 -// CHECK-NEXT: [[METADATA_ADDR:%.*]] = bitcast %swift.type* [[METADATA]] to [[INT]]* -// CHECK-NEXT: [[FIELD_OFFSET_VECTOR:%.*]] = getelementptr inbounds [[INT]], [[INT]]* [[METADATA_ADDR]], [[INT]] 2 -// CHECK-NEXT: [[FIELD_OFFSET_PTR:%.*]] = getelementptr inbounds [[INT]], [[INT]]* [[FIELD_OFFSET_VECTOR]], i32 2 -// CHECK-NEXT: [[FIELD_OFFSET:%.*]] = load [[INT]], [[INT]]* [[FIELD_OFFSET_PTR]] +// CHECK-NEXT: [[METADATA_ADDR:%.*]] = bitcast %swift.type* [[METADATA]] to i32* +// CHECK-NEXT: [[FIELD_OFFSET_VECTOR:%.*]] = getelementptr inbounds i32, i32* [[METADATA_ADDR]], [[INT]] [[IDX:2|4]] +// CHECK-NEXT: [[FIELD_OFFSET_PTR:%.*]] = getelementptr inbounds i32, i32* [[FIELD_OFFSET_VECTOR]], i32 2 +// CHECK-NEXT: [[FIELD_OFFSET:%.*]] = load i32, i32* [[FIELD_OFFSET_PTR]] // CHECK-NEXT: [[STRUCT_ADDR:%.*]] = bitcast %T17struct_resilience26StructWithResilientStorageV* %0 to i8* -// CHECK-NEXT: [[FIELD_ADDR:%.*]] = getelementptr inbounds i8, i8* [[STRUCT_ADDR]], [[INT]] [[FIELD_OFFSET]] +// CHECK-NEXT: [[FIELD_ADDR:%.*]] = getelementptr inbounds i8, i8* [[STRUCT_ADDR]], i32 [[FIELD_OFFSET]] // CHECK-NEXT: [[FIELD_PTR:%.*]] = bitcast i8* [[FIELD_ADDR]] to %TSi* // CHECK-NEXT: [[FIELD_PAYLOAD_PTR:%.*]] = getelementptr inbounds %TSi, %TSi* [[FIELD_PTR]], i32 0, i32 0 // CHECK-NEXT: [[FIELD_PAYLOAD:%.*]] = load [[INT]], [[INT]]* [[FIELD_PAYLOAD_PTR]] @@ -143,7 +143,7 @@ public struct StructWithIndirectResilientEnum { } -// CHECK-LABEL: define{{( protected)?}} swiftcc {{i32|i64}} @"$S17struct_resilience31StructWithIndirectResilientEnumV1nSivg"(%T17struct_resilience31StructWithIndirectResilientEnumV* {{.*}}) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc {{i32|i64}} @"$S17struct_resilience31StructWithIndirectResilientEnumV1nSivg"(%T17struct_resilience31StructWithIndirectResilientEnumV* {{.*}}) // CHECK: [[FIELD_PTR:%.*]] = getelementptr inbounds %T17struct_resilience31StructWithIndirectResilientEnumV, %T17struct_resilience31StructWithIndirectResilientEnumV* %0, i32 0, i32 1 // CHECK-NEXT: [[FIELD_PAYLOAD_PTR:%.*]] = getelementptr inbounds %TSi, %TSi* [[FIELD_PTR]], i32 0, i32 0 // CHECK-NEXT: [[FIELD_PAYLOAD:%.*]] = load [[INT]], [[INT]]* [[FIELD_PAYLOAD_PTR]] @@ -158,26 +158,44 @@ public struct ResilientStructWithMethod { // Corner case -- type is address-only in SIL, but empty in IRGen -// CHECK-LABEL: define{{( protected)?}} swiftcc void @"$S17struct_resilience29partialApplyOfResilientMethod1ryAA0f10StructWithG0V_tF"(%T17struct_resilience25ResilientStructWithMethodV* noalias nocapture) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$S17struct_resilience29partialApplyOfResilientMethod1ryAA0f10StructWithG0V_tF"(%T17struct_resilience25ResilientStructWithMethodV* noalias nocapture) public func partialApplyOfResilientMethod(r: ResilientStructWithMethod) { _ = r.method } // Type is address-only in SIL, and resilient in IRGen -// CHECK-LABEL: define{{( protected)?}} swiftcc void @"$S17struct_resilience29partialApplyOfResilientMethod1sy010resilient_A04SizeV_tF"(%swift.opaque* noalias nocapture) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$S17struct_resilience29partialApplyOfResilientMethod1sy010resilient_A04SizeV_tF"(%swift.opaque* noalias nocapture) public func partialApplyOfResilientMethod(s: Size) { _ = s.method } +public func wantsAny(_ any: Any) {} + +public func resilientAny(s : ResilientWeakRef) { + wantsAny(s) +} + +// CHECK-LABEL: define{{.*}} swiftcc void @"$S17struct_resilience12resilientAny1sy0c1_A016ResilientWeakRefV_tF"(%swift.opaque* noalias nocapture) +// CHECK: entry: +// CHECK: [[ANY:%.*]] = alloca %Any +// CHECK: [[META:%.*]] = call swiftcc %swift.metadata_response @"$S16resilient_struct16ResilientWeakRefVMa"([[INT]] 0) +// CHECK: [[META2:%.*]] = extractvalue %swift.metadata_response %3, 0 +// CHECK: [[TYADDR:%.*]] = getelementptr inbounds %Any, %Any* %1, i32 0, i32 1 +// CHECK: store %swift.type* [[META2]], %swift.type** [[TYADDR]] +// CHECK: call %swift.opaque* @__swift_allocate_boxed_opaque_existential_0(%Any* [[ANY]]) +// CHECK: call swiftcc void @"$S17struct_resilience8wantsAnyyyypF"(%Any* noalias nocapture dereferenceable({{(32|16)}}) [[ANY]]) +// CHECK: call void @__swift_destroy_boxed_opaque_existential_0(%Any* [[ANY]]) +// CHECK: ret void + // Public metadata accessor for our resilient struct -// CHECK-LABEL: define{{( protected)?}} swiftcc %swift.metadata_response @"$S17struct_resilience6MySizeVMa" +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc %swift.metadata_response @"$S17struct_resilience6MySizeVMa" // CHECK-SAME: ([[INT]]) // CHECK: ret %swift.metadata_response { %swift.type* bitcast ([[INT]]* getelementptr inbounds {{.*}} @"$S17struct_resilience6MySizeVMf", i32 0, i32 1) to %swift.type*), [[INT]] 0 } -// CHECK-LABEL: define{{( protected)?}} private void @initialize_metadata_StructWithResilientStorage(i8*) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} private void @initialize_metadata_StructWithResilientStorage(i8*) // CHECK: [[FIELDS:%.*]] = alloca [4 x i8**] // CHECK: [[FIELDS_ADDR:%.*]] = getelementptr inbounds [4 x i8**], [4 x i8**]* [[FIELDS]], i32 0, i32 0 @@ -203,6 +221,6 @@ public func partialApplyOfResilientMethod(s: Size) { // CHECK: [[FIELD_4:%.*]] = getelementptr inbounds i8**, i8*** [[FIELDS_ADDR]], i32 3 // CHECK: store i8** [[SIZE_AND_ALIGNMENT:%.*]], i8*** [[FIELD_4]] -// CHECK: call void @swift_initStructMetadata(%swift.type* {{.*}}, [[INT]] 256, [[INT]] 4, i8*** [[FIELDS_ADDR]], [[INT]]* {{.*}}) +// CHECK: call void @swift_initStructMetadata(%swift.type* {{.*}}, [[INT]] 256, [[INT]] 4, i8*** [[FIELDS_ADDR]], i32* {{.*}}) // CHECK: store atomic %swift.type* {{.*}} @"$S17struct_resilience26StructWithResilientStorageVMf{{.*}}, %swift.type** @"$S17struct_resilience26StructWithResilientStorageVML" release, // CHECK: ret void diff --git a/test/IRGen/struct_with_resilient_type.swift b/test/IRGen/struct_with_resilient_type.swift new file mode 100644 index 0000000000000..e426537dceb83 --- /dev/null +++ b/test/IRGen/struct_with_resilient_type.swift @@ -0,0 +1,46 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-module -enable-resilience -emit-module-path=%t/resilient_struct.swiftmodule -module-name=resilient_struct %S/../Inputs/resilient_struct.swift +// RUN: %target-swift-frontend -I %t -emit-ir %s | %FileCheck %s + +// REQUIRES: CPU=x86_64 + +import resilient_struct + +struct StructWithFunc { + func foo(ptr: @escaping () -> Void) { + } +} + +struct ProtAndResilStruct { + let foundationType: ResilientBool + + let fooImp: StructWithFunc + + init(fType: ResilientBool, fooImp: StructWithFunc) { + self.foundationType = fType + self.fooImp = fooImp + } + + func bar() { + } + + func crash() { + fooImp.foo(ptr: bar) + } +// CHECK-LABEL: define{{.*}} @"$S26struct_with_resilient_type18ProtAndResilStructV3baryyFTc"(%T26struct_with_resilient_type18ProtAndResilStructV* noalias nocapture) +// CHECK: %flags.alignmentMask = and i64 %flags, 65535 +// CHECK: [[XOR_ALIGN:%.*]] = xor i64 %flags.alignmentMask, -1 +// CHECK: [[INIT_OFFSET:%.*]] = add i64 16, %flags.alignmentMask +// CHECK: [[T0:%.*]] = and i64 [[INIT_OFFSET]], [[XOR_ALIGN]] +// CHECK: [[T1:%.*]] = add i64 [[T0]], %size +// CHECK: [[ALIGN:%.*]] = or i64 7, %flags.alignmentMask +} + +func crashCaller() { + let fType = ResilientBool(b: false) + let fooImp = StructWithFunc() + let badStruct = ProtAndResilStruct(fType: fType, fooImp: fooImp) + badStruct.crash() +} + +crashCaller() diff --git a/test/IRGen/subclass.swift b/test/IRGen/subclass.swift index 9a16d90422173..a6eed346a908f 100644 --- a/test/IRGen/subclass.swift +++ b/test/IRGen/subclass.swift @@ -1,7 +1,6 @@ -// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -primary-file %s -emit-ir | %FileCheck %s +// RUN: %target-swift-frontend -enable-objc-interop -assume-parsing-unqualified-ownership-sil -primary-file %s -emit-ir | %FileCheck %s // REQUIRES: CPU=x86_64 -// REQUIRES: objc_interop // CHECK-DAG: %swift.refcounted = type { // CHECK-DAG: [[TYPE:%swift.type]] = type @@ -35,7 +34,7 @@ // CHECK: i64 ([[B]]*)* @"$S8subclass1BC1fSiyF", // CHECK: [[A]]* ([[TYPE]]*)* @"$S8subclass1AC1gACyFZ" // CHECK: }> -// CHECK: @objc_classes = internal global [2 x i8*] [i8* {{.*}} @"$S8subclass1ACN" {{.*}}, i8* {{.*}} @"$S8subclass1BCN" {{.*}}], section "__DATA,__objc_classlist,regular,no_dead_strip", align 8 +// CHECK: @objc_classes = internal global [2 x i8*] [i8* {{.*}} @"$S8subclass1ACN" {{.*}}, i8* {{.*}} @"$S8subclass1BCN" {{.*}}] class A { var x = 0 diff --git a/test/IRGen/subclass_existentials.sil b/test/IRGen/subclass_existentials.sil index 82db8e642b4fd..03f346d082af8 100644 --- a/test/IRGen/subclass_existentials.sil +++ b/test/IRGen/subclass_existentials.sil @@ -18,8 +18,8 @@ protocol R {} // Make sure we use native reference counting when the existential has a Swift // class bound. -// CHECK-objc-LABEL: define{{( protected)?}} swiftcc void @checkRefcounting(%T21subclass_existentials1CC*, i8**, %objc_object*, i8**) -// CHECK-native-LABEL: define{{( protected)?}} swiftcc void @checkRefcounting(%T21subclass_existentials1CC*, i8**, %swift.refcounted*, i8**) +// CHECK-objc-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @checkRefcounting(%T21subclass_existentials1CC*, i8**, %objc_object*, i8**) +// CHECK-native-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @checkRefcounting(%T21subclass_existentials1CC*, i8**, %swift.refcounted*, i8**) // CHECK-NEXT: entry: // CHECK-objc-NEXT: call void @swift_unknownRelease(%objc_object* %2) // CHECK-native-NEXT: call void @swift_release(%swift.refcounted* %2) @@ -39,7 +39,7 @@ bb0(%0 : $C & P, %1 : $AnyObject & P): sil @takesMetadata : $@convention(thin) (@thick T.Type) -> () -// CHECK-LABEL: define{{( protected)?}} swiftcc void @checkMetadata() +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @checkMetadata() // CHECK-NEXT: entry: // CHECK-NEXT: [[TMP:%.*]] = call swiftcc %swift.metadata_response @"$S21subclass_existentials1P_AA1CCXcMa"([[INT]] 0) // CHECK-NEXT: [[TYPE:%.*]] = extractvalue %swift.metadata_response [[TMP]], 0 @@ -70,7 +70,7 @@ bb0: return %4 : $() } -// CHECK-LABEL: define{{( protected)?}} swiftcc { %T21subclass_existentials1CC*, i8** } @eraseToExistential(%T21subclass_existentials1DC*) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc { %T21subclass_existentials1CC*, i8** } @eraseToExistential(%T21subclass_existentials1DC*) // CHECK: [[INSTANCE:%.*]] = bitcast %T21subclass_existentials1DC* %0 to %T21subclass_existentials1CC* // CHECK-NEXT: [[RESULT1:%.*]] = insertvalue { %T21subclass_existentials1CC*, i8** } undef, %T21subclass_existentials1CC* [[INSTANCE]], 0 // CHECK-NEXT: [[RESULT2:%.*]] = insertvalue { %T21subclass_existentials1CC*, i8** } [[RESULT1]], i8** @"$S21subclass_existentials1DCAA1PAAWP", 1 @@ -81,7 +81,7 @@ bb0(%0 : $D): return %2 : $C & P } -// CHECK-LABEL: define{{( protected)?}} swiftcc { %T21subclass_existentials1GC*, i8** } @eraseToExistentialWithGeneric(%T21subclass_existentials1EC*) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc { %T21subclass_existentials1GC*, i8** } @eraseToExistentialWithGeneric(%T21subclass_existentials1EC*) // CHECK: [[INSTANCE:%.*]] = bitcast %T21subclass_existentials1EC* %0 to %T21subclass_existentials1GC* // CHECK-NEXT: [[RESULT1:%.*]] = insertvalue { %T21subclass_existentials1GC*, i8** } undef, %T21subclass_existentials1GC* [[INSTANCE]], 0 // CHECK-NEXT: [[RESULT2:%.*]] = insertvalue { %T21subclass_existentials1GC*, i8** } [[RESULT1]], i8** @"$S21subclass_existentials1ECyxGAA1PAAWP", 1 @@ -92,7 +92,7 @@ bb0(%0 : $E<τ_0_0>): return %2 : $G<τ_0_0> & P } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @checkExistentialDowncast(%T21subclass_existentials1CC*, %T21subclass_existentials1CC*, i8**) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @checkExistentialDowncast(%T21subclass_existentials1CC*, %T21subclass_existentials1CC*, i8**) sil @checkExistentialDowncast : $@convention(thin) (@owned C, @owned C & P) -> () { bb0(%0 : $C, %1 : $C & P): @@ -144,7 +144,7 @@ bb0(%0 : $C, %1 : $C & P): // CHECK-NEXT: call void @llvm.trap() // CHECK-NEXT: unreachable -// CHECK-LABEL: define{{( protected)?}} swiftcc void @checkExistentialSameClassDowncast(%T21subclass_existentials1CC*) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @checkExistentialSameClassDowncast(%T21subclass_existentials1CC*) sil @checkExistentialSameClassDowncast : $@convention(thin) (@owned C) -> () { bb0(%0 : $C): @@ -180,7 +180,7 @@ bb0(%0 : $C): // CHECK-NEXT: call void @llvm.trap() // CHECK-NEXT: unreachable -// CHECK-LABEL: define{{( protected)?}} swiftcc void @checkExistentialMetatypeDowncast(%swift.type*, %swift.type*, i8**) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @checkExistentialMetatypeDowncast(%swift.type*, %swift.type*, i8**) sil @checkExistentialMetatypeDowncast : $@convention(thin) (@owned @thick C.Type, @owned @thick (C & P).Type) -> () { bb0(%0 : $@thick C.Type, %1 : $@thick (C & P).Type): @@ -201,7 +201,7 @@ bb0(%0 : $@thick C.Type, %1 : $@thick (C & P).Type): return %result : $() } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @checkExistentialSameClassMetatypeDowncast(%swift.type*) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @checkExistentialSameClassMetatypeDowncast(%swift.type*) sil @checkExistentialSameClassMetatypeDowncast : $@convention(thin) (@owned @thick C.Type) -> () { bb0(%0 : $@thick C.Type): @@ -220,4 +220,4 @@ bb0(%0 : $@thick C.Type): sil_vtable C {} sil_vtable D {} sil_vtable G {} -sil_vtable E {} \ No newline at end of file +sil_vtable E {} diff --git a/test/IRGen/super.sil b/test/IRGen/super.sil index 005b700e1dd8c..d0978774b848e 100644 --- a/test/IRGen/super.sil +++ b/test/IRGen/super.sil @@ -52,7 +52,7 @@ bb0(%0 : $ChildToResilientParent): } // ChildToResilientParent is in our resilience domain - can load the superclass's metadata directly. -// CHECK-LABEL: define{{( protected)?}} swiftcc void @"$S5super22ChildToResilientParentC6methodyyF"(%T5super22ChildToResilientParentC* swiftself) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$S5super22ChildToResilientParentC6methodyyF"(%T5super22ChildToResilientParentC* swiftself) // CHECK: [[TMP:%.*]] = call swiftcc %swift.metadata_response @"$S15resilient_class22ResilientOutsideParentCMa"([[INT]] 0) // CHECK: [[SUPER_METADATA:%.*]] = extractvalue %swift.metadata_response [[TMP]], 0 // CHECK: [[BASE:%.*]] = load [[INT]], [[INT]]* getelementptr inbounds ([[BOUNDS:{.*}]], {{.*}}* @"$S15resilient_class22ResilientOutsideParentCMo", i32 0, i32 0) @@ -75,7 +75,7 @@ bb0(%0 : $@thick ChildToResilientParent.Type): } // ChildToResilientParent is in our resilience domain - can load the superclass's metadata directly. -// CHECK-LABEL: define{{( protected)?}} swiftcc void @"$S5super22ChildToResilientParentC11classMethodyyFZ"(%swift.type* swiftself) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$S5super22ChildToResilientParentC11classMethodyyFZ"(%swift.type* swiftself) // CHECK: [[TMP:%.*]] = call swiftcc %swift.metadata_response @"$S15resilient_class22ResilientOutsideParentCMa"([[INT]] 0) // CHECK: [[SUPER_METADATA:%.*]] = extractvalue %swift.metadata_response [[TMP]], 0 // CHECK: [[BASE:%.*]] = load [[INT]], [[INT]]* getelementptr inbounds ([[BOUNDS]], [[BOUNDS]]* @"$S15resilient_class22ResilientOutsideParentCMo", i32 0, i32 0) @@ -106,7 +106,7 @@ bb0(%0 : $ChildToFixedParent): return %7 : $() } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @"$S5super18ChildToFixedParentC6methodyyF"(%T5super18ChildToFixedParentC* swiftself) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$S5super18ChildToFixedParentC6methodyyF"(%T5super18ChildToFixedParentC* swiftself) // CHECK: [[TMP:%.*]] = call swiftcc %swift.metadata_response @"$S15resilient_class13OutsideParentCMa"([[INT]] 0) // CHECK: [[SUPER_METADATA:%.*]] = extractvalue %swift.metadata_response [[TMP]], 0 // CHECK: [[OPAQUE_SUPER_METADATA:%.*]] = bitcast %swift.type* [[SUPER_METADATA]] to void (%T15resilient_class13OutsideParentC*)** @@ -127,7 +127,7 @@ bb0(%0 : $@thick ChildToFixedParent.Type): } // ChildToFixedParent is in our resilience domain - load super metadata directly. -// CHECK-LABEL: define{{( protected)?}} swiftcc void @"$S5super18ChildToFixedParentC11classMethodyyFZ"(%swift.type* swiftself) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$S5super18ChildToFixedParentC11classMethodyyFZ"(%swift.type* swiftself) // CHECK: [[TMP:%.*]] = call swiftcc %swift.metadata_response @"$S15resilient_class13OutsideParentCMa"([[INT]] 0) // CHECK: [[SUPER_METADATA:%.*]] = extractvalue %swift.metadata_response [[TMP]], 0 // CHECK: [[OPAQUE_SUPER_METADATA:%.*]] = bitcast %swift.type* [[SUPER_METADATA]] to void (%swift.type*)** @@ -150,7 +150,7 @@ bb0(%0 : $ResilientOutsideChild): } // Extending a resilient class - load super metadata indirectly. -// CHECK-LABEL: define{{( protected)?}} swiftcc void @"$S15resilient_class21ResilientOutsideChildC5superE15callSuperMethodyyF"(%T15resilient_class21ResilientOutsideChildC* swiftself) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$S15resilient_class21ResilientOutsideChildC5superE15callSuperMethodyyF"(%T15resilient_class21ResilientOutsideChildC* swiftself) // CHECK: [[TMP:%.*]] = call swiftcc %swift.metadata_response @"$S15resilient_class21ResilientOutsideChildCMa"([[INT]] 0) // CHECK: [[METADATA:%.*]] = extractvalue %swift.metadata_response [[TMP]], 0 // CHECK: [[OPAQUE_METADATA:%.*]] = bitcast %swift.type* [[METADATA]] to %swift.type** @@ -177,7 +177,7 @@ bb0(%0 : $@thick ResilientOutsideChild.Type): } // Extending a resilient class - load super metadata indirectly. -// CHECK-LABEL: define{{( protected)?}} swiftcc void @"$S15resilient_class21ResilientOutsideChildC5superE20callSuperClassMethodyyFZ"(%swift.type* swiftself) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$S15resilient_class21ResilientOutsideChildC5superE20callSuperClassMethodyyFZ"(%swift.type* swiftself) // CHECK: [[TMP:%.*]] = call swiftcc %swift.metadata_response @"$S15resilient_class21ResilientOutsideChildCMa"([[INT]] 0) // CHECK: [[METADATA:%.*]] = extractvalue %swift.metadata_response [[TMP]], 0 // CHECK: [[OPAQUE_METADATA:%.*]] = bitcast %swift.type* [[METADATA]] to %swift.type** diff --git a/test/IRGen/swift3-metadata-coff.swift b/test/IRGen/swift3-metadata-coff.swift index 38d0887c58e72..15441b762ec25 100644 --- a/test/IRGen/swift3-metadata-coff.swift +++ b/test/IRGen/swift3-metadata-coff.swift @@ -25,7 +25,7 @@ public func f(s : S) -> (() -> ()) { return { gg = s } } -// CHECK-DAG: @"\01l__swift4_reflection_descriptor" = private constant {{.*}}, section ".sw5cptr$B" +// CHECK-DAG: @"\01l__swift5_reflection_descriptor" = private constant {{.*}}, section ".sw5cptr$B" // CHECK-DAG: @"{{.*}}" = {{.*}} c"Sq", {{.*}} section ".sw5tyrf$B" // CHECK-DAG: @{{[0-9]+}} = {{.*}} c"none\00", section ".sw5rfst$B" // CHECK-DAG: @{{[0-9]+}} = {{.*}} c"some\00", section ".sw5rfst$B" diff --git a/test/IRGen/swift_native_objc_runtime_base.sil b/test/IRGen/swift_native_objc_runtime_base.sil index d4d5e837abc7d..40303044fcbcf 100644 --- a/test/IRGen/swift_native_objc_runtime_base.sil +++ b/test/IRGen/swift_native_objc_runtime_base.sil @@ -1,5 +1,4 @@ -// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -emit-ir %s | %FileCheck %s -// REQUIRES: objc_interop +// RUN: %target-swift-frontend -enable-objc-interop -assume-parsing-unqualified-ownership-sil -emit-ir %s | %FileCheck %s // CHECK-LABEL: @"$S30swift_native_objc_runtime_base1CCMm" = hidden global %objc_class { // -- metaclass "isa" is root metaclass diff --git a/test/IRGen/synthesized_conformance.swift b/test/IRGen/synthesized_conformance.swift new file mode 100644 index 0000000000000..9e5db2230e6cd --- /dev/null +++ b/test/IRGen/synthesized_conformance.swift @@ -0,0 +1,55 @@ +// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -emit-ir %s -swift-version 4 | %FileCheck %s + +struct Struct { + var x: T +} + +extension Struct: Equatable where T: Equatable {} +extension Struct: Hashable where T: Hashable {} +extension Struct: Codable where T: Codable {} + +enum Enum { + case a(T), b(T) +} + +extension Enum: Equatable where T: Equatable {} +extension Enum: Hashable where T: Hashable {} + +final class Final { + var x: T + init(x: T) { self.x = x } +} + +extension Final: Encodable where T: Encodable {} +extension Final: Decodable where T: Decodable {} + +class Nonfinal { + var x: T + init(x: T) { self.x = x } +} +extension Nonfinal: Encodable where T: Encodable {} + +func doEquality(_: T) {} +// CHECK-LABEL: define{{( protected)?}} swiftcc void @"$S23synthesized_conformance8equalityyyF"() +public func equality() { + // CHECK: [[Struct_Equatable:%.*]] = call i8** @"$S23synthesized_conformance6StructVySiGACyxGs9EquatableAAsAFRzlWl"() + // CHECK-NEXT: call swiftcc void @"$S23synthesized_conformance10doEqualityyyxs9EquatableRzlF"(%swift.opaque* noalias nocapture {{%.*}}, %swift.type* {{%.*}}, i8** [[Struct_Equatable]]) + doEquality(Struct(x: 1)) + // CHECK: [[Enum_Equatable:%.*]] = call i8** @"$S23synthesized_conformance4EnumOySiGACyxGs9EquatableAAsAFRzlWl"() + // CHECK-NEXT: call swiftcc void @"$S23synthesized_conformance10doEqualityyyxs9EquatableRzlF"(%swift.opaque* noalias nocapture {{%.*}}, %swift.type* {{%.*}}, i8** [[Enum_Equatable]]) + doEquality(Enum.a(1)) +} + +func doEncodable(_: T) {} +// CHECK-LABEL: define{{( protected)?}} swiftcc void @"$S23synthesized_conformance9encodableyyF"() +public func encodable() { + // CHECK: [[Struct_Encodable:%.*]] = call i8** @"$S23synthesized_conformance6StructVySiGACyxGs9EncodableAAs9DecodableRzsAFRzlWl"() + // CHECK-NEXT: call swiftcc void @"$S23synthesized_conformance11doEncodableyyxs0D0RzlF"(%swift.opaque* noalias nocapture {{%.*}}, %swift.type* {{%.*}}, i8** [[Struct_Encodable]]) + doEncodable(Struct(x: 1)) + // CHECK: [[Final_Encodable:%.*]] = call i8** @"$S23synthesized_conformance5FinalCySiGACyxGs9EncodableAAsAFRzlWl"() + // CHECK-NEXT: call swiftcc void @"$S23synthesized_conformance11doEncodableyyxs0D0RzlF"(%swift.opaque* noalias nocapture {{%.*}}, %swift.type* {{%.*}}, i8** [[Final_Encodable]]) + doEncodable(Final(x: 1)) + // CHECK: [[Nonfinal_Encodable:%.*]] = call i8** @"$S23synthesized_conformance8NonfinalCySiGACyxGs9EncodableAAsAFRzlWl"() + // CHECK-NEXT: call swiftcc void @"$S23synthesized_conformance11doEncodableyyxs0D0RzlF"(%swift.opaque* noalias nocapture {{%.*}}, %swift.type* {{%.*}}, i8** [[Nonfinal_Encodable]]) + doEncodable(Nonfinal(x: 1)) +} diff --git a/test/IRGen/tail_alloc.sil b/test/IRGen/tail_alloc.sil index f33fc770975f9..09dfc3f5aab43 100644 --- a/test/IRGen/tail_alloc.sil +++ b/test/IRGen/tail_alloc.sil @@ -15,7 +15,7 @@ class TestClass { sil_vtable TestClass {} -// CHECK-LABEL: define{{( protected)?}} swiftcc void @alloc_on_stack +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @alloc_on_stack // CHECK: %reference.raw = alloca i8, i32 28, align 8 // CHECK-NEXT: [[TMP:%.*]] = call swiftcc %swift.metadata_response @"$S{{[a-zA-Z0-9_]+}}Ma"([[INT]] 0) // CHECK-NEXT: [[M:%[0-9]+]] = extractvalue %swift.metadata_response [[TMP]], 0 @@ -34,7 +34,7 @@ bb0: return %r : $() } -// CHECK-LABEL: define{{( protected)?}} {{.*}}* @alloc_on_heap +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} {{.*}}* @alloc_on_heap // CHECK: [[TMP:%.*]] = call swiftcc %swift.metadata_response @"$S{{[a-zA-Z0-9_]+}}Ma"([[INT]] 0) // CHECK: [[M:%[0-9]+]] = extractvalue %swift.metadata_response [[TMP]], 0 // CHECK-NEXT: [[O:%[0-9]+]] = call noalias %swift.refcounted* @swift_allocObject(%swift.type* [[M]], i64 28, i64 7) @@ -47,7 +47,7 @@ bb0: return %o1 : $TestClass } -// CHECK-LABEL: define{{( protected)?}} {{.*}}* @alloc_3_on_heap +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} {{.*}}* @alloc_3_on_heap // CHECK: [[TMP:%.*]] = call swiftcc %swift.metadata_response @"$S{{[a-zA-Z0-9_]+}}CMa"([[INT]] 0) // CHECK: [[M:%[0-9]+]] = extractvalue %swift.metadata_response [[TMP]], 0 // CHECK-NEXT: [[O:%[0-9]+]] = call noalias %swift.refcounted* @swift_allocObject(%swift.type* [[M]], i64 40, i64 7) @@ -62,7 +62,7 @@ bb0: return %o1 : $TestClass } -// CHECK-LABEL: define{{( protected)?}} {{.*}}* @alloc_non_const_count +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} {{.*}}* @alloc_non_const_count // CHECK: [[TMP:%.*]] = call swiftcc %swift.metadata_response @"$S{{[a-zA-Z0-9_]+}}Ma"([[INT]] 0) // CHECK: [[M:%[0-9]+]] = extractvalue %swift.metadata_response [[TMP]], 0 // CHECK-NEXT: [[S:%[0-9]+]] = mul i64 4, %0 @@ -76,7 +76,7 @@ bb0(%c : $Builtin.Word): return %o1 : $TestClass } -// CHECK-LABEL: define{{( protected)?}} {{.*}}* @alloc_2_non_const_count +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} {{.*}}* @alloc_2_non_const_count // CHECK: [[TMP:%.*]] = call swiftcc %swift.metadata_response @"$S{{[a-zA-Z0-9_]+}}Ma"([[INT]] 0) // CHECK: [[M:%[0-9]+]] = extractvalue %swift.metadata_response [[TMP]], 0 // CHECK-NEXT: [[S1:%[0-9]+]] = mul i64 1, %0 @@ -94,7 +94,7 @@ bb0(%c1 : $Builtin.Word, %c2 : $Builtin.Word): return %o1 : $TestClass } -// CHECK-LABEL: define{{( protected)?}} {{.*}}* @alloc_generic +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} {{.*}}* @alloc_generic // CHECK: [[S1:%[0-9]+]] = add i64 17, %flags.alignmentMask // CHECK-NEXT: [[S2:%[0-9]+]] = xor i64 %flags.alignmentMask, -1 // CHECK-NEXT: [[S3:%[0-9]+]] = and i64 [[S1]], [[S2]] @@ -110,7 +110,7 @@ bb0(%0 : $Builtin.Word, %1 : $@thick T.Type): return %5 : $TestClass } -// CHECK-LABEL: define{{( protected)?}} {{.*}}* @alloc_dynamic +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} {{.*}}* @alloc_dynamic // CHECK: [[BYTE_PTR:%[0-9]+]] = bitcast %swift.type* %0 to i8* // CHECK-NEXT: [[SIZE_ADDR:%[0-9]+]] = getelementptr inbounds i8, i8* [[BYTE_PTR]], i32 48 // CHECK-NEXT: [[INT_PTR:%[0-9]+]] = bitcast i8* [[SIZE_ADDR]] to i32* @@ -134,7 +134,7 @@ bb0(%0 : $@thick TestClass.Type): return %o : $TestClass } -// CHECK-LABEL: define{{( protected)?}} swiftcc i8* @project_tail_byte +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc i8* @project_tail_byte // CHECK: getelementptr inbounds i8, i8* %{{[0-9]+}}, i64 17 // CHECK: ret sil @project_tail_byte : $@convention(thin) (TestClass) -> Builtin.RawPointer { @@ -144,7 +144,7 @@ bb0(%0 : $TestClass): return %p : $Builtin.RawPointer } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @project_tail_int +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @project_tail_int // CHECK: [[S:%[0-9]+]] = getelementptr inbounds i8, i8* %{{[0-9]+}}, i64 20 // CHECK: bitcast i8* [[S]] to %Ts5Int32V* // CHECK: ret @@ -156,7 +156,7 @@ bb0(%0 : $TestClass, %1 : $Int32): return %r : $() } -// CHECK-LABEL: define{{( protected)?}} swiftcc i8* @project_tail_generic +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc i8* @project_tail_generic // CHECK: [[S1:%[0-9]+]] = add i64 17, %flags.alignmentMask // CHECK-NEXT: [[S2:%[0-9]+]] = xor i64 %flags.alignmentMask, -1 // CHECK-NEXT: [[S3:%[0-9]+]] = and i64 [[S1]], [[S2]] @@ -175,7 +175,7 @@ struct Str { var t: T } -// CHECK-LABEL: define{{( protected)?}} swiftcc i8* @project_tail_generic_struct +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc i8* @project_tail_generic_struct // CHECK: [[S1:%[0-9]+]] = add i64 17, %flags.alignmentMask // CHECK-NEXT: [[S2:%[0-9]+]] = xor i64 %flags.alignmentMask, -1 // CHECK-NEXT: [[S3:%[0-9]+]] = and i64 [[S1]], [[S2]] @@ -190,7 +190,7 @@ bb0(%0 : $TestClass, %1 : $@thick T.Type): return %p : $Builtin.RawPointer } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @project_tail_index_byte_to_int +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @project_tail_index_byte_to_int // CHECK: [[S1:%[0-9]+]] = bitcast i8* %0 to %Ts4Int8V* // CHECK-NEXT: [[S2:%[0-9]+]] = getelementptr inbounds %Ts4Int8V, %Ts4Int8V* [[S1]], i64 2 // CHECK-NEXT: [[S3:%[0-9]+]] = ptrtoint %Ts4Int8V* [[S2]] to i64 @@ -209,7 +209,7 @@ bb0(%0 : $Builtin.RawPointer, %1 : $Int32): return %r : $() } -// CHECK-LABEL: define{{( protected)?}} swiftcc i8* @project_tail_index_generic_struct +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc i8* @project_tail_index_generic_struct // CHECK: call swiftcc %swift.metadata_response @{{.*Str.*}}([[INT]] 0, %swift.type* %T) // CHECK: load // CHECK: and @@ -231,7 +231,7 @@ class EmptyClass { sil_vtable EmptyClass {} -// CHECK-LABEL: define{{( protected)?}} swiftcc i8 @test_align_1_int8 +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc i8 @test_align_1_int8 // CHECK: load i8, i8* %{{.*}}, align 1 // CHECK: ret sil @test_align_1_int8 : $@convention(thin) (EmptyClass) -> Int8 { @@ -243,7 +243,7 @@ bb0(%0 : $EmptyClass): return %l : $Int8 } -// CHECK-LABEL: define{{( protected)?}} swiftcc i8 @test_align_2_int8 +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc i8 @test_align_2_int8 // CHECK: load i8, i8* %{{.*}}, align 2 // CHECK: ret sil @test_align_2_int8 : $@convention(thin) (EmptyClass) -> Int8 { @@ -255,7 +255,7 @@ bb0(%0 : $EmptyClass): return %l : $Int8 } -// CHECK-LABEL: define{{( protected)?}} swiftcc i32 @test_align_int32 +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc i32 @test_align_int32 // CHECK: load i32, i32* %{{.*}}, align 4 // CHECK: ret sil @test_align_int32 : $@convention(thin) (EmptyClass, Builtin.Word) -> Int32 { diff --git a/test/IRGen/tsan_instrumentation.sil b/test/IRGen/tsan_instrumentation.sil index 2e6bc28582d56..41ad4204d98d6 100644 --- a/test/IRGen/tsan_instrumentation.sil +++ b/test/IRGen/tsan_instrumentation.sil @@ -17,7 +17,7 @@ sil_global hidden @$S20tsan_instrumentation1gSiv : $Int // compiler-rt instrumentation. Eventually we will add a specific // instrumentation callback for tsan inout accesses to compiler-rt. -// CHECK-LABEL: define{{( protected)?}} swiftcc void @"$S20tsan_instrumentation11inoutAccessyyF"() +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$S20tsan_instrumentation11inoutAccessyyF"() // foo() -> () sil @$S20tsan_instrumentation11inoutAccessyyF : $@convention(thin) () -> () { bb0: diff --git a/test/IRGen/type_layout.swift b/test/IRGen/type_layout.swift index a97a86c457a67..b9fd828c5cbee 100644 --- a/test/IRGen/type_layout.swift +++ b/test/IRGen/type_layout.swift @@ -34,14 +34,14 @@ struct TypeLayoutTest { // CHECK: [[T0:%.*]] = bitcast %swift.type* [[T_CHECKED]] to i8*** // CHECK: [[T1:%.*]] = getelementptr inbounds i8**, i8*** [[T0]], {{i32|i64}} -1 // CHECK: [[T_VALUE_WITNESSES:%.*]] = load i8**, i8*** [[T1]] - // CHECK: [[T_LAYOUT:%.*]] = getelementptr inbounds i8*, i8** [[T_VALUE_WITNESSES]], i32 9 + // CHECK: [[T_LAYOUT:%.*]] = getelementptr inbounds i8*, i8** [[T_VALUE_WITNESSES]], i32 8 // CHECK: store i8** [[T_LAYOUT]] var z: T // -- native class, use standard NativeObject value witness - // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBoWV", i32 9) + // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBoWV", i32 8) var a: C // -- Single-element struct, shares layout of its field (Builtin.Int64) - // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBi64_WV", i32 9) + // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBi64_WV", i32 8) var c: SSing // -- Multi-element structs use open-coded layouts // CHECK: store i8** getelementptr inbounds ([3 x i8*], [3 x i8*]* @type_layout_16_8_0_pod, i32 0, i32 0) @@ -53,13 +53,13 @@ struct TypeLayoutTest { // CHECK-32: store i8** getelementptr inbounds ([4 x i8*], [4 x i8*]* @type_layout_8_4_[[REF_XI]]_bt, i32 0, i32 0) var f: SMult3 // -- Single-case enum, shares layout of its field (Builtin.Int64) - // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBi64_WV", i32 9) + // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBi64_WV", i32 8) var g: ESing // -- Multi-case enum, open-coded layout // CHECK: store i8** getelementptr inbounds ([3 x i8*], [3 x i8*]* @type_layout_9_8_0_pod, i32 0, i32 0) var h: EMult // -- Single-element generic struct, shares layout of its field (T) - // CHECK: [[T_LAYOUT:%.*]] = getelementptr inbounds i8*, i8** [[T_VALUE_WITNESSES]], i32 9 + // CHECK: [[T_LAYOUT:%.*]] = getelementptr inbounds i8*, i8** [[T_VALUE_WITNESSES]], i32 8 // CHECK: store i8** [[T_LAYOUT]] var i: GSing // -- Multi-element generic struct, need to derive from metadata @@ -71,13 +71,13 @@ struct TypeLayoutTest { // CHECK: [[T0:%.*]] = bitcast %swift.type* [[METADATA]] to i8*** // CHECK: [[T1:%.*]] = getelementptr inbounds i8**, i8*** [[T0]], {{i32|i64}} -1 // CHECK: [[VALUE_WITNESSES:%.*]] = load i8**, i8*** [[T1]] - // CHECK: [[LAYOUT:%.*]] = getelementptr inbounds i8*, i8** [[VALUE_WITNESSES]], i32 9 + // CHECK: [[LAYOUT:%.*]] = getelementptr inbounds i8*, i8** [[VALUE_WITNESSES]], i32 8 // CHECK: store i8** [[LAYOUT]] var j: GMult // -- Common layout, reuse common value witness table layout - // CHECK: store i8** getelementptr (i8*, i8** @"$SBi32_WV", i32 9) + // CHECK: store i8** getelementptr (i8*, i8** @"$SBi32_WV", i32 8) var k: CommonLayout // -- Single-field aggregate with alignment - // CHECK: store i8** getelementptr (i8*, i8** @"$SBi128_WV", i32 9) + // CHECK: store i8** getelementptr (i8*, i8** @"$SBi128_WV", i32 8) var l: AlignedFourInts } diff --git a/test/IRGen/type_layout_objc.swift b/test/IRGen/type_layout_objc.swift index 4e42668f522bb..976eb197eba09 100644 --- a/test/IRGen/type_layout_objc.swift +++ b/test/IRGen/type_layout_objc.swift @@ -33,17 +33,17 @@ struct TypeLayoutTest { // CHECK: [[T0:%.*]] = bitcast %swift.type* [[T_CHECKED]] to i8*** // CHECK: [[T1:%.*]] = getelementptr inbounds i8**, i8*** [[T0]], {{i32|i64}} -1 // CHECK: [[T_VALUE_WITNESSES:%.*]] = load i8**, i8*** [[T1]] - // CHECK: [[T_LAYOUT:%.*]] = getelementptr inbounds i8*, i8** [[T_VALUE_WITNESSES]], i32 9 + // CHECK: [[T_LAYOUT:%.*]] = getelementptr inbounds i8*, i8** [[T_VALUE_WITNESSES]], i32 8 // CHECK: store i8** [[T_LAYOUT]] var z: T // -- native class, use standard NativeObject value witness - // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBoWV", i32 9) + // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBoWV", i32 8) var a: C // -- ObjC class, use standard UnknownObject value witness - // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBOWV", i32 9) + // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBOWV", i32 8) var b: O // -- Single-element struct, shares layout of its field (Builtin.Int64) - // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBi64_WV", i32 9) + // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBi64_WV", i32 8) var c: SSing // -- Multi-element structs use open-coded layouts // CHECK: store i8** getelementptr inbounds ([3 x i8*], [3 x i8*]* @type_layout_16_8_0_pod, i32 0, i32 0) @@ -55,13 +55,13 @@ struct TypeLayoutTest { // CHECK-32: store i8** getelementptr inbounds ([4 x i8*], [4 x i8*]* @type_layout_8_4_1000_bt, i32 0, i32 0) var f: SMult3 // -- Single-case enum, shares layout of its field (Builtin.Int64) - // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBi64_WV", i32 9) + // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBi64_WV", i32 8) var g: ESing // -- Multi-case enum, open-coded layout // CHECK: store i8** getelementptr inbounds ([3 x i8*], [3 x i8*]* @type_layout_9_8_0_pod, i32 0, i32 0) var h: EMult // -- Single-element generic struct, shares layout of its field (T) - // CHECK: [[T_LAYOUT:%.*]] = getelementptr inbounds i8*, i8** [[T_VALUE_WITNESSES]], i32 9 + // CHECK: [[T_LAYOUT:%.*]] = getelementptr inbounds i8*, i8** [[T_VALUE_WITNESSES]], i32 8 // CHECK: store i8** [[T_LAYOUT]] var i: GSing // -- Multi-element generic struct, need to derive from metadata @@ -73,10 +73,10 @@ struct TypeLayoutTest { // CHECK: [[T0:%.*]] = bitcast %swift.type* [[METADATA]] to i8*** // CHECK: [[T1:%.*]] = getelementptr inbounds i8**, i8*** [[T0]], {{i32|i64}} -1 // CHECK: [[VALUE_WITNESSES:%.*]] = load i8**, i8*** [[T1]] - // CHECK: [[LAYOUT:%.*]] = getelementptr inbounds i8*, i8** [[VALUE_WITNESSES]], i32 9 + // CHECK: [[LAYOUT:%.*]] = getelementptr inbounds i8*, i8** [[VALUE_WITNESSES]], i32 8 // CHECK: store i8** [[LAYOUT]] var j: GMult // -- Common layout, reuse common value witness table layout - // CHECK: store i8** getelementptr (i8*, i8** @"$SBi32_WV", i32 9) + // CHECK: store i8** getelementptr (i8*, i8** @"$SBi32_WV", i32 8) var k: CommonLayout } diff --git a/test/IRGen/type_layout_reference_storage.swift b/test/IRGen/type_layout_reference_storage.swift index 4128fae73ce4a..9b545e54327bc 100644 --- a/test/IRGen/type_layout_reference_storage.swift +++ b/test/IRGen/type_layout_reference_storage.swift @@ -10,23 +10,23 @@ struct ReferenceStorageTypeLayout { var z: T // -- Known-Swift-refcounted type - // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBoXoWV", i32 9) + // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBoXoWV", i32 8) unowned(safe) var cs: C - // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBomWV", i32 9) + // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBomWV", i32 8) unowned(unsafe) var cu: C - // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBoSgXwWV", i32 9) + // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBoSgXwWV", i32 8) weak var cwo: C? - // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBoSgXwWV", i32 9) + // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBoSgXwWV", i32 8) weak var cwi: C! // -- Known-Swift-refcounted archetype - // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBoXoWV", i32 9) + // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBoXoWV", i32 8) unowned(safe) var nc: Native - // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBomWV", i32 9) + // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBomWV", i32 8) unowned(unsafe) var nu: Native - // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBoSgXwWV", i32 9) + // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBoSgXwWV", i32 8) weak var nwo: Native? - // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBoSgXwWV", i32 9) + // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBoSgXwWV", i32 8) weak var nwi: Native! // -- Open-code layout for protocol types with witness tables. @@ -72,23 +72,23 @@ struct ReferenceStorageTypeLayout { weak var pqcwi: (P & Q & C)! // -- Unknown-refcounted existential without witness tables. - // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$S[[UNKNOWN:B[Oo]]]XoWV", i32 9) + // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$S[[UNKNOWN:B[Oo]]]XoWV", i32 8) unowned(safe) var aos: AnyObject - // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBomWV", i32 9) + // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBomWV", i32 8) unowned(unsafe) var aou: AnyObject - // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$S[[UNKNOWN]]SgXwWV", i32 9) + // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$S[[UNKNOWN]]SgXwWV", i32 8) weak var aowo: AnyObject? - // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$S[[UNKNOWN]]SgXwWV", i32 9) + // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$S[[UNKNOWN]]SgXwWV", i32 8) weak var aowi: AnyObject! // -- Unknown-refcounted archetype - // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$S[[UNKNOWN:B[Oo]]]XoWV", i32 9) + // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$S[[UNKNOWN:B[Oo]]]XoWV", i32 8) unowned(safe) var us: Unknown - // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBomWV", i32 9) + // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBomWV", i32 8) unowned(unsafe) var uu: Unknown - // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$S[[UNKNOWN]]SgXwWV", i32 9) + // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$S[[UNKNOWN]]SgXwWV", i32 8) weak var uwo: Unknown? - // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$S[[UNKNOWN]]SgXwWV", i32 9) + // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$S[[UNKNOWN]]SgXwWV", i32 8) weak var uwi: Unknown! } diff --git a/test/IRGen/type_layout_reference_storage_objc.swift b/test/IRGen/type_layout_reference_storage_objc.swift index bdc4a55679d74..f92974cff30e0 100644 --- a/test/IRGen/type_layout_reference_storage_objc.swift +++ b/test/IRGen/type_layout_reference_storage_objc.swift @@ -15,52 +15,52 @@ struct ReferenceStorageTypeLayout { var z: T // -- ObjC-refcounted class - // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBOXoWV", i32 9) + // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBOXoWV", i32 8) unowned(safe) var cs: C - // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBomWV", i32 9) + // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBomWV", i32 8) unowned(unsafe) var cu: C - // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBOSgXwWV", i32 9) + // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBOSgXwWV", i32 8) weak var cwo: C? - // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBOSgXwWV", i32 9) + // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBOSgXwWV", i32 8) weak var cwi: C! // -- ObjC-refcounted archetype - // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBOXoWV", i32 9) + // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBOXoWV", i32 8) unowned(safe) var os: ObjC - // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBomWV", i32 9) + // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBomWV", i32 8) unowned(unsafe) var ou: ObjC - // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBOSgXwWV", i32 9) + // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBOSgXwWV", i32 8) weak var owo: ObjC? - // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBOSgXwWV", i32 9) + // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBOSgXwWV", i32 8) weak var owi: ObjC! // -- Pure ObjC protocols are unknown-refcounted - // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBOXoWV", i32 9) + // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBOXoWV", i32 8) unowned(safe) var ps: P - // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBomWV", i32 9) + // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBomWV", i32 8) unowned(unsafe) var pu: P - // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBOSgXwWV", i32 9) + // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBOSgXwWV", i32 8) weak var pwo: P? - // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBOSgXwWV", i32 9) + // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBOSgXwWV", i32 8) weak var pwi: P! - // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBOXoWV", i32 9) + // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBOXoWV", i32 8) unowned(safe) var pqs: P & Q - // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBomWV", i32 9) + // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBomWV", i32 8) unowned(unsafe) var pqu: P & Q - // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBOSgXwWV", i32 9) + // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBOSgXwWV", i32 8) weak var pqwo: (P & Q)? - // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBOSgXwWV", i32 9) + // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBOSgXwWV", i32 8) weak var pqwi: (P & Q)! // -- Composition with ObjC protocol and native class is native-refcounted - // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBoXoWV", i32 9) + // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBoXoWV", i32 8) unowned(safe) var pncs: (P & NativeClass) - // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBomWV", i32 9) + // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBomWV", i32 8) unowned(unsafe) var pncu: (P & NativeClass) - // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBoSgXwWV", i32 9) + // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBoSgXwWV", i32 8) weak var pncwo: (P & NativeClass)? - // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBoSgXwWV", i32 9) + // CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBoSgXwWV", i32 8) weak var pncwi: (P & NativeClass)! // -- Open-code layouts when there are witness tables. diff --git a/test/IRGen/typed_boxes.sil b/test/IRGen/typed_boxes.sil index b4c246c29f1bb..ed1f717c20915 100644 --- a/test/IRGen/typed_boxes.sil +++ b/test/IRGen/typed_boxes.sil @@ -4,7 +4,7 @@ sil_stage canonical import Builtin -// CHECK-LABEL: define{{( protected)?}} swiftcc void @pod_box_8_8_a +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @pod_box_8_8_a sil @pod_box_8_8_a : $@convention(thin) () -> () { entry: // CHECK: [[BOX:%.*]] = call noalias %swift.refcounted* @swift_allocObject(%swift.type* {{.*}} [[POD_8_8_METADATA:@metadata[0-9.]*]], i32 0, i32 2), [[WORD:i[0-9]+]] [[POD_8_8_SIZE:[0-9]+]], [[WORD]] 7) @@ -18,7 +18,7 @@ entry: return undef : $() } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @pod_box_8_8_b +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @pod_box_8_8_b sil @pod_box_8_8_b : $@convention(thin) () -> () { entry: // CHECK: [[BOX:%.*]] = call noalias %swift.refcounted* @swift_allocObject(%swift.type* {{.*}} [[POD_8_8_METADATA]], i32 0, i32 2), [[WORD]] [[POD_8_8_SIZE]], [[WORD]] 7) @@ -37,7 +37,7 @@ struct OverAligned { var x, y, z, w: Builtin.Int64 } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @pod_box_32_32 +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @pod_box_32_32 sil @pod_box_32_32 : $@convention(thin) () -> () { entry: // CHECK: [[BOX:%.*]] = call noalias %swift.refcounted* @swift_allocObject(%swift.type* {{.*}} [[POD_32_32_METADATA:@metadata[0-9.]*]], {{.*}} [[WORD]] 64, [[WORD]] 31) @@ -58,7 +58,7 @@ sil_vtable C {} class D {} sil_vtable D {} -// CHECK-LABEL: define{{( protected)?}} swiftcc void @rc_box_a +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @rc_box_a sil @rc_box_a : $@convention(thin) () -> () { entry: // CHECK-32: [[BOX:%.*]] = call noalias %swift.refcounted* @swift_allocObject(%swift.type* {{.*}} [[NATIVE_RC_METADATA:@metadata[0-9.]*]], {{.*}} [[WORD]] 12, [[WORD]] 3) @@ -70,7 +70,7 @@ entry: return undef : $() } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @rc_box_b +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @rc_box_b sil @rc_box_b : $@convention(thin) () -> () { entry: // TODO: Should reuse metadata @@ -83,7 +83,7 @@ entry: return undef : $() } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @unknown_rc_box +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @unknown_rc_box sil @unknown_rc_box : $@convention(thin) () -> () { entry: // CHECK-32: [[BOX:%.*]] = call noalias %swift.refcounted* @swift_allocObject(%swift.type* {{.*}} [[UNKNOWN_RC_METADATA:@metadata[0-9.]*]], {{.*}} [[WORD]] 12, [[WORD]] 3) @@ -117,7 +117,7 @@ struct Dyn { sil @take_dyn : $@convention(thin) (@in Dyn) -> () sil @take_t : $@convention(thin) (@in T) -> () -// CHECK-LABEL: define{{( protected)?}} swiftcc void @dyn_box_a +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @dyn_box_a sil @dyn_box_a : $@convention(thin) () -> () { entry: // CHECK: [[TMP:%.*]] = call swiftcc %swift.metadata_response @"$S11typed_boxes3DynVMa"([[INT]] 0, %swift.type* %T) @@ -136,7 +136,7 @@ entry: return undef : $() } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @dyn_box_b +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @dyn_box_b sil @dyn_box_b : $@convention(thin) () -> () { entry: // CHECK: [[ALLOC:%.*]] = call swiftcc { %swift.refcounted*, %swift.opaque* } @swift_allocBox(%swift.type* %T) @@ -152,7 +152,7 @@ entry: return undef : $() } -// CHECK-LABEL: define{{( protected)?}} swiftcc i64 @proj_box +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc i64 @proj_box sil @proj_box : $@convention(thin) (<τ_0_0> { var τ_0_0 } ) -> Builtin.Int64 { entry(%0 : $<τ_0_0> { var τ_0_0 } ): // CHECK: [[BOX_RAW:%.*]] = bitcast %swift.refcounted* %0 to [[POD_8_8_LAYOUT:<\{ %swift.refcounted, \[8 x i8\] \}>]]* @@ -165,7 +165,7 @@ entry(%0 : $<τ_0_0> { var τ_0_0 } ): return %l : $Builtin.Int64 } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @dyn_proj_box_a +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @dyn_proj_box_a sil @dyn_proj_box_a : $@convention(thin) (<τ_0_0> { var τ_0_0 } >) -> () { entry(%0 : $<τ_0_0> { var τ_0_0 } >): // CHECK: [[PTR:%.*]] = call %swift.opaque* @swift_projectBox(%swift.refcounted* %0) @@ -178,7 +178,7 @@ entry(%0 : $<τ_0_0> { var τ_0_0 } >): } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @dyn_proj_box_b +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @dyn_proj_box_b sil @dyn_proj_box_b : $@convention(thin) (<τ_0_0> { var τ_0_0 } ) -> () { entry(%0 : $<τ_0_0> { var τ_0_0 } ): // CHECK: [[PTR:%.*]] = call %swift.opaque* @swift_projectBox(%swift.refcounted* %0) diff --git a/test/IRGen/typemetadata.sil b/test/IRGen/typemetadata.sil index 6edb82fab833b..9c1c263a925cd 100644 --- a/test/IRGen/typemetadata.sil +++ b/test/IRGen/typemetadata.sil @@ -1,8 +1,7 @@ -// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -// RUN: %target-swift-frontend -Osize -assume-parsing-unqualified-ownership-sil -emit-ir %s | %FileCheck %s --check-prefix=OSIZE -DINT=i%target-ptrsize +// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -enable-objc-interop -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize +// RUN: %target-swift-frontend -Osize -assume-parsing-unqualified-ownership-sil -enable-objc-interop -emit-ir %s | %FileCheck %s --check-prefix=OSIZE -DINT=i%target-ptrsize // REQUIRES: CPU=x86_64 -// XFAIL: linux sil_stage canonical diff --git a/test/IRGen/unconditional_checked_cast.sil b/test/IRGen/unconditional_checked_cast.sil index 3f7fa7a590dce..6406249163fb3 100644 --- a/test/IRGen/unconditional_checked_cast.sil +++ b/test/IRGen/unconditional_checked_cast.sil @@ -10,7 +10,7 @@ sil_vtable C {} class D : C {} sil_vtable D {} -// CHECK-LABEL: define{{( protected)?}} swiftcc void @downcast_test(%T26unconditional_checked_cast1DC** noalias nocapture sret, %T26unconditional_checked_cast1CC** nocapture dereferenceable({{.*}})) {{.*}} { +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @downcast_test(%T26unconditional_checked_cast1DC** noalias nocapture sret, %T26unconditional_checked_cast1CC** nocapture dereferenceable({{.*}})) {{.*}} { // CHECK: entry: // CHECK-NEXT: [[INPUTPTR:%[0-9]+]] = load %T26unconditional_checked_cast1CC*, %T26unconditional_checked_cast1CC** [[INPUTPTRPTR:%[0-9]+]], align 8 // CHECK-NEXT: [[I8INPUTPTR:%[0-9]+]] = bitcast %T26unconditional_checked_cast1CC* [[INPUTPTR]] to i8* diff --git a/test/IRGen/undef.sil b/test/IRGen/undef.sil index 8a1ae9398c34f..16e4f54bfd339 100644 --- a/test/IRGen/undef.sil +++ b/test/IRGen/undef.sil @@ -4,7 +4,7 @@ import Builtin -// CHECK: define{{( protected)?}} swiftcc void @undefined() {{.*}} { +// CHECK: define{{( dllexport)?}}{{( protected)?}} swiftcc void @undefined() {{.*}} { // CHECK: entry: // CHECK: store i64 undef, i64* undef, align 8 // CHECK: store i8 undef, i8* undef, align 8 diff --git a/test/IRGen/unowned.sil b/test/IRGen/unowned.sil index d58e1e2cf9efd..2406e71b137c7 100644 --- a/test/IRGen/unowned.sil +++ b/test/IRGen/unowned.sil @@ -30,7 +30,7 @@ bb0(%0 : $@sil_unowned C): %3 = tuple () return %3 : $() } -// CHECK: define{{( protected)?}} swiftcc void @test_weak_rr_class([[C]]*) {{.*}} { +// CHECK: define{{( dllexport)?}}{{( protected)?}} swiftcc void @test_weak_rr_class([[C]]*) {{.*}} { // CHECK: call [[C]]* bitcast ([[REF]]* ([[REF]]*)* @swift_unownedRetain to [[C]]* ([[C]]*)*)([[C]]* returned %0) // CHECK-NEXT: call void bitcast (void ([[REF]]*)* @swift_unownedRelease to void ([[C]]*)*)([[C]]* %0) // CHECK-NEXT: ret void @@ -42,7 +42,7 @@ bb0(%0 : $@sil_unowned P): %3 = tuple () return %3 : $() } -// CHECK: define{{( protected)?}} swiftcc void @test_weak_rr_proto(%swift.refcounted*, i8**) {{.*}} { +// CHECK: define{{( dllexport)?}}{{( protected)?}} swiftcc void @test_weak_rr_proto(%swift.refcounted*, i8**) {{.*}} { // CHECK: call %swift.refcounted* @swift_unownedRetain(%swift.refcounted* returned %0) // CHECK: call void @swift_unownedRelease(%swift.refcounted* %0) // CHECK-NEXT: ret void diff --git a/test/IRGen/unowned_objc.sil b/test/IRGen/unowned_objc.sil index 226a553da9c36..f14247acc1873 100644 --- a/test/IRGen/unowned_objc.sil +++ b/test/IRGen/unowned_objc.sil @@ -1,7 +1,6 @@ -// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -emit-ir %s | %FileCheck %s +// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -enable-objc-interop -emit-ir %s | %FileCheck %s // REQUIRES: CPU=x86_64 -// XFAIL: linux import Builtin @@ -36,12 +35,12 @@ bb0(%0 : $@sil_unowned C): %3 = tuple () return %3 : $() } -// CHECK: define{{( protected)?}} swiftcc void @test_weak_rr_class([[C]]*) {{.*}} { +// CHECK: define{{( dllexport)?}}{{( protected)?}} swiftcc void @test_weak_rr_class([[C]]*) {{.*}} { // CHECK: call [[C]]* bitcast ([[REF]]* ([[REF]]*)* @swift_unownedRetain to [[C]]* ([[C]]*)*)([[C]]* returned %0) // CHECK-NEXT: call void bitcast (void ([[REF]]*)* @swift_unownedRelease to void ([[C]]*)*)([[C]]* %0) // CHECK-NEXT: ret void -// CHECK: define{{( protected)?}} swiftcc void @test_unknown_unowned_copies([[UNKNOWN]]*, i8**, [[UNKNOWN]]*, i8**) +// CHECK: define{{( dllexport)?}}{{( protected)?}} swiftcc void @test_unknown_unowned_copies([[UNKNOWN]]*, i8**, [[UNKNOWN]]*, i8**) sil @test_unknown_unowned_copies : $@convention(thin) (@owned P, @owned P) -> () { bb0(%p : $P, %q : $P): diff --git a/test/IRGen/unused.sil b/test/IRGen/unused.sil index a2c99255d62cb..912c159086317 100644 --- a/test/IRGen/unused.sil +++ b/test/IRGen/unused.sil @@ -1,6 +1,4 @@ -// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -primary-file %s -emit-ir > %t -// RUN: %FileCheck %s --check-prefix=CHECK-%target-object-format --check-prefix=CHECK < %t -// RUN: %FileCheck -check-prefix=NEGATIVE %s < %t +// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -primary-file %s -emit-ir | %FileCheck -check-prefix CHECK -check-prefix NEGATIVE -check-prefix CHECK-%target-object-format %s // REQUIRES: CPU=x86_64 @@ -50,7 +48,7 @@ bb0: // CHECK: define linkonce_odr hidden swiftcc void @qux() // CHECK: define hidden swiftcc void @fred() -// CHECK: define{{( protected)?}} swiftcc void @frieda() +// CHECK: define{{( dllexport)?}}{{( protected)?}} swiftcc void @frieda() // NEGATIVE-NOT: @foo // NEGATIVE-NOT: @bar diff --git a/test/IRGen/upcast.sil b/test/IRGen/upcast.sil index acbe0106fd1a4..e6c7897270d36 100644 --- a/test/IRGen/upcast.sil +++ b/test/IRGen/upcast.sil @@ -2,7 +2,7 @@ // Make sure that we are able to lower upcast addresses. -// CHECK-LABEL: define{{( protected)?}} swiftcc void @upcast_test(%T6upcast1DC** nocapture dereferenceable({{.*}})) {{.*}} { +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @upcast_test(%T6upcast1DC** nocapture dereferenceable({{.*}})) {{.*}} { // CHECK: entry: // CHECK-NEXT: bitcast %T6upcast1DC** {{%[0-0]+}} to %T6upcast1CC** // CHECK-NEXT: ret void diff --git a/test/IRGen/value_buffers.sil b/test/IRGen/value_buffers.sil index 67d1bfba53f31..2306e6d22db6c 100644 --- a/test/IRGen/value_buffers.sil +++ b/test/IRGen/value_buffers.sil @@ -18,7 +18,7 @@ entry(%b : $*Builtin.UnsafeValueBuffer, %v : $Int): %r = tuple () return %r : $() } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @alloc_small([24 x i8]* nocapture dereferenceable({{.*}}), i64) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @alloc_small([24 x i8]* nocapture dereferenceable({{.*}}), i64) // CHECK-NEXT: entry: // CHECK-NEXT: [[T0:%.*]] = bitcast [24 x i8]* %0 to %TSi* // CHECK-NEXT: [[T2:%.*]] = getelementptr inbounds %TSi, %TSi* [[T0]], i32 0, i32 0 @@ -32,7 +32,7 @@ entry(%b : $*Builtin.UnsafeValueBuffer, %v : $Int): %r = tuple () return %r : $() } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @project_small([24 x i8]* nocapture dereferenceable({{.*}}), i64) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @project_small([24 x i8]* nocapture dereferenceable({{.*}}), i64) // CHECK-NEXT: entry: // CHECK-NEXT: [[T0:%.*]] = bitcast [24 x i8]* %0 to %TSi* // CHECK-NEXT: [[T2:%.*]] = getelementptr inbounds %TSi, %TSi* [[T0]], i32 0, i32 0 @@ -45,7 +45,7 @@ entry(%b : $*Builtin.UnsafeValueBuffer): %r = tuple () return %r : $() } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @dealloc_small([24 x i8]* nocapture dereferenceable({{.*}})) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @dealloc_small([24 x i8]* nocapture dereferenceable({{.*}})) // CHECK-NEXT: entry: // CHECK-NEXT: ret void @@ -56,7 +56,7 @@ entry(%b : $*Builtin.UnsafeValueBuffer, %v : $BigStruct): %r = tuple () return %r : $() } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @alloc_big([24 x i8]* nocapture dereferenceable({{.*}}), %T13value_buffers9BigStructV* noalias nocapture dereferenceable({{.*}})) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @alloc_big([24 x i8]* nocapture dereferenceable({{.*}}), %T13value_buffers9BigStructV* noalias nocapture dereferenceable({{.*}})) // CHECK-NEXT: entry: // CHECK-NEXT: [[T0:%.*]] = call noalias i8* @swift_slowAlloc(i64 48, i64 7) // CHECK-NEXT: [[T1:%.*]] = bitcast [24 x i8]* %0 to i8** @@ -74,7 +74,7 @@ entry(%b : $*Builtin.UnsafeValueBuffer, %v : $BigStruct): %r = tuple () return %r : $() } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @project_big([24 x i8]* nocapture dereferenceable({{.*}}), %T13value_buffers9BigStructV* noalias nocapture dereferenceable({{.*}})) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @project_big([24 x i8]* nocapture dereferenceable({{.*}}), %T13value_buffers9BigStructV* noalias nocapture dereferenceable({{.*}})) // CHECK-NEXT: entry: // CHECK-NEXT: [[T0:%.*]] = bitcast [24 x i8]* %0 to %T13value_buffers9BigStructV** // CHECK-NEXT: [[ADDR:%.*]] = load %T13value_buffers9BigStructV*, %T13value_buffers9BigStructV** [[T0]], align 8 @@ -89,7 +89,7 @@ entry(%b : $*Builtin.UnsafeValueBuffer): %r = tuple () return %r : $() } -// CHECK-LABEL: define{{( protected)?}} swiftcc void @dealloc_big([24 x i8]* nocapture dereferenceable({{.*}})) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @dealloc_big([24 x i8]* nocapture dereferenceable({{.*}})) // CHECK-NEXT: entry: // CHECK-NEXT: [[T0:%.*]] = bitcast [24 x i8]* %0 to i8** // CHECK-NEXT: [[ADDR:%.*]] = load i8*, i8** [[T0]], align 8 diff --git a/test/IRGen/weak.sil b/test/IRGen/weak.sil index bcf055d6efc57..0493b6d8bee71 100644 --- a/test/IRGen/weak.sil +++ b/test/IRGen/weak.sil @@ -1,8 +1,7 @@ -// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -emit-ir %s | %FileCheck %s +// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -enable-objc-interop -emit-ir %s | %FileCheck %s // RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -O -S %s | %FileCheck %s --check-prefix=TAILCALL // REQUIRES: CPU=x86_64 -// XFAIL: linux // CHECK-DAG: [[TYPE:%swift.type]] = type // CHECK-DAG: [[OPAQUE:%swift.opaque]] = type opaque @@ -29,9 +28,9 @@ public struct A { } // size 8 -// flags 0x1100007 == 1114119 (non-POD, non-inline, non-bitwise-takable) +// flags 0x130007 == 1245191 (non-POD, non-inline, non-bitwise-takable) // stride 8 -// CHECK: @"$S4weak1AVWV" = {{.*}} i8* inttoptr (i64 8 to i8*), i8* inttoptr (i64 1114119 to i8*), i8* inttoptr (i64 8 to i8*)] +// CHECK: @"$S4weak1AVWV" = {{.*}} i8* inttoptr (i64 8 to i8*), i8* inttoptr (i64 1245191 to i8*), i8* inttoptr (i64 8 to i8*)] sil @test_weak_load_store : $@convention(thin) (@inout A, Optional) -> () { bb0(%0 : $*A, %1 : $Optional): @@ -42,7 +41,7 @@ bb0(%0 : $*A, %1 : $Optional): %4 = tuple () return %4 : $() } -// CHECK: define{{( protected)?}} swiftcc void @test_weak_load_store([[A]]* nocapture dereferenceable({{.*}}), i64) {{.*}} { +// CHECK: define{{( dllexport)?}}{{( protected)?}} swiftcc void @test_weak_load_store([[A]]* nocapture dereferenceable({{.*}}), i64) {{.*}} { // CHECK: [[X:%.*]] = getelementptr inbounds [[A]], [[A]]* %0, i32 0, i32 0 // CHECK-NEXT: [[T0:%.*]] = call [[C]]* bitcast ([[REF]]* ([[WEAK]]*)* @swift_weakLoadStrong to [[C]]* ([[WEAK]]*)*)([[WEAK]]* [[X]]) // CHECK-NEXT: %3 = ptrtoint %T4weak1CC* %2 to i64 @@ -65,7 +64,7 @@ bb0(%0 : $*B, %1 : $Optional