diff --git a/CHANGELOG.md b/CHANGELOG.md index 569a2069a9dda..a97a6a59746ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,20 @@ CHANGELOG Swift Next ---------- +* [SR-10069][]: + + Function overloading now works in local contexts, making the following valid: + + ```swift + func outer(x: Int, y: String) { + func doIt(_: Int) {} + func doIt(_: String) {} + + doIt(x) // calls the first 'doIt(_:)' with an Int value + doIt(y) // calls the second 'doIt(_:)' with a String value + } + ``` + * [SE-0284][]: Functions, subscripts, and initializers may now have more than one variadic parameter, as long as all parameters which follow variadic parameters are labeled. This makes declarations like the following valid: @@ -8196,6 +8210,7 @@ Swift 1.0 [SR-8974]: [SR-9043]: [SR-9827]: +[SR-10069]: [SR-11298]: [SR-11429]: [SR-11700]: diff --git a/CMakeLists.txt b/CMakeLists.txt index 01711a7377ce3..c8c04c84873ae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,6 +17,11 @@ if(POLICY CMP0076) cmake_policy(SET CMP0076 NEW) endif() +# Don't clobber existing variable values when evaluating `option()` declarations. +if(POLICY CMP0077) + cmake_policy(SET CMP0077 NEW) +endif() + # Add path for custom CMake modules. list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules") diff --git a/apinotes/os.apinotes b/apinotes/os.apinotes index 3b51bfb8cf43e..c0f6364e19575 100644 --- a/apinotes/os.apinotes +++ b/apinotes/os.apinotes @@ -63,5 +63,5 @@ Functions: SwiftPrivate: true NullabilityOfRet: O - Name: _os_signpost_emit_with_name_impl - Availability: nonswift - AvailabilityMsg: 'Use os_signpost' + SwiftPrivate: true + NullabilityOfRet: O diff --git a/cmake/modules/AddSwift.cmake b/cmake/modules/AddSwift.cmake index ec61d2abb836a..fb745168a6ad1 100644 --- a/cmake/modules/AddSwift.cmake +++ b/cmake/modules/AddSwift.cmake @@ -291,6 +291,16 @@ function(_add_host_variant_c_compile_flags target) target_compile_options(${target} PRIVATE -march=core2) endif() endif() + + # The LLVM backend is built with these defines on most 32-bit platforms, + # llvm/llvm-project@66395c9, which can cause incompatibilities with the Swift + # frontend if not built the same way. + if("${SWIFT_HOST_VARIANT_ARCH}" MATCHES "armv6|armv7|i686" AND + NOT (SWIFT_HOST_VARIANT_SDK STREQUAL ANDROID AND SWIFT_ANDROID_API_LEVEL LESS 24)) + target_compile_definitions(${target} PRIVATE + _LARGEFILE_SOURCE + _FILE_OFFSET_BITS=64) + endif() endfunction() function(_add_host_variant_link_flags target) @@ -461,7 +471,7 @@ function(add_swift_host_library name) INSTALL_NAME_DIR "@rpath") elseif(SWIFT_HOST_VARIANT_SDK STREQUAL LINUX) set_target_properties(${name} PROPERTIES - INSTALL_RPATH "$ORIGIN:/usr/lib/swift/linux") + INSTALL_RPATH "$ORIGIN") elseif(SWIFT_HOST_VARIANT_SDK STREQUAL CYGWIN) set_target_properties(${name} PROPERTIES INSTALL_RPATH "$ORIGIN:/usr/lib/swift/cygwin") @@ -630,3 +640,9 @@ 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}) endmacro() + +# Declare that files in this library are built with LLVM's support +# libraries available. +macro(set_swift_llvm_is_available) + add_compile_options(-DSWIFT_LLVM_SUPPORT_IS_AVAILABLE) +endmacro() diff --git a/cmake/modules/AddSwiftUnittests.cmake b/cmake/modules/AddSwiftUnittests.cmake index 531916e071b79..aff795346e6c0 100644 --- a/cmake/modules/AddSwiftUnittests.cmake +++ b/cmake/modules/AddSwiftUnittests.cmake @@ -40,8 +40,13 @@ function(add_swift_unittest test_dirname) endif() if("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin") + # Add an @rpath to the swift library directory. set_target_properties(${test_dirname} PROPERTIES BUILD_RPATH ${SWIFT_LIBRARY_OUTPUT_INTDIR}/swift/macosx) + # Force all the swift libraries to be found via rpath. + add_custom_command(TARGET "${test_dirname}" POST_BUILD + COMMAND "${SWIFT_SOURCE_DIR}/utils/swift-rpathize.py" + "$") elseif("${SWIFT_HOST_VARIANT}" STREQUAL "android") swift_android_lib_for_arch(${SWIFT_HOST_VARIANT_ARCH} android_system_libs) set_property(TARGET "${test_dirname}" APPEND PROPERTY LINK_DIRECTORIES diff --git a/cmake/modules/StandaloneOverlay.cmake b/cmake/modules/StandaloneOverlay.cmake index 7b02fbdf998ad..f84b10d6727af 100644 --- a/cmake/modules/StandaloneOverlay.cmake +++ b/cmake/modules/StandaloneOverlay.cmake @@ -58,9 +58,17 @@ set(SWIFT_NATIVE_CLANG_TOOLS_PATH "${TOOLCHAIN_DIR}/usr/bin" CACHE STRING set(SWIFT_NATIVE_SWIFT_TOOLS_PATH "${TOOLCHAIN_DIR}/usr/bin" CACHE STRING "Path to Swift tools that are executable on the build machine.") +# NOTE: The initialization in stdlib/CMakeLists.txt will be bypassed if we +# directly invoke CMake for this directory, so we initialize the variables +# related to library evolution here as well. + +option(SWIFT_STDLIB_STABLE_ABI + "Should stdlib be built with stable ABI (library evolution, resilience)." + TRUE) + option(SWIFT_ENABLE_MODULE_INTERFACES "Generate .swiftinterface files alongside .swiftmodule files." - TRUE) + "${SWIFT_STDLIB_STABLE_ABI}") set(SWIFT_STDLIB_BUILD_TYPE "${CMAKE_BUILD_TYPE}" CACHE STRING "Build type for the Swift standard library and SDK overlays.") diff --git a/docs/ABI/Mangling.rst b/docs/ABI/Mangling.rst index 9e60d04a4f5e3..9a45251e7b83b 100644 --- a/docs/ABI/Mangling.rst +++ b/docs/ABI/Mangling.rst @@ -523,8 +523,10 @@ Types FUNCTION-KIND ::= 'U' // uncurried function type (currently not used) FUNCTION-KIND ::= 'K' // @auto_closure function type (noescape) FUNCTION-KIND ::= 'B' // objc block function type - FUNCTION-KIND ::= 'L' // objc block function type (escaping) (DWARF only; otherwise use 'B') + FUNCTION-KIND ::= 'zB' C-TYPE // objc block type with non-canonical C type + FUNCTION-KIND ::= 'L' // objc block function type with canonical C type (escaping) (DWARF only; otherwise use 'B' or 'zB' C-TYPE) FUNCTION-KIND ::= 'C' // C function pointer type + FUNCTION-KIND ::= 'zC' C-TYPE // C function pointer type with with non-canonical C type FUNCTION-KIND ::= 'A' // @auto_closure function type (escaping) FUNCTION-KIND ::= 'E' // function type (noescape) FUNCTION-KIND ::= 'F' // @differentiable function type @@ -532,6 +534,9 @@ Types FUNCTION-KIND ::= 'H' // @differentiable(linear) function type FUNCTION-KIND ::= 'I' // @differentiable(linear) function type (escaping) + C-TYPE is mangled according to the Itanium ABI, and prefixed with the length. + Non-ASCII identifiers are preserved as-is; we do not use Punycode. + function-signature ::= params-type params-type async? throws? // results and parameters params-type ::= type 'z'? 'h'? // tuple in case of multiple parameters or a single parameter with a single tuple type @@ -600,7 +605,7 @@ mangled in to disambiguate. impl-function-type ::= type* 'I' FUNC-ATTRIBUTES '_' impl-function-type ::= type* generic-signature 'I' FUNC-ATTRIBUTES '_' - FUNC-ATTRIBUTES ::= PATTERN-SUBS? INVOCATION-SUBS? PSEUDO-GENERIC? CALLEE-ESCAPE? DIFFERENTIABILITY-KIND? CALLEE-CONVENTION FUNC-REPRESENTATION? COROUTINE-KIND? (PARAM-CONVENTION PARAM-DIFFERENTIABILITY?)* RESULT-CONVENTION* ('Y' PARAM-CONVENTION)* ('z' RESULT-CONVENTION RESULT-DIFFERENTIABILITY?)? + FUNC-ATTRIBUTES ::= PATTERN-SUBS? INVOCATION-SUBS? PSEUDO-GENERIC? CALLEE-ESCAPE? DIFFERENTIABILITY-KIND? CALLEE-CONVENTION FUNC-REPRESENTATION? COROUTINE-KIND? ASYNC? (PARAM-CONVENTION PARAM-DIFFERENTIABILITY?)* RESULT-CONVENTION* ('Y' PARAM-CONVENTION)* ('z' RESULT-CONVENTION RESULT-DIFFERENTIABILITY?)? PATTERN-SUBS ::= 's' // has pattern substitutions INVOCATION-SUB ::= 'I' // has invocation substitutions @@ -618,7 +623,9 @@ mangled in to disambiguate. CALLEE-CONVENTION ::= 't' // thin FUNC-REPRESENTATION ::= 'B' // C block invocation function + FUNC-REPRESENTATION ::= 'zB' C-TYPE // C block invocation function with non-canonical C type FUNC-REPRESENTATION ::= 'C' // C global function + FUNC-REPRESENTATION ::= 'zC' C-TYPE // C global function with non-canonical C type FUNC-REPRESENTATION ::= 'M' // Swift method FUNC-REPRESENTATION ::= 'J' // ObjC method FUNC-REPRESENTATION ::= 'K' // closure @@ -627,6 +634,8 @@ mangled in to disambiguate. COROUTINE-KIND ::= 'A' // yield-once coroutine COROUTINE-KIND ::= 'G' // yield-many coroutine + ASYNC ::= 'H' // @async + PARAM-CONVENTION ::= 'i' // indirect in PARAM-CONVENTION ::= 'c' // indirect in constant PARAM-CONVENTION ::= 'l' // indirect inout @@ -993,6 +1002,7 @@ Function Specializations :: specialization ::= type '_' type* 'Tg' SPEC-INFO // Generic re-abstracted specialization + specialization ::= type '_' type* 'Ts' SPEC-INFO // Generic re-abstracted prespecialization specialization ::= type '_' type* 'TG' SPEC-INFO // Generic not re-abstracted specialization specialization ::= type '_' type* 'Ti' SPEC-INFO // Inlined function with generic substitutions. diff --git a/docs/CompilerPerformance.md b/docs/CompilerPerformance.md index c052facc4cd56..702132799676a 100644 --- a/docs/CompilerPerformance.md +++ b/docs/CompilerPerformance.md @@ -815,8 +815,8 @@ are helpful in such cases: - `llvm-bcanalyzer` can print (in rough form) the contents of LLVM bitcode streams, such as Swift module files and the PCH/PCM files clang stores its - serialized ASTs in. The latter requires combing `llvm-objdump` and - `llvm-bcanalyzer` in the following fashion: `llvm-objdump -raw-clang-ast + serialized ASTs in. The latter requires combining `llvm-objdump` and + `llvm-bcanalyzer` in the following fashion: `llvm-objdump --raw-clang-ast file.pcm | llvm-bcanalyzer -dump` - `llvm-dwarfdump` and `llvm-dis` can be used to print textual representations diff --git a/docs/CppInteroperabilityManifesto.md b/docs/CppInteroperabilityManifesto.md index 3ea8d9e85a347..ea4d5cd1171f5 100644 --- a/docs/CppInteroperabilityManifesto.md +++ b/docs/CppInteroperabilityManifesto.md @@ -307,7 +307,7 @@ provide the following guarantees: - `inout` are backed by initialized memory on function entry and function exit (an implication of the copy-in/copy-out semantics). Destroying the object in - `inout` requires using unsafe construts. Therefore, in safe Swift `inout` + `inout` requires using unsafe constructs. Therefore, in safe Swift `inout` parameters are backed by initialized memory throughout function execution. - `inout` parameters don't alias each other or any other values that program is @@ -1033,7 +1033,7 @@ in all Swift code, but it is feasible for local variables of concrete types. An example where it is not possible to maintain precise destruction semantics for C++ classes is in a generic Swift function that is called with a C++ class as a type parameter. In this case, we must be able to compile one definition of -a Swift function, which must work regradless of the nature of the type (be it a +a Swift function, which must work regardless of the nature of the type (be it a Swift type or a C++ class). This limitation does not seem too bad, because RAII types are not often passed as type parameters to templates in C++, which creates a reason to believe it will not be common in Swift either. @@ -3197,7 +3197,7 @@ UTF-8 data. class Employee { public: std::string DebugDescription() const; - [[swift::import_as_std_string]] std::string SeriaziledAsProtobuf() const; + [[swift::import_as_std_string]] std::string SerializedAsProtobuf() const; }; ``` diff --git a/docs/DebuggingTheCompiler.md b/docs/DebuggingTheCompiler.md index 061ed609780a3..6f1d411698219 100644 --- a/docs/DebuggingTheCompiler.md +++ b/docs/DebuggingTheCompiler.md @@ -30,6 +30,8 @@ benefit of all Swift developers. - [Debugging the Compiler using advanced LLDB Breakpoints](#debugging-the-compiler-using-advanced-lldb-breakpoints) - [Debugging the Compiler using LLDB Scripts](#debugging-the-compiler-using-lldb-scripts) - [Custom LLDB Commands](#custom-lldb-commands) + - [Debugging at LLVM Level](#debugging-at-llvm-level) + - [Options for Dumping LLVM IR](#options-for-dumping-llvm-ir) - [Bisecting Compiler Errors](#bisecting-compiler-errors) - [Bisecting on SIL optimizer pass counts to identify optimizer bugs](#bisecting-on-sil-optimizer-pass-counts-to-identify-optimizer-bugs) - [Using git-bisect in the presence of branch forwarding/feature branches](#using-git-bisect-in-the-presence-of-branch-forwardingfeature-branches) @@ -537,6 +539,20 @@ to define custom commands using just other lldb commands. For example, (lldb) command alias cs sequence p/x $rax; stepi +## Debugging at LLVM Level + +### Options for Dumping LLVM IR + +Similar to SIL, one can configure LLVM to dump the llvm-ir at various points in +the pipeline. Here is a quick summary of the various options: + +* ``-Xllvm -print-before=$PASS_ID``: Print the LLVM IR before a specified LLVM pass runs. +* ``-Xllvm -print-before-all``: Print the LLVM IR before each pass runs. +* ``-Xllvm -print-after-all``: Print the LLVM IR after each pass runs. +* ``-Xllvm -filter-print-funcs=$FUNC_NAME_1,$FUNC_NAME_2,...,$FUNC_NAME_N``: + When printing IR for functions for print-[before|after]-all options, Only + print the IR for functions whose name is in this comma separated list. + ## Bisecting Compiler Errors ### Bisecting on SIL optimizer pass counts to identify optimizer bugs diff --git a/docs/DevelopmentTips.md b/docs/DevelopmentTips.md index 41fa0f817ce47..16ba068fe2577 100644 --- a/docs/DevelopmentTips.md +++ b/docs/DevelopmentTips.md @@ -33,9 +33,11 @@ Compilation times for the compiler and the standard library can be agonizing, es ``` $ brew install sccache $ sccache --start-server -$ ./swift/utils/build-script MY_ARGS --cmake-c-launcher $(which sccache) --cmake-cxx-launcher $(which sccache) +$ ./swift/utils/build-script MY_ARGS --sccache ``` +If you want to always use sccache, you can `export SWIFT_USE_SCCACHE=1` and the build script will pick it up. + Given the size of artifacts generated, you might also want to bump the cache size from the default 10GB to something larger, say by putting `export SCCACHE_CACHE_SIZE="50G"` in your dotfile(s). You can run some compiles to see if it is actually doing something by running `sccache --show-stats`. Depending on the exact compilation task you're running, you might see very different cache hit rates. For example, `sccache` is particularly effective if you're rebuilding LLVM, which doesn't change so frequently from the Swift compiler's perspective. On the other hand, if you're changing the compiler's AST, the cache hit rate is likely to be much lower. diff --git a/docs/Diagnostics.md b/docs/Diagnostics.md index 61edb07b80fc5..e75d64a63bc41 100644 --- a/docs/Diagnostics.md +++ b/docs/Diagnostics.md @@ -100,7 +100,7 @@ Educational notes should: - Explain a single language concept. This makes them easy to reuse across related diagnostics and helps keep them clear, concise, and easy to understand. - Be written in unabbreviated English. These are longer-form messages compared to the main diagnostic, so there's no need to omit needless words and punctuation. - Not generally exceed 3-4 paragraphs. Educational notes should be clear and easily digestible. Messages which are too long also have the potential to create UX issues on the command line. -- Be accessible. Educational notes should be beginner friendly and avoid assuming unnecesary prior knowledge. The goal is not only to help users understand what a diagnostic is telling them, but also to turn errors and warnings into "teachable moments". +- Be accessible. Educational notes should be beginner friendly and avoid assuming unnecessary prior knowledge. The goal is not only to help users understand what a diagnostic is telling them, but also to turn errors and warnings into "teachable moments". - Include references to relevant chapters of _The Swift Programming Language_. - Be written in Markdown, but avoid excessive markup which negatively impacts the terminal UX. diff --git a/docs/DifferentiableProgramming.md b/docs/DifferentiableProgramming.md index 9f592884651dd..0c80821f4e030 100644 --- a/docs/DifferentiableProgramming.md +++ b/docs/DifferentiableProgramming.md @@ -2542,7 +2542,7 @@ As defined above, the `@differentiable` function type attributes requires all non-`@noDerivative` arguments and results to conform to the `@differentiable` attribute. However, there is one exception: when the type of an argument or result is a function type, e.g. `@differentiable (T) -> @differentiable (U) -> -V`. This is because we need to differentiate higher-order funtions. +V`. This is because we need to differentiate higher-order functions. Mathematically, the differentiability of `@differentiable (T, U) -> V` is similar to that of `@differentiable (T) -> @differentiable (U) -> V` in that diff --git a/docs/Driver.md b/docs/Driver.md index 8a935bd1f8b51..58054d01d5518 100644 --- a/docs/Driver.md +++ b/docs/Driver.md @@ -205,7 +205,7 @@ in becoming more like non-whole-module builds. ## Incremental Builds ## -Incremental builds in Swift work by primarily by cross-file dependency +Incremental builds in Swift work primarily by cross-file dependency analysis, described in [DependencyAnalysis.md](DependencyAnalysis.md). Compiling a single file might be necessary because that file has changed, but it could also be because that file depends on something else that might have diff --git a/docs/ExternalResources.md b/docs/ExternalResources.md index ea8fecf1b5fa3..7b3f92187e438 100644 --- a/docs/ExternalResources.md +++ b/docs/ExternalResources.md @@ -135,11 +135,24 @@ https://medium.com/kinandcartacreated/contributing-to-swift-part-2-efebcf7b6c93 ## Code generation, runtime and ABI -- The Swift Runtime ([Part 1: Heap Objects][], [Part 2: Type Layout][]) - by Jordan Rose (blog post series, Aug-Sep 2020): This blog post series - describes the runtime layout for classes, closures and structs, as well - as basic runtime functionality such as retain/release that needs to be - handled when porting Swift to a new platform, such as [Mac OS 9][]. +- [The Swift Runtime][] by Jordan Rose (blog post series, Aug-Oct 2020): + This blog post series describes the runtime layout for different structures, + runtime handling for metadata, as well as basic runtime functionality such + as retain/release that needs to be handled when porting Swift to a new + platform, such as [Mac OS 9][]. + - [Part 1: Heap Objects][] + - [Part 2: Type Layout][] + - [Part 3: Type Metadata][] + - [Part 4: Uniquing Caches][] + - [Part 5: Class Metadata][] + - [Part 6: Class Metadata Initialization][] + - [Part 7: Enums][] +- [Implementing the Swift Runtime in Swift][] + by JP Simard and Jesse Squires with Jordan Rose (podcast episode, Oct 2020): + This episode describes Jordan's effort to port Swift to Mac OS 9. + It touches on a wide variety of topics, such as ObjC interop, memory layout + guarantees, performance, library evolution, writing low-level code in Swift, + the workflow of porting Swift to a new platform and the design of Swift. - [How Swift Achieved Dynamic Linking Where Rust Couldn't][] by Alexis Beingessner (blog post, Nov 2019): This blog post describes Swift's approach for compiling polymorphic functions, contrasting it with the strategy used by @@ -174,9 +187,16 @@ https://medium.com/kinandcartacreated/contributing-to-swift-part-2-efebcf7b6c93 value witness tables, type metadata, abstraction patterns, reabstraction, reabstraction thunks and protocol witness tables. +[Mac OS 9]: https://belkadan.com/blog/2020/04/Swift-on-Mac-OS-9/ +[The Swift Runtime]: https://belkadan.com/blog/tags/swift-runtime/ [Part 1: Heap Objects]: https://belkadan.com/blog/2020/08/Swift-Runtime-Heap-Objects/ [Part 2: Type Layout]: https://belkadan.com/blog/2020/09/Swift-Runtime-Type-Layout/ -[Mac OS 9]: https://belkadan.com/blog/2020/04/Swift-on-Mac-OS-9/ +[Part 3: Type Metadata]: https://belkadan.com/blog/2020/09/Swift-Runtime-Type-Metadata/ +[Part 4: Uniquing Caches]: https://belkadan.com/blog/2020/09/Swift-Runtime-Uniquing-Caches/ +[Part 5: Class Metadata]: https://belkadan.com/blog/2020/09/Swift-Runtime-Class-Metadata/ +[Part 6: Class Metadata Initialization]: https://belkadan.com/blog/2020/10/Swift-Runtime-Class-Metadata-Initialization/ +[Part 7: Enums]: https://belkadan.com/blog/2020/10/Swift-Runtime-Enums/ +[Implementing the Swift Runtime in Swift]: https://spec.fm/podcasts/swift-unwrapped/1DMLbJg5 [How Swift Achieved Dynamic Linking Where Rust Couldn't]: https://gankra.github.io/blah/swift-abi/ [arm64e: An ABI for Pointer Authentication]: https://youtu.be/C1nZvpEBfYA [Exploiting The Swift ABI]: https://youtu.be/0rHG_Pa86oA diff --git a/docs/HowToGuides/FAQ.md b/docs/HowToGuides/FAQ.md index d5cc17a363f29..7889fa317c4bc 100644 --- a/docs/HowToGuides/FAQ.md +++ b/docs/HowToGuides/FAQ.md @@ -42,7 +42,7 @@ built compiler to compile both packages and Xcode projects. Create a build setting `SWIFT_EXEC` with the value set to `/path/to/swiftc`. If you now do a clean build, your locally built compiler will be used. - At the time of writing, in the latest Xcode 12 beta, `SWIFT_EXEC` does not + At the time of writing, in the latest Xcode 12.2 beta 3, `SWIFT_EXEC` does not work for SwiftPM integration inside Xcode, so this will not work for Xcode projects that depend on SwiftPM packages. diff --git a/docs/HowToGuides/GettingStarted.md b/docs/HowToGuides/GettingStarted.md index b438527e859ae..98eb50c093400 100644 --- a/docs/HowToGuides/GettingStarted.md +++ b/docs/HowToGuides/GettingStarted.md @@ -97,7 +97,7 @@ toolchain as a one-off, there are a couple of differences: or a specific snapshot. You can update the branch/tag for all repositories as follows: ```sh - utils/update-checkout --branch mybranchname + utils/update-checkout --scheme mybranchname # OR utils/update-checkout --tag mytagname ``` @@ -126,7 +126,7 @@ Double-check that running `pwd` prints a path ending with `swift`. ### macOS -1. Install [Xcode 12 beta 3][Xcode] or newer: +1. Install [Xcode 12.2 beta 3][Xcode] or newer: The required version of Xcode changes frequently and is often a beta release. Check this document or the host information on for the current required version. @@ -246,15 +246,13 @@ Phew, that's a lot to digest! Now let's proceed to the actual build itself! ```sh utils/build-script --skip-build-benchmarks \ --skip-ios --skip-watchos --skip-tvos --swift-darwin-supported-archs "x86_64" \ - --cmake-c-launcher="$(which sccache)" --cmake-cxx-launcher="$(which sccache)" \ - --release-debuginfo --test + --sccache --release-debuginfo --test ``` - Via Xcode: ```sh utils/build-script --skip-build-benchmarks \ --skip-ios --skip-watchos --skip-tvos --swift-darwin-supported-archs "x86_64" \ - --cmake-c-launcher="$(which sccache)" --cmake-cxx-launcher="$(which sccache)" \ - --release-debuginfo --test \ + --sccache --release-debuginfo --test \ --xcode ``` This will create a directory diff --git a/docs/LibraryEvolution.rst b/docs/LibraryEvolution.rst index dac3495fe893a..1fa6741c5e2c0 100644 --- a/docs/LibraryEvolution.rst +++ b/docs/LibraryEvolution.rst @@ -159,7 +159,7 @@ No other changes are permitted; the following are particularly of note: not they have default arguments. - An ABI-public function that throws may not become non-throwing or vice versa. - The ``@escaping`` attribute may not be added to or removed from a parameter. -- Adding or removing a function builder from a parameter is a +- Adding or removing a result builder from a parameter is a `binary-compatible source-breaking change`. diff --git a/docs/README.md b/docs/README.md index 56f4e92c00e86..7e8a019c642de 100644 --- a/docs/README.md +++ b/docs/README.md @@ -113,14 +113,14 @@ documentation, please create a thread on the Swift forums under the Describes how the "Omit Needless Words" algorithm works, making imported names more idiomatic. - Type-checking and inference: - - [TypeChecker.rst](/docs/TypeChecker.rst): + - [TypeChecker.md](/docs/TypeChecker.md): Provides an overview of how type-checking and inference work. - [RequestEvaluator.md](/docs/RequestEvaluator.md): Describes the request evaluator architecture, which is used for lazy type-checking and efficient caching. - [Literals.md](/docs/Literals.md): Describes type-checking and inference specifically for literals. -- [Serialization.rst](/docs/Serialization.rst): +- [Serialization.md](/docs/Serialization.md): Gives an overview of the LLVM bitcode format used for swiftmodules. - [StableBitcode.md](/docs/StableBitcode.md): Describes how to maintain compatibility when changing the serialization diff --git a/docs/RequestEvaluator.md b/docs/RequestEvaluator.md index 8b51ddafe0496..fe878a82913d7 100644 --- a/docs/RequestEvaluator.md +++ b/docs/RequestEvaluator.md @@ -80,4 +80,4 @@ The request-evaluator is relatively new to the Swift compiler, having been intro * Port higher-level queries (e.g., those that come from SourceKit) over to the request-evaluator, so we can see the dependencies of a given SourceKit request for testing and performance tuning. ## Prior art -Rust's compiler went through a similar transformation to support [demand-driven compilation](https://rust-lang-nursery.github.io/rustc-guide/query.html). We should learn from their experience! +Rust's compiler went through a similar transformation to support [demand-driven compilation](https://rustc-dev-guide.rust-lang.org/query.html). We should learn from their experience! diff --git a/docs/SIL.rst b/docs/SIL.rst index 30e10c76c3fd1..77614c55b7ccb 100644 --- a/docs/SIL.rst +++ b/docs/SIL.rst @@ -1750,7 +1750,7 @@ Owned Owned ownership models "move only" values. We require that each such value is consumed exactly once along all program paths. The IR verifier will flag values that are not consumed along a path as a leak and any double consumes as -use-after-frees. We model move operations via "forwarding uses" such as casts +use-after-frees. We model move operations via `forwarding uses`_ such as casts and transforming terminators (e.x.: `switch_enum`_, `checked_cast_br`_) that transform the input value, consuming it in the process, and producing a new transformed owned value as a result. @@ -1805,7 +1805,7 @@ derived from the ARC object. As an example, consider the following Swift/SIL:: } Notice how our individual copy (``%1``) threads its way through the IR using -forwarding of ``@owned`` ownership. These forwarding operations partition the +`forwarding uses`_ of ``@owned`` ownership. These `forwarding uses`_ partition the lifetime of the result of the copy_value into a set of disjoint individual owned lifetimes (``%2``, ``%3``, ``%5``). @@ -1847,7 +1847,7 @@ optimizations that they can never shrink the lifetime of ``%0`` by moving `destroy_value`_ above ``%1``. Values with guaranteed ownership follow a dataflow rule that states that -non-consuming "forwarding" uses of the guaranteed value are also guaranteed and +non-consuming `forwarding uses`_ of the guaranteed value are also guaranteed and are recursively validated as being in the original values scope. This was a choice we made to reduce idempotent scopes in the IR:: @@ -1912,6 +1912,137 @@ This is a form of ownership that is used to model two different use cases: trivial pointer to a class. In that case, since we have no reason to assume that the object will remain alive, we need to make a copy of the value. +Forwarding Uses +~~~~~~~~~~~~~~~ + +NOTE: In the following, we assumed that one read the section above, `Value Ownership Kind`_. + +A subset of SIL instructions define the value ownership kind of their results in +terms of the value ownership kind of their operands. Such an instruction is +called a "forwarding instruction" and any use with such a user instruction a +"forwarding use". This inference generally occurs upon instruction construction +and as a result: + +* When manipulating forwarding instructions programatically, one must manually + update their forwarded ownership since most of the time the ownership is + stored in the instruction itself. Don't worry though because the SIL verifier + will catch this error for you if you forget to do so! + +* Textual SIL does not represent the ownership of forwarding instructions + explicitly. Instead, the instruction's ownership is inferred normally from the + parsed operand. Since the SILVerifier runs on Textual SIL after parsing, you + can feel confident that ownership constraints were inferred correctly. + +Forwarding has slightly different ownership semantics depending on the value +ownership kind of the operand on construction and the result's type. We go +through each below: + +* Given an ``@owned`` operand, the forwarding instruction is assumed to end the + lifetime of the operand and produce an ``@owned`` value if non-trivially typed + and ``@none`` if trivially typed. Example: This is used to represent the + semantics of casts:: + + sil @unsafelyCastToSubClass : $@convention(thin) (@owned Klass) -> @owned SubKlass { + bb0(%0 : @owned $Klass): // %0 is defined here. + + // %0 is consumed here and can no longer be used after this point. + // %1 is defined here and after this point must be used to access the object + // passed in via %0. + %1 = unchecked_ref_cast %0 : $Klass to $SubKlass + + // Then %1's lifetime ends here and we return the casted argument to our + // caller as an @owned result. + return %1 : $SubKlass + } + +* Given a ``@guaranteed`` operand, the forwarding instruction is assumed to + produce ``@guaranteed`` non-trivially typed values and ``@none`` trivially + typed values. Given the non-trivial case, the instruction is assumed to begin + a new implicit borrow scope for the incoming value. Since the borrow scope is + implicit, we validate the uses of the result as if they were uses of the + operand (recursively). This of course means that one should never see + end_borrows on any guaranteed forwarded results, the end_borrow is always on + the instruction that "introduces" the borrowed value. An example of a + guaranteed forwarding instruction is ``struct_extract``:: + + // In this function, I have a pair of Klasses and I want to grab some state + // and then call the hand off function for someone else to continue + // processing the pair. + sil @accessLHSStateAndHandOff : $@convention(thin) (@owned KlassPair) -> @owned State { + bb0(%0 : @owned $KlassPair): // %0 is defined here. + + // Begin the borrow scope for %0. We want to access %1's subfield in a + // read only way that doesn't involve destructuring and extra copies. So + // we construct a guaranteed scope here so we can safely use a + // struct_extract. + %1 = begin_borrow %0 : $KlassPair + + // Now we perform our struct_extract operation. This operation + // structurally grabs a value out of a struct without safety relying on + // the guaranteed ownership of its operand to know that %1 is live at all + // use points of %2, its result. + %2 = struct_extract %1 : $KlassPair, #KlassPair.lhs + + // Then grab the state from our left hand side klass and copy it so we + // can pass off our klass pair to handOff for continued processing. + %3 = ref_element_addr %2 : $Klass, #Klass.state + %4 = load [copy] %3 : $*State + + // Now that we have finished accessing %1, we end the borrow scope for %1. + end_borrow %1 : $KlassPair + + %handOff = function_ref @handOff : $@convention(thin) (@owned KlassPair) -> () + apply %handOff(%0) : $@convention(thin) (@owned KlassPair) -> () + + return %4 : $State + } + +* Given an ``@none`` operand, the result value must have ``@none`` ownership. + +* Given an ``@unowned`` operand, the result value will have ``@unowned`` + ownership. It will be validated just like any other ``@unowned`` value, namely + that it must be copied before use. + +An additional wrinkle here is that even though the vast majority of forwarding +instructions forward all types of ownership, this is not true in general. To see +why this is necessary, lets compare/contrast `struct_extract`_ (which does not +forward ``@owned`` ownership) and `unchecked_enum_data`_ (which can forward +/all/ ownership kinds). The reason for this difference is that `struct_extract`_ +inherently can only extract out a single field of a larger object implying that +the instruction could only represent consuming a sub-field of a value instead of +the entire value at once. This violates our constraint that owned values can +never be partially consumed: a value is either completely alive or completely +dead. In contrast, enums always represent their payloads as elements in a single +tuple value. This means that `unchecked_enum_data`_ when it extracts that +payload from an enum, can consume the entire enum+payload. + +To handle cases where we want to use `struct_extract`_ in a consuming way, we +instead are able to use the `destructure_struct`_ instruction that consumes the +entire struct at once and gives one back the structs individual constituant +parts:: + + struct KlassPair { + var fieldOne: Klass + var fieldTwo: Klass + } + + sil @getFirstPairElt : $@convention(thin) (@owned KlassPair) -> @owned Klass { + bb0(%0 : @owned $KlassPair): + // If we were to just do this directly and consume KlassPair to access + // fieldOne... what would happen to fieldTwo? Would it be consumed? + // + // %1 = struct_extract %0 : $KlassPair, #KlassPair.fieldOne + // + // Instead we need to destructure to ensure we consume the entire owned value at once. + (%1, %2) = destructure_struct $KlassPair + + // We only want to return %1, so we need to cleanup %2. + destroy_value %2 : $Klass + + // Then return %1 to our caller + return %1 : $Klass + } + Runtime Failure --------------- diff --git a/docs/SILProgrammersManual.md b/docs/SILProgrammersManual.md index b5cf4e9c7bd9e..7a43605f1855b 100644 --- a/docs/SILProgrammersManual.md +++ b/docs/SILProgrammersManual.md @@ -159,6 +159,468 @@ rules out PartialApplies is no excuse to conflate applied arguments with function arguments. Also, without consistent use of common idioms, it becomes overly burdensome to evolve these APIs over time. +## `AccessedStorage` and `AccessPath` + +The `AccessedStorage` and `AccessPath` types formalize memory access +in SIL. Given an address-typed SIL value, it is possible to +reliably identify the storage location of the accessed +memory. `AccessedStorage` identifies an accessed storage +location. `AccessPath` contains both a storage location and the +"access path" within that memory object. The relevant API details are +documented in MemAccessUtils.h + +### Formal access + +SIL preserves the language semantics of formal variable access in the +form of access markers. `begin_access` identifies the address of the +formal access and `end_access` delimits the scope of the access. At +the language level, a formal access is an access to a local variable +or class property. For details, see +[SE-0176: Enforce Exclusive Access to Memory](https://github.com/apple/swift-evolution/blob/master/proposals/0176-enforce-exclusive-access-to-memory.md) + +Access markers are preserved in SIL to: + +1. verify exclusivity enforcement + +2. optimize exclusivity checks following other transforms, such as + converting dynamic checks into static checks + +3. simplify and strengthen general analyses of memory access. For +example, `begin_access [read] %address` indicates that the accessed +address is immutable for the duration of its access scope + +### Access path def-use relationship + +Computing `AccessedStorage` and `AccessPath` for any given SIL address +involves a use-def traversal to determine the origin of the +address. It may traverse operations on address, pointer, box, and +reference types. The logic that formalizes which SIL operations may be +involved in the def-use chain is encapsulated with the +`AccessUseDefChainVisitor`. The traversal can be customized by +implementing this visitor. Customization is not expected to change the +meaning of AccessedStorage or AccessPath. Rather, it is intended for +additional pass-specific book-keeping or for higher-level convenience +APIs that operate on the use-def chain bypassing AccessedStorage +completely. + +Access def-use chains are divided by four points: the "root", the +access "base", the outer-most "access" scope, and the "address" of a +memory operation. For example: +``` + struct S { + var field: Int64 + } + class C { + var prop: S + } + + %root = alloc_ref $C + %base = ref_element_addr %root : $C, #C.prop + %access = begin_access [read] [static] %base : $*S + %address = struct_element_addr %access : $*S, #.field + %value = load [trivial] %address : $*Int64 + end_access %access : $*S +``` + +#### Reference root + +The first part of the def-use chain computes the formal access base +from the root of the object (e.g. `alloc_ref -> +ref_element_addr`). The reference root might be a locally allocated +object, a function argument, a function result, or a reference loaded +from storage. There is no enforcement on the type of operation that +can produce a reference; however, only reference types or +Builtin.BridgeObject types are only allowed in this part of the +def-use chain. The reference root is the greatest common ancestor in +the def-use graph that can identify an object by a single SILValue. If +the root as an `alloc_ref`, then it is *uniquely identified*. The +def-use chain from the root to the base may contain reference casts +(`isRCIdentityPreservingCast`) and phis. + +This example has an identifiable def-use chain from `%root` to `%base`: +``` +class A { + var prop0: Int64 +} +class B : A { +} + +bb0: + %root = alloc_ref $B + cond_br _, bb1, bb2 + +bb1: + %a1 = upcast %root : $B to $A + br bb3(%a1 : $A) + +bb2: + %a2 = upcast %root : $B to $A + br bb3(%a2 : $A) + +bb3(%a : $A): + %bridge = ref_to_bridge_object %a : $A, %bits : $Builtin.Word + %ref = bridge_object_to_ref %bridge : $Builtin.BridgeObject to $A + %base = ref_element_addr %ref : $A, #A.prop0 +``` + +Each object property and its tail storage is considered a separate +formal access base. The reference root is only one component of an +`AccessedStorage` location. AccessedStorage also identifies the class +property being accessed within that object. + +#### Access base + +The access base is the SILValue produced by an instruction that +directly identifies the kind of storage being accessed without further +use-def traversal. Common access bases are `alloc_box`, `alloc_stack`, +`global_addr`, `ref_element_addr`, and function arguments (see +`AccessedStorage::Kind`). + +The access base is the same as the "root" SILValue for all storage +kinds except global and class storage. Global storage has no root. For +class storage the root is the SILValue that identifies object, +described as the "reference root" above. + +"Box" storage is uniquely identified by an `alloc_box` +instruction. Therefore, we consider the `alloc_box` to be the base of +the access. Box storage does not apply to all box types or box +projections, which may instead originate from arguments or indirect +enums for example. + +Typically, the base is the address-type source operand of a +`begin_access`. However, the path from the access base to the +`begin_access` may include *storage casts* (see +`isAccessedStorageCast`). It may involve address, pointer, and box +types, and may traverse phis. For some kinds of storage, the base may +itself even be a non-address pointer. For phis that cannot be uniquely +resolved, the base may even be a box type. + +This example has an identifiable def-use chain from `%base` to `%access`: +``` +bb0: + %base = alloc_box $Int { var Int } + %boxadr = project_box %base : ${ var Int } + %p0 = address_to_pointer %boxadr : $*Int to $Builtin.RawPointer + cond_br _, bb1, bb2 + +bb1: + %p1 = copy_value %p0 : $Builtin.RawPointer + br bb3(%p1 : $Builtin.RawPointer) + +bb2: + br bb3(%p0 : $Builtin.RawPointer) + +bb3(%ptr : $Builtin.RawPointer): + %adr = pointer_to_address %ptr : $Builtin.RawPointer to $*Int + %access = begin_access [read] [static] %adr : $*Int +``` + +Note that address-type phis are illegal (full enforcement +pending). This is important for simplicity and efficiency, but also +allows for a class of storage optimizations, such as bitfields, in +which address storage is always uniquely determined. Currently, if a +(non-address) phi on the access path from `base` to `access` does not +have a common base, then it is considered an invalid access (the +AccessedStorage object is not valid). SIL verification ensures that a +formal access always has valid AccessedStorage (WIP). In other words, the +source of a `begin_access` marker must be a single, non-phi base. In +the future, for further simplicity, we may generally disallow box and +pointer phis unless they have a common base. + +Not all SIL memory access is part of a formal access, but the +`AccessedStorage` and `AccessPath` abstractions are universally +applicable. Non-formal access still has an access base, even though +the use-def search does not begin at a `begin_access` marker. For +non-formal access, SIL verification is not as strict. An invalid +access is allowed, but handled conservatively. This is safe as long as +those non-formal accesses can never alias with class and global +storage. Class and global access is always guarded by formal access +markers--at least until static markers are stripped from SIL. + +#### Nested access + +Nested access occurs when an access base is a function argument. The +caller always checks `@inout` arguments for exclusivity (an access +marker must exist in the caller). However, the argument itself is a +variable with its own formal access. Conflicts may occur in the callee +which were not evident in the caller. In this example, a conflict +occurs in `hasNestedAccess` but not in its caller: + +``` +func takesTwoInouts(_ : inout Int, _ : inout Int) -> () {} + +func hasNestedAccess(_ x : inout Int) -> () { + takesTwoInouts(&x, &x) +} + +var x = 0 +hasNestedAccess(&x) +``` + +Produces these access markers: +``` +sil @takesTwoInouts : $@convention(thin) (@inout Int, @inout Int) -> () + +sil @hasNestedAccess : $@convention(thin) (@inout Int) -> () { +bb0(%0 : $*Int): + %innerAccess = begin_access [modify] %0 : $*Int + %conflicting = begin_access [modify] %0 : $*Int + %f = function_ref @takesTwoInouts + apply %f(%innerAccess, %conflicting) + : $@convention(thin) (@inout Int, @inout Int) -> () + end_access %conflicting : $*Int + end_access %innerAccess : $*Int + //... +} + +%var = alloc_stack $Int +%outerAccess = begin_access [modify] %var : $*Int +%f = function_ref @hasNestedAccess +apply %f(%outerAccess) : $@convention(thin) (@inout Int) -> () { +end_access %outerAccess : $*Int +``` + +Nested accesses become part if the def-use chain after inlining. Here, +both `%innerAccess` and `%conflicting` are nested within +`%outerAccess`: + +``` +%var = alloc_stack $Int +%outerAccess = begin_access [modify] %var : $*Int +%innerAccess = begin_access [modify] %outerAccess : $*Int +%conflicting = begin_access [modify] %outerAccess : $*Int +%f = function_ref @takesTwoInouts +apply %f(%innerAccess, %conflicting) + : $@convention(thin) (@inout Int, @inout Int) -> () +end_access %conflicting : $*Int +end_access %innerAccess : $*Int +end_access %outerAccess : $*Int +``` + +For most purposes, the inner access scopes are irrelevant. When we ask +for the "accessed storage" for `%innerAccess`, we get an +`AccessedStorage` value of "Stack" kind with base `%var = +alloc_stack`. If instead of finding the original accessed storage, we +want to identify the enclosing formal access scope, we need to use a +different API that supports the special `Nested` storage kind. This is +typically only used for exclusivity diagnostics though. + +TODO: Nested static accesses that result from inlining could +potentially be removed, as long as DiagnoseStaticExclusivity has +already run. + +#### Access projections + +On the def-use chain between the *outermost* formal access scope within +the current function and a memory operation, *access projections* +identify subobjects laid out within the formally accessed +variable. The sequence of access projections between the base and the +memory address correspond to an access path. + +For example, there is no formal access for struct fields. Instead, +they are addressed using a `struct_element_addr` within the access +scope: + +``` +%access = begin_access [read] [static] %base : $*S +%memaddr = struct_element_addr %access : $*S, #.field +%value = load [trivial] %memaddr : $*Int64 +end_access %access : $*S +``` + +Note that is is possible to have a nested access scope on the address +of a struct field, which may show up as an access of +struct_element_addr after inlining. The rule is that access +projections cannot occur outside of the outermost access scope within +the function. + +Access projections are address projections--they take an address at +operand zero and produce a single address result. Other +straightforward access projections include `tuple_element_addr`, +`index_addr`, and `tail_addr` (an aligned form of `index_addr`). + +Enum payload extraction (`unchecked_take_enum_data_addr`) is also an +access projection, but it has no effect on the access path. + +Indirect enum payload extraction is a special two-instruction form of +address projection (`load : ${ var } -> project_box`). For simplicity, +and to avoid the appearance of box types on the access path, this +should eventually be encapsulated in a single SIL instruction. + +For example, the following complex def-use chain from `%base` to +`%load` actually has an empty access path: +``` +%boxadr = unchecked_take_enum_data_addr %base : $*Enum, #Enum.int!enumelt +%box = load [take] %boxadr : $*<τ_0_0> { var Int } +%valadr = project_box %box : $<τ_0_0> { var Int } , 0 +%load = load [trivial] %valadr : $*Int +``` + +Storage casts may also occur within an access. This typically results +from accessors, which perform address-to-pointer +conversion. Pointer-to-address conversion performs a type cast, and +could lead to different subobject types corresponding to the same base +and access path. Access paths still uniquely identify a memory +location because it is illegal to cast memory to non-layout-compatible +types on same execution path (without an intervening `bind_memory`). + +Address-type phis are prohibited, but because pointer and box types +may be on the def-use chain, phis may also occur on an access path. A +phi is only a valid part of an access path if it has no affect on the +path components. This means that pointer casting and unboxing may +occur on distinct phi paths, but index offsets and subobject +projections may not. These rules are currently enforced to a limited +extent, so it's possible for invalid access path to occur under +certain conditions. + +For example, the following is a valid def-use access chain, with an +access base defined in `bb0`, a memory operation in `bb3` and an +`index_addr` and `struct_element_addr` on the access path: + +``` +class A {} + +struct S { + var field0: Int64 + var field1: Int64 +} + +bb0: + %base = ref_tail_addr %ref : $A, $S + %idxproj = index_addr %tail : $*S, %idx : $Builtin.Word + %p0 = address_to_pointer %idxproj : $*S to $Builtin.RawPointer + cond_br _, bb1, bb2 + +bb1: + %pcopy = copy_value %p0 : $Builtin.RawPointer + %adr1 = pointer_to_address [strict] %pcopy : $Builtin.RawPointer to $*S + %p1 = address_to_pointer %adr1 : $*S to $Builtin.RawPointer + br bb3(%p1 : $Builtin.RawPointer) + +bb2: + br bb3(%p0 : $Builtin.RawPointer) + +bb3(%p3 : $Builtin.RawPointer): + %adr3 = pointer_to_address [strict] %p3 : $Builtin.RawPointer to $*S + %field = struct_element_addr %adr3 : $*S, $S.field0 + load %field : $*Int64 +``` + +### AccessedStorage + +`AccessedStorage` identifies an accessed storage location, be it a +box, stack location, class property, global variable, or argument. It +is implemented as a value object that requires no compile-time memory +allocation and can be used as the hash key for that location. Extra +bits are also available for information specific to a particular +optimization pass. Its API provides the kind of location being +accessed and information about the location's uniqueness or whether it +is distinct from other storage. + +Two __uniquely identified__ storage locations may only alias if their +AccessedStorage objects are identical. + +`AccessedStorage` records the "root" SILValue of the access. The root is +the same as the access base for all storage kinds except global and +class storage. For class properties, the storage root is the reference +root of the object, not the base of the property. Multiple +`ref_element_addr` projections may exist for the same property. Global +variable storage is always uniquely identified, but it is impossible +to find all uses from the def-use chain alone. Multiple `global_addr` +instructions may reference the same variable. To find all global uses, +the client must independently find all global variable references +within the function. Clients that need to know which SILValue base was +discovered during use-def traversal in all cases can make use of +`AccessedStorageWithBase` or `AccessPathWithBase`. + +### AccessPath + +`AccessPath` extends `AccessedStorage` to include the path components +that determine the address of a subobject within the access base. The +access path is a string of index offsets and subobject projection +indices. + +``` +struct S { + var field0: Int64 + var field1: Int64 +} + +%eltadr = struct_element_addr %access : $*S, #.field1 + +Path: (#1) +``` + +``` +class A {} + +%tail = ref_tail_addr %ref : $A, $S +%one = integer_literal $Builtin.Word, 1 +%elt = index_addr %tail : $*S, %one : $Builtin.Word +%field = struct_element_addr %elt : $*S, $S.field0 + +Path: (@1, #0) +``` + +Note that a projection from a reference type to the object's property +or tail storage is not part of the access path because it is already +identified by the storage location. + +Offset indices are all folded into a single index at the head of the +path (a missing offset implies offset zero). Offsets that are not +static constants are still valid but are labeled "@Unknown". Indexing +within a subobject is an ill-formed access, but is handled +conservatively since this rule cannot be fully enforced. + +For example, the following is an invalid access path, which just +happens to point to field1: +``` +%field0 = struct_element_addr %base : $*S, #field0 +%field1 = index_addr %elt : $*Int64, %one : $Builtin.Word + +Path: (INVALID) +``` + +The following APIs determine whether an access path contains another +or may overlap with another. + +`AccessPath::contains(AccessPath subPath)` + +`AccessPath::mayOverlap(AccessPath otherPath)` + +These are extremely light-weight APIs that, in the worst case, require +a trivial linked list traversal with single pointer comparison for the +length of subPath or otherPath. + +Subobjects are both contained with and overlap with their parent +storage. An unknown offset does not contain any known offsets but +overlaps with all offsets. + +### Access path uses + +For any accessed storage location and base, it must also be possible +to reliably identify all uses of that storage location within the +function for a particular access base. If the storage is uniquely +identified, then that also implies that all uses of that storage +within the function have been discovered. In other words, there are no +aliases to the same storage that aren't covered by this use set. + +The `AccessPath::collectUses()` API does this. It is possible to ask +for only the uses contained by the current path, or for all +potentially overlapping uses. It is guaranteed to return a complete +use set unless the client specifies a limit on the number of uses. + +As passes begin to adopt AccessPath::collectUses(), I expect it to +become a visitor pattern that allows the pass to perform custom +book-keeping for certain types of uses. + +The `AccessPathVerification` pass runs at key points in the pipeline +to ensure that all address uses are identified and have consistent +access paths. This pass ensures that the implementations of AccessPath +is internally consistent for all SIL patterns. Enforcing the validity +of the SIL itself, such as which operations are allowed on an access +def-use chain, is handled within the SIL verifier instead. + ## SILGen TBD: Possibly link to a separate document explaining the architecture of SILGen. diff --git a/docs/Serialization.rst b/docs/Serialization.md similarity index 90% rename from docs/Serialization.rst rename to docs/Serialization.md index 57f55ab071a2f..226d4267a271c 100644 --- a/docs/Serialization.rst +++ b/docs/Serialization.md @@ -1,8 +1,4 @@ -:orphan: - -================================= -Swift Binary Serialization Format -================================= +# Swift Binary Serialization Format The fundamental unit of distribution for Swift code is a *module.* A module contains declarations as an interface for clients to write code against. It may @@ -33,10 +29,9 @@ tied to the compiler internals to be useful for this purpose, and it is likely we'll invent a new format instead. -Why LLVM bitcode? -================= +## Why LLVM bitcode? -The `LLVM bitstream `_ format was +The [LLVM bitstream](http://llvm.org/docs/BitCodeFormat.html) format was invented as a container format for LLVM IR. It is a binary format supporting two basic structures: *blocks,* which define regions of the file, and *records,* which contain data fields that can be up to 64 bits. It has a few @@ -60,10 +55,9 @@ have most of these properties as well. But we're already linking against LLVM...might as well use it! -Versioning -========== +## Versioning -.. warning:: +#### _Warning_ This section is relevant to any forward-compatible format used for a library's public interface. However, as mentioned above this may not be @@ -101,13 +95,11 @@ requires extra work on the compiler's part to detect which features are in use; a simpler implementation would just use the latest version number supported: 1.9. -*This versioning scheme was inspired by* `Semantic Versioning -`_. *However, it is not compatible with Semantic Versioning +*This versioning scheme was inspired by* [Semantic Versioning](http://semver.org). *However, it is not compatible with Semantic Versioning because it promises* forward-compatibility *rather than* backward-compatibility. -A High-Level Tour of the Current Module Format -============================================== +## A High-Level Tour of the Current Module Format Every serialized module is represented as a single block called the "module block". The module block is made up of several other block kinds, largely for @@ -132,7 +124,7 @@ organizational purposes. - The **SIL block** contains SIL-level implementations that can be imported into a client's SILModule context. In most cases this is just a performance concern, but sometimes it affects language semantics as well, as in the case - of ``@_transparent``. The SIL block precedes the AST block because it affects + of `@_transparent`. The SIL block precedes the AST block because it affects which AST nodes get serialized. - The **SIL index block** contains tables for accessing various SIL entities by @@ -145,9 +137,7 @@ organizational purposes. Nodes are accessed by a file-unique "DeclIDs" (also covering DeclContexts) and "TypeIDs"; the two sets of IDs use separate numbering schemes. -.. note:: - - The AST block is currently referred to as the "decls block" in the source. + _note_: The AST block is currently referred to as the "decls block" in the source. - The **identifier block** contains a single blob of strings. This is intended for Identifiers---strings uniqued by the ASTContext---but can in theory @@ -160,13 +150,11 @@ organizational purposes. top-level declarations. -SIL -=== +## SIL [to be written] -Cross-reference resilience -========================== +## Cross-reference resilience [to be written] diff --git a/docs/TypeChecker.rst b/docs/TypeChecker.md similarity index 92% rename from docs/TypeChecker.rst rename to docs/TypeChecker.md index a526e1bffd5a0..e01d00d170cf2 100644 --- a/docs/TypeChecker.rst +++ b/docs/TypeChecker.md @@ -1,8 +1,7 @@ -Type Checker Design and Implementation -======================================== +# Type Checker Design and Implementation -Purpose ------------------ + +## Purpose This document describes the design and implementation of the Swift type checker. It is intended for developers who wish to modify, @@ -10,29 +9,28 @@ extend, or improve on the type checker, or simply to understand in greater depth how the Swift type system works. Familiarity with the Swift programming language is assumed. -Approach -------------------- +## Approach The Swift language and its type system incorporate a number of popular language features, including object-oriented programming via classes, function and operator overloading, subtyping, and constrained parametric polymorphism. Swift makes extensive use of type inference, allowing one to omit the types of many variables and expressions. For -example:: - +example: +```swift func round(_ x: Double) -> Int { /* ... */ } var pi: Double = 3.14159 var three = round(pi) // 'three' has type 'Int' func identity(_ x: T) -> T { return x } var eFloat: Float = -identity(2.71828) // numeric literal gets type 'Float' - +``` Swift's type inference allows type information to flow in two directions. As in most mainstream languages, type information can flow -from the leaves of the expression tree (e.g., the expression 'pi', +from the leaves of the expression tree (e.g., the expression `pi`, which refers to a double) up to the root (the type of the variable -'three'). However, Swift also allows type information to flow from the -context (e.g., the fixed type of the variable 'eFloat') at the root of +`three`). However, Swift also allows type information to flow from the +context (e.g., the fixed type of the variable `eFloat`) at the root of the expression tree down to the leaves (the type of the numeric literal 2.71828). This bi-directional type inference is common in languages that use ML-like type systems, but is not present in @@ -57,20 +55,20 @@ and vastly better diagnostics when the problem is limited in scope. Type checking proceeds in three main stages: -`Constraint Generation`_ +[Constraint Generation](#Constraint-Generation) Given an input expression and (possibly) additional contextual information, generate a set of type constraints that describes the relationships among the types of the various subexpressions. The generated constraints may contain unknown types, represented by type variables, which will be determined by the solver. -`Constraint Solving`_ +[Constraint Solving](#Constraint-Generation) Solve the system of constraints by assigning concrete types to each of the type variables in the constraint system. The constraint solver should provide the most specific solution possible among different alternatives. -`Solution Application`_ +[Solution Application](#Solution-Application) Given the input expression, the set of type constraints generated from that expression, and the set of assignments of concrete types to each of the type variables, produce a well-typed expression that @@ -80,8 +78,8 @@ Type checking proceeds in three main stages: The following sections describe these three stages of type checking, as well as matters of performance and diagnostics. -Constraints ----------------- +## Constraints + A constraint system consists of a set of type constraints. Each type constraint either places a requirement on a single type (e.g., it is an integer literal type) or relates two types (e.g., one is a subtype @@ -103,14 +101,14 @@ the Swift type system: ``T1`` get the same concrete type binding. There are two different flavors of equality constraints: - - Exact equality constraints, or "binding", written ``T0 := X`` - for some type variable ``T0`` and type ``X``, which requires - that ``T0`` be exactly identical to ``X``; - - Equality constraints, written ``X == Y`` for types ``X`` and - ``Y``, which require ``X`` and ``Y`` to have the same type, - ignoring lvalue types in the process. For example, the - constraint ``T0 == X`` would be satisfied by assigning ``T0`` - the type ``X`` and by assigning ``T0`` the type ``@lvalue X``. + - Exact equality constraints, or "binding", written ``T0 := X`` + for some type variable ``T0`` and type ``X``, which requires + that ``T0`` be exactly identical to ``X``; + - Equality constraints, written ``X == Y`` for types ``X`` and + ``Y``, which require ``X`` and ``Y`` to have the same type, + ignoring lvalue types in the process. For example, the + constraint ``T0 == X`` would be satisfied by assigning ``T0`` + the type ``X`` and by assigning ``T0`` the type ``@lvalue X``. **Subtyping** A subtype constraint requires the first type to be equivalent to or @@ -193,8 +191,8 @@ the Swift type system: A constraint that requires that the constrained type be DynamicLookup or an lvalue thereof. -Constraint Generation -`````````````````````````` +### Constraint Generation + The process of constraint generation produces a constraint system that relates the types of the various subexpressions within an expression. Programmatically, constraint generation walks an @@ -218,10 +216,10 @@ and types generated from the primary expression kinds are: When a name refers to a set of overloaded declarations, the selection of the appropriate declaration is handled by the - solver. This particular issue is discussed in the `Overloading`_ + solver. This particular issue is discussed in the [Overloading](#Overloading) section. Additionally, when the name refers to a generic function or a generic type, the declaration reference may introduce new type - variables; see the `Polymorphic Types`_ section for more information. + variables; see the [Polymorphic Types](#Polymorphic-Types) section for more information. **Member reference** A member reference expression ``a.b`` is assigned the type ``T0`` @@ -233,7 +231,7 @@ and types generated from the primary expression kinds are: Note that resolution of the member constraint can refer to a set of overloaded declarations; this is described further in the - `Overloading`_ section. + [Overloading](#Overloading) section. **Unresolved member reference** An unresolved member reference ``.name`` refers to a member of a @@ -249,7 +247,7 @@ and types generated from the primary expression kinds are: Note that the constraint system above actually has insufficient information to determine the type ``T0`` without additional - contextual information. The `Overloading`_ section describes how the + contextual information. The [Overloading](#Overloading) section describes how the overload-selection mechanism is used to resolve this problem. **Function application** @@ -327,8 +325,7 @@ and types generated from the primary expression kinds are: There are a number of other expression kinds within the language; see the constraint generator for their mapping to constraints. -Overloading -'''''''''''''''''''''''''' +#### Overloading Overloading is the process of giving multiple, different definitions to the same name. For example, we might overload a ``negate`` function @@ -351,10 +348,12 @@ in which each term binds that type variable (via an exact equality constraint) to the type produced by one of the overloads in the overload set. In our negate example, the disjunction is ``T0 := (Int) -> Int or T0 := (Double) -> Double``. The constraint -solver, discussed in the later section on `Constraint Solving`_, +solver, discussed in the later section on [Constraint Solving](#Constraint-Solving), explores both possible bindings, and the overloaded reference resolves to whichever binding results in a solution that satisfies all -constraints [#]_. +constraints. It is possible that both overloads will result in a solution, +in which case the solutions will be ranked based on the rules +discussed in the section [Comparing Solutions](#Comparing-Solutions). Overloading can be introduced both by expressions that refer to sets of overloaded declarations and by member constraints that end up @@ -368,12 +367,12 @@ issue noted in the prior section is that this constraint does not give the solver enough information to determine ``T0`` without guesswork. However, we note that the type of an enum member actually has a regular structure. For example, consider the ``Optional`` type:: - +```swift enum Optional { case none case some(T) } - +``` The type of ``Optional.none`` is ``Optional``, while the type of ``Optional.some`` is ``(T) -> Optional``. In fact, the type of an enum element can have one of two forms: it can be ``T0``, @@ -383,19 +382,18 @@ latter case, the actual arguments are parsed as part of the unresolved member reference, so that a function application constraint describes their conversion to the input type ``T2``. -Polymorphic Types -'''''''''''''''''''''''''''''''''''''''''''''' +#### Polymorphic Types The Swift language includes generics, a system of constrained parameter polymorphism that enables polymorphic types and functions. For example, one can implement a ``min`` function as, e.g.,:: - +```swift func min(x: T, y: T) -> T { if y < x { return y } return x } - +``` Here, ``T`` is a generic parameter that can be replaced with any concrete type, so long as that type conforms to the protocol ``Comparable``. The type of ``min`` is (internally) written as `` T -> T``. Uses of generic types are also immediately opened by the constraint solver. For example, consider the following generic dictionary type:: - +```swift class Dictionary { // ... } - +``` When the constraint solver encounters the expression ``Dictionary()``, it opens up the type ``Dictionary``---which has not been provided with any specific generic arguments---to the type @@ -443,8 +441,8 @@ unbound generic type, i.e., one that does not have specified generic arguments, cannot be used except where the generic arguments can be inferred. -Constraint Solving ------------------------------ +### Constraint Solving + The primary purpose of the constraint solver is to take a given set of constraints and determine the most specific type binding for each of the type variables in the constraint system. As part of this determination, the @@ -465,8 +463,8 @@ going forward. This section will focus on the basic ideas behind the design of the solver, as well as the type rules that it applies. -Simplification -``````````````````` +#### Simplification + The constraint generation process introduces a number of constraints that can be immediately solved, either directly (because the solution is obvious and trivial) or by breaking the constraint down into a @@ -489,8 +487,7 @@ type properties, conjunctions, and disjunctions. Only the first three kinds of constraints have interesting simplification rules, and are discussed in the following sections. -Relational Constraints -'''''''''''''''''''''''''''''''''''''''''''''''' +##### Relational Constraints Relational constraints describe a relationship between two types. This category covers the equality, subtyping, and conversion constraints, @@ -499,28 +496,27 @@ relationship constraints proceeds by comparing the structure of the two types and applying the typing rules of the Swift language to generate additional constraints. For example, if the constraint is a conversion constraint:: - +``` A -> B D - +``` then both types are function types, and we can break down this constraint into two smaller constraints ``C < A`` and ``B < D`` by applying the conversion rule for function types. Similarly, one can destroy all of the various type constructors---tuple types, generic type specializations, lvalue types, etc.---to produce simpler -requirements, based on the type rules of the language [#]_. +requirements, based on the type rules of the language [1]. Relational constraints involving a type variable on one or both sides generally cannot be solved directly. Rather, these constraints inform the solving process later by providing possible type bindings, -described in the `Type Variable Bindings`_ section. The exception is +described in the [Type Variable Bindings](#Type-Variable-Bindings) section. The exception is an equality constraint between two type variables, e.g., ``T0 == T1``. These constraints are simplified by unifying the equivalence classes of ``T0`` and ``T1`` (using a basic union-find algorithm), such that the solver need only determine a binding for one of the type variables (and the other gets the same binding). -Member Constraints -''''''''''''''''''''''''''''''''''''''''''' +##### Member Constraints Member constraints specify that a certain type has a member of a given name and provide a binding for the type of that member. A member @@ -544,8 +540,8 @@ value, and ``C`` is the type of a reference to that entity. For a reference to a type, ``C`` will be a metatype of the declared type. -Strategies -``````````````````````````````` +#### Strategies + The basic approach to constraint solving is to simplify the constraints until they can no longer be simplified, then produce (and check) educated guesses about which declaration from an overload set @@ -567,8 +563,8 @@ away) or represent sets of assumptions that do not lead to a solution. The following sections describe the techniques used by the solver to produce derived constraint systems that explore the solution space. -Overload Selection -''''''''''''''''''''''''''''''''''''''''''''''''''''' +##### Overload Selection + Overload selection is the simplest way to make an assumption. For an overload set that introduced a disjunction constraint ``T0 := A1 or T0 := A2 or ... or T0 := AN`` into the constraint @@ -576,8 +572,8 @@ system, each term in the disjunction will be visited separately. Each solver state binds the type variable ``T0`` and explores whether the selected overload leads to a suitable solution. -Type Variable Bindings -''''''''''''''''''''''''''''''''''''''''''''''''''''' +##### Type Variable Bindings + A second way in which the solver makes assumptions is to guess at the concrete type to which a given type variable should be bound. That type binding is then introduced in a new, derived constraint system to @@ -588,8 +584,8 @@ does it perform an exhaustive search. Rather, it uses the constraints placed on that type variable to produce potential candidate types. There are several strategies employed by the solver. -Meets and Joins -.......................................... +###### Meets and Joins + A given type variable ``T0`` often has relational constraints placed on it that relate it to concrete types, e.g., ``T0 String { /* ... */ } @@ -732,7 +726,7 @@ checking problem:: var x : X f(10.5, x) - +``` This constraint system generates the constraints "``T(f)`` ==Fn ``T0 -> T1``" (for fresh variables ``T0`` and ``T1``), "``(T2, X) apply argument -> tuple element #1 -> conversion member - +``` When the solver selects a particular overload from the overload set, it records the selected overload based on the locator of the overload set. When it comes time to perform constraint application, the locator @@ -806,8 +800,8 @@ choices made in each solution. Naturally, all of these operations require locators to be unique, which occurs in the constraint system itself. -Simplifying Locators -''''''''''''''''''''''''''''' +##### Simplifying Locators + Locators provide the derivation of location information that follows the path of the solver, and can be used to query and recover the important decisions made by the solver. However, the locators @@ -816,9 +810,9 @@ AST node for the purposes of identifying the corresponding source location. For example, the failed constraint "``Int`` conforms to ``ExpressibleByFloatLiteral``" can most specifically by centered on the floating-point literal ``10.5``, but its locator is:: - +``` function application -> apply argument -> tuple element #0 - +``` The process of locator simplification maps a locator to its most specific AST node. Essentially, it starts at the anchor of the locator (in this case, the application ``f(10.5, x)``) and then walks @@ -833,28 +827,28 @@ Simplification does not always exhaust the complete path. For example, consider a slight modification to our example, so that the argument to ``f`` is provided by another call, we get a different result entirely:: - +```swift func f(_ i : Int, s : String) { } func g() -> (f : Float, x : X) { } f(g()) - +``` Here, the failing constraint is ``Float apply argument -> tuple element #0 - +``` When we simplify this locator, we start with ``f(g())``. The "apply argument" derivation step takes us to the argument expression ``g()``. Here, however, there is no subexpression for the first tuple element of ``g()``, because it's simple part of the tuple returned from ``g``. At this point, simplification ceases, and creates the simplified locator:: - +``` function application of g -> tuple element #0 +``` +### Performance -Performance ------------------ The performance of the type checker is dependent on a number of factors, but the chief concerns are the size of the solution space (which is exponential in the worst case) and the effectiveness of the @@ -862,8 +856,8 @@ solver in exploring that solution space. This section describes some of the techniques used to improve solver performance, many of which can doubtless be improved. -Constraint Graph -```````````````` +#### Constraint Graph + The constraint graph describes the relationships among type variables in the constraint system. Each vertex in the constraint graph corresponds to a single type variable. The edges of the graph @@ -885,8 +879,8 @@ the connected components are then combined into a complete solution. Additionally, the constraint graph is used to direct simplification, described below. -Simplification Worklist -``````````````````````` +#### Simplification Worklist + When the solver has attempted a type variable binding, that binding often leads to additional simplifications in the constraint system. The solver will query the constraint graph to determine which @@ -898,8 +892,8 @@ is exhausted, simplification has completed. The use of the worklist eliminates the need to reprocess constraints that could not have changed because the type variables they mention have not changed. -Solver Scopes -````````````` +#### Solver Scopes + The solver proceeds through the solution space in a depth-first manner. Whenever the solver is about to make a guess---such as a speculative type variable binding or the selection of a term from a @@ -920,8 +914,8 @@ solver scope stores a marker to the current top of the stack, and popping that solver scope reverses all of the operations on that stack until it hits the marker. -Online Scoring -`````````````` +#### Online Scoring + As the solver evaluates potential solutions, it keeps track of the score of the current solution and of the best complete solution found thus far. If the score of the current solution is ever greater than @@ -935,8 +929,8 @@ literal types over other literal types, and prefer cheaper conversions to more expensive conversions. However, some of the rules are fairly ad hoc, and could benefit from more study. -Arena Memory Management -``````````````````````` +#### Arena Memory Management + Each constraint system introduces its own memory allocation arena, making allocations cheap and deallocation essentially free. The allocation arena extends all the way into the AST context, so that @@ -945,36 +939,24 @@ allocated within the constraint system's arena rather than the permanent arena. Most data structures involved in constraint solving use this same arena. -Diagnostics ------------------ -The diagnostics produced by the type checker are currently -terrible. We plan to do something about this, eventually. We also -believe that we can implement some heroics, such as spell-checking -that takes into account the surrounding expression to only provide -well-typed suggestions. +## Diagnostics + +Swift 5.2 introduced a new diagnostic framework, which is described +in detail in this +[blog post](https://swift.org/blog/new-diagnostic-arch-overview/). -.. [#] It is possible that both overloads will result in a solution, - in which case the solutions will be ranked based on the rules - discussed in the section `Comparing Solutions`_. +## Footnotes -.. [#] As of the time of this writing, the type rules of Swift have +[1]: As of the time of this writing, the type rules of Swift have not specifically been documented outside of the source code. The constraints-based type checker contains a function ``matchTypes`` that documents and implements each of these rules. A future revision of this document will provide a more readily-accessible version. -.. [#] More accurately, as of this writing, "will compute". The solver +[2]: More accurately, as of this writing, "will compute". The solver doesn't current compute meets and joins properly. Rather, it arbitrarily picks one of the constraints "below" to start with. -.. [#] Again, as of this writing, the solver doesn't actually compute +[3]: Again, as of this writing, the solver doesn't actually compute meets and joins, so the solver continues until it runs out of - supertypes to enumerate. - -New Diagnostic Architecture ---------------------------- - -We are currently working on porting type-check based diagnostics over -to the new diagnostic framework, which is described in detail in this -`blog post -`_. + supertypes to enumerate. \ No newline at end of file diff --git a/docs/WindowsBuild.md b/docs/WindowsBuild.md index c988b3759646c..ef1683e6881cc 100644 --- a/docs/WindowsBuild.md +++ b/docs/WindowsBuild.md @@ -139,7 +139,7 @@ ninja -C S:\b\1 ```cmd path S:\Library\icu-67\usr\bin;S:\b\1\bin;S:\b\1\tools\swift\libdispatch-prefix\bin;%PATH%;%ProgramFiles%\Git\usr\bin -ninja -C S:\b\toolchain check-swift +ninja -C S:\b\1 check-swift ``` ## Build swift-corelibs-libdispatch diff --git a/docs/contents.rst b/docs/contents.rst index 2487933e92ebb..e6d1180ec105b 100644 --- a/docs/contents.rst +++ b/docs/contents.rst @@ -12,7 +12,6 @@ Contents Generics StoredAndComputedVariables SIL - TypeChecker OptimizationTips ABI: TypeMetadata ABI: TypeLayout diff --git a/include/swift/ABI/Metadata.h b/include/swift/ABI/Metadata.h index 0ac4ab8943c42..4d33e60f7fd73 100644 --- a/include/swift/ABI/Metadata.h +++ b/include/swift/ABI/Metadata.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 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -38,7 +38,7 @@ #include "swift/Basic/RelativePointer.h" #include "swift/Demangling/Demangle.h" #include "swift/Demangling/ManglingMacros.h" -#include "swift/Runtime/Unreachable.h" +#include "swift/Basic/Unreachable.h" #include "../../../stdlib/public/SwiftShims/HeapObject.h" #if SWIFT_OBJC_INTEROP #include @@ -2295,37 +2295,28 @@ struct TargetTypeMetadataRecord { union { /// A direct reference to a nominal type descriptor. RelativeDirectPointerIntPair, - TypeReferenceKind> + TypeMetadataRecordKind> DirectNominalTypeDescriptor; /// An indirect reference to a nominal type descriptor. RelativeDirectPointerIntPair * __ptrauth_swift_type_descriptor>, - TypeReferenceKind> + TypeMetadataRecordKind> IndirectNominalTypeDescriptor; - - // We only allow a subset of the TypeReferenceKinds here. - // Should we just acknowledge that this is a different enum? }; public: - TypeReferenceKind getTypeKind() const { + TypeMetadataRecordKind getTypeKind() const { return DirectNominalTypeDescriptor.getInt(); } const TargetContextDescriptor * getContextDescriptor() const { switch (getTypeKind()) { - case TypeReferenceKind::DirectTypeDescriptor: + case TypeMetadataRecordKind::DirectTypeDescriptor: return DirectNominalTypeDescriptor.getPointer(); - case TypeReferenceKind::IndirectTypeDescriptor: + case TypeMetadataRecordKind::IndirectTypeDescriptor: return *IndirectNominalTypeDescriptor.getPointer(); - - // These types (and any others we might add to TypeReferenceKind - // in the future) are just never used in these lists. - case TypeReferenceKind::DirectObjCClassName: - case TypeReferenceKind::IndirectObjCClass: - return nullptr; } return nullptr; @@ -2415,6 +2406,9 @@ struct TargetTypeReference { /// A direct reference to an Objective-C class name. RelativeDirectPointer DirectObjCClassName; + + /// A "reference" to some metadata kind, e.g. tuple. + MetadataKind MetadataKind; }; const TargetContextDescriptor * @@ -2428,12 +2422,18 @@ struct TargetTypeReference { case TypeReferenceKind::DirectObjCClassName: case TypeReferenceKind::IndirectObjCClass: + case TypeReferenceKind::MetadataKind: return nullptr; } return nullptr; } + enum MetadataKind getMetadataKind(TypeReferenceKind kind) const { + assert(kind == TypeReferenceKind::MetadataKind); + return MetadataKind; + } + #if SWIFT_OBJC_INTEROP /// If this type reference is one of the kinds that supports ObjC /// references, @@ -2519,6 +2519,10 @@ struct TargetProtocolConformanceDescriptor final return Flags.getTypeReferenceKind(); } + enum MetadataKind getMetadataKind() const { + return TypeRef.getMetadataKind(getTypeKind()); + } + const char *getDirectObjCClassName() const { return TypeRef.getDirectObjCClassName(getTypeKind()); } @@ -2546,6 +2550,11 @@ struct TargetProtocolConformanceDescriptor final TargetRelativeContextPointer>(); } + /// Whether this conformance is builtin by the compiler + runtime. + bool isBuiltin() const { + return getTypeKind() == TypeReferenceKind::MetadataKind; + } + /// Whether this conformance is non-unique because it has been synthesized /// for a foreign type. bool isSynthesizedNonUnique() const { @@ -4664,7 +4673,7 @@ int32_t TargetTypeContextDescriptor::getGenericArgumentOffset() const { return llvm::cast>(this) ->getGenericArgumentOffset(); default: - swift_runtime_unreachable("Not a type context descriptor."); + swift_unreachable("Not a type context descriptor."); } } @@ -4682,7 +4691,7 @@ TargetTypeContextDescriptor::getFullGenericContextHeader() const { return llvm::cast>(this) ->getFullGenericContextHeader(); default: - swift_runtime_unreachable("Not a type context descriptor."); + swift_unreachable("Not a type context descriptor."); } } @@ -4699,7 +4708,7 @@ TargetTypeContextDescriptor::getGenericParams() const { case ContextDescriptorKind::OpaqueType: return llvm::cast>(this)->getGenericParams(); default: - swift_runtime_unreachable("Not a type context descriptor."); + swift_unreachable("Not a type context descriptor."); } } @@ -4717,7 +4726,7 @@ TargetTypeContextDescriptor::getForeignMetadataInitialization() const { return llvm::cast>(this) ->getForeignMetadataInitialization(); default: - swift_runtime_unreachable("Not a type context descriptor."); + swift_unreachable("Not a type context descriptor."); } } @@ -4735,7 +4744,7 @@ TargetTypeContextDescriptor::getSingletonMetadataInitialization() const return llvm::cast>(this) ->getSingletonMetadataInitialization(); default: - swift_runtime_unreachable("Not a enum, struct or class type descriptor."); + swift_unreachable("Not a enum, struct or class type descriptor."); } } @@ -4753,7 +4762,7 @@ TargetTypeContextDescriptor::getCanonicicalMetadataPrespecializations() return llvm::cast>(this) ->getCanonicicalMetadataPrespecializations(); default: - swift_runtime_unreachable("Not a type context descriptor."); + swift_unreachable("Not a type context descriptor."); } } diff --git a/include/swift/ABI/MetadataKind.def b/include/swift/ABI/MetadataKind.def index 181292a0bcfb2..27d7e93e37c5d 100644 --- a/include/swift/ABI/MetadataKind.def +++ b/include/swift/ABI/MetadataKind.def @@ -85,6 +85,10 @@ METADATAKIND(HeapGenericLocalVariable, METADATAKIND(ErrorObject, 1 | MetadataKindIsNonType | MetadataKindIsRuntimePrivate) +/// A heap-allocated simple task. +METADATAKIND(SimpleTask, + 2 | MetadataKindIsNonType | MetadataKindIsRuntimePrivate) + // getEnumeratedMetadataKind assumes that all the enumerated values here // will be <= LastEnumeratedMetadataKind. diff --git a/include/swift/ABI/MetadataValues.h b/include/swift/ABI/MetadataValues.h index 54232972ca9fd..fc564085c57a1 100644 --- a/include/swift/ABI/MetadataValues.h +++ b/include/swift/ABI/MetadataValues.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 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -20,10 +20,10 @@ #define SWIFT_ABI_METADATAVALUES_H #include "swift/ABI/KeyPath.h" +#include "swift/ABI/ProtocolDispatchStrategy.h" #include "swift/AST/Ownership.h" #include "swift/Basic/LLVM.h" #include "swift/Basic/FlagSet.h" -#include "swift/Runtime/Unreachable.h" #include #include @@ -361,7 +361,19 @@ enum : unsigned { NumGenericMetadataPrivateDataWords = 16, }; -/// Kinds of type metadata/protocol conformance records. +/// Kinds of type metadata reocrds. +enum class TypeMetadataRecordKind : unsigned { + /// A direct reference to a nominal type descriptor. + DirectTypeDescriptor = 0x00, + + /// An indirect reference to a nominal type descriptor. + IndirectTypeDescriptor = 0x01, + + First_Kind = DirectTypeDescriptor, + Last_Kind = IndirectTypeDescriptor, +}; + +/// Kinds of references to type metadata. enum class TypeReferenceKind : unsigned { /// The conformance is for a nominal type referenced directly; /// getTypeDescriptor() points to the type context descriptor. @@ -384,10 +396,14 @@ enum class TypeReferenceKind : unsigned { /// unused. IndirectObjCClass = 0x03, + /// The conformance is for a non-nominal type whose metadata kind we recorded; + /// getMetadataKind() returns the kind. + MetadataKind = 0x04, + // We only reserve three bits for this in the various places we store it. First_Kind = DirectTypeDescriptor, - Last_Kind = IndirectObjCClass, + Last_Kind = MetadataKind, }; /// Flag that indicates whether an existential type is class-constrained or not. @@ -410,20 +426,6 @@ enum class SpecialProtocol: uint8_t { Error = 1, }; -/// Identifiers for protocol method dispatch strategies. -enum class ProtocolDispatchStrategy: uint8_t { - /// Uses ObjC method dispatch. - /// - /// This must be 0 for ABI compatibility with Objective-C protocol_t records. - ObjC = 0, - - /// Uses Swift protocol witness table dispatch. - /// - /// To invoke methods of this protocol, a pointer to a protocol witness table - /// corresponding to the protocol conformance must be available. - Swift = 1, -}; - /// Flags for protocol descriptors. class ProtocolDescriptorFlags { typedef uint32_t int_type; @@ -486,18 +488,7 @@ class ProtocolDescriptorFlags { /// Does the protocol require a witness table for method dispatch? bool needsWitnessTable() const { - return needsWitnessTable(getDispatchStrategy()); - } - - static bool needsWitnessTable(ProtocolDispatchStrategy strategy) { - switch (strategy) { - case ProtocolDispatchStrategy::ObjC: - return false; - case ProtocolDispatchStrategy::Swift: - return true; - } - - swift_runtime_unreachable("Unhandled ProtocolDispatchStrategy in switch."); + return swift::protocolRequiresWitnessTable(getDispatchStrategy()); } /// Return the identifier if this is a special runtime-known protocol. @@ -1177,6 +1168,19 @@ namespace SpecialPointerAuthDiscriminators { /// Resilient class stub initializer callback const uint16_t ResilientClassStubInitCallback = 0xC671; + + /// Actor enqueue(partialTask:). + const uint16_t ActorEnqueuePartialTask = 0x8f3d; + + /// Jobs, tasks, and continuations. + const uint16_t JobInvokeFunction = 0xcc64; // = 52324 + const uint16_t TaskResumeFunction = 0x2c42; // = 11330 + const uint16_t TaskResumeContext = 0x753a; // = 30010 + const uint16_t AsyncContextParent = 0xbda2; // = 48546 + const uint16_t AsyncContextResume = 0xd707; // = 55047 + const uint16_t AsyncContextYield = 0xe207; // = 57863 + const uint16_t CancellationNotificationFunction = 0x1933; // = 6451 + const uint16_t EscalationNotificationFunction = 0x5be4; // = 23524 } /// The number of arguments that will be passed directly to a generic @@ -1889,6 +1893,163 @@ class IntegerLiteralFlags { } }; +/// Kinds of schedulable job.s +enum class JobKind : size_t { + // There are 256 possible job kinds. + + /// An AsyncTask. + Task = 0, + + /// Job kinds >= 192 are private to the implementation. + First_Reserved = 192 +}; + +/// The priority of a job. Higher priorities are larger values. +enum class JobPriority : size_t { + // This is modelled off of Dispatch.QoS, and the values are directly + // stolen from there. + UserInteractive = 0x21, + UserInitiated = 0x19, + Default = 0x15, + Utility = 0x11, + Background = 0x09, + Unspecified = 0x00, +}; + +/// Flags for schedulable jobs. +class JobFlags : public FlagSet { +public: + enum { + Kind = 0, + Kind_width = 8, + + Priority = 8, + Priority_width = 8, + + // 8 bits reserved for more generic job flags. + + // Kind-specific flags. + + Task_IsChildTask = 24, + Task_IsFuture = 25 + }; + + explicit JobFlags(size_t bits) : FlagSet(bits) {} + JobFlags(JobKind kind) { setKind(kind); } + constexpr JobFlags() {} + + FLAGSET_DEFINE_FIELD_ACCESSORS(Kind, Kind_width, JobKind, + getKind, setKind) + + FLAGSET_DEFINE_FIELD_ACCESSORS(Priority, Priority_width, JobPriority, + getPriority, setPriority) + + bool isAsyncTask() const { + return getKind() == JobKind::Task; + } + + FLAGSET_DEFINE_FLAG_ACCESSORS(Task_IsChildTask, + task_isChildTask, + task_setIsChildTask) + FLAGSET_DEFINE_FLAG_ACCESSORS(Task_IsFuture, + task_isFuture, + task_setIsFuture) + +}; + +/// Kinds of task status record. +enum class TaskStatusRecordKind : uint8_t { + /// A DeadlineStatusRecord, which represents an active deadline. + Deadline = 0, + + /// A ChildTaskStatusRecord, which represents the potential for + /// active child tasks. + ChildTask = 1, + + /// A CancellationNotificationStatusRecord, which represents the + /// need to call a custom function when the task is cancelled. + CancellationNotification = 2, + + /// An EscalationNotificationStatusRecord, which represents the + /// need to call a custom function when the task's priority is + /// escalated. + EscalationNotification = 3, + + // Kinds >= 192 are private to the implementation. + First_Reserved = 192, + Private_RecordLock = 192 +}; + +/// Flags for cancellation records. +class TaskStatusRecordFlags : public FlagSet { +public: + enum { + Kind = 0, + Kind_width = 8, + }; + + explicit TaskStatusRecordFlags(size_t bits) : FlagSet(bits) {} + constexpr TaskStatusRecordFlags() {} + TaskStatusRecordFlags(TaskStatusRecordKind kind) { + setKind(kind); + } + + FLAGSET_DEFINE_FIELD_ACCESSORS(Kind, Kind_width, TaskStatusRecordKind, + getKind, setKind) +}; + +/// Kinds of async context. +enum class AsyncContextKind { + /// An ordinary asynchronous function. + Ordinary = 0, + + /// A context which can yield to its caller. + Yielding = 1, + + // Other kinds are reserved for interesting special + // intermediate contexts. + + // Kinds >= 192 are private to the implementation. + First_Reserved = 192 +}; + +/// Flags for async contexts. +class AsyncContextFlags : public FlagSet { +public: + enum { + Kind = 0, + Kind_width = 8, + + CanThrow = 8, + ShouldNotDeallocate = 9 + }; + + explicit AsyncContextFlags(uint32_t bits) : FlagSet(bits) {} + constexpr AsyncContextFlags() {} + AsyncContextFlags(AsyncContextKind kind) { + setKind(kind); + } + + /// The kind of context this represents. + FLAGSET_DEFINE_FIELD_ACCESSORS(Kind, Kind_width, AsyncContextKind, + getKind, setKind) + + /// Whether this context is permitted to throw. + FLAGSET_DEFINE_FLAG_ACCESSORS(CanThrow, canThrow, setCanThrow) + + /// Whether a function should avoid deallocating its context before + /// returning. It should still pass its caller's context to its + /// return continuation. + /// + /// This flag can be set in the caller to optimize context allocation, + /// e.g. if the callee's context size is known statically and simply + /// allocated as part of the caller's context, or if the callee will + /// be called multiple times. + FLAGSET_DEFINE_FLAG_ACCESSORS(ShouldNotDeallocate, + shouldNotDeallocateInCallee, + setShouldNotDeallocateInCallee) +}; + } // end namespace swift #endif // SWIFT_ABI_METADATAVALUES_H diff --git a/include/swift/ABI/ProtocolDispatchStrategy.h b/include/swift/ABI/ProtocolDispatchStrategy.h new file mode 100644 index 0000000000000..eb89c8f138f4a --- /dev/null +++ b/include/swift/ABI/ProtocolDispatchStrategy.h @@ -0,0 +1,54 @@ +//===--- ProtocolDispatchStrategy.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 +// +//===----------------------------------------------------------------------===// +// +// This header declares the ProtocolDispatchStrategy enum and some +// related operations. It's split out because we would otherwise need +// to include MetadataValues.h in some relatively central headers.s +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_ABI_PROTOCOLDISPATCHSTRATEGY_H +#define SWIFT_ABI_PROTOCOLDISPATCHSTRATEGY_H + +#include "swift/Basic/Unreachable.h" + +namespace swift { + +/// Identifiers for protocol method dispatch strategies. +enum class ProtocolDispatchStrategy: uint8_t { + /// Uses ObjC method dispatch. + /// + /// This must be 0 for ABI compatibility with Objective-C protocol_t records. + ObjC = 0, + + /// Uses Swift protocol witness table dispatch. + /// + /// To invoke methods of this protocol, a pointer to a protocol witness table + /// corresponding to the protocol conformance must be available. + Swift = 1, +}; + +/// Does a protocol using the given strategy require a witness table? +inline bool protocolRequiresWitnessTable(ProtocolDispatchStrategy strategy) { + switch (strategy) { + case ProtocolDispatchStrategy::ObjC: + return false; + case ProtocolDispatchStrategy::Swift: + return true; + } + + swift_unreachable("Unhandled ProtocolDispatchStrategy in switch."); +} + +} + +#endif diff --git a/include/swift/ABI/Task.h b/include/swift/ABI/Task.h new file mode 100644 index 0000000000000..d5f1c822bd64c --- /dev/null +++ b/include/swift/ABI/Task.h @@ -0,0 +1,332 @@ +//===--- Task.h - ABI structures for asynchronous tasks ---------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Swift ABI describing tasks. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_ABI_TASK_H +#define SWIFT_ABI_TASK_H + +#include "swift/Basic/RelativePointer.h" +#include "swift/ABI/HeapObject.h" +#include "swift/ABI/MetadataValues.h" +#include "swift/Runtime/Config.h" +#include "swift/Basic/STLExtras.h" + +namespace swift { + +class AsyncTask; +class AsyncContext; +class Executor; +class Job; +class TaskStatusRecord; + +/// An ExecutorRef isn't necessarily just a pointer to an executor +/// object; it may have other bits set. +struct ExecutorRef { + Executor *Pointer; + + /// Get an executor ref that represents a lack of preference about + /// where execution resumes. This is only valid in continuations, + /// return contexts, and so on; it is not generally passed to + /// executing functions. + static ExecutorRef noPreference() { + return { nullptr }; + } + + bool operator==(ExecutorRef other) const { + return Pointer == other.Pointer; + } +}; + +using JobInvokeFunction = + SWIFT_CC(swift) + void (Job *, ExecutorRef); + +using TaskContinuationFunction = + SWIFT_CC(swift) + void (AsyncTask *, ExecutorRef, AsyncContext *); + +template +struct AsyncFunctionTypeImpl; +template +struct AsyncFunctionTypeImpl { + // TODO: expand and include the arguments in the parameters. + using type = TaskContinuationFunction; +}; + +template +using AsyncFunctionType = typename AsyncFunctionTypeImpl::type; + +/// A "function pointer" for an async function. +/// +/// Eventually, this will always be signed with the data key +/// using a type-specific discriminator. +template +class AsyncFunctionPointer { +public: + /// The function to run. + RelativeDirectPointer, + /*nullable*/ false, + int32_t> Function; + + /// The expected size of the context. + uint32_t ExpectedContextSize; +}; + +/// A schedulable job. +class alignas(2 * alignof(void*)) Job { +public: + // Reserved for the use of the scheduler. + void *SchedulerPrivate[2]; + + JobFlags Flags; + + // We use this union to avoid having to do a second indirect branch + // when resuming an asynchronous task, which we expect will be the + // common case. + union { + // A function to run a job that isn't an AsyncTask. + JobInvokeFunction * __ptrauth_swift_job_invoke_function RunJob; + + // A function to resume an AsyncTask. + TaskContinuationFunction * __ptrauth_swift_task_resume_function ResumeTask; + }; + + Job(JobFlags flags, JobInvokeFunction *invoke) + : Flags(flags), RunJob(invoke) { + assert(!isAsyncTask() && "wrong constructor for a task"); + } + + Job(JobFlags flags, TaskContinuationFunction *invoke) + : Flags(flags), ResumeTask(invoke) { + assert(isAsyncTask() && "wrong constructor for a non-task job"); + } + + bool isAsyncTask() const { + return Flags.isAsyncTask(); + } + + /// Run this job. + void run(ExecutorRef currentExecutor); +}; + +// The compiler will eventually assume these. +static_assert(sizeof(Job) == 4 * sizeof(void*), + "Job size is wrong"); +static_assert(alignof(Job) == 2 * alignof(void*), + "Job alignment is wrong"); + +/// The current state of a task's status records. +class ActiveTaskStatus { + enum : uintptr_t { + IsCancelled = 0x1, + IsLocked = 0x2, + RecordMask = ~uintptr_t(IsCancelled | IsLocked) + }; + + uintptr_t Value; + +public: + constexpr ActiveTaskStatus() : Value(0) {} + ActiveTaskStatus(TaskStatusRecord *innermostRecord, + bool cancelled, bool locked) + : Value(reinterpret_cast(innermostRecord) + + (locked ? IsLocked : 0) + + (cancelled ? IsCancelled : 0)) {} + + /// Is the task currently cancelled? + bool isCancelled() const { return Value & IsCancelled; } + + /// Is there an active lock on the cancellation information? + bool isLocked() const { return Value & IsLocked; } + + /// Return the innermost cancellation record. Code running + /// asynchronously with this task should not access this record + /// without having first locked it; see swift_taskCancel. + TaskStatusRecord *getInnermostRecord() const { + return reinterpret_cast(Value & RecordMask); + } + + static TaskStatusRecord *getStatusRecordParent(TaskStatusRecord *ptr); + + using record_iterator = + LinkedListIterator; + llvm::iterator_range records() const { + return record_iterator::rangeBeginning(getInnermostRecord()); + } +}; + +/// An asynchronous task. Tasks are the analogue of threads for +/// asynchronous functions: that is, they are a persistent identity +/// for the overall async computation. +class AsyncTask : public HeapObject, public Job { +public: + /// The context for resuming the job. When a task is scheduled + /// as a job, the next continuation should be installed as the + /// ResumeTask pointer in the job header, with this serving as + /// the context pointer. + /// + /// We can't protect the data in the context from being overwritten + /// by attackers, but we can at least sign the context pointer to + /// prevent it from being corrupted in flight. + AsyncContext * __ptrauth_swift_task_resume_context ResumeContext; + + /// The currntly-active information about cancellation. + std::atomic Status; + + /// Reserved for the use of the task-local stack allocator. + void *AllocatorPrivate[4]; + + AsyncTask(const HeapMetadata *metadata, JobFlags flags, + TaskContinuationFunction *run, + AsyncContext *initialContext) + : HeapObject(metadata), Job(flags, run), + ResumeContext(initialContext), + Status(ActiveTaskStatus()) { + assert(flags.isAsyncTask()); + } + + void run(ExecutorRef currentExecutor) { + ResumeTask(this, currentExecutor, ResumeContext); + } + + /// Check whether this task has been cancelled. Checking this is, + /// of course, inherently race-prone on its own. + bool isCancelled() const { + return Status.load(std::memory_order_relaxed).isCancelled(); + } + + /// A fragment of an async task structure that happens to be a child task. + class ChildFragment { + /// The parent task of this task. + AsyncTask *Parent; + + /// The next task in the singly-linked list of child tasks. + /// The list must start in a `ChildTaskStatusRecord` registered + /// with the parent task. + /// Note that the parent task may have multiple such records. + AsyncTask *NextChild = nullptr; + + public: + ChildFragment(AsyncTask *parent) : Parent(parent) {} + + AsyncTask *getParent() const { + return Parent; + } + + AsyncTask *getNextChild() const { + return NextChild; + } + }; + + bool isFuture() const { return Flags.task_isFuture(); } + + bool hasChildFragment() const { return Flags.task_isChildTask(); } + ChildFragment *childFragment() { + assert(hasChildFragment()); + return reinterpret_cast(this + 1); + } + + // TODO: Future fragment + + static bool classof(const Job *job) { + return job->isAsyncTask(); + } +}; + +// The compiler will eventually assume these. +static_assert(sizeof(AsyncTask) == 12 * sizeof(void*), + "AsyncTask size is wrong"); +static_assert(alignof(AsyncTask) == 2 * alignof(void*), + "AsyncTask alignment is wrong"); + +inline void Job::run(ExecutorRef currentExecutor) { + if (auto task = dyn_cast(this)) + task->run(currentExecutor); + else + RunJob(this, currentExecutor); +} + +/// An asynchronous context within a task. Generally contexts are +/// allocated using the task-local stack alloc/dealloc operations, but +/// there's no guarantee of that, and the ABI is designed to permit +/// contexts to be allocated within their caller's frame. +class alignas(MaximumAlignment) AsyncContext { +public: + /// The parent context. + AsyncContext * __ptrauth_swift_async_context_parent Parent; + + /// The function to call to resume running in the parent context. + /// Generally this means a semantic return, but for some temporary + /// translation contexts it might mean initiating a call. + /// + /// Eventually, the actual type here will depend on the types + /// which need to be passed to the parent. For now, arguments + /// are always written into the context, and so the type is + /// always the same. + TaskContinuationFunction * __ptrauth_swift_async_context_resume + ResumeParent; + + /// The executor that the parent needs to be resumed on. + ExecutorRef ResumeParentExecutor; + + /// Flags describing this context. + /// + /// Note that this field is only 32 bits; any alignment padding + /// following this on 64-bit platforms can be freely used by the + /// function. If the function is a yielding function, that padding + /// is of course interrupted by the YieldToParent field. + AsyncContextFlags Flags; + + AsyncContext(AsyncContextFlags flags, + TaskContinuationFunction *resumeParent, + ExecutorRef resumeParentExecutor, + AsyncContext *parent) + : Parent(parent), ResumeParent(resumeParent), + ResumeParentExecutor(resumeParentExecutor), + Flags(flags) {} + + AsyncContext(const AsyncContext &) = delete; + AsyncContext &operator=(const AsyncContext &) = delete; +}; + +/// An async context that supports yielding. +class YieldingAsyncContext : public AsyncContext { +public: + /// The function to call to temporarily resume running in the + /// parent context. Generally this means a semantic yield. + TaskContinuationFunction * __ptrauth_swift_async_context_yield + YieldToParent; + + /// The executor that the parent context needs to be yielded to on. + ExecutorRef YieldToParentExecutor; + + YieldingAsyncContext(AsyncContextFlags flags, + TaskContinuationFunction *resumeParent, + ExecutorRef resumeParentExecutor, + TaskContinuationFunction *yieldToParent, + ExecutorRef yieldToParentExecutor, + AsyncContext *parent) + : AsyncContext(flags, resumeParent, resumeParentExecutor, parent), + YieldToParent(yieldToParent), + YieldToParentExecutor(yieldToParentExecutor) {} + + static bool classof(const AsyncContext *context) { + return context->Flags.getKind() == AsyncContextKind::Yielding; + } +}; + +} // end namespace swift + +#endif diff --git a/include/swift/ABI/TaskStatus.h b/include/swift/ABI/TaskStatus.h new file mode 100644 index 0000000000000..f6ff0559fcf6b --- /dev/null +++ b/include/swift/ABI/TaskStatus.h @@ -0,0 +1,220 @@ +//===--- TaskStatusRecord.h - Structures to track task status --*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Swift ABI describing "status records", the mechanism by which +// tasks track dynamic information about their child tasks, custom +// cancellation hooks, and other information which may need to be exposed +// asynchronously outside of the task. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_ABI_TASKSTATUS_H +#define SWIFT_ABI_TASKSTATUS_H + +#include "swift/ABI/MetadataValues.h" +#include "swift/ABI/Task.h" + +namespace swift { + +/// The abstract base class for all sttatus records. +/// +/// TaskStatusRecords are typically allocated on the stack (possibly +/// in the task context), partially initialized, and then atomically +/// added to the task with `swift_task_addTaskStatusRecord`. While +/// registered with the task, a status record should only be +/// modified in ways that respect the possibility of asynchronous +/// access by a cancelling thread. In particular, the chain of +/// status records must not be disturbed. When the task leaves +/// the scope that requires the status record, the record can +/// be unregistered from the task with `swift_task_removeTaskStatusRecord`, +/// at which point the memory can be returned to the system. +class TaskStatusRecord { +public: + TaskStatusRecordFlags Flags; + TaskStatusRecord *Parent; + + TaskStatusRecord(TaskStatusRecordKind kind, + TaskStatusRecord *parent = nullptr) + : Flags(kind) { + resetParent(parent); + } + + TaskStatusRecord(const TaskStatusRecord &) = delete; + TaskStatusRecord &operator=(const TaskStatusRecord &) = delete; + + TaskStatusRecordKind getKind() const { + return Flags.getKind(); + } + + TaskStatusRecord *getParent() const { + return Parent; + } + + /// Change the parent of this unregistered status record to the + /// given record. + /// + /// This should be used when the record has been previously initialized + /// without knowing what the true parent is. If we decide to cache + /// important information (e.g. the earliest timeout) in the innermost + /// status record, this is the method that should fill that in + /// from the parent. + void resetParent(TaskStatusRecord *newParent) { + Parent = newParent; + // TODO: cache + } + + /// Splice a record out of the status-record chain. + /// + /// Unlike resetParent, this assumes that it's just removing one or + /// more records from the chain and that there's no need to do any + /// extra cache manipulation. + void spliceParent(TaskStatusRecord *newParent) { + Parent = newParent; + } +}; + +inline TaskStatusRecord * +ActiveTaskStatus::getStatusRecordParent(TaskStatusRecord *ptr) { + return ptr->getParent(); +} + +/// A deadline for the task. If this is reached, the task will be +/// automatically cancelled. The deadline can also be queried and used +/// in other ways. +struct TaskDeadline { + // FIXME: I don't really know what this should look like right now. + // It's probably target-specific. + uint64_t Value; + + bool operator==(const TaskDeadline &other) const { + return Value == other.Value; + } + bool operator<(const TaskDeadline &other) const { + return Value < other.Value; + } +}; + +/// A status record which states that there's an active deadline +/// within the task. +class DeadlineStatusRecord : public TaskStatusRecord { + TaskDeadline Deadline; +public: + DeadlineStatusRecord(TaskDeadline deadline) + : TaskStatusRecord(TaskStatusRecordKind::Deadline), + Deadline(deadline) {} + + TaskDeadline getDeadline() const { + return Deadline; + } + + static bool classof(const TaskStatusRecord *record) { + return record->getKind() == TaskStatusRecordKind::Deadline; + } +}; + +/// A status record which states that a task has one or +/// more active child tasks. +class ChildTaskStatusRecord : public TaskStatusRecord { + /// FIXME: should this be an array? How are things like task + /// nurseries supposed to actually manage this? Should it be + /// atomically moodifiable? + AsyncTask *FirstChild; + +public: + ChildTaskStatusRecord(AsyncTask *child) + : TaskStatusRecord(TaskStatusRecordKind::ChildTask), + FirstChild(child) {} + + /// Return the first child linked by this record. This may be null; + /// if not, it (and all of its successors) are guaranteed to satisfy + /// `isChildTask()`. + AsyncTask *getFirstChild() const { + return FirstChild; + } + + static AsyncTask *getNextChildTask(AsyncTask *task) { + return task->childFragment()->getNextChild(); + } + + using child_iterator = LinkedListIterator; + llvm::iterator_range children() const { + return child_iterator::rangeBeginning(getFirstChild()); + } + + static bool classof(const TaskStatusRecord *record) { + return record->getKind() == TaskStatusRecordKind::ChildTask; + } +}; + +/// A cancellation record which states that a task has an arbitrary +/// function that needs to be called if the task is cancelled. +/// +/// The end of any call to the function will be ordered before the +/// end of a call to unregister this record from the task. That is, +/// code may call `swift_task_removeTaskStatusRecord` and freely +/// assume after it returns that this function will not be +/// subsequently used. +class CancellationNotificationStatusRecord : public TaskStatusRecord { +public: + using FunctionType = void (void *); + +private: + FunctionType * __ptrauth_swift_cancellation_notification_function Function; + void *Argument; + +public: + CancellationNotificationStatusRecord(FunctionType *fn, void *arg) + : TaskStatusRecord(TaskStatusRecordKind::CancellationNotification), + Function(fn), Argument(arg) {} + + void run() { + Function(Argument); + } + + static bool classof(const TaskStatusRecord *record) { + return record->getKind() == TaskStatusRecordKind::CancellationNotification; + } +}; + +/// A status record which says that a task has an arbitrary +/// function that needs to be called if the task's priority is escalated. +/// +/// The end of any call to the function will be ordered before the +/// end of a call to unregister this record from the task. That is, +/// code may call `swift_task_removeTaskStatusRecord` and freely +/// assume after it returns that this function will not be +/// subsequently used. +class EscalationNotificationStatusRecord : public TaskStatusRecord { +public: + using FunctionType = void (void *, JobPriority); + +private: + FunctionType * __ptrauth_swift_escalation_notification_function Function; + void *Argument; + +public: + EscalationNotificationStatusRecord(FunctionType *fn, void *arg) + : TaskStatusRecord(TaskStatusRecordKind::EscalationNotification), + Function(fn), Argument(arg) {} + + void run(JobPriority newPriority) { + Function(Argument, newPriority); + } + + static bool classof(const TaskStatusRecord *record) { + return record->getKind() == TaskStatusRecordKind::EscalationNotification; + } +}; + +} // end namespace swift + +#endif diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index 70170c014a02b..ae4c051a5a262 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -23,11 +23,12 @@ #include "swift/AST/Import.h" #include "swift/AST/SearchPathOptions.h" #include "swift/AST/Type.h" -#include "swift/AST/Types.h" #include "swift/AST/TypeAlignments.h" +#include "swift/AST/Types.h" #include "swift/Basic/LangOptions.h" #include "swift/Basic/Located.h" #include "swift/Basic/Malloc.h" +#include "clang/AST/DeclTemplate.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" @@ -102,6 +103,7 @@ namespace swift { class InheritedProtocolConformance; class SelfProtocolConformance; class SpecializedProtocolConformance; + class BuiltinProtocolConformance; enum class ProtocolConformanceState; class Pattern; enum PointerTypeKind : unsigned; @@ -531,13 +533,13 @@ class ASTContext final { /// Retrieve the type Swift.Never. CanType getNeverType() const; -#define KNOWN_OBJC_TYPE_DECL(MODULE, NAME, DECL_CLASS) \ +#define KNOWN_SDK_TYPE_DECL(MODULE, NAME, DECL_CLASS, NUM_GENERIC_PARAMS) \ /** Retrieve the declaration of MODULE.NAME. */ \ DECL_CLASS *get##NAME##Decl() const; \ \ /** Retrieve the type of MODULE.NAME. */ \ Type get##NAME##Type() const; -#include "swift/AST/KnownObjCTypes.def" +#include "swift/AST/KnownSDKTypes.def" // Declare accessors for the known declarations. #define FUNC_DECL(Name, Id) \ @@ -660,6 +662,13 @@ class ASTContext final { ArrayRef params, Optional result, SILFunctionType::Representation trueRep); + /// Instantiates "Impl.Converter" if needed, then calls + /// ClangTypeConverter::getClangTemplateArguments. + std::unique_ptr getClangTemplateArguments( + const clang::TemplateParameterList *templateParams, + ArrayRef genericArgs, + SmallVectorImpl &templateArgs); + /// Get the Swift declaration that a Clang declaration was exported from, /// if applicable. const Decl *getSwiftDeclForExportedClangDecl(const clang::Decl *decl); @@ -726,6 +735,9 @@ class ASTContext final { /// generic metadata. AvailabilityContext getIntermodulePrespecializedGenericMetadataAvailability(); + /// Get the runtime availability of support for concurrency. + AvailabilityContext getConcurrencyAvailability(); + /// Get the runtime availability of features introduced in the Swift 5.2 /// compiler for the target platform. AvailabilityContext getSwift52Availability(); @@ -794,6 +806,12 @@ class ASTContext final { bool isClang = false, bool isDWARF = false, bool IsInterface = false); + /// Add a module interface checker to use for this AST context. + void addModuleInterfaceChecker(std::unique_ptr checker); + + /// Retrieve the module interface checker associated with this AST context. + ModuleInterfaceChecker *getModuleInterfaceChecker() const; + /// Retrieve the module dependencies for the module with the given name. /// /// \param isUnderlyingClangModule When true, only look for a Clang module @@ -871,9 +889,6 @@ class ASTContext final { /// If there is no Clang module loader, returns a null pointer. /// The loader is owned by the AST context. ClangModuleLoader *getDWARFModuleLoader() const; - - /// Retrieve the module interface loader for this ASTContext. - ModuleLoader *getModuleInterfaceLoader() const; public: namelookup::ImportCache &getImportCache() const; @@ -966,6 +981,11 @@ class ASTContext final { SelfProtocolConformance * getSelfConformance(ProtocolDecl *protocol); + /// Produce the builtin conformance for some structural type to some protocol. + BuiltinProtocolConformance * + getBuiltinConformance(Type type, ProtocolDecl *protocol, + ArrayRef conformances); + /// A callback used to produce a diagnostic for an ill-formed protocol /// conformance that was type-checked before we're actually walking the /// conformance itself, along with a bit indicating whether this diagnostic diff --git a/include/swift/AST/ASTMangler.h b/include/swift/AST/ASTMangler.h index a62730af7777f..ba9113d5a568e 100644 --- a/include/swift/AST/ASTMangler.h +++ b/include/swift/AST/ASTMangler.h @@ -318,6 +318,9 @@ class ASTMangler : public Mangler { const ValueDecl *forDecl = nullptr); void appendFunctionType(AnyFunctionType *fn, bool isAutoClosure = false, const ValueDecl *forDecl = nullptr); + void appendClangType(AnyFunctionType *fn); + template + void appendClangType(FnType *fn, llvm::raw_svector_ostream &os); void appendFunctionSignature(AnyFunctionType *fn, const ValueDecl *forDecl = nullptr); diff --git a/include/swift/AST/ASTScope.h b/include/swift/AST/ASTScope.h index 8d986f7437036..82fbb09325e30 100644 --- a/include/swift/AST/ASTScope.h +++ b/include/swift/AST/ASTScope.h @@ -29,7 +29,7 @@ #define SWIFT_AST_AST_SCOPE_H #include "swift/AST/ASTNode.h" -#include "swift/AST/NameLookup.h" // for DeclVisibilityKind +#include "swift/AST/NameLookup.h" #include "swift/AST/SimpleRequest.h" #include "swift/Basic/Compiler.h" #include "swift/Basic/Debug.h" @@ -141,7 +141,6 @@ class ASTScopeImpl { ASTScopeImpl *parent = nullptr; // null at the root /// Child scopes, sorted by source range. - /// Must clear source range change whenever this changes Children storedChildren; bool wasExpanded = false; @@ -149,13 +148,7 @@ class ASTScopeImpl { /// Can clear storedChildren, so must remember this bool haveAddedCleanup = false; - // Must be updated after last child is added and after last child's source - // position is known - mutable Optional cachedSourceRange; - - // When ignoring ASTNodes in a scope, they still must count towards a scope's - // source range. So include their ranges here - SourceRange sourceRangeOfIgnoredASTNodes; + mutable Optional cachedCharSourceRange; #pragma mark - constructor / destructor public: @@ -192,9 +185,6 @@ class ASTScopeImpl { public: void addChild(ASTScopeImpl *child, ASTContext &); -private: - NullablePtr getPriorSibling() const; - public: void preOrderDo(function_ref); /// Like preorderDo but without myself. @@ -203,63 +193,11 @@ class ASTScopeImpl { #pragma mark - source ranges -#pragma mark - source range queries - public: - /// Return signum of ranges. Centralize the invariant that ASTScopes use ends. - static int compare(SourceRange, SourceRange, const SourceManager &, - bool ensureDisjoint); - - SourceRange getSourceRangeOfScope(bool omitAssertions = false) const; - - /// InterpolatedStringLiteralExprs and EditorPlaceHolders respond to - /// getSourceRange with the starting point. But we might be asked to lookup an - /// identifer within one of them. So, find the real source range of them here. - SourceRange getEffectiveSourceRange(ASTNode) const; - - void computeAndCacheSourceRangeOfScope(bool omitAssertions = false) const; - bool isSourceRangeCached(bool omitAssertions = false) const; - - bool checkSourceRangeOfThisASTNode() const; - - /// For debugging - bool doesRangeMatch(unsigned start, unsigned end, StringRef file = "", - StringRef className = ""); - - unsigned countDescendants() const; + CharSourceRange getCharSourceRangeOfScope(SourceManager &SM, + bool omitAssertions = false) const; + bool isCharSourceRangeCached() const; - /// Make sure that when the argument is executed, there are as many - /// descendants after as before. - void assertThatTreeDoesNotShrink(function_ref); - -private: - SourceRange computeSourceRangeOfScope(bool omitAssertions = false) const; - SourceRange - computeSourceRangeOfScopeWithChildASTNodes(bool omitAssertions = false) const; - bool ensureNoAncestorsSourceRangeIsCached() const; - -#pragma mark - source range adjustments -private: - SourceRange widenSourceRangeForIgnoredASTNodes(SourceRange range) const; - - /// If the scope refers to a Decl whose source range tells the whole story, - /// for example a NominalTypeScope, it is not necessary to widen the source - /// range by examining the children. In that case we could just return - /// the childlessRange here. - /// But, we have not marked such scopes yet. Doing so would be an - /// optimization. - SourceRange widenSourceRangeForChildren(SourceRange range, - bool omitAssertions) const; - - /// Even ASTNodes that do not form scopes must be included in a Scope's source - /// range. Widen the source range of the receiver to include the (ignored) - /// node. - void widenSourceRangeForIgnoredASTNode(ASTNode); - -private: - void clearCachedSourceRangesOfMeAndAncestors(); - -public: // public for debugging /// Returns source range of this node alone, without factoring in any /// children. virtual SourceRange @@ -267,27 +205,21 @@ class ASTScopeImpl { protected: SourceManager &getSourceManager() const; - bool hasValidSourceRange() const; - bool hasValidSourceRangeOfIgnoredASTNodes() const; - bool precedesInSource(const ASTScopeImpl *) const; - bool verifyThatChildrenAreContainedWithin(SourceRange) const; - bool verifyThatThisNodeComeAfterItsPriorSibling() const; private: - bool checkSourceRangeAfterExpansion(const ASTContext &) const; + void checkSourceRangeBeforeAddingChild(ASTScopeImpl *child, + const ASTContext &ctx) const; #pragma mark common queries public: virtual NullablePtr getClosureIfClosureScope() const; virtual ASTContext &getASTContext() const; - virtual NullablePtr getDeclContext() const; virtual NullablePtr getDeclIfAny() const { return nullptr; }; virtual NullablePtr getStmtIfAny() const { return nullptr; }; virtual NullablePtr getExprIfAny() const { return nullptr; }; virtual NullablePtr getDeclAttributeIfAny() const { return nullptr; } - virtual NullablePtr getReferrent() const { return nullptr; } #pragma mark - debugging and printing @@ -330,22 +262,11 @@ class ASTScopeImpl { void setWasExpanded() { wasExpanded = true; } virtual ASTScopeImpl *expandSpecifically(ScopeCreator &) = 0; -private: - /// Compare the pre-expasion range with the post-expansion range and return - /// false if lazyiness couild miss lookups. - bool checkLazySourceRange(const ASTContext &) const; - public: /// Some scopes can be expanded lazily. - /// Such scopes must: not change their source ranges after expansion, and - /// their expansion must return an insertion point outside themselves. - /// After a node is expanded, its source range (getSourceRangeofThisASTNode - /// union children's ranges) must be same as this. + /// Such scopes must return an insertion point outside themselves when + /// expanded. virtual NullablePtr insertionPointForDeferredExpansion(); - virtual SourceRange sourceRangeForDeferredExpansion() const; - -public: - bool isATypeDeclScope() const; private: virtual ScopeCreator &getScopeCreator(); @@ -357,7 +278,7 @@ class ASTScopeImpl { /// Entry point into ASTScopeImpl-land for lookups static void - unqualifiedLookup(SourceFile *, DeclNameRef, SourceLoc, DeclConsumer); + unqualifiedLookup(SourceFile *, SourceLoc, DeclConsumer); /// Entry point into ASTScopeImpl-land for labeled statement lookups. static llvm::SmallVector @@ -369,7 +290,6 @@ class ASTScopeImpl { #pragma mark - - lookup- starting point private: static const ASTScopeImpl *findStartingScopeForLookup(SourceFile *, - const DeclNameRef name, const SourceLoc where); protected: @@ -435,6 +355,7 @@ class ASTScopeImpl { return p->getParent().isNonNull() ? p : nullptr; } +public: /// The tree is organized by source location and for most nodes this is also /// what obtaines for scoping. However, guards are different. The scope after /// the guard else must hop into the innermoset scope of the guard condition. @@ -448,7 +369,6 @@ class ASTScopeImpl { // It is not an instance variable or inherited type. static bool lookupLocalBindingsInPattern(const Pattern *p, - DeclVisibilityKind vis, DeclConsumer consumer); /// When lookup must stop before the outermost scope, return the scope to stop @@ -477,7 +397,6 @@ class ASTSourceFileScope final : public ASTScopeImpl { public: SourceFile *const SF; ScopeCreator *const scopeCreator; - ASTScopeImpl *insertionPoint; ASTSourceFileScope(SourceFile *SF, ScopeCreator *scopeCreator); @@ -489,8 +408,6 @@ class ASTSourceFileScope final : public ASTScopeImpl { void printSpecifics(llvm::raw_ostream &out) const override; public: - NullablePtr getDeclContext() const override; - void buildFullyExpandedTree(); void buildEnoughOfTreeForTopLevelExpressionsButDontRequestGenericsOrExtendedNominals(); @@ -499,7 +416,8 @@ class ASTSourceFileScope final : public ASTScopeImpl { const SourceFile *getSourceFile() const override; NullablePtr addressForPrinting() const override { return SF; } - bool crossCheckWithAST(); + + ASTContext &getASTContext() const override; protected: ASTScopeImpl *expandSpecifically(ScopeCreator &scopeCreator) override; @@ -546,13 +464,8 @@ class Portion { virtual NullablePtr getLookupLimitFor(const GenericTypeOrExtensionScope *) const; - virtual const Decl * - getReferrentOfScope(const GenericTypeOrExtensionScope *s) const; - virtual NullablePtr insertionPointForDeferredExpansion(IterableTypeScope *) const = 0; - virtual SourceRange - sourceRangeForDeferredExpansion(const IterableTypeScope *) const = 0; }; // For the whole Decl scope of a GenericType or an Extension @@ -571,13 +484,8 @@ class Portion { NullablePtr getLookupLimitFor(const GenericTypeOrExtensionScope *) const override; - const Decl * - getReferrentOfScope(const GenericTypeOrExtensionScope *s) const override; - NullablePtr insertionPointForDeferredExpansion(IterableTypeScope *) const override; - SourceRange - sourceRangeForDeferredExpansion(const IterableTypeScope *) const override; }; /// GenericTypeOrExtension = GenericType or Extension @@ -609,8 +517,6 @@ class GenericTypeOrExtensionWherePortion final NullablePtr insertionPointForDeferredExpansion(IterableTypeScope *) const override; - SourceRange - sourceRangeForDeferredExpansion(const IterableTypeScope *) const override; }; /// Behavior specific to representing the Body of a NominalTypeDecl or @@ -628,8 +534,6 @@ class IterableTypeBodyPortion final NullablePtr insertionPointForDeferredExpansion(IterableTypeScope *) const override; - SourceRange - sourceRangeForDeferredExpansion(const IterableTypeScope *) const override; }; /// GenericType or Extension scope @@ -654,7 +558,6 @@ class GenericTypeOrExtensionScope : public ASTScopeImpl { virtual Decl *getDecl() const = 0; NullablePtr getDeclIfAny() const override { return getDecl(); } - NullablePtr getReferrent() const override; private: AnnotatedInsertionPoint @@ -686,7 +589,6 @@ class GenericTypeOrExtensionScope : public ASTScopeImpl { // Returns the where clause scope, or the parent if none virtual ASTScopeImpl *createTrailingWhereClauseScope(ASTScopeImpl *parent, ScopeCreator &); - NullablePtr getDeclContext() const override; virtual NullablePtr getCorrespondingNominalTypeDecl() const { return nullptr; } @@ -727,7 +629,6 @@ class IterableTypeScope : public GenericTypeScope { public: NullablePtr insertionPointForDeferredExpansion() override; - SourceRange sourceRangeForDeferredExpansion() const override; void countBodies(ScopeCreator &) const; }; @@ -831,8 +732,6 @@ class GenericParamScope final : public ASTScopeImpl { /// Actually holder is always a GenericContext, need to test if /// ProtocolDecl or SubscriptDecl but will refactor later. - NullablePtr getDeclContext() const override; - NullablePtr getReferrent() const override; std::string getClassName() const override; SourceRange getSourceRangeOfThisASTNode(bool omitAssertions = false) const override; @@ -872,13 +771,9 @@ class AbstractFunctionDeclScope final : public ASTScopeImpl { void printSpecifics(llvm::raw_ostream &out) const override; public: - virtual NullablePtr getDeclContext() const override; - virtual NullablePtr getDeclIfAny() const override { return decl; } Decl *getDecl() const { return decl; } - NullablePtr getReferrent() const override; - protected: NullablePtr genericParams() const override; }; @@ -902,13 +797,11 @@ class ParameterListScope final : public ASTScopeImpl { private: void expandAScopeThatDoesNotCreateANewInsertionPoint(ScopeCreator &); - SourceLoc fixupEndForBadInput(SourceRange) const; public: std::string getClassName() const override; SourceRange getSourceRangeOfThisASTNode(bool omitAssertions = false) const override; - virtual NullablePtr getDeclContext() const override; NullablePtr addressForPrinting() const override { return params; } }; @@ -931,9 +824,6 @@ class FunctionBodyScope : public ASTScopeImpl { public: SourceRange getSourceRangeOfThisASTNode(bool omitAssertions = false) const override; - virtual NullablePtr getDeclContext() const override { - return decl; - } virtual NullablePtr getDeclIfAny() const override { return decl; } Decl *getDecl() const { return decl; } @@ -943,7 +833,6 @@ class FunctionBodyScope : public ASTScopeImpl { public: std::string getClassName() const override; NullablePtr insertionPointForDeferredExpansion() override; - SourceRange sourceRangeForDeferredExpansion() const override; }; class DefaultArgumentInitializerScope final : public ASTScopeImpl { @@ -961,7 +850,6 @@ class DefaultArgumentInitializerScope final : public ASTScopeImpl { std::string getClassName() const override; SourceRange getSourceRangeOfThisASTNode(bool omitAssertions = false) const override; - virtual NullablePtr getDeclContext() const override; virtual NullablePtr getDeclIfAny() const override { return decl; } Decl *getDecl() const { return decl; } }; @@ -981,19 +869,8 @@ class AttachedPropertyWrapperScope final : public ASTScopeImpl { CustomAttr *attr; VarDecl *decl; - /// Because we have to avoid request cycles, we approximate the test for an - /// AttachedPropertyWrapper with one based on source location. We might get - /// false positives, that that doesn't hurt anything. However, the result of - /// the conservative source range computation doesn't seem to be stable. So - /// keep the original here, and use it for source range queries. - const SourceRange sourceRangeWhenCreated; - AttachedPropertyWrapperScope(CustomAttr *attr, VarDecl *decl) - : attr(attr), decl(decl), - sourceRangeWhenCreated(attr->getTypeRepr()->getSourceRange()) { - ASTScopeAssert(sourceRangeWhenCreated.isValid(), - "VarDecls must have ranges to be looked-up"); - } + : attr(attr), decl(decl) {} virtual ~AttachedPropertyWrapperScope() {} protected: @@ -1004,12 +881,10 @@ class AttachedPropertyWrapperScope final : public ASTScopeImpl { SourceRange getSourceRangeOfThisASTNode(bool omitAssertions = false) const override; NullablePtr addressForPrinting() const override { return decl; } - virtual NullablePtr getDeclContext() const override; NullablePtr getDeclAttributeIfAny() const override { return attr; } - NullablePtr getReferrent() const override; private: void expandAScopeThatDoesNotCreateANewInsertionPoint(ScopeCreator &); @@ -1039,10 +914,8 @@ class AbstractPatternEntryScope : public ASTScopeImpl { public: PatternBindingDecl *const decl; const unsigned patternEntryIndex; - const DeclVisibilityKind vis; - AbstractPatternEntryScope(PatternBindingDecl *, unsigned entryIndex, - DeclVisibilityKind); + AbstractPatternEntryScope(PatternBindingDecl *, unsigned entryIndex); virtual ~AbstractPatternEntryScope() {} const PatternBindingEntry &getPatternEntry() const; @@ -1057,10 +930,14 @@ class AbstractPatternEntryScope : public ASTScopeImpl { }; class PatternEntryDeclScope final : public AbstractPatternEntryScope { + const bool isLocalBinding; + Optional endLoc; + public: PatternEntryDeclScope(PatternBindingDecl *pbDecl, unsigned entryIndex, - DeclVisibilityKind vis) - : AbstractPatternEntryScope(pbDecl, entryIndex, vis) {} + bool isLocalBinding, Optional endLoc) + : AbstractPatternEntryScope(pbDecl, entryIndex), + isLocalBinding(isLocalBinding), endLoc(endLoc) {} virtual ~PatternEntryDeclScope() {} protected: @@ -1075,24 +952,23 @@ class PatternEntryDeclScope final : public AbstractPatternEntryScope { SourceRange getSourceRangeOfThisASTNode(bool omitAssertions = false) const override; - NullablePtr getReferrent() const override; - protected: bool lookupLocalsOrMembers(DeclConsumer) const override; + bool isLabeledStmtLookupTerminator() const override; }; class PatternEntryInitializerScope final : public AbstractPatternEntryScope { Expr *initAsWrittenWhenCreated; public: - PatternEntryInitializerScope(PatternBindingDecl *pbDecl, unsigned entryIndex, - DeclVisibilityKind vis) - : AbstractPatternEntryScope(pbDecl, entryIndex, vis), + PatternEntryInitializerScope(PatternBindingDecl *pbDecl, unsigned entryIndex) + : AbstractPatternEntryScope(pbDecl, entryIndex), initAsWrittenWhenCreated(pbDecl->getOriginalInit(entryIndex)) {} virtual ~PatternEntryInitializerScope() {} protected: ASTScopeImpl *expandSpecifically(ScopeCreator &scopeCreator) override; + NullablePtr getLookupParent() const override; private: void expandAScopeThatDoesNotCreateANewInsertionPoint(ScopeCreator &); @@ -1101,51 +977,32 @@ class PatternEntryInitializerScope final : public AbstractPatternEntryScope { std::string getClassName() const override; SourceRange getSourceRangeOfThisASTNode(bool omitAssertions = false) const override; - virtual NullablePtr getDeclContext() const override; protected: bool lookupLocalsOrMembers(DeclConsumer) const override; }; -/// The scope introduced by a conditional clause in an if/guard/while -/// statement. -/// Since there may be more than one "let foo = ..." in (e.g.) an "if", -/// we allocate a matrushka of these. -class ConditionalClauseScope final : public ASTScopeImpl { +/// The scope introduced by a conditional clause initializer in an +/// if/while/guard statement. +class ConditionalClauseInitializerScope final : public ASTScopeImpl { public: - LabeledConditionalStmt *const stmt; - const unsigned index; - const SourceLoc endLoc; // cannot get it from the stmt - - ConditionalClauseScope(LabeledConditionalStmt *stmt, unsigned index, - SourceLoc endLoc) - : stmt(stmt), index(index), endLoc(endLoc) {} - - virtual ~ConditionalClauseScope() {} - -protected: - ASTScopeImpl *expandSpecifically(ScopeCreator &scopeCreator) override; - -private: - AnnotatedInsertionPoint - expandAScopeThatCreatesANewInsertionPoint(ScopeCreator &); + Expr *const initializer; + const SourceRange bodyRange; -public: - std::string getClassName() const override; - -protected: - void printSpecifics(llvm::raw_ostream &out) const override; + ConditionalClauseInitializerScope(Expr *initializer) + : initializer(initializer) {} -public: + virtual ~ConditionalClauseInitializerScope() {} SourceRange getSourceRangeOfThisASTNode(bool omitAssertions = false) const override; + std::string getClassName() const override; private: - ArrayRef getCond() const; - const StmtConditionElement &getStmtConditionElement() const; + void expandAScopeThatDoesNotCreateANewInsertionPoint(ScopeCreator &); protected: - bool isLabeledStmtLookupTerminator() const override; + ASTScopeImpl *expandSpecifically(ScopeCreator &scopeCreator) override; + NullablePtr getLookupParent() const override; }; /// If, while, & guard statements all start with a conditional clause, then some @@ -1153,17 +1010,21 @@ class ConditionalClauseScope final : public ASTScopeImpl { /// the normal lookup rule to pass the lookup scope into the deepest conditional /// clause. class ConditionalClausePatternUseScope final : public ASTScopeImpl { - Pattern *const pattern; - const SourceLoc startLoc; + StmtConditionElement sec; + SourceLoc endLoc; public: - ConditionalClausePatternUseScope(Pattern *pattern, SourceLoc startLoc) - : pattern(pattern), startLoc(startLoc) {} + ConditionalClausePatternUseScope(StmtConditionElement sec, SourceLoc endLoc) + : sec(sec), endLoc(endLoc) {} SourceRange getSourceRangeOfThisASTNode(bool omitAssertions = false) const override; std::string getClassName() const override; +private: + AnnotatedInsertionPoint + expandAScopeThatCreatesANewInsertionPoint(ScopeCreator &); + protected: ASTScopeImpl *expandSpecifically(ScopeCreator &) override; bool lookupLocalsOrMembers(DeclConsumer) const override; @@ -1189,10 +1050,8 @@ class CaptureListScope final : public ASTScopeImpl { std::string getClassName() const override; SourceRange getSourceRangeOfThisASTNode(bool omitAssertions = false) const override; - NullablePtr getDeclContext() const override; NullablePtr getExprIfAny() const override { return expr; } Expr *getExpr() const { return expr; } - NullablePtr getReferrent() const override; bool lookupLocalsOrMembers(DeclConsumer) const override; }; @@ -1212,12 +1071,8 @@ class ClosureParametersScope final : public ASTScopeImpl { NullablePtr getClosureIfClosureScope() const override { return closureExpr; } - NullablePtr getDeclContext() const override { - return closureExpr; - } NullablePtr getExprIfAny() const override { return closureExpr; } Expr *getExpr() const { return closureExpr; } - NullablePtr getReferrent() const override; protected: ASTScopeImpl *expandSpecifically(ScopeCreator &scopeCreator) override; @@ -1232,8 +1087,10 @@ class ClosureParametersScope final : public ASTScopeImpl { class TopLevelCodeScope final : public ASTScopeImpl { public: TopLevelCodeDecl *const decl; + SourceLoc endLoc; - TopLevelCodeScope(TopLevelCodeDecl *e) : decl(e) {} + TopLevelCodeScope(TopLevelCodeDecl *e, SourceLoc endLoc) + : decl(e), endLoc(endLoc) {} virtual ~TopLevelCodeScope() {} protected: @@ -1247,12 +1104,8 @@ class TopLevelCodeScope final : public ASTScopeImpl { std::string getClassName() const override; SourceRange getSourceRangeOfThisASTNode(bool omitAssertions = false) const override; - virtual NullablePtr getDeclContext() const override { - return decl; - } virtual NullablePtr getDeclIfAny() const override { return decl; } Decl *getDecl() const { return decl; } - NullablePtr getReferrent() const override; }; /// The \c _@specialize attribute. @@ -1277,7 +1130,6 @@ class SpecializeAttributeScope final : public ASTScopeImpl { NullablePtr getDeclAttributeIfAny() const override { return specializeAttr; } - NullablePtr getReferrent() const override; protected: ASTScopeImpl *expandSpecifically(ScopeCreator &) override; @@ -1307,7 +1159,6 @@ class DifferentiableAttributeScope final : public ASTScopeImpl { NullablePtr getDeclAttributeIfAny() const override { return differentiableAttr; } - NullablePtr getReferrent() const override; protected: ASTScopeImpl *expandSpecifically(ScopeCreator &) override; @@ -1336,12 +1187,8 @@ class SubscriptDeclScope final : public ASTScopeImpl { void printSpecifics(llvm::raw_ostream &out) const override; public: - virtual NullablePtr getDeclContext() const override { - return decl; - } virtual NullablePtr getDeclIfAny() const override { return decl; } Decl *getDecl() const { return decl; } - NullablePtr getReferrent() const override; protected: NullablePtr genericParams() const override; @@ -1358,7 +1205,6 @@ class EnumElementScope : public ASTScopeImpl { std::string getClassName() const override; ASTScopeImpl *expandSpecifically(ScopeCreator &) override; - NullablePtr getDeclContext() const override { return decl; } NullablePtr getDeclIfAny() const override { return decl; } Decl *getDecl() const { return decl; } @@ -1372,7 +1218,6 @@ class AbstractStmtScope : public ASTScopeImpl { getSourceRangeOfThisASTNode(bool omitAssertions = false) const override; virtual Stmt *getStmt() const = 0; NullablePtr getStmtIfAny() const override { return getStmt(); } - NullablePtr getReferrent() const override; protected: bool isLabeledStmtLookupTerminator() const override; @@ -1386,7 +1231,7 @@ class LabeledConditionalStmtScope : public AbstractStmtScope { protected: /// Return the lookupParent required to search these. ASTScopeImpl *createNestedConditionalClauseScopes(ScopeCreator &, - const Stmt *afterConds); + SourceLoc); }; class IfStmtScope final : public LabeledConditionalStmtScope { @@ -1426,7 +1271,8 @@ class WhileStmtScope final : public LabeledConditionalStmtScope { class GuardStmtScope final : public LabeledConditionalStmtScope { public: GuardStmt *const stmt; - GuardStmtScope(GuardStmt *e) : stmt(e) {} + SourceLoc endLoc; + GuardStmtScope(GuardStmt *e, SourceLoc endLoc) : stmt(e), endLoc(endLoc) {} virtual ~GuardStmtScope() {} protected: @@ -1439,28 +1285,28 @@ class GuardStmtScope final : public LabeledConditionalStmtScope { public: std::string getClassName() const override; LabeledConditionalStmt *getLabeledConditionalStmt() const override; + SourceRange + getSourceRangeOfThisASTNode(bool omitAssertions = false) const override; }; -/// A scope after a guard statement that follows lookups into the conditions -/// Also for: -/// The insertion point of the last statement of an active clause in an #if -/// must be the lookup parent -/// of any following scopes. But the active clause may not be the last clause. -/// In short, this is another case where the lookup parent cannot follow the same -/// nesting as the source order. IfConfigUseScope implements this twist. It -/// follows the IfConfig, wraps all subsequent scopes, and redirects the lookup. -class LookupParentDiversionScope final : public ASTScopeImpl { +/// A scope for the body of a guard statement. Lookups from the body must +/// skip the parent scopes for introducing pattern bindings, since they're +/// not visible in the guard body, only after the body ends. +class GuardStmtBodyScope final : public ASTScopeImpl { public: ASTScopeImpl *const lookupParent; - const SourceLoc startLoc; + BraceStmt *const body; - LookupParentDiversionScope(ASTScopeImpl *lookupParent, SourceLoc startLoc) - : lookupParent(lookupParent), startLoc(startLoc) {} + GuardStmtBodyScope(ASTScopeImpl *lookupParent, BraceStmt *body) + : lookupParent(lookupParent), body(body) {} SourceRange getSourceRangeOfThisASTNode(bool omitAssertions = false) const override; std::string getClassName() const override; +private: + void expandAScopeThatDoesNotCreateANewInsertionPoint(ScopeCreator &); + protected: ASTScopeImpl *expandSpecifically(ScopeCreator &) override; NullablePtr getLookupParent() const override { @@ -1683,10 +1529,30 @@ class CaseStmtBodyScope final : public ASTScopeImpl { }; class BraceStmtScope final : public AbstractStmtScope { + BraceStmt *const stmt; + + /// Declarations which are in scope from the beginning of the statement. + ArrayRef localFuncsAndTypes; + + /// Declarations that are normally in scope only after their + /// definition. + ArrayRef localVars; + + /// The end location for bindings introduced in this scope. This can + /// extend past the actual end of the BraceStmt in top-level code, + /// where every TopLevelCodeDecl introduces a new scope through the + /// end of the buffer. + SourceLoc endLoc; public: - BraceStmt *const stmt; - BraceStmtScope(BraceStmt *e) : stmt(e) {} + BraceStmtScope(BraceStmt *e, + ArrayRef localFuncsAndTypes, + ArrayRef localVars, + SourceLoc endLoc) + : stmt(e), + localFuncsAndTypes(localFuncsAndTypes), + localVars(localVars), + endLoc(endLoc) {} virtual ~BraceStmtScope() {} protected: @@ -1700,7 +1566,6 @@ class BraceStmtScope final : public AbstractStmtScope { std::string getClassName() const override; SourceRange getSourceRangeOfThisASTNode(bool omitAssertions = false) const override; - virtual NullablePtr getDeclContext() const override; NullablePtr parentClosureIfAny() const; // public?? Stmt *getStmt() const override { return stmt; } diff --git a/include/swift/AST/ASTTypeIDZone.def b/include/swift/AST/ASTTypeIDZone.def index 0bffc3a56697b..ab542436be917 100644 --- a/include/swift/AST/ASTTypeIDZone.def +++ b/include/swift/AST/ASTTypeIDZone.def @@ -17,10 +17,12 @@ SWIFT_TYPEID(ActorIsolation) SWIFT_TYPEID(AncestryFlags) +SWIFT_TYPEID(BodyInitKind) +SWIFT_TYPEID(BodyInitKindAndExpr) SWIFT_TYPEID(CtorInitializerKind) -SWIFT_TYPEID(FunctionBuilderBodyPreCheck) +SWIFT_TYPEID(ResultBuilderBodyPreCheck) SWIFT_TYPEID(GenericSignature) -SWIFT_TYPEID(ImplicitImport) +SWIFT_TYPEID(ImplicitImportList) SWIFT_TYPEID(ImplicitMemberAction) SWIFT_TYPEID(ParamSpecifier) SWIFT_TYPEID(PropertyWrapperBackingPropertyInfo) @@ -35,6 +37,7 @@ SWIFT_TYPEID(TypePair) SWIFT_TYPEID(TypeWitnessAndDecl) SWIFT_TYPEID(Witness) SWIFT_TYPEID_NAMED(AbstractFunctionDecl *, AbstractFunctionDecl) +SWIFT_TYPEID_NAMED(ApplyExpr *, ApplyExpr) SWIFT_TYPEID_NAMED(ClosureExpr *, ClosureExpr) SWIFT_TYPEID_NAMED(CodeCompletionCallbacksFactory *, CodeCompletionCallbacksFactory) diff --git a/include/swift/AST/ASTTypeIDs.h b/include/swift/AST/ASTTypeIDs.h index e59255ec7ec28..76bc732b975d1 100644 --- a/include/swift/AST/ASTTypeIDs.h +++ b/include/swift/AST/ASTTypeIDs.h @@ -24,6 +24,9 @@ namespace swift { class AbstractFunctionDecl; class ActorIsolation; +class ApplyExpr; +enum class BodyInitKind; +struct BodyInitKindAndExpr; class BraceStmt; class ClosureExpr; class CodeCompletionCallbacksFactory; @@ -32,14 +35,14 @@ class CustomAttr; class Decl; class EnumDecl; class FuncDecl; -enum class FunctionBuilderBodyPreCheck : uint8_t; +enum class ResultBuilderBodyPreCheck : uint8_t; class GenericParamList; class GenericSignature; class GenericTypeParamType; class InfixOperatorDecl; class IterableDeclContext; class ModuleDecl; -struct ImplicitImport; +struct ImplicitImportList; class NamedPattern; class NominalTypeDecl; class OperatorDecl; diff --git a/include/swift/AST/AbstractSourceFileDepGraphFactory.h b/include/swift/AST/AbstractSourceFileDepGraphFactory.h index 80ed4122ec1b2..10e6a6e9f9aa0 100644 --- a/include/swift/AST/AbstractSourceFileDepGraphFactory.h +++ b/include/swift/AST/AbstractSourceFileDepGraphFactory.h @@ -13,6 +13,7 @@ #ifndef SWIFT_AST_SOURCE_FILE_DEP_GRAPH_CONSTRUCTOR_H #define SWIFT_AST_SOURCE_FILE_DEP_GRAPH_CONSTRUCTOR_H +#include "swift/AST/Decl.h" #include "swift/AST/DeclContext.h" #include "swift/AST/FineGrainedDependencies.h" @@ -24,10 +25,6 @@ namespace fine_grained_dependencies { /// \c SourceFile or a unit test class AbstractSourceFileDepGraphFactory { protected: - /// To match the existing system, set this to false. - /// To include even private entities and get intra-file info, set to true. - const bool includePrivateDeps; - /// If there was an error, cannot get accurate info. const bool hadCompilationError; @@ -48,8 +45,7 @@ class AbstractSourceFileDepGraphFactory { public: /// Expose this layer to enable faking up a constructor for testing. /// See the instance variable comments for explanation. - AbstractSourceFileDepGraphFactory(bool includePrivateDeps, - bool hadCompilationError, + AbstractSourceFileDepGraphFactory(bool hadCompilationError, StringRef swiftDeps, StringRef fileFingerprint, bool emitDotFileAfterConstruction, @@ -77,6 +73,21 @@ class AbstractSourceFileDepGraphFactory { Optional fingerprint); void addAUsedDecl(const DependencyKey &def, const DependencyKey &use); + + static Optional getFingerprintIfAny( + std::pair) { + return None; + } + + static Optional getFingerprintIfAny(const Decl *d) { + if (const auto *idc = dyn_cast(d)) { + auto result = idc->getBodyFingerprint(); + assert((!result || !result->empty()) && + "Fingerprint should never be empty"); + return result; + } + return None; + } }; } // namespace fine_grained_dependencies diff --git a/include/swift/AST/ActorIsolation.h b/include/swift/AST/ActorIsolation.h index 2de16ff3fe547..f819345615a60 100644 --- a/include/swift/AST/ActorIsolation.h +++ b/include/swift/AST/ActorIsolation.h @@ -16,6 +16,7 @@ #ifndef SWIFT_AST_ACTORISOLATIONSTATE_H #define SWIFT_AST_ACTORISOLATIONSTATE_H +#include "swift/AST/Type.h" #include "llvm/ADT/Hashing.h" namespace llvm { @@ -24,6 +25,12 @@ class raw_ostream; namespace swift { class ClassDecl; +class SubstitutionMap; +class Type; + +/// Determine whether the given types are (canonically) equal, declared here +/// to avoid having to include Types.h. +bool areTypesEqual(Type type1, Type type2); /// Describes the actor isolation of a given declaration, which determines /// the actors with which it can interact. @@ -37,47 +44,82 @@ class ActorIsolation { /// For example, a mutable stored property or synchronous function within /// the actor is isolated to the instance of that actor. ActorInstance, - /// The declaration can refer to actor-isolated state, but can also be - //// referenced from outside the actor. - ActorPrivileged, /// The declaration is explicitly specified to be independent of any actor, /// meaning that it can be used from any actor but is also unable to /// refer to the isolated state of any given actor. Independent, + /// The declaration is explicitly specified to be independent of any actor, + /// but the programmer promises to protect the declaration from concurrent + /// accesses manually. Thus, it is okay if this declaration is a mutable + /// variable that creates storage. + IndependentUnsafe, + /// The declaration is isolated to a global actor. It can refer to other + /// entities with the same global actor. + GlobalActor, }; private: Kind kind; - ClassDecl *actor; + union { + ClassDecl *actor; + Type globalActor; + void *pointer; + }; ActorIsolation(Kind kind, ClassDecl *actor) : kind(kind), actor(actor) { } + ActorIsolation(Kind kind, Type globalActor) + : kind(kind), globalActor(globalActor) { } public: static ActorIsolation forUnspecified() { return ActorIsolation(Unspecified, nullptr); } - static ActorIsolation forIndependent() { - return ActorIsolation(Independent, nullptr); - } - - static ActorIsolation forActorPrivileged(ClassDecl *actor) { - return ActorIsolation(ActorPrivileged, actor); + static ActorIsolation forIndependent(ActorIndependentKind indepKind) { + ActorIsolation::Kind isoKind; + switch (indepKind) { + case ActorIndependentKind::Safe: + isoKind = Independent; + break; + + case ActorIndependentKind::Unsafe: + isoKind = IndependentUnsafe; + break; + } + return ActorIsolation(isoKind, nullptr); } static ActorIsolation forActorInstance(ClassDecl *actor) { return ActorIsolation(ActorInstance, actor); } + static ActorIsolation forGlobalActor(Type globalActor) { + return ActorIsolation(GlobalActor, globalActor); + } + Kind getKind() const { return kind; } operator Kind() const { return getKind(); } + bool isUnspecified() const { return kind == Unspecified; } + ClassDecl *getActor() const { - assert(getKind() == ActorInstance || getKind() == ActorPrivileged); + assert(getKind() == ActorInstance); return actor; } + Type getGlobalActor() const { + assert(getKind() == GlobalActor); + return globalActor; + } + + /// Determine whether this isolation will require substitution to be + /// evaluated. + bool requiresSubstitution() const; + + /// Substitute into types within the actor isolation. + ActorIsolation subst(SubstitutionMap subs) const; + friend bool operator==(const ActorIsolation &lhs, const ActorIsolation &rhs) { if (lhs.kind != rhs.kind) @@ -85,12 +127,15 @@ class ActorIsolation { switch (lhs.kind) { case Independent: + case IndependentUnsafe: case Unspecified: return true; case ActorInstance: - case ActorPrivileged: return lhs.actor == rhs.actor; + + case GlobalActor: + return areTypesEqual(lhs.globalActor, rhs.globalActor); } } @@ -100,7 +145,7 @@ class ActorIsolation { } friend llvm::hash_code hash_value(const ActorIsolation &state) { - return llvm::hash_combine(state.kind, state.actor); + return llvm::hash_combine(state.kind, state.pointer); } }; diff --git a/include/swift/AST/Attr.def b/include/swift/AST/Attr.def index 748dfaf6c7832..89ab84b014486 100644 --- a/include/swift/AST/Attr.def +++ b/include/swift/AST/Attr.def @@ -120,8 +120,7 @@ DECL_ATTR(_silgen_name, SILGenName, 0) DECL_ATTR(available, Available, OnAbstractFunction | OnGenericType | OnVar | OnSubscript | OnEnumElement | - OnExtension | OnGenericTypeParam | - AllowMultipleAttributes | LongAttribute | + OnExtension | AllowMultipleAttributes | LongAttribute | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, 1) CONTEXTUAL_SIMPLE_DECL_ATTR(final, Final, @@ -494,8 +493,8 @@ SIMPLE_DECL_ATTR(_disfavoredOverload, DisfavoredOverload, OnAbstractFunction | OnVar | OnSubscript | UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, 87) -SIMPLE_DECL_ATTR(_functionBuilder, FunctionBuilder, - OnNominalType | UserInaccessible | +SIMPLE_DECL_ATTR(resultBuilder, ResultBuilder, + OnNominalType | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, 88) DECL_ATTR(_projectedValueProperty, ProjectedValueProperty, @@ -567,22 +566,33 @@ SIMPLE_DECL_ATTR(asyncHandler, AsyncHandler, 101) CONTEXTUAL_SIMPLE_DECL_ATTR(actor, Actor, - OnClass | ConcurrencyOnly | + DeclModifier | OnClass | ConcurrencyOnly | ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove, 102) -SIMPLE_DECL_ATTR(actorIndependent, ActorIndependent, +DECL_ATTR(actorIndependent, ActorIndependent, OnFunc | OnVar | OnSubscript | ConcurrencyOnly | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIBreakingToRemove, 103) +SIMPLE_DECL_ATTR(globalActor, GlobalActor, + OnClass | OnStruct | OnEnum | ConcurrencyOnly | + ABIStableToAdd | ABIBreakingToRemove | + APIStableToAdd | APIBreakingToRemove, + 104) + +SIMPLE_DECL_ATTR(_specializeExtension, SpecializeExtension, + OnExtension | UserInaccessible | + ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + 105) + // SWIFT_ENABLE_TENSORFLOW SIMPLE_DECL_ATTR(compilerEvaluable, CompilerEvaluable, OnAccessor | OnFunc | OnConstructor | OnSubscript | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | - NotSerialized, 104) + NotSerialized, 106) // SWIFT_ENABLE_TENSORFLOW END #undef TYPE_ATTR diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index 1c271d9c8e514..c283f8ba805b4 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -291,6 +291,10 @@ class DeclAttribute : public AttributeBase { kind : NumInlineKindBits ); + SWIFT_INLINE_BITFIELD(ActorIndependentAttr, DeclAttribute, NumActorIndependentKindBits, + kind : NumActorIndependentKindBits + ); + SWIFT_INLINE_BITFIELD(OptimizeAttr, DeclAttribute, NumOptimizationModeBits, mode : NumOptimizationModeBits ); @@ -1330,6 +1334,25 @@ class ReferenceOwnershipAttr : public DeclAttribute { } }; +/// Represents an actorIndependent/actorIndependent(unsafe) decl attribute. +class ActorIndependentAttr : public DeclAttribute { +public: + ActorIndependentAttr(SourceLoc atLoc, SourceRange range, ActorIndependentKind kind) + : DeclAttribute(DAK_ActorIndependent, atLoc, range, /*Implicit=*/false) { + Bits.ActorIndependentAttr.kind = unsigned(kind); + } + + ActorIndependentAttr(ActorIndependentKind kind, bool IsImplicit=false) + : ActorIndependentAttr(SourceLoc(), SourceRange(), kind) { + setImplicit(IsImplicit); + } + + ActorIndependentKind getKind() const { return ActorIndependentKind(Bits.ActorIndependentAttr.kind); } + static bool classof(const DeclAttribute *DA) { + return DA->getKind() == DAK_ActorIndependent; + } +}; + /// Defines the attribute that we use to model documentation comments. class RawDocCommentAttr : public DeclAttribute { /// Source range of the attached comment. This comment is located before @@ -1409,7 +1432,12 @@ class SynthesizedProtocolAttr : public DeclAttribute { /// The @_specialize attribute, which forces specialization on the specified /// type list. -class SpecializeAttr : public DeclAttribute { +class SpecializeAttr final + : public DeclAttribute, + private llvm::TrailingObjects { + friend class SpecializeAttrTargetDeclRequest; + friend TrailingObjects; + public: // NOTE: When adding new kinds, you must update the inline bitfield macro. enum class SpecializationKind { @@ -1421,18 +1449,48 @@ class SpecializeAttr : public DeclAttribute { TrailingWhereClause *trailingWhereClause; GenericSignature specializedSignature; + DeclNameRef targetFunctionName; + LazyMemberLoader *resolver = nullptr; + uint64_t resolverContextData; + size_t numSPIGroups; + SpecializeAttr(SourceLoc atLoc, SourceRange Range, TrailingWhereClause *clause, bool exported, - SpecializationKind kind, - GenericSignature specializedSignature); + SpecializationKind kind, GenericSignature specializedSignature, + DeclNameRef targetFunctionName, + ArrayRef spiGroups); public: static SpecializeAttr *create(ASTContext &Ctx, SourceLoc atLoc, SourceRange Range, TrailingWhereClause *clause, bool exported, SpecializationKind kind, + DeclNameRef targetFunctionName, + ArrayRef spiGroups, GenericSignature specializedSignature = nullptr); + static SpecializeAttr *create(ASTContext &ctx, bool exported, + SpecializationKind kind, + ArrayRef spiGroups, + GenericSignature specializedSignature, + DeclNameRef replacedFunction); + + static SpecializeAttr *create(ASTContext &ctx, bool exported, + SpecializationKind kind, + ArrayRef spiGroups, + GenericSignature specializedSignature, + DeclNameRef replacedFunction, + LazyMemberLoader *resolver, uint64_t data); + + /// Name of SPIs declared by the attribute. + /// + /// Note: A single SPI name per attribute is currently supported but this + /// may change with the syntax change. + ArrayRef getSPIGroups() const { + return { this->template getTrailingObjects(), + numSPIGroups }; + } + TrailingWhereClause *getTrailingWhereClause() const; GenericSignature getSpecializedSignature() const { @@ -1459,6 +1517,13 @@ class SpecializeAttr : public DeclAttribute { return getSpecializationKind() == SpecializationKind::Partial; } + DeclNameRef getTargetFunctionName() const { + return targetFunctionName; + } + + /// \p forDecl is the value decl that the attribute belongs to. + ValueDecl *getTargetFunctionDecl(const ValueDecl *forDecl) const; + static bool classof(const DeclAttribute *DA) { return DA->getKind() == DAK_Specialize; } diff --git a/include/swift/AST/AttrKind.h b/include/swift/AST/AttrKind.h index be74f2a3b472e..7ca1a8153fdd0 100644 --- a/include/swift/AST/AttrKind.h +++ b/include/swift/AST/AttrKind.h @@ -79,6 +79,17 @@ enum class InlineKind : uint8_t { enum : unsigned { NumInlineKindBits = countBitsUsed(static_cast(InlineKind::Last_InlineKind)) }; + +/// Indicates whether an actorIndependent decl is unsafe or not +enum class ActorIndependentKind : uint8_t { + Safe = 0, + Unsafe = 1, + Last_InlineKind = Unsafe +}; + +enum : unsigned { NumActorIndependentKindBits = + countBitsUsed(static_cast(ActorIndependentKind::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. diff --git a/include/swift/AST/AvailabilitySpec.h b/include/swift/AST/AvailabilitySpec.h index 86b0342419a1a..46197b01259d3 100644 --- a/include/swift/AST/AvailabilitySpec.h +++ b/include/swift/AST/AvailabilitySpec.h @@ -85,6 +85,9 @@ class PlatformVersionConstraintAvailabilitySpec : public AvailabilitySpec { SourceRange VersionSrcRange; + // Location of the macro expanded to create this spec. + SourceLoc MacroLoc; + public: PlatformVersionConstraintAvailabilitySpec(PlatformKind Platform, SourceLoc PlatformLoc, @@ -117,6 +120,10 @@ class PlatformVersionConstraintAvailabilitySpec : public AvailabilitySpec { SourceRange getSourceRange() const; + // Location of the macro expanded to create this spec. + SourceLoc getMacroLoc() const { return MacroLoc; } + void setMacroLoc(SourceLoc loc) { MacroLoc = loc; } + void print(raw_ostream &OS, unsigned Indent) const; static bool classof(const AvailabilitySpec *Spec) { diff --git a/include/swift/AST/ClangModuleLoader.h b/include/swift/AST/ClangModuleLoader.h index 06c41a3d8e61e..b02bac076b25a 100644 --- a/include/swift/AST/ClangModuleLoader.h +++ b/include/swift/AST/ClangModuleLoader.h @@ -14,7 +14,9 @@ #define SWIFT_AST_CLANG_MODULE_LOADER_H #include "swift/AST/ModuleLoader.h" +#include "swift/AST/SubstitutionMap.h" #include "swift/Basic/TaggedUnion.h" +#include "clang/AST/DeclTemplate.h" namespace clang { class ASTContext; @@ -219,6 +221,18 @@ class ClangModuleLoader : public ModuleLoader { /// based on subtleties like the target module interface format. virtual bool isSerializable(const clang::Type *type, bool checkCanonical) const = 0; + + virtual clang::FunctionDecl * + instantiateCXXFunctionTemplate(ASTContext &ctx, + clang::FunctionTemplateDecl *func, + SubstitutionMap subst) = 0; +}; + +/// Used to describe a template instantiation error. +struct TemplateInstantiationError { + /// Generic types that could not be converted to QualTypes using the + /// ClangTypeConverter. + SmallVector failedTypes; }; } // namespace swift diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index ed0b7193270c2..aaa823f26923d 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -70,6 +70,7 @@ namespace swift { class BraceStmt; class DeclAttributes; class GenericContext; + class GenericParamList; class GenericSignature; class GenericTypeParamDecl; class GenericTypeParamType; @@ -88,6 +89,7 @@ namespace swift { class ProtocolType; struct RawComment; enum class ResilienceExpansion : unsigned; + class TrailingWhereClause; class TypeAliasDecl; class Stmt; class SubscriptDecl; @@ -451,14 +453,7 @@ class alignas(1 << DeclAlignInBits) Decl { /// Whether we have computed the above. IsTransparentComputed : 1); - SWIFT_INLINE_BITFIELD(ConstructorDecl, AbstractFunctionDecl, 3+1+1, - /// The body initialization kind (+1), or zero if not yet computed. - /// - /// This value is cached but is not serialized, because it is a property - /// of the definition of the constructor that is useful only to semantic - /// analysis and SIL generation. - ComputedBodyInitKind : 3, - + SWIFT_INLINE_BITFIELD(ConstructorDecl, AbstractFunctionDecl, 1+1, /// Whether this constructor can fail, by building an Optional type. Failable : 1, @@ -578,7 +573,7 @@ class alignas(1 << DeclAlignInBits) Decl { HasAnyUnavailableValues : 1 ); - SWIFT_INLINE_BITFIELD(ModuleDecl, TypeDecl, 1+1+1+1+1+1+1+1+1, + SWIFT_INLINE_BITFIELD(ModuleDecl, TypeDecl, 1+1+1+1+1+1+1+1+1+1, /// If the module was or is being compiled with `-enable-testing`. TestingEnabled : 1, @@ -607,7 +602,10 @@ class alignas(1 << DeclAlignInBits) Decl { IsNonSwiftModule : 1, /// Whether this module is the main module. - IsMainModule : 1 + IsMainModule : 1, + + /// Whether this module has incremental dependency information available. + HasIncrementalInfo : 1 ); SWIFT_INLINE_BITFIELD(PrecedenceGroupDecl, Decl, 1+2, @@ -952,8 +950,14 @@ class alignas(1 << DeclAlignInBits) Decl { /// If this returns true, the decl can be safely casted to ValueDecl. bool isPotentiallyOverridable() const; - /// Returns true if this Decl cannot be seen by any other source file - bool isPrivateToEnclosingFile() const; + /// Retrieve the global actor attribute that applies to this declaration, + /// if any. + /// + /// This is the "raw" global actor attribute as written directly on the + /// declaration, along with the nominal type declaration to which it refers, + /// without any inference rules applied. + Optional> + getGlobalActorAttr() const; /// If an alternative module name is specified for this decl, e.g. using /// @_originalDefinedIn attribute, this function returns this module name. @@ -1015,364 +1019,6 @@ void *allocateMemoryForDecl(AllocatorTy &allocator, size_t baseSize, return mem; } -enum class RequirementReprKind : unsigned { - /// A type bound T : P, where T is a type that depends on a generic - /// parameter and P is some type that should bound T, either as a concrete - /// supertype or a protocol to which T must conform. - TypeConstraint, - - /// A same-type requirement T == U, where T and U are types that shall be - /// equivalent. - SameType, - - /// A layout bound T : L, where T is a type that depends on a generic - /// parameter and L is some layout specification that should bound T. - LayoutConstraint, - - // Note: there is code that packs this enum in a 2-bit bitfield. Audit users - // when adding enumerators. -}; - -/// A single requirement in a 'where' clause, which places additional -/// restrictions on the generic parameters or associated types of a generic -/// function, type, or protocol. -/// -/// This always represents a requirement spelled in the source code. It is -/// never generated implicitly. -/// -/// \c GenericParamList assumes these are POD-like. -class RequirementRepr { - SourceLoc SeparatorLoc; - RequirementReprKind Kind : 2; - bool Invalid : 1; - TypeRepr *FirstType; - - /// The second element represents the right-hand side of the constraint. - /// It can be e.g. a type or a layout constraint. - union { - TypeRepr *SecondType; - LayoutConstraintLoc SecondLayout; - }; - - /// Set during deserialization; used to print out the requirements accurately - /// for the generated interface. - StringRef AsWrittenString; - - RequirementRepr(SourceLoc SeparatorLoc, RequirementReprKind Kind, - TypeRepr *FirstType, TypeRepr *SecondType) - : SeparatorLoc(SeparatorLoc), Kind(Kind), Invalid(false), - FirstType(FirstType), SecondType(SecondType) { } - - RequirementRepr(SourceLoc SeparatorLoc, RequirementReprKind Kind, - TypeRepr *FirstType, LayoutConstraintLoc SecondLayout) - : SeparatorLoc(SeparatorLoc), Kind(Kind), Invalid(false), - FirstType(FirstType), SecondLayout(SecondLayout) { } - - void printImpl(ASTPrinter &OS) const; - -public: - /// Construct a new type-constraint requirement. - /// - /// \param Subject The type that must conform to the given protocol or - /// composition, or be a subclass of the given class type. - /// \param ColonLoc The location of the ':', or an invalid location if - /// this requirement was implied. - /// \param Constraint The protocol or protocol composition to which the - /// subject must conform, or superclass from which the subject must inherit. - static RequirementRepr getTypeConstraint(TypeRepr *Subject, - SourceLoc ColonLoc, - TypeRepr *Constraint) { - return { ColonLoc, RequirementReprKind::TypeConstraint, Subject, Constraint }; - } - - /// Construct a new same-type requirement. - /// - /// \param FirstType The first type. - /// \param EqualLoc The location of the '==' in the same-type constraint, or - /// an invalid location if this requirement was implied. - /// \param SecondType The second type. - static RequirementRepr getSameType(TypeRepr *FirstType, - SourceLoc EqualLoc, - TypeRepr *SecondType) { - return { EqualLoc, RequirementReprKind::SameType, FirstType, SecondType }; - } - - /// Construct a new layout-constraint requirement. - /// - /// \param Subject The type that must conform to the given layout - /// requirement. - /// \param ColonLoc The location of the ':', or an invalid location if - /// this requirement was implied. - /// \param Layout The layout requirement to which the - /// subject must conform. - static RequirementRepr getLayoutConstraint(TypeRepr *Subject, - SourceLoc ColonLoc, - LayoutConstraintLoc Layout) { - return {ColonLoc, RequirementReprKind::LayoutConstraint, Subject, - Layout}; - } - - /// Determine the kind of requirement - RequirementReprKind getKind() const { return Kind; } - - /// Determine whether this requirement is invalid. - bool isInvalid() const { return Invalid; } - - /// Mark this requirement invalid. - void setInvalid() { Invalid = true; } - - /// For a type-bound requirement, return the subject of the - /// conformance relationship. - TypeRepr *getSubjectRepr() const { - assert(getKind() == RequirementReprKind::TypeConstraint || - getKind() == RequirementReprKind::LayoutConstraint); - return FirstType; - } - - /// For a type-bound requirement, return the protocol or to which - /// the subject conforms or superclass it inherits. - TypeRepr *getConstraintRepr() const { - assert(getKind() == RequirementReprKind::TypeConstraint); - return SecondType; - } - - LayoutConstraint getLayoutConstraint() const { - assert(getKind() == RequirementReprKind::LayoutConstraint); - return SecondLayout.getLayoutConstraint(); - } - - LayoutConstraintLoc &getLayoutConstraintLoc() { - assert(getKind() == RequirementReprKind::LayoutConstraint); - return SecondLayout; - } - - const LayoutConstraintLoc &getLayoutConstraintLoc() const { - assert(getKind() == RequirementReprKind::LayoutConstraint); - return SecondLayout; - } - - /// Retrieve the first type of a same-type requirement. - TypeRepr *getFirstTypeRepr() const { - assert(getKind() == RequirementReprKind::SameType); - return FirstType; - } - - /// Retrieve the second type of a same-type requirement. - TypeRepr *getSecondTypeRepr() const { - assert(getKind() == RequirementReprKind::SameType); - return SecondType; - } - - /// Retrieve the location of the ':' or '==' in an explicitly-written - /// conformance or same-type requirement respectively. - SourceLoc getSeparatorLoc() const { - return SeparatorLoc; - } - - SourceRange getSourceRange() const; - - /// Retrieve the first or subject type representation from the \c repr, - /// or \c nullptr if \c repr is null. - static TypeRepr *getFirstTypeRepr(const RequirementRepr *repr) { - if (!repr) return nullptr; - return repr->FirstType; - } - - /// Retrieve the second or constraint type representation from the \c repr, - /// or \c nullptr if \c repr is null. - static TypeRepr *getSecondTypeRepr(const RequirementRepr *repr) { - if (!repr) return nullptr; - assert(repr->getKind() == RequirementReprKind::TypeConstraint || - repr->getKind() == RequirementReprKind::SameType); - return repr->SecondType; - } - - SWIFT_DEBUG_DUMP; - void print(raw_ostream &OS) const; - void print(ASTPrinter &Printer) const; -}; - -using GenericParamSource = PointerUnion; - -/// GenericParamList - A list of generic parameters that is part of a generic -/// function or type, along with extra requirements placed on those generic -/// parameters and types derived from them. -class GenericParamList final : - private llvm::TrailingObjects { - friend TrailingObjects; - - SourceRange Brackets; - unsigned NumParams; - SourceLoc WhereLoc; - MutableArrayRef Requirements; - - GenericParamList *OuterParameters; - - GenericParamList(SourceLoc LAngleLoc, - ArrayRef Params, - SourceLoc WhereLoc, - MutableArrayRef Requirements, - SourceLoc RAngleLoc); - - // Don't copy. - GenericParamList(const GenericParamList &) = delete; - GenericParamList &operator=(const GenericParamList &) = delete; - -public: - /// create - Create a new generic parameter list within the given AST context. - /// - /// \param Context The ASTContext in which the generic parameter list will - /// be allocated. - /// \param LAngleLoc The location of the opening angle bracket ('<') - /// \param Params The list of generic parameters, which will be copied into - /// ASTContext-allocated memory. - /// \param RAngleLoc The location of the closing angle bracket ('>') - static GenericParamList *create(ASTContext &Context, - SourceLoc LAngleLoc, - ArrayRef Params, - SourceLoc RAngleLoc); - - /// create - Create a new generic parameter list and "where" clause within - /// the given AST context. - /// - /// \param Context The ASTContext in which the generic parameter list will - /// be allocated. - /// \param LAngleLoc The location of the opening angle bracket ('<') - /// \param Params The list of generic parameters, which will be copied into - /// ASTContext-allocated memory. - /// \param WhereLoc The location of the 'where' keyword, if any. - /// \param Requirements The list of requirements, which will be copied into - /// ASTContext-allocated memory. - /// \param RAngleLoc The location of the closing angle bracket ('>') - static GenericParamList *create(const ASTContext &Context, - SourceLoc LAngleLoc, - ArrayRef Params, - SourceLoc WhereLoc, - ArrayRef Requirements, - SourceLoc RAngleLoc); - - MutableArrayRef getParams() { - return {getTrailingObjects(), NumParams}; - } - - ArrayRef getParams() const { - return {getTrailingObjects(), NumParams}; - } - - using iterator = GenericTypeParamDecl **; - using const_iterator = const GenericTypeParamDecl * const *; - - unsigned size() const { return NumParams; } - iterator begin() { return getParams().begin(); } - iterator end() { return getParams().end(); } - const_iterator begin() const { return getParams().begin(); } - const_iterator end() const { return getParams().end(); } - - /// Retrieve the location of the 'where' keyword, or an invalid - /// location if 'where' was not present. - SourceLoc getWhereLoc() const { return WhereLoc; } - - /// Retrieve the set of additional requirements placed on these - /// generic parameters and types derived from them. - /// - /// This list may contain both explicitly-written requirements as well as - /// implicitly-generated requirements, and may be non-empty even if no - /// 'where' keyword is present. - MutableArrayRef getRequirements() { return Requirements; } - - /// Retrieve the set of additional requirements placed on these - /// generic parameters and types derived from them. - /// - /// This list may contain both explicitly-written requirements as well as - /// implicitly-generated requirements, and may be non-empty even if no - /// 'where' keyword is present. - ArrayRef getRequirements() const { return Requirements; } - - /// Retrieve the outer generic parameter list. - /// - /// This is used for extensions of nested types, and in SIL mode, where a - /// single lexical context can have multiple logical generic parameter - /// lists. - GenericParamList *getOuterParameters() const { return OuterParameters; } - - /// Set the outer generic parameter list. See \c getOuterParameters - /// for more information. - void setOuterParameters(GenericParamList *Outer) { OuterParameters = Outer; } - - void setDeclContext(DeclContext *dc); - - SourceLoc getLAngleLoc() const { return Brackets.Start; } - SourceLoc getRAngleLoc() const { return Brackets.End; } - - SourceRange getSourceRange() const { return Brackets; } - - /// Retrieve the source range covering the where clause. - SourceRange getWhereClauseSourceRange() const { - if (WhereLoc.isInvalid()) - return SourceRange(); - - auto endLoc = Requirements.back().getSourceRange().End; - return SourceRange(WhereLoc, endLoc); - } - - /// Configure the depth of the generic parameters in this list. - void setDepth(unsigned depth); - - /// Create a copy of the generic parameter list and all of its generic - /// parameter declarations. The copied generic parameters are re-parented - /// to the given DeclContext. - GenericParamList *clone(DeclContext *dc) const; - - void print(raw_ostream &OS) const; - SWIFT_DEBUG_DUMP; - - bool walk(ASTWalker &walker); - - /// Finds a generic parameter declaration by name. This should only - /// be used from the SIL parser. - GenericTypeParamDecl *lookUpGenericParam(Identifier name) const; -}; - -/// A trailing where clause. -class alignas(RequirementRepr) TrailingWhereClause final : - private llvm::TrailingObjects { - friend TrailingObjects; - - SourceLoc WhereLoc; - - /// The number of requirements. The actual requirements are tail-allocated. - unsigned NumRequirements; - - TrailingWhereClause(SourceLoc whereLoc, - ArrayRef requirements); - -public: - /// Create a new trailing where clause with the given set of requirements. - static TrailingWhereClause *create(ASTContext &ctx, SourceLoc whereLoc, - ArrayRef requirements); - - /// Retrieve the location of the 'where' keyword. - SourceLoc getWhereLoc() const { return WhereLoc; } - - /// Retrieve the set of requirements. - MutableArrayRef getRequirements() { - return {getTrailingObjects(), NumRequirements}; - } - - /// Retrieve the set of requirements. - ArrayRef getRequirements() const { - return {getTrailingObjects(), NumRequirements}; - } - - /// Compute the source range containing this trailing where clause. - SourceRange getSourceRange() const { - return SourceRange(WhereLoc, - getRequirements().back().getSourceRange().End); - } - - void print(llvm::raw_ostream &OS, bool printWhereKeyword) const; -}; - // A private class for forcing exact field layout. class alignas(8) _GenericContext { // Not really public. See GenericContext. @@ -1556,7 +1202,7 @@ class ExtensionDecl final : public GenericContext, public Decl, /// extended nominal. llvm::PointerIntPair ExtendedNominal; - MutableArrayRef Inherited; + ArrayRef Inherited; /// The next extension in the linked list of extensions. /// @@ -1575,7 +1221,7 @@ class ExtensionDecl final : public GenericContext, public Decl, friend class IterableDeclContext; ExtensionDecl(SourceLoc extensionLoc, TypeRepr *extendedType, - MutableArrayRef inherited, + ArrayRef inherited, DeclContext *parent, TrailingWhereClause *trailingWhereClause); @@ -1600,7 +1246,7 @@ class ExtensionDecl final : public GenericContext, public Decl, /// Create a new extension declaration. static ExtensionDecl *create(ASTContext &ctx, SourceLoc extensionLoc, TypeRepr *extendedType, - MutableArrayRef inherited, + ArrayRef inherited, DeclContext *parent, TrailingWhereClause *trailingWhereClause, ClangNode clangNode = ClangNode()); @@ -1652,10 +1298,9 @@ class ExtensionDecl final : public GenericContext, public Decl, /// Retrieve the set of protocols that this type inherits (i.e, /// explicitly conforms to). - MutableArrayRef getInherited() { return Inherited; } ArrayRef getInherited() const { return Inherited; } - void setInherited(MutableArrayRef i) { Inherited = i; } + void setInherited(ArrayRef i) { Inherited = i; } bool hasDefaultAccessLevel() const { return Bits.ExtensionDecl.DefaultAndMaxAccessLevel != 0; @@ -2539,7 +2184,8 @@ class ValueDecl : public Decl { /// 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; + bool forConformance = false, + bool allowUsableFromInline = false) const; /// Returns whether this declaration should be treated as \c open from /// \p useDC. This is very similar to #getFormalAccess, but takes @@ -2753,12 +2399,12 @@ class ValueDecl : public Decl { OpaqueReturnTypeRepr *getOpaqueResultTypeRepr() const; /// Retrieve the attribute associating this declaration with a - /// function builder, if there is one. - CustomAttr *getAttachedFunctionBuilder() const; + /// result builder, if there is one. + CustomAttr *getAttachedResultBuilder() const; - /// Retrieve the @functionBuilder type attached to this declaration, + /// Retrieve the @resultBuilder type attached to this declaration, /// if there is one. - Type getFunctionBuilderType() const; + Type getResultBuilderType() const; /// If this value or its backing storage is annotated /// @_dynamicReplacement(for: ...), compute the original declaration @@ -2768,12 +2414,12 @@ class ValueDecl : public Decl { /// This is a common base class for declarations which declare a type. class TypeDecl : public ValueDecl { - MutableArrayRef Inherited; + ArrayRef Inherited; protected: TypeDecl(DeclKind K, llvm::PointerUnion context, Identifier name, SourceLoc NameLoc, - MutableArrayRef inherited) : + ArrayRef inherited) : ValueDecl(K, context, name, NameLoc), Inherited(inherited) {} public: @@ -2791,10 +2437,9 @@ class TypeDecl : public ValueDecl { /// Retrieve the set of protocols that this type inherits (i.e, /// explicitly conforms to). - MutableArrayRef getInherited() { return Inherited; } ArrayRef getInherited() const { return Inherited; } - void setInherited(MutableArrayRef i) { Inherited = i; } + void setInherited(ArrayRef i) { Inherited = i; } static bool classof(const Decl *D) { return D->getKind() >= DeclKind::First_TypeDecl && @@ -2819,7 +2464,7 @@ class GenericTypeDecl : public GenericContext, public TypeDecl { public: GenericTypeDecl(DeclKind K, DeclContext *DC, Identifier name, SourceLoc nameLoc, - MutableArrayRef inherited, + ArrayRef inherited, GenericParamList *GenericParams); // Resolve ambiguity due to multiple base classes. @@ -2992,7 +2637,7 @@ class TypeAliasDecl : public GenericTypeDecl { UnboundGenericType *getUnboundGenericType() const; /// Retrieve a sugared interface type containing the structure of the interface - /// type before any semantic validation has occured. + /// type before any semantic validation has occurred. Type getStructuralType() const; /// Whether the typealias forwards perfectly to its underlying type. @@ -3345,7 +2990,7 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext { NominalTypeDecl(DeclKind K, DeclContext *DC, Identifier name, SourceLoc NameLoc, - MutableArrayRef inherited, + ArrayRef inherited, GenericParamList *GenericParams) : GenericTypeDecl(K, DC, name, NameLoc, inherited, GenericParams), IterableDeclContext(IterableDeclContextKind::NominalTypeDecl) @@ -3549,6 +3194,20 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext { void synthesizeSemanticMembersIfNeeded(DeclName member); + /// Retrieves the static 'shared' property of a global actor type, which + /// is used to extract the actor instance. + /// + /// \returns the static 'shared' property for a global actor, or \c nullptr + /// for types that are not global actors. + VarDecl *getGlobalActorInstance() const; + + /// Whether this type is a global actor, which can be used as an + /// attribute to decorate declarations for inclusion in the actor-isolated + /// state denoted by this type. + bool isGlobalActor() const { + return getGlobalActorInstance() != nullptr; + } + // Implement isa/cast/dyncast/etc. static bool classof(const Decl *D) { return D->getKind() >= DeclKind::First_NominalTypeDecl && @@ -3601,34 +3260,14 @@ class EnumDecl final : public NominalTypeDecl { // Is the complete set of raw values type checked? HasFixedRawValuesAndTypes = 1 << 2, }; - - struct { - /// The raw type and a bit to indicate whether the - /// raw was computed yet or not. - llvm::PointerIntPair> RawTypeAndFlags; - - bool hasRawType() const { - return RawTypeAndFlags.getInt().contains(HasComputedRawType); - } - void cacheRawType(Type ty) { - auto flags = RawTypeAndFlags.getInt() | HasComputedRawType; - RawTypeAndFlags.setPointerAndInt(ty, flags); - } - - bool hasFixedRawValues() const { - return RawTypeAndFlags.getInt().contains(HasFixedRawValues); - } - bool hasCheckedRawValues() const { - return RawTypeAndFlags.getInt().contains(HasFixedRawValuesAndTypes); - } - } LazySemanticInfo; + OptionSet SemanticFlags; friend class EnumRawValuesRequest; friend class EnumRawTypeRequest; public: EnumDecl(SourceLoc EnumLoc, Identifier Name, SourceLoc NameLoc, - MutableArrayRef Inherited, + ArrayRef Inherited, GenericParamList *GenericParams, DeclContext *DC); SourceLoc getStartLoc() const { return EnumLoc; } @@ -3703,11 +3342,7 @@ class EnumDecl final : public NominalTypeDecl { Type getRawType() const; /// Set the raw type of the enum from its inheritance clause. - void setRawType(Type rawType) { - auto flags = LazySemanticInfo.RawTypeAndFlags.getInt(); - LazySemanticInfo.RawTypeAndFlags.setPointerAndInt( - rawType, flags | HasComputedRawType); - } + void setRawType(Type rawType); /// True if none of the enum cases have associated values. /// @@ -3764,7 +3399,7 @@ class StructDecl final : public NominalTypeDecl { public: StructDecl(SourceLoc StructLoc, Identifier Name, SourceLoc NameLoc, - MutableArrayRef Inherited, + ArrayRef Inherited, GenericParamList *GenericParams, DeclContext *DC); SourceLoc getStartLoc() const { return StructLoc; } @@ -3903,7 +3538,7 @@ class ClassDecl final : public NominalTypeDecl { public: ClassDecl(SourceLoc ClassLoc, Identifier Name, SourceLoc NameLoc, - MutableArrayRef Inherited, + ArrayRef Inherited, GenericParamList *GenericParams, DeclContext *DC); SourceLoc getStartLoc() const { return ClassLoc; } @@ -3938,9 +3573,6 @@ class ClassDecl final : public NominalTypeDecl { /// Set the superclass of this class. void setSuperclass(Type superclass); - /// Whether this class has a circular reference in its inheritance hierarchy. - bool hasCircularInheritance() const; - /// Walk this class and all of the superclasses of this class, transitively, /// invoking the callback function for each class. /// @@ -4121,60 +3753,80 @@ class ClassDecl final : public NominalTypeDecl { } }; +/// A convenience wrapper around the \c SelfReferencePosition::Kind enum. +struct SelfReferencePosition final { + enum Kind : uint8_t { None, Covariant, Contravariant, Invariant }; -/// Describes whether a requirement refers to 'Self', for use in the -/// is-inheritable and is-available-existential checks. -struct SelfReferenceKind { - bool result; - bool parameter; - bool requirement; - bool other; +private: + Kind kind; - /// The type does not refer to 'Self' at all. - static SelfReferenceKind None() { - return SelfReferenceKind(false, false, false, false); +public: + SelfReferencePosition(Kind kind) : kind(kind) {} + + SelfReferencePosition flipped() const { + switch (kind) { + case None: + case Invariant: + return *this; + case Covariant: + return Contravariant; + case Contravariant: + return Covariant; + } + llvm_unreachable("unhandled self reference position!"); } - /// The type refers to 'Self', but only as the result type of a method. - static SelfReferenceKind Result() { - return SelfReferenceKind(true, false, false, false); - } + explicit operator bool() const { return kind > None; } - /// The type refers to 'Self', but only as the parameter type of a method. - static SelfReferenceKind Parameter() { - return SelfReferenceKind(false, true, false, false); - } + operator Kind() const { return kind; } +}; - /// The type refers to 'Self' within a same-type requiement. - static SelfReferenceKind Requirement() { - return SelfReferenceKind(false, false, true, false); - } +/// Describes the least favorable positions at which a requirement refers +/// to 'Self' in terms of variance, for use in the is-inheritable and +/// is-available-existential checks. +struct SelfReferenceInfo final { + using Position = SelfReferencePosition; + + bool hasCovariantSelfResult; + Position selfRef; + Position assocTypeRef; - /// The type refers to 'Self' in a position that is invariant. - static SelfReferenceKind Other() { - return SelfReferenceKind(false, false, false, true); + /// A reference to 'Self'. + static SelfReferenceInfo forSelfRef(Position position) { + assert(position); + return SelfReferenceInfo(false, position, Position::None); } - SelfReferenceKind flip() const { - return SelfReferenceKind(parameter, result, requirement, other); + /// A reference to 'Self' through an associated type. + static SelfReferenceInfo forAssocTypeRef(Position position) { + assert(position); + return SelfReferenceInfo(false, Position::None, position); } - SelfReferenceKind operator|=(SelfReferenceKind kind) { - result |= kind.result; - requirement |= kind.requirement; - parameter |= kind.parameter; - other |= kind.other; + SelfReferenceInfo operator|=(const SelfReferenceInfo &pos) { + hasCovariantSelfResult |= pos.hasCovariantSelfResult; + if (pos.selfRef > selfRef) { + selfRef = pos.selfRef; + } + if (pos.assocTypeRef > assocTypeRef) { + assocTypeRef = pos.assocTypeRef; + } return *this; } - operator bool() const { - return result || parameter || requirement || other; + explicit operator bool() const { + return hasCovariantSelfResult || selfRef || assocTypeRef; } + SelfReferenceInfo() + : hasCovariantSelfResult(false), selfRef(Position::None), + assocTypeRef(Position::None) {} + private: - SelfReferenceKind(bool result, bool parameter, bool requirement, bool other) - : result(result), parameter(parameter), requirement(requirement), - other(other) { } + SelfReferenceInfo(bool hasCovariantSelfResult, Position selfRef, + Position assocTypeRef) + : hasCovariantSelfResult(hasCovariantSelfResult), selfRef(selfRef), + assocTypeRef(assocTypeRef) {} }; /// The set of known protocols for which derived conformances are supported. @@ -4191,6 +3843,7 @@ enum class KnownDerivableProtocolKind : uint8_t { Decodable, AdditiveArithmetic, Differentiable, + Actor, // SWIFT_ENABLE_TENSORFLOW PointwiseMultiplicative, ElementaryFunctions, @@ -4293,7 +3946,7 @@ class ProtocolDecl final : public NominalTypeDecl { public: ProtocolDecl(DeclContext *DC, SourceLoc ProtocolLoc, SourceLoc NameLoc, - Identifier Name, MutableArrayRef Inherited, + Identifier Name, ArrayRef Inherited, TrailingWhereClause *TrailingWhere); using Decl::getASTContext; @@ -4364,15 +4017,12 @@ class ProtocolDecl final : public NominalTypeDecl { /// Find direct Self references within the given requirement. /// - /// \param allowCovariantParameters If true, 'Self' is assumed to be - /// covariant anywhere; otherwise, only in the return type of the top-level - /// function type. - /// - /// \param skipAssocTypes If true, associated types of 'Self' are ignored; - /// otherwise, they count as an 'other' usage of 'Self'. - SelfReferenceKind findProtocolSelfReferences(const ValueDecl *decl, - bool allowCovariantParameters, - bool skipAssocTypes) const; + /// \param treatNonResultCovariantSelfAsInvariant If true, 'Self' is only + /// assumed to be covariant in a top-level non-function type, or in the + /// eventual result type of a top-level function type. + SelfReferenceInfo + findProtocolSelfReferences(const ValueDecl *decl, + bool treatNonResultCovariantSelfAsInvariant) const; /// Determine whether we are allowed to refer to an existential type /// conforming to this protocol. This is only permitted if the type of @@ -5133,11 +4783,19 @@ class VarDecl : public AbstractStorageDecl { /// Determines if this var has an initializer expression that should be /// exposed to clients. + /// /// There's a very narrow case when we would: if the decl is an instance /// member with an initializer expression and the parent type is /// @frozen and resides in a resilient module. bool isInitExposedToClients() const; - + + /// Determines if this var is exposed as part of the layout of a + /// @frozen struct. + /// + /// From the standpoint of access control and exportability checking, this + /// var will behave as if it was public, even if it is internal or private. + bool isLayoutExposedToClients() const; + /// Is this a special debugger variable? bool isDebuggerVar() const { return Bits.VarDecl.IsDebuggerVar; } void setDebuggerVar(bool IsDebuggerVar) { @@ -5225,6 +4883,14 @@ class VarDecl : public AbstractStorageDecl { /// property wrapper with a \c projectedValue . VarDecl *getPropertyWrapperProjectionVar() const; + /// Visit all auxiliary declarations to this VarDecl. + /// + /// An auxiliary declaration is a declaration synthesized by the compiler to support + /// this VarDecl, such as synthesized property wrapper variables. + /// + /// \note this function only visits auxiliary decls that are not part of the AST. + void visitAuxiliaryDecls(llvm::function_ref) const; + /// Retrieve the backing storage property for a lazy property. VarDecl *getLazyStorageProperty() const; @@ -5971,6 +5637,10 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl { /// Returns true if the function is an @asyncHandler. bool isAsyncHandler() const; + /// Returns true if the function signature matches the form of an + /// @asyncHandler. + bool canBeAsyncHandler() const; + /// Returns true if the function body throws. bool hasThrows() const { return Bits.AbstractFunctionDecl.Throws; } @@ -6318,9 +5988,10 @@ class FuncDecl : public AbstractFunctionDecl { DeclContext *Parent); static FuncDecl *createImported(ASTContext &Context, SourceLoc FuncLoc, - DeclName Name, SourceLoc NameLoc, - bool Async, bool Throws, - ParameterList *BodyParams, Type FnRetType, + DeclName Name, SourceLoc NameLoc, bool Async, + bool Throws, ParameterList *BodyParams, + Type FnRetType, + GenericParamList *GenericParams, DeclContext *Parent, ClangNode ClangN); bool isStatic() const; @@ -6349,6 +6020,14 @@ class FuncDecl : public AbstractFunctionDecl { bool isMainTypeMainMethod() const; + /// Whether the given name is enqueue(partialTask:), which is used for + /// actors. + static bool isEnqueuePartialTaskName(ASTContext &ctx, DeclName name); + + /// Determine whether this function is the witness to the Actor protocol's + /// enqueue(partialTask:) operation within an actor. + bool isActorEnqueuePartialTaskWitness() const; + SelfAccessKind getSelfAccessKind() const; void setSelfAccessKind(SelfAccessKind mod) { @@ -6785,6 +6464,37 @@ enum class CtorInitializerKind { Factory }; +/// Specifies the kind of initialization call performed within the body +/// of the constructor, e.g., self.init or super.init. +enum class BodyInitKind { + /// There are no calls to self.init or super.init. + None, + /// There is a call to self.init, which delegates to another (peer) + /// initializer. + Delegating, + /// There is a call to super.init, which chains to a superclass initializer. + Chained, + /// There are no calls to self.init or super.init explicitly in the body of + /// the constructor, but a 'super.init' call will be implicitly added + /// by semantic analysis. + ImplicitChained +}; + +struct BodyInitKindAndExpr { + BodyInitKind initKind; + ApplyExpr *initExpr; + + BodyInitKindAndExpr() : initKind(BodyInitKind::None), initExpr(nullptr) {} + + BodyInitKindAndExpr(BodyInitKind initKind, ApplyExpr *initExpr) + : initKind(initKind), initExpr(initExpr) {} + + friend bool operator==(BodyInitKindAndExpr lhs, BodyInitKindAndExpr rhs) { + return (lhs.initKind == rhs.initKind && + lhs.initExpr == rhs.initExpr); + } +}; + /// ConstructorDecl - Declares a constructor for a type. For example: /// /// \code @@ -6833,38 +6543,11 @@ class ConstructorDecl : public AbstractFunctionDecl { ParamDecl **getImplicitSelfDeclStorage() { return &SelfDecl; } - /// Specifies the kind of initialization call performed within the body - /// of the constructor, e.g., self.init or super.init. - enum class BodyInitKind { - /// There are no calls to self.init or super.init. - None, - /// There is a call to self.init, which delegates to another (peer) - /// initializer. - Delegating, - /// There is a call to super.init, which chains to a superclass initializer. - Chained, - /// There are no calls to self.init or super.init explicitly in the body of - /// the constructor, but a 'super.init' call will be implicitly added - /// by semantic analysis. - ImplicitChained - }; - /// Determine whether the body of this constructor contains any delegating /// or superclass initializations (\c self.init or \c super.init, /// respectively) within its body. - /// - /// \param diags If non-null, this check will ensure that the constructor - /// body is consistent in its use of delegation vs. chaining and emit any - /// diagnostics through the given diagnostic engine. - /// - /// \param init If non-null and there is an explicit \c self.init or - /// \c super.init within the body, will be set to point at that - /// initializer. - BodyInitKind getDelegatingOrChainedInitKind(DiagnosticEngine *diags, - ApplyExpr **init = nullptr) const; - void clearCachedDelegatingOrChainedInitKind() { - Bits.ConstructorDecl.ComputedBodyInitKind = 0; - } + BodyInitKindAndExpr getDelegatingOrChainedInitKind() const; + void clearCachedDelegatingOrChainedInitKind(); /// Whether this constructor is required. bool isRequired() const { diff --git a/include/swift/AST/DeclContext.h b/include/swift/AST/DeclContext.h index e71f5d96c95fd..697e6e7858588 100644 --- a/include/swift/AST/DeclContext.h +++ b/include/swift/AST/DeclContext.h @@ -57,6 +57,7 @@ namespace swift { class GenericTypeParamType; class InfixOperatorDecl; class InfixOperatorLookupResult; + enum class PlatformKind: uint8_t; class PrecedenceGroupDecl; class ProtocolDecl; class Requirement; @@ -490,6 +491,10 @@ class alignas(1 << DeclContextAlignInBits) DeclContext { /// Determine whether the innermost context is generic. bool isInnermostContextGeneric() const; + /// Determine whether this or any parent context is a `@_specialize` extension + /// context. + bool isInSpecializeExtensionContext() const; + /// Get the most optimal resilience expansion for code in this context. /// If the body is able to be inlined into functions in other resilience /// domains, this ensures that only sufficiently-conservative access patterns @@ -782,9 +787,20 @@ class IterableDeclContext { /// abstractions on top of member loading, such as a name lookup table. DeclRange getCurrentMembersWithoutLoading() const; - /// Add a member to this context. If the hint decl is specified, the new decl - /// is inserted immediately after the hint. - void addMember(Decl *member, Decl *hint = nullptr); + /// Add a member to this context. + /// + /// If the hint decl is specified, the new decl is inserted immediately + /// after the hint. + /// + /// If insertAtHead is set, the new decl is inserted at the beginning of + /// the list. + /// + /// Otherwise, it is inserted at the end. + void addMember(Decl *member, Decl *hint = nullptr, bool insertAtHead = false); + + /// Add a member in the right place to preserve source order. This should + /// only be called from the code completion delayed parsing path. + void addMemberPreservingSourceOrder(Decl *member); /// Check whether there are lazily-loaded members. bool hasLazyMembers() const { @@ -862,10 +878,7 @@ class IterableDeclContext { private: /// Add a member to the list for iteration purposes, but do not notify the /// subclass that we have done so. - /// - /// This is used internally when loading members, because loading a - /// member is an invisible addition. - void addMemberSilently(Decl *member, Decl *hint = nullptr) const; + void addMemberSilently(Decl *member, Decl *hint, bool insertAtHead) const; }; /// Define simple_display for DeclContexts but not for subclasses in order to diff --git a/include/swift/AST/DiagnosticEngine.h b/include/swift/AST/DiagnosticEngine.h index 338f146922b5e..3ec8f3648a08a 100644 --- a/include/swift/AST/DiagnosticEngine.h +++ b/include/swift/AST/DiagnosticEngine.h @@ -18,6 +18,7 @@ #ifndef SWIFT_BASIC_DIAGNOSTICENGINE_H #define SWIFT_BASIC_DIAGNOSTICENGINE_H +#include "swift/AST/ActorIsolation.h" #include "swift/AST/DeclNameLoc.h" #include "swift/AST/DiagnosticConsumer.h" #include "swift/AST/TypeLoc.h" @@ -93,6 +94,7 @@ namespace swift { DeclAttribute, VersionTuple, LayoutConstraint, + ActorIsolation, }; namespace diag { @@ -122,6 +124,7 @@ namespace swift { const DeclAttribute *DeclAttributeVal; llvm::VersionTuple VersionVal; LayoutConstraint LayoutConstraintVal; + ActorIsolation ActorIsolationVal; }; public: @@ -209,6 +212,12 @@ namespace swift { DiagnosticArgument(LayoutConstraint L) : Kind(DiagnosticArgumentKind::LayoutConstraint), LayoutConstraintVal(L) { } + + DiagnosticArgument(ActorIsolation AI) + : Kind(DiagnosticArgumentKind::ActorIsolation), + ActorIsolationVal(AI) { + } + /// Initializes a diagnostic argument using the underlying type of the /// given enum. template< @@ -299,6 +308,11 @@ namespace swift { assert(Kind == DiagnosticArgumentKind::LayoutConstraint); return LayoutConstraintVal; } + + ActorIsolation getAsActorIsolation() const { + assert(Kind == DiagnosticArgumentKind::ActorIsolation); + return ActorIsolationVal; + } }; struct DiagnosticFormatOptions { diff --git a/include/swift/AST/DiagnosticsDriver.def b/include/swift/AST/DiagnosticsDriver.def index 119322b8e8a47..01b8c1b5cc955 100644 --- a/include/swift/AST/DiagnosticsDriver.def +++ b/include/swift/AST/DiagnosticsDriver.def @@ -190,6 +190,8 @@ WARNING(warn_drv_darwin_sdk_invalid_settings, none, "SDK settings were ignored because 'SDKSettings.json' could not be parsed", ()) +REMARK(remark_forwarding_to_new_driver, none, "new Swift driver will be used", ()) + // SWIFT_ENABLE_TENSORFLOW ERROR(error_tensorflow_toolchain_repl_not_supported, none, "The Swift for TensorFlow toolchain does not support the Swift REPL. Colab " diff --git a/include/swift/AST/DiagnosticsFrontend.def b/include/swift/AST/DiagnosticsFrontend.def index 6719a13851ec0..a55b8ca8c97ce 100644 --- a/include/swift/AST/DiagnosticsFrontend.def +++ b/include/swift/AST/DiagnosticsFrontend.def @@ -393,7 +393,7 @@ ERROR(error_creating_remark_serializer,none, "error while creating remark serializer: '%0'", (StringRef)) REMARK(interface_file_lock_failure,none, - "could not acquire lock file for module interface '%0'", (StringRef)) + "building module interface without lock file", ()) REMARK(interface_file_lock_timed_out,none, "timed out waiting to acquire lock file for module interface '%0'", (StringRef)) diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index d0e0bb0ee0563..a0934045bb1cc 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -626,6 +626,8 @@ ERROR(expected_sil_function_type, none, "sil function expected to have SIL function type", ()) ERROR(sil_dynamically_replaced_func_not_found,none, "dynamically replaced function not found %0", (Identifier)) +ERROR(sil_specialize_target_func_not_found,none, + "_specialize target function not found %0", (Identifier)) ERROR(sil_availability_expected_version,none, "expected version number in 'available' attribute", ()) @@ -1356,6 +1358,12 @@ ERROR(attr_expected_comma,none, ERROR(attr_expected_string_literal,none, "expected string literal in '%0' attribute", (StringRef)) +ERROR(attr_expected_option_such_as,none, + "expected '%0' option such as '%1'", (StringRef, StringRef)) + +ERROR(attr_unknown_option,none, + "unknown option '%0' for attribute '%1'", (StringRef, StringRef)) + ERROR(attr_missing_label,PointsToFirstBadToken, "missing label '%0:' in '@%1' attribute", (StringRef, StringRef)) ERROR(attr_expected_label,none, @@ -1431,6 +1439,22 @@ WARNING(attr_availability_nonspecific_platform_unexpected_version,none, "unexpected version number in '%0' attribute for non-specific platform " "'*'", (StringRef)) +// availability macro +ERROR(attr_availability_wildcard_in_macro, none, + "future platforms identified by '*' cannot be used in " + "an availability macro definition", ()) +ERROR(attr_availability_missing_macro_name,none, + "expected an identifier to begin an availability macro definition", ()) +ERROR(attr_availability_expected_colon_macro,none, + "expected ':' after '%0' in availability macro definition", + (StringRef)) +ERROR(attr_availability_unknown_version,none, + "reference to undefined version '%0' for availability macro '%1'", + (StringRef, StringRef)) +ERROR(attr_availability_duplicate,none, + "duplicate definition of availability macro '%0' for version '%1'", + (StringRef, StringRef)) + // originallyDefinedIn ERROR(originally_defined_in_missing_rparen,none, "expected ')' in @_originallyDefinedIn argument list", ()) @@ -1515,17 +1539,9 @@ ERROR(opened_attribute_id_value,none, ERROR(opened_attribute_expected_rparen,none, "expected ')' after id value for 'opened' attribute", ()) -// inline, optimize -ERROR(optimization_attribute_expect_option,none, - "expected '%0' option such as '%1'", (StringRef, StringRef)) -ERROR(optimization_attribute_unknown_option,none, - "unknown option '%0' for attribute '%1'", (StringRef, StringRef)) - // effects ERROR(effects_attribute_expect_option,none, "expected '%0' option (readnone, readonly, readwrite)", (StringRef)) -ERROR(effects_attribute_unknown_option,none, - "unknown option '%0' for attribute '%1'", (StringRef, StringRef)) // unowned ERROR(attr_unowned_invalid_specifier,none, @@ -1563,6 +1579,10 @@ ERROR(attr_specialize_parameter_already_defined,none, ERROR(attr_specialize_expected_partial_or_full,none, "expected 'partial' or 'full' as values of the 'kind' parameter in '_specialize' attribute", ()) +ERROR(attr_specialize_expected_function,none, + "expected a function name as the value of the 'target' parameter in '_specialize' attribute", ()) +ERROR(attr_specialize_expected_spi_name,none, + "expected an SPI identifier as the value of the 'spi' parameter in '_specialize' attribute", ()) // _implements ERROR(attr_implements_expected_member_name,PointsToFirstBadToken, diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index e8e90c51e7a01..3d548ba4d3872 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -1675,6 +1675,11 @@ ERROR(where_nongeneric_ctx,none, ERROR(where_nongeneric_toplevel,none, "'where' clause cannot be applied to a non-generic top-level " "declaration", ()) +ERROR(unable_to_convert_generic_swift_types,none, + "could not generate C++ types from the generic Swift types provided. " + "The following Swift type(s) provided to '%0' were unable to be " + "converted: %1.", + (StringRef, StringRef)) // Type aliases ERROR(type_alias_underlying_type_access,none, @@ -1838,9 +1843,11 @@ ERROR(use_of_equal_instead_of_equality,none, "use of '=' in a boolean context, did you mean '=='?", ()) ERROR(type_cannot_conform, none, "%select{type %1|protocol %1 as a type}0 cannot conform to " - "%select{%3|the protocol itself}2; " - "only concrete types such as structs, enums and classes can conform to protocols", + "%select{%3|the protocol itself}2", (bool, Type, bool, Type)) +NOTE(only_concrete_types_conform_to_protocols,none, + "only concrete types such as structs, enums and classes can conform to protocols", + ()) NOTE(required_by_opaque_return,none, "required by opaque return type of %0 %1", (DescriptiveDeclKind, DeclName)) NOTE(required_by_decl,none, @@ -1993,14 +2000,15 @@ 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 " - "protocol %2", - (DeclName, Type, Type)) + "%select{%error|method|property|subscript}0 %1 in non-final class %2 " + "must %select{%error|return|specify type|return}0 'Self' " + "to conform to protocol %3", + (RequirementKind, 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 " - "requirements", - (DeclName, Type)) + "%select{%error|method|%error|subscript}0 %1 in non-final class %2 " + "cannot be implemented in a protocol extension because it returns 'Self' " + "and has associated type requirements", + (RequirementKind, DeclName, Type)) ERROR(witness_not_accessible_proto,none, "%select{initializer %1|method %1|%select{|setter for }2property %1" "|subscript%select{| setter}2}0 must be declared " @@ -4156,6 +4164,9 @@ ERROR(not_objc_function_async,none, "'async' function cannot be represented in Objective-C", ()) NOTE(not_objc_function_type_async,none, "'async' function types cannot be represented in Objective-C", ()) +ERROR(actor_isolated_objc,none, + "actor-isolated %0 %1 cannot be @objc", + (DescriptiveDeclKind, DeclName)) NOTE(protocol_witness_async_conflict,none, "candidate is %select{not |}0'async', but protocol requirement is%select{| not}0", (bool)) @@ -4205,6 +4216,21 @@ ERROR(actor_isolated_self_independent_context,none, "actor-isolated %0 %1 can not be referenced from an " "'@actorIndependent' context", (DescriptiveDeclKind, DeclName)) +ERROR(actor_isolated_global_actor_context,none, + "actor-isolated %0 %1 can not be referenced from context of global " + "actor %2", + (DescriptiveDeclKind, DeclName, Type)) +ERROR(global_actor_from_instance_actor_context,none, + "%0 %1 isolated to global actor %2 can not be referenced from actor %3", + (DescriptiveDeclKind, DeclName, Type, DeclName)) +ERROR(global_actor_from_other_global_actor_context,none, + "%0 %1 isolated to global actor %2 can not be referenced from " + "different global actor %3", + (DescriptiveDeclKind, DeclName, Type, Type)) +ERROR(global_actor_from_independent_context,none, + "%0 %1 isolated to global actor %2 can not be referenced from an " + "'@actorIndependent' context", + (DescriptiveDeclKind, DeclName, Type)) ERROR(actor_isolated_partial_apply,none, "actor-isolated %0 %1 can not be partially applied", (DescriptiveDeclKind, DeclName)) @@ -4224,32 +4250,85 @@ NOTE(actor_mutable_state,none, WARNING(shared_mutable_state_access,none, "reference to %0 %1 is not concurrency-safe because it involves " "shared mutable state", (DescriptiveDeclKind, DeclName)) -NOTE(actor_isolated_witness,none, +ERROR(actor_isolated_witness,none, "actor-isolated %0 %1 cannot be used to satisfy a protocol requirement", (DescriptiveDeclKind, DeclName)) -NOTE(actor_isolated_witness_could_be_async_handler,none, +ERROR(actor_isolated_witness_could_be_async_handler,none, "actor-isolated %0 %1 cannot be used to satisfy a protocol requirement; " "did you mean to make it an asychronous handler?", (DescriptiveDeclKind, DeclName)) - -ERROR(actorisolated_let,none, - "'@actorIsolated' is meaningless on 'let' declarations because " +ERROR(global_actor_isolated_requirement,none, + "%0 %1 must be isolated to the global actor %2 to satisfy corresponding " + "requirement from protocol %3", + (DescriptiveDeclKind, DeclName, Type, Identifier)) +ERROR(global_actor_isolated_witness,none, + "%0 %1 isolated to global actor %2 can not satisfy corresponding " + "requirement from protocol %3", + (DescriptiveDeclKind, DeclName, Type, Identifier)) +ERROR(global_actor_isolated_requirement_witness_conflict,none, + "%0 %1 isolated to global actor %2 can not satisfy corresponding " + "requirement from protocol %3 isolated to global actor %4", + (DescriptiveDeclKind, DeclName, Type, Identifier, Type)) + +ERROR(actorindependent_let,none, + "'@actorIndependent' is meaningless on 'let' declarations because " "they are immutable", ()) -ERROR(actorisolated_mutable_storage,none, - "'@actorIsolated' can not be applied to stored properties", +ERROR(actorindependent_mutable_storage,none, + "'@actorIndependent' can not be applied to stored properties", ()) -ERROR(actorisolated_local_var,none, - "'@actorIsolated' can not be applied to local variables", +ERROR(actorindependent_local_var,none, + "'@actorIndependent' can not be applied to local variables", ()) -ERROR(actorisolated_not_actor_member,none, - "'@actorIsolated' can only be applied to actor members and " +ERROR(actorindependent_not_actor_member,none, + "'@actorIndependent' can only be applied to actor members and " "global/static variables", ()) -ERROR(actorisolated_not_actor_instance_member,none, - "'@actorIsolated' can only be applied to instance members of actors", +ERROR(actorindependent_not_actor_instance_member,none, + "'@actorIndependent' can only be applied to instance members of actors", ()) +ERROR(concurrency_lib_missing,none, + "missing '%0' declaration, probably because the '_Concurrency' " + "module was not imported", (StringRef)) +ERROR(enqueue_partial_task_not_in_context,none, + "'enqueue(partialTask:)' can only be implemented in the definition of " + "actor class %0", (Type)) + +ERROR(global_actor_missing_shared,none, + "global actor %0 requires a static property 'shared' that produces an " + "actor instance", (Identifier)) +NOTE(global_actor_shared_not_static,none, + "'shared' property in global actor is not 'static'", ()) +NOTE(global_actor_shared_inaccessible,none, + "'shared' property has more restrictive access (%0) than its global actor " + "(%1)", + (StringRef, StringRef)) +NOTE(global_actor_shared_constrained_extension,none, + "'shared' property in global actor cannot be in a constrained extension", + ()) +NOTE(global_actor_shared_non_actor_type,none, + "'shared' property type %0 does not conform to the 'Actor' protocol", + (Type)) + +ERROR(multiple_global_actors,none, + "declaration can not have multiple global actor attributes (%0 and %1)", + (Identifier, Identifier)) +ERROR(global_actor_disallowed,none, + "%0 cannot have a global actor", (DescriptiveDeclKind)) +ERROR(global_actor_on_actor_class,none, + "actor class %0 cannot have a global actor", (Identifier)) +ERROR(global_actor_on_local_variable,none, + "local variable %0 cannot have a global actor", (DeclName)) + +ERROR(actor_isolation_multiple_attr,none, + "%0 %1 has multiple actor-isolation attributes ('%2' and '%3')", + (DescriptiveDeclKind, DeclName, StringRef, StringRef)) + +ERROR(actor_isolation_override_mismatch,none, + "%0 %1 %2 has different actor isolation from %3 overridden declaration", + (ActorIsolation, DescriptiveDeclKind, DeclName, ActorIsolation)) + //------------------------------------------------------------------------------ // MARK: Type Check Types //------------------------------------------------------------------------------ @@ -4980,6 +5059,10 @@ WARNING(public_decl_needs_availability, none, "public declarations should have an availability attribute when building " "with -require-explicit-availability", ()) +ERROR(availability_macro_in_inlinable, none, + "availability macro cannot be used in inlinable %0", + (DescriptiveDeclKind)) + // 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. @@ -5039,12 +5122,12 @@ ERROR(local_type_in_inlinable_function, (Identifier, unsigned)) ERROR(resilience_decl_unavailable, - none, DECL_OR_ACCESSOR "4 %1 is %select{private|fileprivate|internal|'@_spi'|'@_spi'}2 and " + none, DECL_OR_ACCESSOR "4 %1 is %select{private|fileprivate|internal|%error|%error}2 and " "cannot be referenced from " FRAGILE_FUNC_KIND "3", (DescriptiveDeclKind, DeclName, AccessLevel, unsigned, bool)) WARNING(resilience_decl_unavailable_warn, - none, DECL_OR_ACCESSOR "4 %1 is %select{private|fileprivate|internal|'@_spi'|'@_spi'}2 and " + none, DECL_OR_ACCESSOR "4 %1 is %select{private|fileprivate|internal|%error|%error}2 and " "should not be referenced from " FRAGILE_FUNC_KIND "3", (DescriptiveDeclKind, DeclName, AccessLevel, unsigned, bool)) @@ -5112,6 +5195,12 @@ ERROR(specialize_attr_missing_constraint,none, "Missing constraint for %0 in '_specialize' attribute", (DeclName)) ERROR(specialize_attr_unsupported_kind_of_req,none, "Only same-type and layout requirements are supported by '_specialize' attribute", ()) +ERROR(specialize_target_function_not_found, none, + "target function %0 could not be found", (DeclNameRef)) +ERROR(specialize_target_function_of_type_not_found, none, + "target function %0 of type %1 could not be found", (DeclNameRef, Type)) +NOTE(specialize_found_function_of_type, none, + "found function %0 of type %1", (DeclName, Type)) //------------------------------------------------------------------------------ // MARK: Variable usage diagnostics @@ -5264,8 +5353,6 @@ ERROR(property_wrapper_mutating_get_composed_to_get_only,none, "property wrapper %0 with a mutating getter cannot be composed inside " "get-only property wrapper %1", (TypeLoc, TypeLoc)) -ERROR(property_wrapper_local,none, - "property wrappers are not yet supported on local properties", ()) ERROR(property_wrapper_top_level,none, "property wrappers are not yet supported in top-level code", ()) ERROR(property_wrapper_let, none, @@ -5331,83 +5418,83 @@ ERROR(property_wrapper_missing_arg_init, none, "missing argument for parameter " "arguments in '@%1(...)'", (Identifier, StringRef)) //------------------------------------------------------------------------------ -// MARK: function builder diagnostics +// MARK: result builder diagnostics //------------------------------------------------------------------------------ -ERROR(function_builder_decl, none, - "closure containing a declaration cannot be used with function " +ERROR(result_builder_decl, none, + "closure containing a declaration cannot be used with result " "builder %0", (DeclName)) -NOTE(note_function_builder_decl, none, - "closure containing a declaration cannot be used with function " +NOTE(note_result_builder_decl, none, + "closure containing a declaration cannot be used with result " "builder %0", (DeclName)) -ERROR(function_builder_control_flow, none, - "closure containing control flow statement cannot be used with function " +ERROR(result_builder_control_flow, none, + "closure containing control flow statement cannot be used with result " "builder %0", (DeclName)) -NOTE(note_function_builder_control_flow, none, - "closure containing control flow statement cannot be used with function " +NOTE(note_result_builder_control_flow, none, + "closure containing control flow statement cannot be used with result " "builder %0", (DeclName)) -ERROR(function_builder_attribute_not_allowed_here, none, - "function builder attribute %0 can only be applied to a parameter, " +ERROR(result_builder_attribute_not_allowed_here, none, + "result builder attribute %0 can only be applied to a parameter, " "function, or computed property", (Identifier)) -ERROR(function_builder_attribute_on_storage_without_getter, none, - "function builder attribute %0 can only be applied to a " +ERROR(result_builder_attribute_on_storage_without_getter, none, + "result builder attribute %0 can only be applied to a " "%select{subscript|property|constant|variable}1 if it defines a getter", (DeclName, unsigned)) -ERROR(function_builder_parameter_not_of_function_type, none, - "function builder attribute %0 can only be applied to a parameter of " +ERROR(result_builder_parameter_not_of_function_type, none, + "result builder attribute %0 can only be applied to a parameter of " "function type", (Identifier)) -ERROR(function_builder_parameter_autoclosure, none, - "function builder attribute %0 cannot be applied to an autoclosure " +ERROR(result_builder_parameter_autoclosure, none, + "result builder attribute %0 cannot be applied to an autoclosure " "parameter", (Identifier)) -ERROR(function_builder_multiple, none, - "only one function builder attribute can be attached to a " +ERROR(result_builder_multiple, none, + "only one result builder attribute can be attached to a " "%select{declaration|parameter}0", (bool)) -NOTE(previous_function_builder_here, none, - "previous function builder specified here", ()) -ERROR(function_builder_arguments, none, - "function builder attributes cannot have arguments", ()) -WARNING(function_builder_disabled_by_return, none, - "application of function builder %0 disabled by explicit 'return' " +NOTE(previous_result_builder_here, none, + "previous result builder specified here", ()) +ERROR(result_builder_arguments, none, + "result builder attributes cannot have arguments", ()) +WARNING(result_builder_disabled_by_return, none, + "application of result builder %0 disabled by explicit 'return' " "statement", (Type)) -NOTE(function_builder_remove_attr, none, - "remove the attribute to explicitly disable the function builder", ()) -NOTE(function_builder_remove_returns, none, - "remove 'return' statements to apply the function builder", ()) -ERROR(function_builder_infer_ambig, none, - "ambiguous function builder inferred for %0: %1 or %2", +NOTE(result_builder_remove_attr, none, + "remove the attribute to explicitly disable the result builder", ()) +NOTE(result_builder_remove_returns, none, + "remove 'return' statements to apply the result builder", ()) +ERROR(result_builder_infer_ambig, none, + "ambiguous result builder inferred for %0: %1 or %2", (DeclName, Type, Type)) -NOTE(function_builder_infer_add_return, none, - "add an explicit 'return' statement to not use a function builder", ()) -NOTE(function_builder_infer_pick_specific, none, - "apply function builder %0 (inferred from %select{protocol|dynamic replacement of}1 %2)", +NOTE(result_builder_infer_add_return, none, + "add an explicit 'return' statement to not use a result builder", ()) +NOTE(result_builder_infer_pick_specific, none, + "apply result builder %0 (inferred from %select{protocol|dynamic replacement of}1 %2)", (Type, unsigned, DeclName)) -WARNING(function_builder_missing_limited_availability, none, - "function builder %0 does not implement 'buildLimitedAvailability'; " +WARNING(result_builder_missing_limited_availability, none, + "result builder %0 does not implement 'buildLimitedAvailability'; " "this code may crash on earlier versions of the OS", (Type)) -ERROR(function_builder_static_buildblock, none, - "function builder must provide at least one static 'buildBlock' " +ERROR(result_builder_static_buildblock, none, + "result builder must provide at least one static 'buildBlock' " "method", ()) -NOTE(function_builder_non_static_buildblock, none, +NOTE(result_builder_non_static_buildblock, none, "did you mean to make instance method 'buildBlock' static?", ()) -NOTE(function_builder_buildblock_enum_case, none, - "enum case 'buildBlock' cannot be used to satisfy the function builder " +NOTE(result_builder_buildblock_enum_case, none, + "enum case 'buildBlock' cannot be used to satisfy the result builder " "requirement", ()) -NOTE(function_builder_buildblock_not_static_method, none, +NOTE(result_builder_buildblock_not_static_method, none, "potential match 'buildBlock' is not a static method", ()) -NOTE(function_builder_missing_build_optional, none, - "add 'buildOptional(_:)' to the function builder %0 to add support for " +NOTE(result_builder_missing_build_optional, none, + "add 'buildOptional(_:)' to the result builder %0 to add support for " "'if' statements without an 'else'", (Type)) -NOTE(function_builder_missing_build_either, none, - "add 'buildEither(first:)' and 'buildEither(second:)' to the function " +NOTE(result_builder_missing_build_either, none, + "add 'buildEither(first:)' and 'buildEither(second:)' to the result " "builder %0 to add support for 'if'-'else' and 'switch'", (Type)) -NOTE(function_builder_missing_build_array, none, - "add 'buildArray(_:)' to the function builder %0 to add support for " +NOTE(result_builder_missing_build_array, none, + "add 'buildArray(_:)' to the result builder %0 to add support for " "'for'..'in' loops", (Type)) -NOTE(function_builder_missing_build_limited_availability, none, - "add 'buildLimitedAvailability(_:)' to the function " +NOTE(result_builder_missing_build_limited_availability, none, + "add 'buildLimitedAvailability(_:)' to the result " "builder %0 to erase type information for less-available types", (Type)) //------------------------------------------------------------------------------ diff --git a/include/swift/AST/EducationalNotes.def b/include/swift/AST/EducationalNotes.def index 720eebade0c55..b2a2914e85afb 100644 --- a/include/swift/AST/EducationalNotes.def +++ b/include/swift/AST/EducationalNotes.def @@ -81,15 +81,15 @@ EDUCATIONAL_NOTES(type_cannot_conform, "protocol-type-non-conformance.md") EDUCATIONAL_NOTES(unlabeled_trailing_closure_deprecated, "trailing-closure-matching.md") -EDUCATIONAL_NOTES(function_builder_static_buildblock, - "function-builder-methods.md") -EDUCATIONAL_NOTES(function_builder_missing_limited_availability, - "function-builder-methods.md") -EDUCATIONAL_NOTES(function_builder_missing_build_optional, - "function-builder-methods.md") -EDUCATIONAL_NOTES(function_builder_missing_build_either, - "function-builder-methods.md") -EDUCATIONAL_NOTES(function_builder_missing_build_array, - "function-builder-methods.md") +EDUCATIONAL_NOTES(result_builder_static_buildblock, + "result-builder-methods.md") +EDUCATIONAL_NOTES(result_builder_missing_limited_availability, + "result-builder-methods.md") +EDUCATIONAL_NOTES(result_builder_missing_build_optional, + "result-builder-methods.md") +EDUCATIONAL_NOTES(result_builder_missing_build_either, + "result-builder-methods.md") +EDUCATIONAL_NOTES(result_builder_missing_build_array, + "result-builder-methods.md") #undef EDUCATIONAL_NOTES diff --git a/include/swift/AST/Evaluator.h b/include/swift/AST/Evaluator.h index 212682d0bc121..ed7b6a92aed6a 100644 --- a/include/swift/AST/Evaluator.h +++ b/include/swift/AST/Evaluator.h @@ -322,6 +322,13 @@ class Evaluator { cache.insert({AnyRequest(request), std::move(output)}); } + /// Do not introduce new callers of this function. + template::type* = nullptr> + void clearCachedOutput(const Request &request) { + cache.erase(AnyRequest(request)); + } + /// Clear the cache stored within this evaluator. /// /// Note that this does not clear the caches of requests that use external diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index 2bf600146c65f..30934bb682671 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -613,6 +613,9 @@ class CodeCompletionExpr : public Expr { /// LiteralExpr - Common base class between the literals. class LiteralExpr : public Expr { + // Set by Sema: + ConcreteDeclRef Initializer; + public: LiteralExpr(ExprKind Kind, bool Implicit) : Expr(Kind, Implicit) {} @@ -620,13 +623,55 @@ class LiteralExpr : public Expr { return E->getKind() >= ExprKind::First_LiteralExpr && E->getKind() <= ExprKind::Last_LiteralExpr; } + + /// Retrieve the initializer that will be used to construct the + /// literal from the result of the initializer. + /// + /// Only literals that have no builtin literal conformance will have + /// this initializer, which will be called on the result of the builtin + /// initializer. + ConcreteDeclRef getInitializer() const { return Initializer; } + + /// Set the initializer that will be used to construct the literal. + void setInitializer(ConcreteDeclRef initializer) { + Initializer = initializer; + } +}; + +/// BuiltinLiteralExpr - Common base class between all literals +/// that provides BuiltinInitializer +class BuiltinLiteralExpr : public LiteralExpr { + // Set by Seam: + ConcreteDeclRef BuiltinInitializer; + +public: + BuiltinLiteralExpr(ExprKind Kind, bool Implicit) + : LiteralExpr(Kind, Implicit) {} + + static bool classof(const Expr *E) { + return E->getKind() >= ExprKind::First_BuiltinLiteralExpr && + E->getKind() <= ExprKind::Last_BuiltinLiteralExpr; + } + + /// Retrieve the builtin initializer that will be used to construct the + /// literal. + /// + /// Any type-checked literal will have a builtin initializer, which is + /// called first to form a concrete Swift type. + ConcreteDeclRef getBuiltinInitializer() const { return BuiltinInitializer; } + + /// Set the builtin initializer that will be used to construct the + /// literal. + void setBuiltinInitializer(ConcreteDeclRef builtinInitializer) { + BuiltinInitializer = builtinInitializer; + } }; /// The 'nil' literal. /// class NilLiteralExpr : public LiteralExpr { SourceLoc Loc; - ConcreteDeclRef Initializer; + public: NilLiteralExpr(SourceLoc Loc, bool Implicit = false) : LiteralExpr(ExprKind::NilLiteral, Implicit), Loc(Loc) { @@ -635,15 +680,6 @@ class NilLiteralExpr : public LiteralExpr { SourceRange getSourceRange() const { return Loc; } - - /// Retrieve the initializer that will be used to construct the 'nil' - /// literal from the result of the initializer. - ConcreteDeclRef getInitializer() const { return Initializer; } - - /// Set the initializer that will be used to construct the 'nil' literal. - void setInitializer(ConcreteDeclRef initializer) { - Initializer = initializer; - } static bool classof(const Expr *E) { return E->getKind() == ExprKind::NilLiteral; @@ -651,26 +687,23 @@ class NilLiteralExpr : public LiteralExpr { }; /// Abstract base class for numeric literals, potentially with a sign. -class NumberLiteralExpr : public LiteralExpr { +class NumberLiteralExpr : public BuiltinLiteralExpr { /// The value of the literal as an ASTContext-owned string. Underscores must /// be stripped. StringRef Val; // Use StringRef instead of APInt or APFloat, which leak. - ConcreteDeclRef BuiltinInitializer; - ConcreteDeclRef Initializer; protected: SourceLoc MinusLoc; SourceLoc DigitsLoc; public: - NumberLiteralExpr(ExprKind Kind, - StringRef Val, SourceLoc DigitsLoc, bool Implicit) - : LiteralExpr(Kind, Implicit), Val(Val), DigitsLoc(DigitsLoc) - { - Bits.NumberLiteralExpr.IsNegative = false; - Bits.NumberLiteralExpr.IsExplicitConversion = false; - } - + NumberLiteralExpr(ExprKind Kind, StringRef Val, SourceLoc DigitsLoc, + bool Implicit) + : BuiltinLiteralExpr(Kind, Implicit), Val(Val), DigitsLoc(DigitsLoc) { + Bits.NumberLiteralExpr.IsNegative = false; + Bits.NumberLiteralExpr.IsExplicitConversion = false; + } + bool isNegative() const { return Bits.NumberLiteralExpr.IsNegative; } void setNegative(SourceLoc Loc) { MinusLoc = Loc; @@ -701,32 +734,6 @@ class NumberLiteralExpr : public LiteralExpr { return DigitsLoc; } - /// Retrieve the builtin initializer that will be used to construct the - /// boolean literal. - /// - /// Any type-checked boolean literal will have a builtin initializer, which is - /// called first to form a concrete Swift type. - ConcreteDeclRef getBuiltinInitializer() const { return BuiltinInitializer; } - - /// Set the builtin initializer that will be used to construct the boolean - /// literal. - void setBuiltinInitializer(ConcreteDeclRef builtinInitializer) { - BuiltinInitializer = builtinInitializer; - } - - /// Retrieve the initializer that will be used to construct the boolean - /// literal from the result of the initializer. - /// - /// Only boolean literals that have no builtin literal conformance will have - /// this initializer, which will be called on the result of the builtin - /// initializer. - ConcreteDeclRef getInitializer() const { return Initializer; } - - /// Set the initializer that will be used to construct the boolean literal. - void setInitializer(ConcreteDeclRef initializer) { - Initializer = initializer; - } - static bool classof(const Expr *E) { return E->getKind() >= ExprKind::First_NumberLiteralExpr && E->getKind() <= ExprKind::Last_NumberLiteralExpr; @@ -790,14 +797,12 @@ class FloatLiteralExpr : public NumberLiteralExpr { /// A Boolean literal ('true' or 'false') /// -class BooleanLiteralExpr : public LiteralExpr { +class BooleanLiteralExpr : public BuiltinLiteralExpr { SourceLoc Loc; - ConcreteDeclRef BuiltinInitializer; - ConcreteDeclRef Initializer; public: BooleanLiteralExpr(bool Value, SourceLoc Loc, bool Implicit = false) - : LiteralExpr(ExprKind::BooleanLiteral, Implicit), Loc(Loc) { + : BuiltinLiteralExpr(ExprKind::BooleanLiteral, Implicit), Loc(Loc) { Bits.BooleanLiteralExpr.Value = Value; } @@ -808,43 +813,15 @@ class BooleanLiteralExpr : public LiteralExpr { return Loc; } - /// Retrieve the builtin initializer that will be used to construct the - /// boolean literal. - /// - /// Any type-checked boolean literal will have a builtin initializer, which is - /// called first to form a concrete Swift type. - ConcreteDeclRef getBuiltinInitializer() const { return BuiltinInitializer; } - - /// Set the builtin initializer that will be used to construct the boolean - /// literal. - void setBuiltinInitializer(ConcreteDeclRef builtinInitializer) { - BuiltinInitializer = builtinInitializer; - } - - /// Retrieve the initializer that will be used to construct the boolean - /// literal from the result of the initializer. - /// - /// Only boolean literals that have no builtin literal conformance will have - /// this initializer, which will be called on the result of the builtin - /// initializer. - ConcreteDeclRef getInitializer() const { return Initializer; } - - /// Set the initializer that will be used to construct the boolean literal. - void setInitializer(ConcreteDeclRef initializer) { - Initializer = initializer; - } - static bool classof(const Expr *E) { return E->getKind() == ExprKind::BooleanLiteral; } }; - + /// StringLiteralExpr - String literal, like '"foo"'. -class StringLiteralExpr : public LiteralExpr { +class StringLiteralExpr : public BuiltinLiteralExpr { StringRef Val; SourceRange Range; - ConcreteDeclRef BuiltinInitializer; - ConcreteDeclRef Initializer; public: /// The encoding that should be used for the string literal. @@ -879,32 +856,6 @@ class StringLiteralExpr : public LiteralExpr { return Bits.StringLiteralExpr.IsSingleExtendedGraphemeCluster; } - /// Retrieve the builtin initializer that will be used to construct the string - /// literal. - /// - /// Any type-checked string literal will have a builtin initializer, which is - /// called first to form a concrete Swift type. - ConcreteDeclRef getBuiltinInitializer() const { return BuiltinInitializer; } - - /// Set the builtin initializer that will be used to construct the string - /// literal. - void setBuiltinInitializer(ConcreteDeclRef builtinInitializer) { - BuiltinInitializer = builtinInitializer; - } - - /// Retrieve the initializer that will be used to construct the string - /// literal from the result of the initializer. - /// - /// Only string literals that have no builtin literal conformance will have - /// this initializer, which will be called on the result of the builtin - /// initializer. - ConcreteDeclRef getInitializer() const { return Initializer; } - - /// Set the initializer that will be used to construct the string literal. - void setInitializer(ConcreteDeclRef initializer) { - Initializer = initializer; - } - static bool classof(const Expr *E) { return E->getKind() == ExprKind::StringLiteral; } @@ -973,7 +924,6 @@ class InterpolatedStringLiteralExpr : public LiteralExpr { // Set by Sema: OpaqueValueExpr *interpolationExpr = nullptr; ConcreteDeclRef builderInit; - ConcreteDeclRef resultInit; Expr *interpolationCountExpr = nullptr; Expr *literalCapacityExpr = nullptr; @@ -995,11 +945,6 @@ class InterpolatedStringLiteralExpr : public LiteralExpr { void setBuilderInit(ConcreteDeclRef decl) { builderInit = decl; } ConcreteDeclRef getBuilderInit() const { return builderInit; } - /// Sets the decl that constructs the final result type after the - /// AppendingExpr has been evaluated. - void setResultInit(ConcreteDeclRef decl) { resultInit = decl; } - ConcreteDeclRef getResultInit() const { return resultInit; } - /// Sets the OpaqueValueExpr that is passed into AppendingExpr as the SubExpr /// that the tap operates on. void setInterpolationExpr(OpaqueValueExpr *expr) { interpolationExpr = expr; } @@ -1059,7 +1004,7 @@ class InterpolatedStringLiteralExpr : public LiteralExpr { /// MagicIdentifierLiteralExpr - A magic identifier like #file which expands /// out to a literal at SILGen time. -class MagicIdentifierLiteralExpr : public LiteralExpr { +class MagicIdentifierLiteralExpr : public BuiltinLiteralExpr { public: enum Kind : unsigned { #define MAGIC_IDENTIFIER(NAME, STRING, SYNTAX_KIND) NAME, @@ -1077,17 +1022,16 @@ class MagicIdentifierLiteralExpr : public LiteralExpr { private: SourceLoc Loc; - ConcreteDeclRef BuiltinInitializer; - ConcreteDeclRef Initializer; public: MagicIdentifierLiteralExpr(Kind kind, SourceLoc loc, bool implicit = false) - : LiteralExpr(ExprKind::MagicIdentifierLiteral, implicit), Loc(loc) { + : BuiltinLiteralExpr(ExprKind::MagicIdentifierLiteral, implicit), + Loc(loc) { Bits.MagicIdentifierLiteralExpr.Kind = static_cast(kind); Bits.MagicIdentifierLiteralExpr.StringEncoding = static_cast(StringLiteralExpr::UTF8); } - + Kind getKind() const { return static_cast(Bits.MagicIdentifierLiteralExpr.Kind); } @@ -1123,35 +1067,6 @@ class MagicIdentifierLiteralExpr : public LiteralExpr { = static_cast(encoding); } - /// Retrieve the builtin initializer that will be used to construct the - /// literal. - /// - /// Any type-checked literal will have a builtin initializer, which is - /// called first to form a concrete Swift type. - ConcreteDeclRef getBuiltinInitializer() const { - return BuiltinInitializer; - } - - /// Set the builtin initializer that will be used to construct the literal. - void setBuiltinInitializer(ConcreteDeclRef builtinInitializer) { - BuiltinInitializer = builtinInitializer; - } - - /// Retrieve the initializer that will be used to construct the literal from - /// the result of the initializer. - /// - /// Only literals that have no builtin literal conformance will have - /// this initializer, which will be called on the result of the builtin - /// initializer. - ConcreteDeclRef getInitializer() const { - return Initializer; - } - - /// Set the initializer that will be used to construct the literal. - void setInitializer(ConcreteDeclRef initializer) { - Initializer = initializer; - } - static bool classof(const Expr *E) { return E->getKind() == ExprKind::MagicIdentifierLiteral; } @@ -1174,7 +1089,6 @@ class ObjectLiteralExpr final private: Expr *Arg; SourceLoc PoundLoc; - ConcreteDeclRef Initializer; ObjectLiteralExpr(SourceLoc PoundLoc, LiteralKind LitKind, Expr *Arg, @@ -1237,15 +1151,6 @@ class ObjectLiteralExpr final StringRef getLiteralKindPlainName() const; - /// Retrieve the initializer that will be used to construct the 'object' - /// literal from the result of the initializer. - ConcreteDeclRef getInitializer() const { return Initializer; } - - /// Set the initializer that will be used to construct the 'object' literal. - void setInitializer(ConcreteDeclRef initializer) { - Initializer = initializer; - } - static bool classof(const Expr *E) { return E->getKind() == ExprKind::ObjectLiteral; } @@ -1332,6 +1237,7 @@ class SuperRefExpr : public Expr { : Expr(ExprKind::SuperRef, Implicit, SuperTy), Self(Self), Loc(Loc) {} VarDecl *getSelf() const { return Self; } + void setSelf(VarDecl *self) { Self = self; } SourceLoc getSuperLoc() const { return Loc; } SourceRange getSourceRange() const { return Loc; } @@ -3789,7 +3695,7 @@ class ClosureExpr : public AbstractClosureExpr { ReadyForTypeChecking, /// The body was typechecked with the enclosing closure. - /// i.e. single expression closure or function builder closure. + /// i.e. single expression closure or result builder closure. TypeCheckedWithSignature, /// The body was type checked separately from the enclosing closure. @@ -5793,6 +5699,7 @@ Expr *packSingleArgument( void simple_display(llvm::raw_ostream &out, const ClosureExpr *CE); void simple_display(llvm::raw_ostream &out, const DefaultArgumentExpr *expr); +void simple_display(llvm::raw_ostream &out, const Expr *expr); SourceLoc extractNearestSourceLoc(const DefaultArgumentExpr *expr); diff --git a/include/swift/AST/ExprNodes.def b/include/swift/AST/ExprNodes.def index 7c98a83c0afd9..bcb187333fca1 100644 --- a/include/swift/AST/ExprNodes.def +++ b/include/swift/AST/ExprNodes.def @@ -68,16 +68,18 @@ EXPR(Error, Expr) ABSTRACT_EXPR(Literal, Expr) LITERAL_EXPR(NilLiteral, LiteralExpr) - ABSTRACT_EXPR(NumberLiteral, LiteralExpr) - LITERAL_EXPR(IntegerLiteral, NumberLiteralExpr) - LITERAL_EXPR(FloatLiteral, NumberLiteralExpr) - EXPR_RANGE(NumberLiteral, IntegerLiteral, FloatLiteral) - LITERAL_EXPR(BooleanLiteral, LiteralExpr) - LITERAL_EXPR(StringLiteral, LiteralExpr) + ABSTRACT_EXPR(BuiltinLiteral, LiteralExpr) + LITERAL_EXPR(BooleanLiteral, BuiltinLiteralExpr) + ABSTRACT_EXPR(NumberLiteral, BuiltinLiteralExpr) + LITERAL_EXPR(IntegerLiteral, NumberLiteralExpr) + LITERAL_EXPR(FloatLiteral, NumberLiteralExpr) + EXPR_RANGE(NumberLiteral, IntegerLiteral, FloatLiteral) + LITERAL_EXPR(StringLiteral, BuiltinLiteralExpr) + LITERAL_EXPR(MagicIdentifierLiteral, BuiltinLiteralExpr) + EXPR_RANGE(BuiltinLiteral, BooleanLiteral, MagicIdentifierLiteral) LITERAL_EXPR(InterpolatedStringLiteral, LiteralExpr) LITERAL_EXPR(ObjectLiteral, LiteralExpr) - LITERAL_EXPR(MagicIdentifierLiteral, LiteralExpr) - EXPR_RANGE(Literal, NilLiteral, MagicIdentifierLiteral) + EXPR_RANGE(Literal, NilLiteral, ObjectLiteral) EXPR(DiscardAssignment, Expr) EXPR(DeclRef, Expr) EXPR(SuperRef, Expr) diff --git a/include/swift/AST/ExtInfo.h b/include/swift/AST/ExtInfo.h index ffea1db284b01..0620c14285a0c 100644 --- a/include/swift/AST/ExtInfo.h +++ b/include/swift/AST/ExtInfo.h @@ -70,6 +70,7 @@ class ClangTypeInfo { constexpr ClangTypeInfo(const clang::Type *type) : type(type) {} friend bool operator==(ClangTypeInfo lhs, ClangTypeInfo rhs); + friend bool operator!=(ClangTypeInfo lhs, ClangTypeInfo rhs); ClangTypeInfo getCanonical() const; public: diff --git a/include/swift/AST/FileUnit.h b/include/swift/AST/FileUnit.h index ec8ea98f22b88..16d6e46d749dd 100644 --- a/include/swift/AST/FileUnit.h +++ b/include/swift/AST/FileUnit.h @@ -235,12 +235,12 @@ class FileUnit : public DeclContext { /// \p filter controls whether public, private, or any imports are included /// in this list. virtual void - getImportedModules(SmallVectorImpl &imports, + getImportedModules(SmallVectorImpl &imports, ModuleDecl::ImportFilter filter) const {} /// \see ModuleDecl::getImportedModulesForLookup virtual void getImportedModulesForLookup( - SmallVectorImpl &imports) const { + SmallVectorImpl &imports) const { return getImportedModules(imports, ModuleDecl::ImportFilterKind::Exported); } diff --git a/include/swift/AST/FineGrainedDependencies.h b/include/swift/AST/FineGrainedDependencies.h index a73c026580c00..e701ad31cce8a 100644 --- a/include/swift/AST/FineGrainedDependencies.h +++ b/include/swift/AST/FineGrainedDependencies.h @@ -59,11 +59,14 @@ namespace swift { class DependencyTracker; class DiagnosticEngine; class FrontendOptions; +class ModuleDecl; class SourceFile; /// Use a new namespace to help keep the experimental code from clashing. namespace fine_grained_dependencies { +class SourceFileDepGraph; + using StringVec = std::vector; template using ConstPtrVec = std::vector; @@ -343,11 +346,15 @@ class BiIndexedTwoStageMap { // MARK: Start of fine-grained-dependency-specific code //============================================================================== -/// The entry point into this system from the frontend: -/// Write out the .swiftdeps file for a frontend compilation of a primary file. -bool emitReferenceDependencies(DiagnosticEngine &diags, SourceFile *SF, - const DependencyTracker &depTracker, - StringRef outputPath, bool alsoEmitDotFile); +/// Uses the provided module or source file to construct a dependency graph, +/// which is provided back to the caller in the continuation callback. +/// +/// \Note The returned graph should not be escaped from the callback. +bool withReferenceDependencies( + llvm::PointerUnion MSF, + const DependencyTracker &depTracker, StringRef outputPath, + bool alsoEmitDotFile, llvm::function_ref); + //============================================================================== // MARK: Enums //============================================================================== @@ -844,6 +851,8 @@ class SourceFileDepGraph { /// Read a swiftdeps file from \p buffer and return a SourceFileDepGraph if /// successful. Optional static loadFromBuffer(llvm::MemoryBuffer &); + Optional static loadFromSwiftModuleBuffer( + llvm::MemoryBuffer &); void verifySame(const SourceFileDepGraph &other) const; diff --git a/include/swift/AST/FineGrainedDependencyFormat.h b/include/swift/AST/FineGrainedDependencyFormat.h index a4d35695ceebb..9a3030d3dc10f 100644 --- a/include/swift/AST/FineGrainedDependencyFormat.h +++ b/include/swift/AST/FineGrainedDependencyFormat.h @@ -48,6 +48,7 @@ using NodeKindField = BCFixed<3>; using DeclAspectField = BCFixed<1>; const unsigned RECORD_BLOCK_ID = llvm::bitc::FIRST_APPLICATION_BLOCKID; +const unsigned INCREMENTAL_INFORMATION_BLOCK_ID = 196; /// The swiftdeps file format consists of a METADATA record, followed by zero or more /// IDENTIFIER_NODE records. @@ -113,10 +114,16 @@ namespace record_block { } /// Tries to read the dependency graph from the given buffer. -/// Returns true if there was an error. +/// Returns \c true if there was an error. bool readFineGrainedDependencyGraph(llvm::MemoryBuffer &buffer, SourceFileDepGraph &g); +/// Tries to read the dependency graph from the given buffer, assuming that it +/// is in the format of a swiftmodule file. +/// Returns \c true if there was an error. +bool readFineGrainedDependencyGraphFromSwiftModule(llvm::MemoryBuffer &buffer, + SourceFileDepGraph &g); + /// Tries to read the dependency graph from the given path name. /// Returns true if there was an error. bool readFineGrainedDependencyGraph(llvm::StringRef path, @@ -124,8 +131,36 @@ bool readFineGrainedDependencyGraph(llvm::StringRef path, /// Tries to write the dependency graph to the given path name. /// Returns true if there was an error. -bool writeFineGrainedDependencyGraph(DiagnosticEngine &diags, llvm::StringRef path, - const SourceFileDepGraph &g); +bool writeFineGrainedDependencyGraphToPath(DiagnosticEngine &diags, + llvm::StringRef path, + const SourceFileDepGraph &g); + +/// Enumerates the supported set of purposes for writing out or reading in +/// swift dependency information into a file. These can be used to influence +/// the structure of the resulting data that is produced by the serialization +/// machinery defined here. +enum class Purpose : bool { + /// Write out fine grained dependency metadata suitable for embedding in + /// \c .swiftmodule file. + /// + /// The resulting metadata does not contain the usual block descriptor header + /// nor does it contain a leading magic signature, which would otherwise + /// disrupt clients and tools that do not expect them to be present such as + /// llvm-bcanalyzer. + ForSwiftModule = false, + /// Write out fine grained dependency metadata suitable for a standalone + /// \c .swiftdeps file. + /// + /// The resulting metadata will contain a leading magic signature and block + /// descriptor header. + ForSwiftDeps = true, +}; + +/// Tries to write out the given dependency graph with the given +/// bitstream writer. +void writeFineGrainedDependencyGraph(llvm::BitstreamWriter &Out, + const SourceFileDepGraph &g, + Purpose purpose); } // namespace fine_grained_dependencies } // namespace swift diff --git a/include/swift/AST/ForeignErrorConvention.h b/include/swift/AST/ForeignErrorConvention.h index d09a7a80c0d86..3d9d95280c0e2 100644 --- a/include/swift/AST/ForeignErrorConvention.h +++ b/include/swift/AST/ForeignErrorConvention.h @@ -198,6 +198,7 @@ class ForeignErrorConvention { case NonNilError: return false; } + llvm_unreachable("unhandled foreign error kind!"); } bool operator==(ForeignErrorConvention other) const { diff --git a/include/swift/AST/FunctionRefKind.h b/include/swift/AST/FunctionRefKind.h index bcde3a53b6059..50c32b16a4fe6 100644 --- a/include/swift/AST/FunctionRefKind.h +++ b/include/swift/AST/FunctionRefKind.h @@ -17,6 +17,8 @@ #ifndef SWIFT_AST_FUNCTION_REF_KIND_H #define SWIFT_AST_FUNCTION_REF_KIND_H +#include "llvm/ADT/StringRef.h" + namespace swift { /// Describes how a function is referenced within an expression node, @@ -43,7 +45,7 @@ enum class FunctionRefKind : unsigned { /// Produce a string describing a function reference kind, for /// debugging purposes. -StringRef getFunctionRefKindStr(FunctionRefKind refKind); +llvm::StringRef getFunctionRefKindStr(FunctionRefKind refKind); } diff --git a/include/swift/AST/GenericEnvironment.h b/include/swift/AST/GenericEnvironment.h index a47067b940fa7..3c269644ea980 100644 --- a/include/swift/AST/GenericEnvironment.h +++ b/include/swift/AST/GenericEnvironment.h @@ -18,8 +18,9 @@ #define SWIFT_AST_GENERIC_ENVIRONMENT_H #include "swift/AST/SubstitutionMap.h" -#include "swift/AST/GenericSignature.h" #include "swift/AST/GenericParamKey.h" +#include "swift/AST/GenericParamList.h" +#include "swift/AST/GenericSignature.h" #include "swift/Basic/Compiler.h" #include "swift/Basic/Debug.h" #include "llvm/ADT/ArrayRef.h" diff --git a/include/swift/AST/GenericParamList.h b/include/swift/AST/GenericParamList.h new file mode 100644 index 0000000000000..2d3b8c34475c3 --- /dev/null +++ b/include/swift/AST/GenericParamList.h @@ -0,0 +1,392 @@ +//===--- GenericParamList.h - Generic parameter list AST --------*- 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 defines the GenericParamList class, and related classes. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_GENERIC_PARAM_LIST_H +#define SWIFT_GENERIC_PARAM_LIST_H + +#include "swift/AST/Decl.h" +#include "swift/AST/LayoutConstraint.h" +#include "swift/Basic/SourceLoc.h" +#include "llvm/ADT/StringRef.h" + +namespace swift { + +class ASTPrinter; +class TypeRepr; + +enum class RequirementReprKind : unsigned { + /// A type bound T : P, where T is a type that depends on a generic + /// parameter and P is some type that should bound T, either as a concrete + /// supertype or a protocol to which T must conform. + TypeConstraint, + + /// A same-type requirement T == U, where T and U are types that shall be + /// equivalent. + SameType, + + /// A layout bound T : L, where T is a type that depends on a generic + /// parameter and L is some layout specification that should bound T. + LayoutConstraint, + + // Note: there is code that packs this enum in a 2-bit bitfield. Audit users + // when adding enumerators. +}; + +/// A single requirement in a 'where' clause, which places additional +/// restrictions on the generic parameters or associated types of a generic +/// function, type, or protocol. +/// +/// This always represents a requirement spelled in the source code. It is +/// never generated implicitly. +/// +/// \c GenericParamList assumes these are POD-like. + +class RequirementRepr { + SourceLoc SeparatorLoc; + RequirementReprKind Kind : 2; + bool Invalid : 1; + TypeRepr *FirstType; + + /// The second element represents the right-hand side of the constraint. + /// It can be e.g. a type or a layout constraint. + union { + TypeRepr *SecondType; + LayoutConstraintLoc SecondLayout; + }; + + /// Set during deserialization; used to print out the requirements accurately + /// for the generated interface. + StringRef AsWrittenString; + + RequirementRepr(SourceLoc SeparatorLoc, RequirementReprKind Kind, + TypeRepr *FirstType, TypeRepr *SecondType) + : SeparatorLoc(SeparatorLoc), Kind(Kind), Invalid(false), + FirstType(FirstType), SecondType(SecondType) { } + + RequirementRepr(SourceLoc SeparatorLoc, RequirementReprKind Kind, + TypeRepr *FirstType, LayoutConstraintLoc SecondLayout) + : SeparatorLoc(SeparatorLoc), Kind(Kind), Invalid(false), + FirstType(FirstType), SecondLayout(SecondLayout) { } + + void printImpl(ASTPrinter &OS) const; + +public: + /// Construct a new type-constraint requirement. + /// + /// \param Subject The type that must conform to the given protocol or + /// composition, or be a subclass of the given class type. + /// \param ColonLoc The location of the ':', or an invalid location if + /// this requirement was implied. + /// \param Constraint The protocol or protocol composition to which the + /// subject must conform, or superclass from which the subject must inherit. + static RequirementRepr getTypeConstraint(TypeRepr *Subject, + SourceLoc ColonLoc, + TypeRepr *Constraint) { + return { ColonLoc, RequirementReprKind::TypeConstraint, Subject, Constraint }; + } + + /// Construct a new same-type requirement. + /// + /// \param FirstType The first type. + /// \param EqualLoc The location of the '==' in the same-type constraint, or + /// an invalid location if this requirement was implied. + /// \param SecondType The second type. + static RequirementRepr getSameType(TypeRepr *FirstType, + SourceLoc EqualLoc, + TypeRepr *SecondType) { + return { EqualLoc, RequirementReprKind::SameType, FirstType, SecondType }; + } + + /// Construct a new layout-constraint requirement. + /// + /// \param Subject The type that must conform to the given layout + /// requirement. + /// \param ColonLoc The location of the ':', or an invalid location if + /// this requirement was implied. + /// \param Layout The layout requirement to which the + /// subject must conform. + static RequirementRepr getLayoutConstraint(TypeRepr *Subject, + SourceLoc ColonLoc, + LayoutConstraintLoc Layout) { + return {ColonLoc, RequirementReprKind::LayoutConstraint, Subject, + Layout}; + } + + /// Determine the kind of requirement + RequirementReprKind getKind() const { return Kind; } + + /// Determine whether this requirement is invalid. + bool isInvalid() const { return Invalid; } + + /// Mark this requirement invalid. + void setInvalid() { Invalid = true; } + + /// For a type-bound requirement, return the subject of the + /// conformance relationship. + TypeRepr *getSubjectRepr() const { + assert(getKind() == RequirementReprKind::TypeConstraint || + getKind() == RequirementReprKind::LayoutConstraint); + return FirstType; + } + + /// For a type-bound requirement, return the protocol or to which + /// the subject conforms or superclass it inherits. + TypeRepr *getConstraintRepr() const { + assert(getKind() == RequirementReprKind::TypeConstraint); + return SecondType; + } + + LayoutConstraint getLayoutConstraint() const { + assert(getKind() == RequirementReprKind::LayoutConstraint); + return SecondLayout.getLayoutConstraint(); + } + + LayoutConstraintLoc &getLayoutConstraintLoc() { + assert(getKind() == RequirementReprKind::LayoutConstraint); + return SecondLayout; + } + + const LayoutConstraintLoc &getLayoutConstraintLoc() const { + assert(getKind() == RequirementReprKind::LayoutConstraint); + return SecondLayout; + } + + /// Retrieve the first type of a same-type requirement. + TypeRepr *getFirstTypeRepr() const { + assert(getKind() == RequirementReprKind::SameType); + return FirstType; + } + + /// Retrieve the second type of a same-type requirement. + TypeRepr *getSecondTypeRepr() const { + assert(getKind() == RequirementReprKind::SameType); + return SecondType; + } + + /// Retrieve the location of the ':' or '==' in an explicitly-written + /// conformance or same-type requirement respectively. + SourceLoc getSeparatorLoc() const { + return SeparatorLoc; + } + + SourceRange getSourceRange() const; + + /// Retrieve the first or subject type representation from the \c repr, + /// or \c nullptr if \c repr is null. + static TypeRepr *getFirstTypeRepr(const RequirementRepr *repr) { + if (!repr) return nullptr; + return repr->FirstType; + } + + /// Retrieve the second or constraint type representation from the \c repr, + /// or \c nullptr if \c repr is null. + static TypeRepr *getSecondTypeRepr(const RequirementRepr *repr) { + if (!repr) return nullptr; + assert(repr->getKind() == RequirementReprKind::TypeConstraint || + repr->getKind() == RequirementReprKind::SameType); + return repr->SecondType; + } + + SWIFT_DEBUG_DUMP; + void print(raw_ostream &OS) const; + void print(ASTPrinter &Printer) const; +}; + +using GenericParamSource = PointerUnion; + +/// GenericParamList - A list of generic parameters that is part of a generic +/// function or type, along with extra requirements placed on those generic +/// parameters and types derived from them. +class GenericParamList final : + private llvm::TrailingObjects { + friend TrailingObjects; + + SourceRange Brackets; + unsigned NumParams; + SourceLoc WhereLoc; + MutableArrayRef Requirements; + + GenericParamList *OuterParameters; + + GenericParamList(SourceLoc LAngleLoc, + ArrayRef Params, + SourceLoc WhereLoc, + MutableArrayRef Requirements, + SourceLoc RAngleLoc); + + // Don't copy. + GenericParamList(const GenericParamList &) = delete; + GenericParamList &operator=(const GenericParamList &) = delete; + +public: + /// create - Create a new generic parameter list within the given AST context. + /// + /// \param Context The ASTContext in which the generic parameter list will + /// be allocated. + /// \param LAngleLoc The location of the opening angle bracket ('<') + /// \param Params The list of generic parameters, which will be copied into + /// ASTContext-allocated memory. + /// \param RAngleLoc The location of the closing angle bracket ('>') + static GenericParamList *create(ASTContext &Context, + SourceLoc LAngleLoc, + ArrayRef Params, + SourceLoc RAngleLoc); + + /// create - Create a new generic parameter list and "where" clause within + /// the given AST context. + /// + /// \param Context The ASTContext in which the generic parameter list will + /// be allocated. + /// \param LAngleLoc The location of the opening angle bracket ('<') + /// \param Params The list of generic parameters, which will be copied into + /// ASTContext-allocated memory. + /// \param WhereLoc The location of the 'where' keyword, if any. + /// \param Requirements The list of requirements, which will be copied into + /// ASTContext-allocated memory. + /// \param RAngleLoc The location of the closing angle bracket ('>') + static GenericParamList *create(const ASTContext &Context, + SourceLoc LAngleLoc, + ArrayRef Params, + SourceLoc WhereLoc, + ArrayRef Requirements, + SourceLoc RAngleLoc); + + MutableArrayRef getParams() { + return {getTrailingObjects(), NumParams}; + } + + ArrayRef getParams() const { + return {getTrailingObjects(), NumParams}; + } + + using iterator = GenericTypeParamDecl **; + using const_iterator = const GenericTypeParamDecl * const *; + + unsigned size() const { return NumParams; } + iterator begin() { return getParams().begin(); } + iterator end() { return getParams().end(); } + const_iterator begin() const { return getParams().begin(); } + const_iterator end() const { return getParams().end(); } + + /// Retrieve the location of the 'where' keyword, or an invalid + /// location if 'where' was not present. + SourceLoc getWhereLoc() const { return WhereLoc; } + + /// Retrieve the set of additional requirements placed on these + /// generic parameters and types derived from them. + /// + /// This list may contain both explicitly-written requirements as well as + /// implicitly-generated requirements, and may be non-empty even if no + /// 'where' keyword is present. + MutableArrayRef getRequirements() { return Requirements; } + + /// Retrieve the set of additional requirements placed on these + /// generic parameters and types derived from them. + /// + /// This list may contain both explicitly-written requirements as well as + /// implicitly-generated requirements, and may be non-empty even if no + /// 'where' keyword is present. + ArrayRef getRequirements() const { return Requirements; } + + /// Retrieve the outer generic parameter list. + /// + /// This is used for extensions of nested types, and in SIL mode, where a + /// single lexical context can have multiple logical generic parameter + /// lists. + GenericParamList *getOuterParameters() const { return OuterParameters; } + + /// Set the outer generic parameter list. See \c getOuterParameters + /// for more information. + void setOuterParameters(GenericParamList *Outer) { OuterParameters = Outer; } + + void setDeclContext(DeclContext *dc); + + SourceLoc getLAngleLoc() const { return Brackets.Start; } + SourceLoc getRAngleLoc() const { return Brackets.End; } + + SourceRange getSourceRange() const { return Brackets; } + + /// Retrieve the source range covering the where clause. + SourceRange getWhereClauseSourceRange() const { + if (WhereLoc.isInvalid()) + return SourceRange(); + + auto endLoc = Requirements.back().getSourceRange().End; + return SourceRange(WhereLoc, endLoc); + } + + /// Configure the depth of the generic parameters in this list. + void setDepth(unsigned depth); + + /// Create a copy of the generic parameter list and all of its generic + /// parameter declarations. The copied generic parameters are re-parented + /// to the given DeclContext. + GenericParamList *clone(DeclContext *dc) const; + + void print(raw_ostream &OS) const; + SWIFT_DEBUG_DUMP; + + bool walk(ASTWalker &walker); + + /// Finds a generic parameter declaration by name. This should only + /// be used from the SIL parser. + GenericTypeParamDecl *lookUpGenericParam(Identifier name) const; +}; + +/// A trailing where clause. +class alignas(RequirementRepr) TrailingWhereClause final : + private llvm::TrailingObjects { + friend TrailingObjects; + + SourceLoc WhereLoc; + + /// The number of requirements. The actual requirements are tail-allocated. + unsigned NumRequirements; + + TrailingWhereClause(SourceLoc whereLoc, + ArrayRef requirements); + +public: + /// Create a new trailing where clause with the given set of requirements. + static TrailingWhereClause *create(ASTContext &ctx, SourceLoc whereLoc, + ArrayRef requirements); + + /// Retrieve the location of the 'where' keyword. + SourceLoc getWhereLoc() const { return WhereLoc; } + + /// Retrieve the set of requirements. + MutableArrayRef getRequirements() { + return {getTrailingObjects(), NumRequirements}; + } + + /// Retrieve the set of requirements. + ArrayRef getRequirements() const { + return {getTrailingObjects(), NumRequirements}; + } + + /// Compute the source range containing this trailing where clause. + SourceRange getSourceRange() const { + return SourceRange(WhereLoc, + getRequirements().back().getSourceRange().End); + } + + void print(llvm::raw_ostream &OS, bool printWhereKeyword) const; + +}; + +} // namespace swift + +#endif // SWIFT_GENERIC_PARAM_LIST_H \ No newline at end of file diff --git a/include/swift/AST/GenericSignature.h b/include/swift/AST/GenericSignature.h index 3a29d21fbd890..fccbe06bde122 100644 --- a/include/swift/AST/GenericSignature.h +++ b/include/swift/AST/GenericSignature.h @@ -20,6 +20,7 @@ #include "swift/AST/PrintOptions.h" #include "swift/AST/Requirement.h" #include "swift/AST/Type.h" +#include "swift/AST/TypeAlignments.h" #include "swift/Basic/AnyValue.h" #include "swift/Basic/Debug.h" #include "llvm/ADT/ArrayRef.h" diff --git a/include/swift/AST/GenericSignatureBuilder.h b/include/swift/AST/GenericSignatureBuilder.h index e91543b476539..6b8c507569a69 100644 --- a/include/swift/AST/GenericSignatureBuilder.h +++ b/include/swift/AST/GenericSignatureBuilder.h @@ -22,6 +22,7 @@ #include "swift/AST/Decl.h" #include "swift/AST/DiagnosticEngine.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/GenericSignature.h" #include "swift/AST/Identifier.h" #include "swift/AST/ProtocolConformanceRef.h" diff --git a/include/swift/AST/Import.h b/include/swift/AST/Import.h index b4649d259b5c9..b6a1f7a2d352a 100644 --- a/include/swift/AST/Import.h +++ b/include/swift/AST/Import.h @@ -21,13 +21,21 @@ #include "swift/AST/Identifier.h" #include "swift/Basic/Located.h" +#include "swift/Basic/OptionSet.h" #include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/DenseMapInfo.h" +#include "llvm/ADT/PointerIntPair.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" +#include namespace swift { class ASTContext; +class ModuleDecl; + +// MARK: - Fundamental import enums /// Describes what kind of name is being imported. /// @@ -44,6 +52,43 @@ enum class ImportKind : uint8_t { Func }; +inline bool isScopedImportKind(ImportKind importKind) { + return importKind != ImportKind::Module; +} + +/// Possible attributes for imports in source files. +enum class ImportFlags { + /// The imported module is exposed to anyone who imports the parent module. + Exported = 0x1, + + /// This source file has access to testable declarations in the imported + /// module. + Testable = 0x2, + + /// This source file has access to private declarations in the imported + /// module. + PrivateImport = 0x4, + + /// The imported module is an implementation detail of this file and should + /// not be required to be present if the main module is ever imported + /// elsewhere. + /// + /// Mutually exclusive with Exported. + ImplementationOnly = 0x8, + + /// The module is imported to have access to named SPIs which is an + /// implementation detail of this file. + SPIAccessControl = 0x10, + + /// Used for DenseMap. + Reserved = 0x80 +}; + +/// \see ImportFlags +using ImportOptions = OptionSet; + +// MARK: - Import Paths + namespace detail { using ImportPathElement = Located; using ImportPathRaw = llvm::ArrayRef; @@ -107,6 +152,17 @@ namespace detail { if (empty()) return SourceRange(); return SourceRange(raw.front().Loc, raw.back().Loc); } + + void print(llvm::raw_ostream &os) const { + llvm::interleave(*this, + [&](Element elem) { os << elem.Item.str(); }, + [&]() { os << "."; }); + } + + void getString(SmallVectorImpl &modulePathStr) const { + llvm::raw_svector_ostream os(modulePathStr); + print(os); + } }; // These shims avoid circularity between ASTContext.h and Import.h. @@ -117,9 +173,17 @@ namespace detail { template class ImportPathBuilder { - llvm::SmallVector scratch; + using Scratch = llvm::SmallVector; + Scratch scratch; public: + using value_type = Scratch::value_type; + using reference = Scratch::reference; + using iterator = Scratch::iterator; + using const_iterator = Scratch::const_iterator; + using difference_type = Scratch::difference_type; + using size_type = Scratch::size_type; + Subclass get() const { return Subclass(scratch); } @@ -191,6 +255,20 @@ namespace detail { }; } +/// @name ImportPathBase Comparison Operators +/// @{ +template +inline bool operator<(const detail::ImportPathBase &LHS, + const detail::ImportPathBase &RHS) { + using Element = typename detail::ImportPathBase::Element; + auto Comparator = [](const Element &l, const Element &r) { + return l.Item.compare(r.Item) < 0; + }; + return std::lexicographical_compare(LHS.begin(), LHS.end(), RHS.begin(), + RHS.end(), Comparator); +} +/// @} + /// An undifferentiated series of dotted identifiers in an \c import statement, /// like \c Foo.Bar. Each identifier is packaged with its corresponding source /// location. @@ -337,16 +415,326 @@ class ImportPath : public detail::ImportPathBase { /// including submodules, assuming the \c ImportDecl has the indicated /// \c importKind. Module getModulePath(ImportKind importKind) const { - return getModulePath(importKind != ImportKind::Module); + return getModulePath(isScopedImportKind(importKind)); } /// Extracts the portion of the \c ImportPath which represents a scope for the /// import, assuming the \c ImportDecl has the indicated \c importKind. Access getAccessPath(ImportKind importKind) const { - return getAccessPath(importKind != ImportKind::Module); + return getAccessPath(isScopedImportKind(importKind)); + } +}; + +// MARK: - Abstractions of imports + +/// Convenience struct to keep track of an import path and whether or not it +/// is scoped. +class UnloadedImportedModule { + // This is basically an ArrayRef with a bit stolen from the pointer. + // FIXME: Extract an ArrayRefIntPair type from this. + llvm::PointerIntPair dataAndIsScoped; + ImportPath::Raw::size_type length; + + ImportPath::Raw::iterator data() const { + return dataAndIsScoped.getPointer(); + } + + bool isScoped() const { + return dataAndIsScoped.getInt(); + } + + ImportPath::Raw getRaw() const { + return ImportPath::Raw(data(), length); + } + + UnloadedImportedModule(ImportPath::Raw raw, bool isScoped) + : dataAndIsScoped(raw.data(), isScoped), length(raw.size()) { } + +public: + UnloadedImportedModule(ImportPath importPath, bool isScoped) + : UnloadedImportedModule(importPath.getRaw(), isScoped) { } + + UnloadedImportedModule(ImportPath importPath, ImportKind importKind) + : UnloadedImportedModule(importPath, isScopedImportKind(importKind)) { } + + ImportPath getImportPath() const { + return ImportPath(getRaw()); + } + + ImportPath::Module getModulePath() const { + return getImportPath().getModulePath(isScoped()); + } + + ImportPath::Access getAccessPath() const { + return getImportPath().getAccessPath(isScoped()); + } + + friend bool operator==(const UnloadedImportedModule &lhs, + const UnloadedImportedModule &rhs) { + return (lhs.getRaw() == rhs.getRaw()) && + (lhs.isScoped() == rhs.isScoped()); } }; +/// Convenience struct to keep track of a module along with its access path. +struct alignas(uint64_t) ImportedModule { + /// The access path from an import: `import Foo.Bar` -> `Foo.Bar`. + ImportPath::Access accessPath; + /// The actual module corresponding to the import. + /// + /// Invariant: The pointer is non-null. + ModuleDecl *importedModule; + + ImportedModule(ImportPath::Access accessPath, + ModuleDecl *importedModule) + : accessPath(accessPath), importedModule(importedModule) { + assert(this->importedModule); + } + + explicit ImportedModule(ModuleDecl *importedModule) + : ImportedModule(ImportPath::Access(), importedModule) { } + + bool operator==(const ImportedModule &other) const { + return (this->importedModule == other.importedModule) && + (this->accessPath == other.accessPath); + } + + /// Uniques the items in \p imports, ignoring the source locations of the + /// access paths. + /// + /// The order of items in \p imports is \e not preserved. + static void removeDuplicates(SmallVectorImpl &imports); + + // Purely here to allow ImportedModule and UnloadedImportedModule to + // substitute into the same templates. + ImportPath::Access getAccessPath() const { return accessPath; } + + /// Arbitrarily orders ImportedModule records, for inclusion in sets and such. + class Order { + public: + bool operator()(const ImportedModule &lhs, + const ImportedModule &rhs) const { + if (lhs.importedModule != rhs.importedModule) + return std::less()(lhs.importedModule, + rhs.importedModule); + if (lhs.accessPath.getRaw().data() != rhs.accessPath.getRaw().data()) + return std::less()(lhs.accessPath.begin(), + rhs.accessPath.begin()); + return lhs.accessPath.size() < rhs.accessPath.size(); + } + }; +}; + +/// Augments a type representing an import to also include information about the +/// import's attributes. This is usually used with either \c ImportedModule or +/// \c UnloadedImportedModule. +template +struct AttributedImport { + /// Information about the module and access path being imported. + ModuleInfo module; + + /// Flags indicating which attributes of this import are present. + ImportOptions options; + + /// If this is a @_private import, the value of its 'sourceFile:' argument; + /// otherwise, empty string. + StringRef sourceFileArg; + + /// Names of explicitly imported SPI groups. + ArrayRef spiGroups; + + AttributedImport(ModuleInfo module, ImportOptions options = ImportOptions(), + StringRef filename = {}, ArrayRef spiGroups = {}) + : module(module), options(options), sourceFileArg(filename), + spiGroups(spiGroups) { + assert(!(options.contains(ImportFlags::Exported) && + options.contains(ImportFlags::ImplementationOnly)) || + options.contains(ImportFlags::Reserved)); + } + + template + AttributedImport(ModuleInfo module, AttributedImport other) + : AttributedImport(module, other.options, other.sourceFileArg, + other.spiGroups) { } + + friend bool operator==(const AttributedImport &lhs, + const AttributedImport &rhs) { + return lhs.module == rhs.module && + lhs.options.toRaw() == rhs.options.toRaw() && + lhs.sourceFileArg == rhs.sourceFileArg && + lhs.spiGroups == rhs.spiGroups; + } + + AttributedImport getLoaded(ModuleDecl *loadedModule) const { + return { ImportedModule(module.getAccessPath(), loadedModule), *this }; + } +}; + +void simple_display(llvm::raw_ostream &out, + const ImportedModule &import); + +void simple_display(llvm::raw_ostream &out, + const UnloadedImportedModule &import); + +// This is a quasi-implementation detail of the template version below. +void simple_display(llvm::raw_ostream &out, + const AttributedImport> &import); + +template +void simple_display(llvm::raw_ostream &out, + const AttributedImport &import) { + // Print the module. + simple_display(out, import.module); + + // Print the other details of the import, using the std::tuple<> + // specialization. + AttributedImport> importWithoutModule({}, import); + simple_display(out, importWithoutModule); +} + +// MARK: - Implicit imports + +/// The kind of stdlib that should be imported. +enum class ImplicitStdlibKind { + /// No standard library should be implicitly imported. + None, + + /// The Builtin module should be implicitly imported. + Builtin, + + /// The regular Swift standard library should be implicitly imported. + Stdlib +}; + +/// Represents unprocessed options for implicit imports. +struct ImplicitImportInfo { + /// The implicit stdlib to import. + ImplicitStdlibKind StdlibKind; + + /// Whether we should attempt to import an underlying Clang half of this + /// module. + bool ShouldImportUnderlyingModule; + + /// The bridging header path for this module, empty if there is none. + StringRef BridgingHeaderPath; + + /// The names of additional modules to be loaded and implicitly imported. + SmallVector, 4> + AdditionalUnloadedImports; + + /// An additional list of already-loaded modules which should be implicitly + /// imported. + SmallVector, 4> + AdditionalImports; + + ImplicitImportInfo() + : StdlibKind(ImplicitStdlibKind::None), + ShouldImportUnderlyingModule(false) {} +}; + +/// Contains names of and pointers to modules that must be implicitly imported. +struct ImplicitImportList { + ArrayRef> imports; + ArrayRef> unloadedImports; + + friend bool operator==(const ImplicitImportList &lhs, + const ImplicitImportList &rhs) { + return lhs.imports == rhs.imports + && lhs.unloadedImports == rhs.unloadedImports; + } +}; + +/// A list of modules to implicitly import. +void simple_display(llvm::raw_ostream &out, + const ImplicitImportList &importList); + +} + +// MARK: - DenseMapInfo + +namespace llvm { + +template<> +struct DenseMapInfo { + using ImportOptions = swift::ImportOptions; + + using UnsignedDMI = DenseMapInfo; + + static inline ImportOptions getEmptyKey() { + return ImportOptions(UnsignedDMI::getEmptyKey()); + } + static inline ImportOptions getTombstoneKey() { + return ImportOptions(UnsignedDMI::getTombstoneKey()); + } + static inline unsigned getHashValue(ImportOptions options) { + return UnsignedDMI::getHashValue(options.toRaw()); + } + static bool isEqual(ImportOptions a, ImportOptions b) { + return UnsignedDMI::isEqual(a.toRaw(), b.toRaw()); + } +}; + +template <> +class DenseMapInfo { + using ImportedModule = swift::ImportedModule; + using ModuleDecl = swift::ModuleDecl; +public: + static ImportedModule getEmptyKey() { + return {{}, llvm::DenseMapInfo::getEmptyKey()}; + } + static ImportedModule getTombstoneKey() { + return {{}, llvm::DenseMapInfo::getTombstoneKey()}; + } + + static unsigned getHashValue(const ImportedModule &val) { + auto pair = std::make_pair(val.accessPath.size(), val.importedModule); + return llvm::DenseMapInfo::getHashValue(pair); + } + + static bool isEqual(const ImportedModule &lhs, + const ImportedModule &rhs) { + return lhs.importedModule == rhs.importedModule && + lhs.accessPath.isSameAs(rhs.accessPath); + } +}; + +template +struct DenseMapInfo> { + using AttributedImport = swift::AttributedImport; + + using ModuleInfoDMI = DenseMapInfo; + using ImportOptionsDMI = DenseMapInfo; + using StringRefDMI = DenseMapInfo; + // We can't include spiGroups in the hash because ArrayRef is not + // DenseMapInfo-able, but we do check that the spiGroups match in isEqual(). + + static inline AttributedImport getEmptyKey() { + return AttributedImport(ModuleInfoDMI::getEmptyKey(), + ImportOptionsDMI::getEmptyKey(), + StringRefDMI::getEmptyKey(), + {}); + } + static inline AttributedImport getTombstoneKey() { + return AttributedImport(ModuleInfoDMI::getTombstoneKey(), + ImportOptionsDMI::getTombstoneKey(), + StringRefDMI::getTombstoneKey(), + {}); + } + static inline unsigned getHashValue(const AttributedImport &import) { + return detail::combineHashValue( + ModuleInfoDMI::getHashValue(import.module), + detail::combineHashValue( + ImportOptionsDMI::getHashValue(import.options), + StringRefDMI::getHashValue(import.sourceFileArg))); + } + static bool isEqual(const AttributedImport &a, + const AttributedImport &b) { + return ModuleInfoDMI::isEqual(a.module, b.module) && + ImportOptionsDMI::isEqual(a.options, b.options) && + StringRefDMI::isEqual(a.sourceFileArg, b.sourceFileArg) && + a.spiGroups == b.spiGroups; + } +}; } #endif diff --git a/include/swift/AST/ImportCache.h b/include/swift/AST/ImportCache.h index e52e322c604a0..5334b5ddddd36 100644 --- a/include/swift/AST/ImportCache.h +++ b/include/swift/AST/ImportCache.h @@ -49,7 +49,7 @@ namespace namelookup { /// it was explicitly imported (or re-exported). class ImportSet final : public llvm::FoldingSetNode, - private llvm::TrailingObjects { + private llvm::TrailingObjects { friend TrailingObjects; friend class ImportCache; @@ -58,8 +58,8 @@ class ImportSet final : unsigned NumTransitiveImports; ImportSet(bool hasHeaderImportModule, - ArrayRef topLevelImports, - ArrayRef transitiveImports); + ArrayRef topLevelImports, + ArrayRef transitiveImports); ImportSet(const ImportSet &) = delete; void operator=(const ImportSet &) = delete; @@ -70,9 +70,9 @@ class ImportSet final : } static void Profile( llvm::FoldingSetNodeID &ID, - ArrayRef topLevelImports); + ArrayRef topLevelImports); - size_t numTrailingObjects(OverloadToken) const { + size_t numTrailingObjects(OverloadToken) const { return NumTopLevelImports + NumTransitiveImports; } @@ -83,24 +83,24 @@ class ImportSet final : return HasHeaderImportModule; } - ArrayRef getTopLevelImports() const { - return {getTrailingObjects(), + ArrayRef getTopLevelImports() const { + return {getTrailingObjects(), NumTopLevelImports}; } - ArrayRef getTransitiveImports() const { - return {getTrailingObjects() + + ArrayRef getTransitiveImports() const { + return {getTrailingObjects() + NumTopLevelImports, NumTransitiveImports}; } - ArrayRef getAllImports() const { - return {getTrailingObjects(), + ArrayRef getAllImports() const { + return {getTrailingObjects(), NumTopLevelImports + NumTransitiveImports}; } }; -class alignas(ModuleDecl::ImportedModule) ImportCache { +class alignas(ImportedModule) ImportCache { ImportCache(const ImportCache &) = delete; void operator=(const ImportCache &) = delete; @@ -121,7 +121,7 @@ class alignas(ModuleDecl::ImportedModule) ImportCache { SmallVectorImpl &results); ImportSet &getImportSet(ASTContext &ctx, - ArrayRef topLevelImports); + ArrayRef topLevelImports); public: ImportCache() {} @@ -154,7 +154,7 @@ class alignas(ModuleDecl::ImportedModule) ImportCache { } }; -ArrayRef getAllImports(const DeclContext *dc); +ArrayRef getAllImports(const DeclContext *dc); } // namespace namelookup diff --git a/include/swift/AST/KnownIdentifiers.def b/include/swift/AST/KnownIdentifiers.def index 27b678a28cc04..fa7d8d05b5de6 100644 --- a/include/swift/AST/KnownIdentifiers.def +++ b/include/swift/AST/KnownIdentifiers.def @@ -44,6 +44,7 @@ IDENTIFIER(Change) IDENTIFIER_WITH_NAME(code_, "_code") IDENTIFIER(CodingKeys) IDENTIFIER(combine) +IDENTIFIER_(Concurrency) IDENTIFIER(container) IDENTIFIER(CoreGraphics) IDENTIFIER(CoreMedia) @@ -67,6 +68,7 @@ IDENTIFIER(encode) IDENTIFIER(encodeIfPresent) IDENTIFIER(Encoder) IDENTIFIER(encoder) +IDENTIFIER(enqueue) IDENTIFIER(erasing) IDENTIFIER(error) IDENTIFIER(errorDomain) @@ -105,6 +107,8 @@ IDENTIFIER(oldValue) IDENTIFIER(Optional) IDENTIFIER_(OptionalNilComparisonType) IDENTIFIER(parameter) +IDENTIFIER(partialTask) +IDENTIFIER(PartialAsyncTask) IDENTIFIER(projected) IDENTIFIER(projectedValue) IDENTIFIER(Protocol) @@ -115,6 +119,7 @@ IDENTIFIER(Selector) IDENTIFIER(self) IDENTIFIER(Self) IDENTIFIER(setObject) +IDENTIFIER(shared) IDENTIFIER(simd) IDENTIFIER(storage) IDENTIFIER(stringValue) @@ -137,6 +142,7 @@ IDENTIFIER(withKeywordArguments) IDENTIFIER(wrapped) IDENTIFIER(wrappedValue) IDENTIFIER(wrapperValue) +IDENTIFIER_WITH_NAME(actorStorage, "$__actor_storage") // Kinds of layout constraints IDENTIFIER_WITH_NAME(UnknownLayout, "_UnknownLayout") diff --git a/include/swift/AST/KnownObjCTypes.def b/include/swift/AST/KnownObjCTypes.def deleted file mode 100644 index b77eb4f893554..0000000000000 --- a/include/swift/AST/KnownObjCTypes.def +++ /dev/null @@ -1,37 +0,0 @@ -//===--- KnownObjCTypes.def - Common Objective-C types --------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2019 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// -// -// This xmacro generates code for common imported Objective-C types the -// compiler has special knowledge of. -// -//===----------------------------------------------------------------------===// - -#ifndef KNOWN_OBJC_TYPE_DECL -/// KNOWN_OBJC_TYPE_DECL(MODULE, NAME, DECL_CLASS) -/// -/// The macro is expanded for each known imported Objective-C type. MODULE is -/// bound to the name of the module the type comes from. NAME is bound to the -/// unqualified name of the type. DECL_CLASS is bound to the Decl subclass it -/// is expected to be an instance of. -#define KNOWN_OBJC_TYPE_DECL(MODULE, NAME, DECL_CLASS) -#endif - -KNOWN_OBJC_TYPE_DECL(Foundation, NSCopying, ProtocolDecl) -KNOWN_OBJC_TYPE_DECL(Foundation, NSError, ClassDecl) -KNOWN_OBJC_TYPE_DECL(Foundation, NSNumber, ClassDecl) -KNOWN_OBJC_TYPE_DECL(Foundation, NSValue, ClassDecl) - -KNOWN_OBJC_TYPE_DECL(ObjectiveC, NSObject, ClassDecl) -KNOWN_OBJC_TYPE_DECL(ObjectiveC, Selector, StructDecl) -KNOWN_OBJC_TYPE_DECL(ObjectiveC, ObjCBool, StructDecl) - -#undef KNOWN_OBJC_TYPE_DECL diff --git a/include/swift/AST/KnownProtocols.def b/include/swift/AST/KnownProtocols.def index 0166353404040..d0b26260c0546 100644 --- a/include/swift/AST/KnownProtocols.def +++ b/include/swift/AST/KnownProtocols.def @@ -58,6 +58,7 @@ #define BUILTIN_EXPRESSIBLE_BY_LITERAL_PROTOCOL_(name) \ BUILTIN_EXPRESSIBLE_BY_LITERAL_PROTOCOL_WITH_NAME(name, "_" #name) +PROTOCOL(Actor) PROTOCOL(Sequence) PROTOCOL(IteratorProtocol) PROTOCOL(RawRepresentable) diff --git a/include/swift/AST/KnownSDKTypes.def b/include/swift/AST/KnownSDKTypes.def new file mode 100644 index 0000000000000..aaaa2c45411d1 --- /dev/null +++ b/include/swift/AST/KnownSDKTypes.def @@ -0,0 +1,43 @@ +//===--- KnownSDKTypes.def - Common SDK types -----------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This xmacro generates code for common SDK types the +// compiler has special knowledge of. +// +//===----------------------------------------------------------------------===// + +#ifndef KNOWN_SDK_TYPE_DECL +/// KNOWN_SDK_TYPE_DECL(MODULE, NAME, DECL_CLASS, NUM_GENERIC_ARGS) +/// +/// The macro is expanded for each known SDK type. MODULE is +/// bound to the name of the module the type comes from. NAME is bound to the +/// unqualified name of the type. DECL_CLASS is bound to the Decl subclass it +/// is expected to be an instance of. NUM_GENERIC_ARGS is the number of generic +/// parameters the decl ought to have. +#define KNOWN_SDK_TYPE_DECL(MODULE, NAME, DECL_CLASS, NUM_GENERIC_ARGS) +#endif + +KNOWN_SDK_TYPE_DECL(Foundation, NSCopying, ProtocolDecl, 0) +KNOWN_SDK_TYPE_DECL(Foundation, NSError, ClassDecl, 0) +KNOWN_SDK_TYPE_DECL(Foundation, NSNumber, ClassDecl, 0) +KNOWN_SDK_TYPE_DECL(Foundation, NSValue, ClassDecl, 0) + +KNOWN_SDK_TYPE_DECL(ObjectiveC, NSObject, ClassDecl, 0) +KNOWN_SDK_TYPE_DECL(ObjectiveC, Selector, StructDecl, 0) +KNOWN_SDK_TYPE_DECL(ObjectiveC, ObjCBool, StructDecl, 0) + +// TODO(async): These might move to the stdlib module when concurrency is +// standardized +KNOWN_SDK_TYPE_DECL(Concurrency, UnsafeContinuation, NominalTypeDecl, 1) +KNOWN_SDK_TYPE_DECL(Concurrency, UnsafeThrowingContinuation, NominalTypeDecl, 1) + +#undef KNOWN_SDK_TYPE_DECL diff --git a/include/swift/AST/LazyResolver.h b/include/swift/AST/LazyResolver.h index ee6e64ff25ac8..97632e4cbe45c 100644 --- a/include/swift/AST/LazyResolver.h +++ b/include/swift/AST/LazyResolver.h @@ -114,6 +114,10 @@ class alignas(void*) LazyMemberLoader { /// Returns the type for a given @_typeEraser() attribute. virtual Type loadTypeEraserType(const TypeEraserAttr *TRA, uint64_t contextData) = 0; + + // Returns the target parameter of the `@_specialize` attribute or null. + virtual ValueDecl *loadTargetFunctionDecl(const SpecializeAttr *attr, + uint64_t contextData) = 0; }; /// A class that can lazily load conformances from a serialized format. diff --git a/include/swift/AST/LookupKinds.h b/include/swift/AST/LookupKinds.h index 67d45bf74c3f5..aa1245bbe28fc 100644 --- a/include/swift/AST/LookupKinds.h +++ b/include/swift/AST/LookupKinds.h @@ -50,6 +50,9 @@ enum NLOptions : unsigned { /// Include synonyms declared with @_implements() NL_IncludeAttributeImplements = 1 << 5, + // Include @usableFromInline and @inlinable + NL_IncludeUsableFromInline = 1 << 6, + /// The default set of options used for qualified name lookup. /// /// FIXME: Eventually, add NL_ProtocolMembers to this, once all of the diff --git a/include/swift/AST/Module.h b/include/swift/AST/Module.h index 0d8fa3f1012be..c2b94e0e0fc10 100644 --- a/include/swift/AST/Module.h +++ b/include/swift/AST/Module.h @@ -56,7 +56,6 @@ namespace swift { class FuncDecl; class InfixOperatorDecl; class LinkLibrary; - struct ImplicitImport; class ModuleLoader; class NominalTypeDecl; class EnumElementDecl; @@ -154,42 +153,6 @@ enum class ResilienceStrategy : unsigned { Resilient }; -/// The kind of stdlib that should be imported. -enum class ImplicitStdlibKind { - /// No standard library should be implicitly imported. - None, - - /// The Builtin module should be implicitly imported. - Builtin, - - /// The regular Swift standard library should be implicitly imported. - Stdlib -}; - -struct ImplicitImportInfo { - /// The implicit stdlib to import. - ImplicitStdlibKind StdlibKind; - - /// Whether we should attempt to import an underlying Clang half of this - /// module. - bool ShouldImportUnderlyingModule; - - /// The bridging header path for this module, empty if there is none. - StringRef BridgingHeaderPath; - - /// The names of additional modules to be implicitly imported. - SmallVector ModuleNames; - - /// An additional list of already-loaded modules which should be implicitly - /// imported. - SmallVector, 4> - AdditionalModules; - - ImplicitImportInfo() - : StdlibKind(ImplicitStdlibKind::None), - ShouldImportUnderlyingModule(false) {} -}; - class OverlayFile; /// The minimum unit of compilation. @@ -203,42 +166,6 @@ class ModuleDecl : public DeclContext, public TypeDecl { friend class DirectPrecedenceGroupLookupRequest; public: - /// Convenience struct to keep track of a module along with its access path. - struct alignas(uint64_t) ImportedModule { - /// The access path from an import: `import Foo.Bar` -> `Foo.Bar`. - ImportPath::Access accessPath; - /// The actual module corresponding to the import. - /// - /// Invariant: The pointer is non-null. - ModuleDecl *importedModule; - - ImportedModule(ImportPath::Access accessPath, - ModuleDecl *importedModule) - : accessPath(accessPath), importedModule(importedModule) { - assert(this->importedModule); - } - - bool operator==(const ModuleDecl::ImportedModule &other) const { - return (this->importedModule == other.importedModule) && - (this->accessPath == other.accessPath); - } - }; - - /// Arbitrarily orders ImportedModule records, for inclusion in sets and such. - class OrderImportedModules { - public: - bool operator()(const ImportedModule &lhs, - const ImportedModule &rhs) const { - if (lhs.importedModule != rhs.importedModule) - return std::less()(lhs.importedModule, - rhs.importedModule); - if (lhs.accessPath.getRaw().data() != rhs.accessPath.getRaw().data()) - return std::less()(lhs.accessPath.begin(), - rhs.accessPath.begin()); - return lhs.accessPath.size() < rhs.accessPath.size(); - } - }; - /// Produces the components of a given module's full name in reverse order. /// /// For a Swift module, this will only ever have one component, but an @@ -350,7 +277,7 @@ class ModuleDecl : public DeclContext, public TypeDecl { /// Retrieve a list of modules that each file of this module implicitly /// imports. - ArrayRef getImplicitImports() const; + ImplicitImportList getImplicitImports() const; ArrayRef getFiles() { assert(!Files.empty() || failedToLoad()); @@ -517,6 +444,12 @@ class ModuleDecl : public DeclContext, public TypeDecl { Bits.ModuleDecl.RawResilienceStrategy = unsigned(strategy); } + /// Returns true if this module was or is being compiled for testing. + bool hasIncrementalInfo() const { return Bits.ModuleDecl.HasIncrementalInfo; } + void setHasIncrementalInfo(bool enabled = true) { + Bits.ModuleDecl.HasIncrementalInfo = enabled; + } + /// \returns true if this module is a system module; note that the StdLib is /// considered a system module. bool isSystemModule() const { @@ -645,6 +578,13 @@ class ModuleDecl : public DeclContext, public TypeDecl { const ModuleDecl *importedModule, llvm::SmallSetVector &spiGroups) const; + // Is \p attr accessible as an explictly imported SPI from this module? + bool isImportedAsSPI(const SpecializeAttr *attr, + const ValueDecl *targetDecl) const; + + // Is \p spiGroup accessible as an explictly imported SPI from this module? + bool isImportedAsSPI(Identifier spiGroup, const ModuleDecl *fromModule) const; + /// \sa getImportedModules enum class ImportFilterKind { /// Include imports declared with `@_exported`. @@ -653,8 +593,7 @@ class ModuleDecl : public DeclContext, public TypeDecl { Default = 1 << 1, /// Include imports declared with `@_implementationOnly`. ImplementationOnly = 1 << 2, - /// Include imports of SPIs declared with `@_spi`. Non-SPI imports are - /// included whether or not this flag is specified. + /// Include imports of SPIs declared with `@_spi` SPIAccessControl = 1 << 3, /// Include imports shadowed by a cross-import overlay. Unshadowed imports /// are included whether or not this flag is specified. @@ -665,33 +604,8 @@ class ModuleDecl : public DeclContext, public TypeDecl { /// Looks up which modules are imported by this module. /// - /// \p filter controls which imports are included in the list. - /// - /// There are three axes for categorizing imports: - /// 1. Privacy: Exported/Private/ImplementationOnly (mutually exclusive). - /// 2. SPI/non-SPI: An import of any privacy level may be @_spi("SPIName"). - /// 3. Shadowed/Non-shadowed: An import of any privacy level may be shadowed - /// by a cross-import overlay. - /// - /// It is also possible for SPI imports to be shadowed by a cross-import - /// overlay. - /// - /// If \p filter contains multiple privacy levels, modules at all the privacy - /// levels are included. - /// - /// If \p filter contains \c ImportFilterKind::SPIAccessControl, then both - /// SPI and non-SPI imports are included. Otherwise, only non-SPI imports are - /// included. - /// - /// If \p filter contains \c ImportFilterKind::ShadowedByCrossImportOverlay, - /// both shadowed and non-shadowed imports are included. Otherwise, only - /// non-shadowed imports are included. - /// - /// Clang modules have some additional complexities; see the implementation of - /// \c ClangModuleUnit::getImportedModules for details. - /// - /// \pre \p filter must contain at least one privacy level, i.e. one of - /// \c Exported or \c Private or \c ImplementationOnly. + /// \p filter controls whether public, private, or any imports are included + /// in this list. void getImportedModules(SmallVectorImpl &imports, ImportFilter filter = ImportFilterKind::Exported) const; @@ -709,12 +623,6 @@ class ModuleDecl : public DeclContext, public TypeDecl { /// This assumes that \p module was imported. bool isImportedImplementationOnly(const ModuleDecl *module) const; - /// Uniques the items in \p imports, ignoring the source locations of the - /// access paths. - /// - /// The order of items in \p imports is \e not preserved. - static void removeDuplicateImports(SmallVectorImpl &imports); - /// Finds all top-level decls of this module. /// /// This does a simple local lookup, not recursively looking through imports. @@ -881,29 +789,4 @@ inline SourceLoc extractNearestSourceLoc(const ModuleDecl *mod) { } // end namespace swift -namespace llvm { - template <> - class DenseMapInfo { - using ModuleDecl = swift::ModuleDecl; - public: - static ModuleDecl::ImportedModule getEmptyKey() { - return {{}, llvm::DenseMapInfo::getEmptyKey()}; - } - static ModuleDecl::ImportedModule getTombstoneKey() { - return {{}, llvm::DenseMapInfo::getTombstoneKey()}; - } - - static unsigned getHashValue(const ModuleDecl::ImportedModule &val) { - auto pair = std::make_pair(val.accessPath.size(), val.importedModule); - return llvm::DenseMapInfo::getHashValue(pair); - } - - static bool isEqual(const ModuleDecl::ImportedModule &lhs, - const ModuleDecl::ImportedModule &rhs) { - return lhs.importedModule == rhs.importedModule && - lhs.accessPath.isSameAs(rhs.accessPath); - } - }; -} - #endif diff --git a/include/swift/AST/ModuleDependencies.h b/include/swift/AST/ModuleDependencies.h index 5eab3844b32d8..992df4e4a3764 100644 --- a/include/swift/AST/ModuleDependencies.h +++ b/include/swift/AST/ModuleDependencies.h @@ -19,6 +19,7 @@ #define SWIFT_AST_MODULE_DEPENDENCIES_H #include "swift/Basic/LLVM.h" +#include "swift/AST/Import.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringSet.h" @@ -34,7 +35,8 @@ class Identifier; /// Which kind of module dependencies we are looking for. enum class ModuleDependenciesKind : int8_t { - Swift, + SwiftTextual, + SwiftBinary, // Placeholder dependencies are a kind of dependencies used only by the // dependency scanner. They are swift modules that the scanner will not be // able to locate in its search paths and which are the responsibility of the @@ -68,18 +70,13 @@ class ModuleDependenciesStorageBase { public: const ModuleDependenciesKind dependencyKind; - ModuleDependenciesStorageBase(ModuleDependenciesKind dependencyKind, - const std::string &compiledModulePath) - : dependencyKind(dependencyKind), - compiledModulePath(compiledModulePath) { } + ModuleDependenciesStorageBase(ModuleDependenciesKind dependencyKind) + : dependencyKind(dependencyKind) { } virtual ModuleDependenciesStorageBase *clone() const = 0; virtual ~ModuleDependenciesStorageBase(); - /// The path to the compiled module file. - const std::string compiledModulePath; - /// The set of modules on which this module depends. std::vector moduleDependencies; }; @@ -87,7 +84,8 @@ class ModuleDependenciesStorageBase { /// Describes the dependencies of a Swift module. /// /// This class is mostly an implementation detail for \c ModuleDependencies. -class SwiftModuleDependenciesStorage : public ModuleDependenciesStorageBase { +class SwiftTextualModuleDependenciesStorage : + public ModuleDependenciesStorageBase { public: /// The Swift interface file, if it can be used to generate the module file. const Optional swiftInterfaceFile; @@ -122,16 +120,14 @@ class SwiftModuleDependenciesStorage : public ModuleDependenciesStorageBase { /// (Clang) modules on which the bridging header depends. std::vector bridgingModuleDependencies; - SwiftModuleDependenciesStorage( - const std::string &compiledModulePath, + SwiftTextualModuleDependenciesStorage( const Optional &swiftInterfaceFile, ArrayRef compiledModuleCandidates, ArrayRef buildCommandLine, ArrayRef extraPCMArgs, StringRef contextHash, bool isFramework - ) : ModuleDependenciesStorageBase(ModuleDependenciesKind::Swift, - compiledModulePath), + ) : ModuleDependenciesStorageBase(ModuleDependenciesKind::SwiftTextual), swiftInterfaceFile(swiftInterfaceFile), compiledModuleCandidates(compiledModuleCandidates.begin(), compiledModuleCandidates.end()), @@ -140,11 +136,47 @@ class SwiftModuleDependenciesStorage : public ModuleDependenciesStorageBase { contextHash(contextHash), isFramework(isFramework) { } ModuleDependenciesStorageBase *clone() const override { - return new SwiftModuleDependenciesStorage(*this); + return new SwiftTextualModuleDependenciesStorage(*this); } static bool classof(const ModuleDependenciesStorageBase *base) { - return base->dependencyKind == ModuleDependenciesKind::Swift; + return base->dependencyKind == ModuleDependenciesKind::SwiftTextual; + } +}; + +/// Describes the dependencies of a pre-built Swift module (with no .swiftinterface). +/// +/// This class is mostly an implementation detail for \c ModuleDependencies. +class SwiftBinaryModuleDependencyStorage : public ModuleDependenciesStorageBase { +public: + SwiftBinaryModuleDependencyStorage(const std::string &compiledModulePath, + const std::string &moduleDocPath, + const std::string &sourceInfoPath, + const bool isFramework) + : ModuleDependenciesStorageBase(ModuleDependenciesKind::SwiftBinary), + compiledModulePath(compiledModulePath), + moduleDocPath(moduleDocPath), + sourceInfoPath(sourceInfoPath), + isFramework(isFramework) {} + + ModuleDependenciesStorageBase *clone() const override { + return new SwiftBinaryModuleDependencyStorage(*this); + } + + /// The path to the .swiftmodule file. + const std::string compiledModulePath; + + /// The path to the .swiftModuleDoc file. + const std::string moduleDocPath; + + /// The path to the .swiftSourceInfo file. + const std::string sourceInfoPath; + + /// A flag that indicates this dependency is a framework + const bool isFramework; + + static bool classof(const ModuleDependenciesStorageBase *base) { + return base->dependencyKind == ModuleDependenciesKind::SwiftBinary; } }; @@ -166,13 +198,11 @@ class ClangModuleDependenciesStorage : public ModuleDependenciesStorageBase { const std::vector fileDependencies; ClangModuleDependenciesStorage( - const std::string &compiledModulePath, const std::string &moduleMapFile, const std::string &contextHash, const std::vector &nonPathCommandLine, const std::vector &fileDependencies - ) : ModuleDependenciesStorageBase(ModuleDependenciesKind::Clang, - compiledModulePath), + ) : ModuleDependenciesStorageBase(ModuleDependenciesKind::Clang), moduleMapFile(moduleMapFile), contextHash(contextHash), nonPathCommandLine(nonPathCommandLine), @@ -190,20 +220,24 @@ class ClangModuleDependenciesStorage : public ModuleDependenciesStorageBase { /// Describes an placeholder Swift module dependency module stub. /// /// This class is mostly an implementation detail for \c ModuleDependencies. -class PlaceholderSwiftModuleDependencyStorage : public ModuleDependenciesStorageBase { + +class SwiftPlaceholderModuleDependencyStorage : public ModuleDependenciesStorageBase { public: - PlaceholderSwiftModuleDependencyStorage(const std::string &compiledModulePath, - const std::string &moduleDocPath, - const std::string &sourceInfoPath) - : ModuleDependenciesStorageBase(ModuleDependenciesKind::SwiftPlaceholder, - compiledModulePath), + SwiftPlaceholderModuleDependencyStorage(const std::string &compiledModulePath, + const std::string &moduleDocPath, + const std::string &sourceInfoPath) + : ModuleDependenciesStorageBase(ModuleDependenciesKind::SwiftPlaceholder), + compiledModulePath(compiledModulePath), moduleDocPath(moduleDocPath), sourceInfoPath(sourceInfoPath) {} ModuleDependenciesStorageBase *clone() const override { - return new PlaceholderSwiftModuleDependencyStorage(*this); + return new SwiftPlaceholderModuleDependencyStorage(*this); } + /// The path to the .swiftmodule file. + const std::string compiledModulePath; + /// The path to the .swiftModuleDoc file. const std::string moduleDocPath; @@ -248,43 +282,41 @@ class ModuleDependencies { ArrayRef extraPCMArgs, StringRef contextHash, bool isFramework) { - std::string compiledModulePath; return ModuleDependencies( - std::make_unique( - compiledModulePath, swiftInterfaceFile, compiledCandidates, buildCommands, + std::make_unique( + swiftInterfaceFile, compiledCandidates, buildCommands, extraPCMArgs, contextHash, isFramework)); } /// Describe the module dependencies for a serialized or parsed Swift module. - static ModuleDependencies forSwiftModule( - const std::string &compiledModulePath, bool isFramework) { + static ModuleDependencies forSwiftBinaryModule( + const std::string &compiledModulePath, + const std::string &moduleDocPath, + const std::string &sourceInfoPath, + bool isFramework) { return ModuleDependencies( - std::make_unique( - compiledModulePath, None, ArrayRef(), ArrayRef(), - ArrayRef(), StringRef(), isFramework)); + std::make_unique( + compiledModulePath, moduleDocPath, sourceInfoPath, isFramework)); } /// Describe the main Swift module. static ModuleDependencies forMainSwiftModule(ArrayRef extraPCMArgs) { - std::string compiledModulePath; return ModuleDependencies( - std::make_unique( - compiledModulePath, None, ArrayRef(), - ArrayRef(), extraPCMArgs, StringRef(), false)); + std::make_unique( + None, ArrayRef(), ArrayRef(), + extraPCMArgs, StringRef(), false)); } /// Describe the module dependencies for a Clang module that can be /// built from a module map and headers. static ModuleDependencies forClangModule( - const std::string &compiledModulePath, const std::string &moduleMapFile, const std::string &contextHash, const std::vector &nonPathCommandLine, const std::vector &fileDependencies) { return ModuleDependencies( std::make_unique( - compiledModulePath, moduleMapFile, contextHash, nonPathCommandLine, - fileDependencies)); + moduleMapFile, contextHash, nonPathCommandLine, fileDependencies)); } /// Describe a placeholder dependency swift module. @@ -293,43 +325,56 @@ class ModuleDependencies { const std::string &moduleDocPath, const std::string &sourceInfoPath) { return ModuleDependencies( - std::make_unique( + std::make_unique( compiledModulePath, moduleDocPath, sourceInfoPath)); } - /// Retrieve the path to the compiled module. - const std::string getCompiledModulePath() const { - return storage->compiledModulePath; - } - /// Retrieve the module-level dependencies. ArrayRef getModuleDependencies() const { return storage->moduleDependencies; } - /// Whether the dependencies are for a Swift module. + /// Whether the dependencies are for a Swift module: either Textual, Binary, or Placeholder. bool isSwiftModule() const; + /// Whether the dependencies are for a textual Swift module. + bool isSwiftTextualModule() const; + + /// Whether the dependencies are for a binary Swift module. + bool isSwiftBinaryModule() const; + /// Whether this represents a placeholder module stub - bool isPlaceholderSwiftModule() const; + bool isSwiftPlaceholderModule() const; + + /// Whether the dependencies are for a Clang module. + bool isClangModule() const; ModuleDependenciesKind getKind() const { return storage->dependencyKind; } /// Retrieve the dependencies for a Swift module. - const SwiftModuleDependenciesStorage *getAsSwiftModule() const; + const SwiftTextualModuleDependenciesStorage *getAsSwiftTextualModule() const; + + /// Retrieve the dependencies for a binary Swift module. + const SwiftBinaryModuleDependencyStorage *getAsSwiftBinaryModule() const; /// Retrieve the dependencies for a Clang module. const ClangModuleDependenciesStorage *getAsClangModule() const; /// Retrieve the dependencies for a placeholder dependency module stub. - const PlaceholderSwiftModuleDependencyStorage * - getAsPlaceholderDependencyModule() const; + const SwiftPlaceholderModuleDependencyStorage * + getAsPlaceholderDependencyModule() const; /// Add a dependency on the given module, if it was not already in the set. void addModuleDependency(StringRef module, llvm::StringSet<> *alreadyAddedModules = nullptr); + /// Add a dependency on the given module, if it was not already in the set. + void addModuleDependency(ImportPath::Module module, + llvm::StringSet<> *alreadyAddedModules = nullptr) { + addModuleDependency(module.front().Item.str(), alreadyAddedModules); + } + /// Add all of the module dependencies for the imports in the given source /// file to the set of module dependencies. void addModuleDependencies(const SourceFile &sf, @@ -363,11 +408,14 @@ class ModuleDependenciesCache { /// encountered. std::vector AllModules; - /// Dependencies for Swift modules that have already been computed. - llvm::StringMap SwiftModuleDependencies; + /// Dependencies for Textual Swift modules that have already been computed. + llvm::StringMap SwiftTextualModuleDependencies; - /// Dependencies for Swift placeholder dependency modules. - llvm::StringMap PlaceholderSwiftModuleDependencies; + /// Dependencies for Binary Swift modules that have already been computed. + llvm::StringMap SwiftBinaryModuleDependencies; + + /// Dependencies for Swift placeholder dependency modules that have already been computed. + llvm::StringMap SwiftPlaceholderModuleDependencies; /// Dependencies for Clang modules that have already been computed. llvm::StringMap ClangModuleDependencies; @@ -429,8 +477,7 @@ class ModuleDependenciesCache { /// Record dependencies for the given module. void recordDependencies(StringRef moduleName, - ModuleDependencies dependencies, - ModuleDependenciesKind kind); + ModuleDependencies dependencies); /// Update stored dependencies for the given module. void updateDependencies(ModuleDependencyID moduleID, diff --git a/include/swift/AST/ModuleLoader.h b/include/swift/AST/ModuleLoader.h index 23b2729194a60..8905b4338f02f 100644 --- a/include/swift/AST/ModuleLoader.h +++ b/include/swift/AST/ModuleLoader.h @@ -78,6 +78,9 @@ enum class IntermoduleDepTrackingMode { /// implemented in terms of a wrapped clang::DependencyCollector. class DependencyTracker { std::shared_ptr clangCollector; + SmallVector incrementalDeps; + llvm::StringSet<> incrementalDepsUniquer; + public: explicit DependencyTracker( IntermoduleDepTrackingMode Mode, @@ -90,9 +93,19 @@ class DependencyTracker { /// No path canonicalization is done. void addDependency(StringRef File, bool IsSystem); + /// Adds a file as an incremental dependency. + /// + /// No additional canonicalization or adulteration of the file path in + /// \p File is performed. + void addIncrementalDependency(StringRef File); + /// Fetches the list of dependencies. ArrayRef getDependencies() const; + /// Fetches the list of dependencies that are known to have incremental swift + /// dependency information embedded inside of them. + ArrayRef getIncrementalDependencies() const; + /// Return the underlying clang::DependencyCollector that this /// class wraps. std::shared_ptr getClangCollector(); @@ -106,6 +119,23 @@ struct SubCompilerInstanceInfo { ArrayRef ExtraPCMArgs; }; +/// Abstract interface for a checker of module interfaces and prebuilt modules. +class ModuleInterfaceChecker { +public: + virtual std::vector + getCompiledModuleCandidatesForInterface(StringRef moduleName, + StringRef interfacePath) = 0; + + /// Given a list of potential ready-to-use compiled modules for \p interfacePath, + /// check if any one of them is up-to-date. If so, emit a forwarding module + /// to the candidate binary module to \p outPath. + virtual bool tryEmitForwardingModule(StringRef moduleName, + StringRef interfacePath, + ArrayRef candidates, + StringRef outPath) = 0; + virtual ~ModuleInterfaceChecker() = default; +}; + /// Abstract interface to run an action in a sub ASTContext. struct InterfaceSubContextDelegate { virtual std::error_code runInSubContext(StringRef moduleName, diff --git a/include/swift/AST/ModuleNameLookup.h b/include/swift/AST/ModuleNameLookup.h index 54a791980bbce..bb09a801f3d66 100644 --- a/include/swift/AST/ModuleNameLookup.h +++ b/include/swift/AST/ModuleNameLookup.h @@ -56,10 +56,13 @@ void simple_display(llvm::raw_ostream &out, ResolutionKind kind); /// \param moduleScopeContext The top-level context from which the lookup is /// being performed, for checking access. This must be either a /// FileUnit or a Module. +/// \param options name lookup options. Currently only used to communicate the +/// NL_IncludeUsableFromInline option. void lookupInModule(const DeclContext *moduleOrFile, DeclName name, SmallVectorImpl &decls, NLKind lookupKind, ResolutionKind resolutionKind, - const DeclContext *moduleScopeContext); + const DeclContext *moduleScopeContext, + NLOptions options); /// Performs a qualified lookup into the given module and, if necessary, its /// reexports, observing proper shadowing rules. diff --git a/include/swift/AST/NameLookup.h b/include/swift/AST/NameLookup.h index 80c3d72aa1051..ed5f8acb45b5f 100644 --- a/include/swift/AST/NameLookup.h +++ b/include/swift/AST/NameLookup.h @@ -225,6 +225,9 @@ enum class UnqualifiedLookupFlags { /// This lookup should include results from outside the innermost scope with /// results. IncludeOuterResults = 1 << 4, + // This lookup should include results that are @inlinable or + // @usableFromInline. + IncludeUsableFromInline = 1 << 5, }; using UnqualifiedLookupOptions = OptionSet; @@ -396,31 +399,6 @@ class VectorDeclConsumer : public VisibleDeclConsumer { } }; -/// A consumer that inserts found decls with a matching name into an -/// externally-owned SmallVector. -class NamedDeclConsumer : public VisibleDeclConsumer { - virtual void anchor() override; -public: - DeclNameRef name; - SmallVectorImpl &results; - bool isTypeLookup; - - NamedDeclConsumer(DeclNameRef name, - SmallVectorImpl &results, - bool isTypeLookup) - : name(name), results(results), isTypeLookup(isTypeLookup) {} - - virtual void foundDecl(ValueDecl *VD, DeclVisibilityKind Reason, - DynamicLookupInfo dynamicLookupInfo = {}) override { - // Give clients an opportunity to filter out non-type declarations early, - // to avoid circular validation. - if (isTypeLookup && !isa(VD)) - return; - if (VD->getName().matchesRef(name.getFullName())) - results.push_back(LookupResultEntry(VD)); - } -}; - /// A consumer that filters out decls that are not accessible from a given /// DeclContext. class AccessFilteringDeclConsumer final : public VisibleDeclConsumer { @@ -619,18 +597,41 @@ class AbstractASTScopeDeclConsumer { virtual ~AbstractASTScopeDeclConsumer() = default; /// Called for every ValueDecl visible from the lookup. - /// Returns true if the lookup can be stopped at this point. - /// BaseDC is per legacy + /// /// Takes an array in order to batch the consumption before setting /// IndexOfFirstOuterResult when necessary. - virtual bool consume(ArrayRef values, DeclVisibilityKind vis, + /// + /// \param baseDC either a type context or the local context of a + /// `self` parameter declaration. See LookupResult for a discussion + /// of type -vs- instance lookup results. + /// + /// \return true if the lookup should be stopped at this point. + virtual bool consume(ArrayRef values, NullablePtr baseDC = nullptr) = 0; - /// Eventually this functionality should move into ASTScopeLookup + /// Look for members of a nominal type or extension scope. + /// + /// \return true if the lookup should be stopped at this point. virtual bool lookInMembers(DeclContext *const scopeDC, NominalTypeDecl *const nominal) = 0; + /// Called for local VarDecls that might not yet be in scope. + /// + /// Note that the set of VarDecls visited here are going to be a + /// superset of those visited in consume(). + virtual bool consumePossiblyNotInScope(ArrayRef values) { + return false; + } + + /// Called right before looking at the parent scope of a BraceStmt. + /// + /// \return true if the lookup should be stopped at this point. + virtual bool + finishLookupInBraceStmt(BraceStmt *stmt) { + return false; + } + #ifndef NDEBUG virtual void startingNextLookupStep() = 0; virtual void finishingLookup(std::string) const = 0; @@ -646,7 +647,7 @@ class ASTScopeDeclGatherer : public AbstractASTScopeDeclConsumer { public: virtual ~ASTScopeDeclGatherer() = default; - bool consume(ArrayRef values, DeclVisibilityKind vis, + bool consume(ArrayRef values, NullablePtr baseDC = nullptr) override; /// Eventually this functionality should move into ASTScopeLookup @@ -681,9 +682,21 @@ class ASTScope { /// Flesh out the tree for dumping void buildFullyExpandedTree(); - static void unqualifiedLookup(SourceFile *, DeclNameRef, SourceLoc, + static void unqualifiedLookup(SourceFile *, SourceLoc, namelookup::AbstractASTScopeDeclConsumer &); + /// Lookup that only finds local declarations and does not trigger + /// interface type computation. + /// + /// \param stopAfterInnermostBraceStmt If lookup should consider + /// local declarations inside the innermost syntactic scope only. + static void lookupLocalDecls(SourceFile *, DeclName, SourceLoc, + bool stopAfterInnermostBraceStmt, + SmallVectorImpl &); + + /// Returns the result if there is exactly one, nullptr otherwise. + static ValueDecl *lookupSingleLocalDecl(SourceFile *, DeclName, SourceLoc); + /// Entry point to record the visible statement labels from the given /// point. /// diff --git a/include/swift/AST/NameLookupRequests.h b/include/swift/AST/NameLookupRequests.h index e02441cdac004..778b7c3e3c49f 100644 --- a/include/swift/AST/NameLookupRequests.h +++ b/include/swift/AST/NameLookupRequests.h @@ -160,6 +160,10 @@ class SuperclassDeclRequest : evaluate(Evaluator &evaluator, NominalTypeDecl *subject) const; public: + // Cycle handling + void diagnoseCycle(DiagnosticEngine &diags) const; + void noteCycleStep(DiagnosticEngine &diags) const; + // Caching bool isCached() const { return true; } Optional getCachedResult() const; @@ -433,11 +437,12 @@ using QualifiedLookupResult = SmallVector; /// Performs a lookup into a given module and its imports. class LookupInModuleRequest - : public SimpleRequest { + : public SimpleRequest< + LookupInModuleRequest, + QualifiedLookupResult(const DeclContext *, DeclName, NLKind, + namelookup::ResolutionKind, const DeclContext *, + NLOptions), + RequestFlags::Uncached | RequestFlags::DependencySink> { public: using SimpleRequest::SimpleRequest; @@ -448,7 +453,7 @@ class LookupInModuleRequest QualifiedLookupResult evaluate(Evaluator &evaluator, const DeclContext *moduleOrFile, DeclName name, NLKind lookupKind, namelookup::ResolutionKind resolutionKind, - const DeclContext *moduleScopeContext) const; + const DeclContext *moduleScopeContext, NLOptions options) const; public: // Incremental dependencies diff --git a/include/swift/AST/ProtocolConformance.h b/include/swift/AST/ProtocolConformance.h index 1f61cdf438175..9a0b6a6b2ad50 100644 --- a/include/swift/AST/ProtocolConformance.h +++ b/include/swift/AST/ProtocolConformance.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 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -66,7 +66,10 @@ enum class ProtocolConformanceKind { Specialized, /// Conformance of a generic class type projected through one of its /// superclass's conformances. - Inherited + Inherited, + /// Builtin conformances are special conformaces that the runtime handles + /// and isn't implemented directly in Swift. + Builtin }; /// Describes the state of a protocol conformance, which may be complete, @@ -329,7 +332,9 @@ class alignas(1 << DeclAlignInBits) ProtocolConformance { /// - the type is directly declared to conform to the protocol (a /// normal conformance) or /// - the protocol's existential type is known to conform to itself (a -/// self-conformance). +/// self-conformance) or +/// - the type's conformance is declared within the runtime (a builtin +/// conformance). class RootProtocolConformance : public ProtocolConformance { protected: RootProtocolConformance(ProtocolConformanceKind kind, Type conformingType) @@ -380,7 +385,8 @@ class RootProtocolConformance : public ProtocolConformance { static bool classof(const ProtocolConformance *conformance) { return conformance->getKind() == ProtocolConformanceKind::Normal || - conformance->getKind() == ProtocolConformanceKind::Self; + conformance->getKind() == ProtocolConformanceKind::Self || + conformance->getKind() == ProtocolConformanceKind::Builtin; } }; @@ -1014,6 +1020,110 @@ class InheritedProtocolConformance : public ProtocolConformance, } }; +/// A builtin conformance appears when a special non-nominal type has a runtime +/// declared conformance. E.g. the runtime implements Equatable for tuples. +class BuiltinProtocolConformance final : public RootProtocolConformance, + private llvm::TrailingObjects { + friend ASTContext; + friend TrailingObjects; + + ProtocolDecl *protocol = nullptr; + size_t numConformances; + + mutable Optional> conditionalConformances = None; + + BuiltinProtocolConformance(Type conformingType, ProtocolDecl *protocol, + ArrayRef conformances); + + size_t numTrailingObjects(OverloadToken) const { + return numConformances; + } + +public: + /// Get the protocol being conformed to. + ProtocolDecl *getProtocol() const { + return protocol; + } + + /// Get the trailing conformances that this builtin conformance needs. + MutableArrayRef getConformances() { + return {getTrailingObjects(), numConformances}; + } + + /// Get the trailing conformances that this builtin conformance needs. + ArrayRef getConformances() const { + return {getTrailingObjects(), numConformances}; + } + + /// Get any requirements that must be satisfied for this conformance to apply. + Optional> + getConditionalRequirementsIfAvailable() const { + return ArrayRef(); + } + + /// Get any requirements that must be satisfied for this conformance to apply. + ArrayRef getConditionalRequirements() const; + + /// Get the declaration context that contains the nominal type declaration. + DeclContext *getDeclContext() const { + return getProtocol(); + } + + /// Retrieve the state of this conformance. + ProtocolConformanceState getState() const { + return ProtocolConformanceState::Complete; + } + + /// Get the kind of source from which this conformance comes. + ConformanceEntryKind getSourceKind() const { + return ConformanceEntryKind::Synthesized; + } + /// Get the protocol conformance which implied this implied conformance. + NormalProtocolConformance *getImplyingConformance() const { + return nullptr; + } + + bool hasTypeWitness(AssociatedTypeDecl *assocType) const { + llvm_unreachable("builtin-conformances currently don't have associated \ + types"); + } + + /// Retrieve the type witness and type decl (if one exists) + /// for the given associated type. + TypeWitnessAndDecl + getTypeWitnessAndDecl(AssociatedTypeDecl *assocType, + SubstOptions options=None) const { + llvm_unreachable("builtin-conformances currently don't have associated \ + types"); + } + + /// Given that the requirement signature of the protocol directly states + /// that the given dependent type must conform to the given protocol, + /// return its associated conformance. + ProtocolConformanceRef + getAssociatedConformance(Type assocType, ProtocolDecl *protocol) const { + llvm_unreachable("builtin-conformances currently don't have associated \ + types"); + } + + /// Retrieve the witness corresponding to the given value requirement. + ConcreteDeclRef getWitnessDeclRef(ValueDecl *requirement) const { + return ConcreteDeclRef(requirement); + } + + /// Determine whether the witness for the given requirement + /// is either the default definition or was otherwise deduced. + bool usesDefaultDefinition(AssociatedTypeDecl *requirement) const { + llvm_unreachable("builtin-conformances currently don't have associated \ + types"); + } + + static bool classof(const ProtocolConformance *conformance) { + return conformance->getKind() == ProtocolConformanceKind::Builtin; + } +}; + inline bool ProtocolConformance::isInvalid() const { return getRootConformance()->isInvalid(); } diff --git a/include/swift/AST/SourceFile.h b/include/swift/AST/SourceFile.h index 8fe3c8e4dd904..2b5cc9e9c4122 100644 --- a/include/swift/AST/SourceFile.h +++ b/include/swift/AST/SourceFile.h @@ -14,6 +14,7 @@ #define SWIFT_AST_SOURCEFILE_H #include "swift/AST/FileUnit.h" +#include "swift/AST/Import.h" #include "swift/AST/SynthesizedFileUnit.h" #include "swift/Basic/Debug.h" #include "llvm/ADT/SetVector.h" @@ -32,58 +33,6 @@ class SourceFile final : public FileUnit { friend class ParseSourceFileRequest; public: - /// Possible attributes for imports in source files. - enum class ImportFlags { - /// The imported module is exposed to anyone who imports the parent module. - Exported = 0x1, - - /// This source file has access to testable declarations in the imported - /// module. - Testable = 0x2, - - /// This source file has access to private declarations in the imported - /// module. - PrivateImport = 0x4, - - /// The imported module is an implementation detail of this file and should - /// not be required to be present if the main module is ever imported - /// elsewhere. - /// - /// Mutually exclusive with Exported. - ImplementationOnly = 0x8, - - // The module is imported to have access to named SPIs which is an - // implementation detail of this file. - SPIAccessControl = 0x10, - - /// Used for DenseMap. - Reserved = 0x80 - }; - - /// \see ImportFlags - using ImportOptions = OptionSet; - - struct ImportedModuleDesc { - ModuleDecl::ImportedModule module; - ImportOptions importOptions; - - // Filename for a @_private import. - StringRef filename; - - // Names of explicitly imported SPIs. - ArrayRef spiGroups; - - ImportedModuleDesc(ModuleDecl::ImportedModule module, ImportOptions options, - StringRef filename = {}, - ArrayRef spiGroups = {}) - : module(module), importOptions(options), filename(filename), - spiGroups(spiGroups) { - assert(!(importOptions.contains(ImportFlags::Exported) && - importOptions.contains(ImportFlags::ImplementationOnly)) || - importOptions.contains(ImportFlags::Reserved)); - } - }; - /// Flags that direct how the source file is parsed. enum class ParsingFlags : uint8_t { /// Whether to disable delayed parsing for nominal type, extension, and @@ -133,7 +82,7 @@ class SourceFile final : public FileUnit { /// This is the list of modules that are imported by this module. /// /// This is \c None until it is filled in by the import resolution phase. - Optional> Imports; + Optional>> Imports; /// A unique identifier representing this file; used to mark private decls /// within the file to keep them from conflicting with other files in the @@ -346,11 +295,13 @@ class SourceFile final : public FileUnit { ~SourceFile(); /// Retrieve an immutable view of the source file's imports. - ArrayRef getImports() const { return *Imports; } + ArrayRef> getImports() const { + return *Imports; + } /// Set the imports for this source file. This gets called by import /// resolution. - void setImports(ArrayRef imports); + void setImports(ArrayRef> imports); enum ImportQueryKind { /// Return the results for testable or private imports. @@ -454,7 +405,7 @@ class SourceFile final : public FileUnit { getOpaqueReturnTypeDecls(SmallVectorImpl &results) const override; virtual void - getImportedModules(SmallVectorImpl &imports, + getImportedModules(SmallVectorImpl &imports, ModuleDecl::ImportFilter filter) const override; virtual void @@ -667,60 +618,4 @@ inline void simple_display(llvm::raw_ostream &out, const SourceFile *SF) { } } // end namespace swift -namespace llvm { - -template<> -struct DenseMapInfo { - using ImportOptions = swift::SourceFile::ImportOptions; - - using UnsignedDMI = DenseMapInfo; - - static inline ImportOptions getEmptyKey() { - return ImportOptions(UnsignedDMI::getEmptyKey()); - } - static inline ImportOptions getTombstoneKey() { - return ImportOptions(UnsignedDMI::getTombstoneKey()); - } - static inline unsigned getHashValue(ImportOptions options) { - return UnsignedDMI::getHashValue(options.toRaw()); - } - static bool isEqual(ImportOptions a, ImportOptions b) { - return UnsignedDMI::isEqual(a.toRaw(), b.toRaw()); - } -}; - -template<> -struct DenseMapInfo { - using ImportedModuleDesc = swift::SourceFile::ImportedModuleDesc; - - using ImportedModuleDMI = DenseMapInfo; - using ImportOptionsDMI = DenseMapInfo; - using StringRefDMI = DenseMapInfo; - - static inline ImportedModuleDesc getEmptyKey() { - return ImportedModuleDesc(ImportedModuleDMI::getEmptyKey(), - ImportOptionsDMI::getEmptyKey(), - StringRefDMI::getEmptyKey()); - } - static inline ImportedModuleDesc getTombstoneKey() { - return ImportedModuleDesc(ImportedModuleDMI::getTombstoneKey(), - ImportOptionsDMI::getTombstoneKey(), - StringRefDMI::getTombstoneKey()); - } - static inline unsigned getHashValue(const ImportedModuleDesc &import) { - return detail::combineHashValue( - ImportedModuleDMI::getHashValue(import.module), - detail::combineHashValue( - ImportOptionsDMI::getHashValue(import.importOptions), - StringRefDMI::getHashValue(import.filename))); - } - static bool isEqual(const ImportedModuleDesc &a, - const ImportedModuleDesc &b) { - return ImportedModuleDMI::isEqual(a.module, b.module) && - ImportOptionsDMI::isEqual(a.importOptions, b.importOptions) && - StringRefDMI::isEqual(a.filename, b.filename); - } -}; -} - #endif diff --git a/include/swift/AST/Stmt.h b/include/swift/AST/Stmt.h index 258ebe5e7e4d6..6fb2fe9e56bc4 100644 --- a/include/swift/AST/Stmt.h +++ b/include/swift/AST/Stmt.h @@ -632,17 +632,17 @@ class IfStmt : public LabeledConditionalStmt { /// class GuardStmt : public LabeledConditionalStmt { SourceLoc GuardLoc; - Stmt *Body; + BraceStmt *Body; public: GuardStmt(SourceLoc GuardLoc, StmtCondition Cond, - Stmt *Body, Optional implicit = None) + BraceStmt *Body, Optional implicit = None) : LabeledConditionalStmt(StmtKind::Guard, getDefaultImplicitFlag(implicit, GuardLoc), LabeledStmtInfo(), Cond), GuardLoc(GuardLoc), Body(Body) {} - GuardStmt(SourceLoc GuardLoc, Expr *Cond, Stmt *Body, + GuardStmt(SourceLoc GuardLoc, Expr *Cond, BraceStmt *Body, Optional implicit, ASTContext &Ctx); SourceLoc getGuardLoc() const { return GuardLoc; } @@ -654,8 +654,8 @@ class GuardStmt : public LabeledConditionalStmt { return Body->getEndLoc(); } - Stmt *getBody() const { return Body; } - void setBody(Stmt *s) { Body = s; } + BraceStmt *getBody() const { return Body; } + void setBody(BraceStmt *s) { Body = s; } // Implement isa/cast/dyncast/etc. static bool classof(const Stmt *S) { return S->getKind() == StmtKind::Guard; } @@ -945,13 +945,13 @@ class CaseStmt final SourceLoc ItemTerminatorLoc; CaseParentKind ParentKind; - llvm::PointerIntPair BodyAndHasFallthrough; + llvm::PointerIntPair BodyAndHasFallthrough; Optional> CaseBodyVariables; CaseStmt(CaseParentKind ParentKind, SourceLoc ItemIntroducerLoc, ArrayRef CaseLabelItems, SourceLoc UnknownAttrLoc, - SourceLoc ItemTerminatorLoc, Stmt *Body, + SourceLoc ItemTerminatorLoc, BraceStmt *Body, Optional> CaseBodyVariables, Optional Implicit, NullablePtr fallthroughStmt); @@ -960,7 +960,7 @@ class CaseStmt final static CaseStmt * create(ASTContext &C, CaseParentKind ParentKind, SourceLoc ItemIntroducerLoc, ArrayRef CaseLabelItems, SourceLoc UnknownAttrLoc, - SourceLoc ItemTerminatorLoc, Stmt *Body, + SourceLoc ItemTerminatorLoc, BraceStmt *Body, Optional> CaseBodyVariables, Optional Implicit = None, NullablePtr fallthroughStmt = nullptr); @@ -997,8 +997,8 @@ class CaseStmt final bool hasFallthroughDest() const { return BodyAndHasFallthrough.getInt(); } - Stmt *getBody() const { return BodyAndHasFallthrough.getPointer(); } - void setBody(Stmt *body) { BodyAndHasFallthrough.setPointer(body); } + BraceStmt *getBody() const { return BodyAndHasFallthrough.getPointer(); } + void setBody(BraceStmt *body) { BodyAndHasFallthrough.setPointer(body); } /// True if the case block declares any patterns with local variable bindings. bool hasBoundDecls() const { return CaseBodyVariables.hasValue(); } diff --git a/include/swift/AST/TypeAlignments.h b/include/swift/AST/TypeAlignments.h index f0081a80dacf4..6097cf9a29fc5 100644 --- a/include/swift/AST/TypeAlignments.h +++ b/include/swift/AST/TypeAlignments.h @@ -35,9 +35,11 @@ namespace swift { class CaptureListExpr; class Decl; class DeclContext; + class DifferentiableAttr; class Expr; class ExtensionDecl; class GenericEnvironment; + class GenericParamList; class GenericTypeParamDecl; class NominalTypeDecl; class NormalProtocolConformance; @@ -46,9 +48,12 @@ namespace swift { class Pattern; class ProtocolDecl; class ProtocolConformance; + class RequirementRepr; class SILFunction; class SILFunctionType; + class SpecializeAttr; class Stmt; + class TrailingWhereClause; class TypeVariableType; class TypeBase; class TypeDecl; @@ -63,9 +68,15 @@ namespace swift { constexpr size_t StmtAlignInBits = 3; constexpr size_t TypeAlignInBits = 3; constexpr size_t PatternAlignInBits = 3; + constexpr size_t TypeReprAlignInBits = 3; + constexpr size_t SILFunctionAlignInBits = 2; + constexpr size_t ASTContextAlignInBits = 2; constexpr size_t TypeVariableAlignInBits = 4; - constexpr size_t TypeReprAlignInBits = 3; + + // Well, this is the *minimum* pointer alignment; it's going to be 3 on + // 64-bit targets, but that doesn't matter. + constexpr size_t PointerAlignInBits = 2; } namespace llvm { @@ -110,8 +121,9 @@ LLVM_DECLARE_TYPE_ALIGNMENT(swift::SILFunctionType, LLVM_DECLARE_TYPE_ALIGNMENT(swift::Stmt, swift::StmtAlignInBits) LLVM_DECLARE_TYPE_ALIGNMENT(swift::BraceStmt, swift::StmtAlignInBits) -LLVM_DECLARE_TYPE_ALIGNMENT(swift::ASTContext, 2); +LLVM_DECLARE_TYPE_ALIGNMENT(swift::ASTContext, swift::ASTContextAlignInBits); LLVM_DECLARE_TYPE_ALIGNMENT(swift::DeclContext, swift::DeclContextAlignInBits) +LLVM_DECLARE_TYPE_ALIGNMENT(swift::DifferentiableAttr, swift::PointerAlignInBits) LLVM_DECLARE_TYPE_ALIGNMENT(swift::Expr, swift::ExprAlignInBits) LLVM_DECLARE_TYPE_ALIGNMENT(swift::CaptureListExpr, swift::ExprAlignInBits) LLVM_DECLARE_TYPE_ALIGNMENT(swift::AbstractClosureExpr, swift::ExprAlignInBits) @@ -121,10 +133,17 @@ LLVM_DECLARE_TYPE_ALIGNMENT(swift::NormalProtocolConformance, swift::DeclAlignInBits) LLVM_DECLARE_TYPE_ALIGNMENT(swift::GenericEnvironment, swift::DeclAlignInBits) +LLVM_DECLARE_TYPE_ALIGNMENT(swift::GenericParamList, + swift::PointerAlignInBits) LLVM_DECLARE_TYPE_ALIGNMENT(swift::Pattern, swift::PatternAlignInBits) +LLVM_DECLARE_TYPE_ALIGNMENT(swift::RequirementRepr, + swift::PointerAlignInBits) LLVM_DECLARE_TYPE_ALIGNMENT(swift::SILFunction, swift::SILFunctionAlignInBits) +LLVM_DECLARE_TYPE_ALIGNMENT(swift::SpecializeAttr, swift::PointerAlignInBits) +LLVM_DECLARE_TYPE_ALIGNMENT(swift::TrailingWhereClause, + swift::PointerAlignInBits) LLVM_DECLARE_TYPE_ALIGNMENT(swift::AttributeBase, swift::AttrAlignInBits) diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index d89a0e9be011a..d053f58890e61 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -19,6 +19,7 @@ #include "swift/AST/ActorIsolation.h" #include "swift/AST/AnyFunctionRef.h" #include "swift/AST/ASTTypeIDs.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/GenericSignature.h" #include "swift/AST/Type.h" #include "swift/AST/Evaluator.h" @@ -125,10 +126,9 @@ class SuperclassTypeRequest }; /// Request the raw type of the given enum. -class EnumRawTypeRequest : - public SimpleRequest { +class EnumRawTypeRequest + : public SimpleRequest { public: using SimpleRequest::SimpleRequest; @@ -136,18 +136,14 @@ class EnumRawTypeRequest : friend SimpleRequest; // Evaluation. - Type - evaluate(Evaluator &evaluator, EnumDecl *enumDecl, - TypeResolutionStage stage) const; + Type evaluate(Evaluator &evaluator, EnumDecl *enumDecl) const; public: // Cycle handling void diagnoseCycle(DiagnosticEngine &diags) const; + void noteCycleStep(DiagnosticEngine &diags) const; - // Separate caching. - bool isCached() const; - Optional getCachedResult() const; - void cacheResult(Type value) const; + bool isCached() const { return true; } }; /// Request to determine the set of declarations that were are overridden @@ -211,6 +207,29 @@ class InitKindRequest : CtorInitializerKind evaluate(Evaluator &evaluator, ConstructorDecl *decl) const; +public: +// Caching. + bool isCached() const { return true; } +}; + +void simple_display(llvm::raw_ostream &out, BodyInitKind initKind); +void simple_display(llvm::raw_ostream &out, BodyInitKindAndExpr initKindAndExpr); + +/// Computes the kind of initializer call (self.init or super.init) performed +/// in the body of a \c ConstructorDecl +class BodyInitKindRequest : + public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + BodyInitKindAndExpr evaluate(Evaluator &evaluator, ConstructorDecl *decl) const; + public: // Caching. bool isCached() const { return true; } @@ -716,10 +735,10 @@ void simple_display(llvm::raw_ostream &out, FragileFunctionKind value); void simple_display(llvm::raw_ostream &out, ResilienceExpansion value); -/// Request the custom attribute which attaches a function builder to the +/// Request the custom attribute which attaches a result builder to the /// given declaration. -class AttachedFunctionBuilderRequest : - public SimpleRequest { public: @@ -737,10 +756,10 @@ class AttachedFunctionBuilderRequest : bool isCached() const; }; -/// Request the function builder type attached to the given declaration, +/// Request the result builder type attached to the given declaration, /// if any. -class FunctionBuilderTypeRequest : - public SimpleRequest { public: @@ -797,6 +816,25 @@ class IsAsyncHandlerRequest : bool isCached() const { return true; } }; +/// Determine whether the given function can be an @asyncHandler, without +/// producing any diagnostics. +class CanBeAsyncHandlerRequest : + public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + bool evaluate(Evaluator &evaluator, FuncDecl *func) const; + +public: + // Caching + bool isCached() const { return true; } +}; + /// Determine whether the given class is an actor. class IsActorRequest : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + VarDecl *evaluate(Evaluator &evaluator, NominalTypeDecl *nominal) const; + +public: + // Caching + bool isCached() const { return true; } +}; + +using CustomAttrNominalPair = std::pair; + +/// Request the custom attribute which denotes the global actor for the given +/// declaration. +/// +/// This is the "raw" global actor attribute as written directly on the +/// declaration, with any inference rules applied. +class GlobalActorAttributeRequest : + public SimpleRequest< + GlobalActorAttributeRequest, + Optional(Decl *), + RequestFlags::Cached> { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + Optional> + evaluate(Evaluator &evaluator, Decl *decl) const; + +public: + // Caching + bool isCached() const { return true; } +}; + /// Determine the actor isolation for the given declaration. class ActorIsolationRequest : public SimpleRequest { public: using SimpleRequest::SimpleRequest; @@ -1936,37 +2023,14 @@ class PreCheckFunctionBuilderRequest friend SimpleRequest; // Evaluation. - FunctionBuilderBodyPreCheck - evaluate(Evaluator &evaluator, PreCheckFunctionBuilderDescriptor owner) const; + ResultBuilderBodyPreCheck + evaluate(Evaluator &evaluator, PreCheckResultBuilderDescriptor owner) const; public: // Separate caching. bool isCached() const { return true; } }; -/// Computes whether a class has a circular reference in its inheritance -/// hierarchy. -class HasCircularInheritanceRequest - : public SimpleRequest { -public: - using SimpleRequest::SimpleRequest; - -private: - friend SimpleRequest; - - // Evaluation. - bool evaluate(Evaluator &evaluator, ClassDecl *decl) const; - -public: - // Cycle handling. - void diagnoseCycle(DiagnosticEngine &diags) const; - void noteCycleStep(DiagnosticEngine &diags) const; - - // Cached. - bool isCached() const { return true; } -}; - /// Computes whether a protocol has a circular reference in its list of /// inherited protocols. class HasCircularInheritedProtocolsRequest @@ -2113,6 +2177,25 @@ class DynamicallyReplacedDeclRequest bool isCached() const { return true; } }; +class SpecializeAttrTargetDeclRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + ValueDecl *evaluate(Evaluator &evaluator, const ValueDecl *vd, + SpecializeAttr *attr) const; + +public: + // Caching. + bool isCached() const { return true; } +}; + class TypeCheckSourceFileRequest : public SimpleRequest< TypeCheckSourceFileRequest, evaluator::SideEffect(SourceFile *), @@ -2474,30 +2557,11 @@ class SimpleDidSetRequest } }; -/// A module which has been implicitly imported. -struct ImplicitImport { - using ImportOptions = SourceFile::ImportOptions; - - ModuleDecl *Module; - ImportOptions Options; - - ImplicitImport(ModuleDecl *module, ImportOptions opts = {}) - : Module(module), Options(opts) {} - - friend bool operator==(const ImplicitImport &lhs, - const ImplicitImport &rhs) { - return lhs.Module == rhs.Module && - lhs.Options.toRaw() == rhs.Options.toRaw(); - } -}; - -void simple_display(llvm::raw_ostream &out, const ImplicitImport &import); - /// Computes the loaded modules that should be implicitly imported by each file /// of a given module. class ModuleImplicitImportsRequest : public SimpleRequest(ModuleDecl *), + ImplicitImportList(ModuleDecl *), RequestFlags::Cached> { public: using SimpleRequest::SimpleRequest; @@ -2505,8 +2569,7 @@ class ModuleImplicitImportsRequest private: friend SimpleRequest; - ArrayRef - evaluate(Evaluator &evaluator, ModuleDecl *module) const; + ImplicitImportList evaluate(Evaluator &evaluator, ModuleDecl *module) const; public: // Cached. @@ -2624,9 +2687,13 @@ enum class CustomAttrTypeKind { /// any contextual type parameters. NonGeneric, - /// Property delegates have some funky rules, like allowing + /// Property wrappers have some funky rules, like allowing /// unbound generic types. - PropertyDelegate, + PropertyWrapper, + + /// Global actors are represented as custom type attributes. They don't + /// have any particularly interesting semantics. + GlobalActor, }; void simple_display(llvm::raw_ostream &out, CustomAttrTypeKind value); @@ -2692,7 +2759,7 @@ AnyValue::Holder::equals(const HolderBase &other) const { void simple_display(llvm::raw_ostream &out, Type value); void simple_display(llvm::raw_ostream &out, const TypeRepr *TyR); void simple_display(llvm::raw_ostream &out, ImplicitMemberAction action); -void simple_display(llvm::raw_ostream &out, FunctionBuilderBodyPreCheck pck); +void simple_display(llvm::raw_ostream &out, ResultBuilderBodyPreCheck pck); #define SWIFT_TYPEID_ZONE TypeChecker #define SWIFT_TYPEID_HEADER "swift/AST/TypeCheckerTypeIDZone.def" diff --git a/include/swift/AST/TypeCheckerTypeIDZone.def b/include/swift/AST/TypeCheckerTypeIDZone.def index 9778017cc9880..ebfafc30dd6c5 100644 --- a/include/swift/AST/TypeCheckerTypeIDZone.def +++ b/include/swift/AST/TypeCheckerTypeIDZone.def @@ -20,7 +20,7 @@ SWIFT_REQUEST(TypeChecker, AbstractGenericSignatureRequest, SmallVector, SmallVector), Cached, NoLocationInfo) -SWIFT_REQUEST(TypeChecker, AttachedFunctionBuilderRequest, +SWIFT_REQUEST(TypeChecker, AttachedResultBuilderRequest, CustomAttr *(ValueDecl *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, AttachedPropertyWrapperTypeRequest, Type(VarDecl *, unsigned), Cached, NoLocationInfo) @@ -67,24 +67,34 @@ SWIFT_REQUEST(TypeChecker, DynamicallyReplacedDeclRequest, Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, SemanticMembersRequest, ArrayRef(IterableDeclContext *), Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, SpecializeAttrTargetDeclRequest, + ValueDecl *(const ValueDecl *, SpecializeAttr *), + Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, EnumRawValuesRequest, evaluator::SideEffect (EnumDecl *, TypeResolutionStage), SeparatelyCached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, EnumRawTypeRequest, - Type(EnumDecl *, TypeResolutionStage), SeparatelyCached, - NoLocationInfo) + Type(EnumDecl *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, ExistentialConformsToSelfRequest, bool(ProtocolDecl *), SeparatelyCached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, ExistentialTypeSupportedRequest, bool(ProtocolDecl *), SeparatelyCached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, ExtendedTypeRequest, Type(ExtensionDecl *), Cached, NoLocationInfo) -SWIFT_REQUEST(TypeChecker, FunctionBuilderTypeRequest, Type(ValueDecl *), +SWIFT_REQUEST(TypeChecker, ResultBuilderTypeRequest, Type(ValueDecl *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, IsAsyncHandlerRequest, bool(FuncDecl *), Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, CanBeAsyncHandlerRequest, bool(FuncDecl *), + Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, IsActorRequest, bool(ClassDecl *), Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, GlobalActorInstanceRequest, + VarDecl *(NominalTypeDecl *), + Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, GlobalActorAttributeRequest, + Optional(Decl *), + Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, ActorIsolationRequest, ActorIsolationState(ValueDecl *), Cached, NoLocationInfo) @@ -93,8 +103,6 @@ SWIFT_REQUEST(TypeChecker, FunctionOperatorRequest, OperatorDecl *(FuncDecl *), SWIFT_REQUEST(NameLookup, GenericSignatureRequest, GenericSignature (GenericContext *), SeparatelyCached, NoLocationInfo) -SWIFT_REQUEST(TypeChecker, HasCircularInheritanceRequest, - bool(ClassDecl *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, HasCircularInheritedProtocolsRequest, bool(ProtocolDecl *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, HasCircularRawValueRequest, @@ -119,6 +127,9 @@ SWIFT_REQUEST(TypeChecker, InheritsSuperclassInitializersRequest, bool(ClassDecl *), SeparatelyCached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, InitKindRequest, CtorInitializerKind(ConstructorDecl *), Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, BodyInitKindRequest, + BodyInitKindAndExpr(ConstructorDecl *), Cached, + NoLocationInfo) SWIFT_REQUEST(TypeChecker, InterfaceTypeRequest, Type(ValueDecl *), SeparatelyCached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, IsAccessorTransparentRequest, bool(AccessorDecl *), @@ -145,7 +156,7 @@ SWIFT_REQUEST(TypeChecker, ValidatePrecedenceGroupRequest, SWIFT_REQUEST(TypeChecker, MangleLocalTypeDeclRequest, std::string(const TypeDecl *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, ModuleImplicitImportsRequest, - ArrayRef(ModuleDecl *), Cached, NoLocationInfo) + ImplicitImportList(ModuleDecl *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, NamingPatternRequest, NamedPattern *(VarDecl *), SeparatelyCached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, OpaqueReadOwnershipRequest, @@ -238,8 +249,8 @@ SWIFT_REQUEST(TypeChecker, HasUserDefinedDesignatedInitRequest, bool(NominalTypeDecl *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, HasMemberwiseInitRequest, bool(StructDecl *), Cached, NoLocationInfo) -SWIFT_REQUEST(TypeChecker, PreCheckFunctionBuilderRequest, - FunctionBuilderClosurePreCheck(PreCheckFunctionBuilderDescriptor), +SWIFT_REQUEST(TypeChecker, PreCheckResultBuilderRequest, + ResultBuilderBodyPreCheck(PreCheckResultBuilderDescriptor), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, ResolveImplicitMemberRequest, evaluator::SideEffect(NominalTypeDecl *, ImplicitMemberAction), diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index 12c2643de24ea..1a66121f58c56 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -87,6 +87,7 @@ class ModuleType; class ProtocolConformance; enum PointerTypeKind : unsigned; struct ValueOwnershipKind; +class ErrorExpr; typedef CanTypeWrapper CanSILFunctionType; @@ -600,9 +601,6 @@ class alignas(1 << TypeAlignInBits) TypeBase { bool hasOpaqueArchetype() const { return getRecursiveProperties().hasOpaqueArchetype(); } - /// Determine whether the type has any stored properties or enum cases that - /// involve an opaque type. - bool hasOpaqueArchetypePropertiesOrCases(); /// Determine whether the type is an opened existential type. /// @@ -2928,6 +2926,24 @@ class AnyFunctionType : public TypeBase { ClangTypeInfo getClangTypeInfo() const; ClangTypeInfo getCanonicalClangTypeInfo() const; + /// Returns true if the function type stores a Clang type that cannot + /// be derived from its Swift type. Returns false otherwise, including if + /// the function type is not @convention(c) or @convention(block). + /// + /// For example, if you have a function pointer from C getting imported with + /// the following type: + /// + /// @convention(c, cType: "void (*)(size_t (*)(size_t))") + /// (@convention(c, cType: "size_t (*)(size_t)") (Int) -> Int)) -> Void + /// + /// The parameter's function type will have hasNonDerivableClangType() = true, + /// but the outer function type will have hasNonDerivableClangType() = false, + /// because the parameter and result type are sufficient to correctly derive + /// the Clang type for the outer function type. In terms of mangling, + /// the parameter type's mangling will incorporate the Clang type but the + /// outer function type's mangling doesn't need to duplicate that information. + bool hasNonDerivableClangType(); + ExtInfo getExtInfo() const { return ExtInfo(Bits.AnyFunctionType.ExtInfoBits, getClangTypeInfo()); } @@ -4309,6 +4325,11 @@ class SILFunctionType final ClangTypeInfo getClangTypeInfo() const; + /// Returns true if the function type stores a Clang type that cannot + /// be derived from its Swift type. Returns false otherwise, including if + /// the function type is not @convention(c) or @convention(block). + bool hasNonDerivableClangType(); + bool hasSameExtInfoAs(const SILFunctionType *otherFn); /// Given that `this` is a `@differentiable` or `@differentiable(linear)` @@ -5198,6 +5219,10 @@ class PrimaryArchetypeType final : public ArchetypeType, GenericEnvironment *getGenericEnvironment() const { return Environment; } + + GenericTypeParamType *getInterfaceType() const { + return cast(InterfaceType.getPointer()); + } static bool classof(const TypeBase *T) { return T->getKind() == TypeKind::PrimaryArchetype; @@ -5741,7 +5766,8 @@ DEFINE_EMPTY_CAN_TYPE_WRAPPER(TypeVariableType, Type) /// constraint solver and transformed into UnresolvedType to be used in AST. class HoleType : public TypeBase { using Originator = llvm::PointerUnion; + DependentMemberType *, VarDecl *, + ErrorExpr *>; Originator O; diff --git a/include/swift/Basic/CTypeIDZone.def b/include/swift/Basic/CTypeIDZone.def index 007fa187389d4..51aa9bd8ceb61 100644 --- a/include/swift/Basic/CTypeIDZone.def +++ b/include/swift/Basic/CTypeIDZone.def @@ -38,7 +38,9 @@ SWIFT_TYPEID_NAMED(std::string, String) SWIFT_TYPEID_NAMED(evaluator::SideEffect, SideEffect) SWIFT_TYPEID_TEMPLATE1_NAMED(std::vector, Vector, typename T, T) SWIFT_TYPEID_TEMPLATE1_NAMED(std::unique_ptr, UniquePtr, typename T, T) +SWIFT_TYPEID_TEMPLATE2_NAMED(std::pair, Pair, typename T1, T1, typename T2, T2) // LLVM ADT types. SWIFT_TYPEID_TEMPLATE1_NAMED(llvm::TinyPtrVector, TinyPtrVector, typename T, T) SWIFT_TYPEID_TEMPLATE1_NAMED(llvm::ArrayRef, ArrayRef, typename T, T) +SWIFT_TYPEID_TEMPLATE1_NAMED(llvm::Optional, Optional, typename T, T) diff --git a/include/swift/Basic/Compiler.h b/include/swift/Basic/Compiler.h index b422175695ade..1958e762b2b83 100644 --- a/include/swift/Basic/Compiler.h +++ b/include/swift/Basic/Compiler.h @@ -34,10 +34,59 @@ #define SWIFT_ASSUME(x) #endif +/// Attributes. + #if __has_attribute(constructor) #define SWIFT_CONSTRUCTOR __attribute__((constructor)) #else #define SWIFT_CONSTRUCTOR #endif +/// \macro SWIFT_GNUC_PREREQ +/// Extend the default __GNUC_PREREQ even if glibc's features.h isn't +/// available. +#ifndef SWIFT_GNUC_PREREQ +# if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) +# define SWIFT_GNUC_PREREQ(maj, min, patch) \ + ((__GNUC__ << 20) + (__GNUC_MINOR__ << 10) + __GNUC_PATCHLEVEL__ >= \ + ((maj) << 20) + ((min) << 10) + (patch)) +# elif defined(__GNUC__) && defined(__GNUC_MINOR__) +# define SWIFT_GNUC_PREREQ(maj, min, patch) \ + ((__GNUC__ << 20) + (__GNUC_MINOR__ << 10) >= ((maj) << 20) + ((min) << 10)) +# else +# define SWIFT_GNUC_PREREQ(maj, min, patch) 0 +# endif +#endif + + +/// SWIFT_ATTRIBUTE_NOINLINE - On compilers where we have a directive to do so, +/// mark a method "not for inlining". +#if __has_attribute(noinline) || SWIFT_GNUC_PREREQ(3, 4, 0) +#define SWIFT_ATTRIBUTE_NOINLINE __attribute__((noinline)) +#elif defined(_MSC_VER) +#define SWIFT_ATTRIBUTE_NOINLINE __declspec(noinline) +#else +#define SWIFT_ATTRIBUTE_NOINLINE +#endif + +/// SWIFT_ATTRIBUTE_ALWAYS_INLINE - On compilers where we have a directive to do +/// so, mark a method "always inline" because it is performance sensitive. GCC +/// 3.4 supported this but is buggy in various cases and produces unimplemented +/// errors, just use it in GCC 4.0 and later. +#if __has_attribute(always_inline) || SWIFT_GNUC_PREREQ(4, 0, 0) +#define SWIFT_ATTRIBUTE_ALWAYS_INLINE __attribute__((always_inline)) +#elif defined(_MSC_VER) +#define SWIFT_ATTRIBUTE_ALWAYS_INLINE __forceinline +#else +#define SWIFT_ATTRIBUTE_ALWAYS_INLINE +#endif + +#ifdef __GNUC__ +#define SWIFT_ATTRIBUTE_NORETURN __attribute__((noreturn)) +#elif defined(_MSC_VER) +#define SWIFT_ATTRIBUTE_NORETURN __declspec(noreturn) +#else +#define SWIFT_ATTRIBUTE_NORETURN +#endif + #endif // SWIFT_BASIC_COMPILER_H diff --git a/include/swift/Basic/DefineTypeIDZone.h b/include/swift/Basic/DefineTypeIDZone.h index 78779abdc7c9d..315a42a0aa9ed 100644 --- a/include/swift/Basic/DefineTypeIDZone.h +++ b/include/swift/Basic/DefineTypeIDZone.h @@ -40,9 +40,11 @@ template<> struct TypeIDZoneTypes { enum Types : uint8_t { #define SWIFT_TYPEID_NAMED(Type, Name) Name, #define SWIFT_TYPEID_TEMPLATE1_NAMED(Template, Name, Param1, Arg1) Name, +#define SWIFT_TYPEID_TEMPLATE2_NAMED(Template, Name, Param1, Arg1, Param2, Arg2) Name, #include SWIFT_TYPEID_HEADER #undef SWIFT_TYPEID_NAMED #undef SWIFT_TYPEID_TEMPLATE1_NAMED +#undef SWIFT_TYPEID_TEMPLATE2_NAMED }; }; @@ -77,12 +79,34 @@ public: \ \ template const uint64_t TypeID>::value; +#define SWIFT_TYPEID_TEMPLATE2_NAMED(Template, Name, Param1, Arg1, Param2, Arg2) \ +template struct TypeID> { \ +private: \ + static const uint64_t templateID = \ + formTypeID(static_cast(Zone::SWIFT_TYPEID_ZONE), \ + TypeIDZoneTypes::Name); \ + \ +public: \ + static const uint64_t value = \ + (TypeID::value << 32) | \ + (TypeID::value << 16) | \ + templateID; \ + \ + static std::string getName() { \ + return std::string(#Name) + "<" + TypeID::getName() + \ + ", " + TypeID::getName() + ">"; \ + } \ +}; \ + \ +template const uint64_t TypeID>::value; + #include SWIFT_TYPEID_HEADER #undef SWIFT_REQUEST #undef SWIFT_TYPEID_NAMED #undef SWIFT_TYPEID_TEMPLATE1_NAMED +#undef SWIFT_TYPEID_TEMPLATE2_NAMED #undef SWIFT_TYPEID #undef SWIFT_TYPEID_ZONE diff --git a/include/swift/SILOptimizer/Utils/IndexTrie.h b/include/swift/Basic/IndexTrie.h similarity index 75% rename from include/swift/SILOptimizer/Utils/IndexTrie.h rename to include/swift/Basic/IndexTrie.h index b7986cb4d2e22..e9a5133a67162 100644 --- a/include/swift/SILOptimizer/Utils/IndexTrie.h +++ b/include/swift/Basic/IndexTrie.h @@ -13,6 +13,7 @@ #ifndef SWIFT_SILOPTIMIZER_UTILS_INDEXTREE_H #define SWIFT_SILOPTIMIZER_UTILS_INDEXTREE_H +#include "swift/Basic/LLVM.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallVector.h" #include @@ -21,15 +22,18 @@ namespace swift { // Trie node representing a sequence of unsigned integer indices. class IndexTrieNode { - static const unsigned RootIdx = ~0U; - unsigned Index; +public: + static const int RootIndex = std::numeric_limits::min(); + +private: + int Index; llvm::SmallVector Children; IndexTrieNode *Parent; public: - IndexTrieNode(): Index(RootIdx), Parent(nullptr) {} + IndexTrieNode() : Index(RootIndex), Parent(nullptr) {} - explicit IndexTrieNode(unsigned V, IndexTrieNode *P): Index(V), Parent(P) {} + explicit IndexTrieNode(int V, IndexTrieNode *P) : Index(V), Parent(P) {} IndexTrieNode(IndexTrieNode &) =delete; IndexTrieNode &operator=(const IndexTrieNode&) =delete; @@ -39,19 +43,18 @@ class IndexTrieNode { delete N; } - bool isRoot() const { return Index == RootIdx; } + bool isRoot() const { return Index == RootIndex; } bool isLeaf() const { return Children.empty(); } - unsigned getIndex() const { return Index; } + int getIndex() const { return Index; } - IndexTrieNode *getChild(unsigned Idx) { - assert(Idx != RootIdx); + IndexTrieNode *getChild(int Idx) { + assert(Idx != RootIndex); - auto I = std::lower_bound(Children.begin(), Children.end(), Idx, - [](IndexTrieNode *a, unsigned i) { - return a->Index < i; - }); + auto I = + std::lower_bound(Children.begin(), Children.end(), Idx, + [](IndexTrieNode *a, int i) { return a->Index < i; }); if (I != Children.end() && (*I)->Index == Idx) return *I; auto *N = new IndexTrieNode(Idx, this); diff --git a/include/swift/Basic/LLVM.h b/include/swift/Basic/LLVM.h index df5db366409dd..40ab0fe664509 100644 --- a/include/swift/Basic/LLVM.h +++ b/include/swift/Basic/LLVM.h @@ -26,6 +26,14 @@ // without a definition of NoneType. #include "llvm/ADT/None.h" +// Don't pre-declare certain LLVM types in the runtime, which must +// not put things in namespace llvm for ODR reasons. +#if !defined(swiftCore_EXPORTS) +#define SWIFT_LLVM_ODR_SAFE 1 +#else +#define SWIFT_LLVM_ODR_SAFE 0 +#endif + // Forward declarations. namespace llvm { // Containers. @@ -34,18 +42,18 @@ namespace llvm { class Twine; template class SmallPtrSetImpl; template class SmallPtrSet; -#if !defined(swiftCore_EXPORTS) +#if SWIFT_LLVM_ODR_SAFE template class SmallVectorImpl; template class SmallVector; #endif template class SmallString; template class SmallSetVector; -#if !defined(swiftCore_EXPORTS) +#if SWIFT_LLVM_ODR_SAFE template class ArrayRef; template class MutableArrayRef; #endif template class TinyPtrVector; -#if !defined(swiftCore_EXPORTS) +#if SWIFT_LLVM_ODR_SAFE template class Optional; #endif template class PointerUnion; @@ -56,7 +64,7 @@ namespace llvm { class raw_ostream; class APInt; class APFloat; -#if !defined(swiftCore_EXPORTS) +#if SWIFT_LLVM_ODR_SAFE template class function_ref; #endif } // end namespace llvm @@ -71,13 +79,13 @@ namespace swift { using llvm::cast_or_null; // Containers. -#if !defined(swiftCore_EXPORTS) +#if SWIFT_LLVM_ODR_SAFE using llvm::ArrayRef; using llvm::MutableArrayRef; #endif using llvm::iterator_range; using llvm::None; -#if !defined(swiftCore_EXPORTS) +#if SWIFT_LLVM_ODR_SAFE using llvm::Optional; #endif using llvm::PointerUnion; @@ -86,7 +94,7 @@ namespace swift { using llvm::SmallPtrSetImpl; using llvm::SmallSetVector; using llvm::SmallString; -#if !defined(swiftCore_EXPORTS) +#if SWIFT_LLVM_ODR_SAFE using llvm::SmallVector; using llvm::SmallVectorImpl; #endif @@ -98,7 +106,7 @@ namespace swift { // Other common classes. using llvm::APFloat; using llvm::APInt; -#if !defined(swiftCore_EXPORTS) +#if SWIFT_LLVM_ODR_SAFE using llvm::function_ref; #endif using llvm::NoneType; diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index e7054d655cff4..a7f0594f80732 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -115,6 +115,9 @@ namespace swift { /// when using RequireExplicitAvailability. std::string RequireExplicitAvailabilityTarget; + // Availability macros definitions to be expanded at parsing. + SmallVector AvailabilityMacros; + /// If false, '#file' evaluates to the full path rather than a /// human-readable string. bool EnableConcisePoundFile = false; @@ -336,21 +339,10 @@ namespace swift { /// of active clauses aren't hoisted into the enclosing scope. bool DisablePoundIfEvaluation = false; - /// Instead of hashing tokens inside of NominalType and ExtensionBodies into - /// the interface hash, hash them into per-iterable-decl-context - /// fingerprints. Fine-grained dependency types won't dirty every provides - /// in a file when the user adds a member to, e.g., a struct. - bool EnableTypeFingerprints = true; - /// When using fine-grained dependencies, emit dot files for every swiftdeps /// file. bool EmitFineGrainedDependencySourcefileDotFiles = false; - /// To mimic existing system, set to false. - /// To experiment with including file-private and private dependency info, - /// set to true. - bool FineGrainedDependenciesIncludeIntrafileOnes = false; - /// Whether to enable experimental differentiable programming features: /// `@differentiable` declaration attribute, etc. // SWIFT_ENABLE_TENSORFLOW @@ -555,10 +547,6 @@ namespace swift { /// Disable constraint system performance hacks. bool DisableConstraintSolverPerformanceHacks = false; - /// Enable constraint solver support for experimental - /// operator protocol designator feature. - bool SolverEnableOperatorDesignatedTypes = false; - /// Enable experimental support for one-way constraints for the /// parameters of closures. bool EnableOneWayClosureParameters = false; diff --git a/include/swift/Basic/NullablePtr.h b/include/swift/Basic/NullablePtr.h index 316cfbb46251f..75a81eb9a1924 100644 --- a/include/swift/Basic/NullablePtr.h +++ b/include/swift/Basic/NullablePtr.h @@ -20,6 +20,7 @@ #include #include #include +#include "llvm/Support/PointerLikeTypeTraits.h" namespace swift { /// NullablePtr pointer wrapper - NullablePtr is used for APIs where a @@ -81,4 +82,19 @@ class NullablePtr { } // end namespace swift +namespace llvm { +template struct PointerLikeTypeTraits; +template struct PointerLikeTypeTraits> { +public: + static inline void *getAsVoidPointer(swift::NullablePtr ptr) { + return static_cast(ptr.getPtrOrNull()); + } + static inline swift::NullablePtr getFromVoidPointer(void *ptr) { + return swift::NullablePtr(static_cast(ptr)); + } + enum { NumLowBitsAvailable = PointerLikeTypeTraits::NumLowBitsAvailable }; +}; + +} + #endif // SWIFT_BASIC_NULLABLEPTR_H diff --git a/include/swift/Basic/OptionSet.h b/include/swift/Basic/OptionSet.h index 3d632c6c1b6bf..c5758b250ca9c 100644 --- a/include/swift/Basic/OptionSet.h +++ b/include/swift/Basic/OptionSet.h @@ -19,7 +19,6 @@ #include "llvm/ADT/None.h" -#include #include #include #include @@ -99,14 +98,6 @@ class OptionSet { return Storage == set.Storage; } - /// Check if this option set contains any options from \p set. - /// - /// \pre \p set must be non-empty. - bool containsAny(OptionSet set) const { - assert((bool)set && "argument must be non-empty"); - return (bool)((*this) & set); - } - // '==' and '!=' are deliberately not defined because they provide a pitfall // where someone might use '==' but really want 'contains'. If you actually // want '==' behavior, use 'containsOnly'. diff --git a/include/swift/Basic/ReferenceDependencyKeys.h b/include/swift/Basic/ReferenceDependencyKeys.h index 3d7749df035a5..22a68f8ace002 100644 --- a/include/swift/Basic/ReferenceDependencyKeys.h +++ b/include/swift/Basic/ReferenceDependencyKeys.h @@ -53,6 +53,7 @@ enum class NodeKind { dynamicLookup, externalDepend, sourceFileProvide, + incrementalExternalDepend, /// For iterating through the NodeKinds. kindCount }; @@ -60,8 +61,10 @@ enum class NodeKind { /// Used for printing out NodeKinds to dot files, and dumping nodes for /// debugging. const std::string NodeKindNames[]{ - "topLevel", "nominal", "potentialMember", "member", - "dynamicLookup", "externalDepend", "sourceFileProvide"}; + "topLevel", "nominal", + "potentialMember", "member", + "dynamicLookup", "externalDepend", + "sourceFileProvide", "incrementalExternalDepend"}; } // end namespace fine_grained_dependencies } // end namespace swift diff --git a/include/swift/Basic/STLExtras.h b/include/swift/Basic/STLExtras.h index 21d2993bbaa90..2d96e0bc802ae 100644 --- a/include/swift/Basic/STLExtras.h +++ b/include/swift/Basic/STLExtras.h @@ -217,6 +217,47 @@ inline Iterator prev_or_begin(Iterator it, Iterator begin) { /// @} +/// An iterator that walks a linked list of objects until it reaches +/// a null pointer. +template +class LinkedListIterator { + T *Pointer; +public: + using iterator_category = std::forward_iterator_tag; + using value_type = T *; + using reference = T *; + using pointer = void; + + /// Returns an iterator range starting from the given pointer and + /// running until it reaches a null pointer. + static llvm::iterator_range rangeBeginning(T *pointer) { + return {pointer, nullptr}; + } + + constexpr LinkedListIterator(T *pointer) : Pointer(pointer) {} + + T *operator*() const { + assert(Pointer && "dereferencing a null iterator"); + return Pointer; + } + + LinkedListIterator &operator++() { + Pointer = getNext(Pointer); + return *this; + } + LinkedListIterator operator++(int) { + auto copy = *this; + Pointer = getNext(Pointer); + return copy; + } + + friend bool operator==(LinkedListIterator lhs, LinkedListIterator rhs) { + return lhs.Pointer == rhs.Pointer; + } + friend bool operator!=(LinkedListIterator lhs, LinkedListIterator rhs) { + return lhs.Pointer != rhs.Pointer; + } +}; /// An iterator that transforms the result of an underlying bidirectional /// iterator with a given operation. diff --git a/include/swift/Basic/SimpleDisplay.h b/include/swift/Basic/SimpleDisplay.h index 3a31da14654ac..349da1db5f661 100644 --- a/include/swift/Basic/SimpleDisplay.h +++ b/include/swift/Basic/SimpleDisplay.h @@ -23,6 +23,7 @@ #include "llvm/Support/raw_ostream.h" #include #include +#include namespace swift { template @@ -93,7 +94,17 @@ namespace swift { const std::tuple &value) { simple_display_tuple<0>(out, value); } - + + template + void simple_display(llvm::raw_ostream &out, + const std::pair &value) { + out << "("; + simple_display(out, value.first); + out << ", "; + simple_display(out, value.second); + out << ")"; + } + template void simple_display(llvm::raw_ostream &out, const llvm::TinyPtrVector &vector) { diff --git a/include/swift/Basic/Statistics.def b/include/swift/Basic/Statistics.def index 419567fc8bba5..c7fdc1071ef99 100644 --- a/include/swift/Basic/Statistics.def +++ b/include/swift/Basic/Statistics.def @@ -114,6 +114,7 @@ FRONTEND_STATISTIC(AST, NumASTBytesAllocated) /// Number of file-level dependencies of this frontend job, as tracked in the /// AST context's dependency collector. FRONTEND_STATISTIC(AST, NumDependencies) +FRONTEND_STATISTIC(AST, NumIncrementalDependencies) /// Number of top-level, dynamic, and member names referenced in this frontend /// job's source file, as tracked by the AST context's referenced-name tracker. diff --git a/include/swift/Basic/StringExtras.h b/include/swift/Basic/StringExtras.h index 08e4c2cfbe23d..4e837605d2c12 100644 --- a/include/swift/Basic/StringExtras.h +++ b/include/swift/Basic/StringExtras.h @@ -442,6 +442,9 @@ class InheritedNameSet { /// /// \param allPropertyNames The set of property names in the enclosing context. /// +/// \param completionHandlerIndex For an 'async' function, the index of the +/// completion handler in argNames. +/// /// \param scratch Scratch space that will be used for modifications beyond /// just chopping names. /// @@ -455,8 +458,13 @@ bool omitNeedlessWords(StringRef &baseName, bool returnsSelf, bool isProperty, const InheritedNameSet *allPropertyNames, + Optional completionHandlerIndex, + Optional completionHandlerName, StringScratchSpace &scratch); +/// If the name has a completion-handler suffix, strip off that suffix. +Optional stripWithCompletionHandlerSuffix(StringRef name); + } // end namespace swift #endif // SWIFT_BASIC_STRINGEXTRAS_H diff --git a/include/swift/Basic/SupplementaryOutputPaths.h b/include/swift/Basic/SupplementaryOutputPaths.h index 3649d4dbe7ae3..df4701081dcad 100644 --- a/include/swift/Basic/SupplementaryOutputPaths.h +++ b/include/swift/Basic/SupplementaryOutputPaths.h @@ -35,7 +35,7 @@ struct SupplementaryOutputPaths { /// /// This binary format is used to describe the interface of a module when /// imported by client source code. The swiftmodule format is described in - /// docs/Serialization.rst. + /// docs/Serialization.md. /// /// \sa swift::serialize std::string ModuleOutputPath; diff --git a/include/swift/Runtime/Unreachable.h b/include/swift/Basic/Unreachable.h similarity index 53% rename from include/swift/Runtime/Unreachable.h rename to include/swift/Basic/Unreachable.h index 4147d4ce80d23..8d0ed35b5a49d 100644 --- a/include/swift/Runtime/Unreachable.h +++ b/include/swift/Basic/Unreachable.h @@ -1,4 +1,4 @@ -//===--- Unreachable.h - Implements swift_runtime_unreachable ---*- C++ -*-===// +//===--- Unreachable.h - Implements swift_unreachable ---*- C++ -*-===// // // This source file is part of the Swift.org open source project // @@ -10,13 +10,25 @@ // //===----------------------------------------------------------------------===// // -// This file defines swift_runtime_unreachable, an LLVM-independent -// implementation of llvm_unreachable. +// This file defines swift_unreachable, which provides the +// functionality of llvm_unreachable without necessarily depending on +// the LLVM support libraries. // //===----------------------------------------------------------------------===// -#ifndef SWIFT_RUNTIME_UNREACHABLE_H -#define SWIFT_RUNTIME_UNREACHABLE_H +#ifndef SWIFT_BASIC_UNREACHABLE_H +#define SWIFT_BASIC_UNREACHABLE_H + +#ifdef SWIFT_LLVM_SUPPORT_IS_AVAILABLE + +// The implementation when LLVM is available. + +#include "llvm/Support/ErrorHandling.h" +#define swift_unreachable llvm_unreachable + +#else + +// The implementation when LLVM is not available. #include #include @@ -24,10 +36,12 @@ #include "swift/Runtime/Config.h" SWIFT_RUNTIME_ATTRIBUTE_NORETURN -inline static void swift_runtime_unreachable(const char *msg) { +inline static void swift_unreachable(const char *msg) { assert(false && msg); (void)msg; abort(); } -#endif // SWIFT_RUNTIME_UNREACHABLE_H +#endif + +#endif diff --git a/include/swift/ClangImporter/ClangImporter.h b/include/swift/ClangImporter/ClangImporter.h index 578287637964b..2a30888e5cba3 100644 --- a/include/swift/ClangImporter/ClangImporter.h +++ b/include/swift/ClangImporter/ClangImporter.h @@ -472,6 +472,11 @@ class ClangImporter final : public ClangModuleLoader { bool isSerializable(const clang::Type *type, bool checkCanonical) const override; + + clang::FunctionDecl * + instantiateCXXFunctionTemplate(ASTContext &ctx, + clang::FunctionTemplateDecl *func, + SubstitutionMap subst) override; }; ImportDecl *createImportDecl(ASTContext &Ctx, DeclContext *DC, ClangNode ClangN, diff --git a/include/swift/ClangImporter/ClangModule.h b/include/swift/ClangImporter/ClangModule.h index c79fb8c04ece4..6541f1fcacdce 100644 --- a/include/swift/ClangImporter/ClangModule.h +++ b/include/swift/ClangImporter/ClangModule.h @@ -36,7 +36,7 @@ class ClangModuleUnit final : public LoadedFile { ClangImporter::Implementation &owner; const clang::Module *clangModule; llvm::PointerIntPair overlayModule; - mutable Optional> importedModulesForLookup; + mutable Optional> importedModulesForLookup; /// The metadata of the underlying Clang module. clang::ASTSourceDescriptor ASTSourceDescriptor; @@ -92,11 +92,11 @@ class ClangModuleUnit final : public LoadedFile { virtual void getDisplayDecls(SmallVectorImpl &results) const override; virtual void - getImportedModules(SmallVectorImpl &imports, + getImportedModules(SmallVectorImpl &imports, ModuleDecl::ImportFilter filter) const override; virtual void getImportedModulesForLookup( - SmallVectorImpl &imports) const override; + SmallVectorImpl &imports) const override; virtual void collectLinkLibraries(ModuleDecl::LinkLibraryCallback callback) const override; diff --git a/include/swift/Demangling/DemangleNodes.def b/include/swift/Demangling/DemangleNodes.def index 1c3c612f9e98a..3500b5c7a4f62 100644 --- a/include/swift/Demangling/DemangleNodes.def +++ b/include/swift/Demangling/DemangleNodes.def @@ -44,6 +44,7 @@ NODE(BoundGenericTypeAlias) NODE(BoundGenericFunction) NODE(BuiltinTypeName) NODE(CFunctionPointer) +NODE(ClangType) CONTEXT_NODE(Class) NODE(ClassMetadataBaseOffset) NODE(ConcreteProtocolConformance) @@ -104,6 +105,7 @@ NODE(ResilientProtocolWitnessTable) NODE(GenericSpecialization) NODE(GenericSpecializationNotReAbstracted) NODE(GenericSpecializationParam) +NODE(GenericSpecializationPrespecialized) NODE(InlinedGenericFunction) NODE(GenericTypeMetadataPattern) CONTEXT_NODE(Getter) @@ -119,6 +121,8 @@ NODE(ImplEscaping) NODE(ImplConvention) NODE(ImplDifferentiability) NODE(ImplFunctionAttribute) +NODE(ImplFunctionConvention) +NODE(ImplFunctionConventionName) NODE(ImplFunctionType) NODE(ImplInvocationSubstitutions) CONTEXT_NODE(ImplicitClosure) diff --git a/include/swift/Demangling/Demangler.h b/include/swift/Demangling/Demangler.h index 3616ff426af86..81c352a344962 100644 --- a/include/swift/Demangling/Demangler.h +++ b/include/swift/Demangling/Demangler.h @@ -140,7 +140,7 @@ class NodeFactory { #endif // Do we have enough space in the current slab? - if (CurPtr + ObjectSize > End) { + if (!CurPtr || CurPtr + ObjectSize > End) { // No. We have to malloc a new slab. // We double the slab size for each allocated slab. SlabSize = std::max(SlabSize * 2, ObjectSize + alignof(T)); @@ -505,7 +505,7 @@ class Demangler : public NodeFactory { NodePointer demangleAnyGenericType(Node::Kind kind); NodePointer demangleExtensionContext(); NodePointer demanglePlainFunction(); - NodePointer popFunctionType(Node::Kind kind); + NodePointer popFunctionType(Node::Kind kind, bool hasClangType = false); NodePointer popFunctionParams(Node::Kind kind); NodePointer popFunctionParamLabels(NodePointer FuncType); NodePointer popTuple(); @@ -522,6 +522,7 @@ class Demangler : public NodeFactory { NodePointer demangleImplResultConvention(Node::Kind ConvKind); NodePointer demangleImplDifferentiability(); NodePointer demangleImplFunctionType(); + NodePointer demangleClangType(); NodePointer demangleMetatype(); NodePointer demanglePrivateContextDescriptor(); NodePointer createArchetypeRef(int depth, int i); diff --git a/include/swift/Demangling/TypeDecoder.h b/include/swift/Demangling/TypeDecoder.h index 35b62c50d5d53..bbe257b4bf407 100644 --- a/include/swift/Demangling/TypeDecoder.h +++ b/include/swift/Demangling/TypeDecoder.h @@ -24,7 +24,7 @@ #include "swift/Demangling/Demangler.h" #include "swift/Demangling/NamespaceMacros.h" #include "swift/Runtime/Portability.h" -#include "swift/Runtime/Unreachable.h" +#include "swift/Basic/Unreachable.h" #include "swift/Strings.h" #include "llvm/ADT/ArrayRef.h" #include @@ -642,6 +642,12 @@ class TypeDecoder { } unsigned firstChildIdx = 0; + if (Node->getChild(firstChildIdx)->getKind() == NodeKind::ClangType) { + // [TODO: synthesize-Clang-type-from-mangled-name] Use the first child + // to create a ClangTypeInfo. + ++firstChildIdx; + } + bool isThrow = false; if (Node->getChild(firstChildIdx)->getKind() == NodeKind::ThrowsAnnotation) { @@ -706,18 +712,30 @@ class TypeDecoder { } else if (child->getText() == "@callee_guaranteed") { calleeConvention = ImplParameterConvention::Direct_Guaranteed; } - } else if (child->getKind() == NodeKind::ImplFunctionAttribute) { - if (!child->hasText()) - return MAKE_NODE_TYPE_ERROR0(child, "expected text"); - - StringRef text = child->getText(); - if (text == "@convention(c)") { + } else if (child->getKind() == NodeKind::ImplFunctionConvention) { + if (child->getNumChildren() == 0) + return MAKE_NODE_TYPE_ERROR0(child, "expected grandchildren"); + if ((child->getFirstChild()->getKind() != + NodeKind::ImplFunctionConventionName) || + !child->getFirstChild()->hasText()) + return MAKE_NODE_TYPE_ERROR0(child, "expected convention name"); + + // [TODO: synthesize-Clang-type-from-mangled-name] If there are two + // grand-children, the second is going to be the mangled Clang type. + StringRef text = child->getFirstChild()->getText(); + if (text == "c") { flags = flags.withRepresentation(ImplFunctionRepresentation::CFunctionPointer); - } else if (text == "@convention(block)") { + } else if (text == "block") { flags = flags.withRepresentation(ImplFunctionRepresentation::Block); } + } else if (child->getKind() == NodeKind::ImplFunctionAttribute) { + if (!child->hasText()) + return MAKE_NODE_TYPE_ERROR0(child, "expected text"); + if (child->getText() == "@async") { + flags = flags.withAsync(); + } } else if (child->getKind() == NodeKind::ImplDifferentiable) { flags = flags.withDifferentiabilityKind( ImplFunctionDifferentiabilityKind::Normal); diff --git a/include/swift/Demangling/TypeLookupError.h b/include/swift/Demangling/TypeLookupError.h index c67fc35598be8..cf2c75087e0fa 100644 --- a/include/swift/Demangling/TypeLookupError.h +++ b/include/swift/Demangling/TypeLookupError.h @@ -150,6 +150,7 @@ class TypeLookupError { delete castContext; return nullptr; } + llvm_unreachable("unhandled command!"); }; } diff --git a/include/swift/Driver/Action.h b/include/swift/Driver/Action.h index b2d6ed1a2c538..7b325fa0907c6 100644 --- a/include/swift/Driver/Action.h +++ b/include/swift/Driver/Action.h @@ -127,16 +127,31 @@ class JobAction : public Action { } }; -class CompileJobAction : public JobAction { +class IncrementalJobAction : public JobAction { public: struct InputInfo { - enum Status { + /// The status of an input known to the driver. These are used to affect + /// the scheduling decisions made during an incremental build. + /// + /// \Note The order of cases matters. They are ordered from least to + /// greatest impact on the incremental build schedule. + enum class Status { + /// The input to this job is up to date. UpToDate, - NeedsCascadingBuild, + /// The input to this job has changed in a way that requires this job to + /// be rerun, but not in such a way that it requires a cascading rebuild. NeedsNonCascadingBuild, + /// The input to this job has changed in a way that requires this job to + /// be rerun, and in such a way that all jobs dependent upon this one + /// must be scheduled as well. + NeedsCascadingBuild, + /// The input to this job was not known to the driver when it was last + /// run. NewlyAdded }; - Status status = UpToDate; + + public: + Status status = Status::UpToDate; llvm::sys::TimePoint<> previousModTime; InputInfo() = default; @@ -144,7 +159,11 @@ class CompileJobAction : public JobAction { : status(stat), previousModTime(time) {} static InputInfo makeNewlyAdded() { - return InputInfo(Status::NewlyAdded, llvm::sys::TimePoint<>::max()); + return {Status::NewlyAdded, llvm::sys::TimePoint<>::max()}; + } + + static InputInfo makeNeedsCascadingRebuild() { + return {Status::NeedsCascadingBuild, llvm::sys::TimePoint<>::min()}; } }; @@ -153,18 +172,33 @@ class CompileJobAction : public JobAction { InputInfo inputInfo; public: - CompileJobAction(file_types::ID OutputType) - : JobAction(Action::Kind::CompileJob, None, OutputType), - inputInfo() {} - - CompileJobAction(Action *Input, file_types::ID OutputType, InputInfo info) - : JobAction(Action::Kind::CompileJob, Input, OutputType), - inputInfo(info) {} + IncrementalJobAction(Kind Kind, ArrayRef Inputs, + file_types::ID Type, InputInfo info) + : JobAction(Kind, Inputs, Type), inputInfo(info) {} +public: InputInfo getInputInfo() const { return inputInfo; } +public: + static bool classof(const Action *A) { + return A->getKind() == Action::Kind::CompileJob || + A->getKind() == Action::Kind::MergeModuleJob; + } +}; + +class CompileJobAction : public IncrementalJobAction { +private: + virtual void anchor() override; + +public: + CompileJobAction(file_types::ID OutputType) + : IncrementalJobAction(Action::Kind::CompileJob, None, OutputType, {}) {} + CompileJobAction(Action *Input, file_types::ID OutputType, InputInfo info) + : IncrementalJobAction(Action::Kind::CompileJob, Input, OutputType, + info) {} + static bool classof(const Action *A) { return A->getKind() == Action::Kind::CompileJob; } @@ -247,12 +281,12 @@ class REPLJobAction : public JobAction { } }; -class MergeModuleJobAction : public JobAction { +class MergeModuleJobAction : public IncrementalJobAction { virtual void anchor() override; public: - MergeModuleJobAction(ArrayRef Inputs) - : JobAction(Action::Kind::MergeModuleJob, Inputs, - file_types::TY_SwiftModuleFile) {} + MergeModuleJobAction(ArrayRef Inputs, InputInfo input) + : IncrementalJobAction(Action::Kind::MergeModuleJob, Inputs, + file_types::TY_SwiftModuleFile, input) {} static bool classof(const Action *A) { return A->getKind() == Action::Kind::MergeModuleJob; diff --git a/include/swift/Driver/Compilation.h b/include/swift/Driver/Compilation.h index 1da048fa802b5..2dd41bfb57582 100644 --- a/include/swift/Driver/Compilation.h +++ b/include/swift/Driver/Compilation.h @@ -157,6 +157,16 @@ class Compilation { /// The Jobs which will be performed by this compilation. SmallVector, 32> Jobs; + // The Jobs which represent external actions performed by other drivers in + // the build graph. + // + // These Jobs are never scheduled into the build graph. This vector is + // populated by the routine that computes the set of incremental external + // dependencies that affect the current computation. Due to the way the + // Driver models multiple aspects of the incremental compilation scheduler + // by mapping to and from Jobs, it is necessary to lie and retain a set of + // pseudo-Jobs. + SmallVector, 32> ExternalJobs; /// The original (untranslated) input argument list. /// @@ -206,15 +216,6 @@ 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. @@ -274,9 +275,6 @@ class Compilation { const bool OnlyOneDependencyFile; private: - /// Is the parser recording token hashes for each type body? - const bool EnableTypeFingerprints; - /// Helpful for debugging, but slows down the driver. So, only turn on when /// needed. const bool VerifyFineGrainedDependencyGraphAfterEveryImport; @@ -284,12 +282,12 @@ class Compilation { /// needed. const bool EmitFineGrainedDependencyDotFileAfterEveryImport; - /// Experiment with intrafile dependencies - const bool FineGrainedDependenciesIncludeIntrafileOnes; - /// Experiment with source-range-based dependencies const bool EnableSourceRangeDependencies; + /// (experimental) Enable cross-module incremental build scheduling. + const bool EnableCrossModuleIncrementalBuild; + public: /// Will contain a comparator if an argument demands it. Optional IncrementalComparator; @@ -313,7 +311,6 @@ class Compilation { std::unique_ptr TranslatedArgs, InputFileList InputsWithTypes, std::string CompilationRecordPath, - bool OutputCompilationRecordForModuleOnlyBuild, StringRef ArgsHash, llvm::sys::TimePoint<> StartTime, llvm::sys::TimePoint<> LastBuildTime, size_t FilelistThreshold, @@ -326,14 +323,12 @@ class Compilation { bool ShowDriverTimeCompilation = false, std::unique_ptr Stats = nullptr, bool OnlyOneDependencyFile = false, - bool EnableTypeFingerprints = - LangOptions().EnableTypeFingerprints, bool VerifyFineGrainedDependencyGraphAfterEveryImport = false, bool EmitFineGrainedDependencyDotFileAfterEveryImport = false, - bool FineGrainedDependenciesIncludeIntrafileOnes = false, bool EnableSourceRangeDependencies = false, bool CompareIncrementalSchemes = false, - StringRef CompareIncrementalSchemesPath = ""); + StringRef CompareIncrementalSchemesPath = "", + bool EnableCrossModuleIncrementalBuild = false); // clang-format on ~Compilation(); @@ -363,7 +358,6 @@ class Compilation { UnwrappedArrayView getJobs() const { return llvm::makeArrayRef(Jobs); } - Job *addJob(std::unique_ptr J); /// To send job list to places that don't truck in fancy array views. std::vector getJobsSimply() const { @@ -392,8 +386,6 @@ class Compilation { } void disableIncrementalBuild(Twine why); - bool getEnableTypeFingerprints() const { return EnableTypeFingerprints; } - bool getVerifyFineGrainedDependencyGraphAfterEveryImport() const { return VerifyFineGrainedDependencyGraphAfterEveryImport; } @@ -402,10 +394,6 @@ class Compilation { return EmitFineGrainedDependencyDotFileAfterEveryImport; } - bool getFineGrainedDependenciesIncludeIntrafileOnes() const { - return FineGrainedDependenciesIncludeIntrafileOnes; - } - bool getEnableSourceRangeDependencies() const { return EnableSourceRangeDependencies; } @@ -439,6 +427,10 @@ class Compilation { return ShowDriverTimeCompilation; } + bool getEnableCrossModuleIncrementalBuild() const { + return EnableCrossModuleIncrementalBuild; + } + size_t getFilelistThreshold() const { return FilelistThreshold; } @@ -532,6 +524,13 @@ class Compilation { const JobCollection &unsortedJobs, SmallVectorImpl &sortedJobs) const; +private: + friend class Driver; + friend class PerformJobsState; + + Job *addJob(std::unique_ptr J); + Job *addExternalJob(std::unique_ptr J); + private: /// Perform all jobs. /// diff --git a/include/swift/Driver/FineGrainedDependencyDriverGraph.h b/include/swift/Driver/FineGrainedDependencyDriverGraph.h index 16f9a70dea984..20ccaeccd96b0 100644 --- a/include/swift/Driver/FineGrainedDependencyDriverGraph.h +++ b/include/swift/Driver/FineGrainedDependencyDriverGraph.h @@ -168,6 +168,7 @@ class ModuleDepGraph { // Supports requests from the driver to getExternalDependencies. std::unordered_set externalDependencies; + std::unordered_set incrementalExternalDependencies; /// Keyed by swiftdeps filename, so we can get back to Jobs. std::unordered_map jobsBySwiftDeps; @@ -188,8 +189,6 @@ class ModuleDepGraph { const bool verifyFineGrainedDependencyGraphAfterEveryImport; const bool emitFineGrainedDependencyDotFileAfterEveryImport; - const bool EnableTypeFingerprints; - private: /// If tracing dependencies, holds a vector used to hold the current path /// def - use/def - use/def - ... @@ -279,8 +278,6 @@ class ModuleDepGraph { assert(swiftDeps.hasValue() && "Don't call me for expats."); auto iter = jobsBySwiftDeps.find(swiftDeps.getValue()); assert(iter != jobsBySwiftDeps.end() && "All jobs should be tracked."); - assert(getSwiftDeps(iter->second) == swiftDeps.getValue() && - "jobsBySwiftDeps should be inverse of getSwiftDeps."); return iter->second; } @@ -295,14 +292,12 @@ class ModuleDepGraph { /// \p stats may be null ModuleDepGraph(const bool verifyFineGrainedDependencyGraphAfterEveryImport, const bool emitFineGrainedDependencyDotFileAfterEveryImport, - const bool EnableTypeFingerprints, const bool shouldTraceDependencies, UnifiedStatsReporter *stats) : verifyFineGrainedDependencyGraphAfterEveryImport( verifyFineGrainedDependencyGraphAfterEveryImport), emitFineGrainedDependencyDotFileAfterEveryImport( emitFineGrainedDependencyDotFileAfterEveryImport), - EnableTypeFingerprints(EnableTypeFingerprints), currentPathIfTracing( shouldTraceDependencies ? llvm::Optional>( @@ -313,11 +308,10 @@ class ModuleDepGraph { } /// For unit tests. - ModuleDepGraph(const bool EnableTypeFingerprints, - const bool EmitDotFilesForDebugging = false) + ModuleDepGraph(const bool EmitDotFilesForDebugging = false) : ModuleDepGraph( true, /*emitFineGrainedDependencyDotFileAfterEveryImport=*/ - EmitDotFilesForDebugging, EnableTypeFingerprints, false, nullptr) {} + EmitDotFilesForDebugging, false, nullptr) {} //============================================================================ // MARK: ModuleDepGraph - updating from a switdeps file @@ -335,6 +329,8 @@ class ModuleDepGraph { const SourceFileDepGraph &, DiagnosticEngine &); + Changes loadFromSwiftModuleBuffer(const driver::Job *, llvm::MemoryBuffer &, + DiagnosticEngine &); private: /// Read a SourceFileDepGraph belonging to \p job from \p buffer @@ -470,7 +466,7 @@ class ModuleDepGraph { void printPath(raw_ostream &out, const driver::Job *node) const; /// Get a printable filename, given a node's swiftDeps. - StringRef getProvidingFilename(Optional swiftDeps) const; + StringRef getProvidingFilename(const Optional &swiftDeps) const; /// Print one node on the dependency path. static void printOneNodeOfPath(raw_ostream &out, const DependencyKey &key, @@ -512,15 +508,28 @@ class ModuleDepGraph { std::vector findExternallyDependentUntracedJobs(StringRef externalDependency); + /// Find jobs that were previously not known to need compilation but that + /// depend on \c incrementalExternalDependency. + /// + /// This code path should only act as a fallback to the status-quo behavior. + /// Otherwise it acts to pessimize the behavior of cross-module incremental + /// builds. + std::vector + findIncrementalExternallyDependentUntracedJobs(StringRef externalDependency); + //============================================================================ // MARK: ModuleDepGraph - External dependencies //============================================================================ public: std::vector getExternalDependencies() const; + std::vector getIncrementalExternalDependencies() const; void forEachUntracedJobDirectlyDependentOnExternalSwiftDeps( StringRef externalDependency, function_ref fn); + void forEachUntracedJobDirectlyDependentOnExternalIncrementalSwiftDeps( + StringRef externalDependency, function_ref fn); + //============================================================================ // MARK: ModuleDepGraph - verification //============================================================================ diff --git a/include/swift/Driver/Job.h b/include/swift/Driver/Job.h index 52ba51aafa1fe..c14dcbc2a47bc 100644 --- a/include/swift/Driver/Job.h +++ b/include/swift/Driver/Job.h @@ -308,6 +308,23 @@ class Job { /// The modification time of the main input file, if any. llvm::sys::TimePoint<> InputModTime = llvm::sys::TimePoint<>::max(); +#ifndef NDEBUG + /// The "wave" of incremental jobs that this \c Job was scheduled into. + /// + /// The first "wave" of jobs is computed by the driver from the set of inputs + /// and external files that have been mutated by the user. From there, as + /// jobs from the first wave finish executing, we reload their \c swiftdeps + /// files and re-integrate them into the dependency graph to discover + /// the jobs for the second "wave". + /// + /// In +asserts builds, we ensure that no more than two "waves" occur for + /// any given incremental compilation session. This is a consequence of + /// 1) transitivity in dependency arcs + /// 2) dependency tracing from uses that affect a def's interfaces to that + /// def's uses. + mutable unsigned Wave = 1; +#endif + public: Job(const JobAction &Source, SmallVectorImpl &&Inputs, std::unique_ptr Output, const char *Executable, @@ -399,6 +416,11 @@ class Job { /// Assumes that, if a compile job, has one primary swift input /// May return empty if none. StringRef getFirstSwiftPrimaryInput() const; + +#ifndef NDEBUG + unsigned getWave() const { return Wave; } + void setWave(unsigned WaveNum) const { Wave = WaveNum; } +#endif }; /// A BatchJob comprises a _set_ of jobs, each of which is sufficiently similar diff --git a/include/swift/Frontend/BackDeploymentLibs.def b/include/swift/Frontend/BackDeploymentLibs.def index b2d9f8a84acd9..7a2ba2d638c86 100644 --- a/include/swift/Frontend/BackDeploymentLibs.def +++ b/include/swift/Frontend/BackDeploymentLibs.def @@ -26,6 +26,7 @@ BACK_DEPLOYMENT_LIB((5, 0), all, "swiftCompatibility50") BACK_DEPLOYMENT_LIB((5, 1), all, "swiftCompatibility51") +BACK_DEPLOYMENT_LIB((5, 3), all, "swiftCompatibility53") BACK_DEPLOYMENT_LIB((5, 0), executable, "swiftCompatibilityDynamicReplacements") #undef BACK_DEPLOYMENT_LIB diff --git a/include/swift/Frontend/Frontend.h b/include/swift/Frontend/Frontend.h index 482a2be3983cb..64f29e9804f0e 100644 --- a/include/swift/Frontend/Frontend.h +++ b/include/swift/Frontend/Frontend.h @@ -337,6 +337,10 @@ class CompilerInvocation { /// Whether the Swift -Onone support library should be implicitly imported. bool shouldImportSwiftONoneSupport() const; + /// Whether the Swift Concurrency support library should be implicitly + /// imported. + bool shouldImportSwiftConcurrency() const; + /// Performs input setup common to these tools: /// sil-opt, sil-func-extractor, sil-llvm-gen, and sil-nm. /// Return value includes the buffer so caller can keep it alive. diff --git a/include/swift/Frontend/FrontendOptions.h b/include/swift/Frontend/FrontendOptions.h index f39915e8a202d..66fdcc94afefd 100644 --- a/include/swift/Frontend/FrontendOptions.h +++ b/include/swift/Frontend/FrontendOptions.h @@ -34,7 +34,8 @@ class FrontendOptions { friend class ArgsToFrontendOptionsConverter; /// A list of arbitrary modules to import and make implicitly visible. - std::vector ImplicitImportModuleNames; + std::vector> + ImplicitImportModuleNames; public: FrontendInputsAndOutputs InputsAndOutputs; @@ -272,10 +273,22 @@ class FrontendOptions { /// built and given to the compiler invocation. bool DisableImplicitModules = false; + /// Disable building Swift modules from textual interfaces. This should be + /// for testing purposes only. + bool DisableBuildingInterface = false; + /// When performing a dependency scanning action, only identify and output all imports /// of the main Swift module's source files. bool ImportPrescan = false; + /// When performing an incremental build, ensure that cross-module incremental + /// build metadata is available in any swift modules emitted by this frontend + /// job. + /// + /// This flag is currently only propagated from the driver to + /// any merge-modules jobs. + bool EnableExperimentalCrossModuleIncrementalBuild = false; + /// The different modes for validating TBD against the LLVM IR. enum class TBDValidationMode { Default, ///< Do the default validation for the current platform. @@ -339,7 +352,8 @@ class FrontendOptions { /// Retrieves the list of arbitrary modules to import and make implicitly /// visible. - ArrayRef getImplicitImportModuleNames() const { + ArrayRef> + getImplicitImportModuleNames() const { return ImplicitImportModuleNames; } diff --git a/include/swift/Frontend/ModuleInterfaceLoader.h b/include/swift/Frontend/ModuleInterfaceLoader.h index 2e0cc1760e7a1..647cdf8f09651 100644 --- a/include/swift/Frontend/ModuleInterfaceLoader.h +++ b/include/swift/Frontend/ModuleInterfaceLoader.h @@ -290,11 +290,13 @@ struct ModuleInterfaceLoaderOptions { bool remarkOnRebuildFromInterface = false; bool disableInterfaceLock = false; bool disableImplicitSwiftModule = false; + bool disableBuildingInterface = false; std::string mainExecutablePath; ModuleInterfaceLoaderOptions(const FrontendOptions &Opts): remarkOnRebuildFromInterface(Opts.RemarkOnRebuildFromModuleInterface), disableInterfaceLock(Opts.DisableInterfaceFileLock), disableImplicitSwiftModule(Opts.DisableImplicitModules), + disableBuildingInterface(Opts.DisableBuildingInterface), mainExecutablePath(Opts.MainExecutablePath) { switch (Opts.RequestedAction) { @@ -309,6 +311,35 @@ struct ModuleInterfaceLoaderOptions { ModuleInterfaceLoaderOptions() = default; }; +class ModuleInterfaceCheckerImpl: public ModuleInterfaceChecker { + friend class ModuleInterfaceLoader; + ASTContext &Ctx; + std::string CacheDir; + std::string PrebuiltCacheDir; + ModuleInterfaceLoaderOptions Opts; + +public: + explicit ModuleInterfaceCheckerImpl(ASTContext &Ctx, + StringRef cacheDir, + StringRef prebuiltCacheDir, + ModuleInterfaceLoaderOptions Opts) + : Ctx(Ctx), CacheDir(cacheDir), PrebuiltCacheDir(prebuiltCacheDir), + Opts(Opts) {} + + std::vector + getCompiledModuleCandidatesForInterface(StringRef moduleName, + StringRef interfacePath) override; + + /// Given a list of potential ready-to-use compiled modules for \p interfacePath, + /// check if any one of them is up-to-date. If so, emit a forwarding module + /// to the candidate binary module to \p outPath. + bool tryEmitForwardingModule(StringRef moduleName, + StringRef interfacePath, + ArrayRef candidates, + StringRef outPath) override; + bool isCached(StringRef DepPath); +}; + /// A ModuleLoader that runs a subordinate \c CompilerInvocation and /// \c CompilerInstance to convert .swiftinterface files to .swiftmodule /// files on the fly, caching the resulting .swiftmodules in the module cache @@ -316,20 +347,16 @@ struct ModuleInterfaceLoaderOptions { class ModuleInterfaceLoader : public SerializedModuleLoaderBase { friend class unittest::ModuleInterfaceLoaderTest; explicit ModuleInterfaceLoader( - ASTContext &ctx, StringRef cacheDir, StringRef prebuiltCacheDir, + ASTContext &ctx, ModuleInterfaceCheckerImpl &InterfaceChecker, DependencyTracker *tracker, ModuleLoadingMode loadMode, ArrayRef PreferInterfaceForModules, - bool IgnoreSwiftSourceInfoFile, ModuleInterfaceLoaderOptions Opts) - : SerializedModuleLoaderBase(ctx, tracker, loadMode, - IgnoreSwiftSourceInfoFile), - CacheDir(cacheDir), PrebuiltCacheDir(prebuiltCacheDir), - PreferInterfaceForModules(PreferInterfaceForModules), - Opts(Opts) {} + bool IgnoreSwiftSourceInfoFile) + : SerializedModuleLoaderBase(ctx, tracker, loadMode, IgnoreSwiftSourceInfoFile), + InterfaceChecker(InterfaceChecker), + PreferInterfaceForModules(PreferInterfaceForModules){} - std::string CacheDir; - std::string PrebuiltCacheDir; + ModuleInterfaceCheckerImpl &InterfaceChecker; ArrayRef PreferInterfaceForModules; - ModuleInterfaceLoaderOptions Opts; std::error_code findModuleFilesInDirectory( ImportPath::Element ModuleID, @@ -343,17 +370,14 @@ class ModuleInterfaceLoader : public SerializedModuleLoaderBase { bool isCached(StringRef DepPath) override; public: static std::unique_ptr - create(ASTContext &ctx, StringRef cacheDir, StringRef prebuiltCacheDir, + create(ASTContext &ctx, ModuleInterfaceCheckerImpl &InterfaceChecker, DependencyTracker *tracker, ModuleLoadingMode loadMode, ArrayRef PreferInterfaceForModules = {}, - ModuleInterfaceLoaderOptions Opts = ModuleInterfaceLoaderOptions(), bool IgnoreSwiftSourceInfoFile = false) { return std::unique_ptr( - new ModuleInterfaceLoader(ctx, cacheDir, prebuiltCacheDir, - tracker, loadMode, - PreferInterfaceForModules, - IgnoreSwiftSourceInfoFile, - Opts)); + new ModuleInterfaceLoader(ctx, InterfaceChecker, tracker, loadMode, + PreferInterfaceForModules, + IgnoreSwiftSourceInfoFile)); } /// Append visible module names to \p names. Note that names are possibly @@ -373,18 +397,6 @@ class ModuleInterfaceLoader : public SerializedModuleLoaderBase { StringRef ModuleName, StringRef InPath, StringRef OutPath, bool SerializeDependencyHashes, bool TrackSystemDependencies, ModuleInterfaceLoaderOptions Opts); - - std::vector - getCompiledModuleCandidatesForInterface(StringRef moduleName, - StringRef interfacePath) override; - - /// Given a list of potential ready-to-use compiled modules for \p interfacePath, - /// check if any one of them is up-to-date. If so, emit a forwarding module - /// to the candidate binary module to \p outPath. - bool tryEmitForwardingModule(StringRef moduleName, - StringRef interfacePath, - ArrayRef candidates, - StringRef outPath) override; }; struct InterfaceSubContextDelegateImpl: InterfaceSubContextDelegate { diff --git a/include/swift/Option/FrontendOptions.td b/include/swift/Option/FrontendOptions.td index 2b1f8efb047d2..a1fddc99263d2 100644 --- a/include/swift/Option/FrontendOptions.td +++ b/include/swift/Option/FrontendOptions.td @@ -175,36 +175,6 @@ def autolink_library : Separate<["-"], "autolink-library">, def disable_typo_correction : Flag<["-"], "disable-typo-correction">, HelpText<"Disable typo correction">; -} // end let Flags = [FrontendOption, NoDriverOption] - -def debug_crash_Group : OptionGroup<"">; -class DebugCrashOpt : Group; - - -// Flags that are saved into module interfaces -let Flags = [FrontendOption, NoDriverOption, HelpHidden, ModuleInterfaceOption] in { - -def enable_objc_interop : - Flag<["-"], "enable-objc-interop">, - HelpText<"Enable Objective-C interop code generation and config directives">; - -def disable_objc_interop : - Flag<["-"], "disable-objc-interop">, - HelpText<"Disable Objective-C interop code generation and config directives">; - -def enable_objc_attr_requires_foundation_module : - Flag<["-"], "enable-objc-attr-requires-foundation-module">, - HelpText<"Enable requiring uses of @objc to require importing the " - "Foundation module">; - -def disable_objc_attr_requires_foundation_module : - Flag<["-"], "disable-objc-attr-requires-foundation-module">, - HelpText<"Disable requiring uses of @objc to require importing the " - "Foundation module">; - -def enable_resilience : Flag<["-"], "enable-resilience">, - HelpText<"Deprecated, use -enable-library-evolution instead">; - def disable_implicit_swift_modules: Flag<["-"], "disable-implicit-swift-modules">, HelpText<"Disable building Swift modules explicitly by the compiler">; @@ -227,6 +197,37 @@ def batch_scan_input_file def import_prescan : Flag<["-"], "import-prescan">, HelpText<"When performing a dependency scan, only dentify all imports of the main Swift module sources">; +} // end let Flags = [FrontendOption, NoDriverOption] + +def debug_crash_Group : OptionGroup<"">; +class DebugCrashOpt : Group; + + +// Flags that are saved into module interfaces +let Flags = [FrontendOption, NoDriverOption, HelpHidden, ModuleInterfaceOption] in { + def enable_objc_interop : + Flag<["-"], "enable-objc-interop">, + HelpText<"Enable Objective-C interop code generation and config directives">; + + def disable_objc_interop : + Flag<["-"], "disable-objc-interop">, + HelpText<"Disable Objective-C interop code generation and config directives">; + + def enable_objc_attr_requires_foundation_module : + Flag<["-"], "enable-objc-attr-requires-foundation-module">, + HelpText<"Enable requiring uses of @objc to require importing the " + "Foundation module">; + + def disable_objc_attr_requires_foundation_module : + Flag<["-"], "disable-objc-attr-requires-foundation-module">, + HelpText<"Disable requiring uses of @objc to require importing the " + "Foundation module">; + +def enable_experimental_concurrency : + Flag<["-"], "enable-experimental-concurrency">, + HelpText<"Enable experimental concurrency model">; + def enable_resilience : Flag<["-"], "enable-resilience">, + HelpText<"Deprecated, use -enable-library-evolution instead">; } // HIDDEN FLAGS @@ -389,10 +390,6 @@ def enable_experimental_static_assert : Flag<["-"], "enable-experimental-static-assert">, HelpText<"Enable experimental #assert">; -def enable_experimental_concurrency : - Flag<["-"], "enable-experimental-concurrency">, - HelpText<"Enable experimental concurrency model">; - def enable_subst_sil_function_types_for_function_values : Flag<["-"], "enable-subst-sil-function-types-for-function-values">, HelpText<"Use substituted function types for SIL type lowering of function values">; @@ -483,10 +480,6 @@ def enable_operator_designated_types : Flag<["-"], "enable-operator-designated-types">, HelpText<"Enable operator designated types">; -def solver_enable_operator_designated_types : - Flag<["-"], "solver-enable-operator-designated-types">, - HelpText<"Enable operator designated types in constraint solver">; - def enable_invalid_ephemeralness_as_error : Flag<["-"], "enable-invalid-ephemeralness-as-error">, HelpText<"Diagnose invalid ephemeral to non-ephemeral conversions as errors">; @@ -515,6 +508,9 @@ def enable_throw_without_try : Flag<["-"], "enable-throw-without-try">, def import_module : Separate<["-"], "import-module">, HelpText<"Implicitly import the specified module">; +def testable_import_module : Separate<["-"], "testable-import-module">, + HelpText<"Implicitly import the specified module with @testable">; + def print_stats : Flag<["-"], "print-stats">, HelpText<"Print various statistics">; @@ -761,4 +757,8 @@ def candidate_module_file def use_static_resource_dir : Flag<["-"], "use-static-resource-dir">, HelpText<"Use resources in the static resource directory">; + +def disable_building_interface + : Flag<["-"], "disable-building-interface">, + HelpText<"Disallow building binary module from textual interface">; } // end let Flags = [FrontendOption, NoDriverOption, HelpHidden] diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index aab9f604e7caf..5bd4afc571903 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -139,14 +139,6 @@ def driver_always_rebuild_dependents : Flag<["-"], "driver-always-rebuild-dependents">, InternalDebugOpt, HelpText<"Always rebuild dependents of files that have been modified">; -def enable_type_fingerprints : -Flag<["-"], "enable-type-fingerprints">, Flags<[FrontendOption, HelpHidden]>, -HelpText<"Enable per-nominal and extension body fingerprints">; - -def disable_type_fingerprints : -Flag<["-"], "disable-type-fingerprints">, Flags<[FrontendOption, HelpHidden]>, -HelpText<"Disable per-nominal and extension body fingerprints">; - def enable_only_one_dependency_file : Flag<["-"], "enable-only-one-dependency-file">, Flags<[DoesNotAffectIncrementalBuild]>, HelpText<"Enables incremental build optimization that only produces one dependencies file">; @@ -190,11 +182,6 @@ Flag<["-"], "driver-emit-fine-grained-dependency-dot-file-after-every-import">, InternalDebugOpt, HelpText<"Emit dot files every time driver imports an fine-grained swiftdeps file.">; -def fine_grained_dependency_include_intrafile : -Flag<["-"], "fine-grained-dependency-include-intrafile">, -Flags<[FrontendOption, HelpHidden]>, -HelpText<"Include within-file dependencies.">; - def emit_fine_grained_dependency_sourcefile_dot_files : Flag<["-"], "emit-fine-grained-dependency-sourcefile-dot-files">, Flags<[FrontendOption, HelpHidden]>, @@ -414,6 +401,11 @@ def require_explicit_availability_target : Separate<["-"], "require-explicit-ava HelpText<"Suggest fix-its adding @available(, *) to public declarations without availability">, MetaVarName<"">; +def define_availability : Separate<["-"], "define-availability">, + Flags<[FrontendOption, NoInteractiveOption]>, + HelpText<"Define an availability macro in the format 'macroName : iOS 13.0, macOS 10.15'">, + MetaVarName<"">; + def module_name : Separate<["-"], "module-name">, Flags<[FrontendOption, ModuleInterfaceOption]>, HelpText<"Name of the module to build">; @@ -584,6 +576,12 @@ def experimental_cxx_stdlib : Separate<["-"], "experimental-cxx-stdlib">, HelpText<"C++ standard library to use; forwarded to Clang's -stdlib flag">; +def enable_experimental_cross_module_incremental_build : + Flag<["-"], "enable-experimental-cross-module-incremental-build">, + Flags<[FrontendOption]>, + HelpText<"(experimental) Enable cross-module incremental build metadata and " + "driver scheduling">; + // Diagnostic control options def suppress_warnings : Flag<["-"], "suppress-warnings">, @@ -674,7 +672,7 @@ def framework : Separate<["-"], "framework">, Group, def L : JoinedOrSeparate<["-"], "L">, Group, Flags<[FrontendOption, DoesNotAffectIncrementalBuild, ArgumentIsPath]>, HelpText<"Add directory to library link search path">; -def L_EQ : Joined<["-"], "L=">, +def L_EQ : Joined<["-"], "L=">, Group, Flags<[FrontendOption, DoesNotAffectIncrementalBuild, ArgumentIsPath]>, Alias; @@ -763,10 +761,10 @@ def gdwarf_types : Flag<["-"], "gdwarf-types">, HelpText<"Emit full DWARF type info.">; def debug_prefix_map : Separate<["-"], "debug-prefix-map">, Flags<[FrontendOption]>, - HelpText<"Remap source paths in debug info">; + HelpText<"Remap source paths in debug info">, MetaVarName<"">; def coverage_prefix_map : Separate<["-"], "coverage-prefix-map">, Flags<[FrontendOption]>, - HelpText<"Remap source paths in coverage info">; + HelpText<"Remap source paths in coverage info">, MetaVarName<"">; def debug_info_format : Joined<["-"], "debug-info-format=">, Flags<[FrontendOption]>, @@ -1110,7 +1108,11 @@ def scan_clang_dependencies : Flag<["-"], "scan-clang-dependencies">, def disable_parser_lookup : Flag<["-"], "disable-parser-lookup">, Flags<[FrontendOption]>, - HelpText<"Disable parser lookup & use ast scope lookup only (experimental)">; + HelpText<"Disable parser lookup & use ASTScope lookup only (experimental)">; + +def enable_parser_lookup : Flag<["-"], "enable-parser-lookup">, + Flags<[FrontendOption]>, + HelpText<"Enable parser lookup">; def enable_request_based_incremental_dependencies : Flag<["-"], "enable-request-based-incremental-dependencies">, diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index 09eb4b04179a9..d990bd6208c7d 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -50,6 +50,7 @@ namespace swift { class Lexer; class ParsedTypeSyntax; class PersistentParserState; + class RequirementRepr; class SILParserStateBase; class ScopeInfo; class SourceManager; @@ -396,6 +397,22 @@ class Parser { /// Current syntax parsing context where call backs should be directed to. SyntaxParsingContext *SyntaxContext; + /// Maps of macro name and version to availability specifications. + typedef llvm::DenseMap> + AvailabilityMacroVersionMap; + typedef llvm::DenseMap + AvailabilityMacroMap; + + /// Cache of the availability macros parsed from the command line arguments. + /// Organized as two nested \c DenseMap keyed first on the macro name then + /// the macro version. This structure allows to peek at macro names before + /// parsing a version tuple. + AvailabilityMacroMap AvailabilityMacros; + + /// Has \c AvailabilityMacros been computed? + bool AvailabilityMacrosComputed = false; + public: Parser(unsigned BufferID, SourceFile &SF, DiagnosticEngine* LexerDiags, SILParserStateBase *SIL, PersistentParserState *PersistentState, @@ -692,15 +709,6 @@ class Parser { Context.LangOpts.ParseForSyntaxTreeOnly; } - /// If a function or closure body consists of a single expression, determine - /// whether we should turn wrap it in a return statement or not. - /// - /// We don't do this transformation for non-solver-based code completion - /// positions, as the source may be incomplete and the type mismatch in the - /// return statement will just confuse the type checker. - bool shouldSuppressSingleExpressionBodyTransform( - ParserStatus Status, MutableArrayRef BodyElems); - public: InFlightDiagnostic diagnose(SourceLoc Loc, Diagnostic Diag) { if (Diags.isDiagnosticPointsToFirstBadToken(Diag.getID()) && @@ -935,17 +943,6 @@ class Parser { void consumeDecl(ParserPosition BeginParserPosition, ParseDeclOptions Flags, bool IsTopLevel); - /// FIXME: Remove this, it's vestigial. - llvm::SmallPtrSet AlreadyHandledDecls; - - void markWasHandled(Decl *D) { - AlreadyHandledDecls.insert(D); - } - - bool declWasHandledAlready(Decl *D) { - return AlreadyHandledDecls.erase(D); - } - ParserResult parseDecl(ParseDeclOptions Flags, bool IsAtStartOfLineOrPreviousHadSemi, llvm::function_ref Handler); @@ -1006,14 +1003,22 @@ class Parser { /// 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); + bool parseSpecializeAttribute( + swift::tok ClosingBrace, SourceLoc AtLoc, SourceLoc Loc, + SpecializeAttr *&Attr, + llvm::function_ref parseSILTargetName = + [](Parser &) { return false; }, + llvm::function_ref parseSILSIPModule = + [](Parser &) { return false; }); /// Parse the arguments inside the @_specialize attribute bool parseSpecializeAttributeArguments( swift::tok ClosingBrace, bool &DiscardAttribute, Optional &Exported, Optional &Kind, - TrailingWhereClause *&TrailingWhereClause); + TrailingWhereClause *&TrailingWhereClause, DeclNameRef &targetFunction, + SmallVectorImpl &spiGroups, + llvm::function_ref parseSILTargetName, + llvm::function_ref parseSILSIPModule); /// Parse the @_implements attribute. /// \p Attr is where to store the parsed attribute @@ -1684,9 +1689,38 @@ class Parser { //===--------------------------------------------------------------------===// // Availability Specification Parsing - /// Parse a comma-separated list of availability specifications. + /// Parse a comma-separated list of availability specifications. Try to + /// expand availability macros when /p ParsingMacroDefinition is false. + ParserStatus + parseAvailabilitySpecList(SmallVectorImpl &Specs, + bool ParsingMacroDefinition = false); + + /// Does the current matches an argument macro name? Parsing compiler + /// arguments as required without consuming tokens from the source file + /// parser. + bool peekAvailabilityMacroName(); + + /// Try to parse a reference to an availability macro and append its result + /// to \p Specs. If the current token doesn't match a macro name, return + /// a success without appending anything to \c Specs. + ParserStatus + parseAvailabilityMacro(SmallVectorImpl &Specs); + + /// Parse the availability macros definitions passed as arguments. + void parseAllAvailabilityMacroArguments(); + + /// Result of parsing an availability macro definition. + struct AvailabilityMacroDefinition { + StringRef Name; + llvm::VersionTuple Version; + SmallVector Specs; + }; + + /// Parse an availability macro definition from a command line argument. + /// This function should be called on a Parser set up on the command line + /// argument code. ParserStatus - parseAvailabilitySpecList(SmallVectorImpl &Specs); + parseAvailabilityMacroDefinition(AvailabilityMacroDefinition &Result); ParserResult parseAvailabilitySpec(); ParserResult diff --git a/include/swift/Reflection/ReflectionContext.h b/include/swift/Reflection/ReflectionContext.h index 9e26669aa8f38..e52185c0b8ebe 100644 --- a/include/swift/Reflection/ReflectionContext.h +++ b/include/swift/Reflection/ReflectionContext.h @@ -33,7 +33,7 @@ #include "swift/Reflection/TypeLowering.h" #include "swift/Reflection/TypeRef.h" #include "swift/Reflection/TypeRefBuilder.h" -#include "swift/Runtime/Unreachable.h" +#include "swift/Basic/Unreachable.h" #include #include @@ -1302,7 +1302,7 @@ class ReflectionContext return true; } - swift_runtime_unreachable("Unhandled MetadataSourceKind in switch."); + swift_unreachable("Unhandled MetadataSourceKind in switch."); } /// Read metadata for a captured generic type from a closure context. diff --git a/include/swift/Reflection/TypeRef.h b/include/swift/Reflection/TypeRef.h index fb03cbc9e2e82..3d38e47128f85 100644 --- a/include/swift/Reflection/TypeRef.h +++ b/include/swift/Reflection/TypeRef.h @@ -22,7 +22,7 @@ #include "llvm/Support/Casting.h" #include "swift/ABI/MetadataValues.h" #include "swift/Remote/MetadataReader.h" -#include "swift/Runtime/Unreachable.h" +#include "swift/Basic/Unreachable.h" namespace swift { namespace reflection { @@ -856,7 +856,7 @@ class TypeRefVisitor { #include "swift/Reflection/TypeRefs.def" } - swift_runtime_unreachable("Unhandled TypeRefKind in switch."); + swift_unreachable("Unhandled TypeRefKind in switch."); } }; diff --git a/include/swift/Reflection/TypeRefBuilder.h b/include/swift/Reflection/TypeRefBuilder.h index 982bcba2edb4c..2e411fee779ba 100644 --- a/include/swift/Reflection/TypeRefBuilder.h +++ b/include/swift/Reflection/TypeRefBuilder.h @@ -443,6 +443,8 @@ class TypeRefBuilder { break; } + funcFlags = funcFlags.withAsync(flags.isAsync()); + auto result = createTupleType({}, ""); return FunctionTypeRef::create(*this, {}, result, funcFlags); } diff --git a/include/swift/Remote/MetadataReader.h b/include/swift/Remote/MetadataReader.h index 0090ae0746ed6..bee120e64e0bb 100644 --- a/include/swift/Remote/MetadataReader.h +++ b/include/swift/Remote/MetadataReader.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 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -28,7 +28,7 @@ #include "swift/ABI/TypeIdentity.h" #include "swift/Runtime/ExistentialContainer.h" #include "swift/Runtime/HeapObject.h" -#include "swift/Runtime/Unreachable.h" +#include "swift/Basic/Unreachable.h" #include #include @@ -923,7 +923,7 @@ class MetadataReader { } } - swift_runtime_unreachable("Unhandled MetadataKind in switch"); + swift_unreachable("Unhandled MetadataKind in switch"); } TypeLookupErrorOr @@ -1295,7 +1295,7 @@ class MetadataReader { } } - swift_runtime_unreachable("Unhandled IsaEncodingKind in switch."); + swift_unreachable("Unhandled IsaEncodingKind in switch."); } /// Read the offset of the generic parameters of a class from the nominal @@ -1414,6 +1414,9 @@ class MetadataReader { return metadataFn(metadata); } + case TypeReferenceKind::MetadataKind: { + return None; + } } return None; diff --git a/include/swift/Runtime/BuiltinProtocolConformances.h b/include/swift/Runtime/BuiltinProtocolConformances.h new file mode 100644 index 0000000000000..71cf4fef11631 --- /dev/null +++ b/include/swift/Runtime/BuiltinProtocolConformances.h @@ -0,0 +1,156 @@ +//===--- BuiltinProtocolWitnessTable.h --------------------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Swift runtime support for builtin protocol witnesses and related items. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_RUNTIME_BUILTINPROTOCOLCONFORMANCES_H +#define SWIFT_RUNTIME_BUILTINPROTOCOLCONFORMANCES_H + +#include "swift/ABI/Metadata.h" + +namespace swift { + +#define STR(a) #a +#define XSTR(a) STR(a) +#define SYMBOL(name) XSTR(__USER_LABEL_PREFIX__) name + +#define PROTOCOL_DESCRIPTOR_MANGLING Mp + +#define PROTOCOL_DESCRIPTOR_SYM(Proto) \ + MANGLE_SYM(MANGLING_CONCAT2(Proto, PROTOCOL_DESCRIPTOR_MANGLING)) + +//===----------------------------------------------------------------------===// +// Tuple Equatable Conformance +//===----------------------------------------------------------------------===// + +// public protocol Equatable {} +#define SWIFT_EQUATABLE_MANGLING SQ + +#define EQUATABLE_DESCRIPTOR PROTOCOL_DESCRIPTOR_SYM(SWIFT_EQUATABLE_MANGLING) + +#define EQUATABLE_DESCRIPTOR_SYMBOL SYMBOL("$sSQMp") +#define EQUATABLE_EE_METHOD_DESCRIPTOR SYMBOL("$sSQ2eeoiySbx_xtFZTq") + +#define TUPLE_EQUATABLE_CONF SYMBOL("_swift_tupleEquatable_conf") +#define TUPLE_EQUATABLE_EQUALS SYMBOL("_swift_tupleEquatable_equals") + +/// The protocol witness for static Swift.Equatable.== infix(A, A) -> Swift.Bool +/// in conformance (A...): Swift.Equatable in Swift. +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +bool _swift_tupleEquatable_equals(OpaqueValue *tuple1, OpaqueValue *tuple2, + SWIFT_CONTEXT Metadata *swiftSelf, + Metadata *Self, void *witnessTable); + +//===----------------------------------------------------------------------===// +// Tuple Comparable Conformance +//===----------------------------------------------------------------------===// + +// public protocol Comparable {} +#define SWIFT_COMPARABLE_MANGLING SL + +#define COMPARABLE_DESCRIPTOR PROTOCOL_DESCRIPTOR_SYM(SWIFT_COMPARABLE_MANGLING) + +#define COMPARABLE_DESCRIPTOR_SYMBOL SYMBOL("$sSLMp") + +#define COMPARABLE_BASE_CONFORMANCE_DESCRIPTOR SYMBOL("$sSLSQTb") +#define COMPARABLE_LT_METHOD_DESCRIPTOR SYMBOL("$sSL1loiySbx_xtFZTq") +#define COMPARBALE_LTE_METHOD_DESCRIPTOR SYMBOL("$sSL2leoiySbx_xtFZTq") +#define COMPARABLE_GTE_METHOD_DESCRIPTOR SYMBOL("$sSL2geoiySbx_xtFZTq") +#define COMPARABLE_GT_METHOD_DESCRIPTOR SYMBOL("$sSL1goiySbx_xtFZTq") + +#define TUPLE_COMPARABLE_CONF SYMBOL("_swift_tupleComparable_conf") +#define TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE \ + SYMBOL("associated conformance _swift_tupleComparable") +#define TUPLE_COMPARABLE_BASEACCESSOREQUATABLE \ + SYMBOL("_swift_tupleComparable_baseAccessorEquatable") +#define TUPLE_COMPARABLE_LESSTHAN SYMBOL("_swift_tupleComparable_lessThan") +#define TUPLE_COMPARABLE_LESSTHANOREQUAL \ + SYMBOL("_swift_tupleComparable_lessThanOrEqual") +#define TUPLE_COMPARABLE_GREATERTHANOREQUAL \ + SYMBOL("_swift_tupleComparable_greaterThanOrEqual") +#define TUPLE_COMPARABLE_GREATERTHAN \ + SYMBOL("_swift_tupleComparable_greaterThan") + +/// The protocol witness for static Swift.Comparable.< infix(A, A) -> Swift.Bool +/// in conformance (A...): Swift.Comparable in Swift. +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +bool _swift_tupleComparable_lessThan(OpaqueValue *tuple1, OpaqueValue *tuple2, + SWIFT_CONTEXT Metadata *swiftSelf, + Metadata *Self, void *witnessTable); + +/// The protocol witness for static Swift.Comparable.<= infix(A, A) -> Swift.Bool +/// in conformance (A...): Swift.Comparable in Swift. +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +bool _swift_tupleComparable_lessThanOrEqual(OpaqueValue *tuple1, + OpaqueValue *tuple2, + SWIFT_CONTEXT Metadata *swiftSelf, + Metadata *Self, void *witnessTable); + +/// The protocol witness for static Swift.Comparable.>= infix(A, A) -> Swift.Bool +/// in conformance (A...): Swift.Comparable in Swift. +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +bool _swift_tupleComparable_greaterThanOrEqual(OpaqueValue *tuple1, + OpaqueValue *tuple2, + SWIFT_CONTEXT Metadata *swiftSelf, + Metadata *Self, void *witnessTable); + +/// The protocol witness for static Swift.Comparable.> infix(A, A) -> Swift.Bool +/// in conformance (A...): Swift.Comparable in Swift. +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +bool _swift_tupleComparable_greaterThan(OpaqueValue *tuple1, OpaqueValue *tuple2, + SWIFT_CONTEXT Metadata *swiftSelf, + Metadata *Self, void *witnessTable); + +//===----------------------------------------------------------------------===// +// Tuple Hashable Conformance +//===----------------------------------------------------------------------===// + +// public protocol Hashable {} +#define SWIFT_HASHABLE_MANGLING SH + +#define HASHABLE_DESCRIPTOR PROTOCOL_DESCRIPTOR_SYM(SWIFT_HASHABLE_MANGLING) + +#define HASHABLE_DESCRIPTOR_SYMBOL SYMBOL("$sSHMp") + +// Swift._hashValue(for: A) -> Swift.Int +#define SWIFT_HASHVALUE_FUNC $ss10_hashValue3forSix_tSHRzlF +// Swift.Hasher.combine(A) -> () +#define SWIFT_HASHER_COMBINE_FUNC $ss6HasherV7combineyyxSHRzlF + +#define HASHABLE_BASE_CONFORMANCE_DESCRIPTOR SYMBOL("$sSHSQTb") +#define HASHABLE_HASHVALUE_METHOD_DESCRIPTOR SYMBOL("$sSH9hashValueSivgTq") +#define HASHABLE_HASH_METHOD_DESCRIPTOR SYMBOL("$sSH4hash4intoys6HasherVz_tFTq") +#define HASHABLE_RAWHASHVALUE_METHOD_DESCRIPTOR \ + SYMBOL("$sSH13_rawHashValue4seedS2i_tFTq") + +#define TUPLE_HASHABLE_CONF SYMBOL("_swift_tupleHashable_conf") +#define TUPLE_HASHABLE_HASHVALUE SYMBOL("_swift_tupleHashable_hashValue") +#define TUPLE_HASHABLE_HASH SYMBOL("_swift_tupleHashable_hash") + +/// The protocol witness for Swift.Hashable.hashValue.getter: Swift.Int in +/// conformance (A...): Swift.Hashable in Swift. +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +intptr_t _swift_tupleHashable_hashValue(SWIFT_CONTEXT OpaqueValue *tuple, + Metadata *Self, void *witnessTable); + +/// The protocol witness for Swift.Hashable.hash(into:) in conformance +/// (A...): Swift.Hashable in Swift. +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +void _swift_tupleHashable_hash(OpaqueValue *hasher, + SWIFT_CONTEXT OpaqueValue *tuple, + Metadata *Self, void *witnessTable); + +} // end namespace swift + +#endif diff --git a/include/swift/Runtime/Concurrency.h b/include/swift/Runtime/Concurrency.h new file mode 100644 index 0000000000000..b72c22a0c0050 --- /dev/null +++ b/include/swift/Runtime/Concurrency.h @@ -0,0 +1,150 @@ +//===--- Concurrency.h - Runtime interface for concurrency ------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// The runtime interface for concurrency. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_RUNTIME_CONCURRENCY_H +#define SWIFT_RUNTIME_CONCURRENCY_H + +#include "swift/ABI/TaskStatus.h" + +namespace swift { + +struct AsyncTaskAndContext { + AsyncTask *Task; + AsyncContext *InitialContext; +}; + +/// Create a task object with no future which will run the given +/// function. +/// +/// The task is not yet scheduled. +/// +/// If a parent task is provided, flags.task_hasChildFragment() must +/// be true, and this must be called synchronously with the parent. +/// The parent is responsible for creating a ChildTaskStatusRecord. +/// TODO: should we have a single runtime function for creating a task +/// and doing this child task status record management? +SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) +AsyncTaskAndContext swift_task_create(JobFlags flags, + AsyncTask *parent, + const AsyncFunctionPointer *function); + +/// Create a task object with no future which will run the given +/// function. +SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) +AsyncTaskAndContext swift_task_create_f(JobFlags flags, + AsyncTask *parent, + AsyncFunctionType *function, + size_t initialContextSize); + +/// Allocate memory in a task. +/// +/// This must be called synchronously with the task. +/// +/// All allocations will be rounded to a multiple of MAX_ALIGNMENT. +SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) +void *swift_task_alloc(AsyncTask *task, size_t size); + +/// Deallocate memory in a task. +/// +/// The pointer provided must be the last pointer allocated on +/// this task that has not yet been deallocated; that is, memory +/// must be allocated and deallocated in a strict stack discipline. +SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) +void swift_task_dealloc(AsyncTask *task, void *ptr); + +/// Cancel a task and all of its child tasks. +/// +/// This can be called from any thread. +/// +/// This has no effect if the task is already cancelled. +SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) +void swift_task_cancel(AsyncTask *task); + +/// Escalate the priority of a task and all of its child tasks. +/// +/// This can be called from any thread. +/// +/// This has no effect if the task already has at least the given priority. +/// Returns the priority of the task. +SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) +JobPriority +swift_task_escalate(AsyncTask *task, JobPriority newPriority); + +/// Add a status record to a task. The record should not be +/// modified while it is registered with a task. +/// +/// This must be called synchronously with the task. +/// +/// If the task is already cancelled, returns `false` but still adds +/// the status record. +SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) +bool swift_task_addStatusRecord(AsyncTask *task, + TaskStatusRecord *record); + +/// Add a status record to a task if the task has not already +/// been cancelled. The record should not be modified while it is +/// registered with a task. +/// +/// This must be called synchronously with the task. +/// +/// If the task is already cancelled, returns `false` and does not +/// add the status record. +SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) +bool swift_task_tryAddStatusRecord(AsyncTask *task, + TaskStatusRecord *record); + +/// Remove a status record from a task. After this call returns, +/// the record's memory can be freely modified or deallocated. +/// +/// This must be called synchronously with the task. The record must +/// be registered with the task or else this may crash. +/// +/// The given record need not be the last record added to +/// the task, but the operation may be less efficient if not. +///s +/// Returns false if the task has been cancelled. +SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) +bool swift_task_removeStatusRecord(AsyncTask *task, + TaskStatusRecord *record); + +/// This should have the same representation as an enum like this: +/// enum NearestTaskDeadline { +/// case none +/// case alreadyCancelled +/// case active(TaskDeadline) +/// } +/// TODO: decide what this interface should really be. +struct NearestTaskDeadline { + enum Kind : uint8_t { + None, + AlreadyCancelled, + Active + }; + + TaskDeadline Value; + Kind ValueKind; +}; + +/// Returns the nearest deadline that's been registered with this task. +/// +/// This must be called synchronously with the task. +SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) +NearestTaskDeadline +swift_task_getNearestDeadline(AsyncTask *task); + +} + +#endif diff --git a/include/swift/Runtime/Concurrent.h b/include/swift/Runtime/Concurrent.h index e634fbc25660d..cf1b91d5b8c55 100644 --- a/include/swift/Runtime/Concurrent.h +++ b/include/swift/Runtime/Concurrent.h @@ -579,6 +579,11 @@ using llvm::hash_value; /// ensure that readers which started before the clear see valid (pre-clear) /// data. Readers which see any array as empty will produce no results, thus /// providing valid post-clear data. +/// +/// This is intended to be used for tables that exist for the life of the +/// process. It has no destructor, to avoid generating useless global destructor +/// calls. The memory it allocates can be freed by calling clear() with no +/// outstanding readers, but this won't destroy the static mutex it uses. template struct ConcurrentReadableHashMap { // We use memcpy and don't call destructors. Make sure the elements will put // up with this. @@ -588,10 +593,6 @@ template struct ConcurrentReadableHashMap { "Elements must not have destructors (they won't be called)."); private: - /// The type of the elements of the indices array. TODO: use one or two byte - /// indices for smaller tables to save more memory. - using Index = unsigned; - /// The reciprocal of the load factor at which we expand the table. A value of /// 4 means that we resize at 1/4 = 75% load factor. static const size_t ResizeProportion = 4; @@ -619,20 +620,77 @@ template struct ConcurrentReadableHashMap { /// is stored inline. We work around this contradiction by considering the /// first index to always be occupied with a value that never matches any key. struct IndexStorage { - std::atomic Mask; + // Index size is variable based on capacity, either 8, 16, or 32 bits. + // + // This is somewhat conservative. We could have, for example, a capacity of + // 512 but a maximum index of only 200, which would still allow for 8-bit + // indices. However, taking advantage of this would require reallocating + // the index storage when the element count crossed a threshold, which is + // more complex, and the advantages are minimal. This keeps it simple. + // + // The first byte of the storage is the log 2 of the capacity. The remaining + // storage is then an array of 8, 16, or 32 bit integers, depending on the + // capacity number. This union allows us to access the capacity, and then + // access the rest of the storage by taking the address of one of the + // IndexZero members and indexing into it (always avoiding index 0). + union { + uint8_t CapacityLog2; + std::atomic IndexZero8; + std::atomic IndexZero16; + std::atomic IndexZero32; + }; + + // Get the size, in bytes, of the index needed for the given capacity. + static unsigned indexSize(uint8_t capacityLog2) { + if (capacityLog2 <= sizeof(uint8_t) * CHAR_BIT) + return sizeof(uint8_t); + if (capacityLog2 <= sizeof(uint16_t) * CHAR_BIT) + return sizeof(uint16_t); + return sizeof(uint32_t); + } - static IndexStorage *allocate(size_t capacity) { - assert((capacity & (capacity - 1)) == 0 && - "Capacity must be a power of 2"); - auto *ptr = - reinterpret_cast(calloc(capacity, sizeof(Mask))); + unsigned indexSize() { return indexSize(CapacityLog2); } + + static IndexStorage *allocate(size_t capacityLog2) { + assert(capacityLog2 > 0); + size_t capacity = 1UL << capacityLog2; + auto *ptr = reinterpret_cast( + calloc(capacity, indexSize(capacityLog2))); if (!ptr) swift::crash("Could not allocate memory."); - ptr->Mask.store(capacity - 1, std::memory_order_relaxed); + ptr->CapacityLog2 = capacityLog2; return ptr; } - std::atomic &at(size_t i) { return (&Mask)[i]; } + unsigned loadIndexAt(size_t i, std::memory_order order) { + assert(i > 0 && "index zero is off-limits, used to store capacity"); + + switch (indexSize()) { + case sizeof(uint8_t): + return (&IndexZero8)[i].load(order); + case sizeof(uint16_t): + return (&IndexZero16)[i].load(order); + case sizeof(uint32_t): + return (&IndexZero32)[i].load(order); + default: + swift_unreachable("unknown index size"); + } + } + + void storeIndexAt(unsigned value, size_t i, std::memory_order order) { + assert(i > 0 && "index zero is off-limits, used to store capacity"); + + switch (indexSize()) { + case sizeof(uint8_t): + return (&IndexZero8)[i].store(value, order); + case sizeof(uint16_t): + return (&IndexZero16)[i].store(value, order); + case sizeof(uint32_t): + return (&IndexZero32)[i].store(value, order); + default: + swift_unreachable("unknown index size"); + } + } }; /// A simple linked list representing pointers that need to be freed. @@ -671,7 +729,7 @@ template struct ConcurrentReadableHashMap { std::atomic Indices{nullptr}; /// The writer lock, which must be taken before any mutation of the table. - Mutex WriterLock; + StaticMutex WriterLock; /// The maximum number of elements that the current elements array can hold. uint32_t ElementCapacity{0}; @@ -720,17 +778,18 @@ template struct ConcurrentReadableHashMap { /// returning the new array with all existing indices copied into it. This /// operation performs a rehash, so that the indices are in the correct /// location in the new array. - IndexStorage *resize(IndexStorage *indices, Index indicesMask, + IndexStorage *resize(IndexStorage *indices, uint8_t indicesCapacityLog2, ElemTy *elements) { - // Mask is size - 1. Double the size. Start with 4 (fits into 16-byte malloc - // bucket). - size_t newCount = indices ? 2 * (indicesMask + 1) : 4; - size_t newMask = newCount - 1; + // Double the size. Start with 16 (fits into 16-byte malloc + // bucket), which is 2^4. + size_t newCapacityLog2 = indices ? indicesCapacityLog2 + 1 : 4; + size_t newMask = (1UL << newCapacityLog2) - 1; - IndexStorage *newIndices = IndexStorage::allocate(newCount); + IndexStorage *newIndices = IndexStorage::allocate(newCapacityLog2); - for (size_t i = 1; i <= indicesMask; i++) { - Index index = indices->at(i).load(std::memory_order_relaxed); + size_t indicesCount = 1UL << indicesCapacityLog2; + for (size_t i = 1; i < indicesCount; i++) { + unsigned index = indices->loadIndexAt(i, std::memory_order_relaxed); if (index == 0) continue; @@ -738,9 +797,12 @@ template struct ConcurrentReadableHashMap { auto hash = hash_value(*element); size_t newI = hash & newMask; - while (newIndices->at(newI) != 0) + // Index 0 is unusable (occupied by the capacity), so always skip it. + while (newI == 0 || + newIndices->loadIndexAt(newI, std::memory_order_relaxed) != 0) { newI = (newI + 1) & newMask; - newIndices->at(newI).store(index, std::memory_order_relaxed); + } + newIndices->storeIndexAt(index, newI, std::memory_order_relaxed); } Indices.store(newIndices, std::memory_order_release); @@ -752,16 +814,16 @@ template struct ConcurrentReadableHashMap { /// Search for the given key within the given indices and elements arrays. If /// an entry already exists for that key, return a pointer to the element. If - /// no entry exists, return a pointer to the location in the indices array - /// where the index of the new element would be stored. + /// no entry exists, return the location in the indices array where the index + /// of the new element would be stored. template - static std::pair *> + static std::pair find(const KeyTy &key, IndexStorage *indices, size_t elementCount, ElemTy *elements) { if (!indices) - return {nullptr, nullptr}; + return {nullptr, 0}; auto hash = hash_value(key); - auto indicesMask = indices->Mask.load(std::memory_order_relaxed); + auto indicesMask = (1UL << indices->CapacityLog2) - 1; auto i = hash & indicesMask; while (true) { @@ -769,15 +831,14 @@ template struct ConcurrentReadableHashMap { if (i == 0) i++; - auto *indexPtr = &indices->at(i); - auto index = indexPtr->load(std::memory_order_acquire); + auto index = indices->loadIndexAt(i, std::memory_order_acquire); // Element indices are 1-based, 0 means no entry. if (index == 0) - return {nullptr, indexPtr}; + return {nullptr, i}; if (index - 1 < elementCount) { auto *candidate = &elements[index - 1]; if (candidate->matchesKey(key)) - return {candidate, nullptr}; + return {candidate, 0}; } i = (i + 1) & indicesMask; @@ -785,20 +846,19 @@ template struct ConcurrentReadableHashMap { } public: + // Implicitly trivial constructor/destructor. + ConcurrentReadableHashMap() = default; + ~ConcurrentReadableHashMap() = default; + // This type cannot be safely copied or moved. ConcurrentReadableHashMap(const ConcurrentReadableHashMap &) = delete; ConcurrentReadableHashMap(ConcurrentReadableHashMap &&) = delete; ConcurrentReadableHashMap & operator=(const ConcurrentReadableHashMap &) = delete; - ConcurrentReadableHashMap() - : ReaderCount(0), ElementCount(0), Elements(nullptr), Indices(nullptr), - ElementCapacity(0) {} - - ~ConcurrentReadableHashMap() { - assert(ReaderCount.load(std::memory_order_acquire) == 0 && - "deallocating ConcurrentReadableHashMap with outstanding snapshots"); - FreeListNode::freeAll(&FreeList); + /// Returns whether there are outstanding readers. For testing purposes only. + bool hasActiveReaders() { + return ReaderCount.load(std::memory_order_relaxed) > 0; } /// Readers take a snapshot of the hash map, then work with the snapshot. @@ -889,29 +949,31 @@ template struct ConcurrentReadableHashMap { /// The return value is ignored when `created` is `false`. template void getOrInsert(KeyTy key, const Call &call) { - ScopedLock guard(WriterLock); + StaticScopedLock guard(WriterLock); auto *indices = Indices.load(std::memory_order_relaxed); if (!indices) indices = resize(indices, 0, nullptr); - auto indicesMask = indices->Mask.load(std::memory_order_relaxed); + auto indicesCapacityLog2 = indices->CapacityLog2; auto elementCount = ElementCount.load(std::memory_order_relaxed); auto *elements = Elements.load(std::memory_order_relaxed); - auto found = find(key, indices, elementCount, elements); + auto found = this->find(key, indices, elementCount, elements); if (found.first) { call(found.first, false); deallocateFreeListIfSafe(); return; } - // The actual capacity is indicesMask + 1. The number of slots in use is - // elementCount + 1, since the mask also takes a slot. - auto emptyCount = (indicesMask + 1) - (elementCount + 1); - auto proportion = (indicesMask + 1) / emptyCount; + auto indicesCapacity = 1UL << indicesCapacityLog2; + + // The number of slots in use is elementCount + 1, since the capacity also + // takes a slot. + auto emptyCount = indicesCapacity - (elementCount + 1); + auto proportion = indicesCapacity / emptyCount; if (proportion >= ResizeProportion) { - indices = resize(indices, indicesMask, elements); + indices = resize(indices, indicesCapacityLog2, elements); found = find(key, indices, elementCount, elements); assert(!found.first && "Shouldn't suddenly find the key after rehashing"); } @@ -928,7 +990,8 @@ template struct ConcurrentReadableHashMap { assert(hash_value(key) == hash_value(*element) && "Element must have the same hash code as its key."); ElementCount.store(elementCount + 1, std::memory_order_release); - found.second->store(elementCount + 1, std::memory_order_release); + indices->storeIndexAt(elementCount + 1, found.second, + std::memory_order_release); } deallocateFreeListIfSafe(); @@ -937,7 +1000,7 @@ template struct ConcurrentReadableHashMap { /// Clear the hash table, freeing (when safe) all memory currently used for /// indices and elements. void clear() { - ScopedLock guard(WriterLock); + StaticScopedLock guard(WriterLock); auto *indices = Indices.load(std::memory_order_relaxed); auto *elements = Elements.load(std::memory_order_relaxed); @@ -956,6 +1019,66 @@ template struct ConcurrentReadableHashMap { } }; +/// A wrapper type for indirect hash map elements. Stores a pointer to the real +/// element and forwards key matching and hashing. +template struct HashMapElementWrapper { + ElemTy *Ptr; + + template bool matchesKey(const KeyTy &key) { + return Ptr->matchesKey(key); + } + + friend llvm::hash_code hash_value(const HashMapElementWrapper &wrapper) { + return hash_value(*wrapper.Ptr); + } +}; + +/// A ConcurrentReadableHashMap that provides stable addresses for the elements +/// by allocating them separately and storing pointers to them. The elements of +/// the hash table are instances of HashMapElementWrapper. A new getOrInsert +/// method is provided that directly returns the stable element pointer. +template +struct StableAddressConcurrentReadableHashMap + : public ConcurrentReadableHashMap> { + // Implicitly trivial destructor. + ~StableAddressConcurrentReadableHashMap() = default; + + /// Get or insert an element for the given key and arguments. Returns the + /// pointer to the existing or new element, and a bool indicating whether the + /// element was created. When false, the element already existed before the + /// call. + template + std::pair getOrInsert(KeyTy key, ArgTys &&...args) { + // Optimize for the case where the value already exists. + if (auto wrapper = this->snapshot().find(key)) + return {wrapper->Ptr, false}; + + // No such element. Insert if needed. Note: another thread may have inserted + // it in the meantime, so we still have to handle both cases! + ElemTy *ptr = nullptr; + bool outerCreated = false; + ConcurrentReadableHashMap>::getOrInsert( + key, [&](HashMapElementWrapper *wrapper, bool created) { + if (created) { + // Created the indirect entry. Allocate the actual storage. + size_t allocSize = + sizeof(ElemTy) + ElemTy::getExtraAllocationSize(key, args...); + void *memory = Allocator().Allocate(allocSize, alignof(ElemTy)); + new (memory) ElemTy(key, std::forward(args)...); + wrapper->Ptr = reinterpret_cast(memory); + } + ptr = wrapper->Ptr; + outerCreated = created; + return true; // Keep the new entry. + }); + return {ptr, outerCreated}; + } + +private: + // Clearing would require deallocating elements, which we don't support. + void clear() = delete; +}; + } // end namespace swift #endif // SWIFT_RUNTIME_CONCURRENTUTILS_H diff --git a/include/swift/Runtime/Config.h b/include/swift/Runtime/Config.h index 372328a145d5d..64c726311e455 100644 --- a/include/swift/Runtime/Config.h +++ b/include/swift/Runtime/Config.h @@ -17,71 +17,29 @@ #ifndef SWIFT_RUNTIME_CONFIG_H #define SWIFT_RUNTIME_CONFIG_H +#include "swift/Basic/Compiler.h" #include "swift/Runtime/CMakeConfig.h" -/// \macro SWIFT_RUNTIME_GNUC_PREREQ -/// Extend the default __GNUC_PREREQ even if glibc's features.h isn't -/// available. -#ifndef SWIFT_RUNTIME_GNUC_PREREQ -# if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) -# define SWIFT_RUNTIME_GNUC_PREREQ(maj, min, patch) \ - ((__GNUC__ << 20) + (__GNUC_MINOR__ << 10) + __GNUC_PATCHLEVEL__ >= \ - ((maj) << 20) + ((min) << 10) + (patch)) -# elif defined(__GNUC__) && defined(__GNUC_MINOR__) -# define SWIFT_RUNTIME_GNUC_PREREQ(maj, min, patch) \ - ((__GNUC__ << 20) + (__GNUC_MINOR__ << 10) >= ((maj) << 20) + ((min) << 10)) -# else -# define SWIFT_RUNTIME_GNUC_PREREQ(maj, min, patch) 0 -# endif -#endif - /// SWIFT_RUNTIME_LIBRARY_VISIBILITY - If a class marked with this attribute is /// linked into a shared library, then the class should be private to the /// library and not accessible from outside it. Can also be used to mark /// variables and functions, making them private to any shared library they are /// linked into. /// On PE/COFF targets, library visibility is the default, so this isn't needed. -#if (__has_attribute(visibility) || SWIFT_RUNTIME_GNUC_PREREQ(4, 0, 0)) && \ +#if (__has_attribute(visibility) || SWIFT_GNUC_PREREQ(4, 0, 0)) && \ !defined(__MINGW32__) && !defined(__CYGWIN__) && !defined(_WIN32) #define SWIFT_RUNTIME_LIBRARY_VISIBILITY __attribute__ ((visibility("hidden"))) #else #define SWIFT_RUNTIME_LIBRARY_VISIBILITY #endif -/// Attributes. -/// SWIFT_RUNTIME_ATTRIBUTE_NOINLINE - On compilers where we have a directive to do so, -/// mark a method "not for inlining". -#if __has_attribute(noinline) || SWIFT_RUNTIME_GNUC_PREREQ(3, 4, 0) -#define SWIFT_RUNTIME_ATTRIBUTE_NOINLINE __attribute__((noinline)) -#elif defined(_MSC_VER) -#define SWIFT_RUNTIME_ATTRIBUTE_NOINLINE __declspec(noinline) -#else -#define SWIFT_RUNTIME_ATTRIBUTE_NOINLINE -#endif - -/// SWIFT_RUNTIME_ATTRIBUTE_ALWAYS_INLINE - On compilers where we have a directive to do -/// so, mark a method "always inline" because it is performance sensitive. GCC -/// 3.4 supported this but is buggy in various cases and produces unimplemented -/// errors, just use it in GCC 4.0 and later. -#if __has_attribute(always_inline) || SWIFT_RUNTIME_GNUC_PREREQ(4, 0, 0) -#define SWIFT_RUNTIME_ATTRIBUTE_ALWAYS_INLINE __attribute__((always_inline)) -#elif defined(_MSC_VER) -#define SWIFT_RUNTIME_ATTRIBUTE_ALWAYS_INLINE __forceinline -#else -#define SWIFT_RUNTIME_ATTRIBUTE_ALWAYS_INLINE -#endif - -#ifdef __GNUC__ -#define SWIFT_RUNTIME_ATTRIBUTE_NORETURN __attribute__((noreturn)) -#elif defined(_MSC_VER) -#define SWIFT_RUNTIME_ATTRIBUTE_NORETURN __declspec(noreturn) -#else -#define SWIFT_RUNTIME_ATTRIBUTE_NORETURN -#endif +#define SWIFT_RUNTIME_ATTRIBUTE_NOINLINE SWIFT_ATTRIBUTE_NOINLINE +#define SWIFT_RUNTIME_ATTRIBUTE_ALWAYS_INLINE SWIFT_ATTRIBUTE_ALWAYS_INLINE +#define SWIFT_RUNTIME_ATTRIBUTE_NORETURN SWIFT_ATTRIBUTE_NORETURN /// SWIFT_RUNTIME_BUILTIN_TRAP - On compilers which support it, expands to an expression /// which causes the program to exit abnormally. -#if __has_builtin(__builtin_trap) || SWIFT_RUNTIME_GNUC_PREREQ(4, 3, 0) +#if __has_builtin(__builtin_trap) || SWIFT_GNUC_PREREQ(4, 3, 0) # define SWIFT_RUNTIME_BUILTIN_TRAP __builtin_trap() #elif defined(_MSC_VER) // The __debugbreak intrinsic is supported by MSVC, does not require forward @@ -251,6 +209,30 @@ extern uintptr_t __COMPATIBILITY_LIBRARIES_CANNOT_CHECK_THE_IS_SWIFT_BIT_DIRECTL #define __ptrauth_swift_dynamic_replacement_key \ __ptrauth(ptrauth_key_process_independent_data, 1, \ SpecialPointerAuthDiscriminators::DynamicReplacementKey) +#define __ptrauth_swift_job_invoke_function \ + __ptrauth(ptrauth_key_function_pointer, 1, \ + SpecialPointerAuthDiscriminators::JobInvokeFunction) +#define __ptrauth_swift_task_resume_function \ + __ptrauth(ptrauth_key_function_pointer, 1, \ + SpecialPointerAuthDiscriminators::TaskResumeFunction) +#define __ptrauth_swift_task_resume_context \ + __ptrauth(ptrauth_key_process_independent_data, 1, \ + SpecialPointerAuthDiscriminators::TaskResumeContext) +#define __ptrauth_swift_async_context_parent \ + __ptrauth(ptrauth_key_process_independent_data, 1, \ + SpecialPointerAuthDiscriminators::AsyncContextParent) +#define __ptrauth_swift_async_context_resume \ + __ptrauth(ptrauth_key_function_pointer, 1, \ + SpecialPointerAuthDiscriminators::AsyncContextResume) +#define __ptrauth_swift_async_context_yield \ + __ptrauth(ptrauth_key_function_pointer, 1, \ + SpecialPointerAuthDiscriminators::AsyncContextYield) +#define __ptrauth_swift_cancellation_notification_function \ + __ptrauth(ptrauth_key_function_pointer, 1, \ + SpecialPointerAuthDiscriminators::CancellationNotificationFunction) +#define __ptrauth_swift_escalation_notification_function \ + __ptrauth(ptrauth_key_function_pointer, 1, \ + SpecialPointerAuthDiscriminators::EscalationNotificationFunction) #define swift_ptrauth_sign_opaque_read_resume_function(__fn, __buffer) \ ptrauth_auth_and_resign(__fn, ptrauth_key_function_pointer, 0, \ ptrauth_key_process_independent_code, \ @@ -268,6 +250,14 @@ extern uintptr_t __COMPATIBILITY_LIBRARIES_CANNOT_CHECK_THE_IS_SWIFT_BIT_DIRECTL #define __ptrauth_swift_protocol_witness_function_pointer(__declkey) #define __ptrauth_swift_value_witness_function_pointer(__key) #define __ptrauth_swift_type_metadata_instantiation_function +#define __ptrauth_swift_job_invoke_function +#define __ptrauth_swift_task_resume_function +#define __ptrauth_swift_task_resume_context +#define __ptrauth_swift_async_context_parent +#define __ptrauth_swift_async_context_resume +#define __ptrauth_swift_async_context_yield +#define __ptrauth_swift_cancellation_notification_function +#define __ptrauth_swift_escalation_notification_function #define __ptrauth_swift_runtime_function_entry #define __ptrauth_swift_runtime_function_entry_with_key(__key) #define __ptrauth_swift_runtime_function_entry_strip(__fn) (__fn) diff --git a/include/swift/Runtime/Debug.h b/include/swift/Runtime/Debug.h index f14e8d16f1554..f82fee34bc805 100644 --- a/include/swift/Runtime/Debug.h +++ b/include/swift/Runtime/Debug.h @@ -18,7 +18,7 @@ #define SWIFT_RUNTIME_DEBUG_HELPERS_H #include "swift/Runtime/Config.h" -#include "swift/Runtime/Unreachable.h" +#include "swift/Basic/Unreachable.h" #include #include #include @@ -79,7 +79,7 @@ static inline void crash(const char *message) { CRSetCrashLogMessage(message); SWIFT_RUNTIME_BUILTIN_TRAP; - swift_runtime_unreachable("Expected compiler to crash."); + swift_unreachable("Expected compiler to crash."); } // swift::fatalError() halts with a crash log message, diff --git a/include/swift/Runtime/Metadata.h b/include/swift/Runtime/Metadata.h index 8d5537ed2e94b..8333cbf8457b5 100644 --- a/include/swift/Runtime/Metadata.h +++ b/include/swift/Runtime/Metadata.h @@ -878,7 +878,10 @@ const TypeContextDescriptor *swift_getTypeContextDescriptor(const Metadata *type SWIFT_RUNTIME_EXPORT const HeapObject *swift_getKeyPath(const void *pattern, const void *arguments); +// For some reason, MSVC doesn't accept these declarations outside of +// swiftCore. TODO: figure out a reasonable way to declare them. #if defined(swiftCore_EXPORTS) + /// Given a pointer to a borrowed value of type `Root` and a /// `KeyPath`, project a pointer to a borrowed value of type /// `Value`. @@ -900,7 +903,8 @@ swift_modifyAtWritableKeyPath; SWIFT_RUNTIME_EXPORT YieldOnceCoroutine::type swift_modifyAtReferenceWritableKeyPath; -#endif + +#endif // swiftCore_EXPORTS SWIFT_RUNTIME_EXPORT void swift_enableDynamicReplacementScope(const DynamicReplacementScope *scope); diff --git a/include/swift/Runtime/Mutex.h b/include/swift/Runtime/Mutex.h index aff2005d87514..eb568ce314740 100644 --- a/include/swift/Runtime/Mutex.h +++ b/include/swift/Runtime/Mutex.h @@ -76,6 +76,7 @@ class ScopedNotifyAllT { /// multi-threaded producers and consumers to signal each other in a safe way. class ConditionVariable { friend class Mutex; + friend class StaticMutex; ConditionVariable(const ConditionVariable &) = delete; ConditionVariable &operator=(const ConditionVariable &) = delete; @@ -615,6 +616,9 @@ class StaticMutex { void wait(StaticConditionVariable &condition) { ConditionPlatformHelper::wait(condition.Handle, Handle); } + void wait(ConditionVariable &condition) { + ConditionPlatformHelper::wait(condition.Handle, Handle); + } /// See Mutex::lock template diff --git a/include/swift/Runtime/RuntimeFunctions.def b/include/swift/Runtime/RuntimeFunctions.def index 4dac4fca66765..d12c8eb570863 100644 --- a/include/swift/Runtime/RuntimeFunctions.def +++ b/include/swift/Runtime/RuntimeFunctions.def @@ -1461,6 +1461,22 @@ FUNCTION(GetTypeByMangledNameInContextInMetadataState, Int8PtrPtrTy), ATTRS(NoUnwind, ArgMemOnly)) +// void *swift_task_alloc(AsyncTask *task, size_t size); +FUNCTION(TaskAlloc, + swift_task_alloc, SwiftCC, + ConcurrencyAvailability, + RETURNS(Int8PtrTy), + ARGS(SwiftTaskPtrTy, SizeTy), + ATTRS(NoUnwind, ArgMemOnly)) + +// void swift_task_dealloc(AsyncTask *task, void *ptr); +FUNCTION(TaskDealloc, + swift_task_dealloc, SwiftCC, + ConcurrencyAvailability, + RETURNS(VoidTy), + ARGS(SwiftTaskPtrTy, Int8PtrTy), + ATTRS(NoUnwind, ArgMemOnly)) + #undef RETURNS #undef ARGS #undef ATTRS diff --git a/include/swift/SIL/ApplySite.h b/include/swift/SIL/ApplySite.h index e11ca87b047b6..ce7ac40e608db 100644 --- a/include/swift/SIL/ApplySite.h +++ b/include/swift/SIL/ApplySite.h @@ -585,62 +585,28 @@ class FullApplySite : public ApplySite { llvm_unreachable("Covered switch isn't covered?!"); } - /// If this is a terminator apply site, then pass the first instruction of - /// each successor to fun. Otherwise, pass std::next(Inst). + /// If this is a terminator apply site, then pass a builder to insert at the + /// first instruction of each successor to \p func. Otherwise, pass a builder + /// to insert at std::next(Inst). /// /// The intention is that this abstraction will enable the compiler writer to /// ignore whether or not an apply site is a terminator when inserting /// instructions after an apply site. This results in eliminating unnecessary /// if-else code otherwise required to handle such situations. /// - /// NOTE: We return std::next() for begin_apply. If one wishes to insert code + /// NOTE: We pass std::next() for begin_apply. If one wishes to insert code /// /after/ the end_apply/abort_apply, please use instead /// insertAfterFullEvaluation. - void insertAfterInvocation( - function_ref func) const { - switch (getKind()) { - case FullApplySiteKind::ApplyInst: - case FullApplySiteKind::BeginApplyInst: - return func(std::next(getInstruction()->getIterator())); - case FullApplySiteKind::TryApplyInst: - for (auto *succBlock : - cast(getInstruction())->getSuccessorBlocks()) { - func(succBlock->begin()); - } - return; - } - llvm_unreachable("Covered switch isn't covered"); - } + void insertAfterInvocation(function_ref func) const; - /// Pass to func insertion points that are guaranteed to be immediately after - /// this full apply site has completely finished executing. + /// Pass a builder with insertion points that are guaranteed to be immediately + /// after this full apply site has completely finished executing. /// /// This is just like insertAfterInvocation except that if the full apply site /// is a begin_apply, we pass the insertion points after the end_apply, /// abort_apply rather than an insertion point right after the /// begin_apply. For such functionality, please invoke insertAfterInvocation. - void insertAfterFullEvaluation( - function_ref func) const { - switch (getKind()) { - case FullApplySiteKind::ApplyInst: - case FullApplySiteKind::TryApplyInst: - return insertAfterInvocation(func); - case FullApplySiteKind::BeginApplyInst: - SmallVector endApplies; - SmallVector abortApplies; - auto *bai = cast(getInstruction()); - bai->getCoroutineEndPoints(endApplies, abortApplies); - for (auto *eai : endApplies) { - func(std::next(eai->getIterator())); - } - for (auto *aai : abortApplies) { - func(std::next(aai->getIterator())); - } - return; - } - - llvm_unreachable("covered switch isn't covered"); - } + void insertAfterFullEvaluation(function_ref func) const; /// Returns true if \p op is an operand that passes an indirect /// result argument to the apply site. diff --git a/include/swift/SIL/DynamicCasts.h b/include/swift/SIL/DynamicCasts.h index 712115919f7c9..2f08945b77bc9 100644 --- a/include/swift/SIL/DynamicCasts.h +++ b/include/swift/SIL/DynamicCasts.h @@ -433,6 +433,9 @@ struct SILDynamicCastInst { return TargetIsBridgeable != SourceIsBridgeable; } + /// Returns true if this dynamic cast can release its source operand. + bool isRCIdentityPreserving() const; + /// If getSourceType() is a Swift type that can bridge to an ObjC type, return /// the ObjC type it bridges to. If the source type is an objc type, an empty /// CanType() is returned. diff --git a/include/swift/SIL/GenericSpecializationMangler.h b/include/swift/SIL/GenericSpecializationMangler.h new file mode 100644 index 0000000000000..9af0dc34b46d5 --- /dev/null +++ b/include/swift/SIL/GenericSpecializationMangler.h @@ -0,0 +1,105 @@ +//===- GenericSpecializationMangler.h - generic specializations -*- 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_UTILS_GENERICSPECIALIZATIONMANGLER_H +#define SWIFT_SIL_UTILS_GENERICSPECIALIZATIONMANGLER_H + +#include "swift/AST/ASTMangler.h" +#include "swift/Basic/NullablePtr.h" +#include "swift/Demangling/Demangler.h" +#include "swift/SIL/SILFunction.h" + +namespace swift { +namespace Mangle { + +enum class SpecializationKind : uint8_t { + Generic, + NotReAbstractedGeneric, + FunctionSignature, +}; + +/// Inject SpecializationPass into the Mangle namespace. +using SpecializationPass = Demangle::SpecializationPass; + +/// The base class for specialization mangles. +class SpecializationMangler : public Mangle::ASTMangler { +protected: + /// The specialization pass. + SpecializationPass Pass; + + IsSerialized_t Serialized; + + /// The original function which is specialized. + SILFunction *Function; + std::string FunctionName; + + llvm::SmallVector ArgOpStorage; + llvm::raw_svector_ostream ArgOpBuffer; + +protected: + SpecializationMangler(SpecializationPass P, IsSerialized_t Serialized, + SILFunction *F) + : Pass(P), Serialized(Serialized), Function(F), + ArgOpBuffer(ArgOpStorage) {} + + SpecializationMangler(SpecializationPass P, IsSerialized_t Serialized, + std::string functionName) + : Pass(P), Serialized(Serialized), Function(nullptr), + FunctionName(functionName), ArgOpBuffer(ArgOpStorage) {} + + void beginMangling(); + + /// Finish the mangling of the symbol and return the mangled name. + std::string finalize(); + + void appendSpecializationOperator(StringRef Op) { + appendOperator(Op, StringRef(ArgOpStorage.data(), ArgOpStorage.size())); + } +}; + +// The mangler for specialized generic functions. +class GenericSpecializationMangler : public SpecializationMangler { + + SubstitutionMap SubMap; + bool isReAbstracted; + bool isInlined; + bool isPrespecializaton; + +public: + GenericSpecializationMangler(SILFunction *F, SubstitutionMap SubMap, + IsSerialized_t Serialized, bool isReAbstracted, + bool isInlined = false, + bool isPrespecializaton = false) + : SpecializationMangler(SpecializationPass::GenericSpecializer, + Serialized, F), + SubMap(SubMap), isReAbstracted(isReAbstracted), isInlined(isInlined), + isPrespecializaton(isPrespecializaton) {} + + GenericSpecializationMangler(std::string origFuncName, SubstitutionMap SubMap) + : SpecializationMangler(SpecializationPass::GenericSpecializer, + IsNotSerialized, origFuncName), + SubMap(SubMap), isReAbstracted(true), isInlined(false), + isPrespecializaton(true) {} + + std::string mangle(GenericSignature Sig = GenericSignature()); + + // TODO: This utility should move from the libswiftSILOptimizer to + // libswiftSIL. + static std::string manglePrespecialization(std::string unspecializedName, + GenericSignature genericSig, + GenericSignature specializedSig); +}; + +} // end namespace Mangle +} // end namespace swift + +#endif diff --git a/include/swift/SIL/MemAccessUtils.h b/include/swift/SIL/MemAccessUtils.h index dd198db2b6814..2b01282935a2e 100644 --- a/include/swift/SIL/MemAccessUtils.h +++ b/include/swift/SIL/MemAccessUtils.h @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -10,7 +10,8 @@ // //===----------------------------------------------------------------------===// /// -/// These utilities model the storage locations of memory access. +/// These utilities model the storage locations of memory access. See +/// ProgrammersGuide.md for high-level design. /// /// All memory operations that are part of a formal access, as defined by /// exclusivity rules, are marked by begin_access and end_access instructions. @@ -22,47 +23,94 @@ /// To verify access markers, SIL checks that all memory operations either have /// an address that originates in begin_access, or originates from a pattern /// that is recognized as a non-formal-access. This implies that every SIL -/// memory operation has a recognizable address source. -/// -/// If the memory operation is part of a formal access, then getAddressAccess() -/// returns the begin_access marker. -/// -/// AccessedStorage identifies the storage location of a memory access. -/// -/// identifyFormalAccess() returns the formally accessed storage of a -/// begin_access instruction. This must return a valid AccessedStorage value -/// unless the access has "Unsafe" enforcement. The formal access location may -/// be nested within an outer begin_access. For the purpose of exclusivity, -/// nested accesses are considered distinct formal accesses so they return -/// distinct AccessedStorage values even though they may access the same -/// memory. -/// -/// findAccessedStorage() returns the outermost AccessedStorage for any memory -/// address. It can be called on the address of a memory operation, the address -/// of a begin_access, or any other address value. If the address is from an -/// enforced begin_access or from any memory operation that is part of a formal -/// access, then it returns a valid AccessedStorage value. If the memory +/// memory operation has a recognizable address source. Given the address of a +/// memory operation, there are three levels of APIs that inspect the origin of +/// that address: +/// +/// 1. getTypedAccessAddress(): Find the originating address as close as +/// possible to the address of the formal access *without* looking past any +/// storage casts. This is useful when the type of the returned access address +/// must be consistent with the memory operation's type (the same type or a +/// parent type). For a formal access, this typically returns the begin_access, +/// but it is not guaranteed to because some accesses contain storage casts. For +/// non-formal access, it returns a best-effort address corresponding to the +/// base of an access. +/// +/// 2. getAccessScope(): If the memory operation is part of a formal access, +/// then this is guaranteed to return the begin_access marker. Otherwise, it +/// returns the best-effort address or pointer corresponding to the base of an +/// access. Useful to find the scope of a formal access. +/// +/// 3. getAccessBase(): Find the ultimate base of any address corresponding to +/// the accessed object, regardless of whether the address is nested within +/// access scopes, and regardless of any storage casts. This returns either an +/// address type, pointer type, or box type, but never a reference type. +/// Each object's property or its tail storage is separately accessed. +/// +/// For better identification an access base, use +/// AccessedStorage::compute(). It returns an AccessedStorage value +/// that identifies the storage of a memory access. It provides APIs +/// for inspecting type of accessed storage and allows for disambiguation +/// between different types of storage and different properties within a class. +/// +/// AccessedStorage::compute() follows the same logic as getAccessBase(), but if +/// the base is not recognized as a valid access, it returns invalid +/// AccessedStorage. It also performs further analysis to determine the root +/// reference of an object access. +/// +/// AccessedStorage::compute() returns the outermost AccessedStorage for any +/// memory address. It can be called on the address of a memory operation, the +/// address of a begin_access, or any other address value. If the address is +/// from an enforced begin_access or from any memory operation that is part of a +/// formal access, then it returns a valid AccessedStorage value. If the memory /// operation is not part of a formal access, then it still identifies the -/// accessed location as a best effort, but the result may be invalid storage. +/// accessed storage as a best effort, but the result may be invalid storage. /// -/// An active goal is to require findAccessedStorage() to always return a +/// An active goal is to require compute() to always return a /// valid AccessedStorage value even for operations that aren't part of a /// formal access. /// /// The AccessEnforcementWMO pass is an example of an optimistic optimization -/// that relies on the above requirements for correctness. If -/// findAccessedStorage() simply bailed out on an unrecognized memory address by -/// returning an invalid AccessedStorage, then the optimization could make -/// incorrect assumptions about the absence of access to globals or class +/// that relies on this requirement for correctness. If +/// AccessedStorage::compute() simply bailed out on an unrecognized memory +/// address by returning an invalid AccessedStorage, then the optimization could +/// make incorrect assumptions about the absence of access to globals or class /// properties. /// +/// AccessedStorage::computeInScope() returns an AccessedStorage value for the +/// immediately enclosing access scope. Within a formal access, it always +/// returns a Nested storage kind, which provides the begin_access marker. +/// +/// identifyFormalAccess() works like AccessedStorage::computeInScope(), but +/// finds the storage corresponding to a begin_access marker, rather than an +/// arbitrary address. This must return a valid AccessedStorage value unless the +/// access has "Unsafe" enforcement. The given begin_access marker may be nested +/// within another, outer access scope. For the purpose of exclusivity, nested +/// accesses are considered distinct formal accesses so they return distinct +/// AccessedStorage values even though they may access the same memory. This +/// way, nested accesses do not appear to conflict. +/// +/// AccessPath identifies both the accessed storage and the path to a specific +/// storage location within that storage object. See ProgrammersGuide.md and the +/// class comments below for details. AccessPath::compute() and +/// AccessPath::computeInScope() mirror the AccessedStorage API. +/// AccessPath::contains() and AccessPath::mayOverlap() provide efficient +/// comparison of access paths. +/// +/// AccessPath::collectUses() provides all reachable uses of the accessed +/// storage, allowing the selection of Exact, Inner, or Overlapping uses. +/// visitAccessedStorageUses() and visitAccessPathUses() generalize +/// handling of all reachable uses for a given storage location. +/// //===----------------------------------------------------------------------===// #ifndef SWIFT_SIL_MEMACCESSUTILS_H #define SWIFT_SIL_MEMACCESSUTILS_H +#include "swift/Basic/IndexTrie.h" #include "swift/SIL/ApplySite.h" #include "swift/SIL/InstructionUtils.h" +#include "swift/SIL/Projection.h" #include "swift/SIL/SILArgument.h" #include "swift/SIL/SILBasicBlock.h" #include "swift/SIL/SILGlobalVariable.h" @@ -70,12 +118,12 @@ #include "llvm/ADT/DenseMap.h" //===----------------------------------------------------------------------===// -// MARK: General Helpers +// MARK: Standalone API //===----------------------------------------------------------------------===// namespace swift { -/// Get the base address of a formal access by stripping access markers. +/// Get the source address of a formal access by stripping access markers. /// /// Postcondition: If \p v is an address, then the returned value is also an /// address (pointer-to-address is not stripped). @@ -86,65 +134,49 @@ inline SILValue stripAccessMarkers(SILValue v) { return v; } -/// An address projection that may be inside of a formal access, such as -/// (begin_borrow, struct_element_addr, tuple_element_addr). -struct AccessProjection { - SingleValueInstruction *projectionInst = nullptr; - - /// If \p v is not a recognized access projection the result is invalid. - AccessProjection(SILValue v) { - switch (v->getKind()) { - default: - break; - - case ValueKind::StructElementAddrInst: - case ValueKind::TupleElementAddrInst: - case ValueKind::UncheckedTakeEnumDataAddrInst: - case ValueKind::TailAddrInst: - case ValueKind::IndexAddrInst: - projectionInst = cast(v); - }; - } - - operator bool() const { return projectionInst != nullptr; } - - SILValue baseAddress() const { return projectionInst->getOperand(0); } -}; - -/// Return the base address after stripping access projections. If \p v is an -/// access projection, return the enclosing begin_access. Otherwise, return a -/// "best effort" base address. +/// Return the source address after stripping as many access projections as +/// possible without losing the address type. /// -/// Precondition: \p v must be an address. +/// For formal accesses, this typically returns the begin_access, but may fail +/// for accesses that call into an addressor, which performs pointer +/// conversion. /// -/// To get the base address of the formal access behind the access marker, -/// either call stripAccessMarkers() on the returned value, or call -/// getAccessedAddress() on \p v. +/// If there is no access marker, then this returns the "best-effort" address +/// corresponding to the accessed variable. This never looks through +/// pointer_to_address or other conversions that may change the address type +/// other than via type-safe (TBAA-compatible) projection. +SILValue getTypedAccessAddress(SILValue address); + +/// Return the source address or pointer after stripping all access projections +/// and storage casts. /// -/// To identify the underlying storage object of the access, call -/// findAccessedStorage() either on \p v or on the returned address. -SILValue getAddressAccess(SILValue v); - -/// Convenience for stripAccessMarkers(getAddressAccess(v)). -SILValue getAccessedAddress(SILValue v); +/// If this is a formal access, then it is guaranteed to return the immediately +/// enclosing begin_access and may "see through" storage casts to do so. +/// +/// If there is no access marker, then it returns a "best effort" address +/// corresponding to the accessed variable. In this case, the returned value +/// could be a non-address pointer type. +SILValue getAccessScope(SILValue address); -/// Return true if \p accessedAddress points to a let-variable. +/// Return the source address or pointer after stripping access projections, +/// access markers, and storage casts. /// -/// Precondition: \p accessedAddress must be an address-type value representing -/// the base of a formal access (not a projection within the access). +/// The returned base address is guaranteed to match the unique AccessedStorage +/// value for the same \p address. That is, if two calls to getAccessBase() +/// return the same base address, then they must also have the same storage. +SILValue getAccessBase(SILValue address); + +/// Return true if \p address points to a let-variable. /// /// let-variables are only written during let-variable initialization, which is -/// assumed to store directly to the same, unaliased accessedAddress. +/// assumed to store directly to the same, unaliased access base. /// /// The address of a let-variable must be the base of a formal access, not an /// access projection. A 'let' member of a struct is *not* a let-variable, /// because it's memory may be written when formally modifying the outer /// struct. A let-variable is either an entire local variable, global variable, /// or class property (these are all formal access base addresses). -/// -/// The caller should derive the accessed address using -/// stripAccessMarkers(getAccessedAddress(ptr)). -bool isLetAddress(SILValue accessedAddress); +bool isLetAddress(SILValue address); /// Return true if two accesses to the same storage may conflict given the kind /// of each access. @@ -160,6 +192,20 @@ inline bool accessKindMayConflict(SILAccessKind a, SILAccessKind b) { namespace swift { +/// Control def-use traversals, allowing them to remain with an access scope or +/// consider operations across scope boundaries. +enum class NestedAccessType { StopAtAccessBegin, IgnoreAccessBegin }; + +/// Exact uses only include uses whose AccessPath is identical to this one. +/// Inner uses have an AccessPath the same as or contained by this one. +/// Overlapping uses may contain, be contained by, or have an unknown +/// relationship with this one. An unknown relationship typically results from +/// a dynamic index_addr offset. +/// +/// The enum values are ordered. Each successive use type is a superset of the +/// previous. +enum class AccessUseType { Exact, Inner, Overlapping }; + /// Represents the identity of a storage object being accessed. /// /// Requirements: @@ -231,8 +277,8 @@ class AccessedStorage { static const char *getKindName(Kind k); - // Give object tail storage a fake property index for convenience. - static constexpr unsigned TailIndex = ~0U; + // Give object tail storage a fake large property index for convenience. + static constexpr unsigned TailIndex = std::numeric_limits::max(); /// Directly create an AccessedStorage for class or tail property access. static AccessedStorage forClass(SILValue object, unsigned propertyIndex) { @@ -245,14 +291,40 @@ class AccessedStorage { return storage; } + /// Return an AccessedStorage value that best identifies a formally accessed + /// variable pointed to by \p sourceAddress, looking through any nested + /// formal accesses to find the underlying storage. + /// + /// \p sourceAddress may be an address, pointer, or box type. + /// + /// If \p sourceAddress is within a formal access scope, which does not have + /// "Unsafe" enforcement, then this always returns valid storage. + /// + /// If \p sourceAddress is not within a formal access scope, or within an + /// "Unsafe" scope, then this finds the formal storage if possible, otherwise + /// returning invalid storage. + static AccessedStorage compute(SILValue sourceAddress); + + /// Return an AccessedStorage object that identifies formal access scope that + /// immediately encloses \p sourceAddress. + /// + /// \p sourceAddress may be an address, pointer, or box type. + /// + /// If \p sourceAddress is within a formal access scope, this always returns a + /// valid "Nested" storage value. + /// + /// If \p sourceAddress is not within a formal access scope, then this finds + /// the formal storage if possible, otherwise returning invalid storage. + static AccessedStorage computeInScope(SILValue sourceAddress); + protected: // Checking the storage kind is far more common than other fields. Make sure // it can be byte load with no shift. - static const int ReservedKindBits = 8; + static const int ReservedKindBits = 7; static_assert(ReservedKindBits >= NumKindBits, "Too many storage kinds."); static const unsigned InvalidElementIndex = - (1 << (32 - ReservedKindBits)) - 1; + (1 << (32 - (ReservedKindBits + 1))) - 1; // Form a bitfield that is effectively a union over any pass-specific data // with the fields used within this class as a common prefix. @@ -272,9 +344,10 @@ class AccessedStorage { // elementIndex can overflow while gracefully degrading analysis. For now, // reserve an absurd number of bits at a nice alignment boundary, but this // can be reduced. - SWIFT_INLINE_BITFIELD_BASE(AccessedStorage, 32, kind - : ReservedKindBits, - elementIndex : 32 - ReservedKindBits); + SWIFT_INLINE_BITFIELD_BASE(AccessedStorage, 32, + kind : ReservedKindBits, + isLet : 1, + elementIndex : 32 - (ReservedKindBits + 1)); // Define bits for use in AccessedStorageAnalysis. Each identified storage // object is mapped to one instance of this subclass. @@ -363,8 +436,10 @@ class AccessedStorage { return global; } + bool isReference() const { return getKind() == Class || getKind() == Tail; } + SILValue getObject() const { - assert(getKind() == Class || getKind() == Tail); + assert(isReference()); return value; } unsigned getPropertyIndex() const { @@ -372,6 +447,38 @@ class AccessedStorage { return getElementIndex(); } + /// Return the address or reference root that the storage was based + /// on. Returns an invalid SILValue for globals or invalid storage. + SILValue getRoot() const { + switch (getKind()) { + case AccessedStorage::Box: + case AccessedStorage::Stack: + case AccessedStorage::Nested: + case AccessedStorage::Argument: + case AccessedStorage::Yield: + case AccessedStorage::Unidentified: + return getValue(); // Can be invalid for Unidentified storage. + case AccessedStorage::Global: + return SILValue(); + case AccessedStorage::Class: + case AccessedStorage::Tail: + return getObject(); + } + } + + /// Visit all access roots. If any roots are visited then the original memory + /// operation access must be reachable from one of those roots. Unidentified + /// storage might not have any root. Identified storage always has at least + /// one root. Identified non-global storage always has a single root. For + /// Global storage, this visits all global_addr instructions in the function + /// that reference the same SILGlobalVariable. + /// + /// \p function must be non-null for Global storage (global_addr cannot + /// occur in a static initializer). + void + visitRoots(SILFunction *function, + llvm::function_ref visitor) const; + /// Return true if the given storage objects have identical storage locations. /// /// This compares only the AccessedStorage base class bits, ignoring the @@ -417,28 +524,27 @@ class AccessedStorage { llvm_unreachable("unhandled kind"); } - /// Return trye if the given access is guaranteed to be within a heap object. + /// Return true if the given access is guaranteed to be within a heap object. bool isObjectAccess() const { return getKind() == Class || getKind() == Tail; } /// Return true if the given access is on a 'let' lvalue. - bool isLetAccess(SILFunction *F) const; + bool isLetAccess() const { return Bits.AccessedStorage.isLet; } /// If this is a uniquely identified formal access, then it cannot /// alias with any other uniquely identified access to different storage. - /// - /// This determines whether access markers may conflict, so it cannot assume - /// that exclusivity is enforced. bool isUniquelyIdentified() const { switch (getKind()) { case Box: case Stack: case Global: return true; + case Argument: + return + getArgument()->getArgumentConvention().isExclusiveIndirectParameter(); case Class: case Tail: - case Argument: case Yield: case Nested: case Unidentified: @@ -447,16 +553,10 @@ class AccessedStorage { llvm_unreachable("unhandled kind"); } - /// Return true if this a uniquely identified formal access location assuming - /// exclusivity enforcement. Do not use this to optimize access markers. - bool isUniquelyIdentifiedAfterEnforcement() const { - if (isUniquelyIdentified()) - return true; - - return getKind() == Argument - && getArgument() - ->getArgumentConvention() - .isExclusiveIndirectParameter(); + /// Return true if this storage is guaranteed not to overlap with \p other's + /// storage. + bool isDistinctFrom(const AccessedStorage &other) const { + return isDistinctFrom<&AccessedStorage::isUniquelyIdentified>(other); } /// Return true if this identifies the base of a formal access location. @@ -470,11 +570,45 @@ class AccessedStorage { return getKind() == Class; } - // Return true if this storage is guaranteed not to overlap with \p other's - // storage. + /// Returns the ValueDecl for the underlying storage, if it can be + /// determined. Otherwise returns null. + /// + /// If \p base is provided, then it must be the accessed base for this + /// storage, as passed to the AccessedStorage constructor. What \p base is + /// provided, this is guaranteed to return a valid decl for class properties; + /// otherwise it is only a best effort based on the type of the object root + /// *before* the object is cast to the final accessed reference type. + const ValueDecl *getDecl(SILValue base = SILValue()) const; + + /// Get all leaf uses of all address, pointer, or box values that have a this + /// AccessedStorage in common. Return true if all uses were found before + /// reaching the limit. + /// + /// The caller of 'collectUses' can determine the use type (exact, inner, or + /// overlapping) from the resulting \p uses list by checking 'accessPath == + /// usePath', accessPath.contains(usePath)', and + /// 'accessPath.mayOverlap(usePath)'. Alternatively, the client may call + /// 'visitAccessedStorageUses' with its own AccessUseVisitor subclass to + /// sort the use types. + bool + collectUses(SmallVectorImpl &uses, AccessUseType useTy, + SILFunction *function, + unsigned useLimit = std::numeric_limits::max()) 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; + + template bool isDistinctFrom(const AccessedStorage &other) const { - if (isUniquelyIdentified()) { - if (other.isUniquelyIdentified() && !hasIdenticalBase(other)) + if ((this->*IsUniqueFn)()) { + if ((other.*IsUniqueFn)() && !hasIdenticalBase(other)) return true; if (other.isObjectAccess()) @@ -484,8 +618,8 @@ class AccessedStorage { // Box/Stack storage. return false; } - if (other.isUniquelyIdentified()) - return other.isDistinctFrom(*this); + if ((other.*IsUniqueFn)()) + return other.isDistinctFrom(*this); // Neither storage is uniquely identified. if (isObjectAccess()) { @@ -508,7 +642,7 @@ class AccessedStorage { return false; } if (other.isObjectAccess()) - return other.isDistinctFrom(*this); + return other.isDistinctFrom(*this); // Neither storage is from a class or tail. // @@ -517,27 +651,13 @@ class AccessedStorage { return false; } - /// Returns the ValueDecl for the underlying storage, if it can be - /// determined. Otherwise returns null. - /// - /// WARNING: This is not a constant-time operation. It is for diagnostics and - /// checking via the ValueDecl if we are processing a `let` variable. - const ValueDecl *getDecl() 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; + void setLetAccess(SILValue base); }; } // end namespace swift namespace llvm { + /// Enable using AccessedStorage as a key in DenseMap. /// Do *not* include any extra pass data in key equality. /// @@ -588,52 +708,440 @@ template <> struct DenseMapInfo { return LHS.hasIdenticalBase(RHS); } }; + } // namespace llvm namespace swift { -/// Given an address used by an instruction that reads or writes memory, return -/// the AccessedStorage value that identifies the formally accessed memory, -/// looking through any nested formal accesses to find the underlying storage. -/// -/// This may return invalid storage for a memory operation that is not part of -/// a formal access or when the outermost formal access has Unsafe enforcement. -AccessedStorage findAccessedStorage(SILValue sourceAddr); +/// For convenience, encapsulate and AccessedStorage value along with its +/// accessed base address. +struct AccessedStorageWithBase { + AccessedStorage storage; + // The base of the formal access. For class storage, it is the + // ref_element_addr. For global storage it is the global_addr or initializer + // apply. For other storage, it is the same as accessPath.getRoot(). + // + // Base may be invalid for global_addr -> address_to_pointer -> phi patterns. + // FIXME: add a structural requirement to SIL so base is always valid in OSSA. + SILValue base; -// Helper for identifyFormalAccess. -AccessedStorage identifyAccessedStorageImpl(SILValue sourceAddr); + AccessedStorageWithBase(AccessedStorage storage, SILValue base) + : storage(storage), base(base) {} -/// Return an AccessedStorage object that identifies the formal access -/// represented by \p beginAccess. -/// -/// If the given access is nested within an outer access, return a Nested -/// AccessedStorage kind. This is useful for exclusivity checking to distinguish -/// between nested access vs. conflicting access on the same storage. + /// Identical to AccessedStorage::compute but preserves the access base. + static AccessedStorageWithBase compute(SILValue sourceAddress); + + /// Identical to AccessedStorage::computeInScope but preserves the base. + static AccessedStorageWithBase computeInScope(SILValue sourceAddress); +}; + +/// Return an AccessedStorage value that identifies formally accessed storage +/// for \p beginAccess, considering any outer access scope as having distinct +/// storage from this access scope. This is useful for exclusivity checking +/// to distinguish between nested access vs. conflicting access on the same +/// storage. /// /// May return an invalid storage for either: /// - A \p beginAccess with Unsafe enforcement /// - Non-OSSA form in which address-type block args are allowed inline AccessedStorage identifyFormalAccess(BeginAccessInst *beginAccess) { - return identifyAccessedStorageImpl(beginAccess->getSource()); + return AccessedStorage::computeInScope(beginAccess->getSource()); } inline AccessedStorage identifyFormalAccess(BeginUnpairedAccessInst *beginAccess) { - return identifyAccessedStorageImpl(beginAccess->getSource()); + return AccessedStorage::computeInScope(beginAccess->getSource()); +} + +} // end namespace swift + +//===----------------------------------------------------------------------===// +// MARK: AccessPath +//===----------------------------------------------------------------------===// + +namespace swift { + +/// Identify an addressable location based the AccessedStorage and projection +/// path. +/// +/// Each unique path from a base address implies a unique memory location within +/// that object. A path prefix identifies memory that contains all paths with +/// the same prefix. The AccessPath returned by AccessPath::compute(address) +/// identifies the object seen by any memory operation that *directly* operates +/// on 'address'. The computed path is a prefix of the paths of any contained +/// subobjects. +/// +/// Path indices, encoded by AccessPath::Index, may be either subobject +/// projections or offset indices. We print subobject indices as '#n' and offset +/// indices as '@n'. +/// +/// Example Def->Use: (Path indices) +/// struct_element_addr #1: (#1) +/// ref_tail_addr -> struct_element_addr #2: (#2) +/// ref_tail_addr -> index_addr #1 -> struct_element_addr #2: (@1, #2) +/// pointer_to_address -> struct_element_addr #2: (#2) +/// pointer_to_address -> index_addr #1 -> struct_element_addr #2: (@1, #2) +/// +/// The index of ref_element_addr is part of the storage identity and does +/// not contribute to the access path indices. +/// +/// A well-formed path has at most one offset component at the begining of the +/// path (chained index_addrs are merged into one offset). In other words, +/// taking an offset from a subobject projection is not well-formed access +/// path. However, it is possible (however undesirable) for programmers to +/// convert a subobject address into a pointer (for example, via implicit +/// conversion), then advance that pointer. Since we can't absolutely prevent +/// this, we instead consider it an invalid AccessPath. This is the only case in +/// which AccessPath::storage can differ from AccessedStorage::compute(). +/// +/// Storing an AccessPath ammortizes to constant space. To cache identification +/// of address locations, AccessPath should be used rather than the +/// ProjectionPath which requires quadratic space in the number of address +/// values and quadratic time when comparing addresses. +/// +/// Type-cast operations such as address_to_pointer may appear on the access +/// path. It is illegal to use these operations to cast to a non-layout +/// compatible type. TODO: add enforcement for this rule. +class AccessPath { +public: + /// Compute the access path at \p address. This ignores begin_access markers, + /// returning the outermost AccessedStorage. + /// + /// The computed access path corresponds to the subobject for a memory + /// operation that directly operates on \p address; so, for an indexable + /// address, this implies an operation at index zero. + static AccessPath compute(SILValue address); + + /// Compute the access path at \p address. If \p address is within a formal + /// access, then AccessStorage will have a nested type and base will be a + /// begin_access marker. + /// + /// This is primarily useful for recovering the access scope. The original + /// storage kind will only be discovered when \p address is part of a formal + /// access, thus not within an access scope. + static AccessPath computeInScope(SILValue address); + + // Encode a dynamic index_addr as an UnknownOffset. + static constexpr int UnknownOffset = std::numeric_limits::min() >> 1; + + struct PathNode; + + // An access path index. + // + // Note: + // - IndexTrieNode::RootIndex = INT_MIN = 0x80000000 + // - AccessedStorage::TailIndex = INT_MAX = 0x7FFFFFFF + // - AccessPath::UnknownOffset = (INT_MIN>>1) = 0xC0000000 + // - An offset index is never zero + class Index { + public: + friend struct PathNode; + + // Use the sign bit to identify offset indices. Subobject projections are + // always positive. + constexpr static unsigned IndexFlag = unsigned(1) << 31; + static int encodeOffset(int indexValue) { + assert(indexValue != 0 && "an offset index cannot be zero"); + // Must be able to sign-extended the 31-bit value. + assert(((indexValue << 1) >> 1) == indexValue); + return indexValue | IndexFlag; + } + + // Encode a positive field index, property index, or TailIndex. + static Index forSubObjectProjection(unsigned projIdx) { + assert(Index(projIdx).isSubObjectProjection()); + return Index(projIdx); + } + + static Index forOffset(unsigned projIdx) { + return Index(encodeOffset(projIdx)); + } + + private: + int indexEncoding; + Index(int indexEncoding) : indexEncoding(indexEncoding) {} + + public: + bool isSubObjectProjection() const { return indexEncoding >= 0; } + + int getSubObjectIndex() const { + assert(isSubObjectProjection()); + return indexEncoding; + } + + // Sign-extend the 31-bit value. + int getOffset() const { + assert(!isSubObjectProjection()); + return ((indexEncoding << 1) >> 1); + } + + bool isUnknownOffset() const { + return indexEncoding == AccessPath::UnknownOffset; + } + + int getEncoding() const { return indexEncoding; } + + void print(raw_ostream &os) const; + + void dump() const; + }; + + // A component of the AccessPath. + // + // Transient wrapper around the underlying IndexTrieNode that encodes either a + // subobject projection or an offset index. + struct PathNode { + IndexTrieNode *node = nullptr; + + constexpr PathNode() = default; + + PathNode(IndexTrieNode *node) : node(node) {} + + bool isValid() const { return node != nullptr; } + + bool isRoot() const { return node->isRoot(); } + + bool isLeaf() const { return node->isLeaf(); } + + Index getIndex() const { return Index(node->getIndex()); } + + PathNode getParent() const { return node->getParent(); } + + // Return the PathNode from \p subNode's path one level deeper than \p + // prefixNode. + // + // Precondition: this != subNode + PathNode findPrefix(PathNode subNode) const; + + bool operator==(PathNode other) const { return node == other.node; } + bool operator!=(PathNode other) const { return node != other.node; } + }; + +private: + AccessedStorage storage; + PathNode pathNode; + // store the single offset index independent from the PathNode to simplify + // checking for path overlap. + int offset = 0; + +public: + // AccessPaths are built by AccessPath::compute(address). + // + // AccessedStorage is only used to identify the storage location; AccessPath + // ignores its subclass bits. + AccessPath(AccessedStorage storage, PathNode pathNode, int offset) + : storage(storage), pathNode(pathNode), offset(offset) { + assert(storage.getKind() != AccessedStorage::Nested); + assert(pathNode.isValid() || !storage && "Access path requires a pathNode"); + } + + AccessPath() = default; + + bool operator==(AccessPath other) const { + return + storage.hasIdenticalBase(other.storage) && pathNode == other.pathNode; + } + bool operator!=(AccessPath other) const { return !(*this == other); } + + bool isValid() const { return pathNode.isValid(); } + + AccessedStorage getStorage() const { return storage; } + + PathNode getPathNode() const { return pathNode; } + + int getOffset() const { return offset; } + + bool hasUnknownOffset() const { return offset == UnknownOffset; } + + /// Return true if this path contains \p subPath. + /// + /// Identical AccessPath's contain each other. + /// + /// Returns false if either path is invalid. + bool contains(AccessPath subPath) const; + + /// Return true if this path may overlap with \p otherPath. + /// + /// Returns true if either path is invalid. + bool mayOverlap(AccessPath otherPath) const; + + /// Return the address root that the access path was based on. Returns + /// an invalid SILValue for globals or invalid storage. + SILValue getRoot() const { return storage.getRoot(); } + + /// Get all leaf uses of all address, pointer, or box values that have a this + /// AccessedStorage in common. Return true if all uses were found before + /// reaching the limit. + /// + /// The caller of 'collectUses' can determine the use type (exact, inner, or + /// overlapping) from the resulting \p uses list by checking 'accessPath == + /// usePath', accessPath.contains(usePath)', and + /// 'accessPath.mayOverlap(usePath)'. Alternatively, the client may call + /// 'visitAccessPathUses' with its own AccessUseVisitor subclass to + /// sort the use types. + bool + collectUses(SmallVectorImpl &uses, AccessUseType useTy, + SILFunction *function, + unsigned useLimit = std::numeric_limits::max()) const; + + void printPath(raw_ostream &os) const; + void print(raw_ostream &os) const; + void dump() const; +}; + +// Encapsulate the result of computing an AccessPath. AccessPath does not store +// the base address of the formal access because it does not always uniquely +// indentify the access, but AccessPath users may use the base address to to +// recover the def-use chain for a specific global_addr or ref_element_addr. +struct AccessPathWithBase { + AccessPath accessPath; + // The address-type value that is the base of the formal access. For + // class storage, it is the ref_element_addr. For global storage it is the + // global_addr or initializer apply. For other storage, it is the same as + // accessPath.getRoot(). + // + // Note: base may be invalid for global_addr -> address_to_pointer -> phi + // patterns, while the accessPath is still valid. + // + // FIXME: add a structural requirement to SIL so base is always valid in OSSA. + SILValue base; + + /// Compute the access path at \p address, and record the access base. This + /// ignores begin_access markers, returning the outermost AccessedStorage. + static AccessPathWithBase compute(SILValue address); + + /// Compute the access path at \p address, and record the access base. If \p + /// address is within a formal access, then AccessStorage will have a nested + /// type and base will be a begin_access marker. + static AccessPathWithBase computeInScope(SILValue address); + + AccessPathWithBase(AccessPath accessPath, SILValue base) + : accessPath(accessPath), base(base) {} + + bool operator==(AccessPathWithBase other) const { + return accessPath == other.accessPath && base == other.base; + } + bool operator!=(AccessPathWithBase other) const { return !(*this == other); } + + void print(raw_ostream &os) const; + void dump() const; +}; + +inline AccessPath AccessPath::compute(SILValue address) { + return AccessPathWithBase::compute(address).accessPath; } -/// Return a valid AccessedStorage object for an address captured by a no-escape -/// closure. A no-escape closure may capture a regular storage address without -/// guarding it with an access marker. If the captured address does come from an -/// access marker, then this returns a Nested AccessedStorage kind. -inline AccessedStorage identifyCapturedStorage(SILValue capturedAddress) { - auto storage = identifyAccessedStorageImpl(capturedAddress); - assert(storage && "captured access has invalid storage"); - return storage; +inline AccessPath AccessPath::computeInScope(SILValue address) { + return AccessPathWithBase::compute(address).accessPath; } } // end namespace swift +namespace llvm { + +/// Allow AccessPath to be used in DenseMap. +template <> struct DenseMapInfo { + static inline swift::AccessPath getEmptyKey() { + return swift::AccessPath( + DenseMapInfo::getEmptyKey(), + swift::AccessPath::PathNode( + DenseMapInfo::getEmptyKey()), 0); + } + static inline swift::AccessPath getTombstoneKey() { + return swift::AccessPath( + DenseMapInfo::getTombstoneKey(), + swift::AccessPath::PathNode( + DenseMapInfo::getTombstoneKey()), 0); + } + static inline unsigned getHashValue(const swift::AccessPath &val) { + return llvm::hash_combine( + DenseMapInfo::getHashValue(val.getStorage()), + val.getPathNode().node); + } + static bool isEqual(const swift::AccessPath &lhs, + const swift::AccessPath &rhs) { + return lhs == rhs; + } +}; +template <> struct DenseMapInfo { + static inline swift::AccessPathWithBase getEmptyKey() { + return swift::AccessPathWithBase( + DenseMapInfo::getEmptyKey(), + DenseMapInfo::getEmptyKey()); + } + static inline swift::AccessPathWithBase getTombstoneKey() { + return swift::AccessPathWithBase( + DenseMapInfo::getTombstoneKey(), + DenseMapInfo::getTombstoneKey()); + } + static inline unsigned getHashValue(const swift::AccessPathWithBase &val) { + return llvm::hash_combine( + DenseMapInfo::getHashValue(val.accessPath), + DenseMapInfo::getHashValue(val.base)); + } + static bool isEqual(const swift::AccessPathWithBase &lhs, + const swift::AccessPathWithBase &rhs) { + return lhs == rhs; + } +}; + +} // end namespace llvm + +//===----------------------------------------------------------------------===// +// MARK: Use visitors +//===----------------------------------------------------------------------===// + +namespace swift { + +/// Interface to the customizable use visitor. +struct AccessUseVisitor { + AccessUseType useTy; + NestedAccessType nestedAccessTy; + + AccessUseVisitor(AccessUseType useTy, NestedAccessType nestedTy) + : useTy(useTy), nestedAccessTy(nestedTy) {} + + virtual ~AccessUseVisitor() {} + + bool findInnerUses() const { return useTy >= AccessUseType::Inner; } + bool findOverlappingUses() const { + return useTy == AccessUseType::Overlapping; + } + + bool visitExactUse(Operand *use) { + return visitUse(use, AccessUseType::Exact); + } + bool visitInnerUse(Operand *use) { + return findInnerUses() ? visitUse(use, AccessUseType::Inner) : true; + } + bool visitOverlappingUse(Operand *use) { + return + findOverlappingUses() ? visitUse(use, AccessUseType::Overlapping) : true; + } + + virtual bool visitUse(Operand *use, AccessUseType useTy) = 0; +}; + +/// Visit all uses of \p storage. +/// +/// Return true if all uses were collected. This is always true as long the \p +/// visitor's visitUse method returns true. +bool visitAccessedStorageUses(AccessUseVisitor &visitor, + AccessedStorage storage, + SILFunction *function); + +/// Visit the uses of \p accessPath. +/// +/// If the storage kind is Global, then function must be non-null (global_addr +/// only occurs inside SILFunction). +/// +/// Return true if all uses were collected. This is always true as long the \p +/// visitor's visitUse method returns true. +bool visitAccessPathUses(AccessUseVisitor &visitor, AccessPath accessPath, + SILFunction *function); + +} // end namespace swift + //===----------------------------------------------------------------------===// // MARK: Helper API for specific formal access patterns //===----------------------------------------------------------------------===// @@ -691,7 +1199,8 @@ void checkSwitchEnumBlockArg(SILPhiArgument *arg); /// This is not a member of AccessedStorage because it only makes sense to use /// in SILGen before access markers are emitted, or when verifying access /// markers. -bool isPossibleFormalAccessBase(const AccessedStorage &storage, SILFunction *F); +bool isPossibleFormalAccessBase(const AccessedStorage &storage, + SILFunction *F); /// Perform a RAUW operation on begin_access with it's own source operand. /// Then erase the begin_access and all associated end_access instructions. @@ -709,7 +1218,114 @@ SILBasicBlock::iterator removeBeginAccess(BeginAccessInst *beginAccess); namespace swift { -/// Abstract CRTP class for a visitor passed to \c visitAccessUseDefChain. +/// Return true if \p svi is a cast that preserves the identity and +/// reference-counting equivalence of the reference at operand zero. +bool isRCIdentityPreservingCast(SingleValueInstruction *svi); + +/// If \p svi is an access projection, return an address-type operand for the +/// incoming address. +/// +/// An access projection is on the inside of a formal access. It includes +/// struct_element_addr and tuple_element_addr, but not ref_element_addr. +/// +/// The returned address may point to any compatible type, which may alias with +/// the projected address. Arbitrary address casts are not allowed. +inline Operand *getAccessProjectionOperand(SingleValueInstruction *svi) { + switch (svi->getKind()) { + default: + return nullptr; + + case SILInstructionKind::StructElementAddrInst: + case SILInstructionKind::TupleElementAddrInst: + case SILInstructionKind::IndexAddrInst: + case SILInstructionKind::TailAddrInst: + // open_existential_addr and unchecked_take_enum_data_addr are problematic + // because they both modify memory and are access projections. Ideally, they + // would not be casts, but will likely be eliminated with opaque values. + case SILInstructionKind::OpenExistentialAddrInst: + case SILInstructionKind::UncheckedTakeEnumDataAddrInst: + return &svi->getAllOperands()[0]; + + // Special-case this indirect enum pattern: + // unchecked_take_enum_data_addr -> load -> project_box + // (the individual load and project_box are not access projections) + // + // FIXME: Make sure this case goes away with OSSA and opaque values. If not, + // then create a special instruction for this pattern. That way we have an + // invariant that all access projections are single-value address-to-address + // conversions. Then reuse this helper for both use-def an def-use traversals. + // + // Check getAccessProjectionOperand() before isAccessedStorageCast() because + // it will consider any project_box to be a storage cast. + case SILInstructionKind::ProjectBoxInst: + if (auto *load = dyn_cast(svi->getOperand(0))) + return &load->getOperandRef(); + + return nullptr; + }; +} + +/// An address, pointer, or box cast that occurs outside of the formal +/// access. These convert the base of accessed storage without affecting the +/// AccessPath. Useful for both use-def and def-use traversal. The source +/// address must be at operand(0). +/// +/// Some of these casts, such as address_to_pointer, may also occur inside of a +/// formal access. TODO: Add stricter structural guarantee such that these never +/// occur within an access. It's important to be able to get the accessed +/// address without looking though type casts or pointer_to_address [strict], +/// which we can't do if those operations are behind access projections. +inline bool isAccessedStorageCast(SingleValueInstruction *svi) { + switch (svi->getKind()) { + default: + return false; + + // Simply pass-thru the incoming address. + case SILInstructionKind::MarkUninitializedInst: + case SILInstructionKind::UncheckedAddrCastInst: + case SILInstructionKind::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 and begin_borrow. + case SILInstructionKind::ProjectBoxInst: + case SILInstructionKind::ProjectBlockStorageInst: + case SILInstructionKind::CopyValueInst: + case SILInstructionKind::BeginBorrowInst: + // Casting to RawPointer does not affect the AccessPath. When converting + // between address types, they must be layout compatible (with truncation). + case SILInstructionKind::AddressToPointerInst: + // Access to a Builtin.RawPointer. It may be important to continue looking + // through this because some RawPointers originate from identified + // locations. See the special case for global addressors, which return + // RawPointer, above. + // + // If the inductive search does not find a valid addressor, it will + // eventually reach the default case that returns in invalid location. This + // is correct for RawPointer because, although accessing a RawPointer is + // legal SIL, there is no way to guarantee that it doesn't access class or + // global storage, so returning a valid unidentified storage object would be + // incorrect. It is the caller's responsibility to know that formal access + // to such a location can be safely ignored. + // + // For example: + // + // - KeyPath Builtins access RawPointer. However, the caller can check + // that the access `isFromBuilin` and ignore the storage. + // + // - lldb generates RawPointer access for debugger variables, but SILGen + // marks debug VarDecl access as 'Unsafe' and SIL passes don't need the + // AccessedStorage for 'Unsafe' access. + case SILInstructionKind::PointerToAddressInst: + return true; + } +} + +/// Abstract CRTP class for a visiting instructions that are part of the use-def +/// chain from an accessed address up to the storage base. +/// +/// Given the address of a memory operation begin visiting at +/// getAccessedAddress(address). template class AccessUseDefChainVisitor { protected: @@ -751,32 +1367,40 @@ class AccessUseDefChainVisitor { // Result visitBase(SILValue base, AccessedStorage::Kind kind); // Result visitNonAccess(SILValue base); // Result visitPhi(SILPhiArgument *phi); - // Result visitCast(SingleValueInstruction *cast, Operand *parentAddr); - // Result visitPathComponent(SingleValueInstruction *projectedAddr, - // Operand *parentAddr); + // Result visitStorageCast(SingleValueInstruction *cast, Operand *sourceOper); + // Result visitAccessProjection(SingleValueInstruction *cast, + // Operand *sourceOper); Result visit(SILValue sourceAddr); }; template Result AccessUseDefChainVisitor::visit(SILValue sourceAddr) { + if (auto *svi = dyn_cast(sourceAddr)) { + if (auto *projOper = getAccessProjectionOperand(svi)) + return asImpl().visitAccessProjection(svi, projOper); + + if (isAccessedStorageCast(svi)) + return asImpl().visitStorageCast(svi, &svi->getAllOperands()[0]); + } switch (sourceAddr->getKind()) { default: - if (isAddressForLocalInitOnly(sourceAddr)) - return asImpl().visitUnidentified(sourceAddr); - return asImpl().visitNonAccess(sourceAddr); + break; // MARK: Handle immediately-identifiable instructions. // An AllocBox is a fully identified memory location. case ValueKind::AllocBoxInst: return asImpl().visitBoxAccess(cast(sourceAddr)); + // An AllocStack is a fully identified memory location, which may occur // after inlining code already subjected to stack promotion. case ValueKind::AllocStackInst: return asImpl().visitStackAccess(cast(sourceAddr)); + case ValueKind::GlobalAddrInst: return asImpl().visitGlobalAccess(sourceAddr); + case ValueKind::ApplyInst: { FullApplySite apply(cast(sourceAddr)); if (auto *funcRef = apply.getReferencedFunctionOrNull()) { @@ -792,25 +1416,37 @@ Result AccessUseDefChainVisitor::visit(SILValue sourceAddr) { } case ValueKind::RefElementAddrInst: return asImpl().visitClassAccess(cast(sourceAddr)); + + // ref_tail_addr project an address from a reference. + // This is a valid address producer for nested @inout argument + // access, but it is never used for formal access of identified objects. + case ValueKind::RefTailAddrInst: + return asImpl().visitTailAccess(cast(sourceAddr)); + // A yield is effectively a nested access, enforced independently in // the caller and callee. case ValueKind::BeginApplyResult: return asImpl().visitYieldAccess(cast(sourceAddr)); + // A function argument is effectively a nested access, enforced // independently in the caller and callee. case ValueKind::SILFunctionArgument: - return asImpl().visitArgumentAccess(cast(sourceAddr)); + return asImpl().visitArgumentAccess( + cast(sourceAddr)); // View the outer begin_access as a separate location because nested // accesses do not conflict with each other. case ValueKind::BeginAccessInst: return asImpl().visitNestedAccess(cast(sourceAddr)); + // Static index_addr is handled by getAccessProjectionOperand. Dynamic + // index_addr is currently unidentified because we can't form an AccessPath + // including them. case ValueKind::SILUndef: return asImpl().visitUnidentified(sourceAddr); - // MARK: The sourceAddr producer cannot immediately be classified, - // follow the use-def chain. + // MARK: The sourceAddr producer cannot immediately be classified, + // follow the use-def chain. case ValueKind::StructExtractInst: // Handle nested access to a KeyPath projection. The projection itself @@ -834,98 +1470,11 @@ Result AccessUseDefChainVisitor::visit(SILValue sourceAddr) { checkSwitchEnumBlockArg(cast(sourceAddr)); return asImpl().visitUnidentified(sourceAddr); } - // Load a box from an indirect payload of an opaque enum. - // We must have peeked past the project_box earlier in this loop. - // (the indirectness makes it a box, the load is for address-only). - // - // %payload_adr = unchecked_take_enum_data_addr %enum : $*Enum, #Enum.case - // %box = load [take] %payload_adr : $*{ var Enum } - // - // FIXME: this case should go away with opaque values. - // - // Otherwise return invalid AccessedStorage. - case ValueKind::LoadInst: - if (sourceAddr->getType().is()) { - Operand *addrOper = &cast(sourceAddr)->getOperandRef(); - assert(isa(addrOper->get())); - return asImpl().visitCast(cast(sourceAddr), - addrOper); - } - return asImpl().visitNonAccess(sourceAddr); - - // ref_tail_addr project an address from a reference. - // This is a valid address producer for nested @inout argument - // access, but it is never used for formal access of identified objects. - case ValueKind::RefTailAddrInst: - return asImpl().visitTailAccess(cast(sourceAddr)); - - // Inductive single-operand cases: - // Look through address casts to find the source address. - case ValueKind::MarkUninitializedInst: - case ValueKind::OpenExistentialAddrInst: - case ValueKind::UncheckedAddrCastInst: - // Inductive cases that apply to any type. - case ValueKind::CopyValueInst: - case ValueKind::MarkDependenceInst: - // Look through a project_box to identify the underlying alloc_box as the - // accesed object. It must be possible to reach either the alloc_box or the - // containing enum in this loop, only looking through simple value - // propagation such as copy_value. - case ValueKind::ProjectBoxInst: - // Handle project_block_storage just like project_box. - case ValueKind::ProjectBlockStorageInst: - // Look through begin_borrow in case a local box is borrowed. - case ValueKind::BeginBorrowInst: - // Casting to RawPointer does not affect the AccessPath. When converting - // between address types, they must be layout compatible (with truncation). - case ValueKind::AddressToPointerInst: - // A tail_addr is a projection that does not affect the access path because it - // must always originate from a ref_tail_addr. Any projection within the - // object's tail storage effectively has the same access path. - case ValueKind::TailAddrInst: - return asImpl().visitCast( - cast(sourceAddr), - &cast(sourceAddr)->getAllOperands()[0]); + } // end switch + if (isAddressForLocalInitOnly(sourceAddr)) + return asImpl().visitUnidentified(sourceAddr); - // Access to a Builtin.RawPointer. It may be important to continue looking - // through this because some RawPointers originate from identified - // locations. See the special case for global addressors, which return - // RawPointer, above. - // - // If the inductive search does not find a valid addressor, it will - // eventually reach the default case that returns in invalid location. This - // is correct for RawPointer because, although accessing a RawPointer is - // legal SIL, there is no way to guarantee that it doesn't access class or - // global storage, so returning a valid unidentified storage object would be - // incorrect. It is the caller's responsibility to know that formal access - // to such a location can be safely ignored. - // - // For example: - // - // - KeyPath Builtins access RawPointer. However, the caller can check - // that the access `isFromBuilin` and ignore the storage. - // - // - lldb generates RawPointer access for debugger variables, but SILGen - // marks debug VarDecl access as 'Unsafe' and SIL passes don't need the - // AccessedStorage for 'Unsafe' access. - // - // This is always considered a path component because an IndexAddr may - // project from it. - case ValueKind::PointerToAddressInst: - return asImpl().visitPathComponent( - cast(sourceAddr), - &cast(sourceAddr)->getAllOperands()[0]); - - // Address-to-address subobject projections. Projection::isAddressProjection - // returns true for these. - case ValueKind::StructElementAddrInst: - case ValueKind::TupleElementAddrInst: - case ValueKind::UncheckedTakeEnumDataAddrInst: - case ValueKind::IndexAddrInst: - return asImpl().visitPathComponent( - cast(sourceAddr), - &cast(sourceAddr)->getAllOperands()[0]); - } + return asImpl().visitNonAccess(sourceAddr); } } // end namespace swift diff --git a/include/swift/SIL/PatternMatch.h b/include/swift/SIL/PatternMatch.h index 7b3a83bee1c0d..07b7449e1d9eb 100644 --- a/include/swift/SIL/PatternMatch.h +++ b/include/swift/SIL/PatternMatch.h @@ -406,7 +406,7 @@ template struct tupleextractoperation_ty { template bool match(ITy *V) { if (auto *TEI = dyn_cast(V)) { - return TEI->getFieldNo() == index && + return TEI->getFieldIndex() == index && L.match((ValueBase *)TEI->getOperand()); } @@ -668,6 +668,16 @@ using BuiltinApplyTy = typename Apply_match::Ty; // Define matchers for most of builtin instructions. #include "swift/AST/Builtins.def" +#undef BUILTIN_UNARY_OP_MATCH_WITH_ARG_MATCHER +#undef BUILTIN_BINARY_OP_MATCH_WITH_ARG_MATCHER +#undef BUILTIN_VARARGS_OP_MATCH_WITH_ARG_MATCHER +#undef BUILTIN_CAST_OPERATION +#undef BUILTIN_CAST_OR_BITCAST_OPERATION +#undef BUILTIN_BINARY_OPERATION_ALL +#undef BUILTIN_BINARY_PREDICATE +#undef BUILTIN_MISC_OPERATION +#undef BUILTIN + //=== // Convenience compound builtin instructions matchers that succeed // if any of the sub-matchers succeed. diff --git a/include/swift/SIL/Projection.h b/include/swift/SIL/Projection.h index 0202e099d1caa..ece22804b153c 100644 --- a/include/swift/SIL/Projection.h +++ b/include/swift/SIL/Projection.h @@ -67,9 +67,9 @@ inline bool isStrictSubSeqRelation(SubSeqRelation_t Seq) { /// Extract an integer index from a SILValue. /// -/// Return true if IndexVal is a constant index representable as unsigned +/// Return true if IndexVal is a constant index representable as an /// int. We do not support symbolic projections yet. -bool getIntegerIndex(SILValue IndexVal, unsigned &IndexConst); +bool getIntegerIndex(SILValue IndexVal, int &IndexConst); /// The kind of projection that we are representing. /// @@ -136,11 +136,18 @@ static inline bool isCastProjectionKind(ProjectionKind Kind) { /// that immediately contains it. /// /// This lightweight utility maps a SIL address projection to an index. +/// +/// project_box does not have a projection index. At the SIL level, the box +/// storage is considered part of the same object as the. The box projection is +/// does not affect access path so that box projections can occur on distinct +/// phi paths in the address def-use chain. struct ProjectionIndex { + static constexpr int TailIndex = std::numeric_limits::max(); + SILValue Aggregate; - unsigned Index; + int Index = std::numeric_limits::min(); - explicit ProjectionIndex(SILValue V) : Index(~0U) { + explicit ProjectionIndex(SILValue V) { switch (V->getKind()) { default: break; @@ -152,44 +159,37 @@ struct ProjectionIndex { } case ValueKind::StructElementAddrInst: { StructElementAddrInst *SEA = cast(V); - Index = SEA->getFieldNo(); + Index = SEA->getFieldIndex(); Aggregate = SEA->getOperand(); break; } case ValueKind::RefElementAddrInst: { RefElementAddrInst *REA = cast(V); - Index = REA->getFieldNo(); + Index = REA->getFieldIndex(); Aggregate = REA->getOperand(); break; } case ValueKind::RefTailAddrInst: { - RefTailAddrInst *REA = cast(V); - Index = 0; - Aggregate = REA->getOperand(); - break; - } - case ValueKind::ProjectBoxInst: { - ProjectBoxInst *PBI = cast(V); - // A box has only a single payload. - Index = 0; - Aggregate = PBI->getOperand(); + RefTailAddrInst *RTA = cast(V); + Index = TailIndex; + Aggregate = RTA->getOperand(); break; } case ValueKind::TupleElementAddrInst: { TupleElementAddrInst *TEA = cast(V); - Index = TEA->getFieldNo(); + Index = TEA->getFieldIndex(); Aggregate = TEA->getOperand(); break; } case ValueKind::StructExtractInst: { StructExtractInst *SEA = cast(V); - Index = SEA->getFieldNo(); + Index = SEA->getFieldIndex(); Aggregate = SEA->getOperand(); break; } case ValueKind::TupleExtractInst: { TupleExtractInst *TEA = cast(V); - Index = TEA->getFieldNo(); + Index = TEA->getFieldIndex(); Aggregate = TEA->getOperand(); break; } @@ -233,8 +233,7 @@ class Projection { : Projection(dyn_cast(I)) {} explicit Projection(SingleValueInstruction *I); - Projection(ProjectionKind Kind, unsigned NewIndex) - : Value(Kind, NewIndex) {} + Projection(ProjectionKind Kind, int NewIndex) : Value(Kind, NewIndex) {} Projection(ProjectionKind Kind, TypeBase *Ptr) : Value(Kind, Ptr) {} @@ -252,10 +251,8 @@ class Projection { /// Convenience method for getting the underlying index. Assumes that this /// projection is valid. Otherwise it asserts. - unsigned getIndex() const { - return Value.getIndex(); - } - + int getIndex() const { return Value.getIndex(); } + unsigned getHash() const { return (unsigned)Value.getStorage(); } /// Determine if I is a value projection instruction whose corresponding @@ -302,10 +299,9 @@ class Projection { assert(isValid()); assert((getKind() == ProjectionKind::Struct || getKind() == ProjectionKind::Class)); - assert(BaseType.getNominalOrBoundGenericNominal() && - "This should only be called with a nominal type"); - auto *NDecl = BaseType.getNominalOrBoundGenericNominal(); - return NDecl->getStoredProperties()[getIndex()]; + auto *nominalDecl = BaseType.getNominalOrBoundGenericNominal(); + assert(nominalDecl && "This should only be called with a nominal type"); + return getIndexedField(nominalDecl, getIndex()); } EnumElementDecl *getEnumElementDecl(SILType BaseType) const { @@ -360,7 +356,7 @@ class Projection { return nullptr; case ValueKind::IndexAddrInst: { auto *i = cast(v); - unsigned scalar; + int scalar; if (getIntegerIndex(i->getIndex(), scalar)) return i; return nullptr; diff --git a/include/swift/SIL/SILBuilder.h b/include/swift/SIL/SILBuilder.h index 9b84da4bfae9d..52fafb452cc27 100644 --- a/include/swift/SIL/SILBuilder.h +++ b/include/swift/SIL/SILBuilder.h @@ -1125,6 +1125,7 @@ class SILBuilder { UncheckedValueCastInst *createUncheckedValueCast(SILLocation Loc, SILValue Op, SILType Ty) { + assert(hasOwnership()); return insert(UncheckedValueCastInst::create( getSILDebugLocation(Loc), Op, Ty, getFunction(), C.OpenedArchetypes)); } @@ -1934,6 +1935,20 @@ class SILBuilder { getSILDebugLocation(Loc), Operand, Index)); } + GetAsyncContinuationInst *createGetAsyncContinuation(SILLocation Loc, + SILType ContinuationTy) { + return insert(new (getModule()) GetAsyncContinuationInst(getSILDebugLocation(Loc), + ContinuationTy)); + } + + GetAsyncContinuationAddrInst *createGetAsyncContinuationAddr(SILLocation Loc, + SILValue Operand, + SILType ContinuationTy) { + return insert(new (getModule()) GetAsyncContinuationAddrInst(getSILDebugLocation(Loc), + Operand, + ContinuationTy)); + } + //===--------------------------------------------------------------------===// // Terminator SILInstruction Creation Methods //===--------------------------------------------------------------------===// @@ -1964,7 +1979,17 @@ class SILBuilder { YieldInst::create(getSILDebugLocation(loc), yieldedValues, resumeBB, unwindBB, getFunction())); } - + + AwaitAsyncContinuationInst *createAwaitAsyncContinuation(SILLocation loc, + SILValue continuation, + SILBasicBlock *resumeBB, + SILBasicBlock *errorBB) { + return insertTerminator( + new (getModule()) AwaitAsyncContinuationInst(getSILDebugLocation(loc), + continuation, + resumeBB, errorBB)); + } + CondBranchInst * createCondBranch(SILLocation Loc, SILValue Cond, SILBasicBlock *Target1, SILBasicBlock *Target2, @@ -2475,6 +2500,20 @@ class SILBuilderWithScope : public SILBuilder { assert(DS && "Instruction without debug scope associated!"); setCurrentDebugScope(DS); } + + /// If \p inst is a terminator apply site, then pass a builder to insert at + /// the first instruction of each successor to \p func. Otherwise, pass a + /// builder to insert at std::next(inst). + /// + /// The intention is that this abstraction will enable the compiler writer to + /// ignore whether or not \p inst is a terminator when inserting instructions + /// after \p inst. + /// + /// Precondition: It's the responsibility of the caller to ensure that if + /// \p inst is a terminator, all successor blocks have only a single + /// predecessor block: the parent of \p inst. + static void insertAfter(SILInstruction *inst, + function_ref func); }; class SavedInsertionPointRAII { diff --git a/include/swift/SIL/SILCloner.h b/include/swift/SIL/SILCloner.h index c27d345ee8ae2..c51d0b7831019 100644 --- a/include/swift/SIL/SILCloner.h +++ b/include/swift/SIL/SILCloner.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 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -1542,6 +1542,13 @@ template void SILCloner::visitUncheckedValueCastInst( UncheckedValueCastInst *Inst) { getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); + if (!getBuilder().hasOwnership()) { + recordClonedInstruction(Inst, getBuilder().createUncheckedBitwiseCast( + getOpLocation(Inst->getLoc()), + getOpValue(Inst->getOperand()), + getOpType(Inst->getType()))); + return; + } recordClonedInstruction(Inst, getBuilder().createUncheckedValueCast( getOpLocation(Inst->getLoc()), getOpValue(Inst->getOperand()), @@ -1946,7 +1953,7 @@ SILCloner::visitTupleExtractInst(TupleExtractInst *Inst) { recordClonedInstruction( Inst, getBuilder().createTupleExtract( getOpLocation(Inst->getLoc()), getOpValue(Inst->getOperand()), - Inst->getFieldNo(), getOpType(Inst->getType()))); + Inst->getFieldIndex(), getOpType(Inst->getType()))); } template @@ -1956,7 +1963,7 @@ SILCloner::visitTupleElementAddrInst(TupleElementAddrInst *Inst) { recordClonedInstruction( Inst, getBuilder().createTupleElementAddr( getOpLocation(Inst->getLoc()), getOpValue(Inst->getOperand()), - Inst->getFieldNo(), getOpType(Inst->getType()))); + Inst->getFieldIndex(), getOpType(Inst->getType()))); } template @@ -2086,7 +2093,8 @@ SILCloner::visitWitnessMethodInst(WitnessMethodInst *Inst) { if (conformance.isConcrete()) { CanType Ty = conformance.getConcrete()->getType()->getCanonicalType(); - if (Ty != newLookupType) { + if (Ty != newLookupType && + !isa(conformance.getConcrete())) { assert( (Ty->isExactSuperclassOf(newLookupType) || getBuilder().getModule().Types.getLoweredRValueType( @@ -2921,6 +2929,41 @@ void SILCloner::visitDifferentiabilityWitnessFunctionInst( Inst->getWitnessKind(), Inst->getWitness())); } +template +void SILCloner +::visitGetAsyncContinuationInst(GetAsyncContinuationInst *Inst) { + getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); + recordClonedInstruction(Inst, + getBuilder().createGetAsyncContinuation( + getOpLocation(Inst->getLoc()), + getOpType(Inst->getType()))); +} + +template +void SILCloner +::visitGetAsyncContinuationAddrInst(GetAsyncContinuationAddrInst *Inst) { + getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); + recordClonedInstruction(Inst, + getBuilder().createGetAsyncContinuationAddr( + getOpLocation(Inst->getLoc()), + getOpValue(Inst->getOperand()), + getOpType(Inst->getType()))); +} + +template +void SILCloner +::visitAwaitAsyncContinuationInst(AwaitAsyncContinuationInst *Inst) { + getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); + recordClonedInstruction(Inst, + getBuilder().createAwaitAsyncContinuation( + getOpLocation(Inst->getLoc()), + getOpValue(Inst->getOperand()), + getOpBasicBlock(Inst->getResumeBB()), + Inst->getErrorBB() + ? getOpBasicBlock(Inst->getErrorBB()) + : nullptr)); +} + } // end namespace swift #endif diff --git a/include/swift/SIL/SILDeclRef.h b/include/swift/SIL/SILDeclRef.h index 434a17a1831d5..b3f6b0d8f5551 100644 --- a/include/swift/SIL/SILDeclRef.h +++ b/include/swift/SIL/SILDeclRef.h @@ -20,6 +20,7 @@ #define SWIFT_SIL_SILDeclRef_H #include "swift/AST/ClangNode.h" +#include "swift/AST/GenericSignature.h" #include "swift/AST/TypeAlignments.h" #include "llvm/ADT/Hashing.h" #include "llvm/ADT/DenseMap.h" @@ -48,6 +49,7 @@ namespace swift { class SILLocation; enum class SILLinkage : uint8_t; class AnyFunctionRef; + class GenericSignature; /// How a method is dispatched. enum class MethodDispatch { @@ -148,13 +150,28 @@ struct SILDeclRef { unsigned isForeign : 1; /// The default argument index for a default argument getter. unsigned defaultArgIndex : 10; + + PointerUnion + pointer; + /// The derivative function identifier. - AutoDiffDerivativeFunctionIdentifier *derivativeFunctionIdentifier = nullptr; + AutoDiffDerivativeFunctionIdentifier * getDerivativeFunctionIdentifier() const { + if (!pointer.is()) + return nullptr; + return pointer.get(); + } + + GenericSignature getSpecializedSignature() const { + if (!pointer.is()) + return GenericSignature(); + else + return GenericSignature(pointer.get()); + } /// Produces a null SILDeclRef. SILDeclRef() - : loc(), kind(Kind::Func), isForeign(0), defaultArgIndex(0), - derivativeFunctionIdentifier(nullptr) {} + : loc(), kind(Kind::Func), isForeign(0), defaultArgIndex(0) {} /// Produces a SILDeclRef of the given kind for the given decl. explicit SILDeclRef( @@ -174,6 +191,9 @@ struct SILDeclRef { /// SILDeclRef. explicit SILDeclRef(Loc loc, bool isForeign = false); + /// See above put produces a prespecialization according to the signature. + explicit SILDeclRef(Loc loc, GenericSignature prespecializationSig); + /// Produce a SIL constant for a default argument generator. static SILDeclRef getDefaultArgGenerator(Loc loc, unsigned defaultArgIndex); @@ -287,7 +307,7 @@ struct SILDeclRef { return loc.getOpaqueValue() == rhs.loc.getOpaqueValue() && kind == rhs.kind && isForeign == rhs.isForeign && defaultArgIndex == rhs.defaultArgIndex && - derivativeFunctionIdentifier == rhs.derivativeFunctionIdentifier; + pointer == rhs.pointer; } bool operator!=(SILDeclRef rhs) const { return !(*this == rhs); @@ -302,7 +322,7 @@ struct SILDeclRef { /// decl. SILDeclRef asForeign(bool foreign = true) const { return SILDeclRef(loc.getOpaqueValue(), kind, foreign, defaultArgIndex, - derivativeFunctionIdentifier); + pointer.get()); } /// Returns the entry point for the corresponding autodiff derivative @@ -311,16 +331,16 @@ struct SILDeclRef { AutoDiffDerivativeFunctionIdentifier *derivativeId) const { assert(derivativeId); SILDeclRef declRef = *this; - declRef.derivativeFunctionIdentifier = derivativeId; + declRef.pointer = derivativeId; return declRef; } /// Returns the entry point for the original function corresponding to an /// autodiff derivative function. SILDeclRef asAutoDiffOriginalFunction() const { - assert(derivativeFunctionIdentifier); + assert(pointer.get()); SILDeclRef declRef = *this; - declRef.derivativeFunctionIdentifier = nullptr; + declRef.pointer = (AutoDiffDerivativeFunctionIdentifier *)nullptr; return declRef; } @@ -398,13 +418,14 @@ struct SILDeclRef { bool canBeDynamicReplacement() const; bool isAutoDiffDerivativeFunction() const { - return derivativeFunctionIdentifier != nullptr; + return pointer.is() && + pointer.get() != nullptr; } AutoDiffDerivativeFunctionIdentifier * getAutoDiffDerivativeFunctionIdentifier() const { assert(isAutoDiffDerivativeFunction()); - return derivativeFunctionIdentifier; + return pointer.get(); } private: @@ -415,7 +436,7 @@ struct SILDeclRef { AutoDiffDerivativeFunctionIdentifier *derivativeId) : loc(Loc::getFromOpaqueValue(opaqueLoc)), kind(kind), isForeign(isForeign), defaultArgIndex(defaultArgIndex), - derivativeFunctionIdentifier(derivativeId) {} + pointer(derivativeId) {} }; inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, SILDeclRef C) { @@ -450,7 +471,7 @@ template<> struct DenseMapInfo { ? UnsignedInfo::getHashValue(Val.defaultArgIndex) : 0; unsigned h4 = UnsignedInfo::getHashValue(Val.isForeign); - unsigned h5 = PointerInfo::getHashValue(Val.derivativeFunctionIdentifier); + unsigned h5 = PointerInfo::getHashValue(Val.pointer.getOpaqueValue()); return h1 ^ (h2 << 4) ^ (h3 << 9) ^ (h4 << 7) ^ (h5 << 11); } static bool isEqual(swift::SILDeclRef const &LHS, diff --git a/include/swift/SIL/SILFunction.h b/include/swift/SIL/SILFunction.h index 2831bbf36d02f..42e17d4d39b3a 100644 --- a/include/swift/SIL/SILFunction.h +++ b/include/swift/SIL/SILFunction.h @@ -71,7 +71,9 @@ class SILSpecializeAttr final { static SILSpecializeAttr *create(SILModule &M, GenericSignature specializedSignature, - bool exported, SpecializationKind kind); + bool exported, SpecializationKind kind, + SILFunction *target, Identifier spiGroup, + const ModuleDecl *spiModule); bool isExported() const { return exported; @@ -97,16 +99,32 @@ class SILSpecializeAttr final { return F; } + SILFunction *getTargetFunction() const { + return targetFunction; + } + + Identifier getSPIGroup() const { + return spiGroup; + } + + const ModuleDecl *getSPIModule() const { + return spiModule; + } + void print(llvm::raw_ostream &OS) const; private: SpecializationKind kind; bool exported; GenericSignature specializedSignature; + Identifier spiGroup; + const ModuleDecl *spiModule = nullptr; SILFunction *F = nullptr; + SILFunction *targetFunction = nullptr; SILSpecializeAttr(bool exported, SpecializationKind kind, - GenericSignature specializedSignature); + GenericSignature specializedSignature, SILFunction *target, + Identifier spiGroup, const ModuleDecl *spiModule); }; /// SILFunction - A function body that has been lowered to SIL. This consists of @@ -569,15 +587,18 @@ class SILFunction return getLoweredFunctionType()->hasIndirectFormalResults(); } - /// Returns true if this function either has a self metadata argument or - /// object that Self metadata may be derived from. + /// Returns true if this function ie either a class method, or a + /// closure that captures the 'self' value or its metatype. + /// + /// If this returns true, DynamicSelfType can be used in the body + /// of the function. /// /// Note that this is not the same as hasSelfParam(). /// - /// For closures that capture DynamicSelfType, hasSelfMetadataParam() + /// For closures that capture DynamicSelfType, hasDynamicSelfMetadata() /// is true and hasSelfParam() is false. For methods on value types, - /// hasSelfParam() is true and hasSelfMetadataParam() is false. - bool hasSelfMetadataParam() const; + /// hasSelfParam() is true and hasDynamicSelfMetadata() is false. + bool hasDynamicSelfMetadata() const; /// Return the mangled name of this SILFunction. StringRef getName() const { return Name; } @@ -593,6 +614,9 @@ class SILFunction /// Returns true if this is a definition of a function defined in this module. bool isDefinition() const { return !isExternalDeclaration(); } + /// Returns true if there exist pre-specializations. + bool hasPrespecialization() const; + /// Get this function's linkage attribute. SILLinkage getLinkage() const { return SILLinkage(Linkage); } @@ -722,10 +746,18 @@ class SILFunction } /// Removes all specialize attributes from this function. - void clearSpecializeAttrs() { SpecializeAttrSet.clear(); } + void clearSpecializeAttrs() { + forEachSpecializeAttrTargetFunction( + [](SILFunction *targetFun) { targetFun->decrementRefCount(); }); + SpecializeAttrSet.clear(); + } void addSpecializeAttr(SILSpecializeAttr *Attr); + void removeSpecializeAttr(SILSpecializeAttr *attr); + + void forEachSpecializeAttrTargetFunction( + llvm::function_ref action); /// Get this function's optimization mode or OptimizationMode::NotSet if it is /// not set for this specific function. @@ -1055,8 +1087,8 @@ class SILFunction return getArguments().back(); } - const SILArgument *getSelfMetadataArgument() const { - assert(hasSelfMetadataParam() && "This method can only be called if the " + const SILArgument *getDynamicSelfMetadata() const { + assert(hasDynamicSelfMetadata() && "This method can only be called if the " "SILFunction has a self-metadata parameter"); return getArguments().back(); } diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index 6ad16592afbc2..9011f26d28880 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -514,6 +514,10 @@ class SILInstruction "Operand does not belong to a SILInstruction"); return isTypeDependentOperand(Op.getOperandNumber()); } + + /// Returns true if evaluation of this instruction may cause suspension of an + /// async task. + bool maySuspend() const; private: /// Predicate used to filter OperandValueRange. @@ -3039,6 +3043,65 @@ class KeyPathPattern final } }; +/// Base class for instructions that access the continuation of an async task, +/// in order to set up a suspension. +/// The continuation must be consumed by an AwaitAsyncContinuation instruction locally, +/// and must dynamically be resumed exactly once during the program's ensuing execution. +class GetAsyncContinuationInstBase + : public SingleValueInstruction +{ +protected: + using SingleValueInstruction::SingleValueInstruction; + +public: + /// Get the type of the value the async task receives on a resume. + CanType getFormalResumeType() const; + SILType getLoweredResumeType() const; + + /// True if the continuation can be used to resume the task by throwing an error. + bool throws() const; + + static bool classof(const SILNode *I) { + return I->getKind() >= SILNodeKind::First_GetAsyncContinuationInstBase && + I->getKind() <= SILNodeKind::Last_GetAsyncContinuationInstBase; + } +}; + +/// Accesses the continuation for an async task, to prepare a primitive suspend operation. +class GetAsyncContinuationInst final + : public InstructionBase +{ + friend SILBuilder; + + GetAsyncContinuationInst(SILDebugLocation Loc, + SILType ContinuationTy) + : InstructionBase(Loc, ContinuationTy) + {} + +public: + ArrayRef getAllOperands() const { return {}; } + MutableArrayRef getAllOperands() { return {}; } +}; + +/// Accesses the continuation for an async task, to prepare a primitive suspend operation. +/// The continuation must be consumed by an AwaitAsyncContinuation instruction locally, +/// and must dynamically be resumed exactly once during the program's ensuing execution. +/// +/// This variation of the instruction additionally takes an operand for the address of the +/// buffer that receives the incoming value when the continuation is resumed. +class GetAsyncContinuationAddrInst final + : public UnaryInstructionBase +{ + friend SILBuilder; + GetAsyncContinuationAddrInst(SILDebugLocation Loc, + SILValue Operand, + SILType ContinuationTy) + : UnaryInstructionBase(Loc, Operand, ContinuationTy) + {} +}; + /// Instantiates a key path object. class KeyPathInst final : public InstructionBase { + OwnershipForwardingConversionInst> { friend SILBuilder; ThinToThickFunctionInst(SILDebugLocation DebugLoc, SILValue Operand, ArrayRef TypeDependentOperands, SILType Ty) : UnaryInstructionWithTypeDependentOperandsBase( - DebugLoc, Operand, TypeDependentOperands, Ty) {} + DebugLoc, Operand, TypeDependentOperands, Ty, + Operand.getOwnershipKind()) {} static ThinToThickFunctionInst * create(SILDebugLocation DebugLoc, SILValue Operand, SILType Ty, @@ -5715,7 +5779,7 @@ class TupleExtractInst } public: - unsigned getFieldNo() const { + unsigned getFieldIndex() const { return SILInstruction::Bits.TupleExtractInst.FieldNo; } @@ -5747,7 +5811,7 @@ class TupleElementAddrInst } public: - unsigned getFieldNo() const { + unsigned getFieldIndex() const { return SILInstruction::Bits.TupleElementAddrInst.FieldNo; } @@ -5757,6 +5821,24 @@ class TupleElementAddrInst } }; +/// Get a unique index for a struct or class field in layout order. +/// +/// Precondition: \p decl must be a non-resilient struct or class. +/// +/// Precondition: \p field must be a stored property declared in \p decl, +/// not in a superclass. +/// +/// Postcondition: The returned index is unique across all properties in the +/// object, including properties declared in a superclass. +unsigned getFieldIndex(NominalTypeDecl *decl, VarDecl *property); + +/// Get the property for a struct or class by its unique index, or nullptr if +/// the index does not match a property declared in this struct or class or +/// one its superclasses. +/// +/// Precondition: \p decl must be a non-resilient struct or class. +VarDecl *getIndexedField(NominalTypeDecl *decl, unsigned index); + /// A common base for instructions that require a cached field index. /// /// "Field" is a term used here to refer to the ordered, accessible stored @@ -5791,8 +5873,7 @@ class FieldIndexCacheBase : public SingleValueInstruction { VarDecl *getField() const { return field; } - // FIXME: this should be called getFieldIndex(). - unsigned getFieldNo() const { + unsigned getFieldIndex() const { unsigned idx = SILInstruction::Bits.FieldIndexCacheBase.FieldIndex; if (idx != InvalidFieldIndex) return idx; @@ -7212,6 +7293,7 @@ class TermInst : public NonValueInstruction { case TermKind::DynamicMethodBranchInst: case TermKind::CheckedCastAddrBranchInst: case TermKind::CheckedCastValueBranchInst: + case TermKind::AwaitAsyncContinuationInst: return false; case TermKind::SwitchEnumInst: case TermKind::CheckedCastBranchInst: @@ -7307,6 +7389,52 @@ class UnwindInst MutableArrayRef getAllOperands() { return {}; } }; +/// Suspend execution of an async task until +/// essentially just a funny kind of return). +class AwaitAsyncContinuationInst final + : public UnaryInstructionBase +{ + friend SILBuilder; + + std::array Successors; + + AwaitAsyncContinuationInst(SILDebugLocation Loc, SILValue Continuation, + SILBasicBlock *resumeBB, + SILBasicBlock *errorBBOrNull) + : UnaryInstructionBase(Loc, Continuation), + Successors{{{this}, {this}}} + { + Successors[0] = resumeBB; + if (errorBBOrNull) + Successors[1] = errorBBOrNull; + } + +public: + /// Returns the basic block to which control is transferred when the task is + /// resumed normally. + /// + /// This basic block should take an argument of the continuation's resume type, + /// unless the continuation is formed by a \c GetAsyncContinuationAddrInst + /// that binds a specific memory location to receive the resume value. + SILBasicBlock *getResumeBB() const { return Successors[0].getBB(); } + + /// Returns the basic block to which control is transferred when the task is + /// resumed in an error state, or `nullptr` if the continuation does not support + /// failure. + /// + /// This basic block should take an argument of Error type. + SILBasicBlock *getErrorBB() const { + return Successors[1].getBB(); + } + + SuccessorListTy getSuccessors() { + if (getErrorBB()) + return Successors; + return SuccessorListTy(Successors.data(), 1); + } +}; + /// YieldInst - Yield control temporarily to the caller of this coroutine. /// /// This is a terminator because the caller can abort the coroutine, diff --git a/include/swift/SIL/SILModule.h b/include/swift/SIL/SILModule.h index de9753223d43f..5d373b4da028f 100644 --- a/include/swift/SIL/SILModule.h +++ b/include/swift/SIL/SILModule.h @@ -21,6 +21,7 @@ #include "swift/AST/Builtins.h" #include "swift/AST/SILLayout.h" #include "swift/AST/SILOptions.h" +#include "swift/Basic/IndexTrie.h" #include "swift/Basic/LangOptions.h" #include "swift/Basic/ProfileCounter.h" #include "swift/Basic/Range.h" @@ -259,6 +260,10 @@ class SILModule { /// The indexed profile data to be used for PGO, or nullptr. std::unique_ptr PGOReader; + /// A trie of integer indices that gives pointer identity to a path of + /// projections, shared between all functions in the module. + std::unique_ptr indexTrieRoot; + /// The options passed into this SILModule. const SILOptions &Options; @@ -266,6 +271,15 @@ class SILModule { /// to ensure that the module is serialized only once. bool serialized; + /// Set if we have registered a deserialization notification handler for + /// lowering ownership in non transparent functions. + /// This gets set in NonTransparent OwnershipModelEliminator pass. + bool regDeserializationNotificationHandlerForNonTransparentFuncOME; + /// Set if we have registered a deserialization notification handler for + /// lowering ownership in transparent functions. + /// This gets set in OwnershipModelEliminator pass. + bool regDeserializationNotificationHandlerForAllFuncOME; + /// Action to be executed for serializing the SILModule. ActionCallback SerializeSILAction; @@ -304,6 +318,19 @@ class SILModule { deserializationNotificationHandlers.erase(handler); } + bool hasRegisteredDeserializationNotificationHandlerForNonTransparentFuncOME() { + return regDeserializationNotificationHandlerForNonTransparentFuncOME; + } + bool hasRegisteredDeserializationNotificationHandlerForAllFuncOME() { + return regDeserializationNotificationHandlerForAllFuncOME; + } + void setRegisteredDeserializationNotificationHandlerForNonTransparentFuncOME() { + regDeserializationNotificationHandlerForNonTransparentFuncOME = true; + } + void setRegisteredDeserializationNotificationHandlerForAllFuncOME() { + regDeserializationNotificationHandlerForAllFuncOME = true; + } + /// Add a delete notification handler \p Handler to the module context. void registerDeleteNotificationHandler(DeleteNotificationHandler* Handler); @@ -636,6 +663,8 @@ class SILModule { PGOReader = std::move(IPR); } + IndexTrieNode *getIndexTrieRoot() { return indexTrieRoot.get(); } + /// Can value operations (copies and destroys) on the given lowered type /// be performed in this module? bool isTypeABIAccessible(SILType type, diff --git a/include/swift/SIL/SILNode.h b/include/swift/SIL/SILNode.h index 2138510e33a8c..aa6572273efc6 100644 --- a/include/swift/SIL/SILNode.h +++ b/include/swift/SIL/SILNode.h @@ -454,7 +454,7 @@ class alignas(8) SILNode { /// If this is a SILArgument or a SILInstruction get its parent module, /// otherwise return null. SILModule *getModule() const; - + /// Pretty-print the node. If the node is an instruction, the output /// will be valid SIL assembly; otherwise, it will be an arbitrary /// format suitable for debugging. diff --git a/include/swift/SIL/SILNodes.def b/include/swift/SIL/SILNodes.def index d26054e66aed4..c80bd67d4b093 100644 --- a/include/swift/SIL/SILNodes.def +++ b/include/swift/SIL/SILNodes.def @@ -713,6 +713,18 @@ ABSTRACT_VALUE_AND_INST(SingleValueInstruction, ValueBase, SILInstruction) SINGLE_VALUE_INST(DifferentiabilityWitnessFunctionInst, differentiability_witness_function, SingleValueInstruction, None, DoesNotRelease) + + // Async + // TODO: The side effects declarations on this instruction could likely + // be tightened, though we want to be careful that passes that try to do + // code motion or eliminate this instruction don't do so without awareness of + // its structural requirements. + ABSTRACT_SINGLE_VALUE_INST(GetAsyncContinuationInstBase, SingleValueInstruction) + SINGLE_VALUE_INST(GetAsyncContinuationInst, get_async_continuation, + GetAsyncContinuationInstBase, MayHaveSideEffects, MayRelease) + SINGLE_VALUE_INST(GetAsyncContinuationAddrInst, get_async_continuation_addr, + GetAsyncContinuationInstBase, MayHaveSideEffects, MayRelease) + SINGLE_VALUE_INST_RANGE(GetAsyncContinuationInstBase, GetAsyncContinuationInst, GetAsyncContinuationAddrInst) // Key paths // TODO: The only "side effect" is potentially retaining the returned key path @@ -750,6 +762,8 @@ ABSTRACT_INST(TermInst, SILInstruction) TermInst, MayRead, DoesNotRelease) TERMINATOR(DynamicMethodBranchInst, dynamic_method_br, TermInst, None, DoesNotRelease) + TERMINATOR(AwaitAsyncContinuationInst, await_async_continuation, + TermInst, MayHaveSideEffects, MayRelease) DYNAMICCAST_TERMINATOR(CheckedCastBranchInst, checked_cast_br, TermInst, None, DoesNotRelease) DYNAMICCAST_TERMINATOR(CheckedCastAddrBranchInst, checked_cast_addr_br, diff --git a/include/swift/SIL/TypeLowering.h b/include/swift/SIL/TypeLowering.h index ed135eccf8e2f..f9e5883d85769 100644 --- a/include/swift/SIL/TypeLowering.h +++ b/include/swift/SIL/TypeLowering.h @@ -13,7 +13,7 @@ #ifndef SWIFT_SIL_TYPELOWERING_H #define SWIFT_SIL_TYPELOWERING_H -#include "swift/ABI/MetadataValues.h" +#include "swift/ABI/ProtocolDispatchStrategy.h" #include "swift/AST/CaptureInfo.h" #include "swift/AST/Module.h" #include "swift/SIL/AbstractionPattern.h" @@ -147,16 +147,23 @@ enum IsResilient_t : bool { IsResilient = true }; +/// Does this type contain an opaque result type that affects type lowering? +enum IsTypeExpansionSensitive_t : bool { + IsNotTypeExpansionSensitive = false, + IsTypeExpansionSensitive = true +}; + /// Extended type information used by SIL. class TypeLowering { public: class RecursiveProperties { // These are chosen so that bitwise-or merges the flags properly. enum : unsigned { - NonTrivialFlag = 1 << 0, - NonFixedABIFlag = 1 << 1, - AddressOnlyFlag = 1 << 2, - ResilientFlag = 1 << 3, + NonTrivialFlag = 1 << 0, + NonFixedABIFlag = 1 << 1, + AddressOnlyFlag = 1 << 2, + ResilientFlag = 1 << 3, + TypeExpansionSensitiveFlag = 1 << 4, }; uint8_t Flags; @@ -165,15 +172,17 @@ class TypeLowering { /// a trivial, loadable, fixed-layout type. constexpr RecursiveProperties() : Flags(0) {} - constexpr RecursiveProperties(IsTrivial_t isTrivial, - IsFixedABI_t isFixedABI, - IsAddressOnly_t isAddressOnly, - IsResilient_t isResilient) - : Flags((isTrivial ? 0U : NonTrivialFlag) | - (isFixedABI ? 0U : NonFixedABIFlag) | - (isAddressOnly ? AddressOnlyFlag : 0U) | - (isResilient ? ResilientFlag : 0U)) {} - + constexpr RecursiveProperties( + IsTrivial_t isTrivial, IsFixedABI_t isFixedABI, + IsAddressOnly_t isAddressOnly, IsResilient_t isResilient, + IsTypeExpansionSensitive_t isTypeExpansionSensitive = + IsNotTypeExpansionSensitive) + : Flags((isTrivial ? 0U : NonTrivialFlag) | + (isFixedABI ? 0U : NonFixedABIFlag) | + (isAddressOnly ? AddressOnlyFlag : 0U) | + (isResilient ? ResilientFlag : 0U) | + (isTypeExpansionSensitive ? TypeExpansionSensitiveFlag : 0U)) {} + constexpr bool operator==(RecursiveProperties p) const { return Flags == p.Flags; } @@ -183,7 +192,7 @@ class TypeLowering { } static constexpr RecursiveProperties forReference() { - return {IsNotTrivial, IsFixedABI, IsNotAddressOnly, IsNotResilient }; + return {IsNotTrivial, IsFixedABI, IsNotAddressOnly, IsNotResilient}; } static constexpr RecursiveProperties forOpaque() { @@ -194,6 +203,7 @@ class TypeLowering { return {IsTrivial, IsFixedABI, IsNotAddressOnly, IsResilient}; } + void addSubobject(RecursiveProperties other) { Flags |= other.Flags; } @@ -210,10 +220,19 @@ class TypeLowering { IsResilient_t isResilient() const { return IsResilient_t((Flags & ResilientFlag) != 0); } + IsTypeExpansionSensitive_t isTypeExpansionSensitive() const { + return IsTypeExpansionSensitive_t( + (Flags & TypeExpansionSensitiveFlag) != 0); + } void setNonTrivial() { Flags |= NonTrivialFlag; } void setNonFixedABI() { Flags |= NonFixedABIFlag; } void setAddressOnly() { Flags |= AddressOnlyFlag; } + void setTypeExpansionSensitive( + IsTypeExpansionSensitive_t isTypeExpansionSensitive) { + Flags = (Flags & ~TypeExpansionSensitiveFlag) | + (isTypeExpansionSensitive ? TypeExpansionSensitiveFlag : 0); + } }; private: @@ -305,6 +324,12 @@ class TypeLowering { return Properties.isResilient(); } + /// Does this type contain an opaque result type that could influence how the + /// type is lowered if we could look through to the underlying type. + bool isTypeExpansionSensitive() const { + return Properties.isTypeExpansionSensitive(); + } + ResilienceExpansion getResilienceExpansion() const { return expansionContext.getResilienceExpansion(); } @@ -705,8 +730,6 @@ class TypeConverter { llvm::DenseMap LoweredCaptures; - llvm::DenseMap opaqueArchetypeFields; - /// Cache of loadable SILType to number of (estimated) fields /// /// Second element is a ResilienceExpansion. @@ -719,17 +742,15 @@ class TypeConverter { Optional BridgedType##Ty; #include "swift/SIL/BridgedTypes.def" - const TypeLowering & - getTypeLoweringForLoweredType(AbstractionPattern origType, - CanType loweredType, - TypeExpansionContext forExpansion, - bool origHadOpaqueTypeArchetype); + const TypeLowering &getTypeLoweringForLoweredType( + AbstractionPattern origType, CanType loweredType, + TypeExpansionContext forExpansion, + IsTypeExpansionSensitive_t isTypeExpansionSensitive); - const TypeLowering * - getTypeLoweringForExpansion(TypeKey key, - TypeExpansionContext forExpansion, - const TypeLowering *lowering, - bool origHadOpaqueTypeArchetype); + const TypeLowering *getTypeLoweringForExpansion( + TypeKey key, TypeExpansionContext forExpansion, + const TypeLowering *minimalExpansionLowering, + IsTypeExpansionSensitive_t isOrigTypeExpansionSensitive); public: ModuleDecl &M; @@ -792,8 +813,7 @@ class TypeConverter { /// True if a protocol uses witness tables for dynamic dispatch. static bool protocolRequiresWitnessTable(ProtocolDecl *P) { - return ProtocolDescriptorFlags::needsWitnessTable - (getProtocolDispatchStrategy(P)); + return swift::protocolRequiresWitnessTable(getProtocolDispatchStrategy(P)); } /// True if a type is passed indirectly at +0 when used as the "self" @@ -885,8 +905,6 @@ class TypeConverter { CanType getLoweredTypeOfGlobal(VarDecl *var); - bool hasOpaqueArchetypeOrPropertiesOrCases(CanType ty); - /// Return the SILFunctionType for a native function value of the /// given type. CanSILFunctionType getSILFunctionType(TypeExpansionContext context, diff --git a/include/swift/SILOptimizer/Analysis/AccessSummaryAnalysis.h b/include/swift/SILOptimizer/Analysis/AccessSummaryAnalysis.h index e71b6c58fea36..d8a448e7ee85a 100644 --- a/include/swift/SILOptimizer/Analysis/AccessSummaryAnalysis.h +++ b/include/swift/SILOptimizer/Analysis/AccessSummaryAnalysis.h @@ -19,10 +19,10 @@ #ifndef SWIFT_SILOPTIMIZER_ANALYSIS_ACCESS_SUMMARY_ANALYSIS_H_ #define SWIFT_SILOPTIMIZER_ANALYSIS_ACCESS_SUMMARY_ANALYSIS_H_ +#include "swift/Basic/IndexTrie.h" #include "swift/SIL/SILFunction.h" #include "swift/SIL/SILInstruction.h" #include "swift/SILOptimizer/Analysis/BottomUpIPAnalysis.h" -#include "swift/SILOptimizer/Utils/IndexTrie.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallVector.h" @@ -173,22 +173,13 @@ class AccessSummaryAnalysis : public BottomUpIPAnalysis { llvm::SpecificBumpPtrAllocator Allocator; - /// A trie of integer indices that gives pointer identity to a path of - /// projections. This is shared between all functions in the module. - std::unique_ptr SubPathTrie; - public: - AccessSummaryAnalysis() : BottomUpIPAnalysis(SILAnalysisKind::AccessSummary) { - SubPathTrie.reset(new IndexTrieNode()); - } + AccessSummaryAnalysis() + : BottomUpIPAnalysis(SILAnalysisKind::AccessSummary) {} /// Returns a summary of the accesses performed by the given function. const FunctionSummary &getOrCreateSummary(SILFunction *Fn); - IndexTrieNode *getSubPathTrieRoot() { - return SubPathTrie.get(); - } - /// Returns an IndexTrieNode that represents the single subpath accessed from /// BAI or the root if no such node exists. const IndexTrieNode *findSubPathAccessed(BeginAccessInst *BAI); diff --git a/include/swift/SILOptimizer/Analysis/AliasAnalysis.h b/include/swift/SILOptimizer/Analysis/AliasAnalysis.h index 6a1a38c70a77f..a1794a3f01ca5 100644 --- a/include/swift/SILOptimizer/Analysis/AliasAnalysis.h +++ b/include/swift/SILOptimizer/Analysis/AliasAnalysis.h @@ -47,7 +47,6 @@ namespace { struct MemBehaviorKeyTy { // The SILValue pair: size_t V1, V2; - RetainObserveKind InspectionMode; }; } @@ -201,24 +200,16 @@ class AliasAnalysis : public SILAnalysis { /// Use the alias analysis to determine the memory behavior of Inst with /// respect to V. - /// - /// TODO: When ref count behavior is separated from generic memory behavior, - /// the InspectionMode flag will be unnecessary. - MemoryBehavior computeMemoryBehavior(SILInstruction *Inst, SILValue V, - RetainObserveKind); + MemoryBehavior computeMemoryBehavior(SILInstruction *Inst, SILValue V); /// Use the alias analysis to determine the memory behavior of Inst with /// respect to V. - /// - /// TODO: When ref count behavior is separated from generic memory behavior, - /// the InspectionMode flag will be unnecessary. - MemoryBehavior computeMemoryBehaviorInner(SILInstruction *Inst, SILValue V, - RetainObserveKind); + MemoryBehavior computeMemoryBehaviorInner(SILInstruction *Inst, SILValue V); /// Returns true if \p Inst may read from memory in a manner that /// affects V. bool mayReadFromMemory(SILInstruction *Inst, SILValue V) { - auto B = computeMemoryBehavior(Inst, V, RetainObserveKind::IgnoreRetains); + auto B = computeMemoryBehavior(Inst, V); return B == MemoryBehavior::MayRead || B == MemoryBehavior::MayReadWrite || B == MemoryBehavior::MayHaveSideEffects; @@ -227,7 +218,7 @@ class AliasAnalysis : public SILAnalysis { /// Returns true if \p Inst may write to memory in a manner that /// affects V. bool mayWriteToMemory(SILInstruction *Inst, SILValue V) { - auto B = computeMemoryBehavior(Inst, V, RetainObserveKind::IgnoreRetains); + auto B = computeMemoryBehavior(Inst, V); return B == MemoryBehavior::MayWrite || B == MemoryBehavior::MayReadWrite || B == MemoryBehavior::MayHaveSideEffects; @@ -236,26 +227,10 @@ class AliasAnalysis : public SILAnalysis { /// Returns true if \p Inst may read or write to memory in a manner that /// affects V. bool mayReadOrWriteMemory(SILInstruction *Inst, SILValue V) { - auto B = computeMemoryBehavior(Inst, V, RetainObserveKind::IgnoreRetains); + auto B = computeMemoryBehavior(Inst, V); return MemoryBehavior::None != B; } - /// Returns true if Inst may have side effects in a manner that affects V. - bool mayHaveSideEffects(SILInstruction *Inst, SILValue V) { - auto B = computeMemoryBehavior(Inst, V, RetainObserveKind::ObserveRetains); - return B == MemoryBehavior::MayWrite || - B == MemoryBehavior::MayReadWrite || - B == MemoryBehavior::MayHaveSideEffects; - } - - /// Returns true if Inst may have side effects in a manner that affects - /// V. This is independent of whether or not Inst may write to V and is meant - /// to encode notions such as ref count modifications. - bool mayHavePureSideEffects(SILInstruction *Inst, SILValue V) { - auto B = computeMemoryBehavior(Inst, V, RetainObserveKind::ObserveRetains); - return MemoryBehavior::MayHaveSideEffects == B; - } - /// Returns true if \p Ptr may be released in the function call \p FAS. bool canApplyDecrementRefCount(FullApplySite FAS, SILValue Ptr); @@ -268,8 +243,7 @@ class AliasAnalysis : public SILAnalysis { AliasKeyTy toAliasKey(SILValue V1, SILValue V2, SILType Type1, SILType Type2); /// Encodes the memory behavior query as a MemBehaviorKeyTy. - MemBehaviorKeyTy toMemoryBehaviorKey(SILInstruction *V1, SILValue V2, - RetainObserveKind K); + MemBehaviorKeyTy toMemoryBehaviorKey(SILInstruction *V1, SILValue V2); virtual void invalidate() override { AliasCache.clear(); @@ -330,24 +304,21 @@ namespace llvm { template <> struct DenseMapInfo { static inline MemBehaviorKeyTy getEmptyKey() { auto Allone = std::numeric_limits::max(); - return {0, Allone, RetainObserveKind::RetainObserveKindEnd}; + return {0, Allone}; } static inline MemBehaviorKeyTy getTombstoneKey() { auto Allone = std::numeric_limits::max(); - return {Allone, 0, RetainObserveKind::RetainObserveKindEnd}; + return {Allone, 0}; } static unsigned getHashValue(const MemBehaviorKeyTy V) { unsigned H = 0; H ^= DenseMapInfo::getHashValue(V.V1); H ^= DenseMapInfo::getHashValue(V.V2); - H ^= DenseMapInfo::getHashValue(static_cast(V.InspectionMode)); return H; } static bool isEqual(const MemBehaviorKeyTy LHS, const MemBehaviorKeyTy RHS) { - return LHS.V1 == RHS.V1 && - LHS.V2 == RHS.V2 && - LHS.InspectionMode == RHS.InspectionMode; + return LHS.V1 == RHS.V1 && LHS.V2 == RHS.V2; } }; } diff --git a/include/swift/SILOptimizer/Analysis/FunctionOrder.h b/include/swift/SILOptimizer/Analysis/FunctionOrder.h index 8a443a886cbc7..79469c8193133 100644 --- a/include/swift/SILOptimizer/Analysis/FunctionOrder.h +++ b/include/swift/SILOptimizer/Analysis/FunctionOrder.h @@ -43,6 +43,7 @@ class BottomUpFunctionOrder { llvm::SmallSetVector DFSStack; public: + // SWIFT_ENABLE_TENSORFLOW BottomUpFunctionOrder(BasicCalleeAnalysis *BCA) : BCA(BCA), NextDFSNum(0) {} @@ -56,14 +57,17 @@ class BottomUpFunctionOrder { for (auto &F : *M) DFS(&F); } + // SWIFT_ENABLE_TENSORFLOW END /// Get the SCCs in bottom-up order. ArrayRef getSCCs() { return TheSCCs; } + // SWIFT_ENABLE_TENSORFLOW /// Get a flattened view of all functions in all the SCCs in bottom-up order ArrayRef getBottomUpOrder() { + // SWIFT_ENABLE_TENSORFLOW END if (!TheFunctions.empty()) return TheFunctions; for (auto SCC : getSCCs()) diff --git a/include/swift/SILOptimizer/Analysis/SideEffectAnalysis.h b/include/swift/SILOptimizer/Analysis/SideEffectAnalysis.h index f75e577b41052..db3eecb6c9245 100644 --- a/include/swift/SILOptimizer/Analysis/SideEffectAnalysis.h +++ b/include/swift/SILOptimizer/Analysis/SideEffectAnalysis.h @@ -389,6 +389,13 @@ class FunctionSideEffects { /// instructions are considered as side effects. MemoryBehavior getMemBehavior(RetainObserveKind ScanKind) const; + /// Gets the memory behavior for an argument. + /// + /// This is derived from the combined argument and the global effects. + /// Also the argument type and convention are considered. + MemoryBehavior getArgumentBehavior(FullApplySite applySite, + unsigned argIdx); + /// 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. diff --git a/include/swift/SILOptimizer/Analysis/ValueTracking.h b/include/swift/SILOptimizer/Analysis/ValueTracking.h index 2ab2715102cbb..f98d90132e8d4 100644 --- a/include/swift/SILOptimizer/Analysis/ValueTracking.h +++ b/include/swift/SILOptimizer/Analysis/ValueTracking.h @@ -39,8 +39,8 @@ bool pointsToLocalObject(SILValue V); /// Returns true if \p V is a uniquely identified address or reference. Two /// uniquely identified pointers with distinct roots cannot alias. However, a /// uniquely identified pointer may alias with unidentified pointers. For -/// example, the uniquely identified pointer may escape to a call that returns an -/// alias of that pointer. +/// example, the uniquely identified pointer may escape to a call that returns +/// an alias of that pointer. /// /// It may be any of: /// @@ -53,10 +53,25 @@ bool pointsToLocalObject(SILValue V); /// /// - an address projection based on an exclusive argument with no levels of /// indirection (e.g. ref_element_addr, project_box, etc.). +/// +/// TODO: Fold this into the AccessedStorage API. pointsToLocalObject should be +/// performed by AccessedStorage::isUniquelyIdentified. inline bool isUniquelyIdentified(SILValue V) { - return pointsToLocalObject(V) - || (V->getType().isAddress() - && isExclusiveArgument(getAccessedAddress(V))); + SILValue objectRef = V; + if (V->getType().isAddress()) { + auto storage = AccessedStorage::compute(V); + if (!storage) + return false; + + if (storage.isUniquelyIdentified()) + return true; + + if (!storage.isObjectAccess()) + return false; + + objectRef = storage.getObject(); + } + return pointsToLocalObject(objectRef); } enum class IsZeroKind { diff --git a/include/swift/SILOptimizer/PassManager/PassManager.h b/include/swift/SILOptimizer/PassManager/PassManager.h index b72969c96a01b..8c82ff0169dc0 100644 --- a/include/swift/SILOptimizer/PassManager/PassManager.h +++ b/include/swift/SILOptimizer/PassManager/PassManager.h @@ -281,6 +281,11 @@ class SILPassManager { /// Run the passes in Transform from \p FromTransIdx to \p ToTransIdx. void runFunctionPasses(unsigned FromTransIdx, unsigned ToTransIdx); + /// Helper function to check if the function pass should be run mandatorily + /// All passes in mandatory pass pipeline and ownership model elimination are + /// mandatory function passes. + bool isMandatoryFunctionPass(SILFunctionTransform *); + /// A helper function that returns (based on SIL stage and debug /// options) whether we should continue running passes. bool continueTransforming(); diff --git a/include/swift/SILOptimizer/PassManager/Passes.def b/include/swift/SILOptimizer/PassManager/Passes.def index 4d41b442d5b9a..79cf3644b3f8b 100644 --- a/include/swift/SILOptimizer/PassManager/Passes.def +++ b/include/swift/SILOptimizer/PassManager/Passes.def @@ -70,6 +70,8 @@ PASS(AccessSummaryDumper, "access-summary-dump", "Dump Address Parameter Access Summary") PASS(AccessedStorageAnalysisDumper, "accessed-storage-analysis-dump", "Dump Accessed Storage Analysis Summaries") +PASS(AccessPathVerification, "access-path-verification", + "Verify Access Paths (and Accessed Storage)") PASS(AccessedStorageDumper, "accessed-storage-dump", "Dump Accessed Storage Information") PASS(AccessMarkerElimination, "access-marker-elim", @@ -161,6 +163,8 @@ PASS(DifferentiabilityWitnessDevirtualizer, "Inlines Differentiability Witnesses") PASS(EagerSpecializer, "eager-specializer", "Eager Specialization via @_specialize") +PASS(OnonePrespecializations, "onone-prespecializer", + "Pre specialization via @_specialize") PASS(EarlyCodeMotion, "early-codemotion", "Early Code Motion without Release Hoisting") PASS(EarlyInliner, "early-inline", diff --git a/include/swift/SILOptimizer/Utils/GenericCloner.h b/include/swift/SILOptimizer/Utils/GenericCloner.h index 7b5a2c424e74c..6a768210d4890 100644 --- a/include/swift/SILOptimizer/Utils/GenericCloner.h +++ b/include/swift/SILOptimizer/Utils/GenericCloner.h @@ -46,14 +46,12 @@ class GenericCloner public: friend class SILCloner; - GenericCloner(SILOptFunctionBuilder &FuncBuilder, - SILFunction *F, - const ReabstractionInfo &ReInfo, - SubstitutionMap ParamSubs, - StringRef NewName, - CloneCollector::CallbackType Callback) - : SuperTy(*initCloned(FuncBuilder, F, ReInfo, NewName), *F, - ParamSubs), FuncBuilder(FuncBuilder), ReInfo(ReInfo), Callback(Callback) { + GenericCloner(SILOptFunctionBuilder &FuncBuilder, SILFunction *F, + const ReabstractionInfo &ReInfo, SubstitutionMap ParamSubs, + StringRef NewName, CloneCollector::CallbackType Callback) + : SuperTy(*createDeclaration(FuncBuilder, F, ReInfo, NewName), *F, + ParamSubs), + FuncBuilder(FuncBuilder), ReInfo(ReInfo), Callback(Callback) { assert(F->getDebugScope()->Parent != getCloned()->getDebugScope()->Parent); } /// Clone and remap the types in \p F according to the substitution @@ -75,6 +73,11 @@ class GenericCloner void fixUp(SILFunction *calleeFunction); + static SILFunction *createDeclaration(SILOptFunctionBuilder &FuncBuilder, + SILFunction *Orig, + const ReabstractionInfo &ReInfo, + StringRef NewName); + protected: void visitTerminator(SILBasicBlock *BB); @@ -93,10 +96,6 @@ class GenericCloner } private: - static SILFunction *initCloned(SILOptFunctionBuilder &FuncBuilder, - SILFunction *Orig, - const ReabstractionInfo &ReInfo, - StringRef NewName); /// Clone the body of the function into the empty function that was created /// by initCloned. void populateCloned(); diff --git a/include/swift/SILOptimizer/Utils/Generics.h b/include/swift/SILOptimizer/Utils/Generics.h index 22af588a16532..17cb6656ccf27 100644 --- a/include/swift/SILOptimizer/Utils/Generics.h +++ b/include/swift/SILOptimizer/Utils/Generics.h @@ -123,6 +123,8 @@ class ReabstractionInfo { // It uses interface types. SubstitutionMap CallerInterfaceSubs; + bool isPrespecialization = false; + // Is the generated specialization going to be serialized? IsSerialized_t Serialized; @@ -147,8 +149,9 @@ class ReabstractionInfo { void finishPartialSpecializationPreparation( FunctionSignaturePartialSpecializer &FSPS); - ReabstractionInfo() {} public: + ReabstractionInfo() {} + /// Constructs the ReabstractionInfo for generic function \p Callee with /// substitutions \p ParamSubs. /// If specialization is not possible getSpecializedType() will return an @@ -164,7 +167,10 @@ class ReabstractionInfo { /// Constructs the ReabstractionInfo for generic function \p Callee with /// a specialization signature. ReabstractionInfo(ModuleDecl *targetModule, bool isModuleWholeModule, - SILFunction *Callee, GenericSignature SpecializedSig); + SILFunction *Callee, GenericSignature SpecializedSig, + bool isPrespecialization = false); + + bool isPrespecialized() const { return isPrespecialization; } IsSerialized_t isSerialized() const { return Serialized; @@ -306,17 +312,17 @@ class GenericFuncSpecializer { SILFunction *lookupSpecialization(); /// Return a newly created specialized function. - SILFunction *tryCreateSpecialization(); + SILFunction *tryCreateSpecialization(bool forcePrespecialization = false); /// Try to specialize GenericFunc given a list of ParamSubs. /// Returns either a new or existing specialized function, or nullptr. - SILFunction *trySpecialization() { + SILFunction *trySpecialization(bool forcePrespecialization = false) { if (!ReInfo.getSpecializedType()) return nullptr; SILFunction *SpecializedF = lookupSpecialization(); if (!SpecializedF) - SpecializedF = tryCreateSpecialization(); + SpecializedF = tryCreateSpecialization(forcePrespecialization); return SpecializedF; } diff --git a/include/swift/SILOptimizer/Utils/InstOptUtils.h b/include/swift/SILOptimizer/Utils/InstOptUtils.h index 86c1ab802541f..209c154534e8f 100644 --- a/include/swift/SILOptimizer/Utils/InstOptUtils.h +++ b/include/swift/SILOptimizer/Utils/InstOptUtils.h @@ -357,6 +357,11 @@ void getConsumedPartialApplyArgs(PartialApplyInst *pai, SmallVectorImpl &argOperands, bool includeTrivialAddrArgs); +/// Emit destroy operation for \p operand, and call appropriate functions from +/// \p callbacks for newly created instructions and deleted instructions. +void emitDestroyOperation(SILBuilder &builder, SILLocation loc, + SILValue operand, InstModCallbacks callbacks); + /// Collect all (transitive) users of \p inst which just copy or destroy \p /// inst. /// @@ -366,6 +371,7 @@ void getConsumedPartialApplyArgs(PartialApplyInst *pai, /// destroys, i.e. if \p inst can be considered as "dead". bool collectDestroys(SingleValueInstruction *inst, SmallVectorImpl &destroys); + /// If Closure is a partial_apply or thin_to_thick_function with only local /// ref count users and a set of post-dominating releases: /// diff --git a/include/swift/SILOptimizer/Utils/SCCVisitor.h b/include/swift/SILOptimizer/Utils/SCCVisitor.h index f505506765dea..e5f2dd665b201 100644 --- a/include/swift/SILOptimizer/Utils/SCCVisitor.h +++ b/include/swift/SILOptimizer/Utils/SCCVisitor.h @@ -116,6 +116,12 @@ class SCCVisitor { Operands.push_back(CBI->getFalseArgs()[Index]); return; } + + case TermKind::AwaitAsyncContinuationInst: { + auto *AACI = cast(Term); + Operands.push_back(AACI->getOperand()); + return; + } case TermKind::SwitchEnumInst: case TermKind::SwitchEnumAddrInst: diff --git a/include/swift/SILOptimizer/Utils/SpecializationMangler.h b/include/swift/SILOptimizer/Utils/SpecializationMangler.h index 6006521020b3a..24db9306381aa 100644 --- a/include/swift/SILOptimizer/Utils/SpecializationMangler.h +++ b/include/swift/SILOptimizer/Utils/SpecializationMangler.h @@ -13,6 +13,8 @@ #ifndef SWIFT_SILOPTIMIZER_UTILS_SPECIALIZATIONMANGLER_H #define SWIFT_SILOPTIMIZER_UTILS_SPECIALIZATIONMANGLER_H +#include "swift/SIL/GenericSpecializationMangler.h" + #include "swift/Demangling/Demangler.h" #include "swift/Demangling/NamespaceMacros.h" #include "swift/Basic/NullablePtr.h" @@ -24,65 +26,6 @@ namespace swift { namespace Mangle { SWIFT_BEGIN_INLINE_NAMESPACE -enum class SpecializationKind : uint8_t { - Generic, - NotReAbstractedGeneric, - FunctionSignature, -}; - -/// Inject SpecializationPass into the Mangle namespace. -using SpecializationPass = Demangle::SpecializationPass; - -/// The base class for specialization mangles. -class SpecializationMangler : public Mangle::ASTMangler { -protected: - /// The specialization pass. - SpecializationPass Pass; - - IsSerialized_t Serialized; - - /// The original function which is specialized. - SILFunction *Function; - - llvm::SmallVector ArgOpStorage; - llvm::raw_svector_ostream ArgOpBuffer; - -protected: - SpecializationMangler(SpecializationPass P, IsSerialized_t Serialized, - SILFunction *F) - : Pass(P), Serialized(Serialized), Function(F), - ArgOpBuffer(ArgOpStorage) {} - - SILFunction *getFunction() const { return Function; } - - void beginMangling(); - - /// Finish the mangling of the symbol and return the mangled name. - std::string finalize(); - - void appendSpecializationOperator(StringRef Op) { - appendOperator(Op, StringRef(ArgOpStorage.data(), ArgOpStorage.size())); - } -}; - -// The mangler for specialized generic functions. -class GenericSpecializationMangler : public SpecializationMangler { - - SubstitutionMap SubMap; - bool isReAbstracted; - bool isInlined; - -public: - GenericSpecializationMangler(SILFunction *F, SubstitutionMap SubMap, - IsSerialized_t Serialized, bool isReAbstracted, - bool isInlined = false) - : SpecializationMangler(SpecializationPass::GenericSpecializer, - Serialized, F), - SubMap(SubMap), isReAbstracted(isReAbstracted), isInlined(isInlined) {} - - std::string mangle(GenericSignature Sig = GenericSignature()); -}; - class PartialSpecializationMangler : public SpecializationMangler { CanSILFunctionType SpecializedFnTy; diff --git a/include/swift/SILOptimizer/Utils/ValueLifetime.h b/include/swift/SILOptimizer/Utils/ValueLifetime.h index 92af2083ef62f..9fe60c1bbfb43 100644 --- a/include/swift/SILOptimizer/Utils/ValueLifetime.h +++ b/include/swift/SILOptimizer/Utils/ValueLifetime.h @@ -17,8 +17,9 @@ #ifndef SWIFT_SILOPTIMIZER_UTILS_CFG_H #define SWIFT_SILOPTIMIZER_UTILS_CFG_H -#include "swift/SIL/SILInstruction.h" #include "swift/SIL/SILBuilder.h" +#include "swift/SIL/SILInstruction.h" +#include "swift/SILOptimizer/Utils/InstOptUtils.h" namespace swift { @@ -159,7 +160,8 @@ class ValueLifetimeAnalysis { /// destroy_value at each instruction of the \p frontier. void endLifetimeAtFrontier(SILValue valueOrStackLoc, const ValueLifetimeAnalysis::Frontier &frontier, - SILBuilderContext &builderCtxt); + SILBuilderContext &builderCtxt, + InstModCallbacks callbacks); } // end namespace swift diff --git a/lib/Sema/CSFix.h b/include/swift/Sema/CSFix.h similarity index 96% rename from lib/Sema/CSFix.h rename to include/swift/Sema/CSFix.h index f03f2d0c6ebd2..f65cbaf9c702a 100644 --- a/lib/Sema/CSFix.h +++ b/include/swift/Sema/CSFix.h @@ -17,7 +17,6 @@ #ifndef SWIFT_SEMA_CSFIX_H #define SWIFT_SEMA_CSFIX_H -#include "ConstraintLocator.h" #include "swift/AST/ASTNode.h" #include "swift/AST/Decl.h" #include "swift/AST/Expr.h" @@ -25,6 +24,7 @@ #include "swift/AST/Type.h" #include "swift/AST/Types.h" #include "swift/Basic/Debug.h" +#include "swift/Sema/ConstraintLocator.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/TrailingObjects.h" @@ -193,9 +193,8 @@ enum class FixKind : uint8_t { DefaultGenericArgument, /// Skip any unhandled constructs that occur within a closure argument that - /// matches up with a - /// parameter that has a function builder. - SkipUnhandledConstructInFunctionBuilder, + /// matches up with a parameter that has a result builder. + SkipUnhandledConstructInResultBuilder, /// Allow invalid reference to a member declared as `mutating` /// when base is an r-value type. @@ -277,8 +276,11 @@ enum class FixKind : uint8_t { /// Allow key path expressions with no components. AllowKeyPathWithoutComponents, - /// Ignore function builder body which fails `pre-check` call. - IgnoreInvalidFunctionBuilderBody, + /// Ignore result builder body which fails `pre-check` call. + IgnoreInvalidResultBuilderBody, + + /// Resolve type of `nil` by providing a contextual type. + SpecifyContextualTypeForNil, }; class ConstraintFix { @@ -1175,18 +1177,21 @@ class AllowClosureParamDestructuring final : public ConstraintFix { ConstraintLocator *locator); }; +struct SynthesizedArg { + unsigned paramIdx; + AnyFunctionType::Param param; +}; + class AddMissingArguments final : public ConstraintFix, private llvm::TrailingObjects< - AddMissingArguments, std::pair> { + AddMissingArguments, SynthesizedArg> { friend TrailingObjects; - using SynthesizedParam = std::pair; - unsigned NumSynthesized; AddMissingArguments(ConstraintSystem &cs, - ArrayRef synthesizedArgs, + ArrayRef synthesizedArgs, ConstraintLocator *locator) : ConstraintFix(cs, FixKind::AddMissingArguments, locator), NumSynthesized(synthesizedArgs.size()) { @@ -1197,8 +1202,8 @@ class AddMissingArguments final public: std::string getName() const override { return "synthesize missing argument(s)"; } - ArrayRef getSynthesizedArguments() const { - return {getTrailingObjects(), NumSynthesized}; + ArrayRef getSynthesizedArguments() const { + return {getTrailingObjects(), NumSynthesized}; } bool diagnose(const Solution &solution, bool asNote = false) const override; @@ -1208,12 +1213,16 @@ class AddMissingArguments final } static AddMissingArguments *create(ConstraintSystem &cs, - ArrayRef synthesizedArgs, + ArrayRef synthesizedArgs, ConstraintLocator *locator); + static bool classof(const ConstraintFix *fix) { + return fix->getKind() == FixKind::AddMissingArguments; + } + private: - MutableArrayRef getSynthesizedArgumentsBuf() { - return {getTrailingObjects(), NumSynthesized}; + MutableArrayRef getSynthesizedArgumentsBuf() { + return {getTrailingObjects(), NumSynthesized}; } }; @@ -1490,7 +1499,7 @@ class DefaultGenericArgument final : public ConstraintFix { ConstraintLocator *locator); }; -class SkipUnhandledConstructInFunctionBuilder final : public ConstraintFix { +class SkipUnhandledConstructInResultBuilder final : public ConstraintFix { public: using UnhandledNode = llvm::PointerUnion; @@ -1498,22 +1507,22 @@ class SkipUnhandledConstructInFunctionBuilder final : public ConstraintFix { UnhandledNode unhandled; NominalTypeDecl *builder; - SkipUnhandledConstructInFunctionBuilder(ConstraintSystem &cs, + SkipUnhandledConstructInResultBuilder(ConstraintSystem &cs, UnhandledNode unhandled, NominalTypeDecl *builder, ConstraintLocator *locator) - : ConstraintFix(cs, FixKind::SkipUnhandledConstructInFunctionBuilder, + : ConstraintFix(cs, FixKind::SkipUnhandledConstructInResultBuilder, locator), unhandled(unhandled), builder(builder) { } public: std::string getName() const override { - return "skip unhandled constructs when applying a function builder"; + return "skip unhandled constructs when applying a result builder"; } bool diagnose(const Solution &solution, bool asNote = false) const override; - static SkipUnhandledConstructInFunctionBuilder * + static SkipUnhandledConstructInResultBuilder * create(ConstraintSystem &cs, UnhandledNode unhandledNode, NominalTypeDecl *builder, ConstraintLocator *locator); }; @@ -1987,7 +1996,7 @@ class AllowKeyPathWithoutComponents final : public ConstraintFix { ConstraintLocator *locator); }; -class IgnoreInvalidFunctionBuilderBody final : public ConstraintFix { +class IgnoreInvalidResultBuilderBody final : public ConstraintFix { enum class ErrorInPhase { PreCheck, ConstraintGeneration, @@ -1995,14 +2004,14 @@ class IgnoreInvalidFunctionBuilderBody final : public ConstraintFix { ErrorInPhase Phase; - IgnoreInvalidFunctionBuilderBody(ConstraintSystem &cs, ErrorInPhase phase, + IgnoreInvalidResultBuilderBody(ConstraintSystem &cs, ErrorInPhase phase, ConstraintLocator *locator) - : ConstraintFix(cs, FixKind::IgnoreInvalidFunctionBuilderBody, locator), + : ConstraintFix(cs, FixKind::IgnoreInvalidResultBuilderBody, locator), Phase(phase) {} public: std::string getName() const override { - return "ignore invalid function builder body"; + return "ignore invalid result builder body"; } bool diagnose(const Solution &solution, bool asNote = false) const override; @@ -2011,21 +2020,41 @@ class IgnoreInvalidFunctionBuilderBody final : public ConstraintFix { return diagnose(*commonFixes.front().first); } - static IgnoreInvalidFunctionBuilderBody * + static IgnoreInvalidResultBuilderBody * duringPreCheck(ConstraintSystem &cs, ConstraintLocator *locator) { return create(cs, ErrorInPhase::PreCheck, locator); } - static IgnoreInvalidFunctionBuilderBody * + static IgnoreInvalidResultBuilderBody * duringConstraintGeneration(ConstraintSystem &cs, ConstraintLocator *locator) { return create(cs, ErrorInPhase::ConstraintGeneration, locator); } private: - static IgnoreInvalidFunctionBuilderBody * + static IgnoreInvalidResultBuilderBody * create(ConstraintSystem &cs, ErrorInPhase phase, ConstraintLocator *locator); }; +class SpecifyContextualTypeForNil final : public ConstraintFix { + SpecifyContextualTypeForNil(ConstraintSystem &cs, + ConstraintLocator *locator) + : ConstraintFix(cs, FixKind::SpecifyContextualTypeForNil, locator) {} + +public: + std::string getName() const override { + return "specify contextual type to resolve `nil` literal"; + } + + bool diagnose(const Solution &solution, bool asNote = false) const override; + + bool diagnoseForAmbiguity(CommonFixesArray commonFixes) const override { + return diagnose(*commonFixes.front().first); + } + + static SpecifyContextualTypeForNil *create(ConstraintSystem & cs, + ConstraintLocator * locator); +}; + } // end namespace constraints } // end namespace swift diff --git a/lib/Sema/Constraint.h b/include/swift/Sema/Constraint.h similarity index 99% rename from lib/Sema/Constraint.h rename to include/swift/Sema/Constraint.h index 5604145a13bfb..5eb4ab14a0f3a 100644 --- a/lib/Sema/Constraint.h +++ b/include/swift/Sema/Constraint.h @@ -18,12 +18,11 @@ #ifndef SWIFT_SEMA_CONSTRAINT_H #define SWIFT_SEMA_CONSTRAINT_H -#include "CSFix.h" -#include "OverloadChoice.h" #include "swift/AST/FunctionRefKind.h" #include "swift/AST/Identifier.h" #include "swift/AST/Type.h" #include "swift/Basic/Debug.h" +#include "swift/Sema/OverloadChoice.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/ilist.h" #include "llvm/ADT/ilist_node.h" @@ -43,6 +42,7 @@ class TypeVariableType; namespace constraints { +class ConstraintFix; class ConstraintLocator; class ConstraintSystem; enum class TrailingClosureMatching; diff --git a/lib/Sema/ConstraintGraph.h b/include/swift/Sema/ConstraintGraph.h similarity index 100% rename from lib/Sema/ConstraintGraph.h rename to include/swift/Sema/ConstraintGraph.h diff --git a/lib/Sema/ConstraintGraphScope.h b/include/swift/Sema/ConstraintGraphScope.h similarity index 100% rename from lib/Sema/ConstraintGraphScope.h rename to include/swift/Sema/ConstraintGraphScope.h diff --git a/lib/Sema/ConstraintLocator.h b/include/swift/Sema/ConstraintLocator.h similarity index 97% rename from lib/Sema/ConstraintLocator.h rename to include/swift/Sema/ConstraintLocator.h index fd5b07a155675..2d1b01586a2ed 100644 --- a/lib/Sema/ConstraintLocator.h +++ b/include/swift/Sema/ConstraintLocator.h @@ -139,10 +139,6 @@ class ConstraintLocator : public llvm::FoldingSetNode { bool isClosureResult() const { return getKind() == PathElementKind::ClosureResult; } - - /// Determine whether this element points to the contextual type - /// associated with result of a single expression function. - bool isResultOfSingleExprFunction() const; }; /// Return the summary flags for an entire path. @@ -226,8 +222,8 @@ class ConstraintLocator : public llvm::FoldingSetNode { /// Determine whether this locator points to the `try?` expression. bool isForOptionalTry() const; - /// Determine whether this locator is for a function builder body result type. - bool isForFunctionBuilderBodyResult() const; + /// Determine whether this locator is for a result builder body result type. + bool isForResultBuilderBodyResult() const; /// Determine whether this locator points directly to a given expression. template bool directlyAt() const { @@ -647,20 +643,6 @@ class LocatorPathElt::ClosureBody final : public StoredIntegerElement<1> { } }; -class LocatorPathElt::ContextualType final : public StoredIntegerElement<1> { -public: - ContextualType(bool isForSingleExprFunction = false) - : StoredIntegerElement(ConstraintLocator::ContextualType, isForSingleExprFunction) {} - - /// Whether this element points to the contextual type associated with the - /// result of a single expression function. - bool isForSingleExprFunction() const { return bool(getValue()); } - - static bool classof(const LocatorPathElt *elt) { - return elt->getKind() == ConstraintLocator::ContextualType; - } -}; - class LocatorPathElt::Witness final : public StoredPointerElement { public: Witness(ValueDecl *witness) diff --git a/lib/Sema/ConstraintLocatorPathElts.def b/include/swift/Sema/ConstraintLocatorPathElts.def similarity index 96% rename from lib/Sema/ConstraintLocatorPathElts.def rename to include/swift/Sema/ConstraintLocatorPathElts.def index 435d20b74ec9f..cb557f8496408 100644 --- a/lib/Sema/ConstraintLocatorPathElts.def +++ b/include/swift/Sema/ConstraintLocatorPathElts.def @@ -61,7 +61,7 @@ CUSTOM_LOCATOR_PATH_ELT(ClosureBody) SIMPLE_LOCATOR_PATH_ELT(ConstructorMember) /// The desired contextual type passed in to the constraint system. -CUSTOM_LOCATOR_PATH_ELT(ContextualType) +SIMPLE_LOCATOR_PATH_ELT(ContextualType) /// A result of an expression involving dynamic lookup. SIMPLE_LOCATOR_PATH_ELT(DynamicLookupResult) @@ -75,8 +75,8 @@ SIMPLE_LOCATOR_PATH_ELT(FunctionArgument) /// The result type of a function. SIMPLE_LOCATOR_PATH_ELT(FunctionResult) -/// The result type of a function builder body. -SIMPLE_LOCATOR_PATH_ELT(FunctionBuilderBodyResult) +/// The result type of a result builder body. +SIMPLE_LOCATOR_PATH_ELT(ResultBuilderBodyResult) /// A generic argument. /// FIXME: Add support for named generic arguments? @@ -147,8 +147,8 @@ ABSTRACT_LOCATOR_PATH_ELT(AnyRequirement) /// A single requirement placed on the type parameters. CUSTOM_LOCATOR_PATH_ELT(TypeParameterRequirement) -/// RValue adjustment. -SIMPLE_LOCATOR_PATH_ELT(RValueAdjustment) +/// Access to `.dynamicType` element +SIMPLE_LOCATOR_PATH_ELT(DynamicType) /// The element type of a sequence in a for ... in ... loop. SIMPLE_LOCATOR_PATH_ELT(SequenceElementType) diff --git a/lib/Sema/ConstraintSolverStats.def b/include/swift/Sema/ConstraintSolverStats.def similarity index 100% rename from lib/Sema/ConstraintSolverStats.def rename to include/swift/Sema/ConstraintSolverStats.def diff --git a/lib/Sema/ConstraintSystem.h b/include/swift/Sema/ConstraintSystem.h similarity index 96% rename from lib/Sema/ConstraintSystem.h rename to include/swift/Sema/ConstraintSystem.h index d7e24ecbf94a1..543582d078485 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/include/swift/Sema/ConstraintSystem.h @@ -18,22 +18,26 @@ #ifndef SWIFT_SEMA_CONSTRAINT_SYSTEM_H #define SWIFT_SEMA_CONSTRAINT_SYSTEM_H -#include "CSFix.h" -#include "Constraint.h" -#include "ConstraintGraph.h" -#include "ConstraintGraphScope.h" -#include "ConstraintLocator.h" -#include "OverloadChoice.h" -#include "TypeChecker.h" +#include "swift/AST/ASTContext.h" #include "swift/AST/ASTNode.h" #include "swift/AST/ASTVisitor.h" #include "swift/AST/ASTWalker.h" +#include "swift/AST/AnyFunctionRef.h" +#include "swift/AST/DiagnosticsSema.h" #include "swift/AST/NameLookup.h" #include "swift/AST/PropertyWrappers.h" #include "swift/AST/Types.h" #include "swift/Basic/Debug.h" #include "swift/Basic/LLVM.h" #include "swift/Basic/OptionSet.h" +#include "swift/Sema/Constraint.h" +#include "swift/Sema/ConstraintGraph.h" +#include "swift/Sema/ConstraintGraphScope.h" +#include "swift/Sema/ConstraintLocator.h" +#include "swift/Sema/CSFix.h" +#include "swift/Sema/OverloadChoice.h" +#include "swift/Sema/SolutionResult.h" +#include "llvm/ADT/MapVector.h" #include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SetOperations.h" @@ -49,15 +53,38 @@ namespace swift { class Expr; +class FuncDecl; +class BraseStmt; +enum class TypeCheckExprFlags; namespace constraints { class ConstraintGraph; class ConstraintGraphNode; class ConstraintSystem; +class SolutionApplicationTarget; } // end namespace constraints +namespace unittest { + +class SemaTest; + +} // end namespace unittest + +// Forward declare some TypeChecker related functions +// so they could be made friends of ConstraintSystem. +namespace TypeChecker { + +Optional applyResultBuilderBodyTransform(FuncDecl *func, + Type builderType); + +Optional +typeCheckExpression(constraints::SolutionApplicationTarget &target, + OptionSet options); + +} // end namespace TypeChecker + } // end namespace swift /// Allocate memory within the given constraint system. @@ -66,6 +93,57 @@ void *operator new(size_t bytes, swift::constraints::ConstraintSystem& cs, namespace swift { +/// This specifies the purpose of the contextual type, when specified to +/// typeCheckExpression. This is used for diagnostic generation to produce more +/// specified error messages when the conversion fails. +/// +enum ContextualTypePurpose { + CTP_Unused, ///< No contextual type is specified. + CTP_Initialization, ///< Pattern binding initialization. + CTP_ReturnStmt, ///< Value specified to a 'return' statement. + CTP_ReturnSingleExpr, ///< Value implicitly returned from a function. + CTP_YieldByValue, ///< By-value yield operand. + CTP_YieldByReference, ///< By-reference yield operand. + CTP_ThrowStmt, ///< Value specified to a 'throw' statement. + CTP_EnumCaseRawValue, ///< Raw value specified for "case X = 42" in enum. + CTP_DefaultParameter, ///< Default value in parameter 'foo(a : Int = 42)'. + + /// Default value in @autoclosure parameter + /// 'foo(a : @autoclosure () -> Int = 42)'. + CTP_AutoclosureDefaultParameter, + + CTP_CalleeResult, ///< Constraint is placed on the result of a callee. + CTP_CallArgument, ///< Call to function or operator requires type. + CTP_ClosureResult, ///< Closure result expects a specific type. + CTP_ArrayElement, ///< ArrayExpr wants elements to have a specific type. + CTP_DictionaryKey, ///< DictionaryExpr keys should have a specific type. + CTP_DictionaryValue, ///< DictionaryExpr values should have a specific type. + CTP_CoerceOperand, ///< CoerceExpr operand coerced to specific type. + CTP_AssignSource, ///< AssignExpr source operand coerced to result type. + CTP_SubscriptAssignSource, ///< AssignExpr source operand coerced to subscript + ///< result type. + CTP_Condition, ///< Condition expression of various statements e.g. + ///< `if`, `for`, `while` etc. + CTP_ForEachStmt, ///< "expression/sequence" associated with 'for-in' loop + ///< is expected to conform to 'Sequence' protocol. + CTP_WrappedProperty, ///< Property type expected to match 'wrappedValue' type + CTP_ComposedPropertyWrapper, ///< Composed wrapper type expected to match + ///< former 'wrappedValue' type + + CTP_CannotFail, ///< Conversion can never fail. abort() if it does. +}; + +/// Specify how we handle the binding of underconstrained (free) type variables +/// within a solution to a constraint system. +enum class FreeTypeVariableBinding { + /// Disallow any binding of such free type variables. + Disallow, + /// Allow the free type variables to persist in the solution. + Allow, + /// Bind the type variables to UnresolvedType to represent the ambiguity. + UnresolvedType +}; + namespace constraints { /// Describes the algorithm to use for trailing closure matching. @@ -732,7 +810,7 @@ enum ScoreKind { /// The number of score kinds. const unsigned NumScoreKinds = SK_LastScoreKind + 1; -/// Describes what happened when a function builder transform was applied +/// Describes what happened when a result builder transform was applied /// to a particular closure. struct AppliedBuilderTransform { /// The builder type that was applied to the closure. @@ -857,6 +935,14 @@ struct ContextualTypeInfo { TypeLoc typeLoc; ContextualTypePurpose purpose; + ContextualTypeInfo() : typeLoc(TypeLoc()), purpose(CTP_Unused) {} + + ContextualTypeInfo(Type contextualTy, ContextualTypePurpose purpose) + : typeLoc(TypeLoc::withoutLoc(contextualTy)), purpose(purpose) {} + + ContextualTypeInfo(TypeLoc typeLoc, ContextualTypePurpose purpose) + : typeLoc(typeLoc), purpose(purpose) {} + Type getType() const { return typeLoc.getType(); } }; @@ -1098,9 +1184,9 @@ class Solution { std::vector> Conformances; - /// The set of functions that have been transformed by a function builder. + /// The set of functions that have been transformed by a result builder. llvm::MapVector - functionBuilderTransformed; + resultBuilderTransformed; /// Simplify the given type by substituting all occurrences of /// type variables for their fixed types. @@ -1214,8 +1300,8 @@ class Solution { /// Retrieve the builder transform that was applied to this function, if any. const AppliedBuilderTransform *getAppliedBuilderTransform( AnyFunctionRef fn) const { - auto known = functionBuilderTransformed.find(fn); - return known != functionBuilderTransformed.end() + auto known = resultBuilderTransformed.find(fn); + return known != resultBuilderTransformed.end() ? &known->second : nullptr; } @@ -1827,6 +1913,7 @@ class SolutionApplicationTarget { case Kind::patternBinding: return patternBinding; } + llvm_unreachable("invalid case label type"); } VarDecl *getAsUninitializedWrappedVar() const { @@ -1841,6 +1928,7 @@ class SolutionApplicationTarget { case Kind::uninitializedWrappedVar: return uninitializedWrappedVar; } + llvm_unreachable("invalid case label type"); } BraceStmt *getFunctionBody() const { @@ -1936,6 +2024,8 @@ enum class SolutionApplicationToFunctionResult { class ConstraintSystem { ASTContext &Context; + friend class swift::unittest::SemaTest; + public: DeclContext *DC; ConstraintSystemOptions Options; @@ -2035,12 +2125,12 @@ class ConstraintSystem { /// from declared parameters/result and body. llvm::MapVector ClosureTypes; - /// This is a *global* list of all function builder bodies that have + /// This is a *global* list of all result builder bodies that have /// been determined to be incorrect by failing constraint generation. /// /// Tracking this information is useful to avoid producing duplicate - /// diagnostics when function builder has multiple overloads. - llvm::SmallDenseSet InvalidFunctionBuilderBodies; + /// diagnostics when result builder has multiple overloads. + llvm::SmallDenseSet InvalidResultBuilderBodies; /// Maps node types used within all portions of the constraint /// system, instead of directly using the types on the @@ -2128,9 +2218,9 @@ class ConstraintSystem { std::vector> CheckedConformances; - /// The set of functions that have been transformed by a function builder. + /// The set of functions that have been transformed by a result builder. std::vector> - functionBuilderTransformed; + resultBuilderTransformed; /// Cache of the effects any closures visited. llvm::SmallDenseMap closureEffectsCache; @@ -2465,7 +2555,7 @@ class ConstraintSystem { case ConstraintSystemPhase::Solving: // We can come back to constraint generation phase while - // processing function builder body. + // processing result builder body. assert(newPhase == ConstraintSystemPhase::ConstraintGeneration || newPhase == ConstraintSystemPhase::Diagnostics || newPhase == ConstraintSystemPhase::Finalization); @@ -2597,7 +2687,7 @@ class ConstraintSystem { unsigned numFavoredConstraints; - unsigned numFunctionBuilderTransformed; + unsigned numResultBuilderTransformed; /// The length of \c ResolvedOverloads. unsigned numResolvedOverloads; @@ -2669,11 +2759,12 @@ class ConstraintSystem { // FIXME: Perhaps these belong on ConstraintSystem itself. friend Optional - swift::TypeChecker::applyFunctionBuilderBodyTransform(FuncDecl *func, + swift::TypeChecker::applyResultBuilderBodyTransform(FuncDecl *func, Type builderType); + friend Optional - swift::TypeChecker::typeCheckExpression(SolutionApplicationTarget &target, - TypeCheckExprOptions options); + swift::TypeChecker::typeCheckExpression( + SolutionApplicationTarget &target, OptionSet options); /// Emit the fixes computed as part of the solution, returning true if we were /// able to emit an error message, or false if none of the fixits worked out. @@ -3077,8 +3168,7 @@ class ConstraintSystem { /// subsequent solution would be worse than the best known solution. bool recordFix(ConstraintFix *fix, unsigned impact = 1); - void recordPotentialHole(TypeVariableType *typeVar); - void recordPotentialHole(FunctionType *fnType); + void recordPotentialHole(Type type); void recordTrailingClosureMatch( ConstraintLocator *locator, @@ -4513,11 +4603,11 @@ class ConstraintSystem { /// Simplify the given disjunction choice. void simplifyDisjunctionChoice(Constraint *choice); - /// Apply the given function builder to the closure expression. + /// Apply the given result builder to the closure expression. /// - /// \returns \c None when the function builder cannot be applied at all, - /// otherwise the result of applying the function builder. - Optional matchFunctionBuilder( + /// \returns \c None when the result builder cannot be applied at all, + /// otherwise the result of applying the result builder. + Optional matchResultBuilder( AnyFunctionRef fn, Type builderType, Type bodyResultType, ConstraintKind bodyResultConstraintKind, ConstraintLocatorBuilder locator); @@ -4623,7 +4713,11 @@ class ConstraintSystem { SmallVector Bindings; /// The set of protocol requirements placed on this type variable. - llvm::TinyPtrVector Protocols; + llvm::SmallVector Protocols; + + /// The set of transitive protocol requirements inferred through + /// subtype/conversion/equivalence relations with other type variables. + Optional> TransitiveProtocols; /// The set of constraints which would be used to infer default types. llvm::TinyPtrVector Defaults; @@ -4644,6 +4738,8 @@ class ConstraintSystem { /// `bind param` are present in the system. bool PotentiallyIncomplete = false; + ASTNode AssociatedCodeCompletionToken = ASTNode(); + /// Whether this type variable has literal bindings. LiteralBindingKind LiteralBinding = LiteralBindingKind::None; @@ -4656,15 +4752,13 @@ class ConstraintSystem { /// Tracks the position of the last known supertype in the group. Optional lastSupertypeIndex; - /// A set of all constraints which contribute to pontential bindings. - llvm::SmallPtrSet Sources; - /// A set of all not-yet-resolved type variables this type variable - /// is a subtype of. This is used to determine ordering inside a - /// chain of subtypes because binding inference algorithm can't, - /// at the moment, determine bindings transitively through supertype - /// type variables. - llvm::SmallPtrSet SubtypeOf; + /// is a subtype of, supertype of or is equivalent to. This is used + /// to determine ordering inside of a chain of subtypes to help infer + /// transitive bindings and protocol requirements. + llvm::SmallMapVector SubtypeOf; + llvm::SmallMapVector SupertypeOf; + llvm::SmallMapVector EquivalentTo; PotentialBindings(TypeVariableType *typeVar) : TypeVar(typeVar), PotentiallyIncomplete(isGenericParameter()) {} @@ -4709,10 +4803,10 @@ class ConstraintSystem { // This is required because algorithm can't currently infer // bindings for subtype transitively through superclass ones. if (!(x.IsHole && y.IsHole)) { - if (x.SubtypeOf.count(y.TypeVar)) + if (x.isSubtypeOf(y.TypeVar)) return false; - if (y.SubtypeOf.count(x.TypeVar)) + if (y.isSubtypeOf(x.TypeVar)) return true; } @@ -4758,6 +4852,15 @@ class ConstraintSystem { return false; } + bool isSubtypeOf(TypeVariableType *typeVar) const { + auto result = SubtypeOf.find(typeVar); + if (result == SubtypeOf.end()) + return false; + + auto *constraint = result->second; + return constraint->getKind() == ConstraintKind::Subtype; + } + /// Check if this binding is favored over a disjunction e.g. /// if it has only concrete types or would resolve a closure. bool favoredOverDisjunction(Constraint *disjunction) const; @@ -4775,27 +4878,36 @@ class ConstraintSystem { /// \param inferredBindings The set of all bindings inferred for type /// variables in the workset. void inferTransitiveBindings( - const ConstraintSystem &cs, + ConstraintSystem &cs, llvm::SmallPtrSetImpl &existingTypes, const llvm::SmallDenseMap &inferredBindings); + /// Detect subtype, conversion or equivalence relationship + /// between two type variables and attempt to propagate protocol + /// requirements down the subtype or equivalence chain. + void inferTransitiveProtocolRequirements( + const ConstraintSystem &cs, + llvm::SmallDenseMap + &inferredBindings); + /// Infer bindings based on any protocol conformances that have default /// types. - void inferDefaultTypes(const ConstraintSystem &cs, + void inferDefaultTypes(ConstraintSystem &cs, llvm::SmallPtrSetImpl &existingTypes); public: - bool infer(const ConstraintSystem &cs, + bool infer(ConstraintSystem &cs, llvm::SmallPtrSetImpl &exactTypes, Constraint *constraint); /// Finalize binding computation for this type variable by /// inferring bindings from context e.g. transitive bindings. - void finalize(const ConstraintSystem &cs, - const llvm::SmallDenseMap + void finalize(ConstraintSystem &cs, + llvm::SmallDenseMap &inferredBindings); void dump(llvm::raw_ostream &out, @@ -4859,16 +4971,16 @@ class ConstraintSystem { Optional checkTypeOfBinding(TypeVariableType *typeVar, Type type) const; Optional determineBestBindings(); +public: /// Infer bindings for the given type variable based on current /// state of the constraint system. PotentialBindings inferBindingsFor(TypeVariableType *typeVar, - bool finalize = true) const; + bool finalize = true); private: Optional getPotentialBindingForRelationalConstraint(PotentialBindings &result, Constraint *constraint) const; - PotentialBindings getPotentialBindings(TypeVariableType *typeVar) const; /// Add a constraint to the constraint system. SolutionKind addConstraintImpl(ConstraintKind kind, Type first, Type second, @@ -4910,41 +5022,7 @@ class ConstraintSystem { llvm::function_ref pred); bool isReadOnlyKeyPathComponent(const AbstractStorageDecl *storage, - SourceLoc referenceLoc) { - // 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. - return true; - } - } 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. - return true; - } - - // If the setter is unavailable, then the keypath ought to be read-only - // in this context. - if (auto setter = storage->getOpaqueAccessor(AccessorKind::Set)) { - auto maybeUnavail = TypeChecker::checkDeclarationAvailability(setter, - referenceLoc, - DC); - if (maybeUnavail.hasValue()) { - return true; - } - } - - return false; - } + SourceLoc referenceLoc); public: // Given a type variable, attempt to find the disjunction of @@ -4983,8 +5061,6 @@ class ConstraintSystem { /// \returns The selected disjunction. Constraint *selectDisjunction(); - Constraint *selectApplyDisjunction(); - /// Solve the system of constraints generated from provided expression. /// /// \param target The target to generate constraints from. @@ -5244,19 +5320,6 @@ class ConstraintSystem { typedef std::function &options)> PartitionAppendCallback; - // Attempt to sort nominalTypes based on what we can discover about - // calls into the overloads in the disjunction that bindOverload is - // a part of. - void sortDesignatedTypes(SmallVectorImpl &nominalTypes, - Constraint *bindOverload); - - // Partition the choices in a disjunction based on those that match - // the designated types for the operator that the disjunction was - // formed for. - void partitionForDesignatedTypes(ArrayRef Choices, - ConstraintMatchLoop forEachChoice, - PartitionAppendCallback appendPartition); - // Partition the choices in the disjunction into groups that we will // iterate over in an order appropriate to attempt to stop before we // have to visit all of the options. @@ -5344,7 +5407,10 @@ class MatchCallArgumentListener { /// indices. /// /// \param paramIdx The index of the parameter that is missing an argument. - virtual Optional missingArgument(unsigned paramIdx); + /// \param argInsertIdx The index in the argument list where this argument was + /// expected. + virtual Optional missingArgument(unsigned paramIdx, + unsigned argInsertIdx); /// Indicate that there was no label given when one was expected by parameter. /// @@ -5632,6 +5698,11 @@ class TypeVariableBinding { bool attempt(ConstraintSystem &cs) const; + /// Determine what fix (if any) needs to be introduced into a + /// constraint system as part of resolving type variable as a hole. + Optional> + fixForHole(ConstraintSystem &cs) const; + void print(llvm::raw_ostream &Out, SourceManager *) const { PrintOptions PO; PO.PrintTypesForDebugging = true; @@ -5919,7 +5990,7 @@ bool isSIMDOperator(ValueDecl *value); std::string describeGenericType(ValueDecl *GP, bool includeName = false); -/// Apply the given function builder transform within a specific solution +/// Apply the given result builder transform within a specific solution /// to produce the rewritten body. /// /// \param solution The solution to use during application, providing the @@ -5931,7 +6002,7 @@ std::string describeGenericType(ValueDecl *GP, bool includeName = false); /// type-checked version. /// /// \returns the transformed body -BraceStmt *applyFunctionBuilderTransform( +BraceStmt *applyResultBuilderTransform( const constraints::Solution &solution, constraints::AppliedBuilderTransform applied, BraceStmt *body, diff --git a/include/swift/Sema/IDETypeChecking.h b/include/swift/Sema/IDETypeChecking.h index 23ff7e450c6ad..dc8e9cbe3a22a 100644 --- a/include/swift/Sema/IDETypeChecking.h +++ b/include/swift/Sema/IDETypeChecking.h @@ -255,9 +255,9 @@ namespace swift { bool IncludeProtocolRequirements = true, bool Transitive = false); - /// Enumerates the various kinds of "build" functions within a function + /// Enumerates the various kinds of "build" functions within a result /// builder. - enum class FunctionBuilderBuildFunction { + enum class ResultBuilderBuildFunction { BuildBlock, BuildExpression, BuildOptional, @@ -268,21 +268,21 @@ namespace swift { BuildFinalResult, }; - /// Try to infer the component type of a function builder from the type + /// Try to infer the component type of a result builder from the type /// of buildBlock or buildExpression, if it was there. - Type inferFunctionBuilderComponentType(NominalTypeDecl *builder); + Type inferResultBuilderComponentType(NominalTypeDecl *builder); - /// Print the declaration for a function builder "build" function, for use + /// Print the declaration for a result builder "build" function, for use /// in Fix-Its, code completion, and so on. - void printFunctionBuilderBuildFunction( + void printResultBuilderBuildFunction( NominalTypeDecl *builder, Type componentType, - FunctionBuilderBuildFunction function, + ResultBuilderBuildFunction function, Optional stubIndent, llvm::raw_ostream &out); /// Compute the insertion location, indentation string, and component type - /// for a Fix-It that adds a new build* function to a function builder. + /// for a Fix-It that adds a new build* function to a result builder. std::tuple - determineFunctionBuilderBuildFixItInfo(NominalTypeDecl *builder); + determineResultBuilderBuildFixItInfo(NominalTypeDecl *builder); } #endif diff --git a/lib/Sema/OverloadChoice.h b/include/swift/Sema/OverloadChoice.h similarity index 100% rename from lib/Sema/OverloadChoice.h rename to include/swift/Sema/OverloadChoice.h diff --git a/lib/Sema/SolutionResult.h b/include/swift/Sema/SolutionResult.h similarity index 100% rename from lib/Sema/SolutionResult.h rename to include/swift/Sema/SolutionResult.h diff --git a/include/swift/Serialization/ModuleDependencyScanner.h b/include/swift/Serialization/ModuleDependencyScanner.h index 0528b8ccc3364..86b39490435ff 100644 --- a/include/swift/Serialization/ModuleDependencyScanner.h +++ b/include/swift/Serialization/ModuleDependencyScanner.h @@ -40,18 +40,13 @@ namespace swift { public: Optional dependencies; - /// Describes the kind of dependencies this scanner is able to identify - ModuleDependenciesKind dependencyKind; - ModuleDependencyScanner( ASTContext &ctx, ModuleLoadingMode LoadMode, Identifier moduleName, InterfaceSubContextDelegate &astDelegate, - ModuleDependenciesKind dependencyKind = ModuleDependenciesKind::Swift, ScannerKind kind = MDS_plain) : SerializedModuleLoaderBase(ctx, nullptr, LoadMode, /*IgnoreSwiftSourceInfoFile=*/true), - kind(kind), moduleName(moduleName), astDelegate(astDelegate), - dependencyKind(dependencyKind) {} + kind(kind), moduleName(moduleName), astDelegate(astDelegate) {} std::error_code findModuleFilesInDirectory( ImportPath::Element ModuleID, @@ -60,7 +55,7 @@ namespace swift { std::unique_ptr *ModuleBuffer, std::unique_ptr *ModuleDocBuffer, std::unique_ptr *ModuleSourceInfoBuffer, - bool IsFramework) override; + bool IsFramework) override; virtual void collectVisibleTopLevelModuleNames( SmallVectorImpl &names) const override { @@ -106,7 +101,6 @@ namespace swift { StringRef PlaceholderDependencyModuleMap, InterfaceSubContextDelegate &astDelegate) : ModuleDependencyScanner(ctx, LoadMode, moduleName, astDelegate, - ModuleDependenciesKind::SwiftPlaceholder, MDS_placeholder) { // FIXME: Find a better place for this map to live, to avoid diff --git a/include/swift/Serialization/SerializationOptions.h b/include/swift/Serialization/SerializationOptions.h index c252ca20a0a71..1623b00aa38b9 100644 --- a/include/swift/Serialization/SerializationOptions.h +++ b/include/swift/Serialization/SerializationOptions.h @@ -131,6 +131,7 @@ namespace swift { bool SerializeAllSIL = false; bool SerializeOptionsForDebugging = false; bool IsSIB = false; + bool ExperimentalCrossModuleIncrementalInfo = false; }; } // end namespace swift diff --git a/include/swift/Serialization/SerializedModuleLoader.h b/include/swift/Serialization/SerializedModuleLoader.h index f150c8481d1ef..59e813857f324 100644 --- a/include/swift/Serialization/SerializedModuleLoader.h +++ b/include/swift/Serialization/SerializedModuleLoader.h @@ -201,18 +201,6 @@ class SerializedModuleLoaderBase : public ModuleLoader { virtual Optional getModuleDependencies( StringRef moduleName, ModuleDependenciesCache &cache, InterfaceSubContextDelegate &delegate) override; - - virtual std::vector - getCompiledModuleCandidatesForInterface(StringRef moduleName, - StringRef interfacePath) { - return std::vector(); - } - virtual bool tryEmitForwardingModule(StringRef moduleName, - StringRef interfacePath, - ArrayRef candidates, - StringRef outPath) { - return false; - } }; /// Imports serialized Swift modules into an ASTContext. @@ -426,7 +414,7 @@ class SerializedASTFile final : public LoadedFile { virtual void getDisplayDecls(SmallVectorImpl &results) const override; virtual void - getImportedModules(SmallVectorImpl &imports, + getImportedModules(SmallVectorImpl &imports, ModuleDecl::ImportFilter filter) const override; virtual void diff --git a/include/swift/Strings.h b/include/swift/Strings.h index 69b29ff4a52b9..4fa1ca1e20231 100644 --- a/include/swift/Strings.h +++ b/include/swift/Strings.h @@ -22,6 +22,8 @@ namespace swift { constexpr static const StringLiteral STDLIB_NAME = "Swift"; /// The name of the Onone support library, which is a reserved module name. constexpr static const StringLiteral SWIFT_ONONE_SUPPORT = "SwiftOnoneSupport"; +/// The name of the Concurrency module, which supports that extension. +constexpr static const StringLiteral SWIFT_CONCURRENCY_NAME = "_Concurrency"; /// The name of the SwiftShims module, which contains private stdlib decls. constexpr static const StringLiteral SWIFT_SHIMS_NAME = "SwiftShims"; /// The name of the Builtin module, which contains Builtin functions. @@ -113,7 +115,7 @@ constexpr static BuiltinNameStringLiteral BUILTIN_TYPE_NAME_UNSAFEVALUEBUFFER = {"Builtin.UnsafeValueBuffer"}; /// The name of the Builtin type for UnknownObject /// -/// This no longer exists as an AST-accessible type, but it's still used for  +/// This no longer exists as an AST-accessible type, but it's still used for /// fields shaped like AnyObject when ObjC interop is enabled. constexpr static BuiltinNameStringLiteral BUILTIN_TYPE_NAME_UNKNOWNOBJECT = { "Builtin.UnknownObject"}; diff --git a/include/swift/Subsystems.h b/include/swift/Subsystems.h index f474ceeae9366..2ef2942a3e22e 100644 --- a/include/swift/Subsystems.h +++ b/include/swift/Subsystems.h @@ -76,6 +76,10 @@ namespace swift { class TypeConverter; } + namespace fine_grained_dependencies { + class SourceFileDepGraph; + } + /// @{ /// \returns true if the declaration should be verified. This can return @@ -175,8 +179,10 @@ namespace swift { using ModuleOrSourceFile = PointerUnion; /// Serializes a module or single source file to the given output file. - void serialize(ModuleOrSourceFile DC, const SerializationOptions &options, - const SILModule *M = nullptr); + void + serialize(ModuleOrSourceFile DC, const SerializationOptions &options, + const SILModule *M = nullptr, + const fine_grained_dependencies::SourceFileDepGraph *DG = nullptr); /// Serializes a module or single source file to the given output file and /// returns back the file's contents as a memory buffer. diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 34ca1f30e10ad..05ab8667d7edd 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -18,13 +18,13 @@ #include "ClangTypeConverter.h" #include "ForeignRepresentationInfo.h" #include "SubstitutionMapStorage.h" -#include "swift/AST/ForeignAsyncConvention.h" #include "swift/AST/ClangModuleLoader.h" #include "swift/AST/ConcreteDeclRef.h" #include "swift/AST/DiagnosticEngine.h" #include "swift/AST/DiagnosticsSema.h" #include "swift/AST/ExistentialLayout.h" #include "swift/AST/FileUnit.h" +#include "swift/AST/ForeignAsyncConvention.h" #include "swift/AST/ForeignErrorConvention.h" #include "swift/AST/GenericEnvironment.h" #include "swift/AST/GenericSignature.h" @@ -41,19 +41,19 @@ #include "swift/AST/PropertyWrappers.h" #include "swift/AST/ProtocolConformance.h" #include "swift/AST/RawComment.h" +#include "swift/AST/SILLayout.h" #include "swift/AST/SemanticAttrs.h" #include "swift/AST/SourceFile.h" #include "swift/AST/SubstitutionMap.h" -#include "swift/AST/SILLayout.h" #include "swift/AST/TypeCheckRequests.h" #include "swift/Basic/Compiler.h" #include "swift/Basic/SourceManager.h" #include "swift/Basic/Statistic.h" #include "swift/Basic/StringExtras.h" -#include "swift/Syntax/References.h" -#include "swift/Syntax/SyntaxArena.h" #include "swift/Strings.h" #include "swift/Subsystems.h" +#include "swift/Syntax/References.h" +#include "swift/Syntax/SyntaxArena.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/Statistic.h" #include "llvm/ADT/StringMap.h" @@ -206,10 +206,10 @@ struct ASTContext::Implementation { DECL_CLASS *NAME##Decl = nullptr; #include "swift/AST/KnownStdlibTypes.def" -#define KNOWN_OBJC_TYPE_DECL(MODULE, NAME, DECL_CLASS) \ +#define KNOWN_SDK_TYPE_DECL(MODULE, NAME, DECL_CLASS, NUM_GENERIC_PARAMS) \ /** The declaration of MODULE.NAME. */ \ DECL_CLASS *NAME##Decl = nullptr; -#include "swift/AST/KnownObjCTypes.def" +#include "swift/AST/KnownSDKTypes.def" /// The declaration of '+' function for two RangeReplaceableCollection. FuncDecl *PlusFunctionOnRangeReplaceableCollection = nullptr; @@ -270,6 +270,9 @@ struct ASTContext::Implementation { /// The set of known protocols, lazily populated as needed. ProtocolDecl *KnownProtocols[NumKnownProtocols] = { }; + /// The module interface checker owned by the ASTContext. + std::unique_ptr InterfaceChecker; + /// The various module loaders that import external modules into this /// ASTContext. SmallVector, 4> ModuleLoaders; @@ -283,9 +286,6 @@ struct ASTContext::Implementation { /// The module loader used to load Clang modules from DWARF. ClangModuleLoader *TheDWARFModuleLoader = nullptr; - /// The module loader used to load Swift textual interface. - ModuleLoader *TheModuleInterfaceLoader = nullptr; - /// Map from Swift declarations to raw comments. llvm::DenseMap RawComments; @@ -421,6 +421,10 @@ struct ASTContext::Implementation { /// The set of inherited protocol conformances. llvm::FoldingSet InheritedConformances; + /// The set of builtin protocol conformances. + llvm::DenseMap, + BuiltinProtocolConformance *> BuiltinConformances; + /// The set of substitution maps (uniqued by their storage). llvm::FoldingSet SubstitutionMaps; @@ -1053,7 +1057,7 @@ CanType ASTContext::getNeverType() const { return neverDecl->getDeclaredInterfaceType()->getCanonicalType(); } -#define KNOWN_OBJC_TYPE_DECL(MODULE, NAME, DECLTYPE) \ +#define KNOWN_SDK_TYPE_DECL(MODULE, NAME, DECLTYPE, GENERIC_ARGS) \ DECLTYPE *ASTContext::get##NAME##Decl() const { \ if (!getImpl().NAME##Decl) { \ if (ModuleDecl *M = getLoadedModule(Id_##MODULE)) { \ @@ -1064,7 +1068,8 @@ DECLTYPE *ASTContext::get##NAME##Decl() const { \ decls); \ if (decls.size() == 1 && isa(decls[0])) { \ auto decl = cast(decls[0]); \ - if (isa(decl) || decl->getGenericParams() == nullptr) { \ + if (isa(decl) \ + || (bool)decl->getGenericParams() == (bool)GENERIC_ARGS) { \ getImpl().NAME##Decl = decl; \ } \ } \ @@ -1081,7 +1086,7 @@ Type ASTContext::get##NAME##Type() const { \ return decl->getDeclaredInterfaceType(); \ } -#include "swift/AST/KnownObjCTypes.def" +#include "swift/AST/KnownSDKTypes.def" ProtocolDecl *ASTContext::getProtocol(KnownProtocolKind kind) const { // Check whether we've already looked for and cached this protocol. @@ -1103,6 +1108,9 @@ ProtocolDecl *ASTContext::getProtocol(KnownProtocolKind kind) const { case KnownProtocolKind::CFObject: M = getLoadedModule(Id_CoreFoundation); break; + case KnownProtocolKind::Actor: + M = getLoadedModule(Id_Concurrency); + break; // SWIFT_ENABLE_TENSORFLOW // NOTE: The `Differentiable` protocol is in the stdlib module on tensorflow @@ -1687,11 +1695,15 @@ void ASTContext::addModuleLoader(std::unique_ptr loader, if (IsClang && IsDwarf && !getImpl().TheDWARFModuleLoader) getImpl().TheDWARFModuleLoader = static_cast(loader.get()); - if (IsInterface && !getImpl().TheModuleInterfaceLoader) - getImpl().TheModuleInterfaceLoader = loader.get(); getImpl().ModuleLoaders.push_back(std::move(loader)); } +void ASTContext::addModuleInterfaceChecker( + std::unique_ptr checker) { + assert(!getImpl().InterfaceChecker && "Checker has been set already"); + getImpl().InterfaceChecker = std::move(checker); +} + Optional ASTContext::getModuleDependencies( StringRef moduleName, bool isUnderlyingClangModule, ModuleDependenciesCache &cache, InterfaceSubContextDelegate &delegate) { @@ -1778,8 +1790,10 @@ ClangModuleLoader *ASTContext::getDWARFModuleLoader() const { return getImpl().TheDWARFModuleLoader; } -ModuleLoader *ASTContext::getModuleInterfaceLoader() const { - return getImpl().TheModuleInterfaceLoader; +ModuleInterfaceChecker *ASTContext::getModuleInterfaceChecker() const { + auto *result = getImpl().InterfaceChecker.get(); + assert(result); + return result; } ModuleDecl *ASTContext::getLoadedModule( @@ -2172,6 +2186,24 @@ ASTContext::getSelfConformance(ProtocolDecl *protocol) { return entry; } +/// Produce the builtin conformance for some non-nominal to some protocol. +BuiltinProtocolConformance * +ASTContext::getBuiltinConformance(Type type, ProtocolDecl *protocol, + ArrayRef conformances) { + auto key = std::make_pair(type, protocol); + auto &builtinConformances = + getImpl().getArena(AllocationArena::Permanent).BuiltinConformances; + auto &entry = builtinConformances[key]; + if (!entry) { + auto size = BuiltinProtocolConformance:: + totalSizeToAlloc(conformances.size()); + auto mem = this->Allocate(size, alignof(BuiltinProtocolConformance), + AllocationArena::Permanent); + entry = new (mem) BuiltinProtocolConformance(type, protocol, conformances); + } + return entry; +} + /// If one of the ancestor conformances already has a matching type, use /// that instead. static ProtocolConformance *collapseSpecializedConformance( @@ -2188,6 +2220,7 @@ static ProtocolConformance *collapseSpecializedConformance( case ProtocolConformanceKind::Normal: case ProtocolConformanceKind::Inherited: case ProtocolConformanceKind::Self: + case ProtocolConformanceKind::Builtin: // If the conformance matches, return it. if (conformance->getType()->isEqual(type)) { for (auto subConformance : substitutions.getConformances()) @@ -2410,6 +2443,7 @@ size_t ASTContext::Implementation::Arena::getTotalMemory() const { // NormalConformances ? // SpecializedConformances ? // InheritedConformances ? + // BuiltinConformances ? } void AbstractFunctionDecl::setForeignErrorConvention( @@ -4721,6 +4755,21 @@ ASTContext::getCanonicalClangFunctionType( return ty ? ty->getCanonicalTypeInternal().getTypePtr() : nullptr; } +std::unique_ptr +ASTContext::getClangTemplateArguments( + const clang::TemplateParameterList *templateParams, + ArrayRef genericArgs, + SmallVectorImpl &templateArgs) { + auto &impl = getImpl(); + if (!impl.Converter) { + auto *cml = getClangModuleLoader(); + impl.Converter.emplace(*this, cml->getClangASTContext(), LangOpts.Target); + } + + return impl.Converter->getClangTemplateArguments(templateParams, genericArgs, + templateArgs); +} + const Decl * ASTContext::getSwiftDeclForExportedClangDecl(const clang::Decl *decl) { auto &impl = getImpl(); diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index 91b809e881ed5..9228cfb175652 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -21,6 +21,7 @@ #include "swift/AST/ForeignAsyncConvention.h" #include "swift/AST/ForeignErrorConvention.h" #include "swift/AST/GenericEnvironment.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/Initializer.h" #include "swift/AST/ParameterList.h" #include "swift/AST/ProtocolConformance.h" @@ -619,18 +620,17 @@ namespace { OS << " kind=" << getImportKindString(ID->getImportKind()); OS << " '"; - llvm::interleave(ID->getImportPath(), - [&](const ImportPath::Element &Elem) { - OS << Elem.Item; - }, - [&] { OS << '.'; }); + ID->getImportPath().print(OS); OS << "')"; } void visitExtensionDecl(ExtensionDecl *ED) { printCommon(ED, "extension_decl", ExtensionColor); OS << ' '; - ED->getExtendedType().print(OS); + if (ED->hasBeenBound()) + ED->getExtendedType().print(OS); + else + ED->getExtendedTypeRepr()->print(OS); printCommonPost(ED); } @@ -853,20 +853,22 @@ namespace { if (D->isStatic()) PrintWithColorRAII(OS, DeclModifierColor) << " type"; - auto impl = D->getImplInfo(); - PrintWithColorRAII(OS, DeclModifierColor) - << " readImpl=" - << getReadImplKindName(impl.getReadImpl()); - if (!impl.supportsMutation()) { - PrintWithColorRAII(OS, DeclModifierColor) - << " immutable"; - } else { + if (D->hasInterfaceType()) { + auto impl = D->getImplInfo(); PrintWithColorRAII(OS, DeclModifierColor) - << " writeImpl=" - << getWriteImplKindName(impl.getWriteImpl()); - PrintWithColorRAII(OS, DeclModifierColor) - << " readWriteImpl=" - << getReadWriteImplKindName(impl.getReadWriteImpl()); + << " readImpl=" + << getReadImplKindName(impl.getReadImpl()); + if (!impl.supportsMutation()) { + PrintWithColorRAII(OS, DeclModifierColor) + << " immutable"; + } else { + PrintWithColorRAII(OS, DeclModifierColor) + << " writeImpl=" + << getWriteImplKindName(impl.getWriteImpl()); + PrintWithColorRAII(OS, DeclModifierColor) + << " readWriteImpl=" + << getReadWriteImplKindName(impl.getReadWriteImpl()); + } } } @@ -1992,7 +1994,7 @@ class PrintExpr : public ExprVisitor { PrintWithColorRAII(OS, LiteralValueColor) << " builder_init="; E->getBuilderInit().dump(PrintWithColorRAII(OS, LiteralValueColor).getOS()); PrintWithColorRAII(OS, LiteralValueColor) << " result_init="; - E->getResultInit().dump(PrintWithColorRAII(OS, LiteralValueColor).getOS()); + E->getInitializer().dump(PrintWithColorRAII(OS, LiteralValueColor).getOS()); OS << "\n"; printRec(E->getAppendingExpr()); PrintWithColorRAII(OS, ParenthesisColor) << ')'; @@ -3308,6 +3310,10 @@ static void dumpProtocolConformanceRec( visited); break; } + + case ProtocolConformanceKind::Builtin: { + printCommon("builtin"); + } } PrintWithColorRAII(out, ParenthesisColor) << ')'; @@ -3517,6 +3523,8 @@ namespace { printRec("type_variable", typeVar); } else if (auto *VD = originator.dyn_cast()) { VD->dumpRef(PrintWithColorRAII(OS, DeclColor).getOS()); + } else if (auto *EE = originator.dyn_cast()) { + printFlag("error_expr"); } else { printRec("dependent_member_type", originator.get()); diff --git a/lib/AST/ASTMangler.cpp b/lib/AST/ASTMangler.cpp index 8da9843183986..cc9d60109fec2 100644 --- a/lib/AST/ASTMangler.cpp +++ b/lib/AST/ASTMangler.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 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -30,24 +30,27 @@ #include "swift/AST/ProtocolConformanceRef.h" #include "swift/AST/SILLayout.h" #include "swift/Basic/Defer.h" +#include "swift/ClangImporter/ClangImporter.h" +#include "swift/Demangling/Demangler.h" #include "swift/Demangling/ManglingMacros.h" #include "swift/Demangling/ManglingUtils.h" -#include "swift/Demangling/Demangler.h" #include "swift/Strings.h" #include "clang/AST/ASTContext.h" -#include "clang/Basic/CharInfo.h" #include "clang/AST/Attr.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/Mangle.h" +#include "clang/Basic/CharInfo.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Support/CommandLine.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/SaveAndRestore.h" #include "llvm/Support/raw_ostream.h" -#include "llvm/Support/CommandLine.h" + +#include using namespace swift; using namespace swift::Mangle; @@ -197,9 +200,11 @@ std::string ASTMangler::mangleWitnessTable(const RootProtocolConformance *C) { if (isa(C)) { appendProtocolConformance(C); appendOperator("WP"); - } else { + } else if (isa(C)) { appendProtocolName(cast(C)->getProtocol()); appendOperator("WS"); + } else { + llvm_unreachable("mangling unknown conformance kind"); } return finalize(); } @@ -223,7 +228,11 @@ std::string ASTMangler::mangleWitnessThunk( } if (Conformance) { - appendOperator(isa(Conformance) ? "TS" : "TW"); + if (isa(Conformance)) { + appendOperator("TS"); + } else { + appendOperator("TW"); + } } return finalize(); } @@ -1459,7 +1468,8 @@ void ASTMangler::appendBoundGenericArgs(Type type, bool &isFirstArgList) { static bool conformanceHasIdentity(const RootProtocolConformance *root) { auto conformance = dyn_cast(root); if (!conformance) { - assert(isa(root)); + assert(isa(root) || + isa(root)); return true; } @@ -1480,8 +1490,9 @@ static bool conformanceHasIdentity(const RootProtocolConformance *root) { static bool isRetroactiveConformance(const RootProtocolConformance *root) { auto conformance = dyn_cast(root); if (!conformance) { - assert(isa(root)); - return false; // self-conformances are never retroactive. + assert(isa(root) || + isa(root)); + return false; // self-conformances are never retroactive. nor are builtin. } return conformance->isRetroactive(); @@ -1653,15 +1664,35 @@ void ASTMangler::appendImplFunctionType(SILFunctionType *fn) { OpArgs.push_back('t'); } + bool mangleClangType = fn->getASTContext().LangOpts.UseClangFunctionTypes && + fn->hasNonDerivableClangType(); + + auto appendClangTypeToVec = [this, fn](auto &Vec) { + llvm::raw_svector_ostream OpArgsOS(Vec); + appendClangType(fn, OpArgsOS); + }; + switch (fn->getRepresentation()) { case SILFunctionTypeRepresentation::Thick: case SILFunctionTypeRepresentation::Thin: break; case SILFunctionTypeRepresentation::Block: + if (!mangleClangType) { + OpArgs.push_back('B'); + break; + } + OpArgs.push_back('z'); OpArgs.push_back('B'); + appendClangTypeToVec(OpArgs); break; case SILFunctionTypeRepresentation::CFunctionPointer: + if (!mangleClangType) { + OpArgs.push_back('C'); + break; + } + OpArgs.push_back('z'); OpArgs.push_back('C'); + appendClangTypeToVec(OpArgs); break; case SILFunctionTypeRepresentation::ObjCMethod: OpArgs.push_back('O'); @@ -1689,6 +1720,11 @@ void ASTMangler::appendImplFunctionType(SILFunctionType *fn) { break; } + // Asynchronous functions. + if (fn->isAsync()) { + OpArgs.push_back('H'); + } + auto outerGenericSig = CurGenericSignature; CurGenericSignature = fn->getSubstGenericSignature(); @@ -2239,6 +2275,9 @@ void ASTMangler::appendFunctionType(AnyFunctionType *fn, bool isAutoClosure, appendFunctionSignature(fn, forDecl); + bool mangleClangType = fn->getASTContext().LangOpts.UseClangFunctionTypes && + fn->hasNonDerivableClangType(); + // Note that we do not currently use thin representations in the AST // for the types of function decls. This may need to change at some // point, in which case the uncurry logic can probably migrate to that @@ -2251,6 +2290,10 @@ void ASTMangler::appendFunctionType(AnyFunctionType *fn, bool isAutoClosure, // changes to better support thin functions. switch (fn->getRepresentation()) { case AnyFunctionType::Representation::Block: + if (mangleClangType) { + appendOperator("XzB"); + return appendClangType(fn); + } // We distinguish escaping and non-escaping blocks, but only in the DWARF // mangling, because the ABI is already set. if (!fn->isNoEscape() && DWARFMangling) @@ -2282,10 +2325,31 @@ void ASTMangler::appendFunctionType(AnyFunctionType *fn, bool isAutoClosure, return appendOperator("c"); case AnyFunctionType::Representation::CFunctionPointer: + if (mangleClangType) { + appendOperator("XzC"); + return appendClangType(fn); + } return appendOperator("XC"); } } +template +void ASTMangler::appendClangType(FnType *fn, llvm::raw_svector_ostream &out) { + auto clangType = fn->getClangTypeInfo().getType(); + SmallString<64> scratch; + llvm::raw_svector_ostream scratchOS(scratch); + clang::ASTContext &clangCtx = + fn->getASTContext().getClangModuleLoader()->getClangASTContext(); + std::unique_ptr mangler{ + clang::ItaniumMangleContext::create(clangCtx, clangCtx.getDiagnostics())}; + mangler->mangleTypeName(clang::QualType(clangType, 0), scratchOS); + out << scratchOS.str().size() << scratchOS.str(); +} + +void ASTMangler::appendClangType(AnyFunctionType *fn) { + appendClangType(fn, Buffer); +} + void ASTMangler::appendFunctionSignature(AnyFunctionType *fn, const ValueDecl *forDecl) { appendFunctionResultType(fn->getResult(), forDecl); @@ -2854,6 +2918,10 @@ ASTMangler::appendProtocolConformance(const ProtocolConformance *conformance) { appendModule(Mod, DC->getAsDecl()->getAlternateModuleName()); } + // If this is a non-nominal type, we're done. + if (!conformingType->getAnyNominal()) + return; + contextSig = conformingType->getAnyNominal()->getGenericSignatureOfContext(); @@ -2878,6 +2946,9 @@ void ASTMangler::appendProtocolConformanceRef( assert(DC->getAsDecl()); appendModule(conformance->getDeclContext()->getParentModule(), DC->getAsDecl()->getAlternateModuleName()); + // Builtin conformances are always from the Swift module. + } else if (isa(conformance)) { + appendOperator("HP"); } else if (conformance->getDeclContext()->getParentModule() == conformance->getType()->getAnyNominal()->getParentModule()) { appendOperator("HP"); diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index 9365e5c4fcd15..e9a643f5e93a8 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -3845,6 +3845,8 @@ class TypePrinter : public TypeVisitor { } else if (auto *VD = originator.dyn_cast()) { Printer << "decl = "; Printer << VD->getName(); + } else if (auto *EE = originator.dyn_cast()) { + Printer << "error_expr"; } else { visit(originator.get()); } @@ -4054,7 +4056,9 @@ class TypePrinter : public TypeVisitor { visit(staticSelfT); } - void printFunctionExtInfo(ASTContext &Ctx, AnyFunctionType::ExtInfo info) { + void printFunctionExtInfo(AnyFunctionType *fnType) { + auto &ctx = fnType->getASTContext(); + auto info = fnType->getExtInfo(); if (Options.SkipAttributes) return; @@ -4091,13 +4095,13 @@ class TypePrinter : public TypeVisitor { break; case SILFunctionType::Representation::Block: Printer << "block"; - if (printClangType && !info.getClangTypeInfo().empty()) - printCType(Ctx, Printer, info); + if (printClangType && fnType->hasNonDerivableClangType()) + printCType(ctx, Printer, info); break; case SILFunctionType::Representation::CFunctionPointer: Printer << "c"; - if (printClangType && !info.getClangTypeInfo().empty()) - printCType(Ctx, Printer, info); + if (printClangType && fnType->hasNonDerivableClangType()) + printCType(ctx, Printer, info); break; case SILFunctionType::Representation::Method: Printer << "method"; @@ -4118,9 +4122,12 @@ class TypePrinter : public TypeVisitor { } } - void printFunctionExtInfo(ASTContext &Ctx, - SILFunctionType::ExtInfo info, - ProtocolConformanceRef witnessMethodConformance) { + void printFunctionExtInfo(SILFunctionType *fnType) { + auto &Ctx = fnType->getASTContext(); + auto info = fnType->getExtInfo(); + auto witnessMethodConformance = + fnType->getWitnessMethodConformanceOrInvalid(); + if (Options.SkipAttributes) return; @@ -4157,12 +4164,12 @@ class TypePrinter : public TypeVisitor { break; case SILFunctionType::Representation::Block: Printer << "block"; - if (printClangType) + if (printClangType && fnType->hasNonDerivableClangType()) printCType(Ctx, Printer, info); break; case SILFunctionType::Representation::CFunctionPointer: Printer << "c"; - if (printClangType) + if (printClangType && fnType->hasNonDerivableClangType()) printCType(Ctx, Printer, info); break; case SILFunctionType::Representation::Method: @@ -4237,7 +4244,7 @@ class TypePrinter : public TypeVisitor { Printer.printStructurePost(PrintStructureKind::FunctionType); }; - printFunctionExtInfo(T->getASTContext(), T->getExtInfo()); + printFunctionExtInfo(T); // If we're stripping argument labels from types, do it when printing. visitAnyFunctionTypeParams(T->getParams(), /*printLabels*/false); @@ -4277,7 +4284,7 @@ class TypePrinter : public TypeVisitor { Printer.printStructurePost(PrintStructureKind::FunctionType); }; - printFunctionExtInfo(T->getASTContext(), T->getExtInfo()); + printFunctionExtInfo(T); printGenericSignature(T->getGenericSignature(), PrintAST::PrintParams | PrintAST::PrintRequirements); @@ -4339,8 +4346,7 @@ class TypePrinter : public TypeVisitor { void visitSILFunctionType(SILFunctionType *T) { printSILCoroutineKind(T->getCoroutineKind()); - printFunctionExtInfo(T->getASTContext(), T->getExtInfo(), - T->getWitnessMethodConformanceOrInvalid()); + printFunctionExtInfo(T); printCalleeConvention(T->getCalleeConvention()); if (auto sig = T->getInvocationGenericSignature()) { @@ -4550,7 +4556,8 @@ class TypePrinter : public TypeVisitor { visit(T->getOpenedExistentialType()); } - void printArchetypeCommon(ArchetypeType *T) { + void printArchetypeCommon(ArchetypeType *T, + const AbstractTypeParamDecl *Decl) { if (Options.AlternativeTypeNames) { auto found = Options.AlternativeTypeNames->find(T->getCanonicalType()); if (found != Options.AlternativeTypeNames->end()) { @@ -4559,24 +4566,23 @@ class TypePrinter : public TypeVisitor { } } - auto Name = T->getName(); - if (Name.empty()) + const auto Name = T->getName(); + if (Name.empty()) { Printer << ""; - else { - PrintNameContext context = PrintNameContext::Normal; - if (Name == T->getASTContext().Id_Self) - context = PrintNameContext::GenericParameter; - Printer.printName(Name, context); + } else if (Decl) { + Printer.printTypeRef(T, Decl, Name); + } else { + Printer.printName(Name); } } - + void visitNestedArchetypeType(NestedArchetypeType *T) { visitParentType(T->getParent()); - printArchetypeCommon(T); + printArchetypeCommon(T, T->getAssocType()); } void visitPrimaryArchetypeType(PrimaryArchetypeType *T) { - printArchetypeCommon(T); + printArchetypeCommon(T, T->getInterfaceType()->getDecl()); } void visitOpaqueTypeArchetypeType(OpaqueTypeArchetypeType *T) { @@ -4643,26 +4649,23 @@ class TypePrinter : public TypeVisitor { T = Options.GenericSig->getSugaredType(T); } - auto Name = T->getName(); - if (Name.empty()) + const auto Name = T->getName(); + if (Name.empty()) { Printer << ""; - else { - if (T->getDecl() && - T->getDecl()->getDeclContext()->getSelfProtocolDecl()) { - Printer.printTypeRef(T, T->getDecl(), Name); - return; - } - - PrintNameContext context = PrintNameContext::Normal; - if (Name == T->getASTContext().Id_Self) - context = PrintNameContext::GenericParameter; - Printer.printName(Name, context); + } else if (auto *Decl = T->getDecl()) { + Printer.printTypeRef(T, Decl, Name); + } else { + Printer.printName(Name); } } void visitDependentMemberType(DependentMemberType *T) { visitParentType(T->getBase()); - Printer.printName(T->getName()); + if (auto *const Assoc = T->getAssocType()) { + Printer.printTypeRef(T, Assoc, T->getName()); + } else { + Printer.printName(T->getName()); + } } #define REF_STORAGE(Name, name, ...) \ @@ -5046,6 +5049,12 @@ void ProtocolConformance::printName(llvm::raw_ostream &os, os << ")"; break; } + case ProtocolConformanceKind::Builtin: { + auto builtin = cast(this); + os << builtin->getProtocol()->getName() + << " type " << builtin->getType(); + break; + } } } diff --git a/lib/AST/ASTScope.cpp b/lib/AST/ASTScope.cpp index a2d80a9ef3cac..50c48de5bea68 100644 --- a/lib/AST/ASTScope.cpp +++ b/lib/AST/ASTScope.cpp @@ -39,11 +39,11 @@ using namespace ast_scope; #pragma mark ASTScope void ASTScope::unqualifiedLookup( - SourceFile *SF, DeclNameRef name, SourceLoc loc, + SourceFile *SF, SourceLoc loc, namelookup::AbstractASTScopeDeclConsumer &consumer) { if (auto *s = SF->getASTContext().Stats) ++s->getFrontendCounters().NumASTScopeLookups; - ASTScopeImpl::unqualifiedLookup(SF, name, loc, consumer); + ASTScopeImpl::unqualifiedLookup(SF, loc, consumer); } llvm::SmallVector ASTScope::lookupLabeledStmts( @@ -116,62 +116,9 @@ LabeledConditionalStmt *GuardStmtScope::getLabeledConditionalStmt() const { ASTContext &ASTScopeImpl::getASTContext() const { if (auto d = getDeclIfAny()) return d.get()->getASTContext(); - if (auto dc = getDeclContext()) - return dc.get()->getASTContext(); return getParent().get()->getASTContext(); } -#pragma mark getDeclContext - -NullablePtr ASTScopeImpl::getDeclContext() const { - return nullptr; -} - -NullablePtr ASTSourceFileScope::getDeclContext() const { - return NullablePtr(SF); -} - -NullablePtr GenericTypeOrExtensionScope::getDeclContext() const { - return getGenericContext(); -} - -NullablePtr GenericParamScope::getDeclContext() const { - return dyn_cast(holder); -} - -NullablePtr PatternEntryInitializerScope::getDeclContext() const { - return getPatternEntry().getInitContext(); -} - -NullablePtr BraceStmtScope::getDeclContext() const { - return getParent().get()->getDeclContext(); -} - -NullablePtr -DefaultArgumentInitializerScope::getDeclContext() const { - auto *dc = decl->getDefaultArgumentInitContext(); - ASTScopeAssert(dc, "If scope exists, this must exist"); - return dc; -} - -// When asked for a loc in an intializer in a capture list, the asked-for -// context is the closure. -NullablePtr CaptureListScope::getDeclContext() const { - return expr->getClosureBody(); -} - -NullablePtr AttachedPropertyWrapperScope::getDeclContext() const { - return decl->getParentPatternBinding()->getInitContext(0); -} - -NullablePtr AbstractFunctionDeclScope::getDeclContext() const { - return decl; -} - -NullablePtr ParameterListScope::getDeclContext() const { - return matchingContext; -} - #pragma mark getClassName std::string GenericTypeOrExtensionScope::getClassName() const { @@ -190,8 +137,8 @@ DEFINE_GET_CLASS_NAME(DefaultArgumentInitializerScope) DEFINE_GET_CLASS_NAME(AttachedPropertyWrapperScope) DEFINE_GET_CLASS_NAME(PatternEntryDeclScope) DEFINE_GET_CLASS_NAME(PatternEntryInitializerScope) -DEFINE_GET_CLASS_NAME(ConditionalClauseScope) DEFINE_GET_CLASS_NAME(ConditionalClausePatternUseScope) +DEFINE_GET_CLASS_NAME(ConditionalClauseInitializerScope) DEFINE_GET_CLASS_NAME(CaptureListScope) DEFINE_GET_CLASS_NAME(ClosureParametersScope) DEFINE_GET_CLASS_NAME(TopLevelCodeScope) @@ -202,7 +149,7 @@ DEFINE_GET_CLASS_NAME(EnumElementScope) DEFINE_GET_CLASS_NAME(IfStmtScope) DEFINE_GET_CLASS_NAME(WhileStmtScope) DEFINE_GET_CLASS_NAME(GuardStmtScope) -DEFINE_GET_CLASS_NAME(LookupParentDiversionScope) +DEFINE_GET_CLASS_NAME(GuardStmtBodyScope) DEFINE_GET_CLASS_NAME(RepeatWhileScope) DEFINE_GET_CLASS_NAME(DoStmtScope) DEFINE_GET_CLASS_NAME(DoCatchStmtScope) @@ -224,6 +171,10 @@ const SourceFile *ASTScopeImpl::getSourceFile() const { const SourceFile *ASTSourceFileScope::getSourceFile() const { return SF; } +ASTContext &ASTSourceFileScope::getASTContext() const { + return SF->getASTContext(); +} + SourceRange ExtensionScope::getBraces() const { return decl->getBraces(); } SourceRange NominalTypeScope::getBraces() const { return decl->getBraces(); } @@ -246,32 +197,4 @@ void ASTScopeImpl::postOrderDo(function_ref fn) { for (auto *child : getChildren()) child->postOrderDo(fn); fn(this); -} - -ArrayRef ConditionalClauseScope::getCond() const { - return stmt->getCond(); -} - -const StmtConditionElement & -ConditionalClauseScope::getStmtConditionElement() const { - return getCond()[index]; -} - -unsigned ASTScopeImpl::countDescendants() const { - unsigned count = 0; - const_cast(this)->preOrderDo( - [&](ASTScopeImpl *) { ++count; }); - return count - 1; -} - -// Can fail if a subscope is lazy and not reexpanded -void ASTScopeImpl::assertThatTreeDoesNotShrink(function_ref fn) { -#ifndef NDEBUG - unsigned beforeCount = countDescendants(); -#endif - fn(); -#ifndef NDEBUG - unsigned afterCount = countDescendants(); - ASTScopeAssert(beforeCount <= afterCount, "shrank?!"); -#endif -} +} \ No newline at end of file diff --git a/lib/AST/ASTScopeCreation.cpp b/lib/AST/ASTScopeCreation.cpp index bbef211f3a5b2..cb1339f4ab1ab 100644 --- a/lib/AST/ASTScopeCreation.cpp +++ b/lib/AST/ASTScopeCreation.cpp @@ -20,6 +20,7 @@ #include "swift/AST/Attr.h" #include "swift/AST/Decl.h" #include "swift/AST/Expr.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/Initializer.h" #include "swift/AST/LazyResolver.h" #include "swift/AST/Module.h" @@ -38,159 +39,9 @@ using namespace swift; using namespace ast_scope; -/// If true, nest scopes so a variable is out of scope before its declaration -/// Does not handle capture rules for local functions properly. -/// If false don't push uses down into subscopes after decls. -static const bool handleUseBeforeDef = false; - -#pragma mark source range utilities -static bool rangeableIsIgnored(const Decl *d) { return d->isImplicit(); } -static bool rangeableIsIgnored(const Expr *d) { - return false; // implicit expr may contain closures -} -static bool rangeableIsIgnored(const Stmt *d) { - return false; // ?? -} -static bool rangeableIsIgnored(const ASTNode n) { - return (n.is() && rangeableIsIgnored(n.get())) || - (n.is() && rangeableIsIgnored(n.get())) || - (n.is() && rangeableIsIgnored(n.get())); -} - -template -static SourceRange getRangeableSourceRange(const Rangeable *const p) { - return p->getSourceRange(); -} -static SourceRange getRangeableSourceRange(const ASTNode n) { - return n.getSourceRange(); -} - -template -static bool isLocalizable(const Rangeable astElement) { - return !rangeableIsIgnored(astElement) && - getRangeableSourceRange(astElement).isValid(); -} - -template -static void dumpRangeable(const Rangeable r, llvm::raw_ostream &f) { - r.dump(f); -} -template -static void dumpRangeable(const Rangeable *r, llvm::raw_ostream &f) { - r->dump(f); -} -template -static void dumpRangeable(Rangeable *r, llvm::raw_ostream &f) { - r->dump(f); -} - -static void dumpRangeable(const SpecializeAttr *r, - llvm::raw_ostream &f) LLVM_ATTRIBUTE_USED; -static void dumpRangeable(const SpecializeAttr *r, llvm::raw_ostream &f) { - llvm::errs() << "SpecializeAttr\n"; -} -static void dumpRangeable(SpecializeAttr *r, - llvm::raw_ostream &f) LLVM_ATTRIBUTE_USED; -static void dumpRangeable(SpecializeAttr *r, llvm::raw_ostream &f) { - llvm::errs() << "SpecializeAttr\n"; -} - -static void dumpRangeable(const DifferentiableAttr *a, - llvm::raw_ostream &f) LLVM_ATTRIBUTE_USED; -static void dumpRangeable(const DifferentiableAttr *a, llvm::raw_ostream &f) { - llvm::errs() << "DifferentiableAttr\n"; -} -static void dumpRangeable(DifferentiableAttr *a, - llvm::raw_ostream &f) LLVM_ATTRIBUTE_USED; -static void dumpRangeable(DifferentiableAttr *a, llvm::raw_ostream &f) { - llvm::errs() << "DifferentiableAttr\n"; -} - -/// For Debugging -template -bool doesRangeableRangeMatch(const T *x, const SourceManager &SM, - unsigned start, unsigned end, - StringRef file = "") { - auto const r = getRangeableSourceRange(x); - if (r.isInvalid()) - return false; - if (start && SM.getLineAndColumnInBuffer(r.Start).first != start) - return false; - if (end && SM.getLineAndColumnInBuffer(r.End).first != end) - return false; - if (file.empty()) - return true; - const auto buf = SM.findBufferContainingLoc(r.Start); - return SM.getIdentifierForBuffer(buf).endswith(file); -} - -#pragma mark end of rangeable - -static std::vector asNodeVector(DeclRange dr) { - std::vector nodes; - llvm::transform(dr, std::back_inserter(nodes), - [&](Decl *d) { return ASTNode(d); }); - return nodes; -} - namespace swift { namespace ast_scope { -namespace { -/// Use me with any ASTNode, Expr*, Decl*, or Stmt* -/// I will yield a void* that is the same, even when given an Expr* and a -/// ClosureExpr* because I take the Expr*, figure its real class, then up -/// cast. -/// Useful for duplicate checking. -class UniquePointerCalculator - : public ASTVisitor { -public: - template const void *visit(const T *x) { - return const_cast(x); - } - - // Call these only from the superclass - void *visitDecl(Decl *e) { return e; } - void *visitStmt(Stmt *e) { return e; } - void *visitExpr(Expr *e) { return e; } - void *visitPattern(Pattern *e) { return e; } - void *visitDeclAttribute(DeclAttribute *e) { return e; } - -// Provide default implementations for statements as ASTVisitor does for Exprs -#define STMT(CLASS, PARENT) \ - void *visit##CLASS##Stmt(CLASS##Stmt *S) { return visitStmt(S); } -#include "swift/AST/StmtNodes.def" - -// Provide default implementations for patterns as ASTVisitor does for Exprs -#define PATTERN(CLASS, PARENT) \ - void *visit##CLASS##Pattern(CLASS##Pattern *S) { return visitPattern(S); } -#include "swift/AST/PatternNodes.def" -}; - -/// A set that does the right pointer calculation for comparing Decls to -/// DeclContexts, and Exprs -class NodeSet { - ::llvm::DenseSet pointers; - -public: - bool contains(const ASTScopeImpl *const s) { - if (auto *r = s->getReferrent().getPtrOrNull()) - return pointers.count(r); - return false; // never exclude a non-checkable scope - } - bool insert(const ASTScopeImpl *const s) { - if (auto *r = s->getReferrent().getPtrOrNull()) - return pointers.insert(r).second; - return true; - } - void erase(const ASTScopeImpl *const s) { - if (auto *r = s->getReferrent().getPtrOrNull()) - pointers.erase(r); - } -}; -} // namespace - #pragma mark ScopeCreator class ScopeCreator final { @@ -202,125 +53,27 @@ class ScopeCreator final { ASTSourceFileScope *const sourceFileScope; ASTContext &getASTContext() const { return ctx; } - /// The AST can have duplicate nodes, and we don't want to create scopes for - /// those. - NodeSet scopedNodes; - ScopeCreator(SourceFile *SF) : ctx(SF->getASTContext()), - sourceFileScope(new (ctx) ASTSourceFileScope(SF, this)) { - ctx.addDestructorCleanup(scopedNodes); - } + sourceFileScope(new (ctx) ASTSourceFileScope(SF, this)) {} ScopeCreator(const ScopeCreator &) = delete; // ensure no copies ScopeCreator(const ScopeCreator &&) = delete; // ensure no moves - /// Given an array of ASTNodes or Decl pointers, add them - /// Return the resultant insertionPoint - ASTScopeImpl * - addSiblingsToScopeTree(ASTScopeImpl *const insertionPoint, - ASTScopeImpl *const organicInsertionPoint, - ArrayRef nodesOrDeclsToAdd) { - auto *ip = insertionPoint; - for (auto nd : sortBySourceRange(cull(nodesOrDeclsToAdd))) { - auto *const newIP = - addToScopeTreeAndReturnInsertionPoint(nd, ip).getPtrOr(ip); - ip = newIP; - } - return ip; - } - public: /// For each of searching, call this unless the insertion point is needed void addToScopeTree(ASTNode n, ASTScopeImpl *parent) { - (void)addToScopeTreeAndReturnInsertionPoint(n, parent); + (void)addToScopeTreeAndReturnInsertionPoint(n, parent, None); } - /// Return new insertion point if the scope was not a duplicate + /// Return new insertion point. /// For ease of searching, don't call unless insertion point is needed - NullablePtr - addToScopeTreeAndReturnInsertionPoint(ASTNode, ASTScopeImpl *parent); - - bool isWorthTryingToCreateScopeFor(ASTNode n) const { - if (!n) - return false; - if (n.is()) - return true; - // Cannot ignore implicit statements because implict return can contain - // scopes in the expression, such as closures. - // But must ignore other implicit statements, e.g. brace statments - // if they can have no children and no stmt source range. - // Deal with it in visitBraceStmt - if (n.is()) - return true; - - auto *const d = n.get(); - // Implicit nodes may not have source information for name lookup. - if (!isLocalizable(d)) - return false; - - // Commented out for - // validation-test/compiler_crashers_fixed/27962-swift-rebindselfinconstructorexpr-getcalledconstructor.swift - // In that test the invalid PBD -> var decl which contains the desired - // closure scope - // if (const auto *PBD = dyn_cast(d)) - // if (!isLocalizable(PBD)) - // return false; - /// In - /// \code - /// @propertyWrapper - /// public struct Wrapper { - /// public var value: T - /// - /// public init(body: () -> T) { - /// self.value = body() - /// } - /// } - /// - /// let globalInt = 17 - /// - /// @Wrapper(body: { globalInt }) - /// public var y: Int - /// \endcode - /// I'm seeing a dumped AST include: - /// (pattern_binding_decl range=[test.swift:13:8 - line:12:29] - const auto &SM = d->getASTContext().SourceMgr; - - // Once we allow invalid PatternBindingDecls (see - // isWorthTryingToCreateScopeFor), then - // IDE/complete_property_delegate_attribute.swift fails because we try to - // expand a member whose source range is backwards. - (void)SM; - ASTScopeAssert(d->getStartLoc().isInvalid() || - !SM.isBeforeInBuffer(d->getEndLoc(), d->getStartLoc()), - "end-before-start will break tree search via location"); - return true; - } - - /// Create a new scope of class ChildScope initialized with a ChildElement, - /// expandScope it, - /// add it as a child of the receiver, and return the child and the scope to - /// receive more decls. - template - ASTScopeImpl *constructExpandAndInsertUncheckable(ASTScopeImpl *parent, - Args... args) { - ASTScopeAssert(!Scope(args...).getReferrent(), - "Not checking for duplicate ASTNode but class supports it"); - return constructExpandAndInsert(parent, args...); - } - - template - NullablePtr - ifUniqueConstructExpandAndInsert(ASTScopeImpl *parent, Args... args) { - Scope dryRun(args...); - ASTScopeAssert( - dryRun.getReferrent(), - "Checking for duplicate ASTNode but class does not support it"); - if (scopedNodes.insert(&dryRun)) - return constructExpandAndInsert(parent, args...); - return nullptr; - } + /// + /// \param endLoc The end location for any "scopes until the end" that + /// we introduce here, such as PatternEntryDeclScope and GuardStmtScope + ASTScopeImpl * + addToScopeTreeAndReturnInsertionPoint(ASTNode, ASTScopeImpl *parent, + Optional endLoc); -private: template ASTScopeImpl *constructExpandAndInsert(ASTScopeImpl *parent, Args... args) { auto *child = new (ctx) Scope(args...); @@ -331,8 +84,6 @@ class ScopeCreator final { ASTScopeImpl *insertionPoint = child->expandAndBeCurrentDetectingRecursion(*this); - ASTScopeAssert(child->verifyThatThisNodeComeAfterItsPriorSibling(), - "Ensure search will work"); return insertionPoint; } @@ -341,15 +92,7 @@ class ScopeCreator final { ASTScopeImpl *constructWithPortionExpandAndInsert(ASTScopeImpl *parent, Args... args) { const Portion *portion = new (ctx) PortionClass(); - return constructExpandAndInsertUncheckable(parent, portion, args...); - } - - template - NullablePtr - ifUniqueConstructWithPortionExpandAndInsert(ASTScopeImpl *parent, - Args... args) { - const Portion *portion = new (ctx) PortionClass(); - return ifUniqueConstructExpandAndInsert(parent, portion, args...); + return constructExpandAndInsert(parent, portion, args...); } void addExprToScopeTree(Expr *expr, ASTScopeImpl *parent) { @@ -369,13 +112,13 @@ class ScopeCreator final { std::pair walkToExprPre(Expr *E) override { if (auto *closure = dyn_cast(E)) { scopeCreator - .ifUniqueConstructExpandAndInsert( + .constructExpandAndInsert( parent, closure); return {false, E}; } if (auto *capture = dyn_cast(E)) { scopeCreator - .ifUniqueConstructExpandAndInsert( + .constructExpandAndInsert( parent, capture); return {false, E}; } @@ -410,125 +153,30 @@ class ScopeCreator final { return parent; auto *s = parent; for (unsigned i : indices(generics->getParams())) - s = ifUniqueConstructExpandAndInsert( - s, parameterizedDecl, generics, i) - .getPtrOr(s); + s = constructExpandAndInsert( + s, parameterizedDecl, generics, i); return s; } void - addChildrenForAllLocalizableAccessorsInSourceOrder(AbstractStorageDecl *asd, - ASTScopeImpl *parent); + addChildrenForParsedAccessors(AbstractStorageDecl *asd, + ASTScopeImpl *parent); void addChildrenForKnownAttributes(ValueDecl *decl, ASTScopeImpl *parent); -public: - -private: - /// Remove VarDecls because we'll find them when we expand the - /// PatternBindingDecls. Remove EnunCases - /// because they overlap EnumElements and AST includes the elements in the - /// members. - std::vector cull(ArrayRef input) const { - // TODO: Investigate whether to move the real EndLoc tracking of - // SubscriptDecl up into AbstractStorageDecl. May have to cull more. - std::vector culled; - llvm::copy_if(input, std::back_inserter(culled), [&](ASTNode n) { - ASTScopeAssert( - !n.isDecl(DeclKind::Accessor), - "Should not find accessors in iterable types or brace statements"); - return isLocalizable(n) && - !n.isDecl(DeclKind::Var) && - !n.isDecl(DeclKind::EnumCase) && - !n.isDecl(DeclKind::IfConfig); - }); - return culled; - } - - /// Templated to work on either ASTNodes, Decl*'s, or whatnot. - template - std::vector - sortBySourceRange(std::vector toBeSorted) const { - auto compareNodes = [&](Rangeable n1, Rangeable n2) { - return isNotAfter(n1, n2); - }; - std::stable_sort(toBeSorted.begin(), toBeSorted.end(), compareNodes); - return toBeSorted; - } - - template - bool isNotAfter(Rangeable n1, Rangeable n2) const { - const auto r1 = getRangeableSourceRange(n1); - const auto r2 = getRangeableSourceRange(n2); - - const int signum = ASTScopeImpl::compare(r1, r2, ctx.SourceMgr, - /*ensureDisjoint=*/true); - return -1 == signum; - } - -public: - /// For debugging. Return true if scope tree contains all the decl contexts in - /// the AST May modify the scope tree in order to update obsolete scopes. - /// Likely slow. - bool containsAllDeclContextsFromAST() { - auto allDeclContexts = findLocalizableDeclContextsInAST(); - llvm::DenseMap bogusDCs; - sourceFileScope->preOrderDo([&](ASTScopeImpl *scope) { - scope->expandAndBeCurrentDetectingRecursion(*this); - }); - sourceFileScope->postOrderDo([&](ASTScopeImpl *scope) { - if (auto *dc = scope->getDeclContext().getPtrOrNull()) { - auto iter = allDeclContexts.find(dc); - if (iter != allDeclContexts.end()) - ++iter->second; - else - bogusDCs.insert({dc, scope}); - } - }); - - auto printDecl = [&](const Decl *d) { - llvm::errs() << "\ngetAsDecl() -> " << d << " "; - d->getSourceRange().print(llvm::errs(), ctx.SourceMgr); - llvm::errs() << " : "; - d->dump(llvm::errs()); - llvm::errs() << "\n"; - }; - bool foundOmission = false; - for (const auto &p : allDeclContexts) { - if (p.second == 0) { - if (auto *d = p.first->getAsDecl()) { - if (isLocalizable(d)) { - llvm::errs() << "\nASTScope tree omitted DeclContext: " << p.first - << " " - << ":\n"; - p.first->printContext(llvm::errs()); - printDecl(d); - foundOmission = true; - } - } else { - // If no decl, no source range, so no scope - } - } - } - for (const auto &dcAndScope : bogusDCs) { - llvm::errs() << "ASTScope tree confabulated: " << dcAndScope.getFirst() - << ":\n"; - dcAndScope.getFirst()->printContext(llvm::errs()); - if (auto *d = dcAndScope.getFirst()->getAsDecl()) - printDecl(d); - dcAndScope.getSecond()->print(llvm::errs(), 0, false); - } - return !foundOmission && bogusDCs.empty(); - } - -private: - /// Return a map of every DeclContext in the AST, and zero in the 2nd element. - /// For debugging. - llvm::DenseMap - findLocalizableDeclContextsInAST() const; + /// Add PatternEntryDeclScopes for each pattern binding entry. + /// + /// Returns the new insertion point. + /// + /// \param endLoc Must be valid iff the pattern binding is in a local + /// scope, in which case this is the last source location where the + /// pattern bindings are going to be visible. + ASTScopeImpl * + addPatternBindingToScopeTree(PatternBindingDecl *patternBinding, + ASTScopeImpl *parent, + Optional endLoc); -public: SWIFT_DEBUG_DUMP { print(llvm::errs()); } void print(raw_ostream &out) const { @@ -599,7 +247,7 @@ void ASTSourceFileScope::expandFunctionBody(AbstractFunctionDecl *AFD) { ASTSourceFileScope::ASTSourceFileScope(SourceFile *SF, ScopeCreator *scopeCreator) - : SF(SF), scopeCreator(scopeCreator), insertionPoint(this) {} + : SF(SF), scopeCreator(scopeCreator) {} #pragma mark NodeAdder @@ -607,21 +255,19 @@ namespace swift { namespace ast_scope { class NodeAdder - : public ASTVisitor, - NullablePtr, NullablePtr, + : public ASTVisitor { + Optional endLoc; + public: + explicit NodeAdder(Optional endLoc) : endLoc(endLoc) {} #pragma mark ASTNodes that do not create scopes - // Even ignored Decls and Stmts must extend the source range of a scope: - // E.g. a braceStmt with some definitions that ends in a statement that - // accesses such a definition must resolve as being IN the scope. - #define VISIT_AND_IGNORE(What) \ - NullablePtr visit##What(What *w, ASTScopeImpl *p, \ - ScopeCreator &) { \ - p->widenSourceRangeForIgnoredASTNode(w); \ + ASTScopeImpl *visit##What(What *w, ASTScopeImpl *p, \ + ScopeCreator &) { \ return p; \ } @@ -638,6 +284,10 @@ class NodeAdder VISIT_AND_IGNORE(PoundDiagnosticDecl) VISIT_AND_IGNORE(MissingMemberDecl) + // Only members of the active clause are in scope, and those + // are visited separately. + VISIT_AND_IGNORE(IfConfigDecl) + // This declaration is handled from the PatternBindingDecl VISIT_AND_IGNORE(VarDecl) @@ -652,9 +302,9 @@ class NodeAdder #pragma mark simple creation ignoring deferred nodes #define VISIT_AND_CREATE(What, ScopeClass) \ - NullablePtr visit##What(What *w, ASTScopeImpl *p, \ - ScopeCreator &scopeCreator) { \ - return scopeCreator.ifUniqueConstructExpandAndInsert(p, w); \ + ASTScopeImpl *visit##What(What *w, ASTScopeImpl *p, \ + ScopeCreator &scopeCreator) { \ + return scopeCreator.constructExpandAndInsert(p, w); \ } VISIT_AND_CREATE(SubscriptDecl, SubscriptDeclScope) @@ -673,9 +323,9 @@ class NodeAdder #pragma mark 2D simple creation (ignoring deferred nodes) #define VISIT_AND_CREATE_WHOLE_PORTION(What, WhatScope) \ - NullablePtr visit##What(What *w, ASTScopeImpl *p, \ - ScopeCreator &scopeCreator) { \ - return scopeCreator.ifUniqueConstructWithPortionExpandAndInsert< \ + ASTScopeImpl *visit##What(What *w, ASTScopeImpl *p, \ + ScopeCreator &scopeCreator) { \ + return scopeCreator.constructWithPortionExpandAndInsert< \ WhatScope, GenericTypeOrExtensionWholePortion>(p, w); \ } @@ -689,9 +339,9 @@ class NodeAdder #undef VISIT_AND_CREATE_WHOLE_PORTION // This declaration is handled from - // addChildrenForAllLocalizableAccessorsInSourceOrder - NullablePtr visitAccessorDecl(AccessorDecl *ad, ASTScopeImpl *p, - ScopeCreator &scopeCreator) { + // addChildrenForParsedAccessors + ASTScopeImpl *visitAccessorDecl(AccessorDecl *ad, ASTScopeImpl *p, + ScopeCreator &scopeCreator) { return visitAbstractFunctionDecl(ad, p, scopeCreator); } @@ -700,15 +350,18 @@ class NodeAdder // Each of the following creates a new scope, so that nodes which were parsed // after them need to be placed in scopes BELOW them in the tree. So pass down // the deferred nodes. - NullablePtr visitGuardStmt(GuardStmt *e, ASTScopeImpl *p, - ScopeCreator &scopeCreator) { - return scopeCreator.ifUniqueConstructExpandAndInsert(p, e); + ASTScopeImpl *visitGuardStmt(GuardStmt *e, ASTScopeImpl *p, + ScopeCreator &scopeCreator) { + ASTScopeAssert(endLoc.hasValue(), "GuardStmt outside of a BraceStmt?"); + return scopeCreator.constructExpandAndInsert( + p, e, *endLoc); } - NullablePtr visitTopLevelCodeDecl(TopLevelCodeDecl *d, - ASTScopeImpl *p, - ScopeCreator &scopeCreator) { - return scopeCreator.ifUniqueConstructExpandAndInsert(p, - d); + ASTScopeImpl *visitTopLevelCodeDecl(TopLevelCodeDecl *d, + ASTScopeImpl *p, + ScopeCreator &scopeCreator) { + ASTScopeAssert(endLoc.hasValue(), "TopLevelCodeDecl in wrong place?"); + return scopeCreator.constructExpandAndInsert( + p, d, *endLoc); } #pragma mark special-case creation @@ -717,94 +370,98 @@ class NodeAdder ASTScope_unreachable("SourceFiles are orphans."); } - NullablePtr visitYieldStmt(YieldStmt *ys, ASTScopeImpl *p, - ScopeCreator &scopeCreator) { + ASTScopeImpl *visitYieldStmt(YieldStmt *ys, ASTScopeImpl *p, + ScopeCreator &scopeCreator) { for (Expr *e : ys->getYields()) visitExpr(e, p, scopeCreator); return p; } - NullablePtr visitDeferStmt(DeferStmt *ds, ASTScopeImpl *p, - ScopeCreator &scopeCreator) { + ASTScopeImpl *visitDeferStmt(DeferStmt *ds, ASTScopeImpl *p, + ScopeCreator &scopeCreator) { visitFuncDecl(ds->getTempDecl(), p, scopeCreator); return p; } - NullablePtr visitBraceStmt(BraceStmt *bs, ASTScopeImpl *p, - ScopeCreator &scopeCreator) { - auto maybeBraceScope = - scopeCreator.ifUniqueConstructExpandAndInsert(p, bs); - if (auto *s = scopeCreator.getASTContext().Stats) + ASTScopeImpl *visitBraceStmt(BraceStmt *bs, ASTScopeImpl *p, + ScopeCreator &scopeCreator) { + if (bs->empty()) + return p; + + SmallVector localFuncsAndTypes; + SmallVector localVars; + + // All types and functions are visible anywhere within a brace statement + // scope. When ordering matters (i.e. var decl) we will have split the brace + // statement into nested scopes. + for (auto braceElement : bs->getElements()) { + if (auto localBinding = braceElement.dyn_cast()) { + if (auto *vd = dyn_cast(localBinding)) { + if (isa(vd) || isa(vd)) { + localFuncsAndTypes.push_back(vd); + } else if (auto *var = dyn_cast(localBinding)) { + localVars.push_back(var); + } + } + } + } + + SourceLoc endLocForBraceStmt = bs->getEndLoc(); + if (endLoc.hasValue()) + endLocForBraceStmt = *endLoc; + + ASTContext &ctx = scopeCreator.getASTContext(); + if (auto *s = ctx.Stats) ++s->getFrontendCounters().NumBraceStmtASTScopes; - return maybeBraceScope.getPtrOr(p); + + return + scopeCreator.constructExpandAndInsert( + p, bs, + ctx.AllocateCopy(localFuncsAndTypes), + ctx.AllocateCopy(localVars), + endLocForBraceStmt); } - NullablePtr + ASTScopeImpl * visitPatternBindingDecl(PatternBindingDecl *patternBinding, ASTScopeImpl *parentScope, ScopeCreator &scopeCreator) { - if (auto *var = patternBinding->getSingleVar()) - scopeCreator.addChildrenForKnownAttributes(var, parentScope); - - const bool isInTypeDecl = parentScope->isATypeDeclScope(); - - const DeclVisibilityKind vis = - isInTypeDecl ? DeclVisibilityKind::MemberOfCurrentNominal - : DeclVisibilityKind::LocalVariable; - auto *insertionPoint = parentScope; - for (auto i : range(patternBinding->getNumPatternEntries())) { - insertionPoint = - scopeCreator - .ifUniqueConstructExpandAndInsert( - insertionPoint, patternBinding, i, vis) - .getPtrOr(insertionPoint); - } - // If in a type decl, the type search will find these, - // but if in a brace stmt, must continue under the last binding. - return isInTypeDecl ? parentScope : insertionPoint; + return scopeCreator.addPatternBindingToScopeTree( + patternBinding, parentScope, endLoc); } - NullablePtr visitEnumElementDecl(EnumElementDecl *eed, - ASTScopeImpl *p, - ScopeCreator &scopeCreator) { - scopeCreator.constructExpandAndInsertUncheckable(p, eed); + ASTScopeImpl *visitEnumElementDecl(EnumElementDecl *eed, + ASTScopeImpl *p, + ScopeCreator &scopeCreator) { + scopeCreator.constructExpandAndInsert(p, eed); return p; } - NullablePtr visitIfConfigDecl(IfConfigDecl *icd, - ASTScopeImpl *p, - ScopeCreator &scopeCreator) { - ASTScope_unreachable( - "Should be handled inside of " - "expandIfConfigClausesThenCullAndSortElementsOrMembers"); - } - - NullablePtr visitReturnStmt(ReturnStmt *rs, ASTScopeImpl *p, - ScopeCreator &scopeCreator) { + ASTScopeImpl *visitReturnStmt(ReturnStmt *rs, ASTScopeImpl *p, + ScopeCreator &scopeCreator) { if (rs->hasResult()) visitExpr(rs->getResult(), p, scopeCreator); return p; } - NullablePtr visitThrowStmt(ThrowStmt *ts, ASTScopeImpl *p, - ScopeCreator &scopeCreator) { + ASTScopeImpl *visitThrowStmt(ThrowStmt *ts, ASTScopeImpl *p, + ScopeCreator &scopeCreator) { visitExpr(ts->getSubExpr(), p, scopeCreator); return p; } - NullablePtr visitPoundAssertStmt(PoundAssertStmt *pas, - ASTScopeImpl *p, - ScopeCreator &scopeCreator) { + ASTScopeImpl *visitPoundAssertStmt(PoundAssertStmt *pas, + ASTScopeImpl *p, + ScopeCreator &scopeCreator) { visitExpr(pas->getCondition(), p, scopeCreator); return p; } - NullablePtr visitExpr(Expr *expr, ASTScopeImpl *p, - ScopeCreator &scopeCreator) { - if (expr) { - p->widenSourceRangeForIgnoredASTNode(expr); + ASTScopeImpl *visitExpr(Expr *expr, ASTScopeImpl *p, + ScopeCreator &scopeCreator) { + if (expr) scopeCreator.addExprToScopeTree(expr, p); - } + return p; } }; @@ -813,20 +470,27 @@ class NodeAdder // These definitions are way down here so it can call into // NodeAdder -NullablePtr +ASTScopeImpl * ScopeCreator::addToScopeTreeAndReturnInsertionPoint(ASTNode n, - ASTScopeImpl *parent) { - if (!isWorthTryingToCreateScopeFor(n)) + ASTScopeImpl *parent, + Optional endLoc) { + if (!n) return parent; + + if (auto *d = n.dyn_cast()) + if (d->isImplicit()) + return parent; + + NodeAdder adder(endLoc); if (auto *p = n.dyn_cast()) - return NodeAdder().visit(p, parent, *this); + return adder.visit(p, parent, *this); if (auto *p = n.dyn_cast()) - return NodeAdder().visit(p, parent, *this); + return adder.visit(p, parent, *this); auto *p = n.get(); - return NodeAdder().visit(p, parent, *this); + return adder.visit(p, parent, *this); } -void ScopeCreator::addChildrenForAllLocalizableAccessorsInSourceOrder( +void ScopeCreator::addChildrenForParsedAccessors( AbstractStorageDecl *asd, ASTScopeImpl *parent) { asd->visitParsedAccessors([&](AccessorDecl *ad) { assert(asd == ad->getStorage()); @@ -857,25 +521,66 @@ void ScopeCreator::addChildrenForKnownAttributes(ValueDecl *decl, for (auto *attr : relevantAttrs) { if (auto *diffAttr = dyn_cast(attr)) { - ifUniqueConstructExpandAndInsert( + constructExpandAndInsert( parent, diffAttr, decl); } else if (auto *specAttr = dyn_cast(attr)) { if (auto *afd = dyn_cast(decl)) { - ifUniqueConstructExpandAndInsert( + constructExpandAndInsert( parent, specAttr, afd); } } else if (auto *customAttr = dyn_cast(attr)) { if (auto *vd = dyn_cast(decl)) { - ifUniqueConstructExpandAndInsert( + constructExpandAndInsert( parent, customAttr, vd); } } } } +ASTScopeImpl * +ScopeCreator::addPatternBindingToScopeTree(PatternBindingDecl *patternBinding, + ASTScopeImpl *parentScope, + Optional endLoc) { + if (auto *var = patternBinding->getSingleVar()) + addChildrenForKnownAttributes(var, parentScope); + + auto *insertionPoint = parentScope; + for (auto i : range(patternBinding->getNumPatternEntries())) { + bool isLocalBinding = false; + if (auto *varDecl = patternBinding->getAnchoringVarDecl(i)) { + isLocalBinding = varDecl->getDeclContext()->isLocalContext(); + } + + Optional endLocForBinding = None; + if (isLocalBinding) { + endLocForBinding = endLoc; + ASTScopeAssert(endLoc.hasValue() && endLoc->isValid(), + "PatternBindingDecl in local context outside of BraceStmt?"); + } + + insertionPoint = + constructExpandAndInsert( + insertionPoint, patternBinding, i, + isLocalBinding, endLocForBinding); + + ASTScopeAssert(isLocalBinding || insertionPoint == parentScope, + "Bindings at the top-level or members of types should " + "not change the insertion point"); + } + + return insertionPoint; +} + #pragma mark creation helpers void ASTScopeImpl::addChild(ASTScopeImpl *child, ASTContext &ctx) { + ASTScopeAssert(!child->getParent(), "child should not already have parent"); + child->parent = this; + +#ifndef NDEBUG + checkSourceRangeBeforeAddingChild(child, ctx); +#endif + // If this is the first time we've added children, notify the ASTContext // that there's a SmallVector that needs to be cleaned up. // FIXME: If we had access to SmallVector::isSmall(), we could do better. @@ -884,9 +589,6 @@ void ASTScopeImpl::addChild(ASTScopeImpl *child, ASTContext &ctx) { haveAddedCleanup = true; } storedChildren.push_back(child); - ASTScopeAssert(!child->getParent(), "child should not already have parent"); - child->parent = this; - clearCachedSourceRangesOfMeAndAncestors(); } #pragma mark implementations of expansion @@ -921,8 +623,6 @@ ASTScopeImpl *ASTScopeImpl::expandAndBeCurrent(ScopeCreator &scopeCreator) { setWasExpanded(); - ASTScopeAssert(checkSourceRangeAfterExpansion(scopeCreator.getASTContext()), - "Bad range."); return insertionPoint; } @@ -945,17 +645,18 @@ ASTScopeImpl *ASTScopeImpl::expandAndBeCurrent(ScopeCreator &scopeCreator) { ASTScopeImpl *Scope::expandSpecifically(ScopeCreator &) { return this; } CREATES_NEW_INSERTION_POINT(ASTSourceFileScope) -CREATES_NEW_INSERTION_POINT(ConditionalClauseScope) CREATES_NEW_INSERTION_POINT(GuardStmtScope) CREATES_NEW_INSERTION_POINT(PatternEntryDeclScope) CREATES_NEW_INSERTION_POINT(GenericTypeOrExtensionScope) CREATES_NEW_INSERTION_POINT(BraceStmtScope) CREATES_NEW_INSERTION_POINT(TopLevelCodeScope) +CREATES_NEW_INSERTION_POINT(ConditionalClausePatternUseScope) NO_NEW_INSERTION_POINT(FunctionBodyScope) NO_NEW_INSERTION_POINT(AbstractFunctionDeclScope) NO_NEW_INSERTION_POINT(AttachedPropertyWrapperScope) NO_NEW_INSERTION_POINT(EnumElementScope) +NO_NEW_INSERTION_POINT(GuardStmtBodyScope) NO_NEW_INSERTION_POINT(ParameterListScope) NO_NEW_INSERTION_POINT(PatternEntryInitializerScope) @@ -963,6 +664,7 @@ NO_NEW_INSERTION_POINT(CaptureListScope) NO_NEW_INSERTION_POINT(CaseStmtScope) NO_NEW_INSERTION_POINT(CaseLabelItemScope) NO_NEW_INSERTION_POINT(CaseStmtBodyScope) +NO_NEW_INSERTION_POINT(ConditionalClauseInitializerScope) NO_NEW_INSERTION_POINT(ClosureParametersScope) NO_NEW_INSERTION_POINT(DefaultArgumentInitializerScope) NO_NEW_INSERTION_POINT(DoStmtScope) @@ -978,8 +680,6 @@ NO_NEW_INSERTION_POINT(WhileStmtScope) NO_EXPANSION(GenericParamScope) NO_EXPANSION(SpecializeAttributeScope) NO_EXPANSION(DifferentiableAttributeScope) -NO_EXPANSION(ConditionalClausePatternUseScope) -NO_EXPANSION(LookupParentDiversionScope) #undef CREATES_NEW_INSERTION_POINT #undef NO_NEW_INSERTION_POINT @@ -988,14 +688,15 @@ AnnotatedInsertionPoint ASTSourceFileScope::expandAScopeThatCreatesANewInsertionPoint( ScopeCreator &scopeCreator) { ASTScopeAssert(SF, "Must already have a SourceFile."); - ArrayRef decls = SF->getTopLevelDecls(); - // Assume that decls are only added at the end, in source order - std::vector newNodes(decls.begin(), decls.end()); - insertionPoint = - scopeCreator.addSiblingsToScopeTree(insertionPoint, this, newNodes); - // Too slow to perform all the time: - // ASTScopeAssert(scopeCreator->containsAllDeclContextsFromAST(), - // "ASTScope tree missed some DeclContexts or made some up"); + + SourceLoc endLoc = getSourceRangeOfThisASTNode().End; + + ASTScopeImpl *insertionPoint = this; + for (auto *d : SF->getTopLevelDecls()) { + insertionPoint = scopeCreator.addToScopeTreeAndReturnInsertionPoint( + ASTNode(d), insertionPoint, endLoc); + } + return {insertionPoint, "Next time decls are added they go here."}; } @@ -1008,7 +709,7 @@ ParameterListScope::expandAScopeThatDoesNotCreateANewInsertionPoint( for (ParamDecl *pd : params->getArray()) { if (pd->hasDefaultExpr()) scopeCreator - .constructExpandAndInsertUncheckable( + .constructExpandAndInsert( this, pd); } } @@ -1025,27 +726,31 @@ PatternEntryDeclScope::expandAScopeThatCreatesANewInsertionPoint( // so compute it ourselves. // Even if this predicate fails, there may be an initContext but // we cannot make a scope for it, since no source range. - if (patternEntry.getOriginalInit() && - isLocalizable(patternEntry.getOriginalInit())) { + if (patternEntry.getOriginalInit()) { + ASTScopeAssert( + patternEntry.getOriginalInit()->getSourceRange().isValid(), + "pattern initializer has invalid source range"); ASTScopeAssert( !getSourceManager().isBeforeInBuffer( patternEntry.getOriginalInit()->getStartLoc(), decl->getStartLoc()), "Original inits are always after the '='"); scopeCreator - .constructExpandAndInsertUncheckable( - this, decl, patternEntryIndex, vis); + .constructExpandAndInsert( + this, decl, patternEntryIndex); } // Add accessors for the variables in this pattern. patternEntry.getPattern()->forEachVariable([&](VarDecl *var) { - scopeCreator.addChildrenForAllLocalizableAccessorsInSourceOrder(var, this); + scopeCreator.addChildrenForParsedAccessors(var, this); }); - ASTScopeAssert(!handleUseBeforeDef, - "next line is wrong otherwise; would need a use scope"); + // In local context, the PatternEntryDeclScope becomes the insertion point, so + // that all any bindings introduecd by the pattern are in scope for subsequent + // lookups. + if (isLocalBinding) + return {this, "All code that follows is inside this scope"}; - return {getParent().get(), "When not handling use-before-def, succeeding " - "code just goes in the same scope as this one"}; + return {getParent().get(), "Global and type members do not introduce scopes"}; } void @@ -1056,42 +761,49 @@ PatternEntryInitializerScope::expandAScopeThatDoesNotCreateANewInsertionPoint( this); } + AnnotatedInsertionPoint -ConditionalClauseScope::expandAScopeThatCreatesANewInsertionPoint( +ConditionalClausePatternUseScope::expandAScopeThatCreatesANewInsertionPoint( ScopeCreator &scopeCreator) { - const StmtConditionElement &sec = getStmtConditionElement(); - switch (sec.getKind()) { - case StmtConditionElement::CK_Availability: - return {this, "No introduced variables"}; - case StmtConditionElement::CK_Boolean: - scopeCreator.addToScopeTree(sec.getBoolean(), this); - return {this, "No introduced variables"}; - case StmtConditionElement::CK_PatternBinding: - scopeCreator.addToScopeTree(sec.getInitializer(), this); - auto *const ccPatternUseScope = - scopeCreator.constructExpandAndInsertUncheckable< - ConditionalClausePatternUseScope>(this, sec.getPattern(), endLoc); - return {ccPatternUseScope, - "Succeeding code must be in scope of conditional variables"}; - } - ASTScope_unreachable("Unhandled StmtConditionKind in switch"); + auto *initializer = sec.getInitializer(); + if (!isa(initializer)) { + scopeCreator + .constructExpandAndInsert( + this, initializer); + } + + return {this, + "Succeeding code must be in scope of conditional clause pattern bindings"}; +} + +void +ConditionalClauseInitializerScope::expandAScopeThatDoesNotCreateANewInsertionPoint( + ScopeCreator &scopeCreator) { + scopeCreator.addToScopeTree(ASTNode(initializer), this); +} + +void +GuardStmtBodyScope::expandAScopeThatDoesNotCreateANewInsertionPoint(ScopeCreator & + scopeCreator) { + scopeCreator.addToScopeTree(ASTNode(body), this); } AnnotatedInsertionPoint GuardStmtScope::expandAScopeThatCreatesANewInsertionPoint(ScopeCreator & scopeCreator) { - ASTScopeImpl *conditionLookupParent = - createNestedConditionalClauseScopes(scopeCreator, stmt->getBody()); + createNestedConditionalClauseScopes(scopeCreator, endLoc); + // Add a child for the 'guard' body, which always exits. - // Parent is whole guard stmt scope, NOT the cond scopes - scopeCreator.addToScopeTree(stmt->getBody(), this); + // The lookup parent is whole guard stmt scope, NOT the cond scopes + auto *body = stmt->getBody(); + if (!body->empty()) { + scopeCreator + .constructExpandAndInsert( + conditionLookupParent, this, stmt->getBody()); + } - auto *const lookupParentDiversionScope = - scopeCreator - .constructExpandAndInsertUncheckable( - this, conditionLookupParent, stmt->getEndLoc()); - return {lookupParentDiversionScope, + return {conditionLookupParent, "Succeeding code must be in scope of guard variables"}; } @@ -1105,12 +817,15 @@ GenericTypeOrExtensionScope::expandAScopeThatCreatesANewInsertionPoint( AnnotatedInsertionPoint BraceStmtScope::expandAScopeThatCreatesANewInsertionPoint( ScopeCreator &scopeCreator) { - // TODO: remove the sort after fixing parser to create brace statement - // elements in source order - auto *insertionPoint = - scopeCreator.addSiblingsToScopeTree(this, this, stmt->getElements()); + ASTScopeImpl *insertionPoint = this; + for (auto nd : stmt->getElements()) { + insertionPoint = scopeCreator.addToScopeTreeAndReturnInsertionPoint( + nd, insertionPoint, endLoc); + } + if (auto *s = scopeCreator.getASTContext().Stats) ++s->getFrontendCounters().NumBraceStmtASTScopeExpansions; + return { insertionPoint, "For top-level code decls, need the scope under, say a guard statment."}; @@ -1120,14 +835,13 @@ AnnotatedInsertionPoint TopLevelCodeScope::expandAScopeThatCreatesANewInsertionPoint(ScopeCreator & scopeCreator) { - if (auto *body = - scopeCreator - .addToScopeTreeAndReturnInsertionPoint(decl->getBody(), this) - .getPtrOrNull()) - return {body, "So next top level code scope and put its decls in its body " - "under a guard statement scope (etc) from the last top level " - "code scope"}; - return {this, "No body"}; + auto *body = + scopeCreator + .addToScopeTreeAndReturnInsertionPoint(decl->getBody(), this, endLoc); + + return {body, "So next top level code scope and put its decls in its body " + "under a guard statement scope (etc) from the last top level " + "code scope"}; } #pragma mark expandAScopeThatDoesNotCreateANewInsertionPoint @@ -1149,7 +863,7 @@ void AbstractFunctionDeclScope::expandAScopeThatDoesNotCreateANewInsertionPoint( auto *params = decl->getParameters(); if (params->size() > 0) { - scopeCreator.constructExpandAndInsertUncheckable( + scopeCreator.constructExpandAndInsert( leaf, params, nullptr); } } @@ -1158,16 +872,14 @@ void AbstractFunctionDeclScope::expandAScopeThatDoesNotCreateANewInsertionPoint( // We create body scopes when there is no body for source kit to complete // erroneous code in bodies. if (decl->getBodySourceRange().isValid()) { - scopeCreator.constructExpandAndInsertUncheckable(leaf, - decl); + scopeCreator.constructExpandAndInsert(leaf, decl); } } void EnumElementScope::expandAScopeThatDoesNotCreateANewInsertionPoint( ScopeCreator &scopeCreator) { if (auto *pl = decl->getParameterList()) - scopeCreator.constructExpandAndInsertUncheckable( - this, pl, nullptr); + scopeCreator.constructExpandAndInsert(this, pl, nullptr); // The invariant that the raw value expression can never introduce a new scope // is checked in Parse. However, this guarantee is not future-proof. Compute // and add the raw value expression anyways just to be defensive. @@ -1183,20 +895,34 @@ void FunctionBodyScope::expandAScopeThatDoesNotCreateANewInsertionPoint( void IfStmtScope::expandAScopeThatDoesNotCreateANewInsertionPoint( ScopeCreator &scopeCreator) { + auto *thenStmt = stmt->getThenStmt(); + auto *elseStmt = stmt->getElseStmt(); + + SourceLoc endLoc = thenStmt->getEndLoc(); ASTScopeImpl *insertionPoint = - createNestedConditionalClauseScopes(scopeCreator, stmt->getThenStmt()); + createNestedConditionalClauseScopes(scopeCreator, endLoc); // The 'then' branch - scopeCreator.addToScopeTree(stmt->getThenStmt(), insertionPoint); + scopeCreator.addToScopeTree(thenStmt, insertionPoint); + + // Result builders can add an 'else' block consisting entirely of + // implicit expressions. In this case, the end location of the + // 'then' block is equal to the start location of the 'else' + // block, and the 'else' block source range is empty. + if (elseStmt && + thenStmt->getEndLoc() == elseStmt->getStartLoc() && + elseStmt->getStartLoc() == elseStmt->getEndLoc()) + return; // Add the 'else' branch, if needed. - scopeCreator.addToScopeTree(stmt->getElseStmt(), this); + scopeCreator.addToScopeTree(elseStmt, this); } void WhileStmtScope::expandAScopeThatDoesNotCreateANewInsertionPoint( ScopeCreator &scopeCreator) { + SourceLoc endLoc = stmt->getBody()->getEndLoc(); ASTScopeImpl *insertionPoint = - createNestedConditionalClauseScopes(scopeCreator, stmt->getBody()); + createNestedConditionalClauseScopes(scopeCreator, endLoc); scopeCreator.addToScopeTree(stmt->getBody(), insertionPoint); } @@ -1224,9 +950,10 @@ void SwitchStmtScope::expandAScopeThatDoesNotCreateANewInsertionPoint( scopeCreator.addToScopeTree(stmt->getSubjectExpr(), this); for (auto caseStmt : stmt->getCases()) { - if (isLocalizable(caseStmt)) - scopeCreator.ifUniqueConstructExpandAndInsert(this, - caseStmt); + ASTScopeAssert( + caseStmt->getSourceRange().isValid(), + "pattern initializer has invalid source range"); + scopeCreator.constructExpandAndInsert(this, caseStmt); } } @@ -1240,9 +967,10 @@ void ForEachStmtScope::expandAScopeThatDoesNotCreateANewInsertionPoint( // the body is implicit and it would overlap the source range of the expr // above. if (!stmt->getBody()->isImplicit()) { - if (isLocalizable(stmt->getBody())) - scopeCreator.constructExpandAndInsertUncheckable( - this, stmt); + ASTScopeAssert( + stmt->getBody()->getSourceRange().isValid(), + "pattern initializer has invalid source range"); + scopeCreator.constructExpandAndInsert(this, stmt); } } @@ -1256,13 +984,13 @@ void CaseStmtScope::expandAScopeThatDoesNotCreateANewInsertionPoint( ScopeCreator &scopeCreator) { for (auto &item : stmt->getCaseLabelItems()) { if (item.getGuardExpr()) { - scopeCreator.constructExpandAndInsertUncheckable( - this, item); + scopeCreator.constructExpandAndInsert(this, item); } } - scopeCreator.constructExpandAndInsertUncheckable( - this, stmt); + if (!stmt->getBody()->empty()) { + scopeCreator.constructExpandAndInsert(this, stmt); + } } void CaseLabelItemScope::expandAScopeThatDoesNotCreateANewInsertionPoint( @@ -1280,17 +1008,16 @@ void SubscriptDeclScope::expandAScopeThatDoesNotCreateANewInsertionPoint( scopeCreator.addChildrenForKnownAttributes(decl, this); auto *leaf = scopeCreator.addNestedGenericParamScopesToTree( decl, decl->getGenericParams(), this); - scopeCreator.constructExpandAndInsertUncheckable( + scopeCreator.constructExpandAndInsert( leaf, decl->getIndices(), decl->getAccessor(AccessorKind::Get)); - scopeCreator.addChildrenForAllLocalizableAccessorsInSourceOrder(decl, leaf); + scopeCreator.addChildrenForParsedAccessors(decl, leaf); } void CaptureListScope::expandAScopeThatDoesNotCreateANewInsertionPoint( ScopeCreator &scopeCreator) { auto *closureExpr = expr->getClosureBody(); scopeCreator - .ifUniqueConstructExpandAndInsert( - this, closureExpr); + .constructExpandAndInsert(this, closureExpr); } void ClosureParametersScope::expandAScopeThatDoesNotCreateANewInsertionPoint( @@ -1404,31 +1131,34 @@ TypeAliasScope::createTrailingWhereClauseScope(ASTScopeImpl *parent, #pragma mark misc ASTScopeImpl *LabeledConditionalStmtScope::createNestedConditionalClauseScopes( - ScopeCreator &scopeCreator, const Stmt *const afterConds) { + ScopeCreator &scopeCreator, SourceLoc endLoc) { auto *stmt = getLabeledConditionalStmt(); ASTScopeImpl *insertionPoint = this; - for (unsigned i = 0; i < stmt->getCond().size(); ++i) { - insertionPoint = - scopeCreator - .constructExpandAndInsertUncheckable( - insertionPoint, stmt, i, afterConds->getStartLoc()); + for (auto &sec : stmt->getCond()) { + switch (sec.getKind()) { + case StmtConditionElement::CK_Availability: + break; + case StmtConditionElement::CK_Boolean: + scopeCreator.addToScopeTree(sec.getBoolean(), insertionPoint); + break; + case StmtConditionElement::CK_PatternBinding: + insertionPoint = + scopeCreator.constructExpandAndInsert< + ConditionalClausePatternUseScope>( + insertionPoint, sec, endLoc); + break; + } } return insertionPoint; } AbstractPatternEntryScope::AbstractPatternEntryScope( - PatternBindingDecl *declBeingScoped, unsigned entryIndex, - DeclVisibilityKind vis) - : decl(declBeingScoped), patternEntryIndex(entryIndex), vis(vis) { + PatternBindingDecl *declBeingScoped, unsigned entryIndex) + : decl(declBeingScoped), patternEntryIndex(entryIndex) { ASTScopeAssert(entryIndex < declBeingScoped->getPatternList().size(), "out of bounds"); } -bool ASTScopeImpl::isATypeDeclScope() const { - Decl *const pd = getDeclIfAny().getPtrOrNull(); - return pd && (isa(pd) || isa(pd)); -} - #pragma mark new operators void *ASTScopeImpl::operator new(size_t bytes, const ASTContext &ctx, unsigned alignment) { @@ -1457,8 +1187,9 @@ void FunctionBodyScope::expandBody(ScopeCreator &scopeCreator) { void GenericTypeOrExtensionScope::expandBody(ScopeCreator &) {} void IterableTypeScope::expandBody(ScopeCreator &scopeCreator) { - auto nodes = asNodeVector(getIterableDeclContext().get()->getMembers()); - scopeCreator.addSiblingsToScopeTree(this, this, nodes); + for (auto *d : getIterableDeclContext().get()->getMembers()) + scopeCreator.addToScopeTree(ASTNode(d), this); + if (auto *s = scopeCreator.getASTContext().Stats) ++s->getFrontendCounters().NumIterableTypeBodyASTScopeExpansions; } @@ -1470,43 +1201,6 @@ ScopeCreator &ASTScopeImpl::getScopeCreator() { ScopeCreator &ASTSourceFileScope::getScopeCreator() { return *scopeCreator; } -#pragma mark getReferrent - - // These are the scopes whose ASTNodes (etc) might be duplicated in the AST - // getReferrent is the cookie used to dedup them - -#define GET_REFERRENT(Scope, x) \ - NullablePtr Scope::getReferrent() const { \ - return UniquePointerCalculator().visit(x); \ - } - -GET_REFERRENT(AbstractFunctionDeclScope, getDecl()) -// If the PatternBindingDecl is a dup, detect it for the first -// PatternEntryDeclScope; the others are subscopes. -GET_REFERRENT(PatternEntryDeclScope, getPattern()) -GET_REFERRENT(TopLevelCodeScope, getDecl()) -GET_REFERRENT(SubscriptDeclScope, getDecl()) -GET_REFERRENT(GenericParamScope, paramList->getParams()[index]) -GET_REFERRENT(AbstractStmtScope, getStmt()) -GET_REFERRENT(CaptureListScope, getExpr()) -GET_REFERRENT(ClosureParametersScope, getExpr()) -GET_REFERRENT(SpecializeAttributeScope, specializeAttr) -GET_REFERRENT(DifferentiableAttributeScope, differentiableAttr) -GET_REFERRENT(AttachedPropertyWrapperScope, attr) -GET_REFERRENT(GenericTypeOrExtensionScope, portion->getReferrentOfScope(this)); - -const Decl * -Portion::getReferrentOfScope(const GenericTypeOrExtensionScope *s) const { - return nullptr; -}; - -const Decl *GenericTypeOrExtensionWholePortion::getReferrentOfScope( - const GenericTypeOrExtensionScope *s) const { - return s->getDecl(); -}; - -#undef GET_REFERRENT - #pragma mark currency NullablePtr ASTScopeImpl::insertionPointForDeferredExpansion() { return nullptr; @@ -1540,75 +1234,6 @@ IterableTypeBodyPortion::insertionPointForDeferredExpansion( #pragma mark verification -namespace { -class LocalizableDeclContextCollector : public ASTWalker { - -public: - llvm::DenseMap declContexts; - - void record(const DeclContext *dc) { - if (dc) - declContexts.insert({dc, 0}); - } - - bool walkToDeclPre(Decl *D) override { - // catchForDebugging(D, "DictionaryBridging.swift", 694); - if (const auto *dc = dyn_cast(D)) - record(dc); - if (isa(D)) - return false; - if (auto *pd = dyn_cast(D)) - record(pd->getDefaultArgumentInitContext()); - else if (auto *pbd = dyn_cast(D)) - recordInitializers(pbd); - else if (auto *vd = dyn_cast(D)) { - vd->visitParsedAccessors([&](AccessorDecl *ad) { - ad->walk(*this); - }); - } - return ASTWalker::walkToDeclPre(D); - } - - std::pair walkToExprPre(Expr *E) override { - if (const auto *ce = dyn_cast(E)) - record(ce); - return ASTWalker::walkToExprPre(E); - } - -private: - void recordInitializers(PatternBindingDecl *pbd) { - for (auto idx : range(pbd->getNumPatternEntries())) - record(pbd->getInitContext(idx)); - } - - void catchForDebugging(Decl *D, const char *file, const unsigned line) { - auto &SM = D->getASTContext().SourceMgr; - auto loc = D->getStartLoc(); - if (!loc.isValid()) - return; - auto bufID = SM.findBufferContainingLoc(loc); - auto f = SM.getIdentifierForBuffer(bufID); - auto lin = SM.getLineAndColumnInBuffer(loc).first; - if (f.endswith(file) && lin == line) - if (isa(D)) - llvm::errs() << "*** catchForDebugging: " << lin << " ***\n"; - } -}; -} // end namespace - -llvm::DenseMap -ScopeCreator::findLocalizableDeclContextsInAST() const { - LocalizableDeclContextCollector collector; - sourceFileScope->SF->walk(collector); - // Walker omits the top - collector.record(sourceFileScope->SF); - return collector.declContexts; -} - -bool ASTSourceFileScope::crossCheckWithAST() { - return scopeCreator->containsAllDeclContextsFromAST(); -} - void ast_scope::simple_display(llvm::raw_ostream &out, const ScopeCreator *scopeCreator) { scopeCreator->print(out); diff --git a/lib/AST/ASTScopeLookup.cpp b/lib/AST/ASTScopeLookup.cpp index 0acd36052e74e..39db7f27c725d 100644 --- a/lib/AST/ASTScopeLookup.cpp +++ b/lib/AST/ASTScopeLookup.cpp @@ -19,6 +19,7 @@ #include "swift/AST/ASTWalker.h" #include "swift/AST/Decl.h" #include "swift/AST/Expr.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/Initializer.h" #include "swift/AST/LazyResolver.h" #include "swift/AST/Module.h" @@ -36,21 +37,16 @@ using namespace namelookup; using namespace ast_scope; void ASTScopeImpl::unqualifiedLookup( - SourceFile *sourceFile, const DeclNameRef name, const SourceLoc loc, - DeclConsumer consumer) { + SourceFile *sourceFile, const SourceLoc loc, DeclConsumer consumer) { const auto *start = - findStartingScopeForLookup(sourceFile, name, loc); + findStartingScopeForLookup(sourceFile, loc); if (start) start->lookup(nullptr, nullptr, consumer); } const ASTScopeImpl *ASTScopeImpl::findStartingScopeForLookup( - SourceFile *sourceFile, const DeclNameRef name, const SourceLoc loc) { + SourceFile *sourceFile, const SourceLoc loc) { auto *const fileScope = sourceFile->getScope().impl; - // Parser may have added decls to source file, since previous lookup - if (name.isOperator()) - return fileScope; // operators always at file scope - const auto *innermost = fileScope->findInnermostEnclosingScope(loc, nullptr); ASTScopeAssert(innermost->getWasExpanded(), "If looking in a scope, it must have been expanded."); @@ -76,22 +72,14 @@ ASTScopeImpl *ASTScopeImpl::findInnermostEnclosingScopeImpl( scopeCreator); } -bool ASTScopeImpl::checkSourceRangeOfThisASTNode() const { - const auto r = getSourceRangeOfThisASTNode(); - (void)r; - ASTScopeAssert(!getSourceManager().isBeforeInBuffer(r.End, r.Start), - "Range is backwards."); - return true; -} - /// If the \p loc is in a new buffer but \p range is not, consider the location /// is at the start of replaced range. Otherwise, returns \p loc as is. static SourceLoc translateLocForReplacedRange(SourceManager &sourceMgr, - SourceRange range, + CharSourceRange range, SourceLoc loc) { if (const auto &replacedRange = sourceMgr.getReplacedRange()) { if (sourceMgr.rangeContainsTokenLoc(replacedRange.New, loc) && - !sourceMgr.rangeContains(replacedRange.New, range)) { + !sourceMgr.rangeContainsTokenLoc(replacedRange.New, range.getStart())) { return replacedRange.Original.Start; } } @@ -105,17 +93,19 @@ ASTScopeImpl::findChildContaining(SourceLoc loc, auto *const *child = llvm::lower_bound( getChildren(), loc, [&sourceMgr](const ASTScopeImpl *scope, SourceLoc loc) { - ASTScopeAssert(scope->checkSourceRangeOfThisASTNode(), "Bad range."); - auto rangeOfScope = scope->getSourceRangeOfScope(); + auto rangeOfScope = scope->getCharSourceRangeOfScope(sourceMgr); + ASTScopeAssert(!sourceMgr.isBeforeInBuffer(rangeOfScope.getEnd(), + rangeOfScope.getStart()), + "Source range is backwards"); loc = translateLocForReplacedRange(sourceMgr, rangeOfScope, loc); - return -1 == ASTScopeImpl::compare(rangeOfScope, loc, sourceMgr, - /*ensureDisjoint=*/false); + return (rangeOfScope.getEnd() == loc || + sourceMgr.isBeforeInBuffer(rangeOfScope.getEnd(), loc)); }); if (child != getChildren().end()) { - auto rangeOfScope = (*child)->getSourceRangeOfScope(); + auto rangeOfScope = (*child)->getCharSourceRangeOfScope(sourceMgr); loc = translateLocForReplacedRange(sourceMgr, rangeOfScope, loc); - if (sourceMgr.rangeContainsTokenLoc(rangeOfScope, loc)) + if (rangeOfScope.contains(loc)) return *child; } @@ -226,7 +216,7 @@ bool ASTScopeImpl::lookInGenericParametersOf( SmallVector bindings; for (auto *param : paramList.get()->getParams()) bindings.push_back(param); - if (consumer.consume(bindings, DeclVisibilityKind::GenericParameter)) + if (consumer.consume(bindings)) return true; return false; } @@ -253,7 +243,7 @@ bool GenericTypeOrExtensionWhereOrBodyPortion::lookupMembersOf( auto nt = scope->getCorrespondingNominalTypeDecl().getPtrOrNull(); if (!nt) return false; - return consumer.lookInMembers(scope->getDeclContext().get(), nt); + return consumer.lookInMembers(scope->getGenericContext(), nt); } bool GenericTypeOrExtensionWherePortion::lookupMembersOf( @@ -271,32 +261,66 @@ bool GenericTypeOrExtensionScope::areMembersVisibleFromWhereClause() const { return isa(decl) || isa(decl); } +#pragma mark custom lookup parent behavior + +NullablePtr +PatternEntryInitializerScope::getLookupParent() const { + auto parent = getParent().get(); + ASTScopeAssert(parent->getClassName() == "PatternEntryDeclScope", + "PatternEntryInitializerScope in unexpected place"); + + // Lookups from inside a pattern binding initializer skip the parent + // scope that introduces bindings bound by the pattern, since we + // want this to work: + // + // func f(x: Int) { + // let x = x + // print(x) + // } + return parent->getLookupParent(); +} + +NullablePtr +ConditionalClauseInitializerScope::getLookupParent() const { + auto parent = getParent().get(); + ASTScopeAssert(parent->getClassName() == "ConditionalClausePatternUseScope", + "ConditionalClauseInitializerScope in unexpected place"); + + // Lookups from inside a conditional clause initializer skip the parent + // scope that introduces bindings bound by the pattern, since we + // want this to work: + // + // func f(x: Int?) { + // guard let x = x else { return } + // print(x) + // } + return parent->getLookupParent(); +} + #pragma mark looking in locals or members - locals bool GenericParamScope::lookupLocalsOrMembers(DeclConsumer consumer) const { auto *param = paramList->getParams()[index]; - return consumer.consume({param}, DeclVisibilityKind::GenericParameter); + return consumer.consume({param}); } bool PatternEntryDeclScope::lookupLocalsOrMembers(DeclConsumer consumer) const { - if (vis != DeclVisibilityKind::LocalVariable) - return false; // look in self type will find this later - return lookupLocalBindingsInPattern(getPattern(), vis, consumer); + if (!isLocalBinding) + return false; + return lookupLocalBindingsInPattern(getPattern(), consumer); } bool ForEachPatternScope::lookupLocalsOrMembers(DeclConsumer consumer) const { - return lookupLocalBindingsInPattern( - stmt->getPattern(), DeclVisibilityKind::LocalVariable, consumer); + return lookupLocalBindingsInPattern(stmt->getPattern(), consumer); } bool CaseLabelItemScope::lookupLocalsOrMembers(DeclConsumer consumer) const { - return lookupLocalBindingsInPattern( - item.getPattern(), DeclVisibilityKind::LocalVariable, consumer); + return lookupLocalBindingsInPattern(item.getPattern(), consumer); } bool CaseStmtBodyScope::lookupLocalsOrMembers(DeclConsumer consumer) const { for (auto *var : stmt->getCaseBodyVariablesOrEmptyArray()) - if (consumer.consume({var}, DeclVisibilityKind::LocalVariable)) + if (consumer.consume({var})) return true; return false; @@ -306,13 +330,12 @@ bool FunctionBodyScope::lookupLocalsOrMembers( DeclConsumer consumer) const { if (auto *paramList = decl->getParameters()) { for (auto *paramDecl : *paramList) - if (consumer.consume({paramDecl}, DeclVisibilityKind::FunctionParameter)) + if (consumer.consume({paramDecl})) return true; } if (decl->getDeclContext()->isTypeContext()) { - return consumer.consume({decl->getImplicitSelfDecl()}, - DeclVisibilityKind::FunctionParameter); + return consumer.consume({decl->getImplicitSelfDecl()}); } // Consider \c var t: T { (did/will/)get/set { ... t }} @@ -321,7 +344,7 @@ bool FunctionBodyScope::lookupLocalsOrMembers( // then t needs to be found as a local binding: if (auto *accessor = dyn_cast(decl)) { if (auto *storage = accessor->getStorage()) - if (consumer.consume({storage}, DeclVisibilityKind::LocalVariable)) + if (consumer.consume({storage})) return true; } @@ -332,7 +355,7 @@ bool SpecializeAttributeScope::lookupLocalsOrMembers( DeclConsumer consumer) const { if (auto *params = whatWasSpecialized->getGenericParams()) for (auto *param : params->getParams()) - if (consumer.consume({param}, DeclVisibilityKind::GenericParameter)) + if (consumer.consume({param})) return true; return false; } @@ -342,7 +365,7 @@ bool DifferentiableAttributeScope::lookupLocalsOrMembers( auto visitAbstractFunctionDecl = [&](AbstractFunctionDecl *afd) { if (auto *params = afd->getGenericParams()) for (auto *param : params->getParams()) - if (consumer.consume({param}, DeclVisibilityKind::GenericParameter)) + if (consumer.consume({param})) return true; return false; }; @@ -357,20 +380,16 @@ bool DifferentiableAttributeScope::lookupLocalsOrMembers( } bool BraceStmtScope::lookupLocalsOrMembers(DeclConsumer consumer) const { - // All types and functions are visible anywhere within a brace statement - // scope. When ordering matters (i.e. var decl) we will have split the brace - // statement into nested scopes. - // - // Don't stop at the first one, there may be local funcs with same base name - // and want them all. - SmallVector localBindings; - for (auto braceElement : stmt->getElements()) { - if (auto localBinding = braceElement.dyn_cast()) { - if (auto *vd = dyn_cast(localBinding)) - localBindings.push_back(vd); - } - } - return consumer.consume(localBindings, DeclVisibilityKind::LocalVariable); + if (consumer.consume(localFuncsAndTypes)) + return true; + + if (consumer.consumePossiblyNotInScope(localVars)) + return true; + + if (consumer.finishLookupInBraceStmt(stmt)) + return true; + + return false; } bool PatternEntryInitializerScope::lookupLocalsOrMembers( @@ -380,8 +399,7 @@ bool PatternEntryInitializerScope::lookupLocalsOrMembers( decl->getInitContext(0)); if (initContext) { if (auto *selfParam = initContext->getImplicitSelfDecl()) { - return consumer.consume({selfParam}, - DeclVisibilityKind::FunctionParameter); + return consumer.consume({selfParam}); } } return false; @@ -389,9 +407,7 @@ bool PatternEntryInitializerScope::lookupLocalsOrMembers( bool CaptureListScope::lookupLocalsOrMembers(DeclConsumer consumer) const { for (auto &e : expr->getCaptureList()) { - if (consumer.consume( - {e.Var}, - DeclVisibilityKind::LocalVariable)) // or FunctionParameter?? + if (consumer.consume({e.Var})) return true; } return false; @@ -400,26 +416,24 @@ bool CaptureListScope::lookupLocalsOrMembers(DeclConsumer consumer) const { bool ClosureParametersScope::lookupLocalsOrMembers( DeclConsumer consumer) const { for (auto param : *closureExpr->getParameters()) - if (consumer.consume({param}, DeclVisibilityKind::FunctionParameter)) + if (consumer.consume({param})) return true; return false; } bool ConditionalClausePatternUseScope::lookupLocalsOrMembers( DeclConsumer consumer) const { - return lookupLocalBindingsInPattern( - pattern, DeclVisibilityKind::LocalVariable, consumer); + return lookupLocalBindingsInPattern(sec.getPattern(), consumer); } bool ASTScopeImpl::lookupLocalBindingsInPattern(const Pattern *p, - DeclVisibilityKind vis, DeclConsumer consumer) { if (!p) return false; bool isDone = false; p->forEachVariable([&](VarDecl *var) { if (!isDone) - isDone = consumer.consume({var}, vis); + isDone = consumer.consume({var}); }); return isDone; } @@ -486,11 +500,7 @@ bool ASTScopeImpl::isLabeledStmtLookupTerminator() const { return true; } -bool LookupParentDiversionScope::isLabeledStmtLookupTerminator() const { - return false; -} - -bool ConditionalClauseScope::isLabeledStmtLookupTerminator() const { +bool GuardStmtBodyScope::isLabeledStmtLookupTerminator() const { return false; } @@ -510,6 +520,10 @@ bool CaseStmtBodyScope::isLabeledStmtLookupTerminator() const { return false; } +bool PatternEntryDeclScope::isLabeledStmtLookupTerminator() const { + return false; +} + llvm::SmallVector ASTScopeImpl::lookupLabeledStmts(SourceFile *sourceFile, SourceLoc loc) { // Find the innermost scope from which to start our search. diff --git a/lib/AST/ASTScopePrinting.cpp b/lib/AST/ASTScopePrinting.cpp index 5e45c8a95699d..b9f84266768e4 100644 --- a/lib/AST/ASTScopePrinting.cpp +++ b/lib/AST/ASTScopePrinting.cpp @@ -19,6 +19,7 @@ #include "swift/AST/ASTWalker.h" #include "swift/AST/Decl.h" #include "swift/AST/Expr.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/Initializer.h" #include "swift/AST/LazyResolver.h" #include "swift/AST/Module.h" @@ -56,10 +57,6 @@ void ASTScopeImpl::dumpOneScopeMapLocation( auto *locScope = findInnermostEnclosingScope(loc, &llvm::errs()); locScope->print(llvm::errs(), 0, false, false); - // Dump the AST context, too. - if (auto *dc = locScope->getDeclContext().getPtrOrNull()) - dc->printContext(llvm::errs()); - namelookup::ASTScopeDeclGatherer gatherer; // Print the local bindings introduced by this scope. locScope->lookupLocalsOrMembers(gatherer); @@ -126,9 +123,7 @@ static void printSourceRange(llvm::raw_ostream &out, const SourceRange range, } void ASTScopeImpl::printRange(llvm::raw_ostream &out) const { - if (!isSourceRangeCached(true)) - out << "(uncached) "; - SourceRange range = computeSourceRangeOfScope(/*omitAssertions=*/true); + SourceRange range = getSourceRangeOfThisASTNode(/*omitAssertions=*/true); printSourceRange(out, range, getSourceManager()); } @@ -179,18 +174,13 @@ void AbstractPatternEntryScope::printSpecifics(llvm::raw_ostream &out) const { }); } -void ConditionalClauseScope::printSpecifics(llvm::raw_ostream &out) const { - ASTScopeImpl::printSpecifics(out); - out << "index " << index; -} - void SubscriptDeclScope::printSpecifics(llvm::raw_ostream &out) const { decl->dumpRef(out); } void ConditionalClausePatternUseScope::printSpecifics( llvm::raw_ostream &out) const { - pattern->print(out); + sec.getPattern()->print(out); } bool GenericTypeOrExtensionScope::doesDeclHaveABody() const { return false; } diff --git a/lib/AST/ASTScopeSourceRange.cpp b/lib/AST/ASTScopeSourceRange.cpp index fc86ba1c2f84b..7cbc5ce584403 100644 --- a/lib/AST/ASTScopeSourceRange.cpp +++ b/lib/AST/ASTScopeSourceRange.cpp @@ -19,6 +19,7 @@ #include "swift/AST/ASTWalker.h" #include "swift/AST/Decl.h" #include "swift/AST/Expr.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/Initializer.h" #include "swift/AST/LazyResolver.h" #include "swift/AST/Module.h" @@ -35,170 +36,57 @@ using namespace swift; using namespace ast_scope; -static SourceLoc getLocEncompassingPotentialLookups(const SourceManager &, - SourceLoc endLoc); static SourceLoc getLocAfterExtendedNominal(const ExtensionDecl *); -SourceRange ASTScopeImpl::widenSourceRangeForIgnoredASTNodes( - const SourceRange range) const { - if (range.isInvalid()) - return sourceRangeOfIgnoredASTNodes; - auto r = range; - if (sourceRangeOfIgnoredASTNodes.isValid()) - r.widen(sourceRangeOfIgnoredASTNodes); - return r; -} +void ASTScopeImpl::checkSourceRangeBeforeAddingChild(ASTScopeImpl *child, + const ASTContext &ctx) const { + auto &sourceMgr = ctx.SourceMgr; -SourceRange -ASTScopeImpl::widenSourceRangeForChildren(const SourceRange range, - const bool omitAssertions) const { - if (getChildren().empty()) { - ASTScopeAssert(omitAssertions || range.Start.isValid(), "Bad range."); - return range; - } - const auto childStart = - getChildren().front()->getSourceRangeOfScope(omitAssertions).Start; - const auto childEnd = - getChildren().back()->getSourceRangeOfScope(omitAssertions).End; - auto childRange = SourceRange(childStart, childEnd); - ASTScopeAssert(omitAssertions || childRange.isValid(), "Bad range."); - - if (range.isInvalid()) - return childRange; - auto r = range; - - // HACK: For code completion. If the range of the child is from another - // source buffer, don't widen using that range. - if (const auto &replacedRange = getSourceManager().getReplacedRange()) { - if (getSourceManager().rangeContains(replacedRange.Original, range) && - getSourceManager().rangeContains(replacedRange.New, childRange)) - return r; - } - r.widen(childRange); - return r; -} + auto range = getCharSourceRangeOfScope(sourceMgr); -bool ASTScopeImpl::checkSourceRangeAfterExpansion(const ASTContext &ctx) const { - ASTScopeAssert(getSourceRangeOfThisASTNode().isValid() || - !getChildren().empty(), - "need to be able to find source range"); - ASTScopeAssert(verifyThatChildrenAreContainedWithin(getSourceRangeOfScope()), - "Search will fail"); - ASTScopeAssert( - checkLazySourceRange(ctx), - "Lazy scopes must have compatible ranges before and after expansion"); + auto childCharRange = child->getCharSourceRangeOfScope(sourceMgr); - return true; -} - -#pragma mark validation & debugging - -bool ASTScopeImpl::hasValidSourceRange() const { - const auto sourceRange = getSourceRangeOfScope(); - return sourceRange.Start.isValid() && sourceRange.End.isValid() && - !getSourceManager().isBeforeInBuffer(sourceRange.End, - sourceRange.Start); -} - -bool ASTScopeImpl::hasValidSourceRangeOfIgnoredASTNodes() const { - return sourceRangeOfIgnoredASTNodes.isValid(); -} + bool childContainedInParent = [&]() { + // HACK: For code completion. Handle replaced range. + if (const auto &replacedRange = sourceMgr.getReplacedRange()) { + auto originalRange = Lexer::getCharSourceRangeFromSourceRange( + sourceMgr, replacedRange.Original); + auto newRange = Lexer::getCharSourceRangeFromSourceRange( + sourceMgr, replacedRange.New); -bool ASTScopeImpl::precedesInSource(const ASTScopeImpl *next) const { - if (!hasValidSourceRange() || !next->hasValidSourceRange()) - return false; - return !getSourceManager().isBeforeInBuffer( - next->getSourceRangeOfScope().Start, getSourceRangeOfScope().End); -} + if (range.contains(originalRange) && + newRange.contains(childCharRange)) + return true; + } -bool ASTScopeImpl::verifyThatChildrenAreContainedWithin( - const SourceRange range) const { - // assumes children are already in order - if (getChildren().empty()) - return true; - const SourceRange rangeOfChildren = - SourceRange(getChildren().front()->getSourceRangeOfScope().Start, - getChildren().back()->getSourceRangeOfScope().End); - if (getSourceManager().rangeContains(range, rangeOfChildren)) - return true; + return range.contains(childCharRange); + }(); - // HACK: For code completion. Handle replaced range. - if (const auto &replacedRange = getSourceManager().getReplacedRange()) { - if (getSourceManager().rangeContains(replacedRange.Original, range) && - getSourceManager().rangeContains(replacedRange.New, rangeOfChildren)) - return true; + if (!childContainedInParent) { + auto &out = verificationError() << "child not contained in its parent:\n"; + child->print(out); + out << "\n***Parent node***\n"; + this->print(out); + abort(); } - auto &out = verificationError() << "children not contained in its parent\n"; - if (getChildren().size() == 1) { - out << "\n***Only Child node***\n"; - getChildren().front()->print(out); - } else { - out << "\n***First Child node***\n"; - getChildren().front()->print(out); - out << "\n***Last Child node***\n"; - getChildren().back()->print(out); - } - out << "\n***Parent node***\n"; - this->print(out); - abort(); -} - -bool ASTScopeImpl::verifyThatThisNodeComeAfterItsPriorSibling() const { - auto priorSibling = getPriorSibling(); - if (!priorSibling) - return true; - if (priorSibling.get()->precedesInSource(this)) - return true; - auto &out = verificationError() << "unexpected out-of-order nodes\n"; - out << "\n***Penultimate child node***\n"; - priorSibling.get()->print(out); - out << "\n***Last Child node***\n"; - print(out); - out << "\n***Parent node***\n"; - getParent().get()->print(out); - // llvm::errs() << "\n\nsource:\n" - // << getSourceManager() - // .getRangeForBuffer( - // getSourceFile()->getBufferID().getValue()) - // .str(); - ASTScope_unreachable("unexpected out-of-order nodes"); - return false; -} - -NullablePtr ASTScopeImpl::getPriorSibling() const { - auto parent = getParent(); - if (!parent) - return nullptr; - auto const &siblingsAndMe = parent.get()->getChildren(); - // find myIndex, which is probably the last one - int myIndex = -1; - for (int i = siblingsAndMe.size() - 1; i >= 0; --i) { - if (siblingsAndMe[i] == this) { - myIndex = i; - break; + if (!storedChildren.empty()) { + auto previousChild = storedChildren.back(); + auto endOfPreviousChild = previousChild->getCharSourceRangeOfScope( + sourceMgr).getEnd(); + + if (childCharRange.getStart() != endOfPreviousChild && + !sourceMgr.isBeforeInBuffer(endOfPreviousChild, + childCharRange.getStart())) { + auto &out = verificationError() << "child overlaps previous child:\n"; + child->print(out); + out << "\n***Previous child\n"; + previousChild->print(out); + out << "\n***Parent node***\n"; + this->print(out); + abort(); } } - ASTScopeAssert(myIndex != -1, "I have been disowned!"); - if (myIndex == 0) - return nullptr; - return siblingsAndMe[myIndex - 1]; -} - -bool ASTScopeImpl::doesRangeMatch(unsigned start, unsigned end, StringRef file, - StringRef className) { - if (!className.empty() && className != getClassName()) - return false; - const auto &SM = getSourceManager(); - const auto r = getSourceRangeOfScope(true); - if (start && start != SM.getLineAndColumnInBuffer(r.Start).first) - return false; - if (end && end != SM.getLineAndColumnInBuffer(r.End).first) - return false; - if (file.empty()) - return true; - const auto buf = SM.findBufferContainingLoc(r.Start); - return SM.getIdentifierForBuffer(buf).endswith(file); } #pragma mark getSourceRangeOfThisASTNode @@ -220,12 +108,12 @@ SourceRange FunctionBodyScope::getSourceRangeOfThisASTNode( SourceRange TopLevelCodeScope::getSourceRangeOfThisASTNode( const bool omitAssertions) const { - return decl->getSourceRange(); + return SourceRange(decl->getStartLoc(), endLoc); } SourceRange SubscriptDeclScope::getSourceRangeOfThisASTNode( const bool omitAssertions) const { - return decl->getSourceRange(); + return decl->getSourceRangeIncludingAttrs(); } SourceRange @@ -240,14 +128,15 @@ SourceRange AbstractStmtScope::getSourceRangeOfThisASTNode( SourceRange DefaultArgumentInitializerScope::getSourceRangeOfThisASTNode( const bool omitAssertions) const { - if (auto *dv = decl->getStructuralDefaultExpr()) - return dv->getSourceRange(); - return SourceRange(); + return decl->getStructuralDefaultExpr()->getSourceRange(); } SourceRange PatternEntryDeclScope::getSourceRangeOfThisASTNode( const bool omitAssertions) const { - return getPatternEntry().getSourceRange(); + SourceRange range = getPatternEntry().getSourceRange(); + if (endLoc.hasValue()) + range.End = *endLoc; + return range; } SourceRange PatternEntryInitializerScope::getSourceRangeOfThisASTNode( @@ -260,20 +149,14 @@ SourceRange PatternEntryInitializerScope::getSourceRangeOfThisASTNode( SourceRange GenericParamScope::getSourceRangeOfThisASTNode( const bool omitAssertions) const { - auto nOrE = holder; - // A protocol's generic parameter list is not written in source, and - // is visible from the start of the body. - if (auto *protoDecl = dyn_cast(nOrE)) - return SourceRange(protoDecl->getBraces().Start, protoDecl->getEndLoc()); - const auto startLoc = paramList->getSourceRange().Start; - const auto validStartLoc = - startLoc.isValid() ? startLoc : holder->getStartLoc(); - // Since ExtensionScope (whole portion) range doesn't start till after the - // extended nominal, the range here must be pushed back, too. + // We want to ensure the extended type is not part of the generic + // parameter scope. if (auto const *const ext = dyn_cast(holder)) { return SourceRange(getLocAfterExtendedNominal(ext), ext->getEndLoc()); } - return SourceRange(validStartLoc, holder->getEndLoc()); + + // For all other declarations, generic parameters are visible everywhere. + return holder->getSourceRange(); } SourceRange ASTSourceFileScope::getSourceRangeOfThisASTNode( @@ -356,20 +239,7 @@ SourceRange AbstractFunctionDeclScope::getSourceRangeOfThisASTNode( SourceRange ParameterListScope::getSourceRangeOfThisASTNode( const bool omitAssertions) const { - auto rangeForGoodInput = params->getSourceRange(); - auto r = SourceRange(rangeForGoodInput.Start, - fixupEndForBadInput(rangeForGoodInput)); - ASTScopeAssert(getSourceManager().rangeContains( - getParent().get()->getSourceRangeOfThisASTNode(true), r), - "Parameters not within function?!"); - return r; -} - -SourceLoc ParameterListScope::fixupEndForBadInput( - const SourceRange rangeForGoodInput) const { - const auto s = rangeForGoodInput.Start; - const auto e = rangeForGoodInput.End; - return getSourceManager().isBeforeInBuffer(s, e) ? e : s; + return params->getSourceRange(); } SourceRange ForEachPatternScope::getSourceRangeOfThisASTNode( @@ -400,26 +270,19 @@ BraceStmtScope::getSourceRangeOfThisASTNode(const bool omitAssertions) const { // 'in' keyword, when present. if (auto closure = parentClosureIfAny()) { if (closure.get()->getInLoc().isValid()) - return SourceRange(closure.get()->getInLoc(), stmt->getEndLoc()); + return SourceRange(closure.get()->getInLoc(), endLoc); } - return stmt->getSourceRange(); + return SourceRange(stmt->getStartLoc(), endLoc); } -SourceRange ConditionalClauseScope::getSourceRangeOfThisASTNode( +SourceRange ConditionalClauseInitializerScope::getSourceRangeOfThisASTNode( const bool omitAssertions) const { - // From the start of this particular condition to the start of the - // then/body part. - const auto startLoc = getStmtConditionElement().getStartLoc(); - return startLoc.isValid() - ? SourceRange(startLoc, endLoc) - : SourceRange(endLoc); + return initializer->getSourceRange(); } SourceRange ConditionalClausePatternUseScope::getSourceRangeOfThisASTNode( const bool omitAssertions) const { - // For a guard continuation, the scope extends from the end of the 'else' - // to the end of the continuation. - return SourceRange(startLoc); + return SourceRange(sec.getInitializer()->getStartLoc(), endLoc); } SourceRange @@ -441,228 +304,40 @@ SourceRange ClosureParametersScope::getSourceRangeOfThisASTNode( SourceRange AttachedPropertyWrapperScope::getSourceRangeOfThisASTNode( const bool omitAssertions) const { - return sourceRangeWhenCreated; + return attr->getRange(); } -SourceRange LookupParentDiversionScope::getSourceRangeOfThisASTNode( +SourceRange GuardStmtScope::getSourceRangeOfThisASTNode( const bool omitAssertions) const { - return SourceRange(startLoc); + return SourceRange(getStmt()->getStartLoc(), endLoc); } -#pragma mark source range caching - -SourceRange -ASTScopeImpl::getSourceRangeOfScope(const bool omitAssertions) const { - if (!isSourceRangeCached(omitAssertions)) - computeAndCacheSourceRangeOfScope(omitAssertions); - return *cachedSourceRange; -} - -bool ASTScopeImpl::isSourceRangeCached(const bool omitAssertions) const { - const bool isCached = cachedSourceRange.hasValue(); - ASTScopeAssert(omitAssertions || isCached || - ensureNoAncestorsSourceRangeIsCached(), - "Cached ancestor's range likely is obsolete."); - return isCached; -} - -bool ASTScopeImpl::ensureNoAncestorsSourceRangeIsCached() const { - if (const auto *const p = getParent().getPtrOrNull()) { - auto r = !p->isSourceRangeCached(true) && - p->ensureNoAncestorsSourceRangeIsCached(); - if (!r) - ASTScope_unreachable("found a violation"); - return true; - } - return true; -} - -void ASTScopeImpl::computeAndCacheSourceRangeOfScope( +SourceRange GuardStmtBodyScope::getSourceRangeOfThisASTNode( const bool omitAssertions) const { - // In order to satisfy the invariant that, if my range is uncached, - // my parent's range is uncached, (which is needed to optimize invalidation - // by obviating the need to uncache all the way to the root every time), - // when caching a range, must ensure all children's ranges are cached. - for (auto *c : getChildren()) - c->computeAndCacheSourceRangeOfScope(omitAssertions); - - cachedSourceRange = computeSourceRangeOfScope(omitAssertions); -} - -bool ASTScopeImpl::checkLazySourceRange(const ASTContext &ctx) const { - const auto unexpandedRange = sourceRangeForDeferredExpansion(); - const auto expandedRange = computeSourceRangeOfScopeWithChildASTNodes(); - if (unexpandedRange.isInvalid() || expandedRange.isInvalid()) - return true; - if (unexpandedRange == expandedRange) - return true; - - llvm::errs() << "*** Lazy range problem. Parent unexpanded: ***\n"; - unexpandedRange.print(llvm::errs(), getSourceManager(), false); - llvm::errs() << "\n"; - if (!getChildren().empty()) { - llvm::errs() << "*** vs last child: ***\n"; - auto b = getChildren().back()->computeSourceRangeOfScope(); - b.print(llvm::errs(), getSourceManager(), false); - llvm::errs() << "\n"; - } - else if (hasValidSourceRangeOfIgnoredASTNodes()) { - llvm::errs() << "*** vs ignored AST nodes: ***\n"; - sourceRangeOfIgnoredASTNodes.print(llvm::errs(), getSourceManager(), false); - llvm::errs() << "\n"; - } - print(llvm::errs(), 0, false); - llvm::errs() << "\n"; - - return false; + return body->getSourceRange(); } -SourceRange -ASTScopeImpl::computeSourceRangeOfScope(const bool omitAssertions) const { - // If we don't need to consider children, it's cheaper - const auto deferredRange = sourceRangeForDeferredExpansion(); - return deferredRange.isValid() - ? deferredRange - : computeSourceRangeOfScopeWithChildASTNodes(omitAssertions); -} +#pragma mark source range caching -SourceRange ASTScopeImpl::computeSourceRangeOfScopeWithChildASTNodes( - const bool omitAssertions) const { - const auto rangeOfJustThisASTNode = - getSourceRangeOfThisASTNode(omitAssertions); - const auto rangeIncludingIgnoredNodes = - widenSourceRangeForIgnoredASTNodes(rangeOfJustThisASTNode); - const auto uncachedSourceRange = - widenSourceRangeForChildren(rangeIncludingIgnoredNodes, omitAssertions); - return uncachedSourceRange; -} - -void ASTScopeImpl::clearCachedSourceRangesOfMeAndAncestors() { - // An optimization: if my range isn't cached, my ancestors must not be - if (!isSourceRangeCached()) - return; - cachedSourceRange = None; - if (auto p = getParent()) - p.get()->clearCachedSourceRangesOfMeAndAncestors(); -} - -#pragma mark compensating for InterpolatedStringLiteralExprs and EditorPlaceHolders - -static bool isInterpolatedStringLiteral(const Token& tok) { - SmallVector Segments; - Lexer::getStringLiteralSegments(tok, Segments, nullptr); - return Segments.size() != 1 || - Segments.front().Kind != Lexer::StringSegment::Literal; -} - -/// If right brace is missing, the source range of the body will end -/// at the last token, which may be a one of the special cases below. -/// This work is only needed for *unexpanded* scopes because unioning the range -/// with the children will do the same thing for an expanded scope. -/// It is also needed for ignored \c ASTNodes, which may be, e.g. \c -/// InterpolatedStringLiterals -static SourceLoc getLocEncompassingPotentialLookups(const SourceManager &SM, - const SourceLoc endLoc) { - const auto tok = Lexer::getTokenAtLocation(SM, endLoc); - switch (tok.getKind()) { - default: - return endLoc; - case tok::string_literal: - if (!isInterpolatedStringLiteral(tok)) - return endLoc; // Just the start of the last token - break; - case tok::identifier: - // subtract one to get a closed-range endpoint from a half-open - if (!Identifier::isEditorPlaceholder(tok.getText())) - return endLoc; - break; - } - return tok.getRange().getEnd().getAdvancedLoc(-1); -} - -SourceRange ASTScopeImpl::sourceRangeForDeferredExpansion() const { - return SourceRange(); -} -SourceRange IterableTypeScope::sourceRangeForDeferredExpansion() const { - return portion->sourceRangeForDeferredExpansion(this); -} -SourceRange FunctionBodyScope::sourceRangeForDeferredExpansion() const { - const auto bsr = decl->getOriginalBodySourceRange(); - const SourceLoc endEvenIfNoCloseBraceAndEndsWithInterpolatedStringLiteral = - getLocEncompassingPotentialLookups(getSourceManager(), bsr.End); - return SourceRange(bsr.Start, - endEvenIfNoCloseBraceAndEndsWithInterpolatedStringLiteral); -} - -SourceRange GenericTypeOrExtensionWholePortion::sourceRangeForDeferredExpansion( - const IterableTypeScope *s) const { - const auto rangeOfThisNodeWithoutChildren = - getChildlessSourceRangeOf(s, false); - const auto rangeExtendedForFinalToken = SourceRange( - rangeOfThisNodeWithoutChildren.Start, - getLocEncompassingPotentialLookups(s->getSourceManager(), - rangeOfThisNodeWithoutChildren.End)); - const auto rangePastExtendedNominal = - s->moveStartPastExtendedNominal(rangeExtendedForFinalToken); - return rangePastExtendedNominal; -} - -SourceRange GenericTypeOrExtensionWherePortion::sourceRangeForDeferredExpansion( - const IterableTypeScope *) const { - return SourceRange(); -} - -SourceRange IterableTypeBodyPortion::sourceRangeForDeferredExpansion( - const IterableTypeScope *s) const { - const auto bracesRange = getChildlessSourceRangeOf(s, false); - return SourceRange(bracesRange.Start, - getLocEncompassingPotentialLookups(s->getSourceManager(), - bracesRange.End)); -} - -SourceRange ASTScopeImpl::getEffectiveSourceRange(const ASTNode n) const { - if (const auto *d = n.dyn_cast()) - return d->getSourceRange(); - if (const auto *s = n.dyn_cast()) - return s->getSourceRange(); - auto *e = n.dyn_cast(); - return getLocEncompassingPotentialLookups(getSourceManager(), e->getEndLoc()); -} - -/// Some nodes (e.g. the error expression) cannot possibly contain anything to -/// be looked up and if included in a parent scope's source range would expand -/// it beyond an ancestor's source range. But if the ancestor is expanded -/// lazily, we check that its source range does not change when expanding it, -/// and this check would fail. -static bool sourceRangeWouldInterfereWithLaziness(const ASTNode n) { - return n.isExpr(ExprKind::Error); -} - -static bool -shouldIgnoredASTNodeSourceRangeWidenEnclosingScope(const ASTNode n) { - if (n.isDecl(DeclKind::Var)) { - // The pattern scopes will include the source ranges for VarDecls. - // Using its range here would cause a pattern initializer scope's range - // to overlap the pattern use scope's range. - return false; +CharSourceRange +ASTScopeImpl::getCharSourceRangeOfScope(SourceManager &SM, + bool omitAssertions) const { + if (!isCharSourceRangeCached()) { + auto range = getSourceRangeOfThisASTNode(omitAssertions); + ASTScopeAssert(range.isValid(), "scope has invalid source range"); + ASTScopeAssert(SM.isBeforeInBuffer(range.Start, range.End) || + range.Start == range.End, + "scope source range ends before start"); + + cachedCharSourceRange = + Lexer::getCharSourceRangeFromSourceRange(SM, range); } - if (sourceRangeWouldInterfereWithLaziness(n)) - return false; - return true; -} -void ASTScopeImpl::widenSourceRangeForIgnoredASTNode(const ASTNode n) { - if (!shouldIgnoredASTNodeSourceRangeWidenEnclosingScope(n)) - return; + return *cachedCharSourceRange; +} - // FIXME: why only do effectiveness bit for *ignored* nodes? - SourceRange r = getEffectiveSourceRange(n); - if (r.isInvalid()) - return; - if (sourceRangeOfIgnoredASTNodes.isInvalid()) - sourceRangeOfIgnoredASTNodes = r; - else - sourceRangeOfIgnoredASTNodes.widen(r); +bool ASTScopeImpl::isCharSourceRangeCached() const { + return cachedCharSourceRange.hasValue(); } SourceLoc getLocAfterExtendedNominal(const ExtensionDecl *const ext) { @@ -678,35 +353,4 @@ SourceLoc ast_scope::extractNearestSourceLoc( std::tuple scopeAndCreator) { const ASTScopeImpl *scope = std::get<0>(scopeAndCreator); return scope->getSourceRangeOfThisASTNode().Start; -} - -int ASTScopeImpl::compare(const SourceRange lhs, const SourceRange rhs, - const SourceManager &SM, const bool ensureDisjoint) { - ASTScopeAssert(!SM.isBeforeInBuffer(lhs.End, lhs.Start), - "Range is backwards."); - ASTScopeAssert(!SM.isBeforeInBuffer(rhs.End, rhs.Start), - "Range is backwards."); - - auto cmpLoc = [&](const SourceLoc lhs, const SourceLoc rhs) { - return lhs == rhs ? 0 : SM.isBeforeInBuffer(lhs, rhs) ? -1 : 1; - }; - // Establish that we use end locations throughout ASTScopes here - const int endOrder = cmpLoc(lhs.End, rhs.End); - -#ifndef NDEBUG - if (ensureDisjoint) { - const int startOrder = cmpLoc(lhs.Start, rhs.Start); - - if (startOrder * endOrder == -1) { - llvm::errs() << "*** Start order contradicts end order between: ***\n"; - lhs.print(llvm::errs(), SM, false); - llvm::errs() << "\n*** and: ***\n"; - rhs.print(llvm::errs(), SM, false); - } - ASTScopeAssert(startOrder * endOrder != -1, - "Start order contradicts end order"); - } -#endif - - return endOrder; -} +} \ No newline at end of file diff --git a/lib/AST/ASTVerifier.cpp b/lib/AST/ASTVerifier.cpp index a4ae20e95c7ec..c7746b0960dff 100644 --- a/lib/AST/ASTVerifier.cpp +++ b/lib/AST/ASTVerifier.cpp @@ -1828,30 +1828,30 @@ class Verifier : public ASTWalker { Out << "\n"; abort(); } - + + if (!isa(E->getMember().getDecl())) { + Out << "Member reference to a non-VarDecl\n"; + E->dump(Out); + Out << "\n"; + abort(); + } + + auto baseType = E->getBase()->getType(); + if (baseType->is()) { + Out << "Member reference to an inout type\n"; + E->dump(Out); + Out << "\n"; + abort(); + } + // The base of a member reference cannot be an existential type. - if (E->getBase()->getType()->getWithoutSpecifierType() - ->isExistentialType()) { + if (baseType->getWithoutSpecifierType()->isExistentialType()) { Out << "Member reference into an unopened existential type\n"; E->dump(Out); Out << "\n"; abort(); } - // The only time the base is allowed to be inout is if we are accessing - // a computed property or if the base is a protocol or existential. - if (auto *baseIOT = E->getBase()->getType()->getAs()) { - if (!baseIOT->getObjectType()->is()) { - auto *VD = dyn_cast(E->getMember().getDecl()); - if (!VD || !VD->requiresOpaqueAccessors()) { - Out << "member_ref_expr on value of inout type\n"; - E->dump(Out); - Out << "\n"; - abort(); - } - } - } - // FIXME: Check container/member types through substitutions. verifyCheckedBase(E); diff --git a/lib/AST/ASTWalker.cpp b/lib/AST/ASTWalker.cpp index bc5998914d1b3..4dbbe68af288a 100644 --- a/lib/AST/ASTWalker.cpp +++ b/lib/AST/ASTWalker.cpp @@ -55,6 +55,7 @@ #include "swift/AST/ASTWalker.h" #include "swift/AST/ASTVisitor.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/ParameterList.h" #include "swift/AST/PrettyStackTrace.h" using namespace swift; @@ -1477,7 +1478,7 @@ Stmt *Traversal::visitGuardStmt(GuardStmt *US) { if (doIt(US->getCond())) return nullptr; - if (Stmt *S2 = doIt(US->getBody())) + if (BraceStmt *S2 = cast_or_null(doIt(US->getBody()))) US->setBody(S2); else return nullptr; @@ -1630,7 +1631,7 @@ Stmt *Traversal::visitCaseStmt(CaseStmt *S) { } } - if (Stmt *newBody = doIt(S->getBody())) + if (BraceStmt *newBody = cast_or_null(doIt(S->getBody()))) S->setBody(newBody); else return nullptr; diff --git a/lib/AST/AbstractSourceFileDepGraphFactory.cpp b/lib/AST/AbstractSourceFileDepGraphFactory.cpp index 6a4ccfdf78ab6..b70ea103a4040 100644 --- a/lib/AST/AbstractSourceFileDepGraphFactory.cpp +++ b/lib/AST/AbstractSourceFileDepGraphFactory.cpp @@ -33,11 +33,10 @@ using namespace fine_grained_dependencies; //============================================================================== AbstractSourceFileDepGraphFactory::AbstractSourceFileDepGraphFactory( - bool includePrivateDeps, bool hadCompilationError, StringRef swiftDeps, + bool hadCompilationError, StringRef swiftDeps, StringRef fileFingerprint, bool emitDotFileAfterConstruction, DiagnosticEngine &diags) - : includePrivateDeps(includePrivateDeps), - hadCompilationError(hadCompilationError), swiftDeps(swiftDeps.str()), + : hadCompilationError(hadCompilationError), swiftDeps(swiftDeps.str()), fileFingerprint(fileFingerprint.str()), emitDotFileAfterConstruction(emitDotFileAfterConstruction), diags(diags) { } diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index ec3941585a210..3c83dc4d1f8ee 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -639,15 +639,15 @@ void DeclAttributes::print(ASTPrinter &Printer, const PrintOptions &Options, CustomAttr *FuncBuilderAttr = nullptr; if (auto *VD = dyn_cast_or_null(D)) { - FuncBuilderAttr = VD->getAttachedFunctionBuilder(); + FuncBuilderAttr = VD->getAttachedResultBuilder(); } for (auto DA : llvm::reverse(FlattenedAttrs)) { - // Always print function builder attribute. - bool isFunctionBuilderAttr = DA == FuncBuilderAttr; + // Always print result builder attribute. + bool isResultBuilderAttr = DA == FuncBuilderAttr; if (!Options.PrintImplicitAttrs && DA->isImplicit()) continue; if (!Options.PrintUserInaccessibleAttrs && - !isFunctionBuilderAttr && + !isResultBuilderAttr && DeclAttribute::isUserInaccessible(DA->getKind())) continue; if (Options.excludeAttrKind(DA->getKind())) @@ -746,11 +746,11 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options, case DAK_Custom: { if (!Options.IsForSwiftInterface) break; - // For Swift interface, we should print function builder attributes + // For Swift interface, we should print result builder attributes // on parameter decls and on protocol requirements. // Printing the attribute elsewhere isn't ABI relevant. if (auto *VD = dyn_cast(D)) { - if (VD->getAttachedFunctionBuilder() == this) { + if (VD->getAttachedResultBuilder() == this) { if (!isa(D) && !(isa(D) && isa(D->getDeclContext()))) return false; @@ -775,8 +775,13 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options, case DAK_ReferenceOwnership: case DAK_Effects: case DAK_Optimize: + case DAK_ActorIndependent: if (DeclAttribute::isDeclModifier(getKind())) { Printer.printKeyword(getAttrName(), Options); + } else if (Options.IsForSwiftInterface && getKind() == DAK_ResultBuilder) { + // Use @_functionBuilder in Swift interfaces to maintain backward + // compatibility. + Printer.printSimpleAttr("_functionBuilder", /*needAt=*/true); } else { Printer.printSimpleAttr(getAttrName(), /*needAt=*/true); } @@ -904,13 +909,23 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options, } case DAK_Specialize: { - Printer << "@" << getAttrName() << "("; auto *attr = cast(this); + // Don't print the _specialize attribute if it is marked spi and we are + // asked to skip SPI. + if (!Options.PrintSPIs && !attr->getSPIGroups().empty()) + return false; + + Printer << "@" << getAttrName() << "("; auto exported = attr->isExported() ? "true" : "false"; auto kind = attr->isPartialSpecialization() ? "partial" : "full"; - + auto target = attr->getTargetFunctionName(); Printer << "exported: "<< exported << ", "; + for (auto id : attr->getSPIGroups()) { + Printer << "spi: " << id << ", "; + } Printer << "kind: " << kind << ", "; + if (target) + Printer << "target: " << target << ", "; SmallVector requirementsScratch; ArrayRef requirements; if (auto sig = attr->getSpecializedSignature()) @@ -982,8 +997,8 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options, auto *attr = cast(this); if (auto *repr = attr->getParsedTypeEraserTypeRepr()) repr->print(Printer, Options); - else - attr->getTypeWithoutResolving()->print(Printer, Options); + else if (auto proto = dyn_cast(D)) + attr->getResolvedType(proto)->print(Printer, Options); Printer.printNamePost(PrintNameContext::Attribute); Printer << ")"; break; @@ -1132,6 +1147,15 @@ StringRef DeclAttribute::getAttrName() const { } llvm_unreachable("Invalid inline kind"); } + case DAK_ActorIndependent: { + switch (cast(this)->getKind()) { + case ActorIndependentKind::Safe: + return "actorIndependent"; + case ActorIndependentKind::Unsafe: + return "actorIndependent(unsafe)"; + } + llvm_unreachable("Invalid actorIndependent kind"); + } case DAK_Optimize: { switch (cast(this)->getMode()) { case OptimizationMode::NoOptimization: @@ -1562,14 +1586,17 @@ const AvailableAttr *AvailableAttr::isUnavailable(const Decl *D) { } SpecializeAttr::SpecializeAttr(SourceLoc atLoc, SourceRange range, - TrailingWhereClause *clause, - bool exported, + TrailingWhereClause *clause, bool exported, SpecializationKind kind, - GenericSignature specializedSignature) + GenericSignature specializedSignature, + DeclNameRef targetFunctionName, + ArrayRef spiGroups) : DeclAttribute(DAK_Specialize, atLoc, range, /*Implicit=*/clause == nullptr), - trailingWhereClause(clause), - specializedSignature(specializedSignature) { + trailingWhereClause(clause), specializedSignature(specializedSignature), + targetFunctionName(targetFunctionName), numSPIGroups(spiGroups.size()) { + std::uninitialized_copy(spiGroups.begin(), spiGroups.end(), + getTrailingObjects()); Bits.SpecializeAttr.exported = exported; Bits.SpecializeAttr.kind = unsigned(kind); } @@ -1581,11 +1608,48 @@ TrailingWhereClause *SpecializeAttr::getTrailingWhereClause() const { SpecializeAttr *SpecializeAttr::create(ASTContext &Ctx, SourceLoc atLoc, SourceRange range, TrailingWhereClause *clause, - bool exported, - SpecializationKind kind, + bool exported, SpecializationKind kind, + DeclNameRef targetFunctionName, + ArrayRef spiGroups, GenericSignature specializedSignature) { - return new (Ctx) SpecializeAttr(atLoc, range, clause, exported, kind, - specializedSignature); + unsigned size = totalSizeToAlloc(spiGroups.size()); + void *mem = Ctx.Allocate(size, alignof(SpecializeAttr)); + return new (mem) + SpecializeAttr(atLoc, range, clause, exported, kind, specializedSignature, + targetFunctionName, spiGroups); +} + +SpecializeAttr *SpecializeAttr::create(ASTContext &ctx, bool exported, + SpecializationKind kind, + ArrayRef spiGroups, + GenericSignature specializedSignature, + DeclNameRef targetFunctionName) { + unsigned size = totalSizeToAlloc(spiGroups.size()); + void *mem = ctx.Allocate(size, alignof(SpecializeAttr)); + return new (mem) + SpecializeAttr(SourceLoc(), SourceRange(), nullptr, exported, kind, + specializedSignature, targetFunctionName, spiGroups); +} + +SpecializeAttr *SpecializeAttr::create( + ASTContext &ctx, bool exported, SpecializationKind kind, + ArrayRef spiGroups, GenericSignature specializedSignature, + DeclNameRef targetFunctionName, LazyMemberLoader *resolver, uint64_t data) { + unsigned size = totalSizeToAlloc(spiGroups.size()); + void *mem = ctx.Allocate(size, alignof(SpecializeAttr)); + auto *attr = new (mem) + SpecializeAttr(SourceLoc(), SourceRange(), nullptr, exported, kind, + specializedSignature, targetFunctionName, spiGroups); + attr->resolver = resolver; + attr->resolverContextData = data; + return attr; +} + +ValueDecl * SpecializeAttr::getTargetFunctionDecl(const ValueDecl *onDecl) const { + return evaluateOrDefault(onDecl->getASTContext().evaluator, + SpecializeAttrTargetDeclRequest{ + onDecl, const_cast(this)}, + nullptr); } SPIAccessControlAttr::SPIAccessControlAttr(SourceLoc atLoc, SourceRange range, diff --git a/lib/AST/AutoDiff.cpp b/lib/AST/AutoDiff.cpp index 492e38a2cb861..566de24dae297 100644 --- a/lib/AST/AutoDiff.cpp +++ b/lib/AST/AutoDiff.cpp @@ -462,6 +462,7 @@ bool swift::operator==(const TangentPropertyInfo::Error &lhs, case TangentPropertyInfo::Error::Kind::TangentPropertyWrongType: return lhs.getType()->isEqual(rhs.getType()); } + llvm_unreachable("unhandled tangent property!"); } void swift::simple_display(llvm::raw_ostream &os, TangentPropertyInfo info) { diff --git a/lib/AST/Availability.cpp b/lib/AST/Availability.cpp index 743d3f80c69fb..e042a3dda668f 100644 --- a/lib/AST/Availability.cpp +++ b/lib/AST/Availability.cpp @@ -323,6 +323,10 @@ ASTContext::getIntermodulePrespecializedGenericMetadataAvailability() { return getSwift54Availability(); } +AvailabilityContext ASTContext::getConcurrencyAvailability() { + return getSwiftFutureAvailability(); +} + AvailabilityContext ASTContext::getSwift52Availability() { auto target = LangOpts.Target; diff --git a/lib/AST/CMakeLists.txt b/lib/AST/CMakeLists.txt index 62993edae03ea..815d679a8effc 100644 --- a/lib/AST/CMakeLists.txt +++ b/lib/AST/CMakeLists.txt @@ -9,6 +9,8 @@ else() ) endif() +set_swift_llvm_is_available() + add_swift_host_library(swiftAST STATIC AbstractSourceFileDepGraphFactory.cpp AccessRequests.cpp @@ -49,6 +51,7 @@ add_swift_host_library(swiftAST STATIC FineGrainedDependencyFormat.cpp FrontendSourceFileDepGraphFactory.cpp GenericEnvironment.cpp + GenericParamList.cpp GenericSignature.cpp GenericSignatureBuilder.cpp Identifier.cpp @@ -123,15 +126,14 @@ target_link_libraries(swiftAST PUBLIC swiftBasic PRIVATE swiftSyntax) if(SWIFT_BUILD_ONLY_SYNTAXPARSERLIB) - # Add clangBasic as a single direct dependency to avoid bringing along some - # llvm libraries that we don't need. - if("${SWIFT_HOST_VARIANT_SDK}" STREQUAL "WINDOWS") - target_link_libraries(swiftAST PRIVATE - "${LLVM_LIBRARY_OUTPUT_INTDIR}/clangBasic.lib") - else() - target_link_libraries(swiftAST PRIVATE - "${LLVM_LIBRARY_OUTPUT_INTDIR}/libclangBasic.a") - endif() + # Remove dependencies from clangBasic to avoid bringing along some llvm + # libraries that we don't need to be building. + set_property(TARGET clangBasic PROPERTY LINK_LIBRARIES "") + set_property(TARGET clangBasic PROPERTY LINK_DEPENDS "") + set_property(TARGET clangBasic PROPERTY INTERFACE_LINK_LIBRARIES "") + set_property(TARGET clangBasic PROPERTY INTERFACE_LINK_DEPENDS "") + target_link_libraries(swiftAST + PRIVATE clangBasic) target_compile_definitions(swiftAST PRIVATE SWIFT_BUILD_ONLY_SYNTAXPARSERLIB=1) else() diff --git a/lib/AST/ClangTypeConverter.cpp b/lib/AST/ClangTypeConverter.cpp index 9fd13f797798d..9da86112ebae6 100644 --- a/lib/AST/ClangTypeConverter.cpp +++ b/lib/AST/ClangTypeConverter.cpp @@ -207,6 +207,7 @@ const clang::Type *ClangTypeConverter::getFunctionType( case SILFunctionType::Representation::Closure: llvm_unreachable("Expected a C-compatible representation."); } + llvm_unreachable("unhandled representation!"); } clang::QualType ClangTypeConverter::convertMemberType(NominalTypeDecl *DC, @@ -806,3 +807,33 @@ Decl *ClangTypeConverter::getSwiftDeclForExportedClangDecl( auto it = ReversedExportMap.find(decl); return (it != ReversedExportMap.end() ? it->second : nullptr); } + +std::unique_ptr +ClangTypeConverter::getClangTemplateArguments( + const clang::TemplateParameterList *templateParams, + ArrayRef genericArgs, + SmallVectorImpl &templateArgs) { + // Keep track of the types we failed to convert so we can return a useful + // error. + SmallVector failedTypes; + for (clang::NamedDecl *param : *templateParams) { + // Note: all template parameters must be template type parameters. This is + // verified when we import the clang decl. + auto templateParam = cast(param); + auto replacement = genericArgs[templateParam->getIndex()]; + auto qualType = convert(replacement); + if (qualType.isNull()) { + failedTypes.push_back(replacement); + // Find all the types we can't convert. + continue; + } + templateArgs.push_back(clang::TemplateArgument(qualType)); + } + if (failedTypes.empty()) + return nullptr; + auto errorInfo = std::make_unique(); + llvm::for_each(failedTypes, [&errorInfo](auto type) { + errorInfo->failedTypes.push_back(type); + }); + return errorInfo; +} diff --git a/lib/AST/ClangTypeConverter.h b/lib/AST/ClangTypeConverter.h index e41a824163312..b590b3ef0c0ec 100644 --- a/lib/AST/ClangTypeConverter.h +++ b/lib/AST/ClangTypeConverter.h @@ -84,8 +84,18 @@ class ClangTypeConverter : /// Swift declaration. Decl *getSwiftDeclForExportedClangDecl(const clang::Decl *decl) const; + /// Translate Swift generic arguments to template arguments. + /// + /// \returns nullptr if successful. If an error occors, returns a unique_ptr + /// to a `TemplateInstantiationError` with a list of the failed types. + std::unique_ptr getClangTemplateArguments( + const clang::TemplateParameterList *templateParams, + ArrayRef genericArgs, + SmallVectorImpl &templateArgs); + private: clang::QualType convert(Type type); + clang::QualType convertMemberType(NominalTypeDecl *DC, StringRef memberName); diff --git a/lib/AST/ConformanceLookupTable.cpp b/lib/AST/ConformanceLookupTable.cpp index 45a249601a7ac..547fa1bd4b4d0 100644 --- a/lib/AST/ConformanceLookupTable.cpp +++ b/lib/AST/ConformanceLookupTable.cpp @@ -18,6 +18,7 @@ #include "swift/AST/ASTContext.h" #include "swift/AST/Decl.h" #include "swift/AST/ExistentialLayout.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/LazyResolver.h" #include "swift/AST/Module.h" #include "swift/AST/NameLookup.h" diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 79428bc2a7e2f..a7be73d04d86f 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -120,6 +120,10 @@ const clang::Module *ClangNode::getClangModule() const { } void ClangNode::dump() const { +#if SWIFT_BUILD_ONLY_SYNTAXPARSERLIB + return; // not needed for the parser library. +#endif + if (auto D = getAsDecl()) D->dump(); else if (auto M = getAsMacro()) @@ -680,6 +684,14 @@ static_assert(sizeof(checkSourceLocType(&ID##Decl::getLoc)) == 2, \ llvm_unreachable("invalid file kind"); } +Optional Decl::getGlobalActorAttr() const { + auto &ctx = getASTContext(); + auto mutableThis = const_cast(this); + return evaluateOrDefault(ctx.evaluator, + GlobalActorAttributeRequest{mutableThis}, + None); +} + Expr *AbstractFunctionDecl::getSingleExpressionBody() const { assert(hasSingleExpressionBody() && "Not a single-expression body"); auto braceStmt = getBody(); @@ -903,115 +915,6 @@ bool Decl::isWeakImported(ModuleDecl *fromModule) const { return !fromContext.isContainedIn(containingContext); } - -SourceRange RequirementRepr::getSourceRange() const { - if (getKind() == RequirementReprKind::LayoutConstraint) - return SourceRange(FirstType->getSourceRange().Start, - SecondLayout.getSourceRange().End); - return SourceRange(FirstType->getSourceRange().Start, - SecondType->getSourceRange().End); -} - -GenericParamList::GenericParamList(SourceLoc LAngleLoc, - ArrayRef Params, - SourceLoc WhereLoc, - MutableArrayRef Requirements, - SourceLoc RAngleLoc) - : Brackets(LAngleLoc, RAngleLoc), NumParams(Params.size()), - WhereLoc(WhereLoc), Requirements(Requirements), - OuterParameters(nullptr) -{ - std::uninitialized_copy(Params.begin(), Params.end(), - getTrailingObjects()); -} - -GenericParamList * -GenericParamList::create(ASTContext &Context, - SourceLoc LAngleLoc, - ArrayRef Params, - SourceLoc RAngleLoc) { - unsigned Size = totalSizeToAlloc(Params.size()); - void *Mem = Context.Allocate(Size, alignof(GenericParamList)); - return new (Mem) GenericParamList(LAngleLoc, Params, SourceLoc(), - MutableArrayRef(), - RAngleLoc); -} - -GenericParamList * -GenericParamList::create(const ASTContext &Context, - SourceLoc LAngleLoc, - ArrayRef Params, - SourceLoc WhereLoc, - ArrayRef Requirements, - SourceLoc RAngleLoc) { - unsigned Size = totalSizeToAlloc(Params.size()); - void *Mem = Context.Allocate(Size, alignof(GenericParamList)); - return new (Mem) GenericParamList(LAngleLoc, Params, - WhereLoc, - Context.AllocateCopy(Requirements), - RAngleLoc); -} - -GenericParamList * -GenericParamList::clone(DeclContext *dc) const { - auto &ctx = dc->getASTContext(); - SmallVector params; - for (auto param : getParams()) { - auto *newParam = new (ctx) GenericTypeParamDecl( - dc, param->getName(), SourceLoc(), - GenericTypeParamDecl::InvalidDepth, - param->getIndex()); - newParam->setImplicit(true); - params.push_back(newParam); - } - - return GenericParamList::create(ctx, SourceLoc(), params, SourceLoc()); -} - -void GenericParamList::setDepth(unsigned depth) { - for (auto param : *this) - param->setDepth(depth); -} - -void GenericParamList::setDeclContext(DeclContext *dc) { - for (auto param : *this) - param->setDeclContext(dc); -} - -GenericTypeParamDecl *GenericParamList::lookUpGenericParam( - Identifier name) const { - for (const auto *innerParams = this; - innerParams != nullptr; - innerParams = innerParams->getOuterParameters()) { - for (auto *paramDecl : *innerParams) { - if (name == paramDecl->getName()) { - return const_cast(paramDecl); - } - } - } - - return nullptr; -} - -TrailingWhereClause::TrailingWhereClause( - SourceLoc whereLoc, - ArrayRef requirements) - : WhereLoc(whereLoc), - NumRequirements(requirements.size()) -{ - std::uninitialized_copy(requirements.begin(), requirements.end(), - getTrailingObjects()); -} - -TrailingWhereClause *TrailingWhereClause::create( - ASTContext &ctx, - SourceLoc whereLoc, - ArrayRef requirements) { - unsigned size = totalSizeToAlloc(requirements.size()); - void *mem = ctx.Allocate(size, alignof(TrailingWhereClause)); - return new (mem) TrailingWhereClause(whereLoc, requirements); -} - GenericContext::GenericContext(DeclContextKind Kind, DeclContext *Parent, GenericParamList *Params) : _GenericContext(), DeclContext(Kind, Parent) { @@ -1223,7 +1126,7 @@ NominalTypeDecl::takeConformanceLoaderSlow() { ExtensionDecl::ExtensionDecl(SourceLoc extensionLoc, TypeRepr *extendedType, - MutableArrayRef inherited, + ArrayRef inherited, DeclContext *parent, TrailingWhereClause *trailingWhereClause) : GenericContext(DeclContextKind::ExtensionDecl, parent, nullptr), @@ -1240,7 +1143,7 @@ ExtensionDecl::ExtensionDecl(SourceLoc extensionLoc, ExtensionDecl *ExtensionDecl::create(ASTContext &ctx, SourceLoc extensionLoc, TypeRepr *extendedType, - MutableArrayRef inherited, + ArrayRef inherited, DeclContext *parent, TrailingWhereClause *trailingWhereClause, ClangNode clangNode) { @@ -1590,7 +1493,8 @@ void PatternBindingEntry::setInit(Expr *E) { VarDecl *PatternBindingEntry::getAnchoringVarDecl() const { SmallVector variables; getPattern()->collectVariables(variables); - assert(!variables.empty()); + if (variables.empty()) + return nullptr; return variables[0]; } @@ -1712,12 +1616,32 @@ VarDecl *PatternBindingDecl::getAnchoringVarDecl(unsigned i) const { } bool VarDecl::isInitExposedToClients() const { + // 'lazy' initializers are emitted inside the getter, which is never + // @inlinable. + if (getAttrs().hasAttribute()) + return false; + + return hasInitialValue() && isLayoutExposedToClients(); +} + +bool VarDecl::isLayoutExposedToClients() const { auto parent = dyn_cast(getDeclContext()); if (!parent) return false; - if (!hasInitialValue()) return false; if (isStatic()) return false; - return parent->getAttrs().hasAttribute() || - parent->getAttrs().hasAttribute(); + + if (!hasStorage() && + !getAttrs().hasAttribute() && + !hasAttachedPropertyWrapper()) { + return false; + } + + auto nominalAccess = + parent->getFormalAccessScope(/*useDC=*/nullptr, + /*treatUsableFromInlineAsPublic=*/true); + if (!nominalAccess.isPublic()) return false; + + return (parent->getAttrs().hasAttribute() || + parent->getAttrs().hasAttribute()); } /// Check whether the given type representation will be @@ -3425,13 +3349,14 @@ ValueDecl::getFormalAccessScope(const DeclContext *useDC, /// See ValueDecl::isAccessibleFrom for a description of \p forConformance. static bool checkAccessUsingAccessScopes(const DeclContext *useDC, const ValueDecl *VD, - AccessLevel access) { + AccessLevel access, + bool includeInlineable) { if (VD->getASTContext().isAccessControlDisabled()) return true; - AccessScope accessScope = - getAccessScopeForFormalAccess(VD, access, useDC, - /*treatUsableFromInlineAsPublic*/false); + AccessScope accessScope = getAccessScopeForFormalAccess( + VD, access, useDC, + /*treatUsableFromInlineAsPublic*/ includeInlineable); if (accessScope.getDeclContext() == useDC) return true; if (!AccessScope(useDC).isChildOf(accessScope)) return false; @@ -3454,6 +3379,7 @@ static bool checkAccessUsingAccessScopes(const DeclContext *useDC, /// See ValueDecl::isAccessibleFrom for a description of \p forConformance. static bool checkAccess(const DeclContext *useDC, const ValueDecl *VD, bool forConformance, + bool includeInlineable, llvm::function_ref getAccessLevel) { if (VD->getASTContext().isAccessControlDisabled()) return true; @@ -3466,7 +3392,7 @@ static bool checkAccess(const DeclContext *useDC, const ValueDecl *VD, // check declarations inside inaccessible members via slower // access scope based check, which is helpful for diagnostics. if (!(sourceDC->getSelfProtocolDecl() || VD->isOperator())) - return checkAccessUsingAccessScopes(useDC, VD, access); + return checkAccessUsingAccessScopes(useDC, VD, access, includeInlineable); if (!forConformance) { if (auto *proto = sourceDC->getSelfProtocolDecl()) { @@ -3481,7 +3407,7 @@ static bool checkAccess(const DeclContext *useDC, const ValueDecl *VD, } // Skip the fast path below and just compare access scopes. - return checkAccessUsingAccessScopes(useDC, VD, access); + return checkAccessUsingAccessScopes(useDC, VD, access, includeInlineable); } } @@ -3521,8 +3447,9 @@ static bool checkAccess(const DeclContext *useDC, const ValueDecl *VD, } bool ValueDecl::isAccessibleFrom(const DeclContext *useDC, - bool forConformance) const { - return checkAccess(useDC, this, forConformance, + bool forConformance, + bool allowUsableFromInline) const { + return checkAccess(useDC, this, forConformance, allowUsableFromInline, [&]() { return getFormalAccess(); }); } @@ -3539,7 +3466,7 @@ bool AbstractStorageDecl::isSetterAccessibleFrom(const DeclContext *DC, if (isa(this)) return true; - return checkAccess(DC, this, forConformance, + return checkAccess(DC, this, forConformance, /*includeInlineable*/ false, [&]() { return getSetterFormalAccess(); }); } @@ -3842,7 +3769,7 @@ PropertyWrapperTypeInfo NominalTypeDecl::getPropertyWrapperTypeInfo() const { GenericTypeDecl::GenericTypeDecl(DeclKind K, DeclContext *DC, Identifier name, SourceLoc nameLoc, - MutableArrayRef inherited, + ArrayRef inherited, GenericParamList *GenericParams) : GenericContext(DeclContextKind::GenericTypeDecl, DC, GenericParams), TypeDecl(K, DC, name, nameLoc, inherited) {} @@ -4052,7 +3979,7 @@ AssociatedTypeDecl *AssociatedTypeDecl::getAssociatedTypeAnchor() const { EnumDecl::EnumDecl(SourceLoc EnumLoc, Identifier Name, SourceLoc NameLoc, - MutableArrayRef Inherited, + ArrayRef Inherited, GenericParamList *GenericParams, DeclContext *Parent) : NominalTypeDecl(DeclKind::Enum, Parent, Name, NameLoc, Inherited, GenericParams), @@ -4066,13 +3993,17 @@ EnumDecl::EnumDecl(SourceLoc EnumLoc, Type EnumDecl::getRawType() const { ASTContext &ctx = getASTContext(); - return evaluateOrDefault(ctx.evaluator, - EnumRawTypeRequest{const_cast(this), - TypeResolutionStage::Interface}, Type()); + return evaluateOrDefault( + ctx.evaluator, EnumRawTypeRequest{const_cast(this)}, Type()); +} + +void EnumDecl::setRawType(Type rawType) { + getASTContext().evaluator.cacheOutput(EnumRawTypeRequest{this}, + std::move(rawType)); } StructDecl::StructDecl(SourceLoc StructLoc, Identifier Name, SourceLoc NameLoc, - MutableArrayRef Inherited, + ArrayRef Inherited, GenericParamList *GenericParams, DeclContext *Parent) : NominalTypeDecl(DeclKind::Struct, Parent, Name, NameLoc, Inherited, GenericParams), @@ -4182,15 +4113,15 @@ void NominalTypeDecl::synthesizeSemanticMembersIfNeeded(DeclName member) { } } -bool ClassDecl::hasCircularInheritance() const { - auto &ctx = getASTContext(); - auto *mutableThis = const_cast(this); - return evaluateOrDefault(ctx.evaluator, - HasCircularInheritanceRequest{mutableThis}, true); +VarDecl *NominalTypeDecl::getGlobalActorInstance() const { + auto mutableThis = const_cast(this); + return evaluateOrDefault(getASTContext().evaluator, + GlobalActorInstanceRequest{mutableThis}, + nullptr); } ClassDecl::ClassDecl(SourceLoc ClassLoc, Identifier Name, SourceLoc NameLoc, - MutableArrayRef Inherited, + ArrayRef Inherited, GenericParamList *GenericParams, DeclContext *Parent) : NominalTypeDecl(DeclKind::Class, Parent, Name, NameLoc, Inherited, GenericParams), @@ -4310,7 +4241,7 @@ bool ClassDecl::isIncompatibleWithWeakReferences() const { bool ClassDecl::inheritsSuperclassInitializers() const { // If there's no superclass, there's nothing to inherit. - if (!getSuperclass()) + if (!getSuperclassDecl()) return false; auto &ctx = getASTContext(); @@ -4328,19 +4259,11 @@ AncestryOptions ClassDecl::checkAncestry() const { AncestryFlags ClassAncestryFlagsRequest::evaluate(Evaluator &evaluator, ClassDecl *value) const { - llvm::SmallPtrSet visited; - AncestryOptions result; const ClassDecl *CD = value; auto *M = value->getParentModule(); do { - // If we hit circularity, we will diagnose at some point in typeCheckDecl(). - // However we have to explicitly guard against that here because we get - // called as part of the interface type request. - if (!visited.insert(CD).second) - break; - if (CD->isGenericContext()) result |= AncestryFlags::Generic; @@ -4530,10 +4453,9 @@ ClassDecl::findImplementingMethod(const AbstractFunctionDecl *Method) const { bool ClassDecl::walkSuperclasses( llvm::function_ref fn) const { - SmallPtrSet seen; auto *cls = const_cast(this); - while (cls && seen.insert(cls).second) { + while (cls) { switch (fn(cls)) { case TypeWalker::Action::Stop: return true; @@ -4663,9 +4585,7 @@ bool EnumDecl::isEffectivelyExhaustive(ModuleDecl *M, } void EnumDecl::setHasFixedRawValues() { - auto flags = LazySemanticInfo.RawTypeAndFlags.getInt() | - EnumDecl::HasFixedRawValues; - LazySemanticInfo.RawTypeAndFlags.setInt(flags); + SemanticFlags |= OptionSet{EnumDecl::HasFixedRawValues}; } bool EnumDecl::hasCircularRawValue() const { @@ -4677,7 +4597,7 @@ bool EnumDecl::hasCircularRawValue() const { ProtocolDecl::ProtocolDecl(DeclContext *DC, SourceLoc ProtocolLoc, SourceLoc NameLoc, Identifier Name, - MutableArrayRef Inherited, + ArrayRef Inherited, TrailingWhereClause *TrailingWhere) : NominalTypeDecl(DeclKind::Protocol, DC, Name, NameLoc, Inherited, nullptr), @@ -4836,180 +4756,174 @@ bool ProtocolDecl::existentialConformsToSelf() const { } /// Classify usages of Self in the given type. -static SelfReferenceKind +/// +/// \param position The position we are currently at, in terms of variance. +static SelfReferenceInfo findProtocolSelfReferences(const ProtocolDecl *proto, Type type, - bool skipAssocTypes) { + SelfReferencePosition position) { // Tuples preserve variance. if (auto tuple = type->getAs()) { - auto kind = SelfReferenceKind::None(); + auto info = SelfReferenceInfo(); for (auto &elt : tuple->getElements()) { - kind |= findProtocolSelfReferences(proto, elt.getType(), skipAssocTypes); + info |= findProtocolSelfReferences(proto, elt.getType(), position); } - return kind; + + // A covariant Self result inside a tuple will not be bona fide. + info.hasCovariantSelfResult = false; + + return info; } // Function preserve variance in the result type, and flip variance in // the parameter type. if (auto funcTy = type->getAs()) { - auto inputKind = SelfReferenceKind::None(); + auto inputInfo = SelfReferenceInfo(); for (auto param : funcTy->getParams()) { // inout parameters are invariant. if (param.isInOut()) { - if (findProtocolSelfReferences(proto, param.getPlainType(), - skipAssocTypes)) { - return SelfReferenceKind::Other(); - } + inputInfo |= findProtocolSelfReferences( + proto, param.getPlainType(), SelfReferencePosition::Invariant); + continue; } - inputKind |= findProtocolSelfReferences(proto, param.getParameterType(), - skipAssocTypes); + inputInfo |= findProtocolSelfReferences(proto, param.getParameterType(), + position.flipped()); } - auto resultKind = findProtocolSelfReferences(proto, funcTy->getResult(), - skipAssocTypes); - auto kind = inputKind.flip(); - kind |= resultKind; - return kind; + // A covariant Self result inside a parameter will not be bona fide. + inputInfo.hasCovariantSelfResult = false; + + auto resultInfo = + findProtocolSelfReferences(proto, funcTy->getResult(), position); + if (resultInfo.selfRef == SelfReferencePosition::Covariant) { + resultInfo.hasCovariantSelfResult = true; + } + return inputInfo |= resultInfo; } // Metatypes preserve variance. if (auto metaTy = type->getAs()) { return findProtocolSelfReferences(proto, metaTy->getInstanceType(), - skipAssocTypes); + position); } // Optionals preserve variance. if (auto optType = type->getOptionalObjectType()) { - return findProtocolSelfReferences(proto, optType, - skipAssocTypes); + return findProtocolSelfReferences(proto, optType, position); } // DynamicSelfType preserves variance. // FIXME: This shouldn't ever appear in protocol requirement // signatures. if (auto selfType = type->getAs()) { - return findProtocolSelfReferences(proto, selfType->getSelfType(), - skipAssocTypes); + return findProtocolSelfReferences(proto, selfType->getSelfType(), position); } // Bound generic types are invariant. if (auto boundGenericType = type->getAs()) { + auto info = SelfReferenceInfo(); for (auto paramType : boundGenericType->getGenericArgs()) { - if (findProtocolSelfReferences(proto, paramType, - skipAssocTypes)) { - return SelfReferenceKind::Other(); - } + info |= findProtocolSelfReferences(proto, paramType, + SelfReferencePosition::Invariant); } + + return info; } - // A direct reference to 'Self' is covariant. + // A direct reference to 'Self'. if (proto->getSelfInterfaceType()->isEqual(type)) - return SelfReferenceKind::Result(); + return SelfReferenceInfo::forSelfRef(position); - // Special handling for associated types. - if (!skipAssocTypes && type->is()) { + // A reference to an associated type rooted on 'Self'. + if (type->is()) { type = type->getRootGenericParam(); if (proto->getSelfInterfaceType()->isEqual(type)) - return SelfReferenceKind::Other(); - } - - return SelfReferenceKind::None(); -} - -/// Find Self references in a generic signature's same-type requirements. -static SelfReferenceKind -findProtocolSelfReferences(const ProtocolDecl *protocol, - GenericSignature genericSig){ - if (!genericSig) return SelfReferenceKind::None(); - - auto selfTy = protocol->getSelfInterfaceType(); - for (const auto &req : genericSig->getRequirements()) { - if (req.getKind() != RequirementKind::SameType) - continue; - - if (req.getFirstType()->isEqual(selfTy) || - req.getSecondType()->isEqual(selfTy)) - return SelfReferenceKind::Requirement(); + return SelfReferenceInfo::forAssocTypeRef(position); } - return SelfReferenceKind::None(); + return SelfReferenceInfo(); } /// Find Self references within the given requirement. -SelfReferenceKind -ProtocolDecl::findProtocolSelfReferences(const ValueDecl *value, - bool allowCovariantParameters, - bool skipAssocTypes) const { +SelfReferenceInfo ProtocolDecl::findProtocolSelfReferences( + const ValueDecl *value, bool treatNonResultCovariantSelfAsInvariant) const { // Types never refer to 'Self'. if (isa(value)) - return SelfReferenceKind::None(); + return SelfReferenceInfo(); auto type = value->getInterfaceType(); // Skip invalid declarations. if (type->hasError()) - return SelfReferenceKind::None(); + return SelfReferenceInfo(); - if (auto func = dyn_cast(value)) { - // Skip the 'self' parameter. - type = type->castTo()->getResult(); + if (isa(value) || isa(value)) { + // For a method, skip the 'self' parameter. + if (isa(value)) + type = type->castTo()->getResult(); - // Methods of non-final classes can only contain a covariant 'Self' - // as a function result type. - if (!allowCovariantParameters) { - auto inputKind = SelfReferenceKind::None(); - for (auto param : type->castTo()->getParams()) { - // inout parameters are invariant. - if (param.isInOut()) { - if (::findProtocolSelfReferences(this, param.getPlainType(), - skipAssocTypes)) { - return SelfReferenceKind::Other(); - } - } - inputKind |= ::findProtocolSelfReferences(this, param.getParameterType(), - skipAssocTypes); + auto inputInfo = SelfReferenceInfo(); + for (auto param : type->castTo()->getParams()) { + // inout parameters are invariant. + if (param.isInOut()) { + inputInfo |= ::findProtocolSelfReferences( + this, param.getPlainType(), SelfReferencePosition::Invariant); + continue; } - - if (inputKind.parameter) - return SelfReferenceKind::Other(); + inputInfo |= ::findProtocolSelfReferences( + this, param.getParameterType(), SelfReferencePosition::Contravariant); } - // Check the requirements of a generic function. - if (func->isGeneric()) { - if (auto result = - ::findProtocolSelfReferences(this, func->getGenericSignature())) - return result; + // A covariant Self result inside a parameter will not be bona fide. + inputInfo.hasCovariantSelfResult = false; + + // FIXME: Rather than having a special flag for the is-inheritable check, + // ensure non-result covariant Self is always diagnosed during type + // resolution. + // + // Methods of non-final classes can only contain a covariant 'Self' + // as their result type. + if (treatNonResultCovariantSelfAsInvariant && + inputInfo.selfRef == SelfReferencePosition::Covariant) { + inputInfo.selfRef = SelfReferencePosition::Invariant; } - return ::findProtocolSelfReferences(this, type, - skipAssocTypes); - } else if (auto subscript = dyn_cast(value)) { - // Check the requirements of a generic subscript. - if (subscript->isGeneric()) { - if (auto result = - ::findProtocolSelfReferences(this, - subscript->getGenericSignature())) - return result; + auto resultInfo = ::findProtocolSelfReferences( + this, type->castTo()->getResult(), + SelfReferencePosition::Covariant); + if (resultInfo.selfRef == SelfReferencePosition::Covariant) { + resultInfo.hasCovariantSelfResult = true; } - return ::findProtocolSelfReferences(this, type, - skipAssocTypes); + return inputInfo |= resultInfo; } else { - if (::findProtocolSelfReferences(this, type, - skipAssocTypes)) { - return SelfReferenceKind::Other(); + assert(isa(value)); + + auto info = ::findProtocolSelfReferences(this, type, + SelfReferencePosition::Covariant); + if (info.selfRef == SelfReferencePosition::Covariant) { + info.hasCovariantSelfResult = true; } - return SelfReferenceKind::None(); + + return info; } + + return SelfReferenceInfo(); } bool ProtocolDecl::isAvailableInExistential(const ValueDecl *decl) const { - // If the member type uses 'Self' in non-covariant position, - // we cannot use the existential type. - auto selfKind = findProtocolSelfReferences(decl, - /*allowCovariantParameters=*/true, - /*skipAssocTypes=*/false); - if (selfKind.parameter || selfKind.other) + // If the member type references 'Self' in non-covariant position, or an + // associated type in any position, we cannot use the existential type. + const auto info = findProtocolSelfReferences( + decl, /*treatNonResultCovariantSelfAsInvariant=*/false); + if (info.selfRef > SelfReferencePosition::Covariant || info.assocTypeRef) { return false; + } + + // FIXME: Appropriately diagnose assignments instead. + if (auto *const storageDecl = dyn_cast(decl)) { + if (info.hasCovariantSelfResult && storageDecl->supportsMutation()) + return false; + } return true; } @@ -5081,6 +4995,7 @@ void ProtocolDecl::computeKnownProtocolKind() const { if (module != module->getASTContext().getStdlibModule() && !module->getName().is("Foundation") && !module->getName().is("_Differentiation") && + !module->getName().is("_Concurrency") && // SWIFT_ENABLE_TENSORFLOW !module->getName().is("TensorFlow")) { // SWIFT_ENABLE_TENSORFLOW END @@ -5129,6 +5044,8 @@ Optional return KnownDerivableProtocolKind::AdditiveArithmetic; case KnownProtocolKind::Differentiable: return KnownDerivableProtocolKind::Differentiable; + case KnownProtocolKind::Actor: + return KnownDerivableProtocolKind::Actor; // SWIFT_ENABLE_TENSORFLOW case KnownProtocolKind::PointwiseMultiplicative: return KnownDerivableProtocolKind::PointwiseMultiplicative; @@ -5515,8 +5432,8 @@ bool VarDecl::isSettable(const DeclContext *UseDC, // If this is a convenience initializer (i.e. one that calls // self.init), then let properties are never mutable in it. They are // only mutable in designated initializers. - if (CD->getDelegatingOrChainedInitKind(nullptr) == - ConstructorDecl::BodyInitKind::Delegating) + auto initKindAndExpr = CD->getDelegatingOrChainedInitKind(); + if (initKindAndExpr.initKind == BodyInitKind::Delegating) return false; return true; @@ -6009,6 +5926,17 @@ VarDecl *VarDecl::getPropertyWrapperProjectionVar() const { return getPropertyWrapperBackingPropertyInfo().projectionVar; } +void VarDecl::visitAuxiliaryDecls(llvm::function_ref visit) const { + if (getDeclContext()->isTypeContext()) + return; + + if (auto *backingVar = getPropertyWrapperBackingProperty()) + visit(backingVar); + + if (auto *projectionVar = getPropertyWrapperProjectionVar()) + visit(projectionVar); +} + VarDecl *VarDecl::getLazyStorageProperty() const { auto &ctx = getASTContext(); auto mutableThis = const_cast(this); @@ -6399,19 +6327,19 @@ void ParamDecl::setStoredProperty(VarDecl *var) { defaultInfo->DefaultArg = var; } -Type ValueDecl::getFunctionBuilderType() const { +Type ValueDecl::getResultBuilderType() const { auto &ctx = getASTContext(); auto mutableThis = const_cast(this); return evaluateOrDefault(ctx.evaluator, - FunctionBuilderTypeRequest{mutableThis}, + ResultBuilderTypeRequest{mutableThis}, Type()); } -CustomAttr *ValueDecl::getAttachedFunctionBuilder() const { +CustomAttr *ValueDecl::getAttachedResultBuilder() const { auto &ctx = getASTContext(); auto mutableThis = const_cast(this); return evaluateOrDefault(ctx.evaluator, - AttachedFunctionBuilderRequest{mutableThis}, + AttachedResultBuilderRequest{mutableThis}, nullptr); } @@ -6844,6 +6772,17 @@ bool AbstractFunctionDecl::isAsyncHandler() const { false); } +bool AbstractFunctionDecl::canBeAsyncHandler() const { + auto func = dyn_cast(this); + if (!func) + return false; + + auto mutableFunc = const_cast(func); + return evaluateOrDefault(getASTContext().evaluator, + CanBeAsyncHandlerRequest{mutableFunc}, + false); +} + BraceStmt *AbstractFunctionDecl::getBody(bool canSynthesize) const { if ((getBodyKind() == BodyKind::Synthesize || getBodyKind() == BodyKind::Unparsed) && @@ -7361,16 +7300,15 @@ FuncDecl *FuncDecl::createImplicit(ASTContext &Context, } FuncDecl *FuncDecl::createImported(ASTContext &Context, SourceLoc FuncLoc, - DeclName Name, SourceLoc NameLoc, - bool Async, bool Throws, - ParameterList *BodyParams, - Type FnRetType, DeclContext *Parent, - ClangNode ClangN) { + DeclName Name, SourceLoc NameLoc, bool Async, + bool Throws, ParameterList *BodyParams, + Type FnRetType, + GenericParamList *GenericParams, + DeclContext *Parent, ClangNode ClangN) { assert(ClangN && FnRetType); auto *const FD = FuncDecl::createImpl( Context, SourceLoc(), StaticSpellingKind::None, FuncLoc, Name, NameLoc, - Async, SourceLoc(), Throws, SourceLoc(), - /*GenericParams=*/nullptr, Parent, ClangN); + Async, SourceLoc(), Throws, SourceLoc(), GenericParams, Parent, ClangN); FD->setParameters(BodyParams); FD->setResultInterfaceType(FnRetType); return FD; @@ -7546,6 +7484,56 @@ bool FuncDecl::isMainTypeMainMethod() const { getParameters()->size() == 0; } +bool FuncDecl::isEnqueuePartialTaskName(ASTContext &ctx, DeclName name) { + if (name.isCompoundName() && name.getBaseName() == ctx.Id_enqueue) { + auto argumentNames = name.getArgumentNames(); + return argumentNames.size() == 1 && argumentNames[0] == ctx.Id_partialTask; + } + + return false; +} + +bool FuncDecl::isActorEnqueuePartialTaskWitness() const { + if (!isEnqueuePartialTaskName(getASTContext(), getName())) + return false; + + auto classDecl = getDeclContext()->getSelfClassDecl(); + if (!classDecl) + return false; + + if (!classDecl->isActor()) + return false; + + ASTContext &ctx = getASTContext(); + auto actorProto = ctx.getProtocol(KnownProtocolKind::Actor); + if (!actorProto) + return false; + + FuncDecl *requirement = nullptr; + for (auto protoMember : actorProto->getParsedMembers()) { + if (auto protoFunc = dyn_cast(protoMember)) { + if (isEnqueuePartialTaskName(ctx, protoFunc->getName())) { + requirement = protoFunc; + break; + } + } + } + + if (!requirement) + return false; + + SmallVector conformances; + classDecl->lookupConformance( + classDecl->getModuleContext(), actorProto, conformances); + for (auto conformance : conformances) { + auto witness = conformance->getWitnessDecl(requirement); + if (witness == this) + return true; + } + + return false; +} + ConstructorDecl::ConstructorDecl(DeclName Name, SourceLoc ConstructorLoc, bool Failable, SourceLoc FailabilityLoc, bool Throws, @@ -7563,7 +7551,6 @@ ConstructorDecl::ConstructorDecl(DeclName Name, SourceLoc ConstructorLoc, if (BodyParams) setParameters(BodyParams); - Bits.ConstructorDecl.ComputedBodyInitKind = 0; Bits.ConstructorDecl.HasStubImplementation = 0; Bits.ConstructorDecl.Failable = Failable; @@ -7791,169 +7778,17 @@ CtorInitializerKind ConstructorDecl::getInitKind() const { CtorInitializerKind::Designated); } -ConstructorDecl::BodyInitKind -ConstructorDecl::getDelegatingOrChainedInitKind(DiagnosticEngine *diags, - ApplyExpr **init) const { +BodyInitKindAndExpr +ConstructorDecl::getDelegatingOrChainedInitKind() const { + return evaluateOrDefault(getASTContext().evaluator, + BodyInitKindRequest{const_cast(this)}, + BodyInitKindAndExpr()); assert(hasBody() && "Constructor does not have a definition"); +} - if (init) - *init = nullptr; - - // If we already computed the result, return it. - if (Bits.ConstructorDecl.ComputedBodyInitKind) { - auto Kind = static_cast( - Bits.ConstructorDecl.ComputedBodyInitKind - 1); - assert((Kind == BodyInitKind::None || !init) && - "can't return cached result with the init expr"); - return Kind; - } - - - struct FindReferenceToInitializer : ASTWalker { - const ConstructorDecl *Decl; - BodyInitKind Kind = BodyInitKind::None; - ApplyExpr *InitExpr = nullptr; - DiagnosticEngine *Diags; - - FindReferenceToInitializer(const ConstructorDecl *decl, - DiagnosticEngine *diags) - : Decl(decl), Diags(diags) { } - - bool walkToDeclPre(class Decl *D) override { - // Don't walk into further nominal decls. - return !isa(D); - } - - std::pair walkToExprPre(Expr *E) override { - // Don't walk into closures. - if (isa(E)) - return { false, E }; - - // Look for calls of a constructor on self or super. - auto apply = dyn_cast(E); - if (!apply) - return { true, E }; - - auto Callee = apply->getSemanticFn(); - - Expr *arg; - - if (isa(Callee)) { - arg = apply->getArg(); - } else if (auto *CRE = dyn_cast(Callee)) { - arg = CRE->getArg(); - } else if (auto *dotExpr = dyn_cast(Callee)) { - if (dotExpr->getName().getBaseName() != DeclBaseName::createConstructor()) - return { true, E }; - - arg = dotExpr->getBase(); - } else { - // Not a constructor call. - return { true, E }; - } - - // Look for a base of 'self' or 'super'. - BodyInitKind myKind; - if (arg->isSuperExpr()) - myKind = BodyInitKind::Chained; - else if (arg->isSelfExprOf(Decl, /*sameBase*/true)) - myKind = BodyInitKind::Delegating; - else { - // We're constructing something else. - return { true, E }; - } - - if (Kind == BodyInitKind::None) { - Kind = myKind; - - // If we're not emitting diagnostics, we're done. - if (!Diags) - return { false, nullptr }; - - InitExpr = apply; - return { true, E }; - } - - assert(Diags && "Failed to abort traversal early"); - - // If the kind changed, complain. - if (Kind != myKind) { - // The kind changed. Complain. - Diags->diagnose(E->getLoc(), diag::init_delegates_and_chains); - Diags->diagnose(InitExpr->getLoc(), diag::init_delegation_or_chain, - Kind == BodyInitKind::Chained); - } - - return { true, E }; - } - }; - - FindReferenceToInitializer finder(this, diags); - getBody()->walk(finder); - - // get the kind out of the finder. - auto Kind = finder.Kind; - - auto *NTD = getDeclContext()->getSelfNominalTypeDecl(); - - // Protocol extension and enum initializers are always delegating. - if (Kind == BodyInitKind::None) { - if (isa(NTD) || isa(NTD)) { - Kind = BodyInitKind::Delegating; - } - } - - // Struct initializers that cannot see the layout of the struct type are - // always delegating. This occurs if the struct type is not fixed layout, - // and the constructor is either inlinable or defined in another module. - if (Kind == BodyInitKind::None && isa(NTD)) { - // Note: This is specifically not using isFormallyResilient. We relax this - // rule for structs in non-resilient modules so that they can have inlinable - // constructors, as long as those constructors don't reference private - // declarations. - if (NTD->isResilient() && - getResilienceExpansion() == ResilienceExpansion::Minimal) { - Kind = BodyInitKind::Delegating; - - } else if (isa(getDeclContext())) { - const ModuleDecl *containingModule = getParentModule(); - // Prior to Swift 5, cross-module initializers were permitted to be - // non-delegating. However, if the struct isn't fixed-layout, we have to - // be delegating because, well, we don't know the layout. - // A dynamic replacement is permitted to be non-delegating. - if (NTD->isResilient() || - (containingModule->getASTContext().isSwiftVersionAtLeast(5) && - !getAttrs().getAttribute())) { - if (containingModule != NTD->getParentModule()) - Kind = BodyInitKind::Delegating; - } - } - } - - // If we didn't find any delegating or chained initializers, check whether - // the initializer was explicitly marked 'convenience'. - if (Kind == BodyInitKind::None && getAttrs().hasAttribute()) - Kind = BodyInitKind::Delegating; - - // If we still don't know, check whether we have a class with a superclass: it - // gets an implicit chained initializer. - if (Kind == BodyInitKind::None) { - if (auto classDecl = getDeclContext()->getSelfClassDecl()) { - if (classDecl->hasSuperclass()) - Kind = BodyInitKind::ImplicitChained; - } - } - - // Cache the result if it is trustworthy. - if (diags) { - auto *mutableThis = const_cast(this); - mutableThis->Bits.ConstructorDecl.ComputedBodyInitKind = - static_cast(Kind) + 1; - if (init) - *init = finder.InitExpr; - } - - return Kind; +void ConstructorDecl::clearCachedDelegatingOrChainedInitKind() { + getASTContext().evaluator.clearCachedOutput( + BodyInitKindRequest{const_cast(this)}); } SourceRange DestructorDecl::getSourceRange() const { @@ -8292,26 +8127,3 @@ void swift::simple_display(llvm::raw_ostream &out, AnyFunctionRef fn) { else out << "closure"; } - -bool Decl::isPrivateToEnclosingFile() const { - if (auto *VD = dyn_cast(this)) - return VD->getFormalAccess() <= AccessLevel::FilePrivate; - switch (getKind()) { - case DeclKind::Import: - case DeclKind::PatternBinding: - case DeclKind::EnumCase: - case DeclKind::TopLevelCode: - case DeclKind::IfConfig: - case DeclKind::PoundDiagnostic: - return true; - - case DeclKind::Extension: - case DeclKind::InfixOperator: - case DeclKind::PrefixOperator: - case DeclKind::PostfixOperator: - return false; - - default: - llvm_unreachable("everything else is a ValueDecl"); - } -} diff --git a/lib/AST/DeclContext.cpp b/lib/AST/DeclContext.cpp index 8aa916c6e128e..37792d2d5abf2 100644 --- a/lib/AST/DeclContext.cpp +++ b/lib/AST/DeclContext.cpp @@ -368,21 +368,17 @@ swift::FragileFunctionKindRequest::evaluate(Evaluator &evaluator, // Stored property initializer contexts use minimal resilience expansion // if the type is formally fixed layout. - if (isa(dc)) { - if (auto *NTD = dyn_cast(dc->getParent())) { - auto nominalAccess = - NTD->getFormalAccessScope(/*useDC=*/nullptr, - /*treatUsableFromInlineAsPublic=*/true); - if (!nominalAccess.isPublic()) - return {FragileFunctionKind::None, - /*allowUsableFromInline=*/false}; - - if (NTD->isFormallyResilient()) - return {FragileFunctionKind::None, - /*allowUsableFromInline=*/false}; - - return {FragileFunctionKind::PropertyInitializer, true}; + if (auto *init = dyn_cast (dc)) { + auto bindingIndex = init->getBindingIndex(); + if (auto *varDecl = init->getBinding()->getAnchoringVarDecl(bindingIndex)) { + if (varDecl->isInitExposedToClients()) { + return {FragileFunctionKind::PropertyInitializer, + /*allowUsableFromInline=*/true}; + } } + + return {FragileFunctionKind::None, + /*allowUsableFromInline=*/false}; } if (auto *AFD = dyn_cast(dc)) { @@ -434,7 +430,8 @@ swift::FragileFunctionKindRequest::evaluate(Evaluator &evaluator, } } - return {FragileFunctionKind::None, false}; + return {FragileFunctionKind::None, + /*allowUsableFromInline=*/false}; } /// Determine whether the innermost context is generic. @@ -764,10 +761,43 @@ ArrayRef IterableDeclContext::getSemanticMembers() const { ArrayRef()); } +void IterableDeclContext::addMemberPreservingSourceOrder(Decl *member) { + auto &SM = getASTContext().SourceMgr; + + SourceLoc start = member->getStartLoc(); + Decl *hint = nullptr; + + for (auto *existingMember : getMembers()) { + if (existingMember->isImplicit()) + continue; + + // An EnumCaseDecl contains one or more EnumElementDecls, + // but the EnumElementDecls are also added as members of + // the parent enum. We ignore the EnumCaseDecl since its + // source range overlaps with that of the EnumElementDecls. + if (isa(existingMember)) + continue; + + // The elements of the active clause of an IfConfigDecl + // are added to the parent type. We ignore the IfConfigDecl + // since its source range overlaps with the source ranges + // of the active elements. + if (isa(existingMember)) + continue; + + if (!SM.isBeforeInBuffer(existingMember->getEndLoc(), start)) + break; + + hint = existingMember; + } + + addMember(member, hint, /*insertAtHead=*/hint == nullptr); +} + /// Add a member to this context. -void IterableDeclContext::addMember(Decl *member, Decl *Hint) { +void IterableDeclContext::addMember(Decl *member, Decl *hint, bool insertAtHead) { // Add the member to the list of declarations without notification. - addMemberSilently(member, Hint); + addMemberSilently(member, hint, insertAtHead); // Notify our parent declaration that we have added the member, which can // be used to update the lookup tables. @@ -790,29 +820,95 @@ void IterableDeclContext::addMember(Decl *member, Decl *Hint) { } } -void IterableDeclContext::addMemberSilently(Decl *member, Decl *hint) const { +void IterableDeclContext::addMemberSilently(Decl *member, Decl *hint, + bool insertAtHead) const { assert(!isa(member) && "Accessors should not be added here"); assert(!member->NextDecl && "Already added to a container"); - // If there is a hint decl that specifies where to add this, just - // link into the chain immediately following it. - if (hint) { +#ifndef NDEBUG + // Assert that new declarations are always added in source order. + auto checkSourceRange = [&](Decl *prev, Decl *next) { + // SKip these checks for imported and deserialized decls. + if (!member->getDeclContext()->getParentSourceFile()) + return; + + auto shouldSkip = [](Decl *d) { + // PatternBindingDecl source ranges overlap with VarDecls, + // EnumCaseDecl source ranges overlap with EnumElementDecls, + // and IfConfigDecl source ranges overlap with the elements + // of the active clause. Skip them all here to avoid + // spurious assertions. + if (isa(d) || + isa(d) || + isa(d)) + return true; + + // Ignore source location information of implicit declarations. + if (d->isImplicit()) + return true; + + return false; + }; + + if (shouldSkip(prev) || shouldSkip(next)) + return; + + SourceLoc prevEnd = prev->getEndLoc(); + SourceLoc nextStart = next->getStartLoc(); + + assert(prevEnd.isValid() && + "Only implicit decls can have invalid source location"); + assert(nextStart.isValid() && + "Only implicit decls can have invalid source location"); + + if (getASTContext().SourceMgr.isBeforeInBuffer(prevEnd, nextStart)) + return; + + llvm::errs() << "Source ranges out of order in addMember():\n"; + prev->dump(llvm::errs()); + next->dump(llvm::errs()); + abort(); + }; +#endif + + // Empty list. + if (!FirstDeclAndLazyMembers.getPointer()) { + assert(hint == nullptr); + + FirstDeclAndLazyMembers.setPointer(member); + LastDeclAndKind.setPointer(member); + + // Insertion at the head. + } else if (insertAtHead) { + assert(hint == nullptr); + + member->NextDecl = FirstDeclAndLazyMembers.getPointer(); + FirstDeclAndLazyMembers.setPointer(member); + + // Insertion at the tail. + } else if (hint == nullptr) { + auto *last = LastDeclAndKind.getPointer(); + +#ifndef NDEBUG + checkSourceRange(last, member); +#endif + + last->NextDecl = member; + LastDeclAndKind.setPointer(member); + + // Insertion after 'hint' (which may be the tail). + } else { +#ifndef NDEBUG + checkSourceRange(hint, member); +#endif + member->NextDecl = hint->NextDecl; hint->NextDecl = member; - // If the hint was the last in the parent context's chain, update it. + // Handle case where the 'hint' is the tail. if (LastDeclAndKind.getPointer() == hint) LastDeclAndKind.setPointer(member); - return; } - - if (auto last = LastDeclAndKind.getPointer()) { - last->NextDecl = member; - assert(last != member && "Simple cycle in decl list"); - } else { - FirstDeclAndLazyMembers.setPointer(member); - } - LastDeclAndKind.setPointer(member); } void IterableDeclContext::setMemberLoader(LazyMemberLoader *loader, @@ -933,7 +1029,7 @@ bool IterableDeclContext::areTokensHashedForThisBodyInsteadOfInterfaceHash() // corresponding to the fingerprinted nominal dependency node. if (isa(this)) return false; - return getASTContext().LangOpts.EnableTypeFingerprints; + return true; } /// Return the DeclContext to compare when checking private access in @@ -1170,3 +1266,18 @@ void swift::simple_display(llvm::raw_ostream &out, SourceLoc swift::extractNearestSourceLoc(const IterableDeclContext *idc) { return extractNearestSourceLoc(idc->getDecl()); } + +static bool isSpecializeExtensionContext(const DeclContext *dc) { + if (dc->isModuleScopeContext()) + return false; + if (auto *extCtx = dyn_cast(dc)) { + // and has specialized attr ... + return extCtx->getAttrs().hasAttribute(); + } + auto *parentDecl = dc->getParent(); + return isSpecializeExtensionContext(parentDecl); +} + +bool DeclContext::isInSpecializeExtensionContext() const { + return isSpecializeExtensionContext(this); +} diff --git a/lib/AST/DiagnosticEngine.cpp b/lib/AST/DiagnosticEngine.cpp index 9ab4e08c42620..4da5833525b55 100644 --- a/lib/AST/DiagnosticEngine.cpp +++ b/lib/AST/DiagnosticEngine.cpp @@ -660,6 +660,30 @@ static void formatDiagnosticArgument(StringRef Modifier, Out << FormatOpts.OpeningQuotationMark << Arg.getAsLayoutConstraint() << FormatOpts.ClosingQuotationMark; break; + case DiagnosticArgumentKind::ActorIsolation: + switch (auto isolation = Arg.getAsActorIsolation()) { + case ActorIsolation::ActorInstance: + Out << "actor-isolated"; + break; + + case ActorIsolation::GlobalActor: + Out << "global actor " << FormatOpts.OpeningQuotationMark + << isolation.getGlobalActor().getString() + << FormatOpts.ClosingQuotationMark << "-isolated"; + break; + + case ActorIsolation::Independent: + Out << "actor-independent"; + break; + + case ActorIsolation::IndependentUnsafe: + Out << "actor-independent-unsafe"; + break; + + case ActorIsolation::Unspecified: + Out << "non-actor-isolated"; + break; + } } } diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index e91138730e8b9..f721934182b05 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -871,7 +871,7 @@ llvm::APFloat FloatLiteralExpr::getValue() const { StringLiteralExpr::StringLiteralExpr(StringRef Val, SourceRange Range, bool Implicit) - : LiteralExpr(ExprKind::StringLiteral, Implicit), Val(Val), + : BuiltinLiteralExpr(ExprKind::StringLiteral, Implicit), Val(Val), Range(Range) { Bits.StringLiteralExpr.Encoding = static_cast(UTF8); Bits.StringLiteralExpr.IsSingleUnicodeScalar = @@ -2463,6 +2463,11 @@ void swift::simple_display(llvm::raw_ostream &out, simple_display(out, expr->getDefaultArgsOwner().getDecl()); } +void swift::simple_display(llvm::raw_ostream &out, + const Expr *expr) { + out << "expression"; +} + SourceLoc swift::extractNearestSourceLoc(const DefaultArgumentExpr *expr) { return expr->getLoc(); } diff --git a/lib/AST/ExtInfo.cpp b/lib/AST/ExtInfo.cpp index 1fb30adc6af16..a7b2d74955b41 100644 --- a/lib/AST/ExtInfo.cpp +++ b/lib/AST/ExtInfo.cpp @@ -34,6 +34,10 @@ bool operator==(ClangTypeInfo lhs, ClangTypeInfo rhs) { return false; } +bool operator!=(ClangTypeInfo lhs, ClangTypeInfo rhs) { + return !(lhs == rhs); +} + ClangTypeInfo ClangTypeInfo::getCanonical() const { if (!type) return ClangTypeInfo(); @@ -92,6 +96,10 @@ Optional UnexpectedClangTypeError::checkClangType( } void UnexpectedClangTypeError::dump() { +#if SWIFT_BUILD_ONLY_SYNTAXPARSERLIB + return; // not needed for the parser library. +#endif + auto &e = llvm::errs(); using Kind = UnexpectedClangTypeError::Kind; switch (errorKind) { diff --git a/lib/AST/FineGrainedDependencies.cpp b/lib/AST/FineGrainedDependencies.cpp index 5543d03c7f4a5..594b78931eaac 100644 --- a/lib/AST/FineGrainedDependencies.cpp +++ b/lib/AST/FineGrainedDependencies.cpp @@ -58,6 +58,15 @@ SourceFileDepGraph::loadFromBuffer(llvm::MemoryBuffer &buffer) { return Optional(std::move(fg)); } +Optional +SourceFileDepGraph::loadFromSwiftModuleBuffer(llvm::MemoryBuffer &buffer) { + SourceFileDepGraph fg; + if (swift::fine_grained_dependencies:: + readFineGrainedDependencyGraphFromSwiftModule(buffer, fg)) + return None; + return Optional(std::move(fg)); +} + //============================================================================== // MARK: SourceFileDepGraph access //============================================================================== @@ -231,6 +240,7 @@ std::string DependencyKey::humanReadableName() const { switch (kind) { case NodeKind::member: return demangleTypeAsContext(context) + "." + name; + case NodeKind::incrementalExternalDepend: case NodeKind::externalDepend: case NodeKind::sourceFileProvide: return llvm::sys::path::filename(name).str(); @@ -261,9 +271,12 @@ raw_ostream &fine_grained_dependencies::operator<<(raw_ostream &out, bool DependencyKey::verify() const { assert((getKind() != NodeKind::externalDepend || isInterface()) && "All external dependencies must be interfaces."); + assert((getKind() != NodeKind::incrementalExternalDepend || isInterface()) && + "All incremental external dependencies must be interfaces."); switch (getKind()) { case NodeKind::topLevel: case NodeKind::dynamicLookup: + case NodeKind::incrementalExternalDepend: case NodeKind::externalDepend: case NodeKind::sourceFileProvide: assert(context.empty() && !name.empty() && "Must only have a name"); @@ -294,6 +307,7 @@ void DependencyKey::verifyNodeKindNames() { CHECK_NAME(potentialMember) CHECK_NAME(member) CHECK_NAME(dynamicLookup) + CHECK_NAME(incrementalExternalDepend) CHECK_NAME(externalDepend) CHECK_NAME(sourceFileProvide) case NodeKind::kindCount: diff --git a/lib/AST/FineGrainedDependencyFormat.cpp b/lib/AST/FineGrainedDependencyFormat.cpp index b52cc2af98baf..659bc3b0a3001 100644 --- a/lib/AST/FineGrainedDependencyFormat.cpp +++ b/lib/AST/FineGrainedDependencyFormat.cpp @@ -44,7 +44,8 @@ class Deserializer { public: Deserializer(llvm::MemoryBufferRef Data) : Cursor(Data) {} - bool readFineGrainedDependencyGraph(SourceFileDepGraph &g); + bool readFineGrainedDependencyGraph(SourceFileDepGraph &g, Purpose purpose); + bool readFineGrainedDependencyGraphFromSwiftModule(SourceFileDepGraph &g); }; } // end namespace @@ -150,14 +151,24 @@ static llvm::Optional getDeclAspect(unsigned declAspect) { return None; } -bool Deserializer::readFineGrainedDependencyGraph(SourceFileDepGraph &g) { +bool Deserializer::readFineGrainedDependencyGraph(SourceFileDepGraph &g, + Purpose purpose) { using namespace record_block; - if (readSignature()) - return true; + switch (purpose) { + case Purpose::ForSwiftDeps: + if (readSignature()) + return true; - if (enterTopLevelBlock()) - return true; + if (enterTopLevelBlock()) + return true; + LLVM_FALLTHROUGH; + case Purpose::ForSwiftModule: + // N.B. Incremental metadata embedded in swiftmodule files does not have + // a leading signature, and its top-level block has already been + // consumed by the time we get here. + break; + } if (readMetadata()) return true; @@ -259,7 +270,7 @@ bool Deserializer::readFineGrainedDependencyGraph(SourceFileDepGraph &g) { bool swift::fine_grained_dependencies::readFineGrainedDependencyGraph( llvm::MemoryBuffer &buffer, SourceFileDepGraph &g) { Deserializer deserializer(buffer.getMemBufferRef()); - return deserializer.readFineGrainedDependencyGraph(g); + return deserializer.readFineGrainedDependencyGraph(g, Purpose::ForSwiftDeps); } bool swift::fine_grained_dependencies::readFineGrainedDependencyGraph( @@ -289,16 +300,13 @@ class Serializer { unsigned LastIdentifierID = 0; std::vector IdentifiersToWrite; - SmallVector Buffer; - llvm::BitstreamWriter Out{Buffer}; + llvm::BitstreamWriter &Out; /// A reusable buffer for emitting records. SmallVector ScratchRecord; std::array AbbrCodes; - void writeFineGrainedDependencyGraph(const SourceFileDepGraph &g); - void addIdentifier(StringRef str); unsigned getIdentifier(StringRef str); @@ -321,8 +329,11 @@ class Serializer { void writeMetadata(); public: - void writeFineGrainedDependencyGraph(llvm::raw_ostream &os, - const SourceFileDepGraph &g); + Serializer(llvm::BitstreamWriter &ExistingOut) : Out(ExistingOut) {} + +public: + void writeFineGrainedDependencyGraph(const SourceFileDepGraph &g, + Purpose purpose); }; } // end namespace @@ -382,11 +393,21 @@ void Serializer::writeMetadata() { } void -Serializer::writeFineGrainedDependencyGraph(const SourceFileDepGraph &g) { - writeSignature(); - writeBlockInfoBlock(); +Serializer::writeFineGrainedDependencyGraph(const SourceFileDepGraph &g, + Purpose purpose) { + unsigned blockID = 0; + switch (purpose) { + case Purpose::ForSwiftDeps: + writeSignature(); + writeBlockInfoBlock(); + blockID = RECORD_BLOCK_ID; + break; + case Purpose::ForSwiftModule: + blockID = INCREMENTAL_INFORMATION_BLOCK_ID; + break; + } - llvm::BCBlockRAII restoreBlock(Out, RECORD_BLOCK_ID, 8); + llvm::BCBlockRAII restoreBlock(Out, blockID, 8); using namespace record_block; @@ -467,20 +488,129 @@ unsigned Serializer::getIdentifier(StringRef str) { return iter->second; } -void Serializer::writeFineGrainedDependencyGraph(llvm::raw_ostream &os, - const SourceFileDepGraph &g) { - writeFineGrainedDependencyGraph(g); - os.write(Buffer.data(), Buffer.size()); - os.flush(); +void swift::fine_grained_dependencies::writeFineGrainedDependencyGraph( + llvm::BitstreamWriter &Out, const SourceFileDepGraph &g, + Purpose purpose) { + Serializer serializer{Out}; + serializer.writeFineGrainedDependencyGraph(g, purpose); } -bool swift::fine_grained_dependencies::writeFineGrainedDependencyGraph( +bool swift::fine_grained_dependencies::writeFineGrainedDependencyGraphToPath( DiagnosticEngine &diags, StringRef path, const SourceFileDepGraph &g) { PrettyStackTraceStringAction stackTrace("saving fine-grained dependency graph", path); return withOutputFile(diags, path, [&](llvm::raw_ostream &out) { - Serializer serializer; - serializer.writeFineGrainedDependencyGraph(out, g); + SmallVector Buffer; + llvm::BitstreamWriter Writer{Buffer}; + writeFineGrainedDependencyGraph(Writer, g, Purpose::ForSwiftDeps); + out.write(Buffer.data(), Buffer.size()); + out.flush(); return false; }); -} \ No newline at end of file +} + +static bool checkModuleSignature(llvm::BitstreamCursor &cursor, + ArrayRef signature) { + for (unsigned char byte : signature) { + if (cursor.AtEndOfStream()) + return false; + if (llvm::Expected maybeRead = + cursor.Read(8)) { + if (maybeRead.get() != byte) + return false; + } else { + consumeError(maybeRead.takeError()); + return false; + } + } + return true; +} + +static bool enterTopLevelModuleBlock(llvm::BitstreamCursor &cursor, unsigned ID, + bool shouldReadBlockInfo = true) { + llvm::Expected maybeNext = cursor.advance(); + if (!maybeNext) { + consumeError(maybeNext.takeError()); + return false; + } + llvm::BitstreamEntry next = maybeNext.get(); + + if (next.Kind != llvm::BitstreamEntry::SubBlock) + return false; + + if (next.ID == llvm::bitc::BLOCKINFO_BLOCK_ID) { + if (shouldReadBlockInfo) { + if (!cursor.ReadBlockInfoBlock()) + return false; + } else { + if (cursor.SkipBlock()) + return false; + } + return enterTopLevelModuleBlock(cursor, ID, false); + } + + if (next.ID != ID) + return false; + + if (llvm::Error Err = cursor.EnterSubBlock(ID)) { + consumeError(std::move(Err)); + return false; + } + + return true; +} + +bool swift::fine_grained_dependencies:: + readFineGrainedDependencyGraphFromSwiftModule(llvm::MemoryBuffer &buffer, + SourceFileDepGraph &g) { + Deserializer deserializer(buffer.getMemBufferRef()); + return deserializer.readFineGrainedDependencyGraphFromSwiftModule(g); +} + +bool Deserializer::readFineGrainedDependencyGraphFromSwiftModule( + SourceFileDepGraph &g) { + if (!checkModuleSignature(Cursor, {0xE2, 0x9C, 0xA8, 0x0E}) || + !enterTopLevelModuleBlock(Cursor, llvm::bitc::FIRST_APPLICATION_BLOCKID, false)) { + return true; + } + + llvm::BitstreamEntry topLevelEntry; + bool DidNotReadFineGrainedDependencies = true; + while (!Cursor.AtEndOfStream()) { + llvm::Expected maybeEntry = + Cursor.advance(llvm::BitstreamCursor::AF_DontPopBlockAtEnd); + if (!maybeEntry) { + consumeError(maybeEntry.takeError()); + return true; + } + topLevelEntry = maybeEntry.get(); + if (topLevelEntry.Kind != llvm::BitstreamEntry::SubBlock) + break; + + switch (topLevelEntry.ID) { + case INCREMENTAL_INFORMATION_BLOCK_ID: { + if (llvm::Error Err = + Cursor.EnterSubBlock(INCREMENTAL_INFORMATION_BLOCK_ID)) { + consumeError(std::move(Err)); + return true; + } + if (readFineGrainedDependencyGraph(g, Purpose::ForSwiftModule)) { + break; + } + + DidNotReadFineGrainedDependencies = false; + break; + } + + default: + // Unknown top-level block, possibly for use by a future version of the + // module format. + if (Cursor.SkipBlock()) { + return true; + } + break; + } + } + + return DidNotReadFineGrainedDependencies; +} diff --git a/lib/AST/FrontendSourceFileDepGraphFactory.cpp b/lib/AST/FrontendSourceFileDepGraphFactory.cpp index c6e75bc3aa7a9..049d6c7b57205 100644 --- a/lib/AST/FrontendSourceFileDepGraphFactory.cpp +++ b/lib/AST/FrontendSourceFileDepGraphFactory.cpp @@ -65,50 +65,6 @@ static std::string mangleTypeAsContext(const NominalTypeDecl *NTD) { return !NTD ? "" : Mangler.mangleTypeAsContextUSR(NTD); } -//============================================================================== -// MARK: Privacy queries -//============================================================================== - -/// Return true if \ref ED does not contain a member that can affect other -/// files. -static bool allMembersArePrivate(const ExtensionDecl *ED) { - return std::all_of( - ED->getMembers().begin(), ED->getMembers().end(), - [](const Decl *d) { return d->isPrivateToEnclosingFile(); }); -} - -/// \ref inheritedType, an inherited protocol, return true if this inheritance -/// cannot affect other files. -static bool extendedTypeIsPrivate(TypeLoc inheritedType) { - auto type = inheritedType.getType(); - if (!type) - return true; - - if (!type->isExistentialType()) { - // Be conservative. We don't know how to deal with other extended types. - return false; - } - - auto layout = type->getExistentialLayout(); - assert(!layout.explicitSuperclass && - "Should not have a subclass existential " - "in the inheritance clause of an extension"); - for (auto protoTy : layout.getProtocols()) { - if (!protoTy->getDecl()->isPrivateToEnclosingFile()) - return false; - } - - return true; -} - -/// Return true if \ref ED does not inherit a protocol that can affect other -/// files. Was called "justMembers" in ReferenceDependencies.cpp -/// \ref ED might be null. -static bool allInheritedProtocolsArePrivate(const ExtensionDecl *ED) { - return std::all_of(ED->getInherited().begin(), ED->getInherited().end(), - extendedTypeIsPrivate); -} - //============================================================================== // MARK: DependencyKey - creation for Decls //============================================================================== @@ -236,30 +192,22 @@ std::string DependencyKey::computeNameForProvidedEntity< // MARK: Entry point into frontend graph construction //============================================================================== -bool fine_grained_dependencies::emitReferenceDependencies( - DiagnosticEngine &diags, SourceFile *const SF, - const DependencyTracker &depTracker, - StringRef outputPath, - const bool alsoEmitDotFile) { - - // Before writing to the dependencies file path, preserve any previous file - // that may have been there. No error handling -- this is just a nicety, it - // doesn't matter if it fails. - llvm::sys::fs::rename(outputPath, outputPath + "~"); - - SourceFileDepGraph g = FrontendSourceFileDepGraphFactory( - SF, outputPath, depTracker, alsoEmitDotFile) - .construct(); - - bool hadError = writeFineGrainedDependencyGraph(diags, outputPath, g); - - // If path is stdout, cannot read it back, so check for "-" - assert(outputPath == "-" || g.verifyReadsWhatIsWritten(outputPath)); - - if (alsoEmitDotFile) - g.emitDotFile(outputPath, diags); - - return hadError; +bool fine_grained_dependencies::withReferenceDependencies( + llvm::PointerUnion MSF, + const DependencyTracker &depTracker, StringRef outputPath, + bool alsoEmitDotFile, + llvm::function_ref cont) { + if (auto *MD = MSF.dyn_cast()) { + SourceFileDepGraph g = + ModuleDepGraphFactory(MD, alsoEmitDotFile).construct(); + return cont(std::move(g)); + } else { + auto *SF = MSF.get(); + SourceFileDepGraph g = FrontendSourceFileDepGraphFactory( + SF, outputPath, depTracker, alsoEmitDotFile) + .construct(); + return cont(std::move(g)); + } } //============================================================================== @@ -270,30 +218,24 @@ FrontendSourceFileDepGraphFactory::FrontendSourceFileDepGraphFactory( SourceFile *SF, StringRef outputPath, const DependencyTracker &depTracker, const bool alsoEmitDotFile) : AbstractSourceFileDepGraphFactory( - computeIncludePrivateDeps(SF), SF->getASTContext().hadError(), + SF->getASTContext().hadError(), outputPath, getInterfaceHash(SF), alsoEmitDotFile, SF->getASTContext().Diags), SF(SF), depTracker(depTracker) {} -bool FrontendSourceFileDepGraphFactory::computeIncludePrivateDeps( - SourceFile *SF) { - // Since, when fingerprints are enabled, - // the parser diverts token hashing into per-body fingerprints - // before it can know if a difference is in a private type, - // in order to be able to test the changed fingerprints - // we force the inclusion of private declarations when fingerprints - // are enabled. - return SF->getASTContext() - .LangOpts.FineGrainedDependenciesIncludeIntrafileOnes || - SF->getASTContext().LangOpts.EnableTypeFingerprints; -} - /// Centralize the invariant that the fingerprint of the whole file is the /// interface hash std::string FrontendSourceFileDepGraphFactory::getFingerprint(SourceFile *SF) { return getInterfaceHash(SF); } +std::string +FrontendSourceFileDepGraphFactory::getInterfaceHash(SourceFile *SF) { + llvm::SmallString<32> interfaceHash; + SF->getInterfaceHash(interfaceHash); + return interfaceHash.str().str(); +} + //============================================================================== // MARK: FrontendSourceFileDepGraphFactory - adding collections of defined Decls //============================================================================== @@ -305,10 +247,6 @@ namespace { /// Takes all the Decls in a SourceFile, and collects them into buckets by /// groups of DeclKinds. Also casts them to more specific types struct DeclFinder { - /// Existing system excludes private decls in some cases. - /// In the future, we might not want to do this, so use bool to decide. - const bool includePrivateDecls; - // The extracted Decls: ConstPtrVec extensions; ConstPtrVec operators; @@ -327,19 +265,17 @@ struct DeclFinder { /// Construct me and separates the Decls. // clang-format off DeclFinder(ArrayRef topLevelDecls, - const bool includePrivateDecls, - LookupClassMember lookupClassMember) - : includePrivateDecls(includePrivateDecls) { + LookupClassMember lookupClassMember) { for (const Decl *const D : topLevelDecls) { - select(D, extensions, false) || + select(D, extensions) || select(D, operators, false) || + DeclKind::PostfixOperator>(D, operators) || select( - D, precedenceGroups, false) || + D, precedenceGroups) || select(D, topNominals, true) || + DeclKind::Class, DeclKind::Protocol>(D, topNominals) || select(D, topValues, true); + DeclKind::Accessor>(D, topValues); } // clang-format on // The order is important because some of these use instance variables @@ -368,18 +304,7 @@ struct DeclFinder { /// (indirectly recursive) void findNominalsAndOperatorsIn(const NominalTypeDecl *const NTD, const ExtensionDecl *ED = nullptr) { - if (excludeIfPrivate(NTD)) - return; - const bool exposedProtocolIsExtended = - ED && !allInheritedProtocolsArePrivate(ED); - if (ED && !includePrivateDecls && !exposedProtocolIsExtended && - std::all_of( - ED->getMembers().begin(), ED->getMembers().end(), - [&](const Decl *D) { return D->isPrivateToEnclosingFile(); })) { - return; - } - if (includePrivateDecls || !ED || exposedProtocolIsExtended) - allNominals.push_back(NTD); + allNominals.push_back(NTD); potentialMemberHolders.push_back(NTD); findNominalsAndOperatorsInMembers(ED ? ED->getMembers() : NTD->getMembers()); @@ -391,7 +316,7 @@ struct DeclFinder { void findNominalsAndOperatorsInMembers(const DeclRange members) { for (const Decl *const D : members) { auto *VD = dyn_cast(D); - if (!VD || excludeIfPrivate(VD)) + if (!VD) continue; if (VD->getName().isOperator()) memberOperatorDecls.push_back(cast(D)); @@ -404,19 +329,20 @@ struct DeclFinder { void findValuesInExtensions() { for (const auto *ED : extensions) { const auto *const NTD = ED->getExtendedNominal(); - if (!NTD || excludeIfPrivate(NTD)) - continue; - if (!includePrivateDecls && - (!allInheritedProtocolsArePrivate(ED) || allMembersArePrivate(ED))) + if (!NTD) { continue; - for (const auto *member : ED->getMembers()) - if (const auto *VD = dyn_cast(member)) - if (VD->hasName() && - (includePrivateDecls || !VD->isPrivateToEnclosingFile())) { - const auto *const NTD = ED->getExtendedNominal(); - if (NTD) - valuesInExtensions.push_back(std::make_pair(NTD, VD)); - } + } + + for (const auto *member : ED->getMembers()) { + const auto *VD = dyn_cast(member); + if (!VD || !VD->hasName()) { + continue; + } + + if (const auto *const NTD = ED->getExtendedNominal()) { + valuesInExtensions.push_back(std::make_pair(NTD, VD)); + } + } } } @@ -439,30 +365,19 @@ struct DeclFinder { /// \returns true if successful. template - bool select(const Decl *const D, ConstPtrVec &foundDecls, - const bool canExcludePrivateDecls) { + bool select(const Decl *const D, ConstPtrVec &foundDecls) { if (D->getKind() == firstKind) { - auto *dd = cast(D); - const bool exclude = canExcludePrivateDecls && excludeIfPrivate(dd); - if (!exclude) - foundDecls.push_back(cast(D)); + foundDecls.push_back(cast(D)); return true; } - return select(D, foundDecls, - canExcludePrivateDecls); + return select(D, foundDecls); } /// Terminate the template recursion. template - bool select(const Decl *const D, ConstPtrVec &foundDecls, - bool) { + bool select(const Decl *const D, ConstPtrVec &foundDecls) { return false; } - - /// Return true if \param D should be excluded on privacy grounds. - bool excludeIfPrivate(const Decl *const D) { - return !includePrivateDecls && D->isPrivateToEnclosingFile(); - } }; } // namespace @@ -472,7 +387,7 @@ void FrontendSourceFileDepGraphFactory::addAllDefinedDecls() { // Many kinds of Decls become top-level depends. - DeclFinder declFinder(SF->getTopLevelDecls(), includePrivateDeps, + DeclFinder declFinder(SF->getTopLevelDecls(), [this](VisibleDeclConsumer &consumer) { SF->lookupClassMembers({}, consumer); }); @@ -499,7 +414,8 @@ template void FrontendSourceFileDepGraphFactory::addAllDefinedDeclsOfAGivenType( std::vector &contentsVec) { for (const auto &declOrPair : contentsVec) { - Optional fp = getFingerprintIfAny(declOrPair); + Optional fp = + AbstractSourceFileDepGraphFactory::getFingerprintIfAny(declOrPair); addADefinedDecl( DependencyKey::createForProvidedEntityInterface(declOrPair), fp ? StringRef(fp.getValue()) : Optional()); @@ -521,14 +437,11 @@ class UsedDeclEnumerator { const DependencyKey sourceFileInterface; const DependencyKey sourceFileImplementation; - const bool includeIntrafileDeps; - function_ref createDefUse; public: UsedDeclEnumerator( SourceFile *SF, const DependencyTracker &depTracker, StringRef swiftDeps, - bool includeIntrafileDeps, function_ref createDefUse) : SF(SF), depTracker(depTracker), swiftDeps(swiftDeps), @@ -536,7 +449,7 @@ class UsedDeclEnumerator { DeclAspect::interface, swiftDeps)), sourceFileImplementation(DependencyKey::createKeyForWholeSourceFile( DeclAspect::implementation, swiftDeps)), - includeIntrafileDeps(includeIntrafileDeps), createDefUse(createDefUse) { + createDefUse(createDefUse) { } public: @@ -589,11 +502,6 @@ class UsedDeclEnumerator { return; } - bool isPrivate = subject->isPrivateToEnclosingFile(); - if (isPrivate && !includeIntrafileDeps) { - return; - } - std::string context = DependencyKey::computeContextForProvidedEntity( subject); @@ -602,6 +510,9 @@ class UsedDeclEnumerator { } void enumerateExternalUses() { + for (StringRef s : depTracker.getIncrementalDependencies()) + enumerateUse("", s); + for (StringRef s : depTracker.getDependencies()) enumerateUse("", s); } @@ -609,7 +520,7 @@ class UsedDeclEnumerator { } // end namespace void FrontendSourceFileDepGraphFactory::addAllUsedDecls() { - UsedDeclEnumerator(SF, depTracker, swiftDeps, includePrivateDeps, + UsedDeclEnumerator(SF, depTracker, swiftDeps, [&](const DependencyKey &def, const DependencyKey &use) { addAUsedDecl(def, use); }) @@ -617,28 +528,54 @@ void FrontendSourceFileDepGraphFactory::addAllUsedDecls() { } //============================================================================== -// MARK: FrontendSourceFileDepGraphFactory - adding individual defined Decls +// MARK: ModuleDepGraphFactory //============================================================================== -std::string -FrontendSourceFileDepGraphFactory::getInterfaceHash(SourceFile *SF) { - llvm::SmallString<32> interfaceHash; - SF->getInterfaceHash(interfaceHash); - return interfaceHash.str().str(); -} +ModuleDepGraphFactory::ModuleDepGraphFactory(ModuleDecl *Mod, bool emitDot) + : AbstractSourceFileDepGraphFactory( + Mod->getASTContext().hadError(), + Mod->getNameStr(), "0xBADBEEF", emitDot, Mod->getASTContext().Diags), + Mod(Mod) {} -/// At present, only \c NominalTypeDecls have (body) fingerprints -Optional FrontendSourceFileDepGraphFactory::getFingerprintIfAny( - std::pair) { - return None; +void ModuleDepGraphFactory::addAllDefinedDecls() { + // TODO: express the multiple provides and depends streams with variadic + // templates + + // Many kinds of Decls become top-level depends. + + SmallVector TopLevelDecls; + Mod->getTopLevelDecls(TopLevelDecls); + DeclFinder declFinder(TopLevelDecls, + [this](VisibleDeclConsumer &consumer) { + return Mod->lookupClassMembers({}, consumer); + }); + + addAllDefinedDeclsOfAGivenType( + declFinder.precedenceGroups); + addAllDefinedDeclsOfAGivenType( + declFinder.memberOperatorDecls); + addAllDefinedDeclsOfAGivenType(declFinder.operators); + addAllDefinedDeclsOfAGivenType(declFinder.topNominals); + addAllDefinedDeclsOfAGivenType(declFinder.topValues); + addAllDefinedDeclsOfAGivenType(declFinder.allNominals); + addAllDefinedDeclsOfAGivenType( + declFinder.potentialMemberHolders); + addAllDefinedDeclsOfAGivenType( + declFinder.valuesInExtensions); + addAllDefinedDeclsOfAGivenType( + declFinder.classMembers); } -Optional -FrontendSourceFileDepGraphFactory::getFingerprintIfAny(const Decl *d) { - if (const auto *idc = dyn_cast(d)) { - auto result = idc->getBodyFingerprint(); - assert((!result || !result->empty()) && - "Fingerprint should never be empty"); - return result; + +/// Given an array of Decls or pairs of them in \p declsOrPairs +/// create node pairs for context and name +template +void ModuleDepGraphFactory::addAllDefinedDeclsOfAGivenType( + std::vector &contentsVec) { + for (const auto &declOrPair : contentsVec) { + Optional fp = + AbstractSourceFileDepGraphFactory::getFingerprintIfAny(declOrPair); + addADefinedDecl( + DependencyKey::createForProvidedEntityInterface(declOrPair), + fp ? StringRef(fp.getValue()) : Optional()); } - return None; } diff --git a/lib/AST/FrontendSourceFileDepGraphFactory.h b/lib/AST/FrontendSourceFileDepGraphFactory.h index 496c458412732..d11fc431324f1 100644 --- a/lib/AST/FrontendSourceFileDepGraphFactory.h +++ b/lib/AST/FrontendSourceFileDepGraphFactory.h @@ -35,8 +35,6 @@ class FrontendSourceFileDepGraphFactory private: static std::string getFingerprint(SourceFile *SF); - - static bool computeIncludePrivateDeps(SourceFile *SF); static std::string getInterfaceHash(SourceFile *SF); void addAllDefinedDecls() override; @@ -46,12 +44,24 @@ class FrontendSourceFileDepGraphFactory /// create node pairs for context and name template void addAllDefinedDeclsOfAGivenType(std::vector &contentsVec); +}; - /// At present, only nominals, protocols, and extensions have (body) - /// fingerprints - static Optional - getFingerprintIfAny(std::pair); - static Optional getFingerprintIfAny(const Decl *d); +class ModuleDepGraphFactory : public AbstractSourceFileDepGraphFactory { + ModuleDecl *const Mod; + +public: + ModuleDepGraphFactory(ModuleDecl *Mod, bool emitDot); + + ~ModuleDepGraphFactory() override = default; + +private: + void addAllDefinedDecls() override; + void addAllUsedDecls() override {} + + /// Given an array of Decls or pairs of them in \p declsOrPairs + /// create node pairs for context and name + template + void addAllDefinedDeclsOfAGivenType(std::vector &contentsVec); }; } // namespace fine_grained_dependencies diff --git a/lib/AST/GenericParamList.cpp b/lib/AST/GenericParamList.cpp new file mode 100644 index 0000000000000..426833e151997 --- /dev/null +++ b/lib/AST/GenericParamList.cpp @@ -0,0 +1,129 @@ +//===--- GenericParamList.cpp - Swift Language Decl ASTs ------------------===// +// +// 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 the GenericParamList class and related classes. +// +//===----------------------------------------------------------------------===// + +#include "swift/AST/GenericParamList.h" + +#include "swift/AST/ASTContext.h" +#include "swift/AST/TypeRepr.h" + +using namespace swift; +SourceRange RequirementRepr::getSourceRange() const { + if (getKind() == RequirementReprKind::LayoutConstraint) + return SourceRange(FirstType->getSourceRange().Start, + SecondLayout.getSourceRange().End); + return SourceRange(FirstType->getSourceRange().Start, + SecondType->getSourceRange().End); +} + +GenericParamList::GenericParamList(SourceLoc LAngleLoc, + ArrayRef Params, + SourceLoc WhereLoc, + MutableArrayRef Requirements, + SourceLoc RAngleLoc) + : Brackets(LAngleLoc, RAngleLoc), NumParams(Params.size()), + WhereLoc(WhereLoc), Requirements(Requirements), + OuterParameters(nullptr) +{ + std::uninitialized_copy(Params.begin(), Params.end(), + getTrailingObjects()); +} + +GenericParamList * +GenericParamList::create(ASTContext &Context, + SourceLoc LAngleLoc, + ArrayRef Params, + SourceLoc RAngleLoc) { + unsigned Size = totalSizeToAlloc(Params.size()); + void *Mem = Context.Allocate(Size, alignof(GenericParamList)); + return new (Mem) GenericParamList(LAngleLoc, Params, SourceLoc(), + MutableArrayRef(), + RAngleLoc); +} + +GenericParamList * +GenericParamList::create(const ASTContext &Context, + SourceLoc LAngleLoc, + ArrayRef Params, + SourceLoc WhereLoc, + ArrayRef Requirements, + SourceLoc RAngleLoc) { + unsigned Size = totalSizeToAlloc(Params.size()); + void *Mem = Context.Allocate(Size, alignof(GenericParamList)); + return new (Mem) GenericParamList(LAngleLoc, Params, + WhereLoc, + Context.AllocateCopy(Requirements), + RAngleLoc); +} + +GenericParamList * +GenericParamList::clone(DeclContext *dc) const { + auto &ctx = dc->getASTContext(); + SmallVector params; + for (auto param : getParams()) { + auto *newParam = new (ctx) GenericTypeParamDecl( + dc, param->getName(), SourceLoc(), + GenericTypeParamDecl::InvalidDepth, + param->getIndex()); + newParam->setImplicit(true); + params.push_back(newParam); + } + + return GenericParamList::create(ctx, SourceLoc(), params, SourceLoc()); +} + +void GenericParamList::setDepth(unsigned depth) { + for (auto param : *this) + param->setDepth(depth); +} + +void GenericParamList::setDeclContext(DeclContext *dc) { + for (auto param : *this) + param->setDeclContext(dc); +} + +GenericTypeParamDecl *GenericParamList::lookUpGenericParam( + Identifier name) const { + for (const auto *innerParams = this; + innerParams != nullptr; + innerParams = innerParams->getOuterParameters()) { + for (auto *paramDecl : *innerParams) { + if (name == paramDecl->getName()) { + return const_cast(paramDecl); + } + } + } + + return nullptr; +} + +TrailingWhereClause::TrailingWhereClause( + SourceLoc whereLoc, + ArrayRef requirements) + : WhereLoc(whereLoc), + NumRequirements(requirements.size()) +{ + std::uninitialized_copy(requirements.begin(), requirements.end(), + getTrailingObjects()); +} + +TrailingWhereClause *TrailingWhereClause::create( + ASTContext &ctx, + SourceLoc whereLoc, + ArrayRef requirements) { + unsigned size = totalSizeToAlloc(requirements.size()); + void *mem = ctx.Allocate(size, alignof(TrailingWhereClause)); + return new (mem) TrailingWhereClause(whereLoc, requirements); +} diff --git a/lib/AST/GenericSignatureBuilder.cpp b/lib/AST/GenericSignatureBuilder.cpp index bff5a8688291d..09b02ec88d227 100644 --- a/lib/AST/GenericSignatureBuilder.cpp +++ b/lib/AST/GenericSignatureBuilder.cpp @@ -24,6 +24,7 @@ #include "swift/AST/ExistentialLayout.h" #include "swift/AST/FileUnit.h" #include "swift/AST/GenericEnvironment.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/LazyResolver.h" #include "swift/AST/Module.h" #include "swift/AST/ParameterList.h" diff --git a/lib/AST/ImportCache.cpp b/lib/AST/ImportCache.cpp index a9a893934b824..e824d99111f4e 100644 --- a/lib/AST/ImportCache.cpp +++ b/lib/AST/ImportCache.cpp @@ -26,19 +26,19 @@ using namespace swift; using namespace namelookup; ImportSet::ImportSet(bool hasHeaderImportModule, - ArrayRef topLevelImports, - ArrayRef transitiveImports) + ArrayRef topLevelImports, + ArrayRef transitiveImports) : HasHeaderImportModule(hasHeaderImportModule), NumTopLevelImports(topLevelImports.size()), NumTransitiveImports(transitiveImports.size()) { - auto buffer = getTrailingObjects(); + auto buffer = getTrailingObjects(); std::uninitialized_copy(topLevelImports.begin(), topLevelImports.end(), buffer); std::uninitialized_copy(transitiveImports.begin(), transitiveImports.end(), buffer + topLevelImports.size()); #ifndef NDEBUG - llvm::SmallDenseSet unique; + llvm::SmallDenseSet unique; for (auto import : topLevelImports) { auto result = unique.insert(import).second; assert(result && "Duplicate imports in import set"); @@ -52,7 +52,7 @@ ImportSet::ImportSet(bool hasHeaderImportModule, void ImportSet::Profile( llvm::FoldingSetNodeID &ID, - ArrayRef topLevelImports) { + ArrayRef topLevelImports) { ID.AddInteger(topLevelImports.size()); for (auto import : topLevelImports) { ID.AddInteger(import.accessPath.size()); @@ -63,9 +63,9 @@ void ImportSet::Profile( } } -static void collectExports(ModuleDecl::ImportedModule next, - SmallVectorImpl &stack) { - SmallVector exports; +static void collectExports(ImportedModule next, + SmallVectorImpl &stack) { + SmallVector exports; next.importedModule->getImportedModulesForLookup(exports); for (auto exported : exports) { if (next.accessPath.empty()) @@ -81,16 +81,16 @@ static void collectExports(ModuleDecl::ImportedModule next, ImportSet & ImportCache::getImportSet(ASTContext &ctx, - ArrayRef imports) { + ArrayRef imports) { bool hasHeaderImportModule = false; ModuleDecl *headerImportModule = nullptr; if (auto *loader = ctx.getClangModuleLoader()) headerImportModule = loader->getImportedHeaderModule(); - SmallVector topLevelImports; + SmallVector topLevelImports; - SmallVector transitiveImports; - llvm::SmallDenseSet visited; + SmallVector transitiveImports; + llvm::SmallDenseSet visited; for (auto next : imports) { if (!visited.insert(next).second) @@ -115,7 +115,7 @@ ImportCache::getImportSet(ASTContext &ctx, if (ctx.Stats) ++ctx.Stats->getFrontendCounters().ImportSetFoldMiss; - SmallVector stack; + SmallVector stack; for (auto next : topLevelImports) { collectExports(next, stack); } @@ -139,7 +139,7 @@ ImportCache::getImportSet(ASTContext &ctx, if (ImportSet *result = ImportSets.FindNodeOrInsertPos(ID, InsertPos)) return *result; - size_t bytes = ImportSet::totalSizeToAlloc(topLevelImports.size() + transitiveImports.size()); + size_t bytes = ImportSet::totalSizeToAlloc(topLevelImports.size() + transitiveImports.size()); void *mem = ctx.Allocate(bytes, alignof(ImportSet), AllocationArena::Permanent); auto *result = new (mem) ImportSet(hasHeaderImportModule, @@ -169,13 +169,11 @@ ImportSet &ImportCache::getImportSet(const DeclContext *dc) { if (ctx.Stats) ++ctx.Stats->getFrontendCounters().ImportSetCacheMiss; - SmallVector imports; + SmallVector imports; - imports.emplace_back( - ModuleDecl::ImportedModule{ImportPath::Access(), mod}); + imports.emplace_back(ImportPath::Access(), mod); if (file) { - // Should include both SPI & non-SPI. file->getImportedModules(imports, {ModuleDecl::ImportFilterKind::Default, ModuleDecl::ImportFilterKind::ImplementationOnly, @@ -254,14 +252,12 @@ ImportCache::getAllAccessPathsNotShadowedBy(const ModuleDecl *mod, if (ctx.Stats) ++ctx.Stats->getFrontendCounters().ModuleShadowCacheMiss; - SmallVector stack; - llvm::SmallDenseSet visited; + SmallVector stack; + llvm::SmallDenseSet visited; - stack.emplace_back( - ModuleDecl::ImportedModule{ImportPath::Access(), currentMod}); + stack.emplace_back(ImportPath::Access(), currentMod); if (auto *file = dyn_cast(dc)) { - // Should include both SPI & non-SPI file->getImportedModules(stack, {ModuleDecl::ImportFilterKind::Default, ModuleDecl::ImportFilterKind::ImplementationOnly, @@ -297,7 +293,7 @@ ImportCache::getAllAccessPathsNotShadowedBy(const ModuleDecl *mod, return result; }; -ArrayRef +ArrayRef swift::namelookup::getAllImports(const DeclContext *dc) { return dc->getASTContext().getImportCache().getImportSet(dc) .getAllImports(); diff --git a/lib/AST/Module.cpp b/lib/AST/Module.cpp index ee539e9ad01bd..623aefe1614b3 100644 --- a/lib/AST/Module.cpp +++ b/lib/AST/Module.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 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -95,7 +95,7 @@ void BuiltinUnit::LookupCache::lookupValue( SmallVectorImpl &Result) { // Only qualified lookup ever finds anything in the builtin module. if (LookupKind != NLKind::QualifiedLookup) return; - + ValueDecl *&Entry = Cache[Name]; ASTContext &Ctx = M.getParentModule()->getASTContext(); if (!Entry) { @@ -175,7 +175,7 @@ class swift::SourceLookupCache { /// Throw away as much memory as possible. void invalidate(); - + void lookupValue(DeclName Name, NLKind LookupKind, SmallVectorImpl &Result); @@ -203,13 +203,13 @@ class swift::SourceLookupCache { void lookupVisibleDecls(ImportPath::Access AccessPath, VisibleDeclConsumer &Consumer, NLKind LookupKind); - + void populateMemberCache(const SourceFile &SF); void populateMemberCache(const ModuleDecl &Mod); void lookupClassMembers(ImportPath::Access AccessPath, VisibleDeclConsumer &consumer); - + void lookupClassMember(ImportPath::Access accessPath, DeclName name, SmallVectorImpl &results); @@ -331,7 +331,7 @@ void SourceLookupCache::lookupValue(DeclName Name, NLKind LookupKind, SmallVectorImpl &Result) { auto I = TopLevelValues.find(Name); if (I == TopLevelValues.end()) return; - + Result.reserve(I->second.size()); for (ValueDecl *Elt : I->second) Result.push_back(Elt); @@ -398,7 +398,7 @@ void SourceLookupCache::lookupVisibleDecls(ImportPath::Access AccessPath, void SourceLookupCache::lookupClassMembers(ImportPath::Access accessPath, VisibleDeclConsumer &consumer) { assert(accessPath.size() <= 1 && "can only refer to top-level decls"); - + if (!accessPath.empty()) { for (auto &member : ClassMembers) { // Non-simple names are also stored under their simple name, so make @@ -432,11 +432,11 @@ void SourceLookupCache::lookupClassMember(ImportPath::Access accessPath, DeclName name, SmallVectorImpl &results) { assert(accessPath.size() <= 1 && "can only refer to top-level decls"); - + auto iter = ClassMembers.find(name); if (iter == ClassMembers.end()) return; - + if (!accessPath.empty()) { for (ValueDecl *vd : iter->second) { auto *nominal = vd->getDeclContext()->getSelfNominalTypeDecl(); @@ -484,16 +484,16 @@ ModuleDecl::ModuleDecl(Identifier name, ASTContext &ctx, Bits.ModuleDecl.IsSystemModule = 0; Bits.ModuleDecl.IsNonSwiftModule = 0; Bits.ModuleDecl.IsMainModule = 0; + Bits.ModuleDecl.HasIncrementalInfo = 0; } -ArrayRef ModuleDecl::getImplicitImports() const { +ImplicitImportList ModuleDecl::getImplicitImports() const { auto &evaluator = getASTContext().evaluator; auto *mutableThis = const_cast(this); return evaluateOrDefault(evaluator, ModuleImplicitImportsRequest{mutableThis}, {}); } - void ModuleDecl::addFile(FileUnit &newFile) { // If this is a LoadedFile, make sure it loaded without error. assert(!(isa(newFile) && @@ -1013,6 +1013,33 @@ LookupConformanceInModuleRequest::evaluate( if (type->is()) return ProtocolConformanceRef(protocol); + // Tuples have builtin conformances implemented within the runtime. + // These conformances so far consist of Equatable, Comparable, and Hashable. + if (auto tuple = type->getAs()) { + auto equatable = ctx.getProtocol(KnownProtocolKind::Equatable); + auto comparable = ctx.getProtocol(KnownProtocolKind::Comparable); + auto hashable = ctx.getProtocol(KnownProtocolKind::Hashable); + + if (protocol != equatable && protocol != comparable && protocol != hashable) + return ProtocolConformanceRef::forInvalid(); + + SmallVector elementConformances; + + // Ensure that every element in this tuple conforms to said protocol. + for (auto eltTy : tuple->getElementTypes()) { + auto conformance = mod->lookupConformance(eltTy, protocol); + + if (conformance.isInvalid()) + return ProtocolConformanceRef::forInvalid(); + + elementConformances.push_back(conformance); + } + + auto conformance = ctx.getBuiltinConformance(tuple, protocol, + elementConformances); + return ProtocolConformanceRef(conformance); + } + auto nominal = type->getAnyNominal(); // If we don't have a nominal type, there are no conformances. @@ -1163,18 +1190,11 @@ void SourceFile::lookupPrecedenceGroupDirect( void ModuleDecl::getImportedModules(SmallVectorImpl &modules, ModuleDecl::ImportFilter filter) const { - assert(filter.containsAny(ImportFilter({ - ModuleDecl::ImportFilterKind::Exported, - ModuleDecl::ImportFilterKind::Default, - ModuleDecl::ImportFilterKind::ImplementationOnly})) - && "filter should have at least one of Exported|Private|ImplementationOnly" - ); - FORWARD(getImportedModules, (modules, filter)); } void -SourceFile::getImportedModules(SmallVectorImpl &modules, +SourceFile::getImportedModules(SmallVectorImpl &modules, ModuleDecl::ImportFilter filter) const { // FIXME: Ideally we should assert that the file has had its imports resolved // before calling this function. However unfortunately that can cause issues @@ -1190,16 +1210,15 @@ SourceFile::getImportedModules(SmallVectorImpl &modu for (auto desc : *Imports) { ModuleDecl::ImportFilter requiredFilter; - if (desc.importOptions.contains(ImportFlags::Exported)) + if (desc.options.contains(ImportFlags::Exported)) requiredFilter |= ModuleDecl::ImportFilterKind::Exported; - else if (desc.importOptions.contains(ImportFlags::ImplementationOnly)) + else if (desc.options.contains(ImportFlags::ImplementationOnly)) requiredFilter |= ModuleDecl::ImportFilterKind::ImplementationOnly; + else if (desc.options.contains(ImportFlags::SPIAccessControl)) + requiredFilter |= ModuleDecl::ImportFilterKind::SPIAccessControl; else requiredFilter |= ModuleDecl::ImportFilterKind::Default; - if (desc.importOptions.contains(ImportFlags::SPIAccessControl)) - requiredFilter |= ModuleDecl::ImportFilterKind::SPIAccessControl; - if (!separatelyImportedOverlays.lookup(desc.module.importedModule).empty()) requiredFilter |= ModuleDecl::ImportFilterKind::ShadowedByCrossImportOverlay; @@ -1280,7 +1299,7 @@ ModuleDecl::ReverseFullNameIterator::printForward(raw_ostream &out, } void -ModuleDecl::removeDuplicateImports(SmallVectorImpl &imports) { +ImportedModule::removeDuplicates(SmallVectorImpl &imports) { std::sort(imports.begin(), imports.end(), [](const ImportedModule &lhs, const ImportedModule &rhs) -> bool { // Arbitrarily sort by name to get a deterministic order. @@ -1450,7 +1469,7 @@ void ModuleDecl::collectLinkLibraries(LinkLibraryCallback callback) const { void SourceFile::collectLinkLibraries(ModuleDecl::LinkLibraryCallback callback) const { llvm::SmallDenseSet visited; - SmallVector stack; + SmallVector stack; ModuleDecl::ImportFilter filter = { ModuleDecl::ImportFilterKind::Exported, @@ -1464,9 +1483,7 @@ SourceFile::collectLinkLibraries(ModuleDecl::LinkLibraryCallback callback) const topLevel->getImportedModules(stack, topLevelFilter); // Make sure the top-level module is first; we want pre-order-ish traversal. - auto topLevelModule = - ModuleDecl::ImportedModule{ImportPath::Access(), topLevel}; - stack.emplace_back(topLevelModule); + stack.emplace_back(ImportPath::Access(), topLevel); while (!stack.empty()) { auto next = stack.pop_back_val().importedModule; @@ -1684,8 +1701,8 @@ ModuleDecl::getDeclaringModuleAndBystander() { // Search the transitive set of imported @_exported modules to see if any have // this module as their overlay. SmallPtrSet seen; - SmallVector imported; - SmallVector furtherImported; + SmallVector imported; + SmallVector furtherImported; ModuleDecl *overlayModule = this; getImportedModules(imported, ModuleDecl::ImportFilterKind::Exported); @@ -1883,17 +1900,17 @@ void SourceFile::print(ASTPrinter &Printer, const PrintOptions &PO) { } } -void SourceFile::setImports(ArrayRef imports) { +void +SourceFile::setImports(ArrayRef> imports) { assert(!Imports && "Already computed imports"); Imports = getASTContext().AllocateCopy(imports); } bool HasImplementationOnlyImportsRequest::evaluate(Evaluator &evaluator, SourceFile *SF) const { - using ModuleDesc = SourceFile::ImportedModuleDesc; - return llvm::any_of(SF->getImports(), [](ModuleDesc desc) { - return desc.importOptions.contains( - SourceFile::ImportFlags::ImplementationOnly); + return llvm::any_of(SF->getImports(), + [](AttributedImport desc) { + return desc.options.contains(ImportFlags::ImplementationOnly); }); } @@ -1914,20 +1931,19 @@ bool SourceFile::hasTestableOrPrivateImport( // internal/public access only needs an import marked as @_private. The // filename does not need to match (and we don't serialize it for such // decls). - return std::any_of( - Imports->begin(), Imports->end(), - [module, queryKind](ImportedModuleDesc desc) -> bool { + return llvm::any_of(*Imports, + [module, queryKind](AttributedImport desc) -> bool { if (queryKind == ImportQueryKind::TestableAndPrivate) return desc.module.importedModule == module && - (desc.importOptions.contains(ImportFlags::PrivateImport) || - desc.importOptions.contains(ImportFlags::Testable)); + (desc.options.contains(ImportFlags::PrivateImport) || + desc.options.contains(ImportFlags::Testable)); else if (queryKind == ImportQueryKind::TestableOnly) return desc.module.importedModule == module && - desc.importOptions.contains(ImportFlags::Testable); + desc.options.contains(ImportFlags::Testable); else { assert(queryKind == ImportQueryKind::PrivateOnly); return desc.module.importedModule == module && - desc.importOptions.contains(ImportFlags::PrivateImport); + desc.options.contains(ImportFlags::PrivateImport); } }); case AccessLevel::Open: @@ -1957,13 +1973,12 @@ bool SourceFile::hasTestableOrPrivateImport( if (filename.empty()) return false; - return std::any_of(Imports->begin(), Imports->end(), - [module, filename](ImportedModuleDesc desc) -> bool { - return desc.module.importedModule == module && - desc.importOptions.contains( - ImportFlags::PrivateImport) && - desc.filename == filename; - }); + return llvm::any_of(*Imports, + [module, filename](AttributedImport desc) { + return desc.module.importedModule == module && + desc.options.contains(ImportFlags::PrivateImport) && + desc.sourceFileArg == filename; + }); } bool SourceFile::isImportedImplementationOnly(const ModuleDecl *module) const { @@ -1977,7 +1992,7 @@ bool SourceFile::isImportedImplementationOnly(const ModuleDecl *module) const { // Look at the imports of this source file. for (auto &desc : *Imports) { // Ignore implementation-only imports. - if (desc.importOptions.contains(ImportFlags::ImplementationOnly)) + if (desc.options.contains(ImportFlags::ImplementationOnly)) continue; // If the module is imported this way, it's not imported @@ -2000,7 +2015,7 @@ bool ModuleDecl::isImportedImplementationOnly(const ModuleDecl *module) const { ModuleDecl::ImportFilterKind::Default, ModuleDecl::ImportFilterKind::SPIAccessControl, ModuleDecl::ImportFilterKind::ShadowedByCrossImportOverlay}; - SmallVector results; + SmallVector results; getImportedModules(results, filter); for (auto &desc : results) { @@ -2015,7 +2030,7 @@ void SourceFile::lookupImportedSPIGroups( const ModuleDecl *importedModule, llvm::SmallSetVector &spiGroups) const { for (auto &import : *Imports) { - if (import.importOptions.contains(ImportFlags::SPIAccessControl) && + if (import.options.contains(ImportFlags::SPIAccessControl) && importedModule == import.module.importedModule) { auto importedSpis = import.spiGroups; spiGroups.insert(importedSpis.begin(), importedSpis.end()); @@ -2038,6 +2053,31 @@ bool SourceFile::isImportedAsSPI(const ValueDecl *targetDecl) const { return false; } +bool ModuleDecl::isImportedAsSPI(const SpecializeAttr *attr, + const ValueDecl *targetDecl) const { + auto targetModule = targetDecl->getModuleContext(); + llvm::SmallSetVector importedSPIGroups; + lookupImportedSPIGroups(targetModule, importedSPIGroups); + if (importedSPIGroups.empty()) return false; + + auto declSPIGroups = attr->getSPIGroups(); + + for (auto declSPI : declSPIGroups) + if (importedSPIGroups.count(declSPI)) + return true; + + return false; +} + +bool ModuleDecl::isImportedAsSPI(Identifier spiGroup, + const ModuleDecl *fromModule) const { + llvm::SmallSetVector importedSPIGroups; + lookupImportedSPIGroups(fromModule, importedSPIGroups); + if (importedSPIGroups.empty()) + return false; + return importedSPIGroups.count(spiGroup); +} + bool Decl::isSPI() const { return !getSPIGroups().empty(); } @@ -2353,7 +2393,7 @@ bool FileUnit::walk(ASTWalker &walker) { #ifndef NDEBUG PrettyStackTraceDecl debugStack("walking into decl", D); #endif - + if (D->walk(walker)) return true; @@ -2492,7 +2532,7 @@ SourceFile::lookupOpaqueResultType(StringRef MangledName) { auto found = ValidatedOpaqueReturnTypes.find(MangledName); if (found != ValidatedOpaqueReturnTypes.end()) return found->second; - + // If there are unvalidated decls with opaque types, go through and validate // them now. (void) getOpaqueReturnTypeDecls(); @@ -2500,7 +2540,7 @@ SourceFile::lookupOpaqueResultType(StringRef MangledName) { found = ValidatedOpaqueReturnTypes.find(MangledName); if (found != ValidatedOpaqueReturnTypes.end()) return found->second; - + // Otherwise, we don't have a matching opaque decl. return nullptr; } diff --git a/lib/AST/ModuleDependencies.cpp b/lib/AST/ModuleDependencies.cpp index 45821a23424a8..d33dddf5c90fe 100644 --- a/lib/AST/ModuleDependencies.cpp +++ b/lib/AST/ModuleDependencies.cpp @@ -21,17 +21,37 @@ using namespace swift; ModuleDependenciesStorageBase::~ModuleDependenciesStorageBase() { } bool ModuleDependencies::isSwiftModule() const { - return isa(storage.get()); + return isSwiftTextualModule() || + isSwiftBinaryModule() || + isSwiftPlaceholderModule(); } -bool ModuleDependencies::isPlaceholderSwiftModule() const { - return isa(storage.get()); +bool ModuleDependencies::isSwiftTextualModule() const { + return isa(storage.get()); +} + +bool ModuleDependencies::isSwiftBinaryModule() const { + return isa(storage.get()); +} + +bool ModuleDependencies::isSwiftPlaceholderModule() const { + return isa(storage.get()); +} + +bool ModuleDependencies::isClangModule() const { + return isa(storage.get()); } /// Retrieve the dependencies for a Swift module. -const SwiftModuleDependenciesStorage * -ModuleDependencies::getAsSwiftModule() const { - return dyn_cast(storage.get()); +const SwiftTextualModuleDependenciesStorage * +ModuleDependencies::getAsSwiftTextualModule() const { + return dyn_cast(storage.get()); +} + +/// Retrieve the dependencies for a binary Swift dependency module. +const SwiftBinaryModuleDependencyStorage * +ModuleDependencies::getAsSwiftBinaryModule() const { + return dyn_cast(storage.get()); } /// Retrieve the dependencies for a Clang module. @@ -41,9 +61,9 @@ ModuleDependencies::getAsClangModule() const { } /// Retrieve the dependencies for a placeholder dependency module stub. -const PlaceholderSwiftModuleDependencyStorage * +const SwiftPlaceholderModuleDependencyStorage * ModuleDependencies::getAsPlaceholderDependencyModule() const { - return dyn_cast(storage.get()); + return dyn_cast(storage.get()); } void ModuleDependencies::addModuleDependency( @@ -62,8 +82,7 @@ void ModuleDependencies::addModuleDependencies( if (!importDecl) continue; - addModuleDependency(importDecl->getModulePath().front().Item.str(), - &alreadyAddedModules); + addModuleDependency(importDecl->getModulePath(), &alreadyAddedModules); } auto fileName = sf.getFilename(); @@ -72,7 +91,7 @@ void ModuleDependencies::addModuleDependencies( // If the storage is for an interface file, the only source file we // should see is that interface file. - auto swiftStorage = cast(storage.get()); + auto swiftStorage = cast(storage.get()); if (swiftStorage->swiftInterfaceFile) { assert(fileName == *swiftStorage->swiftInterfaceFile); return; @@ -83,26 +102,26 @@ void ModuleDependencies::addModuleDependencies( } Optional ModuleDependencies::getBridgingHeader() const { - auto swiftStorage = cast(storage.get()); + auto swiftStorage = cast(storage.get()); return swiftStorage->bridgingHeaderFile; } void ModuleDependencies::addBridgingHeader(StringRef bridgingHeader) { - auto swiftStorage = cast(storage.get()); + auto swiftStorage = cast(storage.get()); assert(!swiftStorage->bridgingHeaderFile); swiftStorage->bridgingHeaderFile = bridgingHeader.str(); } /// Add source files that the bridging header depends on. void ModuleDependencies::addBridgingSourceFile(StringRef bridgingSourceFile) { - auto swiftStorage = cast(storage.get()); + auto swiftStorage = cast(storage.get()); swiftStorage->bridgingSourceFiles.push_back(bridgingSourceFile.str()); } /// Add (Clang) module on which the bridging header depends. void ModuleDependencies::addBridgingModuleDependency( StringRef module, llvm::StringSet<> &alreadyAddedModules) { - auto swiftStorage = cast(storage.get()); + auto swiftStorage = cast(storage.get()); if (alreadyAddedModules.insert(module).second) swiftStorage->bridgingModuleDependencies.push_back(module.str()); } @@ -110,10 +129,12 @@ void ModuleDependencies::addBridgingModuleDependency( llvm::StringMap & ModuleDependenciesCache::getDependenciesMap(ModuleDependenciesKind kind) { switch (kind) { - case ModuleDependenciesKind::Swift: - return SwiftModuleDependencies; + case ModuleDependenciesKind::SwiftTextual: + return SwiftTextualModuleDependencies; + case ModuleDependenciesKind::SwiftBinary: + return SwiftBinaryModuleDependencies; case ModuleDependenciesKind::SwiftPlaceholder: - return PlaceholderSwiftModuleDependencies; + return SwiftPlaceholderModuleDependencies; case ModuleDependenciesKind::Clang: return ClangModuleDependencies; } @@ -123,10 +144,12 @@ ModuleDependenciesCache::getDependenciesMap(ModuleDependenciesKind kind) { const llvm::StringMap & ModuleDependenciesCache::getDependenciesMap(ModuleDependenciesKind kind) const { switch (kind) { - case ModuleDependenciesKind::Swift: - return SwiftModuleDependencies; + case ModuleDependenciesKind::SwiftTextual: + return SwiftTextualModuleDependencies; + case ModuleDependenciesKind::SwiftBinary: + return SwiftBinaryModuleDependencies; case ModuleDependenciesKind::SwiftPlaceholder: - return PlaceholderSwiftModuleDependencies; + return SwiftPlaceholderModuleDependencies; case ModuleDependenciesKind::Clang: return ClangModuleDependencies; } @@ -137,7 +160,8 @@ bool ModuleDependenciesCache::hasDependencies( StringRef moduleName, Optional kind) const { if (!kind) { - return hasDependencies(moduleName, ModuleDependenciesKind::Swift) || + return hasDependencies(moduleName, ModuleDependenciesKind::SwiftTextual) || + hasDependencies(moduleName, ModuleDependenciesKind::SwiftBinary) || hasDependencies(moduleName, ModuleDependenciesKind::SwiftPlaceholder) || hasDependencies(moduleName, ModuleDependenciesKind::Clang); } @@ -150,9 +174,12 @@ Optional ModuleDependenciesCache::findDependencies( StringRef moduleName, Optional kind) const { if (!kind) { - if (auto swiftDep = findDependencies( - moduleName, ModuleDependenciesKind::Swift)) - return swiftDep; + if (auto swiftTextualDep = findDependencies( + moduleName, ModuleDependenciesKind::SwiftTextual)) + return swiftTextualDep; + else if (auto swiftBinaryDep = findDependencies( + moduleName, ModuleDependenciesKind::SwiftBinary)) + return swiftBinaryDep; else if (auto swiftPlaceholderDep = findDependencies( moduleName, ModuleDependenciesKind::SwiftPlaceholder)) return swiftPlaceholderDep; @@ -170,8 +197,8 @@ Optional ModuleDependenciesCache::findDependencies( void ModuleDependenciesCache::recordDependencies( StringRef moduleName, - ModuleDependencies dependencies, - ModuleDependenciesKind kind) { + ModuleDependencies dependencies) { + auto kind = dependencies.getKind(); auto &map = getDependenciesMap(kind); assert(map.count(moduleName) == 0 && "Already added to map"); map.insert({moduleName, std::move(dependencies)}); diff --git a/lib/AST/ModuleLoader.cpp b/lib/AST/ModuleLoader.cpp index 851cbdba9e254..56d8312dbc739 100644 --- a/lib/AST/ModuleLoader.cpp +++ b/lib/AST/ModuleLoader.cpp @@ -50,11 +50,22 @@ DependencyTracker::addDependency(StringRef File, bool IsSystem) { /*IsMissing=*/false); } +void DependencyTracker::addIncrementalDependency(StringRef File) { + if (incrementalDepsUniquer.insert(File).second) { + incrementalDeps.emplace_back(File.str()); + } +} + ArrayRef DependencyTracker::getDependencies() const { return clangCollector->getDependencies(); } +ArrayRef +DependencyTracker::getIncrementalDependencies() const { + return incrementalDeps; +} + std::shared_ptr DependencyTracker::getClangCollector() { return clangCollector; @@ -168,17 +179,22 @@ ModuleDependencies::collectCrossImportOverlayNames(ASTContext &ctx, // A map from secondary module name to a vector of overlay names. llvm::StringMap> result; // Mimic getModuleDefiningPath() for Swift and Clang module. - if (auto *swiftDep = dyn_cast(storage.get())) { + if (auto *swiftDep = getAsSwiftTextualModule()) { // Prefer interface path to binary module path if we have it. modulePath = swiftDep->swiftInterfaceFile; - if (!modulePath.hasValue()) - modulePath = swiftDep->compiledModulePath; assert(modulePath.hasValue()); StringRef parentDir = llvm::sys::path::parent_path(*modulePath); if (llvm::sys::path::extension(parentDir) == ".swiftmodule") { modulePath = parentDir.str(); } - } else if (auto *clangDep = dyn_cast(storage.get())){ + } else if (auto *swiftBinaryDep = getAsSwiftBinaryModule()) { + modulePath = swiftBinaryDep->compiledModulePath; + assert(modulePath.hasValue()); + StringRef parentDir = llvm::sys::path::parent_path(*modulePath); + if (llvm::sys::path::extension(parentDir) == ".swiftmodule") { + modulePath = parentDir.str(); + } + } else if (auto *clangDep = getAsClangModule()) { modulePath = clangDep->moduleMapFile; assert(modulePath.hasValue()); } else { // PlaceholderSwiftModuleDependencies diff --git a/lib/AST/ModuleNameLookup.cpp b/lib/AST/ModuleNameLookup.cpp index af29ee62144e3..22674d560f0ba 100644 --- a/lib/AST/ModuleNameLookup.cpp +++ b/lib/AST/ModuleNameLookup.cpp @@ -53,7 +53,8 @@ class ModuleNameLookup { void lookupInModule(SmallVectorImpl &decls, const DeclContext *moduleOrFile, ImportPath::Access accessPath, - const DeclContext *moduleScopeContext); + const DeclContext *moduleScopeContext, + NLOptions options); }; @@ -124,7 +125,8 @@ void ModuleNameLookup::lookupInModule( SmallVectorImpl &decls, const DeclContext *moduleOrFile, ImportPath::Access accessPath, - const DeclContext *moduleScopeContext) { + const DeclContext *moduleScopeContext, + NLOptions options) { assert(moduleOrFile->isModuleScopeContext()); // Does the module scope have any separately-imported overlays shadowing @@ -135,7 +137,7 @@ void ModuleNameLookup::lookupInModule( if (!overlays.empty()) { // If so, look in each of those overlays. for (auto overlay : overlays) - lookupInModule(decls, overlay, accessPath, moduleScopeContext); + lookupInModule(decls, overlay, accessPath, moduleScopeContext, options); // FIXME: This may not work gracefully if more than one of these lookups // finds something. return; @@ -143,6 +145,7 @@ void ModuleNameLookup::lookupInModule( const size_t initialCount = decls.size(); size_t currentCount = decls.size(); + bool includeUsableFromInline = options & NL_IncludeUsableFromInline; auto updateNewDecls = [&](const DeclContext *moduleScopeContext) { if (decls.size() == currentCount) @@ -153,7 +156,9 @@ void ModuleNameLookup::lookupInModule( [&](ValueDecl *VD) { if (resolutionKind == ResolutionKind::TypesOnly && !isa(VD)) return true; - if (respectAccessControl && !VD->isAccessibleFrom(moduleScopeContext)) + if (respectAccessControl && + !VD->isAccessibleFrom(moduleScopeContext, false, + includeUsableFromInline)) return true; return false; }); @@ -182,7 +187,7 @@ void ModuleNameLookup::lookupInModule( if (!canReturnEarly) { auto &imports = ctx.getImportCache().getImportSet(moduleOrFile); - auto visitImport = [&](ModuleDecl::ImportedModule import, + auto visitImport = [&](ImportedModule import, const DeclContext *moduleScopeContext) { if (import.accessPath.empty()) import.accessPath = accessPath; @@ -202,9 +207,7 @@ void ModuleNameLookup::lookupInModule( if (auto *loader = ctx.getClangModuleLoader()) { headerImportModule = loader->getImportedHeaderModule(); if (headerImportModule) { - ModuleDecl::ImportedModule import{ImportPath::Access(), - headerImportModule}; - visitImport(import, nullptr); + visitImport(ImportedModule(headerImportModule), nullptr); } } } @@ -249,13 +252,13 @@ QualifiedLookupResult LookupInModuleRequest::evaluate( Evaluator &evaluator, const DeclContext *moduleOrFile, DeclName name, NLKind lookupKind, ResolutionKind resolutionKind, - const DeclContext *moduleScopeContext) const { + const DeclContext *moduleScopeContext, NLOptions options) const { assert(moduleScopeContext->isModuleScopeContext()); QualifiedLookupResult decls; LookupByName lookup(moduleOrFile->getASTContext(), resolutionKind, name, lookupKind); - lookup.lookupInModule(decls, moduleOrFile, {}, moduleScopeContext); + lookup.lookupInModule(decls, moduleOrFile, {}, moduleScopeContext, options); return decls; } @@ -264,10 +267,11 @@ void namelookup::lookupInModule(const DeclContext *moduleOrFile, SmallVectorImpl &decls, NLKind lookupKind, ResolutionKind resolutionKind, - const DeclContext *moduleScopeContext) { + const DeclContext *moduleScopeContext, + NLOptions options) { auto &ctx = moduleOrFile->getASTContext(); LookupInModuleRequest req(moduleOrFile, name, lookupKind, resolutionKind, - moduleScopeContext); + moduleScopeContext, options); auto results = evaluateOrDefault(ctx.evaluator, req, {}); decls.append(results.begin(), results.end()); } @@ -282,7 +286,8 @@ void namelookup::lookupVisibleDeclsInModule( assert(moduleScopeContext->isModuleScopeContext()); auto &ctx = moduleOrFile->getASTContext(); LookupVisibleDecls lookup(ctx, resolutionKind, lookupKind); - lookup.lookupInModule(decls, moduleOrFile, accessPath, moduleScopeContext); + lookup.lookupInModule(decls, moduleOrFile, accessPath, moduleScopeContext, + NL_QualifiedDefault); } void namelookup::simple_display(llvm::raw_ostream &out, ResolutionKind kind) { diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index fb7cbf7a19568..bc323187263f9 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -20,6 +20,7 @@ #include "swift/AST/ClangModuleLoader.h" #include "swift/AST/DebuggerClient.h" #include "swift/AST/ExistentialLayout.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/GenericSignature.h" #include "swift/AST/ImportCache.h" #include "swift/AST/Initializer.h" @@ -44,7 +45,6 @@ using namespace swift::namelookup; void VisibleDeclConsumer::anchor() {} void VectorDeclConsumer::anchor() {} -void NamedDeclConsumer::anchor() {} ValueDecl *LookupResultEntry::getBaseDecl() const { if (BaseDC == nullptr) @@ -290,8 +290,7 @@ static void recordShadowedDeclsAfterTypeMatch( auto file = dc->getParentSourceFile(); if (!file) return false; for (const auto &import : file->getImports()) { - if (import.importOptions.contains( - SourceFile::ImportFlags::PrivateImport) + if (import.options.contains(ImportFlags::PrivateImport) && import.module.importedModule == module && import.module.accessPath.matches(name)) return true; @@ -1503,7 +1502,9 @@ static bool isAcceptableLookupResult(const DeclContext *dc, // Check access. if (!(options & NL_IgnoreAccessControl) && !dc->getASTContext().isAccessControlDisabled()) { - return decl->isAccessibleFrom(dc); + bool allowUsableFromInline = options & NL_IncludeUsableFromInline; + return decl->isAccessibleFrom(dc, /*forConformance*/ false, + allowUsableFromInline); } return true; @@ -1779,8 +1780,8 @@ ModuleQualifiedLookupRequest::evaluate(Evaluator &eval, const DeclContext *DC, : ResolutionKind::Overloadable); auto topLevelScope = DC->getModuleScopeContext(); if (module == topLevelScope->getParentModule()) { - lookupInModule(module, member.getFullName(), decls, - NLKind::QualifiedLookup, kind, topLevelScope); + lookupInModule(module, member.getFullName(), decls, NLKind::QualifiedLookup, + kind, topLevelScope, options); } else { // Note: This is a lookup into another module. Unless we're compiling // multiple modules at once, or if the other module re-exports this one, @@ -1796,7 +1797,8 @@ ModuleQualifiedLookupRequest::evaluate(Evaluator &eval, const DeclContext *DC, return accessPath.matches(member.getFullName()); })) { lookupInModule(module, member.getFullName(), decls, - NLKind::QualifiedLookup, kind, topLevelScope); + NLKind::QualifiedLookup, kind, topLevelScope, + options); } } @@ -2290,12 +2292,33 @@ SuperclassDeclRequest::evaluate(Evaluator &evaluator, inheritedTypes, modulesFound, anyObject); // Look for a class declaration. + ClassDecl *superclass = nullptr; for (auto inheritedNominal : inheritedNominalTypes) { - if (auto classDecl = dyn_cast(inheritedNominal)) - return classDecl; + if (auto classDecl = dyn_cast(inheritedNominal)) { + superclass = classDecl; + break; + } } - } + // If we found a superclass, ensure that we don't have a circular + // inheritance hierarchy by evaluating its superclass. This forces the + // diagnostic at this point and then suppresses the superclass failure. + if (superclass) { + auto result = Ctx.evaluator(SuperclassDeclRequest{superclass}); + bool hadCycle = false; + if (auto err = result.takeError()) { + llvm::handleAllErrors(std::move(err), + [&hadCycle](const CyclicalRequestError &E) { + hadCycle = true; + }); + + if (hadCycle) + return nullptr; + } + + return superclass; + } + } return nullptr; } diff --git a/lib/AST/NameLookupRequests.cpp b/lib/AST/NameLookupRequests.cpp index 7f370ad229fd1..924d93e02049f 100644 --- a/lib/AST/NameLookupRequests.cpp +++ b/lib/AST/NameLookupRequests.cpp @@ -14,6 +14,7 @@ #include "swift/AST/NameLookupRequests.h" #include "swift/AST/ASTContext.h" #include "swift/AST/Decl.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/ProtocolConformance.h" #include "swift/AST/Evaluator.h" #include "swift/AST/Module.h" @@ -45,6 +46,19 @@ SourceLoc InheritedDeclsReferencedRequest::getNearestLoc() const { //----------------------------------------------------------------------------// // Superclass declaration computation. //----------------------------------------------------------------------------// +void SuperclassDeclRequest::diagnoseCycle(DiagnosticEngine &diags) const { + // FIXME: Improve this diagnostic. + auto nominalDecl = std::get<0>(getStorage()); + diags.diagnose(nominalDecl, diag::circular_class_inheritance, + nominalDecl->getName()); +} + +void SuperclassDeclRequest::noteCycleStep(DiagnosticEngine &diags) const { + auto decl = std::get<0>(getStorage()); + diags.diagnose(decl, diag::kind_declname_declared_here, + decl->getDescriptiveKind(), decl->getName()); +} + Optional SuperclassDeclRequest::getCachedResult() const { auto nominalDecl = std::get<0>(getStorage()); @@ -300,16 +314,15 @@ void DirectLookupRequest::writeDependencySink( void LookupInModuleRequest::writeDependencySink( evaluator::DependencyCollector &reqTracker, QualifiedLookupResult l) const { - auto *module = std::get<0>(getStorage()); + auto *DC = std::get<0>(getStorage()); auto member = std::get<1>(getStorage()); - auto *DC = std::get<4>(getStorage()); - // Decline to record lookups outside our module. - if (!DC->getParentSourceFile() || - module->getParentModule() != DC->getParentModule()) { - return; + // Decline to record lookups if the module in question has no incremental + // dependency information available. + auto *module = DC->getParentModule(); + if (module->isMainModule() || module->hasIncrementalInfo()) { + reqTracker.addTopLevelName(member.getBaseName()); } - reqTracker.addTopLevelName(member.getBaseName()); } //----------------------------------------------------------------------------// @@ -343,16 +356,14 @@ swift::extractNearestSourceLoc(const LookupConformanceDescriptor &desc) { void ModuleQualifiedLookupRequest::writeDependencySink( evaluator::DependencyCollector &reqTracker, QualifiedLookupResult l) const { - auto *DC = std::get<0>(getStorage()); auto *module = std::get<1>(getStorage()); auto member = std::get<2>(getStorage()); - // Decline to record lookups outside our module. - if (!DC->getParentSourceFile() || - module != DC->getModuleScopeContext()->getParentModule()) { - return; + // Decline to record lookups if the module in question has no incremental + // dependency information available. + if (module->isMainModule() || module->hasIncrementalInfo()) { + reqTracker.addTopLevelName(member.getBaseName()); } - reqTracker.addTopLevelName(member.getBaseName()); } //----------------------------------------------------------------------------// @@ -370,16 +381,13 @@ void LookupConformanceInModuleRequest::writeDependencySink( if (!Adoptee) return; - auto source = reqTracker.getRecorder().getActiveDependencySourceOrNull(); - if (source.isNull()) - return; - - // Decline to record conformances defined outside of the active module. + // Decline to record lookups if the module in question has no incremental + // dependency information available. auto *conformance = lookupResult.getConcrete(); - if (source.get()->getParentModule() != - conformance->getDeclContext()->getParentModule()) - return; - reqTracker.addPotentialMember(Adoptee); + auto *module = conformance->getDeclContext()->getParentModule(); + if (module->isMainModule() || module->hasIncrementalInfo()) { + reqTracker.addPotentialMember(Adoptee); + } } //----------------------------------------------------------------------------// diff --git a/lib/AST/OperatorNameLookup.cpp b/lib/AST/OperatorNameLookup.cpp index b074fbf30f630..63e64547b4ef9 100644 --- a/lib/AST/OperatorNameLookup.cpp +++ b/lib/AST/OperatorNameLookup.cpp @@ -83,8 +83,7 @@ static TinyPtrVector lookupOperatorImpl( if (!visitedModules.insert(mod).second) continue; - bool isExported = - import.importOptions.contains(SourceFile::ImportFlags::Exported); + bool isExported = import.options.contains(ImportFlags::Exported); if (!includePrivate && !isExported) continue; diff --git a/lib/AST/ProtocolConformance.cpp b/lib/AST/ProtocolConformance.cpp index 5b2ff91752ca6..1c6156608e043 100644 --- a/lib/AST/ProtocolConformance.cpp +++ b/lib/AST/ProtocolConformance.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 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -202,6 +202,11 @@ switch (getKind()) { \ return cast(this)->Method Args; \ case ProtocolConformanceKind::Inherited: \ return cast(this)->Method Args; \ + case ProtocolConformanceKind::Builtin: \ + static_assert(&ProtocolConformance::Method != \ + &BuiltinProtocolConformance::Method, \ + "Must override BuiltinProtocolConformance::" #Method); \ + return cast(this)->Method Args; \ } \ llvm_unreachable("bad ProtocolConformanceKind"); @@ -213,6 +218,7 @@ switch (getKind()) { \ return cast(this)->Method Args; \ case ProtocolConformanceKind::Specialized: \ case ProtocolConformanceKind::Inherited: \ + case ProtocolConformanceKind::Builtin: \ llvm_unreachable("not a root conformance"); \ } \ llvm_unreachable("bad ProtocolConformanceKind"); @@ -275,6 +281,8 @@ ValueDecl *ProtocolConformance::getWitnessDecl(ValueDecl *requirement) const { case ProtocolConformanceKind::Specialized: return cast(this) ->getGenericConformance()->getWitnessDecl(requirement); + case ProtocolConformanceKind::Builtin: + return requirement; } llvm_unreachable("unhandled kind"); } @@ -296,6 +304,7 @@ GenericEnvironment *ProtocolConformance::getGenericEnvironment() const { return getDeclContext()->getGenericEnvironmentOfContext(); case ProtocolConformanceKind::Specialized: + case ProtocolConformanceKind::Builtin: // If we have a specialized protocol conformance, since we do not support // currently partial specialization, we know that it cannot have any open // type variables. @@ -317,6 +326,7 @@ GenericSignature ProtocolConformance::getGenericSignature() const { return getDeclContext()->getGenericSignatureOfContext(); case ProtocolConformanceKind::Specialized: + case ProtocolConformanceKind::Builtin: // If we have a specialized protocol conformance, since we do not support // currently partial specialization, we know that it cannot have any open // type variables. @@ -334,6 +344,7 @@ SubstitutionMap ProtocolConformance::getSubstitutions(ModuleDecl *M) const { switch (parent->getKind()) { case ProtocolConformanceKind::Normal: case ProtocolConformanceKind::Self: + case ProtocolConformanceKind::Builtin: llvm_unreachable("should have exited the loop?!"); case ProtocolConformanceKind::Inherited: parent = @@ -1110,6 +1121,7 @@ ProtocolConformance::getRootConformance() const { switch (C->getKind()) { case ProtocolConformanceKind::Normal: case ProtocolConformanceKind::Self: + case ProtocolConformanceKind::Builtin: return cast(C); case ProtocolConformanceKind::Inherited: C = cast(C) @@ -1158,8 +1170,24 @@ ProtocolConformance::subst(TypeSubstitutionFn subs, const_cast(this), subMap); } - case ProtocolConformanceKind::Self: + case ProtocolConformanceKind::Self: { return const_cast(this); + } + case ProtocolConformanceKind::Builtin: { + auto origType = getType(); + if (!origType->hasTypeParameter() && + !origType->hasArchetype()) + return const_cast(this); + + auto substType = origType.subst(subs, conformances, options); + if (substType->isEqual(origType)) + return const_cast(this); + + // All builtin conformances are concrete at the moment, so it's safe to + // directly call getConcrete. + return getProtocol()->getModuleContext() + ->lookupConformance(substType, getProtocol()).getConcrete(); + } case ProtocolConformanceKind::Inherited: { // Substitute the base. auto inheritedConformance @@ -1256,6 +1284,12 @@ void NominalTypeDecl::prepareConformanceTable() const { addSynthesized(KnownProtocolKind::RawRepresentable); } } + + // Actor classes conform to the actor protocol. + if (auto classDecl = dyn_cast(mutableThis)) { + if (classDecl->isActor()) + addSynthesized(KnownProtocolKind::Actor); + } } bool NominalTypeDecl::lookupConformance( @@ -1412,7 +1446,8 @@ bool ProtocolConformance::isCanonical() const { switch (getKind()) { case ProtocolConformanceKind::Self: - case ProtocolConformanceKind::Normal: { + case ProtocolConformanceKind::Normal: + case ProtocolConformanceKind::Builtin: { return true; } case ProtocolConformanceKind::Inherited: { @@ -1441,7 +1476,8 @@ ProtocolConformance *ProtocolConformance::getCanonicalConformance() { switch (getKind()) { case ProtocolConformanceKind::Self: - case ProtocolConformanceKind::Normal: { + case ProtocolConformanceKind::Normal: + case ProtocolConformanceKind::Builtin: { // Root conformances are always canonical by construction. return this; } @@ -1483,6 +1519,37 @@ ProtocolConformanceRef::getCanonicalConformanceRef() const { return ProtocolConformanceRef(getConcrete()->getCanonicalConformance()); } +BuiltinProtocolConformance::BuiltinProtocolConformance( + Type conformingType, ProtocolDecl *protocol, + ArrayRef conformances) : + RootProtocolConformance(ProtocolConformanceKind::Builtin, conformingType), + protocol(protocol), numConformances(conformances.size()) { + std::uninitialized_copy(conformances.begin(), conformances.end(), + getTrailingObjects()); +} + +ArrayRef +BuiltinProtocolConformance::getConditionalRequirements() const { + if (conditionalConformances == None) { + // Right now only tuples are builtin and have conditional conformances. + if (auto tuple = getType()->getAs()) { + SmallVector requirements; + + for (size_t i = 0; i != getConformances().size(); i += 1) { + auto req = Requirement(RequirementKind::Conformance, + tuple->getElement(i).getType(), + getProtocol()->getDeclaredType()); + requirements.push_back(req); + } + + conditionalConformances = getProtocol()->getASTContext() + .AllocateCopy(requirements); + } + } + + return *conditionalConformances; +} + // See swift/Basic/Statistic.h for declaration: this enables tracing // ProtocolConformances, is defined here to avoid too much layering violation / // circular linkage dependency. diff --git a/lib/AST/Stmt.cpp b/lib/AST/Stmt.cpp index 98541beee21a4..65fff23a3415a 100644 --- a/lib/AST/Stmt.cpp +++ b/lib/AST/Stmt.cpp @@ -380,7 +380,7 @@ IfStmt::IfStmt(SourceLoc IfLoc, Expr *Cond, Stmt *Then, SourceLoc ElseLoc, implicit) { } -GuardStmt::GuardStmt(SourceLoc GuardLoc, Expr *Cond, Stmt *Body, +GuardStmt::GuardStmt(SourceLoc GuardLoc, Expr *Cond, BraceStmt *Body, Optional implicit, ASTContext &Ctx) : GuardStmt(GuardLoc, exprToCond(Cond, Ctx), Body, implicit) { @@ -407,7 +407,7 @@ SourceLoc CaseLabelItem::getEndLoc() const { CaseStmt::CaseStmt(CaseParentKind parentKind, SourceLoc itemIntroducerLoc, ArrayRef caseLabelItems, SourceLoc unknownAttrLoc, SourceLoc itemTerminatorLoc, - Stmt *body, + BraceStmt *body, Optional> caseBodyVariables, Optional implicit, NullablePtr fallthroughStmt) @@ -445,7 +445,7 @@ CaseStmt *CaseStmt::create(ASTContext &ctx, CaseParentKind ParentKind, SourceLoc caseLoc, ArrayRef caseLabelItems, SourceLoc unknownAttrLoc, SourceLoc colonLoc, - Stmt *body, + BraceStmt *body, Optional> caseVarDecls, Optional implicit, NullablePtr fallthroughStmt) { diff --git a/lib/AST/SubstitutionMap.cpp b/lib/AST/SubstitutionMap.cpp index 7653cc6a29961..b512029598d8a 100644 --- a/lib/AST/SubstitutionMap.cpp +++ b/lib/AST/SubstitutionMap.cpp @@ -420,7 +420,19 @@ SubstitutionMap::lookupConformance(CanType type, ProtocolDecl *proto) const { // For the second step, we're looking into the requirement signature for // this protocol. auto concrete = conformance.getConcrete(); - auto normal = concrete->getRootNormalConformance(); + auto root = concrete->getRootConformance(); + + // If we see a builtin conformance here as the root, simply lookup the + // conformance for the substitute type. + if (isa(root)) { + auto substType = type.subst(*this); + if (substType->hasError()) + return ProtocolConformanceRef(proto); + + return proto->getParentModule()->lookupConformance(substType, proto); + } + + auto normal = cast(root); // If we haven't set the signature conformances yet, force the issue now. if (normal->getSignatureConformances().empty()) { diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 129efae29ed8c..8773673dd0f88 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -3424,6 +3424,16 @@ ClangTypeInfo AnyFunctionType::getCanonicalClangTypeInfo() const { return getClangTypeInfo().getCanonical(); } +bool AnyFunctionType::hasNonDerivableClangType() { + auto clangTypeInfo = getClangTypeInfo(); + if (clangTypeInfo.empty()) + return false; + auto computedClangType = getASTContext().getClangFunctionType( + getParams(), getResult(), getRepresentation()); + assert(computedClangType && "Failed to compute Clang type."); + return clangTypeInfo != ClangTypeInfo(computedClangType); +} + bool AnyFunctionType::hasSameExtInfoAs(const AnyFunctionType *otherFn) { return getExtInfo().isEqualTo(otherFn->getExtInfo(), useClangTypes(this)); } @@ -3437,6 +3447,20 @@ ClangTypeInfo SILFunctionType::getClangTypeInfo() const { return *info; } +bool SILFunctionType::hasNonDerivableClangType() { + auto clangTypeInfo = getClangTypeInfo(); + if (clangTypeInfo.empty()) + return false; + auto results = getResults(); + auto computedClangType = + getASTContext().getCanonicalClangFunctionType( + getParameters(), + results.empty() ? None : Optional(results[0]), + getRepresentation()); + assert(computedClangType && "Failed to compute Clang type."); + return clangTypeInfo != ClangTypeInfo(computedClangType); +} + bool SILFunctionType::hasSameExtInfoAs(const SILFunctionType *otherFn) { return getExtInfo().isEqualTo(otherFn->getExtInfo(), useClangTypes(this)); } @@ -3759,7 +3783,7 @@ static Type substType(Type derivedType, // we want to structurally substitute the substitutions. if (auto boxTy = dyn_cast(type)) { auto subMap = boxTy->getSubstitutions(); - auto newSubMap = subMap.subst(substitutions, lookupConformances); + auto newSubMap = subMap.subst(substitutions, lookupConformances, options); return SILBoxType::get(boxTy->getASTContext(), boxTy->getLayout(), @@ -5048,27 +5072,6 @@ Type TypeBase::openAnyExistentialType(OpenedArchetypeType *&opened) { return opened; } -bool TypeBase::hasOpaqueArchetypePropertiesOrCases() { - if (auto *structDecl = getStructOrBoundGenericStruct()) { - for (auto *field : structDecl->getStoredProperties()) { - auto fieldTy = field->getInterfaceType()->getCanonicalType(); - if (fieldTy->hasOpaqueArchetype() || - fieldTy->hasOpaqueArchetypePropertiesOrCases()) - return true; - } - } - - if (auto *enumDecl = getEnumOrBoundGenericEnum()) { - for (auto *elt : enumDecl->getAllElements()) { - auto eltType = elt->getInterfaceType(); - if (eltType->hasOpaqueArchetype() || - eltType->getCanonicalType()->hasOpaqueArchetypePropertiesOrCases()) - return true; - } - } - return false; -} - CanType swift::substOpaqueTypesWithUnderlyingTypes(CanType ty, TypeExpansionContext context, bool allowLoweredTypes) { diff --git a/lib/AST/TypeCheckRequests.cpp b/lib/AST/TypeCheckRequests.cpp index b2d43c411e9ea..ac2dc5e2857f9 100644 --- a/lib/AST/TypeCheckRequests.cpp +++ b/lib/AST/TypeCheckRequests.cpp @@ -179,21 +179,10 @@ void EnumRawTypeRequest::diagnoseCycle(DiagnosticEngine &diags) const { diags.diagnose(enumDecl, diag::circular_enum_inheritance, enumDecl->getName()); } -bool EnumRawTypeRequest::isCached() const { - return std::get<1>(getStorage()) == TypeResolutionStage::Interface; -} - -Optional EnumRawTypeRequest::getCachedResult() const { - auto enumDecl = std::get<0>(getStorage()); - if (enumDecl->LazySemanticInfo.hasRawType()) - return enumDecl->LazySemanticInfo.RawTypeAndFlags.getPointer(); - - return None; -} - -void EnumRawTypeRequest::cacheResult(Type value) const { - auto enumDecl = std::get<0>(getStorage()); - enumDecl->LazySemanticInfo.cacheRawType(value); +void EnumRawTypeRequest::noteCycleStep(DiagnosticEngine &diags) const { + auto *decl = std::get<0>(getStorage()); + diags.diagnose(decl, diag::kind_declname_declared_here, + decl->getDescriptiveKind(), decl->getName()); } //----------------------------------------------------------------------------// @@ -609,10 +598,10 @@ void swift::simple_display(llvm::raw_ostream &out, } //----------------------------------------------------------------------------// -// FunctionBuilder-related requests. +// ResultBuilder-related requests. //----------------------------------------------------------------------------// -bool AttachedFunctionBuilderRequest::isCached() const { +bool AttachedResultBuilderRequest::isCached() const { // Only needs to be cached if there are any custom attributes. auto var = std::get<0>(getStorage()); return var->getAttrs().hasAttribute(); @@ -859,17 +848,15 @@ bool EnumRawValuesRequest::isCached() const { Optional EnumRawValuesRequest::getCachedResult() const { auto *ED = std::get<0>(getStorage()); - if (ED->LazySemanticInfo.hasCheckedRawValues()) + if (ED->SemanticFlags.contains(EnumDecl::HasFixedRawValuesAndTypes)) return std::make_tuple<>(); return None; } void EnumRawValuesRequest::cacheResult(evaluator::SideEffect) const { auto *ED = std::get<0>(getStorage()); - auto flags = ED->LazySemanticInfo.RawTypeAndFlags.getInt() | - EnumDecl::HasFixedRawValues | - EnumDecl::HasFixedRawValuesAndTypes; - ED->LazySemanticInfo.RawTypeAndFlags.setInt(flags); + ED->SemanticFlags |= OptionSet{ + EnumDecl::HasFixedRawValues | EnumDecl::HasFixedRawValuesAndTypes}; } void EnumRawValuesRequest::diagnoseCycle(DiagnosticEngine &diags) const { @@ -1123,41 +1110,24 @@ void ValueWitnessRequest::cacheResult(Witness type) const { } //----------------------------------------------------------------------------// -// PreCheckFunctionBuilderRequest computation. +// PreCheckResultBuilderRequest computation. //----------------------------------------------------------------------------// void swift::simple_display(llvm::raw_ostream &out, - FunctionBuilderBodyPreCheck value) { + ResultBuilderBodyPreCheck value) { switch (value) { - case FunctionBuilderBodyPreCheck::Okay: + case ResultBuilderBodyPreCheck::Okay: out << "okay"; break; - case FunctionBuilderBodyPreCheck::HasReturnStmt: + case ResultBuilderBodyPreCheck::HasReturnStmt: out << "has return statement"; break; - case FunctionBuilderBodyPreCheck::Error: + case ResultBuilderBodyPreCheck::Error: out << "error"; break; } } -//----------------------------------------------------------------------------// -// HasCircularInheritanceRequest computation. -//----------------------------------------------------------------------------// - -void HasCircularInheritanceRequest::diagnoseCycle( - DiagnosticEngine &diags) const { - auto *decl = std::get<0>(getStorage()); - diags.diagnose(decl, diag::circular_class_inheritance, decl->getName()); -} - -void HasCircularInheritanceRequest::noteCycleStep( - DiagnosticEngine &diags) const { - auto *decl = std::get<0>(getStorage()); - diags.diagnose(decl, diag::kind_declname_declared_here, - decl->getDescriptiveKind(), decl->getName()); -} - //----------------------------------------------------------------------------// // HasCircularInheritedProtocolsRequest computation. //----------------------------------------------------------------------------// @@ -1414,9 +1384,62 @@ TypeCheckFunctionBodyRequest::readDependencySource( //----------------------------------------------------------------------------// void swift::simple_display(llvm::raw_ostream &out, - const ImplicitImport &import) { - out << "implicit import of "; - simple_display(out, import.Module); + const ImportedModule &module) { + out << "import of "; + if (!module.accessPath.empty()) { + module.accessPath.print(out); + out << " in "; + } + simple_display(out, module.importedModule); +} + +void swift::simple_display(llvm::raw_ostream &out, + const UnloadedImportedModule &module) { + out << "import of "; + if (!module.getAccessPath().empty()) { + module.getAccessPath().print(out); + out << " in "; + } + out << "unloaded "; + module.getModulePath().print(out); +} + +void swift::simple_display(llvm::raw_ostream &out, + const AttributedImport> &import) { + out << " ["; + + if (import.options.contains(ImportFlags::Exported)) + out << " exported"; + if (import.options.contains(ImportFlags::Testable)) + out << " testable"; + if (import.options.contains(ImportFlags::ImplementationOnly)) + out << " implementation-only"; + if (import.options.contains(ImportFlags::PrivateImport)) + out << " private(" << import.sourceFileArg << ")"; + + if (import.options.contains(ImportFlags::SPIAccessControl)) { + out << " spi("; + llvm::interleaveComma(import.spiGroups, out, [&out](Identifier name) { + simple_display(out, name); + }); + out << ")"; + } + + out << " ]"; +} + +void swift::simple_display(llvm::raw_ostream &out, + const ImplicitImportList &importList) { + llvm::interleaveComma(importList.imports, out, + [&](const auto &import) { + simple_display(out, import); + }); + if (!importList.imports.empty() && !importList.unloadedImports.empty()) + out << ", "; + llvm::interleaveComma(importList.unloadedImports, out, + [&](const auto &import) { + simple_display(out, import); + }); } //----------------------------------------------------------------------------// @@ -1449,8 +1472,12 @@ void swift::simple_display(llvm::raw_ostream &out, CustomAttrTypeKind value) { out << "non-generic"; return; - case CustomAttrTypeKind::PropertyDelegate: - out << "property-delegate"; + case CustomAttrTypeKind::PropertyWrapper: + out << "property-wrapper"; + return; + + case CustomAttrTypeKind::GlobalActor: + out << "global-actor"; return; } llvm_unreachable("bad kind"); @@ -1469,6 +1496,33 @@ void CustomAttrTypeRequest::cacheResult(Type value) const { attr->setType(value); } +bool ActorIsolation::requiresSubstitution() const { + switch (kind) { + case ActorInstance: + case Independent: + case IndependentUnsafe: + case Unspecified: + return false; + + case GlobalActor: + return getGlobalActor()->hasTypeParameter(); + } + llvm_unreachable("unhandled actor isolation kind!"); +} + +ActorIsolation ActorIsolation::subst(SubstitutionMap subs) const { + switch (kind) { + case ActorInstance: + case Independent: + case IndependentUnsafe: + case Unspecified: + return *this; + + case GlobalActor: + return forGlobalActor(getGlobalActor().subst(subs)); + } + llvm_unreachable("unhandled actor isolation kind!"); +} void swift::simple_display( llvm::raw_ostream &out, const ActorIsolation &state) { @@ -1477,16 +1531,46 @@ void swift::simple_display( out << "actor-isolated to instance of " << state.getActor()->getName(); break; - case ActorIsolation::ActorPrivileged: - out << "actor-privileged to instance of " << state.getActor()->getName(); - break; - case ActorIsolation::Independent: out << "actor-independent"; break; + case ActorIsolation::IndependentUnsafe: + out << "actor-independent (unsafe)"; + break; + case ActorIsolation::Unspecified: out << "unspecified actor isolation"; break; + + case ActorIsolation::GlobalActor: + out << "actor-isolated to global actor " + << state.getGlobalActor().getString(); + break; + } +} + +bool swift::areTypesEqual(Type type1, Type type2) { + if (!type1 || !type2) + return !type1 && !type2; + + return type1->isEqual(type2); +} + +void swift::simple_display( + llvm::raw_ostream &out, BodyInitKind initKind) { + switch (initKind) { + case BodyInitKind::None: out << "none"; return; + case BodyInitKind::Delegating: out << "delegating"; return; + case BodyInitKind::Chained: out << "chained"; return; + case BodyInitKind::ImplicitChained: out << "implicit_chained"; return; } + llvm_unreachable("Bad body init kind"); +} + +void swift::simple_display( + llvm::raw_ostream &out, BodyInitKindAndExpr initKindAndExpr) { + simple_display(out, initKindAndExpr.initKind); + out << " "; + simple_display(out, initKindAndExpr.initExpr); } diff --git a/lib/AST/TypeRepr.cpp b/lib/AST/TypeRepr.cpp index 60cbddb578351..cc9c9a3b2f2cd 100644 --- a/lib/AST/TypeRepr.cpp +++ b/lib/AST/TypeRepr.cpp @@ -19,6 +19,7 @@ #include "swift/AST/ASTContext.h" #include "swift/AST/ASTVisitor.h" #include "swift/AST/Expr.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/Module.h" #include "swift/AST/Types.h" #include "swift/Basic/Defer.h" diff --git a/lib/AST/USRGeneration.cpp b/lib/AST/USRGeneration.cpp index 30716b9f498c8..c3fe2322c21c9 100644 --- a/lib/AST/USRGeneration.cpp +++ b/lib/AST/USRGeneration.cpp @@ -12,6 +12,7 @@ #include "swift/AST/ASTContext.h" #include "swift/AST/ClangModuleLoader.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/Module.h" #include "swift/AST/USRGeneration.h" #include "swift/AST/ASTMangler.h" diff --git a/lib/AST/UnqualifiedLookup.cpp b/lib/AST/UnqualifiedLookup.cpp index b32cf8eaede13..27ac8195ebc94 100644 --- a/lib/AST/UnqualifiedLookup.cpp +++ b/lib/AST/UnqualifiedLookup.cpp @@ -22,10 +22,12 @@ #include "swift/AST/ModuleNameLookup.h" #include "swift/AST/NameLookup.h" #include "swift/AST/NameLookupRequests.h" +#include "swift/AST/PropertyWrappers.h" #include "swift/Basic/Debug.h" #include "swift/Basic/STLExtras.h" #include "swift/Basic/SourceManager.h" #include "swift/Basic/Statistic.h" +#include "swift/Parse/Lexer.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/TinyPtrVector.h" #include "llvm/Support/Debug.h" @@ -88,32 +90,6 @@ namespace { SmallVectorImpl &results) const; }; - enum class AddGenericParameters { Yes, No }; - -#ifndef NDEBUG - /// A consumer for debugging that lets the UnqualifiedLookupFactory know when - /// finding something. - class InstrumentedNamedDeclConsumer : public NamedDeclConsumer { - virtual void anchor() override; - UnqualifiedLookupFactory *factory; - - public: - InstrumentedNamedDeclConsumer(UnqualifiedLookupFactory *factory, - DeclNameRef name, - SmallVectorImpl &results, - bool isTypeLookup) - : NamedDeclConsumer(name, results, isTypeLookup), factory(factory) {} - - virtual void foundDecl(ValueDecl *VD, DeclVisibilityKind Reason, - DynamicLookupInfo dynamicLookupInfo = {}) override { - unsigned before = results.size(); - NamedDeclConsumer::foundDecl(VD, Reason, dynamicLookupInfo); - unsigned after = results.size(); - if (after > before) - factory->addedResult(results.back()); - } - }; -#endif // Inputs const DeclNameRef Name; DeclContext *const DC; @@ -128,12 +104,7 @@ namespace { const Options options; const bool isOriginallyTypeLookup; const NLOptions baseNLOptions; - // Transputs -#ifndef NDEBUG - InstrumentedNamedDeclConsumer Consumer; -#else - NamedDeclConsumer Consumer; -#endif + // Outputs SmallVectorImpl &Results; size_t &IndexOfFirstOuterResult; @@ -144,9 +115,6 @@ namespace { static const unsigned targetLookup; #endif - public: // for exp debugging - unsigned resultsSizeBeforeLocalsPass = ~0; - public: // clang-format off UnqualifiedLookupFactory(DeclNameRef Name, @@ -167,7 +135,12 @@ namespace { /// Can lookup stop searching for results, assuming hasn't looked for outer /// results yet? bool isFirstResultEnough() const; - + + /// Do we want precise scoping of VarDecls? If IncludeOuterResults is on, + /// this is true, which allows us to resolve forward references to + /// local VarDecls from inside local function and closure bodies. + bool hasPreciseScopingOfVarDecls() const; + /// Every time lookup finishes searching a scope, call me /// to record the dividing line between results from first fruitful scope and /// the result. @@ -175,7 +148,7 @@ namespace { #pragma mark context-based lookup declarations - bool isOutsideBodyOfFunction(const AbstractFunctionDecl *const AFD) const; + bool isInsideBodyOfFunction(const AbstractFunctionDecl *const AFD) const; /// For diagnostic purposes, move aside the unavailables, and put /// them back as a last-ditch effort. @@ -240,9 +213,11 @@ class ASTScopeDeclConsumerForUnqualifiedLookup void maybeUpdateSelfDC(VarDecl *var); - bool consume(ArrayRef values, DeclVisibilityKind vis, + bool consume(ArrayRef values, NullablePtr baseDC = nullptr) override; + bool consumePossiblyNotInScope(ArrayRef vars) override; + /// returns true if finished bool lookInMembers(DeclContext *const scopeDC, NominalTypeDecl *const nominal) override; @@ -282,11 +257,6 @@ UnqualifiedLookupFactory::UnqualifiedLookupFactory( options(options), isOriginallyTypeLookup(options.contains(Flags::TypeLookup)), baseNLOptions(computeBaseNLOptions(options, isOriginallyTypeLookup)), - #ifdef NDEBUG - Consumer(Name, Results, isOriginallyTypeLookup), - #else - Consumer(this, Name, Results, isOriginallyTypeLookup), - #endif Results(Results), IndexOfFirstOuterResult(IndexOfFirstOuterResult) {} @@ -303,7 +273,9 @@ void UnqualifiedLookupFactory::performUnqualifiedLookup() { DC->getParentSourceFile()); if (Loc.isValid()) { - lookInASTScopes(); + // Operator lookup is always global, for the time being. + if (!Name.isOperator()) + lookInASTScopes(); } else { assert(DC->isModuleScopeContext() && "Unqualified lookup without a source location must start from " @@ -348,11 +320,11 @@ void UnqualifiedLookupFactory::lookUpTopLevelNamesInModuleScopeContext( #pragma mark context-based lookup definitions -bool UnqualifiedLookupFactory::isOutsideBodyOfFunction( +bool UnqualifiedLookupFactory::isInsideBodyOfFunction( const AbstractFunctionDecl *const AFD) const { - return !AFD->isImplicit() && Loc.isValid() && - AFD->getBodySourceRange().isValid() && - !SM.rangeContainsTokenLoc(AFD->getBodySourceRange(), Loc); + auto range = Lexer::getCharSourceRangeFromSourceRange( + SM, AFD->getBodySourceRange()); + return range.contains(Loc); } void UnqualifiedLookupFactory::ResultFinderForTypeContext::findResults( @@ -411,8 +383,11 @@ void UnqualifiedLookupFactory::addImportedResults(const DeclContext *const dc) { SmallVector CurModuleResults; auto resolutionKind = isOriginallyTypeLookup ? ResolutionKind::TypesOnly : ResolutionKind::Overloadable; + auto nlOptions = NL_UnqualifiedDefault; + if (options.contains(Flags::IncludeUsableFromInline)) + nlOptions |= NL_IncludeUsableFromInline; lookupInModule(dc, Name.getFullName(), CurModuleResults, - NLKind::UnqualifiedLookup, resolutionKind, dc); + NLKind::UnqualifiedLookup, resolutionKind, dc, nlOptions); // Always perform name shadowing for type lookup. if (options.contains(Flags::TypeLookup)) { @@ -499,6 +474,10 @@ bool UnqualifiedLookupFactory::isFirstResultEnough() const { return !Results.empty() && !options.contains(Flags::IncludeOuterResults); } +bool UnqualifiedLookupFactory::hasPreciseScopingOfVarDecls() const { + return !options.contains(Flags::IncludeOuterResults); +} + void UnqualifiedLookupFactory::recordCompletionOfAScope() { // OK to call (NOOP) if there are more inner results and Results is empty if (IndexOfFirstOuterResult == 0) @@ -543,8 +522,7 @@ void UnqualifiedLookupFactory::lookInASTScopes() { stopForDebuggingIfStartingTargetLookup(true); #endif - ASTScope::unqualifiedLookup(DC->getParentSourceFile(), - Name, Loc, consumer); + ASTScope::unqualifiedLookup(DC->getParentSourceFile(), Loc, consumer); } void ASTScopeDeclConsumerForUnqualifiedLookup::maybeUpdateSelfDC( @@ -588,33 +566,41 @@ void ASTScopeDeclConsumerForUnqualifiedLookup::maybeUpdateSelfDC( } bool ASTScopeDeclConsumerForUnqualifiedLookup::consume( - ArrayRef values, DeclVisibilityKind vis, - NullablePtr baseDC) { + ArrayRef values, NullablePtr baseDC) { for (auto *value: values) { if (factory.isOriginallyTypeLookup && !isa(value)) continue; - // Try to resolve the base for unqualified instance member - // references. This is used by lookInMembers(). if (auto *var = dyn_cast(value)) { + // Try to resolve the base for unqualified instance member + // references. This is used by lookInMembers(). if (var->getName() == factory.Ctx.Id_self) { maybeUpdateSelfDC(var); } + + // Local VarDecls with a pattern binding are visited as part of their + // BraceStmt when hasPreciseScopingOfVarDecls() is off. + if (var->getParentPatternBinding() && + !factory.hasPreciseScopingOfVarDecls()) + continue; } - if (!value->getName().matchesRef(factory.Name.getFullName())) - continue; + auto fullName = factory.Name.getFullName(); + if (!value->getName().matchesRef(fullName)) { + bool foundMatch = false; + if (auto *varDecl = dyn_cast(value)) { + // Check if the name matches any auxiliary decls not in the AST + varDecl->visitAuxiliaryDecls([&](VarDecl *auxiliaryVar) { + if (auxiliaryVar->ValueDecl::getName().matchesRef(fullName)) { + value = auxiliaryVar; + foundMatch = true; + } + }); + } - // In order to preserve the behavior of the existing context-based lookup, - // which finds all results for non-local variables at the top level instead - // of stopping at the first one, ignore results at the top level that are - // not local variables. The caller \c lookInASTScopes will - // then do the appropriate work when the scope lookup fails. In - // FindLocalVal::visitBraceStmt, it sees PatternBindingDecls, not VarDecls, - // so a VarDecl at top level would not be found by the context-based lookup. - if (isa(value->getDeclContext()) && - (vis != DeclVisibilityKind::LocalVariable || isa(value))) - return false; + if (!foundMatch) + continue; + } factory.Results.push_back(LookupResultEntry(value)); #ifndef NDEBUG @@ -625,8 +611,22 @@ bool ASTScopeDeclConsumerForUnqualifiedLookup::consume( return factory.isFirstResultEnough(); } +bool ASTScopeDeclConsumerForUnqualifiedLookup::consumePossiblyNotInScope( + ArrayRef vars) { + if (factory.hasPreciseScopingOfVarDecls()) + return false; + + for (auto *var : vars) { + if (!factory.Name.getFullName().isSimpleName(var->getName())) + continue; + + factory.Results.push_back(LookupResultEntry(var)); + } + + return false; +} + bool ASTScopeDeclGatherer::consume(ArrayRef valuesArg, - DeclVisibilityKind, NullablePtr) { for (auto *v: valuesArg) values.push_back(v); @@ -639,7 +639,7 @@ bool ASTScopeDeclConsumerForUnqualifiedLookup::lookInMembers( NominalTypeDecl *const nominal) { if (candidateSelfDC) { if (auto *afd = dyn_cast(candidateSelfDC)) { - assert(!factory.isOutsideBodyOfFunction(afd) && "Should be inside"); + assert(factory.isInsideBodyOfFunction(afd) && "Should be inside"); } } @@ -677,9 +677,6 @@ UnqualifiedLookupRequest::evaluate(Evaluator &evaluator, } #pragma mark debugging -#ifndef NDEBUG -void UnqualifiedLookupFactory::InstrumentedNamedDeclConsumer::anchor() {} -#endif void UnqualifiedLookupFactory::ResultFinderForTypeContext::dump() const { (void)factory; @@ -707,16 +704,10 @@ void UnqualifiedLookupFactory::printScopes(raw_ostream &out) const { void UnqualifiedLookupFactory::printResults(raw_ostream &out) const { for (auto i : indices(Results)) { - if (i == resultsSizeBeforeLocalsPass) - out << "============== next pass ============\n"; out << i << ": "; Results[i].print(out); out << "\n"; } - if (resultsSizeBeforeLocalsPass == Results.size()) - out << "============== next pass ============\n"; - if (resultsSizeBeforeLocalsPass == ~0u) - out << "never tried locals\n\n"; } void UnqualifiedLookupFactory::print(raw_ostream &OS) const { @@ -778,3 +769,76 @@ unsigned UnqualifiedLookupFactory::lookupCounter = 0; const unsigned UnqualifiedLookupFactory::targetLookup = ~0; #endif // NDEBUG + +namespace { + +class ASTScopeDeclConsumerForLocalLookup + : public AbstractASTScopeDeclConsumer { + DeclName name; + bool stopAfterInnermostBraceStmt; + SmallVectorImpl &results; + +public: + ASTScopeDeclConsumerForLocalLookup( + DeclName name, bool stopAfterInnermostBraceStmt, + SmallVectorImpl &results) + : name(name), stopAfterInnermostBraceStmt(stopAfterInnermostBraceStmt), + results(results) {} + + bool consume(ArrayRef values, + NullablePtr baseDC) override { + for (auto *value: values) { + if (auto *varDecl = dyn_cast(value)) { + // Check if the name matches any auxiliary decls not in the AST + varDecl->visitAuxiliaryDecls([&](VarDecl *auxiliaryVar) { + if (name.isSimpleName(auxiliaryVar->getName())) { + results.push_back(auxiliaryVar); + } + }); + } + + if (value->getName().matchesRef(name)) + results.push_back(value); + } + + return (!stopAfterInnermostBraceStmt && !results.empty()); + } + + bool lookInMembers(DeclContext *const, + NominalTypeDecl *const) override { + return true; + } + + bool finishLookupInBraceStmt(BraceStmt *stmt) override { + return stopAfterInnermostBraceStmt; + } + +#ifndef NDEBUG + void startingNextLookupStep() override {} + void finishingLookup(std::string) const override {} + bool isTargetLookup() const override { return false; } +#endif +}; + +} + +/// Lookup that only finds local declarations and does not trigger +/// interface type computation. +void ASTScope::lookupLocalDecls(SourceFile *sf, DeclName name, SourceLoc loc, + bool stopAfterInnermostBraceStmt, + SmallVectorImpl &results) { + ASTScopeDeclConsumerForLocalLookup consumer(name, stopAfterInnermostBraceStmt, + results); + ASTScope::unqualifiedLookup(sf, loc, consumer); +} + +ValueDecl *ASTScope::lookupSingleLocalDecl(SourceFile *sf, DeclName name, + SourceLoc loc) { + SmallVector result; + ASTScope::lookupLocalDecls(sf, name, loc, + /*finishLookupInBraceStmt=*/false, + result); + if (result.size() != 1) + return nullptr; + return result[0]; +} diff --git a/lib/ASTSectionImporter/CMakeLists.txt b/lib/ASTSectionImporter/CMakeLists.txt index 0bd9f991fc45e..1a8b262e2a2c0 100644 --- a/lib/ASTSectionImporter/CMakeLists.txt +++ b/lib/ASTSectionImporter/CMakeLists.txt @@ -1,3 +1,5 @@ +set_swift_llvm_is_available() + add_swift_host_library(swiftASTSectionImporter STATIC ASTSectionImporter.cpp LLVM_LINK_COMPONENTS core) diff --git a/lib/Basic/Platform.cpp b/lib/Basic/Platform.cpp index 526f0c265abeb..d939c86e0b003 100644 --- a/lib/Basic/Platform.cpp +++ b/lib/Basic/Platform.cpp @@ -379,8 +379,10 @@ swift::getSwiftRuntimeCompatibilityVersionForTarget( } else if (Minor <= 15) { if (Micro <= 3) { return llvm::VersionTuple(5, 1); - } else { + } else if (Micro <= 4) { return llvm::VersionTuple(5, 2); + } else { + return llvm::VersionTuple(5, 3); } } } else if (Major == 11) { @@ -400,8 +402,10 @@ swift::getSwiftRuntimeCompatibilityVersionForTarget( } else if (Major <= 13) { if (Minor <= 3) { return llvm::VersionTuple(5, 1); - } else { + } else if (Minor <= 4) { return llvm::VersionTuple(5, 2); + } else { + return llvm::VersionTuple(5, 3); } } } else if (Triple.isWatchOS()) { @@ -411,8 +415,10 @@ swift::getSwiftRuntimeCompatibilityVersionForTarget( } else if (Major <= 6) { if (Minor <= 1) { return llvm::VersionTuple(5, 1); - } else { + } else if (Minor <= 2) { return llvm::VersionTuple(5, 2); + } else { + return llvm::VersionTuple(5, 3); } } } diff --git a/lib/Basic/StringExtras.cpp b/lib/Basic/StringExtras.cpp index a33dd51e8caac..3884a8d72deb6 100644 --- a/lib/Basic/StringExtras.cpp +++ b/lib/Basic/StringExtras.cpp @@ -1220,6 +1220,8 @@ bool swift::omitNeedlessWords(StringRef &baseName, bool returnsSelf, bool isProperty, const InheritedNameSet *allPropertyNames, + Optional completionHandlerIndex, + Optional completionHandlerName, StringScratchSpace &scratch) { bool anyChanges = false; OmissionTypeName resultType = returnsSelf ? contextType : givenResultType; @@ -1287,11 +1289,53 @@ bool swift::omitNeedlessWords(StringRef &baseName, } } + // If the base name of a method imported as "async" starts with the word + // "get", drop the "get". + bool isAsync = completionHandlerIndex.hasValue(); + if (isAsync && camel_case::getFirstWord(baseName) == "get" && + baseName.size() > 3) { + baseName = baseName.substr(3); + anyChanges = true; + } + // If needed, split the base name. if (!argNames.empty() && splitBaseName(baseName, argNames[0], paramTypes[0], firstParamName)) anyChanges = true; + // If this is an asynchronous function where the completion handler is + // the first parameter, strip off WithCompletion(Handler) from the base name. + if (isAsync && *completionHandlerIndex == 0) { + if (auto newBaseName = stripWithCompletionHandlerSuffix(baseName)) { + baseName = *newBaseName; + anyChanges = true; + } + } + + // For a method imported as "async", drop the "Asynchronously" suffix from + // the base name. It is redundant with 'async'. + const StringRef asynchronously = "Asynchronously"; + if (isAsync && camel_case::getLastWord(baseName) == asynchronously && + baseName.size() > asynchronously.size()) { + baseName = baseName.drop_back(asynchronously.size()); + anyChanges = true; + } + + // If this is an asynchronous function where the completion handler is + // the second parameter, and the corresponding name has some additional + // information prior to WithCompletion(Handler), append that + // additional text to the base name. + if (isAsync && *completionHandlerIndex == 1 && completionHandlerName) { + if (auto extraParamText = stripWithCompletionHandlerSuffix( + *completionHandlerName)) { + SmallString<32> newBaseName; + newBaseName += baseName; + appendSentenceCase(newBaseName, *extraParamText); + baseName = scratch.copyString(newBaseName); + anyChanges = true; + } + } + // Omit needless words based on parameter types. for (unsigned i = 0, n = argNames.size(); i != n; ++i) { // If there is no corresponding parameter, there is nothing to @@ -1311,6 +1355,20 @@ bool swift::omitNeedlessWords(StringRef &baseName, name, paramTypes[i], role, role == NameRole::BaseName ? allPropertyNames : nullptr); + // If this is an asynchronous function where the completion handler is + // past the second parameter and has additional information in the name, + // add that information to the prior argument name. + if (isAsync && completionHandlerName && *completionHandlerIndex > 1 && + *completionHandlerIndex == i + 1) { + if (auto extraParamText = stripWithCompletionHandlerSuffix( + *completionHandlerName)) { + SmallString<32> extendedName; + extendedName += newName; + appendSentenceCase(extendedName, *extraParamText); + newName = scratch.copyString(extendedName); + } + } + if (name == newName) continue; // Record this change. @@ -1324,3 +1382,15 @@ bool swift::omitNeedlessWords(StringRef &baseName, return lowercaseAcronymsForReturn(); } + +Optional swift::stripWithCompletionHandlerSuffix(StringRef name) { + if (name.endswith("WithCompletionHandler")) { + return name.drop_back(strlen("WithCompletionHandler")); + } + + if (name.endswith("WithCompletion")) { + return name.drop_back(strlen("WithCompletion")); + } + + return None; +} diff --git a/lib/ClangImporter/CMakeLists.txt b/lib/ClangImporter/CMakeLists.txt index a9076c210e033..21294ee6da4ef 100644 --- a/lib/ClangImporter/CMakeLists.txt +++ b/lib/ClangImporter/CMakeLists.txt @@ -4,6 +4,8 @@ set(SWIFT_GYB_FLAGS add_gyb_target(generated_sorted_cf_database SortedCFDatabase.def.gyb) +set_swift_llvm_is_available() + add_swift_host_library(swiftClangImporter STATIC CFTypeInfo.cpp ClangAdapter.cpp diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index 859696e067b4e..4fdf0e7d0e229 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -21,8 +21,9 @@ #include "swift/AST/ClangModuleLoader.h" #include "swift/AST/DiagnosticEngine.h" #include "swift/AST/DiagnosticsClangImporter.h" -#include "swift/AST/ImportCache.h" +#include "swift/AST/DiagnosticsSema.h" #include "swift/AST/IRGenOptions.h" +#include "swift/AST/ImportCache.h" #include "swift/AST/LinkLibrary.h" #include "swift/AST/Module.h" #include "swift/AST/NameLookup.h" @@ -35,8 +36,6 @@ #include "swift/ClangImporter/ClangModule.h" #include "swift/Config.h" #include "swift/Demangling/Demangle.h" -#include "swift/ClangImporter/ClangModule.h" -#include "swift/Config.h" #include "swift/Parse/Lexer.h" #include "swift/Parse/Parser.h" #include "swift/Strings.h" @@ -1651,11 +1650,12 @@ bool ClangImporter::emitPrecompiledModule(StringRef moduleMapPath, LangOpts->CurrentModule = LangOpts->ModuleName; auto language = getLanguageFromOptions(LangOpts); - auto inputFile = clang::FrontendInputFile( - moduleMapPath, clang::InputKind( - language, clang::InputKind::ModuleMap, false)); auto &FrontendOpts = invocation.getFrontendOpts(); + auto inputFile = clang::FrontendInputFile( + moduleMapPath, clang::InputKind( + language, clang::InputKind::ModuleMap, false), + FrontendOpts.IsSystemModule); FrontendOpts.Inputs = {inputFile}; FrontendOpts.OriginalModuleMap = moduleMapPath.str(); FrontendOpts.OutputFile = outputPath.str(); @@ -3461,7 +3461,7 @@ ModuleDecl *ClangModuleUnit::getOverlayModule() const { } void ClangModuleUnit::getImportedModules( - SmallVectorImpl &imports, + SmallVectorImpl &imports, ModuleDecl::ImportFilter filter) const { // Bail out if we /only/ want ImplementationOnly imports; Clang modules never // have any of these. @@ -3532,7 +3532,7 @@ void ClangModuleUnit::getImportedModules( } void ClangModuleUnit::getImportedModulesForLookup( - SmallVectorImpl &imports) const { + SmallVectorImpl &imports) const { // Reuse our cached list of imports if we have one. if (importedModulesForLookup.hasValue()) { @@ -3557,7 +3557,7 @@ void ClangModuleUnit::getImportedModulesForLookup( } if (imported.empty()) { - importedModulesForLookup = ArrayRef(); + importedModulesForLookup = ArrayRef(); return; } @@ -3616,7 +3616,13 @@ void ClangImporter::getMangledName(raw_ostream &os, if (!Impl.Mangler) Impl.Mangler.reset(Impl.getClangASTContext().createMangleContext()); - Impl.Mangler->mangleName(clangDecl, os); + if (auto ctor = dyn_cast(clangDecl)) { + auto ctorGlobalDecl = + clang::GlobalDecl(ctor, clang::CXXCtorType::Ctor_Complete); + Impl.Mangler->mangleCXXName(ctorGlobalDecl, os); + } else { + Impl.Mangler->mangleName(clangDecl, os); + } } // --------------------------------------------------------------------------- @@ -3728,48 +3734,46 @@ void ClangImporter::Implementation::lookupValue( // If we have a declaration and nothing matched so far, try the names used // in other versions of Swift. - if (!anyMatching) { - if (auto clangDecl = entry.dyn_cast()) { - const clang::NamedDecl *recentClangDecl = - clangDecl->getMostRecentDecl(); - - CurrentVersion.forEachOtherImportNameVersion( - SwiftContext.LangOpts.EnableExperimentalConcurrency, - [&](ImportNameVersion nameVersion) { - if (anyMatching) - return; - - // Check to see if the name and context match what we expect. - ImportedName newName = importFullName(recentClangDecl, nameVersion); - if (!newName.getDeclName().matchesRef(name)) - return; - - // If we asked for an async import and didn't find one, skip this. - // This filters out duplicates. - if (nameVersion.supportsConcurrency() && - !newName.getAsyncInfo()) - return; - - const clang::DeclContext *clangDC = - newName.getEffectiveContext().getAsDeclContext(); - if (!clangDC || !clangDC->isFileContext()) - return; - - // Then try to import the decl under the alternate name. - auto alternateNamedDecl = - cast_or_null(importDeclReal(recentClangDecl, - nameVersion)); - if (!alternateNamedDecl || alternateNamedDecl == decl) - return; - assert(alternateNamedDecl->getName().matchesRef(name) && - "importFullName behaved differently from importDecl"); - if (alternateNamedDecl->getDeclContext()->isModuleScopeContext()) { - consumer.foundDecl(alternateNamedDecl, - DeclVisibilityKind::VisibleAtTopLevel); - anyMatching = true; - } - }); - } + if (auto clangDecl = entry.dyn_cast()) { + const clang::NamedDecl *recentClangDecl = + clangDecl->getMostRecentDecl(); + + CurrentVersion.forEachOtherImportNameVersion( + SwiftContext.LangOpts.EnableExperimentalConcurrency, + [&](ImportNameVersion nameVersion) { + if (anyMatching) + return; + + // Check to see if the name and context match what we expect. + ImportedName newName = importFullName(recentClangDecl, nameVersion); + if (!newName.getDeclName().matchesRef(name)) + return; + + // If we asked for an async import and didn't find one, skip this. + // This filters out duplicates. + if (nameVersion.supportsConcurrency() && + !newName.getAsyncInfo()) + return; + + const clang::DeclContext *clangDC = + newName.getEffectiveContext().getAsDeclContext(); + if (!clangDC || !clangDC->isFileContext()) + return; + + // Then try to import the decl under the alternate name. + auto alternateNamedDecl = + cast_or_null(importDeclReal(recentClangDecl, + nameVersion)); + if (!alternateNamedDecl || alternateNamedDecl == decl) + return; + assert(alternateNamedDecl->getName().matchesRef(name) && + "importFullName behaved differently from importDecl"); + if (alternateNamedDecl->getDeclContext()->isModuleScopeContext()) { + consumer.foundDecl(alternateNamedDecl, + DeclVisibilityKind::VisibleAtTopLevel); + anyMatching = true; + } + }); } } } @@ -4104,3 +4108,32 @@ swift::getModuleCachePathFromClang(const clang::CompilerInstance &Clang) { return llvm::sys::path::parent_path(SpecificModuleCachePath).str(); } +clang::FunctionDecl *ClangImporter::instantiateCXXFunctionTemplate( + ASTContext &ctx, clang::FunctionTemplateDecl *func, SubstitutionMap subst) { + SmallVector templateSubst; + std::unique_ptr error = + ctx.getClangTemplateArguments(func->getTemplateParameters(), + subst.getReplacementTypes(), templateSubst); + if (error) { + std::string failedTypesStr; + llvm::raw_string_ostream failedTypesStrStream(failedTypesStr); + llvm::interleaveComma(error->failedTypes, failedTypesStrStream); + // TODO: Use the location of the apply here. + // TODO: This error message should not reference implementation details. + // See: https://github.com/apple/swift/pull/33053#discussion_r477003350 + ctx.Diags.diagnose(SourceLoc(), + diag::unable_to_convert_generic_swift_types.ID, + {func->getName(), StringRef(failedTypesStr)}); + // Return a valid FunctionDecl but, we'll never use it. + return func->getAsFunction(); + } + + // Instanciate a specialization of this template using the substitution map. + auto *templateArgList = clang::TemplateArgumentList::CreateCopy( + func->getASTContext(), templateSubst); + auto &sema = getClangInstance().getSema(); + auto *spec = sema.InstantiateFunctionDeclaration(func, templateArgList, + clang::SourceLocation()); + sema.InstantiateFunctionDefinition(clang::SourceLocation(), spec); + return spec; +} diff --git a/lib/ClangImporter/ClangModuleDependencyScanner.cpp b/lib/ClangImporter/ClangModuleDependencyScanner.cpp index fbc693e5b22ef..b0919422f3f9d 100644 --- a/lib/ClangImporter/ClangModuleDependencyScanner.cpp +++ b/lib/ClangImporter/ClangModuleDependencyScanner.cpp @@ -245,6 +245,15 @@ void ClangImporter::recordModuleDependencies( } } + // Add all args the non-path arguments required to be passed in, according + // to the Clang scanner + for (const auto &clangArg : clangModuleDep.NonPathCommandLine) { + swiftArgs.push_back("-Xcc"); + swiftArgs.push_back("-Xclang"); + swiftArgs.push_back("-Xcc"); + swiftArgs.push_back(clangArg); + } + // Swift frontend action: -emit-pcm swiftArgs.push_back("-emit-pcm"); swiftArgs.push_back("-module-name"); @@ -267,7 +276,6 @@ void ClangImporter::recordModuleDependencies( // Module-level dependencies. llvm::StringSet<> alreadyAddedModules; auto dependencies = ModuleDependencies::forClangModule( - clangModuleDep.ImplicitModulePCMPath, clangModuleDep.ClangModuleMapFile, clangModuleDep.ContextHash, swiftArgs, @@ -277,8 +285,7 @@ void ClangImporter::recordModuleDependencies( } cache.recordDependencies(clangModuleDep.ModuleName, - std::move(dependencies), - ModuleDependenciesKind::Clang); + std::move(dependencies)); } } @@ -327,10 +334,10 @@ bool ClangImporter::addBridgingHeaderDependencies( StringRef moduleName, ModuleDependenciesCache &cache) { auto targetModule = *cache.findDependencies( - moduleName, ModuleDependenciesKind::Swift); + moduleName, ModuleDependenciesKind::SwiftTextual); // If we've already recorded bridging header dependencies, we're done. - auto swiftDeps = targetModule.getAsSwiftModule(); + auto swiftDeps = targetModule.getAsSwiftTextualModule(); if (!swiftDeps->bridgingSourceFiles.empty() || !swiftDeps->bridgingModuleDependencies.empty()) return false; @@ -376,7 +383,7 @@ bool ClangImporter::addBridgingHeaderDependencies( // Update the cache with the new information for the module. cache.updateDependencies( - {moduleName.str(), ModuleDependenciesKind::Swift}, + {moduleName.str(), ModuleDependenciesKind::SwiftTextual}, std::move(targetModule)); return false; diff --git a/lib/ClangImporter/ClangSourceBufferImporter.cpp b/lib/ClangImporter/ClangSourceBufferImporter.cpp index fa4df253d7a0f..097fb96c50cff 100644 --- a/lib/ClangImporter/ClangSourceBufferImporter.cpp +++ b/lib/ClangImporter/ClangSourceBufferImporter.cpp @@ -67,11 +67,17 @@ SourceLoc ClangSourceBufferImporter::resolveSourceLocation( StringRef presumedFile = presumedLoc.getFilename(); SourceLoc startOfLine = loc.getAdvancedLoc(-presumedLoc.getColumn() + 1); - bool isNewVirtualFile = swiftSourceManager.openVirtualFile( - startOfLine, presumedFile, presumedLoc.getLine() - bufferLineNumber); - if (isNewVirtualFile) { - SourceLoc endOfLine = findEndOfLine(swiftSourceManager, loc, mirrorID); - swiftSourceManager.closeVirtualFile(endOfLine); + + // FIXME: Virtual files can't actually model the EOF position correctly, so + // if this virtual file would start at EOF, just hope the physical location + // will do. + if (startOfLine != swiftSourceManager.getRangeForBuffer(mirrorID).getEnd()) { + bool isNewVirtualFile = swiftSourceManager.openVirtualFile( + startOfLine, presumedFile, presumedLoc.getLine() - bufferLineNumber); + if (isNewVirtualFile) { + SourceLoc endOfLine = findEndOfLine(swiftSourceManager, loc, mirrorID); + swiftSourceManager.closeVirtualFile(endOfLine); + } } using SourceManagerRef = llvm::IntrusiveRefCntPtr; diff --git a/lib/ClangImporter/DWARFImporter.cpp b/lib/ClangImporter/DWARFImporter.cpp index 79bb6eeea19e4..8b635d1ce5ed6 100644 --- a/lib/ClangImporter/DWARFImporter.cpp +++ b/lib/ClangImporter/DWARFImporter.cpp @@ -67,11 +67,11 @@ class DWARFModuleUnit final : public LoadedFile { getDisplayDecls(SmallVectorImpl &results) const override {} virtual void - getImportedModules(SmallVectorImpl &imports, + getImportedModules(SmallVectorImpl &imports, ModuleDecl::ImportFilter filter) const override {} virtual void getImportedModulesForLookup( - SmallVectorImpl &imports) const override {}; + SmallVectorImpl &imports) const override {}; virtual void collectLinkLibraries( ModuleDecl::LinkLibraryCallback callback) const override {}; diff --git a/lib/ClangImporter/IAMInference.cpp b/lib/ClangImporter/IAMInference.cpp index f0e4564714045..cdd88dbb60c08 100644 --- a/lib/ClangImporter/IAMInference.cpp +++ b/lib/ClangImporter/IAMInference.cpp @@ -448,7 +448,7 @@ class IAMInference { baseName = humbleBaseName.userFacingName(); bool didOmit = omitNeedlessWords(baseName, argStrs, "", "", "", paramTypeNames, false, - false, nullptr, scratch); + false, nullptr, None, None, scratch); SmallVector argLabels; for (auto str : argStrs) argLabels.push_back(getIdentifier(str)); diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index cae9d66672afb..f4c40262e3398 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -17,6 +17,7 @@ #include "CFTypeInfo.h" #include "ImporterImpl.h" #include "swift/AST/ASTContext.h" +#include "swift/AST/ASTDemangler.h" #include "swift/AST/ASTMangler.h" #include "swift/AST/Attr.h" #include "swift/AST/Builtins.h" @@ -40,6 +41,7 @@ #include "swift/Basic/Defer.h" #include "swift/Basic/PrettyStackTrace.h" #include "swift/Basic/Statistic.h" +#include "swift/Basic/StringExtras.h" #include "swift/ClangImporter/ClangModule.h" #include "swift/Config.h" #include "swift/Parse/Lexer.h" @@ -50,7 +52,6 @@ #include "clang/AST/DeclObjCCommon.h" #include "clang/AST/DeclCXX.h" #include "clang/Basic/CharInfo.h" -#include "swift/Basic/Statistic.h" #include "clang/Basic/Specifiers.h" #include "clang/Basic/TargetInfo.h" #include "clang/Lex/Preprocessor.h" @@ -160,27 +161,22 @@ createVarWithPattern(ASTContext &ctx, DeclContext *dc, Identifier name, Type ty, static FuncDecl *createFuncOrAccessor(ASTContext &ctx, SourceLoc funcLoc, Optional accessorInfo, DeclName name, SourceLoc nameLoc, - ParameterList *bodyParams, - Type resultTy, - bool async, - bool throws, - DeclContext *dc, + GenericParamList *genericParams, + ParameterList *bodyParams, Type resultTy, + bool async, bool throws, DeclContext *dc, ClangNode clangNode) { if (accessorInfo) { return AccessorDecl::create(ctx, funcLoc, /*accessorKeywordLoc*/ SourceLoc(), - accessorInfo->Kind, - accessorInfo->Storage, - /*StaticLoc*/SourceLoc(), - StaticSpellingKind::None, - throws, - /*ThrowsLoc=*/SourceLoc(), - /*GenericParams=*/nullptr, - bodyParams, - resultTy, dc, clangNode); + accessorInfo->Kind, accessorInfo->Storage, + /*StaticLoc*/ SourceLoc(), + StaticSpellingKind::None, throws, + /*ThrowsLoc=*/SourceLoc(), genericParams, + bodyParams, resultTy, dc, clangNode); } else { return FuncDecl::createImported(ctx, funcLoc, name, nameLoc, async, throws, - bodyParams, resultTy, dc, clangNode); + bodyParams, resultTy, genericParams, dc, + clangNode); } } @@ -1240,6 +1236,10 @@ synthesizeStructDefaultConstructorBody(AbstractFunctionDecl *afd, ASTContext &ctx = constructor->getASTContext(); auto structDecl = static_cast(context); + // We should call into C++ constructors directly. + assert(!isa(structDecl->getClangDecl()) && + "Should not synthesize a C++ object constructor."); + // Use a builtin to produce a zero initializer, and assign it to self. // Construct the left-hand reference to self. @@ -1390,7 +1390,8 @@ createValueConstructor(ClangImporter::Implementation &Impl, continue; if (auto clangField = dyn_cast(var->getClangDecl())) - if (clangField->isAnonymousStructOrUnion()) + if (clangField->isAnonymousStructOrUnion() || + clangField->getDeclName().isEmpty()) generateParamName = false; } @@ -2397,6 +2398,8 @@ namespace { if (field->isAnonymousStructOrUnion()) { IdStream << "__Anonymous_field" << field->getFieldIndex(); } else { + assert(!field->getDeclName().isEmpty() && + "Microsoft anonymous struct extension?"); IdStream << field->getName(); } ImportedName Result; @@ -3383,6 +3386,11 @@ namespace { continue; } + if (auto CD = dyn_cast(member)) { + ctors.push_back(CD); + continue; + } + if (auto MD = dyn_cast(member)) { methods.push_back(MD); continue; @@ -3439,12 +3447,17 @@ namespace { bool hasReferenceableFields = !members.empty(); - if (hasZeroInitializableStorage) { - // Add constructors for the struct. + const clang::CXXRecordDecl *cxxRecordDecl = + dyn_cast(decl); + if (hasZeroInitializableStorage && !cxxRecordDecl) { + // Add default constructor for the struct if compiling in C mode. + // If we're compiling for C++, we'll import the C++ default constructor + // (if there is one), so we don't need to synthesize one here. ctors.push_back(createDefaultConstructor(Impl, result)); } - if (hasReferenceableFields && hasMemberwiseInitializer) { + bool isAggregate = !cxxRecordDecl || cxxRecordDecl->isAggregate(); + if (hasReferenceableFields && hasMemberwiseInitializer && isAggregate) { // The default zero initializer suppresses the implicit value // constructor that would normally be formed, so we have to add that // explicitly as well. @@ -3475,7 +3488,7 @@ namespace { result->setHasUnreferenceableStorage(hasUnreferenceableStorage); - if (auto cxxRecordDecl = dyn_cast(decl)) { + if (cxxRecordDecl) { result->setIsCxxNonTrivial(!cxxRecordDecl->isTriviallyCopyable()); for (auto ctor : cxxRecordDecl->ctors()) { @@ -3496,6 +3509,34 @@ namespace { return result; } + Decl *VisitCXXRecordDecl(const clang::CXXRecordDecl *decl) { + // This can be called from lldb without C++ interop being enabled: There + // may be C++ declarations in imported modules, but the interface for + // those modules may be a pure C or Objective-C interface. + // To avoid crashing in Clang's Sema, fall back to importing this as a + // plain RecordDecl. + if (!Impl.SwiftContext.LangOpts.EnableCXXInterop) + return VisitRecordDecl(decl); + + auto &clangSema = Impl.getClangSema(); + // Make Clang define the implicit default constructor if the class needs + // it. Make sure we only do this if the class has been fully defined and + // we're not in a dependent context (this is equivalent to the logic in + // CanDeclareSpecialMemberFunction in Clang's SemaLookup.cpp). + if (decl->getDefinition() && !decl->isBeingDefined() && + !decl->isDependentContext() && + decl->needsImplicitDefaultConstructor()) { + clang::CXXConstructorDecl *ctor = + clangSema.DeclareImplicitDefaultConstructor( + const_cast(decl)); + if (!ctor->isDeleted()) + clangSema.DefineImplicitDefaultConstructor(clang::SourceLocation(), + ctor); + } + + return VisitRecordDecl(decl); + } + Decl *VisitClassTemplateSpecializationDecl( const clang::ClassTemplateSpecializationDecl *decl) { // `Sema::isCompleteType` will try to instantiate the class template as a @@ -3512,6 +3553,14 @@ namespace { auto def = dyn_cast( decl->getDefinition()); assert(def && "Class template instantiation didn't have definition"); + + // If this type is fully specialized (i.e. "Foo<>" or "Foo"), + // bail to prevent a crash. + // TODO: we should be able to support fully specialized class templates. + // See SR-13775 for more info. + if (def->getTypeAsWritten()) + return nullptr; + // FIXME: This will instantiate all members of the specialization (and detect // instantiation failures in them), which can be more than is necessary // and is more than what Clang does. As a result we reject some C++ @@ -3519,7 +3568,7 @@ namespace { Impl.getClangSema().InstantiateClassTemplateSpecializationMembers( def->getLocation(), def, clang::TSK_ExplicitInstantiationDefinition); - return VisitRecordDecl(def); + return VisitCXXRecordDecl(def); } Decl *VisitClassTemplatePartialSpecializationDecl( @@ -3699,9 +3748,9 @@ namespace { continue; nonSelfParams.push_back(decl->getParamDecl(i)); } - return Impl.importFunctionParameterList(dc, decl, nonSelfParams, - decl->isVariadic(), - allowNSUIntegerAsInt, argNames); + return Impl.importFunctionParameterList( + dc, decl, nonSelfParams, decl->isVariadic(), allowNSUIntegerAsInt, + argNames, /*genericParams=*/{}); } Decl *importGlobalAsInitializer(const clang::FunctionDecl *decl, @@ -3746,10 +3795,14 @@ namespace { return importFunctionDecl(decl, importedName, correctSwiftName, None); } - Decl *importFunctionDecl(const clang::FunctionDecl *decl, - ImportedName importedName, - Optional correctSwiftName, - Optional accessorInfo) { + Decl *importFunctionDecl( + const clang::FunctionDecl *decl, ImportedName importedName, + Optional correctSwiftName, + Optional accessorInfo, + const clang::FunctionTemplateDecl *funcTemplate = nullptr) { + if (decl->isDeleted()) + return nullptr; + auto dc = Impl.importDeclContextOf(decl, importedName.getEffectiveContext()); if (!dc) @@ -3758,10 +3811,25 @@ namespace { DeclName name = accessorInfo ? DeclName() : importedName.getDeclName(); auto selfIdx = importedName.getSelfIndex(); - FuncDecl *result = nullptr; ImportedType importedType; bool selfIsInOut = false; ParameterList *bodyParams = nullptr; + GenericParamList *genericParams = nullptr; + SmallVector templateParams; + if (funcTemplate) { + unsigned i = 0; + for (auto param : *funcTemplate->getTemplateParameters()) { + auto *typeParam = Impl.createDeclWithClangNode( + param, AccessLevel::Public, dc, + Impl.SwiftContext.getIdentifier(param->getName()), SourceLoc(), 0, + i); + templateParams.push_back(typeParam); + (void)++i; + } + genericParams = GenericParamList::create(Impl.SwiftContext, SourceLoc(), + templateParams, SourceLoc()); + } + if (!dc->isModuleScopeContext() && !isa(decl)) { // Handle initializers. if (name.getBaseName() == DeclBaseName::createConstructor()) { @@ -3829,7 +3897,8 @@ namespace { // names get into the resulting function type. importedType = Impl.importFunctionParamsAndReturnType( dc, decl, {decl->param_begin(), decl->param_size()}, - decl->isVariadic(), isInSystemModule(dc), name, bodyParams); + decl->isVariadic(), isInSystemModule(dc), name, bodyParams, + templateParams); if (auto *mdecl = dyn_cast(decl)) { if (mdecl->isStatic() || @@ -3860,27 +3929,52 @@ namespace { if (!importedType) return nullptr; - auto resultTy = importedType.getType(); auto loc = Impl.importSourceLoc(decl->getLocation()); + ClangNode clangNode = decl; + if (funcTemplate) + clangNode = funcTemplate; + // FIXME: Poor location info. auto nameLoc = Impl.importSourceLoc(decl->getLocation()); - result = createFuncOrAccessor( - Impl.SwiftContext, loc, accessorInfo, name, - nameLoc, bodyParams, resultTy, - /*async*/ false, /*throws*/ false, dc, decl); - - if (!dc->isModuleScopeContext()) { - if (selfIsInOut) - result->setSelfAccessKind(SelfAccessKind::Mutating); - else - result->setSelfAccessKind(SelfAccessKind::NonMutating); - if (selfIdx) { - result->setSelfIndex(selfIdx.getValue()); - } else { - result->setStatic(); - result->setImportAsStaticMember(); + + AbstractFunctionDecl *result = nullptr; + if (auto *ctordecl = dyn_cast(decl)) { + // Don't import copy constructor or move constructor -- these will be + // provided through the value witness table. + if (ctordecl->isCopyConstructor() || ctordecl->isMoveConstructor()) + return nullptr; + + DeclName ctorName(Impl.SwiftContext, DeclBaseName::createConstructor(), + bodyParams); + result = Impl.createDeclWithClangNode( + clangNode, AccessLevel::Public, ctorName, loc, /*failable=*/false, + /*FailabilityLoc=*/SourceLoc(), /*Throws=*/false, + /*ThrowsLoc=*/SourceLoc(), bodyParams, genericParams, dc); + } else { + auto resultTy = importedType.getType(); + + FuncDecl *func = + createFuncOrAccessor(Impl.SwiftContext, loc, accessorInfo, name, + nameLoc, genericParams, bodyParams, resultTy, + /*async=*/false, /*throws=*/false, dc, + clangNode); + result = func; + + if (!dc->isModuleScopeContext()) { + if (selfIsInOut) + func->setSelfAccessKind(SelfAccessKind::Mutating); + else + func->setSelfAccessKind(SelfAccessKind::NonMutating); + if (selfIdx) { + func->setSelfIndex(selfIdx.getValue()); + } else { + func->setStatic(); + func->setImportAsStaticMember(); + } } + // Someday, maybe this will need to be 'open' for C++ virtual methods. + func->setAccess(AccessLevel::Public); } result->setIsObjC(false); @@ -3894,8 +3988,6 @@ namespace { result->getAttrs().add(new (Impl.SwiftContext) FinalAttr(/*IsImplicit=*/true)); - // Someday, maybe this will need to be 'open' for C++ virtual methods. - result->setAccess(AccessLevel::Public); finishFuncDecl(decl, result); // If this is a compatibility stub, mark it as such. @@ -3936,7 +4028,7 @@ namespace { Optional correctSwiftName; ImportedName importedName; - if (!decl->isAnonymousStructOrUnion()) { + if (!decl->isAnonymousStructOrUnion() && !decl->getDeclName().isEmpty()) { importedName = importFullName(decl, correctSwiftName); if (!importedName) { return nullptr; @@ -4100,6 +4192,21 @@ namespace { return nullptr; } + Decl *VisitFunctionTemplateDecl(const clang::FunctionTemplateDecl *decl) { + Optional correctSwiftName; + auto importedName = + importFullName(decl->getAsFunction(), correctSwiftName); + if (!importedName) + return nullptr; + // All template parameters must be template type parameters. + if (!llvm::all_of(*decl->getTemplateParameters(), [](auto param) { + return isa(param); + })) + return nullptr; + return importFunctionDecl(decl->getAsFunction(), importedName, + correctSwiftName, None, decl); + } + Decl *VisitUsingDecl(const clang::UsingDecl *decl) { // Using declarations are not imported. return nullptr; @@ -4472,12 +4579,11 @@ namespace { } auto result = createFuncOrAccessor(Impl.SwiftContext, - /*funcLoc*/SourceLoc(), - accessorInfo, + /*funcLoc*/ SourceLoc(), accessorInfo, importedName.getDeclName(), - /*nameLoc*/SourceLoc(), - bodyParams, resultTy, - async, throws, dc, decl); + /*nameLoc*/ SourceLoc(), + /*genericParams=*/nullptr, bodyParams, + resultTy, async, throws, dc, decl); result->setAccess(getOverridableAccessLevel(dc)); @@ -5987,7 +6093,7 @@ Decl *SwiftDeclConverter::importGlobalAsInitializer( } else { parameterList = Impl.importFunctionParameterList( dc, decl, {decl->param_begin(), decl->param_end()}, decl->isVariadic(), - allowNSUIntegerAsInt, argNames); + allowNSUIntegerAsInt, argNames, /*genericParams=*/{}); } if (!parameterList) return nullptr; @@ -7581,6 +7687,46 @@ bool importer::isSpecialUIKitStructZeroProperty(const clang::NamedDecl *decl) { return ident->isStr("UIEdgeInsetsZero") || ident->isStr("UIOffsetZero"); } +/// Determine whether any of the parameters to the given function is of an +/// unsafe pointer type. +static bool hasAnyUnsafePointerParameters(FuncDecl *func) { + for (auto param : *func->getParameters()) { + Type paramType = + param->toFunctionParam().getPlainType()->lookThroughAllOptionalTypes(); + if (paramType->getAnyPointerElementType()) { + return true; + } + } + + return false; +} + +/// Determine whether the given Objective-C method is likely to be an +/// asynchronous handler based on its name. +static bool isObjCMethodLikelyAsyncHandler( + const clang::ObjCMethodDecl *method) { + auto selector = method->getSelector(); + + for (unsigned argIdx : range(std::max(selector.getNumArgs(), 1u))) { + auto selectorPiece = selector.getNameForSlot(argIdx); + // For the first selector piece, look for the word "did" anywhere. + if (argIdx == 0) { + for (auto word : camel_case::getWords(selectorPiece)) { + if (word == "did" || word == "Did") + return true; + } + + continue; + } + + // Otherwise, check whether any subsequent selector piece starts with "did". + if (camel_case::getFirstWord(selectorPiece) == "did") + return true; + } + + return false; +} + /// Import Clang attributes as Swift attributes. void ClangImporter::Implementation::importAttributes( const clang::NamedDecl *ClangDecl, @@ -7819,6 +7965,24 @@ void ClangImporter::Implementation::importAttributes( if (ClangDecl->hasAttr()) { MappedDecl->getAttrs().add(new (C) EffectsAttr(EffectsKind::ReadOnly)); } + + // Infer @asyncHandler on imported protocol methods that meet the semantic + // requirements. + if (SwiftContext.LangOpts.EnableExperimentalConcurrency) { + if (auto func = dyn_cast(MappedDecl)) { + if (auto proto = dyn_cast(func->getDeclContext())) { + if (proto->isObjC() && isa(ClangDecl) && + func->isInstanceMember() && !isa(func) && + isObjCMethodLikelyAsyncHandler( + cast(ClangDecl)) && + func->canBeAsyncHandler() && + !hasAnyUnsafePointerParameters(func)) { + MappedDecl->getAttrs().add( + new (C) AsyncHandlerAttr(/*IsImplicit=*/false)); + } + } + } + } } Decl * @@ -8411,11 +8575,19 @@ ClangImporter::Implementation::createConstant(Identifier name, DeclContext *dc, // Create the expression node. StringRef printedValueCopy(context.AllocateCopy(printedValue)); if (value.getKind() == clang::APValue::Int) { - if (type->getCanonicalType()->isBool()) { - auto *boolExpr = - new (context) BooleanLiteralExpr(value.getInt().getBoolValue(), - SourceLoc(), - /**Implicit=*/true); + bool isBool = type->getCanonicalType()->isBool(); + // Check if "type" is a C++ enum with an underlying type of "bool". + if (!isBool && type->getStructOrBoundGenericStruct() && + type->getStructOrBoundGenericStruct()->getClangDecl()) { + if (auto enumDecl = dyn_cast( + type->getStructOrBoundGenericStruct()->getClangDecl())) { + isBool = enumDecl->getIntegerType()->isBooleanType(); + } + } + if (isBool) { + auto *boolExpr = new (context) + BooleanLiteralExpr(value.getInt().getBoolValue(), SourceLoc(), + /*Implicit=*/true); boolExpr->setBuiltinInitializer( context.getBoolBuiltinInitDecl()); diff --git a/lib/ClangImporter/ImportName.cpp b/lib/ClangImporter/ImportName.cpp index 972e9ca887bf7..a517d750d06df 100644 --- a/lib/ClangImporter/ImportName.cpp +++ b/lib/ClangImporter/ImportName.cpp @@ -804,7 +804,8 @@ static bool omitNeedlessWordsInFunctionName( ArrayRef params, clang::QualType resultType, const clang::DeclContext *dc, const SmallBitVector &nonNullArgs, Optional errorParamIndex, bool returnsSelf, bool isInstanceMethod, - NameImporter &nameImporter) { + Optional completionHandlerIndex, + Optional completionHandlerName, NameImporter &nameImporter) { clang::ASTContext &clangCtx = nameImporter.getClangContext(); // Collect the parameter type names. @@ -817,10 +818,6 @@ static bool omitNeedlessWordsInFunctionName( if (i == 0) firstParamName = param->getName(); - // Determine the number of parameters. - unsigned numParams = params.size(); - if (errorParamIndex) --numParams; - bool isLastParameter = (i == params.size() - 1) || (i == params.size() - 2 && @@ -858,7 +855,8 @@ static bool omitNeedlessWordsInFunctionName( getClangTypeNameForOmission(clangCtx, resultType), getClangTypeNameForOmission(clangCtx, contextType), paramTypes, returnsSelf, /*isProperty=*/false, - allPropertyNames, nameImporter.getScratch()); + allPropertyNames, completionHandlerIndex, + completionHandlerName, nameImporter.getScratch()); } /// Prepare global name for importing onto a swift_newtype. @@ -1138,26 +1136,9 @@ Optional NameImporter::considerErrorImport( /// Whether the given parameter name identifies a completion handler. static bool isCompletionHandlerParamName(StringRef paramName) { return paramName == "completionHandler" || paramName == "completion" || - paramName == "withCompletionHandler"; + paramName == "withCompletionHandler" || paramName == "withCompletion"; } -/// Whether the give base name implies that the first parameter is a completion -/// handler. -/// -/// \returns a trimmed base name when it does, \c None others -static Optional isCompletionHandlerInBaseName(StringRef basename) { - if (basename.endswith("WithCompletionHandler")) { - return basename.drop_back(strlen("WithCompletionHandler")); - } - - if (basename.endswith("WithCompletion")) { - return basename.drop_back(strlen("WithCompletion")); - } - - return None; -} - - // Determine whether the given type is a nullable NSError type. static bool isNullableNSErrorType( clang::ASTContext &clangCtx, clang::QualType type) { @@ -1188,7 +1169,7 @@ static bool isNullableNSErrorType( Optional NameImporter::considerAsyncImport( const clang::ObjCMethodDecl *clangDecl, - StringRef &baseName, + StringRef baseName, SmallVectorImpl ¶mNames, ArrayRef params, bool isInitializer, bool hasCustomName, @@ -1210,12 +1191,14 @@ NameImporter::considerAsyncImport( // Determine whether the naming indicates that this is a completion // handler. - Optional newBaseName; if (isCompletionHandlerParamName( - paramNames[completionHandlerParamNameIndex])) { + paramNames[completionHandlerParamNameIndex]) || + (completionHandlerParamNameIndex > 0 && + stripWithCompletionHandlerSuffix( + paramNames[completionHandlerParamNameIndex]))) { // The argument label itself has an appropriate name. } else if (!hasCustomName && completionHandlerParamIndex == 0 && - (newBaseName = isCompletionHandlerInBaseName(baseName))) { + stripWithCompletionHandlerSuffix(baseName)) { // The base name implies that the first parameter is a completion handler. } else if (isCompletionHandlerParamName( params[completionHandlerParamIndex]->getName())) { @@ -1304,10 +1287,6 @@ NameImporter::considerAsyncImport( // Drop the completion handler parameter name. paramNames.erase(paramNames.begin() + completionHandlerParamNameIndex); - // Update the base name, if needed. - if (newBaseName && !hasCustomName) - baseName = *newBaseName; - return ForeignAsyncConvention::Info( completionHandlerParamIndex, completionHandlerErrorParamIndex); } @@ -1366,6 +1345,15 @@ static bool suppressFactoryMethodAsInit(const clang::ObjCMethodDecl *method, initKind == CtorInitializerKind::ConvenienceFactory); } +static void +addEmptyArgNamesForClangFunction(const clang::FunctionDecl *funcDecl, + SmallVectorImpl &argumentNames) { + for (size_t i = 0; i < funcDecl->param_size(); ++i) + argumentNames.push_back(StringRef()); + if (funcDecl->isVariadic()) + argumentNames.push_back(StringRef()); +} + ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D, ImportNameVersion version, clang::DeclarationName givenName) { @@ -1599,7 +1587,19 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D, SmallString<16> selectorSplitScratch; ArrayRef params; switch (D->getDeclName().getNameKind()) { - case clang::DeclarationName::CXXConstructorName: + case clang::DeclarationName::CXXConstructorName: { + isInitializer = true; + isFunction = true; + result.info.initKind = CtorInitializerKind::Designated; + baseName = "init"; + auto ctor = dyn_cast(D); + if (auto templateCtor = dyn_cast(D)) + ctor = cast(templateCtor->getAsFunction()); + assert(ctor && "Unkown decl with CXXConstructorName."); + addEmptyArgNamesForClangFunction(ctor, argumentNames); + break; + } + case clang::DeclarationName::CXXConversionFunctionName: case clang::DeclarationName::CXXDestructorName: case clang::DeclarationName::CXXLiteralOperatorName: @@ -1670,16 +1670,9 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D, } } - // For C functions, create empty argument names. if (auto function = dyn_cast(D)) { isFunction = true; - params = {function->param_begin(), function->param_end()}; - for (auto param : params) { - (void)param; - argumentNames.push_back(StringRef()); - } - if (function->isVariadic()) - argumentNames.push_back(StringRef()); + addEmptyArgNamesForClangFunction(function, argumentNames); } break; @@ -1817,8 +1810,7 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D, result.info.accessorKind == ImportedAccessorKind::None) { if (auto asyncInfo = considerAsyncImport( objcMethod, baseName, argumentNames, params, isInitializer, - /*hasCustomName=*/false, - result.getErrorInfo())) { + /*hasCustomName=*/false, result.getErrorInfo())) { result.info.hasAsyncInfo = true; result.info.asyncInfo = *asyncInfo; } @@ -1958,7 +1950,7 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D, (void)omitNeedlessWords(baseName, {}, "", propertyTypeName, contextTypeName, {}, /*returnsSelf=*/false, /*isProperty=*/true, allPropertyNames, - scratch); + None, None, scratch); } } @@ -1970,7 +1962,17 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D, result.getErrorInfo() ? Optional(result.getErrorInfo()->ErrorParameterIndex) : None, - method->hasRelatedResultType(), method->isInstanceMethod(), *this); + method->hasRelatedResultType(), method->isInstanceMethod(), + result.getAsyncInfo().map( + [](const ForeignAsyncConvention::Info &info) { + return info.CompletionHandlerParamIndex; + }), + result.getAsyncInfo().map( + [&](const ForeignAsyncConvention::Info &info) { + return method->getDeclName().getObjCSelector().getNameForSlot( + info.CompletionHandlerParamIndex); + }), + *this); } // If the result is a value, lowercase it. @@ -2084,13 +2086,15 @@ ImportedName NameImporter::importName(const clang::NamedDecl *decl, bool NameImporter::forEachDistinctImportName( const clang::NamedDecl *decl, ImportNameVersion activeVersion, llvm::function_ref action) { - using ImportNameKey = std::pair; + using ImportNameKey = std::tuple; SmallVector seenNames; ImportedName newName = importName(decl, activeVersion); if (!newName) return true; - ImportNameKey key(newName.getDeclName(), newName.getEffectiveContext()); + + ImportNameKey key(newName.getDeclName(), newName.getEffectiveContext(), + newName.getAsyncInfo().hasValue()); if (action(newName, activeVersion)) seenNames.push_back(key); @@ -2101,15 +2105,18 @@ bool NameImporter::forEachDistinctImportName( ImportedName newName = importName(decl, nameVersion); if (!newName) return; - ImportNameKey key(newName.getDeclName(), newName.getEffectiveContext()); + ImportNameKey key(newName.getDeclName(), newName.getEffectiveContext(), + newName.getAsyncInfo().hasValue()); bool seen = llvm::any_of( seenNames, [&key](const ImportNameKey &existing) -> bool { - return key.first == existing.first && - key.second.equalsWithoutResolving(existing.second); + return std::get<0>(key) == std::get<0>(existing) && + std::get<2>(key) == std::get<2>(existing) && + std::get<1>(key).equalsWithoutResolving(std::get<1>(existing)); }); if (seen) return; + if (action(newName, nameVersion)) seenNames.push_back(key); }); diff --git a/lib/ClangImporter/ImportName.h b/lib/ClangImporter/ImportName.h index 5f5d9a027343e..a35fb6760e6ba 100644 --- a/lib/ClangImporter/ImportName.h +++ b/lib/ClangImporter/ImportName.h @@ -455,7 +455,7 @@ class NameImporter { Optional considerAsyncImport(const clang::ObjCMethodDecl *clangDecl, - StringRef &baseName, + StringRef baseName, SmallVectorImpl ¶mNames, ArrayRef params, bool isInitializer, bool hasCustomName, diff --git a/lib/ClangImporter/ImportType.cpp b/lib/ClangImporter/ImportType.cpp index f58dbb837e5dd..7156b8cde5353 100644 --- a/lib/ClangImporter/ImportType.cpp +++ b/lib/ClangImporter/ImportType.cpp @@ -24,6 +24,7 @@ #include "swift/AST/DiagnosticsClangImporter.h" #include "swift/AST/ExistentialLayout.h" #include "swift/AST/GenericEnvironment.h" +#include "swift/AST/GenericSignatureBuilder.h" #include "swift/AST/Module.h" #include "swift/AST/NameLookup.h" #include "swift/AST/ParameterList.h" @@ -210,10 +211,10 @@ namespace { ImportResult VisitType(const Type*) = delete; -#define DEPENDENT_TYPE(Class, Base) \ - ImportResult Visit##Class##Type(const clang::Class##Type *) { \ - llvm_unreachable("Dependent types cannot be converted"); \ - } +#define DEPENDENT_TYPE(Class, Base) \ + ImportResult Visit##Class##Type(const clang::Class##Type *) { \ + llvm_unreachable("Dependent types cannot be converted"); \ + } #define TYPE(Class, Base) #include "clang/AST/TypeNodes.inc" @@ -556,7 +557,7 @@ namespace { if (size > 4096) return Type(); - SmallVector elts{size, elementType}; + SmallVector elts{static_cast(size), elementType}; return TupleType::get(elts, elementType->getASTContext()); } @@ -1696,22 +1697,51 @@ ImportedType ClangImporter::Implementation::importFunctionReturnType( OptionalityOfReturn); } +static Type +findGenericTypeInGenericDecls(const clang::TemplateTypeParmType *templateParam, + ArrayRef genericParams) { + StringRef name = templateParam->getIdentifier()->getName(); + auto genericParamIter = + llvm::find_if(genericParams, [name](GenericTypeParamDecl *generic) { + return generic->getName().str() == name; + }); + // TODO: once we support generics in class types, replace this with + // "return nullptr". Once support for template classes, this will need to + // be updated, though. I'm leaving the assert here to make it easier to + // find. + assert(genericParamIter != genericParams.end() && + "Could not find generic param type in generic params."); + auto *genericParamDecl = *genericParamIter; + auto metatype = + cast(genericParamDecl->getInterfaceType().getPointer()); + return metatype->getMetatypeInstanceType(); +} + ImportedType ClangImporter::Implementation::importFunctionParamsAndReturnType( DeclContext *dc, const clang::FunctionDecl *clangDecl, ArrayRef params, bool isVariadic, - bool isFromSystemModule, DeclName name, ParameterList *¶meterList) { + bool isFromSystemModule, DeclName name, ParameterList *¶meterList, + ArrayRef genericParams) { bool allowNSUIntegerAsInt = shouldAllowNSUIntegerAsInt(isFromSystemModule, clangDecl); - auto importedType = - importFunctionReturnType(dc, clangDecl, allowNSUIntegerAsInt); - if (!importedType) - return {Type(), false}; + ImportedType importedType; + if (auto templateType = + dyn_cast(clangDecl->getReturnType())) { + importedType = {findGenericTypeInGenericDecls(templateType, genericParams), + false}; + } else { + importedType = + importFunctionReturnType(dc, clangDecl, allowNSUIntegerAsInt); + if (!importedType) + return {Type(), false}; + } ArrayRef argNames = name.getArgumentNames(); parameterList = importFunctionParameterList(dc, clangDecl, params, isVariadic, - allowNSUIntegerAsInt, argNames); + allowNSUIntegerAsInt, argNames, + genericParams); if (!parameterList) return {Type(), false}; @@ -1725,7 +1755,8 @@ ImportedType ClangImporter::Implementation::importFunctionParamsAndReturnType( ParameterList *ClangImporter::Implementation::importFunctionParameterList( DeclContext *dc, const clang::FunctionDecl *clangDecl, ArrayRef params, bool isVariadic, - bool allowNSUIntegerAsInt, ArrayRef argNames) { + bool allowNSUIntegerAsInt, ArrayRef argNames, + ArrayRef genericParams) { // Import the parameters. SmallVector parameters; unsigned index = 0; @@ -1773,12 +1804,21 @@ ParameterList *ClangImporter::Implementation::importFunctionParameterList( importKind = ImportTypeKind::CFUnretainedOutParameter; // Import the parameter type into Swift. - auto importedType = importType(paramTy, importKind, allowNSUIntegerAsInt, - Bridgeability::Full, OptionalityOfParam); - if (!importedType) - return nullptr; + Type swiftParamTy; + bool isParamTypeImplicitlyUnwrapped = false; + if (auto *templateParamType = + dyn_cast(paramTy)) { + swiftParamTy = + findGenericTypeInGenericDecls(templateParamType, genericParams); + } else { + auto importedType = importType(paramTy, importKind, allowNSUIntegerAsInt, + Bridgeability::Full, OptionalityOfParam); + if (!importedType) + return nullptr; - auto swiftParamTy = importedType.getType(); + isParamTypeImplicitlyUnwrapped = importedType.isImplicitlyUnwrapped(); + swiftParamTy = importedType.getType(); + } // Map __attribute__((noescape)) to @noescape. if (param->hasAttr()) { @@ -1806,8 +1846,7 @@ ParameterList *ClangImporter::Implementation::importFunctionParameterList( ImportedHeaderUnit); paramInfo->setSpecifier(ParamSpecifier::Default); paramInfo->setInterfaceType(swiftParamTy); - recordImplicitUnwrapForDecl(paramInfo, - importedType.isImplicitlyUnwrapped()); + recordImplicitUnwrapForDecl(paramInfo, isParamTypeImplicitlyUnwrapped); parameters.push_back(paramInfo); ++index; } diff --git a/lib/ClangImporter/ImporterImpl.h b/lib/ClangImporter/ImporterImpl.h index b82f88a83852f..45f537f3d33fb 100644 --- a/lib/ClangImporter/ImporterImpl.h +++ b/lib/ClangImporter/ImporterImpl.h @@ -1077,13 +1077,11 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation /// to system APIs. /// \param name The name of the function. /// \param[out] parameterList The parameters visible inside the function body. - ImportedType - importFunctionParamsAndReturnType(DeclContext *dc, - const clang::FunctionDecl *clangDecl, - ArrayRef params, - bool isVariadic, bool isFromSystemModule, - DeclName name, - ParameterList *¶meterList); + ImportedType importFunctionParamsAndReturnType( + DeclContext *dc, const clang::FunctionDecl *clangDecl, + ArrayRef params, bool isVariadic, + bool isFromSystemModule, DeclName name, ParameterList *¶meterList, + ArrayRef genericParams); /// Import the given function return type. /// @@ -1110,12 +1108,11 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation /// \param argNames The argument names /// /// \returns The imported parameter list on success, or null on failure - ParameterList * - importFunctionParameterList(DeclContext *dc, - const clang::FunctionDecl *clangDecl, - ArrayRef params, - bool isVariadic, bool allowNSUIntegerAsInt, - ArrayRef argNames); + ParameterList *importFunctionParameterList( + DeclContext *dc, const clang::FunctionDecl *clangDecl, + ArrayRef params, bool isVariadic, + bool allowNSUIntegerAsInt, ArrayRef argNames, + ArrayRef genericParams); ImportedType importPropertyType(const clang::ObjCPropertyDecl *clangDecl, bool isFromSystemModule); @@ -1300,6 +1297,11 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation llvm_unreachable("unimplemented for ClangImporter"); } + ValueDecl *loadTargetFunctionDecl(const SpecializeAttr *attr, + uint64_t contextData) override { + llvm_unreachable("unimplemented for ClangImporter"); + } + void loadRequirementSignature(const ProtocolDecl *decl, uint64_t contextData, SmallVectorImpl &reqs) override { llvm_unreachable("unimplemented for ClangImporter"); diff --git a/lib/ClangImporter/SwiftLookupTable.h b/lib/ClangImporter/SwiftLookupTable.h index 0a5afe26e0d4d..db60c67a90807 100644 --- a/lib/ClangImporter/SwiftLookupTable.h +++ b/lib/ClangImporter/SwiftLookupTable.h @@ -201,6 +201,7 @@ class EffectiveClangContext { DC = nsDecl->getCanonicalDecl(); } else { assert(isa(dc) || + isa(dc) || isa(dc) && "No other kinds of effective Clang contexts"); DC = dc; diff --git a/lib/Demangling/Demangler.cpp b/lib/Demangling/Demangler.cpp index f49ba90261714..b717ad4774454 100644 --- a/lib/Demangling/Demangler.cpp +++ b/lib/Demangling/Demangler.cpp @@ -103,6 +103,7 @@ bool swift::Demangle::isFunctionAttr(Node::Kind kind) { switch (kind) { case Node::Kind::FunctionSignatureSpecialization: case Node::Kind::GenericSpecialization: + case Node::Kind::GenericSpecializationPrespecialized: case Node::Kind::InlinedGenericFunction: case Node::Kind::GenericSpecializationNotReAbstracted: case Node::Kind::GenericPartialSpecialization: @@ -1228,8 +1229,13 @@ NodePointer Demangler::demanglePlainFunction() { return createWithChildren(Node::Kind::Function, Ctx, Name, Type); } -NodePointer Demangler::popFunctionType(Node::Kind kind) { +NodePointer Demangler::popFunctionType(Node::Kind kind, bool hasClangType) { NodePointer FuncType = createNode(kind); + NodePointer ClangType = nullptr; + if (hasClangType) { + ClangType = demangleClangType(); + } + addChild(FuncType, ClangType); addChild(FuncType, popNode(Node::Kind::ThrowsAnnotation)); addChild(FuncType, popNode(Node::Kind::AsyncAnnotation)); @@ -1748,6 +1754,16 @@ NodePointer Demangler::demangleImplDifferentiability() { return createNode(Node::Kind::ImplDifferentiability, attr); } +NodePointer Demangler::demangleClangType() { + int numChars = demangleNatural(); + if (numChars <= 0 || Pos + numChars > Text.size()) + return nullptr; + CharVector mangledClangType; + mangledClangType.append(StringRef(Text.data() + Pos, numChars), *this); + Pos = Pos + numChars; + return createNode(Node::Kind::ClangType, mangledClangType); +} + NodePointer Demangler::demangleImplFunctionType() { NodePointer type = createNode(Node::Kind::ImplFunctionType); @@ -1806,20 +1822,33 @@ NodePointer Demangler::demangleImplFunctionType() { } type->addChild(createNode(Node::Kind::ImplConvention, CAttr), *this); - const char *FAttr = nullptr; + const char *FConv = nullptr; + bool hasClangType = false; switch (nextChar()) { - case 'B': FAttr = "@convention(block)"; break; - case 'C': FAttr = "@convention(c)"; break; - case 'M': FAttr = "@convention(method)"; break; - case 'O': FAttr = "@convention(objc_method)"; break; - case 'K': FAttr = "@convention(closure)"; break; - case 'W': FAttr = "@convention(witness_method)"; break; - default: - pushBack(); - break; + case 'B': FConv = "block"; break; + case 'C': FConv = "c"; break; + case 'z': { + switch (nextChar()) { + case 'B': hasClangType = true; FConv = "block"; break; + case 'C': hasClangType = true; FConv = "c"; break; + default: pushBack(); pushBack(); break; + } + break; + } + case 'M': FConv = "method"; break; + case 'O': FConv = "objc_method"; break; + case 'K': FConv = "closure"; break; + case 'W': FConv = "witness_method"; break; + default: pushBack(); break; + } + if (FConv) { + auto FAttrNode = createNode(Node::Kind::ImplFunctionConvention); + FAttrNode->addChild( + createNode(Node::Kind::ImplFunctionConventionName, FConv), *this); + if (hasClangType) + FAttrNode->addChild(demangleClangType(), *this); + type->addChild(FAttrNode, *this); } - if (FAttr) - type->addChild(createNode(Node::Kind::ImplFunctionAttribute, FAttr), *this); const char *CoroAttr = nullptr; if (nextIf('A')) @@ -1829,6 +1858,11 @@ NodePointer Demangler::demangleImplFunctionType() { if (CoroAttr) type->addChild(createNode(Node::Kind::ImplFunctionAttribute, CoroAttr), *this); + if (nextIf('H')) { + type->addChild(createNode(Node::Kind::ImplFunctionAttribute, "@async"), + *this); + } + addChild(type, GenSig); int NumTypesToAdd = 0; @@ -2263,6 +2297,9 @@ NodePointer Demangler::demangleThunkOrSpecialization() { case 'G': return demangleGenericSpecialization(Node::Kind:: GenericSpecializationNotReAbstracted); + case 's': + return demangleGenericSpecialization( + Node::Kind::GenericSpecializationPrespecialized); case 'i': return demangleGenericSpecialization(Node::Kind::InlinedGenericFunction); case'p': { @@ -2858,6 +2895,15 @@ NodePointer Demangler::demangleSpecialType() { return popFunctionType(Node::Kind::ObjCBlock); case 'C': return popFunctionType(Node::Kind::CFunctionPointer); + case 'z': + switch (auto cchar = nextChar()) { + case 'B': + return popFunctionType(Node::Kind::ObjCBlock, true); + case 'C': + return popFunctionType(Node::Kind::CFunctionPointer, true); + default: + return nullptr; + } case 'F': return popFunctionType(Node::Kind::DifferentiableFunctionType); case 'G': diff --git a/lib/Demangling/NodePrinter.cpp b/lib/Demangling/NodePrinter.cpp index 3a7fc824699b2..afbec113eaae5 100644 --- a/lib/Demangling/NodePrinter.cpp +++ b/lib/Demangling/NodePrinter.cpp @@ -18,6 +18,7 @@ #include "swift/Demangling/Demangle.h" #include "swift/AST/Ownership.h" #include "swift/Strings.h" +#include #include #include @@ -337,6 +338,7 @@ class NodePrinter { case Node::Kind::AutoClosureType: case Node::Kind::BaseConformanceDescriptor: case Node::Kind::BaseWitnessTableAccessor: + case Node::Kind::ClangType: case Node::Kind::ClassMetadataBaseOffset: case Node::Kind::CFunctionPointer: case Node::Kind::Constructor: @@ -387,6 +389,7 @@ class NodePrinter { case Node::Kind::GenericSpecialization: case Node::Kind::GenericSpecializationNotReAbstracted: case Node::Kind::GenericSpecializationParam: + case Node::Kind::GenericSpecializationPrespecialized: case Node::Kind::InlinedGenericFunction: case Node::Kind::GenericTypeMetadataPattern: case Node::Kind::Getter: @@ -402,6 +405,8 @@ class NodePrinter { case Node::Kind::ImplConvention: case Node::Kind::ImplDifferentiability: case Node::Kind::ImplFunctionAttribute: + case Node::Kind::ImplFunctionConvention: + case Node::Kind::ImplFunctionConventionName: case Node::Kind::ImplFunctionType: case Node::Kind::ImplInvocationSubstitutions: case Node::Kind::ImplPatternSubstitutions: @@ -749,12 +754,59 @@ class NodePrinter { } void printFunctionType(NodePointer LabelList, NodePointer node) { - if (node->getNumChildren() < 2 || node->getNumChildren() > 4) { + if (node->getNumChildren() < 2 || node->getNumChildren() > 5) { setInvalid(); return; } + + auto printConventionWithMangledCType = [this, + node](const char *convention) { + Printer << "@convention(" << convention; + if (node->getFirstChild()->getKind() == Node::Kind::ClangType) { + Printer << ", mangledCType: \""; + print(node->getFirstChild()); + Printer << '"'; + } + Printer << ") "; + }; + + switch (node->getKind()) { + case Node::Kind::FunctionType: + case Node::Kind::UncurriedFunctionType: + case Node::Kind::NoEscapeFunctionType: + break; + case Node::Kind::AutoClosureType: + case Node::Kind::EscapingAutoClosureType: + Printer << "@autoclosure "; break; + case Node::Kind::ThinFunctionType: + Printer << "@convention(thin) "; break; + case Node::Kind::CFunctionPointer: + printConventionWithMangledCType("c"); + break; + case Node::Kind::EscapingObjCBlock: + Printer << "@escaping "; + LLVM_FALLTHROUGH; + case Node::Kind::ObjCBlock: + printConventionWithMangledCType("block"); + break; + case Node::Kind::DifferentiableFunctionType: + Printer << "@differentiable "; break; + case Node::Kind::EscapingDifferentiableFunctionType: + Printer << "@escaping @differentiable "; break; + case Node::Kind::LinearFunctionType: + Printer << "@differentiable(linear) "; break; + case Node::Kind::EscapingLinearFunctionType: + Printer << "@escaping @differentiable(linear) "; break; + default: + assert(false && "Unhandled function type in printFunctionType!"); + } + unsigned startIndex = 0; bool isAsync = false, isThrows = false; + if (node->getChild(startIndex)->getKind() == Node::Kind::ClangType) { + // handled earlier + ++startIndex; + } if (node->getChild(startIndex)->getKind() == Node::Kind::ThrowsAnnotation) { ++startIndex; isThrows = true; @@ -1256,40 +1308,23 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { case Node::Kind::UnknownIndex: Printer << "unknown index"; return nullptr; + case Node::Kind::FunctionType: + case Node::Kind::UncurriedFunctionType: case Node::Kind::NoEscapeFunctionType: - printFunctionType(nullptr, Node); - return nullptr; - case Node::Kind::EscapingAutoClosureType: - Printer << "@autoclosure "; - printFunctionType(nullptr, Node); - return nullptr; case Node::Kind::AutoClosureType: - Printer << "@autoclosure "; - printFunctionType(nullptr, Node); - return nullptr; + case Node::Kind::EscapingAutoClosureType: case Node::Kind::ThinFunctionType: - Printer << "@convention(thin) "; - printFunctionType(nullptr, Node); - return nullptr; + case Node::Kind::CFunctionPointer: + case Node::Kind::ObjCBlock: + case Node::Kind::EscapingObjCBlock: case Node::Kind::DifferentiableFunctionType: - Printer << "@differentiable "; - printFunctionType(nullptr, Node); - return nullptr; case Node::Kind::EscapingDifferentiableFunctionType: - Printer << "@escaping @differentiable "; - printFunctionType(nullptr, Node); - return nullptr; case Node::Kind::LinearFunctionType: - Printer << "@differentiable(linear) "; - printFunctionType(nullptr, Node); - return nullptr; case Node::Kind::EscapingLinearFunctionType: - Printer << "@escaping @differentiable(linear) "; printFunctionType(nullptr, Node); return nullptr; - case Node::Kind::FunctionType: - case Node::Kind::UncurriedFunctionType: - printFunctionType(nullptr, Node); + case Node::Kind::ClangType: + Printer << Node->getText(); return nullptr; case Node::Kind::ArgumentTuple: printFunctionParameters(nullptr, Node, Options.ShowFunctionArgumentTypes); @@ -1379,6 +1414,9 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { case Node::Kind::GenericSpecialization: printSpecializationPrefix(Node, "generic specialization"); return nullptr; + case Node::Kind::GenericSpecializationPrespecialized: + printSpecializationPrefix(Node, "generic pre-specialization"); + return nullptr; case Node::Kind::GenericSpecializationNotReAbstracted: printSpecializationPrefix(Node, "generic not re-abstracted specialization"); return nullptr; @@ -1881,21 +1919,6 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { case Node::Kind::DynamicSelf: Printer << "Self"; return nullptr; - case Node::Kind::CFunctionPointer: { - Printer << "@convention(c) "; - printFunctionType(nullptr, Node); - return nullptr; - } - case Node::Kind::ObjCBlock: { - Printer << "@convention(block) "; - printFunctionType(nullptr, Node); - return nullptr; - } - case Node::Kind::EscapingObjCBlock: { - Printer << "@escaping @convention(block) "; - printFunctionType(nullptr, Node); - return nullptr; - } case Node::Kind::SILBoxType: { Printer << "@box "; NodePointer type = Node->getChild(0); @@ -2101,6 +2124,24 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { case Node::Kind::ImplFunctionAttribute: Printer << Node->getText(); return nullptr; + case Node::Kind::ImplFunctionConvention: + Printer << "@convention("; + switch (Node->getNumChildren()) { + case 1: + Printer << Node->getChild(0)->getText(); + break; + case 2: + Printer << Node->getChild(0)->getText() << ", mangledCType: \""; + print(Node->getChild(1)); + Printer << '"'; + break; + default: + assert(false && "Unexpected numChildren for ImplFunctionConvention"); + } + Printer << ')'; + return nullptr; + case Node::Kind::ImplFunctionConventionName: + assert(false && "Already handled in ImplFunctionConvention"); case Node::Kind::ImplErrorResult: Printer << "@error "; printChildren(Node, " "); @@ -2648,14 +2689,6 @@ void NodePrinter::printEntityType(NodePointer Entity, NodePointer type, Printer << ' '; type = dependentType->getFirstChild(); } - if (type->getKind() == Node::Kind::DifferentiableFunctionType) - Printer << "@differentiable "; - else if (type->getKind() == Node::Kind::EscapingDifferentiableFunctionType) - Printer << "@escaping @differentiable "; - else if (type->getKind() == Node::Kind::LinearFunctionType) - Printer << "@differentiable(linear) "; - else if (type->getKind() == Node::Kind::EscapingLinearFunctionType) - Printer << "@escaping @differentiable(linear) "; printFunctionType(labelList, type); } else { print(type); diff --git a/lib/Demangling/OldDemangler.cpp b/lib/Demangling/OldDemangler.cpp index e66d21aaa85f9..f7ed9400adf42 100644 --- a/lib/Demangling/OldDemangler.cpp +++ b/lib/Demangling/OldDemangler.cpp @@ -2135,19 +2135,22 @@ class OldDemangler { if (Mangled.nextIf('C')) { if (Mangled.nextIf('b')) - addImplFunctionAttribute(type, "@convention(block)"); + addImplFunctionConvention(type, "block"); else if (Mangled.nextIf('c')) - addImplFunctionAttribute(type, "@convention(c)"); + addImplFunctionConvention(type, "c"); else if (Mangled.nextIf('m')) - addImplFunctionAttribute(type, "@convention(method)"); + addImplFunctionConvention(type, "method"); else if (Mangled.nextIf('O')) - addImplFunctionAttribute(type, "@convention(objc_method)"); + addImplFunctionConvention(type, "objc_method"); else if (Mangled.nextIf('w')) - addImplFunctionAttribute(type, "@convention(witness_method)"); + addImplFunctionConvention(type, "witness_method"); else return nullptr; } + if (Mangled.nextIf('H')) + addImplFunctionAttribute(type, "@async"); + // Enter a new generic context if this type is generic. // FIXME: replace with std::optional, when we have it. bool isPseudogeneric = false; @@ -2231,6 +2234,14 @@ class OldDemangler { parent->addChild(Factory.createNode(kind, attr), Factory); } + void addImplFunctionConvention(NodePointer parent, StringRef attr) { + auto attrNode = Factory.createNode(Node::Kind::ImplFunctionConvention); + attrNode->addChild( + Factory.createNode(Node::Kind::ImplFunctionConventionName, attr), + Factory); + parent->addChild(attrNode, Factory); + } + // impl-parameter ::= impl-convention type bool demangleImplParameters(NodePointer parent) { while (!Mangled.nextIf('_')) { diff --git a/lib/Demangling/OldRemangler.cpp b/lib/Demangling/OldRemangler.cpp index 7df26cbfad047..dae50d11e241b 100644 --- a/lib/Demangling/OldRemangler.cpp +++ b/lib/Demangling/OldRemangler.cpp @@ -363,6 +363,9 @@ void Remangler::mangleGenericSpecialization(Node *node) { unreachable("unsupported"); } +void Remangler::mangleGenericSpecializationPrespecialized(Node *node) { + unreachable("unsupported"); +} void Remangler::mangleGenericSpecializationNotReAbstracted(Node *node) { unreachable("unsupported"); } @@ -1241,25 +1244,40 @@ void Remangler::mangleImplFunctionType(Node *node) { void Remangler::mangleImplFunctionAttribute(Node *node) { StringRef text = node->getText(); - if (text == "@convention(block)") { + if (text == "@yield_once") { + Buffer << "A"; + } else if (text == "@yield_many") { + Buffer << "G"; + } else if (text == "@async") { + Buffer << "H"; + } else { + unreachable("bad impl-function-attribute"); + } +} + +void Remangler::mangleImplFunctionConvention(Node *node) { + mangle(node->getChild(0)); +} + +void Remangler::mangleImplFunctionConventionName(Node *node) { + StringRef text = node->getText(); + if (text == "block") { Buffer << "Cb"; - } else if (text == "@convention(c)") { + } else if (text == "c") { Buffer << "Cc"; - } else if (text == "@convention(method)") { + } else if (text == "method") { Buffer << "Cm"; - } else if (text == "@convention(objc_method)") { + } else if (text == "objc_method") { Buffer << "CO"; - } else if (text == "@convention(witness_method)") { + } else if (text == "witness_method") { Buffer << "Cw"; - } else if (text == "@yield_once") { - Buffer << "A"; - } else if (text == "@yield_many") { - Buffer << "G"; } else { - unreachable("bad impl-function-attribute"); + unreachable("bad impl-function-convention-name"); } } +void Remangler::mangleClangType(Node *node) { unreachable("unsupported"); } + void Remangler::mangleImplParameter(Node *node) { assert(node->getNumChildren() == 2); mangleChildNodes(node); // impl convention, type diff --git a/lib/Demangling/Remangler.cpp b/lib/Demangling/Remangler.cpp index a61064fdfb1cb..2d3e1f2544e5e 100644 --- a/lib/Demangling/Remangler.cpp +++ b/lib/Demangling/Remangler.cpp @@ -774,6 +774,15 @@ void Remangler::mangleBuiltinTypeName(Node *node) { } void Remangler::mangleCFunctionPointer(Node *node) { + if (node->getNumChildren() > 0 && + node->getFirstChild()->getKind() == Node::Kind::ClangType) { + for (size_t Idx = node->getNumChildren() - 1; Idx >= 1; --Idx) { + mangleChildNode(node, Idx); + } + Buffer << "XzC"; + mangleClangType(node->getFirstChild()); + return; + } mangleChildNodesReversed(node); // argument tuple, result type Buffer << "XC"; } @@ -1289,6 +1298,9 @@ void Remangler::mangleGenericSpecialization(Node *node) { case Node::Kind::GenericSpecialization: Buffer << "Tg"; break; + case Node::Kind::GenericSpecializationPrespecialized: + Buffer << "Ts"; + break; case Node::Kind::GenericSpecializationNotReAbstracted: Buffer << "TG"; break; @@ -1305,6 +1317,10 @@ void Remangler::mangleGenericSpecialization(Node *node) { } } +void Remangler::mangleGenericSpecializationPrespecialized(Node *node) { + mangleGenericSpecialization(node); +} + void Remangler::mangleGenericSpecializationNotReAbstracted(Node *node) { mangleGenericSpecialization(node); } @@ -1340,6 +1356,7 @@ void Remangler::mangleGlobal(Node *node) { switch (Child->getKind()) { case Node::Kind::FunctionSignatureSpecialization: case Node::Kind::GenericSpecialization: + case Node::Kind::GenericSpecializationPrespecialized: case Node::Kind::GenericSpecializationNotReAbstracted: case Node::Kind::InlinedGenericFunction: case Node::Kind::GenericPartialSpecialization: @@ -1436,6 +1453,37 @@ void Remangler::mangleImplFunctionAttribute(Node *node) { unreachable("handled inline"); } +void Remangler::mangleImplFunctionConvention(Node *node) { + StringRef text = + (node->getNumChildren() > 0 && node->getFirstChild()->hasText()) + ? node->getFirstChild()->getText() + : ""; + char FuncAttr = llvm::StringSwitch(text) + .Case("block", 'B') + .Case("c", 'C') + .Case("method", 'M') + .Case("objc_method", 'O') + .Case("closure", 'K') + .Case("witness_method", 'W') + .Default(0); + assert(FuncAttr && "invalid impl function convention"); + if ((FuncAttr == 'B' || FuncAttr == 'C') && node->getNumChildren() > 1 && + node->getChild(1)->getKind() == Node::Kind::ClangType) { + Buffer << 'z' << FuncAttr; + mangleClangType(node->getChild(1)); + return; + } + Buffer << FuncAttr; +} + +void Remangler::mangleImplFunctionConventionName(Node *node) { + unreachable("handled inline"); +} + +void Remangler::mangleClangType(Node *node) { + Buffer << node->getText().size() << node->getText(); +} + void Remangler::mangleImplInvocationSubstitutions(Node *node) { unreachable("handled inline"); } @@ -1527,16 +1575,15 @@ void Remangler::mangleImplFunctionType(Node *node) { Buffer << ConvCh; break; } + case Node::Kind::ImplFunctionConvention: { + mangleImplFunctionConvention(Child); + break; + } case Node::Kind::ImplFunctionAttribute: { char FuncAttr = llvm::StringSwitch(Child->getText()) - .Case("@convention(block)", 'B') - .Case("@convention(c)", 'C') - .Case("@convention(method)", 'M') - .Case("@convention(objc_method)", 'O') - .Case("@convention(closure)", 'K') - .Case("@convention(witness_method)", 'W') .Case("@yield_once", 'A') .Case("@yield_many", 'G') + .Case("@async", 'H') .Default(0); assert(FuncAttr && "invalid impl function attribute"); Buffer << FuncAttr; @@ -1782,6 +1829,15 @@ void Remangler::mangleObjCAttribute(Node *node) { } void Remangler::mangleObjCBlock(Node *node) { + if (node->getNumChildren() > 0 && + node->getFirstChild()->getKind() == Node::Kind::ClangType) { + for (size_t Idx = node->getNumChildren() - 1; Idx >= 1; --Idx) { + mangleChildNode(node, Idx); + } + Buffer << "XzB"; + mangleClangType(node->getFirstChild()); + return; + } mangleChildNodesReversed(node); Buffer << "XB"; } diff --git a/lib/Driver/Action.cpp b/lib/Driver/Action.cpp index 25117800eed96..27f0d9306d95a 100644 --- a/lib/Driver/Action.cpp +++ b/lib/Driver/Action.cpp @@ -43,6 +43,8 @@ void InputAction::anchor() {} void JobAction::anchor() {} +void IncrementalJobAction::anchor() {} + void CompileJobAction::anchor() {} void InterpretJobAction::anchor() {} diff --git a/lib/Driver/CMakeLists.txt b/lib/Driver/CMakeLists.txt index 380c3c6dc8a0c..d210da923bce6 100644 --- a/lib/Driver/CMakeLists.txt +++ b/lib/Driver/CMakeLists.txt @@ -1,3 +1,5 @@ +set_swift_llvm_is_available() + set(swiftDriver_sources Action.cpp Compilation.cpp diff --git a/lib/Driver/Compilation.cpp b/lib/Driver/Compilation.cpp index cf53235560519..10c418565888f 100644 --- a/lib/Driver/Compilation.cpp +++ b/lib/Driver/Compilation.cpp @@ -108,7 +108,6 @@ Compilation::Compilation(DiagnosticEngine &Diags, std::unique_ptr TranslatedArgs, InputFileList InputsWithTypes, std::string CompilationRecordPath, - bool OutputCompilationRecordForModuleOnlyBuild, StringRef ArgsHash, llvm::sys::TimePoint<> StartTime, llvm::sys::TimePoint<> LastBuildTime, @@ -122,13 +121,12 @@ Compilation::Compilation(DiagnosticEngine &Diags, bool ShowDriverTimeCompilation, std::unique_ptr StatsReporter, bool OnlyOneDependencyFile, - bool EnableTypeFingerprints, bool VerifyFineGrainedDependencyGraphAfterEveryImport, bool EmitFineGrainedDependencyDotFileAfterEveryImport, - bool FineGrainedDependenciesIncludeIntrafileOnes, bool EnableSourceRangeDependencies, bool CompareIncrementalSchemes, - StringRef CompareIncrementalSchemesPath) + StringRef CompareIncrementalSchemesPath, + bool EnableCrossModuleIncrementalBuild) : Diags(Diags), TheToolChain(TC), TheOutputInfo(OI), Level(Level), @@ -140,8 +138,6 @@ Compilation::Compilation(DiagnosticEngine &Diags, BuildStartTime(StartTime), LastBuildTime(LastBuildTime), EnableIncrementalBuild(EnableIncrementalBuild), - OutputCompilationRecordForModuleOnlyBuild( - OutputCompilationRecordForModuleOnlyBuild), EnableBatchMode(EnableBatchMode), BatchSeed(BatchSeed), BatchCount(BatchCount), @@ -151,14 +147,12 @@ Compilation::Compilation(DiagnosticEngine &Diags, Stats(std::move(StatsReporter)), FilelistThreshold(FilelistThreshold), OnlyOneDependencyFile(OnlyOneDependencyFile), - EnableTypeFingerprints(EnableTypeFingerprints), VerifyFineGrainedDependencyGraphAfterEveryImport( VerifyFineGrainedDependencyGraphAfterEveryImport), EmitFineGrainedDependencyDotFileAfterEveryImport( EmitFineGrainedDependencyDotFileAfterEveryImport), - FineGrainedDependenciesIncludeIntrafileOnes( - FineGrainedDependenciesIncludeIntrafileOnes), - EnableSourceRangeDependencies(EnableSourceRangeDependencies) + EnableSourceRangeDependencies(EnableSourceRangeDependencies), + EnableCrossModuleIncrementalBuild(EnableCrossModuleIncrementalBuild) { if (CompareIncrementalSchemes) IncrementalComparator.emplace( @@ -329,6 +323,17 @@ namespace driver { return; } +#ifndef NDEBUG + // If we can, assert that no compile jobs are scheduled beyond the second + // wave. If this assertion fails, it indicates one of: + // 1) A failure of the driver's job tracing machinery to follow a + // dependency arc. + // 2) A failure of the frontend to emit a dependency arc. + if (isa(Cmd->getSource()) && Cmd->getWave() > 2) { + llvm_unreachable("Scheduled a command into a third wave!"); + } +#endif + // Adding to scheduled means we've committed to its completion (not // distinguished from skipping). We never remove it once inserted. ScheduledCommands.insert(Cmd); @@ -452,7 +457,7 @@ namespace driver { std::vector reloadAndRemarkDeps(const Job *FinishedCmd, int ReturnCode, - const bool forRanges) { + const bool forRanges) { const CommandOutput &Output = FinishedCmd->getOutput(); StringRef DependenciesFile = Output.getAdditionalOutputForType(file_types::TY_SwiftDeps); @@ -464,7 +469,8 @@ namespace driver { // coarse dependencies that always affect downstream nodes), but we're // not using either of those right now, and this logic should probably // be revisited when we are. - assert(FinishedCmd->getCondition() == Job::Condition::Always); + assert(isa(FinishedCmd->getSource()) || + FinishedCmd->getCondition() == Job::Condition::Always); return {}; } const bool compileExitedNormally = @@ -699,8 +705,16 @@ namespace driver { "because of dependencies discovered later"); scheduleCommandsInSortedOrder(DependentsInEffect); - for (const Job *Cmd : DependentsInEffect) - DeferredCommands.erase(Cmd); + for (const Job *Cmd : DependentsInEffect) { + if (DeferredCommands.erase(Cmd)) { +#ifndef NDEBUG + if (isa(FinishedCmd->getSource())) + Cmd->setWave(FinishedCmd->getWave() + 1); +#else + continue; +#endif + } + } return TaskFinishedResponse::ContinueExecution; } @@ -830,12 +844,12 @@ namespace driver { FineGrainedDepGraph( Comp.getVerifyFineGrainedDependencyGraphAfterEveryImport(), Comp.getEmitFineGrainedDependencyDotFileAfterEveryImport(), - Comp.getEnableTypeFingerprints(), Comp.getTraceDependencies(), + Comp.getTraceDependencies(), Comp.getStatsReporter()), FineGrainedDepGraphForRanges( Comp.getVerifyFineGrainedDependencyGraphAfterEveryImport(), Comp.getEmitFineGrainedDependencyDotFileAfterEveryImport(), - Comp.getEnableTypeFingerprints(), Comp.getTraceDependencies(), + Comp.getTraceDependencies(), Comp.getStatsReporter()), TQ(std::move(TaskQueue)) {} @@ -866,7 +880,7 @@ namespace driver { computeFirstRoundCompileJobsForIncrementalCompilation(); for (const Job *Cmd : Comp.getJobs()) { - if (Cmd->getFirstSwiftPrimaryInput().empty() || + if (!isa(Cmd->getSource()) || compileJobsToSchedule.count(Cmd)) { scheduleCommandIfNecessaryAndPossible(Cmd); noteBuilding(Cmd, /*willBeBuilding*/ true, /*isTentative=*/false, @@ -905,20 +919,36 @@ namespace driver { CommandSet computeDependenciesAndGetNeededCompileJobs(const bool forRanges) { auto getEveryCompileJob = [&] { - CommandSet everyCompileJob; + CommandSet everyIncrementalJob; for (const Job *Cmd : Comp.getJobs()) { - if (!Cmd->getFirstSwiftPrimaryInput().empty()) - everyCompileJob.insert(Cmd); + if (isa(Cmd->getSource())) + everyIncrementalJob.insert(Cmd); } - return everyCompileJob; + return everyIncrementalJob; }; + bool sawModuleWrapJob = false; + const Job *mergeModulesJob = nullptr; CommandSet jobsToSchedule; CommandSet initialCascadingCommands; for (const Job *cmd : Comp.getJobs()) { - const StringRef primary = cmd->getFirstSwiftPrimaryInput(); - if (primary.empty()) - continue; // not Compile + // A modulewrap job consumes the output of merge-modules. If it is + // in the queue, we must run merge-modules or empty temporary files + // will be consumed by the job instead. + // FIXME: We should be able to ditch this if we compare the timestamps + // of the temporary file to the build record, if it exists. + sawModuleWrapJob |= isa(cmd->getSource()); + + // Skip jobs that have no associated incremental info. + if (!isa(cmd->getSource())) { + continue; + } + + if (isa(cmd->getSource())) { + assert(!mergeModulesJob && "multiple scheduled merge-modules jobs?"); + mergeModulesJob = cmd; + } + const Optional> shouldSchedAndIsCascading = computeShouldInitiallyScheduleJobAndDependendents(cmd, forRanges); if (!shouldSchedAndIsCascading) @@ -936,6 +966,19 @@ namespace driver { for (const auto cmd : collectExternallyDependentJobsFromDependencyGraph(forRanges)) jobsToSchedule.insert(cmd); + for (const auto cmd : + collectIncrementalExternallyDependentJobsFromDependencyGraph( + forRanges)) + jobsToSchedule.insert(cmd); + + // The merge-modules job is special: it *must* be scheduled if any other + // job has been scheduled because any other job can influence the + // structure of the resulting module. Additionally, the initial scheduling + // predicate above is only aware of intra-module changes. External + // dependencies changing *must* cause merge-modules to be scheduled. + if ((!jobsToSchedule.empty() || sawModuleWrapJob) && mergeModulesJob) { + jobsToSchedule.insert(mergeModulesJob); + } return jobsToSchedule; } @@ -1031,6 +1074,13 @@ namespace driver { /// But returns None if there was a dependency read error. Optional> loadDependenciesAndComputeCondition(const Job *const Cmd, bool forRanges) { + // merge-modules Jobs do not have .swiftdeps files associated with them, + // however, their compilation condition is computed as a function of their + // inputs, so their condition can be used as normal. + if (isa(Cmd->getSource())) { + return std::make_pair(Cmd->getCondition(), true); + } + // Try to load the dependencies file for this job. If there isn't one, we // always have to run the job, but it doesn't affect any other jobs. If // there should be one but it's not present or can't be loaded, we have to @@ -1142,6 +1192,77 @@ namespace driver { return ExternallyDependentJobs; } + SmallVector + collectIncrementalExternallyDependentJobsFromDependencyGraph( + const bool forRanges) { + SmallVector ExternallyDependentJobs; + auto fallbackToExternalBehavior = [&](StringRef external) { + for (const auto cmd : + markIncrementalExternalInDepGraph(external, forRanges)) { + ExternallyDependentJobs.push_back(cmd); + } + }; + + for (auto external : getFineGrainedDepGraph(forRanges) + .getIncrementalExternalDependencies()) { + llvm::sys::fs::file_status depStatus; + // Can't `stat` this dependency? Treat it as a plain external + // dependency and drop schedule all of its consuming jobs to run. + if (llvm::sys::fs::status(external, depStatus)) { + fallbackToExternalBehavior(external); + continue; + } + + // Is this module out of date? If not, just keep searching. + if (Comp.getLastBuildTime() >= depStatus.getLastModificationTime()) + continue; + + // Can we run a cross-module incremental build at all? + // If not, fall back. + if (!Comp.getEnableCrossModuleIncrementalBuild()) { + fallbackToExternalBehavior(external); + continue; + } + + // If loading the buffer fails, the user may have deleted this external + // dependency or it could have become corrupted. We need to + // pessimistically schedule a rebuild to get dependent jobs to drop + // this dependency from their swiftdeps files if possible. + auto buffer = llvm::MemoryBuffer::getFile(external); + if (!buffer) { + fallbackToExternalBehavior(external); + continue; + } + + // Cons up a fake `Job` to satisfy the incremental job tracing + // code's internal invariants. + const auto *externalJob = Comp.addExternalJob( + std::make_unique(Comp.getDerivedOutputFileMap(), external)); + auto subChanges = + getFineGrainedDepGraph(forRanges).loadFromSwiftModuleBuffer( + externalJob, *buffer.get(), Comp.getDiags()); + + // If the incremental dependency graph failed to load, fall back to + // treating this as plain external job. + if (!subChanges.hasValue()) { + fallbackToExternalBehavior(external); + continue; + } + + for (auto *CMD : + getFineGrainedDepGraph(forRanges) + .findJobsToRecompileWhenNodesChange(subChanges.getValue())) { + if (CMD == externalJob) { + continue; + } + ExternallyDependentJobs.push_back(CMD); + } + } + noteBuildingJobs(ExternallyDependentJobs, forRanges, + "because of incremental external dependencies"); + return ExternallyDependentJobs; + } + /// Insert all jobs in \p Cmds (of descriptive name \p Kind) to the \c /// TaskQueue, and clear \p Cmds. template @@ -1543,8 +1664,8 @@ namespace driver { CompileJobAction::InputInfo info; info.previousModTime = entry.first->getInputModTime(); info.status = entry.second ? - CompileJobAction::InputInfo::NeedsCascadingBuild : - CompileJobAction::InputInfo::NeedsNonCascadingBuild; + CompileJobAction::InputInfo::Status::NeedsCascadingBuild : + CompileJobAction::InputInfo::Status::NeedsNonCascadingBuild; inputs[&inputFile->getInputArg()] = info; } } @@ -1561,7 +1682,7 @@ namespace driver { CompileJobAction::InputInfo info; info.previousModTime = entry->getInputModTime(); - info.status = CompileJobAction::InputInfo::UpToDate; + info.status = CompileJobAction::InputInfo::Status::UpToDate; inputs[&inputFile->getInputArg()] = info; } } @@ -1595,6 +1716,12 @@ namespace driver { return getFineGrainedDepGraph(forRanges).getExternalDependencies(); } + std::vector + getIncrementalExternalDependencies(const bool forRanges) const { + return getFineGrainedDepGraph(forRanges) + .getIncrementalExternalDependencies(); + } + std::vector markExternalInDepGraph(StringRef externalDependency, const bool forRanges) { @@ -1602,6 +1729,13 @@ namespace driver { .findExternallyDependentUntracedJobs(externalDependency); } + std::vector + markIncrementalExternalInDepGraph(StringRef externalDependency, + const bool forRanges) { + return getFineGrainedDepGraph(forRanges) + .findIncrementalExternallyDependentUntracedJobs(externalDependency); + } + std::vector findJobsToRecompileWhenWholeJobChanges( const Job *Cmd, const bool forRanges) { return getFineGrainedDepGraph(forRanges) @@ -1641,6 +1775,12 @@ Job *Compilation::addJob(std::unique_ptr J) { return result; } +Job *Compilation::addExternalJob(std::unique_ptr J) { + Job *result = J.get(); + ExternalJobs.emplace_back(std::move(J)); + return result; +} + static void checkForOutOfDateInputs(DiagnosticEngine &diags, const InputInfoMap &inputs) { for (const auto &inputPair : inputs) { @@ -1815,12 +1955,6 @@ 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(); return State.getResult(); diff --git a/lib/Driver/CompilationRecord.h b/lib/Driver/CompilationRecord.h index ce1215008a058..0fdf38dc72350 100644 --- a/lib/Driver/CompilationRecord.h +++ b/lib/Driver/CompilationRecord.h @@ -59,12 +59,12 @@ inline static StringRef getName(TopLevelKey Key) { inline static StringRef getIdentifierForInputInfoStatus(CompileJobAction::InputInfo::Status Status) { switch (Status) { - case CompileJobAction::InputInfo::UpToDate: + case CompileJobAction::InputInfo::Status::UpToDate: return ""; - case CompileJobAction::InputInfo::NewlyAdded: - case CompileJobAction::InputInfo::NeedsCascadingBuild: + case CompileJobAction::InputInfo::Status::NewlyAdded: + case CompileJobAction::InputInfo::Status::NeedsCascadingBuild: return "!dirty"; - case CompileJobAction::InputInfo::NeedsNonCascadingBuild: + case CompileJobAction::InputInfo::Status::NeedsNonCascadingBuild: return "!private"; } @@ -76,11 +76,11 @@ getIdentifierForInputInfoStatus(CompileJobAction::InputInfo::Status Status) { /// compilation record file (.swiftdeps file). inline static Optional getInfoStatusForIdentifier(StringRef Identifier) { - return llvm::StringSwitch>(Identifier) - .Case("", CompileJobAction::InputInfo::UpToDate) - .Case("!dirty", CompileJobAction::InputInfo::NeedsCascadingBuild) - .Case("!private", CompileJobAction::InputInfo::NeedsNonCascadingBuild) + using InputStatus = CompileJobAction::InputInfo::Status; + return llvm::StringSwitch>(Identifier) + .Case("", InputStatus::UpToDate) + .Case("!dirty", InputStatus::NeedsCascadingBuild) + .Case("!private", InputStatus::NeedsNonCascadingBuild) .Default(None); } diff --git a/lib/Driver/DarwinToolChains.cpp b/lib/Driver/DarwinToolChains.cpp index fb2a9274b7c0b..db3a6793e2d86 100644 --- a/lib/Driver/DarwinToolChains.cpp +++ b/lib/Driver/DarwinToolChains.cpp @@ -364,6 +364,7 @@ bool jobMatchesFilter(LinkKind jobKind, BackDeployLibFilter filter) { case BackDeployLibFilter::all: return true; } + llvm_unreachable("unhandled back deploy lib filter!"); } } @@ -377,19 +378,18 @@ toolchains::Darwin::addArgsToLinkStdlib(ArgStringList &Arguments, // have an older Swift runtime. SmallString<128> SharedResourceDirPath; getResourceDirPath(SharedResourceDirPath, context.Args, /*Shared=*/true); - Optional runtimeCompatibilityVersion; + Optional runtimeCompatibilityVersion + = llvm::VersionTuple(); if (context.Args.hasArg(options::OPT_runtime_compatibility_version)) { auto value = context.Args.getLastArgValue( options::OPT_runtime_compatibility_version); - if (value.equals("5.0")) { - runtimeCompatibilityVersion = llvm::VersionTuple(5, 0); - } else if (value.equals("5.1")) { - runtimeCompatibilityVersion = llvm::VersionTuple(5, 1); - } else if (value.equals("none")) { - runtimeCompatibilityVersion = None; - } else { - // TODO: diagnose unknown runtime compatibility version? + if (runtimeCompatibilityVersion->tryParse(value)) { + if (value.equals("none")) { + runtimeCompatibilityVersion = None; + } else { + // TODO: diagnose unknown runtime compatibility version? + } } } else if (job.getKind() == LinkKind::Executable) { runtimeCompatibilityVersion diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index 85deae853bd49..1e95936514ac3 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -404,10 +404,7 @@ 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) { @@ -430,17 +427,6 @@ static bool getCompilationRecordPath(std::string &buildRecordPath, 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; } @@ -960,8 +946,7 @@ Driver::buildCompilation(const ToolChain &TC, computeIncremental(ArgList.get(), ShowIncrementalBuildDecisions); std::string buildRecordPath; - bool outputBuildRecordForModuleOnlyBuild = false; - getCompilationRecordPath(buildRecordPath, outputBuildRecordForModuleOnlyBuild, + getCompilationRecordPath(buildRecordPath, OI, OFM, Incremental ? &Diags : nullptr); SmallString<32> ArgsHash; @@ -1029,19 +1014,14 @@ Driver::buildCompilation(const ToolChain &TC, ArgList->hasFlag(options::OPT_enable_only_one_dependency_file, options::OPT_disable_only_one_dependency_file, false); - const bool EnableTypeFingerprints = - ArgList->hasFlag(options::OPT_enable_type_fingerprints, - options::OPT_disable_type_fingerprints, - LangOptions().EnableTypeFingerprints); - const bool VerifyFineGrainedDependencyGraphAfterEveryImport = ArgList->hasArg( options:: OPT_driver_verify_fine_grained_dependency_graph_after_every_import); const bool EmitFineGrainedDependencyDotFileAfterEveryImport = ArgList->hasArg( options:: OPT_driver_emit_fine_grained_dependency_dot_file_after_every_import); - const bool FineGrainedDependenciesIncludeIntrafileOnes = - ArgList->hasArg(options::OPT_fine_grained_dependency_include_intrafile); + const bool EnableCrossModuleDependencies = ArgList->hasArg( + options::OPT_enable_experimental_cross_module_incremental_build); // clang-format off C = std::make_unique( @@ -1050,7 +1030,6 @@ Driver::buildCompilation(const ToolChain &TC, std::move(TranslatedArgList), std::move(Inputs), buildRecordPath, - outputBuildRecordForModuleOnlyBuild, ArgsHash, StartTime, LastBuildTime, @@ -1064,13 +1043,12 @@ Driver::buildCompilation(const ToolChain &TC, ShowDriverTimeCompilation, std::move(StatsReporter), OnlyOneDependencyFile, - EnableTypeFingerprints, VerifyFineGrainedDependencyGraphAfterEveryImport, EmitFineGrainedDependencyDotFileAfterEveryImport, - FineGrainedDependenciesIncludeIntrafileOnes, EnableSourceRangeDependencies, CompareIncrementalSchemes, - CompareIncrementalSchemesPath); + CompareIncrementalSchemesPath, + EnableCrossModuleDependencies); // clang-format on } @@ -1898,6 +1876,54 @@ Driver::computeCompilerMode(const DerivedArgList &Args, return OutputInfo::Mode::StandardCompile; } +namespace { +/// Encapsulates the computation of input jobs that are relevant to the +/// merge-modules job the scheduler can insert if we are not in a single compile +/// mode. +class ModuleInputs final { +private: + using InputInfo = IncrementalJobAction::InputInfo; + SmallVector AllModuleInputs; + InputInfo StatusBound; + +public: + explicit ModuleInputs() + : StatusBound + {InputInfo::Status::UpToDate, llvm::sys::TimePoint<>::min()} {} + +public: + void addInput(const Action *inputAction) { + if (auto *IJA = dyn_cast(inputAction)) { + // Take the upper bound of the status of any incremental inputs to + // ensure that the merge-modules job gets run if *any* input job is run. + const auto conservativeStatus = + std::max(StatusBound.status, IJA->getInputInfo().status); + // The modification time here is not important to the rest of the + // incremental build. We take the upper bound in case an attempt to + // compare the swiftmodule output's mod time and any input files is + // made. If the compilation has been correctly scheduled, the + // swiftmodule's mod time will always strictly exceed the mod time of + // any of its inputs when we are able to skip it. + const auto conservativeModTime = std::max( + StatusBound.previousModTime, IJA->getInputInfo().previousModTime); + StatusBound = InputInfo{conservativeStatus, conservativeModTime}; + } + AllModuleInputs.push_back(inputAction); + } + +public: + /// Returns \c true if no inputs have been registered with this instance. + bool empty() const { return AllModuleInputs.empty(); } + +public: + /// Consumes this \c ModuleInputs instance and returns a merge-modules action + /// from the list of input actions and status it has computed thus far. + JobAction *intoAction(Compilation &C) && { + return C.createAction(AllModuleInputs, StatusBound); + } +}; +} // namespace + void Driver::buildActions(SmallVectorImpl &TopLevelActions, const ToolChain &TC, const OutputInfo &OI, const InputInfoMap *OutOfDateMap, @@ -1910,7 +1936,7 @@ void Driver::buildActions(SmallVectorImpl &TopLevelActions, return; } - SmallVector AllModuleInputs; + ModuleInputs AllModuleInputs; SmallVector AllLinkerInputs; switch (OI.CompilerMode) { @@ -1951,10 +1977,8 @@ void Driver::buildActions(SmallVectorImpl &TopLevelActions, // Source inputs always need to be compiled. assert(file_types::isPartOfSwiftCompilation(InputType)); - CompileJobAction::InputInfo previousBuildState = { - CompileJobAction::InputInfo::NeedsCascadingBuild, - llvm::sys::TimePoint<>::min() - }; + auto previousBuildState = + IncrementalJobAction::InputInfo::makeNeedsCascadingRebuild(); if (OutOfDateMap) previousBuildState = OutOfDateMap->lookup(InputArg); if (Args.hasArg(options::OPT_embed_bitcode)) { @@ -1962,7 +1986,7 @@ void Driver::buildActions(SmallVectorImpl &TopLevelActions, Current, file_types::TY_LLVM_BC, previousBuildState); if (PCH) cast(Current)->addInput(PCH); - AllModuleInputs.push_back(Current); + AllModuleInputs.addInput(Current); Current = C.createAction(Current, OI.CompilerOutputType, 0); } else { @@ -1971,7 +1995,7 @@ void Driver::buildActions(SmallVectorImpl &TopLevelActions, previousBuildState); if (PCH) cast(Current)->addInput(PCH); - AllModuleInputs.push_back(Current); + AllModuleInputs.addInput(Current); } AllLinkerInputs.push_back(Current); break; @@ -1983,7 +2007,7 @@ void Driver::buildActions(SmallVectorImpl &TopLevelActions, // When generating a .swiftmodule as a top-level output (as opposed // to, for example, linking an image), treat .swiftmodule files as // inputs to a MergeModule action. - AllModuleInputs.push_back(Current); + AllModuleInputs.addInput(Current); break; } else if (OI.shouldLink()) { // Otherwise, if linking, pass .swiftmodule files as inputs to the @@ -2065,7 +2089,7 @@ void Driver::buildActions(SmallVectorImpl &TopLevelActions, // Create a single CompileJobAction and a single BackendJobAction. JobAction *CA = C.createAction(file_types::TY_LLVM_BC); - AllModuleInputs.push_back(CA); + AllModuleInputs.addInput(CA); int InputIndex = 0; for (const InputPair &Input : Inputs) { @@ -2101,7 +2125,7 @@ void Driver::buildActions(SmallVectorImpl &TopLevelActions, CA->addInput(C.createAction(*InputArg, InputType)); } - AllModuleInputs.push_back(CA); + AllModuleInputs.addInput(CA); AllLinkerInputs.push_back(CA); break; } @@ -2150,7 +2174,7 @@ void Driver::buildActions(SmallVectorImpl &TopLevelActions, !AllModuleInputs.empty()) { // We're performing multiple compilations; set up a merge module step // so we generate a single swiftmodule as output. - MergeModuleAction = C.createAction(AllModuleInputs); + MergeModuleAction = std::move(AllModuleInputs).intoAction(C); } bool shouldPerformLTO = OI.LTOVariant != OutputInfo::LTOKind::None; @@ -2734,42 +2758,49 @@ static void addDiagFileOutputForPersistentPCHAction( /// If the file at \p input has not been modified since the last build (i.e. its /// mtime has not changed), adjust the Job's condition accordingly. -static void -handleCompileJobCondition(Job *J, CompileJobAction::InputInfo inputInfo, - StringRef input, bool alwaysRebuildDependents) { - if (inputInfo.status == CompileJobAction::InputInfo::NewlyAdded) { +static void handleCompileJobCondition(Job *J, + CompileJobAction::InputInfo inputInfo, + Optional input, + bool alwaysRebuildDependents) { + using InputStatus = CompileJobAction::InputInfo::Status; + + if (inputInfo.status == InputStatus::NewlyAdded) { J->setCondition(Job::Condition::NewlyAdded); return; } + auto output = J->getOutput().getPrimaryOutputFilename(); bool hasValidModTime = false; llvm::sys::fs::file_status inputStatus; - if (!llvm::sys::fs::status(input, inputStatus)) { + if (input.hasValue() && !llvm::sys::fs::status(*input, inputStatus)) { + J->setInputModTime(inputStatus.getLastModificationTime()); + hasValidModTime = J->getInputModTime() == inputInfo.previousModTime; + } else if (!llvm::sys::fs::status(output, inputStatus)) { J->setInputModTime(inputStatus.getLastModificationTime()); hasValidModTime = true; } Job::Condition condition; - if (hasValidModTime && J->getInputModTime() == inputInfo.previousModTime) { + if (hasValidModTime) { switch (inputInfo.status) { - case CompileJobAction::InputInfo::UpToDate: - if (llvm::sys::fs::exists(J->getOutput().getPrimaryOutputFilename())) + case InputStatus::UpToDate: + if (llvm::sys::fs::exists(output)) condition = Job::Condition::CheckDependencies; else condition = Job::Condition::RunWithoutCascading; break; - case CompileJobAction::InputInfo::NeedsCascadingBuild: + case InputStatus::NeedsCascadingBuild: condition = Job::Condition::Always; break; - case CompileJobAction::InputInfo::NeedsNonCascadingBuild: + case InputStatus::NeedsNonCascadingBuild: condition = Job::Condition::RunWithoutCascading; break; - case CompileJobAction::InputInfo::NewlyAdded: + case InputStatus::NewlyAdded: llvm_unreachable("handled above"); } } else { if (alwaysRebuildDependents || - inputInfo.status == CompileJobAction::InputInfo::NeedsCascadingBuild) { + inputInfo.status == InputStatus::NeedsCascadingBuild) { condition = Job::Condition::Always; } else { condition = Job::Condition::RunWithoutCascading; @@ -2926,14 +2957,18 @@ Job *Driver::buildJobsForAction(Compilation &C, const JobAction *JA, Job *J = C.addJob(std::move(ownedJob)); // If we track dependencies for this job, we may be able to avoid running it. - if (!J->getOutput() - .getAdditionalOutputForType(file_types::TY_SwiftDeps) - .empty()) { - if (InputActions.size() == 1) { - auto compileJob = cast(JA); - bool alwaysRebuildDependents = - C.getArgs().hasArg(options::OPT_driver_always_rebuild_dependents); - handleCompileJobCondition(J, compileJob->getInputInfo(), BaseInput, + if (auto incrementalJob = dyn_cast(JA)) { + const bool alwaysRebuildDependents = + C.getArgs().hasArg(options::OPT_driver_always_rebuild_dependents); + if (!J->getOutput() + .getAdditionalOutputForType(file_types::TY_SwiftDeps) + .empty()) { + if (InputActions.size() == 1) { + handleCompileJobCondition(J, incrementalJob->getInputInfo(), BaseInput, + alwaysRebuildDependents); + } + } else if (isa(JA)) { + handleCompileJobCondition(J, incrementalJob->getInputInfo(), None, alwaysRebuildDependents); } } diff --git a/lib/Driver/FineGrainedDependencyDriverGraph.cpp b/lib/Driver/FineGrainedDependencyDriverGraph.cpp index 6111b3690a7e1..d7cf58be0cb8e 100644 --- a/lib/Driver/FineGrainedDependencyDriverGraph.cpp +++ b/lib/Driver/FineGrainedDependencyDriverGraph.cpp @@ -108,6 +108,27 @@ ModuleDepGraph::Changes ModuleDepGraph::loadFromSourceFileDepGraph( return changes; } +ModuleDepGraph::Changes ModuleDepGraph::loadFromSwiftModuleBuffer( + const Job *Cmd, llvm::MemoryBuffer &buffer, DiagnosticEngine &diags) { + FrontendStatsTracer tracer( + stats, "fine-grained-dependencies-loadFromSwiftModuleBuffer"); + PrettyStackTraceStringAction stackTrace( + "loading fine-grained dependency graph from swiftmodule", + buffer.getBufferIdentifier()); + + Optional sourceFileDepGraph = + SourceFileDepGraph::loadFromSwiftModuleBuffer(buffer); + if (!sourceFileDepGraph) + return None; + jobsBySwiftDeps[buffer.getBufferIdentifier().str()] = Cmd; + auto changes = integrate(*sourceFileDepGraph, buffer.getBufferIdentifier()); + if (verifyFineGrainedDependencyGraphAfterEveryImport) + verify(); + if (emitFineGrainedDependencyDotFileAfterEveryImport) + emitDotFileForJob(diags, Cmd); + return changes; +} + bool ModuleDepGraph::haveAnyNodesBeenTraversedIn(const Job *cmd) const { std::string swiftDeps = getSwiftDeps(cmd).str(); @@ -195,6 +216,12 @@ std::vector ModuleDepGraph::getExternalDependencies() const { externalDependencies.end()); } +std::vector +ModuleDepGraph::getIncrementalExternalDependencies() const { + return std::vector(incrementalExternalDependencies.begin(), + incrementalExternalDependencies.end()); +} + // Add every (swiftdeps) use of the external dependency to foundJobs. // Can return duplicates, but it doesn't break anything, and they will be // canonicalized later. @@ -216,6 +243,26 @@ std::vector ModuleDepGraph::findExternallyDependentUntracedJobs( return foundJobs; } +std::vector +ModuleDepGraph::findIncrementalExternallyDependentUntracedJobs( + StringRef externalDependency) { + FrontendStatsTracer tracer(stats, + "fine-grained-dependencies-" + "findIncrementalExternallyDependentUntracedJobs"); + std::vector foundJobs; + forEachUntracedJobDirectlyDependentOnExternalIncrementalSwiftDeps( + externalDependency, [&](const Job *job) { + foundJobs.push_back(job); + for (const Job *marked : findJobsToRecompileWhenWholeJobChanges(job)) { + // findJobsToRecompileWhenWholeJobChanges is reflexive + // Don't return job twice. + if (marked != job) + foundJobs.push_back(marked); + } + }); + return foundJobs; +} + void ModuleDepGraph::forEachUntracedJobDirectlyDependentOnExternalSwiftDeps( StringRef externalSwiftDeps, function_ref fn) { // TODO move nameForDep into key @@ -228,6 +275,19 @@ void ModuleDepGraph::forEachUntracedJobDirectlyDependentOnExternalSwiftDeps( } } +void ModuleDepGraph:: + forEachUntracedJobDirectlyDependentOnExternalIncrementalSwiftDeps( + StringRef externalSwiftDeps, function_ref fn) { + // TODO move nameForDep into key + // These nodes will depend on the *interface* of the external Decl. + DependencyKey key(NodeKind::incrementalExternalDepend, DeclAspect::interface, + "", externalSwiftDeps.str()); + for (const ModuleDepGraphNode *useNode : usesByDef[key]) { + if (!useNode->getHasBeenTraced()) + fn(getJob(useNode->getSwiftDepsOfProvides())); + } +} + //============================================================================== // MARK: Integrating SourceFileDepGraph into ModuleDepGraph //============================================================================== @@ -309,12 +369,6 @@ ModuleDepGraph::integrateSourceFileDepGraphNode( const SourceFileDepGraph &g, const SourceFileDepGraphNode *integrand, const PreexistingNodeIfAny preexistingMatch, const StringRef swiftDepsOfJob) { - - if (!EnableTypeFingerprints && - integrand->getKey().getKind() != NodeKind::sourceFileProvide && - integrand->getFingerprint()) - return None; - if (!integrand->getIsProvides()) return NullablePtr(); // depends are captured by // recordWhatUseDependsUpon below @@ -387,10 +441,15 @@ bool ModuleDepGraph::recordWhatUseDependsUpon( sourceFileUseNode, [&](const SourceFileDepGraphNode *def) { const bool isNewUse = usesByDef[def->getKey()].insert(moduleUseNode).second; - if (isNewUse && def->getKey().getKind() == NodeKind::externalDepend) { + if (isNewUse) { StringRef externalSwiftDeps = def->getKey().getName(); - externalDependencies.insert(externalSwiftDeps.str()); - useHasNewExternalDependency = true; + if (def->getKey().getKind() == NodeKind::externalDepend) { + externalDependencies.insert(externalSwiftDeps.str()); + useHasNewExternalDependency = true; + } else if (def->getKey().getKind() == + NodeKind::incrementalExternalDepend) { + incrementalExternalDependencies.insert(externalSwiftDeps.str()); + } } }); return useHasNewExternalDependency; @@ -641,6 +700,9 @@ void ModuleDepGraph::verifyExternalDependencyUniqueness( assert((key.getKind() != NodeKind::externalDepend || externalDependencies.count(key.getName().str()) == 1) && "Ensure each external dependency is tracked exactly once"); + assert((key.getKind() != NodeKind::incrementalExternalDepend || + incrementalExternalDependencies.count(key.getName().str()) == 1) && + "Ensure each incremental external dependency is tracked exactly once"); } void ModuleDepGraph::verifyCanFindEachJob() const { @@ -687,9 +749,14 @@ void ModuleDepGraph::printPath(raw_ostream &out, } StringRef ModuleDepGraph::getProvidingFilename( - const Optional swiftDeps) const { + const Optional &swiftDeps) const { if (!swiftDeps) return "getFirstSwiftPrimaryInput()); // FineGrainedDependencyGraphTests work with simulated jobs with empty @@ -725,6 +792,10 @@ void ModuleDepGraph::printOneNodeOfPath(raw_ostream &out, out << filename << " depends on " << key.aspectName() << " of module '" << key.humanReadableName() << "'"; break; + case NodeKind::incrementalExternalDepend: + out << filename << " depends on " << key.aspectName() + << " of incremental module '" << key.humanReadableName() << "'"; + break; case NodeKind::sourceFileProvide: out << key.aspectName() << " of source file " << key.humanReadableName(); break; diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index c8e8d51b5d56a..3be82ed413aaf 100644 --- a/lib/Driver/ToolChains.cpp +++ b/lib/Driver/ToolChains.cpp @@ -259,10 +259,6 @@ void ToolChain::addCommonFrontendArgs(const OutputInfo &OI, inputArgs.AddLastArg(arguments, options::OPT_O_Group); inputArgs.AddLastArg(arguments, options::OPT_RemoveRuntimeAsserts); inputArgs.AddLastArg(arguments, options::OPT_AssumeSingleThreaded); - inputArgs.AddLastArg(arguments, options::OPT_enable_type_fingerprints); - inputArgs.AddLastArg(arguments, options::OPT_disable_type_fingerprints); - inputArgs.AddLastArg(arguments, - options::OPT_fine_grained_dependency_include_intrafile); inputArgs.AddLastArg(arguments, options::OPT_emit_fine_grained_dependency_sourcefile_dot_files); inputArgs.AddLastArg(arguments, options::OPT_package_description_version); @@ -273,6 +269,7 @@ void ToolChain::addCommonFrontendArgs(const OutputInfo &OI, inputArgs.AddLastArg(arguments, options::OPT_print_educational_notes); inputArgs.AddLastArg(arguments, options::OPT_diagnostic_style); inputArgs.AddLastArg(arguments, options::OPT_disable_parser_lookup); + inputArgs.AddLastArg(arguments, options::OPT_enable_parser_lookup); inputArgs.AddLastArg(arguments, options::OPT_enable_experimental_concise_pound_file); inputArgs.AddLastArg( @@ -1052,6 +1049,10 @@ ToolChain::constructInvocation(const MergeModuleJobAction &job, context.Args.AddLastArg(Arguments, options::OPT_import_objc_header); + context.Args.AddLastArg( + Arguments, + options::OPT_enable_experimental_cross_module_incremental_build); + Arguments.push_back("-module-name"); Arguments.push_back(context.Args.MakeArgString(context.OI.ModuleName)); diff --git a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp index 4ef0b90131e8e..43c131981211a 100644 --- a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp +++ b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp @@ -92,6 +92,9 @@ bool ArgsToFrontendOptionsConverter::convert( Opts.ImportPrescan |= Args.hasArg(OPT_import_prescan); + Opts.EnableExperimentalCrossModuleIncrementalBuild |= + Args.hasArg(OPT_enable_experimental_cross_module_incremental_build); + // Always track system dependencies when scanning dependencies. if (const Arg *ModeArg = Args.getLastArg(OPT_modes_Group)) { if (ModeArg->getOption().matches(OPT_scan_dependencies)) { @@ -209,9 +212,11 @@ bool ArgsToFrontendOptionsConverter::convert( Opts.ImportUnderlyingModule |= Args.hasArg(OPT_import_underlying_module); Opts.EnableIncrementalDependencyVerifier |= Args.hasArg(OPT_verify_incremental_dependencies); Opts.UseSharedResourceFolder = !Args.hasArg(OPT_use_static_resource_dir); + Opts.DisableBuildingInterface = Args.hasArg(OPT_disable_building_interface); computeImportObjCHeaderOptions(); - computeImplicitImportModuleNames(); + computeImplicitImportModuleNames(OPT_import_module, /*isTestable=*/false); + computeImplicitImportModuleNames(OPT_testable_import_module, /*isTestable=*/true); computeLLVMArgs(); return false; @@ -582,16 +587,17 @@ void ArgsToFrontendOptionsConverter::computeImportObjCHeaderOptions() { Opts.SerializeBridgingHeader |= !Opts.InputsAndOutputs.hasPrimaryInputs(); } } -void ArgsToFrontendOptionsConverter::computeImplicitImportModuleNames() { +void ArgsToFrontendOptionsConverter:: +computeImplicitImportModuleNames(OptSpecifier id, bool isTestable) { using namespace options; - for (const Arg *A : Args.filtered(OPT_import_module)) { + for (const Arg *A : Args.filtered(id)) { auto *moduleStr = A->getValue(); if (!Lexer::isIdentifier(moduleStr)) { Diags.diagnose(SourceLoc(), diag::error_bad_module_name, moduleStr, /*suggestModuleNameFlag*/ false); continue; } - Opts.ImplicitImportModuleNames.push_back(moduleStr); + Opts.ImplicitImportModuleNames.emplace_back(moduleStr, isTestable); } } void ArgsToFrontendOptionsConverter::computeLLVMArgs() { diff --git a/lib/Frontend/ArgsToFrontendOptionsConverter.h b/lib/Frontend/ArgsToFrontendOptionsConverter.h index 6e20e3688a8fa..66d85512ff068 100644 --- a/lib/Frontend/ArgsToFrontendOptionsConverter.h +++ b/lib/Frontend/ArgsToFrontendOptionsConverter.h @@ -40,7 +40,8 @@ class ArgsToFrontendOptionsConverter { bool computeMainAndSupplementaryOutputFilenames(); void computeDumpScopeMapLocations(); void computeHelpOptions(); - void computeImplicitImportModuleNames(); + void computeImplicitImportModuleNames(llvm::opt::OptSpecifier id, + bool isTestable); void computeImportObjCHeaderOptions(); void computeLLVMArgs(); void computePlaygroundOptions(); diff --git a/lib/Frontend/CMakeLists.txt b/lib/Frontend/CMakeLists.txt index 08988f9a078ee..2c8c24f9e4dd9 100644 --- a/lib/Frontend/CMakeLists.txt +++ b/lib/Frontend/CMakeLists.txt @@ -1,3 +1,4 @@ +set_swift_llvm_is_available() add_swift_host_library(swiftFrontend STATIC ArgsToFrontendInputsConverter.cpp ArgsToFrontendOptionsConverter.cpp diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index c4f1c698ac7bd..aacb59c859485 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -425,7 +425,9 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, = A->getOption().matches(OPT_enable_target_os_checking); } - Opts.DisableParserLookup |= Args.hasArg(OPT_disable_parser_lookup); + Opts.DisableParserLookup |= Args.hasFlag(OPT_disable_parser_lookup, + OPT_enable_parser_lookup, + /*default*/ true); Opts.EnableNewOperatorLookup = Args.hasFlag(OPT_enable_new_operator_lookup, OPT_disable_new_operator_lookup, /*default*/ false); @@ -438,17 +440,9 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, Opts.VerifySyntaxTree = true; } - Opts.EnableTypeFingerprints = - Args.hasFlag(options::OPT_enable_type_fingerprints, - options::OPT_disable_type_fingerprints, - LangOptions().EnableTypeFingerprints); - if (Args.hasArg(OPT_emit_fine_grained_dependency_sourcefile_dot_files)) Opts.EmitFineGrainedDependencySourcefileDotFiles = true; - if (Args.hasArg(OPT_fine_grained_dependency_include_intrafile)) - Opts.FineGrainedDependenciesIncludeIntrafileOnes = true; - if (Args.hasArg(OPT_enable_experimental_additive_arithmetic_derivation)) Opts.EnableExperimentalAdditiveArithmeticDerivedConformances = true; @@ -504,6 +498,10 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, } } + for (const Arg *A : Args.filtered(OPT_define_availability)) { + Opts.AvailabilityMacros.push_back(A->getValue()); + } + if (const Arg *A = Args.getLastArg(OPT_value_recursion_threshold)) { unsigned threshold; if (StringRef(A->getValue()).getAsInteger(10, threshold)) { @@ -744,8 +742,6 @@ static bool ParseTypeCheckerArgs(TypeCheckerOptions &Opts, ArgList &Args, // Always enable operator designated types for the standard library. Opts.EnableOperatorDesignatedTypes |= FrontendOpts.ParseStdlib; - Opts.SolverEnableOperatorDesignatedTypes |= - Args.hasArg(OPT_solver_enable_operator_designated_types); Opts.EnableOneWayClosureParameters |= Args.hasArg(OPT_experimental_one_way_closure_params); @@ -1602,6 +1598,8 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args, runtimeCompatibilityVersion = llvm::VersionTuple(5, 0); } else if (version.equals("5.1")) { runtimeCompatibilityVersion = llvm::VersionTuple(5, 1); + } else if (version.equals("5.3")) { + runtimeCompatibilityVersion = llvm::VersionTuple(5, 3); } else { Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value, versionArg->getAsString(Args), version); diff --git a/lib/Frontend/Frontend.cpp b/lib/Frontend/Frontend.cpp index 798f1538a4bb1..c1ec9198ff0c9 100644 --- a/lib/Frontend/Frontend.cpp +++ b/lib/Frontend/Frontend.cpp @@ -175,6 +175,9 @@ SerializationOptions CompilerInvocation::computeSerializationOptions( opts.SerializeOptionsForDebugging.getValueOr( !isModuleExternallyConsumed(module)); + serializationOpts.ExperimentalCrossModuleIncrementalInfo = + opts.EnableExperimentalCrossModuleIncrementalBuild; + return serializationOpts; } @@ -490,6 +493,14 @@ bool CompilerInstance::setUpModuleLoaders() { return true; } + // Configure ModuleInterfaceChecker for the ASTContext. + auto const &Clang = clangImporter->getClangInstance(); + std::string ModuleCachePath = getModuleCachePathFromClang(Clang); + auto &FEOpts = Invocation.getFrontendOptions(); + ModuleInterfaceLoaderOptions LoaderOpts(FEOpts); + Context->addModuleInterfaceChecker( + std::make_unique(*Context, ModuleCachePath, + FEOpts.PrebuiltModuleCachePath, LoaderOpts)); // If implicit modules are disabled, we need to install an explicit module // loader. bool ExplicitModuleBuild = Invocation.getFrontendOptions().DisableImplicitModules; @@ -502,23 +513,15 @@ bool CompilerInstance::setUpModuleLoaders() { IgnoreSourceInfoFile); this->DefaultSerializedLoader = ESML.get(); Context->addModuleLoader(std::move(ESML)); - } - - if (MLM != ModuleLoadingMode::OnlySerialized) { - auto const &Clang = clangImporter->getClangInstance(); - std::string ModuleCachePath = getModuleCachePathFromClang(Clang); - auto &FEOpts = Invocation.getFrontendOptions(); - StringRef PrebuiltModuleCachePath = FEOpts.PrebuiltModuleCachePath; - ModuleInterfaceLoaderOptions LoaderOpts(FEOpts); - auto PIML = ModuleInterfaceLoader::create( - *Context, ModuleCachePath, PrebuiltModuleCachePath, - getDependencyTracker(), MLM, FEOpts.PreferInterfaceForModules, - LoaderOpts, - IgnoreSourceInfoFile); - Context->addModuleLoader(std::move(PIML), false, false, true); - } - - if (!ExplicitModuleBuild) { + } else { + if (MLM != ModuleLoadingMode::OnlySerialized) { + // We only need ModuleInterfaceLoader for implicit modules. + auto PIML = ModuleInterfaceLoader::create( + *Context, *static_cast(Context + ->getModuleInterfaceChecker()), getDependencyTracker(), MLM, + FEOpts.PreferInterfaceForModules, IgnoreSourceInfoFile); + Context->addModuleLoader(std::move(PIML), false, false, true); + } std::unique_ptr ISML = ImplicitSerializedModuleLoader::create(*Context, getDependencyTracker(), MLM, IgnoreSourceInfoFile); @@ -704,6 +707,10 @@ CompilerInstance::openModuleDoc(const InputFile &input) { return None; } +bool CompilerInvocation::shouldImportSwiftConcurrency() const { + return getLangOptions().EnableExperimentalConcurrency; +} + /// Implicitly import the SwiftOnoneSupport module in non-optimized /// builds. This allows for use of popular specialized functions /// from the standard library, which makes the non-optimized builds @@ -737,11 +744,27 @@ ImplicitImportInfo CompilerInstance::getImplicitImportInfo() const { ImplicitImportInfo imports; imports.StdlibKind = Invocation.getImplicitStdlibKind(); - for (auto &moduleStr : frontendOpts.getImplicitImportModuleNames()) - imports.ModuleNames.push_back(Context->getIdentifier(moduleStr)); + auto pushImport = [&](StringRef moduleStr, + ImportOptions options = ImportOptions()) { + ImportPath::Builder importPath(Context->getIdentifier(moduleStr)); + UnloadedImportedModule import(importPath.copyTo(*Context), + /*isScoped=*/false); + imports.AdditionalUnloadedImports.emplace_back(import, options); + }; - if (Invocation.shouldImportSwiftONoneSupport()) - imports.ModuleNames.push_back(Context->getIdentifier(SWIFT_ONONE_SUPPORT)); + for (auto &moduleStrAndTestable : frontendOpts.getImplicitImportModuleNames()) { + pushImport(moduleStrAndTestable.first, + moduleStrAndTestable.second ? ImportFlags::Testable + : ImportOptions()); + } + + if (Invocation.shouldImportSwiftONoneSupport()) { + pushImport(SWIFT_ONONE_SUPPORT); + } + + if (Invocation.shouldImportSwiftConcurrency()) { + pushImport(SWIFT_CONCURRENCY_NAME); + } imports.ShouldImportUnderlyingModule = frontendOpts.ImportUnderlyingModule; imports.BridgingHeaderPath = frontendOpts.ImplicitObjCHeaderPath; diff --git a/lib/Frontend/ModuleInterfaceBuilder.cpp b/lib/Frontend/ModuleInterfaceBuilder.cpp index 70259522380ea..ea0b21cf6e7a4 100644 --- a/lib/Frontend/ModuleInterfaceBuilder.cpp +++ b/lib/Frontend/ModuleInterfaceBuilder.cpp @@ -153,6 +153,7 @@ bool ModuleInterfaceBuilder::buildSwiftModuleInternal( auto outerPrettyStackState = llvm::SavePrettyStackState(); bool SubError = false; + static const size_t ThreadStackSize = 8 << 20; // 8 MB. bool RunSuccess = llvm::CrashRecoveryContext().RunSafelyOnThread([&] { // Pretend we're on the original thread for pretty-stack-trace purposes. auto savedInnerPrettyStackState = llvm::SavePrettyStackState(); @@ -169,10 +170,9 @@ bool ModuleInterfaceBuilder::buildSwiftModuleInternal( auto &SubInstance = *info.Instance; auto subInvocation = SubInstance.getInvocation(); // Try building forwarding module first. If succeed, return. - if (static_cast(SubInstance.getASTContext() - .getModuleInterfaceLoader())->tryEmitForwardingModule(moduleName, - interfacePath, - CompiledCandidates, OutPath)) { + if (SubInstance.getASTContext().getModuleInterfaceChecker() + ->tryEmitForwardingModule(moduleName, interfacePath, + CompiledCandidates, OutPath)) { return std::error_code(); } FrontendOptions &FEOpts = subInvocation.getFrontendOptions(); @@ -265,7 +265,7 @@ bool ModuleInterfaceBuilder::buildSwiftModuleInternal( } return std::error_code(); }); - }); + }, ThreadStackSize); return !RunSuccess || SubError; } @@ -297,7 +297,7 @@ bool ModuleInterfaceBuilder::buildSwiftModule(StringRef OutPath, // necessary for performance. Fallback to building the module in case of any lock // related errors. if (RemarkRebuild) { - diagnose(diag::interface_file_lock_failure, interfacePath); + diagnose(diag::interface_file_lock_failure); } // Clear out any potential leftover. Locked.unsafeRemoveLockFile(); diff --git a/lib/Frontend/ModuleInterfaceLoader.cpp b/lib/Frontend/ModuleInterfaceLoader.cpp index e647e526a88f1..408e588b70a51 100644 --- a/lib/Frontend/ModuleInterfaceLoader.cpp +++ b/lib/Frontend/ModuleInterfaceLoader.cpp @@ -335,6 +335,7 @@ struct ModuleRebuildInfo { /// a module that we'll build from a module interface. class ModuleInterfaceLoaderImpl { friend class swift::ModuleInterfaceLoader; + friend class swift::ModuleInterfaceCheckerImpl; ASTContext &ctx; llvm::vfs::FileSystem &fs; DiagnosticEngine &diags; @@ -858,13 +859,6 @@ class ModuleInterfaceLoaderImpl { prebuiltCacheDir, /*serializeDependencyHashes*/false, trackSystemDependencies); - // Set up a builder if we need to build the module. It'll also set up - // the genericSubInvocation we'll need to use to compute the cache paths. - ModuleInterfaceBuilder builder( - ctx.SourceMgr, ctx.Diags, astDelegate, interfacePath, moduleName, cacheDir, - prebuiltCacheDir, - Opts.disableInterfaceLock, diagnosticLoc, - dependencyTracker); // Compute the output path if we're loading or emitting a cached module. llvm::SmallString<256> cachedOutputPath; @@ -907,11 +901,19 @@ class ModuleInterfaceLoaderImpl { return std::move(module.moduleBuffer); } - // If implicit module is disabled, we are done. - if (Opts.disableImplicitSwiftModule) { + // If building from interface is disabled, return error. + if (Opts.disableBuildingInterface) { return std::make_error_code(std::errc::not_supported); } + // Set up a builder if we need to build the module. It'll also set up + // the genericSubInvocation we'll need to use to compute the cache paths. + ModuleInterfaceBuilder builder( + ctx.SourceMgr, ctx.Diags, astDelegate, interfacePath, moduleName, cacheDir, + prebuiltCacheDir, + Opts.disableInterfaceLock, diagnosticLoc, + dependencyTracker); + std::unique_ptr moduleBuffer; // We didn't discover a module corresponding to this interface. @@ -942,12 +944,16 @@ class ModuleInterfaceLoaderImpl { } // end anonymous namespace -bool ModuleInterfaceLoader::isCached(StringRef DepPath) { +bool ModuleInterfaceCheckerImpl::isCached(StringRef DepPath) { if (!CacheDir.empty() && DepPath.startswith(CacheDir)) return true; return !PrebuiltCacheDir.empty() && DepPath.startswith(PrebuiltCacheDir); } +bool ModuleInterfaceLoader::isCached(StringRef DepPath) { + return InterfaceChecker.isCached(DepPath); +} + /// Load a .swiftmodule associated with a .swiftinterface either from a /// cache or by converting it in a subordinate \c CompilerInstance, caching /// the results. @@ -990,8 +996,8 @@ std::error_code ModuleInterfaceLoader::findModuleFilesInDirectory( auto ModuleName = ModuleID.Item.str(); ModuleInterfaceLoaderImpl Impl( Ctx, ModPath, InPath, ModuleName, - CacheDir, PrebuiltCacheDir, ModuleID.Loc, - Opts, + InterfaceChecker.CacheDir, InterfaceChecker.PrebuiltCacheDir, + ModuleID.Loc, InterfaceChecker.Opts, dependencyTracker, llvm::is_contained(PreferInterfaceForModules, ModuleName) ? @@ -1024,8 +1030,8 @@ std::error_code ModuleInterfaceLoader::findModuleFilesInDirectory( } std::vector -ModuleInterfaceLoader::getCompiledModuleCandidatesForInterface(StringRef moduleName, - StringRef interfacePath) { +ModuleInterfaceCheckerImpl::getCompiledModuleCandidatesForInterface( + StringRef moduleName, StringRef interfacePath) { // Derive .swiftmodule path from the .swiftinterface path. auto newExt = file_types::getExtension(file_types::TY_SwiftModuleFile); llvm::SmallString<32> modulePath = interfacePath; @@ -1034,9 +1040,8 @@ ModuleInterfaceLoader::getCompiledModuleCandidatesForInterface(StringRef moduleN Ctx, modulePath, interfacePath, moduleName, CacheDir, PrebuiltCacheDir, SourceLoc(), Opts, - dependencyTracker, - llvm::is_contained(PreferInterfaceForModules, moduleName) ? - ModuleLoadingMode::PreferInterface : LoadMode); + nullptr, + ModuleLoadingMode::PreferSerialized); std::vector results; auto pair = Impl.getCompiledModuleCandidates(); // Add compiled module candidates only when they are non-empty. @@ -1047,7 +1052,7 @@ ModuleInterfaceLoader::getCompiledModuleCandidatesForInterface(StringRef moduleN return results; } -bool ModuleInterfaceLoader::tryEmitForwardingModule(StringRef moduleName, +bool ModuleInterfaceCheckerImpl::tryEmitForwardingModule(StringRef moduleName, StringRef interfacePath, ArrayRef candidates, StringRef outputPath) { @@ -1059,9 +1064,8 @@ bool ModuleInterfaceLoader::tryEmitForwardingModule(StringRef moduleName, Ctx, modulePath, interfacePath, moduleName, CacheDir, PrebuiltCacheDir, SourceLoc(), Opts, - dependencyTracker, - llvm::is_contained(PreferInterfaceForModules, moduleName) ? - ModuleLoadingMode::PreferInterface : LoadMode); + nullptr, + ModuleLoadingMode::PreferSerialized); SmallVector deps; std::unique_ptr moduleBuffer; for (auto mod: candidates) { diff --git a/lib/Frontend/ModuleInterfaceSupport.cpp b/lib/Frontend/ModuleInterfaceSupport.cpp index 52eb640375d99..ea04e971aef0a 100644 --- a/lib/Frontend/ModuleInterfaceSupport.cpp +++ b/lib/Frontend/ModuleInterfaceSupport.cpp @@ -46,8 +46,8 @@ version::Version swift::InterfaceFormatVersion({1, 0}); /// /// These come from declarations like `import class FooKit.MainFooController`. static void diagnoseScopedImports(DiagnosticEngine &diags, - ArrayRef imports){ - for (const ModuleDecl::ImportedModule &importPair : imports) { + ArrayRef imports){ + for (const ImportedModule &importPair : imports) { if (importPair.accessPath.empty()) continue; diags.diagnose(importPair.accessPath.front().Loc, @@ -109,29 +109,26 @@ static void printImports(raw_ostream &out, // When printing the private swiftinterface file, print implementation-only // imports only if they are also SPI. First, list all implementation-only // imports and filter them later. - llvm::SmallSet ioiImportSet; + llvm::SmallSet ioiImportSet; if (Opts.PrintSPIs && Opts.ExperimentalSPIImports) { allImportFilter |= ModuleDecl::ImportFilterKind::ImplementationOnly; - SmallVector ioiImport; + SmallVector ioiImport; M->getImportedModules(ioiImport, - {ModuleDecl::ImportFilterKind::ImplementationOnly, - ModuleDecl::ImportFilterKind::SPIAccessControl}); + ModuleDecl::ImportFilterKind::ImplementationOnly); ioiImportSet.insert(ioiImport.begin(), ioiImport.end()); } - SmallVector allImports; + SmallVector allImports; M->getImportedModules(allImports, allImportFilter); - ModuleDecl::removeDuplicateImports(allImports); + ImportedModule::removeDuplicates(allImports); diagnoseScopedImports(M->getASTContext().Diags, allImports); // Collect the public imports as a subset so that we can mark them with // '@_exported'. - SmallVector publicImports; + SmallVector publicImports; M->getImportedModules(publicImports, ModuleDecl::ImportFilterKind::Exported); - llvm::SmallSet publicImportSet; + llvm::SmallSet publicImportSet; publicImportSet.insert(publicImports.begin(), publicImports.end()); diff --git a/lib/FrontendTool/CMakeLists.txt b/lib/FrontendTool/CMakeLists.txt index 0e08c752eb30d..26b37f661b82b 100644 --- a/lib/FrontendTool/CMakeLists.txt +++ b/lib/FrontendTool/CMakeLists.txt @@ -1,3 +1,4 @@ +set_swift_llvm_is_available() add_swift_host_library(swiftFrontendTool STATIC FrontendTool.cpp ImportedModules.cpp diff --git a/lib/FrontendTool/FrontendTool.cpp b/lib/FrontendTool/FrontendTool.cpp index d75af390e817e..65ed97bc973aa 100644 --- a/lib/FrontendTool/FrontendTool.cpp +++ b/lib/FrontendTool/FrontendTool.cpp @@ -30,6 +30,7 @@ #include "swift/AST/DiagnosticsSema.h" #include "swift/AST/FileSystem.h" #include "swift/AST/FineGrainedDependencies.h" +#include "swift/AST/FineGrainedDependencyFormat.h" #include "swift/AST/GenericSignatureBuilder.h" #include "swift/AST/IRGenOptions.h" #include "swift/AST/IRGenRequests.h" @@ -208,6 +209,12 @@ static bool emitMakeDependenciesIfNeeded(DiagnosticEngine &diags, dependencyString.push_back(' '); dependencyString.append(frontend::utils::escapeForMake(path, buffer).str()); } + auto incrementalDependencyPaths = + reversePathSortedFilenames(depTracker->getIncrementalDependencies()); + for (auto const &path : incrementalDependencyPaths) { + dependencyString.push_back(' '); + dependencyString.append(frontend::utils::escapeForMake(path, buffer).str()); + } // FIXME: Xcode can't currently handle multiple targets in a single // dependency line. @@ -311,10 +318,10 @@ static void getImmediateImports( ModuleDecl::ImportFilterKind::SPIAccessControl, ModuleDecl::ImportFilterKind::ShadowedByCrossImportOverlay }) { - SmallVector importList; + SmallVector importList; module->getImportedModules(importList, importFilter); - for (ModuleDecl::ImportedModule &import : importList) + for (ImportedModule &import : importList) imports.insert(import.importedModule); } @@ -1175,6 +1182,7 @@ static void countASTStats(UnifiedStatsReporter &Stats, if (auto *D = Instance.getDependencyTracker()) { C.NumDependencies = D->getDependencies().size(); + C.NumIncrementalDependencies = D->getIncrementalDependencies().size(); } for (auto SF : Instance.getPrimarySourceFiles()) { @@ -1370,6 +1378,35 @@ static bool dumpAST(CompilerInstance &Instance) { return Instance.getASTContext().hadError(); } +static bool emitReferenceDependencies(CompilerInstance &Instance, + SourceFile *const SF, + StringRef outputPath) { + const auto alsoEmitDotFile = Instance.getInvocation() + .getLangOptions() + .EmitFineGrainedDependencySourcefileDotFiles; + + // Before writing to the dependencies file path, preserve any previous file + // that may have been there. No error handling -- this is just a nicety, it + // doesn't matter if it fails. + llvm::sys::fs::rename(outputPath, outputPath + "~"); + + using SourceFileDepGraph = fine_grained_dependencies::SourceFileDepGraph; + return fine_grained_dependencies::withReferenceDependencies( + SF, *Instance.getDependencyTracker(), outputPath, alsoEmitDotFile, + [&](SourceFileDepGraph &&g) -> bool { + const bool hadError = + fine_grained_dependencies::writeFineGrainedDependencyGraphToPath( + Instance.getDiags(), outputPath, g); + + // If path is stdout, cannot read it back, so check for "-" + assert(outputPath == "-" || g.verifyReadsWhatIsWritten(outputPath)); + + if (alsoEmitDotFile) + g.emitDotFile(outputPath, Instance.getDiags()); + return hadError; + }); +} + static void emitReferenceDependenciesForAllPrimaryInputsIfNeeded( CompilerInstance &Instance) { const auto &Invocation = Instance.getInvocation(); @@ -1384,13 +1421,11 @@ static void emitReferenceDependenciesForAllPrimaryInputsIfNeeded( const std::string &referenceDependenciesFilePath = Invocation.getReferenceDependenciesFilePathForPrimary( SF->getFilename()); - if (!referenceDependenciesFilePath.empty()) { - const auto LangOpts = Invocation.getLangOptions(); - (void)fine_grained_dependencies::emitReferenceDependencies( - Instance.getDiags(), SF, *Instance.getDependencyTracker(), - referenceDependenciesFilePath, - LangOpts.EmitFineGrainedDependencySourcefileDotFiles); + if (referenceDependenciesFilePath.empty()) { + continue; } + + emitReferenceDependencies(Instance, SF, referenceDependenciesFilePath); } } static void @@ -2257,7 +2292,23 @@ static bool performCompileStepsPostSILGen(CompilerInstance &Instance, SerializationOptions serializationOpts = Invocation.computeSerializationOptions(outs, Instance.getMainModule()); - serialize(MSF, serializationOpts, SM.get()); + if (serializationOpts.ExperimentalCrossModuleIncrementalInfo) { + const auto alsoEmitDotFile = + Instance.getInvocation() + .getLangOptions() + .EmitFineGrainedDependencySourcefileDotFiles; + + using SourceFileDepGraph = fine_grained_dependencies::SourceFileDepGraph; + auto *Mod = MSF.get(); + fine_grained_dependencies::withReferenceDependencies( + Mod, *Instance.getDependencyTracker(), Mod->getModuleFilename(), + alsoEmitDotFile, [&](SourceFileDepGraph &&g) { + serialize(MSF, serializationOpts, SM.get(), &g); + return false; + }); + } else { + serialize(MSF, serializationOpts, SM.get()); + } }; // Set the serialization action, so that the SIL module diff --git a/lib/FrontendTool/ImportedModules.cpp b/lib/FrontendTool/ImportedModules.cpp index 4ced1a7c5c8aa..857efcda8f98b 100644 --- a/lib/FrontendTool/ImportedModules.cpp +++ b/lib/FrontendTool/ImportedModules.cpp @@ -78,7 +78,7 @@ bool swift::emitImportedModules(ModuleDecl *mainModule, StringRef implicitHeaderPath = opts.ImplicitObjCHeaderPath; if (!implicitHeaderPath.empty()) { if (!clangImporter->importBridgingHeader(implicitHeaderPath, mainModule)) { - SmallVector imported; + SmallVector imported; clangImporter->getImportedHeaderModule()->getImportedModules( imported, {ModuleDecl::ImportFilterKind::Exported, ModuleDecl::ImportFilterKind::Default, diff --git a/lib/FrontendTool/ScanDependencies.cpp b/lib/FrontendTool/ScanDependencies.cpp index a39468e13fb21..f40d9c44a6892 100644 --- a/lib/FrontendTool/ScanDependencies.cpp +++ b/lib/FrontendTool/ScanDependencies.cpp @@ -158,7 +158,7 @@ static std::vector resolveDirectDependencies( InterfaceSubContextDelegate &ASTDelegate) { auto &ctx = instance.getASTContext(); auto knownDependencies = *cache.findDependencies(module.first, module.second); - auto isSwift = knownDependencies.isSwiftModule(); + auto isSwift = knownDependencies.isSwiftTextualModule(); // Find the dependencies of every module this module directly depends on. std::vector result; @@ -189,7 +189,7 @@ static std::vector resolveDirectDependencies( // Add the Clang modules referenced from the bridging header to the // set of Clang modules we know about. - auto swiftDeps = knownDependencies.getAsSwiftModule(); + auto swiftDeps = knownDependencies.getAsSwiftTextualModule(); for (const auto &clangDep : swiftDeps->bridgingModuleDependencies) { findAllImportedClangModules(ctx, clangDep, cache, allClangModules, knownModules); @@ -214,7 +214,9 @@ static std::vector resolveDirectDependencies( // ASTContext::getModuleDependencies returns dependencies for a module with a given name. // This Clang module may have the same name as the Swift module we are resolving, so we // need to make sure we don't add a dependency from a Swift module to itself. - if (found->getKind() == ModuleDependenciesKind::Swift && clangDep != module.first) + if ((found->getKind() == ModuleDependenciesKind::SwiftTextual || + found->getKind() == ModuleDependenciesKind::SwiftBinary) && + clangDep != module.first) result.push_back({clangDep, found->getKind()}); } } @@ -258,17 +260,18 @@ static void discoverCrosssImportOverlayDependencies( auto dummyMainDependencies = ModuleDependencies::forMainSwiftModule({}); // Update main module's dependencies to include these new overlays. - auto mainDep = *cache.findDependencies(mainModuleName, ModuleDependenciesKind::Swift); + auto mainDep = *cache.findDependencies(mainModuleName, + ModuleDependenciesKind::SwiftTextual); std::for_each(newOverlays.begin(), newOverlays.end(), [&](Identifier modName) { dummyMainDependencies.addModuleDependency(modName.str()); mainDep.addModuleDependency(modName.str()); }); - cache.updateDependencies({mainModuleName.str(), ModuleDependenciesKind::Swift}, mainDep); + cache.updateDependencies({mainModuleName.str(), + ModuleDependenciesKind::SwiftTextual}, mainDep); // Record the dummy main module's direct dependencies. The dummy main module // only directly depend on these newly discovered overlay modules. - cache.recordDependencies(dummyMainName, dummyMainDependencies, - ModuleDependenciesKind::Swift); + cache.recordDependencies(dummyMainName, dummyMainDependencies); llvm::SetVector, std::set> allModules; @@ -304,7 +307,7 @@ namespace { StringRef value, unsigned indentLevel) { out << "\""; - out.write_escaped(value); + out << value; out << "\""; } @@ -321,8 +324,11 @@ namespace { unsigned indentLevel) { out << "{\n"; std::string moduleKind; - if (module.second == ModuleDependenciesKind::Swift) + if (module.second == ModuleDependenciesKind::SwiftTextual) moduleKind = "swift"; + else if (module.second == ModuleDependenciesKind::SwiftBinary) + // FIXME: rename to be consistent in the clients (swift-driver) + moduleKind = "swiftPrebuiltExternal"; else if (module.second == ModuleDependenciesKind::SwiftPlaceholder) moduleKind = "swiftPlaceholder"; else @@ -434,23 +440,29 @@ static void writeJSON(llvm::raw_ostream &out, out.indent(2 * 2); out << "{\n"; - auto externalSwiftDep = moduleDeps.getAsPlaceholderDependencyModule(); - auto swiftDeps = moduleDeps.getAsSwiftModule(); + auto swiftPlaceholderDeps = moduleDeps.getAsPlaceholderDependencyModule(); + auto swiftTextualDeps = moduleDeps.getAsSwiftTextualModule(); + auto swiftBinaryDeps = moduleDeps.getAsSwiftBinaryModule(); auto clangDeps = moduleDeps.getAsClangModule(); // Module path. const char *modulePathSuffix = moduleDeps.isSwiftModule() ? ".swiftmodule" : ".pcm"; - std::string modulePath = externalSwiftDep - ? externalSwiftDep->compiledModulePath - : module.first + modulePathSuffix; + std::string modulePath; + if (swiftPlaceholderDeps) + modulePath = swiftPlaceholderDeps->compiledModulePath; + else if (swiftBinaryDeps) + modulePath = swiftBinaryDeps->compiledModulePath; + else + modulePath = module.first + modulePathSuffix; + writeJSONSingleField(out, "modulePath", modulePath, /*indentLevel=*/3, /*trailingComma=*/true); // Source files. - if (swiftDeps) { - writeJSONSingleField(out, "sourceFiles", swiftDeps->sourceFiles, 3, + if (swiftTextualDeps) { + writeJSONSingleField(out, "sourceFiles", swiftTextualDeps->sourceFiles, 3, /*trailingComma=*/true); } else if (clangDeps) { writeJSONSingleField(out, "sourceFiles", clangDeps->fileDependencies, 3, @@ -458,7 +470,7 @@ static void writeJSON(llvm::raw_ostream &out, } // Direct dependencies. - if (swiftDeps || clangDeps) + if (swiftTextualDeps || swiftBinaryDeps || clangDeps) writeJSONSingleField(out, "directDependencies", directDependencies, 3, /*trailingComma=*/true); @@ -466,25 +478,25 @@ static void writeJSON(llvm::raw_ostream &out, out.indent(3 * 2); out << "\"details\": {\n"; out.indent(4 * 2); - if (swiftDeps) { + if (swiftTextualDeps) { out << "\"swift\": {\n"; - /// Swift interface file, if any. - if (swiftDeps->swiftInterfaceFile) { - writeJSONSingleField( - out, "moduleInterfacePath", - *swiftDeps->swiftInterfaceFile, 5, - /*trailingComma=*/true); + /// Swift interface file, if there is one. The main module, for example, will not have + /// an interface file. + if (swiftTextualDeps->swiftInterfaceFile) { + writeJSONSingleField(out, "moduleInterfacePath", + *swiftTextualDeps->swiftInterfaceFile, 5, + /*trailingComma=*/true); writeJSONSingleField(out, "contextHash", - swiftDeps->contextHash, 5, + swiftTextualDeps->contextHash, 5, /*trailingComma=*/true); out.indent(5 * 2); out << "\"commandLine\": [\n"; - for (auto &arg :swiftDeps->buildCommandLine) { + for (auto &arg :swiftTextualDeps->buildCommandLine) { out.indent(6 * 2); out << "\"" << arg << "\""; - if (&arg != &swiftDeps->buildCommandLine.back()) + if (&arg != &swiftTextualDeps->buildCommandLine.back()) out << ","; out << "\n"; } @@ -492,70 +504,86 @@ static void writeJSON(llvm::raw_ostream &out, out << "],\n"; out.indent(5 * 2); out << "\"compiledModuleCandidates\": [\n"; - for (auto &candidate: swiftDeps->compiledModuleCandidates) { + for (auto &candidate: swiftTextualDeps->compiledModuleCandidates) { out.indent(6 * 2); out << "\"" << candidate << "\""; - if (&candidate != &swiftDeps->compiledModuleCandidates.back()) + if (&candidate != &swiftTextualDeps->compiledModuleCandidates.back()) out << ","; out << "\n"; } out.indent(5 * 2); out << "],\n"; - } else if (!swiftDeps->compiledModulePath.empty()) { - writeJSONSingleField( - out, "compiledModulePath", - swiftDeps->compiledModulePath, 5, - /*trailingComma=*/false); } writeJSONSingleField( out, "isFramework", - swiftDeps->isFramework, 5, - /*trailingComma=*/!swiftDeps->extraPCMArgs.empty() || - swiftDeps->bridgingHeaderFile.hasValue()); - if (!swiftDeps->extraPCMArgs.empty()) { + swiftTextualDeps->isFramework, 5, + /*trailingComma=*/!swiftTextualDeps->extraPCMArgs.empty() || + swiftTextualDeps->bridgingHeaderFile.hasValue()); + if (!swiftTextualDeps->extraPCMArgs.empty()) { out.indent(5 * 2); out << "\"extraPcmArgs\": [\n"; - for (auto &arg : swiftDeps->extraPCMArgs) { + for (auto &arg : swiftTextualDeps->extraPCMArgs) { out.indent(6 * 2); out << "\"" << arg << "\""; - if (&arg != &swiftDeps->extraPCMArgs.back()) + if (&arg != &swiftTextualDeps->extraPCMArgs.back()) out << ","; out << "\n"; } out.indent(5 * 2); - out << (swiftDeps->bridgingHeaderFile.hasValue() ? "],\n" : "]\n"); + out << (swiftTextualDeps->bridgingHeaderFile.hasValue() ? "],\n" : "]\n"); } /// Bridging header and its source file dependencies, if any. - if (swiftDeps->bridgingHeaderFile) { + if (swiftTextualDeps->bridgingHeaderFile) { out.indent(5 * 2); out << "\"bridgingHeader\": {\n"; - writeJSONSingleField(out, "path", *swiftDeps->bridgingHeaderFile, 6, + writeJSONSingleField(out, "path", *swiftTextualDeps->bridgingHeaderFile, 6, /*trailingComma=*/true); - writeJSONSingleField(out, "sourceFiles", swiftDeps->bridgingSourceFiles, + writeJSONSingleField(out, "sourceFiles", swiftTextualDeps->bridgingSourceFiles, 6, /*trailingComma=*/true); writeJSONSingleField(out, "moduleDependencies", - swiftDeps->bridgingModuleDependencies, 6, + swiftTextualDeps->bridgingModuleDependencies, 6, /*trailingComma=*/false); out.indent(5 * 2); out << "}\n"; } - } else if (externalSwiftDep) { + } else if (swiftPlaceholderDeps) { out << "\"swiftPlaceholder\": {\n"; // Module doc file - if (externalSwiftDep->moduleDocPath != "") + if (swiftPlaceholderDeps->moduleDocPath != "") writeJSONSingleField(out, "moduleDocPath", - externalSwiftDep->moduleDocPath, + swiftPlaceholderDeps->moduleDocPath, /*indentLevel=*/5, /*trailingComma=*/true); // Module Source Info file - if (externalSwiftDep->moduleDocPath != "") + if (swiftPlaceholderDeps->moduleDocPath != "") writeJSONSingleField(out, "moduleSourceInfoPath", - externalSwiftDep->sourceInfoPath, + swiftPlaceholderDeps->sourceInfoPath, + /*indentLevel=*/5, + /*trailingComma=*/false); + } else if (swiftBinaryDeps) { + out << "\"swiftPrebuiltExternal\": {\n"; + assert(swiftBinaryDeps->compiledModulePath != "" && + "Expected .swiftmodule for a Binary Swift Module Dependency."); + writeJSONSingleField(out, "compiledModulePath", + swiftBinaryDeps->compiledModulePath, + /*indentLevel=*/5, + /*trailingComma=*/true); + // Module doc file + if (swiftBinaryDeps->moduleDocPath != "") + writeJSONSingleField(out, "moduleDocPath", + swiftBinaryDeps->moduleDocPath, /*indentLevel=*/5, /*trailingComma=*/true); + + // Module Source Info file + if (swiftBinaryDeps->moduleDocPath != "") + writeJSONSingleField(out, "moduleSourceInfoPath", + swiftBinaryDeps->sourceInfoPath, + /*indentLevel=*/5, + /*trailingComma=*/false); } else { out << "\"clang\": {\n"; @@ -611,12 +639,14 @@ static bool diagnoseCycle(CompilerInstance &instance, llvm::SmallString<64> buffer; for (auto it = startIt; it != openSet.end(); ++ it) { buffer.append(it->first); - buffer.append(it->second == ModuleDependenciesKind::Swift? + buffer.append((it->second == ModuleDependenciesKind::SwiftTextual || + it->second == ModuleDependenciesKind::SwiftBinary)? ".swiftmodule": ".pcm"); buffer.append(" -> "); } buffer.append(startIt->first); - buffer.append(startIt->second == ModuleDependenciesKind::Swift? + buffer.append((startIt->second == ModuleDependenciesKind::SwiftTextual || + startIt->second == ModuleDependenciesKind::SwiftBinary)? ".swiftmodule": ".pcm"); instance.getASTContext().Diags.diagnose(SourceLoc(), diag::scanner_find_cycle, @@ -677,7 +707,7 @@ static bool scanModuleDependencies(CompilerInstance &instance, } // Add the main module. allModules.insert({moduleName.str(), isClang ? ModuleDependenciesKind::Clang: - ModuleDependenciesKind::Swift}); + ModuleDependenciesKind::SwiftTextual}); // Output module prescan. if (FEOpts.ImportPrescan) { @@ -817,13 +847,13 @@ bool swift::scanDependencies(CompilerInstance &instance) { } // Add any implicit module names. - for (const auto &moduleName : importInfo.ModuleNames) { - mainDependencies.addModuleDependency(moduleName.str(), &alreadyAddedModules); + for (const auto &import : importInfo.AdditionalUnloadedImports) { + mainDependencies.addModuleDependency(import.module.getModulePath(), &alreadyAddedModules); } // Already-loaded, implicitly imported module names. - for (const auto &module : importInfo.AdditionalModules) { - mainDependencies.addModuleDependency(module.first->getNameStr(), &alreadyAddedModules); + for (const auto &import : importInfo.AdditionalImports) { + mainDependencies.addModuleDependency(import.module.importedModule->getNameStr(), &alreadyAddedModules); } // Add the bridging header. @@ -859,8 +889,7 @@ bool swift::scanDependencies(CompilerInstance &instance) { // Create the module dependency cache. ModuleDependenciesCache cache; - cache.recordDependencies(mainModuleName, std::move(mainDependencies), - ModuleDependenciesKind::Swift); + cache.recordDependencies(mainModuleName, std::move(mainDependencies)); auto &ctx = instance.getASTContext(); auto ModuleCachePath = getModuleCachePathFromClang(ctx @@ -908,7 +937,7 @@ bool swift::scanDependencies(CompilerInstance &instance) { if (!deps) continue; - if (auto swiftDeps = deps->getAsSwiftModule()) { + if (auto swiftDeps = deps->getAsSwiftTextualModule()) { if (auto swiftInterfaceFile = swiftDeps->swiftInterfaceFile) depTracker->addDependency(*swiftInterfaceFile, /*IsSystem=*/false); for (const auto &sourceFile : swiftDeps->sourceFiles) diff --git a/lib/IDE/APIDigesterData.cpp b/lib/IDE/APIDigesterData.cpp index 5bda079279477..d4d9a6655aba1 100644 --- a/lib/IDE/APIDigesterData.cpp +++ b/lib/IDE/APIDigesterData.cpp @@ -56,6 +56,7 @@ StringRef swift::ide::api::getDeclKindStr(const DeclKind Value, bool lower) { } #include "swift/AST/DeclNodes.def" } + llvm_unreachable("Unhandled DeclKind in switch."); } else { return getDeclKindStrRaw(Value); } diff --git a/lib/IDE/CMakeLists.txt b/lib/IDE/CMakeLists.txt index d028e874d39b7..48ebc21981d30 100644 --- a/lib/IDE/CMakeLists.txt +++ b/lib/IDE/CMakeLists.txt @@ -1,3 +1,4 @@ +set_swift_llvm_is_available() add_swift_host_library(swiftIDE STATIC CodeCompletion.cpp CodeCompletionCache.cpp diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp index 1b4ba390087c3..51a66cb92081e 100644 --- a/lib/IDE/CodeCompletion.cpp +++ b/lib/IDE/CodeCompletion.cpp @@ -1672,7 +1672,7 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks { private: void addKeywords(CodeCompletionResultSink &Sink, bool MaybeFuncBody); - bool trySolverCompletion(); + bool trySolverCompletion(bool MaybeFuncBody); }; } // end anonymous namespace @@ -1813,6 +1813,9 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { bool PreferFunctionReferencesToCalls = false; bool HaveLeadingSpace = false; + bool CheckForDuplicates = false; + llvm::DenseSet> PreviouslySeen; + bool IncludeInstanceMembers = false; /// True if we are code completing inside a static method. @@ -1998,6 +2001,10 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { IsKeyPathExpr = true; } + void shouldCheckForDuplicates(bool value = true) { + CheckForDuplicates = value; + } + void setIsSwiftKeyPathExpr(bool onRoot) { IsSwiftKeyPathExpr = true; IsAfterSwiftKeyPathRoot = onRoot; @@ -2036,8 +2043,8 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { } void collectImportedModules(llvm::StringSet<> &ImportedModules) { - SmallVector Imported; - SmallVector FurtherImported; + SmallVector Imported; + SmallVector FurtherImported; CurrDeclContext->getParentSourceFile()->getImportedModules( Imported, {ModuleDecl::ImportFilterKind::Exported, @@ -2901,8 +2908,13 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { IsImplicitlyCurriedInstanceMethod = isImplicitlyCurriedInstanceMethod(FD); // Strip off '(_ self: Self)' if needed. - if (AFT && !IsImplicitlyCurriedInstanceMethod) + if (AFT && !IsImplicitlyCurriedInstanceMethod) { AFT = AFT->getResult()->getAs(); + + // Check for duplicates with the adjusted type too. + if (isDuplicate(FD, AFT)) + return; + } } bool trivialTrailingClosure = false; @@ -3375,10 +3387,10 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { DynamicLookupInfo dynamicLookupInfo) { auto funcTy = getTypeOfMember(AFD, dynamicLookupInfo)->getAs(); - if (funcTy && AFD->getDeclContext()->isTypeContext() && - !isImplicitlyCurriedInstanceMethod(AFD)) { + bool dropCurryLevel = funcTy && AFD->getDeclContext()->isTypeContext() && + !isImplicitlyCurriedInstanceMethod(AFD); + if (dropCurryLevel) funcTy = funcTy->getResult()->getAs(); - } bool useFunctionReference = PreferFunctionReferencesToCalls; if (!useFunctionReference && funcTy) { @@ -3390,6 +3402,10 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { if (!useFunctionReference) return false; + // Check for duplicates with the adjusted type too. + if (dropCurryLevel && isDuplicate(AFD, funcTy)) + return true; + CommandWordsPairs Pairs; CodeCompletionResultBuilder Builder( Sink, CodeCompletionResult::ResultKind::Declaration, @@ -3427,6 +3443,29 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { return true; } +private: + + /// Returns true if duplicate checking is enabled (via + /// \c shouldCheckForDuplicates) and this decl + type combination has been + /// checked previously. Returns false otherwise. + bool isDuplicate(const ValueDecl *D, Type Ty) { + if (!CheckForDuplicates) + return false; + return !PreviouslySeen.insert({D, Ty}).second; + } + + /// Returns true if duplicate checking is enabled (via + /// \c shouldCheckForDuplicates) and this decl has been checked previously + /// with the type according to \c getTypeOfMember. Returns false otherwise. + bool isDuplicate(const ValueDecl *D, DynamicLookupInfo dynamicLookupInfo) { + if (!CheckForDuplicates) + return false; + Type Ty = getTypeOfMember(D, dynamicLookupInfo); + return !PreviouslySeen.insert({D, Ty}).second; + } + +public: + // Implement swift::VisibleDeclConsumer. void foundDecl(ValueDecl *D, DeclVisibilityKind Reason, DynamicLookupInfo dynamicLookupInfo) override { @@ -3442,6 +3481,11 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { if (IsSwiftKeyPathExpr && !SwiftKeyPathFilter(D, Reason)) return; + + // If we've seen this decl+type before (possible when multiple lookups are + // performed e.g. because of ambiguous base types), bail. + if (isDuplicate(D, dynamicLookupInfo)) + return; // FIXME(InterfaceTypeRequest): Remove this. (void)D->getInterfaceType(); @@ -3532,6 +3576,11 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { Type funcType = getTypeOfMember(FD, dynamicLookupInfo) ->castTo() ->getResult(); + + // Check for duplicates with the adjusted type too. + if (isDuplicate(FD, funcType)) + return; + addFunctionCallPattern( funcType->castTo(), FD, getSemanticContext(FD, Reason, dynamicLookupInfo)); @@ -5130,48 +5179,48 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer { } } - static StringRef getFunctionBuilderDocComment( - FunctionBuilderBuildFunction function) { + static StringRef getResultBuilderDocComment( + ResultBuilderBuildFunction function) { switch (function) { - case FunctionBuilderBuildFunction::BuildArray: - return "Enables support for..in loops in a function builder by " + case ResultBuilderBuildFunction::BuildArray: + return "Enables support for..in loops in a result builder by " "combining the results of all iterations into a single result"; - case FunctionBuilderBuildFunction::BuildBlock: - return "Required by every function builder to build combined results " + case ResultBuilderBuildFunction::BuildBlock: + return "Required by every result builder to build combined results " "from statement blocks"; - case FunctionBuilderBuildFunction::BuildEitherFirst: + case ResultBuilderBuildFunction::BuildEitherFirst: return "With buildEither(second:), enables support for 'if-else' and " "'switch' statements by folding conditional results into a single " "result"; - case FunctionBuilderBuildFunction::BuildEitherSecond: + case ResultBuilderBuildFunction::BuildEitherSecond: return "With buildEither(first:), enables support for 'if-else' and " "'switch' statements by folding conditional results into a single " "result"; - case FunctionBuilderBuildFunction::BuildExpression: + case ResultBuilderBuildFunction::BuildExpression: return "If declared, provides contextual type information for statement " "expressions to translate them into partial results"; - case FunctionBuilderBuildFunction::BuildFinalResult: + case ResultBuilderBuildFunction::BuildFinalResult: return "If declared, this will be called on the partial result from the " "outermost block statement to produce the final returned result"; - case FunctionBuilderBuildFunction::BuildLimitedAvailability: + case ResultBuilderBuildFunction::BuildLimitedAvailability: return "If declared, this will be called on the partial result of " - "an 'if #available' block to allow the function builder to erase " + "an 'if #available' block to allow the result builder to erase " "type information"; - case FunctionBuilderBuildFunction::BuildOptional: + case ResultBuilderBuildFunction::BuildOptional: return "Enables support for `if` statements that do not have an `else`"; } } - void addFunctionBuilderBuildCompletion( + void addResultBuilderBuildCompletion( NominalTypeDecl *builder, Type componentType, - FunctionBuilderBuildFunction function) { + ResultBuilderBuildFunction function) { CodeCompletionResultBuilder Builder( Sink, CodeCompletionResult::ResultKind::Pattern, @@ -5193,35 +5242,35 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer { std::string declStringWithoutFunc; { llvm::raw_string_ostream out(declStringWithoutFunc); - printFunctionBuilderBuildFunction( + printResultBuilderBuildFunction( builder, componentType, function, None, out); } Builder.addTextChunk(declStringWithoutFunc); Builder.addBraceStmtWithCursor(); - Builder.setBriefDocComment(getFunctionBuilderDocComment(function)); - } - - /// Add completions for the various "build" functions in a function builder. - void addFunctionBuilderBuildCompletions(NominalTypeDecl *builder) { - Type componentType = inferFunctionBuilderComponentType(builder); - - addFunctionBuilderBuildCompletion( - builder, componentType, FunctionBuilderBuildFunction::BuildBlock); - addFunctionBuilderBuildCompletion( - builder, componentType, FunctionBuilderBuildFunction::BuildExpression); - addFunctionBuilderBuildCompletion( - builder, componentType, FunctionBuilderBuildFunction::BuildOptional); - addFunctionBuilderBuildCompletion( - builder, componentType, FunctionBuilderBuildFunction::BuildEitherFirst); - addFunctionBuilderBuildCompletion( - builder, componentType, FunctionBuilderBuildFunction::BuildEitherSecond); - addFunctionBuilderBuildCompletion( - builder, componentType, FunctionBuilderBuildFunction::BuildArray); - addFunctionBuilderBuildCompletion( + Builder.setBriefDocComment(getResultBuilderDocComment(function)); + } + + /// Add completions for the various "build" functions in a result builder. + void addResultBuilderBuildCompletions(NominalTypeDecl *builder) { + Type componentType = inferResultBuilderComponentType(builder); + + addResultBuilderBuildCompletion( + builder, componentType, ResultBuilderBuildFunction::BuildBlock); + addResultBuilderBuildCompletion( + builder, componentType, ResultBuilderBuildFunction::BuildExpression); + addResultBuilderBuildCompletion( + builder, componentType, ResultBuilderBuildFunction::BuildOptional); + addResultBuilderBuildCompletion( + builder, componentType, ResultBuilderBuildFunction::BuildEitherFirst); + addResultBuilderBuildCompletion( + builder, componentType, ResultBuilderBuildFunction::BuildEitherSecond); + addResultBuilderBuildCompletion( + builder, componentType, ResultBuilderBuildFunction::BuildArray); + addResultBuilderBuildCompletion( builder, componentType, - FunctionBuilderBuildFunction::BuildLimitedAvailability); - addFunctionBuilderBuildCompletion( - builder, componentType, FunctionBuilderBuildFunction::BuildFinalResult); + ResultBuilderBuildFunction::BuildLimitedAvailability); + addResultBuilderBuildCompletion( + builder, componentType, ResultBuilderBuildFunction::BuildFinalResult); } void getOverrideCompletions(SourceLoc Loc) { @@ -5243,8 +5292,8 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer { addAssociatedTypes(NTD); } - if (NTD && NTD->getAttrs().hasAttribute()) { - addFunctionBuilderBuildCompletions(NTD); + if (NTD && NTD->getAttrs().hasAttribute()) { + addResultBuilderBuildCompletions(NTD); } } }; @@ -5929,7 +5978,7 @@ static void deliverCompletionResults(CodeCompletionContext &CompletionContext, for (auto &Request: Lookup.RequestedCachedResults) { llvm::DenseSet ImportsSeen; - auto handleImport = [&](ModuleDecl::ImportedModule Import) { + auto handleImport = [&](ImportedModule Import) { ModuleDecl *TheModule = Import.importedModule; ImportPath::Access Path = Import.accessPath; if (TheModule->getFiles().empty()) @@ -5997,7 +6046,7 @@ static void deliverCompletionResults(CodeCompletionContext &CompletionContext, Lookup.addModuleName(curModule); // Add results for all imported modules. - SmallVector Imports; + SmallVector Imports; SF.getImportedModules( Imports, {ModuleDecl::ImportFilterKind::Exported, ModuleDecl::ImportFilterKind::Default, @@ -6045,6 +6094,7 @@ void deliverDotExprResults( Lookup.setPreferFunctionReferencesToCalls(); } + Lookup.shouldCheckForDuplicates(Results.size() > 1); for (auto &Result: Results) { Lookup.setIsStaticMetatype(Result.BaseIsStaticMetaType); Lookup.getPostfixKeywordCompletions(Result.BaseTy, BaseExpr); @@ -6060,25 +6110,7 @@ void deliverDotExprResults( deliverCompletionResults(CompletionCtx, Lookup, *SF, Consumer); } -bool CodeCompletionCallbacksImpl::trySolverCompletion() { - CompletionContext.CodeCompletionKind = Kind; - - if (Kind == CompletionKind::None) - return true; - - bool MaybeFuncBody = true; - if (CurDeclContext) { - auto *CD = CurDeclContext->getLocalContext(); - if (!CD || CD->getContextKind() == DeclContextKind::Initializer || - CD->getContextKind() == DeclContextKind::TopLevelCodeDecl) - MaybeFuncBody = false; - } - - if (auto *DC = dyn_cast_or_null(ParsedDecl)) { - if (DC->isChildContextOf(CurDeclContext)) - CurDeclContext = DC; - } - +bool CodeCompletionCallbacksImpl::trySolverCompletion(bool MaybeFuncBody) { assert(ParsedExpr || CurDeclContext); SourceLoc CompletionLoc = ParsedExpr @@ -6117,12 +6149,42 @@ bool CodeCompletionCallbacksImpl::trySolverCompletion() { } } +// Undoes the single-expression closure/function body transformation on the +// given DeclContext and its parent contexts if they have a single expression +// body that contains the code completion location. +// +// FIXME: Remove this once all expression position completions are migrated +// to work via TypeCheckCompletionCallback. +static void undoSingleExpressionReturn(DeclContext *DC) { + auto updateBody = [](BraceStmt *BS, ASTContext &Ctx) -> bool { + ASTNode FirstElem = BS->getFirstElement(); + auto *RS = dyn_cast_or_null(FirstElem.dyn_cast()); + + if (!RS || !RS->isImplicit()) + return false; + + BS->setFirstElement(RS->getResult()); + return true; + }; + + while (ClosureExpr *CE = dyn_cast_or_null(DC)) { + if (CE->hasSingleExpressionBody()) { + if (updateBody(CE->getBody(), CE->getASTContext())) + CE->setBody(CE->getBody(), false); + } + DC = DC->getParent(); + } + if (FuncDecl *FD = dyn_cast_or_null(DC)) { + if (FD->hasSingleExpressionBody()) { + if (updateBody(FD->getBody(), FD->getASTContext())) + FD->setHasSingleExpressionBody(false); + } + } +} + void CodeCompletionCallbacksImpl::doneParsing() { CompletionContext.CodeCompletionKind = Kind; - if (trySolverCompletion()) - return; - if (Kind == CompletionKind::None) { return; } @@ -6140,6 +6202,10 @@ void CodeCompletionCallbacksImpl::doneParsing() { CurDeclContext = DC; } + if (trySolverCompletion(MaybeFuncBody)) + return; + + undoSingleExpressionReturn(CurDeclContext); typeCheckContextAt( CurDeclContext, ParsedExpr diff --git a/lib/IDE/CodeCompletionCache.cpp b/lib/IDE/CodeCompletionCache.cpp index ede2b04cdd6f4..dc5aeb80ca19f 100644 --- a/lib/IDE/CodeCompletionCache.cpp +++ b/lib/IDE/CodeCompletionCache.cpp @@ -343,7 +343,7 @@ static void writeCachedModule(llvm::raw_ostream &out, if (R->getKind() == CodeCompletionResult::Declaration) LE.write(static_cast(R->getAssociatedDeclKind())); else - LE.write(~static_cast(0u)); + LE.write(static_cast(~0u)); if (R->isOperator()) LE.write(static_cast(R->getOperatorKind())); else diff --git a/lib/IDE/CompletionInstance.cpp b/lib/IDE/CompletionInstance.cpp index 2c2babd019b7c..355713a970591 100644 --- a/lib/IDE/CompletionInstance.cpp +++ b/lib/IDE/CompletionInstance.cpp @@ -198,6 +198,10 @@ forEachDependencyUntilTrue(CompilerInstance &CI, unsigned excludeBufferID, if (callback(dep)) return true; } + for (auto &dep : CI.getDependencyTracker()->getIncrementalDependencies()) { + if (callback(dep)) + return true; + } return false; } @@ -272,6 +276,51 @@ static bool areAnyDependentFilesInvalidated( }); } +/// Get interface hash of \p SF including the type members in the file. +/// +/// See if the inteface of the function and types visible from a function body +/// has changed since the last completion. If they haven't changed, completion +/// can reuse the existing AST of the source file. \c SF->getInterfaceHash() is +/// not enough because it doesn't take the interface of the type members into +/// account. For example: +/// +/// struct S { +/// func foo() {} +/// } +/// func main(val: S) { +/// val. +/// } +/// +/// In this case, we need to ensure that the interface of \c S hasn't changed. +/// Note that we don't care about local types (i.e. type declarations inside +/// function bodies, closures, or top level statement bodies) because they are +/// not visible from other functions where the completion is happening. +void getInterfaceHashIncludingTypeMembers(SourceFile *SF, + llvm::SmallString<32> &str) { + /// FIXME: Gross. Hashing multiple "hash" values. + llvm::MD5 hash; + SF->getInterfaceHash(str); + hash.update(str); + + std::function hashTypeBodyFingerprints = + [&](IterableDeclContext *IDC) { + if (auto fp = IDC->getBodyFingerprint()) + hash.update(*fp); + for (auto *member : IDC->getParsedMembers()) + if (auto *childIDC = dyn_cast(member)) + hashTypeBodyFingerprints(childIDC); + }; + + for (auto *D : SF->getTopLevelDecls()) { + if (auto IDC = dyn_cast(D)) + hashTypeBodyFingerprints(IDC); + } + + llvm::MD5::MD5Result result; + hash.final(result); + str = result.digest(); +} + } // namespace bool CompletionInstance::performCachedOperationIfPossible( @@ -318,8 +367,6 @@ bool CompletionInstance::performCachedOperationIfPossible( LangOptions langOpts = CI.getASTContext().LangOpts; langOpts.DisableParserLookup = true; - // Ensure all non-function-body tokens are hashed into the interface hash - langOpts.EnableTypeFingerprints = false; TypeCheckerOptions typeckOpts = CI.getASTContext().TypeCheckerOpts; SearchPathOptions searchPathOpts = CI.getASTContext().SearchPathOpts; DiagnosticEngine tmpDiags(tmpSM); @@ -353,8 +400,8 @@ bool CompletionInstance::performCachedOperationIfPossible( // If the interface has changed, AST must be refreshed. llvm::SmallString<32> oldInterfaceHash{}; llvm::SmallString<32> newInterfaceHash{}; - oldSF->getInterfaceHash(oldInterfaceHash); - tmpSF->getInterfaceHash(newInterfaceHash); + getInterfaceHashIncludingTypeMembers(oldSF, oldInterfaceHash); + getInterfaceHashIncludingTypeMembers(tmpSF, newInterfaceHash); if (oldInterfaceHash != newInterfaceHash) return false; @@ -404,6 +451,10 @@ bool CompletionInstance::performCachedOperationIfPossible( Scope Top(SI, ScopeKind::TopLevel); Scope Body(SI, ScopeKind::FunctionBody); + assert(oldInfo.Kind == CodeCompletionDelayedDeclKind::FunctionBody && + "If the interface hash is the same as old one, the previous kind " + "must be FunctionBody too. Otherwise, hashing is too weak"); + oldInfo.Kind = CodeCompletionDelayedDeclKind::FunctionBody; oldInfo.ParentContext = DC; oldInfo.StartOffset = newInfo.StartOffset; oldInfo.EndOffset = newInfo.EndOffset; @@ -595,10 +646,6 @@ bool swift::ide::CompletionInstance::performOperation( // source text. That breaks an invariant of syntax tree building. Invocation.getLangOptions().BuildSyntaxTree = false; - // Since caching uses the interface hash, and since per type fingerprints - // weaken that hash, disable them here: - Invocation.getLangOptions().EnableTypeFingerprints = false; - // We don't need token list. Invocation.getLangOptions().CollectParsedToken = false; diff --git a/lib/IDE/Formatting.cpp b/lib/IDE/Formatting.cpp index 6100a21d41669..a35bb306e10cf 100644 --- a/lib/IDE/Formatting.cpp +++ b/lib/IDE/Formatting.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "swift/AST/ASTWalker.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/TypeRepr.h" #include "swift/IDE/SourceEntityWalker.h" #include "swift/Parse/Parser.h" diff --git a/lib/IDE/ModuleInterfacePrinting.cpp b/lib/IDE/ModuleInterfacePrinting.cpp index b7a7ba2f08486..3446c135f8455 100644 --- a/lib/IDE/ModuleInterfacePrinting.cpp +++ b/lib/IDE/ModuleInterfacePrinting.cpp @@ -374,15 +374,7 @@ static bool printModuleInterfaceDecl(Decl *D, /// Sorts import declarations for display. static bool compareImports(ImportDecl *LHS, ImportDecl *RHS) { - // TODO(SR-13490): Probably buggy--thinks "import Foo" == "import Foo.Bar". - // ImportPathBase should provide universal comparison functions to avoid this. - auto LHSPath = LHS->getImportPath(); - auto RHSPath = RHS->getImportPath(); - for (unsigned i: range(std::min(LHSPath.size(), RHSPath.size()))) { - if (int Ret = LHSPath[i].Item.str().compare(RHSPath[i].Item.str())) - return Ret < 0; - } - return false; + return LHS->getImportPath() < RHS->getImportPath(); }; /// Sorts Swift declarations for display. diff --git a/lib/IDE/REPLCodeCompletion.cpp b/lib/IDE/REPLCodeCompletion.cpp index caf7aef65bf70..02cdd4d82078b 100644 --- a/lib/IDE/REPLCodeCompletion.cpp +++ b/lib/IDE/REPLCodeCompletion.cpp @@ -221,16 +221,14 @@ doCodeCompletion(SourceFile &SF, StringRef EnteredCode, unsigned *BufferID, auto *lastModule = SF.getParentModule(); ImplicitImportInfo implicitImports; - implicitImports.AdditionalModules.emplace_back(lastModule, - /*exported*/ false); + implicitImports.AdditionalImports.emplace_back(ImportedModule(lastModule)); // Carry over the private imports from the last module. - SmallVector imports; + SmallVector imports; lastModule->getImportedModules(imports, ModuleDecl::ImportFilterKind::Default); for (auto &import : imports) { - implicitImports.AdditionalModules.emplace_back(import.importedModule, - /*exported*/ false); + implicitImports.AdditionalImports.emplace_back(import); } // Create a new module and file for the code completion buffer, similar to how diff --git a/lib/IRGen/CMakeLists.txt b/lib/IRGen/CMakeLists.txt index 7cfe61b987e86..777096b39e6e0 100644 --- a/lib/IRGen/CMakeLists.txt +++ b/lib/IRGen/CMakeLists.txt @@ -1,3 +1,4 @@ +set_swift_llvm_is_available() add_swift_host_library(swiftIRGen STATIC AllocStackHoisting.cpp ClassLayout.cpp diff --git a/lib/IRGen/CallEmission.h b/lib/IRGen/CallEmission.h index c48ed42f0062d..dcab4708bb933 100644 --- a/lib/IRGen/CallEmission.h +++ b/lib/IRGen/CallEmission.h @@ -33,10 +33,15 @@ struct WitnessMetadata; /// A plan for emitting a series of calls. class CallEmission { + enum class State { Emitting, Finished }; + State state = State::Emitting; + public: IRGenFunction &IGF; -private: +protected: + llvm::Value *selfValue; + /// The builtin/special arguments to pass to the call. SmallVector Args; @@ -57,21 +62,21 @@ class CallEmission { /// RemainingArgsForCallee, at least between calls. bool EmittedCall; - void setFromCallee(); + virtual void setFromCallee(); void emitToUnmappedMemory(Address addr); void emitToUnmappedExplosion(Explosion &out); + virtual void emitCallToUnmappedExplosion(llvm::CallInst *call, Explosion &out) = 0; void emitYieldsToExplosion(Explosion &out); llvm::CallInst *emitCallSite(); + CallEmission(IRGenFunction &IGF, llvm::Value *selfValue, Callee &&callee) + : IGF(IGF), selfValue(selfValue), CurCallee(std::move(callee)) {} + public: - CallEmission(IRGenFunction &IGF, Callee &&callee) - : IGF(IGF), CurCallee(std::move(callee)) { - setFromCallee(); - } CallEmission(const CallEmission &other) = delete; CallEmission(CallEmission &&other); CallEmission &operator=(const CallEmission &other) = delete; - ~CallEmission(); + virtual ~CallEmission(); const Callee &getCallee() const { return CurCallee; } @@ -79,9 +84,13 @@ class CallEmission { return CurCallee.getSubstitutions(); } + virtual void begin(); + virtual void end(); + virtual SILType getParameterType(unsigned index) = 0; /// Set the arguments to the function from an explosion. - void setArgs(Explosion &arg, bool isOutlined, - WitnessMetadata *witnessMetadata = nullptr); + virtual void setArgs(Explosion &arg, bool isOutlined, + WitnessMetadata *witnessMetadata); + virtual Address getCalleeErrorSlot(SILType errorType) = 0; void addAttribute(unsigned Index, llvm::Attribute::AttrKind Attr); @@ -100,6 +109,9 @@ class CallEmission { } }; +std::unique_ptr +getCallEmission(IRGenFunction &IGF, llvm::Value *selfValue, Callee &&callee); + } // end namespace irgen } // end namespace swift diff --git a/lib/IRGen/EntryPointArgumentEmission.h b/lib/IRGen/EntryPointArgumentEmission.h new file mode 100644 index 0000000000000..f41961ebfb4fb --- /dev/null +++ b/lib/IRGen/EntryPointArgumentEmission.h @@ -0,0 +1,50 @@ +//===-- EntryPointArgumentEmission.h - Emit function entries. -------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#pragma once + +namespace llvm { +class Value; +} + +namespace swift { +namespace irgen { + +class Explosion; +struct GenericRequirement; + +class EntryPointArgumentEmission { + +public: + virtual ~EntryPointArgumentEmission() {} + virtual bool requiresIndirectResult(SILType retType) = 0; + virtual llvm::Value *getIndirectResultForFormallyDirectResult() = 0; + virtual llvm::Value *getIndirectResult(unsigned index) = 0; + virtual llvm::Value *getNextPolymorphicParameterAsMetadata() = 0; + virtual llvm::Value * + getNextPolymorphicParameter(GenericRequirement &requirement) = 0; +}; + +class NativeCCEntryPointArgumentEmission + : public virtual EntryPointArgumentEmission { + +public: + virtual llvm::Value *getCallerErrorResultArgument() = 0; + virtual llvm::Value *getContext() = 0; + virtual Explosion getArgumentExplosion(unsigned index, unsigned size) = 0; + virtual llvm::Value *getSelfWitnessTable() = 0; + virtual llvm::Value *getSelfMetadata() = 0; + virtual llvm::Value *getCoroutineBuffer() = 0; +}; + +} // end namespace irgen +} // end namespace swift diff --git a/lib/IRGen/GenBuiltin.cpp b/lib/IRGen/GenBuiltin.cpp index fa1c53f73a465..72abfd50e7a92 100644 --- a/lib/IRGen/GenBuiltin.cpp +++ b/lib/IRGen/GenBuiltin.cpp @@ -408,10 +408,10 @@ if (Builtin.ID == BuiltinValueKind::id) { \ auto *fn = cast(IGF.IGM.getWillThrowFn()); auto error = args.claimNext(); - auto errorBuffer = IGF.getErrorResultSlot( - SILType::getPrimitiveObjectType(IGF.IGM.Context.getErrorDecl() - ->getDeclaredInterfaceType() - ->getCanonicalType())); + auto errorBuffer = IGF.getCalleeErrorResultSlot( + SILType::getPrimitiveObjectType(IGF.IGM.Context.getErrorDecl() + ->getDeclaredInterfaceType() + ->getCanonicalType())); IGF.Builder.CreateStore(error, errorBuffer); auto context = llvm::UndefValue::get(IGF.IGM.Int8PtrTy); diff --git a/lib/IRGen/GenCall.cpp b/lib/IRGen/GenCall.cpp index bd530aac23ac9..7ccc39ec061b1 100644 --- a/lib/IRGen/GenCall.cpp +++ b/lib/IRGen/GenCall.cpp @@ -16,23 +16,26 @@ // //===----------------------------------------------------------------------===// -#include "GenCall.h" -#include "Signature.h" - +#include "swift/ABI/MetadataValues.h" +#include "swift/AST/ASTContext.h" +#include "swift/AST/GenericEnvironment.h" +#include "swift/Runtime/Config.h" +#include "swift/SIL/SILModule.h" +#include "swift/SIL/SILType.h" #include "clang/AST/ASTContext.h" #include "clang/AST/RecordLayout.h" #include "clang/Basic/TargetInfo.h" #include "clang/CodeGen/CodeGenABITypes.h" #include "clang/CodeGen/ModuleBuilder.h" -#include "swift/AST/GenericEnvironment.h" -#include "swift/SIL/SILType.h" -#include "swift/ABI/MetadataValues.h" -#include "swift/Runtime/Config.h" #include "llvm/IR/GlobalPtrAuthInfo.h" #include "llvm/Support/Compiler.h" #include "CallEmission.h" +#include "EntryPointArgumentEmission.h" #include "Explosion.h" +#include "GenCall.h" +#include "GenFunc.h" +#include "GenHeap.h" #include "GenObjC.h" #include "GenPointerAuth.h" #include "GenPoly.h" @@ -42,6 +45,8 @@ #include "IRGenModule.h" #include "LoadableTypeInfo.h" #include "NativeConventionSchema.h" +#include "Signature.h" +#include "StructLayout.h" using namespace swift; using namespace irgen; @@ -73,6 +78,242 @@ static Size getCoroutineContextSize(IRGenModule &IGM, llvm_unreachable("bad kind"); } +AsyncContextLayout irgen::getAsyncContextLayout(IRGenFunction &IGF, + SILFunction *function) { + SubstitutionMap forwardingSubstitutionMap = + function->getForwardingSubstitutionMap(); + CanSILFunctionType originalType = function->getLoweredFunctionType(); + CanSILFunctionType substitutedType = originalType->substGenericArgs( + IGF.IGM.getSILModule(), forwardingSubstitutionMap, + IGF.IGM.getMaximalTypeExpansionContext()); + auto layout = getAsyncContextLayout(IGF, originalType, substitutedType, + forwardingSubstitutionMap); + return layout; +} + +AsyncContextLayout irgen::getAsyncContextLayout( + IRGenFunction &IGF, CanSILFunctionType originalType, + CanSILFunctionType substitutedType, SubstitutionMap substitutionMap) { + SmallVector typeInfos; + SmallVector valTypes; + SmallVector paramInfos; + SmallVector indirectReturnInfos; + SmallVector directReturnInfos; + + auto parameters = substitutedType->getParameters(); + SILFunctionConventions fnConv(substitutedType, IGF.getSILModule()); + + // AsyncContext * __ptrauth_swift_async_context_parent Parent; + { + auto ty = SILType(); + auto &ti = IGF.IGM.getSwiftContextPtrTypeInfo(); + valTypes.push_back(ty); + typeInfos.push_back(&ti); + } + + // TaskContinuationFunction * __ptrauth_swift_async_context_resume + // ResumeParent; + { + auto ty = SILType(); + auto &ti = IGF.IGM.getTaskContinuationFunctionPtrTypeInfo(); + valTypes.push_back(ty); + typeInfos.push_back(&ti); + } + + // ExecutorRef ResumeParentExecutor; + { + auto ty = SILType(); + auto &ti = IGF.IGM.getSwiftExecutorPtrTypeInfo(); + valTypes.push_back(ty); + typeInfos.push_back(&ti); + } + + // SwiftError *errorResult; + auto errorCanType = IGF.IGM.Context.getExceptionType(); + auto errorType = SILType::getPrimitiveObjectType(errorCanType); + auto &errorTypeInfo = IGF.getTypeInfoForLowered(errorCanType); + typeInfos.push_back(&errorTypeInfo); + valTypes.push_back(errorType); + + // IndirectResultTypes *indirectResults...; + auto indirectResults = fnConv.getIndirectSILResults(); + for (auto indirectResult : indirectResults) { + auto ty = fnConv.getSILType(indirectResult, + IGF.IGM.getMaximalTypeExpansionContext()); + auto retLoweringTy = CanInOutType::get(ty.getASTType()); + auto &ti = IGF.getTypeInfoForLowered(retLoweringTy); + valTypes.push_back(ty); + typeInfos.push_back(&ti); + indirectReturnInfos.push_back(indirectResult); + } + + // ResultTypes directResults...; + auto directResults = fnConv.getDirectSILResults(); + for (auto result : directResults) { + auto ty = + fnConv.getSILType(result, IGF.IGM.getMaximalTypeExpansionContext()); + auto &ti = IGF.getTypeInfoForLowered(ty.getASTType()); + valTypes.push_back(ty); + typeInfos.push_back(&ti); + directReturnInfos.push_back(result); + } + + // SelfType self?; + bool hasLocalContextParameter = hasSelfContextParameter(substitutedType); + bool canHaveValidError = substitutedType->hasErrorResult(); + bool hasLocalContext = (hasLocalContextParameter || canHaveValidError); + SILParameterInfo localContextParameter = + hasLocalContextParameter ? parameters.back() : SILParameterInfo(); + if (hasLocalContextParameter) { + parameters = parameters.drop_back(); + } + + // ArgTypes formalArguments...; + for (auto parameter : parameters) { + SILType ty = IGF.IGM.silConv.getSILType( + parameter, substitutedType, IGF.IGM.getMaximalTypeExpansionContext()); + + auto argumentLoweringType = + getArgumentLoweringType(ty.getASTType(), parameter, + /*isNoEscape*/ true); + + auto &ti = IGF.getTypeInfoForLowered(argumentLoweringType); + + valTypes.push_back(ty); + typeInfos.push_back(&ti); + paramInfos.push_back({ty, parameter.getConvention()}); + } + auto bindings = NecessaryBindings::forAsyncFunctionInvocation( + IGF.IGM, originalType, substitutionMap); + if (!bindings.empty()) { + auto bindingsSize = bindings.getBufferSize(IGF.IGM); + auto &bindingsTI = IGF.IGM.getOpaqueStorageTypeInfo( + bindingsSize, IGF.IGM.getPointerAlignment()); + valTypes.push_back(SILType()); + typeInfos.push_back(&bindingsTI); + } + + Optional localContextInfo = llvm::None; + if (hasLocalContext) { + if (hasLocalContextParameter) { + SILType ty = + IGF.IGM.silConv.getSILType(localContextParameter, substitutedType, + IGF.IGM.getMaximalTypeExpansionContext()); + auto argumentLoweringType = + getArgumentLoweringType(ty.getASTType(), localContextParameter, + /*isNoEscape*/ true); + + auto &ti = IGF.getTypeInfoForLowered(argumentLoweringType); + valTypes.push_back(ty); + typeInfos.push_back(&ti); + localContextInfo = {ty, localContextParameter.getConvention()}; + } else { + // TODO: DETERMINE: Is there a field in this case to match the sync ABI? + auto &ti = IGF.IGM.getNativeObjectTypeInfo(); + SILType ty = SILType::getNativeObjectType(IGF.IGM.Context); + valTypes.push_back(ty); + typeInfos.push_back(&ti); + localContextInfo = {ty, substitutedType->getCalleeConvention()}; + } + } + + + Optional trailingWitnessInfo; + if (originalType->getRepresentation() == + SILFunctionTypeRepresentation::WitnessMethod) { + assert(getTrailingWitnessSignatureLength(IGF.IGM, originalType) == 2); + + // First, the Self metadata. + { + auto ty = SILType(); + auto &ti = IGF.IGM.getTypeMetadataPtrTypeInfo(); + valTypes.push_back(ty); + typeInfos.push_back(&ti); + } + // Then, the Self witness table. + { + auto ty = SILType(); + auto &ti = IGF.IGM.getWitnessTablePtrTypeInfo(); + valTypes.push_back(ty); + typeInfos.push_back(&ti); + } + trailingWitnessInfo = AsyncContextLayout::TrailingWitnessInfo(); + } + + return AsyncContextLayout( + IGF.IGM, LayoutStrategy::Optimal, valTypes, typeInfos, IGF, originalType, + substitutedType, substitutionMap, std::move(bindings), + trailingWitnessInfo, errorType, canHaveValidError, paramInfos, + indirectReturnInfos, directReturnInfos, localContextInfo); +} + +AsyncContextLayout::AsyncContextLayout( + IRGenModule &IGM, LayoutStrategy strategy, ArrayRef fieldTypes, + ArrayRef fieldTypeInfos, IRGenFunction &IGF, + CanSILFunctionType originalType, CanSILFunctionType substitutedType, + SubstitutionMap substitutionMap, NecessaryBindings &&bindings, + Optional trailingWitnessInfo, SILType errorType, + bool canHaveValidError, ArrayRef argumentInfos, + ArrayRef indirectReturnInfos, + ArrayRef directReturnInfos, + Optional localContextInfo) + : StructLayout(IGM, /*decl=*/nullptr, LayoutKind::NonHeapObject, strategy, + fieldTypeInfos, /*typeToFill*/ nullptr), + IGF(IGF), originalType(originalType), substitutedType(substitutedType), + substitutionMap(substitutionMap), errorType(errorType), + canHaveValidError(canHaveValidError), + directReturnInfos(directReturnInfos.begin(), directReturnInfos.end()), + indirectReturnInfos(indirectReturnInfos.begin(), + indirectReturnInfos.end()), + localContextInfo(localContextInfo), bindings(std::move(bindings)), + trailingWitnessInfo(trailingWitnessInfo), + argumentInfos(argumentInfos.begin(), argumentInfos.end()) { +#ifndef NDEBUG + assert(fieldTypeInfos.size() == fieldTypes.size() && + "type infos don't match types"); + if (!bindings.empty()) { + assert(fieldTypeInfos.size() >= 2 && "no field for bindings"); + auto fixedBindingsField = + dyn_cast(fieldTypeInfos[getBindingsIndex()]); + assert(fixedBindingsField && "bindings field is not fixed size"); + assert(fixedBindingsField->getFixedSize() == bindings.getBufferSize(IGM) && + fixedBindingsField->getFixedAlignment() == + IGM.getPointerAlignment() && + "bindings field doesn't fit bindings"); + } + assert(this->isFixedLayout()); +#endif +} + +static Size getAsyncContextSize(AsyncContextLayout layout) { + return layout.getSize(); +} + +static Alignment getAsyncContextAlignment(IRGenModule &IGM) { + return IGM.getPointerAlignment(); +} + +llvm::Value *IRGenFunction::getAsyncTask() { + assert(isAsync()); + auto *value = CurFn->getArg((unsigned)AsyncFunctionArgumentIndex::Task); + assert(value->getType() == IGM.SwiftTaskPtrTy); + return value; +} + +llvm::Value *IRGenFunction::getAsyncExecutor() { + assert(isAsync()); + auto *value = CurFn->getArg((unsigned)AsyncFunctionArgumentIndex::Executor); + assert(value->getType() == IGM.SwiftExecutorPtrTy); + return value; +} + +llvm::Value *IRGenFunction::getAsyncContext() { + assert(isAsync()); + auto *value = CurFn->getArg((unsigned)AsyncFunctionArgumentIndex::Context); + assert(value->getType() == IGM.SwiftContextPtrTy); + return value; +} + llvm::Type *ExplosionSchema::getScalarResultType(IRGenModule &IGM) const { if (size() == 0) { return IGM.VoidTy; @@ -288,6 +529,7 @@ namespace { } void addCoroutineContextParameter(); + void addAsyncParameters(); void expandResult(); llvm::Type *expandDirectResult(); @@ -312,6 +554,12 @@ llvm::Type *SignatureExpansion::addIndirectResult() { /// Expand all of the direct and indirect result types. void SignatureExpansion::expandResult() { + if (FnType->isAsync()) { + // The result will be stored within the SwiftContext that is passed to async + // functions. + ResultIRType = IGM.VoidTy; + return; + } if (FnType->isCoroutine()) { // This should be easy enough to support if we need to: use the // same algorithm but add the direct results to the results as if @@ -482,6 +730,19 @@ void SignatureExpansion::expandCoroutineContinuationParameters() { ParamIRTypes.push_back(IGM.Int1Ty); } +void SignatureExpansion::addAsyncParameters() { + // using TaskContinuationFunction = + // SWIFT_CC(swift) + // void (AsyncTask *, ExecutorRef, AsyncContext *); + ParamIRTypes.push_back(IGM.SwiftTaskPtrTy); + ParamIRTypes.push_back(IGM.SwiftExecutorPtrTy); + ParamIRTypes.push_back(IGM.SwiftContextPtrTy); + if (FnType->getRepresentation() == SILFunctionTypeRepresentation::Thick) { + IGM.addSwiftSelfAttributes(Attrs, ParamIRTypes.size()); + ParamIRTypes.push_back(IGM.RefCountedPtrTy); + } +} + void SignatureExpansion::addCoroutineContextParameter() { // Flag that the context is dereferenceable and unaliased. auto contextSize = getCoroutineContextSize(IGM, FnType); @@ -803,7 +1064,7 @@ namespace { llvm::Type *convertVectorType(clang::CanQual type) { auto eltTy = convertBuiltinType(type->getElementType().castAs()); - return llvm::VectorType::get(eltTy, type->getNumElements()); + return llvm::FixedVectorType::get(eltTy, type->getNumElements()); } llvm::Type *convertBuiltinType(clang::CanQual type) { @@ -1080,6 +1341,16 @@ void SignatureExpansion::expandExternalSignatureTypes() { SmallVector paramTys; auto const &clangCtx = IGM.getClangASTContext(); + bool formalIndirectResult = FnType->getNumResults() > 0 && + FnType->getSingleResult().isFormalIndirect(); + if (formalIndirectResult) { + auto resultType = getSILFuncConventions().getSingleSILResultType( + IGM.getMaximalTypeExpansionContext()); + auto clangTy = + IGM.getClangASTContext().getPointerType(IGM.getClangType(resultType)); + paramTys.push_back(clangTy); + } + switch (FnType->getRepresentation()) { case SILFunctionTypeRepresentation::ObjCMethod: { // ObjC methods take their 'self' argument first, followed by an @@ -1343,6 +1614,13 @@ void SignatureExpansion::expandParameters() { assert(FnType->getRepresentation() != SILFunctionTypeRepresentation::Block && "block with non-C calling conv?!"); + if (FnType->isAsync()) { + addAsyncParameters(); + // All other parameters will be passed inside the context added by the + // addAsyncParameters call. + return; + } + // First, if this is a coroutine, add the coroutine-context parameter. switch (FnType->getCoroutineKind()) { case SILCoroutineKind::None: @@ -1515,69 +1793,507 @@ void irgen::extractScalarResults(IRGenFunction &IGF, llvm::Type *bodyType, out.add(returned); } -/// Emit the unsubstituted result of this call into the given explosion. -/// The unsubstituted result must be naturally returned directly. -void CallEmission::emitToUnmappedExplosion(Explosion &out) { - assert(LastArgWritten == 0 && "emitting unnaturally to explosion"); +static void externalizeArguments(IRGenFunction &IGF, const Callee &callee, + Explosion &in, Explosion &out, + TemporarySet &temporaries, bool isOutlined); + +llvm::Value *irgen::getDynamicAsyncContextSize(IRGenFunction &IGF, + AsyncContextLayout layout, + CanSILFunctionType functionType, + llvm::Value *thickContext) { + // TODO: This calculation should be extracted out into a standalone function + // emitted on-demand per-module to improve codesize. + switch (functionType->getRepresentation()) { + case SILFunctionTypeRepresentation::Thick: { + // If the called function is thick, the size of the called function's + // async context may not be statically knowable. + // + // Specifically, if the thick function was produced by a partial_apply, + // the function which was originally partially applied determines the + // size of the needed async context. That original function isn't known + // statically. The dynamic size is available within the context as an + // i32 at the first index: <{ %swift.refcounted*, /*size*/ i32, ... }>. + // + // On the other hand, if the thick function was produced by a + // thin_to_thick_function, then the context will be nullptr. In that + // case, the size of the needed async context is known statically to + // be the size dictated by the function signature. + // + // We are currently emitting into some basic block. To handle these two + // cases, we need to branch based on whether the context is nullptr; each + // branch must then determine the size in the manner appropriate to it. + // Finally, both blocks must join back together to make the call: + // + // SIL: IR: + // +-----+ +-------------------------+ + // |.....| |%cond = %ctx == nullptr | + // |apply| |br %cond, static, dynamic| + // |.....| +--------/--------------\-+ + // +-----+ / \ + // +-static-------+ +-dynamic----------------------------------------------+ + // |%size = K | |%layout = bitcast %context to <{%swift.context*, i32}>| + // |br join(%size)| |%size_addr = getelementptr %layout, i32 1, i32 0 | + // +-----\--------+ |%size = load %size_addr | + // \ |br join(%size) | + // \ +------------------------------------------------------+ + // \ / + // +-join(%size)-----------------------------------------------------------+ + // |%dataAddr = swift_taskAlloc(%task, %size) | + // |%async_context = bitcast %dataAddr to ASYNC_CONTEXT(static_callee_type)| + // |... // populate the fields %context with arguments | + // |call %callee(%async_context, %context) | + // +-----------------------------------------------------------------------+ + auto *staticSizeBlock = llvm::BasicBlock::Create(IGF.IGM.getLLVMContext()); + auto *dynamicSizeBlock = llvm::BasicBlock::Create(IGF.IGM.getLLVMContext()); + auto *joinBlock = llvm::BasicBlock::Create(IGF.IGM.getLLVMContext()); + + auto hasThickContext = + IGF.Builder.CreateICmpNE(thickContext, IGF.IGM.RefCountedNull); + IGF.Builder.CreateCondBr(hasThickContext, dynamicSizeBlock, + staticSizeBlock); + + SmallVector, 2> phiValues; + { + IGF.Builder.emitBlock(staticSizeBlock); + auto size = getAsyncContextSize(layout); + auto *sizeValue = + llvm::ConstantInt::get(IGF.IGM.Int32Ty, size.getValue()); + phiValues.push_back({staticSizeBlock, sizeValue}); + IGF.Builder.CreateBr(joinBlock); + } - auto call = emitCallSite(); + { + IGF.Builder.emitBlock(dynamicSizeBlock); + SmallVector argTypeInfos; + SmallVector argValTypes; + auto int32ASTType = + BuiltinIntegerType::get(32, IGF.IGM.IRGen.SIL.getASTContext()) + ->getCanonicalType(); + auto int32SILType = SILType::getPrimitiveObjectType(int32ASTType); + const TypeInfo &int32TI = IGF.IGM.getTypeInfo(int32SILType); + argValTypes.push_back(int32SILType); + argTypeInfos.push_back(&int32TI); + HeapLayout layout(IGF.IGM, LayoutStrategy::Optimal, argValTypes, + argTypeInfos, + /*typeToFill*/ nullptr, NecessaryBindings()); + auto castThickContext = + layout.emitCastTo(IGF, thickContext, "context.prefix"); + auto sizeLayout = layout.getElement(0); + auto sizeAddr = sizeLayout.project(IGF, castThickContext, + /*NonFixedOffsets*/ llvm::None); + auto *sizeValue = IGF.Builder.CreateLoad(sizeAddr); + phiValues.push_back({dynamicSizeBlock, sizeValue}); + IGF.Builder.CreateBr(joinBlock); + } - // Bail out immediately on a void result. - llvm::Value *result = call; - if (result->getType()->isVoidTy()) - return; + { + IGF.Builder.emitBlock(joinBlock); + auto *phi = IGF.Builder.CreatePHI(IGF.IGM.Int32Ty, phiValues.size()); + for (auto &entry : phiValues) { + phi->addIncoming(entry.second, entry.first); + } + return phi; + } + } + case SILFunctionTypeRepresentation::Thin: + case SILFunctionTypeRepresentation::CFunctionPointer: + case SILFunctionTypeRepresentation::Method: + case SILFunctionTypeRepresentation::ObjCMethod: + case SILFunctionTypeRepresentation::WitnessMethod: + case SILFunctionTypeRepresentation::Closure: + case SILFunctionTypeRepresentation::Block: { + auto size = getAsyncContextSize(layout); + auto *sizeValue = llvm::ConstantInt::get(IGF.IGM.Int32Ty, size.getValue()); + return sizeValue; + } + } +} - SILFunctionConventions fnConv(getCallee().getOrigFunctionType(), - IGF.getSILModule()); +namespace { + +class SyncCallEmission final : public CallEmission { + using super = CallEmission; - // If the result was returned autoreleased, implicitly insert the reclaim. - // This is only allowed on a single direct result. - if (fnConv.getNumDirectSILResults() == 1 - && (fnConv.getDirectSILResults().begin()->getConvention() - == ResultConvention::Autoreleased)) { - result = emitObjCRetainAutoreleasedReturnValue(IGF, result); +public: + SyncCallEmission(IRGenFunction &IGF, llvm::Value *selfValue, Callee &&callee) + : CallEmission(IGF, selfValue, std::move(callee)) { + setFromCallee(); } - auto origFnType = getCallee().getOrigFunctionType(); + SILType getParameterType(unsigned index) override { + SILFunctionConventions origConv(getCallee().getOrigFunctionType(), + IGF.getSILModule()); + return origConv.getSILArgumentType( + index, IGF.IGM.getMaximalTypeExpansionContext()); + } + void begin() override { super::begin(); } + void end() override { super::end(); } + void setFromCallee() override { + super::setFromCallee(); - // Specially handle noreturn c function which would return a 'Never' SIL result - // type. - if (origFnType->getLanguage() == SILFunctionLanguage::C && - origFnType->isNoReturnFunction( - IGF.getSILModule(), IGF.IGM.getMaximalTypeExpansionContext())) { - auto clangResultTy = result->getType(); - extractScalarResults(IGF, clangResultTy, result, out); - return; + auto fnType = CurCallee.getOrigFunctionType(); + + if (fnType->getRepresentation() == + SILFunctionTypeRepresentation::WitnessMethod) { + unsigned n = getTrailingWitnessSignatureLength(IGF.IGM, fnType); + while (n--) { + Args[--LastArgWritten] = nullptr; + } + } + + llvm::Value *contextPtr = CurCallee.getSwiftContext(); + + // Add the error result if we have one. + if (fnType->hasErrorResult()) { + // The invariant is that this is always zero-initialized, so we + // don't need to do anything extra here. + SILFunctionConventions fnConv(fnType, IGF.getSILModule()); + Address errorResultSlot = IGF.getCalleeErrorResultSlot( + fnConv.getSILErrorType(IGF.IGM.getMaximalTypeExpansionContext())); + + assert(LastArgWritten > 0); + Args[--LastArgWritten] = errorResultSlot.getAddress(); + addAttribute(LastArgWritten + llvm::AttributeList::FirstArgIndex, + llvm::Attribute::NoCapture); + IGF.IGM.addSwiftErrorAttributes(CurCallee.getMutableAttributes(), + LastArgWritten); + + // Fill in the context pointer if necessary. + if (!contextPtr) { + assert(!CurCallee.getOrigFunctionType()->getExtInfo().hasContext() && + "Missing context?"); + contextPtr = llvm::UndefValue::get(IGF.IGM.RefCountedPtrTy); + } + } + + // Add the data pointer if we have one. + // (Note that we're emitting backwards, so this correctly goes + // *before* the error pointer.) + if (contextPtr) { + assert(LastArgWritten > 0); + Args[--LastArgWritten] = contextPtr; + IGF.IGM.addSwiftSelfAttributes(CurCallee.getMutableAttributes(), + LastArgWritten); + } } + void setArgs(Explosion &original, bool isOutlined, + WitnessMetadata *witnessMetadata) override { + // Convert arguments to a representation appropriate to the calling + // convention. + Explosion adjusted; - // Get the natural IR type in the body of the function that makes - // the call. This may be different than the IR type returned by the - // call itself due to ABI type coercion. - auto resultType = - fnConv.getSILResultType(IGF.IGM.getMaximalTypeExpansionContext()); - auto &nativeSchema = IGF.IGM.getTypeInfo(resultType).nativeReturnValueSchema(IGF.IGM); + auto origCalleeType = CurCallee.getOrigFunctionType(); + SILFunctionConventions fnConv(origCalleeType, IGF.getSILModule()); - // For ABI reasons the result type of the call might not actually match the - // expected result type. - // - // This can happen when calling C functions, or class method dispatch thunks - // for methods that have covariant ABI-compatible overrides. - auto expectedNativeResultType = nativeSchema.getExpandedType(IGF.IGM); - if (result->getType() != expectedNativeResultType) { - result = - IGF.coerceValue(result, expectedNativeResultType, IGF.IGM.DataLayout); + // Pass along the indirect result pointers. + original.transferInto(adjusted, fnConv.getNumIndirectSILResults()); + + // Pass along the coroutine buffer. + switch (origCalleeType->getCoroutineKind()) { + case SILCoroutineKind::YieldMany: + case SILCoroutineKind::YieldOnce: + original.transferInto(adjusted, 1); + break; + + case SILCoroutineKind::None: + break; + } + + // Translate the formal arguments and handle any special arguments. + switch (getCallee().getRepresentation()) { + case SILFunctionTypeRepresentation::ObjCMethod: + adjusted.add(getCallee().getObjCMethodReceiver()); + adjusted.add(getCallee().getObjCMethodSelector()); + externalizeArguments(IGF, getCallee(), original, adjusted, Temporaries, + isOutlined); + break; + + case SILFunctionTypeRepresentation::Block: + adjusted.add(getCallee().getBlockObject()); + LLVM_FALLTHROUGH; + + case SILFunctionTypeRepresentation::CFunctionPointer: + externalizeArguments(IGF, getCallee(), original, adjusted, Temporaries, + isOutlined); + break; + + case SILFunctionTypeRepresentation::WitnessMethod: + assert(witnessMetadata); + assert(witnessMetadata->SelfMetadata->getType() == + IGF.IGM.TypeMetadataPtrTy); + assert(witnessMetadata->SelfWitnessTable->getType() == + IGF.IGM.WitnessTablePtrTy); + Args.rbegin()[1] = witnessMetadata->SelfMetadata; + Args.rbegin()[0] = witnessMetadata->SelfWitnessTable; + LLVM_FALLTHROUGH; + + case SILFunctionTypeRepresentation::Closure: + case SILFunctionTypeRepresentation::Method: + case SILFunctionTypeRepresentation::Thin: + case SILFunctionTypeRepresentation::Thick: { + // Check for value arguments that need to be passed indirectly. + // But don't expect to see 'self' if it's been moved to the context + // position. + auto params = origCalleeType->getParameters(); + if (hasSelfContextParameter(origCalleeType)) { + params = params.drop_back(); + } + for (auto param : params) { + addNativeArgument(IGF, original, origCalleeType, param, adjusted, + isOutlined); + } + + // Anything else, just pass along. This will include things like + // generic arguments. + adjusted.add(original.claimAll()); + + break; + } + } + super::setArgs(adjusted, isOutlined, witnessMetadata); } + void emitCallToUnmappedExplosion(llvm::CallInst *call, Explosion &out) override { + // Bail out immediately on a void result. + llvm::Value *result = call; + if (result->getType()->isVoidTy()) + return; - // Gather the values. - Explosion nativeExplosion; - extractScalarResults(IGF, result->getType(), result, nativeExplosion); + SILFunctionConventions fnConv(getCallee().getOrigFunctionType(), + IGF.getSILModule()); + + // If the result was returned autoreleased, implicitly insert the reclaim. + // This is only allowed on a single direct result. + if (fnConv.getNumDirectSILResults() == 1 + && (fnConv.getDirectSILResults().begin()->getConvention() + == ResultConvention::Autoreleased)) { + result = emitObjCRetainAutoreleasedReturnValue(IGF, result); + } + + auto origFnType = getCallee().getOrigFunctionType(); + + // Specially handle noreturn c function which would return a 'Never' SIL result + // type. + if (origFnType->getLanguage() == SILFunctionLanguage::C && + origFnType->isNoReturnFunction( + IGF.getSILModule(), IGF.IGM.getMaximalTypeExpansionContext())) { + auto clangResultTy = result->getType(); + extractScalarResults(IGF, clangResultTy, result, out); + return; + } + + // Get the natural IR type in the body of the function that makes + // the call. This may be different than the IR type returned by the + // call itself due to ABI type coercion. + auto resultType = + fnConv.getSILResultType(IGF.IGM.getMaximalTypeExpansionContext()); + auto &nativeSchema = IGF.IGM.getTypeInfo(resultType).nativeReturnValueSchema(IGF.IGM); + + // For ABI reasons the result type of the call might not actually match the + // expected result type. + // + // This can happen when calling C functions, or class method dispatch thunks + // for methods that have covariant ABI-compatible overrides. + auto expectedNativeResultType = nativeSchema.getExpandedType(IGF.IGM); + // If the expected result type is void, bail. + if (expectedNativeResultType->isVoidTy()) + return; + if (result->getType() != expectedNativeResultType) { + result = + IGF.coerceValue(result, expectedNativeResultType, IGF.IGM.DataLayout); + } + + // Gather the values. + Explosion nativeExplosion; + extractScalarResults(IGF, result->getType(), result, nativeExplosion); + + out = nativeSchema.mapFromNative(IGF.IGM, IGF, nativeExplosion, resultType); + } + Address getCalleeErrorSlot(SILType errorType) override { + return IGF.getCalleeErrorResultSlot(errorType); + }; +}; + +class AsyncCallEmission final : public CallEmission { + using super = CallEmission; + + Address contextBuffer; + Size contextSize; + Address context; + llvm::Value *thickContext = nullptr; + Optional asyncContextLayout; + + AsyncContextLayout getAsyncContextLayout() { + if (!asyncContextLayout) { + asyncContextLayout.emplace(::getAsyncContextLayout( + IGF, getCallee().getOrigFunctionType(), + getCallee().getSubstFunctionType(), getCallee().getSubstitutions())); + } + return *asyncContextLayout; + } + + void saveValue(ElementLayout layout, Explosion &explosion, bool isOutlined) { + Address addr = layout.project(IGF, context, /*offsets*/ llvm::None); + auto &ti = cast(layout.getType()); + ti.initialize(IGF, explosion, addr, isOutlined); + } + void loadValue(ElementLayout layout, Explosion &explosion) { + Address addr = layout.project(IGF, context, /*offsets*/ llvm::None); + auto &ti = cast(layout.getType()); + ti.loadAsTake(IGF, addr, explosion); + } + +public: + AsyncCallEmission(IRGenFunction &IGF, llvm::Value *selfValue, Callee &&callee) + : CallEmission(IGF, selfValue, std::move(callee)) { + setFromCallee(); + } + + void begin() override { + super::begin(); + assert(!contextBuffer.isValid()); + assert(!context.isValid()); + auto layout = getAsyncContextLayout(); + // Allocate space for the async arguments. + auto *dynamicContextSize32 = getDynamicAsyncContextSize( + IGF, layout, CurCallee.getOrigFunctionType(), thickContext); + auto *dynamicContextSize = + IGF.Builder.CreateZExt(dynamicContextSize32, IGF.IGM.SizeTy); + std::tie(contextBuffer, contextSize) = emitAllocAsyncContext( + IGF, layout, dynamicContextSize, getAsyncContextSize(layout)); + context = layout.emitCastTo(IGF, contextBuffer.getAddress()); + if (layout.canHaveError()) { + auto fieldLayout = layout.getErrorLayout(); + auto addr = fieldLayout.project(IGF, context, /*offsets*/ llvm::None); + auto &ti = fieldLayout.getType(); + auto nullError = llvm::Constant::getNullValue(ti.getStorageType()); + IGF.Builder.CreateStore(nullError, addr); + } + } + void end() override { + assert(contextBuffer.isValid()); + assert(context.isValid()); + emitDeallocAsyncContext(IGF, contextBuffer, contextSize); + super::end(); + } + void setFromCallee() override { + super::setFromCallee(); + thickContext = CurCallee.getSwiftContext(); + } + SILType getParameterType(unsigned index) override { + return getAsyncContextLayout().getParameterType(index); + } + void setArgs(Explosion &llArgs, bool isOutlined, + WitnessMetadata *witnessMetadata) override { + Explosion asyncExplosion; + asyncExplosion.add(IGF.getAsyncTask()); + asyncExplosion.add(IGF.getAsyncExecutor()); + asyncExplosion.add(contextBuffer.getAddress()); + if (getCallee().getRepresentation() == + SILFunctionTypeRepresentation::Thick) { + asyncExplosion.add(getCallee().getSwiftContext()); + } + super::setArgs(asyncExplosion, false, witnessMetadata); + SILFunctionConventions fnConv(getCallee().getSubstFunctionType(), + IGF.getSILModule()); + auto layout = getAsyncContextLayout(); + + // Set caller info into the context. + { // caller context + Explosion explosion; + explosion.add(IGF.getAsyncContext()); + auto fieldLayout = layout.getParentLayout(); + saveValue(fieldLayout, explosion, isOutlined); + } + { // caller executor + Explosion explosion; + explosion.add(IGF.getAsyncExecutor()); + auto fieldLayout = layout.getResumeParentExecutorLayout(); + saveValue(fieldLayout, explosion, isOutlined); + } + // Move all the arguments into the context. + for (unsigned index = 0, count = layout.getIndirectReturnCount(); + index < count; ++index) { + auto fieldLayout = layout.getIndirectReturnLayout(index); + saveValue(fieldLayout, llArgs, isOutlined); + } + for (unsigned index = 0, count = layout.getArgumentCount(); index < count; + ++index) { + auto fieldLayout = layout.getArgumentLayout(index); + saveValue(fieldLayout, llArgs, isOutlined); + } + if (layout.hasBindings()) { + auto bindingLayout = layout.getBindingsLayout(); + auto bindingsAddr = bindingLayout.project(IGF, context, /*offsets*/ None); + layout.getBindings().save(IGF, bindingsAddr, llArgs); + } + if (selfValue) { + Explosion selfExplosion; + selfExplosion.add(selfValue); + auto fieldLayout = layout.getLocalContextLayout(); + saveValue(fieldLayout, selfExplosion, isOutlined); + } + } + void emitCallToUnmappedExplosion(llvm::CallInst *call, Explosion &out) override { + SILFunctionConventions fnConv(getCallee().getSubstFunctionType(), + IGF.getSILModule()); + auto resultType = + fnConv.getSILResultType(IGF.IGM.getMaximalTypeExpansionContext()); + auto &nativeSchema = + IGF.IGM.getTypeInfo(resultType).nativeReturnValueSchema(IGF.IGM); + auto expectedNativeResultType = nativeSchema.getExpandedType(IGF.IGM); + if (expectedNativeResultType->isVoidTy()) { + // If the async return is void, there is no return to move out of the + // argument buffer. + return; + } + // Gather the values. + Explosion nativeExplosion; + auto layout = getAsyncContextLayout(); + for (unsigned index = 0, count = layout.getDirectReturnCount(); + index < count; ++index) { + auto fieldLayout = layout.getDirectReturnLayout(index); + loadValue(fieldLayout, nativeExplosion); + } + + out = nativeSchema.mapFromNative(IGF.IGM, IGF, nativeExplosion, resultType); + } + Address getCalleeErrorSlot(SILType errorType) override { + auto layout = getAsyncContextLayout(); + auto errorLayout = layout.getErrorLayout(); + auto address = errorLayout.project(IGF, context, /*offsets*/ llvm::None); + return address; + }; +}; + +} // end anonymous namespace + +std::unique_ptr irgen::getCallEmission(IRGenFunction &IGF, + llvm::Value *selfValue, + Callee &&callee) { + if (callee.getOrigFunctionType()->isAsync()) { + return std::make_unique(IGF, selfValue, + std::move(callee)); + } else { + return std::make_unique(IGF, selfValue, + std::move(callee)); + } +} + +/// Emit the unsubstituted result of this call into the given explosion. +/// The unsubstituted result must be naturally returned directly. +void CallEmission::emitToUnmappedExplosion(Explosion &out) { + assert(state == State::Emitting); + assert(LastArgWritten == 0 && "emitting unnaturally to explosion"); + + auto call = emitCallSite(); - out = nativeSchema.mapFromNative(IGF.IGM, IGF, nativeExplosion, resultType); + emitCallToUnmappedExplosion(call, out); } /// Emit the unsubstituted result of this call to the given address. /// The unsubstituted result must be naturally returned indirectly. void CallEmission::emitToUnmappedMemory(Address result) { + assert(state == State::Emitting); assert(LastArgWritten == 1 && "emitting unnaturally to indirect result"); Args[0] = result.getAddress(); @@ -1594,6 +2310,7 @@ void CallEmission::emitToUnmappedMemory(Address result) { /// The private routine to ultimately emit a call or invoke instruction. llvm::CallInst *CallEmission::emitCallSite() { + assert(state == State::Emitting); assert(LastArgWritten == 0); assert(!EmittedCall); EmittedCall = true; @@ -1681,6 +2398,7 @@ llvm::CallInst *IRBuilder::CreateCall(const FunctionPointer &fn, void CallEmission::emitToMemory(Address addr, const LoadableTypeInfo &indirectedResultTI, bool isOutlined) { + assert(state == State::Emitting); assert(LastArgWritten <= 1); // If the call is naturally to an explosion, emit it that way and @@ -1742,6 +2460,7 @@ static void emitCastToSubstSchema(IRGenFunction &IGF, Explosion &in, } void CallEmission::emitYieldsToExplosion(Explosion &out) { + assert(state == State::Emitting); // Emit the call site. auto call = emitCallSite(); @@ -1820,6 +2539,7 @@ void CallEmission::emitYieldsToExplosion(Explosion &out) { /// Emit the result of this call to an explosion. void CallEmission::emitToExplosion(Explosion &out, bool isOutlined) { + assert(state == State::Emitting); assert(LastArgWritten <= 1); // For coroutine calls, we need to collect the yields, not the results; @@ -1892,12 +2612,21 @@ CallEmission::CallEmission(CallEmission &&other) // Prevent other's destructor from asserting. LastArgWritten = 0; EmittedCall = true; + state = State::Finished; } CallEmission::~CallEmission() { assert(LastArgWritten == 0); assert(EmittedCall); assert(Temporaries.hasBeenCleared()); + assert(state == State::Finished); +} + +void CallEmission::begin() {} + +void CallEmission::end() { + assert(state == State::Emitting); + state = State::Finished; } Callee::Callee(CalleeInfo &&info, const FunctionPointer &fn, @@ -1982,6 +2711,7 @@ llvm::Value *Callee::getObjCMethodSelector() const { /// Set up this emitter afresh from the current callee specs. void CallEmission::setFromCallee() { + assert(state == State::Emitting); IsCoroutine = CurCallee.getSubstFunctionType()->isCoroutine(); EmittedCall = false; @@ -1992,51 +2722,6 @@ void CallEmission::setFromCallee() { Args.reserve(numArgs); Args.set_size(numArgs); LastArgWritten = numArgs; - - auto fnType = CurCallee.getOrigFunctionType(); - - if (fnType->getRepresentation() - == SILFunctionTypeRepresentation::WitnessMethod) { - unsigned n = getTrailingWitnessSignatureLength(IGF.IGM, fnType); - while (n--) { - Args[--LastArgWritten] = nullptr; - } - } - - llvm::Value *contextPtr = CurCallee.getSwiftContext(); - - // Add the error result if we have one. - if (fnType->hasErrorResult()) { - // The invariant is that this is always zero-initialized, so we - // don't need to do anything extra here. - SILFunctionConventions fnConv(fnType, IGF.getSILModule()); - Address errorResultSlot = IGF.getErrorResultSlot( - fnConv.getSILErrorType(IGF.IGM.getMaximalTypeExpansionContext())); - - assert(LastArgWritten > 0); - Args[--LastArgWritten] = errorResultSlot.getAddress(); - addAttribute(LastArgWritten + llvm::AttributeList::FirstArgIndex, - llvm::Attribute::NoCapture); - IGF.IGM.addSwiftErrorAttributes(CurCallee.getMutableAttributes(), - LastArgWritten); - - // Fill in the context pointer if necessary. - if (!contextPtr) { - assert(!CurCallee.getOrigFunctionType()->getExtInfo().hasContext() && - "Missing context?"); - contextPtr = llvm::UndefValue::get(IGF.IGM.RefCountedPtrTy); - } - } - - // Add the data pointer if we have one. - // (Note that we're emitting backwards, so this correctly goes - // *before* the error pointer.) - if (contextPtr) { - assert(LastArgWritten > 0); - Args[--LastArgWritten] = contextPtr; - IGF.IGM.addSwiftSelfAttributes(CurCallee.getMutableAttributes(), - LastArgWritten); - } } bool irgen::canCoerceToSchema(IRGenModule &IGM, @@ -2335,6 +3020,11 @@ static void externalizeArguments(IRGenFunction &IGF, const Callee &callee, == SILFunctionTypeRepresentation::Block) { // Ignore the physical block-object parameter. firstParam += 1; + // Or the indirect result parameter. + } else if (fnType->getNumResults() > 0 && + fnType->getSingleResult().isFormalIndirect()) { + // Ignore the indirect result parameter. + firstParam += 1; } for (unsigned i = firstParam, e = FI.arg_size(); i != e; ++i) { @@ -2610,12 +3300,11 @@ irgen::getCoroutineResumeFunctionPointerAuth(IRGenModule &IGM, llvm_unreachable("bad coroutine kind"); } -static void emitRetconCoroutineEntry(IRGenFunction &IGF, - CanSILFunctionType fnType, - Explosion &allParamValues, - llvm::Intrinsic::ID idIntrinsic, - Size bufferSize, - Alignment bufferAlignment) { +static void +emitRetconCoroutineEntry(IRGenFunction &IGF, CanSILFunctionType fnType, + NativeCCEntryPointArgumentEmission &emission, + llvm::Intrinsic::ID idIntrinsic, Size bufferSize, + Alignment bufferAlignment) { auto prototype = IGF.IGM.getOpaquePtr(IGF.IGM.getAddrOfContinuationPrototype(fnType)); @@ -2624,7 +3313,7 @@ static void emitRetconCoroutineEntry(IRGenFunction &IGF, auto deallocFn = IGF.IGM.getOpaquePtr(IGF.IGM.getFreeFn()); // Call the right 'llvm.coro.id.retcon' variant. - llvm::Value *buffer = allParamValues.claimNext(); + llvm::Value *buffer = emission.getCoroutineBuffer(); llvm::Value *id = IGF.Builder.CreateIntrinsicCall(idIntrinsic, { llvm::ConstantInt::get(IGF.IGM.Int32Ty, bufferSize.getValue()), llvm::ConstantInt::get(IGF.IGM.Int32Ty, bufferAlignment.getValue()), @@ -2645,19 +3334,19 @@ static void emitRetconCoroutineEntry(IRGenFunction &IGF, IGF.setCoroutineHandle(hdl); } -void irgen::emitYieldOnceCoroutineEntry(IRGenFunction &IGF, - CanSILFunctionType fnType, - Explosion &allParamValues) { - emitRetconCoroutineEntry(IGF, fnType, allParamValues, +void irgen::emitYieldOnceCoroutineEntry( + IRGenFunction &IGF, CanSILFunctionType fnType, + NativeCCEntryPointArgumentEmission &emission) { + emitRetconCoroutineEntry(IGF, fnType, emission, llvm::Intrinsic::coro_id_retcon_once, getYieldOnceCoroutineBufferSize(IGF.IGM), getYieldOnceCoroutineBufferAlignment(IGF.IGM)); } -void irgen::emitYieldManyCoroutineEntry(IRGenFunction &IGF, - CanSILFunctionType fnType, - Explosion &allParamValues) { - emitRetconCoroutineEntry(IGF, fnType, allParamValues, +void irgen::emitYieldManyCoroutineEntry( + IRGenFunction &IGF, CanSILFunctionType fnType, + NativeCCEntryPointArgumentEmission &emission) { + emitRetconCoroutineEntry(IGF, fnType, emission, llvm::Intrinsic::coro_id_retcon, getYieldManyCoroutineBufferSize(IGF.IGM), getYieldManyCoroutineBufferAlignment(IGF.IGM)); @@ -2694,9 +3383,56 @@ void irgen::emitDeallocYieldManyCoroutineBuffer(IRGenFunction &IGF, IGF.Builder.CreateLifetimeEnd(buffer, bufferSize); } +Address irgen::emitTaskAlloc(IRGenFunction &IGF, llvm::Value *size, + Alignment alignment) { + auto *call = IGF.Builder.CreateCall(IGF.IGM.getTaskAllocFn(), + {IGF.getAsyncTask(), size}); + call->setDoesNotThrow(); + call->setCallingConv(IGF.IGM.SwiftCC); + call->addAttribute(llvm::AttributeList::FunctionIndex, + llvm::Attribute::ReadNone); + auto address = Address(call, alignment); + return address; +} + +void irgen::emitTaskDealloc(IRGenFunction &IGF, Address address, + llvm::Value *size) { + auto *call = IGF.Builder.CreateCall( + IGF.IGM.getTaskDeallocFn(), {IGF.getAsyncTask(), address.getAddress()}); + call->setDoesNotThrow(); + call->setCallingConv(IGF.IGM.SwiftCC); + call->addAttribute(llvm::AttributeList::FunctionIndex, + llvm::Attribute::ReadNone); +} + +std::pair irgen::emitAllocAsyncContext(IRGenFunction &IGF, + AsyncContextLayout layout, + llvm::Value *sizeValue, + Size sizeLowerBound) { + auto alignment = getAsyncContextAlignment(IGF.IGM); + auto address = emitTaskAlloc(IGF, sizeValue, alignment); + IGF.Builder.CreateLifetimeStart(address, sizeLowerBound); + return {address, sizeLowerBound}; +} + +std::pair +irgen::emitAllocAsyncContext(IRGenFunction &IGF, AsyncContextLayout layout) { + auto size = getAsyncContextSize(layout); + auto *sizeValue = llvm::ConstantInt::get(IGF.IGM.SizeTy, size.getValue()); + return emitAllocAsyncContext(IGF, layout, sizeValue, size); +} + +void irgen::emitDeallocAsyncContext(IRGenFunction &IGF, Address context, + Size size) { + auto *sizeValue = llvm::ConstantInt::get(IGF.IGM.SizeTy, size.getValue()); + emitTaskDealloc(IGF, context, sizeValue); + IGF.Builder.CreateLifetimeEnd(context, size); +} + llvm::Value *irgen::emitYield(IRGenFunction &IGF, CanSILFunctionType coroutineType, Explosion &substValues) { + // TODO: Handle async! auto coroSignature = IGF.IGM.getSignature(coroutineType); auto coroInfo = coroSignature.getCoroutineInfo(); @@ -2772,88 +3508,16 @@ llvm::Value *irgen::emitYield(IRGenFunction &IGF, } /// Add a new set of arguments to the function. -void CallEmission::setArgs(Explosion &original, bool isOutlined, +void CallEmission::setArgs(Explosion &adjusted, bool isOutlined, WitnessMetadata *witnessMetadata) { - // Convert arguments to a representation appropriate to the calling - // convention. - Explosion adjusted; - - auto origCalleeType = CurCallee.getOrigFunctionType(); - SILFunctionConventions fnConv(origCalleeType, IGF.getSILModule()); - - // Pass along the indirect result pointers. - original.transferInto(adjusted, fnConv.getNumIndirectSILResults()); - - // Pass along the coroutine buffer. - switch (origCalleeType->getCoroutineKind()) { - case SILCoroutineKind::YieldMany: - case SILCoroutineKind::YieldOnce: - original.transferInto(adjusted, 1); - break; - - case SILCoroutineKind::None: - break; - } - - // Translate the formal arguments and handle any special arguments. - switch (getCallee().getRepresentation()) { - case SILFunctionTypeRepresentation::ObjCMethod: - adjusted.add(getCallee().getObjCMethodReceiver()); - adjusted.add(getCallee().getObjCMethodSelector()); - externalizeArguments(IGF, getCallee(), original, adjusted, - Temporaries, isOutlined); - break; - - case SILFunctionTypeRepresentation::Block: - adjusted.add(getCallee().getBlockObject()); - LLVM_FALLTHROUGH; - - case SILFunctionTypeRepresentation::CFunctionPointer: - externalizeArguments(IGF, getCallee(), original, adjusted, - Temporaries, isOutlined); - break; - - case SILFunctionTypeRepresentation::WitnessMethod: - assert(witnessMetadata); - assert(witnessMetadata->SelfMetadata->getType() == - IGF.IGM.TypeMetadataPtrTy); - assert(witnessMetadata->SelfWitnessTable->getType() == - IGF.IGM.WitnessTablePtrTy); - Args.rbegin()[1] = witnessMetadata->SelfMetadata; - Args.rbegin()[0] = witnessMetadata->SelfWitnessTable; - LLVM_FALLTHROUGH; - - case SILFunctionTypeRepresentation::Closure: - case SILFunctionTypeRepresentation::Method: - case SILFunctionTypeRepresentation::Thin: - case SILFunctionTypeRepresentation::Thick: { - // Check for value arguments that need to be passed indirectly. - // But don't expect to see 'self' if it's been moved to the context - // position. - auto params = origCalleeType->getParameters(); - if (hasSelfContextParameter(origCalleeType)) { - params = params.drop_back(); - } - for (auto param : params) { - addNativeArgument(IGF, original, - origCalleeType, param, adjusted, isOutlined); - } - - // Anything else, just pass along. This will include things like - // generic arguments. - adjusted.add(original.claimAll()); - - break; - } - } - + assert(state == State::Emitting); // Add the given number of arguments. assert(LastArgWritten >= adjusted.size()); size_t targetIndex = LastArgWritten - adjusted.size(); assert(targetIndex <= 1); LastArgWritten = targetIndex; - + auto argIterator = Args.begin() + targetIndex; for (auto value : adjusted.claimAll()) { *argIterator++ = value; @@ -2862,6 +3526,7 @@ void CallEmission::setArgs(Explosion &original, bool isOutlined, void CallEmission::addAttribute(unsigned index, llvm::Attribute::AttrKind attr) { + assert(state == State::Emitting); auto &attrs = CurCallee.getMutableAttributes(); attrs = attrs.addAttribute(IGF.IGM.getLLVMContext(), index, attr); } @@ -2877,8 +3542,8 @@ Explosion IRGenFunction::collectParameters() { } /// Fetch the error result slot. -Address IRGenFunction::getErrorResultSlot(SILType errorType) { - if (!ErrorResultSlot) { +Address IRGenFunction::getCalleeErrorResultSlot(SILType errorType) { + if (!CalleeErrorResultSlot) { auto &errorTI = cast(getTypeInfo(errorType)); IRBuilder builder(IGM.getLLVMContext(), IGM.DebugInfo != nullptr); @@ -2902,23 +3567,28 @@ Address IRGenFunction::getErrorResultSlot(SILType errorType) { cast(errorTI.getStorageType())); builder.CreateStore(nullError, addr); - ErrorResultSlot = addr.getAddress(); + CalleeErrorResultSlot = addr.getAddress(); } - return Address(ErrorResultSlot, IGM.getPointerAlignment()); + return Address(CalleeErrorResultSlot, IGM.getPointerAlignment()); } /// Fetch the error result slot received from the caller. Address IRGenFunction::getCallerErrorResultSlot() { - assert(ErrorResultSlot && "no error result slot!"); - assert(isa(ErrorResultSlot) && "error result slot is local!"); - return Address(ErrorResultSlot, IGM.getPointerAlignment()); + assert(CallerErrorResultSlot && "no error result slot!"); + assert(isa(CallerErrorResultSlot) && !isAsync() || + isa(CallerErrorResultSlot) && isAsync() && + "error result slot is local!"); + return Address(CallerErrorResultSlot, IGM.getPointerAlignment()); } // Set the error result slot. This should only be done in the prologue. -void IRGenFunction::setErrorResultSlot(llvm::Value *address) { - assert(!ErrorResultSlot && "already have error result slot!"); +void IRGenFunction::setCallerErrorResultSlot(llvm::Value *address) { + assert(!CallerErrorResultSlot && "already have a caller error result slot!"); assert(isa(address->getType())); - ErrorResultSlot = address; + CallerErrorResultSlot = address; + if (!isAsync()) { + CalleeErrorResultSlot = address; + } } /// Emit the basic block that 'return' should branch to and insert it into diff --git a/lib/IRGen/GenCall.h b/lib/IRGen/GenCall.h index c8e3a2c54dd19..9f3b05d7dfaa2 100644 --- a/lib/IRGen/GenCall.h +++ b/lib/IRGen/GenCall.h @@ -20,10 +20,14 @@ #include -#include "swift/Basic/LLVM.h" #include "swift/AST/Types.h" +#include "swift/Basic/LLVM.h" +#include "swift/SIL/ApplySite.h" #include "llvm/IR/CallingConv.h" +#include "GenHeap.h" +#include "IRGenModule.h" + namespace llvm { class AttributeList; class Constant; @@ -49,6 +53,7 @@ namespace irgen { class IRGenFunction; class IRGenModule; class LoadableTypeInfo; + class NativeCCEntryPointArgumentEmission; class Size; class TypeInfo; @@ -60,6 +65,197 @@ namespace irgen { return TranslationDirection(!bool(direction)); } + // struct SwiftContext { + // SwiftContext * __ptrauth(...) callerContext; + // SwiftPartialFunction * __ptrauth(...) returnToCaller; + // SwiftActor * __ptrauth(...) callerActor; + // SwiftPartialFunction * __ptrauth(...) yieldToCaller?; + // SwiftError *errorResult; + // IndirectResultTypes *indirectResults...; + // union { + // struct { + // SwiftPartialFunction * __ptrauth(...) resumeFromYield?; + // SwiftPartialFunction * __ptrauth(...) abortFromYield?; + // SwiftActor * __ptrauth(...) calleeActorDuringYield?; + // YieldTypes yieldValues...; + // }; + // ResultTypes directResults...; + // }; + // ArgTypes formalArguments...; + // SelfType self?; + // }; + struct AsyncContextLayout : StructLayout { + struct ArgumentInfo { + SILType type; + ParameterConvention convention; + }; + struct TrailingWitnessInfo {}; + + private: + enum class FixedIndex : unsigned { + Parent = 0, + ResumeParent = 1, + ResumeParentExecutor = 2, + Error = 3, + }; + enum class FixedCount : unsigned { + Parent = 1, + ResumeParent = 1, + ResumeParentExecutor = 1, + Error = 1, + }; + IRGenFunction &IGF; + CanSILFunctionType originalType; + CanSILFunctionType substitutedType; + SubstitutionMap substitutionMap; + SILType errorType; + bool canHaveValidError; + SmallVector directReturnInfos; + SmallVector indirectReturnInfos; + Optional localContextInfo; + NecessaryBindings bindings; + Optional trailingWitnessInfo; + SmallVector argumentInfos; + + unsigned getParentIndex() { return (unsigned)FixedIndex::Parent; } + unsigned getResumeParentIndex() { + return (unsigned)FixedIndex::ResumeParent; + } + unsigned getResumeParentExecutorIndex() { + return (unsigned)FixedIndex::ResumeParentExecutor; + } + unsigned getErrorIndex() { return (unsigned)FixedIndex::Error; } + unsigned getFirstIndirectReturnIndex() { + return getErrorIndex() + getErrorCount(); + } + unsigned getIndexAfterIndirectReturns() { + return getFirstIndirectReturnIndex() + getIndirectReturnCount(); + } + unsigned getFirstDirectReturnIndex() { + return getIndexAfterIndirectReturns(); + } + unsigned getIndexAfterDirectReturns() { + return getFirstDirectReturnIndex() + getDirectReturnCount(); + } + unsigned getFirstArgumentIndex() { return getIndexAfterDirectReturns(); } + unsigned getIndexAfterArguments() { + return getFirstArgumentIndex() + getArgumentCount(); + } + unsigned getBindingsIndex() { + assert(hasBindings()); + return getIndexAfterArguments(); + } + unsigned getIndexAfterBindings() { + return getIndexAfterArguments() + (hasBindings() ? 1 : 0); + } + unsigned getLocalContextIndex() { + assert(hasLocalContext()); + return getIndexAfterBindings(); + } + unsigned getIndexAfterLocalContext() { + return getIndexAfterBindings() + + (hasLocalContext() ? 1 : 0); + } + unsigned getSelfMetadataIndex() { + assert(hasTrailingWitnesses()); + return getIndexAfterLocalContext(); + } + unsigned getSelfWitnessTableIndex() { + assert(hasTrailingWitnesses()); + return getIndexAfterLocalContext() + 1; + } + unsigned getIndexAfterTrailingWitnesses() { + return getIndexAfterLocalContext() + (hasTrailingWitnesses() ? 2 : 0); + } + + public: + ElementLayout getParentLayout() { return getElement(getParentIndex()); } + ElementLayout getResumeParentLayout() { + return getElement(getResumeParentIndex()); + } + ElementLayout getResumeParentExecutorLayout() { + return getElement(getResumeParentExecutorIndex()); + } + bool canHaveError() { return canHaveValidError; } + ElementLayout getErrorLayout() { return getElement(getErrorIndex()); } + unsigned getErrorCount() { return (unsigned)FixedCount::Error; } + SILType getErrorType() { return errorType; } + + ElementLayout getIndirectReturnLayout(unsigned index) { + return getElement(getFirstIndirectReturnIndex() + index); + } + unsigned getIndirectReturnCount() { return indirectReturnInfos.size(); } + + bool hasLocalContext() { return (bool)localContextInfo; } + ElementLayout getLocalContextLayout() { + assert(hasLocalContext()); + return getElement(getLocalContextIndex()); + } + SILType getLocalContextType() { + assert(hasLocalContext()); + return localContextInfo->type; + } + + bool hasBindings() const { return !bindings.empty(); } + ElementLayout getBindingsLayout() { + assert(hasBindings()); + return getElement(getBindingsIndex()); + } + const NecessaryBindings &getBindings() const { return bindings; } + + ElementLayout getArgumentLayout(unsigned index) { + return getElement(getFirstArgumentIndex() + index); + } + SILType getArgumentType(unsigned index) { + return argumentInfos[index].type; + } + // Returns the type of a parameter of the substituted function using the + // indexing of the function parameters, *not* the indexing of + // AsyncContextLayout. + SILType getParameterType(unsigned index) { + SILFunctionConventions origConv(substitutedType, IGF.getSILModule()); + return origConv.getSILArgumentType( + index, IGF.IGM.getMaximalTypeExpansionContext()); + } + unsigned getArgumentCount() { return argumentInfos.size(); } + bool hasTrailingWitnesses() { return (bool)trailingWitnessInfo; } + ElementLayout getSelfMetadataLayout() { + assert(hasTrailingWitnesses()); + return getElement(getSelfMetadataIndex()); + } + ElementLayout getSelfWitnessTableLayout() { + return getElement(getSelfWitnessTableIndex()); + } + + unsigned getDirectReturnCount() { return directReturnInfos.size(); } + ElementLayout getDirectReturnLayout(unsigned index) { + return getElement(getFirstDirectReturnIndex() + index); + } + + AsyncContextLayout( + IRGenModule &IGM, LayoutStrategy strategy, ArrayRef fieldTypes, + ArrayRef fieldTypeInfos, IRGenFunction &IGF, + CanSILFunctionType originalType, CanSILFunctionType substitutedType, + SubstitutionMap substitutionMap, NecessaryBindings &&bindings, + Optional trailingWitnessInfo, SILType errorType, + bool canHaveValidError, ArrayRef argumentInfos, + ArrayRef directReturnInfos, + ArrayRef indirectReturnInfos, + Optional localContextInfo); + }; + + llvm::Value *getDynamicAsyncContextSize(IRGenFunction &IGF, + AsyncContextLayout layout, + CanSILFunctionType functionType, + llvm::Value *thickContext); + AsyncContextLayout getAsyncContextLayout(IRGenFunction &IGF, + SILFunction *function); + + AsyncContextLayout getAsyncContextLayout(IRGenFunction &IGF, + CanSILFunctionType originalType, + CanSILFunctionType substitutedType, + SubstitutionMap substitutionMap); + llvm::CallingConv::ID expandCallingConv(IRGenModule &IGM, SILFunctionTypeRepresentation convention); @@ -127,15 +323,34 @@ namespace irgen { Address emitAllocYieldOnceCoroutineBuffer(IRGenFunction &IGF); void emitDeallocYieldOnceCoroutineBuffer(IRGenFunction &IGF, Address buffer); - void emitYieldOnceCoroutineEntry(IRGenFunction &IGF, - CanSILFunctionType coroutineType, - Explosion &allParams); + void + emitYieldOnceCoroutineEntry(IRGenFunction &IGF, + CanSILFunctionType coroutineType, + NativeCCEntryPointArgumentEmission &emission); Address emitAllocYieldManyCoroutineBuffer(IRGenFunction &IGF); void emitDeallocYieldManyCoroutineBuffer(IRGenFunction &IGF, Address buffer); - void emitYieldManyCoroutineEntry(IRGenFunction &IGF, - CanSILFunctionType coroutineType, - Explosion &allParams); + void + emitYieldManyCoroutineEntry(IRGenFunction &IGF, + CanSILFunctionType coroutineType, + NativeCCEntryPointArgumentEmission &emission); + + Address emitTaskAlloc(IRGenFunction &IGF, llvm::Value *size, + Alignment alignment); + void emitTaskDealloc(IRGenFunction &IGF, Address address, llvm::Value *size); + /// Allocate task local storage for the specified layout but using the + /// provided dynamic size. Allowing the size to be specified dynamically is + /// necessary for applies of thick functions the sizes of whose async contexts + /// are dependent on the underlying, already partially applied, called + /// function. The provided sizeLowerBound will be used to track the lifetime + /// of the allocation that is known statically. + std::pair emitAllocAsyncContext(IRGenFunction &IGF, + AsyncContextLayout layout, + llvm::Value *sizeValue, + Size sizeLowerBound); + std::pair emitAllocAsyncContext(IRGenFunction &IGF, + AsyncContextLayout layout); + void emitDeallocAsyncContext(IRGenFunction &IGF, Address context, Size size); /// Yield the given values from the current continuation. /// @@ -145,6 +360,11 @@ namespace irgen { CanSILFunctionType coroutineType, Explosion &yieldedValues); + enum class AsyncFunctionArgumentIndex : unsigned { + Task = 0, + Executor = 1, + Context = 2, + }; } // end namespace irgen } // end namespace swift diff --git a/lib/IRGen/GenClass.cpp b/lib/IRGen/GenClass.cpp index 94ab37632feba..74f0376b9683a 100644 --- a/lib/IRGen/GenClass.cpp +++ b/lib/IRGen/GenClass.cpp @@ -2340,7 +2340,7 @@ ClassDecl *IRGenModule::getObjCRuntimeBaseClass(Identifier name, // Make a really fake-looking class. auto SwiftRootClass = new (Context) ClassDecl(SourceLoc(), name, SourceLoc(), - MutableArrayRef(), + ArrayRef(), /*generics*/ nullptr, Context.TheBuiltinModule); SwiftRootClass->setIsObjC(Context.LangOpts.EnableObjCInterop); diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index d38101150fa24..3d2051ec16f33 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -38,6 +38,8 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/GlobalDecl.h" +#include "clang/CodeGen/CodeGenABITypes.h" +#include "clang/CodeGen/ModuleBuilder.h" #include "llvm/ADT/SmallString.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/GlobalAlias.h" @@ -2759,6 +2761,77 @@ void IRGenModule::emitDynamicReplacementOriginalFunctionThunk(SILFunction *f) { IGF.Builder.CreateRet(Res); } +/// If the calling convention for `ctor` doesn't match the calling convention +/// that we assumed for it when we imported it as `initializer`, emit and +/// return a thunk that conforms to the assumed calling convention. The thunk +/// is marked `alwaysinline`, so it doesn't generate any runtime overhead. +/// If the assumed calling convention was correct, just return `ctor`. +/// +/// See also comments in CXXMethodConventions in SIL/IR/SILFunctionType.cpp. +static llvm::Constant * +emitCXXConstructorThunkIfNeeded(IRGenModule &IGM, SILFunction *initializer, + const clang::CXXConstructorDecl *ctor, + const LinkEntity &entity, + llvm::Constant *ctorAddress) { + Signature signature = IGM.getSignature(initializer->getLoweredFunctionType()); + + llvm::FunctionType *assumedFnType = signature.getType(); + llvm::FunctionType *ctorFnType = + cast(ctorAddress->getType()->getPointerElementType()); + + if (assumedFnType == ctorFnType) { + return ctorAddress; + } + + // The thunk has private linkage, so it doesn't need to have a predictable + // mangled name -- we just need to make sure the name is unique. + llvm::SmallString<32> name; + llvm::raw_svector_ostream stream(name); + stream << "__swift_cxx_ctor"; + entity.mangle(stream); + + llvm::Function *thunk = llvm::Function::Create( + assumedFnType, llvm::Function::PrivateLinkage, name, &IGM.Module); + + thunk->setCallingConv(llvm::CallingConv::C); + + llvm::AttrBuilder attrBuilder; + IGM.constructInitialFnAttributes(attrBuilder); + attrBuilder.addAttribute(llvm::Attribute::AlwaysInline); + llvm::AttributeList attr = signature.getAttributes().addAttributes( + IGM.getLLVMContext(), llvm::AttributeList::FunctionIndex, attrBuilder); + thunk->setAttributes(attr); + + IRGenFunction subIGF(IGM, thunk); + if (IGM.DebugInfo) + IGM.DebugInfo->emitArtificialFunction(subIGF, thunk); + + SmallVector Args; + for (auto i = thunk->arg_begin(), e = thunk->arg_end(); i != e; ++i) { + auto *argTy = i->getType(); + auto *paramTy = ctorFnType->getParamType(i - thunk->arg_begin()); + if (paramTy != argTy) + Args.push_back(subIGF.coerceValue(i, paramTy, IGM.DataLayout)); + else + Args.push_back(i); + } + + clang::CodeGen::ImplicitCXXConstructorArgs implicitArgs = + clang::CodeGen::getImplicitCXXConstructorArgs(IGM.ClangCodeGen->CGM(), + ctor); + for (size_t i = 0; i < implicitArgs.Prefix.size(); ++i) { + Args.insert(Args.begin() + 1 + i, implicitArgs.Prefix[i]); + } + for (const auto &arg : implicitArgs.Suffix) { + Args.push_back(arg); + } + + subIGF.Builder.CreateCall(ctorFnType, ctorAddress, Args); + subIGF.Builder.CreateRetVoid(); + + return thunk; +} + /// Find the entry point for a SIL function. llvm::Function *IRGenModule::getAddrOfSILFunction( SILFunction *f, ForDefinition_t forDefinition, @@ -2787,6 +2860,11 @@ llvm::Function *IRGenModule::getAddrOfSILFunction( if (auto clangDecl = f->getClangDecl()) { auto globalDecl = getClangGlobalDeclForFunction(clangDecl); clangAddr = getAddrOfClangGlobalDecl(globalDecl, forDefinition); + + if (auto ctor = dyn_cast(clangDecl)) { + clangAddr = + emitCXXConstructorThunkIfNeeded(*this, f, ctor, entity, clangAddr); + } } bool isDefinition = f->isDefinition(); diff --git a/lib/IRGen/GenEnum.cpp b/lib/IRGen/GenEnum.cpp index a2666b7fad980..93d7b4dc7f512 100644 --- a/lib/IRGen/GenEnum.cpp +++ b/lib/IRGen/GenEnum.cpp @@ -2716,8 +2716,8 @@ namespace { // Unpack as an instance of the payload type and use its fixLifetime // operation. Explosion srcAsPayload; - unpackIntoPayloadExplosion(IGF, srcAsPayload, srcAsPayload); - payloadTI.fixLifetime(IGF, src); + unpackIntoPayloadExplosion(IGF, src, srcAsPayload); + payloadTI.fixLifetime(IGF, srcAsPayload); return; } } diff --git a/lib/IRGen/GenFunc.cpp b/lib/IRGen/GenFunc.cpp index 98232c7722ac6..77dd505a0c1f2 100644 --- a/lib/IRGen/GenFunc.cpp +++ b/lib/IRGen/GenFunc.cpp @@ -189,6 +189,7 @@ namespace { class FuncTypeInfo : public ScalarPairTypeInfo, public FuncSignatureInfo { + protected: FuncTypeInfo(CanSILFunctionType formalType, llvm::StructType *storageType, Size size, Alignment align, SpareBitVector &&spareBits, IsPOD_t pod) @@ -654,7 +655,7 @@ static void emitApplyArgument(IRGenFunction &IGF, out); } -static CanType getArgumentLoweringType(CanType type, SILParameterInfo paramInfo, +CanType irgen::getArgumentLoweringType(CanType type, SILParameterInfo paramInfo, bool isNoEscape) { switch (paramInfo.getConvention()) { // Capture value parameters by value, consuming them. @@ -718,54 +719,40 @@ static unsigned findSinglePartiallyAppliedParameterIndexIgnoringEmptyTypes( return firstNonEmpty; } -/// Emit the forwarding stub function for a partial application. -/// -/// If 'layout' is null, there is a single captured value of -/// Swift-refcountable type that is being used directly as the -/// context object. -static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM, - const Optional &staticFnPtr, - bool calleeHasContext, - const Signature &origSig, - CanSILFunctionType origType, - CanSILFunctionType substType, - CanSILFunctionType outType, - SubstitutionMap subs, - HeapLayout const *layout, - ArrayRef conventions) { - auto outSig = IGM.getSignature(outType); - llvm::AttributeList outAttrs = outSig.getAttributes(); - llvm::FunctionType *fwdTy = outSig.getType(); - SILFunctionConventions outConv(outType, IGM.getSILModule()); - - StringRef FnName; - if (staticFnPtr) - FnName = staticFnPtr->getPointer()->getName(); - - IRGenMangler Mangler; - std::string thunkName = Mangler.manglePartialApplyForwarder(FnName); - - // FIXME: Maybe cache the thunk by function and closure types?. - llvm::Function *fwd = - llvm::Function::Create(fwdTy, llvm::Function::InternalLinkage, - llvm::StringRef(thunkName), &IGM.Module); - fwd->setCallingConv(outSig.getCallingConv()); - - fwd->setAttributes(outAttrs); - // Merge initial attributes with outAttrs. - llvm::AttrBuilder b; - IGM.constructInitialFnAttributes(b); - fwd->addAttributes(llvm::AttributeList::FunctionIndex, b); - - IRGenFunction subIGF(IGM, fwd); - if (IGM.DebugInfo) - IGM.DebugInfo->emitArtificialFunction(subIGF, fwd); - - Explosion origParams = subIGF.collectParameters(); - - // Create a new explosion for potentially reabstracted parameters. - Explosion args; - +namespace { +class PartialApplicationForwarderEmission { +protected: + IRGenModule &IGM; + IRGenFunction &subIGF; + llvm::Function *fwd; + const Optional &staticFnPtr; + bool calleeHasContext; + const Signature &origSig; + CanSILFunctionType origType; + CanSILFunctionType substType; + CanSILFunctionType outType; + SubstitutionMap subs; + HeapLayout const *layout; + const ArrayRef conventions; + SILFunctionConventions origConv; + SILFunctionConventions outConv; + Explosion origParams; + + PartialApplicationForwarderEmission( + IRGenModule &IGM, IRGenFunction &subIGF, llvm::Function *fwd, + const Optional &staticFnPtr, bool calleeHasContext, + const Signature &origSig, CanSILFunctionType origType, + CanSILFunctionType substType, CanSILFunctionType outType, + SubstitutionMap subs, HeapLayout const *layout, + ArrayRef conventions) + : IGM(IGM), subIGF(subIGF), fwd(fwd), staticFnPtr(staticFnPtr), + calleeHasContext(calleeHasContext), origSig(origSig), + origType(origType), substType(substType), outType(outType), subs(subs), + conventions(conventions), origConv(origType, IGM.getSILModule()), + outConv(outType, IGM.getSILModule()), + origParams(subIGF.collectParameters()) {} + +public: // SWIFT_ENABLE_TENSORFLOW // The witness method self argument comes after polymorphic arguments (and is // followed by the self type and the witness table). However, we may encounter @@ -774,11 +761,65 @@ static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM, // until it's time to add it to 'args'. bool isWitnessMethodCallee = origType->getRepresentation() == SILFunctionTypeRepresentation::WitnessMethod; + bool isMethodCallee = + origType->getRepresentation() == SILFunctionTypeRepresentation::Method; + Explosion witnessMethodSelfValue; + + enum class DynamicFunctionKind { + Witness, + PartialApply, + }; + virtual void begin(){}; + virtual void gatherArgumentsFromApply() = 0; + virtual unsigned getCurrentArgumentIndex() = 0; + virtual bool transformArgumentToNative(SILParameterInfo origParamInfo, + Explosion &in, Explosion &out) = 0; + virtual void addArgument(Explosion &explosion) = 0; + virtual void addArgument(llvm::Value *argValue) = 0; + virtual void addArgument(Explosion &explosion, unsigned index) = 0; + virtual void addArgument(llvm::Value *argValue, unsigned index) = 0; + virtual SILParameterInfo getParameterInfo(unsigned index) = 0; + virtual llvm::Value *getContext() = 0; + virtual llvm::Value *getDynamicFunctionPointer() = 0; + virtual llvm::Value *getDynamicFunctionContext() = 0; + virtual void addDynamicFunctionContext(Explosion &explosion, + DynamicFunctionKind kind) = 0; + virtual void addDynamicFunctionPointer(Explosion &explosion, + DynamicFunctionKind kind) = 0; + virtual void addSelf(Explosion &explosion) = 0; + virtual void addWitnessSelfMetadata(llvm::Value *value) = 0; + virtual void addWitnessSelfWitnessTable(llvm::Value *value) = 0; + virtual void forwardErrorResult() = 0; + virtual bool originalParametersConsumed() = 0; + virtual void addPolymorphicArguments(Explosion polyArgs) = 0; + virtual llvm::CallInst *createCall(FunctionPointer &fnPtr) = 0; + virtual void createReturn(llvm::CallInst *call) = 0; + virtual void end(){}; + virtual ~PartialApplicationForwarderEmission() {} +}; +class SyncPartialApplicationForwarderEmission + : public PartialApplicationForwarderEmission { + using super = PartialApplicationForwarderEmission; + // Create a new explosion for potentially reabstracted parameters. + Explosion args; Address resultValueAddr; - { +public: + SyncPartialApplicationForwarderEmission( + IRGenModule &IGM, IRGenFunction &subIGF, llvm::Function *fwd, + const Optional &staticFnPtr, bool calleeHasContext, + const Signature &origSig, CanSILFunctionType origType, + CanSILFunctionType substType, CanSILFunctionType outType, + SubstitutionMap subs, HeapLayout const *layout, + ArrayRef conventions) + : PartialApplicationForwarderEmission( + IGM, subIGF, fwd, staticFnPtr, calleeHasContext, origSig, origType, + substType, outType, subs, layout, conventions) {} + + void begin() override { super::begin(); } + void gatherArgumentsFromApply() override { // Lower the forwarded arguments in the original function's generic context. GenericContextScope scope(IGM, origType->getInvocationGenericSignature()); @@ -894,6 +935,410 @@ static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM, nativeApplyArg.size()); } } + unsigned getCurrentArgumentIndex() override { return args.size(); } + bool transformArgumentToNative(SILParameterInfo origParamInfo, Explosion &in, + Explosion &out) override { + return addNativeArgument(subIGF, in, origType, origParamInfo, out, false); + } + void addArgument(Explosion &explosion) override { + args.add(explosion.claimAll()); + } + void addArgument(llvm::Value *argValue) override { args.add(argValue); } + void addArgument(Explosion &explosion, unsigned index) override { + addArgument(explosion); + } + void addArgument(llvm::Value *argValue, unsigned index) override { + addArgument(argValue); + } + SILParameterInfo getParameterInfo(unsigned index) override { + return substType->getParameters()[index]; + } + llvm::Value *getContext() override { return origParams.claimNext(); } + llvm::Value *getDynamicFunctionPointer() override { return args.takeLast(); } + llvm::Value *getDynamicFunctionContext() override { return args.takeLast(); } + void addDynamicFunctionContext(Explosion &explosion, + DynamicFunctionKind kind) override { + addArgument(explosion); + } + void addDynamicFunctionPointer(Explosion &explosion, + DynamicFunctionKind kind) override { + addArgument(explosion); + } + void addSelf(Explosion &explosion) override { addArgument(explosion); } + void addWitnessSelfMetadata(llvm::Value *value) override { + addArgument(value); + } + void addWitnessSelfWitnessTable(llvm::Value *value) override { + addArgument(value); + } + void forwardErrorResult() override { + llvm::Value *errorResultPtr = origParams.claimNext(); + args.add(errorResultPtr); + } + bool originalParametersConsumed() override { return origParams.empty(); } + void addPolymorphicArguments(Explosion polyArgs) override { + polyArgs.transferInto(args, polyArgs.size()); + } + llvm::CallInst *createCall(FunctionPointer &fnPtr) override { + return subIGF.Builder.CreateCall(fnPtr, args.claimAll()); + } + void createReturn(llvm::CallInst *call) override { + // Reabstract the result value as substituted. + SILFunctionConventions origConv(origType, IGM.getSILModule()); + auto &outResultTI = IGM.getTypeInfo( + outConv.getSILResultType(IGM.getMaximalTypeExpansionContext())); + auto &nativeResultSchema = outResultTI.nativeReturnValueSchema(IGM); + if (call->getType()->isVoidTy()) { + if (!resultValueAddr.isValid()) + subIGF.Builder.CreateRetVoid(); + else { + // Okay, we have called a function that expects an indirect return type + // but the partially applied return type is direct. + assert(!nativeResultSchema.requiresIndirect()); + Explosion loadedResult; + cast(outResultTI) + .loadAsTake(subIGF, resultValueAddr, loadedResult); + Explosion nativeResult = nativeResultSchema.mapIntoNative( + IGM, subIGF, loadedResult, + outConv.getSILResultType(IGM.getMaximalTypeExpansionContext()), + false); + outResultTI.deallocateStack( + subIGF, resultValueAddr, + outConv.getSILResultType(IGM.getMaximalTypeExpansionContext())); + if (nativeResult.size() == 1) + subIGF.Builder.CreateRet(nativeResult.claimNext()); + else { + llvm::Value *nativeAgg = + llvm::UndefValue::get(nativeResultSchema.getExpandedType(IGM)); + for (unsigned i = 0, e = nativeResult.size(); i != e; ++i) { + auto *elt = nativeResult.claimNext(); + nativeAgg = subIGF.Builder.CreateInsertValue(nativeAgg, elt, i); + } + subIGF.Builder.CreateRet(nativeAgg); + } + } + } else { + llvm::Value *callResult = call; + // If the result type is dependent on a type parameter we might have to + // cast to the result type - it could be substituted. + if (origConv.getSILResultType(IGM.getMaximalTypeExpansionContext()) + .hasTypeParameter()) { + auto ResType = fwd->getReturnType(); + if (ResType != callResult->getType()) + callResult = + subIGF.coerceValue(callResult, ResType, subIGF.IGM.DataLayout); + } + subIGF.Builder.CreateRet(callResult); + } + } + void end() override { super::end(); } +}; +class AsyncPartialApplicationForwarderEmission + : public PartialApplicationForwarderEmission { + using super = PartialApplicationForwarderEmission; + AsyncContextLayout layout; + llvm::Value *task; + llvm::Value *executor; + llvm::Value *contextBuffer; + Size contextSize; + Address context; + llvm::Value *heapContextBuffer; + unsigned currentArgumentIndex; + struct DynamicFunction { + using Kind = DynamicFunctionKind; + Kind kind; + llvm::Value *pointer; + llvm::Value *context; + }; + Optional dynamicFunction = llvm::None; + struct Self { + enum class Kind { + Method, + WitnessMethod, + }; + Kind kind; + llvm::Value *value; + }; + Optional self = llvm::None; + + llvm::Value *loadValue(ElementLayout layout) { + Address addr = layout.project(subIGF, context, /*offsets*/ llvm::None); + auto &ti = cast(layout.getType()); + Explosion explosion; + ti.loadAsTake(subIGF, addr, explosion); + return explosion.claimNext(); + } + void saveValue(ElementLayout layout, Explosion &explosion) { + Address addr = layout.project(subIGF, context, /*offsets*/ llvm::None); + auto &ti = cast(layout.getType()); + ti.initialize(subIGF, explosion, addr, /*isOutlined*/ false); + } + void loadValue(ElementLayout layout, Explosion &explosion) { + Address addr = layout.project(subIGF, context, /*offsets*/ llvm::None); + auto &ti = cast(layout.getType()); + ti.loadAsTake(subIGF, addr, explosion); + } + +public: + AsyncPartialApplicationForwarderEmission( + IRGenModule &IGM, IRGenFunction &subIGF, llvm::Function *fwd, + const Optional &staticFnPtr, bool calleeHasContext, + const Signature &origSig, CanSILFunctionType origType, + CanSILFunctionType substType, CanSILFunctionType outType, + SubstitutionMap subs, HeapLayout const *layout, + ArrayRef conventions) + : PartialApplicationForwarderEmission( + IGM, subIGF, fwd, staticFnPtr, calleeHasContext, origSig, origType, + substType, outType, subs, layout, conventions), + layout(getAsyncContextLayout(subIGF, origType, substType, subs)), + currentArgumentIndex(outType->getNumParameters()) { + task = origParams.claimNext(); + executor = origParams.claimNext(); + contextBuffer = origParams.claimNext(); + heapContextBuffer = origParams.claimNext(); + } + + void begin() override { + super::begin(); + assert(contextBuffer); + assert(heapContextBuffer); + context = layout.emitCastTo(subIGF, contextBuffer); + } + bool transformArgumentToNative(SILParameterInfo origParamInfo, Explosion &in, + Explosion &out) override { + out.add(in.claimAll()); + return false; + } + unsigned getCurrentArgumentIndex() override { return currentArgumentIndex; } + void gatherArgumentsFromApply() override { + // The provided %swift.context* already contains all the values from the + // apply site. All that remains to do is bind polymorphic parameters. + for (unsigned index = 0; index < outType->getParameters().size(); ++index) { + auto fieldLayout = layout.getArgumentLayout(index); + Explosion explosion; + loadValue(fieldLayout, explosion); + bindPolymorphicParameter(subIGF, origType, substType, explosion, index); + (void)explosion.claimAll(); + // TODO: Rather than just discard this explosion, avoid loading the + // parameters if no polymorphic binding is necessary. + } + } + void addArgument(llvm::Value *argValue) override { + addArgument(argValue, currentArgumentIndex); + } + void addArgument(Explosion &explosion) override { + addArgument(explosion, currentArgumentIndex); + } + void addArgument(llvm::Value *argValue, unsigned index) override { + Explosion explosion; + explosion.add(argValue); + addArgument(explosion, index); + } + void addArgument(Explosion &explosion, unsigned index) override { + currentArgumentIndex = index + 1; + auto isLocalContext = (hasSelfContextParameter(origType) && + index == origType->getParameters().size() - 1); + if (isLocalContext) { + addSelf(explosion); + return; + } + auto fieldLayout = layout.getArgumentLayout(index); + saveValue(fieldLayout, explosion); + } + SILParameterInfo getParameterInfo(unsigned index) override { + return origType->getParameters()[index]; + } + llvm::Value *getContext() override { return heapContextBuffer; } + llvm::Value *getDynamicFunctionPointer() override { + assert(dynamicFunction && dynamicFunction->pointer); + return dynamicFunction->pointer; + } + llvm::Value *getDynamicFunctionContext() override { + assert((dynamicFunction && dynamicFunction->context) || + (self && self->value)); + return dynamicFunction ? dynamicFunction->context : self->value; + } + void addDynamicFunctionContext(Explosion &explosion, + DynamicFunction::Kind kind) override { + auto *value = explosion.claimNext(); + assert(explosion.empty()); + if (dynamicFunction) { + assert(dynamicFunction->kind == kind); + if (dynamicFunction->context) { + assert(dynamicFunction->context == value); + } else { + dynamicFunction->context = value; + } + return; + } + dynamicFunction = {kind, /*pointer*/ nullptr, /*context*/ value}; + } + void addDynamicFunctionPointer(Explosion &explosion, + DynamicFunction::Kind kind) override { + auto *value = explosion.claimNext(); + assert(explosion.empty()); + if (dynamicFunction) { + assert(dynamicFunction->kind == kind); + if (dynamicFunction->pointer) { + assert(dynamicFunction->pointer == value); + } else { + dynamicFunction->pointer = value; + } + return; + } + dynamicFunction = {kind, /*pointer*/ value, /*context*/ nullptr}; + } + void addSelf(Explosion &explosion) override { + auto *value = explosion.claimNext(); + assert(explosion.empty()); + Self::Kind kind = [&](SILFunctionTypeRepresentation representation) { + switch (representation) { + case SILFunctionTypeRepresentation::Method: + return Self::Kind::Method; + case SILFunctionTypeRepresentation::WitnessMethod: + return Self::Kind::WitnessMethod; + default: + llvm_unreachable("representation does not have a self"); + } + }(origType->getRepresentation()); + if (self) { + assert(self->kind == kind); + if (self->value) { + assert(self->value == value); + } else { + self->value = value; + } + return; + } + self = {kind, value}; + + Explosion toSave; + toSave.add(value); + auto fieldLayout = layout.getLocalContextLayout(); + saveValue(fieldLayout, toSave); + } + void addWitnessSelfMetadata(llvm::Value *value) override { + auto fieldLayout = layout.getSelfMetadataLayout(); + Explosion explosion; + explosion.add(value); + saveValue(fieldLayout, explosion); + } + void addWitnessSelfWitnessTable(llvm::Value *value) override { + auto fieldLayout = layout.getSelfWitnessTableLayout(); + Explosion explosion; + explosion.add(value); + saveValue(fieldLayout, explosion); + } + void forwardErrorResult() override { + // Nothing to do here. The error result pointer is already in the + // appropriate position. + } + bool originalParametersConsumed() override { + // The original parameters remain in the initially allocated + // %swift.context*, so they have always already been consumed. + return true; + } + void addPolymorphicArguments(Explosion polyArgs) override { + if (polyArgs.size() == 0) { + return; + } + assert(layout.hasBindings()); + auto bindingsLayout = layout.getBindingsLayout(); + auto bindingsAddr = + bindingsLayout.project(subIGF, context, /*offsets*/ None); + layout.getBindings().save(subIGF, bindingsAddr, polyArgs); + } + llvm::CallInst *createCall(FunctionPointer &fnPtr) override { + Explosion asyncExplosion; + asyncExplosion.add(llvm::Constant::getNullValue(subIGF.IGM.SwiftTaskPtrTy)); + asyncExplosion.add( + llvm::Constant::getNullValue(subIGF.IGM.SwiftExecutorPtrTy)); + asyncExplosion.add(contextBuffer); + if (dynamicFunction && + dynamicFunction->kind == DynamicFunction::Kind::PartialApply) { + assert(dynamicFunction->context); + asyncExplosion.add(dynamicFunction->context); + } + + return subIGF.Builder.CreateCall(fnPtr, asyncExplosion.claimAll()); + } + void createReturn(llvm::CallInst *call) override { + subIGF.Builder.CreateRetVoid(); + } + void end() override { + assert(context.isValid()); + super::end(); + } +}; +std::unique_ptr +getPartialApplicationForwarderEmission( + IRGenModule &IGM, IRGenFunction &subIGF, llvm::Function *fwd, + const Optional &staticFnPtr, bool calleeHasContext, + const Signature &origSig, CanSILFunctionType origType, + CanSILFunctionType substType, CanSILFunctionType outType, + SubstitutionMap subs, HeapLayout const *layout, + ArrayRef conventions) { + if (origType->isAsync()) { + return std::make_unique( + IGM, subIGF, fwd, staticFnPtr, calleeHasContext, origSig, origType, + substType, outType, subs, layout, conventions); + } else { + return std::make_unique( + IGM, subIGF, fwd, staticFnPtr, calleeHasContext, origSig, origType, + substType, outType, subs, layout, conventions); + } +} + +} // end anonymous namespace + +/// Emit the forwarding stub function for a partial application. +/// +/// If 'layout' is null, there is a single captured value of +/// Swift-refcountable type that is being used directly as the +/// context object. +static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM, + const Optional &staticFnPtr, + bool calleeHasContext, + const Signature &origSig, + CanSILFunctionType origType, + CanSILFunctionType substType, + CanSILFunctionType outType, + SubstitutionMap subs, + HeapLayout const *layout, + ArrayRef conventions) { + auto outSig = IGM.getSignature(outType); + llvm::AttributeList outAttrs = outSig.getAttributes(); + llvm::FunctionType *fwdTy = outSig.getType(); + SILFunctionConventions outConv(outType, IGM.getSILModule()); + + StringRef FnName; + if (staticFnPtr) + FnName = staticFnPtr->getPointer()->getName(); + + IRGenMangler Mangler; + std::string thunkName = Mangler.manglePartialApplyForwarder(FnName); + + // FIXME: Maybe cache the thunk by function and closure types?. + llvm::Function *fwd = + llvm::Function::Create(fwdTy, llvm::Function::InternalLinkage, + llvm::StringRef(thunkName), &IGM.Module); + fwd->setCallingConv(outSig.getCallingConv()); + + fwd->setAttributes(outAttrs); + // Merge initial attributes with outAttrs. + llvm::AttrBuilder b; + IGM.constructInitialFnAttributes(b); + fwd->addAttributes(llvm::AttributeList::FunctionIndex, b); + + IRGenFunction subIGF(IGM, fwd); + if (IGM.DebugInfo) + IGM.DebugInfo->emitArtificialFunction(subIGF, fwd); + + auto emission = getPartialApplicationForwarderEmission( + IGM, subIGF, fwd, staticFnPtr, calleeHasContext, origSig, origType, + substType, outType, subs, layout, conventions); + emission->begin(); + emission->gatherArgumentsFromApply(); struct AddressToDeallocate { SILType Type; @@ -930,10 +1375,15 @@ static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM, Address data; unsigned nextCapturedField = 0; if (!layout) { - rawData = origParams.claimNext(); + rawData = emission->getContext(); } else if (!layout->isKnownEmpty()) { - rawData = origParams.claimNext(); + rawData = emission->getContext(); data = layout->emitCastTo(subIGF, rawData); + if (origType->isAsync()) { + // Async layouts contain the size of the needed async context as their + // first element. It is not a parameter and needs to be skipped. + ++nextCapturedField; + } // Restore type metadata bindings, if we have them. if (layout->hasBindings()) { @@ -949,7 +1399,7 @@ static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM, // or there's an error result. } else if (outType->getRepresentation()==SILFunctionTypeRepresentation::Thick || outType->hasErrorResult()) { - llvm::Value *contextPtr = origParams.claimNext(); (void)contextPtr; + llvm::Value *contextPtr = emission->getContext(); (void)contextPtr; assert(contextPtr->getType() == IGM.RefCountedPtrTy); } @@ -1008,6 +1458,8 @@ static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM, // arguments come before this. bool isWitnessMethodCallee = origType->getRepresentation() == SILFunctionTypeRepresentation::WitnessMethod; + bool isMethodCallee = + origType->getRepresentation() == SILFunctionTypeRepresentation::Method; Explosion witnessMethodSelfValue; #endif @@ -1058,7 +1510,7 @@ static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM, // If there is a context argument, it comes after the polymorphic // arguments. - auto argIndex = args.size(); + auto argIndex = emission->getCurrentArgumentIndex(); if (haveContextArgument) argIndex += polyArgs.size(); @@ -1084,12 +1536,13 @@ static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM, } else { argValue = subIGF.Builder.CreateBitCast(rawData, expectedArgTy); } - args.add(argValue); + emission->addArgument(argValue); // If there's a data pointer required, grab it and load out the // extra, previously-curried parameters. } else { unsigned origParamI = outType->getParameters().size(); + unsigned extraFieldIndex = 0; assert(layout->getElements().size() == conventions.size() && "conventions don't match context layout"); @@ -1195,18 +1648,41 @@ static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM, if (hasPolymorphicParams) bindPolymorphicParameter(subIGF, origType, substType, param, origParamI); - emitApplyArgument(subIGF, - origType, origParamInfo, - substType, substType->getParameters()[origParamI], - param, origParam); - bool isWitnessMethodCalleeSelf = (isWitnessMethodCallee && + emitApplyArgument(subIGF, origType, origParamInfo, substType, + emission->getParameterInfo(origParamI), param, + origParam); + bool isWitnessMethodCalleeSelf = (emission->isWitnessMethodCallee && origParamI + 1 == origType->getParameters().size()); - needsAllocas |= addNativeArgument( - subIGF, origParam, origType, origParamInfo, - isWitnessMethodCalleeSelf ? witnessMethodSelfValue : args, false); + Explosion arg; + needsAllocas |= emission->transformArgumentToNative( + origParamInfo, origParam, + isWitnessMethodCalleeSelf ? emission->witnessMethodSelfValue : arg); + if (!isWitnessMethodCalleeSelf) { + emission->addArgument(arg, origParamI); + } ++origParamI; } else { - args.add(param.claimAll()); + switch (extraFieldIndex) { + case 0: + emission->addDynamicFunctionContext( + param, emission->isWitnessMethodCallee + ? PartialApplicationForwarderEmission:: + DynamicFunctionKind::Witness + : PartialApplicationForwarderEmission:: + DynamicFunctionKind::PartialApply); + break; + case 1: + emission->addDynamicFunctionPointer( + param, emission->isWitnessMethodCallee + ? PartialApplicationForwarderEmission:: + DynamicFunctionKind::Witness + : PartialApplicationForwarderEmission:: + DynamicFunctionKind::PartialApply); + break; + default: + llvm_unreachable("unexpected extra field in thick context"); + } + ++extraFieldIndex; } } @@ -1244,7 +1720,7 @@ static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM, // The dynamic function pointer is packed "last" into the context, // and we pulled it out as an argument. Just pop it off. - auto fnPtr = args.takeLast(); + auto fnPtr = emission->getDynamicFunctionPointer(); // It comes out of the context as an i8*. Cast to the function type. fnPtr = subIGF.Builder.CreateBitCast(fnPtr, fnTy); @@ -1266,15 +1742,15 @@ static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM, // In either case, it's the last thing in 'args'. llvm::Value *fnContext = nullptr; if (haveContextArgument) - fnContext = args.takeLast(); + fnContext = emission->getDynamicFunctionContext(); - polyArgs.transferInto(args, polyArgs.size()); + emission->addPolymorphicArguments(std::move(polyArgs)); // If we have a witness method call, the inner context is the // witness table. Metadata for Self is derived inside the partial // application thunk and doesn't need to be stored in the outer // context. - if (isWitnessMethodCallee) { + if (emission->isWitnessMethodCallee) { assert(fnContext->getType() == IGM.Int8PtrTy); llvm::Value *wtable = subIGF.Builder.CreateBitCast( fnContext, IGM.WitnessTablePtrTy); @@ -1283,35 +1759,45 @@ static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM, // Okay, this is where the callee context goes. } else if (fnContext) { - args.add(fnContext); + Explosion explosion; + explosion.add(fnContext); + if (emission->isMethodCallee) { + emission->addSelf(explosion); + } else { + emission->addDynamicFunctionContext( + explosion, emission->isWitnessMethodCallee + ? PartialApplicationForwarderEmission:: + DynamicFunctionKind::Witness + : PartialApplicationForwarderEmission:: + DynamicFunctionKind::PartialApply); + } // Pass a placeholder for thin function calls. } else if (origType->hasErrorResult()) { - args.add(llvm::UndefValue::get(IGM.RefCountedPtrTy)); + emission->addArgument(llvm::UndefValue::get(IGM.RefCountedPtrTy)); } // Add the witness methods self argument before the error parameter after the // polymorphic arguments. - if (isWitnessMethodCallee) - witnessMethodSelfValue.transferInto(args, witnessMethodSelfValue.size()); + if (emission->isWitnessMethodCallee) + emission->addSelf(emission->witnessMethodSelfValue); // Pass down the error result. if (origType->hasErrorResult()) { - llvm::Value *errorResultPtr = origParams.claimNext(); - args.add(errorResultPtr); + emission->forwardErrorResult(); } - assert(origParams.empty()); + assert(emission->originalParametersConsumed()); - if (isWitnessMethodCallee) { + if (emission->isWitnessMethodCallee) { assert(witnessMetadata.SelfMetadata->getType() == IGM.TypeMetadataPtrTy); - args.add(witnessMetadata.SelfMetadata); + emission->addWitnessSelfMetadata(witnessMetadata.SelfMetadata); assert(witnessMetadata.SelfWitnessTable->getType() == IGM.WitnessTablePtrTy); - args.add(witnessMetadata.SelfWitnessTable); + emission->addWitnessSelfWitnessTable(witnessMetadata.SelfWitnessTable); } - llvm::CallInst *call = subIGF.Builder.CreateCall(fnPtr, args.claimAll()); - + llvm::CallInst *call = emission->createCall(fnPtr); + if (addressesToDeallocate.empty() && !needsAllocas && (!consumesContext || !dependsOnContextLifetime)) call->setTailCall(); @@ -1328,52 +1814,8 @@ static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM, subIGF.emitNativeStrongRelease(rawData, subIGF.getDefaultAtomicity()); } - // Reabstract the result value as substituted. - SILFunctionConventions origConv(origType, IGM.getSILModule()); - auto &outResultTI = IGM.getTypeInfo( - outConv.getSILResultType(IGM.getMaximalTypeExpansionContext())); - auto &nativeResultSchema = outResultTI.nativeReturnValueSchema(IGM); - if (call->getType()->isVoidTy()) { - if (!resultValueAddr.isValid()) - subIGF.Builder.CreateRetVoid(); - else { - // Okay, we have called a function that expects an indirect return type - // but the partially applied return type is direct. - assert(!nativeResultSchema.requiresIndirect()); - Explosion loadedResult; - cast(outResultTI) - .loadAsTake(subIGF, resultValueAddr, loadedResult); - Explosion nativeResult = nativeResultSchema.mapIntoNative( - IGM, subIGF, loadedResult, - outConv.getSILResultType(IGM.getMaximalTypeExpansionContext()), - false); - outResultTI.deallocateStack( - subIGF, resultValueAddr, - outConv.getSILResultType(IGM.getMaximalTypeExpansionContext())); - if (nativeResult.size() == 1) - subIGF.Builder.CreateRet(nativeResult.claimNext()); - else { - llvm::Value *nativeAgg = - llvm::UndefValue::get(nativeResultSchema.getExpandedType(IGM)); - for (unsigned i = 0, e = nativeResult.size(); i != e; ++i) { - auto *elt = nativeResult.claimNext(); - nativeAgg = subIGF.Builder.CreateInsertValue(nativeAgg, elt, i); - } - subIGF.Builder.CreateRet(nativeAgg); - } - } - } else { - llvm::Value *callResult = call; - // If the result type is dependent on a type parameter we might have to - // cast to the result type - it could be substituted. - if (origConv.getSILResultType(IGM.getMaximalTypeExpansionContext()) - .hasTypeParameter()) { - auto ResType = fwd->getReturnType(); - if (ResType != callResult->getType()) - callResult = subIGF.coerceValue(callResult, ResType, subIGF.IGM.DataLayout); - } - subIGF.Builder.CreateRet(callResult); - } + emission->createReturn(call); + emission->end(); return fwd; } @@ -1396,6 +1838,19 @@ Optional irgen::emitFunctionPartialApplication( SmallVector argValTypes; SmallVector argConventions; + if (origType->isAsync()) { + // Store the size of the partially applied async function's context here so + // that it can be recovered by at the apply site. + auto int32ASTType = + BuiltinIntegerType::get(32, IGF.IGM.IRGen.SIL.getASTContext()) + ->getCanonicalType(); + auto int32SILType = SILType::getPrimitiveObjectType(int32ASTType); + const TypeInfo &int32TI = IGF.IGM.getTypeInfo(int32SILType); + argValTypes.push_back(int32SILType); + argTypeInfos.push_back(&int32TI); + argConventions.push_back(ParameterConvention::Direct_Unowned); + } + // A context's HeapLayout stores all of the partially applied args. // A HeapLayout is "fixed" if all of its fields have a fixed layout. // Otherwise the HeapLayout is "non-fixed". @@ -1425,6 +1880,23 @@ Optional irgen::emitFunctionPartialApplication( auto bindings = NecessaryBindings::forPartialApplyForwarder( IGF.IGM, origType, subs, considerParameterSources); + if (origType->isAsync()) { + // The size of the async context needs to be available at the apply site. + // + // TODO: In the "single refcounted context" case the async "function + // pointer" (actually a pointer to a + // constant { + // /*context size*/ i32, + // /*relative address of function*/ i32 + // } + // rather than a pointer directly to the function) would be able to + // provide the async context size required. At the apply site, it is + // possible to determine whether we're in the "single refcounted + // context" by looking at the metadata of a nonnull context pointer + // and checking whether it is TargetHeapMetadata. + hasSingleSwiftRefcountedContext = No; + } + if (!bindings.empty()) { hasSingleSwiftRefcountedContext = No; auto bindingsSize = bindings.getBufferSize(IGF.IGM); @@ -1610,8 +2082,8 @@ Optional irgen::emitFunctionPartialApplication( && argTypeInfos.size() == argConventions.size() && "argument info lists out of sync"); HeapLayout layout(IGF.IGM, LayoutStrategy::Optimal, argValTypes, argTypeInfos, - /*typeToFill*/ nullptr, - std::move(bindings)); + /*typeToFill*/ nullptr, std::move(bindings), + /*bindingsIndex*/ origType->isAsync() ? 1 : 0); llvm::Value *data; @@ -1642,7 +2114,16 @@ Optional irgen::emitFunctionPartialApplication( Address dataAddr = layout.emitCastTo(IGF, data); unsigned i = 0; - + + if (origType->isAsync()) { + auto &fieldLayout = layout.getElement(i); + auto &fieldTI = fieldLayout.getType(); + Address fieldAddr = fieldLayout.project(IGF, dataAddr, offsets); + cast(fieldTI).initialize(IGF, args, fieldAddr, + isOutlined); + ++i; + } + // Store necessary bindings, if we have them. if (layout.hasBindings()) { auto &bindingsLayout = layout.getElement(i); diff --git a/lib/IRGen/GenFunc.h b/lib/IRGen/GenFunc.h index 04bc2e4732182..4c839cb6b509f 100644 --- a/lib/IRGen/GenFunc.h +++ b/lib/IRGen/GenFunc.h @@ -53,6 +53,8 @@ namespace irgen { ArrayRef argTypes, SubstitutionMap subs, CanSILFunctionType origType, CanSILFunctionType substType, CanSILFunctionType outType, Explosion &out, bool isOutlined); + CanType getArgumentLoweringType(CanType type, SILParameterInfo paramInfo, + bool isNoEscape); } // end namespace irgen } // end namespace swift diff --git a/lib/IRGen/GenHeap.cpp b/lib/IRGen/GenHeap.cpp index ec3d205149d3a..4d9cd2d13347b 100644 --- a/lib/IRGen/GenHeap.cpp +++ b/lib/IRGen/GenHeap.cpp @@ -257,18 +257,19 @@ HeapLayout::HeapLayout(IRGenModule &IGM, LayoutStrategy strategy, ArrayRef fieldTypes, ArrayRef fieldTypeInfos, llvm::StructType *typeToFill, - NecessaryBindings &&bindings) - : StructLayout(IGM, /*decl=*/nullptr, LayoutKind::HeapObject, strategy, - fieldTypeInfos, typeToFill), - ElementTypes(fieldTypes.begin(), fieldTypes.end()), - Bindings(std::move(bindings)) -{ + NecessaryBindings &&bindings, unsigned bindingsIndex) + : StructLayout(IGM, /*decl=*/nullptr, LayoutKind::HeapObject, strategy, + fieldTypeInfos, typeToFill), + ElementTypes(fieldTypes.begin(), fieldTypes.end()), + Bindings(std::move(bindings)), BindingsIndex(bindingsIndex) { #ifndef NDEBUG assert(fieldTypeInfos.size() == fieldTypes.size() && "type infos don't match types"); if (!Bindings.empty()) { - assert(fieldTypeInfos.size() >= 1 && "no field for bindings"); - auto fixedBindingsField = dyn_cast(fieldTypeInfos[0]); + assert(fieldTypeInfos.size() >= (bindingsIndex + 1) && + "no field for bindings"); + auto fixedBindingsField = + dyn_cast(fieldTypeInfos[bindingsIndex]); assert(fixedBindingsField && "bindings field is not fixed size"); assert(fixedBindingsField->getFixedSize() @@ -425,7 +426,8 @@ static llvm::Function *createDtorFn(IRGenModule &IGM, if (layout.hasBindings()) { // The type metadata bindings should be at a fixed offset, so we can pass // None for NonFixedOffsets. If we didn't, we'd have a chicken-egg problem. - auto bindingsAddr = layout.getElement(0).project(IGF, structAddr, None); + auto bindingsAddr = layout.getElement(layout.getBindingsIndex()) + .project(IGF, structAddr, None); layout.getBindings().restore(IGF, bindingsAddr, MetadataState::Complete); } @@ -1731,12 +1733,12 @@ void IRGenFunction::emit##ID(llvm::Value *value, Address src) { \ #undef DEFINE_STORE_WEAK_OP #undef NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE_HELPER -llvm::Value *IRGenFunction::getLocalSelfMetadata() { - assert(LocalSelf && "no local self metadata"); +llvm::Value *IRGenFunction::getDynamicSelfMetadata() { + assert(SelfValue && "no local self metadata"); // If we already have a metatype, just return it. if (SelfKind == SwiftMetatype) - return LocalSelf; + return SelfValue; // We need to materialize a metatype. Emit the code for that once at the // top of the function and cache the result. @@ -1752,9 +1754,9 @@ llvm::Value *IRGenFunction::getLocalSelfMetadata() { // with the correct value. llvm::IRBuilderBase::InsertPointGuard guard(Builder); - auto insertPt = isa(LocalSelf) + auto insertPt = isa(SelfValue) ? std::next(llvm::BasicBlock::iterator( - cast(LocalSelf))) + cast(SelfValue))) : CurFn->getEntryBlock().begin(); Builder.SetInsertPoint(&CurFn->getEntryBlock(), insertPt); @@ -1762,19 +1764,19 @@ llvm::Value *IRGenFunction::getLocalSelfMetadata() { case SwiftMetatype: llvm_unreachable("Already handled"); case ObjCMetatype: - LocalSelf = emitObjCMetadataRefForMetadata(*this, LocalSelf); + SelfValue = emitObjCMetadataRefForMetadata(*this, SelfValue); SelfKind = SwiftMetatype; break; case ObjectReference: - LocalSelf = emitDynamicTypeOfHeapObject(*this, LocalSelf, + SelfValue = emitDynamicTypeOfHeapObject(*this, SelfValue, MetatypeRepresentation::Thick, - SILType::getPrimitiveObjectType(LocalSelfType), + SILType::getPrimitiveObjectType(SelfType), /*allow artificial*/ false); SelfKind = SwiftMetatype; break; } - return LocalSelf; + return SelfValue; } /// Given a non-tagged object pointer, load a pointer to its class object. diff --git a/lib/IRGen/GenHeap.h b/lib/IRGen/GenHeap.h index 7bc4792877bf0..2b35a56affd3a 100644 --- a/lib/IRGen/GenHeap.h +++ b/lib/IRGen/GenHeap.h @@ -37,6 +37,7 @@ namespace irgen { class HeapLayout : public StructLayout { SmallVector ElementTypes; NecessaryBindings Bindings; + unsigned BindingsIndex; mutable llvm::Constant *privateMetadata = nullptr; public: @@ -44,8 +45,8 @@ class HeapLayout : public StructLayout { ArrayRef elementTypes, ArrayRef elementTypeInfos, llvm::StructType *typeToFill = 0, - NecessaryBindings &&bindings = {}); - + NecessaryBindings &&bindings = {}, unsigned bindingsIndex = 0); + /// True if the heap object carries type bindings. /// /// If true, the first element of the heap layout will be the type metadata @@ -58,6 +59,12 @@ class HeapLayout : public StructLayout { return Bindings; } + unsigned getBindingsIndex() const { return BindingsIndex; } + + unsigned getIndexAfterBindings() const { + return BindingsIndex + (hasBindings() ? 1 : 0); + } + /// Get the types of the elements. ArrayRef getElementTypes() const { return ElementTypes; diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index 1d0814b0874a0..96e5cf9949bba 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -1683,7 +1683,7 @@ namespace { void emitNonoverriddenMethod(SILDeclRef fn) { // TODO: Derivative functions do not distinguish themselves in the mangled // names of method descriptor symbols yet, causing symbol name collisions. - if (fn.derivativeFunctionIdentifier) + if (fn.getDerivativeFunctionIdentifier()) return; HasNonoverriddenMethods = true; @@ -5045,6 +5045,7 @@ SpecialProtocol irgen::getSpecialProtocolID(ProtocolDecl *P) { case KnownProtocolKind::AdditiveArithmetic: case KnownProtocolKind::Differentiable: case KnownProtocolKind::FloatingPoint: + case KnownProtocolKind::Actor: // SWIFT_ENABLE_TENSORFLOW case KnownProtocolKind::PointwiseMultiplicative: case KnownProtocolKind::ElementaryFunctions: diff --git a/lib/IRGen/GenObjC.cpp b/lib/IRGen/GenObjC.cpp index c46cadcaa5745..d31ee8328e724 100644 --- a/lib/IRGen/GenObjC.cpp +++ b/lib/IRGen/GenObjC.cpp @@ -833,11 +833,13 @@ static llvm::Function *emitObjCPartialApplicationForwarder(IRGenModule &IGM, } // Prepare the call to the underlying method. - CallEmission emission(subIGF, - getObjCMethodCallee(subIGF, method, self, + auto emission = getCallEmission( + subIGF, self, + getObjCMethodCallee(subIGF, method, self, CalleeInfo(origMethodType, origMethodType, {}))); + emission->begin(); - emission.setArgs(translatedParams, false); + emission->setArgs(translatedParams, false, /*witnessMetadata*/ nullptr); // Cleanup that always has to occur after the function call. auto cleanup = [&]{ @@ -855,14 +857,16 @@ static llvm::Function *emitObjCPartialApplicationForwarder(IRGenModule &IGM, if (indirectedDirectResult) { Address addr = indirectedResultTI->getAddressForPointer(indirectedDirectResult); - emission.emitToMemory(addr, *indirectedResultTI, false); + emission->emitToMemory(addr, *indirectedResultTI, false); + emission->end(); cleanup(); subIGF.Builder.CreateRetVoid(); } else { Explosion result; - emission.emitToExplosion(result, false); + emission->emitToExplosion(result, false); + emission->end(); cleanup(); - auto &callee = emission.getCallee(); + auto &callee = emission->getCallee(); auto resultType = callee.getOrigFunctionType()->getDirectFormalResultsType( IGM.getSILModule(), IGM.getMaximalTypeExpansionContext()); subIGF.emitScalarReturn(resultType, resultType, result, diff --git a/lib/IRGen/GenPointerAuth.cpp b/lib/IRGen/GenPointerAuth.cpp index fa6821b267900..aeae73999d0d8 100644 --- a/lib/IRGen/GenPointerAuth.cpp +++ b/lib/IRGen/GenPointerAuth.cpp @@ -400,6 +400,17 @@ PointerAuthEntity::getDeclDiscriminator(IRGenModule &IGM) const { assert(!constant.isForeign && "discriminator for foreign declaration not supported yet!"); + // Special case: methods that are witnesses to Actor.enqueue(partialTask:) + // have their own discriminator, which is shared across all actor classes. + if (constant.hasFuncDecl()) { + auto func = dyn_cast(constant.getFuncDecl()); + if (func->isActorEnqueuePartialTaskWitness()) { + cache = IGM.getSize( + Size(SpecialPointerAuthDiscriminators::ActorEnqueuePartialTask)); + return cache; + } + } + auto mangling = constant.mangle(); cache = getDiscriminatorForString(IGM, mangling); return cache; diff --git a/lib/IRGen/GenProto.cpp b/lib/IRGen/GenProto.cpp index 2795553485a5a..1231d799fa855 100644 --- a/lib/IRGen/GenProto.cpp +++ b/lib/IRGen/GenProto.cpp @@ -54,11 +54,13 @@ #include "CallEmission.h" #include "ConformanceDescription.h" #include "ConstantBuilder.h" +#include "EntryPointArgumentEmission.h" #include "EnumPayload.h" #include "Explosion.h" #include "FixedTypeInfo.h" #include "Fulfillment.h" #include "GenArchetype.h" +#include "GenCall.h" #include "GenCast.h" #include "GenClass.h" #include "GenEnum.h" @@ -73,6 +75,7 @@ #include "IRGenFunction.h" #include "IRGenMangler.h" #include "IRGenModule.h" +#include "LoadableTypeInfo.h" #include "MetadataPath.h" #include "MetadataRequest.h" #include "NecessaryBindings.h" @@ -486,7 +489,8 @@ class EmitPolymorphicParameters : public PolymorphicConvention { public: EmitPolymorphicParameters(IRGenFunction &IGF, SILFunction &Fn); - void emit(Explosion &in, WitnessMetadata *witnessMetadata, + void emit(EntryPointArgumentEmission &emission, + WitnessMetadata *witnessMetadata, const GetParameterFn &getParameter); private: @@ -496,7 +500,8 @@ class EmitPolymorphicParameters : public PolymorphicConvention { /// Fulfill local type data from any extra information associated with /// the given source. - void bindExtraSource(const MetadataSource &source, Explosion &in, + void bindExtraSource(const MetadataSource &source, + EntryPointArgumentEmission &emission, WitnessMetadata *witnessMetadata); void bindParameterSources(const GetParameterFn &getParameter); @@ -525,9 +530,9 @@ CanType EmitPolymorphicParameters::getArgTypeInContext(unsigned paramIndex) cons IGM.getSILModule(), FnType, IGM.getMaximalTypeExpansionContext())); } -void EmitPolymorphicParameters::bindExtraSource(const MetadataSource &source, - Explosion &in, - WitnessMetadata *witnessMetadata) { +void EmitPolymorphicParameters::bindExtraSource( + const MetadataSource &source, EntryPointArgumentEmission &emission, + WitnessMetadata *witnessMetadata) { switch (source.getKind()) { case MetadataSource::Kind::Metadata: case MetadataSource::Kind::ClassPointer: @@ -537,7 +542,7 @@ void EmitPolymorphicParameters::bindExtraSource(const MetadataSource &source, case MetadataSource::Kind::GenericLValueMetadata: { CanType argTy = getArgTypeInContext(source.getParamIndex()); - llvm::Value *metadata = in.claimNext(); + llvm::Value *metadata = emission.getNextPolymorphicParameterAsMetadata(); setTypeMetadataName(IGF.IGM, metadata, argTy); IGF.bindLocalTypeDataFromTypeMetadata(argTy, IsExact, metadata, @@ -901,6 +906,16 @@ static bool isDependentConformance( IRGenModule &IGM, const RootProtocolConformance *rootConformance, llvm::SmallPtrSet &visited){ + // Some Builtin conformances are dependent. + if (auto builtin = dyn_cast(rootConformance)) { + // Tuples are conditionally conformed to any builtin conformance. + if (builtin->getType()->is()) + return true; + + // Otherwise, this builtin conformance is not dependant. + return false; + } + // Self-conformances are never dependent. auto conformance = dyn_cast(rootConformance); if (!conformance) @@ -966,12 +981,10 @@ static bool isSynthesizedNonUnique(const RootProtocolConformance *conformance) { static llvm::Value * emitConditionalConformancesBuffer(IRGenFunction &IGF, const ProtocolConformance *substConformance) { - auto rootConformance = - dyn_cast(substConformance->getRootConformance()); + auto rootConformance = substConformance->getRootConformance(); - // Not a normal conformance means no conditional requirements means no need - // for a buffer. - if (!rootConformance) + if (!isa(rootConformance) && + !isa(rootConformance)) return llvm::UndefValue::get(IGF.IGM.WitnessTablePtrPtrTy); // Pointers to the witness tables, in the right order, which will be included @@ -981,9 +994,18 @@ emitConditionalConformancesBuffer(IRGenFunction &IGF, auto subMap = substConformance->getSubstitutions(IGF.IGM.getSwiftModule()); SILWitnessTable::enumerateWitnessTableConditionalConformances( - rootConformance, [&](unsigned, CanType type, ProtocolDecl *proto) { + rootConformance, [&](unsigned i, CanType type, ProtocolDecl *proto) { auto substType = type.subst(subMap)->getCanonicalType(); auto reqConformance = subMap.lookupConformance(type, proto); + + // Builtin conformances don't have a substitution list, so accomodate + // for that here. + if (auto builtin + = dyn_cast(rootConformance)) { + substType = type->getCanonicalType(); + reqConformance = builtin->getConformances()[i]; + } + assert(reqConformance && "conditional conformance must be valid"); tables.push_back(emitWitnessTableRef(IGF, substType, reqConformance)); @@ -1132,8 +1154,10 @@ class AccessorConformanceInfo : public ConformanceInfo { llvm::Value *getTable(IRGenFunction &IGF, llvm::Value **typeMetadataCache) const override { // If we're looking up a dependent type, we can't cache the result. + // If the conformance is builtin, go ahead and emit an accessor call. if (Conformance->getType()->hasArchetype() || - Conformance->getType()->hasDynamicSelfType()) { + Conformance->getType()->hasDynamicSelfType() || + isa(Conformance)) { return emitWitnessTableAccessorCall(IGF, Conformance, typeMetadataCache); } @@ -2220,12 +2244,12 @@ bool irgen::hasPolymorphicParameters(CanSILFunctionType ty) { } /// Emit a polymorphic parameters clause, binding all the metadata necessary. -void EmitPolymorphicParameters::emit(Explosion &in, +void EmitPolymorphicParameters::emit(EntryPointArgumentEmission &emission, WitnessMetadata *witnessMetadata, const GetParameterFn &getParameter) { // Collect any early sources and bind local type data from them. for (auto &source : getSources()) { - bindExtraSource(source, in, witnessMetadata); + bindExtraSource(source, emission, witnessMetadata); } auto getInContext = [&](CanType type) -> CanType { @@ -2234,7 +2258,7 @@ void EmitPolymorphicParameters::emit(Explosion &in, // Collect any concrete type metadata that's been passed separately. enumerateUnfulfilledRequirements([&](GenericRequirement requirement) { - auto value = in.claimNext(); + llvm::Value *value = emission.getNextPolymorphicParameter(requirement); bindGenericRequirement(IGF, requirement, value, MetadataState::Complete, getInContext); }); @@ -2532,8 +2556,10 @@ MetadataResponse MetadataPath::followComponent(IRGenFunction &IGF, if (!source) return MetadataResponse(); - auto sourceMetadata = IGF.emitTypeMetadataRef(sourceType); - auto associatedMetadata = IGF.emitTypeMetadataRef(sourceKey.Type); + auto sourceMetadata = + IGF.emitAbstractTypeMetadataRef(sourceType); + auto associatedMetadata = + IGF.emitAbstractTypeMetadataRef(sourceKey.Type); auto sourceWTable = source.getMetadata(); AssociatedConformance associatedConformanceRef(sourceProtocol, @@ -2631,21 +2657,21 @@ void MetadataPath::print(llvm::raw_ostream &out) const { /// Collect any required metadata for a witness method from the end of /// the given parameter list. -void irgen::collectTrailingWitnessMetadata(IRGenFunction &IGF, - SILFunction &fn, - Explosion ¶ms, - WitnessMetadata &witnessMetadata) { +void irgen::collectTrailingWitnessMetadata( + IRGenFunction &IGF, SILFunction &fn, + NativeCCEntryPointArgumentEmission &emission, + WitnessMetadata &witnessMetadata) { assert(fn.getLoweredFunctionType()->getRepresentation() == SILFunctionTypeRepresentation::WitnessMethod); - llvm::Value *wtable = params.takeLast(); + llvm::Value *wtable = emission.getSelfWitnessTable(); assert(wtable->getType() == IGF.IGM.WitnessTablePtrTy && "parameter signature mismatch: witness metadata didn't " "end in witness table?"); wtable->setName("SelfWitnessTable"); witnessMetadata.SelfWitnessTable = wtable; - llvm::Value *metatype = params.takeLast(); + llvm::Value *metatype = emission.getSelfMetadata(); assert(metatype->getType() == IGF.IGM.TypeMetadataPtrTy && "parameter signature mismatch: witness metadata didn't " "end in metatype?"); @@ -2654,12 +2680,12 @@ void irgen::collectTrailingWitnessMetadata(IRGenFunction &IGF, } /// Perform all the bindings necessary to emit the given declaration. -void irgen::emitPolymorphicParameters(IRGenFunction &IGF, - SILFunction &Fn, - Explosion &in, +void irgen::emitPolymorphicParameters(IRGenFunction &IGF, SILFunction &Fn, + EntryPointArgumentEmission &emission, WitnessMetadata *witnessMetadata, const GetParameterFn &getParameter) { - EmitPolymorphicParameters(IGF, Fn).emit(in, witnessMetadata, getParameter); + EmitPolymorphicParameters(IGF, Fn).emit(emission, witnessMetadata, + getParameter); } /// Given an array of polymorphic arguments as might be set up by @@ -2683,36 +2709,63 @@ void irgen::emitPolymorphicParametersFromArray(IRGenFunction &IGF, Size NecessaryBindings::getBufferSize(IRGenModule &IGM) const { // We need one pointer for each archetype or witness table. - return IGM.getPointerSize() * Requirements.size(); + return IGM.getPointerSize() * size(); } void NecessaryBindings::restore(IRGenFunction &IGF, Address buffer, MetadataState metadataState) const { - bindFromGenericRequirementsBuffer(IGF, Requirements.getArrayRef(), buffer, + bindFromGenericRequirementsBuffer(IGF, getRequirements(), buffer, metadataState, - [&](CanType type) { return type;}); + [&](CanType type) { return type; }); +} + +template +static void save(const NecessaryBindings &bindings, IRGenFunction &IGF, + Address buffer, Transform transform) { + emitInitOfGenericRequirementsBuffer( + IGF, bindings.getRequirements(), buffer, + [&](GenericRequirement requirement) -> llvm::Value * { + CanType type = requirement.TypeParameter; + if (auto protocol = requirement.Protocol) { + if (auto archetype = dyn_cast(type)) { + auto wtable = + emitArchetypeWitnessTableRef(IGF, archetype, protocol); + return transform(requirement, wtable); + } else { + auto conformance = bindings.getConformance(requirement); + auto wtable = emitWitnessTableRef(IGF, type, conformance); + return transform(requirement, wtable); + } + } else { + auto metadata = IGF.emitTypeMetadataRef(type); + return transform(requirement, metadata); + } + }); +}; + +void NecessaryBindings::save(IRGenFunction &IGF, Address buffer, + Explosion &source) const { + ::save(*this, IGF, buffer, + [&](GenericRequirement requirement, + llvm::Value *expected) -> llvm::Value * { + auto *value = source.claimNext(); + assert(value == expected); + return value; + }); } void NecessaryBindings::save(IRGenFunction &IGF, Address buffer) const { - emitInitOfGenericRequirementsBuffer(IGF, Requirements.getArrayRef(), buffer, - [&](GenericRequirement requirement) -> llvm::Value* { - CanType type = requirement.TypeParameter; - if (auto protocol = requirement.Protocol) { - auto wtable = - emitArchetypeWitnessTableRef(IGF, cast(type), protocol); - return wtable; - } else { - auto metadata = IGF.emitTypeMetadataRef(type); - return metadata; - } - }); + ::save(*this, IGF, buffer, + [](GenericRequirement requirement, + llvm::Value *value) -> llvm::Value * { return value; }); } void NecessaryBindings::addTypeMetadata(CanType type) { assert(!isa(type)); // Bindings are only necessary at all if the type is dependent. - if (!type->hasArchetype()) return; + if (!type->hasArchetype() && !forAsyncFunction()) + return; // Break down structural types so that we don't eagerly pass metadata // for the structural type. Future considerations for this: @@ -2738,14 +2791,13 @@ void NecessaryBindings::addTypeMetadata(CanType type) { // Generic types are trickier, because they can require conformances. // Otherwise, just record the need for this metadata. - Requirements.insert({type, nullptr}); + addRequirement({type, nullptr}); } /// Add all the abstract conditional conformances in the specialized /// conformance to the \p requirements. -static void addAbstractConditionalRequirements( - SpecializedProtocolConformance *specializedConformance, - llvm::SetVector &requirements) { +void NecessaryBindings::addAbstractConditionalRequirements( + SpecializedProtocolConformance *specializedConformance) { auto subMap = specializedConformance->getSubstitutionMap(); auto condRequirements = specializedConformance->getConditionalRequirements(); for (auto req : condRequirements) { @@ -2757,7 +2809,7 @@ static void addAbstractConditionalRequirements( auto archetype = dyn_cast(ty); if (!archetype) continue; - requirements.insert({ty, proto}); + addRequirement({ty, proto}); } // Recursively add conditional requirements. for (auto &conf : subMap.getConformances()) { @@ -2767,27 +2819,37 @@ static void addAbstractConditionalRequirements( dyn_cast(conf.getConcrete()); if (!specializedConf) continue; - addAbstractConditionalRequirements(specializedConf, requirements); + addAbstractConditionalRequirements(specializedConf); } } void NecessaryBindings::addProtocolConformance(CanType type, ProtocolConformanceRef conf) { if (!conf.isAbstract()) { + auto concreteConformance = conf.getConcrete(); auto specializedConf = - dyn_cast(conf.getConcrete()); + dyn_cast(concreteConformance); // The partial apply forwarder does not have the context to reconstruct // abstract conditional conformance requirements. - if (forPartialApply && specializedConf) { - addAbstractConditionalRequirements(specializedConf, Requirements); + if (forPartialApply() && specializedConf) { + addAbstractConditionalRequirements(specializedConf); + } else if (forAsyncFunction()) { + ProtocolDecl *protocol = conf.getRequirement(); + GenericRequirement requirement; + requirement.TypeParameter = type; + requirement.Protocol = protocol; + std::pair pair{requirement, + conf}; + Conformances.insert(pair); + addRequirement({type, concreteConformance->getProtocol()}); } return; } - assert(isa(type)); + assert(isa(type) || forAsyncFunction()); // TODO: pass something about the root conformance necessary to // reconstruct this. - Requirements.insert({type, conf.getAbstract()}); + addRequirement({type, conf.getAbstract()}); } llvm::Value *irgen::emitWitnessTableRef(IRGenFunction &IGF, @@ -2970,10 +3032,8 @@ void EmitPolymorphicArguments::emit(SubstitutionMap subs, } } -NecessaryBindings -NecessaryBindings::forFunctionInvocations(IRGenModule &IGM, - CanSILFunctionType origType, - SubstitutionMap subs) { +NecessaryBindings NecessaryBindings::forAsyncFunctionInvocation( + IRGenModule &IGM, CanSILFunctionType origType, SubstitutionMap subs) { return computeBindings(IGM, origType, subs, false /*forPartialApplyForwarder*/); } @@ -2993,7 +3053,8 @@ NecessaryBindings NecessaryBindings::computeBindings( bool forPartialApplyForwarder, bool considerParameterSources) { NecessaryBindings bindings; - bindings.forPartialApply = forPartialApplyForwarder; + bindings.kind = + forPartialApplyForwarder ? Kind::PartialApply : Kind::AsyncFunction; // Bail out early if we don't have polymorphic parameters. if (!hasPolymorphicParameters(origType)) @@ -3028,7 +3089,11 @@ NecessaryBindings NecessaryBindings::computeBindings( continue; case MetadataSource::Kind::SelfMetadata: - bindings.addTypeMetadata(getSubstSelfType(IGM, origType, subs)); + // Async functions pass the SelfMetadata and SelfWitnessTable parameters + // along explicitly. + if (forPartialApplyForwarder) { + bindings.addTypeMetadata(getSubstSelfType(IGM, origType, subs)); + } continue; case MetadataSource::Kind::SelfWitnessTable: diff --git a/lib/IRGen/GenProto.h b/lib/IRGen/GenProto.h index f91b802841c85..dada1eee4310e 100644 --- a/lib/IRGen/GenProto.h +++ b/lib/IRGen/GenProto.h @@ -40,12 +40,14 @@ namespace swift { namespace irgen { class Address; class DynamicMetadataRequest; + class EntryPointArgumentEmission; class Explosion; class FunctionPointer; class IRGenFunction; class IRGenModule; class MetadataPath; class MetadataResponse; + class NativeCCEntryPointArgumentEmission; class ProtocolInfo; class TypeInfo; @@ -120,9 +122,10 @@ namespace irgen { /// Collect any required metadata for a witness method from the end /// of the given parameter list. - void collectTrailingWitnessMetadata(IRGenFunction &IGF, SILFunction &fn, - Explosion ¶ms, - WitnessMetadata &metadata); + void + collectTrailingWitnessMetadata(IRGenFunction &IGF, SILFunction &fn, + NativeCCEntryPointArgumentEmission ¶ms, + WitnessMetadata &metadata); using GetParameterFn = llvm::function_ref; @@ -131,12 +134,11 @@ namespace irgen { /// /// \param witnessMetadata - can be omitted if the function is /// definitely not a witness method - void emitPolymorphicParameters(IRGenFunction &IGF, - SILFunction &Fn, - Explosion &args, + void emitPolymorphicParameters(IRGenFunction &IGF, SILFunction &Fn, + EntryPointArgumentEmission &emission, WitnessMetadata *witnessMetadata, const GetParameterFn &getParameter); - + void emitPolymorphicParametersFromArray(IRGenFunction &IGF, NominalTypeDecl *typeDecl, Address array, diff --git a/lib/IRGen/GenReflection.cpp b/lib/IRGen/GenReflection.cpp index 6790794422dd0..bc3ab970407b2 100644 --- a/lib/IRGen/GenReflection.cpp +++ b/lib/IRGen/GenReflection.cpp @@ -1025,8 +1025,8 @@ class CaptureDescriptorBuilder : public ReflectionMetadataBuilder { return true; } - auto ElementTypes = Layout.getElementTypes().slice( - Layout.hasBindings() ? 1 : 0); + auto ElementTypes = + Layout.getElementTypes().slice(Layout.getIndexAfterBindings()); for (auto ElementType : ElementTypes) { auto SwiftType = ElementType.getASTType(); if (SwiftType->hasOpenedExistential()) @@ -1040,7 +1040,7 @@ class CaptureDescriptorBuilder : public ReflectionMetadataBuilder { /// We'll keep track of how many things are in the bindings struct with its /// own count in the capture descriptor. ArrayRef getElementTypes() { - return Layout.getElementTypes().slice(Layout.hasBindings() ? 1 : 0); + return Layout.getElementTypes().slice(Layout.getIndexAfterBindings()); } /// Build a map from generic parameter -> source of its metadata at runtime. diff --git a/lib/IRGen/GenType.cpp b/lib/IRGen/GenType.cpp index 1d99e48ca3f42..4ea4745806455 100644 --- a/lib/IRGen/GenType.cpp +++ b/lib/IRGen/GenType.cpp @@ -1216,7 +1216,7 @@ TypeConverter::createPrimitiveForAlignedPointer(llvm::PointerType *type, Alignment align, Alignment pointerAlignment) { SpareBitVector spareBits = IGM.TargetInfo.PointerSpareBits; - for (unsigned bit = 0; Alignment(1 << bit) != pointerAlignment; ++bit) { + for (unsigned bit = 0; Alignment(1ull << bit) != pointerAlignment; ++bit) { spareBits.setBit(bit); } @@ -1453,6 +1453,48 @@ const TypeInfo &TypeConverter::getTypeMetadataPtrTypeInfo() { return *TypeMetadataPtrTI; } +const TypeInfo &IRGenModule::getSwiftContextPtrTypeInfo() { + return Types.getSwiftContextPtrTypeInfo(); +} + +const TypeInfo &TypeConverter::getSwiftContextPtrTypeInfo() { + if (SwiftContextPtrTI) return *SwiftContextPtrTI; + SwiftContextPtrTI = createUnmanagedStorageType(IGM.SwiftContextPtrTy, + ReferenceCounting::Unknown, + /*isOptional*/false); + SwiftContextPtrTI->NextConverted = FirstType; + FirstType = SwiftContextPtrTI; + return *SwiftContextPtrTI; +} + +const TypeInfo &IRGenModule::getTaskContinuationFunctionPtrTypeInfo() { + return Types.getTaskContinuationFunctionPtrTypeInfo(); +} + +const TypeInfo &TypeConverter::getTaskContinuationFunctionPtrTypeInfo() { + if (TaskContinuationFunctionPtrTI) return *TaskContinuationFunctionPtrTI; + TaskContinuationFunctionPtrTI = createUnmanagedStorageType( + IGM.TaskContinuationFunctionPtrTy, ReferenceCounting::Unknown, + /*isOptional*/ false); + TaskContinuationFunctionPtrTI->NextConverted = FirstType; + FirstType = TaskContinuationFunctionPtrTI; + return *TaskContinuationFunctionPtrTI; +} + +const TypeInfo &IRGenModule::getSwiftExecutorPtrTypeInfo() { + return Types.getSwiftExecutorPtrTypeInfo(); +} + +const TypeInfo &TypeConverter::getSwiftExecutorPtrTypeInfo() { + if (SwiftExecutorPtrTI) return *SwiftExecutorPtrTI; + SwiftExecutorPtrTI = createUnmanagedStorageType(IGM.SwiftExecutorPtrTy, + ReferenceCounting::Unknown, + /*isOptional*/ false); + SwiftExecutorPtrTI->NextConverted = FirstType; + FirstType = SwiftExecutorPtrTI; + return *SwiftExecutorPtrTI; +} + const LoadableTypeInfo & IRGenModule::getReferenceObjectTypeInfo(ReferenceCounting refcounting) { switch (refcounting) { @@ -1872,7 +1914,7 @@ convertPrimitiveBuiltin(IRGenModule &IGM, CanType canTy) { = convertPrimitiveBuiltin(IGM, vecTy->getElementType()->getCanonicalType()); - auto llvmVecTy = llvm::VectorType::get(elementTy, vecTy->getNumElements()); + auto llvmVecTy = llvm::FixedVectorType::get(elementTy, vecTy->getNumElements()); unsigned bitSize = size.getValue() * vecTy->getNumElements() * 8; if (!llvm::isPowerOf2_32(bitSize)) bitSize = llvm::NextPowerOf2(bitSize); @@ -2381,16 +2423,16 @@ unsigned IRGenModule::getBuiltinIntegerWidth(BuiltinIntegerWidth w) { llvm_unreachable("impossible width value"); } -void IRGenFunction::setLocalSelfMetadata(CanType selfClass, - bool isExactSelfClass, - llvm::Value *value, - IRGenFunction::LocalSelfKind kind) { - assert(!LocalSelf && "already have local self metadata"); - LocalSelf = value; +void IRGenFunction::setDynamicSelfMetadata(CanType selfClass, + bool isExactSelfClass, + llvm::Value *value, + IRGenFunction::DynamicSelfKind kind) { + assert(!SelfValue && "already have local self metadata"); + SelfValue = value; assert(selfClass->getClassOrBoundGenericClass() && "self type not a class?"); - LocalSelfIsExact = isExactSelfClass; - LocalSelfType = selfClass; + SelfTypeIsExact = isExactSelfClass; + SelfType = selfClass; SelfKind = kind; } diff --git a/lib/IRGen/GenType.h b/lib/IRGen/GenType.h index 3b03e931f42d0..b62e2cfdf7fc5 100644 --- a/lib/IRGen/GenType.h +++ b/lib/IRGen/GenType.h @@ -105,6 +105,9 @@ class TypeConverter { const LoadableTypeInfo *RawPointerTI = nullptr; const LoadableTypeInfo *WitnessTablePtrTI = nullptr; const TypeInfo *TypeMetadataPtrTI = nullptr; + const TypeInfo *SwiftContextPtrTI = nullptr; + const TypeInfo *TaskContinuationFunctionPtrTI = nullptr; + const TypeInfo *SwiftExecutorPtrTI = nullptr; const TypeInfo *ObjCClassPtrTI = nullptr; const LoadableTypeInfo *EmptyTI = nullptr; const LoadableTypeInfo *IntegerLiteralTI = nullptr; @@ -181,6 +184,9 @@ class TypeConverter { const LoadableTypeInfo &getBridgeObjectTypeInfo(); const LoadableTypeInfo &getRawPointerTypeInfo(); const TypeInfo &getTypeMetadataPtrTypeInfo(); + const TypeInfo &getSwiftContextPtrTypeInfo(); + const TypeInfo &getTaskContinuationFunctionPtrTypeInfo(); + const TypeInfo &getSwiftExecutorPtrTypeInfo(); const TypeInfo &getObjCClassPtrTypeInfo(); const LoadableTypeInfo &getWitnessTablePtrTypeInfo(); const LoadableTypeInfo &getEmptyTypeInfo(); diff --git a/lib/IRGen/IRGen.cpp b/lib/IRGen/IRGen.cpp index 7fe0c9ffffdcb..1c58d8206d624 100644 --- a/lib/IRGen/IRGen.cpp +++ b/lib/IRGen/IRGen.cpp @@ -16,6 +16,7 @@ #define DEBUG_TYPE "irgen" #include "IRGenModule.h" +#include "swift/ABI/MetadataValues.h" #include "swift/AST/DiagnosticsIRGen.h" #include "swift/AST/IRGenOptions.h" #include "swift/AST/IRGenRequests.h" diff --git a/lib/IRGen/IRGenDebugInfo.cpp b/lib/IRGen/IRGenDebugInfo.cpp index 5d91564f12c26..d462676af3db3 100644 --- a/lib/IRGen/IRGenDebugInfo.cpp +++ b/lib/IRGen/IRGenDebugInfo.cpp @@ -661,7 +661,7 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { } void createImportedModule(llvm::DIScope *Context, - ModuleDecl::ImportedModule M, llvm::DIFile *File, + ImportedModule M, llvm::DIFile *File, unsigned Line) { // For overlays of Clang modules also emit an import of the underlying Clang // module. The helps the debugger resolve types that are present only in the @@ -761,7 +761,7 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { return None; } - llvm::DIModule *getOrCreateModule(ModuleDecl::ImportedModule IM) { + llvm::DIModule *getOrCreateModule(ImportedModule IM) { ModuleDecl *M = IM.importedModule; if (Optional ModuleDesc = getClangModule(*M)) return getOrCreateModule(*ModuleDesc, ModuleDesc->getModuleOrNull()); @@ -1882,7 +1882,7 @@ void IRGenDebugInfoImpl::finalize() { // Get the list of imported modules (which may actually be different // from all ImportDecls). - SmallVector ModuleWideImports; + SmallVector ModuleWideImports; IGM.getSwiftModule()->getImportedModules( ModuleWideImports, {ModuleDecl::ImportFilterKind::Exported, ModuleDecl::ImportFilterKind::Default, @@ -2129,7 +2129,7 @@ void IRGenDebugInfoImpl::emitImport(ImportDecl *D) { return; assert(D->getModule() && "compiler-synthesized ImportDecl is incomplete"); - ModuleDecl::ImportedModule Imported = { D->getAccessPath(), D->getModule() }; + ImportedModule Imported = { D->getAccessPath(), D->getModule() }; auto L = getDebugLoc(*this, D); auto *File = getOrCreateFile(L.Filename); createImportedModule(File, Imported, File, L.Line); diff --git a/lib/IRGen/IRGenDebugInfo.h b/lib/IRGen/IRGenDebugInfo.h index 229a544b7ce1e..a34b99b2dc0c7 100644 --- a/lib/IRGen/IRGenDebugInfo.h +++ b/lib/IRGen/IRGenDebugInfo.h @@ -79,7 +79,7 @@ class IRGenDebugInfo { /// If we are not emitting CodeView, this does nothing since the ``llvm.trap`` /// instructions should already have an artificial location of zero. /// In CodeView, since zero is not an artificial location, we emit the - /// location of the unified trap block at the end of the fuction as an + /// location of the unified trap block at the end of the function as an /// artificial inline location pointing to the user's instruction. void setInlinedTrapLocation(IRBuilder &Builder, const SILDebugScope *Scope); diff --git a/lib/IRGen/IRGenFunction.h b/lib/IRGen/IRGenFunction.h index 0a1affaf92acb..61f43c33993c4 100644 --- a/lib/IRGen/IRGenFunction.h +++ b/lib/IRGen/IRGenFunction.h @@ -83,7 +83,7 @@ class IRGenFunction { OptimizationMode Mode = OptimizationMode::NotSet, const SILDebugScope *DbgScope = nullptr, Optional DbgLoc = None); - ~IRGenFunction(); + virtual ~IRGenFunction(); void unimplemented(SourceLoc Loc, StringRef Message); @@ -100,15 +100,18 @@ class IRGenFunction { void emitBBForReturn(); bool emitBranchToReturnBB(); - /// Return the error result slot, given an error type. There's - /// always only one error type. - Address getErrorResultSlot(SILType errorType); + /// Return the error result slot to be passed to the callee, given an error + /// type. There's always only one error type. + /// + /// For async functions, this is different from the caller result slot because + /// that is a gep into the %swift.context. + Address getCalleeErrorResultSlot(SILType errorType); /// Return the error result slot provided by the caller. Address getCallerErrorResultSlot(); - /// Set the error result slot. - void setErrorResultSlot(llvm::Value *address); + /// Set the error result slot for the current function. + void setCallerErrorResultSlot(llvm::Value *address); /// Are we currently emitting a coroutine? bool isCoroutine() { @@ -124,15 +127,21 @@ class IRGenFunction { assert(handle != nullptr && "setting a null handle"); CoroutineHandle = handle; } - + + virtual llvm::Value *getAsyncTask(); + virtual llvm::Value *getAsyncExecutor(); + virtual llvm::Value *getAsyncContext(); + private: void emitPrologue(); void emitEpilogue(); Address ReturnSlot; llvm::BasicBlock *ReturnBB; - llvm::Value *ErrorResultSlot = nullptr; + llvm::Value *CalleeErrorResultSlot = nullptr; + llvm::Value *CallerErrorResultSlot = nullptr; llvm::Value *CoroutineHandle = nullptr; + bool IsAsync = false; //--- Helper methods ----------------------------------------------------------- public: @@ -146,6 +155,9 @@ class IRGenFunction { return getEffectiveOptimizationMode() == OptimizationMode::ForSize; } + bool isAsync() const { return IsAsync; } + void setAsync(bool async = true) { IsAsync = async; } + Address createAlloca(llvm::Type *ty, Alignment align, const llvm::Twine &name = ""); Address createAlloca(llvm::Type *ty, llvm::Value *arraySize, Alignment align, @@ -635,8 +647,8 @@ class IRGenFunction { ~ConditionalDominanceScope(); }; - /// The kind of value LocalSelf is. - enum LocalSelfKind { + /// The kind of value DynamicSelf is. + enum DynamicSelfKind { /// An object reference. ObjectReference, /// A Swift metatype. @@ -645,9 +657,9 @@ class IRGenFunction { ObjCMetatype, }; - llvm::Value *getLocalSelfMetadata(); - void setLocalSelfMetadata(CanType selfBaseTy, bool selfIsExact, - llvm::Value *value, LocalSelfKind kind); + llvm::Value *getDynamicSelfMetadata(); + void setDynamicSelfMetadata(CanType selfBaseTy, bool selfIsExact, + llvm::Value *value, DynamicSelfKind kind); private: LocalTypeDataCache &getOrCreateLocalTypeData(); @@ -661,12 +673,12 @@ class IRGenFunction { DominancePoint ActiveDominancePoint = DominancePoint::universal(); ConditionalDominanceScope *ConditionalDominance = nullptr; - /// The value that satisfies metadata lookups for dynamic Self. - llvm::Value *LocalSelf = nullptr; + /// The value that satisfies metadata lookups for DynamicSelfType. + llvm::Value *SelfValue = nullptr; /// If set, the dynamic Self type is assumed to be equivalent to this exact class. - CanType LocalSelfType; - bool LocalSelfIsExact = false; - LocalSelfKind SelfKind; + CanType SelfType; + bool SelfTypeIsExact = false; + DynamicSelfKind SelfKind; }; using ConditionalDominanceScope = IRGenFunction::ConditionalDominanceScope; diff --git a/lib/IRGen/IRGenMangler.cpp b/lib/IRGen/IRGenMangler.cpp index 720a85ecc9887..c6bd374d7df0b 100644 --- a/lib/IRGen/IRGenMangler.cpp +++ b/lib/IRGen/IRGenMangler.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 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -147,6 +147,37 @@ IRGenMangler::mangleTypeForReflection(IRGenModule &IGM, std::string IRGenMangler::mangleProtocolConformanceDescriptor( const RootProtocolConformance *conformance) { + // Builtin conformances are different because they don't use a mangled name + // for their conformance descriptors. For now, we use predefined symbol names + // just in case in the future we have some conflict between actual + // conformances and these builtin ones. + if (isa(conformance)) { + auto &ctx = conformance->getType()->getASTContext(); + + if (conformance->getType()->is()) { + auto equatable = ctx.getProtocol(KnownProtocolKind::Equatable); + auto comparable = ctx.getProtocol(KnownProtocolKind::Comparable); + auto hashable = ctx.getProtocol(KnownProtocolKind::Hashable); + + if (conformance->getProtocol() == equatable) { + return "_swift_tupleEquatable_conf"; + } + + if (conformance->getProtocol() == comparable) { + return "_swift_tupleComparable_conf"; + } + + if (conformance->getProtocol() == hashable) { + return "_swift_tupleHashable_conf"; + } + + llvm_unreachable("mangling conformance descriptor for unknown tuple \ + protocol"); + } + + llvm_unreachable("mangling conformance descriptor for unknown builtin type"); + } + beginMangling(); if (isa(conformance)) { appendProtocolConformance(conformance); diff --git a/lib/IRGen/IRGenModule.cpp b/lib/IRGen/IRGenModule.cpp index 91eefd6d832dc..3bf43c1de55f0 100644 --- a/lib/IRGen/IRGenModule.cpp +++ b/lib/IRGen/IRGenModule.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 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -591,6 +591,21 @@ IRGenModule::IRGenModule(IRGenerator &irgen, DynamicReplacementKeyTy = createStructType(*this, "swift.dyn_repl_key", {RelativeAddressTy, Int32Ty}); + SwiftContextTy = createStructType(*this, "swift.context", {}); + SwiftTaskTy = createStructType(*this, "swift.task", {}); + SwiftExecutorTy = createStructType(*this, "swift.executor", {}); + SwiftContextPtrTy = SwiftContextTy->getPointerTo(DefaultAS); + SwiftTaskPtrTy = SwiftTaskTy->getPointerTo(DefaultAS); + SwiftExecutorPtrTy = SwiftExecutorTy->getPointerTo(DefaultAS); + + // using TaskContinuationFunction = + // SWIFT_CC(swift) + // void (AsyncTask *, ExecutorRef, AsyncContext *); + TaskContinuationFunctionTy = llvm::FunctionType::get( + VoidTy, {SwiftTaskPtrTy, SwiftExecutorPtrTy, SwiftContextPtrTy}, + /*isVarArg*/ false); + TaskContinuationFunctionPtrTy = TaskContinuationFunctionTy->getPointerTo(); + DifferentiabilityWitnessTy = createStructType( *this, "swift.differentiability_witness", {Int8PtrTy, Int8PtrTy}); } @@ -685,6 +700,14 @@ namespace RuntimeConstants { } return RuntimeAvailability::AlwaysAvailable; } + + RuntimeAvailability ConcurrencyAvailability(ASTContext &context) { + auto featureAvailability = context.getConcurrencyAvailability(); + if (!isDeploymentAvailabilityContainedIn(context, featureAvailability)) { + return RuntimeAvailability::ConditionallyAvailable; + } + return RuntimeAvailability::AlwaysAvailable; + } } // namespace RuntimeConstants // We don't use enough attributes to justify generalizing the @@ -970,6 +993,11 @@ bool IRGenerator::canEmitWitnessTableLazily(SILWitnessTable *wt) { if (wt->getLinkage() == SILLinkage::Shared) return true; + // If we happen to see a builtin witness table here, we can't emit those. + // The runtime has those for us. + if (isa(wt->getConformance())) + return false; + NominalTypeDecl *ConformingTy = wt->getConformingType()->getNominalOrBoundGenericNominal(); diff --git a/lib/IRGen/IRGenModule.h b/lib/IRGen/IRGenModule.h index 6c2d988fdff7c..9abffb152146c 100644 --- a/lib/IRGen/IRGenModule.h +++ b/lib/IRGen/IRGenModule.h @@ -723,6 +723,15 @@ class IRGenModule { *DynamicReplacementLinkEntryPtrTy; // %link_entry* llvm::StructType *DynamicReplacementKeyTy; // { i32, i32} + llvm::StructType *SwiftContextTy; + llvm::StructType *SwiftTaskTy; + llvm::StructType *SwiftExecutorTy; + llvm::PointerType *SwiftContextPtrTy; + llvm::PointerType *SwiftTaskPtrTy; + llvm::PointerType *SwiftExecutorPtrTy; + llvm::FunctionType *TaskContinuationFunctionTy; + llvm::PointerType *TaskContinuationFunctionPtrTy; + llvm::StructType *DifferentiabilityWitnessTy; // { i8*, i8* } llvm::GlobalVariable *TheTrivialPropertyDescriptor = nullptr; @@ -884,6 +893,9 @@ class IRGenModule { const TypeInfo &getTypeInfo(SILType T); const TypeInfo &getWitnessTablePtrTypeInfo(); const TypeInfo &getTypeMetadataPtrTypeInfo(); + const TypeInfo &getSwiftContextPtrTypeInfo(); + const TypeInfo &getTaskContinuationFunctionPtrTypeInfo(); + const TypeInfo &getSwiftExecutorPtrTypeInfo(); const TypeInfo &getObjCClassPtrTypeInfo(); const LoadableTypeInfo &getOpaqueStorageTypeInfo(Size size, Alignment align); const LoadableTypeInfo & diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index 8862177ea71cb..06127a14fd9f3 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -55,6 +55,7 @@ #include "llvm/Transforms/Utils/Local.h" #include "CallEmission.h" +#include "EntryPointArgumentEmission.h" #include "Explosion.h" #include "GenArchetype.h" #include "GenBuiltin.h" @@ -70,8 +71,8 @@ #include "GenIntegerLiteral.h" #include "GenObjC.h" #include "GenOpaque.h" -#include "GenPoly.h" #include "GenPointerAuth.h" +#include "GenPoly.h" #include "GenProto.h" #include "GenStruct.h" #include "GenTuple.h" @@ -406,6 +407,9 @@ class IRGenSILFunction : llvm::SmallVector FailBBs; SILFunction *CurSILFn; + // If valid, the address by means of which a return--which is direct in + // SIL--is passed indirectly in IR. Such indirection is necessary when the + // value which would be returned directly cannot fit into registers. Address IndirectReturn; /// The unique block that calls @llvm.coro.end. @@ -849,6 +853,42 @@ class IRGenSILFunction : } } + llvm::Value *getAsyncTask() override { + // FIXME: (1) Remove this override, (2) mark the IRGenFunction::getAsyncTask + // declaration as non-virtual, and (3) mark IRGenFunction's + // destructor non-virtual once Task.runDetached is available. + // rdar://problem/70597390*/ + if (CurSILFn->getLoweredFunctionType()->getRepresentation() == + SILFunctionTypeRepresentation::CFunctionPointer) { + return llvm::Constant::getNullValue(IGM.SwiftTaskPtrTy); + } + return IRGenFunction::getAsyncTask(); + } + + llvm::Value *getAsyncExecutor() override { + // FIXME: (1) Remove this override, (2) mark the + // IRGenFunction::getAsyncExecutor declaration as non-virtual, and + // (3) mark IRGenFunction's destructor non-virtual once + // Task.runDetached is available. rdar://problem/70597390*/ + if (CurSILFn->getLoweredFunctionType()->getRepresentation() == + SILFunctionTypeRepresentation::CFunctionPointer) { + return llvm::Constant::getNullValue(IGM.SwiftExecutorPtrTy); + } + return IRGenFunction::getAsyncExecutor(); + } + + llvm::Value *getAsyncContext() override { + // FIXME: (1) Remove this override, (2) mark the + // IRGenFunction::getAsyncContext declaration as non-virtual, and + // (3) mark IRGenFunction's destructor non-virtual once + // Task.runDetached is available. rdar://problem/70597390*/ + if (CurSILFn->getLoweredFunctionType()->getRepresentation() == + SILFunctionTypeRepresentation::CFunctionPointer) { + return llvm::Constant::getNullValue(IGM.SwiftContextPtrTy); + } + return IRGenFunction::getAsyncContext(); + } + //===--------------------------------------------------------------------===// // SIL instruction lowering //===--------------------------------------------------------------------===// @@ -1083,6 +1123,19 @@ class IRGenSILFunction : void visitCheckedCastValueBranchInst(CheckedCastValueBranchInst *i); void visitCheckedCastAddrBranchInst(CheckedCastAddrBranchInst *i); + void visitGetAsyncContinuationInst(GetAsyncContinuationInst *i) { + //TODO(async) + llvm_unreachable("not implemented"); + } + void visitGetAsyncContinuationAddrInst(GetAsyncContinuationAddrInst *i) { + //TODO(async) + llvm_unreachable("not implemented"); + } + void visitAwaitAsyncContinuationInst(AwaitAsyncContinuationInst *i) { + //TODO(async) + llvm_unreachable("not implemented"); + } + void visitKeyPathInst(KeyPathInst *I); void visitDifferentiableFunctionInst(DifferentiableFunctionInst *i); @@ -1112,10 +1165,245 @@ class IRGenSILFunction : LOADABLE_REF_STORAGE_HELPER(Name) #include "swift/AST/ReferenceStorage.def" #undef LOADABLE_REF_STORAGE_HELPER + }; } // end anonymous namespace +static AsyncContextLayout getAsyncContextLayout(IRGenSILFunction &IGF) { + return getAsyncContextLayout(IGF, IGF.CurSILFn); +} + +namespace { +class SyncEntryPointArgumentEmission + : public virtual EntryPointArgumentEmission { +protected: + IRGenSILFunction &IGF; + SILBasicBlock &entry; + Explosion &allParamValues; + SyncEntryPointArgumentEmission(IRGenSILFunction &IGF, SILBasicBlock &entry, + Explosion &allParamValues) + : IGF(IGF), entry(entry), allParamValues(allParamValues){}; + +public: + bool requiresIndirectResult(SILType retType) override { + auto &schema = + IGF.IGM.getTypeInfo(retType).nativeReturnValueSchema(IGF.IGM); + return schema.requiresIndirect(); + } + llvm::Value *getIndirectResultForFormallyDirectResult() override { + return allParamValues.claimNext(); + } + llvm::Value *getIndirectResult(unsigned index) override { + return allParamValues.claimNext(); + }; + llvm::Value * + getNextPolymorphicParameter(GenericRequirement &requirement) override { + return allParamValues.claimNext(); + } + llvm::Value *getNextPolymorphicParameterAsMetadata() override { + return allParamValues.claimNext(); + } +}; +class AsyncEntryPointArgumentEmission + : public virtual EntryPointArgumentEmission { +protected: + IRGenSILFunction &IGF; + SILBasicBlock &entry; + Explosion &allParamValues; + AsyncEntryPointArgumentEmission(IRGenSILFunction &IGF, SILBasicBlock &entry, + Explosion &allParamValues) + : IGF(IGF), entry(entry), allParamValues(allParamValues){}; +}; + +class COrObjCEntryPointArgumentEmission + : public virtual EntryPointArgumentEmission {}; + +class SyncCOrObjCEntryPointArgumentEmission + : public SyncEntryPointArgumentEmission, + public COrObjCEntryPointArgumentEmission { +public: + SyncCOrObjCEntryPointArgumentEmission(IRGenSILFunction &_IGF, + SILBasicBlock &_entry, + Explosion &_allParamValues) + : SyncEntryPointArgumentEmission(_IGF, _entry, _allParamValues){}; +}; + +class SyncNativeCCEntryPointArgumentEmission final + : public NativeCCEntryPointArgumentEmission, + public SyncEntryPointArgumentEmission { +public: + SyncNativeCCEntryPointArgumentEmission(IRGenSILFunction &_IGF, + SILBasicBlock &_entry, + Explosion &_allParamValues) + : SyncEntryPointArgumentEmission(_IGF, _entry, _allParamValues){}; + + llvm::Value *getCallerErrorResultArgument() override { + return allParamValues.takeLast(); + } + llvm::Value *getContext() override { return allParamValues.takeLast(); } + Explosion getArgumentExplosion(unsigned index, unsigned size) override { + assert(size > 0); + Explosion result; + allParamValues.transferInto(result, size); + return result; + } + llvm::Value *getSelfWitnessTable() override { + return allParamValues.takeLast(); + } + llvm::Value *getSelfMetadata() override { return allParamValues.takeLast(); } + llvm::Value *getCoroutineBuffer() override { + return allParamValues.claimNext(); + } + +public: + using SyncEntryPointArgumentEmission::requiresIndirectResult; + using SyncEntryPointArgumentEmission::getIndirectResultForFormallyDirectResult; + using SyncEntryPointArgumentEmission::getIndirectResult; + using SyncEntryPointArgumentEmission::getNextPolymorphicParameterAsMetadata; + using SyncEntryPointArgumentEmission::getNextPolymorphicParameter; +}; + +class AsyncNativeCCEntryPointArgumentEmission final + : public NativeCCEntryPointArgumentEmission, + public AsyncEntryPointArgumentEmission { + llvm::Value *task; + llvm::Value *executor; + llvm::Value *context; + /*const*/ AsyncContextLayout layout; + const Address dataAddr; + unsigned polymorphicParameterIndex = 0; + + Explosion loadExplosion(ElementLayout layout) { + Address addr = layout.project(IGF, dataAddr, /*offsets*/ llvm::None); + auto &ti = cast(layout.getType()); + Explosion explosion; + ti.loadAsTake(IGF, addr, explosion); + return explosion; + } + llvm::Value *loadValue(ElementLayout layout) { + auto explosion = loadExplosion(layout); + return explosion.claimNext(); + } + +public: + AsyncNativeCCEntryPointArgumentEmission(IRGenSILFunction &IGF, + SILBasicBlock &entry, + Explosion &allParamValues) + : AsyncEntryPointArgumentEmission(IGF, entry, allParamValues), + task(allParamValues.claimNext()), executor(allParamValues.claimNext()), + context(allParamValues.claimNext()), layout(getAsyncContextLayout(IGF)), + dataAddr(layout.emitCastTo(IGF, context)){}; + + llvm::Value *getCallerErrorResultArgument() override { + auto errorLayout = layout.getErrorLayout(); + Address addr = errorLayout.project(IGF, dataAddr, /*offsets*/ llvm::None); + return addr.getAddress(); + } + llvm::Value *getContext() override { + auto contextLayout = layout.getLocalContextLayout(); + return loadValue(contextLayout); + } + Explosion getArgumentExplosion(unsigned index, unsigned size) override { + assert(size > 0); + auto argumentLayout = layout.getArgumentLayout(index); + auto result = loadExplosion(argumentLayout); + assert(result.size() == size); + return result; + } + bool requiresIndirectResult(SILType retType) override { return false; } + llvm::Value *getIndirectResultForFormallyDirectResult() override { + llvm_unreachable("async function do not need to lower direct SIL results " + "into indirect IR results because all results are already " + "indirected through the context"); + } + Address getNextUncastBinding() { + auto index = polymorphicParameterIndex; + ++polymorphicParameterIndex; + + assert(layout.hasBindings()); + + auto bindingLayout = layout.getBindingsLayout(); + auto bindingsAddr = bindingLayout.project(IGF, dataAddr, /*offsets*/ None); + auto erasedBindingsAddr = + IGF.Builder.CreateBitCast(bindingsAddr, IGF.IGM.Int8PtrPtrTy); + auto uncastBindingAddr = IGF.Builder.CreateConstArrayGEP( + erasedBindingsAddr, index, IGF.IGM.getPointerSize()); + return uncastBindingAddr; + } + llvm::Value *castUncastBindingToMetadata(Address uncastBindingAddr) { + auto bindingAddrAddr = IGF.Builder.CreateBitCast( + uncastBindingAddr.getAddress(), IGF.IGM.TypeMetadataPtrPtrTy); + auto bindingAddr = + IGF.Builder.CreateLoad(bindingAddrAddr, IGF.IGM.getPointerAlignment()); + return bindingAddr; + } + llvm::Value *castUncastBindingToWitnessTable(Address uncastBindingAddr) { + auto bindingAddrAddr = IGF.Builder.CreateBitCast( + uncastBindingAddr.getAddress(), IGF.IGM.WitnessTablePtrPtrTy); + auto bindingAddr = + IGF.Builder.CreateLoad(bindingAddrAddr, IGF.IGM.getPointerAlignment()); + return bindingAddr; + } + llvm::Value * + getNextPolymorphicParameter(GenericRequirement &requirement) override { + auto uncastBindingAddr = getNextUncastBinding(); + if (requirement.Protocol) { + return castUncastBindingToWitnessTable(uncastBindingAddr); + } else { + return castUncastBindingToMetadata(uncastBindingAddr); + } + } + llvm::Value *getNextPolymorphicParameterAsMetadata() override { + return castUncastBindingToMetadata(getNextUncastBinding()); + } + llvm::Value *getIndirectResult(unsigned index) override { + auto fieldLayout = layout.getIndirectReturnLayout(index); + return loadValue(fieldLayout); + }; + llvm::Value *getSelfWitnessTable() override { + auto fieldLayout = layout.getSelfWitnessTableLayout(); + return loadValue(fieldLayout); + } + llvm::Value *getSelfMetadata() override { + auto fieldLayout = layout.getSelfMetadataLayout(); + return loadValue(fieldLayout); + } + llvm::Value *getCoroutineBuffer() override { + llvm_unreachable( + "async functions do not use a fixed size coroutine buffer"); + } +}; + +std::unique_ptr +getNativeCCEntryPointArgumentEmission(IRGenSILFunction &IGF, + SILBasicBlock &entry, + Explosion &allParamValues) { + if (IGF.CurSILFn->isAsync()) { + return std::make_unique( + IGF, entry, allParamValues); + } else { + return std::make_unique( + IGF, entry, allParamValues); + } +} +std::unique_ptr +getCOrObjCEntryPointArgumentEmission(IRGenSILFunction &IGF, + SILBasicBlock &entry, + Explosion &allParamValues) { + if (IGF.CurSILFn->isAsync() && + !(/*FIXME: Remove this condition once Task.runDetached is + available. rdar://problem/70597390*/ + IGF.CurSILFn->getLoweredFunctionType()->getRepresentation() == + SILFunctionTypeRepresentation::CFunctionPointer)) { + llvm_unreachable("unsupported"); + } else { + return std::make_unique( + IGF, entry, allParamValues); + } +} +} // end anonymous namespace + void LoweredValue::getExplosion(IRGenFunction &IGF, SILType type, Explosion &ex) const { switch (kind) { @@ -1218,6 +1506,8 @@ IRGenSILFunction::IRGenSILFunction(IRGenModule &IGM, SILFunction *f) if (f->isDynamicallyReplaceable()) { IGM.createReplaceableProlog(*this, f); } + + setAsync(f->getLoweredFunctionType()->isAsync()); } IRGenSILFunction::~IRGenSILFunction() { @@ -1300,22 +1590,24 @@ static void addIncomingExplosionToPHINodes(IRGenSILFunction &IGF, Explosion &argValue); // TODO: Handle this during SIL AddressLowering. -static ArrayRef emitEntryPointIndirectReturn( - IRGenSILFunction &IGF, - SILBasicBlock *entry, - Explosion ¶ms, - CanSILFunctionType funcTy, - llvm::function_ref requiresIndirectResult) { +static ArrayRef emitEntryPointIndirectReturn( + EntryPointArgumentEmission &emission, IRGenSILFunction &IGF, + SILBasicBlock *entry, CanSILFunctionType funcTy, + llvm::function_ref requiresIndirectResult) { // Map an indirect return for a type SIL considers loadable but still // requires an indirect return at the IR level. SILFunctionConventions fnConv(funcTy, IGF.getSILModule()); SILType directResultType = IGF.CurSILFn->mapTypeIntoContext( fnConv.getSILResultType(IGF.IGM.getMaximalTypeExpansionContext())); if (requiresIndirectResult(directResultType)) { + assert(!IGF.CurSILFn->isAsync() && + "async function do not need to lower direct SIL results into " + "indirect IR results because all results are already indirected " + "through the context"); auto ¶mTI = IGF.IGM.getTypeInfo(directResultType); auto &retTI = IGF.IGM.getTypeInfo(IGF.getLoweredTypeInContext(directResultType)); - auto ptr = params.claimNext(); + auto ptr = emission.getIndirectResultForFormallyDirectResult(); if (paramTI.getStorageType() != retTI.getStorageType()) { assert(directResultType.getASTType()->hasOpaqueArchetype()); ptr = IGF.Builder.CreateBitCast(ptr, @@ -1335,10 +1627,11 @@ static ArrayRef emitEntryPointIndirectReturn( auto inContextResultType = IGF.CurSILFn->mapTypeIntoContext(indirectResultType); auto &retTI = IGF.IGM.getTypeInfo(ret->getType()); + auto ¶mTI = IGF.IGM.getTypeInfo(inContextResultType); + // The parameter's type might be different due to looking through opaque // archetypes. - auto ptr = params.claimNext(); - auto ¶mTI = IGF.IGM.getTypeInfo(inContextResultType); + llvm::Value *ptr = emission.getIndirectResult(idx); if (paramTI.getStorageType() != retTI.getStorageType()) { assert(inContextResultType.getASTType()->hasOpaqueArchetype()); ptr = IGF.Builder.CreateBitCast(ptr, @@ -1353,10 +1646,10 @@ static ArrayRef emitEntryPointIndirectReturn( return bbargs.slice(numIndirectResults); } -static void bindParameter(IRGenSILFunction &IGF, - SILArgument *param, - SILType paramTy, - Explosion &allParamValues) { +template +static void bindParameter(IRGenSILFunction &IGF, unsigned index, + SILArgument *param, SILType paramTy, + ExplosionForArgument explosionForArgument) { // Pull out the parameter value and its formal type. auto ¶mTI = IGF.getTypeInfo(IGF.CurSILFn->mapTypeIntoContext(paramTy)); auto &argTI = IGF.getTypeInfo(param->getType()); @@ -1371,8 +1664,9 @@ static void bindParameter(IRGenSILFunction &IGF, // indirect address. auto &nativeSchema = argTI.nativeParameterValueSchema(IGF.IGM); if (nativeSchema.requiresIndirect()) { + Explosion paramExplosion = explosionForArgument(index, 1); Address paramAddr = - loadableParamTI.getAddressForPointer(allParamValues.claimNext()); + loadableParamTI.getAddressForPointer(paramExplosion.claimNext()); if (paramTI.getStorageType() != argTI.getStorageType()) paramAddr = loadableArgTI.getAddressForPointer(IGF.Builder.CreateBitCast( @@ -1384,7 +1678,9 @@ static void bindParameter(IRGenSILFunction &IGF, // Otherwise, we map from the native convention to the type's explosion // schema. Explosion nativeParam; - allParamValues.transferInto(nativeParam, nativeSchema.size()); + unsigned size = nativeSchema.size(); + Explosion paramExplosion = explosionForArgument(index, size); + paramExplosion.transferInto(nativeParam, size); paramValues = nativeSchema.mapFromNative(IGF.IGM, IGF, nativeParam, param->getType()); } else { @@ -1400,7 +1696,8 @@ static void bindParameter(IRGenSILFunction &IGF, // FIXME: that doesn't mean we should physically pass it // indirectly at this resilience expansion. An @in or @in_guaranteed parameter // could be passed by value in the right resilience domain. - auto ptr = allParamValues.claimNext(); + Explosion paramExplosion = explosionForArgument(index, 1); + auto ptr = paramExplosion.claimNext(); if (paramTI.getStorageType() != argTI.getStorageType()) { ptr = IGF.Builder.CreateBitCast(ptr, argTI.getStorageType()->getPointerTo()); @@ -1414,36 +1711,36 @@ static void bindParameter(IRGenSILFunction &IGF, static void emitEntryPointArgumentsNativeCC(IRGenSILFunction &IGF, SILBasicBlock *entry, Explosion &allParamValues) { + auto emission = + getNativeCCEntryPointArgumentEmission(IGF, *entry, allParamValues); auto funcTy = IGF.CurSILFn->getLoweredFunctionType(); - + // Map the indirect return if present. ArrayRef params = emitEntryPointIndirectReturn( - IGF, entry, allParamValues, funcTy, [&](SILType retType) -> bool { - auto &schema = - IGF.IGM.getTypeInfo(retType).nativeReturnValueSchema(IGF.IGM); - return schema.requiresIndirect(); + *emission, IGF, entry, funcTy, [&](SILType retType) -> bool { + return emission->requiresIndirectResult(retType); }); // The witness method CC passes Self as a final argument. WitnessMetadata witnessMetadata; if (funcTy->getRepresentation() == SILFunctionTypeRepresentation::WitnessMethod) { - collectTrailingWitnessMetadata(IGF, *IGF.CurSILFn, allParamValues, + collectTrailingWitnessMetadata(IGF, *IGF.CurSILFn, *emission, witnessMetadata); } // Bind the error result by popping it off the parameter list. if (funcTy->hasErrorResult()) { - IGF.setErrorResultSlot(allParamValues.takeLast()); + IGF.setCallerErrorResultSlot(emission->getCallerErrorResultArgument()); } // The coroutine context should be the first parameter. switch (funcTy->getCoroutineKind()) { case SILCoroutineKind::None: break; case SILCoroutineKind::YieldOnce: - emitYieldOnceCoroutineEntry(IGF, funcTy, allParamValues); + emitYieldOnceCoroutineEntry(IGF, funcTy, *emission); break; case SILCoroutineKind::YieldMany: - emitYieldManyCoroutineEntry(IGF, funcTy, allParamValues); + emitYieldManyCoroutineEntry(IGF, funcTy, *emission); break; } @@ -1455,20 +1752,24 @@ static void emitEntryPointArgumentsNativeCC(IRGenSILFunction &IGF, SILArgument *selfParam = params.back(); params = params.drop_back(); - Explosion selfTemp; - selfTemp.add(allParamValues.takeLast()); bindParameter( - IGF, selfParam, + IGF, 0, selfParam, conv.getSILArgumentType(conv.getNumSILArguments() - 1, IGF.IGM.getMaximalTypeExpansionContext()), - selfTemp); + [&](unsigned startIndex, unsigned size) { + assert(size == 1); + Explosion selfTemp; + selfTemp.add(emission->getContext()); + return selfTemp; + }); // Even if we don't have a 'self', if we have an error result, we // should have a placeholder argument here. } else if (funcTy->hasErrorResult() || funcTy->getRepresentation() == SILFunctionTypeRepresentation::Thick) { - llvm::Value *contextPtr = allParamValues.takeLast(); (void) contextPtr; + llvm::Value *contextPtr = emission->getContext(); + (void)contextPtr; assert(contextPtr->getType() == IGF.IGM.RefCountedPtrTy); } @@ -1476,23 +1777,25 @@ static void emitEntryPointArgumentsNativeCC(IRGenSILFunction &IGF, unsigned i = 0; for (SILArgument *param : params) { auto argIdx = conv.getSILArgIndexOfFirstParam() + i; - bindParameter(IGF, param, + bindParameter(IGF, i, param, conv.getSILArgumentType( argIdx, IGF.IGM.getMaximalTypeExpansionContext()), - allParamValues); + [&](unsigned index, unsigned size) { + return emission->getArgumentExplosion(index, size); + }); ++i; } // Bind polymorphic arguments. This can only be done after binding // all the value parameters. if (hasPolymorphicParameters(funcTy)) { - emitPolymorphicParameters(IGF, *IGF.CurSILFn, allParamValues, - &witnessMetadata, - [&](unsigned paramIndex) -> llvm::Value* { - SILValue parameter = - IGF.CurSILFn->getArgumentsWithoutIndirectResults()[paramIndex]; - return IGF.getLoweredSingletonExplosion(parameter); - }); + emitPolymorphicParameters( + IGF, *IGF.CurSILFn, *emission, &witnessMetadata, + [&](unsigned paramIndex) -> llvm::Value * { + SILValue parameter = + IGF.CurSILFn->getArgumentsWithoutIndirectResults()[paramIndex]; + return IGF.getLoweredSingletonExplosion(parameter); + }); } assert(allParamValues.empty() && "didn't claim all parameters!"); @@ -1504,6 +1807,7 @@ static void emitEntryPointArgumentsCOrObjC(IRGenSILFunction &IGF, SILBasicBlock *entry, Explosion ¶ms, CanSILFunctionType funcTy) { + auto emission = getCOrObjCEntryPointArgumentEmission(IGF, *entry, params); // First, lower the method type. ForeignFunctionInfo foreignInfo = IGF.IGM.getForeignFunctionInfo(funcTy); assert(foreignInfo.ClangInfo); @@ -1512,9 +1816,8 @@ static void emitEntryPointArgumentsCOrObjC(IRGenSILFunction &IGF, // Okay, start processing the parameters explosion. // First, claim all the indirect results. - ArrayRef args - = emitEntryPointIndirectReturn(IGF, entry, params, funcTy, - [&](SILType directResultType) -> bool { + ArrayRef args = emitEntryPointIndirectReturn( + *emission, IGF, entry, funcTy, [&](SILType directResultType) -> bool { return FI.getReturnInfo().isIndirect(); }); @@ -1587,7 +1890,7 @@ static void emitEntryPointArgumentsCOrObjC(IRGenSILFunction &IGF, // all the value parameters, and must be done even for non-polymorphic // functions because of imported Objective-C generics. emitPolymorphicParameters( - IGF, *IGF.CurSILFn, params, nullptr, + IGF, *IGF.CurSILFn, *emission, nullptr, [&](unsigned paramIndex) -> llvm::Value * { SILValue parameter = entry->getArguments()[paramIndex]; return IGF.getLoweredSingletonExplosion(parameter); @@ -1595,15 +1898,15 @@ static void emitEntryPointArgumentsCOrObjC(IRGenSILFunction &IGF, } /// Get metadata for the dynamic Self type if we have it. -static void emitLocalSelfMetadata(IRGenSILFunction &IGF) { - if (!IGF.CurSILFn->hasSelfMetadataParam()) +static void emitDynamicSelfMetadata(IRGenSILFunction &IGF) { + if (!IGF.CurSILFn->hasDynamicSelfMetadata()) return; - const SILArgument *selfArg = IGF.CurSILFn->getSelfMetadataArgument(); + const SILArgument *selfArg = IGF.CurSILFn->getDynamicSelfMetadata(); auto selfTy = selfArg->getType().getASTType(); CanMetatypeType metaTy = dyn_cast(selfTy); - IRGenFunction::LocalSelfKind selfKind; + IRGenFunction::DynamicSelfKind selfKind; if (!metaTy) selfKind = IRGenFunction::ObjectReference; else { @@ -1629,7 +1932,7 @@ static void emitLocalSelfMetadata(IRGenSILFunction &IGF) { bool isExact = selfTy->getClassOrBoundGenericClass()->isFinal() || IGF.CurSILFn->isExactSelfClass(); - IGF.setLocalSelfMetadata(selfTy, isExact, value, selfKind); + IGF.setDynamicSelfMetadata(selfTy, isExact, value, selfKind); } /// Emit the definition for the given SIL constant. @@ -1700,7 +2003,7 @@ void IRGenSILFunction::emitSILFunction() { emitEntryPointArgumentsCOrObjC(*this, entry->first, params, funcTy); break; } - emitLocalSelfMetadata(*this); + emitDynamicSelfMetadata(*this); assert(params.empty() && "did not map all llvm params to SIL params?!"); @@ -2339,14 +2642,11 @@ Callee LoweredValue::getCallee(IRGenFunction &IGF, llvm_unreachable("bad kind"); } -static CallEmission getCallEmissionForLoweredValue(IRGenSILFunction &IGF, - CanSILFunctionType origCalleeType, - CanSILFunctionType substCalleeType, - const LoweredValue &lv, - llvm::Value *selfValue, - SubstitutionMap substitutions, - WitnessMetadata *witnessMetadata, - Explosion &args) { +static std::unique_ptr getCallEmissionForLoweredValue( + IRGenSILFunction &IGF, CanSILFunctionType origCalleeType, + CanSILFunctionType substCalleeType, const LoweredValue &lv, + llvm::Value *selfValue, SubstitutionMap substitutions, + WitnessMetadata *witnessMetadata) { Callee callee = lv.getCallee(IGF, selfValue, CalleeInfo(origCalleeType, substCalleeType, substitutions)); @@ -2368,10 +2668,10 @@ static CallEmission getCallEmissionForLoweredValue(IRGenSILFunction &IGF, break; } - CallEmission callEmission(IGF, std::move(callee)); + auto callEmission = getCallEmission(IGF, selfValue, std::move(callee)); if (IGF.CurSILFn->isThunk()) - callEmission.addAttribute(llvm::AttributeList::FunctionIndex, - llvm::Attribute::NoInline); + callEmission->addAttribute(llvm::AttributeList::FunctionIndex, + llvm::Attribute::NoInline); return callEmission; } @@ -2450,13 +2750,13 @@ void IRGenSILFunction::visitFullApplySite(FullApplySite site) { } } - Explosion llArgs; + Explosion llArgs; WitnessMetadata witnessMetadata; - CallEmission emission = - getCallEmissionForLoweredValue(*this, origCalleeType, substCalleeType, - calleeLV, selfValue, - site.getSubstitutionMap(), - &witnessMetadata, llArgs); + auto emission = getCallEmissionForLoweredValue( + *this, origCalleeType, substCalleeType, calleeLV, selfValue, + site.getSubstitutionMap(), &witnessMetadata); + + emission->begin(); // Lower the arguments and return value in the callee's generic context. GenericContextScope scope(IGM, origCalleeType->getInvocationGenericSignature()); @@ -2483,9 +2783,7 @@ void IRGenSILFunction::visitFullApplySite(FullApplySite site) { // Turn the formal SIL parameters into IR-gen things. for (auto index : indices(args)) { - emitApplyArgument(*this, args[index], - origConv.getSILArgumentType( - index, IGM.getMaximalTypeExpansionContext()), + emitApplyArgument(*this, args[index], emission->getParameterType(index), llArgs); } @@ -2497,29 +2795,30 @@ void IRGenSILFunction::visitFullApplySite(FullApplySite site) { } // Add all those arguments. - emission.setArgs(llArgs, false, &witnessMetadata); + emission->setArgs(llArgs, false, &witnessMetadata); SILInstruction *i = site.getInstruction(); Explosion result; - emission.emitToExplosion(result, false); + emission->emitToExplosion(result, false); // For a simple apply, just bind the apply result to the result of the call. if (auto apply = dyn_cast(i)) { setLoweredExplosion(apply, result); + emission->end(); // For begin_apply, we have to destructure the call. } else if (auto beginApply = dyn_cast(i)) { // Grab the continuation pointer. This will still be an i8*. auto continuation = result.claimNext(); - setLoweredCoroutine(beginApply->getTokenResult(), - { *coroutineBuffer, - continuation, - emission.claimTemporaries() }); + setLoweredCoroutine( + beginApply->getTokenResult(), + {*coroutineBuffer, continuation, emission->claimTemporaries()}); setCorrespondingLoweredValues(beginApply->getYieldedValues(), result); + emission->end(); } else { auto tryApplyInst = cast(i); @@ -2527,8 +2826,9 @@ void IRGenSILFunction::visitFullApplySite(FullApplySite site) { SILFunctionConventions substConv(substCalleeType, getSILModule()); SILType errorType = substConv.getSILErrorType(IGM.getMaximalTypeExpansionContext()); - Address errorSlot = getErrorResultSlot(errorType); - auto errorValue = Builder.CreateLoad(errorSlot); + Address calleeErrorSlot = emission->getCalleeErrorSlot(errorType); + auto errorValue = Builder.CreateLoad(calleeErrorSlot); + emission->end(); auto &normalDest = getLoweredBB(tryApplyInst->getNormalBB()); auto &errorDest = getLoweredBB(tryApplyInst->getErrorBB()); @@ -2539,7 +2839,7 @@ void IRGenSILFunction::visitFullApplySite(FullApplySite site) { if (!tryApplyInst->getErrorBB()->getSinglePredecessorBlock()) { // Only do that here if we can't move the store to the error block. // See below. - Builder.CreateStore(nullError, errorSlot); + Builder.CreateStore(nullError, calleeErrorSlot); } // If the error value is non-null, branch to the error destination. @@ -2560,7 +2860,7 @@ void IRGenSILFunction::visitFullApplySite(FullApplySite site) { // that it will become a dead store. auto origBB = Builder.GetInsertBlock(); Builder.SetInsertPoint(errorDest.bb); - Builder.CreateStore(nullError, errorSlot); + Builder.CreateStore(nullError, calleeErrorSlot); Builder.SetInsertPoint(origBB); } } @@ -2750,6 +3050,19 @@ void IRGenSILFunction::visitPartialApplyInst(swift::PartialApplyInst *i) { Explosion llArgs; + if (i->getOrigCalleeType()->isAsync()) { + auto result = getPartialApplicationFunction(*this, i->getCallee(), + i->getSubstitutionMap(), + i->getSubstCalleeType()); + llvm::Value *innerContext = std::get<1>(result); + auto layout = + getAsyncContextLayout(*this, i->getOrigCalleeType(), + i->getSubstCalleeType(), i->getSubstitutionMap()); + auto size = getDynamicAsyncContextSize( + *this, layout, i->getOrigCalleeType(), innerContext); + llArgs.add(size); + } + // Lower the parameters in the callee's generic context. { GenericContextScope scope(IGM, @@ -2888,9 +3201,33 @@ static void emitReturnInst(IRGenSILFunction &IGF, // Even if SIL has a direct return, the IR-level calling convention may // require an indirect return. if (IGF.IndirectReturn.isValid()) { + assert(!IGF.isAsync()); auto &retTI = cast(IGF.getTypeInfo(resultTy)); retTI.initialize(IGF, result, IGF.IndirectReturn, false); IGF.Builder.CreateRetVoid(); + } else if (IGF.isAsync() && + !(/*FIXME: Remove this condition once Task.runDetached is + available. rdar://problem/70597390*/ + IGF.CurSILFn->getLoweredFunctionType() + ->getRepresentation() == + SILFunctionTypeRepresentation::CFunctionPointer)) { + // If we're generating an async function, store the result into the buffer. + assert(!IGF.IndirectReturn.isValid() && + "Formally direct results should stay direct results for async " + "functions"); + llvm::Value *context = IGF.getAsyncContext(); + auto layout = getAsyncContextLayout(IGF); + + Address dataAddr = layout.emitCastTo(IGF, context); + for (unsigned index = 0, count = layout.getDirectReturnCount(); + index < count; ++index) { + auto fieldLayout = layout.getDirectReturnLayout(index); + Address fieldAddr = + fieldLayout.project(IGF, dataAddr, /*offsets*/ llvm::None); + cast(fieldLayout.getType()) + .initialize(IGF, result, fieldAddr, /*isOutlined*/ false); + } + IGF.Builder.CreateRetVoid(); } else { auto funcLang = IGF.CurSILFn->getLoweredFunctionType()->getLanguage(); auto swiftCCReturn = funcLang == SILFunctionLanguage::Swift; @@ -3787,7 +4124,7 @@ void IRGenSILFunction::visitTupleExtractInst(swift::TupleExtractInst *i) { projectTupleElementFromExplosion(*this, baseType, fullTuple, - i->getFieldNo(), + i->getFieldIndex(), output); (void)fullTuple.claimAll(); setLoweredExplosion(i, output); @@ -3799,7 +4136,7 @@ void IRGenSILFunction::visitTupleElementAddrInst(swift::TupleElementAddrInst *i) SILType baseType = i->getOperand()->getType(); Address field = projectTupleElementAddress(*this, base, baseType, - i->getFieldNo()); + i->getFieldIndex()); setLoweredAddress(i, field); } @@ -3849,7 +4186,7 @@ void IRGenSILFunction::visitRefTailAddrInst(RefTailAddrInst *i) { } static bool isInvariantAddress(SILValue v) { - SILValue accessedAddress = getAccessedAddress(v); + SILValue accessedAddress = getTypedAccessAddress(v); if (auto *ptrRoot = dyn_cast(accessedAddress)) { return ptrRoot->isInvariant(); } @@ -3915,7 +4252,7 @@ void IRGenSILFunction::emitErrorResultVar(CanSILFunctionType FnTy, // swifterror in a register. if (IGM.IsSwiftErrorInRegister) return; - auto ErrorResultSlot = getErrorResultSlot(IGM.silConv.getSILType( + auto ErrorResultSlot = getCalleeErrorResultSlot(IGM.silConv.getSILType( ErrorInfo, FnTy, IGM.getMaximalTypeExpansionContext())); auto Var = DbgValue->getVarInfo(); assert(Var && "error result without debug info"); @@ -4354,8 +4691,9 @@ void IRGenSILFunction::visitAllocStackInst(swift::AllocStackInst *i) { dbgname = getVarName(i, IsAnonymous); # endif - auto addr = type.allocateStack(*this, i->getElementType(), dbgname); - setLoweredStackAddress(i, addr); + auto stackAddr = type.allocateStack(*this, i->getElementType(), dbgname); + setLoweredStackAddress(i, stackAddr); + Address addr = stackAddr.getAddress(); // Generate Debug Info. if (!Decl) @@ -4366,8 +4704,8 @@ void IRGenSILFunction::visitAllocStackInst(swift::AllocStackInst *i) { Type Desugared = Decl->getType()->getDesugaredType(); if (Desugared->getClassOrBoundGenericClass() || Desugared->getStructOrBoundGenericStruct()) - zeroInit(dyn_cast(addr.getAddress().getAddress())); - emitDebugInfoForAllocStack(i, type, addr.getAddress().getAddress()); + zeroInit(dyn_cast(addr.getAddress())); + emitDebugInfoForAllocStack(i, type, addr.getAddress()); } static void diff --git a/lib/IRGen/LoadableByAddress.cpp b/lib/IRGen/LoadableByAddress.cpp index 2a509cbfeeb89..1d74b82059c7a 100644 --- a/lib/IRGen/LoadableByAddress.cpp +++ b/lib/IRGen/LoadableByAddress.cpp @@ -1407,7 +1407,8 @@ void LoadableStorageAllocation::insertIndirectReturnArgs() { canType = genEnv->mapTypeIntoContext(canType)->getCanonicalType(); } resultStorageType = SILType::getPrimitiveObjectType(canType); - auto newResultStorageType = pass.getNewSILType(loweredTy, resultStorageType); + auto newResultStorageType = + pass.F->getLoweredType(pass.getNewSILType(loweredTy, resultStorageType)); auto &ctx = pass.F->getModule().getASTContext(); auto var = new (ctx) ParamDecl( diff --git a/lib/IRGen/MetadataLayout.cpp b/lib/IRGen/MetadataLayout.cpp index e93d370240ea4..5ea2eb6ee2be4 100644 --- a/lib/IRGen/MetadataLayout.cpp +++ b/lib/IRGen/MetadataLayout.cpp @@ -414,6 +414,7 @@ ClassMetadataLayout::getMethodInfo(IRGenFunction &IGF, SILDeclRef method) const{ case MethodInfo::Kind::DirectImpl: return MethodInfo(stored.TheImpl); } + llvm_unreachable("unhandled method info kind!"); } Offset ClassMetadataLayout::getFieldOffset(IRGenFunction &IGF, diff --git a/lib/IRGen/MetadataRequest.cpp b/lib/IRGen/MetadataRequest.cpp index d3f9fe58b6a16..3df5013d7827a 100644 --- a/lib/IRGen/MetadataRequest.cpp +++ b/lib/IRGen/MetadataRequest.cpp @@ -616,6 +616,7 @@ static MetadataResponse emitNominalPrespecializedGenericMetadataRef( return MetadataResponse::handle(IGF, request, call); } } + llvm_unreachable("unhandled metadata canonicality"); } static llvm::Value * @@ -1573,7 +1574,7 @@ namespace { MetadataResponse visitDynamicSelfType(CanDynamicSelfType type, DynamicMetadataRequest request) { - return MetadataResponse::forComplete(IGF.getLocalSelfMetadata()); + return MetadataResponse::forComplete(IGF.getDynamicSelfMetadata()); } MetadataResponse emitExistentialTypeMetadata(CanType type, @@ -2750,8 +2751,8 @@ IRGenFunction::emitTypeMetadataRef(CanType type, // If we're asking for the metadata of the type that dynamic Self is known // to be equal to, we can just use the self metadata. - if (LocalSelfIsExact && LocalSelfType == type) { - return MetadataResponse::forComplete(getLocalSelfMetadata()); + if (SelfTypeIsExact && SelfType == type) { + return MetadataResponse::forComplete(getDynamicSelfMetadata()); } if (type->hasArchetype() || diff --git a/lib/IRGen/NecessaryBindings.h b/lib/IRGen/NecessaryBindings.h index b4ad8200bbd12..0426d6d533ace 100644 --- a/lib/IRGen/NecessaryBindings.h +++ b/lib/IRGen/NecessaryBindings.h @@ -30,9 +30,11 @@ namespace swift { enum class MetadataState : size_t; class ProtocolDecl; class ProtocolConformanceRef; + class SpecializedProtocolConformance; -namespace irgen { + namespace irgen { class Address; + class Explosion; class IRGenFunction; class IRGenModule; class Size; @@ -40,23 +42,41 @@ namespace irgen { /// NecessaryBindings - The set of metadata that must be saved in /// order to perform some set of operations on a type. class NecessaryBindings { - llvm::SetVector Requirements; - + enum class Kind { + /// Are the bindings to be computed for a partial apply forwarder. + /// In the case this is true we need to store/restore the conformance of a + /// specialized type with conditional conformance because the conditional + /// requirements are not available in the partial apply forwarder. + PartialApply, + AsyncFunction, + }; + Kind kind; + llvm::SetVector RequirementsSet; + llvm::SmallVector RequirementsVector; + llvm::DenseMap Conformances; + + void addRequirement(GenericRequirement requirement) { + switch (kind) { + case Kind::PartialApply: + RequirementsSet.insert(requirement); + break; + case Kind::AsyncFunction: + RequirementsVector.push_back(requirement); + break; + } + } - /// Are the bindings to be computed for a partial apply forwarder. - /// In the case this is true we need to store/restore the conformance of a - /// specialized type with conditional conformance because the conditional - /// requirements are not available in the partial apply forwarder. - bool forPartialApply = false; + void addAbstractConditionalRequirements( + SpecializedProtocolConformance *specializedConformance); public: NecessaryBindings() = default; /// Collect the necessary bindings to invoke a function with the given /// signature. - static NecessaryBindings forFunctionInvocations(IRGenModule &IGM, - CanSILFunctionType origType, - SubstitutionMap subs); + static NecessaryBindings + forAsyncFunctionInvocation(IRGenModule &IGM, CanSILFunctionType origType, + SubstitutionMap subs); static NecessaryBindings forPartialApplyForwarder(IRGenModule &IGM, CanSILFunctionType origType, SubstitutionMap subs, @@ -68,19 +88,27 @@ class NecessaryBindings { /// Get the requirement from the bindings at index i. const GenericRequirement &operator[](size_t i) const { - return Requirements[i]; + switch (kind) { + case Kind::PartialApply: + return RequirementsSet[i]; + case Kind::AsyncFunction: + return RequirementsVector[i]; + } } - size_t size() const { - return Requirements.size(); + ProtocolConformanceRef + getConformance(const GenericRequirement &requirement) const { + return Conformances.lookup(requirement); } + size_t size() const { return getRequirements().size(); } + /// Add whatever information is necessary to reconstruct a witness table /// reference for the given type. void addProtocolConformance(CanType type, ProtocolConformanceRef conf); /// Is the work to do trivial? - bool empty() const { return Requirements.empty(); } + bool empty() const { return getRequirements().empty(); } /// Returns the required size of the bindings. /// Pointer alignment is sufficient. @@ -89,12 +117,23 @@ class NecessaryBindings { /// Save the necessary bindings to the given buffer. void save(IRGenFunction &IGF, Address buffer) const; + void save(IRGenFunction &IGF, Address buffer, Explosion &source) const; + /// Restore the necessary bindings from the given buffer. void restore(IRGenFunction &IGF, Address buffer, MetadataState state) const; - const llvm::SetVector &getRequirements() const { - return Requirements; + const llvm::ArrayRef getRequirements() const { + switch (kind) { + case Kind::PartialApply: + return RequirementsSet.getArrayRef(); + case Kind::AsyncFunction: + return RequirementsVector; + } } + + bool forPartialApply() { return kind == Kind::PartialApply; } + bool forAsyncFunction() { return kind == Kind::AsyncFunction; } + private: static NecessaryBindings computeBindings(IRGenModule &IGM, CanSILFunctionType origType, diff --git a/lib/Index/Index.cpp b/lib/Index/Index.cpp index f26580b5d3324..8d4dba5536fd8 100644 --- a/lib/Index/Index.cpp +++ b/lib/Index/Index.cpp @@ -124,7 +124,7 @@ class SourceFileOrModule { } void - getImportedModules(SmallVectorImpl &Modules) const { + getImportedModules(SmallVectorImpl &Modules) const { constexpr ModuleDecl::ImportFilter ImportFilter = { ModuleDecl::ImportFilterKind::Exported, ModuleDecl::ImportFilterKind::Default, @@ -631,6 +631,22 @@ class IndexSwiftASTWalker : public SourceEntityWalker { return true; } + // Are there members or conformances in \c D that should be indexed? + bool shouldIndexMembers(ExtensionDecl *D) { + for (auto Member : D->getMembers()) + if (auto VD = dyn_cast(Member)) + if (shouldIndex(VD, /*IsRef=*/false)) + return true; + + for (auto Inherit : D->getInherited()) + if (auto T = Inherit.getType()) + if (T->getAnyNominal() && + shouldIndex(T->getAnyNominal(), /*IsRef=*/false)) + return true; + + return false; + } + /// Reports all implicit member value decl conformances that \p D introduces /// as implicit overrides at the source location of \p D, and returns the /// explicit ones so we can check against them later on when visiting them as @@ -715,7 +731,7 @@ bool IndexSwiftASTWalker::visitImports( if (!IsNew) return true; - SmallVector Imports; + SmallVector Imports; TopMod.getImportedModules(Imports); llvm::SmallPtrSet Reported; @@ -1068,6 +1084,10 @@ bool IndexSwiftASTWalker::reportExtension(ExtensionDecl *D) { if (!shouldIndex(NTD, /*IsRef=*/false)) return true; + // Don't index "empty" extensions in imported modules. + if (IsModuleFile && !shouldIndexMembers(D)) + return true; + IndexSymbol Info; if (initIndexSymbol(D, NTD, Loc, Info)) return true; @@ -1602,7 +1622,7 @@ void IndexSwiftASTWalker::collectRecursiveModuleImports( ImportFilter |= ModuleDecl::ImportFilterKind::Exported; ImportFilter |= ModuleDecl::ImportFilterKind::Default; // FIXME: ImportFilterKind::ShadowedByCrossImportOverlay? - SmallVector Imports; + SmallVector Imports; TopMod.getImportedModules(Imports); for (auto Import : Imports) { diff --git a/lib/Index/IndexRecord.cpp b/lib/Index/IndexRecord.cpp index 9beed7ea04ca0..4d0706ff6fa6e 100644 --- a/lib/Index/IndexRecord.cpp +++ b/lib/Index/IndexRecord.cpp @@ -383,7 +383,7 @@ emitDataForSwiftSerializedModule(ModuleDecl *module, IndexUnitWriter &parentUnitWriter, SourceFile *initialFile); -static void addModuleDependencies(ArrayRef imports, +static void addModuleDependencies(ArrayRef imports, StringRef indexStorePath, bool indexSystemModules, bool skipStdlib, @@ -580,7 +580,7 @@ emitDataForSwiftSerializedModule(ModuleDecl *module, unitWriter.addRecordFile(recordFile, *FE, isSystemModule, mod); } - SmallVector imports; + SmallVector imports; module->getImportedModules(imports, {ModuleDecl::ImportFilterKind::Exported, ModuleDecl::ImportFilterKind::Default}); StringScratchSpace moduleNameScratch; @@ -619,7 +619,7 @@ recordSourceFileUnit(SourceFile *primarySourceFile, StringRef indexUnitToken, getModuleInfoFromOpaqueModule); // Module dependencies. - SmallVector imports; + SmallVector imports; primarySourceFile->getImportedModules( imports, {ModuleDecl::ImportFilterKind::Exported, ModuleDecl::ImportFilterKind::Default, diff --git a/lib/LLVMPasses/CMakeLists.txt b/lib/LLVMPasses/CMakeLists.txt index 80edee61d1236..f5a3ee28b7562 100644 --- a/lib/LLVMPasses/CMakeLists.txt +++ b/lib/LLVMPasses/CMakeLists.txt @@ -1,3 +1,4 @@ +set_swift_llvm_is_available() add_swift_host_library(swiftLLVMPasses STATIC LLVMSwiftAA.cpp LLVMSwiftRCIdentity.cpp diff --git a/lib/Migrator/CMakeLists.txt b/lib/Migrator/CMakeLists.txt index 3e37ff2ca48a9..473ad151f0640 100644 --- a/lib/Migrator/CMakeLists.txt +++ b/lib/Migrator/CMakeLists.txt @@ -47,6 +47,8 @@ swift_install_in_component(FILES ${datafiles} DESTINATION "lib/swift/migrator" COMPONENT compiler) +set_swift_llvm_is_available() + add_swift_host_library(swiftMigrator STATIC APIDiffMigratorPass.cpp EditorAdapter.cpp diff --git a/lib/Parse/CMakeLists.txt b/lib/Parse/CMakeLists.txt index 7158bdce8b24e..550af6bf5954a 100644 --- a/lib/Parse/CMakeLists.txt +++ b/lib/Parse/CMakeLists.txt @@ -1,3 +1,5 @@ +set_swift_llvm_is_available() + if(CMAKE_HOST_SYSTEM_NAME STREQUAL Windows) set(SWIFT_GYB_FLAGS --line-directive "^\"#line %(line)d \\\"%(file)s\\\"^\"") else() diff --git a/lib/Parse/Lexer.cpp b/lib/Parse/Lexer.cpp index fef46453c959e..06a949bda90b5 100644 --- a/lib/Parse/Lexer.cpp +++ b/lib/Parse/Lexer.cpp @@ -1241,19 +1241,6 @@ static bool diagnoseZeroWidthMatchAndAdvance(char Target, const char *&CurPtr, return *CurPtr == Target && CurPtr++; } -/// advanceIfMultilineDelimiter - Centralized check for multiline delimiter. -static bool advanceIfMultilineDelimiter(const char *&CurPtr, - DiagnosticEngine *Diags) { - const char *TmpPtr = CurPtr; - if (*(TmpPtr - 1) == '"' && - diagnoseZeroWidthMatchAndAdvance('"', TmpPtr, Diags) && - diagnoseZeroWidthMatchAndAdvance('"', TmpPtr, Diags)) { - CurPtr = TmpPtr; - return true; - } - return false; -} - /// advanceIfCustomDelimiter - Extracts/detects any custom delimiter on /// opening a string literal, advances CurPtr if a delimiter is found and /// returns a non-zero delimiter length. CurPtr[-1] must be '#' when called. @@ -1300,6 +1287,37 @@ static bool delimiterMatches(unsigned CustomDelimiterLen, const char *&BytesPtr, return true; } +/// advanceIfMultilineDelimiter - Centralized check for multiline delimiter. +static bool advanceIfMultilineDelimiter(unsigned CustomDelimiterLen, + const char *&CurPtr, + DiagnosticEngine *Diags, + bool IsOpening = false) { + + // Test for single-line string literals that resemble multiline delimiter. + const char *TmpPtr = CurPtr + 1; + if (IsOpening && CustomDelimiterLen) { + while (*TmpPtr != '\r' && *TmpPtr != '\n') { + if (*TmpPtr == '"') { + if (delimiterMatches(CustomDelimiterLen, ++TmpPtr, nullptr)) { + return false; + } + continue; + } + ++TmpPtr; + } + } + + TmpPtr = CurPtr; + if (*(TmpPtr - 1) == '"' && + diagnoseZeroWidthMatchAndAdvance('"', TmpPtr, Diags) && + diagnoseZeroWidthMatchAndAdvance('"', TmpPtr, Diags)) { + CurPtr = TmpPtr; + return true; + } + + return false; +} + /// lexCharacter - Read a character and return its UTF32 code. If this is the /// end of enclosing string/character sequence (i.e. the character is equal to /// 'StopQuote'), this returns ~0U and advances 'CurPtr' pointing to the end of @@ -1342,7 +1360,8 @@ unsigned Lexer::lexCharacter(const char *&CurPtr, char StopQuote, DiagnosticEngine *D = EmitDiagnostics ? Diags : nullptr; auto TmpPtr = CurPtr; - if (IsMultilineString && !advanceIfMultilineDelimiter(TmpPtr, D)) + if (IsMultilineString && + !advanceIfMultilineDelimiter(CustomDelimiterLen, TmpPtr, D)) return '"'; if (CustomDelimiterLen && !delimiterMatches(CustomDelimiterLen, TmpPtr, D, /*IsClosing=*/true)) @@ -1478,7 +1497,9 @@ static const char *skipToEndOfInterpolatedExpression(const char *CurPtr, if (!inStringLiteral()) { // Open string literal. OpenDelimiters.push_back(CurPtr[-1]); - AllowNewline.push_back(advanceIfMultilineDelimiter(CurPtr, nullptr)); + AllowNewline.push_back(advanceIfMultilineDelimiter(CustomDelimiterLen, + CurPtr, nullptr, + true)); CustomDelimiter.push_back(CustomDelimiterLen); continue; } @@ -1490,7 +1511,8 @@ static const char *skipToEndOfInterpolatedExpression(const char *CurPtr, continue; // Multi-line string can only be closed by '"""'. - if (AllowNewline.back() && !advanceIfMultilineDelimiter(CurPtr, nullptr)) + if (AllowNewline.back() && + !advanceIfMultilineDelimiter(CustomDelimiterLen, CurPtr, nullptr)) continue; // Check whether we have equivalent number of '#'s. @@ -1827,7 +1849,8 @@ void Lexer::lexStringLiteral(unsigned CustomDelimiterLen) { // diagnostics about changing them to double quotes. assert((QuoteChar == '"' || QuoteChar == '\'') && "Unexpected start"); - bool IsMultilineString = advanceIfMultilineDelimiter(CurPtr, Diags); + bool IsMultilineString = advanceIfMultilineDelimiter(CustomDelimiterLen, + CurPtr, Diags, true); if (IsMultilineString && *CurPtr != '\n' && *CurPtr != '\r') diagnose(CurPtr, diag::lex_illegal_multiline_string_start) .fixItInsert(Lexer::getSourceLoc(CurPtr), "\n"); diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index e1872ce6737f0..d12ce218373ec 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -23,6 +23,7 @@ #include "swift/Subsystems.h" #include "swift/AST/ASTWalker.h" #include "swift/AST/Attr.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/LazyResolver.h" #include "swift/AST/DebuggerClient.h" #include "swift/AST/DiagnosticsParse.h" @@ -582,16 +583,23 @@ ParserResult Parser::parseExtendedAvailabilitySpecList( bool Parser::parseSpecializeAttributeArguments( swift::tok ClosingBrace, bool &DiscardAttribute, Optional &Exported, Optional &Kind, - swift::TrailingWhereClause *&TrailingWhereClause) { + swift::TrailingWhereClause *&TrailingWhereClause, + DeclNameRef &targetFunction, SmallVectorImpl &spiGroups, + llvm::function_ref parseSILTargetName, + llvm::function_ref parseSILSIPModule) { 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") { + SyntaxParsingContext ArgumentContext( + SyntaxContext, ParamLabel == "target" + ? SyntaxKind::TargetFunctionEntry + : SyntaxKind::LabeledSpecializeEntry); + if (ParamLabel != "exported" && ParamLabel != "kind" && + ParamLabel != "target" && ParamLabel != "spi" && + ParamLabel != "spiModule") { diagnose(Tok.getLoc(), diag::attr_specialize_unknown_parameter_name, ParamLabel); } @@ -612,16 +620,14 @@ bool Parser::parseSpecializeAttributeArguments( return false; } if ((ParamLabel == "exported" && Exported.hasValue()) || - (ParamLabel == "kind" && Kind.hasValue())) { + (ParamLabel == "kind" && Kind.hasValue()) || + (ParamLabel == "spi" && !spiGroups.empty())) { diagnose(Tok.getLoc(), diag::attr_specialize_parameter_already_defined, ParamLabel); } if (ParamLabel == "exported") { auto trueLoc = Tok.getLoc(); bool isTrue = consumeIf(tok::kw_true); - if (isTrue) { - diagnose(trueLoc, diag::attr_specialize_export_true_no_op); - } bool isFalse = consumeIf(tok::kw_false); if (!isTrue && !isFalse) { diagnose(Tok.getLoc(), diag::attr_specialize_expected_bool_value); @@ -660,6 +666,35 @@ bool Parser::parseSpecializeAttributeArguments( diag::attr_specialize_expected_partial_or_full); } } + if (ParamLabel == "target") { + if (!parseSILTargetName(*this)) { + SyntaxParsingContext ContentContext(SyntaxContext, + SyntaxKind::DeclName); + DeclNameLoc loc; + targetFunction = parseDeclNameRef( + loc, diag::attr_specialize_expected_function, + DeclNameFlag::AllowZeroArgCompoundNames | + DeclNameFlag::AllowKeywordsUsingSpecialNames | + DeclNameFlag::AllowOperators); + } + } + if (ParamLabel == "spiModule") { + if (!parseSILSIPModule(*this)) { + diagnose(Tok.getLoc(), diag::attr_specialize_unknown_parameter_name, + ParamLabel); + return false; + } + } + if (ParamLabel == "spi") { + if (!Tok.is(tok::identifier)) { + diagnose(Tok.getLoc(), diag::attr_specialize_expected_spi_name); + consumeToken(); + return false; + } + auto text = Tok.getText(); + spiGroups.push_back(Context.getIdentifier(text)); + consumeToken(); + } if (!consumeIf(tok::comma)) { diagnose(Tok.getLoc(), diag::attr_specialize_missing_comma); skipUntil(tok::comma, tok::kw_where); @@ -696,8 +731,11 @@ bool Parser::parseSpecializeAttributeArguments( return true; } -bool Parser::parseSpecializeAttribute(swift::tok ClosingBrace, SourceLoc AtLoc, - SourceLoc Loc, SpecializeAttr *&Attr) { +bool Parser::parseSpecializeAttribute( + swift::tok ClosingBrace, SourceLoc AtLoc, SourceLoc Loc, + SpecializeAttr *&Attr, + llvm::function_ref parseSILTargetName, + llvm::function_ref parseSILSIPModule) { assert(ClosingBrace == tok::r_paren || ClosingBrace == tok::r_square); SourceLoc lParenLoc = consumeToken(); @@ -709,8 +747,11 @@ bool Parser::parseSpecializeAttribute(swift::tok ClosingBrace, SourceLoc AtLoc, TrailingWhereClause *trailingWhereClause = nullptr; - if (!parseSpecializeAttributeArguments(ClosingBrace, DiscardAttribute, - exported, kind, trailingWhereClause)) { + DeclNameRef targetFunction; + SmallVector spiGroups; + if (!parseSpecializeAttributeArguments( + ClosingBrace, DiscardAttribute, exported, kind, trailingWhereClause, + targetFunction, spiGroups, parseSILTargetName, parseSILSIPModule)) { return false; } @@ -739,7 +780,7 @@ bool Parser::parseSpecializeAttribute(swift::tok ClosingBrace, SourceLoc AtLoc, // Store the attribute. Attr = SpecializeAttr::create(Context, AtLoc, SourceRange(Loc, rParenLoc), trailingWhereClause, exported.getValue(), - kind.getValue()); + kind.getValue(), targetFunction, spiGroups); return true; } @@ -1374,6 +1415,125 @@ void Parser::parseObjCSelector(SmallVector &Names, } } +bool Parser::peekAvailabilityMacroName() { + parseAllAvailabilityMacroArguments(); + AvailabilityMacroMap Map = AvailabilityMacros; + + StringRef MacroName = Tok.getText(); + return Map.find(MacroName) != Map.end(); +} + +ParserStatus +Parser::parseAvailabilityMacro(SmallVectorImpl &Specs) { + // Get the macros from the compiler arguments. + parseAllAvailabilityMacroArguments(); + AvailabilityMacroMap Map = AvailabilityMacros; + + StringRef MacroName = Tok.getText(); + auto NameMatch = Map.find(MacroName); + if (NameMatch == Map.end()) + return makeParserSuccess(); // No match, it could be a standard platform. + + consumeToken(); + + llvm::VersionTuple Version; + SourceRange VersionRange; + if (Tok.isAny(tok::integer_literal, tok::floating_literal)) { + if (parseVersionTuple(Version, VersionRange, + diag::avail_query_expected_version_number)) + return makeParserError(); + } + + auto VersionMatch = NameMatch->getSecond().find(Version); + if (VersionMatch == NameMatch->getSecond().end()) { + diagnose(PreviousLoc, diag::attr_availability_unknown_version, + Version.getAsString(), MacroName); + return makeParserError(); // Failed to match the version, that's an error. + } + + // Make a copy of the specs to add the macro source location + // for the diagnostic about the use of macros in inlinable code. + SourceLoc MacroLoc = Tok.getLoc(); + for (auto *Spec : VersionMatch->getSecond()) + if (auto *PlatformVersionSpec = + dyn_cast(Spec)) { + auto SpecCopy = + new (Context) PlatformVersionConstraintAvailabilitySpec( + *PlatformVersionSpec); + SpecCopy->setMacroLoc(MacroLoc); + Specs.push_back(SpecCopy); + } + + return makeParserSuccess(); +} + +void Parser::parseAllAvailabilityMacroArguments() { + + if (AvailabilityMacrosComputed) return; + + AvailabilityMacroMap Map; + + SourceManager &SM = Context.SourceMgr; + const LangOptions &LangOpts = Context.LangOpts; + + for (StringRef macro: LangOpts.AvailabilityMacros) { + + // Create temporary parser. + int bufferID = SM.addMemBufferCopy(macro, + "-define-availability argument"); + swift::ParserUnit PU(SM, + SourceFileKind::Main, bufferID, + LangOpts, + TypeCheckerOptions(), "unknown"); + + ForwardingDiagnosticConsumer PDC(Context.Diags); + PU.getDiagnosticEngine().addConsumer(PDC); + + // Parse the argument. + AvailabilityMacroDefinition ParsedMacro; + ParserStatus Status = + PU.getParser().parseAvailabilityMacroDefinition(ParsedMacro); + if (Status.isError()) + continue; + + // Copy the Specs to the requesting ASTContext from the temporary context + // that parsed the argument. + auto SpecsCopy = SmallVector(); + for (auto *Spec : ParsedMacro.Specs) + if (auto *PlatformVersionSpec = + dyn_cast(Spec)) { + auto SpecCopy = + new (Context) PlatformVersionConstraintAvailabilitySpec( + *PlatformVersionSpec); + SpecsCopy.push_back(SpecCopy); + } + + ParsedMacro.Specs = SpecsCopy; + + // Find the macro info by name. + AvailabilityMacroVersionMap MacroDefinition; + auto NameMatch = Map.find(ParsedMacro.Name); + if (NameMatch != Map.end()) { + MacroDefinition = NameMatch->getSecond(); + } + + // Set the macro info by version. + auto PreviousEntry = + MacroDefinition.insert({ParsedMacro.Version, ParsedMacro.Specs}); + if (!PreviousEntry.second) { + diagnose(PU.getParser().PreviousLoc, diag::attr_availability_duplicate, + ParsedMacro.Name, ParsedMacro.Version.getAsString()); + } + + // Save back the macro spec. + Map.erase(ParsedMacro.Name); + Map.insert({ParsedMacro.Name, MacroDefinition}); + } + + AvailabilityMacros = Map; + AvailabilityMacrosComputed = true; +} + bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, DeclAttrKind DK) { // Ok, it is a valid attribute, eat it, and then process it. @@ -1461,7 +1621,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, else if (Tok.getText() == "releasenone") kind = EffectsKind::ReleaseNone; else { - diagnose(Loc, diag::effects_attribute_unknown_option, + diagnose(Loc, diag::attr_unknown_option, Tok.getText(), AttrName); return false; } @@ -1487,8 +1647,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, } if (Tok.isNot(tok::identifier)) { - diagnose(Loc, diag::optimization_attribute_expect_option, AttrName, - "none"); + diagnose(Loc, diag::attr_expected_option_such_as, AttrName, "none"); return false; } @@ -1498,8 +1657,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, else if (Tok.getText() == "__always") kind = InlineKind::Always; else { - diagnose(Loc, diag::optimization_attribute_unknown_option, - Tok.getText(), AttrName); + diagnose(Loc, diag::attr_unknown_option, Tok.getText(), AttrName); return false; } consumeToken(tok::identifier); @@ -1517,6 +1675,45 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, break; } + case DAK_ActorIndependent: { + // if no option is provided, then it's the 'safe' version. + if (!consumeIf(tok::l_paren)) { + if (!DiscardAttribute) { + AttrRange = SourceRange(Loc, Tok.getRange().getStart()); + Attributes.add(new (Context) ActorIndependentAttr(AtLoc, AttrRange, + ActorIndependentKind::Safe)); + } + break; + } + + // otherwise, make sure it looks like an identifier. + if (Tok.isNot(tok::identifier)) { + diagnose(Loc, diag::attr_expected_option_such_as, AttrName, "unsafe"); + return false; + } + + // make sure the identifier is 'unsafe' + if (Tok.getText() != "unsafe") { + diagnose(Loc, diag::attr_unknown_option, Tok.getText(), AttrName); + return false; + } + + consumeToken(tok::identifier); + AttrRange = SourceRange(Loc, Tok.getRange().getStart()); + + if (!consumeIf(tok::r_paren)) { + diagnose(Loc, diag::attr_expected_rparen, AttrName, + DeclAttribute::isDeclModifier(DK)); + return false; + } + + if (!DiscardAttribute) + Attributes.add(new (Context) ActorIndependentAttr(AtLoc, AttrRange, + ActorIndependentKind::Unsafe)); + + break; + } + case DAK_Optimize: { if (!consumeIf(tok::l_paren)) { diagnose(Loc, diag::attr_expected_lparen, AttrName, @@ -1525,8 +1722,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, } if (Tok.isNot(tok::identifier)) { - diagnose(Loc, diag::optimization_attribute_expect_option, AttrName, - "speed"); + diagnose(Loc, diag::attr_expected_option_such_as, AttrName, "speed"); return false; } @@ -1538,8 +1734,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, else if (Tok.getText() == "size") optMode = OptimizationMode::ForSize; else { - diagnose(Loc, diag::optimization_attribute_unknown_option, - Tok.getText(), AttrName); + diagnose(Loc, diag::attr_unknown_option, Tok.getText(), AttrName); return false; } consumeToken(tok::identifier); @@ -1977,7 +2172,8 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, StringRef Platform = Tok.getText(); if (Platform != "*" && - peekToken().isAny(tok::integer_literal, tok::floating_literal)) { + (peekToken().isAny(tok::integer_literal, tok::floating_literal) || + peekAvailabilityMacroName())) { // We have the short form of available: @available(iOS 8.0.1, *) SmallVector Specs; ParserStatus Status = parseAvailabilitySpecList(Specs); @@ -2550,6 +2746,11 @@ ParserStatus Parser::parseDeclAttribute(DeclAttributes &Attributes, SourceLoc At checkInvalidAttrName("_propertyWrapper", "propertyWrapper", DAK_PropertyWrapper, diag::attr_renamed_warning); + // Historical name for result builders. + checkInvalidAttrName( + "_functionBuilder", "resultBuilder", DAK_ResultBuilder, + diag::attr_renamed_warning); + if (DK == DAK_Count && Tok.getText() == "warn_unused_result") { // The behavior created by @warn_unused_result is now the default. Emit a // Fix-It to remove. @@ -3418,6 +3619,30 @@ static bool isParenthesizedUnowned(Parser &P) { (P.Tok.getText() == "safe" || P.Tok.getText() == "unsafe"); } +static void skipAttribute(Parser &P) { + // Consider unexpected tokens to be incomplete attributes. + + // Parse the attribute name, which can be qualified, have + // generic arguments, and so on. + do { + if (!P.consumeIf(tok::identifier) && !P.consumeIf(tok::code_complete)) + return; + + if (P.startsWithLess(P.Tok)) { + P.consumeStartingLess(); + P.skipUntilGreaterInTypeList(); + } + } while (P.consumeIf(tok::period)); + + // Skip an argument clause after the attribute name. + if (P.consumeIf(tok::l_paren)) { + while (P.Tok.isNot(tok::r_brace, tok::eof, tok::pound_endif)) { + if (P.consumeIf(tok::r_paren)) break; + P.skipSingle(); + } + } +} + bool Parser::isStartOfSwiftDecl() { // If this is obviously not the start of a decl, then we're done. if (!isKeywordPossibleDeclStart(Tok)) return false; @@ -3447,23 +3672,14 @@ bool Parser::isStartOfSwiftDecl() { if (Tok.is(tok::kw_try)) return peekToken().isAny(tok::kw_let, tok::kw_var); - // Look through attribute list, because it may be an *type* attribute list. + // Skip an attribute, since it might be a type attribute. This can't + // happen at the top level of a scope, but we do use isStartOfSwiftDecl() + // in positions like generic argument lists. if (Tok.is(tok::at_sign)) { BacktrackingScope backtrack(*this); - while (consumeIf(tok::at_sign)) { - // If not identifier or code complete token, consider '@' as an incomplete - // attribute. - if (Tok.isNot(tok::identifier, tok::code_complete)) - continue; - consumeToken(); - // Eat paren after attribute name; e.g. @foo(x) - if (consumeIf(tok::l_paren)) { - while (Tok.isNot(tok::r_brace, tok::eof, tok::pound_endif)) { - if (consumeIf(tok::r_paren)) break; - skipSingle(); - } - } - } + while (consumeIf(tok::at_sign)) + skipAttribute(*this); + // If this attribute is the last element in the block, // consider it is a start of incomplete decl. if (Tok.isAny(tok::r_brace, tok::eof, tok::pound_endif)) @@ -3731,6 +3947,8 @@ Parser::parseDecl(ParseDeclOptions Flags, auto OrigTok = Tok; bool MayNeedOverrideCompletion = false; + bool HandlerAlreadyCalled = false; + auto parseLetOrVar = [&](bool HasLetOrVarKeyword) { // Collect all modifiers into a modifier list. DeclParsingContext.setCreateSyntax(SyntaxKind::VariableDecl); @@ -3743,8 +3961,7 @@ Parser::parseDecl(ParseDeclOptions Flags, && isCodeCompletionFirstPass()) return; std::for_each(Entries.begin(), Entries.end(), Handler); - if (auto *D = DeclResult.getPtrOrNull()) - markWasHandled(D); + HandlerAlreadyCalled = true; }; auto parseFunc = [&](bool HasFuncKeyword) { @@ -3791,8 +4008,7 @@ Parser::parseDecl(ParseDeclOptions Flags, isCodeCompletionFirstPass()) break; std::for_each(Entries.begin(), Entries.end(), Handler); - if (auto *D = DeclResult.getPtrOrNull()) - markWasHandled(D); + HandlerAlreadyCalled = true; break; } case tok::kw_class: @@ -3837,8 +4053,7 @@ Parser::parseDecl(ParseDeclOptions Flags, break; std::for_each(Entries.begin(), Entries.end(), Handler); MayNeedOverrideCompletion = true; - if (auto *D = DeclResult.getPtrOrNull()) - markWasHandled(D); + HandlerAlreadyCalled = true; break; } @@ -4018,8 +4233,7 @@ Parser::parseDecl(ParseDeclOptions Flags, if (DeclResult.isNonNull()) { Decl *D = DeclResult.get(); - - if (!declWasHandledAlready(D)) { + if (!HandlerAlreadyCalled) { Handler(D); } @@ -4593,9 +4807,6 @@ Parser::parseDeclList(SourceLoc LBLoc, SourceLoc &RBLoc, Diag<> ErrorDiag, if (RBLoc.isInvalid()) hadError = true; - if (!Context.LangOpts.EnableTypeFingerprints) - return std::make_pair(decls, None); - llvm::MD5::MD5Result result; auto declListHash = MemberHashingScope ? *CurrentTokenHash : llvm::MD5(); declListHash.final(result); @@ -6000,7 +6211,7 @@ Parser::parseDeclVar(ParseDeclOptions Flags, // up in Decls to be returned to caller. if (topLevelDecl) { PBD->setDeclContext(topLevelDecl); - auto range = PBD->getSourceRange(); + auto range = PBD->getSourceRangeIncludingAttrs(); topLevelDecl->setBody(BraceStmt::create(Context, range.Start, ASTNode(PBD), range.End, true)); Decls.insert(Decls.begin()+NumDeclsInResult, topLevelDecl); @@ -6469,11 +6680,7 @@ BraceStmt *Parser::parseAbstractFunctionBodyImpl(AbstractFunctionDecl *AFD) { // If the body consists of a single expression, turn it into a return // statement. - // - // But don't do this transformation when performing certain kinds of code - // completion, as the source may be incomplete and the type mismatch in return - // statement will just confuse the type checker. - if (shouldSuppressSingleExpressionBodyTransform(Body, BS->getElements())) + if (BS->getNumElements() != 1) return BS; auto Element = BS->getFirstElement(); diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index b61e21834871e..b22c13da29bdf 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -421,6 +421,7 @@ ParserResult Parser::parseExprSequenceElement(Diag<> message, isType = canParseType(); } if (isType) { + SyntaxParsingContext TyExprCtx(SyntaxContext, SyntaxKind::TypeExpr); ParserResult ty = parseType(); if (ty.isNonNull()) return makeParserResult( @@ -853,29 +854,6 @@ UnresolvedDeclRefExpr *Parser::parseExprOperator() { return new (Context) UnresolvedDeclRefExpr(name, refKind, DeclNameLoc(loc)); } -static VarDecl *getImplicitSelfDeclForSuperContext(Parser &P, - DeclContext *DC, - SourceLoc Loc) { - auto *methodContext = DC->getInnermostMethodContext(); - if (!methodContext) { - P.diagnose(Loc, diag::super_not_in_class_method); - return nullptr; - } - - // Do an actual lookup for 'self' in case it shows up in a capture list. - auto *methodSelf = methodContext->getImplicitSelfDecl(); - auto *lookupSelf = P.lookupInScope(DeclNameRef(P.Context.Id_self)); - if (lookupSelf && lookupSelf != methodSelf) { - // FIXME: This is the wrong diagnostic for if someone manually declares a - // variable named 'self' using backticks. - P.diagnose(Loc, diag::super_in_closure_with_capture); - P.diagnose(lookupSelf->getLoc(), diag::super_in_closure_with_capture_here); - return nullptr; - } - - return methodSelf; -} - /// parseExprSuper /// /// expr-super: @@ -903,12 +881,8 @@ ParserResult Parser::parseExprSuper() { return nullptr; } - VarDecl *selfDecl = - getImplicitSelfDeclForSuperContext(*this, CurDeclContext, superLoc); - if (!selfDecl) - return makeParserResult(new (Context) ErrorExpr(superLoc)); - - return makeParserResult(new (Context) SuperRefExpr(selfDecl, superLoc, + return makeParserResult(new (Context) SuperRefExpr(/*selfDecl=*/nullptr, + superLoc, /*Implicit=*/false)); } @@ -2235,27 +2209,30 @@ Expr *Parser::parseExprIdentifier() { // lookups, so disable this check when parsing for SwiftSyntax. if (!InPoundIfEnvironment && !Context.LangOpts.ParseForSyntaxTreeOnly) { D = lookupInScope(name); - // FIXME: We want this to work: "var x = { x() }", but for now it's better - // to disallow it than to crash. - if (D) { - for (auto activeVar : DisabledVars) { - if (activeVar == D) { - diagnose(loc.getBaseNameLoc(), DisabledVarReason); - return new (Context) ErrorExpr(loc.getSourceRange()); + + if (!Context.LangOpts.DisableParserLookup) { + // FIXME: We want this to work: "var x = { x() }", but for now it's better + // to disallow it than to crash. + if (D) { + for (auto activeVar : DisabledVars) { + if (activeVar == D) { + diagnose(loc.getBaseNameLoc(), DisabledVarReason); + return new (Context) ErrorExpr(loc.getSourceRange()); + } } - } - } else { - for (auto activeVar : DisabledVars) { - if (activeVar->getName() == name.getFullName()) { - diagnose(loc.getBaseNameLoc(), DisabledVarReason); - return new (Context) ErrorExpr(loc.getSourceRange()); + } else { + for (auto activeVar : DisabledVars) { + if (activeVar->getName() == name.getFullName()) { + diagnose(loc.getBaseNameLoc(), DisabledVarReason); + return new (Context) ErrorExpr(loc.getSourceRange()); + } } } } } Expr *E; - if (D == nullptr) { + if (D == nullptr || D->getAttrs().hasAttribute()) { if (name.getBaseName().isEditorPlaceholder()) { IDSyntaxContext.setCreateSyntax(SyntaxKind::EditorPlaceholderExpr); return parseExprEditorPlaceholder(IdentTok, name.getBaseIdentifier()); @@ -2865,13 +2842,8 @@ ParserResult Parser::parseExprClosure() { // If the body consists of a single expression, turn it into a return // statement. - // - // But don't do this transformation when performing certain kinds of code - // completion, as the source may be incomplete and the type mismatch in return - // statement will just confuse the type checker. bool hasSingleExpressionBody = false; - if (!missingRBrace && - !shouldSuppressSingleExpressionBodyTransform(Status, bodyElements)) { + if (!missingRBrace && bodyElements.size() == 1) { // If the closure's only body element is a single return statement, // use that instead of creating a new wrapping return expression. Expr *returnExpr = nullptr; diff --git a/lib/Parse/ParseGeneric.cpp b/lib/Parse/ParseGeneric.cpp index f980de64d56c8..969635bdd3cb6 100644 --- a/lib/Parse/ParseGeneric.cpp +++ b/lib/Parse/ParseGeneric.cpp @@ -16,6 +16,7 @@ #include "swift/Parse/Parser.h" #include "swift/AST/DiagnosticsParse.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/TypeRepr.h" #include "swift/Parse/CodeCompletionCallbacks.h" #include "swift/Parse/SyntaxParsingContext.h" diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index 090c05110529d..5da41db1c609b 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -1183,8 +1183,10 @@ static void parseGuardedPattern(Parser &P, GuardedPattern &result, /// Validate availability spec list, emitting diagnostics if necessary and removing /// specs for unrecognized platforms. -static void validateAvailabilitySpecList(Parser &P, - SmallVectorImpl &Specs) { +static void +validateAvailabilitySpecList(Parser &P, + SmallVectorImpl &Specs, + bool ParsingMacroDefinition) { llvm::SmallSet Platforms; bool HasOtherPlatformSpec = false; @@ -1232,8 +1234,13 @@ static void validateAvailabilitySpecList(Parser &P, } } - if (!HasOtherPlatformSpec) { - SourceLoc InsertWildcardLoc = Specs.back()->getSourceRange().End; + if (ParsingMacroDefinition) { + if (HasOtherPlatformSpec) { + SourceLoc InsertWildcardLoc = P.PreviousLoc; + P.diagnose(InsertWildcardLoc, diag::attr_availability_wildcard_in_macro); + } + } else if (!HasOtherPlatformSpec) { + SourceLoc InsertWildcardLoc = P.PreviousLoc; P.diagnose(InsertWildcardLoc, diag::availability_query_wildcard_required) .fixItInsertAfter(InsertWildcardLoc, ", *"); } @@ -1285,7 +1292,40 @@ ParserResult Parser::parseStmtConditionPoundAvailable() { } ParserStatus -Parser::parseAvailabilitySpecList(SmallVectorImpl &Specs) { +Parser::parseAvailabilityMacroDefinition(AvailabilityMacroDefinition &Result) { + + // Prime the lexer. + if (Tok.is(tok::NUM_TOKENS)) + consumeTokenWithoutFeedingReceiver(); + + if (!Tok.isIdentifierOrUnderscore()) { + diagnose(Tok, diag::attr_availability_missing_macro_name); + return makeParserError(); + } + + Result.Name = Tok.getText(); + consumeToken(); + + if (Tok.isAny(tok::integer_literal, tok::floating_literal)) { + SourceRange VersionRange; + if (parseVersionTuple(Result.Version, VersionRange, + diag::avail_query_expected_version_number)) { + return makeParserError(); + } + } + + if (!consumeIf(tok::colon)) { + diagnose(Tok, diag::attr_availability_expected_colon_macro, Result.Name); + return makeParserError(); + } + + return parseAvailabilitySpecList(Result.Specs, + /*ParsingMacroDefinition=*/true); +} + +ParserStatus +Parser::parseAvailabilitySpecList(SmallVectorImpl &Specs, + bool ParsingMacroDefinition) { SyntaxParsingContext AvailabilitySpecContext( SyntaxContext, SyntaxKind::AvailabilitySpecList); ParserStatus Status = makeParserSuccess(); @@ -1295,14 +1335,34 @@ Parser::parseAvailabilitySpecList(SmallVectorImpl &Specs) { while (1) { SyntaxParsingContext AvailabilityEntryContext( SyntaxContext, SyntaxKind::AvailabilityArgument); - auto SpecResult = parseAvailabilitySpec(); - if (auto *Spec = SpecResult.getPtrOrNull()) { - Specs.push_back(Spec); - } else { - if (SpecResult.hasCodeCompletion()) { - return makeParserCodeCompletionStatus(); + + // First look for a macro as we need Specs for the expansion. + bool MatchedAMacro = false; + if (!ParsingMacroDefinition && Tok.is(tok::identifier)) { + SmallVector MacroSpecs; + ParserStatus MacroStatus = parseAvailabilityMacro(MacroSpecs); + + if (MacroStatus.isError()) { + // There's a parsing error if the platform name matches a macro + // but something goes wrong after. + Status.setIsParseError(); + MatchedAMacro = true; + } else { + MatchedAMacro = !MacroSpecs.empty(); + Specs.append(MacroSpecs.begin(), MacroSpecs.end()); + } + } + + if (!MatchedAMacro) { + auto SpecResult = parseAvailabilitySpec(); + if (auto *Spec = SpecResult.getPtrOrNull()) { + Specs.push_back(Spec); + } else { + if (SpecResult.hasCodeCompletion()) { + return makeParserCodeCompletionStatus(); + } + Status.setIsParseError(); } - Status.setIsParseError(); } // We don't allow binary operators to combine specs. @@ -1362,7 +1422,7 @@ Parser::parseAvailabilitySpecList(SmallVectorImpl &Specs) { } if (Status.isSuccess() && !Status.hasCodeCompletion()) - validateAvailabilitySpecList(*this, Specs); + validateAvailabilitySpecList(*this, Specs, ParsingMacroDefinition); return Status; } @@ -1876,9 +1936,8 @@ ParserResult Parser::parseStmtRepeat(LabeledStmtInfo labelInfo) { ParserResult condition; if (Tok.is(tok::l_brace)) { - SourceLoc lbraceLoc = Tok.getLoc(); diagnose(whileLoc, diag::missing_condition_after_while); - condition = makeParserErrorResult(new (Context) ErrorExpr(lbraceLoc)); + condition = makeParserErrorResult(new (Context) ErrorExpr(whileLoc)); } else { condition = parseExpr(diag::expected_expr_repeat_while); status |= condition; diff --git a/lib/Parse/ParseType.cpp b/lib/Parse/ParseType.cpp index 016281ab3d6b0..26e7b0999ea28 100644 --- a/lib/Parse/ParseType.cpp +++ b/lib/Parse/ParseType.cpp @@ -17,6 +17,7 @@ #include "swift/Parse/Parser.h" #include "swift/AST/ASTWalker.h" #include "swift/AST/Attr.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/TypeRepr.h" #include "swift/Parse/Lexer.h" #include "swift/Parse/CodeCompletionCallbacks.h" @@ -840,7 +841,7 @@ Parser::parseTypeSimpleOrComposition(Diag<> MessageID, // This is only semantically allowed in certain contexts, but we parse it // generally for diagnostics and recovery. SourceLoc opaqueLoc; - if (Tok.is(tok::identifier) && Tok.getRawText() == "some") { + if (Tok.isContextualKeyword("some")) { // Treat some as a keyword. TokReceiver->registerTokenKindChange(Tok.getLoc(), tok::contextual_keyword); opaqueLoc = consumeToken(); @@ -904,7 +905,7 @@ Parser::parseTypeSimpleOrComposition(Diag<> MessageID, } // Diagnose invalid `some` after an ampersand. - if (Tok.is(tok::identifier) && Tok.getRawText() == "some") { + if (Tok.isContextualKeyword("some")) { auto badLoc = consumeToken(); diagnose(badLoc, diag::opaque_mid_composition) @@ -1093,7 +1094,7 @@ ParserResult Parser::parseTypeTupleBody() { // If the label is "some", this could end up being an opaque type // description if there's `some ` without a following colon, // so we may need to backtrack as well. - if (Tok.getText().equals("some")) { + if (Tok.isContextualKeyword("some")) { Backtracking.emplace(*this); } @@ -1194,8 +1195,10 @@ ParserResult Parser::parseTypeTupleBody() { if (EllipsisLoc.isInvalid()) EllipsisIdx = ElementsR.size(); - bool isFunctionType = Tok.isAny(tok::arrow, tok::kw_throws, - tok::kw_rethrows); + bool isFunctionType = + Tok.isAny(tok::arrow, tok::kw_throws, tok::kw_rethrows) || + (shouldParseExperimentalConcurrency() && + Tok.isContextualKeyword("async")); // If there were any labels, figure out which labels should go into the type // representation. @@ -1528,6 +1531,9 @@ bool Parser::canParseType() { // Accept 'inout' at for better recovery. consumeIf(tok::kw_inout); + if (Tok.isContextualKeyword("some")) + consumeToken(); + switch (Tok.getKind()) { case tok::kw_Self: case tok::kw_Any: diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index 66eeaced9ffa6..ce02e6849f797 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -189,9 +189,9 @@ void Parser::performCodeCompletionSecondPassImpl( parseDecl(ParseDeclOptions(info.Flags), /*IsAtStartOfLineOrPreviousHadSemi=*/true, [&](Decl *D) { if (auto *NTD = dyn_cast(DC)) { - NTD->addMember(D); + NTD->addMemberPreservingSourceOrder(D); } else if (auto *ED = dyn_cast(DC)) { - ED->addMember(D); + ED->addMemberPreservingSourceOrder(D); } else if (auto *SF = dyn_cast(DC)) { SF->addTopLevelDecl(D); } else { @@ -1185,34 +1185,6 @@ Parser::getStringLiteralIfNotInterpolated(SourceLoc Loc, Segments.front().Length)); } -bool Parser:: -shouldSuppressSingleExpressionBodyTransform(ParserStatus Status, - MutableArrayRef BodyElems) { - if (BodyElems.size() != 1) - return true; - - if (!Status.hasCodeCompletion()) - return false; - - struct HasMemberCompletion: public ASTWalker { - bool Value = false; - std::pair walkToExprPre(Expr *E) override { - if (auto *CCE = dyn_cast(E)) { - // If it has a base expression this is member completion, which is - // performed using the new solver-based mechanism, so it's ok to go - // ahead with the transform (and necessary to pick up the correct - // expected type). - Value = CCE->getBase(); - return {false, nullptr}; - } - return {true, E}; - } - }; - HasMemberCompletion Check; - BodyElems.front().walk(Check); - return !Check.Value; -} - struct ParserUnit::Implementation { std::shared_ptr SPActions; LangOptions LangOpts; diff --git a/lib/PrintAsObjC/CMakeLists.txt b/lib/PrintAsObjC/CMakeLists.txt index ae667af64ac31..66044fa3fc7ff 100644 --- a/lib/PrintAsObjC/CMakeLists.txt +++ b/lib/PrintAsObjC/CMakeLists.txt @@ -1,3 +1,4 @@ +set_swift_llvm_is_available() add_swift_host_library(swiftPrintAsObjC STATIC DeclAndTypePrinter.cpp ModuleContentsWriter.cpp diff --git a/lib/SIL/CMakeLists.txt b/lib/SIL/CMakeLists.txt index 74ca0507393d3..96a6b852ad8b0 100644 --- a/lib/SIL/CMakeLists.txt +++ b/lib/SIL/CMakeLists.txt @@ -1,3 +1,4 @@ +set_swift_llvm_is_available() add_swift_host_library(swiftSIL STATIC SIL.cpp) target_link_libraries(swiftSIL PUBLIC diff --git a/lib/SIL/IR/ApplySite.cpp b/lib/SIL/IR/ApplySite.cpp new file mode 100644 index 0000000000000..2e6aa2bd2077c --- /dev/null +++ b/lib/SIL/IR/ApplySite.cpp @@ -0,0 +1,46 @@ +//===--- ApplySite.cpp - Wrapper around apply instructions ----------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "swift/SIL/ApplySite.h" +#include "swift/SIL/SILBuilder.h" + + +using namespace swift; + +void FullApplySite::insertAfterInvocation(function_ref func) const { + SILBuilderWithScope::insertAfter(getInstruction(), func); +} + +void FullApplySite::insertAfterFullEvaluation( + function_ref func) const { + switch (getKind()) { + case FullApplySiteKind::ApplyInst: + case FullApplySiteKind::TryApplyInst: + return insertAfterInvocation(func); + case FullApplySiteKind::BeginApplyInst: + SmallVector endApplies; + SmallVector abortApplies; + auto *bai = cast(getInstruction()); + bai->getCoroutineEndPoints(endApplies, abortApplies); + for (auto *eai : endApplies) { + SILBuilderWithScope builder(std::next(eai->getIterator())); + func(builder); + } + for (auto *aai : abortApplies) { + SILBuilderWithScope builder(std::next(aai->getIterator())); + func(builder); + } + return; + } + llvm_unreachable("covered switch isn't covered"); +} + diff --git a/lib/SIL/IR/CMakeLists.txt b/lib/SIL/IR/CMakeLists.txt index d5b426ed47358..394ceb88544bf 100644 --- a/lib/SIL/IR/CMakeLists.txt +++ b/lib/SIL/IR/CMakeLists.txt @@ -1,5 +1,6 @@ target_sources(swiftSIL PRIVATE AbstractionPattern.cpp + ApplySite.cpp Bridging.cpp Linker.cpp Notifications.cpp diff --git a/lib/SIL/IR/OperandOwnership.cpp b/lib/SIL/IR/OperandOwnership.cpp index 7f928667b8913..6b51e8b4f69f9 100644 --- a/lib/SIL/IR/OperandOwnership.cpp +++ b/lib/SIL/IR/OperandOwnership.cpp @@ -136,6 +136,8 @@ SHOULD_NEVER_VISIT_INST(Unwind) SHOULD_NEVER_VISIT_INST(ReleaseValue) SHOULD_NEVER_VISIT_INST(ReleaseValueAddr) SHOULD_NEVER_VISIT_INST(StrongRelease) +SHOULD_NEVER_VISIT_INST(GetAsyncContinuation) + #define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ SHOULD_NEVER_VISIT_INST(StrongRetain##Name) \ SHOULD_NEVER_VISIT_INST(Name##Retain) @@ -174,6 +176,7 @@ CONSTANT_OWNERSHIP_INST(Owned, MustBeInvalidated, DestroyValue) CONSTANT_OWNERSHIP_INST(Owned, MustBeInvalidated, EndLifetime) CONSTANT_OWNERSHIP_INST(Owned, MustBeInvalidated, BeginCOWMutation) CONSTANT_OWNERSHIP_INST(Owned, MustBeInvalidated, EndCOWMutation) +CONSTANT_OWNERSHIP_INST(Owned, MustBeLive, AwaitAsyncContinuation) CONSTANT_OWNERSHIP_INST(None, MustBeLive, AbortApply) CONSTANT_OWNERSHIP_INST(None, MustBeLive, AddressToPointer) CONSTANT_OWNERSHIP_INST(None, MustBeLive, BeginAccess) @@ -189,6 +192,7 @@ CONSTANT_OWNERSHIP_INST(None, MustBeLive, DestroyAddr) CONSTANT_OWNERSHIP_INST(None, MustBeLive, EndAccess) CONSTANT_OWNERSHIP_INST(None, MustBeLive, EndApply) CONSTANT_OWNERSHIP_INST(None, MustBeLive, EndUnpairedAccess) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, GetAsyncContinuationAddr) CONSTANT_OWNERSHIP_INST(None, MustBeLive, IndexAddr) CONSTANT_OWNERSHIP_INST(None, MustBeLive, IndexRawPointer) CONSTANT_OWNERSHIP_INST(None, MustBeLive, InitBlockStorageHeader) diff --git a/lib/SIL/IR/SIL.cpp b/lib/SIL/IR/SIL.cpp index e9c739af20805..ef497ac51eb9b 100644 --- a/lib/SIL/IR/SIL.cpp +++ b/lib/SIL/IR/SIL.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 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -80,6 +80,11 @@ swift::getLinkageForProtocolConformance(const RootProtocolConformance *C, if (isa(C->getDeclContext()->getModuleScopeContext())) return SILLinkage::Shared; + // If the conforming type is a non-nomianl, give it public linkage. + // These conformances are implemented within the runtime. + if (!C->getType()->getAnyNominal()) + return definition ? SILLinkage::Public : SILLinkage::PublicExternal; + auto typeDecl = C->getType()->getNominalOrBoundGenericNominal(); AccessLevel access = std::min(C->getProtocol()->getEffectiveAccess(), typeDecl->getEffectiveAccess()); diff --git a/lib/SIL/IR/SILArgument.cpp b/lib/SIL/IR/SILArgument.cpp index 1d447fbd00976..48affb815ab21 100644 --- a/lib/SIL/IR/SILArgument.cpp +++ b/lib/SIL/IR/SILArgument.cpp @@ -228,6 +228,7 @@ getSingleTerminatorOperandForPred(const SILBasicBlock *parentBlock, case TermKind::CheckedCastAddrBranchInst: case TermKind::DynamicMethodBranchInst: case TermKind::YieldInst: + case TermKind::AwaitAsyncContinuationInst: return SILValue(); case TermKind::BranchInst: return cast(predTermInst)->getArg(argIndex); diff --git a/lib/SIL/IR/SILBuilder.cpp b/lib/SIL/IR/SILBuilder.cpp index c925cc83bb737..ff246686d696d 100644 --- a/lib/SIL/IR/SILBuilder.cpp +++ b/lib/SIL/IR/SILBuilder.cpp @@ -661,3 +661,19 @@ CheckedCastBranchInst *SILBuilder::createCheckedCastBranch( destLoweredTy, destFormalTy, successBB, failureBB, getFunction(), C.OpenedArchetypes, target1Count, target2Count)); } + +void SILBuilderWithScope::insertAfter(SILInstruction *inst, + function_ref func) { + if (isa(inst)) { + for (const SILSuccessor &succ : inst->getParent()->getSuccessors()) { + SILBasicBlock *succBlock = succ; + assert(succBlock->getSinglePredecessorBlock() == inst->getParent() && + "the terminator instruction must not have critical successors"); + SILBuilderWithScope builder(succBlock->begin()); + func(builder); + } + } else { + SILBuilderWithScope builder(std::next(inst->getIterator())); + func(builder); + } +} diff --git a/lib/SIL/IR/SILDeclRef.cpp b/lib/SIL/IR/SILDeclRef.cpp index 1b6f2ce79950f..eeb75288027fb 100644 --- a/lib/SIL/IR/SILDeclRef.cpp +++ b/lib/SIL/IR/SILDeclRef.cpp @@ -20,6 +20,7 @@ #include "swift/ClangImporter/ClangModule.h" #include "swift/SIL/SILLinkage.h" #include "swift/SIL/SILLocation.h" +#include "swift/SILOptimizer/Utils/SpecializationMangler.h" #include "clang/AST/Attr.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" @@ -118,10 +119,11 @@ bool swift::requiresForeignEntryPoint(ValueDecl *vd) { SILDeclRef::SILDeclRef(ValueDecl *vd, SILDeclRef::Kind kind, bool isForeign, AutoDiffDerivativeFunctionIdentifier *derivativeId) : loc(vd), kind(kind), isForeign(isForeign), defaultArgIndex(0), - derivativeFunctionIdentifier(derivativeId) {} + pointer(derivativeId) {} SILDeclRef::SILDeclRef(SILDeclRef::Loc baseLoc, bool asForeign) - : defaultArgIndex(0), derivativeFunctionIdentifier(nullptr) { + : defaultArgIndex(0), + pointer((AutoDiffDerivativeFunctionIdentifier *)nullptr) { if (auto *vd = baseLoc.dyn_cast()) { if (auto *fd = dyn_cast(vd)) { // Map FuncDecls directly to Func SILDeclRefs. @@ -160,6 +162,12 @@ SILDeclRef::SILDeclRef(SILDeclRef::Loc baseLoc, bool asForeign) isForeign = asForeign; } +SILDeclRef::SILDeclRef(SILDeclRef::Loc baseLoc, + GenericSignature prespecializedSig) + : SILDeclRef(baseLoc, false) { + pointer = prespecializedSig.getPointer(); +} + Optional SILDeclRef::getAnyFunctionRef() const { if (auto vd = loc.dyn_cast()) { if (auto afd = dyn_cast(vd)) { @@ -223,6 +231,12 @@ bool SILDeclRef::isImplicit() const { } SILLinkage SILDeclRef::getLinkage(ForDefinition_t forDefinition) const { + + // Prespecializations are public. + if (getSpecializedSignature()) { + return SILLinkage::Public; + } + if (getAbstractClosureExpr()) { return isSerialized() ? SILLinkage::Shared : SILLinkage::Private; } @@ -487,7 +501,8 @@ IsSerialized_t SILDeclRef::isSerialized() const { // Stored property initializers are inlinable if the type is explicitly // marked as @frozen. - if (isStoredPropertyInitializer() || isPropertyWrapperBackingInitializer()) { + if (isStoredPropertyInitializer() || (isPropertyWrapperBackingInitializer() && + d->getDeclContext()->isTypeContext())) { auto *nominal = cast(d->getDeclContext()); auto scope = nominal->getFormalAccessScope(/*useDC=*/nullptr, @@ -516,6 +531,11 @@ IsSerialized_t SILDeclRef::isSerialized() const { if (d->getEffectiveAccess() < AccessLevel::Public) return IsNotSerialized; + // Enum element constructors are serializable if the enum is + // @usableFromInline or public. + if (isEnumElement()) + return IsSerializable; + // 'read' and 'modify' accessors synthesized on-demand are serialized if // visible outside the module. if (auto fn = dyn_cast(d)) @@ -664,6 +684,7 @@ std::string SILDeclRef::mangle(ManglingKind MKind) const { using namespace Mangle; ASTMangler mangler; + auto *derivativeFunctionIdentifier = getDerivativeFunctionIdentifier(); if (derivativeFunctionIdentifier) { std::string originalMangled = asAutoDiffOriginalFunction().mangle(MKind); auto *silParameterIndices = autodiff::getLoweredParameterIndices( @@ -701,6 +722,18 @@ std::string SILDeclRef::mangle(ManglingKind MKind) const { } } + // Mangle prespecializations. + if (getSpecializedSignature()) { + SILDeclRef nonSpecializedDeclRef = *this; + nonSpecializedDeclRef.pointer = + (AutoDiffDerivativeFunctionIdentifier *)nullptr; + auto mangledNonSpecializedString = nonSpecializedDeclRef.mangle(); + auto *funcDecl = cast(getDecl()); + auto genericSig = funcDecl->getGenericSignature(); + return GenericSpecializationMangler::manglePrespecialization( + mangledNonSpecializedString, genericSig, getSpecializedSignature()); + } + ASTMangler::SymbolKind SKind = ASTMangler::SymbolKind::Default; switch (MKind) { case SILDeclRef::ManglingKind::Default: @@ -793,7 +826,7 @@ std::string SILDeclRef::mangle(ManglingKind MKind) const { // Returns true if the given JVP/VJP SILDeclRef requires a new vtable entry. // FIXME(TF-1213): Also consider derived declaration `@derivative` attributes. static bool derivativeFunctionRequiresNewVTableEntry(SILDeclRef declRef) { - assert(declRef.derivativeFunctionIdentifier && + assert(declRef.getDerivativeFunctionIdentifier() && "Expected a derivative function SILDeclRef"); auto overridden = declRef.getOverridden(); if (!overridden) @@ -803,7 +836,7 @@ static bool derivativeFunctionRequiresNewVTableEntry(SILDeclRef declRef) { declRef.getDecl()->getAttrs().getAttributes(), [&](const DifferentiableAttr *derivedDiffAttr) { return derivedDiffAttr->getParameterIndices() == - declRef.derivativeFunctionIdentifier->getParameterIndices(); + declRef.getDerivativeFunctionIdentifier()->getParameterIndices(); }); assert(derivedDiffAttr && "Expected `@differentiable` attribute"); // Otherwise, if the base `@differentiable` attribute specifies a derivative @@ -813,7 +846,7 @@ static bool derivativeFunctionRequiresNewVTableEntry(SILDeclRef declRef) { overridden.getDecl()->getAttrs().getAttributes(); for (auto *baseDiffAttr : baseDiffAttrs) { if (baseDiffAttr->getParameterIndices() == - declRef.derivativeFunctionIdentifier->getParameterIndices()) + declRef.getDerivativeFunctionIdentifier()->getParameterIndices()) return false; } // Otherwise, if there is no base `@differentiable` attribute exists, then a @@ -822,7 +855,7 @@ static bool derivativeFunctionRequiresNewVTableEntry(SILDeclRef declRef) { } bool SILDeclRef::requiresNewVTableEntry() const { - if (derivativeFunctionIdentifier) + if (getDerivativeFunctionIdentifier()) if (derivativeFunctionRequiresNewVTableEntry(*this)) return true; if (!hasDecl()) @@ -903,15 +936,16 @@ SILDeclRef SILDeclRef::getNextOverriddenVTableEntry() const { // JVPs/VJPs are overridden only if the base declaration has a // `@differentiable` attribute with the same parameter indices. - if (derivativeFunctionIdentifier) { + if (getDerivativeFunctionIdentifier()) { auto overriddenAttrs = overridden.getDecl()->getAttrs().getAttributes(); for (const auto *attr : overriddenAttrs) { if (attr->getParameterIndices() != - derivativeFunctionIdentifier->getParameterIndices()) + getDerivativeFunctionIdentifier()->getParameterIndices()) continue; - auto *overriddenDerivativeId = overridden.derivativeFunctionIdentifier; - overridden.derivativeFunctionIdentifier = + auto *overriddenDerivativeId = + overridden.getDerivativeFunctionIdentifier(); + overridden.pointer = AutoDiffDerivativeFunctionIdentifier::get( overriddenDerivativeId->getKind(), overriddenDerivativeId->getParameterIndices(), diff --git a/lib/SIL/IR/SILFunction.cpp b/lib/SIL/IR/SILFunction.cpp index 37c79531badc3..762b31378046b 100644 --- a/lib/SIL/IR/SILFunction.cpp +++ b/lib/SIL/IR/SILFunction.cpp @@ -32,15 +32,23 @@ using namespace swift; using namespace Lowering; SILSpecializeAttr::SILSpecializeAttr(bool exported, SpecializationKind kind, - GenericSignature specializedSig) - : kind(kind), exported(exported), specializedSignature(specializedSig) { } - -SILSpecializeAttr *SILSpecializeAttr::create(SILModule &M, - GenericSignature specializedSig, - bool exported, - SpecializationKind kind) { + GenericSignature specializedSig, + SILFunction *target, Identifier spiGroup, + const ModuleDecl *spiModule) + : kind(kind), exported(exported), specializedSignature(specializedSig), + spiGroup(spiGroup), spiModule(spiModule), targetFunction(target) { + if (targetFunction) + targetFunction->incrementRefCount(); +} + +SILSpecializeAttr * +SILSpecializeAttr::create(SILModule &M, GenericSignature specializedSig, + bool exported, SpecializationKind kind, + SILFunction *target, Identifier spiGroup, + const ModuleDecl *spiModule) { void *buf = M.allocate(sizeof(SILSpecializeAttr), alignof(SILSpecializeAttr)); - return ::new (buf) SILSpecializeAttr(exported, kind, specializedSig); + return ::new (buf) SILSpecializeAttr(exported, kind, specializedSig, target, + spiGroup, spiModule); } void SILFunction::addSpecializeAttr(SILSpecializeAttr *Attr) { @@ -50,6 +58,19 @@ void SILFunction::addSpecializeAttr(SILSpecializeAttr *Attr) { } } +void SILFunction::removeSpecializeAttr(SILSpecializeAttr *attr) { + // Drop the reference to the _specialize(target:) function. + if (auto *targetFun = attr->getTargetFunction()) { + targetFun->decrementRefCount(); + } + SpecializeAttrSet.erase(std::remove_if(SpecializeAttrSet.begin(), + SpecializeAttrSet.end(), + [attr](SILSpecializeAttr *member) { + return member == attr; + }), + SpecializeAttrSet.end()); +} + SILFunction * SILFunction::create(SILModule &M, SILLinkage linkage, StringRef name, CanSILFunctionType loweredType, @@ -563,7 +584,7 @@ void SILFunction::viewCFGOnly() const { } -bool SILFunction::hasSelfMetadataParam() const { +bool SILFunction::hasDynamicSelfMetadata() const { auto paramTypes = getConventions().getParameterSILTypes(TypeExpansionContext::minimal()); if (paramTypes.empty()) @@ -702,3 +723,20 @@ const UnifiedStatsReporter::TraceFormatter* FrontendStatsTracer::getTraceFormatter() { return &TF; } + +bool SILFunction::hasPrespecialization() const { + for (auto *attr : getSpecializeAttrs()) { + if (attr->isExported()) + return true; + } + return false; +} + +void SILFunction::forEachSpecializeAttrTargetFunction( + llvm::function_ref action) { + for (auto *attr : getSpecializeAttrs()) { + if (auto *f = attr->getTargetFunction()) { + action(f); + } + } +} diff --git a/lib/SIL/IR/SILFunctionBuilder.cpp b/lib/SIL/IR/SILFunctionBuilder.cpp index 0ec7e74dbb4d2..460be1d8889d5 100644 --- a/lib/SIL/IR/SILFunctionBuilder.cpp +++ b/lib/SIL/IR/SILFunctionBuilder.cpp @@ -16,8 +16,7 @@ using namespace swift; SILFunction *SILFunctionBuilder::getOrCreateFunction( - SILLocation loc, StringRef name, SILLinkage linkage, - CanSILFunctionType type, IsBare_t isBareSILFunction, + SILLocation loc, StringRef name, SILLinkage linkage, CanSILFunctionType type, IsBare_t isBareSILFunction, IsTransparent_t isTransparent, IsSerialized_t isSerialized, IsDynamicallyReplaceable_t isDynamic, ProfileCounter entryCount, IsThunk_t isThunk, SubclassScope subclassScope) { @@ -53,9 +52,36 @@ void SILFunctionBuilder::addFunctionAttributes( SA->getSpecializationKind() == SpecializeAttr::SpecializationKind::Full ? SILSpecializeAttr::SpecializationKind::Full : SILSpecializeAttr::SpecializationKind::Partial; - F->addSpecializeAttr( - SILSpecializeAttr::create(M, SA->getSpecializedSignature(), - SA->isExported(), kind)); + assert(!constant.isNull()); + SILFunction *targetFunction = nullptr; + auto *attributedFuncDecl = constant.getDecl(); + auto *targetFunctionDecl = SA->getTargetFunctionDecl(attributedFuncDecl); + // Filter out _spi. + auto spiGroups = SA->getSPIGroups(); + bool hasSPI = !spiGroups.empty(); + if (hasSPI) { + if (attributedFuncDecl->getModuleContext() != M.getSwiftModule() && + !M.getSwiftModule()->isImportedAsSPI(SA, attributedFuncDecl)) { + continue; + } + } + assert(spiGroups.size() <= 1 && "SIL does not support multiple SPI groups"); + Identifier spiGroupIdent; + if (hasSPI) { + spiGroupIdent = spiGroups[0]; + } + if (targetFunctionDecl) { + SILDeclRef declRef(targetFunctionDecl, constant.kind, false); + targetFunction = getOrCreateDeclaration(targetFunctionDecl, declRef); + F->addSpecializeAttr(SILSpecializeAttr::create( + M, SA->getSpecializedSignature(), SA->isExported(), kind, + targetFunction, spiGroupIdent, + attributedFuncDecl->getModuleContext())); + } else { + F->addSpecializeAttr(SILSpecializeAttr::create( + M, SA->getSpecializedSignature(), SA->isExported(), kind, nullptr, + spiGroupIdent, attributedFuncDecl->getModuleContext())); + } } if (auto *OA = Attrs.getAttribute()) { diff --git a/lib/SIL/IR/SILFunctionType.cpp b/lib/SIL/IR/SILFunctionType.cpp index 744eab450ac83..ddc9c1d7f62cf 100644 --- a/lib/SIL/IR/SILFunctionType.cpp +++ b/lib/SIL/IR/SILFunctionType.cpp @@ -2788,6 +2788,27 @@ class CXXMethodConventions : public CFunctionTypeConventions { return ParameterConvention::Indirect_In_Guaranteed; return ParameterConvention::Indirect_Inout; } + ResultConvention getResult(const TypeLowering &resultTL) const override { + if (isa(TheDecl)) { + // Represent the `this` pointer as an indirectly returned result. + // This gets us most of the way towards representing the ABI of a + // constructor correctly, but it's not guaranteed to be entirely correct. + // C++ constructor ABIs are complicated and can require passing additional + // "implicit" arguments that depend not only on the signature of the + // constructor but on the class on which it's defined (e.g. whether that + // class has a virtual base class). + // Effectively, we're making an assumption here that there are no implicit + // arguments and that the return type of the constructor ABI is void (and + // indeed we have no way to represent anything else here). If this assumed + // ABI doesn't match the actual ABI, we insert a thunk in IRGen. On some + // ABIs (e.g. Itanium x64), we get lucky and the ABI for a complete + // constructor call always matches the ABI we assume here. Even if the + // actual ABI doesn't match the assumed ABI, we try to get as close as + // possible to make it easy for LLVM to optimize away the thunk. + return ResultConvention::Indirect; + } + return CFunctionTypeConventions::getResult(resultTL); + } static bool classof(const Conventions *C) { return C->getKind() == ConventionsKind::CXXMethod; } @@ -3266,7 +3287,7 @@ TypeConverter::getConstantInfo(TypeExpansionContext expansion, // preserving SIL typing invariants. // // Always use (ad) to compute lowered derivative function types. - if (auto *derivativeId = constant.derivativeFunctionIdentifier) { + if (auto *derivativeId = constant.getDerivativeFunctionIdentifier()) { // Get lowered original function type. auto origFnConstantInfo = getConstantInfo( TypeExpansionContext::minimal(), constant.asAutoDiffOriginalFunction()); @@ -4198,8 +4219,20 @@ TypeConverter::getLoweredFormalTypes(SILDeclRef constant, // Build the uncurried function type. if (innerExtInfo.isThrowing()) extInfo = extInfo.withThrows(true); - - bridgedParams.push_back(selfParam); + if (innerExtInfo.isAsync()) + extInfo = extInfo.withAsync(true); + + // If this is a C++ constructor, don't add the metatype "self" parameter + // because we'll never use it and it will cause problems in IRGen. + if (constant.getDecl()->getClangDecl() && + isa(constant.getDecl()->getClangDecl())) { + // But, make sure it is actually a metatype that we're not adding. If + // changes to the self parameter are made in the future, this logic may + // need to be updated. + assert(selfParam.getParameterType()->is()); + } else { + bridgedParams.push_back(selfParam); + } auto uncurried = CanAnyFunctionType::get(genericSig, diff --git a/lib/SIL/IR/SILGlobalVariable.cpp b/lib/SIL/IR/SILGlobalVariable.cpp index 116cf3572c3c3..729b53a815fad 100644 --- a/lib/SIL/IR/SILGlobalVariable.cpp +++ b/lib/SIL/IR/SILGlobalVariable.cpp @@ -85,7 +85,7 @@ BuiltinInst *SILGlobalVariable::getOffsetSubtract(const TupleExtractInst *TE, // Match the pattern: // tuple_extract(usub_with_overflow(x, integer_literal, integer_literal 0), 0) - if (TE->getFieldNo() != 0) + if (TE->getFieldIndex() != 0) return nullptr; auto *BI = dyn_cast(TE->getOperand()); diff --git a/lib/SIL/IR/SILInstruction.cpp b/lib/SIL/IR/SILInstruction.cpp index afb60bea88446..7d05a902c11b8 100644 --- a/lib/SIL/IR/SILInstruction.cpp +++ b/lib/SIL/IR/SILInstruction.cpp @@ -22,6 +22,7 @@ #include "swift/SIL/SILCloner.h" #include "swift/SIL/SILDebugScope.h" #include "swift/SIL/SILVisitor.h" +#include "swift/SIL/DynamicCasts.h" #include "swift/Basic/AssertImplements.h" #include "swift/ClangImporter/ClangModule.h" #include "swift/SIL/SILModule.h" @@ -579,7 +580,7 @@ namespace { auto *X = cast(LHS); if (X->getTupleType() != RHS->getTupleType()) return false; - if (X->getFieldNo() != RHS->getFieldNo()) + if (X->getFieldIndex() != RHS->getFieldIndex()) return false; return true; } @@ -590,7 +591,7 @@ namespace { auto *X = cast(LHS); if (X->getTupleType() != RHS->getTupleType()) return false; - if (X->getFieldNo() != RHS->getFieldNo()) + if (X->getFieldIndex() != RHS->getFieldIndex()) return false; return true; } @@ -1059,6 +1060,13 @@ bool SILInstruction::mayHaveSideEffects() const { } bool SILInstruction::mayRelease() const { + // Overrule a "DoesNotRelease" of dynamic casts. If a dynamic cast is not + // RC identity preserving it can release it's source (in some cases - we are + // conservative here). + auto dynCast = SILDynamicCastInst::getAs(const_cast(this)); + if (dynCast && !dynCast.isRCIdentityPreserving()) + return true; + if (getReleasingBehavior() == SILInstruction::ReleasingBehavior::DoesNotRelease) return false; @@ -1519,6 +1527,21 @@ MultipleValueInstruction *MultipleValueInstructionResult::getParent() { return reinterpret_cast(value); } +/// Returns true if evaluation of this node may cause suspension of an +/// async task. +bool SILInstruction::maySuspend() const { + // await_async_continuation always suspends the current task. + if (isa(this)) + return true; + + // Fully applying an async function may suspend the caller. + if (auto applySite = FullApplySite::isa(const_cast(this))) { + return applySite.getOrigCalleeType()->isAsync(); + } + + return false; +} + #ifndef NDEBUG //--- @@ -1536,7 +1559,7 @@ MultipleValueInstruction *MultipleValueInstructionResult::getParent() { // Check that all subclasses of MultipleValueInstructionResult are the same size // as MultipleValueInstructionResult. // -// If this changes, we just need to expand the size fo SILInstructionResultArray +// If this changes, we just need to expand the size of SILInstructionResultArray // to contain a stride. But we assume this now so we should enforce it. #define MULTIPLE_VALUE_INST_RESULT(ID, PARENT) \ static_assert( \ diff --git a/lib/SIL/IR/SILInstructions.cpp b/lib/SIL/IR/SILInstructions.cpp index bef2b17c534bc..aa38f2fa5f503 100644 --- a/lib/SIL/IR/SILInstructions.cpp +++ b/lib/SIL/IR/SILInstructions.cpp @@ -84,7 +84,7 @@ static void buildTypeDependentOperands( TypeDependentOperands.push_back(Def); } if (hasDynamicSelf) - TypeDependentOperands.push_back(F.getSelfMetadataArgument()); + TypeDependentOperands.push_back(F.getDynamicSelfMetadata()); } // Collects all opened archetypes from a type and a substitutions list and form @@ -1258,7 +1258,7 @@ bool TupleExtractInst::isTrivialEltOfOneRCIDTuple() const { // parent tuple has only one non-trivial field. bool FoundNonTrivialField = false; SILType OpTy = getOperand()->getType(); - unsigned FieldNo = getFieldNo(); + unsigned FieldNo = getFieldIndex(); // For each element index of the tuple... for (unsigned i = 0, e = getNumTupleElts(); i != e; ++i) { @@ -1300,7 +1300,7 @@ bool TupleExtractInst::isEltOnlyNonTrivialElt() const { // Ok, we know that the elt we are extracting is non-trivial. Make sure that // we have no other non-trivial elts. SILType OpTy = getOperand()->getType(); - unsigned FieldNo = getFieldNo(); + unsigned FieldNo = getFieldIndex(); // For each element index of the tuple... for (unsigned i = 0, e = getNumTupleElts(); i != e; ++i) { @@ -1323,18 +1323,51 @@ bool TupleExtractInst::isEltOnlyNonTrivialElt() const { return true; } -unsigned FieldIndexCacheBase::cacheFieldIndex() { - unsigned i = 0; - for (VarDecl *property : getParentDecl()->getStoredProperties()) { +/// Get a unique index for a struct or class field in layout order. +unsigned swift::getFieldIndex(NominalTypeDecl *decl, VarDecl *field) { + unsigned index = 0; + if (auto *classDecl = dyn_cast(decl)) { + for (auto *superDecl = classDecl->getSuperclassDecl(); superDecl != nullptr; + superDecl = superDecl->getSuperclassDecl()) { + index += superDecl->getStoredProperties().size(); + } + } + for (VarDecl *property : decl->getStoredProperties()) { if (field == property) { - SILInstruction::Bits.FieldIndexCacheBase.FieldIndex = i; - return i; + return index; } - ++i; + ++index; } llvm_unreachable("The field decl for a struct_extract, struct_element_addr, " - "or ref_element_addr must be an accessible stored property " - "of the operand's type"); + "or ref_element_addr must be an accessible stored " + "property of the operand type"); +} + +/// Get the property for a struct or class by its unique index. +VarDecl *swift::getIndexedField(NominalTypeDecl *decl, unsigned index) { + if (auto *structDecl = dyn_cast(decl)) { + return structDecl->getStoredProperties()[index]; + } + auto *classDecl = cast(decl); + SmallVector superclasses; + for (auto *superDecl = classDecl; superDecl != nullptr; + superDecl = superDecl->getSuperclassDecl()) { + superclasses.push_back(superDecl); + } + std::reverse(superclasses.begin(), superclasses.end()); + for (auto *superDecl : superclasses) { + if (index < superDecl->getStoredProperties().size()) { + return superDecl->getStoredProperties()[index]; + } + index -= superDecl->getStoredProperties().size(); + } + return nullptr; +} + +unsigned FieldIndexCacheBase::cacheFieldIndex() { + unsigned index = ::getFieldIndex(getParentDecl(), getField()); + SILInstruction::Bits.FieldIndexCacheBase.FieldIndex = index; + return index; } // FIXME: this should be cached during cacheFieldIndex(). @@ -1441,6 +1474,7 @@ TermInst::SuccessorListTy TermInst::getSuccessors() { bool TermInst::isFunctionExiting() const { switch (getTermKind()) { + case TermKind::AwaitAsyncContinuationInst: case TermKind::BranchInst: case TermKind::CondBranchInst: case TermKind::SwitchValueInst: @@ -1465,6 +1499,7 @@ bool TermInst::isFunctionExiting() const { bool TermInst::isProgramTerminating() const { switch (getTermKind()) { + case TermKind::AwaitAsyncContinuationInst: case TermKind::BranchInst: case TermKind::CondBranchInst: case TermKind::SwitchValueInst: @@ -2895,3 +2930,23 @@ DestructureTupleInst *DestructureTupleInst::create(const SILFunction &F, return ::new (Buffer) DestructureTupleInst(M, Loc, Operand, Types, OwnershipKinds); } + +CanType GetAsyncContinuationInstBase::getFormalResumeType() const { + // The resume type is the type argument to the continuation type. + return getType().castTo().getGenericArgs()[0]; +} + +SILType GetAsyncContinuationInstBase::getLoweredResumeType() const { + // The lowered resume type is the maximally-abstracted lowering of the + // formal resume type. + auto formalType = getFormalResumeType(); + auto &M = getFunction()->getModule(); + auto c = getFunction()->getTypeExpansionContext(); + return M.Types.getLoweredType(AbstractionPattern::getOpaque(), formalType, c); +} + +bool GetAsyncContinuationInstBase::throws() const { + // The continuation throws if it's an UnsafeThrowingContinuation + return getType().castTo()->getDecl() + == getFunction()->getASTContext().getUnsafeThrowingContinuationDecl(); +} diff --git a/lib/SIL/IR/SILModule.cpp b/lib/SIL/IR/SILModule.cpp index 46786ff4539fd..44aec3201f3bd 100644 --- a/lib/SIL/IR/SILModule.cpp +++ b/lib/SIL/IR/SILModule.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 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -95,7 +95,10 @@ class SILModule::SerializationCallback final SILModule::SILModule(llvm::PointerUnion context, Lowering::TypeConverter &TC, const SILOptions &Options) - : Stage(SILStage::Raw), Options(Options), serialized(false), + : Stage(SILStage::Raw), indexTrieRoot(new IndexTrieNode()), + Options(Options), serialized(false), + regDeserializationNotificationHandlerForNonTransparentFuncOME(false), + regDeserializationNotificationHandlerForAllFuncOME(false), SerializeSILAction(), Types(TC) { assert(!context.isNull()); if (auto *file = context.dyn_cast()) { @@ -132,6 +135,7 @@ SILModule::~SILModule() { for (SILFunction &F : *this) { F.dropAllReferences(); F.dropDynamicallyReplacedFunction(); + F.clearSpecializeAttrs(); } } @@ -217,6 +221,11 @@ SILModule::lookUpWitnessTable(const ProtocolConformance *C, SILWitnessTable *wtable; auto rootC = C->getRootConformance(); + + // Builtin conformances don't have witness tables in SIL. + if (isa(rootC)) + return nullptr; + // Attempt to lookup the witness table from the table. auto found = WitnessTableMap.find(rootC); if (found == WitnessTableMap.end()) { @@ -515,6 +524,8 @@ void SILModule::eraseFunction(SILFunction *F) { F->dropAllReferences(); F->dropDynamicallyReplacedFunction(); F->getBlocks().clear(); + // Drop references for any _specialize(target:) functions. + F->clearSpecializeAttrs(); } void SILModule::invalidateFunctionInSILCache(SILFunction *F) { diff --git a/lib/SIL/IR/SILPrinter.cpp b/lib/SIL/IR/SILPrinter.cpp index 81207e5fd5306..3b057395d2156 100644 --- a/lib/SIL/IR/SILPrinter.cpp +++ b/lib/SIL/IR/SILPrinter.cpp @@ -349,9 +349,9 @@ void SILDeclRef::print(raw_ostream &OS) const { if (isForeign) OS << (isDot ? '.' : '!') << "foreign"; - if (derivativeFunctionIdentifier) { + if (getDerivativeFunctionIdentifier()) { OS << ((isDot || isForeign) ? '.' : '!'); - switch (derivativeFunctionIdentifier->getKind()) { + switch (getDerivativeFunctionIdentifier()->getKind()) { case AutoDiffDerivativeFunctionKind::JVP: OS << "jvp."; break; @@ -359,9 +359,9 @@ void SILDeclRef::print(raw_ostream &OS) const { OS << "vjp."; break; } - OS << derivativeFunctionIdentifier->getParameterIndices()->getString(); + OS << getDerivativeFunctionIdentifier()->getParameterIndices()->getString(); if (auto derivativeGenSig = - derivativeFunctionIdentifier->getDerivativeGenericSignature()) { + getDerivativeFunctionIdentifier()->getDerivativeGenericSignature()) { OS << "." << derivativeGenSig; } } @@ -1773,11 +1773,11 @@ class SILPrinter : public SILInstructionVisitor { } void visitTupleExtractInst(TupleExtractInst *EI) { - *this << getIDAndType(EI->getOperand()) << ", " << EI->getFieldNo(); + *this << getIDAndType(EI->getOperand()) << ", " << EI->getFieldIndex(); } void visitTupleElementAddrInst(TupleElementAddrInst *EI) { - *this << getIDAndType(EI->getOperand()) << ", " << EI->getFieldNo(); + *this << getIDAndType(EI->getOperand()) << ", " << EI->getFieldIndex(); } void visitStructExtractInst(StructExtractInst *EI) { *this << getIDAndType(EI->getOperand()) << ", #"; @@ -2073,6 +2073,28 @@ class SILPrinter : public SILInstructionVisitor { *this << ", resume " << Ctx.getID(YI->getResumeBB()) << ", unwind " << Ctx.getID(YI->getUnwindBB()); } + + void visitGetAsyncContinuationInst(GetAsyncContinuationInst *GI) { + if (GI->throws()) + *this << "[throws] "; + *this << '$' << GI->getFormalResumeType(); + } + + void visitGetAsyncContinuationAddrInst(GetAsyncContinuationAddrInst *GI) { + if (GI->throws()) + *this << "[throws] "; + *this << '$' << GI->getFormalResumeType() + << ", " << getIDAndType(GI->getOperand()); + } + + void visitAwaitAsyncContinuationInst(AwaitAsyncContinuationInst *AI) { + *this << getIDAndType(AI->getOperand()) + << ", resume " << Ctx.getID(AI->getResumeBB()); + + if (auto errorBB = AI->getErrorBB()) { + *this << ", error " << Ctx.getID(AI->getErrorBB()); + } + } void visitSwitchValueInst(SwitchValueInst *SII) { *this << getIDAndType(SII->getOperand()); @@ -3443,6 +3465,12 @@ void SILSpecializeAttr::print(llvm::raw_ostream &OS) const { OS << "exported: " << exported << ", "; OS << "kind: " << kind << ", "; + if (!getSPIGroup().empty()) { + OS << "spi: " << getSPIGroup() << ", "; + OS << "spiModule: "; + getSPIModule()->getReverseFullModuleName().printForward(OS); + OS << ", "; + } auto *genericEnv = getFunction()->getGenericEnvironment(); GenericSignature genericSig; @@ -3460,6 +3488,9 @@ void SILSpecializeAttr::print(llvm::raw_ostream &OS) const { requirements = specializedSig->getRequirements(); } } + if (targetFunction) { + OS << "target: \"" << targetFunction->getName() << "\", "; + } if (!requirements.empty()) { OS << "where "; SILFunction *F = getFunction(); diff --git a/lib/SIL/IR/SILType.cpp b/lib/SIL/IR/SILType.cpp index 48ca1e5cbc47d..c478940010411 100644 --- a/lib/SIL/IR/SILType.cpp +++ b/lib/SIL/IR/SILType.cpp @@ -129,6 +129,9 @@ bool SILType::isPointerSizeAndAligned() { // // TODO: handle casting to a loadable existential by generating // init_existential_ref. Until then, only promote to a heap object dest. +// +// This cannot allow trivial-to-reference casts, as required by +// isRCIdentityPreservingCast. bool SILType::canRefCast(SILType operTy, SILType resultTy, SILModule &M) { auto fromTy = operTy.unwrapOptionalType(); auto toTy = resultTy.unwrapOptionalType(); diff --git a/lib/SIL/IR/TypeLowering.cpp b/lib/SIL/IR/TypeLowering.cpp index a1f00e6bb8699..2320cedb9b549 100644 --- a/lib/SIL/IR/TypeLowering.cpp +++ b/lib/SIL/IR/TypeLowering.cpp @@ -154,7 +154,7 @@ namespace { /// classification. template class TypeClassifierBase - : public CanTypeVisitor + : public CanTypeVisitor { Impl &asImpl() { return *static_cast(this); } protected: @@ -167,9 +167,10 @@ namespace { // The subclass should implement: // // Trivial, fixed-layout, and non-address-only. // RetTy handleTrivial(CanType); - // RetTy handleTrivial(CanType. RecursiveProperties properties); + // RetTy handleTrivial(CanType, RecursiveProperties properties); // // A reference type. - // RetTy handleReference(CanType); + // RetTy handleReference(CanType, RecursiveProperties properties); + // RetTy handleReference(CanType, RecursiveProperties properties); // // Non-trivial and address-only. // RetTy handleAddressOnly(CanType, RecursiveProperties properties); // and, if it doesn't override handleTupleType, @@ -196,13 +197,46 @@ namespace { RetTy handleTrivial(CanType type) { return asImpl().handleTrivial(type, RecursiveProperties::forTrivial()); } + RetTy handleReference(CanType type) { - return asImpl().handle(type, RecursiveProperties::forReference()); + return handleReference(type, RecursiveProperties::forReference()); + } + + RetTy handleReference(CanType type, RecursiveProperties properties) { + return asImpl().handle(type, properties); + } + + RecursiveProperties + mergeIsTypeExpansionSensitive(IsTypeExpansionSensitive_t isSensitive, + RecursiveProperties props) { + if (isSensitive == IsTypeExpansionSensitive) + props.setTypeExpansionSensitive(isSensitive); + return props; + } + + RecursiveProperties + getTrivialRecursiveProperties(IsTypeExpansionSensitive_t isSensitive) { + return mergeIsTypeExpansionSensitive(isSensitive, + RecursiveProperties::forTrivial()); + } + + RecursiveProperties + getReferenceRecursiveProperties(IsTypeExpansionSensitive_t isSensitive) { + return mergeIsTypeExpansionSensitive(isSensitive, + RecursiveProperties::forReference()); + } + + RecursiveProperties + getOpaqueRecursiveProperties(IsTypeExpansionSensitive_t isSensitive) { + return mergeIsTypeExpansionSensitive(isSensitive, + RecursiveProperties::forOpaque()); } #define IMPL(TYPE, LOWERING) \ - RetTy visit##TYPE##Type(Can##TYPE##Type type, AbstractionPattern orig) { \ - return asImpl().handle##LOWERING(type); \ + RetTy visit##TYPE##Type(Can##TYPE##Type type, AbstractionPattern orig, \ + IsTypeExpansionSensitive_t isSensitive) { \ + return asImpl().handle##LOWERING(type, \ + get##LOWERING##RecursiveProperties(isSensitive)); \ } IMPL(BuiltinInteger, Trivial) @@ -222,36 +256,46 @@ namespace { RetTy visitBuiltinUnsafeValueBufferType( CanBuiltinUnsafeValueBufferType type, - AbstractionPattern origType) { + AbstractionPattern origType, + IsTypeExpansionSensitive_t isSensitive) { return asImpl().handleAddressOnly(type, {IsNotTrivial, IsFixedABI, - IsAddressOnly, IsNotResilient}); + IsAddressOnly, IsNotResilient, + isSensitive}); } RetTy visitAnyFunctionType(CanAnyFunctionType type, - AbstractionPattern origType) { + AbstractionPattern origType, + IsTypeExpansionSensitive_t isSensitive) { switch (type->getRepresentation()) { case AnyFunctionType::Representation::Swift: case AnyFunctionType::Representation::Block: - return asImpl().handleReference(type); + return asImpl().handleReference( + type, getReferenceRecursiveProperties(isSensitive)); case AnyFunctionType::Representation::CFunctionPointer: case AnyFunctionType::Representation::Thin: - return asImpl().handleTrivial(type); + return asImpl().handleTrivial( + type, getTrivialRecursiveProperties(isSensitive)); } llvm_unreachable("bad function representation"); } RetTy visitSILFunctionType(CanSILFunctionType type, - AbstractionPattern origType) { + AbstractionPattern origType, + IsTypeExpansionSensitive_t isSensitive) { // Handle `@differentiable` and `@differentiable(linear)` functions. switch (type->getDifferentiabilityKind()) { case DifferentiabilityKind::Normal: return asImpl().visitNormalDifferentiableSILFunctionType( - type, getNormalDifferentiableSILFunctionTypeRecursiveProperties( - type, origType)); + type, mergeIsTypeExpansionSensitive( + isSensitive, + getNormalDifferentiableSILFunctionTypeRecursiveProperties( + type, origType))); case DifferentiabilityKind::Linear: return asImpl().visitLinearDifferentiableSILFunctionType( - type, getLinearDifferentiableSILFunctionTypeRecursiveProperties( - type, origType)); + type, mergeIsTypeExpansionSensitive( + isSensitive, + getLinearDifferentiableSILFunctionTypeRecursiveProperties( + type, origType))); case DifferentiabilityKind::NonDifferentiable: break; } @@ -260,9 +304,11 @@ namespace { type->getExtInfo().getRepresentation() == SILFunctionType::Representation::Thick; if (type->getExtInfo().hasContext() && !isSwiftEscaping) - return asImpl().handleReference(type); + return asImpl().handleReference( + type, getReferenceRecursiveProperties(isSensitive)); // No escaping closures are trivial types. - return asImpl().handleTrivial(type); + return asImpl().handleTrivial(type, + getTrivialRecursiveProperties(isSensitive)); } RecursiveProperties @@ -315,48 +361,55 @@ namespace { } RetTy visitLValueType(CanLValueType type, - AbstractionPattern origType) { + AbstractionPattern origType, + IsTypeExpansionSensitive_t) { llvm_unreachable("shouldn't get an l-value type here"); } RetTy visitInOutType(CanInOutType type, - AbstractionPattern origType) { + AbstractionPattern origType, + IsTypeExpansionSensitive_t) { llvm_unreachable("shouldn't get an inout type here"); } RetTy visitErrorType(CanErrorType type, - AbstractionPattern origType) { - return asImpl().handleTrivial(type); + AbstractionPattern origType, + IsTypeExpansionSensitive_t isSensitive) { + return asImpl().handleTrivial(type, + getTrivialRecursiveProperties(isSensitive)); } // Dependent types can be lowered according to their corresponding // abstraction pattern. - RetTy visitAbstractTypeParamType(CanType type, - AbstractionPattern origType) { + RetTy visitAbstractTypeParamType(CanType type, AbstractionPattern origType, + IsTypeExpansionSensitive_t isSensitive) { if (origType.isTypeParameterOrOpaqueArchetype() || origType.isOpaqueFunctionOrOpaqueDerivativeFunction()) { if (origType.requiresClass()) { - return asImpl().handleReference(type); + return asImpl().handleReference( + type, getReferenceRecursiveProperties(isSensitive)); } else { - return asImpl().handleAddressOnly(type, - RecursiveProperties::forOpaque()); + return asImpl().handleAddressOnly( + type, getOpaqueRecursiveProperties(isSensitive)); } } else { // If the abstraction pattern provides a concrete type, lower as that // type. This can occur if the abstraction pattern provides a more // constrained generic signature with more same-type constraints than // the original declaration whose type we're lowering. - return asImpl().visit(origType.getType(), origType); + return asImpl().visit(origType.getType(), origType, isSensitive); } } RetTy visitGenericTypeParamType(CanGenericTypeParamType type, - AbstractionPattern origType) { - return visitAbstractTypeParamType(type, origType); + AbstractionPattern origType, + IsTypeExpansionSensitive_t isSensitive) { + return visitAbstractTypeParamType(type, origType, isSensitive); } RetTy visitDependentMemberType(CanDependentMemberType type, - AbstractionPattern origType) { - return visitAbstractTypeParamType(type, origType); + AbstractionPattern origType, + IsTypeExpansionSensitive_t isSensitive) { + return visitAbstractTypeParamType(type, origType, isSensitive); } Type getConcreteReferenceStorageReferent(Type type) { @@ -369,78 +422,102 @@ namespace { #define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ RetTy visit##Name##StorageType(Can##Name##StorageType type, \ - AbstractionPattern origType) { \ + AbstractionPattern origType, \ + IsTypeExpansionSensitive_t isSensitive) { \ return asImpl().handleAddressOnly(type, {IsNotTrivial, \ IsFixedABI, \ IsAddressOnly, \ - IsNotResilient}); \ + IsNotResilient, \ + isSensitive}); \ } #define ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ RetTy visit##Name##StorageType(Can##Name##StorageType type, \ - AbstractionPattern origType) { \ - return asImpl().handleReference(type); \ + AbstractionPattern origType, \ + IsTypeExpansionSensitive_t isSensitive) { \ + return asImpl().handleReference(type, \ + getReferenceRecursiveProperties(isSensitive)); \ } #define SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ RetTy visitLoadable##Name##StorageType(Can##Name##StorageType type, \ - AbstractionPattern origType) { \ - return asImpl().handleReference(type); \ + AbstractionPattern origType, \ + IsTypeExpansionSensitive_t isSensitive) { \ + return asImpl().handleReference(type, \ + getReferenceRecursiveProperties(isSensitive)); \ } \ RetTy visitAddressOnly##Name##StorageType(Can##Name##StorageType type, \ - AbstractionPattern origType) { \ + AbstractionPattern origType, \ + IsTypeExpansionSensitive_t isSensitive) { \ return asImpl().handleAddressOnly(type, {IsNotTrivial, \ IsFixedABI, \ IsAddressOnly, \ - IsNotResilient}); \ + IsNotResilient, \ + isSensitive}); \ } \ RetTy visit##Name##StorageType(Can##Name##StorageType type, \ - AbstractionPattern origType) { \ + AbstractionPattern origType, \ + IsTypeExpansionSensitive_t isSensitive) { \ auto referentType = \ type->getReferentType()->lookThroughSingleOptionalType(); \ auto concreteType = getConcreteReferenceStorageReferent(referentType); \ if (Name##StorageType::get(concreteType, TC.Context) \ ->isLoadable(Expansion.getResilienceExpansion())) { \ - return asImpl().visitLoadable##Name##StorageType(type, origType); \ + return asImpl().visitLoadable##Name##StorageType(type, origType, \ + isSensitive); \ } else { \ - return asImpl().visitAddressOnly##Name##StorageType(type, origType); \ + return asImpl().visitAddressOnly##Name##StorageType(type, origType, \ + isSensitive); \ } \ } #define UNCHECKED_REF_STORAGE(Name, ...) \ RetTy visit##Name##StorageType(Can##Name##StorageType type, \ - AbstractionPattern origType) { \ - return asImpl().handleTrivial(type); \ + AbstractionPattern origType, \ + IsTypeExpansionSensitive_t isSensitive) { \ + return asImpl().handleTrivial(type, \ + getTrivialRecursiveProperties(isSensitive)); \ } #include "swift/AST/ReferenceStorage.def" RetTy visitOpaqueTypeArchetypeType(CanOpaqueTypeArchetypeType ty, - AbstractionPattern origType) { + AbstractionPattern origType, \ + IsTypeExpansionSensitive_t) { auto replacedTy = substOpaqueTypesWithUnderlyingTypes(ty, Expansion); if (replacedTy == ty) - return visitArchetypeType(ty, origType); - return this->visit(replacedTy, origType); + return visitArchetypeType(ty, origType, IsTypeExpansionSensitive); + return this->visit(replacedTy, origType, IsTypeExpansionSensitive); } - RetTy visitArchetypeType(CanArchetypeType type, - AbstractionPattern origType) { + RetTy visitArchetypeType(CanArchetypeType ty, AbstractionPattern origType) { + return visitArchetypeType(ty, origType, IsNotTypeExpansionSensitive); + } + + RetTy + visitArchetypeType(CanArchetypeType type, AbstractionPattern origType, + IsTypeExpansionSensitive_t isSensitive) { auto LayoutInfo = type->getLayoutConstraint(); if (LayoutInfo) { if (LayoutInfo->isFixedSizeTrivial()) { - return asImpl().handleTrivial(type); + return asImpl().handleTrivial( + type, getTrivialRecursiveProperties(isSensitive)); } if (LayoutInfo->isAddressOnlyTrivial()) { - auto properties = RecursiveProperties::forTrivial(); + auto properties = getTrivialRecursiveProperties(isSensitive); properties.setAddressOnly(); return asImpl().handleAddressOnly(type, properties); } - if (LayoutInfo->isRefCounted()) - return asImpl().handleReference(type); + if (LayoutInfo->isRefCounted()) { + return asImpl().handleReference( + type, getReferenceRecursiveProperties(isSensitive)); + } } - return asImpl().handleAddressOnly(type, RecursiveProperties::forOpaque()); + return asImpl().handleAddressOnly( + type, getOpaqueRecursiveProperties(isSensitive)); } RetTy visitExistentialType(CanType type, - AbstractionPattern origType) { + AbstractionPattern origType, + IsTypeExpansionSensitive_t isSensitive) { switch (SILType::getPrimitiveObjectType(type) .getPreferredExistentialRepresentation()) { case ExistentialRepresentation::None: @@ -450,77 +527,93 @@ namespace { return asImpl().handleAddressOnly(type, {IsNotTrivial, IsFixedABI, IsAddressOnly, - IsNotResilient}); + IsNotResilient, + isSensitive}); // Class-constrained and boxed existentials are refcounted. case ExistentialRepresentation::Class: case ExistentialRepresentation::Boxed: - return asImpl().handleReference(type); + return asImpl().handleReference( + type, getReferenceRecursiveProperties(isSensitive)); // Existential metatypes are trivial. case ExistentialRepresentation::Metatype: - return asImpl().handleTrivial(type); + return asImpl().handleTrivial( + type, getTrivialRecursiveProperties(isSensitive)); } llvm_unreachable("Unhandled ExistentialRepresentation in switch."); } - RetTy visitProtocolType(CanProtocolType type, - AbstractionPattern origType) { - return visitExistentialType(type, origType); + RetTy visitProtocolType(CanProtocolType type, AbstractionPattern origType, + IsTypeExpansionSensitive_t isSensitive) { + return visitExistentialType(type, origType, isSensitive); } RetTy visitProtocolCompositionType(CanProtocolCompositionType type, - AbstractionPattern origType) { - return visitExistentialType(type, origType); + AbstractionPattern origType, + IsTypeExpansionSensitive_t isSensitive) { + return visitExistentialType(type, origType, isSensitive); } // Enums depend on their enumerators. - RetTy visitEnumType(CanEnumType type, - AbstractionPattern origType) { - return asImpl().visitAnyEnumType(type, origType, type->getDecl()); + RetTy visitEnumType(CanEnumType type, AbstractionPattern origType, + IsTypeExpansionSensitive_t isSensitive) { + return asImpl().visitAnyEnumType(type, origType, type->getDecl(), + isSensitive); } RetTy visitBoundGenericEnumType(CanBoundGenericEnumType type, - AbstractionPattern origType) { - return asImpl().visitAnyEnumType(type, origType, type->getDecl()); + AbstractionPattern origType, + IsTypeExpansionSensitive_t isSensitive) { + return asImpl().visitAnyEnumType(type, origType, type->getDecl(), + isSensitive); } - + // Structs depend on their physical fields. - RetTy visitStructType(CanStructType type, - AbstractionPattern origType) { - return asImpl().visitAnyStructType(type, origType, type->getDecl()); + RetTy visitStructType(CanStructType type, AbstractionPattern origType, + IsTypeExpansionSensitive_t isSensitive) { + return asImpl().visitAnyStructType(type, origType, type->getDecl(), + isSensitive); } RetTy visitBoundGenericStructType(CanBoundGenericStructType type, - AbstractionPattern origType) { - return asImpl().visitAnyStructType(type, origType, type->getDecl()); + AbstractionPattern origType, + IsTypeExpansionSensitive_t isSensitive) { + return asImpl().visitAnyStructType(type, origType, type->getDecl(), + isSensitive); } // Tuples depend on their elements. - RetTy visitTupleType(CanTupleType type, - AbstractionPattern origType) { + RetTy visitTupleType(CanTupleType type, AbstractionPattern origType, + IsTypeExpansionSensitive_t isSensitive) { RecursiveProperties props; for (unsigned i = 0, e = type->getNumElements(); i < e; ++i) { props.addSubobject(classifyType(origType.getTupleElementType(i), type.getElementType(i), TC, Expansion)); } + props = mergeIsTypeExpansionSensitive(isSensitive, props); return asImpl().handleAggregateByProperties(type, props); } RetTy visitDynamicSelfType(CanDynamicSelfType type, - AbstractionPattern origType) { - return this->visit(type.getSelfType(), origType); + AbstractionPattern origType, + IsTypeExpansionSensitive_t isSensitive) { + return this->visit(type.getSelfType(), origType, isSensitive); } RetTy visitSILBlockStorageType(CanSILBlockStorageType type, - AbstractionPattern origType) { + AbstractionPattern origType, + IsTypeExpansionSensitive_t isSensitive) { // Should not be loaded. return asImpl().handleAddressOnly(type, {IsNotTrivial, IsFixedABI, IsAddressOnly, - IsNotResilient}); + IsNotResilient, + isSensitive}); } RetTy visitSILBoxType(CanSILBoxType type, - AbstractionPattern origType) { + AbstractionPattern origType, + IsTypeExpansionSensitive_t isSensitive) { // Should not be loaded. - return asImpl().handleReference(type); + return asImpl().handleReference( + type, getReferenceRecursiveProperties(isSensitive)); } RetTy handleAggregateByProperties(CanType type, RecursiveProperties props) { @@ -548,32 +641,37 @@ namespace { RecursiveProperties visitAnyEnumType(CanType type, AbstractionPattern origType, - EnumDecl *D) { + EnumDecl *D, + IsTypeExpansionSensitive_t isSensitive) { // 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. if (D->isOptionalDecl()) { return visit(type.getOptionalObjectType(), - origType.getOptionalObjectType()); + origType.getOptionalObjectType(), + isSensitive); } // Consult the type lowering. auto &lowering = TC.getTypeLowering(origType, type, Expansion); - return handleClassificationFromLowering(type, lowering); + return handleClassificationFromLowering(type, lowering, isSensitive); } RecursiveProperties visitAnyStructType(CanType type, AbstractionPattern origType, - StructDecl *D) { + StructDecl *D, + IsTypeExpansionSensitive_t isSensitive) { // Consult the type lowering. auto &lowering = TC.getTypeLowering(origType, type, Expansion); - return handleClassificationFromLowering(type, lowering); + return handleClassificationFromLowering(type, lowering, isSensitive); } private: - RecursiveProperties handleClassificationFromLowering(CanType type, - const TypeLowering &lowering) { - return handle(type, lowering.getRecursiveProperties()); + RecursiveProperties + handleClassificationFromLowering(CanType type, const TypeLowering &lowering, + IsTypeExpansionSensitive_t isSensitive) { + return handle(type, mergeIsTypeExpansionSensitive( + isSensitive, lowering.getRecursiveProperties())); } }; } // end anonymous namespace @@ -582,7 +680,8 @@ static RecursiveProperties classifyType(AbstractionPattern origType, CanType type, TypeConverter &tc, TypeExpansionContext expansion) { - return TypeClassifier(tc, expansion).visit(type, origType); + return TypeClassifier(tc, expansion) + .visit(type, origType, IsNotTypeExpansionSensitive); } /// True if the type, or the referenced type of an address @@ -1292,9 +1391,10 @@ namespace { /// loadable. class ReferenceTypeLowering : public LeafLoadableTypeLowering { public: - ReferenceTypeLowering(SILType type, TypeExpansionContext forExpansion) - : LeafLoadableTypeLowering(type, RecursiveProperties::forReference(), - IsReferenceCounted, forExpansion) {} + ReferenceTypeLowering(SILType type, RecursiveProperties properties, + TypeExpansionContext forExpansion) + : LeafLoadableTypeLowering(type, properties, IsReferenceCounted, + forExpansion) {} SILValue emitCopyValue(SILBuilder &B, SILLocation loc, SILValue value) const override { @@ -1324,8 +1424,9 @@ namespace { class Loadable##Name##TypeLowering final : public LeafLoadableTypeLowering { \ public: \ Loadable##Name##TypeLowering(SILType type, \ - TypeExpansionContext forExpansion) \ - : LeafLoadableTypeLowering(type, RecursiveProperties::forReference(), \ + TypeExpansionContext forExpansion, \ + RecursiveProperties props) \ + : LeafLoadableTypeLowering(type, props, \ IsReferenceCounted, \ forExpansion) {} \ SILValue emitCopyValue(SILBuilder &B, SILLocation loc, \ @@ -1436,10 +1537,11 @@ namespace { class UnsafeValueBufferTypeLowering : public AddressOnlyTypeLowering { public: UnsafeValueBufferTypeLowering(SILType type, - TypeExpansionContext forExpansion) + TypeExpansionContext forExpansion, + IsTypeExpansionSensitive_t isSensitive) : AddressOnlyTypeLowering(type, {IsNotTrivial, IsFixedABI, - IsAddressOnly, IsNotResilient}, + IsAddressOnly, IsNotResilient, isSensitive}, forExpansion) {} void emitCopyInto(SILBuilder &B, SILLocation loc, @@ -1520,9 +1622,16 @@ namespace { return new (TC) TrivialTypeLowering(silType, properties, Expansion); } + TypeLowering *handleReference(CanType type, + RecursiveProperties properties) { + auto silType = SILType::getPrimitiveObjectType(type); + return new (TC) ReferenceTypeLowering(silType, properties, Expansion); + } + TypeLowering *handleReference(CanType type) { auto silType = SILType::getPrimitiveObjectType(type); - return new (TC) ReferenceTypeLowering(silType, Expansion); + return new (TC) ReferenceTypeLowering( + silType, RecursiveProperties::forReference(), Expansion); } TypeLowering *handleAddressOnly(CanType type, @@ -1539,30 +1648,37 @@ namespace { #define ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ TypeLowering * \ visit##Name##StorageType(Can##Name##StorageType type, \ - AbstractionPattern origType) { \ + AbstractionPattern origType, \ + IsTypeExpansionSensitive_t isSensitive) { \ return new (TC) Loadable##Name##TypeLowering( \ SILType::getPrimitiveObjectType(type), \ - Expansion); \ + Expansion, \ + getReferenceRecursiveProperties(isSensitive)); \ } #define SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ TypeLowering * \ visitLoadable##Name##StorageType(Can##Name##StorageType type, \ - AbstractionPattern origType) { \ + AbstractionPattern origType, \ + IsTypeExpansionSensitive_t isSensitive) { \ return new (TC) Loadable##Name##TypeLowering( \ SILType::getPrimitiveObjectType(type), \ - Expansion); \ + Expansion, \ + getReferenceRecursiveProperties(isSensitive)); \ } #include "swift/AST/ReferenceStorage.def" TypeLowering * visitBuiltinUnsafeValueBufferType(CanBuiltinUnsafeValueBufferType type, - AbstractionPattern origType) { + AbstractionPattern origType, + IsTypeExpansionSensitive_t isSensitive) { auto silType = SILType::getPrimitiveAddressType(type); - return new (TC) UnsafeValueBufferTypeLowering(silType, Expansion); + return new (TC) + UnsafeValueBufferTypeLowering(silType, Expansion, isSensitive); } TypeLowering *visitTupleType(CanTupleType tupleType, - AbstractionPattern origType) { + AbstractionPattern origType, + IsTypeExpansionSensitive_t isSensitive) { RecursiveProperties properties; for (unsigned i = 0, e = tupleType->getNumElements(); i < e; ++i) { auto eltType = tupleType.getElementType(i); @@ -1570,6 +1686,7 @@ namespace { auto &lowering = TC.getTypeLowering(origEltType, eltType, Expansion); properties.addSubobject(lowering.getRecursiveProperties()); } + properties = mergeIsTypeExpansionSensitive(isSensitive, properties); return handleAggregateByProperties(tupleType, properties); @@ -1602,9 +1719,12 @@ namespace { TypeLowering *visitAnyStructType(CanType structType, AbstractionPattern origType, - StructDecl *D) { + StructDecl *D, + IsTypeExpansionSensitive_t isSensitive) { RecursiveProperties properties; + properties = mergeIsTypeExpansionSensitive(isSensitive, properties); + if (handleResilience(structType, D, properties)) return handleAddressOnly(structType, properties); @@ -1638,9 +1758,12 @@ namespace { TypeLowering *visitAnyEnumType(CanType enumType, AbstractionPattern origType, - EnumDecl *D) { + EnumDecl *D, + IsTypeExpansionSensitive_t isSensitive) { RecursiveProperties properties; + properties = mergeIsTypeExpansionSensitive(isSensitive, properties); + if (handleResilience(enumType, D, properties)) return handleAddressOnly(enumType, properties); @@ -1859,32 +1982,33 @@ TypeConverter::getSILFunctionType(TypeExpansionContext context, getLoweredRValueType(context, origType, substType)); } -bool TypeConverter::hasOpaqueArchetypeOrPropertiesOrCases(CanType ty) { - if (ty->hasOpaqueArchetype()) - return true; - - auto it = opaqueArchetypeFields.find(ty); - if (it == opaqueArchetypeFields.end()) { - bool res = ty->hasOpaqueArchetypePropertiesOrCases(); - opaqueArchetypeFields[ty] = res; - return res; - } - return it->second; -} - const TypeLowering & TypeConverter::getTypeLowering(AbstractionPattern origType, Type origSubstType, TypeExpansionContext forExpansion) { CanType substType = origSubstType->getCanonicalType(); - auto origHadOpaqueTypeArchetype = - hasOpaqueArchetypeOrPropertiesOrCases(origSubstType->getCanonicalType()); + bool origHasOpaqueArchetype = substType->hasOpaqueArchetype(); + // A type is type expansion sensitive if its lowering could depend on the type + // expansion context: + // - If the type has an opaque archetype + // Because depending on the type expansion context we might get a different + // SIL type (Foo vs Foo). + // - or if during type lowering we discover an opaque archetype that + // influences type lowering by type expansion context + // E.g a struct containing a field that is a opaque archetype will be + // loadable or not depending on the type expansion context. In a more + // permissive type expansion context we will look through the opaque + // archetype and could discover a loadable type making the whole aggregate + // loadable. + auto isTypeExpansionSensitive = origHasOpaqueArchetype + ? IsTypeExpansionSensitive + : IsNotTypeExpansionSensitive; auto key = getTypeKey(origType, substType, forExpansion); assert(!substType->is()); auto *candidateLowering = find(key.getKeyForMinimalExpansion()); auto *lowering = getTypeLoweringForExpansion( - key, forExpansion, candidateLowering, origHadOpaqueTypeArchetype); + key, forExpansion, candidateLowering, IsNotTypeExpansionSensitive); if (lowering != nullptr) return *lowering; @@ -1902,20 +2026,19 @@ TypeConverter::getTypeLowering(AbstractionPattern origType, // point in re-checking the table, so just construct a type lowering // and cache it. if (loweredSubstType == substType && key.isCacheable()) { - lowering = LowerType(*this, forExpansion) - .visit(key.SubstType, key.OrigType); + lowering = + LowerType(*this, forExpansion) + .visit(key.SubstType, key.OrigType, isTypeExpansionSensitive); - // Otherwise, check the table at a key that would be used by the - // SILType-based lookup path for the type we just lowered to, then cache - // that same result at this key if possible. + // Otherwise, check the table at a key that would be used by the + // SILType-based lookup path for the type we just lowered to, then cache + // that same result at this key if possible. } else { - lowering = &getTypeLoweringForLoweredType(origType, - loweredSubstType, - forExpansion, - origHadOpaqueTypeArchetype); + lowering = &getTypeLoweringForLoweredType( + origType, loweredSubstType, forExpansion, isTypeExpansionSensitive); } - if (!lowering->isResilient() && !origHadOpaqueTypeArchetype) { + if (!lowering->isResilient() && !lowering->isTypeExpansionSensitive()) { insert(key.getKeyForMinimalExpansion(), lowering); } else { insert(key, lowering); @@ -2099,13 +2222,12 @@ TypeConverter::getTypeLowering(SILType type, // The type lowering for a type parameter relies on its context. assert(sig || !type.getASTType()->hasTypeParameter()); auto loweredType = type.getASTType(); - auto origHadOpaqueTypeArchetype = - hasOpaqueArchetypeOrPropertiesOrCases(loweredType); - - return getTypeLoweringForLoweredType( - AbstractionPattern(sig, loweredType), - loweredType, forExpansion, - origHadOpaqueTypeArchetype); + auto isTypeExpansionSensitive = loweredType->hasOpaqueArchetype() + ? IsTypeExpansionSensitive + : IsNotTypeExpansionSensitive; + return getTypeLoweringForLoweredType(AbstractionPattern(sig, loweredType), + loweredType, forExpansion, + isTypeExpansionSensitive); } const TypeLowering & @@ -2114,11 +2236,10 @@ TypeConverter::getTypeLowering(SILType t, SILFunction &F) { F.getLoweredFunctionType()->getSubstGenericSignature()); } -const TypeLowering & -TypeConverter::getTypeLoweringForLoweredType(AbstractionPattern origType, - CanType loweredType, - TypeExpansionContext forExpansion, - bool origHadOpaqueTypeArchetype) { +const TypeLowering &TypeConverter::getTypeLoweringForLoweredType( + AbstractionPattern origType, CanType loweredType, + TypeExpansionContext forExpansion, + IsTypeExpansionSensitive_t isTypeExpansionSensitive) { assert(loweredType->isLegalSILType() && "type is not lowered!"); (void)loweredType; @@ -2133,7 +2254,7 @@ TypeConverter::getTypeLoweringForLoweredType(AbstractionPattern origType, auto *candidateLowering = find(key.getKeyForMinimalExpansion()); auto *lowering = getTypeLoweringForExpansion( - key, forExpansion, candidateLowering, origHadOpaqueTypeArchetype); + key, forExpansion, candidateLowering, isTypeExpansionSensitive); if (lowering != nullptr) return *lowering; @@ -2151,9 +2272,9 @@ TypeConverter::getTypeLoweringForLoweredType(AbstractionPattern origType, lowering = LowerType(*this, forExpansion) - .visit(loweredType, origType); + .visit(loweredType, origType, isTypeExpansionSensitive); - if (!lowering->isResilient() && !origHadOpaqueTypeArchetype) + if (!lowering->isResilient() && !lowering->isTypeExpansionSensitive()) insert(key.getKeyForMinimalExpansion(), lowering); else { insert(key, lowering); @@ -2169,22 +2290,23 @@ TypeConverter::getTypeLoweringForLoweredType(AbstractionPattern origType, /// check if its the one we want; if not, walk the list until we /// find the right one, returning nullptr if the caller needs to /// go ahead and lower the type with the correct expansion. -const TypeLowering *TypeConverter:: -getTypeLoweringForExpansion(TypeKey key, - TypeExpansionContext forExpansion, - const TypeLowering *lowering, - bool origHadOpaqueTypeArchetype) { - if (lowering == nullptr) +const TypeLowering *TypeConverter::getTypeLoweringForExpansion( + TypeKey key, TypeExpansionContext forExpansion, + const TypeLowering *minimalExpansionLowering, + IsTypeExpansionSensitive_t isOrigTypeSensitive) { + if (minimalExpansionLowering == nullptr) return nullptr; - if (!lowering->isResilient() && !origHadOpaqueTypeArchetype) { + if (!minimalExpansionLowering->isResilient() && + !minimalExpansionLowering->isTypeExpansionSensitive() && + !isOrigTypeSensitive) { // Don't try to refine the lowering for other resilience expansions if // we don't expect to get a different lowering anyway. Similar if the // original type did not have opaque type archetypes. // // See LowerType::handleResilience() for the gory details; we only // set this flag if the type is resilient *and* inside our module. - return lowering; + return minimalExpansionLowering; } auto *exactLowering = find(key); @@ -2398,7 +2520,7 @@ getFunctionInterfaceTypeWithCaptures(TypeConverter &TC, } CanAnyFunctionType TypeConverter::makeConstantInterfaceType(SILDeclRef c) { - if (auto *derivativeId = c.derivativeFunctionIdentifier) { + if (auto *derivativeId = c.getDerivativeFunctionIdentifier()) { auto originalFnTy = makeConstantInterfaceType(c.asAutoDiffOriginalFunction()); auto *derivativeFnTy = originalFnTy->getAutoDiffDerivativeFunctionType( diff --git a/lib/SIL/IR/ValueOwnership.cpp b/lib/SIL/IR/ValueOwnership.cpp index 47560bd48fba0..cc800afa16914 100644 --- a/lib/SIL/IR/ValueOwnership.cpp +++ b/lib/SIL/IR/ValueOwnership.cpp @@ -148,6 +148,9 @@ CONSTANT_OWNERSHIP_INST(None, DifferentiabilityWitnessFunction) CONSTANT_OWNERSHIP_INST(Unowned, RawPointerToRef) CONSTANT_OWNERSHIP_INST(Unowned, ObjCProtocol) CONSTANT_OWNERSHIP_INST(Unowned, ValueToBridgeObject) +CONSTANT_OWNERSHIP_INST(None, GetAsyncContinuation) +CONSTANT_OWNERSHIP_INST(Unowned, GetAsyncContinuationAddr) +CONSTANT_OWNERSHIP_INST(None, ThinToThickFunction) #undef CONSTANT_OWNERSHIP_INST #define CONSTANT_OR_NONE_OWNERSHIP_INST(OWNERSHIP, INST) \ @@ -204,9 +207,6 @@ CONSTANT_OR_NONE_OWNERSHIP_INST(Owned, MarkUninitialized) // a performance perspective. CONSTANT_OR_NONE_OWNERSHIP_INST(Unowned, UncheckedBitwiseCast) -// A thin_to_thick instruction can return a trivial (@noescape) type. -CONSTANT_OR_NONE_OWNERSHIP_INST(Owned, ThinToThickFunction) - #undef CONSTANT_OR_NONE_OWNERSHIP_INST // For a forwarding instruction, we loop over all operands and make sure that diff --git a/lib/SIL/Parser/ParseSIL.cpp b/lib/SIL/Parser/ParseSIL.cpp index efe874cd5456a..38213c74bc8f1 100644 --- a/lib/SIL/Parser/ParseSIL.cpp +++ b/lib/SIL/Parser/ParseSIL.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "SILParserFunctionBuilder.h" +#include "SILParserState.h" #include "swift/AST/ASTWalker.h" #include "swift/AST/ExistentialLayout.h" #include "swift/AST/GenericEnvironment.h" @@ -45,40 +46,6 @@ using namespace swift::syntax; // SILParserState implementation //===----------------------------------------------------------------------===// -namespace { -class SILParserState : public SILParserStateBase { -public: - explicit SILParserState(SILModule &M) : M(M) {} - ~SILParserState(); - - SILModule &M; - - /// This is all of the forward referenced functions with - /// the location for where the reference is. - llvm::DenseMap> ForwardRefFns; - /// A list of all functions forward-declared by a sil_scope. - llvm::DenseSet PotentialZombieFns; - - /// A map from textual .sil scope number to SILDebugScopes. - llvm::DenseMap ScopeSlots; - - /// Did we parse a sil_stage for this module? - bool DidParseSILStage = false; - - bool parseDeclSIL(Parser &P) override; - bool parseDeclSILStage(Parser &P) override; - bool parseSILVTable(Parser &P) override; - bool parseSILGlobal(Parser &P) override; - bool parseSILWitnessTable(Parser &P) override; - bool parseSILDefaultWitnessTable(Parser &P) override; - bool parseSILDifferentiabilityWitness(Parser &P) override; - bool parseSILCoverageMap(Parser &P) override; - bool parseSILProperty(Parser &P) override; - bool parseSILScope(Parser &P) override; -}; -} // end anonymous namespace - SILParserState::~SILParserState() { if (!ForwardRefFns.empty()) { for (auto Entry : ForwardRefFns) { @@ -136,6 +103,9 @@ namespace { ArrayRef requirements; bool exported; SILSpecializeAttr::SpecializationKind kind; + SILFunction *target = nullptr; + Identifier spiGroupID; + ModuleDecl *spiModule; }; class SILParser { @@ -1049,10 +1019,44 @@ static bool parseDeclSILOptional(bool *isTransparent, SpecAttr.exported = false; SpecAttr.kind = SILSpecializeAttr::SpecializationKind::Full; SpecializeAttr *Attr; + StringRef targetFunctionName; + ModuleDecl *module = nullptr; + + if (!SP.P.parseSpecializeAttribute( + tok::r_square, AtLoc, Loc, Attr, + [&targetFunctionName](Parser &P) -> bool { + if (P.Tok.getKind() != tok::string_literal) { + P.diagnose(P.Tok, diag::expected_in_attribute_list); + return true; + } + // Drop the double quotes. + targetFunctionName = P.Tok.getText().drop_front().drop_back(); - if (!SP.P.parseSpecializeAttribute(tok::r_square, AtLoc, Loc, Attr)) - return true; - + P.consumeToken(tok::string_literal); + return true; + }, + [&module](Parser &P) -> bool { + if (P.Tok.getKind() != tok::identifier) { + P.diagnose(P.Tok, diag::expected_in_attribute_list); + return true; + } + auto ident = P.Context.getIdentifier(P.Tok.getText()); + module = P.Context.getModuleByIdentifier(ident); + assert(module); + P.consumeToken(); + return true; + })) + return true; + SILFunction *targetFunction = nullptr; + if (!targetFunctionName.empty()) { + targetFunction = M.lookUpFunction(targetFunctionName.str()); + if (!targetFunction) { + Identifier Id = SP.P.Context.getIdentifier(targetFunctionName); + SP.P.diagnose(SP.P.Tok, diag::sil_specialize_target_func_not_found, + Id); + return true; + } + } // Convert SpecializeAttr into ParsedSpecAttr. SpecAttr.requirements = Attr->getTrailingWhereClause()->getRequirements(); SpecAttr.kind = Attr->getSpecializationKind() == @@ -1060,7 +1064,11 @@ static bool parseDeclSILOptional(bool *isTransparent, ? SILSpecializeAttr::SpecializationKind::Full : SILSpecializeAttr::SpecializationKind::Partial; SpecAttr.exported = Attr->isExported(); + SpecAttr.target = targetFunction; SpecAttrs->emplace_back(SpecAttr); + if (!Attr->getSPIGroups().empty()) { + SpecAttr.spiGroupID = Attr->getSPIGroups()[0]; + } continue; } else if (ClangDecl && SP.P.Tok.getText() == "clang") { @@ -5240,6 +5248,81 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B, InstLoc, witnessKind, witness, functionType); break; } + case SILInstructionKind::AwaitAsyncContinuationInst: { + // 'await_async_continuation' operand, 'resume' bb, 'error' bb + Identifier ResumeBBName, ErrorBBName{}; + SourceLoc ResumeNameLoc, ErrorNameLoc{}; + if (parseTypedValueRef(Val, B) + || P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") + || P.parseSpecificIdentifier("resume", diag::expected_tok_in_sil_instr, "resume") + || parseSILIdentifier(ResumeBBName, ResumeNameLoc, diag::expected_sil_block_name)) { + return true; + } + + if (P.consumeIf(tok::comma)) { + if (P.parseSpecificIdentifier("error", diag::expected_tok_in_sil_instr, "error") + || parseSILIdentifier(ErrorBBName, ErrorNameLoc, diag::expected_sil_block_name) + || parseSILDebugLocation(InstLoc, B)) { + return true; + } + } + + SILBasicBlock *resumeBB, *errorBB = nullptr; + resumeBB = getBBForReference(ResumeBBName, ResumeNameLoc); + if (!ErrorBBName.empty()) { + errorBB = getBBForReference(ErrorBBName, ErrorNameLoc); + } + ResultVal = B.createAwaitAsyncContinuation(InstLoc, Val, resumeBB, errorBB); + break; + } + case SILInstructionKind::GetAsyncContinuationInst: + case SILInstructionKind::GetAsyncContinuationAddrInst: { + // 'get_async_continuation' '[throws]'? type + // 'get_async_continuation_addr' '[throws]'? type ',' operand + bool throws = false; + if (P.consumeIf(tok::l_square)) { + if (P.parseToken(tok::kw_throws, diag::expected_tok_in_sil_instr, "throws") + || P.parseToken(tok::r_square, diag::expected_tok_in_sil_instr, "]")) + return true; + + throws = true; + } + + SILType resumeTy; + if (parseSILType(resumeTy)) { + return true; + } + + SILValue resumeBuffer; + if (Opcode == SILInstructionKind::GetAsyncContinuationAddrInst) { + if (P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") + || parseTypedValueRef(resumeBuffer, B)) { + return true; + } + } + + if (parseSILDebugLocation(InstLoc, B)) + return true; + + auto &M = B.getModule(); + NominalTypeDecl *continuationDecl = throws + ? M.getASTContext().getUnsafeThrowingContinuationDecl() + : M.getASTContext().getUnsafeContinuationDecl(); + + auto continuationTy = BoundGenericType::get(continuationDecl, Type(), + resumeTy.getASTType()); + auto continuationSILTy + = SILType::getPrimitiveObjectType(continuationTy->getCanonicalType()); + + if (Opcode == SILInstructionKind::GetAsyncContinuationAddrInst) { + ResultVal = B.createGetAsyncContinuationAddr(InstLoc, resumeBuffer, + continuationSILTy); + } else { + ResultVal = B.createGetAsyncContinuation(InstLoc, continuationSILTy); + } + break; + } + } return false; @@ -5774,7 +5857,7 @@ bool SILParserState::parseDeclSIL(Parser &P) { GenericSignature()); FunctionState.F->addSpecializeAttr(SILSpecializeAttr::create( FunctionState.F->getModule(), genericSig, Attr.exported, - Attr.kind)); + Attr.kind, Attr.target, Attr.spiGroupID, Attr.spiModule)); } } diff --git a/lib/SIL/Parser/SILParserState.h b/lib/SIL/Parser/SILParserState.h new file mode 100644 index 0000000000000..7d915657db170 --- /dev/null +++ b/lib/SIL/Parser/SILParserState.h @@ -0,0 +1,61 @@ +//===--- SILParserState.h - SILParserState declaration -------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SIL_SILPARSERSTATE_H +#define SWIFT_SIL_SILPARSERSTATE_H + +#include "swift/Basic/LLVM.h" +#include "swift/Parse/ParseSILSupport.h" + +//===----------------------------------------------------------------------===// +// SILParserState +//===----------------------------------------------------------------------===// + +namespace swift { + +class Parser; +class SILModule; + +class SILParserState : public SILParserStateBase { +public: + explicit SILParserState(SILModule &M) : M(M) {} + ~SILParserState(); + + SILModule &M; + + /// This is all of the forward referenced functions with + /// the location for where the reference is. + llvm::DenseMap> ForwardRefFns; + /// A list of all functions forward-declared by a sil_scope. + llvm::DenseSet PotentialZombieFns; + + /// A map from textual .sil scope number to SILDebugScopes. + llvm::DenseMap ScopeSlots; + + /// Did we parse a sil_stage for this module? + bool DidParseSILStage = false; + + bool parseDeclSIL(Parser &P) override; + bool parseDeclSILStage(Parser &P) override; + bool parseSILVTable(Parser &P) override; + bool parseSILGlobal(Parser &P) override; + bool parseSILWitnessTable(Parser &P) override; + bool parseSILDefaultWitnessTable(Parser &P) override; + bool parseSILDifferentiabilityWitness(Parser &P) override; + bool parseSILCoverageMap(Parser &P) override; + bool parseSILProperty(Parser &P) override; + bool parseSILScope(Parser &P) override; +}; + +} // end namespace swift + +#endif // SWIFT_SIL_SILPARSERSTATE_H diff --git a/lib/SIL/Utils/BasicBlockUtils.cpp b/lib/SIL/Utils/BasicBlockUtils.cpp index 4548f8c99949f..bbae9f40edfbe 100644 --- a/lib/SIL/Utils/BasicBlockUtils.cpp +++ b/lib/SIL/Utils/BasicBlockUtils.cpp @@ -115,6 +115,37 @@ void swift::getEdgeArgs(TermInst *T, unsigned edgeIdx, SILBasicBlock *newEdgeBB, args.push_back(V); return; } + + case SILInstructionKind::AwaitAsyncContinuationInst: { + auto AACI = cast(T); + + switch (edgeIdx) { + case 0: + // resume BB. this takes the resume value argument if the operand is + // GetAsyncContinuation, or no argument if the operand is + // GetAsyncContinuationAddr + if (auto contOperand = dyn_cast(AACI->getOperand())) { + args.push_back( + newEdgeBB->createPhiArgument(contOperand->getLoweredResumeType(), + ValueOwnershipKind::Owned)); + } + return; + + case 1: { + assert(AACI->getErrorBB()); + auto &C = AACI->getFunction()->getASTContext(); + auto errorTy = C.getErrorDecl()->getDeclaredType(); + auto errorSILTy = SILType::getPrimitiveObjectType(errorTy->getCanonicalType()); + // error BB. this takes the error value argument + args.push_back(newEdgeBB->createPhiArgument(errorSILTy, + ValueOwnershipKind::Owned)); + return; + } + + default: + llvm_unreachable("only has at most two edges"); + } + } case SILInstructionKind::SwitchValueInst: { auto SEI = cast(T); diff --git a/lib/SIL/Utils/CMakeLists.txt b/lib/SIL/Utils/CMakeLists.txt index 503af715ecab2..0fc6b38212f57 100644 --- a/lib/SIL/Utils/CMakeLists.txt +++ b/lib/SIL/Utils/CMakeLists.txt @@ -3,6 +3,7 @@ target_sources(swiftSIL PRIVATE DebugUtils.cpp Dominance.cpp DynamicCasts.cpp + GenericSpecializationMangler.cpp InstructionUtils.cpp LoopInfo.cpp MemAccessUtils.cpp diff --git a/lib/SIL/Utils/DynamicCasts.cpp b/lib/SIL/Utils/DynamicCasts.cpp index ffbd852e73ab5..5c7998bec7856 100644 --- a/lib/SIL/Utils/DynamicCasts.cpp +++ b/lib/SIL/Utils/DynamicCasts.cpp @@ -208,6 +208,50 @@ static CanType getHashableExistentialType(ModuleDecl *M) { return hashable->getDeclaredInterfaceType()->getCanonicalType(); } +static bool canBeExistential(CanType ty) { + // If ty is an archetype, conservatively assume it's an existential. + return ty.isAnyExistentialType() || ty->is(); +} + +static bool canBeClass(CanType ty) { + // If ty is an archetype, conservatively assume it's an existential. + return ty.getClassOrBoundGenericClass() || ty->is(); +} + +bool SILDynamicCastInst::isRCIdentityPreserving() const { + // Casts which cast from a trivial type, like a metatype, to something which + // is retainable (or vice versa), like an AnyObject, are not RC identity + // preserving. + // On some platforms such casts dynamically allocate a ref-counted box for the + // metatype. Naturally that is the place where a new rc-identity begins. + // Therefore such a cast is introducing a new rc identical object. + // + // If RCIdentityAnalysis would look through such a cast, ARC optimizations + // would get confused and might eliminate a retain of such an object + // completely. + SILFunction &f = *getFunction(); + if (getSourceLoweredType().isTrivial(f) != getTargetLoweredType().isTrivial(f)) + return false; + + CanType source = getSourceFormalType(); + CanType target = getTargetFormalType(); + + // An existential may be holding a reference to a bridgeable struct. + // In this case, ARC on the existential affects the refcount of the container + // holding the struct, not the class to which the struct is bridged. + // Therefore, don't assume RC identity when casting between existentials and + // classes (and also between two existentials). + if (canBeExistential(source) && + (canBeClass(target) || canBeExistential(target))) + return false; + + // And vice versa. + if (canBeClass(source) && canBeExistential(target)) + return false; + + return true; +} + /// Check if a given type conforms to _BridgedToObjectiveC protocol. bool swift::isObjectiveCBridgeable(ModuleDecl *M, CanType Ty) { // Retrieve the _BridgedToObjectiveC protocol. diff --git a/lib/SIL/Utils/GenericSpecializationMangler.cpp b/lib/SIL/Utils/GenericSpecializationMangler.cpp new file mode 100644 index 0000000000000..529e36b09ff6a --- /dev/null +++ b/lib/SIL/Utils/GenericSpecializationMangler.cpp @@ -0,0 +1,137 @@ +//===--- GenericSpecializationMangler.cpp - mangling of specializations ---===// +// +// 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/GenericSpecializationMangler.h" +#include "swift/AST/GenericEnvironment.h" +#include "swift/AST/GenericSignature.h" +#include "swift/AST/SubstitutionMap.h" +#include "swift/Demangling/ManglingMacros.h" + +using namespace swift; +using namespace Mangle; + +void SpecializationMangler::beginMangling() { + ASTMangler::beginManglingWithoutPrefix(); + if (Serialized) + ArgOpBuffer << 'q'; + ArgOpBuffer << char(uint8_t(Pass) + '0'); +} + +namespace { + +/// Utility class for demangling specialization attributes. +class AttributeDemangler : public Demangle::Demangler { +public: + void demangleAndAddAsChildren(StringRef MangledSpecialization, + NodePointer Parent) { + DemangleInitRAII state(*this, MangledSpecialization, nullptr); + if (!parseAndPushNodes()) { + llvm::errs() << "Can't demangle: " << MangledSpecialization << '\n'; + abort(); + } + for (Node *Nd : NodeStack) { + addChild(Parent, Nd); + } + } +}; + +} // namespace + +std::string SpecializationMangler::finalize() { + StringRef MangledSpecialization(Storage.data(), Storage.size()); + AttributeDemangler D; + NodePointer TopLevel = D.createNode(Node::Kind::Global); + D.demangleAndAddAsChildren(MangledSpecialization, TopLevel); + + StringRef FuncName = Function ? Function->getName() : StringRef(FunctionName); + NodePointer FuncTopLevel = nullptr; + if (FuncName.startswith(MANGLING_PREFIX_STR)) { + FuncTopLevel = D.demangleSymbol(FuncName); + assert(FuncTopLevel); + } + if (!FuncTopLevel) { + FuncTopLevel = D.createNode(Node::Kind::Global); + FuncTopLevel->addChild(D.createNode(Node::Kind::Identifier, FuncName), D); + } + for (NodePointer FuncChild : *FuncTopLevel) { + TopLevel->addChild(FuncChild, D); + } + std::string mangledName = Demangle::mangleNode(TopLevel); + verify(mangledName); + return mangledName; +} + +//===----------------------------------------------------------------------===// +// Generic Specialization +//===----------------------------------------------------------------------===// + +std::string GenericSpecializationMangler::mangle(GenericSignature Sig) { + beginMangling(); + + if (!Sig) { + assert(Function && + "Need a SIL function if no generic signature is provided"); + SILFunctionType *FTy = Function->getLoweredFunctionType(); + Sig = FTy->getInvocationGenericSignature(); + } + + bool First = true; + Sig->forEachParam([&](GenericTypeParamType *ParamType, bool Canonical) { + if (Canonical) { + appendType(Type(ParamType).subst(SubMap)->getCanonicalType()); + appendListSeparator(First); + } + }); + assert(!First && "no generic substitutions"); + + if (isInlined) + appendSpecializationOperator("Ti"); + else + appendSpecializationOperator( + isPrespecializaton ? "Ts" : (isReAbstracted ? "Tg" : "TG")); + return finalize(); +} + +static SubstitutionMap +getSubstitutionMapForPrespecialization(GenericSignature genericSig, + GenericSignature specSig) { + auto CalleeGenericSig = genericSig; + auto SpecializedGenericSig = specSig; + auto SpecializedGenericEnv = specSig->getGenericEnvironment(); + + auto CalleeInterfaceToSpecializedInterfaceMap = SubstitutionMap::get( + CalleeGenericSig, + [&](SubstitutableType *type) -> Type { + return type; + }, + LookUpConformanceInSignature(CalleeGenericSig.getPointer())); + + auto subs = SubstitutionMap::get( + CalleeGenericSig, + [&](SubstitutableType *type) -> Type { + auto SpecializedInterfaceTy = + Type(type).subst(CalleeInterfaceToSpecializedInterfaceMap); + return SpecializedGenericEnv->mapTypeIntoContext( + SpecializedInterfaceTy); + }, + LookUpConformanceInSignature(SpecializedGenericSig.getPointer())); + return subs; +} + +std::string GenericSpecializationMangler::manglePrespecialization( + std::string unspecializedName, GenericSignature genericSig, + GenericSignature specializedSig) { + auto subs = + getSubstitutionMapForPrespecialization(genericSig, specializedSig); + GenericSpecializationMangler mangler(unspecializedName, subs); + return mangler.mangle(genericSig); +} diff --git a/lib/SIL/Utils/InstructionUtils.cpp b/lib/SIL/Utils/InstructionUtils.cpp index 73e84417f3fbb..6b9d7e6bf0aea 100644 --- a/lib/SIL/Utils/InstructionUtils.cpp +++ b/lib/SIL/Utils/InstructionUtils.cpp @@ -12,6 +12,7 @@ #define DEBUG_TYPE "sil-inst-utils" #include "swift/SIL/InstructionUtils.h" +#include "swift/SIL/MemAccessUtils.h" #include "swift/AST/SubstitutionMap.h" #include "swift/Basic/NullablePtr.h" #include "swift/Basic/STLExtras.h" @@ -64,20 +65,6 @@ SILValue swift::getUnderlyingObjectStopAtMarkDependence(SILValue v) { } } -static bool isRCIdentityPreservingCast(ValueKind Kind) { - switch (Kind) { - case ValueKind::UpcastInst: - case ValueKind::UncheckedRefCastInst: - case ValueKind::UnconditionalCheckedCastInst: - case ValueKind::UnconditionalCheckedCastValueInst: - case ValueKind::RefToBridgeObjectInst: - case ValueKind::BridgeObjectToRefInst: - return true; - default: - return false; - } -} - /// Return the underlying SILValue after stripping off identity SILArguments if /// we belong to a BB with one predecessor. SILValue swift::stripSinglePredecessorArgs(SILValue V) { @@ -122,42 +109,39 @@ SILValue swift::stripSinglePredecessorArgs(SILValue V) { } } -SILValue swift::stripCastsWithoutMarkDependence(SILValue V) { +SILValue swift::stripCastsWithoutMarkDependence(SILValue v) { while (true) { - V = stripSinglePredecessorArgs(V); - - auto K = V->getKind(); - if (isRCIdentityPreservingCast(K) - || K == ValueKind::UncheckedTrivialBitCastInst - || K == ValueKind::BeginAccessInst - || K == ValueKind::EndCOWMutationInst) { - V = cast(V)->getOperand(0); - continue; + v = stripSinglePredecessorArgs(v); + if (auto *svi = dyn_cast(v)) { + if (isRCIdentityPreservingCast(svi) + || isa(v) + || isa(v) + || isa(v)) { + v = svi->getOperand(0); + continue; + } } - - return V; + return v; } } SILValue swift::stripCasts(SILValue v) { while (true) { v = stripSinglePredecessorArgs(v); - - auto k = v->getKind(); - if (isRCIdentityPreservingCast(k) - || k == ValueKind::UncheckedTrivialBitCastInst - || k == ValueKind::MarkDependenceInst - || k == ValueKind::BeginAccessInst) { - v = cast(v)->getOperand(0); - continue; + if (auto *svi = dyn_cast(v)) { + if (isRCIdentityPreservingCast(svi) + || isa(v) + || isa(v) + || isa(v)) { + v = cast(v)->getOperand(0); + continue; + } } - SILValue v2 = stripOwnershipInsts(v); if (v2 != v) { v = v2; continue; } - return v; } } diff --git a/lib/SIL/Utils/MemAccessUtils.cpp b/lib/SIL/Utils/MemAccessUtils.cpp index dedba04a24ce1..73d9d3e43920d 100644 --- a/lib/SIL/Utils/MemAccessUtils.cpp +++ b/lib/SIL/Utils/MemAccessUtils.cpp @@ -13,64 +13,465 @@ #define DEBUG_TYPE "sil-access-utils" #include "swift/SIL/MemAccessUtils.h" -#include "swift/SIL/ApplySite.h" -#include "swift/SIL/Projection.h" -#include "swift/SIL/SILGlobalVariable.h" #include "swift/SIL/SILModule.h" #include "swift/SIL/SILUndef.h" +#include "swift/SIL/DynamicCasts.h" +#include "llvm/Support/Debug.h" using namespace swift; //===----------------------------------------------------------------------===// -// MARK: General Helpers +// MARK: FindAccessVisitor //===----------------------------------------------------------------------===// -// TODO: When the optimizer stops stripping begin_access markers, then we should -// be able to assert that the result is a BeginAccessInst and the default case -// is unreachable. -SILValue swift::getAddressAccess(SILValue v) { - while (true) { - assert(v->getType().isAddress()); - auto projection = AccessProjection(v); - if (!projection) - return v; +namespace { + +enum StorageCastTy { StopAtStorageCast, IgnoreStorageCast }; + +// Handle a single phi-web within an access use-def chain. +// +// Recursively calls the useDefVisitor on any operations that aren't recognized +// as storage casts or projections. If the useDefVisitor finds a consistent +// result for all operands, then it's result will remain valid. If the +// useDefVisitor has an invalid result after processing the phi web, then it's +// original result is restored, then the phi reported to the useDefVisitor as a +// NonAccess. +// +// Phi-web's are only allowed to contain casts and projections that do not +// affect the access path. If AccessPhiVisitor reaches an unhandled projection, +// it remembers that as the commonDefinition. If after processing the entire +// web, the commonDefinition is unique, then it calls the original useDefVisitor +// to update its result. Note that visitAccessProjection and setDefinition are +// only used by visitors that process access projections; once the accessed +// address is reached, they are no longer relevant. +template +class AccessPhiVisitor + : public AccessUseDefChainVisitor> { + + UseDefVisitor &useDefVisitor; + StorageCastTy storageCastTy; + + Optional commonDefinition; + SmallVector pointerWorklist; + SmallPtrSet nestedPhis; + +public: + AccessPhiVisitor(UseDefVisitor &useDefVisitor, StorageCastTy storageCastTy) + : useDefVisitor(useDefVisitor), storageCastTy(storageCastTy) {} - v = projection.baseAddress(); + // Main entry point. + void findPhiAccess(SILPhiArgument *phiArg) && { + auto savedResult = useDefVisitor.saveResult(); + visitPhi(phiArg); + while (!pointerWorklist.empty()) { + this->visit(pointerWorklist.pop_back_val()); + } + // If a common path component was found, recursively look for the result. + if (commonDefinition) { + if (commonDefinition.getValue()) { + useDefVisitor.reenterUseDef(commonDefinition.getValue()); + } else { + // Divergent paths were found; invalidate any previously discovered + // storage. + useDefVisitor.invalidateResult(); + } + } + // If the result is now invalid, reset it and process the current phi as an + // unrecgonized access instead. + if (!useDefVisitor.isResultValid()) { + useDefVisitor.restoreResult(savedResult); + visitNonAccess(phiArg); + } } -} -SILValue swift::getAccessedAddress(SILValue v) { - while (true) { - SILValue v2 = stripAccessMarkers(getAddressAccess(v)); - if (v2 == v) - return v; - v = v2; + // Visitor helper. + void setDefinition(SILValue def) { + if (!commonDefinition) { + commonDefinition = def; + return; + } + if (commonDefinition.getValue() != def) + commonDefinition = SILValue(); + } + + void checkVisitorResult(SILValue result) { + assert(!result && "must override any visitor that returns a result"); + } + + // MARK: Visitor implementation. + + // Recursively call the original storageVisitor for each base. We can't simply + // look for a common definition on all phi inputs, because the base may be + // cloned on each path. For example, two global_addr instructions may refer to + // the same global storage. Those global_addr instructions may each be + // converted to a RawPointer before being passed into the non-address phi. + void visitBase(SILValue base, AccessedStorage::Kind kind) { + checkVisitorResult(useDefVisitor.visitBase(base, kind)); } + + void visitNonAccess(SILValue value) { + checkVisitorResult(useDefVisitor.visitNonAccess(value)); + } + + void visitNestedAccess(BeginAccessInst *access) { + checkVisitorResult(useDefVisitor.visitNestedAccess(access)); + } + + void visitPhi(SILPhiArgument *phiArg) { + if (nestedPhis.insert(phiArg).second) + phiArg->getIncomingPhiValues(pointerWorklist); + } + + void visitStorageCast(SingleValueInstruction *projectedAddr, + Operand *sourceOper) { + // Allow conversions to/from pointers and addresses on disjoint phi paths + // only if the underlying useDefVisitor allows it. + if (storageCastTy == IgnoreStorageCast) + pointerWorklist.push_back(sourceOper->get()); + else + visitNonAccess(projectedAddr); + } + + void visitAccessProjection(SingleValueInstruction *projectedAddr, + Operand *sourceOper) { + // An offset index on a phi path is always conservatively considered an + // unknown offset. + if (isa(projectedAddr) || isa(projectedAddr)) { + useDefVisitor.addUnknownOffset(); + pointerWorklist.push_back(sourceOper->get()); + return; + } + // No other access projections are expected to occur on disjoint phi + // paths. Stop searching at this projection. + setDefinition(projectedAddr); + } +}; + +// Find the origin of an access while skipping projections and casts and +// handling phis. +template +class FindAccessVisitorImpl : public AccessUseDefChainVisitor { + using SuperTy = AccessUseDefChainVisitor; + +protected: + NestedAccessType nestedAccessTy; + StorageCastTy storageCastTy; + + SmallPtrSet visitedPhis; + bool hasUnknownOffset = false; + +public: + FindAccessVisitorImpl(NestedAccessType nestedAccessTy, + StorageCastTy storageCastTy) + : nestedAccessTy(nestedAccessTy), storageCastTy(storageCastTy) {} + + // MARK: AccessPhiVisitor::UseDefVisitor implementation. + // + // Subclasses must implement: + // isResultValid() + // invalidateResult() + // saveResult() + // restoreResult(Result) + // addUnknownOffset() + + void reenterUseDef(SILValue sourceAddr) { + SILValue nextAddr = this->visit(sourceAddr); + while (nextAddr) { + checkNextAddressType(nextAddr, sourceAddr); + nextAddr = this->visit(nextAddr); + } + } + + // MARK: visitor implementation. + + // Override AccessUseDefChainVisitor to ignore access markers and find the + // outer access base. + SILValue visitNestedAccess(BeginAccessInst *access) { + if (nestedAccessTy == NestedAccessType::IgnoreAccessBegin) + return access->getSource(); + + return SuperTy::visitNestedAccess(access); + } + + SILValue visitPhi(SILPhiArgument *phiArg) { + // Cycles involving phis are only handled within AccessPhiVisitor. + // Path components are not allowed in phi cycles. + if (visitedPhis.insert(phiArg).second) { + AccessPhiVisitor(this->asImpl(), storageCastTy) + .findPhiAccess(phiArg); + // Each phi operand was now reentrantly processed. Stop visiting. + return SILValue(); + } + // Cannot treat unresolved phis as "unidentified" because they may alias + // with global or class access. + return this->asImpl().visitNonAccess(phiArg); + } + + SILValue visitStorageCast(SingleValueInstruction *projectedAddr, + Operand *sourceAddr) { + assert(storageCastTy == IgnoreStorageCast); + return sourceAddr->get(); + } + + SILValue visitAccessProjection(SingleValueInstruction *projectedAddr, + Operand *sourceAddr) { + if (auto *indexAddr = dyn_cast(projectedAddr)) { + if (!Projection(indexAddr).isValid()) + this->asImpl().addUnknownOffset(); + } else if (isa(projectedAddr)) { + this->asImpl().addUnknownOffset(); + } + return sourceAddr->get(); + } + +protected: + // Helper for reenterUseDef + void checkNextAddressType(SILValue nextAddr, SILValue sourceAddr) { +#ifdef NDEBUG + return; +#endif + SILType type = nextAddr->getType(); + // FIXME: This relatively expensive pointer getAnyPointerElementType check + // is only needed because keypath generation incorrectly produces + // pointer_to_address directly from stdlib Pointer types without a + // struct_extract (as is correctly done in emitAddressorAccessor), and + // the PointerToAddressInst operand type is never verified. + if (type.getASTType()->getAnyPointerElementType()) + return; + + if (type.isAddress() || isa(type.getASTType()) + || isa(type.getASTType())) { + return; + } + llvm::errs() << "Visiting "; + sourceAddr->print(llvm::errs()); + llvm::errs() << " not an address "; + nextAddr->print(llvm::errs()); + nextAddr->getFunction()->print(llvm::errs()); + assert(false); + } +}; + +// Implement getAccessAddress, getAccessBegin, and getAccessBase. +class FindAccessBaseVisitor + : public FindAccessVisitorImpl { + using SuperTy = FindAccessVisitorImpl; + +protected: + Optional base; + +public: + FindAccessBaseVisitor(NestedAccessType nestedAccessTy, + StorageCastTy storageCastTy) + : FindAccessVisitorImpl(nestedAccessTy, storageCastTy) {} + + // Returns the accessed address or an invalid SILValue. + SILValue findBase(SILValue sourceAddr) && { + reenterUseDef(sourceAddr); + return base.getValueOr(SILValue()); + } + + void setResult(SILValue foundBase) { + if (!base) + base = foundBase; + else if (base.getValue() != foundBase) + base = SILValue(); + } + + // MARK: AccessPhiVisitor::UseDefVisitor implementation. + + bool isResultValid() const { return base && bool(base.getValue()); } + + void invalidateResult() { base = SILValue(); } + + Optional saveResult() const { return base; } + + void restoreResult(Optional result) { base = result; } + + void addUnknownOffset() { return; } + + // MARK: visitor implementation. + + SILValue visitBase(SILValue base, AccessedStorage::Kind kind) { + setResult(base); + return SILValue(); + } + + SILValue visitNonAccess(SILValue value) { + setResult(value); + return SILValue(); + } + + // Override visitStorageCast to avoid seeing through arbitrary address casts. + SILValue visitStorageCast(SingleValueInstruction *projectedAddr, + Operand *sourceAddr) { + if (storageCastTy == StopAtStorageCast) + return visitNonAccess(projectedAddr); + + return SuperTy::visitStorageCast(projectedAddr, sourceAddr); + } +}; + +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// MARK: Standalone API +//===----------------------------------------------------------------------===// + +SILValue swift::getTypedAccessAddress(SILValue address) { + assert(address->getType().isAddress()); + SILValue accessAddress = + FindAccessBaseVisitor(NestedAccessType::StopAtAccessBegin, + StopAtStorageCast) + .findBase(address); + assert(accessAddress->getType().isAddress()); + return accessAddress; } -bool swift::isLetAddress(SILValue accessedAddress) { - assert(accessedAddress == getAccessedAddress(accessedAddress) - && "caller must find the address root"); +// TODO: When the optimizer stops stripping begin_access markers and SILGen +// protects all memory operations with at least an "unsafe" access scope, then +// we should be able to assert that this returns a BeginAccessInst. +SILValue swift::getAccessScope(SILValue address) { + assert(address->getType().isAddress()); + return FindAccessBaseVisitor(NestedAccessType::StopAtAccessBegin, + IgnoreStorageCast) + .findBase(address); +} + +// This is allowed to be called on a non-address pointer type. +SILValue swift::getAccessBase(SILValue address) { + return FindAccessBaseVisitor(NestedAccessType::IgnoreAccessBegin, + IgnoreStorageCast) + .findBase(address); +} + +bool swift::isLetAddress(SILValue address) { + SILValue base = getAccessBase(address); + if (!base) + return false; + // Is this an address of a "let" class member? - if (auto *rea = dyn_cast(accessedAddress)) + if (auto *rea = dyn_cast(base)) return rea->getField()->isLet(); // Is this an address of a global "let"? - if (auto *gai = dyn_cast(accessedAddress)) { + if (auto *gai = dyn_cast(base)) { auto *globalDecl = gai->getReferencedGlobal()->getDecl(); return globalDecl && globalDecl->isLet(); } return false; } +//===----------------------------------------------------------------------===// +// MARK: FindReferenceRoot +//===----------------------------------------------------------------------===// + +// On some platforms, casting from a metatype to a reference type dynamically +// allocates a ref-counted box for the metatype. Naturally that is the place +// where RC-identity begins. Considering the source of such a casts to be +// RC-identical would confuse ARC optimization, which might eliminate a retain +// of such an object completely. +// +// The SILVerifier checks that none of these operations cast a nontrivial value +// to a reference except unconditional_checked_cast[_value]. +bool swift::isRCIdentityPreservingCast(SingleValueInstruction *svi) { + switch (svi->getKind()) { + default: + return false; + // Ignore ownership casts + case SILInstructionKind::CopyValueInst: + case SILInstructionKind::BeginBorrowInst: + // Ignore class type casts + case SILInstructionKind::UpcastInst: + case SILInstructionKind::UncheckedRefCastInst: + case SILInstructionKind::RefToBridgeObjectInst: + case SILInstructionKind::BridgeObjectToRefInst: + return true; + case SILInstructionKind::UnconditionalCheckedCastInst: + case SILInstructionKind::UnconditionalCheckedCastValueInst: + return SILDynamicCastInst(svi).isRCIdentityPreserving(); + } +} + +namespace { + +// Essentially RC identity where the starting point is already a reference. +class FindReferenceRoot { + SmallPtrSet visitedPhis; + +public: + SILValue findRoot(SILValue ref) && { + SILValue root = recursiveFindRoot(ref); + assert(root && "all phi inputs must be reachable"); + return root; + } + +protected: + // Return an invalid value for a phi with no resolved inputs. + SILValue recursiveFindRoot(SILValue ref) { + while (auto *svi = dyn_cast(ref)) { + if (!isRCIdentityPreservingCast(svi)) { + break; + } + ref = svi->getOperand(0); + }; + auto *phi = dyn_cast(ref); + if (!phi || !phi->isPhiArgument()) { + return ref; + } + // Handle phis... + if (!visitedPhis.insert(phi).second) { + return SILValue(); + } + SILValue commonInput; + phi->visitIncomingPhiOperands([&](Operand *operand) { + SILValue input = recursiveFindRoot(operand->get()); + // Ignore "back/cross edges" to previously visited phis. + if (!input) + return true; + + if (!commonInput) { + commonInput = input; + return true; + } + if (commonInput == input) + return true; + + commonInput = phi; + return false; + }); + return commonInput; + } +}; + +} // end anonymous namespace + +static SILValue findReferenceRoot(SILValue ref) { + return FindReferenceRoot().findRoot(ref); +} + //===----------------------------------------------------------------------===// // MARK: AccessedStorage //===----------------------------------------------------------------------===// +SILGlobalVariable *getReferencedGlobal(SILInstruction *inst) { + if (auto *gai = dyn_cast(inst)) { + return gai->getReferencedGlobal(); + } + if (auto apply = FullApplySite::isa(inst)) { + if (auto *funcRef = apply.getReferencedFunctionOrNull()) { + return getVariableOfGlobalInit(funcRef); + } + } + return nullptr; +} + +constexpr unsigned AccessedStorage::TailIndex; + AccessedStorage::AccessedStorage(SILValue base, Kind kind) { + // For kind==Unidentified, base may be an invalid empty or tombstone value. assert(base && "invalid storage base"); initKind(kind); - switch (kind) { case Box: assert(isa(base)); @@ -96,54 +497,73 @@ AccessedStorage::AccessedStorage(SILValue base, Kind kind) { setElementIndex(cast(base)->getIndex()); break; case Global: - if (auto *GAI = dyn_cast(base)) - global = GAI->getReferencedGlobal(); - else { - FullApplySite apply(cast(base)); - auto *funcRef = apply.getReferencedFunctionOrNull(); - assert(funcRef); - global = getVariableOfGlobalInit(funcRef); - assert(global); - // Require a decl for all formally accessed globals defined in this - // module. (Access of globals defined elsewhere has Unidentified storage). - // AccessEnforcementWMO requires this. - assert(global->getDecl()); - } + global = getReferencedGlobal(cast(base)); + // Require a decl for all formally accessed globals defined in this + // module. AccessEnforcementWMO requires this. Swift globals defined in + // another module either use an addressor, which has Unidentified + // storage. Imported non-Swift globals are accessed via global_addr but have + // no declaration. + assert(global->getDecl() || isa(base)); 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 addresses will be - // dynamically checked, and static analysis will be sufficiently - // conservative given that classes are not "uniquely identified". + // actually refer the same address) because, when the effort fails, static + // analysis will be sufficiently conservative given that classes are not + // "uniquely identified", and these addresses will be dynamically checked. auto *REA = cast(base); - value = stripBorrow(REA->getOperand()); - setElementIndex(REA->getFieldNo()); + value = findReferenceRoot(REA->getOperand()); + setElementIndex(REA->getFieldIndex()); break; } case Tail: { auto *RTA = cast(base); - value = stripBorrow(RTA->getOperand()); + value = findReferenceRoot(RTA->getOperand()); break; } } + setLetAccess(base); } -// Return true if the given access is on a 'let' lvalue. -bool AccessedStorage::isLetAccess(SILFunction *F) const { - if (auto *decl = dyn_cast_or_null(getDecl())) - return decl->isLet(); +void AccessedStorage::visitRoots( + SILFunction *function, + llvm::function_ref visitor) const { + if (SILValue root = getRoot()) { + visitor(root); + return; + } + if (getKind() == Unidentified) { + return; + } + assert(getKind() == Global && function); + SILGlobalVariable *global = getGlobal(); + for (auto &block : *function) { + for (auto &instruction : block) { + if (global == getReferencedGlobal(&instruction)) { + visitor(cast(&instruction)); + } + } + } +} +// Set 'isLet' to true if this storage can be determined to be a 'let' variable. +// +// \p base must be the access base for this storage, as passed to the +// AccessedStorage constructor. +void AccessedStorage::setLetAccess(SILValue base) { // 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 (getKind() == AccessedStorage::Global) - return getGlobal()->isLet(); - - return false; + if (getKind() == AccessedStorage::Global) { + Bits.AccessedStorage.isLet = getGlobal()->isLet(); + return; + } + if (auto *decl = dyn_cast_or_null(getDecl(base))) { + Bits.AccessedStorage.isLet = decl->isLet(); + } } -const ValueDecl *AccessedStorage::getDecl() const { +const ValueDecl *AccessedStorage::getDecl(SILValue base) const { switch (getKind()) { case Box: return cast(value)->getLoc().getAsASTNode(); @@ -155,8 +575,16 @@ const ValueDecl *AccessedStorage::getDecl() const { return global->getDecl(); case Class: { - auto *decl = getObject()->getType().getNominalOrBoundGenericNominal(); - return decl->getStoredProperties()[getPropertyIndex()]; + // The property index is relative to the VarDecl in ref_element_addr, and + // can only be reliably determined when the base is avaiable. Otherwise, we + // can only make a best effort to extract it from the object type, which + // might not even be a class in the case of bridge objects. + if (ClassDecl *classDecl = + base ? cast(base)->getClassDecl() + : getObject()->getType().getClassOrBoundGenericClass()) { + return getIndexedField(classDecl, getPropertyIndex()); + } + return nullptr; } case Tail: return nullptr; @@ -222,206 +650,853 @@ void AccessedStorage::print(raw_ostream &os) const { break; case Class: os << getObject(); - os << " Field: "; - getDecl()->print(os); + if (auto *decl = getDecl()) { + os << " Field: "; + decl->print(os); + } os << " Index: " << getPropertyIndex() << "\n"; break; case Tail: os << getObject(); - os << " Tail\n"; } } -void AccessedStorage::dump() const { print(llvm::dbgs()); } +LLVM_ATTRIBUTE_USED void AccessedStorage::dump() const { print(llvm::dbgs()); } namespace { -// Find common AccessedStorage that leads to all arguments of a given -// pointer phi use. Return an invalid SILValue on failure. -// -// Also guarantees that all phi inputs follow the same access path. If any phi -// inputs have different access path components, then the phi is considered an -// invalid access. This is ok because path components always have an address -// type, and we are phasing out all address-type phis. Pointer-phis will -// continue to be allowed but they cannot affect the access path. -template -class FindPhiStorageVisitor - : public AccessUseDefChainVisitor> { - StorageVisitor &storageVisitor; - Optional commonDefinition; - SmallVector pointerWorklist; - SmallPtrSet nestedPhis; + +// Implementation of AccessUseDefChainVisitor that looks for a single common +// AccessedStorage object for all projection paths. +class FindAccessedStorageVisitor + : public FindAccessVisitorImpl { + +public: + struct Result { + Optional storage; + SILValue base; + }; + +private: + Result result; + + void setResult(AccessedStorage foundStorage, SILValue foundBase) { + if (!result.storage) { + result.storage = foundStorage; + assert(!result.base); + result.base = foundBase; + } else { + // `storage` may still be invalid. If both `storage` and `foundStorage` + // are invalid, this check passes, but we return an invalid storage + // below. + if (!result.storage->hasIdenticalBase(foundStorage)) + result.storage = AccessedStorage(); + if (result.base != foundBase) + result.base = SILValue(); + } + } + +public: + FindAccessedStorageVisitor(NestedAccessType nestedAccessTy) + : FindAccessVisitorImpl(nestedAccessTy, IgnoreStorageCast) {} + + // Main entry point + void findStorage(SILValue sourceAddr) { this->reenterUseDef(sourceAddr); } + + AccessedStorage getStorage() const { + return result.storage.getValueOr(AccessedStorage()); + } + // getBase may return an invalid value for valid Global storage because there + // may be multiple global_addr bases for identical storage. + SILValue getBase() const { return result.base; } + + // MARK: AccessPhiVisitor::UseDefVisitor implementation. + + // A valid result requires valid storage, but not a valid base. + bool isResultValid() const { + return result.storage && bool(result.storage.getValue()); + } + + void invalidateResult() { setResult(AccessedStorage(), SILValue()); } + + Result saveResult() const { return result; } + + void restoreResult(Result savedResult) { result = savedResult; } + + void addUnknownOffset() { return; } + + // MARK: visitor implementation. + + SILValue visitBase(SILValue base, AccessedStorage::Kind kind) { + setResult(AccessedStorage(base, kind), base); + return SILValue(); + } + + SILValue visitNonAccess(SILValue value) { + invalidateResult(); + return SILValue(); + } +}; + +} // end anonymous namespace + +AccessedStorageWithBase +AccessedStorageWithBase::compute(SILValue sourceAddress) { + FindAccessedStorageVisitor visitor(NestedAccessType::IgnoreAccessBegin); + visitor.findStorage(sourceAddress); + return {visitor.getStorage(), visitor.getBase()}; +} + +AccessedStorageWithBase +AccessedStorageWithBase::computeInScope(SILValue sourceAddress) { + FindAccessedStorageVisitor visitor(NestedAccessType::StopAtAccessBegin); + visitor.findStorage(sourceAddress); + return {visitor.getStorage(), visitor.getBase()}; +} + +AccessedStorage AccessedStorage::compute(SILValue sourceAddress) { + return AccessedStorageWithBase::compute(sourceAddress).storage; +} + +AccessedStorage AccessedStorage::computeInScope(SILValue sourceAddress) { + return AccessedStorageWithBase::computeInScope(sourceAddress).storage; +} + +//===----------------------------------------------------------------------===// +// MARK: AccessPath +//===----------------------------------------------------------------------===// + +bool AccessPath::contains(AccessPath subPath) const { + if (!isValid() || !subPath.isValid()) { + return false; + } + if (!storage.hasIdenticalBase(subPath.storage)) { + return false; + } + // Does the offset index match? + if (offset != subPath.offset || offset == UnknownOffset) { + return false; + } + return pathNode.node->isPrefixOf(subPath.pathNode.node); +} + +bool AccessPath::mayOverlap(AccessPath otherPath) const { + if (!isValid() || !otherPath.isValid()) + return true; + + if (storage.isDistinctFrom(otherPath.storage)) { + return false; + } + // If subpaths are disjoint, they do not overlap regardless of offset. + if (!pathNode.node->isPrefixOf(otherPath.pathNode.node) + && !otherPath.pathNode.node->isPrefixOf(pathNode.node)) { + return true; + } + return offset == otherPath.offset || offset == UnknownOffset + || otherPath.offset == UnknownOffset; +} + +namespace { + +// Implementation of AccessUseDefChainVisitor that builds an AccessPath. +class AccessPathVisitor : public FindAccessVisitorImpl { + using SuperTy = FindAccessVisitorImpl; + + SILModule *module; + + // This nested visitor holds the AccessedStorage and base results. + FindAccessedStorageVisitor storageVisitor; + + // Save just enough information for to checkpoint before processing phis. Phis + // can add path components and add an unknown offset. + struct Result { + FindAccessedStorageVisitor::Result storageResult; + int savedOffset; + unsigned pathLength; + + Result(FindAccessedStorageVisitor::Result storageResult, int offset, + unsigned pathLength) + : storageResult(storageResult), savedOffset(offset), + pathLength(pathLength) {} + }; + + // Only access projections affect this path. Since they are are not allowed + // beyond phis, this path is not part of AccessPathVisitor::Result. + llvm::SmallVector reversePath; + // Holds a non-zero value if an index_addr has been processed without yet + // creating a path index for it. + int pendingOffset = 0; public: - FindPhiStorageVisitor(StorageVisitor &storageVisitor) - : storageVisitor(storageVisitor) {} + AccessPathVisitor(SILModule *module, NestedAccessType nestedAccessTy) + : FindAccessVisitorImpl(nestedAccessTy, IgnoreStorageCast), + module(module), storageVisitor(NestedAccessType::IgnoreAccessBegin) {} // Main entry point. - void findPhiStorage(SILPhiArgument *phiArg) { - // Visiting a phi will call storageVisitor to set the storage result - // whenever it finds a base. - visitPhi(phiArg); - while (!pointerWorklist.empty()) { - this->visit(pointerWorklist.pop_back_val()); - } - // If a common path component was found, recursively look for the storage. - if (commonDefinition) { - if (commonDefinition.getValue()) { - auto storage = storageVisitor.findStorage(commonDefinition.getValue()); - (void)storage; // The same storageVisitor called us. It has already - // recorded the storage that it found. - } else { - // If divergent paths were found, invalidate any previously discovered - // storage. - storageVisitor.setStorage(AccessedStorage()); - } + AccessPathWithBase findAccessPath(SILValue sourceAddr) && { + this->reenterUseDef(sourceAddr); + if (auto storage = storageVisitor.getStorage()) { + return AccessPathWithBase( + AccessPath(storage, computeForwardPath(), pendingOffset), + storageVisitor.getBase()); } + return AccessPathWithBase(AccessPath(), SILValue()); } - // Visitor helper. - void setDefinition(SILValue def) { - if (!commonDefinition) { - commonDefinition = def; +protected: + void addPathOffset(int offset) { + if (pendingOffset == AccessPath::UnknownOffset) + return; + + if (offset == AccessPath::UnknownOffset) { + pendingOffset = offset; return; } - if (commonDefinition.getValue() != def) - commonDefinition = SILValue(); + // Accumulate static offsets + pendingOffset = pendingOffset + offset; } - // MARK: Visitor implementation. - - void checkResult(SILValue result) { - assert(!result && "must override any visitor that returns a result"); + // Return the trie node corresponding to the current state of reversePath. + AccessPath::PathNode computeForwardPath() { + IndexTrieNode *forwardPath = module->getIndexTrieRoot(); + for (AccessPath::Index nextIndex : llvm::reverse(reversePath)) { + forwardPath = forwardPath->getChild(nextIndex.getEncoding()); + } + return AccessPath::PathNode(forwardPath); } - // Recursively call the original storageVisitor for each base. We can't simply - // look for a common definition on all phi inputs, because the base may be - // cloned on each path. For example, two global_addr instructions may refer to - // the same global storage. Those global_addr instructions may each be - // converted to a RawPointer before being passed into the non-address phi. - void visitBase(SILValue base, AccessedStorage::Kind kind) { - checkResult(storageVisitor.visitBase(base, kind)); +public: + // MARK: AccessPhiVisitor::UseDefVisitor implementation. + + bool isResultValid() const { return storageVisitor.isResultValid(); } + + void invalidateResult() { + storageVisitor.invalidateResult(); + // Don't clear reversePath. We my call restoreResult later. + pendingOffset = 0; } - void visitNonAccess(SILValue value) { - checkResult(storageVisitor.visitNonAccess(value)); + Result saveResult() const { + return Result(storageVisitor.saveResult(), pendingOffset, + reversePath.size()); } - void visitNestedAccess(BeginAccessInst *access) { - checkResult(storageVisitor.visitNestedAccess(access)); + void restoreResult(Result result) { + storageVisitor.restoreResult(result.storageResult); + pendingOffset = result.savedOffset; + assert(result.pathLength <= reversePath.size() + && "a phi should only add to the path"); + reversePath.erase(reversePath.begin() + result.pathLength, + reversePath.end()); } - void visitPhi(SILPhiArgument *phiArg) { - if (nestedPhis.insert(phiArg).second) - phiArg->getIncomingPhiValues(pointerWorklist); + void addUnknownOffset() { pendingOffset = AccessPath::UnknownOffset; } + + // MARK: visitor implementation. Return the address source as the next use-def + // value to process. An invalid SILValue stops def-use traversal. + + SILValue visitBase(SILValue base, AccessedStorage::Kind kind) { + return storageVisitor.visitBase(base, kind); } - void visitCast(SingleValueInstruction *projectedAddr, Operand *parentAddr) { - // Allow conversions to/from pointers and addresses on disjoint phi paths. - this->pointerWorklist.push_back(parentAddr->get()); + SILValue visitNonAccess(SILValue value) { + invalidateResult(); + return SILValue(); } - void visitPathComponent(SingleValueInstruction *projectedAddr, - Operand *parentAddr) { - // Path components are not expected to occur on disjoint phi paths. Stop - // searching at this projection. - setDefinition(projectedAddr); + // Override FindAccessVisitorImpl to record path components. + SILValue visitAccessProjection(SingleValueInstruction *projectedAddr, + Operand *sourceAddr) { + auto projIdx = ProjectionIndex(projectedAddr); + if (auto *indexAddr = dyn_cast(projectedAddr)) { + addPathOffset(projIdx.isValid() ? projIdx.Index + : AccessPath::UnknownOffset); + } else if (isa(projectedAddr)) { + addPathOffset(AccessPath::UnknownOffset); + } else if (projIdx.isValid()) { + if (pendingOffset) { + LLVM_DEBUG(llvm::dbgs() << "Subobject projection with offset index: " + << *projectedAddr); + // Return an invalid result even though findAccessedStorage() may be + // able to find valid storage, because an offset from a subobject is an + // invalid access path. + return visitNonAccess(projectedAddr); + } + reversePath.push_back( + AccessPath::Index::forSubObjectProjection(projIdx.Index)); + } else { + // Ignore everything in getAccessProjectionOperand that is an access + // projection with no affect on the access path. + assert(isa(projectedAddr) + || isa(projectedAddr) + || isa(projectedAddr)); + } + return sourceAddr->get(); } }; -} // namespace + +} // end anonymous namespace + +AccessPathWithBase AccessPathWithBase::compute(SILValue address) { + return AccessPathVisitor(address->getModule(), + NestedAccessType::IgnoreAccessBegin) + .findAccessPath(address); +} + +AccessPathWithBase AccessPathWithBase::computeInScope(SILValue address) { + return AccessPathVisitor(address->getModule(), + NestedAccessType::StopAtAccessBegin) + .findAccessPath(address); +} + +void AccessPath::Index::print(raw_ostream &os) const { + if (isSubObjectProjection()) + os << '#' << getSubObjectIndex(); + else { + os << '@'; + if (isUnknownOffset()) + os << "Unknown"; + else + os << getOffset(); + } +} + +LLVM_ATTRIBUTE_USED void AccessPath::Index::dump() const { + print(llvm::dbgs()); +} + +static void recursivelyPrintPath(AccessPath::PathNode node, raw_ostream &os) { + AccessPath::PathNode parent = node.getParent(); + if (!parent.isRoot()) { + recursivelyPrintPath(parent, os); + os << ","; + } + node.getIndex().print(os); +} + +void AccessPath::printPath(raw_ostream &os) const { + os << "Path: "; + if (!isValid()) { + os << "INVALID\n"; + return; + } + os << "("; + PathNode node = getPathNode(); + if (offset != 0) { + Index::forOffset(offset).print(os); + if (!node.isRoot()) + os << ","; + } + if (!node.isRoot()) + recursivelyPrintPath(node, os); + os << ")\n"; +} + +void AccessPath::print(raw_ostream &os) const { + if (!isValid()) { + os << "INVALID\n"; + return; + } + os << "Storage: "; + getStorage().print(os); + printPath(os); +} + +LLVM_ATTRIBUTE_USED void AccessPath::dump() const { print(llvm::dbgs()); } + +void AccessPathWithBase::print(raw_ostream &os) const { + if (base) + os << "Base: " << base; + + accessPath.print(os); +} + +LLVM_ATTRIBUTE_USED void AccessPathWithBase::dump() const { + print(llvm::dbgs()); +} + +//===----------------------------------------------------------------------===// +// MARK: AccessPathDefUseTraversal +//===----------------------------------------------------------------------===// namespace { -// Implementation of AccessUseDefChainVisitor that looks for a single common -// AccessedStorage object for all projection paths. -template -class FindAccessedStorageVisitorBase - : public AccessUseDefChainVisitor { -protected: - Optional storage; - SmallPtrSet visitedPhis; + +// Perform def-use DFS traversal along a given AccessPath. DFS terminates at +// each discovered use. +// +// For useTy == Exact, the collected uses all have the same AccessPath. +// Subobject projections within that access path and their transitive uses are +// not included. +// +// For useTy == Inner, the collected uses to subobjects contained by the +// current access path. +// +// For useTy == Overlapping, the collected uses also include uses that +// access an object that contains the given AccessPath as well as uses at +// an unknown offset relative to the current path. +// +// Example, where AccessPath == (#2): +// %base = ... // access base +// load %base // containing use +// %elt1 = struct_element_addr %base, #1 // non-use (ignored) +// load %elt1 // non-use (unseen) +// %elt2 = struct_element_addr %base, #2 // outer projection (followed) +// load %elt2 // exact use +// %sub = struct_element_addr %elt2, #i // inner projection (followed) +// load %sub // inner use +// +// A use may be a BranchInst if the corresponding phi does not have common +// AccessedStorage. +// +// For class storage, the def-use traversal starts at the reference +// root. Eventually, traversal reach the base address of the formal access: +// +// %ref = ... // reference root +// %base = ref_element_addr %refRoot // formal access address +// load %base // use +class AccessPathDefUseTraversal { + AccessUseVisitor &visitor; + + // The origin of the def-use traversal. + AccessedStorage storage; + + // Remaining access path indices from the most recently visited def to any + // exact use in def-use order. + SmallVector pathIndices; + + // A point in the def-use traversal. isRef() is true only for object access + // prior to reaching the base address. + struct DFSEntry { + // Next potential use to visit and flag indicating whether traversal has + // reachaed the access base yet. + llvm::PointerIntPair useAndIsRef; + int pathCursor; // position within pathIndices + int offset; // index_addr offsets seen prior to this use + + DFSEntry(Operand *use, bool isRef, int pathCursor, int offset) + : useAndIsRef(use, isRef), pathCursor(pathCursor), offset(offset) {} + + Operand *getUse() const { return useAndIsRef.getPointer(); } + // Is this pointer a reference? + bool isRef() const { return useAndIsRef.getInt(); } + }; + SmallVector dfsStack; + + SmallPtrSet visitedPhis; + + // Transient traversal data should not be copied. + AccessPathDefUseTraversal(const AccessPathDefUseTraversal &) = delete; + AccessPathDefUseTraversal & + operator=(const AccessPathDefUseTraversal &) = delete; public: - // Main entry point. May be called reentrantly by the phi visitor. - AccessedStorage findStorage(SILValue sourceAddr) { - SILValue nextAddr = this->visit(sourceAddr); - while (nextAddr) { - assert(nextAddr->getType().isAddress() - || isa(nextAddr->getType().getASTType()) - || isa(nextAddr->getType().getASTType())); - nextAddr = this->visit(nextAddr); - } - return storage.getValueOr(AccessedStorage()); + AccessPathDefUseTraversal(AccessUseVisitor &visitor, AccessPath accessPath, + SILFunction *function) + : visitor(visitor), storage(accessPath.getStorage()) { + assert(accessPath.isValid()); + + initializePathIndices(accessPath); + + storage.visitRoots(function, [this](SILValue root) { + initializeDFS(root); + return true; + }); } - void setStorage(AccessedStorage foundStorage) { + // Return true is all uses have been visited. + bool visitUses() { + // Return false if initialization failed. if (!storage) { - storage = foundStorage; - } else { - // `storage` may still be invalid. If both `storage` and `foundStorage` - // are invalid, this check passes, but we return an invalid storage - // below. - if (!storage->hasIdenticalBase(foundStorage)) - storage = AccessedStorage(); + return false; } + while (!dfsStack.empty()) { + if (!visitUser(dfsStack.pop_back_val())) + return false; + } + return true; } - // MARK: visitor implementation. +protected: + void initializeDFS(SILValue root) { + // If root is a phi, record it so that its uses aren't visited twice. + if (auto *phi = dyn_cast(root)) { + if (phi->isPhiArgument()) + visitedPhis.insert(phi); + } + pushUsers(root, + DFSEntry(nullptr, storage.isReference(), pathIndices.size(), 0)); + } - SILValue visitBase(SILValue base, AccessedStorage::Kind kind) { - setStorage(AccessedStorage(base, kind)); - return SILValue(); + void pushUsers(SILValue def, const DFSEntry &dfs) { + for (auto *use : def->getUses()) + pushUser(DFSEntry(use, dfs.isRef(), dfs.pathCursor, dfs.offset)); } - SILValue visitNonAccess(SILValue value) { - setStorage(AccessedStorage()); - return SILValue(); + void pushUser(DFSEntry dfs) { + Operand *use = dfs.getUse(); + if (auto *bi = dyn_cast(use->getUser())) { + if (pushPhiUses(bi->getArgForOperand(use), dfs)) + return; + } + // If we didn't find and process a phi, continue DFS. + dfsStack.emplace_back(dfs); } - SILValue visitPhi(SILPhiArgument *phiArg) { - // Cycles involving phis are only handled within FindPhiStorageVisitor. - // Path components are not allowed in phi cycles. - if (visitedPhis.insert(phiArg).second) { - FindPhiStorageVisitor(this->asImpl()).findPhiStorage(phiArg); - return SILValue(); + bool pushPhiUses(const SILPhiArgument *phi, DFSEntry dfs); + + void initializePathIndices(AccessPath accessPath); + + // Return the offset at the current DFS path cursor, or zero. + int getPathOffset(const DFSEntry &dfs) const; + + // Return true if the accumulated offset matches the current path index. + // Update the DFSEntry and pathCursor to skip remaining offsets. + bool checkAndUpdateOffset(DFSEntry &dfs); + + // Handle non-index_addr projections. + void followProjection(SingleValueInstruction *svi, DFSEntry dfs); + + enum UseKind { LeafUse, IgnoredUse }; + UseKind visitSingleValueUser(SingleValueInstruction *svi, DFSEntry dfs); + + // Returns true as long as the visitor returns true. + bool visitUser(DFSEntry dfs); +}; + +} // end anonymous namespace + +// Initialize the array of remaining path indices. +void AccessPathDefUseTraversal::initializePathIndices(AccessPath accessPath) { + for (AccessPath::PathNode currentNode = accessPath.getPathNode(); + !currentNode.isRoot(); currentNode = currentNode.getParent()) { + assert(currentNode.getIndex().isSubObjectProjection() + && "a valid AccessPath does not contain any intermediate offsets"); + pathIndices.push_back(currentNode.getIndex()); + } + if (int offset = accessPath.getOffset()) { + pathIndices.push_back(AccessPath::Index::forOffset(offset)); + } + // The search will start from the object root, not the formal access base, + // so add the class index to the front. + if (storage.getKind() == AccessedStorage::Class) { + pathIndices.push_back( + AccessPath::Index::forSubObjectProjection(storage.getPropertyIndex())); + } + if (storage.getKind() == AccessedStorage::Tail) { + pathIndices.push_back( + AccessPath::Index::forSubObjectProjection(ProjectionIndex::TailIndex)); + } + // If the expected path has an unknown offset, then none of the uses are + // exact. + if (!visitor.findOverlappingUses() && !pathIndices.empty() + && pathIndices.back().isUnknownOffset()) { + return; + } +} + +// Return true if this phi has been processed and does not need to be +// considered as a separate use. +bool AccessPathDefUseTraversal::pushPhiUses(const SILPhiArgument *phi, + DFSEntry dfs) { + if (!visitedPhis.insert(phi).second) + return true; + + // If this phi has a common base, continue to follow the access path. This + // check is different for reference types vs pointer types. + if (dfs.isRef()) { + assert(!dfs.offset && "index_addr not allowed on reference roots"); + // When isRef is true, the address access hasn't been seen yet and + // we're still following the reference root's users. Check if all phi + // inputs have the same reference root before looking through it. + if (findReferenceRoot(phi) == storage.getObject()) { + pushUsers(phi, dfs); + return true; } - // Cannot treat unresolved phis as "unidentified" because they may alias - // with global or class access. - return visitNonAccess(phiArg); + // The branch will be pushed onto the normal user list. + return false; } + // Check if all phi inputs have the same accessed storage before + // looking through it. If the phi input differ the its storage is invalid. + auto phiPath = AccessPath::compute(phi); + if (phiPath.isValid()) { + assert(phiPath.getStorage().hasIdenticalBase(storage) + && "inconsistent phi storage"); + // If the phi paths have different offsets, its path has unknown offset. + if (phiPath.getOffset() == AccessPath::UnknownOffset) { + if (!visitor.findOverlappingUses()) + return true; + dfs.offset = AccessPath::UnknownOffset; + } + pushUsers(phi, dfs); + return true; + } + // The branch will be pushed onto the normal user list. + return false; +} - SILValue visitCast(SingleValueInstruction *projectedAddr, - Operand *parentAddr) { - return parentAddr->get(); +// Return the offset at the current DFS path cursor, or zero. +int AccessPathDefUseTraversal::getPathOffset(const DFSEntry &dfs) const { + if (dfs.pathCursor <= 0 + || pathIndices[dfs.pathCursor - 1].isSubObjectProjection()) { + return 0; } + return pathIndices[dfs.pathCursor - 1].getOffset(); +} - SILValue visitPathComponent(SingleValueInstruction *projectedAddr, - Operand *parentAddr) { - return parentAddr->get(); +// Return true if the accumulated offset matches the current path index. +// Update the DFSEntry and pathCursor to skip remaining offsets. +bool AccessPathDefUseTraversal::checkAndUpdateOffset(DFSEntry &dfs) { + int pathOffset = getPathOffset(dfs); + if (dfs.offset == AccessPath::UnknownOffset) { + if (pathOffset > 0) { + // Pop the offset from the expected path; there should only be + // one. Continue matching subobject indices even after seeing an unknown + // offset. A subsequent mismatching subobject index is still considered + // non-overlapping. This is valid for aliasing since an offset from a + // subobject is considered an invalid access path. + --dfs.pathCursor; + assert(getPathOffset(dfs) == 0 && "only one offset index allowed"); + } + // Continue searching only if we need to find overlapping uses. Preserve the + // unknown dfs offset so we don't consider any dependent operations to be + // exact or inner uses. + return visitor.findOverlappingUses(); } -}; + if (pathOffset == 0) { + return dfs.offset == 0; + } + // pop the offset from the expected path; there should only be one. + --dfs.pathCursor; + assert(getPathOffset(dfs) == 0 && "only one offset index allowed"); + + // Ignore all uses on this path unless we're collecting containing uses. + // UnknownOffset appears to overlap with all offsets and subobject uses. + if (pathOffset == AccessPath::UnknownOffset) { + // Set the dfs offset to unknown to avoid considering any dependent + // operations as exact or inner uses. + dfs.offset = AccessPath::UnknownOffset; + return visitor.findOverlappingUses(); + } + int useOffset = dfs.offset; + dfs.offset = 0; + // A known offset must match regardless of findOverlappingUses. + return pathOffset == useOffset; +} -struct FindAccessedStorageVisitor - : public FindAccessedStorageVisitorBase { +// Handle non-index_addr projections. +void AccessPathDefUseTraversal::followProjection(SingleValueInstruction *svi, + DFSEntry dfs) { + if (!checkAndUpdateOffset(dfs)) { + return; + } + if (dfs.pathCursor <= 0) { + if (visitor.useTy == AccessUseType::Exact) { + assert(dfs.pathCursor == 0); + return; + } + --dfs.pathCursor; + pushUsers(svi, dfs); + return; + } + AccessPath::Index pathIndex = pathIndices[dfs.pathCursor - 1]; + auto projIdx = ProjectionIndex(svi); + assert(projIdx.isValid()); + // Only subobjects indices are expected because offsets are handled above. + if (projIdx.Index == pathIndex.getSubObjectIndex()) { + --dfs.pathCursor; + pushUsers(svi, dfs); + } + return; +} - SILValue visitNestedAccess(BeginAccessInst *access) { - return access->getSource(); +// During the def-use traversal, visit a single-value instruction in which the +// used address is at operand zero. +// +// This must handle the def-use side of all operations that +// AccessUseDefChainVisitor::visit can handle. +// +// Return IgnoredUse if the def-use traversal either continues past \p +// svi or ignores this use. +// +// FIXME: Reuse getAccessProjectionOperand() instead of using special cases once +// the unchecked_take_enum_data_addr -> load -> project_box pattern is fixed. +AccessPathDefUseTraversal::UseKind +AccessPathDefUseTraversal::visitSingleValueUser(SingleValueInstruction *svi, + DFSEntry dfs) { + if (dfs.isRef()) { + if (isRCIdentityPreservingCast(svi)) { + pushUsers(svi, dfs); + return IgnoredUse; + } + // 'svi' will be processed below as either RefElementAddrInst, + // RefTailAddrInst, or some unknown LeafUse. + } else if (isAccessedStorageCast(svi)) { + pushUsers(svi, dfs); + return IgnoredUse; } -}; + switch (svi->getKind()) { + default: + return LeafUse; -struct IdentifyAccessedStorageVisitor - : public FindAccessedStorageVisitorBase {}; + case SILInstructionKind::BeginAccessInst: + if (visitor.nestedAccessTy == NestedAccessType::StopAtAccessBegin) { + return LeafUse; + } + pushUsers(svi, dfs); + return IgnoredUse; + + // Handle ref_element_addr since we start at the object root instead of + // the access base. + case SILInstructionKind::RefElementAddrInst: + assert(dfs.isRef()); + assert(dfs.pathCursor > 0 && "ref_element_addr cannot occur within access"); + dfs.useAndIsRef.setInt(false); + followProjection(svi, dfs); + return IgnoredUse; + + case SILInstructionKind::RefTailAddrInst: { + assert(dfs.isRef()); + assert(dfs.pathCursor > 0 && "ref_tail_addr cannot occur within an access"); + dfs.useAndIsRef.setInt(false); + --dfs.pathCursor; + AccessPath::Index pathIndex = pathIndices[dfs.pathCursor]; + assert(pathIndex.isSubObjectProjection()); + if (pathIndex.getSubObjectIndex() == AccessedStorage::TailIndex) + pushUsers(svi, dfs); + + return IgnoredUse; + } -} // namespace + // MARK: Access projections + + case SILInstructionKind::StructElementAddrInst: + case SILInstructionKind::TupleElementAddrInst: + followProjection(svi, dfs); + return IgnoredUse; + + case SILInstructionKind::IndexAddrInst: + case SILInstructionKind::TailAddrInst: { + auto projIdx = ProjectionIndex(svi); + if (projIdx.isValid()) { + if (dfs.offset != AccessPath::UnknownOffset) + dfs.offset += projIdx.Index; + else + assert(visitor.findOverlappingUses()); + } else if (visitor.findOverlappingUses()) { + dfs.offset = AccessPath::UnknownOffset; + } else { + return IgnoredUse; + } + pushUsers(svi, dfs); + return IgnoredUse; + } -AccessedStorage swift::findAccessedStorage(SILValue sourceAddr) { - return FindAccessedStorageVisitor().findStorage(sourceAddr); + // open_existential_addr and unchecked_take_enum_data_addr are classified as + // access projections, but they also modify memory. Both see through them and + // also report them as uses. + case SILInstructionKind::OpenExistentialAddrInst: + case SILInstructionKind::UncheckedTakeEnumDataAddrInst: + pushUsers(svi, dfs); + return LeafUse; + + case SILInstructionKind::StructExtractInst: + // Handle nested access to a KeyPath projection. The projection itself + // uses a Builtin. However, the returned UnsafeMutablePointer may be + // converted to an address and accessed via an inout argument. + if (isUnsafePointerExtraction(cast(svi))) { + pushUsers(svi, dfs); + return IgnoredUse; + } + return LeafUse; + + case SILInstructionKind::LoadInst: + // Load a box from an indirect payload of an opaque enum. See comments + // in AccessUseDefChainVisitor::visit. Record this load as a leaf-use even + // when we look through its project_box because anyone inspecting the load + // itself will see the same AccessPath. + // FIXME: if this doesn't go away with opaque values, add a new instruction + // for load+project_box. + if (svi->getType().is()) { + Operand *addrOper = &cast(svi)->getOperandRef(); + assert(isa(addrOper->get())); + // Push the project_box uses + for (auto *use : svi->getUses()) { + if (isa(use->getUser())) + pushUser(DFSEntry(use, dfs.isRef(), dfs.pathCursor, dfs.offset)); + } + } + return LeafUse; + } } -AccessedStorage swift::identifyAccessedStorageImpl(SILValue sourceAddr) { - return IdentifyAccessedStorageVisitor().findStorage(sourceAddr); +bool AccessPathDefUseTraversal::visitUser(DFSEntry dfs) { + Operand *use = dfs.getUse(); + assert(!(dfs.isRef() && use->get()->getType().isAddress())); + if (auto *svi = dyn_cast(use->getUser())) { + if (use->getOperandNumber() == 0 + && visitSingleValueUser(svi, dfs) == IgnoredUse) { + return true; + } + } + // We weren't able to "see through" any more address conversions; so + // record this as a use. + + // Do the path offsets match? + if (!checkAndUpdateOffset(dfs)) + return true; + + // Is this a partial path match? + if (dfs.pathCursor > 0 || dfs.offset == AccessPath::UnknownOffset) { + return visitor.visitOverlappingUse(use); + } + if (dfs.pathCursor < 0) { + return visitor.visitInnerUse(use); + } + return visitor.visitExactUse(use); +} + +bool swift::visitAccessPathUses(AccessUseVisitor &visitor, + AccessPath accessPath, SILFunction *function) { + return AccessPathDefUseTraversal(visitor, accessPath, function).visitUses(); +} + +bool swift::visitAccessedStorageUses(AccessUseVisitor &visitor, + AccessedStorage storage, + SILFunction *function) { + IndexTrieNode *emptyPath = function->getModule().getIndexTrieRoot(); + return visitAccessPathUses(visitor, AccessPath(storage, emptyPath, 0), + function); +} + +class CollectAccessPathUses : public AccessUseVisitor { + // Result: Exact uses, projection uses, and containing uses. + SmallVectorImpl &uses; + + unsigned useLimit; + +public: + CollectAccessPathUses(SmallVectorImpl &uses, AccessUseType useTy, + unsigned useLimit) + : AccessUseVisitor(useTy, NestedAccessType::IgnoreAccessBegin), uses(uses), + useLimit(useLimit) {} + + bool visitUse(Operand *use, AccessUseType useTy) { + if (uses.size() == useLimit) { + return false; + } + uses.push_back(use); + return true; + } +}; + +bool AccessPath::collectUses(SmallVectorImpl &uses, + AccessUseType useTy, SILFunction *function, + unsigned useLimit) const { + CollectAccessPathUses collector(uses, useTy, useLimit); + return visitAccessPathUses(collector, *this, function); } //===----------------------------------------------------------------------===// -// MARK: Helper API +// MARK: Helper API for specific formal access patterns //===----------------------------------------------------------------------===// static bool isScratchBuffer(SILValue value) { @@ -556,7 +1631,9 @@ bool swift::isExternalGlobalAddressor(ApplyInst *AI) { // Return true if the given StructExtractInst extracts the RawPointer from // Unsafe[Mutable]Pointer. bool swift::isUnsafePointerExtraction(StructExtractInst *SEI) { - assert(isa(SEI->getType().getASTType())); + if (!isa(SEI->getType().getASTType())) + return false; + auto &C = SEI->getModule().getASTContext(); auto *decl = SEI->getStructDecl(); return decl == C.getUnsafeMutablePointerDecl() @@ -626,7 +1703,7 @@ bool swift::isPossibleFormalAccessBase(const AccessedStorage &storage, // Additional checks that apply to anything that may fall through. // Immutable values are only accessed for initialization. - if (storage.isLetAccess(F)) + if (storage.isLetAccess()) return false; return true; @@ -649,12 +1726,12 @@ SILBasicBlock::iterator swift::removeBeginAccess(BeginAccessInst *beginAccess) { } //===----------------------------------------------------------------------===// -// Verification +// MARK: Verification //===----------------------------------------------------------------------===// -/// Helper for visitApplyAccesses that visits address-type call arguments, -/// including arguments to @noescape functions that are passed as closures to -/// the current call. +// 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()) { @@ -686,14 +1763,27 @@ static void visitBuiltinAddress(BuiltinInst *builtin, builtin->dump(); llvm_unreachable("unexpected builtin memory access."); - // WillThrow exists for the debugger, does nothing. + // Handle builtin "generic_add"($*V, $*V, $*V) and the like. +#define BUILTIN(Id, Name, Attrs) +#define BUILTIN_BINARY_OPERATION_POLYMORPHIC(Id, Name) \ + case BuiltinValueKind::Id: + +#include "swift/AST/Builtins.def" + + visitor(&builtin->getAllOperands()[1]); + visitor(&builtin->getAllOperands()[2]); + return; + + // WillThrow exists for the debugger, does nothing. case BuiltinValueKind::WillThrow: return; - // Buitins that affect memory but can't be formal accesses. + // Buitins that affect memory but can't be formal accesses. + case BuiltinValueKind::AssumeTrue: case BuiltinValueKind::UnexpectedError: case BuiltinValueKind::ErrorInMain: case BuiltinValueKind::IsOptionalType: + case BuiltinValueKind::CondFailMessage: case BuiltinValueKind::AllocRaw: case BuiltinValueKind::DeallocRaw: case BuiltinValueKind::Fence: @@ -703,14 +1793,15 @@ static void visitBuiltinAddress(BuiltinInst *builtin, case BuiltinValueKind::Unreachable: case BuiltinValueKind::CondUnreachable: case BuiltinValueKind::DestroyArray: - case BuiltinValueKind::COWBufferForReading: case BuiltinValueKind::UnsafeGuaranteed: case BuiltinValueKind::UnsafeGuaranteedEnd: case BuiltinValueKind::Swift3ImplicitObjCEntrypoint: + case BuiltinValueKind::PoundAssert: + case BuiltinValueKind::IntInstrprofIncrement: case BuiltinValueKind::TSanInoutAccess: return; - // General memory access to a pointer in first operand position. + // General memory access to a pointer in first operand position. case BuiltinValueKind::CmpXChg: case BuiltinValueKind::AtomicLoad: case BuiltinValueKind::AtomicStore: @@ -720,8 +1811,8 @@ static void visitBuiltinAddress(BuiltinInst *builtin, // visitor(&builtin->getAllOperands()[0]); return; - // Arrays: (T.Type, Builtin.RawPointer, Builtin.RawPointer, - // Builtin.Word) + // Arrays: (T.Type, Builtin.RawPointer, Builtin.RawPointer, + // Builtin.Word) case BuiltinValueKind::CopyArray: case BuiltinValueKind::TakeArrayNoAlias: case BuiltinValueKind::TakeArrayFrontToBack: @@ -811,11 +1902,11 @@ void swift::visitAccessedAddress(SILInstruction *I, visitor(&I->getAllOperands()[0]); return; + case SILInstructionKind::InitExistentialAddrInst: + case SILInstructionKind::InjectEnumAddrInst: #define NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ case SILInstructionKind::Load##Name##Inst: #include "swift/AST/ReferenceStorage.def" - case SILInstructionKind::InitExistentialAddrInst: - case SILInstructionKind::InjectEnumAddrInst: case SILInstructionKind::LoadInst: case SILInstructionKind::LoadBorrowInst: case SILInstructionKind::OpenExistentialAddrInst: @@ -845,6 +1936,8 @@ void swift::visitAccessedAddress(SILInstruction *I, case SILInstructionKind::BeginAccessInst: case SILInstructionKind::BeginApplyInst: case SILInstructionKind::BeginBorrowInst: + case SILInstructionKind::BeginCOWMutationInst: + case SILInstructionKind::EndCOWMutationInst: case SILInstructionKind::BeginUnpairedAccessInst: case SILInstructionKind::BindMemoryInst: case SILInstructionKind::CheckedCastValueBranchInst: @@ -863,6 +1956,7 @@ void swift::visitAccessedAddress(SILInstruction *I, case SILInstructionKind::EndLifetimeInst: case SILInstructionKind::ExistentialMetatypeInst: case SILInstructionKind::FixLifetimeInst: + case SILInstructionKind::GlobalAddrInst: case SILInstructionKind::InitExistentialValueInst: case SILInstructionKind::IsUniqueInst: case SILInstructionKind::IsEscapingClosureInst: diff --git a/lib/SIL/Utils/OwnershipUtils.cpp b/lib/SIL/Utils/OwnershipUtils.cpp index 7cd18ae2376e1..3e6457c77d98a 100644 --- a/lib/SIL/Utils/OwnershipUtils.cpp +++ b/lib/SIL/Utils/OwnershipUtils.cpp @@ -543,7 +543,7 @@ bool InteriorPointerOperand::getImplicitUses( isa(user) || isa(user) || isa(user) || isa(user) || isa(user) || - isa(user)) { + isa(user) || isa(user)) { continue; } @@ -551,8 +551,10 @@ bool InteriorPointerOperand::getImplicitUses( if (Projection::isAddressProjection(user) || isa(user) || isa(user) || - isa(user) || isa(user) || - isa(user) || isa(user)) { + isa(user) || + isa(user) || isa(user) || + isa(user) || isa(user) || + isa(user)) { for (SILValue r : user->getResults()) { llvm::copy(r->getUses(), std::back_inserter(worklist)); } diff --git a/lib/SIL/Utils/Projection.cpp b/lib/SIL/Utils/Projection.cpp index 31bebe00aa415..8c6b783c842cb 100644 --- a/lib/SIL/Utils/Projection.cpp +++ b/lib/SIL/Utils/Projection.cpp @@ -12,10 +12,11 @@ #define DEBUG_TYPE "sil-projection" #include "swift/SIL/Projection.h" +#include "swift/Basic/IndexTrie.h" #include "swift/Basic/NullablePtr.h" -#include "swift/SIL/SILBuilder.h" -#include "swift/SIL/InstructionUtils.h" #include "swift/SIL/DebugUtils.h" +#include "swift/SIL/InstructionUtils.h" +#include "swift/SIL/SILBuilder.h" #include "swift/SIL/SILUndef.h" #include "llvm/ADT/None.h" #include "llvm/Support/Debug.h" @@ -42,22 +43,27 @@ static_assert(std::is_standard_layout::value, /// Return true if IndexVal is a constant index representable as unsigned /// int. We do not support symbolic projections yet, only 32-bit unsigned /// integers. -bool swift::getIntegerIndex(SILValue IndexVal, unsigned &IndexConst) { - if (auto *IndexLiteral = dyn_cast(IndexVal)) { - APInt ConstInt = IndexLiteral->getValue(); - // IntegerLiterals are signed. - if (ConstInt.isIntN(32) && ConstInt.isNonNegative()) { - IndexConst = (unsigned)ConstInt.getSExtValue(); - return true; - } - } - return false; +bool swift::getIntegerIndex(SILValue IndexVal, int &IndexConst) { + auto *IndexLiteral = dyn_cast(IndexVal); + if (!IndexLiteral) + return false; + + APInt ConstInt = IndexLiteral->getValue(); + // Reserve 1 bit for encoding. See AccessPath::Index. + if (!ConstInt.isSignedIntN(31)) + return false; + + IndexConst = ConstInt.getSExtValue(); + assert(((IndexConst << 1) >> 1) == IndexConst); + return true; } //===----------------------------------------------------------------------===// // Projection //===----------------------------------------------------------------------===// +constexpr int ProjectionIndex::TailIndex; + Projection::Projection(SingleValueInstruction *I) : Value() { if (!I) return; @@ -70,23 +76,23 @@ Projection::Projection(SingleValueInstruction *I) : Value() { return; case SILInstructionKind::StructElementAddrInst: { auto *SEAI = cast(I); - Value = ValueTy(ProjectionKind::Struct, SEAI->getFieldNo()); + Value = ValueTy(ProjectionKind::Struct, SEAI->getFieldIndex()); assert(getKind() == ProjectionKind::Struct); - assert(getIndex() == SEAI->getFieldNo()); + assert(getIndex() == int(SEAI->getFieldIndex())); break; } case SILInstructionKind::StructExtractInst: { auto *SEI = cast(I); - Value = ValueTy(ProjectionKind::Struct, SEI->getFieldNo()); + Value = ValueTy(ProjectionKind::Struct, SEI->getFieldIndex()); assert(getKind() == ProjectionKind::Struct); - assert(getIndex() == SEI->getFieldNo()); + assert(getIndex() == int(SEI->getFieldIndex())); break; } case SILInstructionKind::RefElementAddrInst: { auto *REAI = cast(I); - Value = ValueTy(ProjectionKind::Class, REAI->getFieldNo()); + Value = ValueTy(ProjectionKind::Class, REAI->getFieldIndex()); assert(getKind() == ProjectionKind::Class); - assert(getIndex() == REAI->getFieldNo()); + assert(getIndex() == int(REAI->getFieldIndex())); break; } case SILInstructionKind::RefTailAddrInst: { @@ -106,30 +112,30 @@ Projection::Projection(SingleValueInstruction *I) : Value() { } case SILInstructionKind::TupleExtractInst: { auto *TEI = cast(I); - Value = ValueTy(ProjectionKind::Tuple, TEI->getFieldNo()); + Value = ValueTy(ProjectionKind::Tuple, TEI->getFieldIndex()); assert(getKind() == ProjectionKind::Tuple); - assert(getIndex() == TEI->getFieldNo()); + assert(getIndex() == int(TEI->getFieldIndex())); break; } case SILInstructionKind::TupleElementAddrInst: { auto *TEAI = cast(I); - Value = ValueTy(ProjectionKind::Tuple, TEAI->getFieldNo()); + Value = ValueTy(ProjectionKind::Tuple, TEAI->getFieldIndex()); assert(getKind() == ProjectionKind::Tuple); - assert(getIndex() == TEAI->getFieldNo()); + assert(getIndex() == int(TEAI->getFieldIndex())); break; } case SILInstructionKind::UncheckedEnumDataInst: { auto *UEDI = cast(I); Value = ValueTy(ProjectionKind::Enum, UEDI->getElementNo()); assert(getKind() == ProjectionKind::Enum); - assert(getIndex() == UEDI->getElementNo()); + assert(getIndex() == int(UEDI->getElementNo())); break; } case SILInstructionKind::UncheckedTakeEnumDataAddrInst: { auto *UTEDAI = cast(I); Value = ValueTy(ProjectionKind::Enum, UTEDAI->getElementNo()); assert(getKind() == ProjectionKind::Enum); - assert(getIndex() == UTEDAI->getElementNo()); + assert(getIndex() == int(UTEDAI->getElementNo())); break; } case SILInstructionKind::IndexAddrInst: { @@ -138,9 +144,10 @@ Projection::Projection(SingleValueInstruction *I) : Value() { // updated and a MaxLargeIndex will need to be used here. Currently we // represent large Indexes using a 64 bit integer, so we don't need to mess // with anything. - unsigned NewIndex = 0; + int NewIndex = 0; auto *IAI = cast(I); - if (getIntegerIndex(IAI->getIndex(), NewIndex)) { + // TODO: handle negative indices + if (getIntegerIndex(IAI->getIndex(), NewIndex) && NewIndex >= 0) { Value = ValueTy(ProjectionKind::Index, NewIndex); assert(getKind() == ProjectionKind::Index); assert(getIndex() == NewIndex); diff --git a/lib/SIL/Verifier/CMakeLists.txt b/lib/SIL/Verifier/CMakeLists.txt index b9e7fb1fb5910..244a34304a862 100644 --- a/lib/SIL/Verifier/CMakeLists.txt +++ b/lib/SIL/Verifier/CMakeLists.txt @@ -1,5 +1,5 @@ target_sources(swiftSIL PRIVATE - LoadBorrowInvalidationChecker.cpp + LoadBorrowImmutabilityChecker.cpp LinearLifetimeChecker.cpp MemoryLifetime.cpp SILOwnershipVerifier.cpp diff --git a/lib/SIL/Verifier/LoadBorrowImmutabilityChecker.cpp b/lib/SIL/Verifier/LoadBorrowImmutabilityChecker.cpp new file mode 100644 index 0000000000000..f9d78fc8012bb --- /dev/null +++ b/lib/SIL/Verifier/LoadBorrowImmutabilityChecker.cpp @@ -0,0 +1,369 @@ +//===--- LoadBorrowImmutabilityChecker.cpp --------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// +/// This file defines a verifier that exhaustively validates that there aren't +/// any load_borrows in a SIL module that have in-scope writes to their +/// underlying storage. +/// +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "sil-load-borrow-immutability-checker" +#include "VerifierPrivate.h" +#include "swift/Basic/Debug.h" +#include "swift/Basic/LLVM.h" +#include "swift/Basic/MultiMapCache.h" +#include "swift/SIL/BasicBlockUtils.h" +#include "swift/SIL/LinearLifetimeChecker.h" +#include "swift/SIL/MemAccessUtils.h" +#include "swift/SIL/OwnershipUtils.h" +#include "swift/SIL/Projection.h" +#include "swift/SIL/SILInstruction.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" + +using namespace swift; +using namespace swift::silverifier; + +//===----------------------------------------------------------------------===// +// Write Gatherer +//===----------------------------------------------------------------------===// + +namespace { + +// Visitor for visitAccessPathUses(). +class GatherWritesVisitor : public AccessUseVisitor { + // Result: writes to the AccessPath being visited. + SmallVectorImpl &writeAccumulator; + +public: + GatherWritesVisitor(SmallVectorImpl &writes) + : AccessUseVisitor(AccessUseType::Overlapping, + NestedAccessType::StopAtAccessBegin), + writeAccumulator(writes) {} + + bool visitUse(Operand *op, AccessUseType useTy); +}; + +// Functor for MultiMapCache construction. +struct GatherWrites { + const SILFunction *function; + GatherWrites(const SILFunction *function) : function(function) {} + + bool operator()(const AccessPath &accessPath, + SmallVectorImpl &writeAccumulator) { + GatherWritesVisitor visitor(writeAccumulator); + return visitAccessPathUses(visitor, accessPath, + const_cast(function)); + } +}; + +} // end anonymous namespace + +// Filter out recognized uses that do not write to memory. +// +// TODO: Ensure that all of the conditional-write logic below is encapsulated in +// mayWriteToMemory and just call that instead. Possibly add additional +// verification that visitAccessPathUses recognizes all instructions that may +// propagate pointers (even though they don't write). +bool GatherWritesVisitor::visitUse(Operand *op, AccessUseType useTy) { + // If this operand is for a dependent type, then it does not actually access + // the operand's address value. It only uses the metatype defined by the + // operation (e.g. open_existential). + if (op->isTypeDependent()) { + return true; + } + SILInstruction *user = op->getUser(); + if (isIncidentalUse(user)) { + return true; + } + switch (user->getKind()) { + + // Known reads... + case SILInstructionKind::LoadBorrowInst: + case SILInstructionKind::SelectEnumAddrInst: + case SILInstructionKind::SwitchEnumAddrInst: + case SILInstructionKind::DeallocStackInst: + case SILInstructionKind::DeallocBoxInst: + case SILInstructionKind::WitnessMethodInst: + case SILInstructionKind::ExistentialMetatypeInst: + return true; + + // Known writes... + case SILInstructionKind::DestroyAddrInst: + case SILInstructionKind::DestroyValueInst: + case SILInstructionKind::InjectEnumAddrInst: + case SILInstructionKind::StoreInst: + case SILInstructionKind::AssignInst: + case SILInstructionKind::UncheckedTakeEnumDataAddrInst: + case SILInstructionKind::MarkFunctionEscapeInst: + writeAccumulator.push_back(op); + return true; + + // Load/Store variations... +#define NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, name, NAME) \ + case SILInstructionKind::Load##Name##Inst: \ + if (cast(user)->isTake() == IsTake) { \ + writeAccumulator.push_back(op); \ + } \ + return true; \ + \ + case SILInstructionKind::Store##Name##Inst: \ + writeAccumulator.push_back(op); \ + return true; +#include "swift/AST/ReferenceStorage.def" + + // Ignored pointer uses... + + // Allow store_borrow within the load_borrow scope. + // FIXME: explain why. + case SILInstructionKind::StoreBorrowInst: + // Returns are never in scope. + case SILInstructionKind::ReturnInst: + return true; + + // Reads that may perform a "take"... + + case SILInstructionKind::LoadInst: + if (cast(user)->getOwnershipQualifier() + == LoadOwnershipQualifier::Take) { + writeAccumulator.push_back(op); + } + return true; + + case SILInstructionKind::UnconditionalCheckedCastAddrInst: + return true; + + case SILInstructionKind::CheckedCastAddrBranchInst: { + auto *ccbi = cast(user); + if (ccbi->getConsumptionKind() != CastConsumptionKind::CopyOnSuccess) { + writeAccumulator.push_back(op); + } + return true; + } + + // Conditional writes... + + case SILInstructionKind::CopyAddrInst: + if (cast(user)->getDest() == op->get()) { + writeAccumulator.push_back(op); + return true; + } + // This operand is the copy source. Check if it is taken. + if (cast(user)->isTakeOfSrc()) { + writeAccumulator.push_back(op); + } + return true; + + // If this value is dependent on another, conservatively consider it a write. + // + // FIXME: explain why a mark_dependence effectively writes to storage. + case SILInstructionKind::MarkDependenceInst: + if (cast(user)->getValue() == op->get()) { + writeAccumulator.push_back(op); + } + return true; + + // Check for mutable existentials. + case SILInstructionKind::OpenExistentialAddrInst: + if (cast(user)->getAccessKind() + != OpenedExistentialAccess::Immutable) { + writeAccumulator.push_back(op); + } + return true; + + case SILInstructionKind::BeginAccessInst: + if (cast(user)->getAccessKind() != SILAccessKind::Read) { + writeAccumulator.push_back(op); + } + return true; + + case SILInstructionKind::BuiltinInst: + if (!cast(user)->mayWriteToMemory()) { + return true; + } + writeAccumulator.push_back(op); + return true; + + case SILInstructionKind::YieldInst: { + SILYieldInfo info = cast(user)->getYieldInfoForOperand(*op); + if (info.isIndirectInGuaranteed()) { + return true; + } + if (info.isIndirectMutating() || info.isConsumed()) { + writeAccumulator.push_back(op); + return true; + } + break; // unknown yield convention + } + + default: + break; + } // end switch(user->getKind()) + + // If we have a FullApplySite, see if we use the value as an + // indirect_guaranteed parameter. If we use it as inout, we need + // interprocedural analysis that we do not perform here. + if (auto fas = FullApplySite::isa(user)) { + if (fas.isIndirectResultOperand(*op)) { + writeAccumulator.push_back(op); + return true; + } + auto argConv = fas.getArgumentConvention(*op); + + // A box or pointer value may be passed directly. Consider that a write. + if (!argConv.isIndirectConvention()) { + writeAccumulator.push_back(op); + return true; + } + if (argConv == SILArgumentConvention::Indirect_In_Guaranteed) { + return true; + } + if (argConv.isInoutConvention()) { + writeAccumulator.push_back(op); + return true; + } + if (argConv.isOwnedConvention()) { + writeAccumulator.push_back(op); + return true; + } + // Otherwise, be conservative and return that we had a write that we did + // not understand. + llvm::errs() << "Full apply site not understood: " << *user; + return false; + } + + // Handle a capture-by-address like a write. + if (auto as = ApplySite::isa(user)) { + writeAccumulator.push_back(op); + return true; + } + // We don't have an inclusive list of all use patterns for non-address + // values. References and pointers can be passed to almost anything that takes + // a value. We assume that visitAccessPathUses has already looked past + // operations that can propagate a reference or pointer, and simply check that + // the leaf use that it returned cannot itself write to memory. + if (!op->get()->getType().isAddress() && !user->mayWriteToMemory()) { + return true; + } + // If we did not recognize the user, just return conservatively that it was + // written to in a way we did not understand. + llvm::errs() << "Function: " << user->getFunction()->getName() << "\n"; + llvm::errs() << "Value: " << op->get(); + llvm::errs() << "Unknown instruction: " << *user; + llvm::report_fatal_error("Unexpected instruction using borrowed address?!"); + return false; +} + +//===----------------------------------------------------------------------===// +// Load Borrow Immutability Analysis +//===----------------------------------------------------------------------===// + +LoadBorrowImmutabilityAnalysis::LoadBorrowImmutabilityAnalysis( + DeadEndBlocks &deadEndBlocks, const SILFunction *f) + : cache(GatherWrites(f)), deadEndBlocks(deadEndBlocks) {} + +// \p address may be an address, pointer, or box type. +bool LoadBorrowImmutabilityAnalysis::isImmutableInScope( + LoadBorrowInst *lbi, ArrayRef endBorrowUses, + AccessPath accessPath) { + + SmallPtrSet visitedBlocks; + LinearLifetimeChecker checker(visitedBlocks, deadEndBlocks); + auto writes = cache.get(accessPath); + + // Treat None as a write. + if (!writes) { + llvm::errs() << "Failed to find cached writes for: "; + accessPath.getStorage().print(llvm::errs()); + return false; + } + // Then for each write... + for (auto *op : *writes) { + visitedBlocks.clear(); + + // First see if the write is a dead end block. In such a case, just skip it. + if (deadEndBlocks.isDeadEnd(op->getUser()->getParent())) { + continue; + } + // See if the write is within the load borrow's lifetime. If it isn't, we + // don't have to worry about it. + if (!checker.validateLifetime(lbi, endBorrowUses, op)) { + continue; + } + llvm::errs() << "Write: " << *op->getUser(); + return false; + } + // Ok, we are good. + return true; +} + +//===----------------------------------------------------------------------===// +// Top Level Entrypoint +//===----------------------------------------------------------------------===// + +bool LoadBorrowImmutabilityAnalysis::isImmutable(LoadBorrowInst *lbi) { + AccessPath accessPath = AccessPath::computeInScope(lbi->getOperand()); + // Bail on an invalid AccessPath. AccessPath completeness is verified + // independently--it may be invalid in extraordinary situations. When + // AccessPath is valid, we know all its uses are recognizable. + if (!accessPath.isValid()) { + return true; + } + // If we have a let address, then we are already done. + if (accessPath.getStorage().isLetAccess()) { + return true; + } + // At this point, we know that we /may/ have writes. Now we go through various + // cases to try and exhaustively identify if those writes overlap with our + // load_borrow. + SmallVector endBorrowUses; + transform(lbi->getUsersOfType(), + std::back_inserter(endBorrowUses), + [](EndBorrowInst *ebi) { return &ebi->getAllOperands()[0]; }); + + switch (accessPath.getStorage().getKind()) { + case AccessedStorage::Nested: { + // If we have a begin_access and... + auto *bai = cast(accessPath.getStorage().getValue()); + // We do not have a modify, assume we are correct. + if (bai->getAccessKind() != SILAccessKind::Modify) { + return true; + } + // Otherwise, validate that any writes to our begin_access is not when the + // load_borrow's result is live. + // + // TODO: As a separate analysis, verify that the load_borrow scope is always + // nested within the begin_access scope (to ensure no aliasing access). + return isImmutableInScope(lbi, endBorrowUses, accessPath); + } + case AccessedStorage::Argument: { + auto *arg = + cast(accessPath.getStorage().getArgument()); + if (arg->hasConvention(SILArgumentConvention::Indirect_In_Guaranteed)) { + return true; + } + return isImmutableInScope(lbi, endBorrowUses, accessPath); + } + // FIXME: A yielded address could overlap with another in this function. + case AccessedStorage::Yield: + case AccessedStorage::Stack: + case AccessedStorage::Box: + case AccessedStorage::Class: + case AccessedStorage::Tail: + case AccessedStorage::Global: + case AccessedStorage::Unidentified: + return isImmutableInScope(lbi, endBorrowUses, accessPath); + } + llvm_unreachable("Covered switch isn't covered?!"); +} diff --git a/lib/SIL/Verifier/LoadBorrowInvalidationChecker.cpp b/lib/SIL/Verifier/LoadBorrowInvalidationChecker.cpp deleted file mode 100644 index 1f5e84803d5b9..0000000000000 --- a/lib/SIL/Verifier/LoadBorrowInvalidationChecker.cpp +++ /dev/null @@ -1,573 +0,0 @@ -//===--- LoadBorrowInvalidationChecker.cpp --------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// -/// This file defines a verifier that exhaustively validates that there aren't -/// any load_borrows in a SIL module that are invalidated by a write to their -/// underlying storage. -/// -//===----------------------------------------------------------------------===// - -#define DEBUG_TYPE "sil-load-borrow-invalidation-checker" -#include "VerifierPrivate.h" -#include "swift/Basic/Debug.h" -#include "swift/Basic/LLVM.h" -#include "swift/Basic/MultiMapCache.h" -#include "swift/SIL/BasicBlockUtils.h" -#include "swift/SIL/LinearLifetimeChecker.h" -#include "swift/SIL/MemAccessUtils.h" -#include "swift/SIL/OwnershipUtils.h" -#include "swift/SIL/Projection.h" -#include "swift/SIL/SILInstruction.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/ErrorHandling.h" - -using namespace swift; -using namespace swift::silverifier; - -//===----------------------------------------------------------------------===// -// Write Gatherer -//===----------------------------------------------------------------------===// - -static bool constructValuesForBuiltinKey( - Operand *op, BuiltinInst *bi, - SmallVectorImpl &wellBehavedWriteAccumulator) { - // If we definitely do not write to memory, just return true early. - if (!bi->mayWriteToMemory()) { - return true; - } - - // TODO: Should we make this an exhaustive list so that when new builtins are - // added, they need to actually update this code? - wellBehavedWriteAccumulator.push_back(op); - return true; -} - -/// Returns true if we were able to ascertain that either the initialValue has -/// no write uses or all of the write uses were writes that we could understand. -static bool -constructValuesForKey(SILValue initialValue, - SmallVectorImpl &wellBehavedWriteAccumulator) { - SmallVector worklist(initialValue->getUses()); - - while (!worklist.empty()) { - auto *op = worklist.pop_back_val(); - - // Skip type dependent operands. - if (op->isTypeDependent()) { - continue; - } - - SILInstruction *user = op->getUser(); - - if (Projection::isAddressProjection(user) || - isa(user)) { - for (SILValue r : user->getResults()) { - llvm::copy(r->getUses(), std::back_inserter(worklist)); - } - continue; - } - - if (auto *oeai = dyn_cast(user)) { - // Mutable access! - if (oeai->getAccessKind() != OpenedExistentialAccess::Immutable) { - wellBehavedWriteAccumulator.push_back(op); - } - - // Otherwise, look through it and continue. - llvm::copy(oeai->getUses(), std::back_inserter(worklist)); - continue; - } - - // Add any destroy_addrs to the resultAccumulator. - if (isa(user)) { - wellBehavedWriteAccumulator.push_back(op); - continue; - } - - // load_borrow, load_weak, load_unowned and incidental uses are fine as - // well. - if (isa(user) || isIncidentalUse(user)) { - continue; - } - - if (auto *mdi = dyn_cast(user)) { - if (mdi->getValue() == op->get()) { - wellBehavedWriteAccumulator.push_back(op); - } - continue; - } - - if (isa(user)) { - wellBehavedWriteAccumulator.push_back(op); - continue; - } - - // switch_enum_addr never writes to memory. - if (isa(user)) { - continue; - } - - if (auto *ccbi = dyn_cast(user)) { - if (ccbi->getConsumptionKind() == CastConsumptionKind::TakeAlways || - ccbi->getConsumptionKind() == CastConsumptionKind::TakeOnSuccess) { - wellBehavedWriteAccumulator.push_back(op); - continue; - } - } - - if (isa(user)) { - continue; - } - - // Skip store_borrow. - if (isa(user)) { - continue; - } - - // Look through immutable begin_access. - if (auto *bai = dyn_cast(user)) { - // If we do not have a read, mark this as a write. - if (bai->getAccessKind() != SILAccessKind::Read) { - wellBehavedWriteAccumulator.push_back(op); - } - - // Otherwise, add the users to the worklist and continue. - llvm::copy(bai->getUses(), std::back_inserter(worklist)); - continue; - } - - // If we have a load, we just need to mark the load [take] as a write. - if (auto *li = dyn_cast(user)) { - if (li->getOwnershipQualifier() == LoadOwnershipQualifier::Take) { - wellBehavedWriteAccumulator.push_back(op); - } - continue; - } - -#define NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, name, NAME) \ - if (auto *li = dyn_cast(user)) { \ - if (li->isTake() == IsTake) { \ - wellBehavedWriteAccumulator.push_back(op); \ - } \ - continue; \ - } \ - if (isa(user)) { \ - wellBehavedWriteAccumulator.push_back(op); \ - continue; \ - } -#include "swift/AST/ReferenceStorage.def" - - // If we have a FullApplySite, see if we use the value as an - // indirect_guaranteed parameter. If we use it as inout, we need - // interprocedural analysis that we do not perform here. - if (auto fas = FullApplySite::isa(user)) { - if (fas.isIndirectResultOperand(*op)) { - wellBehavedWriteAccumulator.push_back(op); - continue; - } - - auto argConv = fas.getArgumentConvention(*op); - - // We should have an indirect convention here. - if (!argConv.isIndirectConvention()) { - llvm::errs() << "Full apply site taking non-indirect operand: " - << *user; - return false; - } - - if (argConv == SILArgumentConvention::Indirect_In_Guaranteed) { - continue; - } - - if (argConv.isInoutConvention()) { - wellBehavedWriteAccumulator.push_back(op); - continue; - } - - if (argConv.isOwnedConvention()) { - wellBehavedWriteAccumulator.push_back(op); - continue; - } - - // Otherwise, be conservative and return that we had a write that we did - // not understand. - llvm::errs() << "Full apply site not understood: " << *user; - return false; - } - - if (auto as = ApplySite::isa(user)) { - wellBehavedWriteAccumulator.push_back(op); - continue; - } - - // Copy addr that read are just loads. - if (auto *cai = dyn_cast(user)) { - // If our value is the destination, this is a write. - if (cai->getDest() == op->get()) { - wellBehavedWriteAccumulator.push_back(op); - continue; - } - - // Ok, so we are Src by process of elimination. Make sure we are not being - // taken. - if (cai->isTakeOfSrc()) { - wellBehavedWriteAccumulator.push_back(op); - continue; - } - - // Otherwise, we are safe and can continue. - continue; - } - - if (isa(user) || isa(user)) { - wellBehavedWriteAccumulator.push_back(op); - continue; - } - - if (isa(user)) { - continue; - } - - if (isa(user)) { - continue; - } - - if (isa(user)) { - continue; - } - - // We consider address_to_pointer to be an escape from our system. The - // frontend must protect the uses of the load_borrow as appropriate in other - // ways (for instance by using a mark_dependence). - if (isa(user)) { - continue; - } - - if (auto *yi = dyn_cast(user)) { - auto info = yi->getYieldInfoForOperand(*op); - if (info.isIndirectInGuaranteed()) { - continue; - } - - if (info.isIndirectMutating() || info.isConsumed()) { - wellBehavedWriteAccumulator.push_back(op); - continue; - } - } - - // Existential metatype doesnt write to memory. - if (isa(user)) { - continue; - } - - // unconditional_checked_cast_addr does a take on its input memory. - if (isa(user)) { - wellBehavedWriteAccumulator.push_back(op); - continue; - } - - if (auto *ccabi = dyn_cast(user)) { - if (ccabi->getConsumptionKind() != CastConsumptionKind::CopyOnSuccess) { - wellBehavedWriteAccumulator.push_back(op); - } - continue; - } - - if (auto *bi = dyn_cast(user)) { - if (constructValuesForBuiltinKey(op, bi, wellBehavedWriteAccumulator)) { - continue; - } - } - - // If we did not recognize the user, just return conservatively that it was - // written to in a way we did not understand. - llvm::errs() << "Function: " << user->getFunction()->getName() << "\n"; - llvm::errs() << "Value: " << op->get(); - llvm::errs() << "Unknown instruction!: " << *user; - // llvm::report_fatal_error("Unable to handle instruction?!"); - return false; - } - - // Ok, we finished our worklist and this address is not being written to. - return true; -} - -//===----------------------------------------------------------------------===// -// Load Borrow Never Invalidated Analysis -//===----------------------------------------------------------------------===// - -LoadBorrowNeverInvalidatedAnalysis::LoadBorrowNeverInvalidatedAnalysis( - DeadEndBlocks &deadEndBlocks) - : cache(constructValuesForKey), deadEndBlocks(deadEndBlocks) {} - -bool LoadBorrowNeverInvalidatedAnalysis:: - doesAddressHaveWriteThatInvalidatesLoadBorrow( - LoadBorrowInst *lbi, ArrayRef endBorrowUses, - SILValue address) { - SmallPtrSet visitedBlocks; - LinearLifetimeChecker checker(visitedBlocks, deadEndBlocks); - auto writes = cache.get(address); - - // Treat None as a write. - if (!writes) { - llvm::errs() << "Failed to find cached writes for: " << *address; - return false; - } - - auto lbiProjPath = - ProjectionPath::getProjectionPath(address, lbi->getOperand()); - - // Then for each write... - for (auto *op : *writes) { - visitedBlocks.clear(); - - // First see if the write is a dead end block. In such a case, just skip it. - if (deadEndBlocks.isDeadEnd(op->getUser()->getParent())) { - continue; - } - - // See if the write is within the load borrow's lifetime. If it isn't, we - // don't have to worry about it. - if (!checker.validateLifetime(lbi, endBorrowUses, op)) { - continue; - } - - // Ok, we found a write that overlaps with our load_borrow. We now need to - // prove that the write is to an address that can not trivially alias our - // load_borrow. - // - // First we check if we were actually able to compute a projection path to - // our address from lbiProjPath. If not, we have to a - if (!lbiProjPath) { - llvm::errs() << "Couldn't find path root for load_borrow: " << *lbi; - return false; - } - - auto writePath = ProjectionPath::getProjectionPath(address, op->get()); - if (!writePath) { - llvm::errs() << "Couldn't find path root for write: " << *op->getUser(); - return false; - } - - // The symmetric difference of two projection paths consists of the parts of - // two projection paths that are unique to each of the two. Naturally, if - // such a thing exists we must have that the two values can not alias. - if (writePath->hasNonEmptySymmetricDifference(*lbiProjPath)) { - continue; - } - - llvm::errs() << "Write: " << *op->getUser(); - return false; - } - - // Ok, we are good. - return true; -} - -//===----------------------------------------------------------------------===// -// Top Level Entrypoint -//===----------------------------------------------------------------------===// - -bool LoadBorrowNeverInvalidatedAnalysis:: - doesBoxHaveWritesThatInvalidateLoadBorrow(LoadBorrowInst *lbi, - ArrayRef endBorrowUses, - SILValue originalBox) { - SILValue box = originalBox; - SmallVector otherProjBoxInsts; - SmallVector worklist; - - // First walk up use->def from our project_box's operand to the actual box. As - // we do the walk, we gather up any project_box that we see (for later write - // checking) and then if we are either a copy_value or a begin_borrow strip - // the value and continue. - // - // So by the end of this loop, the worklist will contain not the copy_value, - // begin_borrow that we stripped, but rather any uses of those copy_value, - // begin_borrow that we stripped. This is to make sure we find project_box - // from webs of copy_value, begin_borrow. - do { - for (auto *use : box->getUses()) { - auto *user = use->getUser(); - if (auto *pbi = dyn_cast(user)) { - otherProjBoxInsts.push_back(pbi); - continue; - } - - if (isa(user) || isa(user)) { - worklist.push_back(user); - } - } - - if (isa(box) || isa(box)) { - box = cast(box)->getOperand(0); - continue; - } - } while (false); - - // Now that we finished our walk and gathered up copy_value, begin_borrow, - // visit each of those instructions recursively def->use, looking through - // further copy_value, begin_borrow and stashing any project_box we see for - // later write checking. - while (!worklist.empty()) { - auto *inst = worklist.pop_back_val(); - for (SILValue result : inst->getResults()) { - for (auto *use : result->getUses()) { - auto *user = use->getUser(); - if (auto *pbi = dyn_cast(user)) { - otherProjBoxInsts.push_back(pbi); - continue; - } - - if (isa(user) || isa(user)) { - worklist.push_back(user); - } - } - } - } - - // Ok! We now know that we have all project_box from the "local phi" web of - // the alloc_box our project_box is from. Now check that none of those have - // simple aliasing writes when our load_borrow is live. - while (!otherProjBoxInsts.empty()) { - auto *otherProjBox = otherProjBoxInsts.pop_back_val(); - - if (doesAddressHaveWriteThatInvalidatesLoadBorrow(lbi, endBorrowUses, - otherProjBox)) { - return true; - } - } - - return false; -} - -bool LoadBorrowNeverInvalidatedAnalysis::isNeverInvalidated( - LoadBorrowInst *lbi) { - SILValue address = getAddressAccess(lbi->getOperand()); - if (!address) - return false; - - // If we have a let address, then we are already done. - if (isLetAddress(stripAccessMarkers(address))) - return true; - - // At this point, we know that we /may/ have writes. Now we go through various - // cases to try and exhaustively identify if those writes overlap with our - // load_borrow. - SmallVector endBorrowUses; - transform(lbi->getUsersOfType(), - std::back_inserter(endBorrowUses), - [](EndBorrowInst *ebi) { return &ebi->getAllOperands()[0]; }); - - // If we have a begin_access and... - if (auto *bai = dyn_cast(address)) { - // We do not have a modify, assume we are correct. - if (bai->getAccessKind() != SILAccessKind::Modify) { - return true; - } - - // Otherwise, validate that any writes to our begin_access is not when the - // load_borrow's result is live. - return doesAddressHaveWriteThatInvalidatesLoadBorrow(lbi, endBorrowUses, - bai); - } - - // Check if our unidentified access storage is a project_box. In such a case, - // validate that all uses of the project_box are not writes that overlap with - // our load_borrow's result. These are things that may not be a formal access - // base. - // - // FIXME: we don't seem to verify anywhere that a pointer_to_address cannot - // itself be derived from another address that is accessible in the same - // function, either because it was returned from a call or directly - // address_to_pointer'd. - if (isa(address)) { - return doesAddressHaveWriteThatInvalidatesLoadBorrow(lbi, endBorrowUses, - address); - } - - // If we have a project_box, we need to see if our base, modulo begin_borrow, - // copy_value have any other project_box that we need to analyze. - if (auto *pbi = dyn_cast(address)) { - return doesBoxHaveWritesThatInvalidateLoadBorrow(lbi, endBorrowUses, - pbi->getOperand()); - } - - auto storage = findAccessedStorage(address); - - // If we couldn't find an access storage, return that we are assumed to write. - if (!storage) { - llvm::errs() << "Couldn't compute access storage?!\n"; - return false; - } - - switch (storage.getKind()) { - case AccessedStorage::Stack: { - // For now assume that stack is safe. - return doesAddressHaveWriteThatInvalidatesLoadBorrow(lbi, endBorrowUses, - address); - } - case AccessedStorage::Argument: { - auto *arg = cast(storage.getArgument()); - // We return false if visit a non-address here. Object args are things like - // pointer_to_address that we handle earlier. - if (!arg->getType().isAddress()) - return false; - if (arg->hasConvention(SILArgumentConvention::Indirect_In_Guaranteed)) - return true; - return doesAddressHaveWriteThatInvalidatesLoadBorrow(lbi, endBorrowUses, - arg); - } - case AccessedStorage::Yield: { - // For now, do this. Otherwise, check for in_guaranteed, etc. - // - // FIXME: The yielded address could overlap with another address in this - // function. - return true; - } - case AccessedStorage::Box: { - return doesAddressHaveWriteThatInvalidatesLoadBorrow(lbi, endBorrowUses, - address); - } - case AccessedStorage::Class: { - // Check that the write to the class's memory doesn't overlap with our - // load_borrow. - // - // FIXME: how do we know that other projections of the same object don't - // occur within the same function? - return doesAddressHaveWriteThatInvalidatesLoadBorrow(lbi, endBorrowUses, - address); - } - case AccessedStorage::Tail: { - // This should be as strong as the Class address case, but to handle it we - // need to find all aliases of the object and all tail projections within - // that object. - return false; - } - case AccessedStorage::Global: { - // Check that the write to the class's memory doesn't overlap with our - // load_borrow. - return doesAddressHaveWriteThatInvalidatesLoadBorrow(lbi, endBorrowUses, - address); - } - case AccessedStorage::Unidentified: { - // Otherwise, we didn't understand this, so bail. - llvm::errs() << "Unidentified access storage: " << storage; - return false; - } - case AccessedStorage::Nested: { - llvm_unreachable("Should have been handled above"); - } - } - llvm_unreachable("Covered switch isn't covered?!"); -} diff --git a/lib/SIL/Verifier/MemoryLifetime.cpp b/lib/SIL/Verifier/MemoryLifetime.cpp index 0ee60934e8265..25c31c55bfc97 100644 --- a/lib/SIL/Verifier/MemoryLifetime.cpp +++ b/lib/SIL/Verifier/MemoryLifetime.cpp @@ -261,14 +261,14 @@ bool MemoryLocations::analyzeLocationUsesRecursively(SILValue V, unsigned locIdx switch (user->getKind()) { case SILInstructionKind::StructElementAddrInst: { auto SEAI = cast(user); - if (!analyzeAddrProjection(SEAI, locIdx, SEAI->getFieldNo(), + if (!analyzeAddrProjection(SEAI, locIdx, SEAI->getFieldIndex(), collectedVals, subLocationMap)) return false; break; } case SILInstructionKind::TupleElementAddrInst: { auto *TEAI = cast(user); - if (!analyzeAddrProjection(TEAI, locIdx, TEAI->getFieldNo(), + if (!analyzeAddrProjection(TEAI, locIdx, TEAI->getFieldIndex(), collectedVals, subLocationMap)) return false; break; diff --git a/lib/SIL/Verifier/SILOwnershipVerifier.cpp b/lib/SIL/Verifier/SILOwnershipVerifier.cpp index 8490e003d920a..c738568f4ca15 100644 --- a/lib/SIL/Verifier/SILOwnershipVerifier.cpp +++ b/lib/SIL/Verifier/SILOwnershipVerifier.cpp @@ -13,6 +13,7 @@ #define DEBUG_TYPE "sil-ownership-verifier" #include "LinearLifetimeCheckerPrivate.h" + #include "swift/AST/ASTContext.h" #include "swift/AST/AnyFunctionRef.h" #include "swift/AST/Decl.h" @@ -32,16 +33,20 @@ #include "swift/SIL/SILBuiltinVisitor.h" #include "swift/SIL/SILDebugScope.h" #include "swift/SIL/SILFunction.h" +#include "swift/SIL/SILInstruction.h" #include "swift/SIL/SILModule.h" #include "swift/SIL/SILOpenedArchetypesTracker.h" #include "swift/SIL/SILVTable.h" #include "swift/SIL/SILVisitor.h" #include "swift/SIL/TypeLowering.h" + #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/PostOrderIterator.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringSet.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" + #include using namespace swift; @@ -130,10 +135,11 @@ class SILValueOwnershipChecker { bool gatherNonGuaranteedUsers(SmallVectorImpl &lifetimeEndingUsers, SmallVectorImpl ®ularUsers); - bool checkValueWithoutLifetimeEndingUses(); + bool checkValueWithoutLifetimeEndingUses(ArrayRef regularUsers); bool checkFunctionArgWithoutLifetimeEndingUses(SILFunctionArgument *arg); - bool checkYieldWithoutLifetimeEndingUses(BeginApplyResult *yield); + bool checkYieldWithoutLifetimeEndingUses(BeginApplyResult *yield, + ArrayRef regularUsers); bool isGuaranteedFunctionArgWithLifetimeEndingUses( SILFunctionArgument *arg, @@ -385,7 +391,7 @@ bool SILValueOwnershipChecker::gatherUsers( llvm::errs() << "Could not recognize address user of interior " "pointer operand!\n" << "Interior Pointer Operand: " - << interiorPointerOperand->operand->getUser() + << *interiorPointerOperand->operand->getUser() << "Address User: " << *op->getUser(); }); }; @@ -501,25 +507,56 @@ bool SILValueOwnershipChecker::checkFunctionArgWithoutLifetimeEndingUses( } bool SILValueOwnershipChecker::checkYieldWithoutLifetimeEndingUses( - BeginApplyResult *yield) { + BeginApplyResult *yield, ArrayRef regularUses) { switch (yield->getOwnershipKind()) { - case ValueOwnershipKind::Guaranteed: case ValueOwnershipKind::Unowned: case ValueOwnershipKind::None: return true; case ValueOwnershipKind::Owned: + if (deadEndBlocks.isDeadEnd(yield->getParent()->getParent())) + return true; + + return !errorBuilder.handleMalformedSIL([&] { + llvm::errs() << "Owned yield without life ending uses!\n" + << "Value: " << *yield << '\n'; + }); + case ValueOwnershipKind::Guaranteed: + // NOTE: If we returned false here, we would catch any error caught below as + // an out of lifetime use of the yielded value. That being said, that would + // be confusing from a code perspective since we would be validating + // something that did not have a /real/ lifetime ending use (one could + // consider the end_apply to be a pseudo-lifetime ending uses) along a code + // path that is explicitly trying to do that. break; } - if (deadEndBlocks.isDeadEnd(yield->getParent()->getParent())) + // If we have a guaranteed value, make sure that all uses are before our + // end_yield. + SmallVector coroutineEndUses; + for (auto *use : yield->getParent()->getTokenResult()->getUses()) { + coroutineEndUses.push_back(use); + } + + assert(visitedBlocks.empty()); + LinearLifetimeChecker checker(visitedBlocks, deadEndBlocks); + auto linearLifetimeResult = + checker.checkValue(yield, coroutineEndUses, regularUses, errorBuilder); + if (linearLifetimeResult.getFoundError()) { + // We return true here even if we find an error since we want to only emit + // this error for the value rather than continue and go down the "has + // consuming use" path. This is to work around any confusion that maybe + // caused by end_apply/abort_apply acting as a pseudo-ending lifetime use. + result = true; return true; + } - return !errorBuilder.handleMalformedSIL([&] { - llvm::errs() << "Owned yield without life ending uses!\n" - << "Value: " << *yield << '\n'; - }); + // Otherwise, we do not set result to have a value and return since all of our + // guaranteed value's uses are appropriate. + return true; } -bool SILValueOwnershipChecker::checkValueWithoutLifetimeEndingUses() { + +bool SILValueOwnershipChecker::checkValueWithoutLifetimeEndingUses( + ArrayRef regularUses) { LLVM_DEBUG(llvm::dbgs() << "No lifetime ending users?! Bailing early.\n"); if (auto *arg = dyn_cast(value)) { if (checkFunctionArgWithoutLifetimeEndingUses(arg)) { @@ -528,9 +565,7 @@ bool SILValueOwnershipChecker::checkValueWithoutLifetimeEndingUses() { } if (auto *yield = dyn_cast(value)) { - if (checkYieldWithoutLifetimeEndingUses(yield)) { - return true; - } + return checkYieldWithoutLifetimeEndingUses(yield, regularUses); } // Check if we are a guaranteed subobject. In such a case, we should never @@ -622,6 +657,7 @@ bool SILValueOwnershipChecker::checkUses() { // 1. A trivial typed value. // 2. An address type value. // 3. A guaranteed function argument. + // 4. A yielded guaranteed value. // // In the first two cases, it is easy to see that there is nothing further to // do but return false. @@ -630,8 +666,13 @@ bool SILValueOwnershipChecker::checkUses() { // more. Specifically, we should have /no/ lifetime ending uses of a // guaranteed function argument, since a guaranteed function argument should // outlive the current function always. - if (lifetimeEndingUsers.empty() && checkValueWithoutLifetimeEndingUses()) { - return false; + // + // In the case of a yielded guaranteed value, we need to validate that all + // regular uses of the value are within the co + if (lifetimeEndingUsers.empty()) { + if (checkValueWithoutLifetimeEndingUses(regularUsers)) + return false; + return true; } LLVM_DEBUG(llvm::dbgs() << " Found lifetime ending users! Performing " diff --git a/lib/SIL/Verifier/SILVerifier.cpp b/lib/SIL/Verifier/SILVerifier.cpp index 43c753403cae8..97e5b6a30645c 100644 --- a/lib/SIL/Verifier/SILVerifier.cpp +++ b/lib/SIL/Verifier/SILVerifier.cpp @@ -669,9 +669,9 @@ class SILVerifier : public SILVerifierBase { // Used for dominance checking within a basic block. llvm::DenseMap InstNumbers; - + DeadEndBlocks DEBlocks; - LoadBorrowNeverInvalidatedAnalysis loadBorrowNeverInvalidatedAnalysis; + LoadBorrowImmutabilityAnalysis loadBorrowImmutabilityAnalysis; bool SingleFunction = true; SILVerifier(const SILVerifier&) = delete; @@ -870,7 +870,7 @@ class SILVerifier : public SILVerifierBase { fnConv(F.getConventionsInContext()), TC(F.getModule().Types), OpenedArchetypes(&F), Dominance(nullptr), InstNumbers(numInstsInFunction(F)), DEBlocks(&F), - loadBorrowNeverInvalidatedAnalysis(DEBlocks), + loadBorrowImmutabilityAnalysis(DEBlocks, &F), SingleFunction(SingleFunction) { if (F.isExternalDeclaration()) return; @@ -1398,9 +1398,9 @@ class SILVerifier : public SILVerifierBase { if (isa(V)) { require(hasDynamicSelf, "dynamic self operand without dynamic self type"); - require(AI->getFunction()->hasSelfMetadataParam(), + require(AI->getFunction()->hasDynamicSelfMetadata(), "self metadata operand in function without self metadata param"); - require((ValueBase *)V == AI->getFunction()->getSelfMetadataArgument(), + require((ValueBase *)V == AI->getFunction()->getDynamicSelfMetadata(), "wrong self metadata operand"); } else { require(isa(V), @@ -1894,7 +1894,7 @@ class SILVerifier : public SILVerifierBase { requireSameType(LBI->getOperand()->getType().getObjectType(), LBI->getType(), "Load operand type and result type mismatch"); - require(loadBorrowNeverInvalidatedAnalysis.isNeverInvalidated(LBI), + require(loadBorrowImmutabilityAnalysis.isImmutable(LBI), "Found load borrow that is invalidated by a local write?!"); } @@ -2760,11 +2760,11 @@ class SILVerifier : public SILVerifierBase { require(EI->getType().isObject(), "result of tuple_extract must be object"); - require(EI->getFieldNo() < operandTy->getNumElements(), + require(EI->getFieldIndex() < operandTy->getNumElements(), "invalid field index for tuple_extract instruction"); if (EI->getModule().getStage() != SILStage::Lowered) { requireSameType(EI->getType().getASTType(), - operandTy.getElementType(EI->getFieldNo()), + operandTy.getElementType(EI->getFieldIndex()), "type of tuple_extract does not match type of element"); } } @@ -2806,12 +2806,12 @@ class SILVerifier : public SILVerifierBase { "must derive tuple_element_addr from tuple"); ArrayRef fields = operandTy.castTo()->getElements(); - require(EI->getFieldNo() < fields.size(), + require(EI->getFieldIndex() < fields.size(), "invalid field index for element_addr instruction"); if (EI->getModule().getStage() != SILStage::Lowered) { requireSameType( EI->getType().getASTType(), - CanType(fields[EI->getFieldNo()].getType()), + CanType(fields[EI->getFieldIndex()].getType()), "type of tuple_element_addr does not match type of element"); } } @@ -2869,7 +2869,7 @@ class SILVerifier : public SILVerifierBase { loweredFieldTy, EI->getType(), "result of ref_element_addr does not match type of field"); } - EI->getFieldNo(); // Make sure we can access the field without crashing. + EI->getFieldIndex(); // Make sure we can access the field without crashing. } void checkRefTailAddrInst(RefTailAddrInst *RTAI) { @@ -3604,6 +3604,15 @@ class SILVerifier : public SILVerifierBase { verifyOpenedArchetype(CI, CI->getType().getASTType()); } + // Make sure that opcodes handled by isRCIdentityPreservingCast cannot cast + // from a trivial to a reference type. Such a cast may dynamically + // instantiate a new reference-counted object. + void checkNoTrivialToReferenceCast(SingleValueInstruction *svi) { + require(!svi->getOperand(0)->getType().isTrivial(*svi->getFunction()) + || svi->getType().isTrivial(*svi->getFunction()), + "Unexpected trivial-to-reference conversion: "); + } + /// Verify if a given type is or contains an opened archetype or dynamic self. /// If this is the case, verify that the provided instruction has a type /// dependent operand for it. @@ -3619,10 +3628,10 @@ class SILVerifier : public SILVerifierBase { require(Def, "Opened archetype should be registered in SILFunction"); } else if (t->hasDynamicSelfType()) { require(I->getFunction()->hasSelfParam() || - I->getFunction()->hasSelfMetadataParam(), + I->getFunction()->hasDynamicSelfMetadata(), "Function containing dynamic self type must have self parameter"); - if (I->getFunction()->hasSelfMetadataParam()) - Def = I->getFunction()->getArguments().back(); + if (I->getFunction()->hasDynamicSelfMetadata()) + Def = I->getFunction()->getDynamicSelfMetadata(); else Def = I->getFunction()->getSelfArgument(); } else { @@ -3766,6 +3775,7 @@ class SILVerifier : public SILVerifierBase { void checkUpcastInst(UpcastInst *UI) { require(UI->getType() != UI->getOperand()->getType(), "can't upcast to same type"); + checkNoTrivialToReferenceCast(UI); if (UI->getType().is()) { CanType instTy(UI->getType().castTo()->getInstanceType()); require(UI->getOperand()->getType().is(), @@ -4761,6 +4771,72 @@ class SILVerifier : public SILVerifierBase { "Type of witness instruction does not match actual type of " "witnessed function"); } + + void checkGetAsyncContinuationInstBase(GetAsyncContinuationInstBase *GACI) { + auto resultTy = GACI->getType(); + auto &C = resultTy.getASTContext(); + auto resultBGT = resultTy.getAs(); + require(resultBGT, "Instruction type must be a continuation type"); + auto resultDecl = resultBGT->getDecl(); + require(resultDecl == C.getUnsafeContinuationDecl() + || resultDecl == C.getUnsafeThrowingContinuationDecl(), + "Instruction type must be a continuation type"); + } + + void checkGetAsyncContinuationInst(GetAsyncContinuationInst *GACI) { + checkGetAsyncContinuationInstBase(GACI); + } + + void checkGetAsyncContinuationAddrInst(GetAsyncContinuationAddrInst *GACI) { + checkGetAsyncContinuationInstBase(GACI); + + requireSameType(GACI->getOperand()->getType(), + GACI->getLoweredResumeType().getAddressType(), + "Operand type must match continuation resume type"); + } + + void checkAwaitAsyncContinuationInst(AwaitAsyncContinuationInst *AACI) { + // The operand must be a GetAsyncContinuation* instruction. + auto cont = dyn_cast(AACI->getOperand()); + require(cont, "can only await the result of a get_async_continuation instruction"); + bool isAddressForm = isa(cont); + + auto &C = cont->getType().getASTContext(); + + // The shape of the successors depends on the continuation instruction being + // awaited. + require((bool)AACI->getErrorBB() == cont->throws(), + "must have an error successor if and only if the continuation is throwing"); + if (cont->throws()) { + require(AACI->getErrorBB()->getNumArguments() == 1, + "error successor must take one argument"); + auto arg = AACI->getErrorBB()->getArgument(0); + auto errorType = C.getErrorDecl()->getDeclaredType()->getCanonicalType(); + requireSameType(arg->getType(), + SILType::getPrimitiveObjectType(errorType), + "error successor argument must have Error type"); + + if (AACI->getFunction()->hasOwnership()) { + require(arg->getOwnershipKind() == ValueOwnershipKind::Owned, + "error successor argument must be owned"); + } + } + if (isAddressForm) { + require(AACI->getResumeBB()->getNumArguments() == 0, + "resume successor must take no arguments for get_async_continuation_addr"); + } else { + require(AACI->getResumeBB()->getNumArguments() == 1, + "resume successor must take one argument for get_async_continuation"); + auto arg = AACI->getResumeBB()->getArgument(0); + + requireSameType(arg->getType(), cont->getLoweredResumeType(), + "resume successor must take an argument of the continuation resume type"); + if (AACI->getFunction()->hasOwnership()) { + require(arg->getOwnershipKind() == ValueOwnershipKind::Owned, + "resume successor argument must be owned"); + } + } + } // This verifies that the entry block of a SIL function doesn't have // any predecessors and also verifies the entry point arguments. @@ -4915,6 +4991,8 @@ class SILVerifier : public SILVerifierBase { std::set ActiveOps; CFGState CFG = Normal; + + GetAsyncContinuationInstBase *GotAsyncContinuation = nullptr; }; }; @@ -4922,6 +5000,8 @@ class SILVerifier : public SILVerifierBase { /// /// - stack allocations and deallocations must obey a stack discipline /// - accesses must be uniquely ended + /// - async continuations must be awaited before getting the continuation again, suspending + /// the task, or exiting the function /// - flow-sensitive states must be equivalent on all paths into a block void verifyFlowSensitiveRules(SILFunction *F) { // Do a traversal of the basic blocks. @@ -4937,6 +5017,20 @@ class SILVerifier : public SILVerifierBase { for (SILInstruction &i : *BB) { CurInstruction = &i; + if (i.maySuspend()) { + // Instructions that may suspend an async context must not happen + // while the continuation is being accessed, with the exception of + // the AwaitAsyncContinuationInst that completes suspending the task. + if (auto aaci = dyn_cast(&i)) { + require(state.GotAsyncContinuation == aaci->getOperand(), + "encountered await_async_continuation that doesn't match active gotten continuation"); + state.GotAsyncContinuation = nullptr; + } else { + require(!state.GotAsyncContinuation, + "cannot suspend async task while unawaited continuation is active"); + } + } + if (i.isAllocatingStack()) { state.Stack.push_back(cast(&i)); @@ -4959,13 +5053,18 @@ class SILVerifier : public SILVerifierBase { bool present = state.ActiveOps.erase(beginOp); require(present, "operation has already been ended"); } - + } else if (auto gaci = dyn_cast(&i)) { + require(!state.GotAsyncContinuation, + "get_async_continuation while unawaited continuation is already active"); + state.GotAsyncContinuation = gaci; } else if (auto term = dyn_cast(&i)) { if (term->isFunctionExiting()) { require(state.Stack.empty(), "return with stack allocs that haven't been deallocated"); require(state.ActiveOps.empty(), "return with operations still active"); + require(!state.GotAsyncContinuation, + "return with unawaited async continuation"); if (isa(term)) { require(state.CFG == VerifyFlowSensitiveRulesDetails::YieldUnwind, @@ -4983,12 +5082,14 @@ class SILVerifier : public SILVerifierBase { } } } - + if (isa(term)) { require(state.CFG != VerifyFlowSensitiveRulesDetails::YieldOnceResume, "encountered multiple 'yield's along single path"); require(state.CFG == VerifyFlowSensitiveRulesDetails::Normal, "encountered 'yield' on abnormal CFG path"); + require(!state.GotAsyncContinuation, + "encountered 'yield' while an unawaited continuation is active"); } auto successors = term->getSuccessors(); @@ -5050,6 +5151,8 @@ class SILVerifier : public SILVerifierBase { "inconsistent active-operations sets entering basic block"); require(state.CFG == foundState.CFG, "inconsistent coroutine states entering basic block"); + require(state.GotAsyncContinuation == foundState.GotAsyncContinuation, + "inconsistent active async continuations entering basic block"); } } } diff --git a/lib/SIL/Verifier/VerifierPrivate.h b/lib/SIL/Verifier/VerifierPrivate.h index 8d3d912acccb6..29fdccc569f63 100644 --- a/lib/SIL/Verifier/VerifierPrivate.h +++ b/lib/SIL/Verifier/VerifierPrivate.h @@ -14,6 +14,7 @@ #define SWIFT_SIL_VERIFIER_VERIFIERPRIVATE_H #include "swift/Basic/MultiMapCache.h" +#include "swift/SIL/MemAccessUtils.h" #include "swift/SIL/SILValue.h" namespace swift { @@ -25,22 +26,22 @@ class Operand; namespace silverifier { -class LoadBorrowNeverInvalidatedAnalysis { - SmallMultiMapCache cache; +class LoadBorrowImmutabilityAnalysis { + SmallMultiMapCache cache; DeadEndBlocks &deadEndBlocks; public: - LoadBorrowNeverInvalidatedAnalysis(DeadEndBlocks &deadEndBlocks); + LoadBorrowImmutabilityAnalysis(DeadEndBlocks &deadEndBlocks, + const SILFunction *f); /// Returns true if exhaustively lbi is guaranteed to never be invalidated by /// local writes. - bool isNeverInvalidated(LoadBorrowInst *lbi); + bool isImmutable(LoadBorrowInst *lbi); private: - bool doesAddressHaveWriteThatInvalidatesLoadBorrow( - LoadBorrowInst *lbi, ArrayRef endBorrowUses, SILValue address); - bool doesBoxHaveWritesThatInvalidateLoadBorrow( - LoadBorrowInst *lbi, ArrayRef endBorrowUses, SILValue box); + bool isImmutableInScope(LoadBorrowInst *lbi, + ArrayRef endBorrowUses, + AccessPath accessPath); }; } // namespace silverifier diff --git a/lib/SILGen/CMakeLists.txt b/lib/SILGen/CMakeLists.txt index c144b14bf69ee..c4daab77fd3e6 100644 --- a/lib/SILGen/CMakeLists.txt +++ b/lib/SILGen/CMakeLists.txt @@ -1,3 +1,4 @@ +set_swift_llvm_is_available() add_swift_host_library(swiftSILGen STATIC ArgumentSource.cpp Cleanup.cpp diff --git a/lib/SILGen/Cleanup.cpp b/lib/SILGen/Cleanup.cpp index 20eb0cc3c8e7e..868a0f1c6ecab 100644 --- a/lib/SILGen/Cleanup.cpp +++ b/lib/SILGen/Cleanup.cpp @@ -359,14 +359,16 @@ void CleanupStateRestorationScope::pop() && { popImpl(); } //===----------------------------------------------------------------------===// CleanupCloner::CleanupCloner(SILGenFunction &SGF, const ManagedValue &mv) - : SGF(SGF), hasCleanup(mv.hasCleanup()), isLValue(mv.isLValue()), - writebackBuffer(None) { + : SGF(SGF), writebackBuffer(None), hasCleanup(mv.hasCleanup()), + isLValue(mv.isLValue()), isFormalAccess(false) { if (hasCleanup) { auto handle = mv.getCleanup(); auto state = SGF.Cleanups.getFlagsAndWritebackBuffer(handle); - if (SILValue value = std::get<1>(state).getValueOr(SILValue())) { + using RawTy = std::underlying_type::type; + if (RawTy(std::get<0>(state)) & RawTy(Cleanup::Flags::FormalAccessCleanup)) + isFormalAccess = true; + if (SILValue value = std::get<1>(state).getValueOr(SILValue())) writebackBuffer = value; - } } } @@ -405,8 +407,16 @@ ManagedValue CleanupCloner::clone(SILValue value) const { } if (value->getType().isAddress()) { + if (isFormalAccess) { + auto loc = RegularLocation::getAutoGeneratedLocation(); + return SGF.emitFormalAccessManagedBufferWithCleanup(loc, value); + } return SGF.emitManagedBufferWithCleanup(value); } + if (isFormalAccess) { + auto loc = RegularLocation::getAutoGeneratedLocation(); + return SGF.emitFormalAccessManagedRValueWithCleanup(loc, value); + } return SGF.emitManagedRValueWithCleanup(value); } diff --git a/lib/SILGen/Cleanup.h b/lib/SILGen/Cleanup.h index cf8b98df606e3..e2361797d79d6 100644 --- a/lib/SILGen/Cleanup.h +++ b/lib/SILGen/Cleanup.h @@ -84,9 +84,9 @@ class LLVM_LIBRARY_VISIBILITY Cleanup { // // Example: Distinguishing in between @owned cleanups with a writeback buffer // (ExclusiveBorrowCleanup) or ones that involve formal access cleanups. - enum class Flags : uint8_t { + enum Flags : uint8_t { None = 0, - ExclusiveBorrowCleanup = 1, + FormalAccessCleanup = 1, }; private: @@ -95,7 +95,7 @@ class LLVM_LIBRARY_VISIBILITY Cleanup { Flags flags : 8; protected: - Cleanup() {} + Cleanup() : flags(Flags::None) {} virtual ~Cleanup() {} public: @@ -123,6 +123,14 @@ class LLVM_LIBRARY_VISIBILITY Cleanup { virtual bool getWritebackBuffer(function_ref func) { return false; } + + bool isFormalAccess() const { + return getFlags() & Flags::FormalAccessCleanup; + } + + void setIsFormalAccess() { + flags = Flags(flags | Flags::FormalAccessCleanup); + } }; /// A cleanup depth is generally used to denote the set of cleanups @@ -310,9 +318,10 @@ class CleanupStateRestorationScope { /// writeback buffers. class CleanupCloner { SILGenFunction &SGF; + Optional writebackBuffer; bool hasCleanup; bool isLValue; - Optional writebackBuffer; + bool isFormalAccess; public: CleanupCloner(SILGenFunction &SGF, const ManagedValue &mv); diff --git a/lib/SILGen/Initialization.h b/lib/SILGen/Initialization.h index 60ddc9c893a3f..022eab9b5f1bf 100644 --- a/lib/SILGen/Initialization.h +++ b/lib/SILGen/Initialization.h @@ -307,6 +307,39 @@ class TupleInitialization : public Initialization { void finishUninitialized(SILGenFunction &SGF) override; }; +/// A "null" initialization that indicates that any value being initialized +/// into this initialization should be discarded. This represents AnyPatterns +/// (that is, 'var (_)') that bind to values without storing them. +class BlackHoleInitialization : public Initialization { +public: + BlackHoleInitialization() {} + + bool canSplitIntoTupleElements() const override { + return true; + } + + MutableArrayRef + splitIntoTupleElements(SILGenFunction &SGF, SILLocation loc, + CanType type, + SmallVectorImpl &buf) override { + // "Destructure" an ignored binding into multiple ignored bindings. + for (auto fieldType : cast(type)->getElementTypes()) { + (void) fieldType; + buf.push_back(InitializationPtr(new BlackHoleInitialization())); + } + return buf; + } + + void copyOrInitValueInto(SILGenFunction &SGF, SILLocation loc, + ManagedValue value, bool isInit) override { + /// This just ignores the provided value. + } + + void finishUninitialized(SILGenFunction &SGF) override { + // do nothing + } +}; + } // end namespace Lowering } // end namespace swift diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index a077fc8a303eb..b6f8cd32ae4dc 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -1289,8 +1289,8 @@ bool SILGenModule::requiresIVarDestroyer(ClassDecl *cd) { // Only needed if we have non-trivial ivars, we're not a root class, and // the superclass is not @objc. return (hasNonTrivialIVars(cd) && - cd->getSuperclass() && - !cd->getSuperclass()->getClassOrBoundGenericClass()->hasClangNode()); + cd->getSuperclassDecl() && + !cd->getSuperclassDecl()->hasClangNode()); } /// TODO: This needs a better name. @@ -1989,3 +1989,57 @@ swift::performASTLowering(FileUnit &sf, Lowering::TypeConverter &tc, auto desc = ASTLoweringDescriptor::forFile(sf, tc, options); return llvm::cantFail(sf.getASTContext().evaluator(ASTLoweringRequest{desc})); } + +static void transferSpecializeAttributeTargets(SILGenModule &SGM, SILModule &M, + Decl *d) { + if (auto *asd = dyn_cast(d)) { + for (auto ad : asd->getAllAccessors()) { + transferSpecializeAttributeTargets(SGM, M, ad); + } + } else if (auto *vd = dyn_cast(d)) { + for (auto *A : vd->getAttrs().getAttributes()) { + auto *SA = cast(A); + // Filter _spi. + auto spiGroups = SA->getSPIGroups(); + auto hasSPIGroup = !spiGroups.empty(); + if (hasSPIGroup) { + if (vd->getModuleContext() != M.getSwiftModule() && + !M.getSwiftModule()->isImportedAsSPI(SA, vd)) { + continue; + } + } + if (auto *targetFunctionDecl = SA->getTargetFunctionDecl(vd)) { + auto target = SILDeclRef(targetFunctionDecl); + auto targetSILFunction = SGM.getFunction(target, NotForDefinition); + auto kind = SA->getSpecializationKind() == + SpecializeAttr::SpecializationKind::Full + ? SILSpecializeAttr::SpecializationKind::Full + : SILSpecializeAttr::SpecializationKind::Partial; + Identifier spiGroupIdent; + if (hasSPIGroup) { + spiGroupIdent = spiGroups[0]; + } + targetSILFunction->addSpecializeAttr(SILSpecializeAttr::create( + M, SA->getSpecializedSignature(), SA->isExported(), kind, nullptr, + spiGroupIdent, vd->getModuleContext())); + } + } + } +} + +void SILGenModule::visitImportDecl(ImportDecl *import) { + auto *module = import->getModule(); + if (module->isNonSwiftModule()) + return; + + SmallVector topLevelDecls; + module->getTopLevelDecls(topLevelDecls); + for (auto *t : topLevelDecls) { + if (auto *vd = dyn_cast(t)) { + transferSpecializeAttributeTargets(*this, M, vd); + } else if (auto *extension = dyn_cast(t)) { + for (auto *d : extension->getMembers()) + transferSpecializeAttributeTargets(*this, M, d); + } + } +} diff --git a/lib/SILGen/SILGen.h b/lib/SILGen/SILGen.h index 76708975f8919..37a0e7fdb11b8 100644 --- a/lib/SILGen/SILGen.h +++ b/lib/SILGen/SILGen.h @@ -237,7 +237,7 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor { // These are either not allowed at global scope or don't require // code emission. - void visitImportDecl(ImportDecl *d) {} + void visitImportDecl(ImportDecl *d); void visitEnumCaseDecl(EnumCaseDecl *d) {} void visitEnumElementDecl(EnumElementDecl *d) {} void visitOperatorDecl(OperatorDecl *d) {} diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index 65c44598af668..c65d95decd9f4 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -27,9 +27,9 @@ #include "swift/AST/ForeignErrorConvention.h" #include "swift/AST/GenericEnvironment.h" #include "swift/AST/GenericSignature.h" -#include "swift/AST/ParameterList.h" #include "swift/AST/Module.h" #include "swift/AST/ModuleLoader.h" +#include "swift/AST/ParameterList.h" #include "swift/AST/SubstitutionMap.h" #include "swift/Basic/ExternalUnion.h" #include "swift/Basic/Range.h" @@ -37,6 +37,7 @@ #include "swift/Basic/Unicode.h" #include "swift/SIL/PrettyStackTrace.h" #include "swift/SIL/SILArgument.h" +#include "clang/AST/DeclCXX.h" #include "llvm/Support/Compiler.h" using namespace swift; @@ -559,6 +560,11 @@ class Callee { result.foreignError = func->getForeignErrorConvention(); result.foreignSelf = func->getImportAsMemberStatus(); + // Remove the metatype "self" parameter by making this a static member. + if (constant->getDecl()->getClangDecl() && + isa(constant->getDecl()->getClangDecl())) + result.foreignSelf.setStatic(); + return result; } @@ -1786,6 +1792,139 @@ static bool hasUnownedInnerPointerResult(CanSILFunctionType fnType) { return false; } +//===----------------------------------------------------------------------===// +// Argument Emission for Builtin Initializer +//===----------------------------------------------------------------------===// +static inline PreparedArguments +buildBuiltinLiteralArgs(SILGenFunction &SGF, SGFContext C, + StringLiteralExpr *stringLiteral) { + return emitStringLiteral(SGF, stringLiteral, stringLiteral->getValue(), C, + stringLiteral->getEncoding()); +} + +static inline PreparedArguments +buildBuiltinLiteralArgs(NilLiteralExpr *nilLiteral) { + PreparedArguments builtinLiteralArgs; + builtinLiteralArgs.emplace({}); + return builtinLiteralArgs; +} + +static inline PreparedArguments +buildBuiltinLiteralArgs(SILGenFunction &SGF, SGFContext C, + BooleanLiteralExpr *booleanLiteral) { + PreparedArguments builtinLiteralArgs; + auto i1Ty = SILType::getBuiltinIntegerType(1, SGF.getASTContext()); + SILValue boolValue = SGF.B.createIntegerLiteral(booleanLiteral, i1Ty, + booleanLiteral->getValue()); + ManagedValue boolManaged = ManagedValue::forUnmanaged(boolValue); + CanType ty = boolManaged.getType().getASTType()->getCanonicalType(); + builtinLiteralArgs.emplace(AnyFunctionType::Param(ty)); + builtinLiteralArgs.add(booleanLiteral, RValue(SGF, {boolManaged}, ty)); + return builtinLiteralArgs; +} + +static inline PreparedArguments +buildBuiltinLiteralArgs(SILGenFunction &SGF, SGFContext C, + IntegerLiteralExpr *integerLiteral) { + PreparedArguments builtinLiteralArgs; + ManagedValue integerManaged = + ManagedValue::forUnmanaged(SGF.B.createIntegerLiteral( + integerLiteral, + SILType::getBuiltinIntegerLiteralType(SGF.getASTContext()), + integerLiteral->getRawValue())); + CanType ty = integerManaged.getType().getASTType(); + builtinLiteralArgs.emplace(AnyFunctionType::Param(ty)); + builtinLiteralArgs.add(integerLiteral, RValue(SGF, {integerManaged}, ty)); + return builtinLiteralArgs; +} + +static inline PreparedArguments +buildBuiltinLiteralArgs(SILGenFunction &SGF, SGFContext C, + FloatLiteralExpr *floatLiteral) { + PreparedArguments builtinLiteralArgs; + auto *litTy = floatLiteral->getBuiltinType()->castTo(); + ManagedValue floatManaged = + ManagedValue::forUnmanaged(SGF.B.createFloatLiteral( + floatLiteral, + SILType::getBuiltinFloatType(litTy->getFPKind(), SGF.getASTContext()), + floatLiteral->getValue())); + + CanType ty = floatManaged.getType().getASTType(); + builtinLiteralArgs.emplace(AnyFunctionType::Param(ty)); + builtinLiteralArgs.add(floatLiteral, RValue(SGF, {floatManaged}, ty)); + return builtinLiteralArgs; +} + +static inline PreparedArguments +buildBuiltinLiteralArgs(SILGenFunction &SGF, SGFContext C, + MagicIdentifierLiteralExpr *magicLiteral) { + ASTContext &ctx = SGF.getASTContext(); + SourceLoc loc = magicLiteral->getStartLoc(); + + switch (magicLiteral->getKind()) { + case MagicIdentifierLiteralExpr::FileIDSpelledAsFile: + case MagicIdentifierLiteralExpr::FileID: { + std::string value = loc.isValid() ? SGF.getMagicFileIDString(loc) : ""; + return emitStringLiteral(SGF, magicLiteral, value, C, + magicLiteral->getStringEncoding()); + } + + case MagicIdentifierLiteralExpr::FilePathSpelledAsFile: + case MagicIdentifierLiteralExpr::FilePath: { + StringRef value = loc.isValid() ? SGF.getMagicFilePathString(loc) : ""; + return emitStringLiteral(SGF, magicLiteral, value, C, + magicLiteral->getStringEncoding()); + } + + case MagicIdentifierLiteralExpr::Function: { + StringRef value = loc.isValid() ? SGF.getMagicFunctionString() : ""; + return emitStringLiteral(SGF, magicLiteral, value, C, + magicLiteral->getStringEncoding()); + } + + case MagicIdentifierLiteralExpr::Line: + case MagicIdentifierLiteralExpr::Column: { + SourceLoc Loc = magicLiteral->getStartLoc(); + unsigned Value = 0; + if (Loc.isValid()) { + Value = magicLiteral->getKind() == MagicIdentifierLiteralExpr::Line + ? ctx.SourceMgr.getPresumedLineAndColumnForLoc(Loc).first + : ctx.SourceMgr.getPresumedLineAndColumnForLoc(Loc).second; + } + + auto silTy = SILType::getBuiltinIntegerLiteralType(ctx); + auto ty = silTy.getASTType(); + SILValue integer = SGF.B.createIntegerLiteral(magicLiteral, silTy, Value); + ManagedValue integerManaged = ManagedValue::forUnmanaged(integer); + PreparedArguments builtinLiteralArgs; + builtinLiteralArgs.emplace(AnyFunctionType::Param(ty)); + builtinLiteralArgs.add(magicLiteral, RValue(SGF, {integerManaged}, ty)); + return builtinLiteralArgs; + } + case MagicIdentifierLiteralExpr::DSOHandle: + llvm_unreachable("handled elsewhere"); + } +} + +static inline PreparedArguments buildBuiltinLiteralArgs(SILGenFunction &SGF, + SGFContext C, + LiteralExpr *literal) { + if (auto stringLiteral = dyn_cast(literal)) { + return buildBuiltinLiteralArgs(SGF, C, stringLiteral); + } else if (auto nilLiteral = dyn_cast(literal)) { + return buildBuiltinLiteralArgs(nilLiteral); + } else if (auto booleanLiteral = dyn_cast(literal)) { + return buildBuiltinLiteralArgs(SGF, C, booleanLiteral); + } else if (auto integerLiteral = dyn_cast(literal)) { + return buildBuiltinLiteralArgs(SGF, C, integerLiteral); + } else if (auto floatLiteral = dyn_cast(literal)) { + return buildBuiltinLiteralArgs(SGF, C, floatLiteral); + } else { + return buildBuiltinLiteralArgs( + SGF, C, cast(literal)); + } +} + //===----------------------------------------------------------------------===// // Argument Emission //===----------------------------------------------------------------------===// @@ -4794,124 +4933,30 @@ RValue SILGenFunction::emitApplyOfPropertyWrapperBackingInitializer( RValue SILGenFunction::emitLiteral(LiteralExpr *literal, SGFContext C) { ConcreteDeclRef builtinInit; ConcreteDeclRef init; - // Emit the raw, builtin literal arguments. - PreparedArguments builtinLiteralArgs; - if (auto stringLiteral = dyn_cast(literal)) { - builtinLiteralArgs = emitStringLiteral(*this, literal, - stringLiteral->getValue(), C, - stringLiteral->getEncoding()); - builtinInit = stringLiteral->getBuiltinInitializer(); - init = stringLiteral->getInitializer(); - } else if (auto nilLiteral = dyn_cast(literal)) { - builtinLiteralArgs.emplace({}); - builtinInit = nilLiteral->getInitializer(); - } else if (auto booleanLiteral = dyn_cast(literal)) { - auto i1Ty = SILType::getBuiltinIntegerType(1, getASTContext()); - SILValue boolValue = B.createIntegerLiteral(booleanLiteral, i1Ty, - booleanLiteral->getValue()); - ManagedValue boolManaged = ManagedValue::forUnmanaged(boolValue); - CanType ty = boolManaged.getType().getASTType()->getCanonicalType(); - builtinLiteralArgs.emplace(AnyFunctionType::Param(ty)); - builtinLiteralArgs.add(literal, RValue(*this, {boolManaged}, ty)); - builtinInit = booleanLiteral->getBuiltinInitializer(); - init = booleanLiteral->getInitializer(); - } else if (auto integerLiteral = dyn_cast(literal)) { - ManagedValue integerManaged = - ManagedValue::forUnmanaged(B.createIntegerLiteral( - integerLiteral, - SILType::getBuiltinIntegerLiteralType(getASTContext()), - integerLiteral->getRawValue())); - CanType ty = integerManaged.getType().getASTType(); - builtinLiteralArgs.emplace(AnyFunctionType::Param(ty)); - builtinLiteralArgs.add(literal, RValue(*this, {integerManaged}, ty)); - builtinInit = integerLiteral->getBuiltinInitializer(); - init = integerLiteral->getInitializer(); - } else if (auto floatLiteral = dyn_cast(literal)) { - auto *litTy = floatLiteral->getBuiltinType()->castTo(); - ManagedValue floatManaged = ManagedValue::forUnmanaged(B.createFloatLiteral( - floatLiteral, - SILType::getBuiltinFloatType(litTy->getFPKind(), getASTContext()), - floatLiteral->getValue())); - - CanType ty = floatManaged.getType().getASTType(); - builtinLiteralArgs.emplace(AnyFunctionType::Param(ty)); - builtinLiteralArgs.add(literal, RValue(*this, {floatManaged}, ty)); - builtinInit = floatLiteral->getBuiltinInitializer(); - init = floatLiteral->getInitializer(); + if (auto builtinLiteral = dyn_cast(literal)) { + builtinInit = builtinLiteral->getBuiltinInitializer(); + init = builtinLiteral->getInitializer(); } else { - ASTContext &ctx = getASTContext(); - SourceLoc loc = literal->getStartLoc(); - - auto magicLiteral = cast(literal); - switch (magicLiteral->getKind()) { - case MagicIdentifierLiteralExpr::FileIDSpelledAsFile: - case MagicIdentifierLiteralExpr::FileID: { - std::string value = loc.isValid() ? getMagicFileIDString(loc) : ""; - builtinLiteralArgs = emitStringLiteral(*this, literal, value, C, - magicLiteral->getStringEncoding()); - builtinInit = magicLiteral->getBuiltinInitializer(); - init = magicLiteral->getInitializer(); - break; - } - - case MagicIdentifierLiteralExpr::FilePathSpelledAsFile: - case MagicIdentifierLiteralExpr::FilePath: { - StringRef value = loc.isValid() ? getMagicFilePathString(loc) : ""; - builtinLiteralArgs = emitStringLiteral(*this, literal, value, C, - magicLiteral->getStringEncoding()); - builtinInit = magicLiteral->getBuiltinInitializer(); - init = magicLiteral->getInitializer(); - break; - } - - case MagicIdentifierLiteralExpr::Function: { - StringRef value = loc.isValid() ? getMagicFunctionString() : ""; - builtinLiteralArgs = emitStringLiteral(*this, literal, value, C, - magicLiteral->getStringEncoding()); - builtinInit = magicLiteral->getBuiltinInitializer(); - init = magicLiteral->getInitializer(); - break; - } - - case MagicIdentifierLiteralExpr::Line: - case MagicIdentifierLiteralExpr::Column: { - SourceLoc Loc = literal->getStartLoc(); - unsigned Value = 0; - if (Loc.isValid()) { - Value = magicLiteral->getKind() == MagicIdentifierLiteralExpr::Line - ? ctx.SourceMgr.getPresumedLineAndColumnForLoc(Loc).first - : ctx.SourceMgr.getPresumedLineAndColumnForLoc(Loc).second; - } - - auto silTy = SILType::getBuiltinIntegerLiteralType(ctx); - auto ty = silTy.getASTType(); - SILValue integer = B.createIntegerLiteral(literal, silTy, Value); - ManagedValue integerManaged = ManagedValue::forUnmanaged(integer); - builtinLiteralArgs.emplace(AnyFunctionType::Param(ty)); - builtinLiteralArgs.add(literal, RValue(*this, {integerManaged}, ty)); - builtinInit = magicLiteral->getBuiltinInitializer(); - init = magicLiteral->getInitializer(); - break; - } - case MagicIdentifierLiteralExpr::DSOHandle: - llvm_unreachable("handled elsewhere"); - } + builtinInit = literal->getInitializer(); } + // Emit the raw, builtin literal arguments. + PreparedArguments builtinLiteralArgs = + buildBuiltinLiteralArgs(*this, C, literal); + // Call the builtin initializer. - RValue builtinLiteral = - emitApplyAllocatingInitializer(literal, builtinInit, - std::move(builtinLiteralArgs), - Type(), - init ? SGFContext() : C); + RValue builtinResult = emitApplyAllocatingInitializer( + literal, builtinInit, std::move(builtinLiteralArgs), Type(), + init ? SGFContext() : C); // If we were able to directly initialize the literal we wanted, we're done. - if (!init) return builtinLiteral; + if (!init) + return builtinResult; // Otherwise, perform the second initialization step. - auto ty = builtinLiteral.getType(); + auto ty = builtinResult.getType(); PreparedArguments args((AnyFunctionType::Param(ty))); - args.add(literal, std::move(builtinLiteral)); + args.add(literal, std::move(builtinResult)); RValue result = emitApplyAllocatingInitializer(literal, init, std::move(args), diff --git a/lib/SILGen/SILGenBridging.cpp b/lib/SILGen/SILGenBridging.cpp index e48750a4ccc05..989b6e0f19804 100644 --- a/lib/SILGen/SILGenBridging.cpp +++ b/lib/SILGen/SILGenBridging.cpp @@ -336,10 +336,17 @@ static ManagedValue emitManagedParameter(SILGenFunction &SGF, SILLocation loc, /// Get the type of each parameter, filtering out empty tuples. static SmallVector -getParameterTypes(AnyFunctionType::CanParamArrayRef params) { +getParameterTypes(AnyFunctionType::CanParamArrayRef params, + bool hasSelfParam=false) { SmallVector results; - for (auto param : params) { - assert(!param.isInOut() && !param.isVariadic()); + for (auto n : indices(params)) { + bool isSelf = (hasSelfParam ? n == params.size() - 1 : false); + + const auto ¶m = params[n]; + assert(isSelf || !param.isInOut() && + "Only the 'self' parameter can be inout in a bridging thunk"); + assert(!param.isVariadic()); + if (param.getPlainType()->isVoid()) continue; results.push_back(param.getPlainType()); @@ -1723,10 +1730,11 @@ void SILGenFunction::emitForeignToNativeThunk(SILDeclRef thunk) { }; { + bool hasSelfParam = fd->hasImplicitSelfDecl(); auto foreignFormalParams = - getParameterTypes(foreignCI.LoweredType.getParams()); + getParameterTypes(foreignCI.LoweredType.getParams(), hasSelfParam); auto nativeFormalParams = - getParameterTypes(nativeCI.LoweredType.getParams()); + getParameterTypes(nativeCI.LoweredType.getParams(), hasSelfParam); for (unsigned nativeParamIndex : indices(params)) { // Bring the parameter to +1. @@ -1762,7 +1770,7 @@ void SILGenFunction::emitForeignToNativeThunk(SILDeclRef thunk) { maybeAddForeignErrorArg(); - bool isSelf = nativeParamIndex == params.size() - 1; + bool isSelf = (hasSelfParam && nativeParamIndex == params.size() - 1); if (memberStatus.isInstance()) { // Leave space for `self` to be filled in later. @@ -1787,6 +1795,12 @@ void SILGenFunction::emitForeignToNativeThunk(SILDeclRef thunk) { F.mapTypeIntoContext(foreignFormalParams[nativeParamIndex]) ->getCanonicalType(); + if (isSelf) { + assert(!nativeCI.LoweredType.getParams()[nativeParamIndex].isInOut() || + nativeFormalType == foreignFormalType && + "Cannot bridge 'self' parameter if passed inout"); + } + auto foreignParam = foreignFnTy->getParameters()[foreignArgIndex++]; SILType foreignLoweredTy = F.mapTypeIntoContext(foreignParam.getSILStorageType( diff --git a/lib/SILGen/SILGenConstructor.cpp b/lib/SILGen/SILGenConstructor.cpp index dbbb66b81f35e..32373d6c728f2 100644 --- a/lib/SILGen/SILGenConstructor.cpp +++ b/lib/SILGen/SILGenConstructor.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "ArgumentSource.h" +#include "Conversion.h" #include "Initialization.h" #include "LValue.h" #include "RValue.h" @@ -18,6 +19,7 @@ #include "SILGenFunctionBuilder.h" #include "Scope.h" #include "swift/AST/ASTMangler.h" +#include "swift/AST/ForeignErrorConvention.h" #include "swift/AST/GenericEnvironment.h" #include "swift/AST/ParameterList.h" #include "swift/AST/PropertyWrappers.h" @@ -211,8 +213,24 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF, "number of args does not match number of fields"); (void)eltEnd; FullExpr scope(SGF.Cleanups, field->getParentPatternBinding()); + + RValue arg = std::move(*elti); + + // If the stored property has an attached result builder and its + // type is not a function type, the argument is a noescape closure + // that needs to be called. + if (field->getResultBuilderType()) { + if (!field->getValueInterfaceType() + ->lookThroughAllOptionalTypes()->is()) { + auto resultTy = cast(arg.getType()).getResult(); + arg = SGF.emitMonomorphicApply( + Loc, std::move(arg).getAsSingleValue(SGF, Loc), { }, resultTy, + resultTy, ApplyOptions::None, None, None); + } + } + maybeEmitPropertyWrapperInitFromValue(SGF, Loc, field, subs, - std::move(*elti)) + std::move(arg)) .forwardInto(SGF, Loc, init.get()); ++elti; } else { @@ -303,8 +321,8 @@ void SILGenFunction::emitValueConstructor(ConstructorDecl *ctor) { return emitImplicitValueConstructor(*this, ctor); // True if this constructor delegates to a peer constructor with self.init(). - bool isDelegating = ctor->getDelegatingOrChainedInitKind(nullptr) == - ConstructorDecl::BodyInitKind::Delegating; + bool isDelegating = ctor->getDelegatingOrChainedInitKind().initKind == + BodyInitKind::Delegating; // Get the 'self' decl and type. VarDecl *selfDecl = ctor->getImplicitSelfDecl(); @@ -526,8 +544,11 @@ void SILGenFunction::emitEnumConstructor(EnumElementDecl *element) { // Return the enum. auto ReturnLoc = ImplicitReturnLocation::getImplicitReturnLoc(Loc); - if (mv.isInContext()) { - assert(enumTI.isAddressOnly()); + if (dest) { + if (!mv.isInContext()) { + dest->copyOrInitValueInto(*this, Loc, mv, /*isInit*/ true); + dest->finishInitialization(*this); + } scope.pop(); B.createReturn(ReturnLoc, emitEmptyTuple(Loc)); } else { @@ -637,8 +658,8 @@ void SILGenFunction::emitClassConstructorInitializer(ConstructorDecl *ctor) { // True if this constructor delegates to a peer constructor with self.init(). bool isDelegating = false; if (!ctor->hasStubImplementation()) { - isDelegating = ctor->getDelegatingOrChainedInitKind(nullptr) == - ConstructorDecl::BodyInitKind::Delegating; + isDelegating = ctor->getDelegatingOrChainedInitKind().initKind == + BodyInitKind::Delegating; } // Set up the 'self' argument. If this class has a superclass, we set up @@ -884,72 +905,71 @@ static ManagedValue emitSelfForMemberInit(SILGenFunction &SGF, SILLocation loc, SGFAccessKind::Write); } -static LValue emitLValueForMemberInit(SILGenFunction &SGF, SILLocation loc, - VarDecl *selfDecl, - VarDecl *property) { - CanType selfFormalType = selfDecl->getType()->getCanonicalType(); - auto self = emitSelfForMemberInit(SGF, loc, selfDecl); - return SGF.emitPropertyLValue(loc, self, selfFormalType, property, - LValueOptions(), SGFAccessKind::Write, - AccessSemantics::DirectToStorage); -} - -/// Emit a member initialization for the members described in the -/// given pattern from the given source value. -static void emitMemberInit(SILGenFunction &SGF, VarDecl *selfDecl, - Pattern *pattern, RValue &&src) { +// FIXME: Can emitMemberInit() share code with InitializationForPattern in +// SILGenDecl.cpp? Note that this version operates on stored properties of +// types, whereas the former only knows how to handle local bindings, but +// we could generalize it. +static InitializationPtr +emitMemberInit(SILGenFunction &SGF, VarDecl *selfDecl, Pattern *pattern) { switch (pattern->getKind()) { case PatternKind::Paren: return emitMemberInit(SGF, selfDecl, - cast(pattern)->getSubPattern(), - std::move(src)); + cast(pattern)->getSubPattern()); case PatternKind::Tuple: { + TupleInitialization *init = new TupleInitialization(); auto tuple = cast(pattern); - auto fields = tuple->getElements(); - - SmallVector elements; - std::move(src).extractElements(elements); - for (unsigned i = 0, n = fields.size(); i != n; ++i) { - emitMemberInit(SGF, selfDecl, fields[i].getPattern(), - std::move(elements[i])); + for (auto &elt : tuple->getElements()) { + init->SubInitializations.push_back( + emitMemberInit(SGF, selfDecl, elt.getPattern())); } - break; + return InitializationPtr(init); } case PatternKind::Named: { auto named = cast(pattern); - // Form the lvalue referencing this member. - FormalEvaluationScope scope(SGF); - LValue memberRef = emitLValueForMemberInit(SGF, pattern, selfDecl, - named->getDecl()); - // Assign to it. - SGF.emitAssignToLValue(pattern, std::move(src), std::move(memberRef)); - return; + auto self = emitSelfForMemberInit(SGF, pattern, selfDecl); + + auto *field = named->getDecl(); + + auto selfTy = self.getType(); + auto fieldTy = + selfTy.getFieldType(field, SGF.SGM.M, SGF.getTypeExpansionContext()); + SILValue slot; + + if (auto *structDecl = dyn_cast(field->getDeclContext())) { + slot = SGF.B.createStructElementAddr(pattern, self.forward(SGF), field, + fieldTy.getAddressType()); + } else { + assert(isa(field->getDeclContext())); + slot = SGF.B.createRefElementAddr(pattern, self.forward(SGF), field, + fieldTy.getAddressType()); + } + + return InitializationPtr(new KnownAddressInitialization(slot)); } case PatternKind::Any: - return; + return InitializationPtr(new BlackHoleInitialization());; case PatternKind::Typed: return emitMemberInit(SGF, selfDecl, - cast(pattern)->getSubPattern(), - std::move(src)); + cast(pattern)->getSubPattern()); case PatternKind::Binding: return emitMemberInit(SGF, selfDecl, - cast(pattern)->getSubPattern(), - std::move(src)); + cast(pattern)->getSubPattern()); #define PATTERN(Name, Parent) #define REFUTABLE_PATTERN(Name, Parent) case PatternKind::Name: #include "swift/AST/PatternNodes.def" - llvm_unreachable("Refutable pattern in pattern binding"); + llvm_unreachable("Refutable pattern in stored property pattern binding"); } } -static Type getInitializationTypeInContext( +static std::pair +getInitializationTypeInContext( DeclContext *fromDC, DeclContext *toDC, Pattern *pattern) { auto interfaceType = pattern->getType()->mapTypeOutOfContext(); @@ -964,9 +984,53 @@ static Type getInitializationTypeInContext( } } - auto resultType = toDC->mapTypeIntoContext(interfaceType); + AbstractionPattern origType( + fromDC->getGenericSignatureOfContext().getCanonicalSignature(), + interfaceType->getCanonicalType()); + + auto substType = toDC->mapTypeIntoContext(interfaceType)->getCanonicalType(); + + return std::make_pair(origType, substType); +} + +static void +emitAndStoreInitialValueInto(SILGenFunction &SGF, + SILLocation loc, + PatternBindingDecl *pbd, unsigned i, + SubstitutionMap subs, + AbstractionPattern origType, + CanType substType, + Initialization *init) { + bool injectIntoWrapper = false; + if (auto singleVar = pbd->getSingleVar()) { + auto originalVar = singleVar->getOriginalWrappedProperty(); + if (originalVar && + originalVar->isPropertyMemberwiseInitializedWithWrappedType()) { + injectIntoWrapper = true; + } + } + + SGFContext C = (injectIntoWrapper ? SGFContext() : SGFContext(init)); + + RValue result = SGF.emitApplyOfStoredPropertyInitializer( + pbd->getExecutableInit(i), + pbd->getAnchoringVarDecl(i), + subs, substType, origType, C); + + // need to store result into the init if its in context + + // If we have the backing storage for a property with an attached + // property wrapper initialized with `=`, inject the value into an + // instance of the wrapper. + if (injectIntoWrapper) { + auto *singleVar = pbd->getSingleVar(); + result = maybeEmitPropertyWrapperInitFromValue( + SGF, pbd->getExecutableInit(i), + singleVar, subs, std::move(result)); + } - return resultType; + if (!result.isInContext()) + std::move(result).forwardInto(SGF, loc, init); } void SILGenFunction::emitMemberInitializers(DeclContext *dc, @@ -984,35 +1048,51 @@ void SILGenFunction::emitMemberInitializers(DeclContext *dc, if (!init) continue; auto *varPattern = pbd->getPattern(i); + // Cleanup after this initialization. FullExpr scope(Cleanups, varPattern); // Get the type of the initialization result, in terms // of the constructor context's archetypes. - CanType resultType = getInitializationTypeInContext( - pbd->getDeclContext(), dc, varPattern)->getCanonicalType(); - AbstractionPattern origResultType(resultType); - - // FIXME: Can emitMemberInit() share code with - // InitializationForPattern in SILGenDecl.cpp? - RValue result = emitApplyOfStoredPropertyInitializer( - init, pbd->getAnchoringVarDecl(i), subs, - resultType, origResultType, - SGFContext()); - - // If we have the backing storage for a property with an attached - // property wrapper initialized with `=`, inject the value into an - // instance of the wrapper. - if (auto singleVar = pbd->getSingleVar()) { - auto originalVar = singleVar->getOriginalWrappedProperty(); - if (originalVar && - originalVar->isPropertyMemberwiseInitializedWithWrappedType()) { - result = maybeEmitPropertyWrapperInitFromValue( - *this, init, singleVar, subs, std::move(result)); - } + auto resultType = getInitializationTypeInContext( + pbd->getDeclContext(), dc, varPattern); + AbstractionPattern origType = resultType.first; + CanType substType = resultType.second; + + // Figure out what we're initializing. + auto memberInit = emitMemberInit(*this, selfDecl, varPattern); + + // This whole conversion thing is about eliminating the + // paired orig-to-subst subst-to-orig conversions that + // will happen if the storage is at a different abstraction + // level than the constructor. When emitApply() is used + // to call the stored property initializer, it naturally + // wants to convert the result back to the most substituted + // abstraction level. To undo this, we use a converting + // initialization and rely on the peephole that optimizes + // out the redundant conversion. + auto loweredResultTy = getLoweredType(origType, substType); + auto loweredSubstTy = getLoweredType(substType); + + if (loweredResultTy != loweredSubstTy) { + Conversion conversion = Conversion::getSubstToOrig( + origType, substType, + loweredResultTy); + + ConvertingInitialization convertingInit(conversion, + SGFContext(memberInit.get())); + + emitAndStoreInitialValueInto(*this, varPattern, pbd, i, subs, + origType, substType, &convertingInit); + + auto finalValue = convertingInit.finishEmission( + *this, varPattern, ManagedValue::forInContext()); + if (!finalValue.isInContext()) + finalValue.forwardInto(*this, varPattern, memberInit.get()); + } else { + emitAndStoreInitialValueInto(*this, varPattern, pbd, i, subs, + origType, substType, memberInit.get()); } - - emitMemberInit(*this, selfDecl, varPattern, std::move(result)); } } } diff --git a/lib/SILGen/SILGenConvert.cpp b/lib/SILGen/SILGenConvert.cpp index 953c3ae518556..a75fdb01c9a3e 100644 --- a/lib/SILGen/SILGenConvert.cpp +++ b/lib/SILGen/SILGenConvert.cpp @@ -1354,7 +1354,25 @@ Lowering::canPeepholeConversions(SILGenFunction &SGF, switch (outerConversion.getKind()) { case Conversion::OrigToSubst: case Conversion::SubstToOrig: - // TODO: peephole these when the abstraction patterns are the same! + switch (innerConversion.getKind()) { + case Conversion::OrigToSubst: + case Conversion::SubstToOrig: + if (innerConversion.getKind() == outerConversion.getKind()) + break; + + if (innerConversion.getReabstractionOrigType().getCachingKey() != + outerConversion.getReabstractionOrigType().getCachingKey() || + innerConversion.getReabstractionSubstType() != + outerConversion.getReabstractionSubstType()) { + break; + } + + return ConversionPeepholeHint(ConversionPeepholeHint::Identity, false); + + default: + break; + } + return None; case Conversion::AnyErasure: diff --git a/lib/SILGen/SILGenDecl.cpp b/lib/SILGen/SILGenDecl.cpp index dfa1f39b72ac6..6d89e8e192733 100644 --- a/lib/SILGen/SILGenDecl.cpp +++ b/lib/SILGen/SILGenDecl.cpp @@ -22,6 +22,7 @@ #include "swift/AST/Module.h" #include "swift/AST/NameLookup.h" #include "swift/AST/ProtocolConformance.h" +#include "swift/AST/PropertyWrappers.h" #include "swift/Basic/ProfileCounter.h" #include "swift/SIL/FormalLinkage.h" #include "swift/SIL/PrettyStackTrace.h" @@ -38,41 +39,6 @@ using namespace Lowering; void Initialization::_anchor() {} void SILDebuggerClient::anchor() {} -namespace { - /// A "null" initialization that indicates that any value being initialized - /// into this initialization should be discarded. This represents AnyPatterns - /// (that is, 'var (_)') that bind to values without storing them. - class BlackHoleInitialization : public Initialization { - public: - BlackHoleInitialization() {} - - bool canSplitIntoTupleElements() const override { - return true; - } - - MutableArrayRef - splitIntoTupleElements(SILGenFunction &SGF, SILLocation loc, - CanType type, - SmallVectorImpl &buf) override { - // "Destructure" an ignored binding into multiple ignored bindings. - for (auto fieldType : cast(type)->getElementTypes()) { - (void) fieldType; - buf.push_back(InitializationPtr(new BlackHoleInitialization())); - } - return buf; - } - - void copyOrInitValueInto(SILGenFunction &SGF, SILLocation loc, - ManagedValue value, bool isInit) override { - /// This just ignores the provided value. - } - - void finishUninitialized(SILGenFunction &SGF) override { - // do nothing - } - }; -} // end anonymous namespace - static void copyOrInitValueIntoHelper( SILGenFunction &SGF, SILLocation loc, ManagedValue value, bool isInit, ArrayRef subInitializations, @@ -1171,6 +1137,22 @@ void SILGenFunction::emitPatternBinding(PatternBindingDecl *PBD, // the initialization. Otherwise, mark it uninitialized for DI to resolve. if (auto *Init = PBD->getExecutableInit(idx)) { FullExpr Scope(Cleanups, CleanupLocation(Init)); + + auto *var = PBD->getSingleVar(); + if (var && var->getDeclContext()->isLocalContext()) { + if (auto *orig = var->getOriginalWrappedProperty()) { + auto wrapperInfo = orig->getPropertyWrapperBackingPropertyInfo(); + Init = wrapperInfo.wrappedValuePlaceholder->getOriginalWrappedValue(); + + auto value = emitRValue(Init); + emitApplyOfPropertyWrapperBackingInitializer(SILLocation(PBD), orig, + getForwardingSubstitutionMap(), + std::move(value)) + .forwardInto(*this, SILLocation(PBD), initialization.get()); + return; + } + } + emitExprInto(Init, initialization.get(), SILLocation(PBD)); } else { initialization->finishUninitialized(*this); @@ -1189,6 +1171,18 @@ void SILGenFunction::visitPatternBindingDecl(PatternBindingDecl *PBD) { void SILGenFunction::visitVarDecl(VarDecl *D) { // We handle emitting the variable storage when we see the pattern binding. + // Emit the property wrapper backing initializer if necessary. + auto wrapperInfo = D->getPropertyWrapperBackingPropertyInfo(); + if (wrapperInfo && wrapperInfo.initializeFromOriginal) + SGM.emitPropertyWrapperBackingInitializer(D); + + D->visitAuxiliaryDecls([&](VarDecl *var) { + if (auto *patternBinding = var->getParentPatternBinding()) + visitPatternBindingDecl(patternBinding); + + visit(var); + }); + // Emit the variable's accessors. D->visitEmittedAccessors([&](AccessorDecl *accessor) { SGM.emitFunction(accessor); @@ -1261,7 +1255,7 @@ void SILGenFunction::emitStmtCondition(StmtCondition Cond, JumpDest FalseDest, switch (elt.getKind()) { case StmtConditionElement::CK_PatternBinding: { InitializationPtr initialization = - InitializationForPattern(*this, FalseDest).visit(elt.getPattern()); + emitPatternBindingInitialization(elt.getPattern(), FalseDest); // Emit the initial value into the initialization. FullExpr Scope(Cleanups, CleanupLocation(elt.getInitializer())); @@ -1437,10 +1431,12 @@ SILGenFunction::enterDormantTemporaryCleanup(SILValue addr, namespace { -struct FormalAccessReleaseValueCleanup : Cleanup { +struct FormalAccessReleaseValueCleanup final : Cleanup { FormalEvaluationContext::stable_iterator Depth; - FormalAccessReleaseValueCleanup() : Depth() {} + FormalAccessReleaseValueCleanup() : Cleanup(), Depth() { + setIsFormalAccess(); + } void setState(SILGenFunction &SGF, CleanupState newState) override { if (newState == CleanupState::Dead) { diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 6a3b8bde0cf57..4e4cf08b84e3e 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -2515,7 +2515,7 @@ visitInterpolatedStringLiteralExpr(InterpolatedStringLiteralExpr *E, resultInitArgs.add(E, std::move(interpolation)); return SGF.emitApplyAllocatingInitializer( - E, E->getResultInit(), std::move(resultInitArgs), Type(), C); + E, E->getInitializer(), std::move(resultInitArgs), Type(), C); } RValue RValueEmitter:: diff --git a/lib/SILGen/SILGenFunction.cpp b/lib/SILGen/SILGenFunction.cpp index 006a46461f3b4..a67469719a35a 100644 --- a/lib/SILGen/SILGenFunction.cpp +++ b/lib/SILGen/SILGenFunction.cpp @@ -187,6 +187,7 @@ void SILGenFunction::emitCaptures(SILLocation loc, // Partial applications take ownership of the context parameters, so we'll // need to pass ownership rather than merely guaranteeing parameters. bool canGuarantee; + bool captureCanEscape = true; switch (purpose) { case CaptureEmission::PartialApplication: canGuarantee = false; @@ -194,6 +195,10 @@ void SILGenFunction::emitCaptures(SILLocation loc, case CaptureEmission::ImmediateApplication: canGuarantee = true; break; + case CaptureEmission::AssignByWrapper: + canGuarantee = false; + captureCanEscape = false; + break; } auto expansion = getTypeExpansionContext(); @@ -381,7 +386,8 @@ void SILGenFunction::emitCaptures(SILLocation loc, } else { capturedArgs.push_back(emitManagedRetain(loc, Entry.box)); } - escapesToMark.push_back(entryValue); + if (captureCanEscape) + escapesToMark.push_back(entryValue); } else { // Address only 'let' values are passed by box. This isn't great, in // that a variable captured by multiple closures will be boxed for each diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h index b5e98c78bda87..1b474d092345e 100644 --- a/lib/SILGen/SILGenFunction.h +++ b/lib/SILGen/SILGenFunction.h @@ -115,6 +115,11 @@ enum class CaptureEmission { /// Captures are being emitted for partial application to form a closure /// value. PartialApplication, + /// Captures are being emitted for partial application of a local property + /// wrapper setter for assign_by_wrapper. Captures are guaranteed to not + /// escape, because assign_by_wrapper will not use the setter if the captured + /// variable is not initialized. + AssignByWrapper, }; /// Different ways in which an l-value can be emitted. diff --git a/lib/SILGen/SILGenLValue.cpp b/lib/SILGen/SILGenLValue.cpp index 522f89f02f47b..fb2e6f0952347 100644 --- a/lib/SILGen/SILGenLValue.cpp +++ b/lib/SILGen/SILGenLValue.cpp @@ -603,7 +603,7 @@ SILValue UnenforcedAccess::beginAccess(SILGenFunction &SGF, SILLocation loc, if (!SGF.getOptions().VerifyExclusivity) return address; - const AccessedStorage &storage = findAccessedStorage(address); + auto storage = AccessedStorage::compute(address); // Unsafe access may have invalid storage (e.g. a RawPointer). if (storage && !isPossibleFormalAccessBase(storage, &SGF.F)) return address; @@ -1314,6 +1314,17 @@ namespace { IsOnSelfParameter && isa(SGF.FunctionDC->getAsDecl()); + // Assignment to a wrapped property can only be re-written to initialization for + // members of `self` in an initializer, and for local variables. + if (!(isAssignmentToSelfParamInInit || VD->getDeclContext()->isLocalContext())) + return false; + + // If this var isn't in a type context, assignment will always use the setter + // if there is an initial value. + if (!VD->getDeclContext()->isTypeContext() && + wrapperInfo.wrappedValuePlaceholder->getOriginalWrappedValue()) + return false; + // If we have a nonmutating setter on a value type, the call // captures all of 'self' and we cannot rewrite an assignment // into an initialization. @@ -1403,7 +1414,7 @@ namespace { assert(getAccessorDecl()->isSetter()); SILDeclRef setter = Accessor; - if (IsOnSelfParameter && canRewriteSetAsPropertyWrapperInit(SGF) && + if (canRewriteSetAsPropertyWrapperInit(SGF) && !Storage->isStatic() && isBackingVarVisible(cast(Storage), SGF.FunctionDC)) { @@ -1434,7 +1445,9 @@ namespace { // Get the address of the storage property. ManagedValue proj; - if (BaseFormalType->mayHaveSuperclass()) { + if (!BaseFormalType) { + proj = SGF.maybeEmitValueOfLocalVarDecl(backingVar); + } else if (BaseFormalType->mayHaveSuperclass()) { RefElementComponent REC(backingVar, LValueOptions(), varStorageType, typeData); proj = std::move(REC).project(SGF, loc, base); @@ -1471,33 +1484,51 @@ namespace { .SILFnType) .getValue(); - } else + } else { setterFRef = SGF.emitGlobalFunctionRef(loc, setter, setterInfo); + } + CanSILFunctionType setterTy = setterFRef->getType().castTo(); SILFunctionConventions setterConv(setterTy, SGF.SGM.M); - SILValue capturedBase; - unsigned argIdx = setterConv.getNumSILArguments() - 1; - if (setterConv.getSILArgumentConvention(argIdx).isInoutConvention()) { - capturedBase = base.getValue(); - } else { - capturedBase = base.copy(SGF, loc).forward(SGF); - } + // Emit captures for the setter + SmallVector capturedArgs; + auto captureInfo = SGF.SGM.Types.getLoweredLocalCaptures(setter); + if (!captureInfo.getCaptures().empty()) { + SmallVector captures; + SGF.emitCaptures(loc, setter, CaptureEmission::AssignByWrapper, captures); - // If the base is a reference and the setter expects a value, emit a - // load. This pattern is emitted for property wrappers with a - // nonmutating setter, for example. - if (base.getType().isAddress() && - base.getType().getObjectType() == - setterConv.getSILArgumentType(argIdx, - SGF.getTypeExpansionContext())) { - capturedBase = SGF.B.createTrivialLoadOr( - loc, capturedBase, LoadOwnershipQualifier::Take); + for (auto capture : captures) + capturedArgs.push_back(capture.forward(SGF)); + } else { + assert(base); + + SILValue capturedBase; + unsigned argIdx = setterConv.getNumSILArguments() - 1; + + if (setterConv.getSILArgumentConvention(argIdx).isInoutConvention()) { + capturedBase = base.getValue(); + } else { + capturedBase = base.copy(SGF, loc).forward(SGF); + } + + // If the base is a reference and the setter expects a value, emit a + // load. This pattern is emitted for property wrappers with a + // nonmutating setter, for example. + if (base.getType().isAddress() && + base.getType().getObjectType() == + setterConv.getSILArgumentType(argIdx, + SGF.getTypeExpansionContext())) { + capturedBase = SGF.B.createTrivialLoadOr( + loc, capturedBase, LoadOwnershipQualifier::Take); + } + + capturedArgs.push_back(capturedBase); } PartialApplyInst *setterPAI = SGF.B.createPartialApply(loc, setterFRef, - Substitutions, { capturedBase }, + Substitutions, capturedArgs, ParameterConvention::Direct_Guaranteed); ManagedValue setterFn = SGF.emitManagedRValueWithCleanup(setterPAI); diff --git a/lib/SILGen/SILGenLazyConformance.cpp b/lib/SILGen/SILGenLazyConformance.cpp index 791ae643c7f4c..f568f26dfe0dc 100644 --- a/lib/SILGen/SILGenLazyConformance.cpp +++ b/lib/SILGen/SILGenLazyConformance.cpp @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -31,7 +31,7 @@ void SILGenModule::useConformance(ProtocolConformanceRef conformanceRef) { if (auto *inherited = dyn_cast(conformance)) conformance = inherited->getInheritedConformance(); - // Get the normal conformance. If we don't have one, this is a self + // Get the normal conformance. If we don't have one, this is a self or builtin // conformance, which we can ignore. auto normal = dyn_cast( conformance->getRootConformance()); diff --git a/lib/SILGen/SILGenPoly.cpp b/lib/SILGen/SILGenPoly.cpp index 0ee11d74f6439..4f6ad045b6996 100644 --- a/lib/SILGen/SILGenPoly.cpp +++ b/lib/SILGen/SILGenPoly.cpp @@ -3204,7 +3204,7 @@ CanSILFunctionType SILGenFunction::buildThunkType( // If this thunk involves DynamicSelfType in any way, add a capture for it // in case we need to recover metadata. if (hasDynamicSelf) { - dynamicSelfType = F.getSelfMetadataArgument()->getType().getASTType(); + dynamicSelfType = F.getDynamicSelfMetadata()->getType().getASTType(); if (!isa(dynamicSelfType)) { dynamicSelfType = CanMetatypeType::get(dynamicSelfType, MetatypeRepresentation::Thick); @@ -4577,7 +4577,7 @@ getWitnessFunctionRef(SILGenFunction &SGF, SILLocation loc) { switch (witnessKind) { case WitnessDispatchKind::Static: - if (auto *derivativeId = witness.derivativeFunctionIdentifier) { + if (auto *derivativeId = witness.getDerivativeFunctionIdentifier()) { auto originalFn = SGF.emitGlobalFunctionRef(loc, witness.asAutoDiffOriginalFunction()); auto *loweredParamIndices = autodiff::getLoweredParameterIndices( @@ -4594,7 +4594,7 @@ getWitnessFunctionRef(SILGenFunction &SGF, } return SGF.emitGlobalFunctionRef(loc, witness); case WitnessDispatchKind::Dynamic: - assert(!witness.derivativeFunctionIdentifier); + assert(!witness.getDerivativeFunctionIdentifier()); return SGF.emitDynamicMethodRef(loc, witness, witnessFTy).getValue(); case WitnessDispatchKind::Witness: { auto typeAndConf = @@ -4609,7 +4609,7 @@ getWitnessFunctionRef(SILGenFunction &SGF, // If `witness` is a derivative function `SILDeclRef`, replace the // derivative function identifier's generic signature with the witness thunk // substitution map's generic signature. - if (auto *derivativeId = witness.derivativeFunctionIdentifier) { + if (auto *derivativeId = witness.getDerivativeFunctionIdentifier()) { auto *newDerivativeId = AutoDiffDerivativeFunctionIdentifier::get( derivativeId->getKind(), derivativeId->getParameterIndices(), witnessSubs.getGenericSignature(), SGF.getASTContext()); diff --git a/lib/SILGen/SILGenThunk.cpp b/lib/SILGen/SILGenThunk.cpp index e2c65ef1ee7be..74d1efeceec69 100644 --- a/lib/SILGen/SILGenThunk.cpp +++ b/lib/SILGen/SILGenThunk.cpp @@ -124,8 +124,14 @@ SILGenFunction::emitGlobalFunctionRef(SILLocation loc, SILDeclRef constant, } auto f = SGM.getFunction(constant, NotForDefinition); - assert(f->getLoweredFunctionTypeInContext(B.getTypeExpansionContext()) == - constantInfo.SILFnType); +#ifndef NDEBUG + auto constantFnTypeInContext = + SGM.Types.getLoweredType(constantInfo.SILFnType, + B.getTypeExpansionContext()) + .castTo(); + assert(f->getLoweredFunctionTypeInContext(B.getTypeExpansionContext()) + == constantFnTypeInContext); +#endif if (callPreviousDynamicReplaceableImpl) return B.createPreviousDynamicFunctionRef(loc, f); else @@ -169,7 +175,7 @@ getOrCreateReabstractionThunk(CanSILFunctionType thunkType, SILFunction *SILGenModule::getOrCreateAutoDiffClassMethodThunk( SILDeclRef derivativeFnDeclRef, CanSILFunctionType constantTy) { - auto *derivativeId = derivativeFnDeclRef.derivativeFunctionIdentifier; + auto *derivativeId = derivativeFnDeclRef.getDerivativeFunctionIdentifier(); assert(derivativeId); auto *derivativeFnDecl = derivativeFnDeclRef.getDecl(); diff --git a/lib/SILGen/SILGenType.cpp b/lib/SILGen/SILGenType.cpp index 2dd3de4156090..3f4313f4374b1 100644 --- a/lib/SILGen/SILGenType.cpp +++ b/lib/SILGen/SILGenType.cpp @@ -90,7 +90,7 @@ SILGenModule::emitVTableMethod(ClassDecl *theClass, implFn = getDynamicThunk( derived, Types.getConstantInfo(TypeExpansionContext::minimal(), derived) .SILFnType); - } else if (auto *derivativeId = derived.derivativeFunctionIdentifier) { + } else if (auto *derivativeId = derived.getDerivativeFunctionIdentifier()) { // For JVP/VJP methods, create a vtable entry thunk. The thunk contains an // `differentiable_function` instruction, which is later filled during the // differentiation transform. @@ -168,7 +168,7 @@ SILGenModule::emitVTableMethod(ClassDecl *theClass, base.kind == SILDeclRef::Kind::Allocator); } // TODO(TF-685): Use proper autodiff thunk mangling. - if (auto *derivativeId = derived.derivativeFunctionIdentifier) { + if (auto *derivativeId = derived.getDerivativeFunctionIdentifier()) { switch (derivativeId->getKind()) { case AutoDiffDerivativeFunctionKind::JVP: name += "_jvp"; @@ -743,7 +743,7 @@ SILFunction *SILGenModule::emitProtocolWitness( std::string nameBuffer = NewMangler.mangleWitnessThunk(manglingConformance, requirement.getDecl()); // TODO(TF-685): Proper mangling for derivative witness thunks. - if (auto *derivativeId = requirement.derivativeFunctionIdentifier) { + if (auto *derivativeId = requirement.getDerivativeFunctionIdentifier()) { std::string kindString; switch (derivativeId->getKind()) { case AutoDiffDerivativeFunctionKind::JVP: diff --git a/lib/SILOptimizer/ARC/ARCSequenceOptUtils.cpp b/lib/SILOptimizer/ARC/ARCSequenceOptUtils.cpp index ff04e27b99264..62c501669051b 100644 --- a/lib/SILOptimizer/ARC/ARCSequenceOptUtils.cpp +++ b/lib/SILOptimizer/ARC/ARCSequenceOptUtils.cpp @@ -31,6 +31,7 @@ bool isARCSignificantTerminator(TermInst *TI) { case TermKind::ReturnInst: case TermKind::UnwindInst: case TermKind::YieldInst: + case TermKind::AwaitAsyncContinuationInst: case TermKind::TryApplyInst: case TermKind::SwitchValueInst: case TermKind::SwitchEnumInst: diff --git a/lib/SILOptimizer/Analysis/ARCAnalysis.cpp b/lib/SILOptimizer/Analysis/ARCAnalysis.cpp index c64298fad8890..2b36c0e62a0c0 100644 --- a/lib/SILOptimizer/Analysis/ARCAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/ARCAnalysis.cpp @@ -1084,11 +1084,11 @@ swift::getSingleUnsafeGuaranteedValueResult(BuiltinInst *BI) { if (!TE || TE->getOperand() != BI) return Failed; - if (TE->getFieldNo() == 0 && !GuaranteedValue) { + if (TE->getFieldIndex() == 0 && !GuaranteedValue) { GuaranteedValue = TE; continue; } - if (TE->getFieldNo() == 1 && !Token) { + if (TE->getFieldIndex() == 1 && !Token) { Token = TE; continue; } diff --git a/lib/SILOptimizer/Analysis/AccessSummaryAnalysis.cpp b/lib/SILOptimizer/Analysis/AccessSummaryAnalysis.cpp index b280a5b74efe7..0d0a7de0fda58 100644 --- a/lib/SILOptimizer/Analysis/AccessSummaryAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/AccessSummaryAnalysis.cpp @@ -49,9 +49,9 @@ void AccessSummaryAnalysis::processFunction(FunctionInfo *info, /// started by a begin_access and any flows of the arguments to other /// functions. void AccessSummaryAnalysis::processArgument(FunctionInfo *info, - SILFunctionArgument *argument, - ArgumentSummary &summary, - FunctionOrder &order) { + SILFunctionArgument *argument, + ArgumentSummary &summary, + FunctionOrder &order) { unsigned argumentIndex = argument->getIndex(); // Use a worklist to track argument uses to be processed. @@ -77,7 +77,7 @@ void AccessSummaryAnalysis::processArgument(FunctionInfo *info, // call. if (!callee || callee->empty()) { summary.mergeWith(SILAccessKind::Modify, apply.getLoc(), - getSubPathTrieRoot()); + apply.getModule().getIndexTrieRoot()); continue; } unsigned operandNumber = operand->getOperandNumber(); @@ -468,7 +468,6 @@ AccessSummaryAnalysis::getOrCreateSummary(SILFunction *fn) { void AccessSummaryAnalysis::AccessSummaryAnalysis::invalidate() { FunctionInfos.clear(); Allocator.DestroyAll(); - SubPathTrie.reset(new IndexTrieNode()); } void AccessSummaryAnalysis::invalidate(SILFunction *F, InvalidationKind K) { @@ -506,13 +505,13 @@ getSingleAddressProjectionUser(SingleValueInstruction *I) { switch (User->getKind()) { case SILInstructionKind::StructElementAddrInst: { auto inst = cast(User); - ProjectionIndex = inst->getFieldNo(); + ProjectionIndex = inst->getFieldIndex(); SingleUser = inst; break; } case SILInstructionKind::TupleElementAddrInst: { auto inst = cast(User); - ProjectionIndex = inst->getFieldNo(); + ProjectionIndex = inst->getFieldIndex(); SingleUser = inst; break; } @@ -526,7 +525,7 @@ getSingleAddressProjectionUser(SingleValueInstruction *I) { const IndexTrieNode * AccessSummaryAnalysis::findSubPathAccessed(BeginAccessInst *BAI) { - IndexTrieNode *SubPath = getSubPathTrieRoot(); + IndexTrieNode *SubPath = BAI->getModule().getIndexTrieRoot(); // For each single-user projection of BAI, construct or get a node // from the trie representing the index of the field or tuple element diff --git a/lib/SILOptimizer/Analysis/AccessedStorageAnalysis.cpp b/lib/SILOptimizer/Analysis/AccessedStorageAnalysis.cpp index 90caebc1b3310..64a80796a3d19 100644 --- a/lib/SILOptimizer/Analysis/AccessedStorageAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/AccessedStorageAnalysis.cpp @@ -256,8 +256,7 @@ transformCalleeStorage(const StorageAccessInfo &storage, SILValue argVal = getCallerArg(fullApply, storage.getParamIndex()); if (argVal) { // Remap the argument source value and inherit the old storage info. - auto calleeStorage = findAccessedStorage(argVal); - if (calleeStorage) + if (auto calleeStorage = AccessedStorage::compute(argVal)) return StorageAccessInfo(calleeStorage, storage); } // If the argument can't be transformed, demote it to an unidentified @@ -265,7 +264,7 @@ transformCalleeStorage(const StorageAccessInfo &storage, // // This is an untested bailout. It is only reachable if the call graph // contains an edge that getCallerArg is unable to analyze OR if - // findAccessedStorage returns an invalid SILValue, which won't + // AccessedStorage::compute returns an invalid SILValue, which won't // pass SIL verification. // // FIXME: In case argVal is invalid, support Unidentified access for invalid @@ -301,8 +300,7 @@ void AccessedStorageResult::visitBeginAccess(B *beginAccess) { if (beginAccess->getEnforcement() != SILAccessEnforcement::Dynamic) return; - const AccessedStorage &storage = - findAccessedStorage(beginAccess->getSource()); + auto storage = AccessedStorage::compute(beginAccess->getSource()); if (storage.getKind() == AccessedStorage::Unidentified) { // This also catches invalid storage. diff --git a/lib/SILOptimizer/Analysis/AliasAnalysis.cpp b/lib/SILOptimizer/Analysis/AliasAnalysis.cpp index f9b28518454ca..6e47743ba3310 100644 --- a/lib/SILOptimizer/Analysis/AliasAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/AliasAnalysis.cpp @@ -334,7 +334,7 @@ static bool isAccessedAddressTBAASafe(SILValue V) { if (!V->getType().isAddress()) return false; - SILValue accessedAddress = getAccessedAddress(V); + SILValue accessedAddress = getTypedAccessAddress(V); if (isa(accessedAddress)) return true; diff --git a/lib/SILOptimizer/Analysis/ArraySemantic.cpp b/lib/SILOptimizer/Analysis/ArraySemantic.cpp index abfd4510a4cbe..a6732c60f8132 100644 --- a/lib/SILOptimizer/Analysis/ArraySemantic.cpp +++ b/lib/SILOptimizer/Analysis/ArraySemantic.cpp @@ -672,7 +672,7 @@ static SILValue getArrayUninitializedInitResult(ArraySemanticsCall arrayCall, auto *tupleElt = dyn_cast(op->getUser()); if (!tupleElt) return SILValue(); - if (tupleElt->getFieldNo() != tupleElementIndex) + if (tupleElt->getFieldIndex() != tupleElementIndex) continue; tupleExtractInst = tupleElt; break; diff --git a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp index c651cb5bf2943..bcfb689ea1018 100644 --- a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp @@ -1871,11 +1871,11 @@ EscapeAnalysis::canOptimizeArrayUninitializedCall(ApplyInst *ai) { // uses must be mapped to ConnectionGraph nodes by the client of this API. for (Operand *use : getNonDebugUses(ai)) { if (auto *tei = dyn_cast(use->getUser())) { - if (tei->getFieldNo() == 0 && !call.arrayStruct) { + if (tei->getFieldIndex() == 0 && !call.arrayStruct) { call.arrayStruct = tei; continue; } - if (tei->getFieldNo() == 1 && !call.arrayElementPtr) { + if (tei->getFieldIndex() == 1 && !call.arrayElementPtr) { call.arrayElementPtr = tei; continue; } diff --git a/lib/SILOptimizer/Analysis/FunctionOrder.cpp b/lib/SILOptimizer/Analysis/FunctionOrder.cpp index e0bf1a17ef177..d5806bda01e86 100644 --- a/lib/SILOptimizer/Analysis/FunctionOrder.cpp +++ b/lib/SILOptimizer/Analysis/FunctionOrder.cpp @@ -73,4 +73,3 @@ void BottomUpFunctionOrder::DFS(SILFunction *Start) { TheSCCs.push_back(CurrentSCC); } } - diff --git a/lib/SILOptimizer/Analysis/MemoryBehavior.cpp b/lib/SILOptimizer/Analysis/MemoryBehavior.cpp index 62b15e917b1d3..aac68b590a2d2 100644 --- a/lib/SILOptimizer/Analysis/MemoryBehavior.cpp +++ b/lib/SILOptimizer/Analysis/MemoryBehavior.cpp @@ -66,15 +66,10 @@ class MemoryBehaviorVisitor /// The SILType of the value. Optional TypedAccessTy; - /// Should we treat instructions that increment ref counts as None instead of - /// MayHaveSideEffects. - RetainObserveKind InspectionMode; - public: MemoryBehaviorVisitor(AliasAnalysis *AA, SideEffectAnalysis *SEA, - EscapeAnalysis *EA, SILValue V, - RetainObserveKind IgnoreRefCountIncs) - : AA(AA), SEA(SEA), EA(EA), V(V), InspectionMode(IgnoreRefCountIncs) {} + EscapeAnalysis *EA, SILValue V) + : AA(AA), SEA(SEA), EA(EA), V(V) {} SILType getValueTBAAType() { if (!TypedAccessTy) @@ -83,11 +78,14 @@ class MemoryBehaviorVisitor } /// If 'V' is an address projection within a formal access, return the - /// canonical address of the formal access. Otherwise, return 'V' itself, - /// which is either a reference or unknown pointer or address. + /// canonical address of the formal access if possible without looking past + /// any storage casts. Otherwise, a "best-effort" address + /// + /// If 'V' is an address, then the returned value is also an address. SILValue getValueAddress() { if (!cachedValueAddress) { - cachedValueAddress = V->getType().isAddress() ? getAccessedAddress(V) : V; + cachedValueAddress = + V->getType().isAddress() ? getTypedAccessAddress(V) : V; } return cachedValueAddress; } @@ -152,7 +150,7 @@ class MemoryBehaviorVisitor case SILAccessKind::Modify: if (isLetValue()) { - assert(stripAccessMarkers(beginAccess) != getValueAddress() + assert(getAccessBase(beginAccess) != getValueAddress() && "let modification not allowed"); return MemBehavior::None; } @@ -177,6 +175,10 @@ class MemoryBehaviorVisitor MemBehavior visitCopyAddrInst(CopyAddrInst *CAI); MemBehavior visitApplyInst(ApplyInst *AI); MemBehavior visitTryApplyInst(TryApplyInst *AI); + MemBehavior visitBeginApplyInst(BeginApplyInst *AI); + MemBehavior visitEndApplyInst(EndApplyInst *EAI); + MemBehavior visitAbortApplyInst(AbortApplyInst *AAI); + MemBehavior getApplyBehavior(FullApplySite AS); MemBehavior visitBuiltinInst(BuiltinInst *BI); MemBehavior visitStrongReleaseInst(StrongReleaseInst *BI); MemBehavior visitReleaseValueInst(ReleaseValueInst *BI); @@ -216,16 +218,10 @@ class MemoryBehaviorVisitor SIMPLE_MEMBEHAVIOR_INST(CondFailInst, None) #undef SIMPLE_MEMBEHAVIOR_INST - // If we are asked to treat ref count increments as being inert, return None - // for these. - // - // FIXME: Once we separate the notion of ref counts from reading/writing - // memory this will be unnecessary. + // Incrementing reference counts doesn't have an observable memory effect. #define REFCOUNTINC_MEMBEHAVIOR_INST(Name) \ MemBehavior visit##Name(Name *I) { \ - if (InspectionMode == RetainObserveKind::IgnoreRetains) \ - return MemBehavior::None; \ - return I->getMemoryBehavior(); \ + return MemBehavior::None; \ } REFCOUNTINC_MEMBEHAVIOR_INST(StrongRetainInst) REFCOUNTINC_MEMBEHAVIOR_INST(RetainValueInst) @@ -258,8 +254,7 @@ MemBehavior MemoryBehaviorVisitor::visitLoadInst(LoadInst *LI) { MemBehavior MemoryBehaviorVisitor::visitStoreInst(StoreInst *SI) { // No store besides the initialization of a "let"-variable // can have any effect on the value of this "let" variable. - if (isLetValue() - && (getAccessedAddress(SI->getDest()) != getValueAddress())) { + if (isLetValue() && (getAccessBase(SI->getDest()) != getValueAddress())) { return MemBehavior::None; } // If the store dest cannot alias the pointer in question, then the @@ -333,74 +328,139 @@ MemBehavior MemoryBehaviorVisitor::visitBuiltinInst(BuiltinInst *BI) { } MemBehavior MemoryBehaviorVisitor::visitTryApplyInst(TryApplyInst *AI) { - MemBehavior Behavior = MemBehavior::MayHaveSideEffects; - // Ask escape analysis. - if (!EA->canEscapeTo(V, AI)) - Behavior = MemBehavior::None; - - // Otherwise be conservative and return that we may have side effects. - LLVM_DEBUG(llvm::dbgs() << " Found tryapply, returning " << Behavior <<'\n'); - return Behavior; + return getApplyBehavior(AI); } MemBehavior MemoryBehaviorVisitor::visitApplyInst(ApplyInst *AI) { + return getApplyBehavior(AI); +} + +MemBehavior MemoryBehaviorVisitor::visitBeginApplyInst(BeginApplyInst *AI) { + return getApplyBehavior(AI); +} - FunctionSideEffects ApplyEffects; - SEA->getCalleeEffects(ApplyEffects, AI); +MemBehavior MemoryBehaviorVisitor::visitEndApplyInst(EndApplyInst *EAI) { + return getApplyBehavior(EAI->getBeginApply()); +} - MemBehavior Behavior = MemBehavior::None; +MemBehavior MemoryBehaviorVisitor::visitAbortApplyInst(AbortApplyInst *AAI) { + return getApplyBehavior(AAI->getBeginApply()); +} - // We can ignore mayTrap(). - bool any_in_guaranteed_params = false; - for (auto op : enumerate(AI->getArgumentOperands())) { - if (op.value().get() == V && - AI->getSubstCalleeConv().getSILArgumentConvention(op.index()) == swift::SILArgumentConvention::Indirect_In_Guaranteed) { - any_in_guaranteed_params = true; - break; +/// Returns true if the \p address may have any users which let the address +/// escape in an unusual way, e.g. with an address_to_pointer instruction. +static bool hasEscapingUses(SILValue address, int &numChecks) { + for (Operand *use : address->getUses()) { + SILInstruction *user = use->getUser(); + + // Avoid quadratic complexity in corner cases. A limit of 24 is more than + // enough in most cases. + if (++numChecks > 24) + return true; + + switch (user->getKind()) { + case SILInstructionKind::DebugValueAddrInst: + case SILInstructionKind::FixLifetimeInst: + case SILInstructionKind::LoadInst: + case SILInstructionKind::StoreInst: + case SILInstructionKind::CopyAddrInst: + case SILInstructionKind::DestroyAddrInst: + case SILInstructionKind::DeallocStackInst: + case SILInstructionKind::EndAccessInst: + // Those instructions have no result and cannot escape the address. + break; + case SILInstructionKind::ApplyInst: + case SILInstructionKind::TryApplyInst: + case SILInstructionKind::BeginApplyInst: + // Apply instructions can not let an address escape either. It's not + // possible that an address, passed as an indirect parameter, escapes + // the function in any way (which is not unsafe and undefined behavior). + break; + case SILInstructionKind::BeginAccessInst: + case SILInstructionKind::OpenExistentialAddrInst: + case SILInstructionKind::UncheckedTakeEnumDataAddrInst: + case SILInstructionKind::StructElementAddrInst: + case SILInstructionKind::TupleElementAddrInst: + case SILInstructionKind::UncheckedAddrCastInst: + // Check the uses of address projections. + if (hasEscapingUses(cast(user), numChecks)) + return true; + break; + case SILInstructionKind::AddressToPointerInst: + // This is _the_ instruction which can let an address escape. + return true; + default: + // To be conservative, also bail for anything we don't handle here. + return true; } } + return false; +} - if (any_in_guaranteed_params) { - // one the parameters in the function call is @in_guaranteed of V, ie. the - // callee isn't allowed to modify it. - Behavior = MemBehavior::MayRead; - } else if (ApplyEffects.mayReadRC() || - (InspectionMode == RetainObserveKind::ObserveRetains && - ApplyEffects.mayAllocObjects())) { - Behavior = MemBehavior::MayHaveSideEffects; - } else { - auto &GlobalEffects = ApplyEffects.getGlobalEffects(); - Behavior = GlobalEffects.getMemBehavior(InspectionMode); - - // Check all parameter effects. - for (unsigned Idx = 0, End = AI->getNumArguments(); - Idx < End && Behavior < MemBehavior::MayHaveSideEffects; ++Idx) { - auto &ArgEffect = ApplyEffects.getParameterEffects()[Idx]; - auto ArgBehavior = ArgEffect.getMemBehavior(InspectionMode); - if (ArgEffect.mayRelease()) { - Behavior = MemBehavior::MayHaveSideEffects; - break; - } - auto NewBehavior = combineMemoryBehavior(Behavior, ArgBehavior); - if (NewBehavior != Behavior) { - SILValue Arg = AI->getArgument(Idx); - // We only consider the argument effects if the argument aliases V. - if (!Arg->getType().isAddress() || mayAlias(Arg)) - Behavior = NewBehavior; - } +MemBehavior MemoryBehaviorVisitor::getApplyBehavior(FullApplySite AS) { + + // Do a quick check first: if V is directly passed to an in_guaranteed + // argument, we know that the function cannot write to it. + for (Operand &argOp : AS.getArgumentOperands()) { + if (argOp.get() == V && + AS.getArgumentConvention(argOp) == + swift::SILArgumentConvention::Indirect_In_Guaranteed) { + return MemBehavior::MayRead; } } - if (Behavior > MemBehavior::None) { - if (Behavior > MemBehavior::MayRead && isLetValue()) - Behavior = MemBehavior::MayRead; + SILValue object = getUnderlyingObject(V); + int numUsesChecked = 0; + + // For exclusive/local addresses we can do a quick and good check with alias + // analysis. For everything else we use escape analysis (see below). + // TODO: The check for not-escaping can probably done easier with the upcoming + // API of AccessStorage. + bool nonEscapingAddress = + (isa(object) || isExclusiveArgument(object)) && + !hasEscapingUses(object, numUsesChecked); + + FunctionSideEffects applyEffects; + SEA->getCalleeEffects(applyEffects, AS); + + MemBehavior behavior = MemBehavior::None; + MemBehavior globalBehavior = applyEffects.getGlobalEffects().getMemBehavior( + RetainObserveKind::IgnoreRetains); + + // If it's a non-escaping address, we don't care about the "global" effects + // of the called function. + if (!nonEscapingAddress) + behavior = globalBehavior; + + // Check all parameter effects. + for (unsigned argIdx = 0, end = AS.getNumArguments(); + argIdx < end && behavior < MemBehavior::MayHaveSideEffects; + ++argIdx) { + SILValue arg = AS.getArgument(argIdx); + + // In case the argument is not an address, alias analysis will always report + // a no-alias. Therefore we have to treat non-address arguments + // conservatively here. For example V could be a ref_element_addr of a + // reference argument. In this case V clearly "aliases" the argument, but + // this is not reported by alias analysis. + if ((!nonEscapingAddress && !arg->getType().isAddress()) || + mayAlias(arg)) { + MemBehavior argBehavior = applyEffects.getArgumentBehavior(AS, argIdx); + behavior = combineMemoryBehavior(behavior, argBehavior); + } + } + + if (behavior > MemBehavior::None) { + if (behavior > MemBehavior::MayRead && isLetValue()) + behavior = MemBehavior::MayRead; // Ask escape analysis. - if (!EA->canEscapeTo(V, AI)) - Behavior = MemBehavior::None; + if (!EA->canEscapeTo(V, AS)) + behavior = MemBehavior::None; } - LLVM_DEBUG(llvm::dbgs() << " Found apply, returning " << Behavior << '\n'); - return Behavior; + LLVM_DEBUG(llvm::dbgs() << " Found apply, returning " << behavior << '\n'); + + return behavior; } MemBehavior @@ -442,9 +502,8 @@ visitBeginCOWMutationInst(BeginCOWMutationInst *BCMI) { //===----------------------------------------------------------------------===// MemBehavior -AliasAnalysis::computeMemoryBehavior(SILInstruction *Inst, SILValue V, - RetainObserveKind InspectionMode) { - MemBehaviorKeyTy Key = toMemoryBehaviorKey(Inst, V, InspectionMode); +AliasAnalysis::computeMemoryBehavior(SILInstruction *Inst, SILValue V) { + MemBehaviorKeyTy Key = toMemoryBehaviorKey(Inst, V); // Check if we've already computed this result. auto It = MemoryBehaviorCache.find(Key); if (It != MemoryBehaviorCache.end()) { @@ -457,27 +516,25 @@ AliasAnalysis::computeMemoryBehavior(SILInstruction *Inst, SILValue V, MemoryBehaviorNodeToIndex.clear(); // Key is no longer valid as we cleared the MemoryBehaviorNodeToIndex. - Key = toMemoryBehaviorKey(Inst, V, InspectionMode); + Key = toMemoryBehaviorKey(Inst, V); } // Calculate the aliasing result and store it in the cache. - auto Result = computeMemoryBehaviorInner(Inst, V, InspectionMode); + auto Result = computeMemoryBehaviorInner(Inst, V); MemoryBehaviorCache[Key] = Result; return Result; } MemBehavior -AliasAnalysis::computeMemoryBehaviorInner(SILInstruction *Inst, SILValue V, - RetainObserveKind InspectionMode) { +AliasAnalysis::computeMemoryBehaviorInner(SILInstruction *Inst, SILValue V) { LLVM_DEBUG(llvm::dbgs() << "GET MEMORY BEHAVIOR FOR:\n " << *Inst << " " << *V); assert(SEA && "SideEffectsAnalysis must be initialized!"); - return MemoryBehaviorVisitor(this, SEA, EA, V, InspectionMode).visit(Inst); + return MemoryBehaviorVisitor(this, SEA, EA, V).visit(Inst); } MemBehaviorKeyTy AliasAnalysis::toMemoryBehaviorKey(SILInstruction *V1, - SILValue V2, - RetainObserveKind M) { + SILValue V2) { size_t idx1 = MemoryBehaviorNodeToIndex.getIndex(V1->getRepresentativeSILNodeInObject()); assert(idx1 != std::numeric_limits::max() && @@ -486,5 +543,5 @@ MemBehaviorKeyTy AliasAnalysis::toMemoryBehaviorKey(SILInstruction *V1, V2->getRepresentativeSILNodeInObject()); assert(idx2 != std::numeric_limits::max() && "~0 index reserved for empty/tombstone keys"); - return {idx1, idx2, M}; + return {idx1, idx2}; } diff --git a/lib/SILOptimizer/Analysis/RCIdentityAnalysis.cpp b/lib/SILOptimizer/Analysis/RCIdentityAnalysis.cpp index e9e07a1a13f0a..45394c1376aa7 100644 --- a/lib/SILOptimizer/Analysis/RCIdentityAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/RCIdentityAnalysis.cpp @@ -13,6 +13,7 @@ #include "swift/SILOptimizer/Analysis/RCIdentityAnalysis.h" #include "swift/SILOptimizer/Analysis/DominanceAnalysis.h" #include "swift/SIL/SILInstruction.h" +#include "swift/SIL/DynamicCasts.h" #include "llvm/Support/CommandLine.h" using namespace swift; @@ -42,20 +43,26 @@ static bool isNoPayloadEnum(SILValue V) { /// necessarily preserve RC identity because it may cast from a /// reference-counted type to a non-reference counted type, or from a larger to /// a smaller struct with fewer references. -static bool isRCIdentityPreservingCast(ValueKind Kind) { - switch (Kind) { +static SILValue getRCIdentityPreservingCastOperand(SILValue V) { + switch (V->getKind()) { case ValueKind::UpcastInst: case ValueKind::UncheckedRefCastInst: - case ValueKind::UnconditionalCheckedCastInst: case ValueKind::InitExistentialRefInst: case ValueKind::OpenExistentialRefInst: case ValueKind::RefToBridgeObjectInst: case ValueKind::BridgeObjectToRefInst: case ValueKind::ConvertFunctionInst: - return true; + return cast(V)->getOperand(0); + case ValueKind::UnconditionalCheckedCastInst: { + auto *castInst = cast(V); + if (SILDynamicCastInst(castInst).isRCIdentityPreserving()) + return castInst->getOperand(); + break; + } default: - return false; + break; } + return SILValue(); } //===----------------------------------------------------------------------===// @@ -64,8 +71,8 @@ static bool isRCIdentityPreservingCast(ValueKind Kind) { static SILValue stripRCIdentityPreservingInsts(SILValue V) { // First strip off RC identity preserving casts. - if (isRCIdentityPreservingCast(V->getKind())) - return cast(V)->getOperand(0); + if (SILValue castOp = getRCIdentityPreservingCastOperand(V)) + return castOp; // Then if we have a struct_extract that is extracting a non-trivial member // from a struct with no other non-trivial members, a ref count operation on @@ -116,9 +123,16 @@ static SILValue stripRCIdentityPreservingInsts(SILValue V) { // since we will only visit it twice if we go around a back edge due to a // different SILArgument that is actually being used for its phi node like // purposes. - if (auto *A = dyn_cast(V)) - if (SILValue Result = A->getSingleTerminatorOperand()) + if (auto *A = dyn_cast(V)) { + if (SILValue Result = A->getSingleTerminatorOperand()) { + // In case the terminator is a conditional cast, Result is the source of + // the cast. + auto dynCast = SILDynamicCastInst::getAs(A->getSingleTerminator()); + if (dynCast && !dynCast.isRCIdentityPreserving()) + return SILValue(); return Result; + } + } return SILValue(); } @@ -321,9 +335,15 @@ SILValue RCIdentityFunctionInfo::stripRCIdentityPreservingArgs(SILValue V, } unsigned IVListSize = IncomingValues.size(); - - assert(IVListSize != 1 && "Should have been handled in " - "stripRCIdentityPreservingInsts"); + if (IVListSize == 1) { +#ifndef NDEBUG + auto dynCast = SILDynamicCastInst::getAs(A->getSingleTerminator()); + assert((dynCast && !dynCast.isRCIdentityPreserving()) + && "Should have been handled in stripRCIdentityPreservingInsts"); +#endif + return SILValue(); + + } // Ok, we have multiple predecessors. See if all of them are the same // value. If so, just return that value. diff --git a/lib/SILOptimizer/Analysis/SideEffectAnalysis.cpp b/lib/SILOptimizer/Analysis/SideEffectAnalysis.cpp index 0712744d5a5a8..017edb97342a4 100644 --- a/lib/SILOptimizer/Analysis/SideEffectAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/SideEffectAnalysis.cpp @@ -219,6 +219,29 @@ FunctionSideEffects::getMemBehavior(RetainObserveKind ScanKind) const { return Behavior; } +MemoryBehavior +FunctionSideEffects::getArgumentBehavior(FullApplySite applySite, + unsigned argIdx) { + // The overall argument effect is the combination of the argument and the + // global effects. + MemoryBehavior behavior = + GlobalEffects.getMemBehavior(RetainObserveKind::IgnoreRetains); + MemoryBehavior argBehavior = + ParamEffects[argIdx].getMemBehavior(RetainObserveKind::IgnoreRetains); + + behavior = combineMemoryBehavior(behavior, argBehavior); + + if (behavior > MemoryBehavior::MayRead && + applySite.getArgumentConvention(applySite.getArgumentRef(argIdx)) == + SILArgumentConvention::Indirect_In_Guaranteed) { + // Even if side-effect analysis doesn't know anything about the called + // called function, the in_guaranteed convention guarantees that the + // argument is never written to. + return MemoryBehavior::MayRead; + } + return behavior; +} + bool FunctionSideEffects::mergeFrom(const FunctionSideEffects &RHS) { bool Changed = mergeFlags(RHS); Changed |= GlobalEffects.mergeFrom(RHS.GlobalEffects); diff --git a/lib/SILOptimizer/Analysis/SimplifyInstruction.cpp b/lib/SILOptimizer/Analysis/SimplifyInstruction.cpp index 86d91eb494161..0a1d5083ce5e6 100644 --- a/lib/SILOptimizer/Analysis/SimplifyInstruction.cpp +++ b/lib/SILOptimizer/Analysis/SimplifyInstruction.cpp @@ -97,7 +97,7 @@ SILValue InstSimplifier::visitStructInst(StructInst *SI) { return SILValue(); // And the order of the field must be identical to the construction order. - if (Ex->getFieldNo() != i) + if (Ex->getFieldIndex() != i) return SILValue(); } @@ -132,7 +132,7 @@ SILValue InstSimplifier::visitTupleInst(TupleInst *TI) { return SILValue(); // And the order of the field must be identical to the construction order. - if (Ex->getFieldNo() != i) + if (Ex->getFieldIndex() != i) return SILValue(); } @@ -145,11 +145,11 @@ SILValue InstSimplifier::visitTupleInst(TupleInst *TI) { SILValue InstSimplifier::visitTupleExtractInst(TupleExtractInst *TEI) { // tuple_extract(tuple(x, y), 0) -> x if (auto *TheTuple = dyn_cast(TEI->getOperand())) - return TheTuple->getElement(TEI->getFieldNo()); + return TheTuple->getElement(TEI->getFieldIndex()); // tuple_extract(apply([add|sub|...]overflow(x,y)), 0) -> x // tuple_extract(apply(checked_trunc(ext(x))), 0) -> x - if (TEI->getFieldNo() == 0) + if (TEI->getFieldIndex() == 0) if (auto *BI = dyn_cast(TEI->getOperand())) return simplifyOverflowBuiltin(BI); @@ -488,7 +488,8 @@ SILValue InstSimplifier::visitMetatypeInst(MetatypeInst *MI) { || instanceType.getStructOrBoundGenericStruct() || instanceType.getEnumOrBoundGenericEnum()) { for (SILArgument *argument : MI->getFunction()->getArguments()) { - if (argument->getType().getASTType() == metaType) + if (argument->getType().getASTType() == metaType && + argument->getType().isObject()) return argument; } } diff --git a/lib/SILOptimizer/Analysis/ValueTracking.cpp b/lib/SILOptimizer/Analysis/ValueTracking.cpp index 17a8994f8f9e9..32cfcce18a344 100644 --- a/lib/SILOptimizer/Analysis/ValueTracking.cpp +++ b/lib/SILOptimizer/Analysis/ValueTracking.cpp @@ -136,7 +136,7 @@ IsZeroKind swift::isZeroValue(SILValue Value) { if (auto *T = dyn_cast(Value)) { // Make sure we are extracting the number value and not // the overflow flag. - if (T->getFieldNo() != 0) + if (T->getFieldIndex() != 0) return IsZeroKind::Unknown; auto *BI = dyn_cast(T->getOperand()); diff --git a/lib/SILOptimizer/CMakeLists.txt b/lib/SILOptimizer/CMakeLists.txt index ec1440d2b06a1..c9ddd901ab5dc 100644 --- a/lib/SILOptimizer/CMakeLists.txt +++ b/lib/SILOptimizer/CMakeLists.txt @@ -1,3 +1,4 @@ +set_swift_llvm_is_available() add_swift_host_library(swiftSILOptimizer STATIC SILOptimizer.cpp) target_link_libraries(swiftSILOptimizer PRIVATE diff --git a/lib/SILOptimizer/Differentiation/JVPCloner.cpp b/lib/SILOptimizer/Differentiation/JVPCloner.cpp index 26477af0d56aa..9f068289716e1 100644 --- a/lib/SILOptimizer/Differentiation/JVPCloner.cpp +++ b/lib/SILOptimizer/Differentiation/JVPCloner.cpp @@ -1113,7 +1113,7 @@ class JVPCloner::Implementation final auto loc = tei->getLoc(); auto origTupleTy = tei->getOperand()->getType().castTo(); unsigned tanIndex = 0; - for (unsigned i : range(tei->getFieldNo())) { + for (unsigned i : range(tei->getFieldIndex())) { if (getTangentSpace( origTupleTy->getElement(i).getType()->getCanonicalType())) ++tanIndex; @@ -1142,7 +1142,7 @@ class JVPCloner::Implementation final auto &diffBuilder = getDifferentialBuilder(); auto origTupleTy = teai->getOperand()->getType().castTo(); unsigned tanIndex = 0; - for (unsigned i : range(teai->getFieldNo())) { + for (unsigned i : range(teai->getFieldIndex())) { if (getTangentSpace( origTupleTy->getElement(i).getType()->getCanonicalType())) ++tanIndex; diff --git a/lib/SILOptimizer/Differentiation/PullbackCloner.cpp b/lib/SILOptimizer/Differentiation/PullbackCloner.cpp index c0be157f43174..38f94d73f8fec 100644 --- a/lib/SILOptimizer/Differentiation/PullbackCloner.cpp +++ b/lib/SILOptimizer/Differentiation/PullbackCloner.cpp @@ -336,6 +336,7 @@ class PullbackCloner::Implementation final case AdjointValueKind::Concrete: return val.getConcreteValue(); } + llvm_unreachable("unhandled adjoint value kind!"); } /// Materializes an adjoint value indirectly to a SIL buffer. @@ -1350,7 +1351,7 @@ class PullbackCloner::Implementation final if (!getTangentSpace( tupleTy->getElement(i).getType()->getCanonicalType())) continue; - if (tei->getFieldNo() == i) + if (tei->getFieldIndex() == i) elements.push_back(av); else elements.push_back(makeZeroAdjointValue( @@ -2718,7 +2719,7 @@ SILValue PullbackCloner::Implementation::getAdjointProjection( return adjSource; auto origTupleTy = source->getType().castTo(); unsigned adjIndex = 0; - for (unsigned i : range(teai->getFieldNo())) { + for (unsigned i : range(teai->getFieldIndex())) { if (getTangentSpace( origTupleTy->getElement(i).getType()->getCanonicalType())) ++adjIndex; diff --git a/lib/SILOptimizer/IPO/DeadFunctionElimination.cpp b/lib/SILOptimizer/IPO/DeadFunctionElimination.cpp index c72e35aee7817..4908107793fe4 100644 --- a/lib/SILOptimizer/IPO/DeadFunctionElimination.cpp +++ b/lib/SILOptimizer/IPO/DeadFunctionElimination.cpp @@ -105,6 +105,11 @@ class FunctionLivenessComputation { if (F->isDynamicallyReplaceable()) return true; + // Don't remove pre-specialized functions. We need to preserver the + // pre-specialization specifications from other modules. + if (F->hasPrespecialization()) + return true; + // ObjC functions are called through the runtime and are therefore alive // even if not referenced inside SIL. if (F->getRepresentation() == SILFunctionTypeRepresentation::ObjCMethod) @@ -391,6 +396,11 @@ class FunctionLivenessComputation { ensureAlive(&F); } + // Make sure that functions referenced by _specialize(target: targetFun()) + // are kept alive. + F.forEachSpecializeAttrTargetFunction( + [this](SILFunction *targetFun) { ensureAlive(targetFun); }); + if (!F.shouldOptimize()) { LLVM_DEBUG(llvm::dbgs() << " anchor a no optimization function: " << F.getName() << "\n"); diff --git a/lib/SILOptimizer/IPO/GlobalOpt.cpp b/lib/SILOptimizer/IPO/GlobalOpt.cpp index 9c9e7bfc3b7a9..e8093e53dffc3 100644 --- a/lib/SILOptimizer/IPO/GlobalOpt.cpp +++ b/lib/SILOptimizer/IPO/GlobalOpt.cpp @@ -358,7 +358,7 @@ static void replaceLoadsFromGlobal(SILValue addr, if (auto *teai = dyn_cast(user)) { auto *ti = cast(initVal); auto *member = cast( - ti->getElement(teai->getFieldNo())); + ti->getElement(teai->getFieldIndex())); replaceLoadsFromGlobal(teai, member, cloner); continue; } diff --git a/lib/SILOptimizer/LoopTransforms/ArrayOpt.h b/lib/SILOptimizer/LoopTransforms/ArrayOpt.h index 00bf1e0d9b492..2139d2a5aabec 100644 --- a/lib/SILOptimizer/LoopTransforms/ArrayOpt.h +++ b/lib/SILOptimizer/LoopTransforms/ArrayOpt.h @@ -63,7 +63,7 @@ class StructUseCollector { /// Do not form a path with an IndexAddrInst because we have no way to /// distinguish between indexing and subelement access. The same index could /// either refer to the next element (indexed) or a subelement. - static SILValue getAccessPath(SILValue V, SmallVectorImpl& Path) { + static SILValue getAccessPath(SILValue V, SmallVectorImpl &Path) { V = stripCasts(V); if (auto *IA = dyn_cast(V)) { // Don't include index_addr projections in the access path. We could if @@ -89,7 +89,7 @@ class StructUseCollector { VisitedSet Visited; /// Collect all uses of the value at the given address. - void collectUses(ValueBase *V, ArrayRef AccessPath) { + void collectUses(ValueBase *V, ArrayRef AccessPath) { // Save our old indent and increment. // Collect all users of the address and loads. collectAddressUses(V, AccessPath, nullptr); @@ -142,7 +142,7 @@ class StructUseCollector { /// StructVal is invalid, then the value is the address of the Struct. If /// StructVal is valid, the value is the address of an element within the /// Struct. - void collectAddressUses(ValueBase *V, ArrayRef AccessPathSuffix, + void collectAddressUses(ValueBase *V, ArrayRef AccessPathSuffix, Operand *StructVal) { for (auto *UI : V->getUses()) { // Keep the operand, not the instruction in the visited set. The same diff --git a/lib/SILOptimizer/LoopTransforms/ArrayPropertyOpt.cpp b/lib/SILOptimizer/LoopTransforms/ArrayPropertyOpt.cpp index 76a30bfaca3d2..9e9334ff59061 100644 --- a/lib/SILOptimizer/LoopTransforms/ArrayPropertyOpt.cpp +++ b/lib/SILOptimizer/LoopTransforms/ArrayPropertyOpt.cpp @@ -402,7 +402,7 @@ class ArrayPropertiesAnalysis { if (!Call.canHoist(Preheader->getTerminator(), DomTree)) return false; - SmallVector AccessPath; + SmallVector AccessPath; SILValue ArrayContainer = StructUseCollector::getAccessPath(Arr, AccessPath); diff --git a/lib/SILOptimizer/LoopTransforms/COWArrayOpt.cpp b/lib/SILOptimizer/LoopTransforms/COWArrayOpt.cpp index 1141d8dd7ea6d..ed54541cfb107 100644 --- a/lib/SILOptimizer/LoopTransforms/COWArrayOpt.cpp +++ b/lib/SILOptimizer/LoopTransforms/COWArrayOpt.cpp @@ -843,7 +843,7 @@ bool COWArrayOpt::hoistMakeMutable(ArraySemanticsCall MakeMutable, return false; } - SmallVector AccessPath; + SmallVector AccessPath; SILValue ArrayContainer = StructUseCollector::getAccessPath(CurrentArrayAddr, AccessPath); bool arrayContainerIsUnique = checkUniqueArrayContainer(ArrayContainer); diff --git a/lib/SILOptimizer/LoopTransforms/LICM.cpp b/lib/SILOptimizer/LoopTransforms/LICM.cpp index 69a0f32be343d..bf489fe892e13 100644 --- a/lib/SILOptimizer/LoopTransforms/LICM.cpp +++ b/lib/SILOptimizer/LoopTransforms/LICM.cpp @@ -700,18 +700,18 @@ static bool analyzeBeginAccess(BeginAccessInst *BI, InstSet &SideEffectInsts, AccessedStorageAnalysis *ASA, DominanceInfo *DT) { - const AccessedStorage &storage = findAccessedStorage(BI->getSource()); + auto storage = AccessedStorage::compute(BI->getSource()); if (!storage) { return false; } - auto BIAccessedStorageNonNested = findAccessedStorage(BI); + auto BIAccessedStorageNonNested = AccessedStorage::compute(BI); auto safeBeginPred = [&](BeginAccessInst *OtherBI) { if (BI == OtherBI) { return true; } return BIAccessedStorageNonNested.isDistinctFrom( - findAccessedStorage(OtherBI)); + AccessedStorage::compute(OtherBI)); }; if (!std::all_of(BeginAccesses.begin(), BeginAccesses.end(), safeBeginPred)) @@ -940,7 +940,7 @@ static SILValue projectLoadValue(SILValue addr, SILValue rootAddr, SILValue val = projectLoadValue(TEI->getOperand(), rootAddr, rootVal, beforeInst); SILBuilder B(beforeInst); - return B.createTupleExtract(beforeInst->getLoc(), val, TEI->getFieldNo(), + return B.createTupleExtract(beforeInst->getLoc(), val, TEI->getFieldIndex(), TEI->getType().getObjectType()); } llvm_unreachable("unknown projection"); diff --git a/lib/SILOptimizer/Mandatory/AddressLowering.cpp b/lib/SILOptimizer/Mandatory/AddressLowering.cpp index 788dd4760e03a..d4814087f681a 100644 --- a/lib/SILOptimizer/Mandatory/AddressLowering.cpp +++ b/lib/SILOptimizer/Mandatory/AddressLowering.cpp @@ -891,7 +891,7 @@ void ApplyRewriter::convertApplyWithIndirectResults() { if (origCallInst->getType().is()) { for (Operand *operand : origCallInst->getUses()) { if (auto *extract = dyn_cast(operand->getUser())) - origDirectResultValues[extract->getFieldNo()] = extract; + origDirectResultValues[extract->getFieldIndex()] = extract; else nonCanonicalUses.push_back(operand); } @@ -1002,7 +1002,7 @@ void ApplyRewriter::convertApplyWithIndirectResults() { assert(pass.valueStorageMap.contains(origCallInst)); continue; } - unsigned origResultIdx = extractInst->getFieldNo(); + unsigned origResultIdx = extractInst->getFieldIndex(); auto resultInfo = origFnConv.getResults()[origResultIdx]; if (extractInst->getType().isAddressOnly(*pass.F)) { diff --git a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp index 4c884da10b911..3f5a40f12c0aa 100644 --- a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp +++ b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp @@ -466,48 +466,6 @@ void DIElementUseInfo::trackStoreToSelf(SILInstruction *I) { StoresToSelf.push_back(I); } -//===----------------------------------------------------------------------===// -// Scalarization Logic -//===----------------------------------------------------------------------===// - -/// Given a pointer to a tuple type, compute the addresses of each element and -/// add them to the ElementAddrs vector. -static void -getScalarizedElementAddresses(SILValue Pointer, SILBuilder &B, SILLocation Loc, - SmallVectorImpl &ElementAddrs) { - TupleType *TT = Pointer->getType().castTo(); - for (auto Index : indices(TT->getElements())) { - ElementAddrs.push_back(B.createTupleElementAddr(Loc, Pointer, Index)); - } -} - -/// Given an RValue of aggregate type, compute the values of the elements by -/// emitting a destructure. -static void getScalarizedElements(SILValue V, - SmallVectorImpl &ElementVals, - SILLocation Loc, SILBuilder &B) { - auto *DTI = B.createDestructureTuple(Loc, V); - llvm::copy(DTI->getResults(), std::back_inserter(ElementVals)); -} - -/// Scalarize a load down to its subelements. If NewLoads is specified, this -/// can return the newly generated sub-element loads. -static SILValue scalarizeLoad(LoadInst *LI, - SmallVectorImpl &ElementAddrs) { - SILBuilderWithScope B(LI); - SmallVector ElementTmps; - - for (unsigned i = 0, e = ElementAddrs.size(); i != e; ++i) { - auto *SubLI = B.createTrivialLoadOr(LI->getLoc(), ElementAddrs[i], - LI->getOwnershipQualifier()); - ElementTmps.push_back(SubLI); - } - - if (LI->getType().is()) - return B.createTuple(LI->getLoc(), LI->getType(), ElementTmps); - return B.createStruct(LI->getLoc(), LI->getType(), ElementTmps); -} - //===----------------------------------------------------------------------===// // ElementUseCollector Implementation //===----------------------------------------------------------------------===// @@ -613,7 +571,7 @@ void ElementUseCollector::collectTupleElementUses(TupleElementAddrInst *TEAI, // tuple_element_addr P, 42 indexes into the current tuple element. // Recursively process its uses with the adjusted element number. - unsigned FieldNo = TEAI->getFieldNo(); + unsigned FieldNo = TEAI->getFieldIndex(); auto T = TEAI->getOperand()->getType(); if (T.is()) { for (unsigned i = 0; i != FieldNo; ++i) { @@ -671,11 +629,6 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { "Walked through the pointer to the value?"); SILType PointeeType = Pointer->getType().getObjectType(); - // This keeps track of instructions in the use list that touch multiple tuple - // elements and should be scalarized. This is done as a second phase to - // avoid invalidating the use iterator. - SmallVector UsesToScalarize; - for (auto *Op : Pointer->getUses()) { auto *User = Op->getUser(); @@ -705,10 +658,7 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { // Loads are a use of the value. if (isa(User)) { - if (PointeeType.is()) - UsesToScalarize.push_back(User); - else - addElementUses(BaseEltNo, PointeeType, User, DIUseKind::Load); + addElementUses(BaseEltNo, PointeeType, User, DIUseKind::Load); continue; } @@ -730,13 +680,6 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { if ((isa(User) || isa(User) || isa(User)) && Op->getOperandNumber() == 1) { - if (PointeeType.is()) { - assert(!isa(User) && - "cannot assign a typle with assign_by_wrapper"); - UsesToScalarize.push_back(User); - continue; - } - // Coming out of SILGen, we assume that raw stores are initializations, // unless they have trivial type (which we classify as InitOrAssign). DIUseKind Kind; @@ -769,13 +712,6 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { #include "swift/AST/ReferenceStorage.def" if (auto *CAI = dyn_cast(User)) { - // If this is a copy of a tuple, we should scalarize it so that we don't - // have an access that crosses elements. - if (PointeeType.is()) { - UsesToScalarize.push_back(CAI); - continue; - } - // If this is the source of the copy_addr, then this is a load. If it is // the destination, then this is an unknown assignment. Note that we'll // revisit this instruction and add it to Uses twice if it is both a load @@ -988,88 +924,6 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { // Otherwise, the use is something complicated, it escapes. addElementUses(BaseEltNo, PointeeType, User, DIUseKind::Escape); } - - // Now that we've walked all of the immediate uses, scalarize any operations - // working on tuples if we need to for canonicalization or analysis reasons. - if (!UsesToScalarize.empty()) { - SILInstruction *PointerInst = Pointer->getDefiningInstruction(); - SmallVector ElementAddrs; - SILBuilderWithScope AddrBuilder(++SILBasicBlock::iterator(PointerInst), - PointerInst); - getScalarizedElementAddresses(Pointer, AddrBuilder, PointerInst->getLoc(), - ElementAddrs); - - SmallVector ElementTmps; - for (auto *User : UsesToScalarize) { - ElementTmps.clear(); - - LLVM_DEBUG(llvm::errs() << " *** Scalarizing: " << *User << "\n"); - - // Scalarize LoadInst - if (auto *LI = dyn_cast(User)) { - SILValue Result = scalarizeLoad(LI, ElementAddrs); - LI->replaceAllUsesWith(Result); - LI->eraseFromParent(); - continue; - } - - // Scalarize AssignInst - if (auto *AI = dyn_cast(User)) { - SILBuilderWithScope B(User, AI); - getScalarizedElements(AI->getOperand(0), ElementTmps, AI->getLoc(), B); - - for (unsigned i = 0, e = ElementAddrs.size(); i != e; ++i) - B.createAssign(AI->getLoc(), ElementTmps[i], ElementAddrs[i], - AssignOwnershipQualifier::Unknown); - AI->eraseFromParent(); - continue; - } - - // Scalarize StoreInst - if (auto *SI = dyn_cast(User)) { - SILBuilderWithScope B(User, SI); - getScalarizedElements(SI->getOperand(0), ElementTmps, SI->getLoc(), B); - - for (unsigned i = 0, e = ElementAddrs.size(); i != e; ++i) - B.createTrivialStoreOr(SI->getLoc(), ElementTmps[i], ElementAddrs[i], - SI->getOwnershipQualifier()); - SI->eraseFromParent(); - continue; - } - - // Scalarize CopyAddrInst. - auto *CAI = cast(User); - SILBuilderWithScope B(User, CAI); - - // Determine if this is a copy *from* or *to* "Pointer". - if (CAI->getSrc() == Pointer) { - // Copy from pointer. - getScalarizedElementAddresses(CAI->getDest(), B, CAI->getLoc(), - ElementTmps); - for (unsigned i = 0, e = ElementAddrs.size(); i != e; ++i) - B.createCopyAddr(CAI->getLoc(), ElementAddrs[i], ElementTmps[i], - CAI->isTakeOfSrc(), CAI->isInitializationOfDest()); - - } else { - getScalarizedElementAddresses(CAI->getSrc(), B, CAI->getLoc(), - ElementTmps); - for (unsigned i = 0, e = ElementAddrs.size(); i != e; ++i) - B.createCopyAddr(CAI->getLoc(), ElementTmps[i], ElementAddrs[i], - CAI->isTakeOfSrc(), CAI->isInitializationOfDest()); - } - CAI->eraseFromParent(); - } - - // 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 (SILValue EltPtr : ElementAddrs) { - if (auto *TEAI = dyn_cast(EltPtr)) { - collectTupleElementUses(TEAI, BaseEltNo); - continue; - } - } - } } /// collectClassSelfUses - Collect all the uses of a 'self' pointer in a class @@ -1555,6 +1409,10 @@ collectDelegatingInitUses(const DIMemoryObjectInfo &TheMemory, Kind = DIUseKind::LoadForTypeOfSelf; } } + // value_metatype may also use the 'self' value directly, if it has an + // address-only type. + if (isa(User)) + Kind = DIUseKind::TypeOfSelf; // We can safely handle anything else as an escape. They should all happen // after self.init is invoked. diff --git a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h index fd81d130e4bcf..30cb0ce287f12 100644 --- a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h +++ b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h @@ -281,6 +281,9 @@ enum DIUseKind { /// This instruction is a load that's only used to answer a `type(of: self)` /// question. LoadForTypeOfSelf, + + /// This instruction is a value_metatype on the address of 'self'. + TypeOfSelf }; /// This struct represents a single classified access to the memory object diff --git a/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp b/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp index 28135f2625196..03098ea21812b 100644 --- a/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp +++ b/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp @@ -394,7 +394,7 @@ namespace { /// This is a map of uses that are not loads (i.e., they are Stores, /// InOutUses, and Escapes), to their entry in Uses. - llvm::SmallDenseMap NonLoadUses; + llvm::SmallDenseMap, 16> NonLoadUses; /// This is true when there is an ambiguous store, which may be an init or /// assign, depending on the CFG path. @@ -460,6 +460,7 @@ namespace { void handleStoreUse(unsigned UseID); void handleLoadUse(const DIMemoryUse &Use); void handleLoadForTypeOfSelfUse(DIMemoryUse &Use); + void handleTypeOfSelfUse(DIMemoryUse &Use); void handleInOutUse(const DIMemoryUse &Use); void handleEscapeUse(const DIMemoryUse &Use); @@ -472,7 +473,7 @@ namespace { void handleSelfInitUse(unsigned UseID); - void updateInstructionForInitState(DIMemoryUse &Use); + void updateInstructionForInitState(unsigned UseID); void processUninitializedRelease(SILInstruction *Release, @@ -530,6 +531,7 @@ LifetimeChecker::LifetimeChecker(const DIMemoryObjectInfo &TheMemory, switch (Use.Kind) { case DIUseKind::Load: case DIUseKind::LoadForTypeOfSelf: + case DIUseKind::TypeOfSelf: case DIUseKind::Escape: continue; case DIUseKind::Assign: @@ -544,7 +546,7 @@ LifetimeChecker::LifetimeChecker(const DIMemoryObjectInfo &TheMemory, break; } - NonLoadUses[Use.Inst] = ui; + NonLoadUses[Use.Inst].push_back(ui); auto &BBInfo = getBlockInfo(Use.Inst->getParent()); BBInfo.HasNonLoadUse = true; @@ -562,9 +564,8 @@ LifetimeChecker::LifetimeChecker(const DIMemoryObjectInfo &TheMemory, getBlockInfo(bb).markStoreToSelf(); } - // If isn't really a use, but we account for the mark_uninitialized or + // It isn't really a use, but we account for the mark_uninitialized or // project_box as a use so we see it in our dataflow walks. - NonLoadUses[TheMemory.getUninitializedValue()] = ~0U; auto &MemBBInfo = getBlockInfo(TheMemory.getParentBlock()); MemBBInfo.HasNonLoadUse = true; @@ -788,6 +789,9 @@ void LifetimeChecker::doIt() { case DIUseKind::LoadForTypeOfSelf: handleLoadForTypeOfSelfUse(Use); break; + case DIUseKind::TypeOfSelf: + handleTypeOfSelfUse(Use); + break; } } @@ -826,7 +830,7 @@ void LifetimeChecker::doIt() { // postpone lowering of assignment instructions to avoid deleting // instructions that still appear in the Uses list. for (unsigned UseID : NeedsUpdateForInitState) - updateInstructionForInitState(Uses[UseID]); + updateInstructionForInitState(UseID); } void LifetimeChecker::handleLoadUse(const DIMemoryUse &Use) { @@ -836,6 +840,35 @@ void LifetimeChecker::handleLoadUse(const DIMemoryUse &Use) { return handleLoadUseFailure(Use, IsSuperInitComplete, FailedSelfUse); } +static void replaceValueMetatypeInstWithMetatypeArgument( + ValueMetatypeInst *valueMetatype) { + SILValue metatypeArgument = valueMetatype->getFunction()->getSelfArgument(); + + // SILFunction parameter types never have a DynamicSelfType, since it only + // makes sense in the context of a given method's body. Since the + // value_metatype instruction might produce a DynamicSelfType we have to + // cast the metatype argument. + // + // FIXME: Semantically, we're "opening" the class metatype here to produce + // the "opened" DynamicSelfType. Ideally it would be modeled as an opened + // archetype associated with the original metatype or class instance value, + // instead of as a "global" type. + auto metatypeSelfType = metatypeArgument->getType() + .castTo().getInstanceType(); + auto valueSelfType = valueMetatype->getType() + .castTo().getInstanceType(); + if (metatypeSelfType != valueSelfType) { + assert(metatypeSelfType == + cast(valueSelfType).getSelfType()); + + SILBuilderWithScope B(valueMetatype); + metatypeArgument = B.createUncheckedTrivialBitCast( + valueMetatype->getLoc(), metatypeArgument, + valueMetatype->getType()); + } + replaceAllSimplifiedUsesAndErase(valueMetatype, metatypeArgument); +} + void LifetimeChecker::handleLoadForTypeOfSelfUse(DIMemoryUse &Use) { bool IsSuperInitComplete, FailedSelfUse; // If the value is not definitively initialized, replace the @@ -850,32 +883,7 @@ void LifetimeChecker::handleLoadForTypeOfSelfUse(DIMemoryUse &Use) { if (valueMetatype) break; } - assert(valueMetatype); - SILValue metatypeArgument = load->getFunction()->getSelfMetadataArgument(); - - // SILFunction parameter types never have a DynamicSelfType, since it only - // makes sense in the context of a given method's body. Since the - // value_metatype instruction might produce a DynamicSelfType we have to - // cast the metatype argument. - // - // FIXME: Semantically, we're "opening" the class metatype here to produce - // the "opened" DynamicSelfType. Ideally it would be modeled as an opened - // archetype associated with the original metatype or class instance value, - // instead of as a "global" type. - auto metatypeSelfType = metatypeArgument->getType() - .castTo().getInstanceType(); - auto valueSelfType = valueMetatype->getType() - .castTo().getInstanceType(); - if (metatypeSelfType != valueSelfType) { - assert(metatypeSelfType == - cast(valueSelfType).getSelfType()); - - SILBuilderWithScope B(valueMetatype); - metatypeArgument = B.createUncheckedTrivialBitCast( - valueMetatype->getLoc(), metatypeArgument, - valueMetatype->getType()); - } - replaceAllSimplifiedUsesAndErase(valueMetatype, metatypeArgument); + replaceValueMetatypeInstWithMetatypeArgument(valueMetatype); // Dead loads for type-of-self must be removed. // Otherwise it's a violation of memory lifetime. @@ -890,6 +898,20 @@ void LifetimeChecker::handleLoadForTypeOfSelfUse(DIMemoryUse &Use) { } } +void LifetimeChecker::handleTypeOfSelfUse(DIMemoryUse &Use) { + bool IsSuperInitComplete, FailedSelfUse; + // If the value is not definitively initialized, replace the + // value_metatype instruction with the metatype argument that was passed into + // the initializer. + if (!isInitializedAtUse(Use, &IsSuperInitComplete, &FailedSelfUse)) { + auto *valueMetatype = cast(Use.Inst); + replaceValueMetatypeInstWithMetatypeArgument(valueMetatype); + + // Clear the Inst pointer just to be sure to avoid use-after-free. + Use.Inst = nullptr; + } +} + void LifetimeChecker::emitSelfConsumedDiagnostic(SILInstruction *Inst) { if (!shouldEmitError(Inst)) return; @@ -1911,7 +1933,8 @@ void LifetimeChecker::handleSelfInitUse(unsigned UseID) { /// from being InitOrAssign to some concrete state, update it for that state. /// This includes rewriting them from assign instructions into their composite /// operations. -void LifetimeChecker::updateInstructionForInitState(DIMemoryUse &Use) { +void LifetimeChecker::updateInstructionForInitState(unsigned UseID) { + DIMemoryUse &Use = Uses[UseID]; SILInstruction *Inst = Use.Inst; IsInitialization_t InitKind; @@ -1945,10 +1968,11 @@ void LifetimeChecker::updateInstructionForInitState(DIMemoryUse &Use) { // If this is an assign, rewrite it based on whether it is an initialization // or not. if (auto *AI = dyn_cast(Inst)) { + // Remove this instruction from our data structures, since we will be // removing it. Use.Inst = nullptr; - NonLoadUses.erase(Inst); + llvm::erase_if(NonLoadUses[Inst], [&](unsigned id) { return id == UseID; }); if (TheMemory.isClassInitSelf() && Use.Kind == DIUseKind::SelfInit) { @@ -1966,7 +1990,7 @@ void LifetimeChecker::updateInstructionForInitState(DIMemoryUse &Use) { // Remove this instruction from our data structures, since we will be // removing it. Use.Inst = nullptr; - NonLoadUses.erase(Inst); + llvm::erase_if(NonLoadUses[Inst], [&](unsigned id) { return id == UseID; }); switch (Use.Kind) { case DIUseKind::Initialization: @@ -2819,17 +2843,17 @@ LifetimeChecker::getLivenessAtNonTupleInst(swift::SILInstruction *Inst, --BBI; SILInstruction *TheInst = &*BBI; - // If this instruction is unrelated to the memory, ignore it. - if (!NonLoadUses.count(TheInst)) - continue; + if (TheInst == TheMemory.getUninitializedValue()) { + Result.set(0, DIKind::No); + return Result; + } - // If we found the allocation itself, then we are loading something that - // is not defined at all yet. Otherwise, we've found a definition, or - // something else that will require that the memory is initialized at - // this point. - Result.set(0, TheInst == TheMemory.getUninitializedValue() ? DIKind::No - : DIKind::Yes); - return Result; + if (NonLoadUses.count(TheInst)) { + // We've found a definition, or something else that will require that + // the memory is initialized at this point. + Result.set(0, DIKind::Yes); + return Result; + } } } @@ -2882,11 +2906,6 @@ AvailabilitySet LifetimeChecker::getLivenessAtInst(SILInstruction *Inst, --BBI; SILInstruction *TheInst = &*BBI; - // If this instruction is unrelated to the memory, ignore it. - auto It = NonLoadUses.find(TheInst); - if (It == NonLoadUses.end()) - continue; - // If we found the allocation itself, then we are loading something that // is not defined at all yet. Scan no further. if (TheInst == TheMemory.getUninitializedValue()) { @@ -2896,11 +2915,19 @@ AvailabilitySet LifetimeChecker::getLivenessAtInst(SILInstruction *Inst, return Result; } + // If this instruction is unrelated to the memory, ignore it. + auto It = NonLoadUses.find(TheInst); + if (It == NonLoadUses.end()) + continue; + // Check to see which tuple elements this instruction defines. Clear them // from the set we're scanning from. - auto &TheInstUse = Uses[It->second]; - NeededElements.reset(TheInstUse.FirstElement, - TheInstUse.FirstElement+TheInstUse.NumElements); + for (unsigned TheUse : It->second) { + auto &TheInstUse = Uses[TheUse]; + NeededElements.reset(TheInstUse.FirstElement, + TheInstUse.FirstElement+TheInstUse.NumElements); + } + // If that satisfied all of the elements we're looking for, then we're // done. Otherwise, keep going. if (NeededElements.none()) { diff --git a/lib/SILOptimizer/Mandatory/DiagnoseInfiniteRecursion.cpp b/lib/SILOptimizer/Mandatory/DiagnoseInfiniteRecursion.cpp index c8bac8b584ff5..5969a6c224ab7 100644 --- a/lib/SILOptimizer/Mandatory/DiagnoseInfiniteRecursion.cpp +++ b/lib/SILOptimizer/Mandatory/DiagnoseInfiniteRecursion.cpp @@ -52,11 +52,15 @@ static bool hasRecursiveCallInPath(SILBasicBlock &Block, if (FullApplySite FAI = FullApplySite::isa(&I)) { // Don't touch dynamic dispatch. - if (isa(FAI.getCallee())) + const auto callee = FAI.getCallee(); + if (isa(callee) || + isa(callee) || + isa(callee)) { continue; + } auto &M = FAI.getModule(); - if (auto *CMI = dyn_cast(FAI.getCallee())) { + if (auto *CMI = dyn_cast(callee)) { auto ClassType = CMI->getOperand()->getType().getASTType(); // FIXME: If we're not inside the module context of the method, @@ -71,6 +75,18 @@ static bool hasRecursiveCallInPath(SILBasicBlock &Block, continue; } + if (!calleesAreStaticallyKnowable(FAI.getModule(), CMI->getMember())) { + continue; + } + + // The "statically knowable" check just means that we have all the + // callee candidates available for analysis. We still need to check + // if the current function has a known override point. + auto *method = CMI->getMember().getAbstractFunctionDecl(); + if (method->isOverridden()) { + continue; + } + auto *F = getTargetClassMethod(M, CD, CMI); if (F == Target) return true; @@ -78,7 +94,7 @@ static bool hasRecursiveCallInPath(SILBasicBlock &Block, continue; } - if (auto *WMI = dyn_cast(FAI.getCallee())) { + if (auto *WMI = dyn_cast(callee)) { SILFunction *F; SILWitnessTable *WT; diff --git a/lib/SILOptimizer/Mandatory/DiagnoseStaticExclusivity.cpp b/lib/SILOptimizer/Mandatory/DiagnoseStaticExclusivity.cpp index f27984e9ec524..f9e68af362c47 100644 --- a/lib/SILOptimizer/Mandatory/DiagnoseStaticExclusivity.cpp +++ b/lib/SILOptimizer/Mandatory/DiagnoseStaticExclusivity.cpp @@ -711,7 +711,8 @@ checkAccessSummary(ApplySite Apply, AccessState &State, // A valid AccessedStorage should always be found because Unsafe accesses // are not tracked by AccessSummaryAnalysis. - const AccessedStorage &Storage = identifyCapturedStorage(Argument); + auto Storage = AccessedStorage::computeInScope(Argument); + assert(Storage && "captured address must have valid storage"); auto AccessIt = State.Accesses->find(Storage); // Are there any accesses in progress at the time of the call? @@ -745,7 +746,8 @@ static void checkCaptureAccess(ApplySite Apply, AccessState &State) { // A valid AccessedStorage should always be found because Unsafe accesses // are not tracked by AccessSummaryAnalysis. - const AccessedStorage &Storage = identifyCapturedStorage(argOper.get()); + auto Storage = AccessedStorage::computeInScope(argOper.get()); + assert(Storage && "captured address must have valid storage"); // Are there any accesses in progress at the time of the call? auto AccessIt = State.Accesses->find(Storage); @@ -754,7 +756,7 @@ static void checkCaptureAccess(ApplySite Apply, AccessState &State) { // The unknown argument access is considered a modify of the root subpath. auto argAccess = RecordedAccess(SILAccessKind::Modify, Apply.getLoc(), - State.ASA->getSubPathTrieRoot()); + Apply.getModule().getIndexTrieRoot()); // Construct a conflicting RecordedAccess if one doesn't already exist. const AccessInfo &info = AccessIt->getSecond(); @@ -964,20 +966,20 @@ static void checkStaticExclusivity(SILFunction &Fn, PostOrderFunctionInfo *PO, // Check that the given address-type operand is guarded by begin/end access // markers. static void checkAccessedAddress(Operand *memOper, StorageMap &Accesses) { - SILValue address = getAddressAccess(memOper->get()); + SILValue accessBegin = getAccessScope(memOper->get()); SILInstruction *memInst = memOper->getUser(); - auto error = [address, memInst]() { + auto error = [accessBegin, memInst]() { llvm::dbgs() << "Memory access not protected by begin_access:\n"; memInst->printInContext(llvm::dbgs()); - llvm::dbgs() << "Accessing: " << address; + llvm::dbgs() << "Accessing: " << accessBegin; llvm::dbgs() << "In function:\n"; memInst->getFunction()->print(llvm::dbgs()); abort(); }; // Check if this address is guarded by an access. - if (auto *BAI = dyn_cast(address)) { + if (auto *BAI = dyn_cast(accessBegin)) { if (BAI->getEnforcement() == SILAccessEnforcement::Unsafe) return; @@ -1017,13 +1019,12 @@ static void checkAccessedAddress(Operand *memOper, StorageMap &Accesses) { return; } - const AccessedStorage &storage = findAccessedStorage(address); - // findAccessedStorage may return an invalid storage object if the address - // producer is not recognized by its allowlist. For the purpose of - // verification, we assume that this can only happen for local - // initialization, not a formal memory access. The strength of - // verification rests on the completeness of the opcode list inside - // findAccessedStorage. + auto storage = AccessedStorage::compute(accessBegin); + // AccessedStorage::compute may return an invalid storage object if the + // address producer is not recognized by its allowlist. For the purpose of + // verification, we assume that this can only happen for local initialization, + // not a formal memory access. The strength of verification rests on the + // completeness of the opcode list inside AccessedStorage::compute. // // For the purpose of verification, an unidentified access is // unenforced. These occur in cases like global addressors and local buffers diff --git a/lib/SILOptimizer/Mandatory/Differentiation.cpp b/lib/SILOptimizer/Mandatory/Differentiation.cpp index 6c5f962729b83..86df50569101e 100644 --- a/lib/SILOptimizer/Mandatory/Differentiation.cpp +++ b/lib/SILOptimizer/Mandatory/Differentiation.cpp @@ -327,7 +327,9 @@ static void copyParameterArgumentsForApply( // Copy the argument if it's to be owned by the newly created closure. // Objects are to be retained. if (arg->getType().isObject()) { - auto newArg = copyBuilder.emitCopyValueOperation(loc, arg); + auto newArg = arg; + if (newArg.getOwnershipKind() != ValueOwnershipKind::None) + newArg = copyBuilder.emitCopyValueOperation(loc, arg); collectNewArg(newArg); continue; } @@ -502,8 +504,9 @@ emitDerivativeFunctionReference( builder.emitBeginBorrowOperation(original.getLoc(), original); SILValue derivativeFn = builder.createDifferentiableFunctionExtract( borrowedDiffFunc.getLoc(), kind, borrowedDiffFunc); - derivativeFn = - builder.emitCopyValueOperation(original.getLoc(), derivativeFn); + if (derivativeFn.getOwnershipKind() != ValueOwnershipKind::None) + derivativeFn = + builder.emitCopyValueOperation(original.getLoc(), derivativeFn); builder.emitEndBorrowOperation(original.getLoc(), borrowedDiffFunc); return std::make_pair(derivativeFn, desiredIndices); } @@ -1212,9 +1215,11 @@ SILValue DifferentiationTransformer::promoteToDifferentiableFunction( for (auto *buf : llvm::reverse(newBuffersToDealloc)) builder.createDeallocStack(loc, buf); - auto origFnCopy = builder.emitCopyValueOperation(loc, origFnOperand); + // If our original copy does not have none ownership, copy it. + if (origFnOperand.getOwnershipKind() != ValueOwnershipKind::None) + origFnOperand = builder.emitCopyValueOperation(loc, origFnOperand); auto *newDiffFn = context.createDifferentiableFunction( - builder, loc, parameterIndices, resultIndices, origFnCopy, + builder, loc, parameterIndices, resultIndices, origFnOperand, std::make_pair(derivativeFns[0], derivativeFns[1])); context.getDifferentiableFunctionInstWorklist().push_back(dfi); return newDiffFn; @@ -1227,7 +1232,8 @@ SILValue DifferentiationTransformer::promoteToLinearFunction( // with an undef transpose function operand. Eventually, a legitimate // transpose function operand should be created and used. auto origFnOperand = lfi->getOriginalFunction(); - auto origFnCopy = builder.emitCopyValueOperation(loc, origFnOperand); + if (origFnOperand.getOwnershipKind() != ValueOwnershipKind::None) + origFnOperand = builder.emitCopyValueOperation(loc, origFnOperand); auto *parameterIndices = lfi->getParameterIndices(); auto originalType = origFnOperand->getType().castTo(); auto transposeFnType = originalType->getAutoDiffTransposeFunctionType( @@ -1236,7 +1242,7 @@ SILValue DifferentiationTransformer::promoteToLinearFunction( auto transposeType = SILType::getPrimitiveObjectType(transposeFnType); auto transposeFn = SILUndef::get(transposeType, builder.getFunction()); auto *newLinearFn = context.createLinearFunction( - builder, loc, parameterIndices, origFnCopy, SILValue(transposeFn)); + builder, loc, parameterIndices, origFnOperand, SILValue(transposeFn)); context.getLinearFunctionInstWorklist().push_back(lfi); return newLinearFn; } diff --git a/lib/SILOptimizer/Mandatory/MandatoryInlining.cpp b/lib/SILOptimizer/Mandatory/MandatoryInlining.cpp index c69f739cf3bb9..e829648d5ffd0 100644 --- a/lib/SILOptimizer/Mandatory/MandatoryInlining.cpp +++ b/lib/SILOptimizer/Mandatory/MandatoryInlining.cpp @@ -123,9 +123,9 @@ static bool fixupReferenceCounts( }); if (!consumedInLoop) { - applySite.insertAfterInvocation([&](SILBasicBlock::iterator iter) { - SILBuilderWithScope(iter).createDestroyAddr(loc, stackLoc); - SILBuilderWithScope(iter).createDeallocStack(loc, stackLoc); + applySite.insertAfterInvocation([&](SILBuilder &builder) { + builder.createDestroyAddr(loc, stackLoc); + builder.createDeallocStack(loc, stackLoc); }); } v = stackLoc; @@ -176,11 +176,11 @@ static bool fixupReferenceCounts( // uses in the top of a diamond and need to insert a destroy after the // apply since the leak will just cover the other path. if (!consumedInLoop) { - applySite.insertAfterInvocation([&](SILBasicBlock::iterator iter) { + applySite.insertAfterInvocation([&](SILBuilder &builder) { if (hasOwnership) { - SILBuilderWithScope(iter).createEndBorrow(loc, argument); + builder.createEndBorrow(loc, argument); } - SILBuilderWithScope(iter).emitDestroyValueOperation(loc, copy); + builder.emitDestroyValueOperation(loc, copy); }); } v = argument; @@ -217,8 +217,8 @@ static bool fixupReferenceCounts( // Then insert destroys after the apply site since our value is not being // consumed as part of the actual apply. - applySite.insertAfterInvocation([&](SILBasicBlock::iterator iter) { - SILBuilderWithScope(iter).emitDestroyValueOperation(loc, v); + applySite.insertAfterInvocation([&](SILBuilder &builder) { + builder.emitDestroyValueOperation(loc, v); }); break; } @@ -263,8 +263,8 @@ static bool fixupReferenceCounts( // Destroy the callee as the apply would have done if our function is not // callee guaranteed. if (!isCalleeGuaranteed) { - applySite.insertAfterInvocation([&](SILBasicBlock::iterator iter) { - SILBuilderWithScope(iter).emitDestroyValueOperation(loc, calleeValue); + applySite.insertAfterInvocation([&](SILBuilder &builder) { + builder.emitDestroyValueOperation(loc, calleeValue); }); } return invalidatedStackNesting; diff --git a/lib/SILOptimizer/Mandatory/OwnershipModelEliminator.cpp b/lib/SILOptimizer/Mandatory/OwnershipModelEliminator.cpp index 769ee6c2cef75..0e57fc67d8a6e 100644 --- a/lib/SILOptimizer/Mandatory/OwnershipModelEliminator.cpp +++ b/lib/SILOptimizer/Mandatory/OwnershipModelEliminator.cpp @@ -370,7 +370,7 @@ static void prepareSILFunctionForOptimization(ModuleDecl *, SILFunction *F) { namespace { -struct OwnershipModelEliminator : SILModuleTransform { +struct OwnershipModelEliminator : SILFunctionTransform { bool SkipTransparent; bool SkipStdlibModule; @@ -379,10 +379,11 @@ struct OwnershipModelEliminator : SILModuleTransform { void run() override { if (DumpBefore.size()) { - getModule()->dump(DumpBefore.c_str()); + getFunction()->dump(DumpBefore.c_str()); } - auto &Mod = *getModule(); + auto *F = getFunction(); + auto &Mod = getFunction()->getModule(); // If we are supposed to skip the stdlib module and we are in the stdlib // module bail. @@ -390,42 +391,38 @@ struct OwnershipModelEliminator : SILModuleTransform { return; } - for (auto &F : Mod) { - // If F does not have ownership, skip it. We have no further work to do. - if (!F.hasOwnership()) - continue; - - // If we were asked to not strip ownership from transparent functions in - // /our/ module, continue. - if (SkipTransparent && F.isTransparent()) - continue; - - // Verify here to make sure ownership is correct before we strip. - { - // Add a pretty stack trace entry to tell users who see a verification - // failure triggered by this verification check that they need to re-run - // with -sil-verify-all to actually find the pass that introduced the - // verification error. - // - // DISCUSSION: This occurs due to the crash from the verification - // failure happening in the pass itself. This causes us to dump the - // SILFunction and emit a msg that this pass (OME) is the culprit. This - // is generally correct for most passes, but not for OME since we are - // verifying before we have even modified the function to ensure that - // all ownership invariants have been respected before we lower - // ownership from the function. - llvm::PrettyStackTraceString silVerifyAllMsgOnFailure( - "Found verification error when verifying before lowering " - "ownership. Please re-run with -sil-verify-all to identify the " - "actual pass that introduced the verification error."); - F.verify(); - } + if (!F->hasOwnership()) + return; - if (stripOwnership(F)) { - auto InvalidKind = - SILAnalysis::InvalidationKind::BranchesAndInstructions; - invalidateAnalysis(&F, InvalidKind); - } + // If we were asked to not strip ownership from transparent functions in + // /our/ module, return. + if (SkipTransparent && F->isTransparent()) + return; + + // Verify here to make sure ownership is correct before we strip. + { + // Add a pretty stack trace entry to tell users who see a verification + // failure triggered by this verification check that they need to re-run + // with -sil-verify-all to actually find the pass that introduced the + // verification error. + // + // DISCUSSION: This occurs due to the crash from the verification + // failure happening in the pass itself. This causes us to dump the + // SILFunction and emit a msg that this pass (OME) is the culprit. This + // is generally correct for most passes, but not for OME since we are + // verifying before we have even modified the function to ensure that + // all ownership invariants have been respected before we lower + // ownership from the function. + llvm::PrettyStackTraceString silVerifyAllMsgOnFailure( + "Found verification error when verifying before lowering " + "ownership. Please re-run with -sil-verify-all to identify the " + "actual pass that introduced the verification error."); + F->verify(); + } + + if (stripOwnership(*F)) { + auto InvalidKind = SILAnalysis::InvalidationKind::BranchesAndInstructions; + invalidateAnalysis(InvalidKind); } // If we were asked to strip transparent, we are at the beginning of the @@ -435,12 +432,19 @@ struct OwnershipModelEliminator : SILModuleTransform { FunctionBodyDeserializationNotificationHandler; std::unique_ptr ptr; if (SkipTransparent) { - ptr.reset(new NotificationHandlerTy( - prepareNonTransparentSILFunctionForOptimization)); + if (!Mod.hasRegisteredDeserializationNotificationHandlerForNonTransparentFuncOME()) { + ptr.reset(new NotificationHandlerTy( + prepareNonTransparentSILFunctionForOptimization)); + Mod.registerDeserializationNotificationHandler(std::move(ptr)); + Mod.setRegisteredDeserializationNotificationHandlerForNonTransparentFuncOME(); + } } else { - ptr.reset(new NotificationHandlerTy(prepareSILFunctionForOptimization)); + if (!Mod.hasRegisteredDeserializationNotificationHandlerForAllFuncOME()) { + ptr.reset(new NotificationHandlerTy(prepareSILFunctionForOptimization)); + Mod.registerDeserializationNotificationHandler(std::move(ptr)); + Mod.setRegisteredDeserializationNotificationHandlerForAllFuncOME(); + } } - Mod.registerDeserializationNotificationHandler(std::move(ptr)); } }; diff --git a/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp b/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp index 1b7a6c384fc80..a45a3d19316b3 100644 --- a/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp +++ b/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp @@ -134,7 +134,7 @@ static unsigned computeSubelement(SILValue Pointer, SILType TT = TEAI->getOperand()->getType(); // Keep track of what subelement is being referenced. - for (unsigned i = 0, e = TEAI->getFieldNo(); i != e; ++i) { + for (unsigned i = 0, e = TEAI->getFieldIndex(); i != e; ++i) { SubElementNumber += getNumSubElements(TT.getTupleElementType(i), M, TypeExpansionContext(*RootInst->getFunction())); diff --git a/lib/SILOptimizer/PassManager/PassManager.cpp b/lib/SILOptimizer/PassManager/PassManager.cpp index 463fd6dd52e13..6f08a5432040b 100644 --- a/lib/SILOptimizer/PassManager/PassManager.cpp +++ b/lib/SILOptimizer/PassManager/PassManager.cpp @@ -393,12 +393,26 @@ static void logS4TFPassEvent(long long Delta, llvm::sys::TimePoint<> StartTime, llvm::dbgs() << "S4TF," << Delta << "," << strTime << "," << passName << "," << (isFunctionPass ? "F" : "M") << "," << funcName << "\n"; } +// SWIFT_ENABLE_TENSORFLOW END + +bool SILPassManager::isMandatoryFunctionPass(SILFunctionTransform *sft) { + return isMandatory || sft->getPassKind() == + PassKind::NonTransparentFunctionOwnershipModelEliminator || + sft->getPassKind() == PassKind::OwnershipModelEliminator || + sft->getPassKind() == + PassKind::NonStdlibNonTransparentFunctionOwnershipModelEliminator; +} void SILPassManager::runPassOnFunction(unsigned TransIdx, SILFunction *F) { assert(analysesUnlocked() && "Expected all analyses to be unlocked!"); auto *SFT = cast(Transformations[TransIdx]); + + if (!F->shouldOptimize() && !isMandatoryFunctionPass(SFT)) { + return; + } + SFT->injectPassManager(this); SFT->injectFunction(F); @@ -406,9 +420,10 @@ void SILPassManager::runPassOnFunction(unsigned TransIdx, SILFunction *F) { DebugPrintEnabler DebugPrint(NumPassesRun); // If nothing changed since the last run of this pass, we can skip this - // pass. + // pass if it is not mandatory CompletedPasses &completedPasses = CompletedPassesMap[F]; - if (completedPasses.test((size_t)SFT->getPassKind()) && + if (!isMandatoryFunctionPass(SFT) && + completedPasses.test((size_t)SFT->getPassKind()) && !SILDisableSkippingPasses) { if (SILPrintPassName) dumpPassInfo("(Skip)", TransIdx, F); @@ -518,9 +533,11 @@ runFunctionPasses(unsigned FromTransIdx, unsigned ToTransIdx) { return; BasicCalleeAnalysis *BCA = getAnalysis(); + // SWIFT_ENABLE_TENSORFLOW BottomUpFunctionOrder BottomUpOrder(BCA); BottomUpOrder.computeBottomUpOrder(Mod); auto BottomUpFunctions = BottomUpOrder.getBottomUpOrder(); + // SWIFT_ENABLE_TENSORFLOW END assert(FunctionWorklist.empty() && "Expected empty function worklist!"); @@ -531,7 +548,7 @@ runFunctionPasses(unsigned FromTransIdx, unsigned ToTransIdx) { // Only include functions that are definitions, and which have not // been intentionally excluded from optimization. - if (F.isDefinition() && (isMandatory || F.shouldOptimize())) + if (F.isDefinition()) FunctionWorklist.push_back(*I); } @@ -574,6 +591,7 @@ runFunctionPasses(unsigned FromTransIdx, unsigned ToTransIdx) { } clearRestartPipeline(); + // SWIFT_ENABLE_TENSORFLOW if (TailIdx == (FunctionWorklist.size() - 1)) { // No new functions to process continue; @@ -614,6 +632,7 @@ runFunctionPasses(unsigned FromTransIdx, unsigned ToTransIdx) { } FunctionWorklist.push_back((*Entry).second); } + // SWIFT_ENABLE_TENSORFLOW END } } diff --git a/lib/SILOptimizer/PassManager/PassPipeline.cpp b/lib/SILOptimizer/PassManager/PassPipeline.cpp index fda5b5fe7f589..4e4a4ab495e2e 100644 --- a/lib/SILOptimizer/PassManager/PassPipeline.cpp +++ b/lib/SILOptimizer/PassManager/PassPipeline.cpp @@ -299,6 +299,11 @@ void addFunctionPasses(SILPassPipelinePlan &P, // Promote stack allocations to values. P.addMem2Reg(); + // We earlier eliminated ownership if we are not compiling the stdlib. Now + // handle the stdlib functions, re-simplifying, eliminating ARC as we do. + P.addSemanticARCOpts(); + P.addNonTransparentFunctionOwnershipModelEliminator(); + // Run the existential specializer Pass. P.addExistentialSpecializer(); @@ -389,6 +394,7 @@ void addFunctionPasses(SILPassPipelinePlan &P, P.addEarlyCodeMotion(); P.addReleaseHoisting(); P.addARCSequenceOpts(); + P.addTempRValueOpt(); P.addSimplifyCFG(); if (OpLevel == OptimizationLevelKind::LowLevel) { @@ -418,6 +424,11 @@ static void addPerfDebugSerializationPipeline(SILPassPipelinePlan &P) { static void addPrepareOptimizationsPipeline(SILPassPipelinePlan &P) { P.startPipeline("PrepareOptimizationPasses"); + // Verify AccessedStorage once in OSSA before optimizing. +#ifndef NDEBUG + P.addAccessPathVerification(); +#endif + P.addForEachLoopUnroll(); P.addMandatoryCombine(); P.addAccessMarkerElimination(); @@ -489,10 +500,7 @@ static void addHighLevelFunctionPipeline(SILPassPipelinePlan &P) { // FIXME: update EagerSpecializer to be a function pass! P.addEagerSpecializer(); - // We earlier eliminated ownership if we are not compiling the stdlib. Now - // handle the stdlib functions. - P.addNonTransparentFunctionOwnershipModelEliminator(); - + // stdlib ownership model elimination is done within addFunctionPasses addFunctionPasses(P, OptimizationLevelKind::HighLevel); addHighLevelLoopOptPasses(P); @@ -668,6 +676,11 @@ static void addLastChanceOptPassPipeline(SILPassPipelinePlan &P) { // A loop might have only one dynamic access now, i.e. hoistable P.addLICM(); + // Verify AccessedStorage once again after optimizing and lowering OSSA. +#ifndef NDEBUG + P.addAccessPathVerification(); +#endif + // Only has an effect if the -assume-single-thread option is specified. P.addAssumeSingleThreaded(); @@ -805,6 +818,9 @@ SILPassPipelinePlan::getOnonePassPipeline(const SILOptions &Options) { // Has only an effect if the -assume-single-thread option is specified. P.addAssumeSingleThreaded(); + // Create pre-specializations. + P.addOnonePrespecializations(); + // Has only an effect if the -gsil option is specified. P.addSILDebugInfoGenerator(); diff --git a/lib/SILOptimizer/SILCombiner/SILCombiner.h b/lib/SILOptimizer/SILCombiner/SILCombiner.h index 44669e43fb2b0..4dd78cc66237d 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombiner.h +++ b/lib/SILOptimizer/SILCombiner/SILCombiner.h @@ -291,6 +291,8 @@ class SILCombiner : bool canReplaceArg(FullApplySite Apply, const OpenedArchetypeInfo &OAI, const ConcreteExistentialInfo &CEI, unsigned ArgIdx); + SILValue canCastArg(FullApplySite Apply, const OpenedArchetypeInfo &OAI, + const ConcreteExistentialInfo &CEI, unsigned ArgIdx); SILInstruction *createApplyWithConcreteType( FullApplySite Apply, diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp index 4774b9fd46a75..bb5c825bc7f86 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp @@ -994,6 +994,48 @@ struct ConcreteArgumentCopy { } }; +SILValue SILCombiner::canCastArg(FullApplySite Apply, + const OpenedArchetypeInfo &OAI, + const ConcreteExistentialInfo &CEI, + unsigned ArgIdx) { + if (!CEI.ConcreteValue || CEI.ConcreteType->isOpenedExistential() || + !CEI.ConcreteValue->getType().isAddress()) + return SILValue(); + + // Don't specialize apply instructions that return the callee's Arg type, + // because this optimization does not know how to substitute types in the + // users of this apply. In the function type substitution below, all + // references to OpenedArchetype will be substituted. So walk to type to + // find all possible references, such as returning Optional. + if (Apply.getType().getASTType().findIf( + [&OAI](Type t) -> bool { return t->isEqual(OAI.OpenedArchetype); })) { + return SILValue(); + } + // Bail out if any other arguments or indirect result that refer to the + // OpenedArchetype. The following optimization substitutes all occurrences + // of OpenedArchetype in the function signature, but will only rewrite the + // Arg operand. + // + // Note that the language does not allow Self to occur in contravariant + // position. However, SIL does allow this and it can happen as a result of + // upstream transformations. Since this is bail-out logic, it must handle + // all verifiable SIL. + + // This bailout check is also needed for non-Self arguments [including Self]. + unsigned NumApplyArgs = Apply.getNumArguments(); + for (unsigned Idx = 0; Idx < NumApplyArgs; ++Idx) { + if (Idx == ArgIdx) + continue; + if (Apply.getArgument(Idx)->getType().getASTType().findIf( + [&OAI](Type t) -> bool { + return t->isEqual(OAI.OpenedArchetype); + })) { + return SILValue(); + } + } + return Builder.createUncheckedAddrCast( + Apply.getLoc(), Apply.getArgument(ArgIdx), CEI.ConcreteValue->getType()); +} /// Rewrite the given method apply instruction in terms of the provided conrete /// type information. /// @@ -1049,6 +1091,32 @@ SILInstruction *SILCombiner::createApplyWithConcreteType( // Check for Arg's concrete type propagation legality. if (!canReplaceArg(Apply, OAI, CEI, ArgIdx)) { + + // As on last fall-back try to cast the argument. + if (auto cast = canCastArg(Apply, OAI, CEI, ArgIdx)) { + NewArgs.push_back(cast); + // Form a new set of substitutions where the argument is + // replaced with a concrete type. + NewCallSubs = NewCallSubs.subst( + [&](SubstitutableType *type) -> Type { + if (type == OAI.OpenedArchetype) + return CEI.ConcreteType; + return type; + }, + [&](CanType origTy, Type substTy, + ProtocolDecl *proto) -> ProtocolConformanceRef { + if (origTy->isEqual(OAI.OpenedArchetype)) { + assert(substTy->isEqual(CEI.ConcreteType)); + // Do a conformance lookup on this witness requirement using the + // existential's conformances. The witness requirement may be a + // base type of the existential's requirements. + return CEI.lookupExistentialConformance(proto); + } + return ProtocolConformanceRef(proto); + }); + continue; + } + // Otherwise, use the original argument. NewArgs.push_back(Apply.getArgument(ArgIdx)); continue; } diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerCastVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerCastVisitors.cpp index 4230ceaa37d28..35735ba6c9c5c 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerCastVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerCastVisitors.cpp @@ -472,6 +472,9 @@ visitUncheckedTrivialBitCastInst(UncheckedTrivialBitCastInst *UTBCI) { SILInstruction * SILCombiner:: visitUncheckedBitwiseCastInst(UncheckedBitwiseCastInst *UBCI) { + if (UBCI->getFunction()->hasOwnership()) + return nullptr; + // (unchecked_bitwise_cast Y->Z (unchecked_bitwise_cast X->Y x)) // OR (unchecked_trivial_cast Y->Z (unchecked_bitwise_cast X->Y x)) // -> diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp index 4321485140fb2..144c1d30a04e7 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp @@ -453,6 +453,10 @@ bool SILCombiner::optimizeStackAllocatedEnum(AllocStackInst *AS) { return false; EnumElementDecl *element = nullptr; + unsigned numInits =0; + unsigned numTakes = 0; + SILBasicBlock *initBlock = nullptr; + SILBasicBlock *takeBlock = nullptr; SILType payloadType; // First step: check if the stack location is only used to hold one specific @@ -463,6 +467,8 @@ bool SILCombiner::optimizeStackAllocatedEnum(AllocStackInst *AS) { case SILInstructionKind::DebugValueAddrInst: case SILInstructionKind::DestroyAddrInst: case SILInstructionKind::DeallocStackInst: + case SILInstructionKind::InjectEnumAddrInst: + // We'll check init_enum_addr below. break; case SILInstructionKind::InitEnumDataAddrInst: { auto *ieda = cast(user); @@ -472,13 +478,8 @@ bool SILCombiner::optimizeStackAllocatedEnum(AllocStackInst *AS) { element = el; assert(!payloadType || payloadType == ieda->getType()); payloadType = ieda->getType(); - break; - } - case SILInstructionKind::InjectEnumAddrInst: { - auto *el = cast(user)->getElement(); - if (element && el != element) - return false; - element = el; + numInits++; + initBlock = user->getParent(); break; } case SILInstructionKind::UncheckedTakeEnumDataAddrInst: { @@ -486,6 +487,8 @@ bool SILCombiner::optimizeStackAllocatedEnum(AllocStackInst *AS) { if (element && el != element) return false; element = el; + numTakes++; + takeBlock = user->getParent(); break; } default: @@ -495,6 +498,24 @@ bool SILCombiner::optimizeStackAllocatedEnum(AllocStackInst *AS) { if (!element || !payloadType) return false; + // If the enum has a single init-take pair in a single block, we know that + // the enum cannot contain any valid payload outside that init-take pair. + // + // This also means that we can ignore any inject_enum_addr of another enum + // case, because this can only inject a case without a payload. + bool singleInitTakePair = + (numInits == 1 && numTakes == 1 && initBlock == takeBlock); + if (!singleInitTakePair) { + // No single init-take pair: We cannot ignore inject_enum_addrs with a + // mismatching case. + for (auto *use : AS->getUses()) { + if (auto *inject = dyn_cast(use->getUser())) { + if (inject->getElement() != element) + return false; + } + } + } + // Second step: replace the enum alloc_stack with a payload alloc_stack. auto *newAlloc = Builder.createAllocStack( AS->getLoc(), payloadType, AS->getVarInfo(), AS->hasDynamicLifetime()); @@ -508,6 +529,22 @@ bool SILCombiner::optimizeStackAllocatedEnum(AllocStackInst *AS) { eraseInstFromFunction(*user); break; case SILInstructionKind::DestroyAddrInst: + if (singleInitTakePair) { + // It's not possible that the enum has a payload at the destroy_addr, + // because it must have already been taken by the take of the + // single init-take pair. + // We _have_ to remove the destroy_addr, because we also remove all + // inject_enum_addrs which might inject a payload-less case before + // the destroy_addr. + eraseInstFromFunction(*user); + } else { + // The enum payload can still be valid at the destroy_addr, so we have + // to keep the destroy_addr. Just replace the enum with the payload + // (and because it's not a singleInitTakePair, we can be sure that the + // enum cannot have any other case than the payload case). + use->set(newAlloc); + } + break; case SILInstructionKind::DeallocStackInst: use->set(newAlloc); break; @@ -803,7 +840,7 @@ static SingleValueInstruction *getValueFromStaticLet(SILValue v) { if (!tupleVal) return nullptr; return cast( - cast(tupleVal)->getElement(teai->getFieldNo())); + cast(tupleVal)->getElement(teai->getFieldIndex())); } return nullptr; } @@ -1111,9 +1148,9 @@ static SILValue createValueFromAddr(SILValue addr, SILBuilder *builder, kind = tuple; } if (kind == tuple) { - if (elems[telem->getFieldNo()]) + if (elems[telem->getFieldIndex()]) return SILValue(); - elems[telem->getFieldNo()] = createValueFromAddr(telem, builder, loc); + elems[telem->getFieldIndex()] = createValueFromAddr(telem, builder, loc); continue; } } @@ -1802,7 +1839,7 @@ SILInstruction *SILCombiner::visitTupleExtractInst(TupleExtractInst *TEI) { // tuple_extract(apply([add|sub|...]overflow(x, 0)), 1) -> 0 // if it can be proven that no overflow can happen. - if (TEI->getFieldNo() != 1) + if (TEI->getFieldIndex() != 1) return nullptr; Builder.setCurrentDebugScope(TEI->getDebugScope()); diff --git a/lib/SILOptimizer/SemanticARC/LoadCopyToLoadBorrowOpt.cpp b/lib/SILOptimizer/SemanticARC/LoadCopyToLoadBorrowOpt.cpp index 157e21c3a9406..71323e34eac8f 100644 --- a/lib/SILOptimizer/SemanticARC/LoadCopyToLoadBorrowOpt.cpp +++ b/lib/SILOptimizer/SemanticARC/LoadCopyToLoadBorrowOpt.cpp @@ -193,7 +193,7 @@ class StorageGuaranteesLoadVisitor void visitGlobalAccess(SILValue global) { return answer( - !AccessedStorage(global, AccessedStorage::Global).isLetAccess(&ctx.fn)); + !AccessedStorage(global, AccessedStorage::Global).isLetAccess()); } void visitClassAccess(RefElementAddrInst *field) { @@ -266,8 +266,13 @@ class StorageGuaranteesLoadVisitor return next(parentAddr->get()); } - void visitPathComponent(SingleValueInstruction *projectedAddr, - Operand *parentAddr) { + void visitStorageCast(SingleValueInstruction *projectedAddr, + Operand *parentAddr) { + return next(parentAddr->get()); + } + + void visitAccessProjection(SingleValueInstruction *projectedAddr, + Operand *parentAddr) { return next(parentAddr->get()); } diff --git a/lib/SILOptimizer/SemanticARC/OwnershipLiveRange.cpp b/lib/SILOptimizer/SemanticARC/OwnershipLiveRange.cpp index 187b28e68c27b..e7068997d289b 100644 --- a/lib/SILOptimizer/SemanticARC/OwnershipLiveRange.cpp +++ b/lib/SILOptimizer/SemanticARC/OwnershipLiveRange.cpp @@ -13,6 +13,8 @@ #include "OwnershipLiveRange.h" #include "OwnershipPhiOperand.h" +#include "swift/SIL/BasicBlockUtils.h" + using namespace swift; using namespace swift::semanticarc; @@ -162,6 +164,61 @@ void OwnershipLiveRange::insertEndBorrowsAtDestroys( auto loc = RegularLocation::getAutoGeneratedLocation(); while (!scratch.empty()) { auto *insertPoint = scratch.pop_back_val(); + + // Do not insert end_borrow if the block insert point is a dead end block. + // + // DISCUSSION: This is important to do since otherwise, we may be implicitly + // reducing the lifetime of a value which we can not do yet since we do not + // require all interior pointer instructions to be guarded by borrows + // (yet). Once that constraint is in place, we will not have this problem. + // + // Consider a situation where one has a @owned switch_enum on an + // indirect box case which is post-dominated by an unreachable that we want + // to convert to @guaranteed: + // + // enum MyEnum { + // indirect case FirstCase(Int) + // ... + // } + // + // bb0(%in_guaranteed_addr : $*MyEnum): + // ... + // %0 = load [copy] %in_guaranteed_addr : $*MyEnum + // switch_enum %0 : $MyEnum, case #MyEnum.FirstCase: bb1, ... + // + // bb1(%1 : @owned ${ var Int }): + // %2 = project_box %1 : ${ var Int }, 0 + // %3 = load [trivial] %2 : $*Int + // apply %log(%3) : $@convention(thin) (Int) -> () + // unreachable + // + // In this case, we will not have a destroy_value on the box, but we may + // have a project_box on the box. This is ok since we are going to leak the + // value. But since we are using all consuming uses to determine the + // lifetime, we will want to insert an end_borrow at the head of the + // switch_enum dest block like follows: + // + // bb0(%in_guaranteed_addr : $*MyEnum): + // ... + // %0 = load_borrow %in_guaranteed_addr : $*MyEnum + // switch_enum %0 : $MyEnum, case #MyEnum.FirstCase: bb1, ... + // + // bb1(%1 : @guaranteed ${ var Int }): + // end_borrow %1 : ${ var Int } + // %2 = project_box %1 : ${ var Int }, 0 + // %3 = load [trivial] %2 : $*Int + // apply %log(%3) : $@convention(thin) (Int) -> () + // unreachable + // + // which would violate ownership invariants. Instead, we need to realize + // that %1 is dominated by a dead end block so we may not have a + // destroy_value upon it meaning we should just not insert the end_borrow + // here. If we have a destroy_value upon it (since we did not get rid of a + // destroy_value), then we will still get rid of the destroy_value if we are + // going to optimize this, so we are still correct. + if (deadEndBlocks.isDeadEnd(insertPoint->getParent())) + continue; + SILBuilderWithScope builder(insertPoint); builder.createEndBorrow(loc, newGuaranteedValue); } diff --git a/lib/SILOptimizer/SemanticARC/OwnershipPhiOperand.h b/lib/SILOptimizer/SemanticARC/OwnershipPhiOperand.h index 169eb9859bea8..88ba1007d2ea2 100644 --- a/lib/SILOptimizer/SemanticARC/OwnershipPhiOperand.h +++ b/lib/SILOptimizer/SemanticARC/OwnershipPhiOperand.h @@ -88,6 +88,7 @@ class LLVM_LIBRARY_VISIBILITY OwnershipPhiOperand { case Kind::Struct: return false; } + llvm_unreachable("unhandled operand kind!"); } bool operator<(const OwnershipPhiOperand &other) const { @@ -113,6 +114,7 @@ class LLVM_LIBRARY_VISIBILITY OwnershipPhiOperand { }); } } + llvm_unreachable("unhandled operand kind!"); } }; diff --git a/lib/SILOptimizer/SemanticARC/SemanticARCOpts.cpp b/lib/SILOptimizer/SemanticARC/SemanticARCOpts.cpp index 40ffe3015e1de..b8eb67ec2f2e0 100644 --- a/lib/SILOptimizer/SemanticARC/SemanticARCOpts.cpp +++ b/lib/SILOptimizer/SemanticARC/SemanticARCOpts.cpp @@ -100,8 +100,10 @@ struct SemanticARCOpts : SILFunctionTransform { void run() override { SILFunction &f = *getFunction(); - // Return early if we are not performing OSSA optimizations. - if (!f.getModule().getOptions().EnableOSSAOptimizations) + // Return early if we are not performing OSSA optimizations or we are not in + // ownership. + if (!f.getModule().getOptions().EnableOSSAOptimizations || + !f.hasOwnership()) return; // Make sure we are running with ownership verification enabled. diff --git a/lib/SILOptimizer/Transforms/AccessEnforcementDom.cpp b/lib/SILOptimizer/Transforms/AccessEnforcementDom.cpp index 06fefeeac81c9..dd20d41da8e58 100644 --- a/lib/SILOptimizer/Transforms/AccessEnforcementDom.cpp +++ b/lib/SILOptimizer/Transforms/AccessEnforcementDom.cpp @@ -251,7 +251,7 @@ void DominatedAccessAnalysis::analyzeAccess(BeginAccessInst *BAI, // Only track dynamic access in the result. Static accesses still need to be // tracked by data flow, but they can't be optimized as "dominating". if (BAI->getEnforcement() == SILAccessEnforcement::Dynamic) { - AccessedStorage storage = findAccessedStorage(BAI->getSource()); + auto storage = AccessedStorage::compute(BAI->getSource()); // Copy the AccessStorage into DomAccessedStorage. All pass-specific bits // are initialized to zero. domStorage = DomAccessedStorage(storage); diff --git a/lib/SILOptimizer/Transforms/AccessEnforcementOpts.cpp b/lib/SILOptimizer/Transforms/AccessEnforcementOpts.cpp index 88e4ed0588e96..c0011d148b2e3 100644 --- a/lib/SILOptimizer/Transforms/AccessEnforcementOpts.cpp +++ b/lib/SILOptimizer/Transforms/AccessEnforcementOpts.cpp @@ -566,7 +566,7 @@ bool AccessConflictAndMergeAnalysis::identifyBeginAccesses() { // now, since this optimization runs at the end of the pipeline, we // gracefully ignore unrecognized source address patterns, which show up // here as an invalid `storage` value. - AccessedStorage storage = findAccessedStorage(beginAccess->getSource()); + auto storage = AccessedStorage::compute(beginAccess->getSource()); auto iterAndInserted = storageSet.insert(storage); diff --git a/lib/SILOptimizer/Transforms/AccessEnforcementWMO.cpp b/lib/SILOptimizer/Transforms/AccessEnforcementWMO.cpp index 36c82f0f676cc..e8db5bc90fc90 100644 --- a/lib/SILOptimizer/Transforms/AccessEnforcementWMO.cpp +++ b/lib/SILOptimizer/Transforms/AccessEnforcementWMO.cpp @@ -67,22 +67,21 @@ using llvm::DenseMap; using llvm::SmallDenseSet; // Get the VarDecl that represents the DisjointAccessLocation for the given -// AccessedStorage. Returns nullptr for any storage that can't be partitioned -// into a disjoint location. +// storage and access base. Returns nullptr for any storage that can't be +// partitioned into a disjoint location. // -// identifyFormalAccess may only return Unidentified storage for a global -// variable access if the global is defined in a different module. -// -// WARNING: Retrieving VarDecl for Class access is not constant time. -const VarDecl *getDisjointAccessLocation(const AccessedStorage &storage) { +// Global storage is expected to be disjoint because identifyFormalAccess may +// only return Unidentified storage for a global variable access if the global +// is defined in a different module. +const VarDecl * +getDisjointAccessLocation(AccessedStorageWithBase storageAndBase) { + auto storage = storageAndBase.storage; switch (storage.getKind()) { case AccessedStorage::Global: - // A global variable may return a null decl. These variables are - // implementation details that aren't formally accessed. - return storage.getGlobal()->getDecl(); - case AccessedStorage::Class: { - return cast(storage.getDecl()); - } + case AccessedStorage::Class: + // Class and Globals are always a VarDecl, but the global decl may have a + // null value for global_addr -> phi. + return cast_or_null(storage.getDecl(storageAndBase.base)); case AccessedStorage::Box: case AccessedStorage::Stack: case AccessedStorage::Tail: @@ -169,15 +168,17 @@ void GlobalAccessRemoval::perform() { void GlobalAccessRemoval::visitInstruction(SILInstruction *I) { if (auto *BAI = dyn_cast(I)) { - AccessedStorage storage = findAccessedStorage(BAI->getSource()); - const VarDecl *decl = getDisjointAccessLocation(storage); - recordAccess(BAI, decl, storage.getKind(), BAI->hasNoNestedConflict()); + auto storageAndBase = AccessedStorageWithBase::compute(BAI->getSource()); + const VarDecl *decl = getDisjointAccessLocation(storageAndBase); + recordAccess(BAI, decl, storageAndBase.storage.getKind(), + BAI->hasNoNestedConflict()); return; } if (auto *BUAI = dyn_cast(I)) { - AccessedStorage storage = findAccessedStorage(BUAI->getSource()); - const VarDecl *decl = getDisjointAccessLocation(storage); - recordAccess(BUAI, decl, storage.getKind(), BUAI->hasNoNestedConflict()); + auto storageAndBase = AccessedStorageWithBase::compute(BUAI->getSource()); + const VarDecl *decl = getDisjointAccessLocation(storageAndBase); + recordAccess(BUAI, decl, storageAndBase.storage.getKind(), + BUAI->hasNoNestedConflict()); return; } if (auto *KPI = dyn_cast(I)) { diff --git a/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp b/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp index 6483b47a12a6f..fd7695b164640 100644 --- a/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp +++ b/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp @@ -984,6 +984,7 @@ specializeApplySite(SILOptFunctionBuilder &FuncBuilder, ApplySite Apply, GenericSpecializationInformation::create(ApplyInst, Builder)); } } + llvm_unreachable("unhandled apply inst kind!"); } static void rewriteApplySites(AllocBoxToStackState &pass) { diff --git a/lib/SILOptimizer/Transforms/CSE.cpp b/lib/SILOptimizer/Transforms/CSE.cpp index 4c88beb860bbe..ab8a5f0c88115 100644 --- a/lib/SILOptimizer/Transforms/CSE.cpp +++ b/lib/SILOptimizer/Transforms/CSE.cpp @@ -220,12 +220,12 @@ class HashVisitor : public SILInstructionVisitor { } hash_code visitTupleExtractInst(TupleExtractInst *X) { - return llvm::hash_combine(X->getKind(), X->getTupleType(), X->getFieldNo(), + return llvm::hash_combine(X->getKind(), X->getTupleType(), X->getFieldIndex(), X->getOperand()); } hash_code visitTupleElementAddrInst(TupleElementAddrInst *X) { - return llvm::hash_combine(X->getKind(), X->getTupleType(), X->getFieldNo(), + return llvm::hash_combine(X->getKind(), X->getTupleType(), X->getFieldIndex(), X->getOperand()); } diff --git a/lib/SILOptimizer/Transforms/CopyPropagation.cpp b/lib/SILOptimizer/Transforms/CopyPropagation.cpp index 82bb2269f0d4f..68fecb46372f6 100644 --- a/lib/SILOptimizer/Transforms/CopyPropagation.cpp +++ b/lib/SILOptimizer/Transforms/CopyPropagation.cpp @@ -123,12 +123,12 @@ /// ===----------------------------------------------------------------------=== #define DEBUG_TYPE "copy-propagation" +#include "swift/Basic/IndexTrie.h" #include "swift/SIL/InstructionUtils.h" #include "swift/SIL/Projection.h" #include "swift/SILOptimizer/PassManager/Passes.h" #include "swift/SILOptimizer/PassManager/Transforms.h" #include "swift/SILOptimizer/Utils/CFGOptUtils.h" -#include "swift/SILOptimizer/Utils/IndexTrie.h" #include "swift/SILOptimizer/Utils/InstOptUtils.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/Statistic.h" @@ -145,200 +145,12 @@ STATISTIC(NumCopiesGenerated, "number of copy_value instructions created"); STATISTIC(NumDestroysGenerated, "number of destroy_value instructions created"); STATISTIC(NumUnknownUsers, "number of functions with unknown users"); -//===----------------------------------------------------------------------===// -// Ownership Abstraction. -// -// FIXME: These helpers are only defined in this pass for prototyping. After -// bootstrapping, they should be moved to a central ownership API (shared by -// SILOwnershipVerifier, etc.). -// -// Categories of owned value users. (U1-U2 apply to any value, O3-O5 only apply -// to owned values). -// -// U1. Use the value instantaneously (copy_value, @guaranteed). -// -// U2. Escape the nontrivial contents of the value (ref_to_unowned, -// unchecked_trivial_bitcast). -// -// O3. Propagate the value without consuming it (mark_dependence, begin_borrow). -// -// O4. Consume the value immediately (store, destroy, @owned, destructure). -// -// O5. Consume the value indirectly via a move (tuple, struct). -// ===---------------------------------------------------------------------===// - -// TODO: Figure out how to handle these cases if possible. -static bool isUnknownUse(Operand *use) { - switch (use->getUser()->getKind()) { - default: - return false; - // FIXME: (Category O3) mark_dependence requires recursion to find all - // uses. It should be replaced by begin/end dependence. - case SILInstructionKind::MarkDependenceInst: // Dependent - // FIXME: (Category O3) ref_tail_addr should require a borrow because it - // doesn't rely on fix_lifetime like other escaping instructions. - case SILInstructionKind::RefTailAddrInst: - // FIXME: (Category O3) dynamic_method_br seems to capture self, presumably - // propagating lifetime. This should probably borrow self, then be treated - // like mark_dependence. - case SILInstructionKind::DynamicMethodBranchInst: - // FIXME: (Category O3) The ownership verifier says project_box can accept an - // owned value as a normal use, but it projects the address. That's either an - // ownership bug or a special case. - case SILInstructionKind::ProjectBoxInst: - case SILInstructionKind::ProjectExistentialBoxInst: - // FIXME: (Category O3) The ownership verifier says open_existential_box can - // accept an owned value as a normal use, but it projects an address. - case SILInstructionKind::OpenExistentialBoxInst: - // Unmanaged operations hopefully don't apply to the same value as CopyValue? - case SILInstructionKind::UnmanagedRetainValueInst: - case SILInstructionKind::UnmanagedReleaseValueInst: - case SILInstructionKind::UnmanagedAutoreleaseValueInst: - return true; - } -} - -/// Return true if the given owned operand is consumed by the given call. -static bool isAppliedArgConsumed(ApplySite apply, Operand *oper) { - ParameterConvention paramConv; - if (oper->get() == apply.getCallee()) { - assert(oper->getOperandNumber() == 0 - && "function can't be passed to itself"); - paramConv = apply.getSubstCalleeType()->getCalleeConvention(); - } else { - unsigned argIndex = apply.getCalleeArgIndex(*oper); - paramConv = apply.getSubstCalleeConv() - .getParamInfoForSILArg(argIndex) - .getConvention(); - } - return isConsumedParameter(paramConv); -} - -/// Return true if the given builtin consumes its operand. -static bool isBuiltinArgConsumed(BuiltinInst *BI) { - const BuiltinInfo &Builtin = BI->getBuiltinInfo(); - switch (Builtin.ID) { - default: - llvm_unreachable("Unexpected Builtin with owned value operand."); - // Extend lifetime without consuming. - case BuiltinValueKind::ErrorInMain: - case BuiltinValueKind::UnexpectedError: - case BuiltinValueKind::WillThrow: - return false; - // UnsafeGuaranteed moves the value, which will later be destroyed. - case BuiltinValueKind::UnsafeGuaranteed: - return true; - } -} - -/// Return true if the given operand is consumed by its user. -/// -/// TODO: Review the semantics of operations that extend the lifetime *without* -/// propagating the value. Ideally, that never happens without borrowing first. -static bool isConsuming(Operand *use) { - auto *user = use->getUser(); - if (isa(user)) - return isAppliedArgConsumed(ApplySite(user), use); - - if (auto *BI = dyn_cast(user)) - return isBuiltinArgConsumed(BI); - - switch (user->getKind()) { - default: - llvm::dbgs() << *user; - llvm_unreachable("Unexpected use of a loadable owned value."); - - // Consume the value. - case SILInstructionKind::AutoreleaseValueInst: - case SILInstructionKind::DeallocBoxInst: - case SILInstructionKind::DeallocExistentialBoxInst: - case SILInstructionKind::DeallocRefInst: - case SILInstructionKind::DeinitExistentialValueInst: - case SILInstructionKind::DestroyValueInst: - case SILInstructionKind::EndLifetimeInst: - case SILInstructionKind::InitExistentialRefInst: - case SILInstructionKind::InitExistentialValueInst: - case SILInstructionKind::KeyPathInst: - case SILInstructionKind::ReleaseValueInst: - case SILInstructionKind::ReleaseValueAddrInst: - case SILInstructionKind::StoreInst: - case SILInstructionKind::StrongReleaseInst: - case SILInstructionKind::UnownedReleaseInst: - case SILInstructionKind::UnconditionalCheckedCastValueInst: - return true; - - // Terminators must consume their owned values. - case SILInstructionKind::BranchInst: - case SILInstructionKind::CheckedCastBranchInst: - case SILInstructionKind::CheckedCastValueBranchInst: - case SILInstructionKind::CondBranchInst: - case SILInstructionKind::ReturnInst: - case SILInstructionKind::ThrowInst: - return true; - - case SILInstructionKind::DeallocPartialRefInst: - return cast(user)->getInstance() == use->get(); - - // Move the value. - case SILInstructionKind::TupleInst: - case SILInstructionKind::StructInst: - case SILInstructionKind::ObjectInst: - case SILInstructionKind::EnumInst: - case SILInstructionKind::OpenExistentialRefInst: - case SILInstructionKind::UpcastInst: - case SILInstructionKind::UncheckedRefCastInst: - case SILInstructionKind::ConvertFunctionInst: - case SILInstructionKind::RefToBridgeObjectInst: - case SILInstructionKind::BridgeObjectToRefInst: - case SILInstructionKind::UnconditionalCheckedCastInst: - case SILInstructionKind::MarkUninitializedInst: - case SILInstructionKind::UncheckedEnumDataInst: - case SILInstructionKind::DestructureStructInst: - case SILInstructionKind::DestructureTupleInst: - return true; - - // BeginBorrow should already be skipped. - // EndBorrow extends the lifetime like a normal use. - case SILInstructionKind::EndBorrowInst: - return false; - - // Extend the lifetime without borrowing, propagating, or destroying it. - case SILInstructionKind::BridgeObjectToWordInst: - case SILInstructionKind::ClassMethodInst: - case SILInstructionKind::CopyBlockInst: - case SILInstructionKind::CopyValueInst: - case SILInstructionKind::DebugValueInst: - case SILInstructionKind::ExistentialMetatypeInst: - case SILInstructionKind::FixLifetimeInst: - case SILInstructionKind::SelectEnumInst: - case SILInstructionKind::SetDeallocatingInst: - case SILInstructionKind::StoreWeakInst: - case SILInstructionKind::ValueMetatypeInst: - return false; - - // Escape the value. The lifetime must already be enforced via something like - // fix_lifetime. - case SILInstructionKind::RefToRawPointerInst: - case SILInstructionKind::RefToUnmanagedInst: - case SILInstructionKind::RefToUnownedInst: - case SILInstructionKind::UncheckedBitwiseCastInst: - case SILInstructionKind::UncheckedTrivialBitCastInst: - return false; - - // Dynamic dispatch without capturing self. - case SILInstructionKind::ObjCMethodInst: - case SILInstructionKind::ObjCSuperMethodInst: - case SILInstructionKind::SuperMethodInst: - case SILInstructionKind::WitnessMethodInst: - return false; - } -} - //===----------------------------------------------------------------------===// // CopyPropagationState: shared state for the pass's analysis and transforms. //===----------------------------------------------------------------------===// namespace { + /// LiveWithin blocks have at least one use and/or def within the block, but are /// not LiveOut. /// @@ -357,9 +169,10 @@ class LivenessInfo { // used value is consumed. (Non-consuming uses within a block that is already // known to be live are uninteresting.) DenseMap users; + // Original points in the CFG where the current value was consumed or // destroyed. - typedef SmallSetVector BlockSetVec; + using BlockSetVec = SmallSetVector; BlockSetVec originalDestroyBlocks; public: @@ -409,7 +222,7 @@ class LivenessInfo { // // This call cannot be allowed to destroy %val. void recordUser(Operand *use) { - bool consume = isConsuming(use); + bool consume = use->isConsumingUse(); auto iterAndSuccess = users.try_emplace(use->getUser(), consume); if (!iterAndSuccess.second) iterAndSuccess.first->second &= consume; @@ -468,7 +281,7 @@ class DestroyInfo { /// This pass' shared state. struct CopyPropagationState { - SILFunction *F; + SILFunction *func; // Per-function invalidation state. unsigned invalidation; @@ -483,7 +296,7 @@ struct CopyPropagationState { DestroyInfo destroys; CopyPropagationState(SILFunction *F) - : F(F), invalidation(SILAnalysis::InvalidationKind::Nothing) {} + : func(F), invalidation(SILAnalysis::InvalidationKind::Nothing) {} bool isValueOwned() const { return currDef.getOwnershipKind() == ValueOwnershipKind::Owned; @@ -600,27 +413,23 @@ static bool computeLiveness(CopyPropagationState &pass) { for (Operand *use : value->getUses()) { auto *user = use->getUser(); - // Bailout if we cannot yet determine the ownership of a use. - if (isUnknownUse(use)) { - LLVM_DEBUG(llvm::dbgs() << "Unknown owned value user: "; user->dump()); - ++NumUnknownUsers; - return false; - } // Recurse through copies. if (auto *copy = dyn_cast(user)) { defUseWorkList.insert(copy); continue; } + // An entire borrow scope is considered a single use that occurs at the // point of the end_borrow. - if (auto *BBI = dyn_cast(user)) { - for (Operand *use : BBI->getUses()) { + if (auto *bbi = dyn_cast(user)) { + for (Operand *use : bbi->getUses()) { if (isa(use->getUser())) computeUseLiveness(use, pass); } continue; } - if (isConsuming(use)) { + + if (use->isConsumingUse()) { pass.liveness.recordOriginalDestroy(use); // Destroying a values does not force liveness. if (isa(user)) @@ -649,13 +458,14 @@ static void insertDestroyOnCFGEdge(SILBasicBlock *predBB, SILBasicBlock *succBB, if (destroyBB != succBB) pass.markInvalid(SILAnalysis::InvalidationKind::Branches); - SILBuilderWithScope B(destroyBB->begin()); - auto *DI = B.createDestroyValue(succBB->begin()->getLoc(), pass.currDef); + SILBuilderWithScope builder(destroyBB->begin()); + auto *di = + builder.createDestroyValue(succBB->begin()->getLoc(), pass.currDef); - pass.destroys.recordFinalDestroy(DI); + pass.destroys.recordFinalDestroy(di); ++NumDestroysGenerated; - LLVM_DEBUG(llvm::dbgs() << " Destroy on edge "; DI->dump()); + LLVM_DEBUG(llvm::dbgs() << " Destroy on edge "; di->dump()); pass.markInvalid(SILAnalysis::InvalidationKind::Instructions); } @@ -665,11 +475,11 @@ static void insertDestroyOnCFGEdge(SILBasicBlock *predBB, SILBasicBlock *succBB, /// Create a final destroy, immediately after `pos`. static void insertDestroyAtInst(SILBasicBlock::iterator pos, CopyPropagationState &pass) { - SILBuilderWithScope B(pos); - auto *DI = B.createDestroyValue((*pos).getLoc(), pass.currDef); - pass.destroys.recordFinalDestroy(DI); + SILBuilderWithScope builder(pos); + auto *di = builder.createDestroyValue((*pos).getLoc(), pass.currDef); + pass.destroys.recordFinalDestroy(di); ++NumDestroysGenerated; - LLVM_DEBUG(llvm::dbgs() << " Destroy at last use "; DI->dump()); + LLVM_DEBUG(llvm::dbgs() << " Destroy at last use "; di->dump()); pass.markInvalid(SILAnalysis::InvalidationKind::Instructions); } @@ -679,9 +489,9 @@ static void insertDestroyAtInst(SILBasicBlock::iterator pos, static void findOrInsertDestroyInBlock(SILBasicBlock *bb, CopyPropagationState &pass) { auto *defInst = pass.currDef->getDefiningInstruction(); - auto I = bb->getTerminator()->getIterator(); + auto instIter = bb->getTerminator()->getIterator(); while (true) { - auto *inst = &*I; + auto *inst = &*instIter; Optional isConsumingResult = pass.liveness.isConsumingUser(inst); if (isConsumingResult.hasValue()) { if (isConsumingResult.getValue()) { @@ -691,20 +501,20 @@ static void findOrInsertDestroyInBlock(SILBasicBlock *bb, } // Insert a destroy after this non-consuming use. assert(inst != bb->getTerminator() && "Terminator must consume operand."); - insertDestroyAtInst(std::next(I), pass); + insertDestroyAtInst(std::next(instIter), pass); break; } // This is not a potential last user. Keep scanning. // If the original destroy is reached, this is a dead live range. Insert a // destroy immediately after the def. - if (I == bb->begin()) { + if (instIter == bb->begin()) { assert(cast(pass.currDef)->getParent() == bb); - insertDestroyAtInst(I, pass); + insertDestroyAtInst(instIter, pass); break; } - --I; - if (&*I == defInst) { - insertDestroyAtInst(std::next(I), pass); + --instIter; + if (&*instIter == defInst) { + insertDestroyAtInst(std::next(instIter), pass); break; } } @@ -799,6 +609,7 @@ static void rewriteCopies(CopyPropagationState &pass) { defUseWorklist.insert(copy); return; } + if (auto *destroy = dyn_cast(user)) { // If this destroy was marked as a final destroy, ignore it; otherwise, // delete it. @@ -809,8 +620,9 @@ static void rewriteCopies(CopyPropagationState &pass) { } return; } + // Nonconsuming uses do not need copies and cannot be marked as destroys. - if (!isConsuming(use)) + if (!use->isConsumingUse()) return; // If this use was marked as a final destroy *and* this is the first @@ -862,6 +674,7 @@ static SILValue stripCopies(SILValue v) { v = srcCopy->getOperand(); continue; } + return v; } } @@ -885,12 +698,13 @@ void CopyPropagation::run() { // Step 1. Find all copied defs. CopyPropagationState pass(getFunction()); SmallSetVector copiedDefs; - for (auto &BB : *pass.F) { - for (auto &I : BB) { - if (auto *copy = dyn_cast(&I)) + for (auto &bb : *pass.func) { + for (auto &i : bb) { + if (auto *copy = dyn_cast(&i)) copiedDefs.insert(stripCopies(copy)); } } + for (auto &def : copiedDefs) { pass.resetDef(def); // Step 2: computeLiveness diff --git a/lib/SILOptimizer/Transforms/DeadCodeElimination.cpp b/lib/SILOptimizer/Transforms/DeadCodeElimination.cpp index 9737b457831a4..20ea063804885 100644 --- a/lib/SILOptimizer/Transforms/DeadCodeElimination.cpp +++ b/lib/SILOptimizer/Transforms/DeadCodeElimination.cpp @@ -349,6 +349,7 @@ void DCE::markTerminatorArgsLive(SILBasicBlock *Pred, break; } + case TermKind::AwaitAsyncContinuationInst: case TermKind::TryApplyInst: { assert(ArgIndex == 0 && "Expect a single argument!"); break; @@ -412,6 +413,7 @@ void DCE::propagateLiveness(SILInstruction *I) { markValueLive(I->getOperand(0)); return; + case TermKind::AwaitAsyncContinuationInst: case TermKind::CheckedCastBranchInst: case TermKind::CheckedCastValueBranchInst: case TermKind::CheckedCastAddrBranchInst: diff --git a/lib/SILOptimizer/Transforms/DeadObjectElimination.cpp b/lib/SILOptimizer/Transforms/DeadObjectElimination.cpp index a040d1166d176..58a47795051b7 100644 --- a/lib/SILOptimizer/Transforms/DeadObjectElimination.cpp +++ b/lib/SILOptimizer/Transforms/DeadObjectElimination.cpp @@ -24,6 +24,7 @@ //===----------------------------------------------------------------------===// #define DEBUG_TYPE "dead-object-elim" +#include "swift/Basic/IndexTrie.h" #include "swift/AST/ResilienceExpansion.h" #include "swift/SIL/BasicBlockUtils.h" #include "swift/SIL/DebugUtils.h" @@ -38,7 +39,6 @@ #include "swift/SILOptimizer/Analysis/ArraySemantic.h" #include "swift/SILOptimizer/PassManager/Passes.h" #include "swift/SILOptimizer/PassManager/Transforms.h" -#include "swift/SILOptimizer/Utils/IndexTrie.h" #include "swift/SILOptimizer/Utils/InstOptUtils.h" #include "swift/SILOptimizer/Utils/SILSSAUpdater.h" #include "swift/SILOptimizer/Utils/ValueLifetime.h" @@ -573,9 +573,9 @@ static bool removeAndReleaseArray(SingleValueInstruction *NewArrayValue, auto *TupleElt = dyn_cast(Op->getUser()); if (!TupleElt) return false; - if (TupleElt->getFieldNo() == 0 && !ArrayDef) { + if (TupleElt->getFieldIndex() == 0 && !ArrayDef) { ArrayDef = TupleElt; - } else if (TupleElt->getFieldNo() == 1 && !StorageAddress) { + } else if (TupleElt->getFieldIndex() == 1 && !StorageAddress) { StorageAddress = TupleElt; } else { return false; diff --git a/lib/SILOptimizer/Transforms/DestroyHoisting.cpp b/lib/SILOptimizer/Transforms/DestroyHoisting.cpp index f6ca2fa454d10..bda18ec671b4e 100644 --- a/lib/SILOptimizer/Transforms/DestroyHoisting.cpp +++ b/lib/SILOptimizer/Transforms/DestroyHoisting.cpp @@ -594,7 +594,7 @@ SILValue DestroyHoisting::createAddress(unsigned locIdx, SILBuilder &builder) { } else { auto *TEA = dyn_cast(projInst); newProj = projBuilder.createTupleElementAddr(TEA->getLoc(), baseAddr, - TEA->getFieldNo(), TEA->getType()); + TEA->getFieldIndex(), TEA->getType()); } assert(domTree->properlyDominates(newProj, ip) && "new projection does not dominate insert point"); diff --git a/lib/SILOptimizer/Transforms/EagerSpecializer.cpp b/lib/SILOptimizer/Transforms/EagerSpecializer.cpp index 234b856d7c0ff..9e6be717987b8 100644 --- a/lib/SILOptimizer/Transforms/EagerSpecializer.cpp +++ b/lib/SILOptimizer/Transforms/EagerSpecializer.cpp @@ -739,8 +739,10 @@ namespace { // FIXME: This should be a function transform that pushes cloned functions on // the pass manager worklist. class EagerSpecializerTransform : public SILFunctionTransform { + bool onlyCreatePrespecializations; public: - EagerSpecializerTransform() {} + EagerSpecializerTransform(bool onlyPrespecialize) + : onlyCreatePrespecializations(onlyPrespecialize) {} void run() override; @@ -772,13 +774,19 @@ static SILFunction *eagerSpecialize(SILOptFunctionBuilder &FuncBuilder, SILFunction *NewFunc = FuncSpecializer.lookupSpecialization(); if (!NewFunc) { - NewFunc = FuncSpecializer.tryCreateSpecialization(); + NewFunc = FuncSpecializer.tryCreateSpecialization( + true /*forcePrespecialization*/); if (NewFunc) newFunctions.push_back(NewFunc); } - if (!NewFunc) + if (!NewFunc) { LLVM_DEBUG(llvm::dbgs() << " Failed. Cannot specialize function.\n"); + } else if (SA.isExported()) { + NewFunc->setLinkage(NewFunc->isDefinition() ? SILLinkage::Public + : SILLinkage::PublicExternal); + } + return NewFunc; } @@ -791,7 +799,7 @@ void EagerSpecializerTransform::run() { auto &F = *getFunction(); // Process functions in any order. - if (!F.shouldOptimize()) { + if (!F.shouldOptimize() && !onlyCreatePrespecializations) { LLVM_DEBUG(llvm::dbgs() << " Cannot specialize function " << F.getName() << " because it is marked to be " "excluded from optimizations.\n"); @@ -816,28 +824,53 @@ void EagerSpecializerTransform::run() { // TODO: Use a decision-tree to reduce the amount of dynamic checks being // performed. + SmallVector attrsToRemove; + for (auto *SA : F.getSpecializeAttrs()) { + if (onlyCreatePrespecializations && !SA->isExported()) { + attrsToRemove.push_back(SA); + continue; + } + auto *targetFunc = &F; + // If the _specialize attribute specifies another target function use it + // instead to specialize. + if (SA->getTargetFunction()) { + targetFunc = SA->getTargetFunction(); + if (!targetFunc->isDefinition()) { + auto &module = FuncBuilder.getModule(); + bool success = module.loadFunction(targetFunc); + assert(success); + module.linkFunction(targetFunc); + } + onlyCreatePrespecializations = true; + } ReInfoVec.emplace_back(FuncBuilder.getModule().getSwiftModule(), - FuncBuilder.getModule().isWholeModule(), &F, - SA->getSpecializedSignature()); - auto *NewFunc = - eagerSpecialize(FuncBuilder, &F, *SA, ReInfoVec.back(), newFunctions); - - SpecializedFuncs.push_back(NewFunc); + FuncBuilder.getModule().isWholeModule(), targetFunc, + SA->getSpecializedSignature(), SA->isExported()); + auto *NewFunc = eagerSpecialize(FuncBuilder, targetFunc, *SA, + ReInfoVec.back(), newFunctions); + SpecializedFuncs.push_back( + (onlyCreatePrespecializations || + SA->isExported() /*currently we don't handle coroutines in emitDispatchTo*/) + ? nullptr + : NewFunc); + if (!SA->isExported()) + attrsToRemove.push_back(SA); } // TODO: Optimize the dispatch code to minimize the amount // of checks. Use decision trees for this purpose. bool Changed = false; - for_each3(F.getSpecializeAttrs(), SpecializedFuncs, ReInfoVec, - [&](const SILSpecializeAttr *SA, SILFunction *NewFunc, - const ReabstractionInfo &ReInfo) { - if (NewFunc) { - NewFunc->verify(); - Changed = true; - EagerDispatch(&F, ReInfo).emitDispatchTo(NewFunc); - } - }); + if (!onlyCreatePrespecializations) + for_each3(F.getSpecializeAttrs(), SpecializedFuncs, ReInfoVec, + [&](const SILSpecializeAttr *SA, SILFunction *NewFunc, + const ReabstractionInfo &ReInfo) { + if (NewFunc) { + NewFunc->verify(); + Changed = true; + EagerDispatch(&F, ReInfo).emitDispatchTo(NewFunc); + } + }); // Invalidate everything since we delete calls as well as add new // calls and branches. @@ -845,8 +878,10 @@ void EagerSpecializerTransform::run() { invalidateAnalysis(SILAnalysis::InvalidationKind::Everything); } - // As specializations are created, the attributes should be removed. - F.clearSpecializeAttrs(); + // As specializations are created, the non-exported attributes should be + // removed. + for (auto *SA : attrsToRemove) + F.removeSpecializeAttr(SA); F.verify(); for (SILFunction *newF : newFunctions) { @@ -855,5 +890,9 @@ void EagerSpecializerTransform::run() { } SILTransform *swift::createEagerSpecializer() { - return new EagerSpecializerTransform(); + return new EagerSpecializerTransform(false/*onlyCreatePrespecializations*/); +} + +SILTransform *swift::createOnonePrespecializations() { + return new EagerSpecializerTransform(true /*onlyCreatePrespecializations*/); } diff --git a/lib/SILOptimizer/Transforms/GenericSpecializer.cpp b/lib/SILOptimizer/Transforms/GenericSpecializer.cpp index e52357cd22cad..326cb1b6311c3 100644 --- a/lib/SILOptimizer/Transforms/GenericSpecializer.cpp +++ b/lib/SILOptimizer/Transforms/GenericSpecializer.cpp @@ -81,7 +81,7 @@ bool GenericSpecializer::specializeAppliesInFunction(SILFunction &F) { auto *Callee = Apply.getReferencedFunctionOrNull(); if (!Callee) continue; - if (!Callee->isDefinition()) { + if (!Callee->isDefinition() && !Callee->hasPrespecialization()) { ORE.emit([&]() { using namespace OptRemark; return RemarkMissed("NoDef", I) diff --git a/lib/SILOptimizer/Transforms/ObjectOutliner.cpp b/lib/SILOptimizer/Transforms/ObjectOutliner.cpp index 7f7289e195acd..ee236efd2746f 100644 --- a/lib/SILOptimizer/Transforms/ObjectOutliner.cpp +++ b/lib/SILOptimizer/Transforms/ObjectOutliner.cpp @@ -210,7 +210,7 @@ bool ObjectOutliner::handleTailAddr(int TailIdx, SILInstruction *TailAddr, EndCOWMutationInst *toIgnore) { if (NumTailTupleElements > 0) { if (auto *TEA = dyn_cast(TailAddr)) { - unsigned TupleIdx = TEA->getFieldNo(); + unsigned TupleIdx = TEA->getFieldIndex(); assert(TupleIdx < NumTailTupleElements); for (Operand *Use : TEA->getUses()) { if (!handleTailAddr(TailIdx * NumTailTupleElements + TupleIdx, Use->getUser(), 0, diff --git a/lib/SILOptimizer/Transforms/PerformanceInliner.cpp b/lib/SILOptimizer/Transforms/PerformanceInliner.cpp index ce3a00e413754..435751fcf20e2 100644 --- a/lib/SILOptimizer/Transforms/PerformanceInliner.cpp +++ b/lib/SILOptimizer/Transforms/PerformanceInliner.cpp @@ -318,6 +318,14 @@ bool SILPerformanceInliner::isProfitableToInline( return false; } + // Bail out if this is a generic call of a `@_specialize(exported:)` function + // and we are in the early inliner. We want to give the generic specializer + // the opportunity to see specialized call sites. + if (IsGeneric && WhatToInline == InlineSelection::NoSemanticsAndGlobalInit && + Callee->hasPrespecialization()) { + return false; + } + SILLoopInfo *LI = LA->get(Callee); ShortestPathAnalysis *SPA = getSPA(Callee, LI); assert(SPA->isValid()); @@ -439,7 +447,7 @@ bool SILPerformanceInliner::isProfitableToInline( // The access is dynamic and has no nested conflict // See if the storage location is considered by // access enforcement optimizations - AccessedStorage storage = findAccessedStorage(BAI->getSource()); + auto storage = AccessedStorage::compute(BAI->getSource()); if (BAI->hasNoNestedConflict() && (storage.isFormalAccessBase())) { BlockW.updateBenefit(ExclusivityBenefitWeight, ExclusivityBenefitBase); diff --git a/lib/SILOptimizer/Transforms/SILMem2Reg.cpp b/lib/SILOptimizer/Transforms/SILMem2Reg.cpp index 95cdcc3588e5c..7abd2ddf6a03e 100644 --- a/lib/SILOptimizer/Transforms/SILMem2Reg.cpp +++ b/lib/SILOptimizer/Transforms/SILMem2Reg.cpp @@ -187,23 +187,43 @@ class MemoryToRegisters { /// Returns true if \p I is an address of a LoadInst, skipping struct and /// tuple address projections. Sets \p singleBlock to null if the load (or /// it's address is not in \p singleBlock. -static bool isAddressForLoad(SILInstruction *I, SILBasicBlock *&singleBlock) { - - if (isa(I)) +/// This function looks for these patterns: +/// 1. (load %ASI) +/// 2. (load (struct_element_addr/tuple_element_addr/unchecked_addr_cast %ASI)) +static bool isAddressForLoad(SILInstruction *I, SILBasicBlock *&singleBlock, + bool &hasGuaranteedOwnership) { + + if (isa(I)) { + // SILMem2Reg is disabled when we find: + // (load [take] (struct_element_addr/tuple_element_addr %ASI)) + // struct_element_addr and tuple_element_addr are lowered into + // struct_extract and tuple_extract and these SIL instructions have a + // guaranteed ownership. For replacing load's users, we need an owned value. + // We will need a new copy and destroy of the running val placed after the + // last use. This is not implemented currently. + if (hasGuaranteedOwnership && cast(I)->getOwnershipQualifier() == + LoadOwnershipQualifier::Take) { + return false; + } return true; + } if (!isa(I) && !isa(I) && !isa(I)) return false; - + + if (isa(I) || isa(I)) { + hasGuaranteedOwnership = true; + } + // Recursively search for other (non-)loads in the instruction's uses. for (auto UI : cast(I)->getUses()) { SILInstruction *II = UI->getUser(); if (II->getParent() != singleBlock) singleBlock = nullptr; - - if (!isAddressForLoad(II, singleBlock)) - return false; + + if (!isAddressForLoad(II, singleBlock, hasGuaranteedOwnership)) + return false; } return true; } @@ -237,7 +257,8 @@ static bool isCaptured(AllocStackInst *ASI, bool &inSingleBlock) { singleBlock = nullptr; // Loads are okay. - if (isAddressForLoad(II, singleBlock)) + bool hasGuaranteedOwnership = false; + if (isAddressForLoad(II, singleBlock, hasGuaranteedOwnership)) continue; // We can store into an AllocStack (but not the pointer). @@ -306,8 +327,10 @@ promoteDebugValueAddr(DebugValueAddrInst *DVAI, SILValue Value, SILBuilder &B) { // Avoid inserting the same debug_value twice. for (Operand *Use : Value->getUses()) if (auto *DVI = dyn_cast(Use->getUser())) - if (*DVI->getVarInfo() == *DVAI->getVarInfo()) + if (*DVI->getVarInfo() == *DVAI->getVarInfo()) { + DVAI->eraseFromParent(); return; + } B.setInsertionPoint(DVAI); B.setCurrentDebugScope(DVAI->getDebugScope()); B.createDebugValue(DVAI->getLoc(), Value, *DVAI->getVarInfo()); @@ -352,6 +375,8 @@ static void collectLoads(SILInstruction *I, SmallVectorImpl &Loads) static void replaceLoad(LoadInst *LI, SILValue val, AllocStackInst *ASI) { ProjectionPath projections(val->getType()); SILValue op = LI->getOperand(); + SILBuilderWithScope builder(LI); + while (op != ASI) { assert(isa(op) || isa(op) || isa(op)); @@ -359,14 +384,45 @@ static void replaceLoad(LoadInst *LI, SILValue val, AllocStackInst *ASI) { projections.push_back(Projection(Inst)); op = Inst->getOperand(0); } - SILBuilder builder(LI); + + SmallVector borrowedVals; for (auto iter = projections.rbegin(); iter != projections.rend(); ++iter) { const Projection &projection = *iter; + assert(projection.getKind() == ProjectionKind::BitwiseCast || + projection.getKind() == ProjectionKind::Struct || + projection.getKind() == ProjectionKind::Tuple); + + // struct_extract and tuple_extract expect guaranteed operand ownership + // non-trivial RunningVal is owned. Insert borrow operation to convert + if (projection.getKind() == ProjectionKind::Struct || + projection.getKind() == ProjectionKind::Tuple) { + SILValue opVal = builder.emitBeginBorrowOperation(LI->getLoc(), val); + if (opVal != val) { + borrowedVals.push_back(opVal); + val = opVal; + } + } val = projection.createObjectProjection(builder, LI->getLoc(), val).get(); } + op = LI->getOperand(); - LI->replaceAllUsesWith(val); + // Replace users of the loaded value with `val` + // If we have a load [copy], replace the users with copy_value of `val` + if (LI->getOwnershipQualifier() == LoadOwnershipQualifier::Copy) { + LI->replaceAllUsesWith(builder.createCopyValue(LI->getLoc(), val)); + } else { + assert(!ASI->getFunction()->hasOwnership() || + val.getOwnershipKind() != ValueOwnershipKind::Guaranteed); + LI->replaceAllUsesWith(val); + } + + for (auto borrowedVal : borrowedVals) { + builder.emitEndBorrowOperation(LI->getLoc(), borrowedVal); + } + + // Delete the load LI->eraseFromParent(); + while (op != ASI && op->use_empty()) { assert(isa(op) || isa(op) || isa(op)); @@ -403,6 +459,7 @@ StoreInst * StackAllocationPromoter::promoteAllocationInBlock(SILBasicBlock *BB) { LLVM_DEBUG(llvm::dbgs() << "*** Promoting ASI in block: " << *ASI); + // RunningVal is the current value in the stack location. // We don't know the value of the alloca until we find the first store. SILValue RunningVal = SILValue(); // Keep track of the last StoreInst that we found. @@ -419,12 +476,16 @@ StackAllocationPromoter::promoteAllocationInBlock(SILBasicBlock *BB) { // If we are loading from the AllocStackInst and we already know the // content of the Alloca then use it. LLVM_DEBUG(llvm::dbgs() << "*** Promoting load: " << *Load); - replaceLoad(Load, RunningVal, ASI); ++NumInstRemoved; - } else if (Load->getOperand() == ASI) { + } else if (Load->getOperand() == ASI && + Load->getOwnershipQualifier() != + LoadOwnershipQualifier::Copy) { // If we don't know the content of the AllocStack then the loaded // value *is* the new value; + // Don't use result of load [copy] as a RunningVal, it necessitates + // additional logic for cleanup of consuming instructions of the result. + // StackAllocationPromoter::fixBranchesAndUses will later handle it. LLVM_DEBUG(llvm::dbgs() << "*** First load: " << *Load); RunningVal = Load; } @@ -437,16 +498,51 @@ StackAllocationPromoter::promoteAllocationInBlock(SILBasicBlock *BB) { if (SI->getDest() != ASI) continue; - // The stored value is the new running value. - RunningVal = SI->getSrc(); + // Special handling of entry block + // If we have a store [assign] in the first block, OSSA guarantees we can + // find the previous value stored in the stack location in RunningVal. + // Create destroy_value of the RunningVal. + // For all other blocks we may not know the previous value stored in the + // stack location. So we will create destroy_value in + // StackAllocationPromoter::fixBranchesAndUses, by getting the live-in + // value to the block. + if (BB->isEntry()) { + if (SI->getOwnershipQualifier() == StoreOwnershipQualifier::Assign) { + assert(RunningVal); + SILBuilderWithScope(SI).createDestroyValue(SI->getLoc(), RunningVal); + } + } // If we met a store before this one, delete it. + // If the LastStore was a store with [assign], delete it only if we know + // the RunningValue to destroy. If not, it will be deleted in + // StackAllocationPromoter::fixBranchesAndUses. if (LastStore) { - ++NumInstRemoved; - LLVM_DEBUG(llvm::dbgs() << "*** Removing redundant store: " - << *LastStore); - LastStore->eraseFromParent(); + if (LastStore->getOwnershipQualifier() == + StoreOwnershipQualifier::Assign) { + if (RunningVal) { + // For entry block, we would have already created the destroy_value, + // skip it. + if (!BB->isEntry()) { + SILBuilderWithScope(LastStore).createDestroyValue( + LastStore->getLoc(), RunningVal); + } + LLVM_DEBUG(llvm::dbgs() + << "*** Removing redundant store: " << *LastStore); + ++NumInstRemoved; + LastStore->eraseFromParent(); + } + } else { + LLVM_DEBUG(llvm::dbgs() + << "*** Removing redundant store: " << *LastStore); + ++NumInstRemoved; + LastStore->eraseFromParent(); + } } + + // The stored value is the new running value. + RunningVal = SI->getSrc(); + // The current store is now the LastStore LastStore = SI; continue; } @@ -470,10 +566,23 @@ StackAllocationPromoter::promoteAllocationInBlock(SILBasicBlock *BB) { continue; } + if (auto *DVI = dyn_cast(Inst)) { + if (DVI->getOperand() == RunningVal) { + // Reset LastStore. + // So that we don't end up passing dead values as phi args in + // StackAllocationPromoter::fixBranchesAndUses + LastStore = nullptr; + } + } + // Stop on deallocation. if (auto *DSI = dyn_cast(Inst)) { - if (DSI->getOperand() == ASI) + if (DSI->getOperand() == ASI) { + // Reset LastStore. + // So that we don't pass RunningVal as a phi arg beyond dealloc_stack + LastStore = nullptr; break; + } } } if (LastStore) { @@ -516,6 +625,10 @@ void MemoryToRegisters::removeSingleBlockAllocation(AllocStackInst *ASI) { // value. if (auto *SI = dyn_cast(Inst)) { if (SI->getDest() == ASI) { + if (SI->getOwnershipQualifier() == StoreOwnershipQualifier::Assign) { + assert(RunningVal); + SILBuilderWithScope(SI).createDestroyValue(SI->getLoc(), RunningVal); + } RunningVal = SI->getSrc(); Inst->eraseFromParent(); ++NumInstRemoved; @@ -647,6 +760,21 @@ void StackAllocationPromoter::fixPhiPredBlock(BlockSet &PhiBlocks, TI->eraseFromParent(); } +static bool hasOnlyUndefIncomingValues(SILPhiArgument *phiArg) { + SmallVector incomingValues; + phiArg->getIncomingPhiValues(incomingValues); + for (auto predArg : incomingValues) { + if (isa(predArg)) + continue; + if (isa(predArg) && + hasOnlyUndefIncomingValues(cast(predArg))) { + continue; + } + return false; + } + return true; +} + void StackAllocationPromoter::fixBranchesAndUses(BlockSet &PhiBlocks) { // First update uses of the value. SmallVector collectedLoads; @@ -683,6 +811,16 @@ void StackAllocationPromoter::fixBranchesAndUses(BlockSet &PhiBlocks) { // on. SILBasicBlock *BB = Inst->getParent(); + if (!BB->isEntry()) { + if (auto *SI = dyn_cast(Inst)) { + if (SI->getOwnershipQualifier() == StoreOwnershipQualifier::Assign) { + SILValue Def = getLiveInValue(PhiBlocks, BB); + SILBuilderWithScope(SI).createDestroyValue(SI->getLoc(), Def); + continue; + } + } + } + if (auto *DVAI = dyn_cast(Inst)) { // Replace DebugValueAddr with DebugValue. SILValue Def = getLiveInValue(PhiBlocks, BB); @@ -714,6 +852,22 @@ void StackAllocationPromoter::fixBranchesAndUses(BlockSet &PhiBlocks) { fixPhiPredBlock(PhiBlocks, Block, PBB); } } + + // If the owned phi arg we added did not have any uses, create end_lifetime to + // end its lifetime. In asserts mode, make sure we have only undef incoming + // values for such phi args. + if (ASI->getFunction()->hasOwnership()) { + for (auto Block : PhiBlocks) { + auto *phiArg = cast( + Block->getArgument(Block->getNumArguments() - 1)); + if (phiArg->getOwnershipKind() == ValueOwnershipKind::Owned && + phiArg->use_empty()) { + assert(hasOnlyUndefIncomingValues(phiArg)); + SILBuilderWithScope(&Block->front()) + .createEndLifetime(Block->front().getLoc(), phiArg); + } + } + } } void StackAllocationPromoter::pruneAllocStackUsage() { @@ -980,10 +1134,6 @@ class SILMem2Reg : public SILFunctionTransform { void run() override { SILFunction *F = getFunction(); - // FIXME: We should be able to support ownership. - if (F->hasOwnership()) - return; - LLVM_DEBUG(llvm::dbgs() << "** Mem2Reg on function: " << F->getName() << " **\n"); diff --git a/lib/SILOptimizer/Transforms/SILSROA.cpp b/lib/SILOptimizer/Transforms/SILSROA.cpp index b931a3c156769..5ebe88c589b56 100644 --- a/lib/SILOptimizer/Transforms/SILSROA.cpp +++ b/lib/SILOptimizer/Transforms/SILSROA.cpp @@ -87,7 +87,7 @@ SROAMemoryUseAnalyzer::createAgg(SILBuilder &B, SILLocation Loc, unsigned SROAMemoryUseAnalyzer::getEltNoForProjection(SILInstruction *Inst) { if (TT) - return cast(Inst)->getFieldNo(); + return cast(Inst)->getFieldIndex(); assert(SD && "SD should not be null since either it or TT must be set at " "this point."); diff --git a/lib/SILOptimizer/Transforms/SimplifyCFG.cpp b/lib/SILOptimizer/Transforms/SimplifyCFG.cpp index b2de748e580db..6e45047787dc8 100644 --- a/lib/SILOptimizer/Transforms/SimplifyCFG.cpp +++ b/lib/SILOptimizer/Transforms/SimplifyCFG.cpp @@ -802,10 +802,12 @@ static int getThreadingCost(SILInstruction *I) { return 0; } +static int maxBranchRecursionDepth = 6; /// couldSimplifyUsers - Check to see if any simplifications are possible if /// "Val" is substituted for BBArg. If so, return true, if nothing obvious /// is possible, return false. -static bool couldSimplifyEnumUsers(SILArgument *BBArg, int Budget) { +static bool couldSimplifyEnumUsers(SILArgument *BBArg, int Budget, + int recursionDepth = 0) { SILBasicBlock *BB = BBArg->getParent(); int BudgetForBranch = 100; @@ -833,6 +835,9 @@ static bool couldSimplifyEnumUsers(SILArgument *BBArg, int Budget) { } if (auto *BI = dyn_cast(User)) { + if (recursionDepth >= maxBranchRecursionDepth) { + return false; + } if (BudgetForBranch > Budget) { BudgetForBranch = Budget; for (SILInstruction &I : *BB) { @@ -844,7 +849,8 @@ static bool couldSimplifyEnumUsers(SILArgument *BBArg, int Budget) { if (BudgetForBranch > 0) { SILBasicBlock *DestBB = BI->getDestBB(); unsigned OpIdx = UI->getOperandNumber(); - if (couldSimplifyEnumUsers(DestBB->getArgument(OpIdx), BudgetForBranch)) + if (couldSimplifyEnumUsers(DestBB->getArgument(OpIdx), BudgetForBranch, + recursionDepth + 1)) return true; } } @@ -916,16 +922,57 @@ static bool couldRemoveRelease(SILBasicBlock *SrcBB, SILValue SrcV, return IsReleaseOfDest; } +/// Returns true if any instruction in \p block may write memory. +static bool blockMayWriteMemory(SILBasicBlock *block) { + for (auto instAndIdx : llvm::enumerate(*block)) { + if (instAndIdx.value().mayWriteToMemory()) + return true; + // Only look at the first 20 instructions to avoid compile time problems for + // corner cases of very large blocks without memory writes. + // 20 instructions is more than enough. + if (instAndIdx.index() > 50) + return true; + } + return false; +} + +// Returns true if \p block contains an injected an enum case into \p enumAddr +// which is valid at the end of the block. +static bool hasInjectedEnumAtEndOfBlock(SILBasicBlock *block, SILValue enumAddr) { + for (auto instAndIdx : llvm::enumerate(llvm::reverse(*block))) { + SILInstruction &inst = instAndIdx.value(); + if (auto *injectInst = dyn_cast(&inst)) { + return injectInst->getOperand() == enumAddr; + } + if (inst.mayWriteToMemory()) + return false; + // Only look at the first 20 instructions to avoid compile time problems for + // corner cases of very large blocks without memory writes. + // 20 instructions is more than enough. + if (instAndIdx.index() > 50) + return false; + } + return false; +} + /// tryJumpThreading - Check to see if it looks profitable to duplicate the /// destination of an unconditional jump into the bottom of this block. bool SimplifyCFG::tryJumpThreading(BranchInst *BI) { auto *DestBB = BI->getDestBB(); auto *SrcBB = BI->getParent(); + TermInst *destTerminator = DestBB->getTerminator(); // If the destination block ends with a return, we don't want to duplicate it. // We want to maintain the canonical form of a single return where possible. - if (DestBB->getTerminator()->isFunctionExiting()) + if (destTerminator->isFunctionExiting()) return false; + // Jump threading only makes sense if there is an argument on the branch + // (which is reacted on in the DestBB), or if this goes through a memory + // location (switch_enum_addr is the only adress-instruction which we + // currently handle). + if (BI->getArgs().empty() && !isa(destTerminator)) + return false; + // We don't have a great cost model at the SIL level, so we don't want to // blissly duplicate tons of code with a goal of improved performance (we'll // leave that to LLVM). However, doing limited code duplication can lead to @@ -956,11 +1003,29 @@ bool SimplifyCFG::tryJumpThreading(BranchInst *BI) { } } - if (ThreadingBudget == 0 && isa(DestBB->getTerminator())) { - for (auto V : BI->getArgs()) { - if (isa(V) || isa(V)) { + if (ThreadingBudget == 0) { + if (isa(destTerminator)) { + for (auto V : BI->getArgs()) { + if (isa(V) || isa(V)) { + ThreadingBudget = 4; + break; + } + } + } else if (auto *SEA = dyn_cast(destTerminator)) { + // If the branch-block injects a certain enum case and the destination + // switches on that enum, it's worth jump threading. E.g. + // + // inject_enum_addr %enum : $*Optional, #Optional.some + // ... // no memory writes here + // br DestBB + // DestBB: + // ... // no memory writes here + // switch_enum_addr %enum : $*Optional, case #Optional.some ... + // + SILValue enumAddr = SEA->getOperand(); + if (!blockMayWriteMemory(DestBB) && + hasInjectedEnumAtEndOfBlock(SrcBB, enumAddr)) { ThreadingBudget = 4; - break; } } } @@ -976,7 +1041,7 @@ bool SimplifyCFG::tryJumpThreading(BranchInst *BI) { // control flow. Still, we make an exception for switch_enum. bool DestIsLoopHeader = (LoopHeaders.count(DestBB) != 0); if (DestIsLoopHeader) { - if (!isa(DestBB->getTerminator())) + if (!isa(destTerminator)) return false; } @@ -1189,21 +1254,8 @@ bool SimplifyCFG::simplifyBranchBlock(BranchInst *BI) { LLVM_DEBUG(llvm::dbgs() << "merge bb" << BB->getDebugID() << " with bb" << DestBB->getDebugID() << '\n'); - // If there are any BB arguments in the destination, replace them with the - // branch operands, since they must dominate the dest block. for (unsigned i = 0, e = BI->getArgs().size(); i != e; ++i) { - if (DestBB->getArgument(i) != BI->getArg(i)) { - SILValue Val = BI->getArg(i); - DestBB->getArgument(i)->replaceAllUsesWith(Val); - if (!isVeryLargeFunction) { - if (auto *I = dyn_cast(Val)) { - // Replacing operands may trigger constant folding which then could - // trigger other simplify-CFG optimizations. - ConstFolder.addToWorklist(I); - ConstFolder.processWorkList(); - } - } - } else { + if (DestBB->getArgument(i) == BI->getArg(i)) { // We must be processing an unreachable part of the cfg with a cycle. // bb1(arg1): // preds: bb3 // br bb2 @@ -1214,6 +1266,23 @@ bool SimplifyCFG::simplifyBranchBlock(BranchInst *BI) { // bb3: // preds: bb2 // br bb1(arg1) assert(!isReachable(BB) && "Should only occur in unreachable block"); + return Simplified; + } + } + + // If there are any BB arguments in the destination, replace them with the + // branch operands, since they must dominate the dest block. + for (unsigned i = 0, e = BI->getArgs().size(); i != e; ++i) { + assert(DestBB->getArgument(i) != BI->getArg(i)); + SILValue Val = BI->getArg(i); + DestBB->getArgument(i)->replaceAllUsesWith(Val); + if (!isVeryLargeFunction) { + if (auto *I = dyn_cast(Val)) { + // Replacing operands may trigger constant folding which then could + // trigger other simplify-CFG optimizations. + ConstFolder.addToWorklist(I); + ConstFolder.processWorkList(); + } } } @@ -1286,8 +1355,7 @@ bool SimplifyCFG::simplifyBranchBlock(BranchInst *BI) { // If this unconditional branch has BBArgs, check to see if duplicating the // destination would allow it to be simplified. This is a simple form of jump // threading. - if (!isVeryLargeFunction && !BI->getArgs().empty() && - tryJumpThreading(BI)) + if (!isVeryLargeFunction && tryJumpThreading(BI)) return true; return Simplified; @@ -1503,17 +1571,29 @@ bool SimplifyCFG::simplifyCondBrBlock(CondBranchInst *BI) { // Simplify cond_br where both sides jump to the same blocks with the same // args. - if (TrueArgs == FalseArgs && (TrueSide == FalseTrampolineDest || - FalseSide == TrueTrampolineDest)) { - LLVM_DEBUG(llvm::dbgs() << "replace cond_br with same dests with br: " - << *BI); - SILBuilderWithScope(BI).createBranch(BI->getLoc(), - TrueTrampolineDest ? FalseSide : TrueSide, TrueArgs); + auto condBrToBr = [&](OperandValueArrayRef branchArgs, + SILBasicBlock *newDest) { + LLVM_DEBUG(llvm::dbgs() + << "replace cond_br with same dests with br: " << *BI); + SILBuilderWithScope(BI).createBranch(BI->getLoc(), newDest, branchArgs); BI->eraseFromParent(); addToWorklist(ThisBB); - addToWorklist(TrueSide); ++NumConstantFolded; - return true; + }; + if (TrueArgs == FalseArgs) { + if (TrueTrampolineDest) { + if (TrueTrampolineDest == FalseSide + || TrueTrampolineDest == FalseTrampolineDest) { + condBrToBr(TrueArgs, TrueTrampolineDest); + removeIfDead(TrueSide); + removeIfDead(FalseSide); + return true; + } + } else if (FalseTrampolineDest == TrueSide) { + condBrToBr(TrueArgs, FalseTrampolineDest); + removeIfDead(FalseSide); + return true; + } } auto *TrueTrampolineBr = getTrampolineWithoutBBArgsTerminator(TrueSide); @@ -2630,6 +2710,9 @@ bool SimplifyCFG::simplifyBlocks() { case TermKind::UnwindInst: case TermKind::YieldInst: break; + case TermKind::AwaitAsyncContinuationInst: + // TODO(async): Simplify AwaitAsyncContinuationInst + break; } // If the block has a cond_fail, try to move it to the predecessors. Changed |= tryMoveCondFailToPreds(BB); @@ -3144,7 +3227,7 @@ static SILValue getInsertedValue(SILInstruction *Aggregate, } auto *Tuple = cast(Aggregate); auto *TEI = cast(Extract); - return Tuple->getElement(TEI->getFieldNo()); + return Tuple->getElement(TEI->getFieldIndex()); } /// Find a parent SwitchEnumInst of the block \p BB. The block \p BB is a diff --git a/lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp b/lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp index 325c9bd7a7daf..abee9e0f0ed4d 100644 --- a/lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp +++ b/lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp @@ -380,7 +380,7 @@ static bool tryToSpeculateTarget(FullApplySite AI, ClassHierarchyAnalysis *CHA, dyn_cast(TupleExtract->getOperand())) if (UnsafeGuaranteedSelf->getBuiltinKind() == BuiltinValueKind::UnsafeGuaranteed && - TupleExtract->getFieldNo() == 0) + TupleExtract->getFieldIndex() == 0) return false; // Strip any upcasts off of our 'self' value, potentially leaving us diff --git a/lib/SILOptimizer/Transforms/TempLValueOpt.cpp b/lib/SILOptimizer/Transforms/TempLValueOpt.cpp index 8511de4452e75..36303388a2a02 100644 --- a/lib/SILOptimizer/Transforms/TempLValueOpt.cpp +++ b/lib/SILOptimizer/Transforms/TempLValueOpt.cpp @@ -15,6 +15,7 @@ //===----------------------------------------------------------------------===// #define DEBUG_TYPE "cow-opts" + #include "swift/SILOptimizer/PassManager/Transforms.h" #include "swift/SILOptimizer/Analysis/AliasAnalysis.h" #include "swift/SIL/SILFunction.h" @@ -105,8 +106,8 @@ void TempLValueOptPass::run() { } // Do the optimizations. for (CopyAddrInst *copyInst : copyInsts) { - changed |=combineCopyAndDestroy(copyInst); - changed |=tempLValueOpt(copyInst); + changed |= combineCopyAndDestroy(copyInst); + changed |= tempLValueOpt(copyInst); } } diff --git a/lib/SILOptimizer/Transforms/TempRValueElimination.cpp b/lib/SILOptimizer/Transforms/TempRValueElimination.cpp index 6e4ab1b04bbbc..6f060ec7d2db2 100644 --- a/lib/SILOptimizer/Transforms/TempRValueElimination.cpp +++ b/lib/SILOptimizer/Transforms/TempRValueElimination.cpp @@ -61,27 +61,31 @@ namespace { /// it finds cases in which it is easy to determine that the source is /// unmodified during the copy destination's lifetime. Thus, the destination can /// be viewed as a short-lived "rvalue". +/// +/// As a second optimization, also stores into temporaries are handled. This is +/// a simple form of redundant-load-elimination (RLE). +/// +/// %temp = alloc_stack $T +/// store %src to [initialization] %temp : $*T +/// // no writes to %temp +/// %v = load [take] %temp : $*T +/// dealloc_stack %temp : $*T +/// +/// TODO: Check if we still need to handle stores when RLE supports OSSA. class TempRValueOptPass : public SILFunctionTransform { AliasAnalysis *aa = nullptr; - bool collectLoads(Operand *userOp, SILInstruction *userInst, - SingleValueInstruction *addr, SILValue srcObject, + bool collectLoads(Operand *addressUse, CopyAddrInst *originalCopy, SmallPtrSetImpl &loadInsts); bool collectLoadsFromProjection(SingleValueInstruction *projection, - SILValue srcAddr, + CopyAddrInst *originalCopy, SmallPtrSetImpl &loadInsts); - bool - checkNoSourceModification(CopyAddrInst *copyInst, SILValue copySrc, - const SmallPtrSetImpl &useInsts); + SILInstruction *getLastUseWhileSourceIsNotModified( + CopyAddrInst *copyInst, const SmallPtrSetImpl &useInsts); bool - checkTempObjectDestroy(AllocStackInst *tempObj, CopyAddrInst *copyInst, - ValueLifetimeAnalysis::Frontier &tempAddressFrontier); - - bool checkNoTempObjectModificationInApply(Operand *tempObjUser, - SILInstruction *inst, - SILValue srcAddr); + checkTempObjectDestroy(AllocStackInst *tempObj, CopyAddrInst *copyInst); bool tryOptimizeCopyIntoTemp(CopyAddrInst *copyInst); std::pair @@ -93,84 +97,20 @@ class TempRValueOptPass : public SILFunctionTransform { } // anonymous namespace bool TempRValueOptPass::collectLoadsFromProjection( - SingleValueInstruction *projection, SILValue srcAddr, + SingleValueInstruction *projection, CopyAddrInst *originalCopy, SmallPtrSetImpl &loadInsts) { - if (!srcAddr) { - LLVM_DEBUG( - llvm::dbgs() - << " Temp has addr_projection use?! Can not yet promote to value" - << *projection); - return false; - } - // Transitively look through projections on stack addresses. for (auto *projUseOper : projection->getUses()) { auto *user = projUseOper->getUser(); if (user->isTypeDependentOperand(*projUseOper)) continue; - if (!collectLoads(projUseOper, user, projection, srcAddr, loadInsts)) + if (!collectLoads(projUseOper, originalCopy, loadInsts)) return false; } return true; } -/// Check if 'tempObjUser' passed to the apply instruction can be modified by it -bool TempRValueOptPass::checkNoTempObjectModificationInApply( - Operand *tempObjUser, SILInstruction *applyInst, SILValue srcAddr) { - ApplySite apply(applyInst); - - // Check if the function can just read from tempObjUser. - auto convention = apply.getArgumentConvention(*tempObjUser); - if (!convention.isGuaranteedConvention()) { - LLVM_DEBUG(llvm::dbgs() << " Temp consuming use may write/destroy " - "its source" - << *applyInst); - return false; - } - - // If we do not have an src address, but are indirect, bail. We would need - // to perform function signature specialization to change the functions - // signature to pass something direct. - if (!srcAddr && convention.isIndirectConvention()) { - LLVM_DEBUG( - llvm::dbgs() - << " Temp used to materialize value for indirect convention?! Can " - "not remove temporary without func sig opts" - << *applyInst); - return false; - } - - // Check if there is another function argument, which is inout which might - // modify the source address if we have one. - // - // When a use of the temporary is an apply, then we need to prove that the - // function called by the apply cannot modify the temporary's source - // value. By design, this should be handled by - // `checkNoSourceModification`. However, this would be too conservative - // since it's common for the apply to have an @out argument, and alias - // analysis cannot prove that the @out does not alias with `src`. Instead, - // `checkNoSourceModification` always avoids analyzing the current use, so - // applies need to be handled here. We already know that an @out cannot - // alias with `src` because the `src` value must be initialized at the point - // of the call. Hence, it is sufficient to check specifically for another - // @inout that might alias with `src`. - if (srcAddr) { - auto calleeConv = apply.getSubstCalleeConv(); - unsigned calleeArgIdx = apply.getCalleeArgIndexOfFirstAppliedArg(); - for (const auto &operand : apply.getArgumentOperands()) { - auto argConv = calleeConv.getSILArgumentConvention(calleeArgIdx); - if (argConv.isInoutConvention()) { - if (!aa->isNoAlias(operand.get(), srcAddr)) { - return false; - } - } - ++calleeArgIdx; - } - } - return true; -} - /// Transitively explore all data flow uses of the given \p address until /// reaching a load or returning false. /// @@ -184,12 +124,16 @@ bool TempRValueOptPass::checkNoTempObjectModificationInApply( /// location at \address. The temporary must be initialized by the original copy /// and never written to again. Therefore, collectLoads disallows any operation /// that may write to memory at \p address. -bool TempRValueOptPass::collectLoads( - Operand *userOp, SILInstruction *user, SingleValueInstruction *address, - SILValue srcAddr, SmallPtrSetImpl &loadInsts) { +bool TempRValueOptPass:: +collectLoads(Operand *addressUse, CopyAddrInst *originalCopy, + SmallPtrSetImpl &loadInsts) { + SILInstruction *user = addressUse->getUser(); + SILValue address = addressUse->get(); + // All normal uses (loads) must be in the initialization block. // (The destroy and dealloc are commonly in a different block though.) - if (user->getParent() != address->getParent()) + SILBasicBlock *block = originalCopy->getParent(); + if (user->getParent() != block) return false; // Only allow uses that cannot destroy their operand. We need to be sure @@ -207,9 +151,29 @@ bool TempRValueOptPass::collectLoads( LLVM_DEBUG(llvm::dbgs() << " Temp use may write/destroy its source" << *user); return false; - case SILInstructionKind::BeginAccessInst: - return cast(user)->getAccessKind() == SILAccessKind::Read; + case SILInstructionKind::BeginAccessInst: { + auto *beginAccess = cast(user); + if (beginAccess->getAccessKind() != SILAccessKind::Read) + return false; + // We don't have to recursively call collectLoads for the beginAccess + // result, because a SILAccessKind::Read already guarantees that there are + // no writes to the beginAccess result address (or any projection from it). + // But we have to register the end-accesses as loads to correctly mark the + // end-of-lifetime of the tempObj. + // + // %addr = begin_access [read] + // ... // there can be no writes to %addr here + // end_acess %addr // <- This is where the use actually ends. + for (Operand *accessUse : beginAccess->getUses()) { + if (auto *endAccess = dyn_cast(accessUse->getUser())) { + if (endAccess->getParent() != block) + return false; + loadInsts.insert(endAccess); + } + } + return true; + } case SILInstructionKind::MarkDependenceInst: { auto mdi = cast(user); // If the user is the base operand of the MarkDependenceInst we can return @@ -220,8 +184,7 @@ bool TempRValueOptPass::collectLoads( // If the user is the value operand of the MarkDependenceInst we have to // transitively explore its uses until we reach a load or return false for (auto *mdiUseOper : mdi->getUses()) { - if (!collectLoads(mdiUseOper, mdiUseOper->getUser(), mdi, srcAddr, - loadInsts)) + if (!collectLoads(mdiUseOper, originalCopy, loadInsts)) return false; } return true; @@ -231,35 +194,27 @@ bool TempRValueOptPass::collectLoads( return false; LLVM_FALLTHROUGH; case SILInstructionKind::ApplyInst: - case SILInstructionKind::TryApplyInst: { - if (!checkNoTempObjectModificationInApply(userOp, user, srcAddr)) - return false; - // Everything is okay with the function call. Register it as a "load". - loadInsts.insert(user); - return true; - } + case SILInstructionKind::TryApplyInst: case SILInstructionKind::BeginApplyInst: { - if (!checkNoTempObjectModificationInApply(userOp, user, srcAddr)) + auto convention = ApplySite(user).getArgumentConvention(*addressUse); + if (!convention.isGuaranteedConvention()) return false; - auto beginApply = cast(user); - // Register 'end_apply'/'abort_apply' as loads as well - // 'checkNoSourceModification' should check instructions until - // 'end_apply'/'abort_apply'. - for (auto tokenUses : beginApply->getTokenResult()->getUses()) { - loadInsts.insert(tokenUses->getUser()); + loadInsts.insert(user); + if (auto *beginApply = dyn_cast(user)) { + // Register 'end_apply'/'abort_apply' as loads as well + // 'checkNoSourceModification' should check instructions until + // 'end_apply'/'abort_apply'. + for (auto tokenUse : beginApply->getTokenResult()->getUses()) { + SILInstruction *tokenUser = tokenUse->getUser(); + if (tokenUser->getParent() != block) + return false; + loadInsts.insert(tokenUser); + } } return true; } case SILInstructionKind::OpenExistentialAddrInst: { - // If we do not have an srcAddr, bail. We do not support promoting this yet. - if (!srcAddr) { - LLVM_DEBUG(llvm::dbgs() << " Temp has open_existential_addr use?! Can " - "not yet promote to value" - << *user); - return false; - } - // We only support open existential addr if the access is immutable. auto *oeai = cast(user); if (oeai->getAccessKind() != OpenedExistentialAccess::Immutable) { @@ -268,7 +223,7 @@ bool TempRValueOptPass::collectLoads( << *user); return false; } - return collectLoadsFromProjection(oeai, srcAddr, loadInsts); + return collectLoadsFromProjection(oeai, originalCopy, loadInsts); } case SILInstructionKind::UncheckedTakeEnumDataAddrInst: { // In certain cases, unchecked_take_enum_data_addr invalidates the @@ -282,14 +237,15 @@ bool TempRValueOptPass::collectLoads( return false; } - return collectLoadsFromProjection(utedai, srcAddr, loadInsts); + return collectLoadsFromProjection(utedai, originalCopy, loadInsts); } case SILInstructionKind::StructElementAddrInst: - case SILInstructionKind::TupleElementAddrInst: { + case SILInstructionKind::TupleElementAddrInst: + case SILInstructionKind::UncheckedAddrCastInst: return collectLoadsFromProjection(cast(user), - srcAddr, loadInsts); - } - case SILInstructionKind::LoadInst: + originalCopy, loadInsts); + + case SILInstructionKind::LoadInst: { // Loads are the end of the data flow chain. The users of the load can't // access the temporary storage. // @@ -299,20 +255,18 @@ bool TempRValueOptPass::collectLoads( // function. So if we have such a load [take], we /must/ have a // reinitialization or an alloc_stack that does not fit the pattern we are // expecting from SILGen. Be conservative and return false. - if (auto *li = dyn_cast(user)) { - if (li->getOwnershipQualifier() == LoadOwnershipQualifier::Take) { - return false; - } + auto *li = cast(user); + if (li->getOwnershipQualifier() == LoadOwnershipQualifier::Take && + // Only accept load [take] if it takes the whole temporary object. + // load [take] from a projection would destroy only a part of the + // temporary and we don't handle this. + address != originalCopy->getDest()) { + return false; } loadInsts.insert(user); return true; - + } case SILInstructionKind::LoadBorrowInst: - // If we do not have a source addr, we must be trying to eliminate a - // store. Until we check that the source object is not destroyed within the - // given range, we need bail. - if (!srcAddr) - return false; loadInsts.insert(user); return true; case SILInstructionKind::FixLifetimeInst: @@ -327,26 +281,39 @@ bool TempRValueOptPass::collectLoads( LLVM_DEBUG(llvm::dbgs() << " Temp written or taken" << *user); return false; } + // As with load [take], only accept copy_addr [take] if it takes the whole + // temporary object. + if (copyFromTmp->isTakeOfSrc() && address != originalCopy->getDest()) + return false; loadInsts.insert(copyFromTmp); return true; } } } -/// Checks if the copy's source can be modified within the temporary's lifetime. +/// Checks if the source of \p copyInst is not modified within the temporary's +/// lifetime, i.e. is not modified before the last use of \p useInsts. +/// +/// If there are no source modifications with the lifetime, returns the last +/// user (or copyInst if there are no uses at all). +/// Otherwise, returns a nullptr. /// /// Unfortunately, we cannot simply use the destroy points as the lifetime end, /// because they can be in a different basic block (that's what SILGen /// generates). Instead we guarantee that all normal uses are within the block /// of the temporary and look for the last use, which effectively ends the /// lifetime. -bool TempRValueOptPass::checkNoSourceModification( - CopyAddrInst *copyInst, SILValue copySrc, - const SmallPtrSetImpl &useInsts) { +SILInstruction *TempRValueOptPass::getLastUseWhileSourceIsNotModified( + CopyAddrInst *copyInst, const SmallPtrSetImpl &useInsts) { + if (useInsts.empty()) + return copyInst; unsigned numLoadsFound = 0; - auto iter = std::next(copyInst->getIterator()); + SILValue copySrc = copyInst->getSrc(); + // We already checked that the useful lifetime of the temporary ends in - // the initialization block. + // the initialization block. Iterate over the instructions of the block, + // starting at copyInst, until we get to the last user. + auto iter = std::next(copyInst->getIterator()); auto iterEnd = copyInst->getParent()->end(); for (; iter != iterEnd; ++iter) { SILInstruction *inst = &*iter; @@ -356,17 +323,26 @@ bool TempRValueOptPass::checkNoSourceModification( // If this is the last use of the temp we are ok. After this point, // modifications to the source don't matter anymore. - if (numLoadsFound == useInsts.size()) - return true; + // Note that we are assuming here that if an instruction loads and writes + // to copySrc at the same time (like a copy_addr could do), the write + // takes effect after the load. + if (numLoadsFound == useInsts.size()) { + // Function calls are an exception: in a called function a potential + // modification of copySrc could occur _before_ the read of the temporary. + if (FullApplySite::isa(inst) && aa->mayWriteToMemory(inst, copySrc)) + return nullptr; + + return inst; + } if (aa->mayWriteToMemory(inst, copySrc)) { LLVM_DEBUG(llvm::dbgs() << " Source modified by" << *iter); - return false; + return nullptr; } } // For some reason, not all normal uses have been seen between the copy and // the end of the initialization block. We should never reach here. - return false; + return nullptr; } /// Return true if the \p tempObj, which is initialized by \p copyInst, is @@ -380,13 +356,7 @@ bool TempRValueOptPass::checkNoSourceModification( /// releasing it. Rather than detecting unbalanced load releases, simply check /// that tempObj is destroyed directly on all paths. bool TempRValueOptPass::checkTempObjectDestroy( - AllocStackInst *tempObj, CopyAddrInst *copyInst, - ValueLifetimeAnalysis::Frontier &tempAddressFrontier) { - // If the original copy was a take, then replacing all uses cannot affect - // the lifetime. - if (copyInst->isTakeOfSrc()) - return true; - + AllocStackInst *tempObj, CopyAddrInst *copyInst) { // ValueLifetimeAnalysis is not normally used for address types. It does not // reason about the lifetime of the in-memory object. However the utility can // be abused here to check that the address is directly destroyed on all @@ -405,6 +375,7 @@ bool TempRValueOptPass::checkTempObjectDestroy( } // Find the boundary of tempObj's address lifetime, starting at copyInst. ValueLifetimeAnalysis vla(copyInst, users); + ValueLifetimeAnalysis::Frontier tempAddressFrontier; if (!vla.computeFrontier(tempAddressFrontier, ValueLifetimeAnalysis::DontModifyCFG)) { return false; @@ -425,15 +396,8 @@ bool TempRValueOptPass::checkTempObjectDestroy( if (isa(lastUser)) continue; - if (auto *li = dyn_cast(lastUser)) { - if (li->getOwnershipQualifier() == LoadOwnershipQualifier::Take) { - continue; - } - } - if (auto *cai = dyn_cast(lastUser)) { assert(cai->getSrc() == tempObj && "collectLoads checks for writes"); - assert(!copyInst->isTakeOfSrc() && "checked above"); if (cai->isTakeOfSrc()) continue; } @@ -451,6 +415,8 @@ bool TempRValueOptPass::tryOptimizeCopyIntoTemp(CopyAddrInst *copyInst) { if (!tempObj) return false; + bool isOSSA = copyInst->getFunction()->hasOwnership(); + // The copy's source address must not be a scoped instruction, like // begin_borrow. When the temporary object is eliminated, it's uses are // replaced with the copy's source. Therefore, the source address must be @@ -458,9 +424,13 @@ bool TempRValueOptPass::tryOptimizeCopyIntoTemp(CopyAddrInst *copyInst) { // source. End-of-scope markers, such as end_borrow, do not write to or // destroy memory, so scoped addresses are not valid replacements. SILValue copySrc = stripAccessMarkers(copyInst->getSrc()); - assert(tempObj != copySrc && "can't initialize temporary with itself"); + // If the source of the copyInst is taken, we must insert a compensating + // destroy_addr. This must be done at the right spot: after the last use + // tempObj, but before any (potential) re-initialization of the source. + bool needToInsertDestroy = copyInst->isTakeOfSrc(); + // Scan all uses of the temporary storage (tempObj) to verify they all refer // to the value initialized by this copy. It is sufficient to check that the // only users that modify memory are the copy_addr [initialization] and @@ -472,98 +442,90 @@ bool TempRValueOptPass::tryOptimizeCopyIntoTemp(CopyAddrInst *copyInst) { if (user == copyInst) continue; - // Destroys and deallocations are allowed to be in a different block. - if (isa(user) || isa(user)) + // Deallocations are allowed to be in a different block. + if (isa(user)) continue; - // Same for load [take] on the top level temp object. SILGen always takes - // whole values from temporaries. If we have load [take] on projections from - // our base, we fail since those would be re-initializations. - if (auto *li = dyn_cast(user)) { - if (li->getOwnershipQualifier() == LoadOwnershipQualifier::Take) { - continue; + // Also, destroys are allowed to be in a different block. + if (isa(user)) { + if (!isOSSA && needToInsertDestroy) { + // In non-OSSA mode, for the purpose of inserting the destroy of + // copySrc, we have to be conservative and assume that the lifetime of + // tempObj goes beyond it's last use - until the final destroy_addr. + // Otherwise we would risk of inserting the destroy too early. + // So we just treat the destroy_addr as any other use of tempObj. + if (user->getParent() != copyInst->getParent()) + return false; + loadInsts.insert(user); } + continue; } - if (!collectLoads(useOper, user, tempObj, copySrc, loadInsts)) + if (!collectLoads(useOper, copyInst, loadInsts)) return false; } // Check if the source is modified within the lifetime of the temporary. - if (!checkNoSourceModification(copyInst, copySrc, loadInsts)) + SILInstruction *lastLoadInst = getLastUseWhileSourceIsNotModified(copyInst, + loadInsts); + if (!lastLoadInst) return false; - ValueLifetimeAnalysis::Frontier tempAddressFrontier; - if (!checkTempObjectDestroy(tempObj, copyInst, tempAddressFrontier)) + // We cannot insert the destroy of copySrc after lastLoadInst if copySrc is + // re-initialized by exactly this instruction. + // This is a corner case, but can happen if lastLoadInst is a copy_addr. + // Example: + // copy_addr [take] %copySrc to [initialization] %tempObj // copyInst + // copy_addr [take] %tempObj to [initialization] %copySrc // lastLoadInst + if (needToInsertDestroy && lastLoadInst != copyInst && + !isa(lastLoadInst) && + aa->mayWriteToMemory(lastLoadInst, copySrc)) + return false; + + if (!isOSSA && !checkTempObjectDestroy(tempObj, copyInst)) return false; LLVM_DEBUG(llvm::dbgs() << " Success: replace temp" << *tempObj); - // Do a "replaceAllUses" by either deleting the users or replacing them with - // the source address. Note: we must not delete the original copyInst because - // it would crash the instruction iteration in run(). Instead the copyInst - // gets identical Src and Dest operands. + if (needToInsertDestroy) { + // Compensate the [take] of the original copyInst. + SILBuilderWithScope::insertAfter(lastLoadInst, [&] (SILBuilder &builder) { + builder.createDestroyAddr(builder.getInsertionPoint()->getLoc(), copySrc); + }); + } + + // * Replace all uses of the tempObj with the copySrc. // - // NOTE: We delete instructions at the end to allow us to use - // tempAddressFrontier to insert compensating destroys for load [take]. - SmallVector toDelete; + // * Delete the destroy(s) of tempObj (to compensate the removal of the + // original copyInst): either by erasing the destroy_addr or by converting + // load/copy_addr [take] into copying instructions. + // + // Note: we must not delete the original copyInst because it would crash the + // instruction iteration in run(). Instead the copyInst gets identical Src and + // Dest operands. while (!tempObj->use_empty()) { Operand *use = *tempObj->use_begin(); SILInstruction *user = use->getUser(); switch (user->getKind()) { case SILInstructionKind::DestroyAddrInst: - if (copyInst->isTakeOfSrc()) { - use->set(copySrc); - } else { - user->dropAllReferences(); - toDelete.push_back(user); - } - break; case SILInstructionKind::DeallocStackInst: - user->dropAllReferences(); - toDelete.push_back(user); + user->eraseFromParent(); break; case SILInstructionKind::CopyAddrInst: { auto *cai = cast(user); if (cai != copyInst) { assert(cai->getSrc() == tempObj); - if (cai->isTakeOfSrc() && !copyInst->isTakeOfSrc()) + if (cai->isTakeOfSrc()) cai->setIsTakeOfSrc(IsNotTake); } use->set(copySrc); break; } case SILInstructionKind::LoadInst: { - // If we do not have a load [take] or we have a load [take] and our - // copy_addr takes the source, just do the normal thing of setting the - // load to use the copyInst's source. auto *li = cast(user); - if (li->getOwnershipQualifier() != LoadOwnershipQualifier::Take || - copyInst->isTakeOfSrc()) { - use->set(copyInst->getSrc()); - break; - } - - // Otherwise, since copy_addr is not taking src, we need to ensure that we - // insert a copy of our value. We do that by creating a load [copy] at the - // copy_addr inst and RAUWing the load [take] with that. We then insert - // destroy_value for the load [copy] at all points where we had destroys - // that are not the specific take that we were optimizing. - SILBuilderWithScope builder(copyInst); - SILValue newLoad = builder.emitLoadValueOperation( - copyInst->getLoc(), copyInst->getSrc(), LoadOwnershipQualifier::Copy); - for (auto *inst : tempAddressFrontier) { - assert(inst->getIterator() != inst->getParent()->begin() && - "Should have caught this when checking destructor"); - auto prevInst = std::prev(inst->getIterator()); - if (&*prevInst == li) - continue; - SILBuilderWithScope builder(prevInst); - builder.emitDestroyValueOperation(prevInst->getLoc(), newLoad); - } - li->replaceAllUsesWith(newLoad); - li->dropAllReferences(); - toDelete.push_back(li); + if (li->getOwnershipQualifier() == LoadOwnershipQualifier::Take) + li->setOwnershipQualifier(LoadOwnershipQualifier::Copy); + use->set(copyInst->getSrc()); break; } @@ -576,9 +538,6 @@ bool TempRValueOptPass::tryOptimizeCopyIntoTemp(CopyAddrInst *copyInst) { } } - while (!toDelete.empty()) { - toDelete.pop_back_val()->eraseFromParent(); - } tempObj->eraseFromParent(); return true; } @@ -612,29 +571,21 @@ TempRValueOptPass::tryOptimizeStoreIntoTemp(StoreInst *si) { if (user == si) continue; - // Destroys and deallocations are allowed to be in a different block. - if (isa(user) || isa(user)) - continue; - - // Same for load [take] on the top level temp object. SILGen always takes - // whole values from temporaries. If we have load [take] on projections from - // our base, we fail since those would be re-initializations. - if (auto *li = dyn_cast(user)) { - if (li->getOwnershipQualifier() == LoadOwnershipQualifier::Take) { - continue; - } - } - - // We pass in SILValue() since we do not have a source address. - if (!collectLoads(useOper, user, tempObj, SILValue(), loadInsts)) - return {std::next(si->getIterator()), false}; - // Bail if there is any kind of user which is not handled in the code below. switch (user->getKind()) { - case SILInstructionKind::CopyAddrInst: + case SILInstructionKind::DestroyAddrInst: + case SILInstructionKind::DeallocStackInst: + case SILInstructionKind::LoadInst: case SILInstructionKind::FixLifetimeInst: + break; + case SILInstructionKind::CopyAddrInst: + if (cast(user)->getDest() == tempObj) + return {std::next(si->getIterator()), false}; + break; case SILInstructionKind::MarkDependenceInst: - continue; + if (cast(user)->getValue() == tempObj) + return {std::next(si->getIterator()), false}; + break; default: return {std::next(si->getIterator()), false}; } @@ -704,11 +655,13 @@ TempRValueOptPass::tryOptimizeStoreIntoTemp(StoreInst *si) { break; } case SILInstructionKind::MarkDependenceInst: { - SILBuilderWithScope builder(user); auto mdi = cast(user); - auto newInst = builder.createMarkDependence(user->getLoc(), mdi->getValue(), si->getSrc()); + assert(mdi->getBase() == tempObj); + SILBuilderWithScope builder(user); + auto newInst = builder.createMarkDependence(user->getLoc(), + mdi->getValue(), si->getSrc()); mdi->replaceAllUsesWith(newInst); - toDelete.push_back(user); + toDelete.push_back(mdi); break; } // ASSUMPTION: no operations that may be handled by this default clause can diff --git a/lib/SILOptimizer/UtilityPasses/AccessPathVerification.cpp b/lib/SILOptimizer/UtilityPasses/AccessPathVerification.cpp new file mode 100644 index 0000000000000..27c9ad9e61851 --- /dev/null +++ b/lib/SILOptimizer/UtilityPasses/AccessPathVerification.cpp @@ -0,0 +1,125 @@ +//===--- AccessPathVerification.cpp - verify access paths and storage -----===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +/// +/// Verify AccessPath computation. For the address of every memory operation in +/// the module, compute the access path, compute all the users of that path, +/// then verify that all users have the same access path. +/// +/// This is potentially expensive, so there is a fast mode that limits the +/// number of uses visited per path. +/// +/// During full verification, also check that all addresses that share an +/// AccessPath are covered when computed the use list of that AccessPath. This +/// is important because optimizations may assume that the use list is +/// complete. +/// +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "access-path-verification" +#include "swift/SIL/MemAccessUtils.h" +#include "swift/SIL/PrettyStackTrace.h" +#include "swift/SIL/SILFunction.h" +#include "swift/SIL/SILInstruction.h" +#include "swift/SIL/SILValue.h" +#include "swift/SILOptimizer/PassManager/Passes.h" +#include "swift/SILOptimizer/PassManager/Transforms.h" +#include "llvm/Support/Debug.h" + +using namespace swift; + +namespace { + +/// Verify access path and uses of each access. +class AccessPathVerification : public SILModuleTransform { + llvm::DenseMap useToPathMap; + + // Transient uses + llvm::SmallVector uses; + +public: + void verifyAccessPath(Operand *operand) { + auto accessPath = AccessPath::compute(operand->get()); + if (!accessPath.isValid()) + return; + + auto iterAndInserted = useToPathMap.try_emplace(operand, accessPath); + // If this use was already computed from a previously visited path, make + // sure the path we just computed matches. + if (!iterAndInserted.second) { + auto collectedFromPath = iterAndInserted.first->second; + if (collectedFromPath != accessPath) { + llvm::errs() << "Address use: " << *operand->getUser() + << " collected from path\n "; + collectedFromPath.print(llvm::errs()); + llvm::errs() << " has different path\n "; + accessPath.print(llvm::errs()); + operand->getUser()->getFunction()->print(llvm::errs()); + assert(false && "computed path does not match collected path"); + } + return; + } + // This is a new path, so map all its uses. + assert(uses.empty()); + accessPath.collectUses(uses, AccessUseType::Exact, + operand->getParentFunction()); + bool foundOperandUse = false; + for (Operand *use : uses) { + if (use == operand) { + foundOperandUse = true; + continue; + } + auto iterAndInserted = useToPathMap.try_emplace(use, accessPath); + if (!iterAndInserted.second) { + llvm::errs() << "Address use: " << *operand->getUser() + << " with path...\n"; + accessPath.print(llvm::errs()); + llvm::errs() << " was not collected for: " << *use->getUser(); + llvm::errs() << " with path...\n"; + auto computedPath = iterAndInserted.first->second; + computedPath.print(llvm::errs()); + use->getUser()->getFunction()->print(llvm::errs()); + assert(false && "missing collected use"); + } + } + if (!foundOperandUse && !accessPath.hasUnknownOffset()) { + llvm::errs() << "Address use: " << *operand->getUser() + << " is not a use of path\n "; + accessPath.print(llvm::errs()); + assert(false && "not a user of its own computed path "); + } + uses.clear(); + } + + void run() override { + for (auto &fn : *getModule()) { + if (fn.empty()) + continue; + + PrettyStackTraceSILFunction functionDumper("...", &fn); + for (auto &bb : fn) { + for (auto &inst : bb) { + if (inst.mayReadOrWriteMemory()) + visitAccessedAddress(&inst, [this](Operand *operand) { + return verifyAccessPath(operand); + }); + } + } + useToPathMap.clear(); + } + } +}; + +} // end anonymous namespace + +SILTransform *swift::createAccessPathVerification() { + return new AccessPathVerification(); +} diff --git a/lib/SILOptimizer/UtilityPasses/AccessedStorageDumper.cpp b/lib/SILOptimizer/UtilityPasses/AccessedStorageDumper.cpp index ee5208a4bc6aa..f1bbb45d640b0 100644 --- a/lib/SILOptimizer/UtilityPasses/AccessedStorageDumper.cpp +++ b/lib/SILOptimizer/UtilityPasses/AccessedStorageDumper.cpp @@ -1,8 +1,8 @@ -//===--- AccessedStorageDumper.cpp - Dump accessed storage for functions ---===// +//===--- AccessedStorageDumper.cpp - Dump accessed storage ----------------===// // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -12,29 +12,74 @@ #define DEBUG_TYPE "sil-accessed-storage-dumper" #include "swift/SIL/MemAccessUtils.h" +#include "swift/SIL/PrettyStackTrace.h" #include "swift/SIL/SILFunction.h" #include "swift/SIL/SILInstruction.h" #include "swift/SIL/SILValue.h" #include "swift/SILOptimizer/PassManager/Passes.h" #include "swift/SILOptimizer/PassManager/Transforms.h" +#include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" using namespace swift; -static void dumpAccessedStorage(SILInstruction *inst) { - visitAccessedAddress( - inst, - [&](Operand *operand) { - inst->print(llvm::outs()); - findAccessedStorage(operand->get()).print(llvm::outs()); - } - ); -} +static llvm::cl::opt EnableDumpUses( + "enable-accessed-storage-dump-uses", llvm::cl::init(false), + llvm::cl::desc("With --sil-access-storage-dumper, dump all uses")); namespace { /// Dumps sorage information for each access. class AccessedStorageDumper : public SILModuleTransform { + llvm::SmallVector uses; + + void dumpAccessedStorage(Operand *operand) { + SILFunction *function = operand->getParentFunction(); + // Print storage itself first, for comparison against AccessPath. They can + // differ in rare cases of unidentified storage with phis. + AccessedStorage::compute(operand->get()).print(llvm::outs()); + // Now print the access path and base. + auto pathAndBase = AccessPathWithBase::compute(operand->get()); + pathAndBase.print(llvm::outs()); + // If enable-accessed-storage-dump-uses is set, dump all types of uses. + auto accessPath = pathAndBase.accessPath; + if (!accessPath.isValid() || !EnableDumpUses) + return; + + uses.clear(); + accessPath.collectUses(uses, AccessUseType::Exact, function); + llvm::outs() << "Exact Uses {\n"; + for (auto *useOperand : uses) { + llvm::outs() << *useOperand->getUser() << " "; + auto usePath = AccessPath::compute(useOperand->get()); + usePath.printPath(llvm::outs()); + assert(accessPath == usePath + && "access path does not match use access path"); + } + llvm::outs() << "}\n"; + uses.clear(); + accessPath.collectUses(uses, AccessUseType::Inner, function); + llvm::outs() << "Inner Uses {\n"; + for (auto *useOperand : uses) { + llvm::outs() << *useOperand->getUser() << " "; + auto usePath = AccessPath::compute(useOperand->get()); + usePath.printPath(llvm::outs()); + assert(accessPath.contains(usePath) + && "access path does not contain use access path"); + } + llvm::outs() << "}\n"; + uses.clear(); + accessPath.collectUses(uses, AccessUseType::Overlapping, function); + llvm::outs() << "Overlapping Uses {\n"; + for (auto *useOperand : uses) { + llvm::outs() << *useOperand->getUser() << " "; + auto usePath = AccessPath::compute(useOperand->get()); + usePath.printPath(llvm::outs()); + assert(accessPath.mayOverlap(usePath) + && "access path does not overlap with use access path"); + } + llvm::outs() << "}\n"; + } void run() override { for (auto &fn : *getModule()) { @@ -43,10 +88,15 @@ class AccessedStorageDumper : public SILModuleTransform { llvm::outs() << "\n"; continue; } + PrettyStackTraceSILFunction functionDumper("...", &fn); for (auto &bb : fn) { for (auto &inst : bb) { - if (inst.mayReadOrWriteMemory()) - dumpAccessedStorage(&inst); + if (inst.mayReadOrWriteMemory()) { + llvm::outs() << "###For MemOp: " << inst; + visitAccessedAddress(&inst, [this](Operand *operand) { + dumpAccessedStorage(operand); + }); + } } } } diff --git a/lib/SILOptimizer/UtilityPasses/CMakeLists.txt b/lib/SILOptimizer/UtilityPasses/CMakeLists.txt index 871ae9f29d3ec..0fbf885ed8e7b 100644 --- a/lib/SILOptimizer/UtilityPasses/CMakeLists.txt +++ b/lib/SILOptimizer/UtilityPasses/CMakeLists.txt @@ -1,6 +1,7 @@ target_sources(swiftSILOptimizer PRIVATE AADumper.cpp AccessSummaryDumper.cpp + AccessPathVerification.cpp AccessedStorageAnalysisDumper.cpp AccessedStorageDumper.cpp BasicCalleePrinter.cpp diff --git a/lib/SILOptimizer/UtilityPasses/FunctionOrderPrinter.cpp b/lib/SILOptimizer/UtilityPasses/FunctionOrderPrinter.cpp index da66869263cac..446f124b5ba90 100644 --- a/lib/SILOptimizer/UtilityPasses/FunctionOrderPrinter.cpp +++ b/lib/SILOptimizer/UtilityPasses/FunctionOrderPrinter.cpp @@ -35,8 +35,10 @@ class FunctionOrderPrinterPass : public SILModuleTransform { /// The entry point to the transformation. void run() override { BCA = getAnalysis(); + // SWIFT_ENABLE_TENSORFLOW BottomUpFunctionOrder Orderer(BCA); Orderer.computeBottomUpOrder(getModule()); + // SWIFT_ENABLE_TENSORFLOW END llvm::outs() << "Bottom up function order:\n"; auto SCCs = Orderer.getSCCs(); diff --git a/lib/SILOptimizer/UtilityPasses/MemBehaviorDumper.cpp b/lib/SILOptimizer/UtilityPasses/MemBehaviorDumper.cpp index a9fa20e226ecc..68e568865d37b 100644 --- a/lib/SILOptimizer/UtilityPasses/MemBehaviorDumper.cpp +++ b/lib/SILOptimizer/UtilityPasses/MemBehaviorDumper.cpp @@ -59,7 +59,10 @@ class MemBehaviorDumper : public SILModuleTransform { // selected types of instructions. static bool shouldTestInstruction(SILInstruction *I) { // Only consider function calls. - if ((EnableDumpAll && I->mayReadOrWriteMemory()) || FullApplySite::isa(I)) + if ((EnableDumpAll && I->mayReadOrWriteMemory()) || + FullApplySite::isa(I) || + isa(I) || + isa(I)) return true; return false; @@ -88,11 +91,9 @@ class MemBehaviorDumper : public SILModuleTransform { bool Read = AA->mayReadFromMemory(&I, V); bool Write = AA->mayWriteToMemory(&I, V); - bool SideEffects = AA->mayHaveSideEffects(&I, V); llvm::outs() << "PAIR #" << PairCount++ << ".\n" << " " << I << " " << V - << " r=" << Read << ",w=" << Write - << ",se=" << SideEffects << "\n"; + << " r=" << Read << ",w=" << Write << "\n"; } } } diff --git a/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp b/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp index 88b831e20f2ba..ce9ebdb5a597d 100644 --- a/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp +++ b/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp @@ -336,6 +336,9 @@ static bool hasOpaqueArchetype(TypeExpansionContext context, case SILInstructionKind::DifferentiabilityWitnessFunctionInst: case SILInstructionKind::BeginCOWMutationInst: case SILInstructionKind::EndCOWMutationInst: + case SILInstructionKind::GetAsyncContinuationInst: + case SILInstructionKind::GetAsyncContinuationAddrInst: + case SILInstructionKind::AwaitAsyncContinuationInst: // Handle by operand and result check. break; diff --git a/lib/SILOptimizer/Utils/BasicBlockOptUtils.cpp b/lib/SILOptimizer/Utils/BasicBlockOptUtils.cpp index 6b5a52240da00..8ef2dd4a1b451 100644 --- a/lib/SILOptimizer/Utils/BasicBlockOptUtils.cpp +++ b/lib/SILOptimizer/Utils/BasicBlockOptUtils.cpp @@ -44,6 +44,11 @@ bool ReachableBlocks::visit(SILFunction *f, /// Remove all instructions in the body of \p bb in safe manner by using /// undef. void swift::clearBlockBody(SILBasicBlock *bb) { + + for (SILArgument *arg : bb->getArguments()) { + arg->replaceAllUsesWithUndef(); + } + // Instructions in the dead block may be used by other dead blocks. Replace // any uses of them with undef values. while (!bb->empty()) { diff --git a/lib/SILOptimizer/Utils/CFGOptUtils.cpp b/lib/SILOptimizer/Utils/CFGOptUtils.cpp index 213ebe5f15cf9..565fe1d1455bb 100644 --- a/lib/SILOptimizer/Utils/CFGOptUtils.cpp +++ b/lib/SILOptimizer/Utils/CFGOptUtils.cpp @@ -449,6 +449,20 @@ void swift::replaceBranchTarget(TermInst *t, SILBasicBlock *oldDest, yi->eraseFromParent(); return; } + + case TermKind::AwaitAsyncContinuationInst: { + auto ai = cast(t); + SILBasicBlock *resumeBB = + (oldDest == ai->getResumeBB() ? newDest : ai->getResumeBB()); + SILBasicBlock *errorBB = + (oldDest == ai->getErrorBB() ? newDest : ai->getErrorBB()); + + builder.createAwaitAsyncContinuation(ai->getLoc(), + ai->getOperand(), + resumeBB, errorBB); + ai->eraseFromParent(); + return; + } case TermKind::ReturnInst: case TermKind::ThrowInst: @@ -726,6 +740,7 @@ static bool isSafeNonExitTerminator(TermInst *ti) { // yield is special because it can do arbitrary, // potentially-process-terminating things. case TermKind::YieldInst: + case TermKind::AwaitAsyncContinuationInst: return false; case TermKind::TryApplyInst: return true; diff --git a/lib/SILOptimizer/Utils/ConstExpr.cpp b/lib/SILOptimizer/Utils/ConstExpr.cpp index 28d760e070a91..ad862054d5df8 100644 --- a/lib/SILOptimizer/Utils/ConstExpr.cpp +++ b/lib/SILOptimizer/Utils/ConstExpr.cpp @@ -300,7 +300,7 @@ SymbolicValue ConstExprFunctionState::computeConstantValue(SILValue value) { auto val = getConstantValue(tei->getOperand()); if (!val.isConstant()) return val; - return val.getAggregateMembers()[tei->getFieldNo()]; + return val.getAggregateMembers()[tei->getFieldIndex()]; } // If this is a struct extract from a fragile type, then we can return the @@ -312,7 +312,7 @@ SymbolicValue ConstExprFunctionState::computeConstantValue(SILValue value) { return val; } assert(val.getKind() == SymbolicValue::Aggregate); - return val.getAggregateMembers()[sei->getFieldNo()]; + return val.getAggregateMembers()[sei->getFieldIndex()]; } // If this is an unchecked_enum_data from a fragile type, then we can return @@ -379,9 +379,9 @@ SymbolicValue ConstExprFunctionState::computeConstantValue(SILValue value) { // Add our index onto the next of the list. unsigned index; if (auto sea = dyn_cast(inst)) - index = sea->getFieldNo(); + index = sea->getFieldIndex(); else - index = cast(inst)->getFieldNo(); + index = cast(inst)->getFieldIndex(); accessPath.push_back(index); return SymbolicValue::getAddress(memObject, accessPath, evaluator.getAllocator()); diff --git a/lib/SILOptimizer/Utils/ConstantFolding.cpp b/lib/SILOptimizer/Utils/ConstantFolding.cpp index 84eaaf2d0cfc4..b7d4a051ef9ad 100644 --- a/lib/SILOptimizer/Utils/ConstantFolding.cpp +++ b/lib/SILOptimizer/Utils/ConstantFolding.cpp @@ -1395,7 +1395,7 @@ static bool constantFoldInstruction(Operand *Op, Optional &ResultsInError, // Constant fold extraction of a constant element. if (auto *TEI = dyn_cast(User)) { if (auto *TheTuple = dyn_cast(TEI->getOperand())) { - Results.push_back(TheTuple->getElement(TEI->getFieldNo())); + Results.push_back(TheTuple->getElement(TEI->getFieldIndex())); return true; } } diff --git a/lib/SILOptimizer/Utils/GenericCloner.cpp b/lib/SILOptimizer/Utils/GenericCloner.cpp index e027cdfb3927f..57dc9cc1f1743 100644 --- a/lib/SILOptimizer/Utils/GenericCloner.cpp +++ b/lib/SILOptimizer/Utils/GenericCloner.cpp @@ -26,10 +26,9 @@ using namespace swift; /// Create a new empty function with the correct arguments and a unique name. -SILFunction *GenericCloner::initCloned(SILOptFunctionBuilder &FunctionBuilder, - SILFunction *Orig, - const ReabstractionInfo &ReInfo, - StringRef NewName) { +SILFunction *GenericCloner::createDeclaration( + SILOptFunctionBuilder &FunctionBuilder, SILFunction *Orig, + const ReabstractionInfo &ReInfo, StringRef NewName) { assert((!ReInfo.isSerialized() || Orig->isSerialized()) && "Specialization cannot make body more resilient"); assert((Orig->isTransparent() || Orig->isBare() || Orig->getLocation()) diff --git a/lib/SILOptimizer/Utils/Generics.cpp b/lib/SILOptimizer/Utils/Generics.cpp index 7740d0803b344..3301e70fb806c 100644 --- a/lib/SILOptimizer/Utils/Generics.cpp +++ b/lib/SILOptimizer/Utils/Generics.cpp @@ -1783,9 +1783,12 @@ void ReabstractionInfo::finishPartialSpecializationPreparation( /// This constructor is used when processing @_specialize. ReabstractionInfo::ReabstractionInfo(ModuleDecl *targetModule, bool isWholeModule, SILFunction *Callee, - GenericSignature SpecializedSig) - : TargetModule(targetModule), isWholeModule(isWholeModule) { - Serialized = Callee->isSerialized(); + GenericSignature SpecializedSig, + bool isPrespecialization) + : TargetModule(targetModule), isWholeModule(isWholeModule), + isPrespecialization(isPrespecialization) { + Serialized = + this->isPrespecialization ? IsNotSerialized : Callee->isSerialized(); if (shouldNotSpecialize(Callee, nullptr)) return; @@ -1819,7 +1822,8 @@ GenericFuncSpecializer::GenericFuncSpecializer( ParamSubs(ParamSubs), ReInfo(ReInfo) { - assert(GenericFunc->isDefinition() && "Expected definition to specialize!"); + assert((GenericFunc->isDefinition() || ReInfo.isPrespecialized()) && + "Expected definition or pre-specialized entry-point to specialize!"); auto FnTy = ReInfo.getSpecializedType(); if (ReInfo.isPartialSpecialization()) { @@ -1828,7 +1832,8 @@ GenericFuncSpecializer::GenericFuncSpecializer( ClonedName = Mangler.mangle(); } else { Mangle::GenericSpecializationMangler Mangler( - GenericFunc, ParamSubs, ReInfo.isSerialized(), /*isReAbstracted*/ true); + GenericFunc, ParamSubs, ReInfo.isSerialized(), /*isReAbstracted*/ true, + /*isInlined*/ false, ReInfo.isPrespecialized()); ClonedName = Mangler.mangle(); } LLVM_DEBUG(llvm::dbgs() << " Specialized function " << ClonedName << '\n'); @@ -1863,9 +1868,10 @@ void ReabstractionInfo::verify() const { } /// Create a new specialized function if possible, and cache it. -SILFunction *GenericFuncSpecializer::tryCreateSpecialization() { +SILFunction * +GenericFuncSpecializer::tryCreateSpecialization(bool forcePrespecialization) { // Do not create any new specializations at Onone. - if (!GenericFunc->shouldOptimize()) + if (!GenericFunc->shouldOptimize() && !forcePrespecialization) return nullptr; LLVM_DEBUG(llvm::dbgs() << "Creating a specialization: " @@ -2035,8 +2041,7 @@ static ApplySite replaceWithSpecializedCallee(ApplySite applySite, assert(resultBlock->getSinglePredecessorBlock() == tai->getParent()); // First insert the cleanups for our arguments int he appropriate spot. FullApplySite(tai).insertAfterFullEvaluation( - [&](SILBasicBlock::iterator insertPt) { - SILBuilderWithScope argBuilder(insertPt); + [&](SILBuilder &argBuilder) { cleanupCallArguments(argBuilder, loc, arguments, argsNeedingEndBorrow); }); @@ -2060,8 +2065,7 @@ static ApplySite replaceWithSpecializedCallee(ApplySite applySite, case ApplySiteKind::ApplyInst: { auto *ai = cast(applySite); FullApplySite(ai).insertAfterFullEvaluation( - [&](SILBasicBlock::iterator insertPt) { - SILBuilderWithScope argBuilder(insertPt); + [&](SILBuilder &argBuilder) { cleanupCallArguments(argBuilder, loc, arguments, argsNeedingEndBorrow); }); @@ -2090,8 +2094,7 @@ static ApplySite replaceWithSpecializedCallee(ApplySite applySite, auto *bai = cast(applySite); assert(!resultOut); FullApplySite(bai).insertAfterFullEvaluation( - [&](SILBasicBlock::iterator insertPt) { - SILBuilderWithScope argBuilder(insertPt); + [&](SILBuilder &argBuilder) { cleanupCallArguments(argBuilder, loc, arguments, argsNeedingEndBorrow); }); @@ -2235,8 +2238,7 @@ SILFunction *ReabstractionThunkGenerator::createThunk() { // Now that we have finished constructing our CFG (note the return above), // insert any compensating end borrows that we need. - ApplySite.insertAfterFullEvaluation([&](SILBasicBlock::iterator insertPt) { - SILBuilderWithScope argBuilder(insertPt); + ApplySite.insertAfterFullEvaluation([&](SILBuilder &argBuilder) { cleanupCallArguments(argBuilder, Loc, Arguments, ArgsThatNeedEndBorrow); }); @@ -2430,6 +2432,62 @@ static bool createPrespecializations(ApplySite Apply, SILFunction *ProxyFunc, return prespecializeFound; } +static SILFunction * +lookupOrCreatePrespecialization(SILOptFunctionBuilder &funcBuilder, + SILFunction *origF, std::string clonedName, + ReabstractionInfo &reInfo) { + + if (auto *specializedF = funcBuilder.getModule().lookUpFunction(clonedName)) { + assert(reInfo.getSpecializedType() == + specializedF->getLoweredFunctionType() && + "Previously specialized function does not match expected type."); + return specializedF; + } + + auto *declaration = + GenericCloner::createDeclaration(funcBuilder, origF, reInfo, clonedName); + declaration->setLinkage(SILLinkage::PublicExternal); + + return declaration; +} + +static SILFunction * +usePrespecialized(SILOptFunctionBuilder &funcBuilder, ApplySite apply, + SILFunction *refF, const ReabstractionInfo &specializedReInfo, + ReabstractionInfo &prespecializedReInfo) { + for (auto *SA : refF->getSpecializeAttrs()) { + if (!SA->isExported()) + continue; + // Check whether SPI allows using this function. + auto spiGroup = SA->getSPIGroup(); + if (!spiGroup.empty()) { + auto currentModule = funcBuilder.getModule().getSwiftModule(); + auto funcModule = SA->getSPIModule(); + // Don't use this SPI if the current module does not import the function's + // module with @_spi(). + if (currentModule != funcModule && + !currentModule->isImportedAsSPI(spiGroup, funcModule)) + continue; + } + ReabstractionInfo reInfo(funcBuilder.getModule().getSwiftModule(), + funcBuilder.getModule().isWholeModule(), refF, + SA->getSpecializedSignature(), + /*isPrespecialization*/ true); + if (specializedReInfo.getSpecializedType() != reInfo.getSpecializedType()) + continue; + + Mangle::GenericSpecializationMangler mangler( + refF, reInfo.getCalleeParamSubstitutionMap(), reInfo.isSerialized(), + /*isReAbstracted*/ true, /*isInlined*/ false, + reInfo.isPrespecialized()); + + prespecializedReInfo = reInfo; + return lookupOrCreatePrespecialization(funcBuilder, refF, mangler.mangle(), + reInfo); + } + return nullptr; +} + void swift::trySpecializeApplyOfGeneric( SILOptFunctionBuilder &FuncBuilder, ApplySite Apply, DeadInstructionSet &DeadApplies, @@ -2480,6 +2538,18 @@ void swift::trySpecializeApplyOfGeneric( if (!ReInfo.canBeSpecialized()) return; + // Check if there is a pre-specialization available in a library. + SILFunction *prespecializedF = nullptr; + ReabstractionInfo prespecializedReInfo; + if ((prespecializedF = usePrespecialized(FuncBuilder, Apply, RefF, ReInfo, + prespecializedReInfo))) { + ReInfo = prespecializedReInfo; + } + + // If there is not pre-specialization and we don't have a body give up. + if (!prespecializedF && !RefF->isDefinition()) + return; + SILModule &M = F->getModule(); bool needAdaptUsers = false; @@ -2535,7 +2605,9 @@ void swift::trySpecializeApplyOfGeneric( GenericFuncSpecializer FuncSpecializer(FuncBuilder, RefF, Apply.getSubstitutionMap(), ReInfo); - SILFunction *SpecializedF = FuncSpecializer.lookupSpecialization(); + SILFunction *SpecializedF = prespecializedF + ? prespecializedF + : FuncSpecializer.lookupSpecialization(); if (!SpecializedF) { SpecializedF = FuncSpecializer.tryCreateSpecialization(); if (!SpecializedF) @@ -2724,4 +2796,3 @@ SILFunction *swift::lookupPrespecializedSymbol(SILModule &M, return Specialization; } - diff --git a/lib/SILOptimizer/Utils/InstOptUtils.cpp b/lib/SILOptimizer/Utils/InstOptUtils.cpp index 19ce86358904c..df8876f539333 100644 --- a/lib/SILOptimizer/Utils/InstOptUtils.cpp +++ b/lib/SILOptimizer/Utils/InstOptUtils.cpp @@ -793,12 +793,12 @@ FullApplySite swift::findApplyFromDevirtualizedResult(SILValue v) { } bool swift::mayBindDynamicSelf(SILFunction *F) { - if (!F->hasSelfMetadataParam()) + if (!F->hasDynamicSelfMetadata()) return false; - SILValue mdArg = F->getSelfMetadataArgument(); + SILValue mdArg = F->getDynamicSelfMetadata(); - for (Operand *mdUse : F->getSelfMetadataArgument()->getUses()) { + for (Operand *mdUse : mdArg->getUses()) { SILInstruction *mdUser = mdUse->getUser(); for (Operand &typeDepOp : mdUser->getTypeDependentOperands()) { if (typeDepOp.get() == mdArg) @@ -1187,28 +1187,16 @@ static bool shouldDestroyPartialApplyCapturedArg(SILValue arg, return true; } -// *HEY YOU, YES YOU, PLEASE READ*. Even though a textual partial apply is -// printed with the convention of the closed over function upon it, all -// non-inout arguments to a partial_apply are passed at +1. This includes -// arguments that will eventually be passed as guaranteed or in_guaranteed to -// the closed over function. This is because the partial apply is building up a -// boxed aggregate to send off to the closed over function. Of course when you -// call the function, the proper conventions will be used. -void swift::releasePartialApplyCapturedArg(SILBuilder &builder, SILLocation loc, - SILValue arg, - SILParameterInfo paramInfo, - InstModCallbacks callbacks) { - if (!shouldDestroyPartialApplyCapturedArg(arg, paramInfo, - builder.getFunction())) - return; - - // Otherwise, we need to destroy the argument. If we have an address, we - // insert a destroy_addr and return. Any live range issues must have been - // dealt with by our caller. - if (arg->getType().isAddress()) { - // Then emit the destroy_addr for this arg - SILInstruction *newInst = builder.emitDestroyAddrAndFold(loc, arg); - callbacks.createdNewInst(newInst); +void swift::emitDestroyOperation(SILBuilder &builder, SILLocation loc, + SILValue operand, InstModCallbacks callbacks) { + // If we have an address, we insert a destroy_addr and return. Any live range + // issues must have been dealt with by our caller. + if (operand->getType().isAddress()) { + // Then emit the destroy_addr for this operand. This function does not + // delete any instructions + SILInstruction *newInst = builder.emitDestroyAddrAndFold(loc, operand); + if (newInst != nullptr) + callbacks.createdNewInst(newInst); return; } @@ -1217,12 +1205,12 @@ void swift::releasePartialApplyCapturedArg(SILBuilder &builder, SILLocation loc, // If we have qualified ownership, we should just emit a destroy value. if (builder.getFunction().hasOwnership()) { - callbacks.createdNewInst(builder.createDestroyValue(loc, arg)); + callbacks.createdNewInst(builder.createDestroyValue(loc, operand)); return; } - if (arg->getType().hasReferenceSemantics()) { - auto u = builder.emitStrongRelease(loc, arg); + if (operand->getType().hasReferenceSemantics()) { + auto u = builder.emitStrongRelease(loc, operand); if (u.isNull()) return; @@ -1235,7 +1223,7 @@ void swift::releasePartialApplyCapturedArg(SILBuilder &builder, SILLocation loc, return; } - auto u = builder.emitReleaseValue(loc, arg); + auto u = builder.emitReleaseValue(loc, operand); if (u.isNull()) return; @@ -1247,6 +1235,24 @@ void swift::releasePartialApplyCapturedArg(SILBuilder &builder, SILLocation loc, callbacks.createdNewInst(u.get()); } +// *HEY YOU, YES YOU, PLEASE READ*. Even though a textual partial apply is +// printed with the convention of the closed over function upon it, all +// non-inout arguments to a partial_apply are passed at +1. This includes +// arguments that will eventually be passed as guaranteed or in_guaranteed to +// the closed over function. This is because the partial apply is building up a +// boxed aggregate to send off to the closed over function. Of course when you +// call the function, the proper conventions will be used. +void swift::releasePartialApplyCapturedArg(SILBuilder &builder, SILLocation loc, + SILValue arg, + SILParameterInfo paramInfo, + InstModCallbacks callbacks) { + if (!shouldDestroyPartialApplyCapturedArg(arg, paramInfo, + builder.getFunction())) + return; + + emitDestroyOperation(builder, loc, arg, callbacks); +} + void swift::deallocPartialApplyCapturedArg(SILBuilder &builder, SILLocation loc, SILValue arg, SILParameterInfo paramInfo) { @@ -1317,7 +1323,8 @@ bool swift::collectDestroys(SingleValueInstruction *inst, /// not be needed anymore with OSSA. static bool keepArgsOfPartialApplyAlive(PartialApplyInst *pai, ArrayRef paiUsers, - SILBuilderContext &builderCtxt) { + SILBuilderContext &builderCtxt, + InstModCallbacks callbacks) { SmallVector argsToHandle; getConsumedPartialApplyArgs(pai, argsToHandle, /*includeTrivialAddrArgs*/ false); @@ -1351,7 +1358,7 @@ static bool keepArgsOfPartialApplyAlive(PartialApplyInst *pai, // Delay the destroy of the value (either as SSA value or in the stack- // allocated temporary) at the end of the partial_apply's lifetime. - endLifetimeAtFrontier(tmp, partialApplyFrontier, builderCtxt); + endLifetimeAtFrontier(tmp, partialApplyFrontier, builderCtxt, callbacks); } return true; } @@ -1400,7 +1407,8 @@ bool swift::tryDeleteDeadClosure(SingleValueInstruction *closure, "partial_apply [stack] should have been handled before"); SILBuilderContext builderCtxt(pai->getModule()); if (needKeepArgsAlive) { - if (!keepArgsOfPartialApplyAlive(pai, closureDestroys, builderCtxt)) + if (!keepArgsOfPartialApplyAlive(pai, closureDestroys, builderCtxt, + callbacks)) return false; } else { // A preceeding partial_apply -> apply conversion (done in @@ -1415,11 +1423,7 @@ bool swift::tryDeleteDeadClosure(SingleValueInstruction *closure, for (Operand *argOp : argsToHandle) { SILValue arg = argOp->get(); SILBuilderWithScope builder(pai, builderCtxt); - if (arg->getType().isObject()) { - builder.emitDestroyValueOperation(pai->getLoc(), arg); - } else { - builder.emitDestroyAddr(pai->getLoc(), arg); - } + emitDestroyOperation(builder, pai->getLoc(), arg, callbacks); } } } @@ -1644,7 +1648,7 @@ void swift::replaceLoadSequence(SILInstruction *inst, SILValue value) { if (auto *teai = dyn_cast(inst)) { SILBuilder builder(teai); auto *tei = - builder.createTupleExtract(teai->getLoc(), value, teai->getFieldNo()); + builder.createTupleExtract(teai->getLoc(), value, teai->getFieldIndex()); for (auto teaiUse : teai->getUses()) { replaceLoadSequence(teaiUse->getUser(), tei); } diff --git a/lib/SILOptimizer/Utils/PartialApplyCombiner.cpp b/lib/SILOptimizer/Utils/PartialApplyCombiner.cpp index d9a36eb051e16..d3161d892b6d1 100644 --- a/lib/SILOptimizer/Utils/PartialApplyCombiner.cpp +++ b/lib/SILOptimizer/Utils/PartialApplyCombiner.cpp @@ -115,7 +115,7 @@ bool PartialApplyCombiner::copyArgsToTemporaries( // Destroy the argument value (either as SSA value or in the stack- // allocated temporary) at the end of the partial_apply's lifetime. - endLifetimeAtFrontier(tmp, partialApplyFrontier, builderCtxt); + endLifetimeAtFrontier(tmp, partialApplyFrontier, builderCtxt, callbacks); } return true; } @@ -153,8 +153,7 @@ void PartialApplyCombiner::processSingleApply(FullApplySite paiAI) { auto *ASI = builder.createAllocStack(pai->getLoc(), arg->getType()); builder.createCopyAddr(pai->getLoc(), arg, ASI, IsTake_t::IsNotTake, IsInitialization_t::IsInitialization); - paiAI.insertAfterFullEvaluation([&](SILBasicBlock::iterator insertPt) { - SILBuilderWithScope builder(insertPt); + paiAI.insertAfterFullEvaluation([&](SILBuilder &builder) { builder.createDeallocStack(destroyloc, ASI); }); arg = ASI; @@ -184,8 +183,7 @@ void PartialApplyCombiner::processSingleApply(FullApplySite paiAI) { // We also need to destroy the partial_apply instruction itself because it is // consumed by the apply_instruction. if (!pai->hasCalleeGuaranteedContext()) { - paiAI.insertAfterFullEvaluation([&](SILBasicBlock::iterator insertPt) { - SILBuilderWithScope builder(insertPt); + paiAI.insertAfterFullEvaluation([&](SILBuilder &builder) { builder.emitDestroyValueOperation(destroyloc, pai); }); } diff --git a/lib/SILOptimizer/Utils/PerformanceInlinerUtils.cpp b/lib/SILOptimizer/Utils/PerformanceInlinerUtils.cpp index f8bcce57b36d7..330fbc66f95cd 100644 --- a/lib/SILOptimizer/Utils/PerformanceInlinerUtils.cpp +++ b/lib/SILOptimizer/Utils/PerformanceInlinerUtils.cpp @@ -726,11 +726,11 @@ SILFunction *swift::getEligibleFunction(FullApplySite AI, // Check if passed Self is the same as the Self of the caller. // In this case, it is safe to inline because both functions // use the same Self. - if (!AI.hasSelfArgument() || !Caller->hasSelfMetadataParam()) { + if (!AI.hasSelfArgument() || !Caller->hasDynamicSelfMetadata()) { return nullptr; } auto CalleeSelf = stripCasts(AI.getSelfArgument()); - auto CallerSelf = Caller->getSelfMetadataArgument(); + auto CallerSelf = Caller->getDynamicSelfMetadata(); if (CalleeSelf != SILValue(CallerSelf)) { return nullptr; } diff --git a/lib/SILOptimizer/Utils/SILInliner.cpp b/lib/SILOptimizer/Utils/SILInliner.cpp index b08f9832e0237..d5fe59ce1ad15 100644 --- a/lib/SILOptimizer/Utils/SILInliner.cpp +++ b/lib/SILOptimizer/Utils/SILInliner.cpp @@ -688,6 +688,7 @@ InlineCost swift::instructionInlineCost(SILInstruction &I) { case SILInstructionKind::BaseAddrForOffsetInst: case SILInstructionKind::EndLifetimeInst: case SILInstructionKind::UncheckedOwnershipConversionInst: + case SILInstructionKind::BindMemoryInst: return InlineCost::Free; // Typed GEPs are free. @@ -783,6 +784,17 @@ InlineCost swift::instructionInlineCost(SILInstruction &I) { case SILInstructionKind::EndCOWMutationInst: return InlineCost::Free; + // Turning the task reference into a continuation should be basically free. + // TODO(async): make sure this is true. + case SILInstructionKind::GetAsyncContinuationAddrInst: + case SILInstructionKind::GetAsyncContinuationInst: + return InlineCost::Free; + + // Unconditional branch is free in empty blocks. + case SILInstructionKind::BranchInst: + return (I.getIterator() == I.getParent()->begin()) + ? InlineCost::Free : InlineCost::Expensive; + case SILInstructionKind::AbortApplyInst: case SILInstructionKind::ApplyInst: case SILInstructionKind::TryApplyInst: @@ -792,13 +804,11 @@ InlineCost swift::instructionInlineCost(SILInstruction &I) { case SILInstructionKind::AllocRefDynamicInst: case SILInstructionKind::AllocStackInst: case SILInstructionKind::AllocValueBufferInst: - case SILInstructionKind::BindMemoryInst: case SILInstructionKind::BeginApplyInst: case SILInstructionKind::ValueMetatypeInst: case SILInstructionKind::WitnessMethodInst: case SILInstructionKind::AssignInst: case SILInstructionKind::AssignByWrapperInst: - case SILInstructionKind::BranchInst: case SILInstructionKind::CheckedCastBranchInst: case SILInstructionKind::CheckedCastValueBranchInst: case SILInstructionKind::CheckedCastAddrBranchInst: @@ -884,6 +894,7 @@ InlineCost swift::instructionInlineCost(SILInstruction &I) { case SILInstructionKind::DifferentiableFunctionExtractInst: case SILInstructionKind::LinearFunctionExtractInst: case SILInstructionKind::DifferentiabilityWitnessFunctionInst: + case SILInstructionKind::AwaitAsyncContinuationInst: #define COMMON_ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name) \ case SILInstructionKind::Name##ToRefInst: \ case SILInstructionKind::RefTo##Name##Inst: \ diff --git a/lib/SILOptimizer/Utils/SpecializationMangler.cpp b/lib/SILOptimizer/Utils/SpecializationMangler.cpp index 3a37e3f3430ba..a5076f0309d25 100644 --- a/lib/SILOptimizer/Utils/SpecializationMangler.cpp +++ b/lib/SILOptimizer/Utils/SpecializationMangler.cpp @@ -11,93 +11,15 @@ //===----------------------------------------------------------------------===// #include "swift/SILOptimizer/Utils/SpecializationMangler.h" -#include "swift/SIL/SILGlobalVariable.h" +#include "swift/AST/GenericEnvironment.h" #include "swift/AST/GenericSignature.h" #include "swift/AST/SubstitutionMap.h" #include "swift/Demangling/ManglingMacros.h" +#include "swift/SIL/SILGlobalVariable.h" using namespace swift; using namespace Mangle; -void SpecializationMangler::beginMangling() { - ASTMangler::beginManglingWithoutPrefix(); - if (Serialized) - ArgOpBuffer << 'q'; - ArgOpBuffer << char(uint8_t(Pass) + '0'); -} - -namespace { - -/// Utility class for demangling specialization attributes. -class AttributeDemangler : public Demangle::Demangler { -public: - void demangleAndAddAsChildren(StringRef MangledSpecialization, - NodePointer Parent) { - DemangleInitRAII state(*this, MangledSpecialization, nullptr); - if (!parseAndPushNodes()) { - llvm::errs() << "Can't demangle: " << MangledSpecialization << '\n'; - abort(); - } - for (Node *Nd : NodeStack) { - addChild(Parent, Nd); - } - } -}; - -} // namespace - -std::string SpecializationMangler::finalize() { - StringRef MangledSpecialization(Storage.data(), Storage.size()); - AttributeDemangler D; - NodePointer TopLevel = D.createNode(Node::Kind::Global); - D.demangleAndAddAsChildren(MangledSpecialization, TopLevel); - - StringRef FuncName = Function->getName(); - NodePointer FuncTopLevel = nullptr; - if (FuncName.startswith(MANGLING_PREFIX_STR)) { - FuncTopLevel = D.demangleSymbol(FuncName); - assert(FuncTopLevel); - } - if (!FuncTopLevel) { - FuncTopLevel = D.createNode(Node::Kind::Global); - FuncTopLevel->addChild(D.createNode(Node::Kind::Identifier, FuncName), D); - } - for (NodePointer FuncChild : *FuncTopLevel) { - TopLevel->addChild(FuncChild, D); - } - std::string mangledName = Demangle::mangleNode(TopLevel); - verify(mangledName); - return mangledName; -} - -//===----------------------------------------------------------------------===// -// Generic Specialization -//===----------------------------------------------------------------------===// - -std::string GenericSpecializationMangler::mangle(GenericSignature Sig) { - beginMangling(); - - if (!Sig) { - SILFunctionType *FTy = Function->getLoweredFunctionType(); - Sig = FTy->getInvocationGenericSignature(); - } - - bool First = true; - Sig->forEachParam([&](GenericTypeParamType *ParamType, bool Canonical) { - if (Canonical) { - appendType(Type(ParamType).subst(SubMap)->getCanonicalType()); - appendListSeparator(First); - } - }); - assert(!First && "no generic substitutions"); - - if (isInlined) - appendSpecializationOperator("Ti"); - else - appendSpecializationOperator(isReAbstracted ? "Tg" : "TG"); - return finalize(); -} - //===----------------------------------------------------------------------===// // Partial Generic Specialization //===----------------------------------------------------------------------===// diff --git a/lib/SILOptimizer/Utils/ValueLifetime.cpp b/lib/SILOptimizer/Utils/ValueLifetime.cpp index 4f90e9cc0bd80..55c0e05625a87 100644 --- a/lib/SILOptimizer/Utils/ValueLifetime.cpp +++ b/lib/SILOptimizer/Utils/ValueLifetime.cpp @@ -327,15 +327,12 @@ void ValueLifetimeAnalysis::dump() const { void swift::endLifetimeAtFrontier( SILValue valueOrStackLoc, const ValueLifetimeAnalysis::Frontier &frontier, - SILBuilderContext &builderCtxt) { + SILBuilderContext &builderCtxt, InstModCallbacks callbacks) { for (SILInstruction *endPoint : frontier) { SILBuilderWithScope builder(endPoint, builderCtxt); SILLocation loc = RegularLocation(endPoint->getLoc().getSourceLoc()); - if (valueOrStackLoc->getType().isObject()) { - builder.emitDestroyValueOperation(loc, valueOrStackLoc); - } else { - assert(isa(valueOrStackLoc)); - builder.createDestroyAddr(loc, valueOrStackLoc); + emitDestroyOperation(builder, loc, valueOrStackLoc, callbacks); + if (isa(valueOrStackLoc)) { builder.createDeallocStack(loc, valueOrStackLoc); } } diff --git a/lib/Sema/BuilderTransform.cpp b/lib/Sema/BuilderTransform.cpp index fdcf4ee263a92..c52b3ae5acf8e 100644 --- a/lib/Sema/BuilderTransform.cpp +++ b/lib/Sema/BuilderTransform.cpp @@ -1,4 +1,4 @@ -//===--- BuilderTransform.cpp - Function-builder transformation -----------===// +//===--- BuilderTransform.cpp - Result-builder transformation -----------===// // // This source file is part of the Swift.org open source project // @@ -10,14 +10,12 @@ // //===----------------------------------------------------------------------===// // -// This file implements routines associated with the function-builder +// This file implements routines associated with the result-builder // transformation. // //===----------------------------------------------------------------------===// -#include "ConstraintSystem.h" #include "MiscDiagnostics.h" -#include "SolutionResult.h" #include "TypeChecker.h" #include "TypeCheckAvailability.h" #include "swift/Sema/IDETypeChecking.h" @@ -28,6 +26,8 @@ #include "swift/AST/NameLookupRequests.h" #include "swift/AST/ParameterList.h" #include "swift/AST/TypeCheckRequests.h" +#include "swift/Sema/ConstraintSystem.h" +#include "swift/Sema/SolutionResult.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallVector.h" #include @@ -73,7 +73,7 @@ class BuilderClosureVisitor Identifier buildOptionalId; llvm::SmallDenseMap supportedOps; - SkipUnhandledConstructInFunctionBuilder::UnhandledNode unhandledNode; + SkipUnhandledConstructInResultBuilder::UnhandledNode unhandledNode; /// Whether an error occurred during application of the builder closure, /// e.g., during constraint generation. @@ -251,9 +251,9 @@ class BuilderClosureVisitor return std::move(applied); } - /// Check whether the function builder can be applied to this statement. + /// Check whether the result builder can be applied to this statement. /// \returns the node that cannot be handled by this builder on failure. - SkipUnhandledConstructInFunctionBuilder::UnhandledNode check(Stmt *stmt) { + SkipUnhandledConstructInResultBuilder::UnhandledNode check(Stmt *stmt) { (void)visit(stmt); return unhandledNode; } @@ -770,7 +770,7 @@ class BuilderClosureVisitor // For-each statements require the Sequence protocol. If we don't have // it (which generally means the standard library isn't loaded), fall - // out of the function-builder path entirely to let normal type checking + // out of the result-builder path entirely to let normal type checking // take care of this. auto sequenceProto = TypeChecker::getProtocol( dc->getASTContext(), forEachStmt->getForLoc(), @@ -810,10 +810,10 @@ class BuilderClosureVisitor VarDecl *arrayVar = buildVar(loc); Type arrayElementType = cs->createTypeVariable( cs->getConstraintLocator(forEachStmt), 0); - cs->addConstraint( - ConstraintKind::Equal, cs->getType(bodyVar), arrayElementType, - cs->getConstraintLocator( - forEachStmt, ConstraintLocator::RValueAdjustment)); + cs->addConstraint(ConstraintKind::Equal, cs->getType(bodyVar), + arrayElementType, + cs->getConstraintLocator( + forEachStmt, ConstraintLocator::SequenceElementType)); Type arrayType = ArraySliceType::get(arrayElementType); cs->setType(arrayVar, arrayType); @@ -863,7 +863,7 @@ class BuilderClosureVisitor } // Form a final variable for the for-each expression itself, which will - // be initialized with the call to the function builder's buildArray(_:). + // be initialized with the call to the result builder's buildArray(_:). auto finalForEachVar = buildVar(loc); cs->setType(finalForEachVar, cs->getType(buildArrayCall)); applied.capturedStmts.insert( @@ -909,8 +909,8 @@ class BuilderClosureVisitor }; /// Describes the target into which the result of a particular statement in -/// a closure involving a function builder should be written. -struct FunctionBuilderTarget { +/// a closure involving a result builder should be written. +struct ResultBuilderTarget { enum Kind { /// The resulting value is returned from the closure. ReturnValue, @@ -924,24 +924,24 @@ struct FunctionBuilderTarget { /// Captured variable information. std::pair> captured; - static FunctionBuilderTarget forReturn(Expr *expr) { - return FunctionBuilderTarget{ReturnValue, {nullptr, {expr}}}; + static ResultBuilderTarget forReturn(Expr *expr) { + return ResultBuilderTarget{ReturnValue, {nullptr, {expr}}}; } - static FunctionBuilderTarget forAssign(VarDecl *temporaryVar, + static ResultBuilderTarget forAssign(VarDecl *temporaryVar, llvm::TinyPtrVector exprs) { - return FunctionBuilderTarget{TemporaryVar, {temporaryVar, exprs}}; + return ResultBuilderTarget{TemporaryVar, {temporaryVar, exprs}}; } - static FunctionBuilderTarget forExpression(Expr *expr) { - return FunctionBuilderTarget{Expression, { nullptr, { expr }}}; + static ResultBuilderTarget forExpression(Expr *expr) { + return ResultBuilderTarget{Expression, { nullptr, { expr }}}; } }; -/// Handles the rewrite of the body of a closure to which a function builder +/// Handles the rewrite of the body of a closure to which a result builder /// has been applied. class BuilderClosureRewriter - : public StmtVisitor { + : public StmtVisitor { ASTContext &ctx; const Solution &solution; DeclContext *dc; @@ -1001,12 +1001,12 @@ class BuilderClosureRewriter private: /// Build the statement or expression to initialize the target. - ASTNode initializeTarget(FunctionBuilderTarget target) { + ASTNode initializeTarget(ResultBuilderTarget target) { assert(target.captured.second.size() == 1); auto capturedExpr = target.captured.second.front(); SourceLoc implicitLoc = capturedExpr->getEndLoc(); switch (target.kind) { - case FunctionBuilderTarget::ReturnValue: { + case ResultBuilderTarget::ReturnValue: { // Return the expression. Type bodyResultType = solution.simplifyType(builderTransform.bodyResultType); @@ -1021,7 +1021,7 @@ class BuilderClosureRewriter return new (ctx) ReturnStmt(implicitLoc, resultExpr); } - case FunctionBuilderTarget::TemporaryVar: { + case ResultBuilderTarget::TemporaryVar: { // Assign the expression into a variable. auto temporaryVar = target.captured.first; auto declRef = new (ctx) DeclRefExpr( @@ -1041,11 +1041,11 @@ class BuilderClosureRewriter return assign; } - case FunctionBuilderTarget::Expression: + case ResultBuilderTarget::Expression: // Execute the expression. return rewriteExpr(capturedExpr); } - llvm_unreachable("invalid function builder target"); + llvm_unreachable("invalid result builder target"); } /// Declare the given temporary variable, adding the appropriate @@ -1102,8 +1102,8 @@ class BuilderClosureRewriter solution(solution), dc(dc), builderTransform(builderTransform), rewriteTarget(rewriteTarget) { } - Stmt *visitBraceStmt(BraceStmt *braceStmt, FunctionBuilderTarget target, - Optional innerTarget = None) { + Stmt *visitBraceStmt(BraceStmt *braceStmt, ResultBuilderTarget target, + Optional innerTarget = None) { std::vector newElements; // If there is an "inner" target corresponding to this brace, declare @@ -1156,7 +1156,7 @@ class BuilderClosureRewriter Stmt *finalStmt = visit( stmt, - FunctionBuilderTarget{FunctionBuilderTarget::TemporaryVar, + ResultBuilderTarget{ResultBuilderTarget::TemporaryVar, std::move(captured)}); newElements.push_back(finalStmt); continue; @@ -1208,21 +1208,21 @@ class BuilderClosureRewriter braceStmt->getRBraceLoc()); } - Stmt *visitIfStmt(IfStmt *ifStmt, FunctionBuilderTarget target) { + Stmt *visitIfStmt(IfStmt *ifStmt, ResultBuilderTarget target) { // Rewrite the condition. if (auto condition = rewriteTarget( SolutionApplicationTarget(ifStmt->getCond(), dc))) ifStmt->setCond(*condition->getAsStmtCondition()); - assert(target.kind == FunctionBuilderTarget::TemporaryVar); + assert(target.kind == ResultBuilderTarget::TemporaryVar); auto temporaryVar = target.captured.first; // Translate the "then" branch. auto capturedThen = takeCapturedStmt(ifStmt->getThenStmt()); auto newThen = visitBraceStmt(cast(ifStmt->getThenStmt()), - FunctionBuilderTarget::forAssign( + ResultBuilderTarget::forAssign( temporaryVar, {target.captured.second[0]}), - FunctionBuilderTarget::forAssign( + ResultBuilderTarget::forAssign( capturedThen.first, {capturedThen.second.front()})); ifStmt->setThenStmt(newThen); @@ -1232,7 +1232,7 @@ class BuilderClosureRewriter // // Note that this is for staging in support for buildLimitedAvailability(); // the diagnostic is currently a warning, so that existing code that - // compiles today will continue to compile. Once function builder types + // compiles today will continue to compile. Once result builder types // have had the chance to adopt buildLimitedAvailability(), we'll upgrade // this warning to an error. if (auto availabilityCond = findAvailabilityCondition(ifStmt->getCond())) { @@ -1247,28 +1247,28 @@ class BuilderClosureRewriter if (auto reason = TypeChecker::checkDeclarationAvailability( nominal, loc, dc)) { ctx.Diags.diagnose( - loc, diag::function_builder_missing_limited_availability, + loc, diag::result_builder_missing_limited_availability, builderTransform.builderType); - // Add a note to the function builder with a stub for + // Add a note to the result builder with a stub for // buildLimitedAvailability(). if (auto builder = builderTransform.builderType->getAnyNominal()) { SourceLoc buildInsertionLoc; std::string stubIndent; Type componentType; std::tie(buildInsertionLoc, stubIndent, componentType) = - determineFunctionBuilderBuildFixItInfo(builder); + determineResultBuilderBuildFixItInfo(builder); if (buildInsertionLoc.isValid()) { std::string fixItString; { llvm::raw_string_ostream out(fixItString); - printFunctionBuilderBuildFunction( + printResultBuilderBuildFunction( builder, componentType, - FunctionBuilderBuildFunction::BuildLimitedAvailability, + ResultBuilderBuildFunction::BuildLimitedAvailability, stubIndent, out); builder->diagnose( - diag::function_builder_missing_build_limited_availability, + diag::result_builder_missing_build_limited_availability, builderTransform.builderType) .fixItInsert(buildInsertionLoc, fixItString); } @@ -1288,9 +1288,9 @@ class BuilderClosureRewriter auto capturedElse = takeCapturedStmt(elseBraceStmt); Stmt *newElse = visitBraceStmt( elseBraceStmt, - FunctionBuilderTarget::forAssign( + ResultBuilderTarget::forAssign( temporaryVar, {target.captured.second[1]}), - FunctionBuilderTarget::forAssign( + ResultBuilderTarget::forAssign( capturedElse.first, {capturedElse.second.front()})); ifStmt->setElseStmt(newElse); } else if (auto elseIfStmt = cast_or_null(ifStmt->getElseStmt())){ @@ -1301,11 +1301,11 @@ class BuilderClosureRewriter newElseElements.push_back( visitIfStmt( elseIfStmt, - FunctionBuilderTarget::forAssign( + ResultBuilderTarget::forAssign( capturedElse.first, capturedElse.second))); newElseElements.push_back( initializeTarget( - FunctionBuilderTarget::forAssign( + ResultBuilderTarget::forAssign( temporaryVar, {target.captured.second[1]}))); Stmt *newElse = BraceStmt::create( @@ -1316,7 +1316,7 @@ class BuilderClosureRewriter // Form an "else" brace containing an assignment to the temporary // variable. auto init = initializeTarget( - FunctionBuilderTarget::forAssign( + ResultBuilderTarget::forAssign( temporaryVar, {target.captured.second[1]})); auto newElse = BraceStmt::create( ctx, ifStmt->getEndLoc(), { init }, ifStmt->getEndLoc()); @@ -1326,7 +1326,7 @@ class BuilderClosureRewriter return ifStmt; } - Stmt *visitDoStmt(DoStmt *doStmt, FunctionBuilderTarget target) { + Stmt *visitDoStmt(DoStmt *doStmt, ResultBuilderTarget target) { // Each statement turns into a (potential) temporary variable // binding followed by the statement itself. auto body = cast(doStmt->getBody()); @@ -1336,13 +1336,13 @@ class BuilderClosureRewriter visitBraceStmt( body, target, - FunctionBuilderTarget::forAssign( + ResultBuilderTarget::forAssign( captured.first, {captured.second.front()}))); doStmt->setBody(newInnerBody); return doStmt; } - Stmt *visitSwitchStmt(SwitchStmt *switchStmt, FunctionBuilderTarget target) { + Stmt *visitSwitchStmt(SwitchStmt *switchStmt, ResultBuilderTarget target) { // Translate the subject expression. ConstraintSystem &cs = solution.getConstraintSystem(); auto subjectTarget = @@ -1362,13 +1362,13 @@ class BuilderClosureRewriter // Translate all of the cases. bool limitExhaustivityChecks = false; - assert(target.kind == FunctionBuilderTarget::TemporaryVar); + assert(target.kind == ResultBuilderTarget::TemporaryVar); auto temporaryVar = target.captured.first; unsigned caseIndex = 0; for (auto caseStmt : switchStmt->getCases()) { if (!visitCaseStmt( caseStmt, - FunctionBuilderTarget::forAssign( + ResultBuilderTarget::forAssign( temporaryVar, {target.captured.second[caseIndex]}))) return nullptr; @@ -1387,7 +1387,7 @@ class BuilderClosureRewriter return switchStmt; } - Stmt *visitCaseStmt(CaseStmt *caseStmt, FunctionBuilderTarget target) { + Stmt *visitCaseStmt(CaseStmt *caseStmt, ResultBuilderTarget target) { // Translate the patterns and guard expressions for each case label item. for (auto &caseLabelItem : caseStmt->getMutableCaseLabelItems()) { SolutionApplicationTarget caseLabelTarget(&caseLabelItem, dc); @@ -1402,7 +1402,7 @@ class BuilderClosureRewriter visitBraceStmt( body, target, - FunctionBuilderTarget::forAssign( + ResultBuilderTarget::forAssign( captured.first, {captured.second.front()}))); caseStmt->setBody(newInnerBody); @@ -1410,7 +1410,7 @@ class BuilderClosureRewriter } Stmt *visitForEachStmt( - ForEachStmt *forEachStmt, FunctionBuilderTarget target) { + ForEachStmt *forEachStmt, ResultBuilderTarget target) { // Translate the for-each loop header. ConstraintSystem &cs = solution.getConstraintSystem(); auto forEachTarget = @@ -1443,18 +1443,18 @@ class BuilderClosureRewriter auto newBody = cast( visitBraceStmt( body, - FunctionBuilderTarget::forExpression(arrayAppendCall), - FunctionBuilderTarget::forAssign( + ResultBuilderTarget::forExpression(arrayAppendCall), + ResultBuilderTarget::forAssign( capturedBody.first, {capturedBody.second.front()}))); forEachStmt->setBody(newBody); outerBodySteps.push_back(forEachStmt); // Step 3. Perform the buildArray() call to turn the array of results // collected from the iterations into a single value under the control of - // the function builder. + // the result builder. outerBodySteps.push_back( initializeTarget( - FunctionBuilderTarget::forAssign(finalForEachVar, {buildArrayCall}))); + ResultBuilderTarget::forAssign(finalForEachVar, {buildArrayCall}))); // Form a brace statement to put together the three main steps for the // for-each loop translation outlined above. @@ -1474,34 +1474,34 @@ class BuilderClosureRewriter return throwStmt; } - Stmt *visitThrowStmt(ThrowStmt *throwStmt, FunctionBuilderTarget target) { + Stmt *visitThrowStmt(ThrowStmt *throwStmt, ResultBuilderTarget target) { llvm_unreachable("Throw statements produce no value"); } -#define UNHANDLED_FUNCTION_BUILDER_STMT(STMT) \ - Stmt *visit##STMT##Stmt(STMT##Stmt *stmt, FunctionBuilderTarget target) { \ +#define UNHANDLED_RESULT_BUILDER_STMT(STMT) \ + Stmt *visit##STMT##Stmt(STMT##Stmt *stmt, ResultBuilderTarget target) { \ llvm_unreachable("Function builders do not allow statement of kind " \ #STMT); \ } - UNHANDLED_FUNCTION_BUILDER_STMT(Return) - UNHANDLED_FUNCTION_BUILDER_STMT(Yield) - UNHANDLED_FUNCTION_BUILDER_STMT(Guard) - UNHANDLED_FUNCTION_BUILDER_STMT(While) - UNHANDLED_FUNCTION_BUILDER_STMT(Defer) - UNHANDLED_FUNCTION_BUILDER_STMT(DoCatch) - UNHANDLED_FUNCTION_BUILDER_STMT(RepeatWhile) - UNHANDLED_FUNCTION_BUILDER_STMT(Break) - UNHANDLED_FUNCTION_BUILDER_STMT(Continue) - UNHANDLED_FUNCTION_BUILDER_STMT(Fallthrough) - UNHANDLED_FUNCTION_BUILDER_STMT(Fail) - UNHANDLED_FUNCTION_BUILDER_STMT(PoundAssert) -#undef UNHANDLED_FUNCTION_BUILDER_STMT + UNHANDLED_RESULT_BUILDER_STMT(Return) + UNHANDLED_RESULT_BUILDER_STMT(Yield) + UNHANDLED_RESULT_BUILDER_STMT(Guard) + UNHANDLED_RESULT_BUILDER_STMT(While) + UNHANDLED_RESULT_BUILDER_STMT(Defer) + UNHANDLED_RESULT_BUILDER_STMT(DoCatch) + UNHANDLED_RESULT_BUILDER_STMT(RepeatWhile) + UNHANDLED_RESULT_BUILDER_STMT(Break) + UNHANDLED_RESULT_BUILDER_STMT(Continue) + UNHANDLED_RESULT_BUILDER_STMT(Fallthrough) + UNHANDLED_RESULT_BUILDER_STMT(Fail) + UNHANDLED_RESULT_BUILDER_STMT(PoundAssert) +#undef UNHANDLED_RESULT_BUILDER_STMT }; } // end anonymous namespace -BraceStmt *swift::applyFunctionBuilderTransform( +BraceStmt *swift::applyResultBuilderTransform( const Solution &solution, AppliedBuilderTransform applied, BraceStmt *body, @@ -1514,12 +1514,12 @@ BraceStmt *swift::applyFunctionBuilderTransform( return cast( rewriter.visitBraceStmt( body, - FunctionBuilderTarget::forReturn(applied.returnExpr), - FunctionBuilderTarget::forAssign( + ResultBuilderTarget::forReturn(applied.returnExpr), + ResultBuilderTarget::forAssign( captured.first, captured.second))); } -Optional TypeChecker::applyFunctionBuilderBodyTransform( +Optional TypeChecker::applyResultBuilderBodyTransform( FuncDecl *func, Type builderType) { // Pre-check the body: pre-check any expressions in it and look // for return statements. @@ -1528,38 +1528,38 @@ Optional TypeChecker::applyFunctionBuilderBodyTransform( // bail out and report that to the caller. auto &ctx = func->getASTContext(); auto request = - PreCheckFunctionBuilderRequest{{AnyFunctionRef(func), + PreCheckResultBuilderRequest{{AnyFunctionRef(func), /*SuppressDiagnostics=*/false}}; switch (evaluateOrDefault(ctx.evaluator, request, - FunctionBuilderBodyPreCheck::Error)) { - case FunctionBuilderBodyPreCheck::Okay: - // If the pre-check was okay, apply the function-builder transform. + ResultBuilderBodyPreCheck::Error)) { + case ResultBuilderBodyPreCheck::Okay: + // If the pre-check was okay, apply the result-builder transform. break; - case FunctionBuilderBodyPreCheck::Error: + case ResultBuilderBodyPreCheck::Error: return nullptr; - case FunctionBuilderBodyPreCheck::HasReturnStmt: { + case ResultBuilderBodyPreCheck::HasReturnStmt: { // One or more explicit 'return' statements were encountered, which - // disables the function builder transform. Warn when we do this. + // disables the result builder transform. Warn when we do this. auto returnStmts = findReturnStatements(func); assert(!returnStmts.empty()); ctx.Diags.diagnose( returnStmts.front()->getReturnLoc(), - diag::function_builder_disabled_by_return, builderType); + diag::result_builder_disabled_by_return, builderType); - // Note that one can remove the function builder attribute. - auto attr = func->getAttachedFunctionBuilder(); + // Note that one can remove the result builder attribute. + auto attr = func->getAttachedResultBuilder(); if (!attr) { if (auto accessor = dyn_cast(func)) { - attr = accessor->getStorage()->getAttachedFunctionBuilder(); + attr = accessor->getStorage()->getAttachedResultBuilder(); } } if (attr) { ctx.Diags.diagnose( - attr->getLocation(), diag::function_builder_remove_attr) + attr->getLocation(), diag::result_builder_remove_attr) .fixItRemove(attr->getRangeWithAt()); attr->setInvalid(); } @@ -1568,7 +1568,7 @@ Optional TypeChecker::applyFunctionBuilderBodyTransform( { auto diag = ctx.Diags.diagnose( returnStmts.front()->getReturnLoc(), - diag::function_builder_remove_returns); + diag::result_builder_remove_returns); for (auto returnStmt : returnStmts) { diag.fixItRemove(returnStmt->getReturnLoc()); } @@ -1594,7 +1594,7 @@ Optional TypeChecker::applyFunctionBuilderBodyTransform( // Build a constraint system in which we can check the body of the function. ConstraintSystem cs(func, options); - if (auto result = cs.matchFunctionBuilder( + if (auto result = cs.matchResultBuilder( func, builderType, resultContextType, resultConstraintKind, cs.getConstraintLocator(func->getBody()))) { if (result->isFailure()) @@ -1653,17 +1653,17 @@ Optional TypeChecker::applyFunctionBuilderBodyTransform( } Optional -ConstraintSystem::matchFunctionBuilder( +ConstraintSystem::matchResultBuilder( AnyFunctionRef fn, Type builderType, Type bodyResultType, ConstraintKind bodyResultConstraintKind, ConstraintLocatorBuilder locator) { auto builder = builderType->getAnyNominal(); - assert(builder && "Bad function builder type"); - assert(builder->getAttrs().hasAttribute()); + assert(builder && "Bad result builder type"); + assert(builder->getAttrs().hasAttribute()); - if (InvalidFunctionBuilderBodies.count(fn)) { + if (InvalidResultBuilderBodies.count(fn)) { (void)recordFix( - IgnoreInvalidFunctionBuilderBody::duringConstraintGeneration( + IgnoreInvalidResultBuilderBody::duringConstraintGeneration( *this, getConstraintLocator(fn.getBody()))); return getTypeMatchSuccess(); } @@ -1671,40 +1671,40 @@ ConstraintSystem::matchFunctionBuilder( // Pre-check the body: pre-check any expressions in it and look // for return statements. auto request = - PreCheckFunctionBuilderRequest{{fn, /*SuppressDiagnostics=*/true}}; + PreCheckResultBuilderRequest{{fn, /*SuppressDiagnostics=*/true}}; switch (evaluateOrDefault(getASTContext().evaluator, request, - FunctionBuilderBodyPreCheck::Error)) { - case FunctionBuilderBodyPreCheck::Okay: - // If the pre-check was okay, apply the function-builder transform. + ResultBuilderBodyPreCheck::Error)) { + case ResultBuilderBodyPreCheck::Okay: + // If the pre-check was okay, apply the result-builder transform. break; - case FunctionBuilderBodyPreCheck::Error: { + case ResultBuilderBodyPreCheck::Error: { if (!shouldAttemptFixes()) return getTypeMatchFailure(locator); - if (recordFix(IgnoreInvalidFunctionBuilderBody::duringPreCheck( + if (recordFix(IgnoreInvalidResultBuilderBody::duringPreCheck( *this, getConstraintLocator(fn.getBody())))) return getTypeMatchFailure(locator); return getTypeMatchSuccess(); } - case FunctionBuilderBodyPreCheck::HasReturnStmt: + case ResultBuilderBodyPreCheck::HasReturnStmt: // If the body has a return statement, suppress the transform but // continue solving the constraint system. return None; } // Check the form of this body to see if we can apply the - // function-builder translation at all. + // result-builder translation at all. auto dc = fn.getAsDeclContext(); { - // Check whether we can apply this specific function builder. + // Check whether we can apply this specific result builder. BuilderClosureVisitor visitor(getASTContext(), nullptr, dc, builderType, bodyResultType); // If we saw a control-flow statement or declaration that the builder - // cannot handle, we don't have a well-formed function builder application. + // cannot handle, we don't have a well-formed result builder application. if (auto unhandledNode = visitor.check(fn.getBody())) { // If we aren't supposed to attempt fixes, fail. if (!shouldAttemptFixes()) { @@ -1713,7 +1713,7 @@ ConstraintSystem::matchFunctionBuilder( // Record the first unhandled construct as a fix. if (recordFix( - SkipUnhandledConstructInFunctionBuilder::create( + SkipUnhandledConstructInResultBuilder::create( *this, unhandledNode, builder, getConstraintLocator(locator)))) { return getTypeMatchFailure(locator); @@ -1733,10 +1733,10 @@ ConstraintSystem::matchFunctionBuilder( return getTypeMatchFailure(locator); if (transaction.hasErrors()) { - InvalidFunctionBuilderBodies.insert(fn); + InvalidResultBuilderBodies.insert(fn); if (recordFix( - IgnoreInvalidFunctionBuilderBody::duringConstraintGeneration( + IgnoreInvalidResultBuilderBody::duringConstraintGeneration( *this, getConstraintLocator(fn.getBody())))) return getTypeMatchFailure(locator); @@ -1749,13 +1749,13 @@ ConstraintSystem::matchFunctionBuilder( // Record the transformation. assert(std::find_if( - functionBuilderTransformed.begin(), - functionBuilderTransformed.end(), + resultBuilderTransformed.begin(), + resultBuilderTransformed.end(), [&](const std::pair &elt) { return elt.first == fn; - }) == functionBuilderTransformed.end() && + }) == resultBuilderTransformed.end() && "already transformed this body along this path!?!"); - functionBuilderTransformed.push_back( + resultBuilderTransformed.push_back( std::make_pair(fn, std::move(*applied))); // If builder is applied to the closure expression then @@ -1765,7 +1765,7 @@ ConstraintSystem::matchFunctionBuilder( locator = getConstraintLocator(closure, ConstraintLocator::ClosureResult); } else { locator = getConstraintLocator(fn.getAbstractFunctionDecl(), - ConstraintLocator::FunctionBuilderBodyResult); + ConstraintLocator::ResultBuilderBodyResult); } // Bind the body result type to the type of the transformed expression. @@ -1777,7 +1777,7 @@ ConstraintSystem::matchFunctionBuilder( namespace { /// Pre-check all the expressions in the body. -class PreCheckFunctionBuilderApplication : public ASTWalker { +class PreCheckResultBuilderApplication : public ASTWalker { AnyFunctionRef Fn; bool SkipPrecheck = false; bool SuppressDiagnostics = false; @@ -1787,14 +1787,14 @@ class PreCheckFunctionBuilderApplication : public ASTWalker { bool hasReturnStmt() const { return !ReturnStmts.empty(); } public: - PreCheckFunctionBuilderApplication(AnyFunctionRef fn, bool skipPrecheck, + PreCheckResultBuilderApplication(AnyFunctionRef fn, bool skipPrecheck, bool suppressDiagnostics) : Fn(fn), SkipPrecheck(skipPrecheck), SuppressDiagnostics(suppressDiagnostics) {} const std::vector getReturnStmts() const { return ReturnStmts; } - FunctionBuilderBodyPreCheck run() { + ResultBuilderBodyPreCheck run() { Stmt *oldBody = Fn.getBody(); Stmt *newBody = oldBody->walk(*this); @@ -1803,14 +1803,14 @@ class PreCheckFunctionBuilderApplication : public ASTWalker { assert((newBody == nullptr) == HasError && "unexpected short-circuit while walking body"); if (HasError) - return FunctionBuilderBodyPreCheck::Error; + return ResultBuilderBodyPreCheck::Error; assert(oldBody == newBody && "pre-check walk wasn't in-place?"); if (hasReturnStmt()) - return FunctionBuilderBodyPreCheck::HasReturnStmt; + return ResultBuilderBodyPreCheck::HasReturnStmt; - return FunctionBuilderBodyPreCheck::Okay; + return ResultBuilderBodyPreCheck::Okay; } std::pair walkToExprPre(Expr *E) override { @@ -1859,8 +1859,8 @@ class PreCheckFunctionBuilderApplication : public ASTWalker { } -FunctionBuilderBodyPreCheck PreCheckFunctionBuilderRequest::evaluate( - Evaluator &evaluator, PreCheckFunctionBuilderDescriptor owner) const { +ResultBuilderBodyPreCheck PreCheckResultBuilderRequest::evaluate( + Evaluator &evaluator, PreCheckResultBuilderDescriptor owner) const { // We don't want to do the precheck if it will already have happened in // the enclosing expression. bool skipPrecheck = false; @@ -1868,14 +1868,14 @@ FunctionBuilderBodyPreCheck PreCheckFunctionBuilderRequest::evaluate( owner.Fn.getAbstractClosureExpr())) skipPrecheck = shouldTypeCheckInEnclosingExpression(closure); - return PreCheckFunctionBuilderApplication( + return PreCheckResultBuilderApplication( owner.Fn, /*skipPrecheck=*/false, /*suppressDiagnostics=*/owner.SuppressDiagnostics) .run(); } std::vector TypeChecker::findReturnStatements(AnyFunctionRef fn) { - PreCheckFunctionBuilderApplication precheck(fn, /*skipPreCheck=*/true, + PreCheckResultBuilderApplication precheck(fn, /*skipPreCheck=*/true, /*SuppressDiagnostics=*/true); (void)precheck.run(); return precheck.getReturnStmts(); @@ -1914,7 +1914,7 @@ bool TypeChecker::typeSupportsBuilderOp( return foundMatch; } -Type swift::inferFunctionBuilderComponentType(NominalTypeDecl *builder) { +Type swift::inferResultBuilderComponentType(NominalTypeDecl *builder) { Type componentType; SmallVector potentialMatches; @@ -1946,7 +1946,7 @@ Type swift::inferFunctionBuilderComponentType(NominalTypeDecl *builder) { } std::tuple -swift::determineFunctionBuilderBuildFixItInfo(NominalTypeDecl *builder) { +swift::determineResultBuilderBuildFixItInfo(NominalTypeDecl *builder) { SourceLoc buildInsertionLoc = builder->getBraces().Start; std::string stubIndent; Type componentType; @@ -1963,13 +1963,13 @@ swift::determineFunctionBuilderBuildFixItInfo(NominalTypeDecl *builder) { ctx.SourceMgr, buildInsertionLoc, &extraIndent); stubIndent = (currentIndent + extraIndent).str(); - componentType = inferFunctionBuilderComponentType(builder); + componentType = inferResultBuilderComponentType(builder); return std::make_tuple(buildInsertionLoc, stubIndent, componentType); } -void swift::printFunctionBuilderBuildFunction( +void swift::printResultBuilderBuildFunction( NominalTypeDecl *builder, Type componentType, - FunctionBuilderBuildFunction function, + ResultBuilderBuildFunction function, Optional stubIndent, llvm::raw_ostream &out) { // Render the component type into a string. std::string componentTypeString; @@ -1979,7 +1979,8 @@ void swift::printFunctionBuilderBuildFunction( componentTypeString = "<#Component#>"; // Render the code. - ExtraIndentStreamPrinter printer(out, stubIndent.getValueOr(std::string())); + std::string stubIndentStr = stubIndent.getValueOr(std::string()); + ExtraIndentStreamPrinter printer(out, stubIndentStr); // If we're supposed to provide a full stub, add a newline and the introducer // keywords. @@ -1994,36 +1995,36 @@ void swift::printFunctionBuilderBuildFunction( bool printedResult = false; switch (function) { - case FunctionBuilderBuildFunction::BuildBlock: + case ResultBuilderBuildFunction::BuildBlock: printer << "buildBlock(_ components: " << componentTypeString << "...)"; break; - case FunctionBuilderBuildFunction::BuildExpression: + case ResultBuilderBuildFunction::BuildExpression: printer << "buildExpression(_ expression: <#Expression#>)"; break; - case FunctionBuilderBuildFunction::BuildOptional: + case ResultBuilderBuildFunction::BuildOptional: printer << "buildOptional(_ component: " << componentTypeString << "?)"; break; - case FunctionBuilderBuildFunction::BuildEitherFirst: + case ResultBuilderBuildFunction::BuildEitherFirst: printer << "buildEither(first component: " << componentTypeString << ")"; break; - case FunctionBuilderBuildFunction::BuildEitherSecond: + case ResultBuilderBuildFunction::BuildEitherSecond: printer << "buildEither(second component: " << componentTypeString << ")"; break; - case FunctionBuilderBuildFunction::BuildArray: + case ResultBuilderBuildFunction::BuildArray: printer << "buildArray(_ components: [" << componentTypeString << "])"; break; - case FunctionBuilderBuildFunction::BuildLimitedAvailability: + case ResultBuilderBuildFunction::BuildLimitedAvailability: printer << "buildLimitedAvailability(_ component: " << componentTypeString << ")"; break; - case FunctionBuilderBuildFunction::BuildFinalResult: + case ResultBuilderBuildFunction::BuildFinalResult: printer << "buildFinalResult(_ component: " << componentTypeString << ") -> <#Result#>"; printedResult = true; diff --git a/lib/Sema/CMakeLists.txt b/lib/Sema/CMakeLists.txt index b9782c5465e26..79d9dedd9c33d 100644 --- a/lib/Sema/CMakeLists.txt +++ b/lib/Sema/CMakeLists.txt @@ -1,3 +1,4 @@ +set_swift_llvm_is_available() add_swift_host_library(swiftSema STATIC BuilderTransform.cpp CSApply.cpp @@ -17,6 +18,7 @@ add_swift_host_library(swiftSema STATIC ConstraintLocator.cpp ConstraintSystem.cpp DebuggerTestingTransform.cpp + DerivedConformanceActor.cpp DerivedConformanceAdditiveArithmetic.cpp DerivedConformanceCaseIterable.cpp DerivedConformanceCodable.cpp @@ -43,6 +45,7 @@ add_swift_host_library(swiftSema STATIC MiscDiagnostics.cpp PCMacro.cpp PlaygroundTransform.cpp + PreCheckExpr.cpp ResilienceDiagnostics.cpp SourceLoader.cpp TypeCheckAccess.cpp diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index b73e40525cdaa..329fc2d4a827a 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -16,24 +16,31 @@ // //===----------------------------------------------------------------------===// -#include "ConstraintSystem.h" -#include "CodeSynthesis.h" #include "CSDiagnostics.h" +#include "CodeSynthesis.h" #include "MiscDiagnostics.h" -#include "SolutionResult.h" #include "TypeCheckProtocol.h" #include "TypeCheckType.h" #include "swift/AST/ASTVisitor.h" #include "swift/AST/ASTWalker.h" +#include "swift/AST/ClangModuleLoader.h" #include "swift/AST/ExistentialLayout.h" -#include "swift/AST/Initializer.h" #include "swift/AST/GenericEnvironment.h" #include "swift/AST/GenericSignature.h" +#include "swift/AST/Initializer.h" #include "swift/AST/OperatorNameLookup.h" #include "swift/AST/ParameterList.h" #include "swift/AST/ProtocolConformance.h" #include "swift/AST/SubstitutionMap.h" #include "swift/Basic/StringExtras.h" +#include "swift/Sema/ConstraintSystem.h" +#include "swift/Sema/SolutionResult.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/Mangle.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Sema/Sema.h" +#include "clang/Sema/Template.h" +#include "clang/Sema/TemplateDeduction.h" #include "llvm/ADT/APFloat.h" #include "llvm/ADT/APInt.h" #include "llvm/ADT/SmallString.h" @@ -99,6 +106,45 @@ Solution::computeSubstitutions(GenericSignature sig, lookupConformanceFn); } +static ConcreteDeclRef generateDeclRefForSpecializedCXXFunctionTemplate( + ASTContext &ctx, FuncDecl *oldDecl, SubstitutionMap subst, + clang::FunctionDecl *specialized) { + // Create a new ParameterList with the substituted type. + auto oldFnType = + cast(oldDecl->getInterfaceType().getPointer()); + auto newFnType = oldFnType->substGenericArgs(subst); + SmallVector newParams; + unsigned i = 0; + for (auto paramTy : newFnType->getParams()) { + auto *oldParamDecl = oldDecl->getParameters()->get(i); + auto *newParamDecl = + ParamDecl::cloneWithoutType(oldDecl->getASTContext(), oldParamDecl); + newParamDecl->setInterfaceType(paramTy.getParameterType()); + newParams.push_back(newParamDecl); + (void)++i; + } + auto *newParamList = + ParameterList::create(ctx, SourceLoc(), newParams, SourceLoc()); + + // Generate a name for the specialized function. + std::string newNameStr; + llvm::raw_string_ostream buffer(newNameStr); + clang::MangleContext *mangler = + specialized->getASTContext().createMangleContext(); + mangler->mangleName(specialized, buffer); + buffer.flush(); + // Add all parameters as empty parameters. + auto newName = DeclName( + ctx, DeclName(ctx.getIdentifier(newNameStr)).getBaseName(), newParamList); + + auto newFnDecl = FuncDecl::createImported( + ctx, oldDecl->getLoc(), newName, oldDecl->getNameLoc(), + /*Async*/ false, oldDecl->hasThrows(), newParamList, + newFnType->getResult(), /*GenericParams*/ nullptr, + oldDecl->getDeclContext(), specialized); + return ConcreteDeclRef(newFnDecl); +} + ConcreteDeclRef Solution::resolveConcreteDeclRef(ValueDecl *decl, ConstraintLocator *locator) const { @@ -107,7 +153,25 @@ Solution::resolveConcreteDeclRef(ValueDecl *decl, // Get the generic signatue of the decl and compute the substitutions. auto sig = decl->getInnermostDeclContext()->getGenericSignatureOfContext(); - return ConcreteDeclRef(decl, computeSubstitutions(sig, locator)); + auto subst = computeSubstitutions(sig, locator); + + // If this is a C++ function template, get it's specialization for the given + // substitution map and update the decl accordingly. + if (decl->getClangDecl() && + isa(decl->getClangDecl())) { + auto *newFn = + decl->getASTContext() + .getClangModuleLoader() + ->instantiateCXXFunctionTemplate( + decl->getASTContext(), + const_cast( + cast(decl->getClangDecl())), + subst); + return generateDeclRefForSpecializedCXXFunctionTemplate( + decl->getASTContext(), cast(decl), subst, newFn); + } + + return ConcreteDeclRef(decl, subst); } ConstraintLocator *Solution::getCalleeLocator(ConstraintLocator *locator, @@ -482,8 +546,7 @@ namespace { public: /// Build a reference to the given declaration. Expr *buildDeclRef(SelectedOverload overload, DeclNameLoc loc, - ConstraintLocatorBuilder locator, bool implicit, - AccessSemantics semantics) { + ConstraintLocatorBuilder locator, bool implicit) { auto choice = overload.choice; assert(choice.getKind() != OverloadChoiceKind::DeclViaDynamic); auto *decl = choice.getDecl(); @@ -492,6 +555,9 @@ namespace { // Determine the declaration selected for this overloaded reference. auto &ctx = cs.getASTContext(); + auto semantics = decl->getAccessSemanticsFromContext(cs.DC, + /*isAccessOnSelf*/false); + // If this is a member of a nominal type, build a reference to the // member with an implied base type. if (decl->getDeclContext()->isTypeContext() && isa(decl)) { @@ -632,7 +698,6 @@ namespace { if (!ref) ref = solution.resolveConcreteDeclRef(decl, loc); - assert(ref.getDecl() == decl); return ref; } @@ -1057,7 +1122,6 @@ namespace { AccessSemantics semantics) { auto choice = overload.choice; auto openedType = overload.openedType; - auto openedFullType = overload.openedFullType; ValueDecl *member = choice.getDecl(); @@ -1093,13 +1157,15 @@ namespace { return result; } + auto refTy = simplifyType(overload.openedFullType); + // If we're referring to the member of a module, it's just a simple // reference. if (baseTy->is()) { assert(semantics == AccessSemantics::Ordinary && "Direct property access doesn't make sense for this"); auto ref = new (context) DeclRefExpr(memberRef, memberLoc, Implicit); - cs.setType(ref, simplifyType(openedFullType)); + cs.setType(ref, refTy); ref->setFunctionRefKind(choice.getFunctionRefKind()); auto *DSBI = cs.cacheType(new (context) DotSyntaxBaseIgnoredExpr( base, dotLoc, ref, cs.getType(ref))); @@ -1110,8 +1176,6 @@ namespace { (!baseIsInstance && member->isInstanceMember()); bool isPartialApplication = shouldBuildCurryThunk(choice, baseIsInstance); - auto refTy = simplifyType(openedFullType); - // The formal type of the 'self' value for the member's declaration. Type containerTy = getBaseType(refTy->castTo()); @@ -1174,8 +1238,8 @@ namespace { if (cs.getType(base)->is()) selfParamTy = InOutType::get(selfTy); - base = coerceObjectArgumentToType( - base, selfParamTy, member, semantics, + base = coerceSelfArgumentToType( + base, selfParamTy, member, locator.withPathElement(ConstraintLocator::MemberRefBase)); } else { if (!isExistentialMetatype || openedExistential) { @@ -1257,7 +1321,7 @@ namespace { } // For properties, build member references. - if (isa(member)) { + if (auto *varDecl = dyn_cast(member)) { if (isUnboundInstanceMember) { assert(memberLocator.getBaseLocator() && cs.UnevaluatedRootExprs.count( @@ -1270,17 +1334,22 @@ namespace { base->setImplicit(); } + auto hasDynamicSelf = + varDecl->getValueInterfaceType()->hasDynamicSelfType(); + auto memberRefExpr = new (context) MemberRefExpr(base, dotLoc, memberRef, memberLoc, Implicit, semantics); memberRefExpr->setIsSuper(isSuper); - cs.setType(memberRefExpr, simplifyType(openedType)); + if (hasDynamicSelf) + refTy = refTy->replaceCovariantResultType(containerTy, 1); + cs.setType(memberRefExpr, refTy->castTo()->getResult()); + Expr *result = memberRefExpr; closeExistential(result, locator); - if (cast(member)->getValueInterfaceType() - ->hasDynamicSelfType()) { + if (hasDynamicSelf) { if (!baseTy->isEqual(containerTy)) { result = new (context) CovariantReturnConversionExpr( result, simplifyType(openedType)); @@ -1512,10 +1581,8 @@ namespace { /// protocol is broken. /// /// \returns the converted literal expression. - Expr *convertLiteralInPlace(Expr *literal, - Type type, - ProtocolDecl *protocol, - Identifier literalType, + Expr *convertLiteralInPlace(LiteralExpr *literal, Type type, + ProtocolDecl *protocol, Identifier literalType, DeclName literalFuncName, ProtocolDecl *builtinProtocol, DeclName builtinLiteralFuncName, @@ -1588,7 +1655,7 @@ namespace { ArrayRef argLabels, ConstraintLocatorBuilder locator); - /// Coerce the given object argument (e.g., for the base of a + /// Coerce the given 'self' argument (e.g., for the base of a /// member expression) to the given type. /// /// \param expr The expression to coerce. @@ -1597,13 +1664,10 @@ namespace { /// /// \param member The member being accessed. /// - /// \param semantics The kind of access we've been asked to perform. - /// /// \param locator Locator used to describe where in this expression we are. - Expr *coerceObjectArgumentToType(Expr *expr, - Type baseTy, ValueDecl *member, - AccessSemantics semantics, - ConstraintLocatorBuilder locator); + Expr *coerceSelfArgumentToType(Expr *expr, + Type baseTy, ValueDecl *member, + ConstraintLocatorBuilder locator); private: /// Build a new subscript. @@ -1807,8 +1871,7 @@ namespace { // Handle dynamic lookup. if (choice.getKind() == OverloadChoiceKind::DeclViaDynamic || subscript->getAttrs().hasAttribute()) { - base = coerceObjectArgumentToType(base, baseTy, subscript, - AccessSemantics::Ordinary, locator); + base = coerceSelfArgumentToType(base, baseTy, subscript, locator); if (!base) return nullptr; @@ -1832,8 +1895,8 @@ namespace { auto containerTy = solution.simplifyType(openedBaseType); if (baseIsInstance) { - base = coerceObjectArgumentToType( - base, containerTy, subscript, AccessSemantics::Ordinary, + base = coerceSelfArgumentToType( + base, containerTy, subscript, locator.withPathElement(ConstraintLocator::MemberRefBase)); } else { base = coerceToType(base, @@ -2535,7 +2598,7 @@ namespace { KnownProtocolKind::ExpressibleByStringInterpolation, type, {ctx.Id_stringInterpolation}); if (!resultInit) return nullptr; - expr->setResultInit(resultInit); + expr->setInitializer(resultInit); // Make the integer literals for the parameters. auto buildExprFromUnsigned = [&](unsigned value) { @@ -2687,7 +2750,7 @@ namespace { // Find the overload choice used for this declaration reference. auto selected = solution.getOverloadChoice(locator); return buildDeclRef(selected, expr->getNameLoc(), locator, - expr->isImplicit(), expr->getAccessSemantics()); + expr->isImplicit()); } Expr *visitSuperRefExpr(SuperRefExpr *expr) { @@ -2717,7 +2780,7 @@ namespace { auto selected = solution.getOverloadChoice(locator); return buildDeclRef(selected, expr->getNameLoc(), locator, - expr->isImplicit(), AccessSemantics::Ordinary); + expr->isImplicit()); } Expr *visitUnresolvedDeclRefExpr(UnresolvedDeclRefExpr *expr) { @@ -3037,8 +3100,7 @@ namespace { diagnoseDeprecatedConditionalConformanceOuterAccess( UDE, selected.choice.getDecl()); - return buildDeclRef(selected, nameLoc, memberLocator, implicit, - AccessSemantics::Ordinary); + return buildDeclRef(selected, nameLoc, memberLocator, implicit); } switch (selected.choice.getKind()) { @@ -4310,7 +4372,7 @@ namespace { { Identifier() }); auto resultTy = TypeChecker::typeCheckExpression( - callExpr, cs.DC, valueType, CTP_CannotFail); + callExpr, cs.DC, /*contextualInfo=*/{valueType, CTP_CannotFail}); assert(resultTy && "Conversion cannot fail!"); (void)resultTy; @@ -6880,9 +6942,14 @@ static bool isNonMutatingSetterPWAssignInsideInit(Expr *baseExpr, /// the given member. static Type adjustSelfTypeForMember(Expr *baseExpr, Type baseTy, ValueDecl *member, - AccessSemantics semantics, DeclContext *UseDC) { - auto baseObjectTy = baseTy->getWithoutSpecifierType(); + assert(!baseTy->is()); + + auto inOutTy = baseTy->getAs(); + if (!inOutTy) + return baseTy; + + auto baseObjectTy = inOutTy->getObjectType(); if (isa(member)) return baseObjectTy; @@ -6891,7 +6958,7 @@ static Type adjustSelfTypeForMember(Expr *baseExpr, // If 'self' is an inout type, turn the base type into an lvalue // type with the same qualifiers. if (func->isMutating()) - return InOutType::get(baseObjectTy); + return baseTy; // Otherwise, return the rvalue type. return baseObjectTy; @@ -6914,34 +6981,17 @@ static Type adjustSelfTypeForMember(Expr *baseExpr, !isNonMutatingSetterPWAssignInsideInit(baseExpr, member, UseDC)) return baseObjectTy; - // If we're calling an accessor, keep the base as an inout type, because the - // getter may be mutating. - auto strategy = SD->getAccessStrategy(semantics, - isSettableFromHere - ? AccessKind::ReadWrite - : AccessKind::Read, - UseDC->getParentModule(), - UseDC->getResilienceExpansion()); - if (baseTy->is() && strategy.getKind() != AccessStrategy::Storage) - return InOutType::get(baseObjectTy); - - // Accesses to non-function members in value types are done through an @lvalue - // type. - if (baseTy->is()) - return LValueType::get(baseObjectTy); - - // Accesses to members in values of reference type (classes, metatypes) are - // always done through a the reference to self. Accesses to value types with - // a non-mutable self are also done through the base type. - return baseTy; + if (isa(member)) + return baseTy; + + return LValueType::get(baseObjectTy); } Expr * -ExprRewriter::coerceObjectArgumentToType(Expr *expr, - Type baseTy, ValueDecl *member, - AccessSemantics semantics, - ConstraintLocatorBuilder locator) { - Type toType = adjustSelfTypeForMember(expr, baseTy, member, semantics, dc); +ExprRewriter::coerceSelfArgumentToType(Expr *expr, + Type baseTy, ValueDecl *member, + ConstraintLocatorBuilder locator) { + Type toType = adjustSelfTypeForMember(expr, baseTy, member, dc); // If our expression already has the right type, we're done. Type fromType = cs.getType(expr); @@ -6964,15 +7014,11 @@ ExprRewriter::coerceObjectArgumentToType(Expr *expr, /*isImplicit*/ true)); } -Expr *ExprRewriter::convertLiteralInPlace(Expr *literal, - Type type, - ProtocolDecl *protocol, - Identifier literalType, - DeclName literalFuncName, - ProtocolDecl *builtinProtocol, - DeclName builtinLiteralFuncName, - Diag<> brokenProtocolDiag, - Diag<> brokenBuiltinProtocolDiag) { +Expr *ExprRewriter::convertLiteralInPlace( + LiteralExpr *literal, Type type, ProtocolDecl *protocol, + Identifier literalType, DeclName literalFuncName, + ProtocolDecl *builtinProtocol, DeclName builtinLiteralFuncName, + Diag<> brokenProtocolDiag, Diag<> brokenBuiltinProtocolDiag) { // If coercing a literal to an unresolved type, we don't try to look up the // witness members, just do it. if (type->is()) { @@ -6996,16 +7042,7 @@ Expr *ExprRewriter::convertLiteralInPlace(Expr *literal, // Form a reference to the builtin conversion function. // Set the builtin initializer. - if (auto stringLiteral = dyn_cast(literal)) - stringLiteral->setBuiltinInitializer(witness); - else if (auto booleanLiteral = dyn_cast(literal)) - booleanLiteral->setBuiltinInitializer(witness); - else if (auto numberLiteral = dyn_cast(literal)) - numberLiteral->setBuiltinInitializer(witness); - else { - cast(literal)->setBuiltinInitializer( - witness); - } + dyn_cast(literal)->setBuiltinInitializer(witness); // The literal expression has this type. cs.setType(literal, type); @@ -7042,16 +7079,7 @@ Expr *ExprRewriter::convertLiteralInPlace(Expr *literal, return nullptr; // Set the initializer. - if (auto nilLiteral = dyn_cast(literal)) - nilLiteral->setInitializer(witness); - else if (auto stringLiteral = dyn_cast(literal)) - stringLiteral->setInitializer(witness); - else if (auto booleanLiteral = dyn_cast(literal)) - booleanLiteral->setInitializer(witness); - else if (auto numberLiteral = dyn_cast(literal)) - numberLiteral->setInitializer(witness); - else - cast(literal)->setInitializer(witness); + literal->setInitializer(witness); // The literal expression has this type. cs.setType(literal, type); @@ -8052,8 +8080,8 @@ static Optional applySolutionToForEachStmt( Expr *convertElementExpr = elementExpr; if (TypeChecker::typeCheckExpression( convertElementExpr, dc, - optPatternType, - CTP_CoerceOperand).isNull()) { + /*contextualInfo=*/{optPatternType, CTP_CoerceOperand}) + .isNull()) { return None; } elementExpr->setIsPlaceholder(false); diff --git a/lib/Sema/CSBindings.cpp b/lib/Sema/CSBindings.cpp index 7db71f632008a..7a84967671844 100644 --- a/lib/Sema/CSBindings.cpp +++ b/lib/Sema/CSBindings.cpp @@ -13,44 +13,155 @@ // This file implements selection of bindings for type variables. // //===----------------------------------------------------------------------===// -#include "ConstraintGraph.h" -#include "ConstraintSystem.h" +#include "TypeChecker.h" +#include "swift/Sema/ConstraintGraph.h" +#include "swift/Sema/ConstraintSystem.h" #include "llvm/ADT/SetVector.h" #include using namespace swift; using namespace constraints; +void ConstraintSystem::PotentialBindings::inferTransitiveProtocolRequirements( + const ConstraintSystem &cs, + llvm::SmallDenseMap + &inferredBindings) { + if (TransitiveProtocols) + return; + + llvm::SmallVector, 4> + workList; + llvm::SmallPtrSet visitedRelations; + + llvm::SmallDenseMap, 4> + protocols; + + auto addToWorkList = [&](TypeVariableType *parent, + TypeVariableType *typeVar) { + if (visitedRelations.insert(typeVar).second) + workList.push_back({parent, typeVar}); + }; + + auto propagateProtocolsTo = + [&protocols](TypeVariableType *dstVar, + const SmallVectorImpl &direct, + const SmallPtrSetImpl &transitive) { + auto &destination = protocols[dstVar]; + + for (auto *protocol : direct) + destination.insert(protocol); + + for (auto *protocol : transitive) + destination.insert(protocol); + }; + + addToWorkList(nullptr, TypeVar); + + do { + auto *currentVar = workList.back().second; + + auto cachedBindings = inferredBindings.find(currentVar); + if (cachedBindings == inferredBindings.end()) { + workList.pop_back(); + continue; + } + + auto &bindings = cachedBindings->getSecond(); + + // If current variable already has transitive protocol + // conformances inferred, there is no need to look deeper + // into subtype/equivalence chain. + if (bindings.TransitiveProtocols) { + TypeVariableType *parent = nullptr; + std::tie(parent, currentVar) = workList.pop_back_val(); + assert(parent); + propagateProtocolsTo(parent, bindings.Protocols, + *bindings.TransitiveProtocols); + continue; + } + + for (const auto &entry : bindings.SubtypeOf) + addToWorkList(currentVar, entry.first); + + // If current type variable is part of an equivalence + // class, make it a "representative" and let's it infer + // supertypes and direct protocol requirements from + // other members. + for (const auto &entry : bindings.EquivalentTo) { + auto eqBindings = inferredBindings.find(entry.first); + if (eqBindings != inferredBindings.end()) { + const auto &bindings = eqBindings->getSecond(); + + llvm::SmallPtrSet placeholder; + // Add any direct protocols from members of the + // equivalence class, so they could be propagated + // to all of the members. + propagateProtocolsTo(currentVar, bindings.Protocols, placeholder); + + // Since type variables are equal, current type variable + // becomes a subtype to any supertype found in the current + // equivalence class. + for (const auto &eqEntry : bindings.SubtypeOf) + addToWorkList(currentVar, eqEntry.first); + } + } + + // More subtype/equivalences relations have been added. + if (workList.back().second != currentVar) + continue; + + TypeVariableType *parent = nullptr; + std::tie(parent, currentVar) = workList.pop_back_val(); + + // At all of the protocols associated with current type variable + // are transitive to its parent, propogate them down the subtype/equivalence + // chain. + if (parent) { + propagateProtocolsTo(parent, bindings.Protocols, protocols[currentVar]); + } + + auto inferredProtocols = std::move(protocols[currentVar]); + + llvm::SmallPtrSet protocolsForEquivalence; + + // Equivalence class should contain both: + // - direct protocol requirements of the current type + // variable; + // - all of the transitive protocols inferred through + // the members of the equivalence class. + { + protocolsForEquivalence.insert(bindings.Protocols.begin(), + bindings.Protocols.end()); + + protocolsForEquivalence.insert(inferredProtocols.begin(), + inferredProtocols.end()); + } + + // Propogate inferred protocols to all of the members of the + // equivalence class. + for (const auto &equivalence : bindings.EquivalentTo) { + auto eqBindings = inferredBindings.find(equivalence.first); + if (eqBindings != inferredBindings.end()) { + auto &bindings = eqBindings->getSecond(); + bindings.TransitiveProtocols.emplace(protocolsForEquivalence); + } + } + + // Update the bindings associated with current type variable, + // to avoid repeating this inference process. + bindings.TransitiveProtocols.emplace(std::move(inferredProtocols)); + } while (!workList.empty()); +} + void ConstraintSystem::PotentialBindings::inferTransitiveBindings( - const ConstraintSystem &cs, llvm::SmallPtrSetImpl &existingTypes, + ConstraintSystem &cs, llvm::SmallPtrSetImpl &existingTypes, const llvm::SmallDenseMap &inferredBindings) { using BindingKind = ConstraintSystem::AllowedBindingKind; - llvm::SmallVector conversions; - // First, let's collect all of the conversions associated - // with this type variable. - llvm::copy_if( - Sources, std::back_inserter(conversions), - [&](const Constraint *constraint) -> bool { - if (constraint->getKind() != ConstraintKind::Subtype && - constraint->getKind() != ConstraintKind::Conversion && - constraint->getKind() != ConstraintKind::ArgumentConversion && - constraint->getKind() != ConstraintKind::OperatorArgumentConversion) - return false; - - auto rhs = cs.simplifyType(constraint->getSecondType()); - return rhs->getAs() == TypeVar; - }); - - for (auto *constraint : conversions) { - auto *tv = - cs.simplifyType(constraint->getFirstType())->getAs(); - if (!tv || tv == TypeVar) - continue; - - auto relatedBindings = inferredBindings.find(tv); + for (const auto &entry : SupertypeOf) { + auto relatedBindings = inferredBindings.find(entry.first); if (relatedBindings == inferredBindings.end()) continue; @@ -88,7 +199,7 @@ void ConstraintSystem::PotentialBindings::inferTransitiveBindings( llvm::copy(bindings.Defaults, std::back_inserter(Defaults)); // TODO: We shouldn't need this in the future. - if (constraint->getKind() != ConstraintKind::Subtype) + if (entry.second->getKind() != ConstraintKind::Subtype) continue; for (auto &binding : bindings.Bindings) { @@ -144,7 +255,7 @@ isUnviableDefaultType(Type defaultType, } void ConstraintSystem::PotentialBindings::inferDefaultTypes( - const ConstraintSystem &cs, llvm::SmallPtrSetImpl &existingTypes) { + ConstraintSystem &cs, llvm::SmallPtrSetImpl &existingTypes) { auto isDirectRequirement = [&](Constraint *constraint) -> bool { if (auto *typeVar = constraint->getFirstType()->getAs()) { auto *repr = cs.getRepresentative(typeVar); @@ -300,9 +411,8 @@ void ConstraintSystem::PotentialBindings::inferDefaultTypes( } void ConstraintSystem::PotentialBindings::finalize( - const ConstraintSystem &cs, - const llvm::SmallDenseMap + ConstraintSystem &cs, + llvm::SmallDenseMap &inferredBindings) { // We need to make sure that there are no duplicate bindings in the // set, otherwise solver would produce multiple identical solutions. @@ -310,8 +420,8 @@ void ConstraintSystem::PotentialBindings::finalize( for (const auto &binding : Bindings) existingTypes.insert(binding.BindingType->getCanonicalType()); + inferTransitiveProtocolRequirements(cs, inferredBindings); inferTransitiveBindings(cs, existingTypes, inferredBindings); - inferDefaultTypes(cs, existingTypes); // Adjust optionality of existing bindings based on presence of @@ -368,6 +478,24 @@ void ConstraintSystem::PotentialBindings::finalize( PotentiallyIncomplete = true; } + // Delay resolution of the `nil` literal to a hole until + // the very end to give it a change to be bound to some + // other type, just like code completion expression which + // relies solely on contextual information. + if (locator->directlyAt()) { + FullyBound = true; + PotentiallyIncomplete = true; + } + + // If this type variable is associated with a code completion token + // and it failed to infer any bindings let's adjust hole's locator + // to point to a code completion token to avoid attempting to "fix" + // this problem since its rooted in the fact that constraint system + // is under-constrained. + if (AssociatedCodeCompletionToken) { + locator = cs.getConstraintLocator(AssociatedCodeCompletionToken); + } + addPotentialBinding(PotentialBinding::forHole(TypeVar, locator)); } @@ -611,8 +739,7 @@ bool ConstraintSystem::PotentialBindings::favoredOverDisjunction( } ConstraintSystem::PotentialBindings -ConstraintSystem::inferBindingsFor(TypeVariableType *typeVar, - bool finalize) const { +ConstraintSystem::inferBindingsFor(TypeVariableType *typeVar, bool finalize) { assert(typeVar->getImpl().getRepresentative(nullptr) == typeVar && "not a representative"); assert(!typeVar->getImpl().getFixedType(nullptr) && "has a fixed type"); @@ -652,10 +779,6 @@ ConstraintSystem::getPotentialBindingForRelationalConstraint( auto *typeVar = result.TypeVar; - // Record constraint which contributes to the - // finding of potential bindings. - result.Sources.insert(constraint); - auto first = simplifyType(constraint->getFirstType()); auto second = simplifyType(constraint->getSecondType()); @@ -760,9 +883,41 @@ ConstraintSystem::getPotentialBindingForRelationalConstraint( result.InvolvesTypeVariables = true; - if (constraint->getKind() == ConstraintKind::Subtype && - kind == AllowedBindingKind::Subtypes) { - result.SubtypeOf.insert(bindingTypeVar); + // If current type variable is associated with a code completion token + // it's possible that it doesn't have enough contextual information + // to be resolved to anything, so let's note that fact in the potential + // bindings and use it when forming a hole if there are no other bindings + // available. + if (auto *locator = bindingTypeVar->getImpl().getLocator()) { + if (locator->directlyAt()) { + result.AssociatedCodeCompletionToken = locator->getAnchor(); + result.PotentiallyIncomplete = true; + } + } + + switch (constraint->getKind()) { + case ConstraintKind::Subtype: + case ConstraintKind::Conversion: + case ConstraintKind::ArgumentConversion: + case ConstraintKind::OperatorArgumentConversion: { + if (kind == AllowedBindingKind::Subtypes) { + result.SubtypeOf.insert({bindingTypeVar, constraint}); + } else { + assert(kind == AllowedBindingKind::Supertypes); + result.SupertypeOf.insert({bindingTypeVar, constraint}); + } + break; + } + + case ConstraintKind::Bind: + case ConstraintKind::BindParam: + case ConstraintKind::Equal: { + result.EquivalentTo.insert({bindingTypeVar, constraint}); + break; + } + + default: + break; } return None; @@ -820,7 +975,7 @@ ConstraintSystem::getPotentialBindingForRelationalConstraint( /// representative type variable, along with flags indicating whether /// those types should be opened. bool ConstraintSystem::PotentialBindings::infer( - const ConstraintSystem &cs, llvm::SmallPtrSetImpl &exactTypes, + ConstraintSystem &cs, llvm::SmallPtrSetImpl &exactTypes, Constraint *constraint) { switch (constraint->getKind()) { case ConstraintKind::Bind: @@ -956,8 +1111,13 @@ bool ConstraintSystem::PotentialBindings::infer( break; case ConstraintKind::ConformsTo: - case ConstraintKind::SelfObjectOfProtocol: - return false; + case ConstraintKind::SelfObjectOfProtocol: { + auto protocolTy = constraint->getSecondType(); + if (!protocolTy->is()) + return false; + + LLVM_FALLTHROUGH; + } case ConstraintKind::LiteralConformsTo: { // Record constraint where protocol requirement originated @@ -1208,6 +1368,92 @@ bool TypeVarBindingProducer::computeNext() { return true; } +Optional> +TypeVariableBinding::fixForHole(ConstraintSystem &cs) const { + auto *dstLocator = TypeVar->getImpl().getLocator(); + auto *srcLocator = Binding.getLocator(); + + // FIXME: This check could be turned into an assert once + // all code completion kinds are ported to use + // `TypeChecker::typeCheckForCodeCompletion` API. + if (cs.isForCodeCompletion()) { + // If the hole is originated from code completion expression + // let's not try to fix this, anything connected to a + // code completion is allowed to be a hole because presence + // of a code completion token makes constraint system + // under-constrained due to e.g. lack of expressions on the + // right-hand side of the token, which are required for a + // regular type-check. + if (dstLocator->directlyAt()) + return None; + } + + unsigned defaultImpact = 1; + + if (auto *GP = TypeVar->getImpl().getGenericParameter()) { + // If it is represetative for a key path root, let's emit a more + // specific diagnostic. + auto *keyPathRoot = + cs.isRepresentativeFor(TypeVar, ConstraintLocator::KeyPathRoot); + if (keyPathRoot) { + ConstraintFix *fix = SpecifyKeyPathRootType::create( + cs, keyPathRoot->getImpl().getLocator()); + return std::make_pair(fix, defaultImpact); + } else { + auto path = dstLocator->getPath(); + // Drop `generic parameter` locator element so that all missing + // generic parameters related to the same path can be coalesced later. + ConstraintFix *fix = DefaultGenericArgument::create( + cs, GP, + cs.getConstraintLocator(dstLocator->getAnchor(), path.drop_back())); + return std::make_pair(fix, defaultImpact); + } + } + + if (TypeVar->getImpl().isClosureParameterType()) { + ConstraintFix *fix = SpecifyClosureParameterType::create(cs, dstLocator); + return std::make_pair(fix, defaultImpact); + } + + if (TypeVar->getImpl().isClosureResultType()) { + auto *closure = castToExpr(dstLocator->getAnchor()); + // If the whole body is being ignored due to a pre-check failure, + // let's not record a fix about result type since there is + // just not enough context to infer it without a body. + if (cs.hasFixFor(cs.getConstraintLocator(closure->getBody()), + FixKind::IgnoreInvalidResultBuilderBody)) + return None; + + ConstraintFix *fix = SpecifyClosureReturnType::create(cs, dstLocator); + return std::make_pair(fix, defaultImpact); + } + + if (srcLocator->directlyAt()) { + ConstraintFix *fix = SpecifyObjectLiteralTypeImport::create(cs, dstLocator); + return std::make_pair(fix, defaultImpact); + } + + if (srcLocator->isKeyPathRoot()) { + // If we recorded an invalid key path fix, let's skip this specify root + // type fix because it wouldn't produce a useful diagnostic. + auto *kpLocator = cs.getConstraintLocator(srcLocator->getAnchor()); + if (cs.hasFixFor(kpLocator, FixKind::AllowKeyPathWithoutComponents)) + return None; + + ConstraintFix *fix = SpecifyKeyPathRootType::create(cs, dstLocator); + return std::make_pair(fix, defaultImpact); + } + + if (dstLocator->directlyAt()) { + // This is a dramatic event, it means that there is absolutely + // no contextual information to resolve type of `nil`. + ConstraintFix *fix = SpecifyContextualTypeForNil::create(cs, dstLocator); + return std::make_pair(fix, /*impact=*/(unsigned)10); + } + + return None; +} + bool TypeVariableBinding::attempt(ConstraintSystem &cs) const { auto type = Binding.BindingType; auto *srcLocator = Binding.getLocator(); @@ -1229,49 +1475,10 @@ bool TypeVariableBinding::attempt(ConstraintSystem &cs) const { // resolved and had to be bound to a placeholder "hole" type. cs.increaseScore(SK_Hole); - ConstraintFix *fix = nullptr; - if (auto *GP = TypeVar->getImpl().getGenericParameter()) { - // If it is represetative for a key path root, let's emit a more - // specific diagnostic. - auto *keyPathRoot = - cs.isRepresentativeFor(TypeVar, ConstraintLocator::KeyPathRoot); - if (keyPathRoot) { - fix = SpecifyKeyPathRootType::create( - cs, keyPathRoot->getImpl().getLocator()); - } else { - auto path = dstLocator->getPath(); - // Drop `generic parameter` locator element so that all missing - // generic parameters related to the same path can be coalesced later. - fix = DefaultGenericArgument::create( - cs, GP, - cs.getConstraintLocator(dstLocator->getAnchor(), - path.drop_back())); - } - } else if (TypeVar->getImpl().isClosureParameterType()) { - fix = SpecifyClosureParameterType::create(cs, dstLocator); - } else if (TypeVar->getImpl().isClosureResultType()) { - auto *locator = TypeVar->getImpl().getLocator(); - auto *closure = castToExpr(locator->getAnchor()); - // If the whole body is being ignored due to a pre-check failure, - // let's not record a fix about result type since there is - // just not enough context to infer it without a body. - if (!cs.hasFixFor(cs.getConstraintLocator(closure->getBody()), - FixKind::IgnoreInvalidFunctionBuilderBody)) - fix = SpecifyClosureReturnType::create(cs, dstLocator); - } else if (srcLocator->directlyAt()) { - fix = SpecifyObjectLiteralTypeImport::create(cs, dstLocator); - } else if (srcLocator->isKeyPathRoot()) { - // If we recorded an invalid key path fix, let's skip this specify root - // type fix because it wouldn't produce a useful diagnostic. - auto *kpLocator = cs.getConstraintLocator(srcLocator->getAnchor()); - if (cs.hasFixFor(kpLocator, FixKind::AllowKeyPathWithoutComponents)) + if (auto fix = fixForHole(cs)) { + if (cs.recordFix(/*fix=*/fix->first, /*impact=*/fix->second)) return true; - - fix = SpecifyKeyPathRootType::create(cs, dstLocator); } - - if (fix && cs.recordFix(fix)) - return true; } } diff --git a/lib/Sema/CSClosure.cpp b/lib/Sema/CSClosure.cpp index 228fa555b20e8..5788096930edc 100644 --- a/lib/Sema/CSClosure.cpp +++ b/lib/Sema/CSClosure.cpp @@ -16,7 +16,9 @@ // //===----------------------------------------------------------------------===// -#include "ConstraintSystem.h" +#include "TypeChecker.h" +#include "swift/Sema/ConstraintSystem.h" + using namespace swift; using namespace swift::constraints; @@ -311,11 +313,11 @@ SolutionApplicationToFunctionResult ConstraintSystem::applySolution( // transformations. llvm::SaveAndRestore savedDC(currentDC, fn.getAsDeclContext()); - // Apply the function builder transform, if there is one. + // Apply the result builder transform, if there is one. if (auto transform = solution.getAppliedBuilderTransform(fn)) { - // Apply the function builder to the closure. We want to be in the + // Apply the result builder to the closure. We want to be in the // context of the closure for subsequent transforms. - auto newBody = applyFunctionBuilderTransform( + auto newBody = applyResultBuilderTransform( solution, *transform, fn.getBody(), fn.getAsDeclContext(), [&](SolutionApplicationTarget target) { auto resultTarget = rewriteTarget(target); diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 405fc84535684..108a267866267 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -15,7 +15,6 @@ //===----------------------------------------------------------------------===// #include "CSDiagnostics.h" -#include "ConstraintSystem.h" #include "MiscDiagnostics.h" #include "TypeCheckProtocol.h" #include "TypoCorrection.h" @@ -243,9 +242,9 @@ ValueDecl *RequirementFailure::getDeclRef() const { return type->getAnyGeneric(); }; - // If the locator is for a function builder body result type, the requirement + // If the locator is for a result builder body result type, the requirement // came from the function's return type. - if (getLocator()->isForFunctionBuilderBodyResult()) { + if (getLocator()->isForResultBuilderBodyResult()) { auto *func = getAsDecl(getAnchor()); return getAffectedDeclFromType(func->getResultInterfaceType()); } @@ -519,6 +518,8 @@ bool MissingConformanceFailure::diagnoseTypeCannotConform( nonConformingType->isEqual(protocolType), protocolType); + emitDiagnostic(diag::only_concrete_types_conform_to_protocols); + if (auto *OTD = dyn_cast(AffectedDecl)) { auto *namingDecl = OTD->getNamingDecl(); if (auto *repr = namingDecl->getOpaqueResultTypeRepr()) { @@ -1197,7 +1198,12 @@ void MissingOptionalUnwrapFailure::offerForceUnwrapFixIt( } } +// FIXME: This walks a partially-type checked function body, which +// is not guaranteed to yield consistent results. We should come up +// with another way of performing this analysis, for example by moving +// it to a post-type checking pass in MiscDiagnostics. class VarDeclMultipleReferencesChecker : public ASTWalker { + DeclContext *DC; VarDecl *varDecl; int count; @@ -1206,11 +1212,30 @@ class VarDeclMultipleReferencesChecker : public ASTWalker { if (DRE->getDecl() == varDecl) ++count; } + + // FIXME: We can see UnresolvedDeclRefExprs here because we have + // not yet run preCheckExpression() on the entire function body + // yet. + // + // We could consider pre-checking more eagerly. + if (auto *UDRE = dyn_cast(E)) { + auto name = UDRE->getName(); + auto loc = UDRE->getLoc(); + if (name.isSimpleName(varDecl->getName()) && loc.isValid()) { + auto *otherDecl = + ASTScope::lookupSingleLocalDecl(DC->getParentSourceFile(), + name.getFullName(), loc); + if (otherDecl == varDecl) + ++count; + } + } + return { true, E }; } public: - VarDeclMultipleReferencesChecker(VarDecl *varDecl) : varDecl(varDecl),count(0) {} + VarDeclMultipleReferencesChecker(DeclContext *DC, VarDecl *varDecl) + : DC(DC), varDecl(varDecl),count(0) {} int referencesCount() { return count; } }; @@ -1269,12 +1294,10 @@ bool MissingOptionalUnwrapFailure::diagnoseAsError() { if (auto varDecl = dyn_cast(declRef->getDecl())) { bool singleUse = false; AbstractFunctionDecl *AFD = nullptr; - if (auto contextDecl = varDecl->getDeclContext()->getAsDecl()) { - if ((AFD = dyn_cast(contextDecl))) { - auto checker = VarDeclMultipleReferencesChecker(varDecl); - AFD->getBody()->walk(checker); - singleUse = checker.referencesCount() == 1; - } + if ((AFD = dyn_cast(varDecl->getDeclContext()))) { + auto checker = VarDeclMultipleReferencesChecker(getDC(), varDecl); + AFD->getBody()->walk(checker); + singleUse = checker.referencesCount() == 1; } PatternBindingDecl *binding = varDecl->getParentPatternBinding(); @@ -1430,8 +1453,8 @@ bool RValueTreatedAsLValueFailure::diagnoseAsError() { if (auto *ctor = dyn_cast(getDC())) { if (auto *baseRef = dyn_cast(member->getBase())) { if (baseRef->getDecl() == ctor->getImplicitSelfDecl() && - ctor->getDelegatingOrChainedInitKind(nullptr) == - ConstructorDecl::BodyInitKind::Delegating) { + ctor->getDelegatingOrChainedInitKind().initKind == + BodyInitKind::Delegating) { emitDiagnosticAt(loc, diag::assignment_let_property_delegating_init, member->getName()); if (auto overload = getOverloadChoiceIfAvailable( @@ -1481,31 +1504,31 @@ bool RValueTreatedAsLValueFailure::diagnoseAsNote() { return true; } -static Decl *findSimpleReferencedDecl(const Expr *E) { +static VarDecl *findSimpleReferencedVarDecl(const Expr *E) { if (auto *LE = dyn_cast(E)) E = LE->getSubExpr(); if (auto *DRE = dyn_cast(E)) - return DRE->getDecl(); + return dyn_cast(DRE->getDecl()); return nullptr; } -static std::pair findReferencedDecl(const Expr *E) { +static std::pair findReferencedVarDecl(const Expr *E) { E = E->getValueProvidingExpr(); if (auto *LE = dyn_cast(E)) - return findReferencedDecl(LE->getSubExpr()); + return findReferencedVarDecl(LE->getSubExpr()); if (auto *AE = dyn_cast(E)) - return findReferencedDecl(AE->getDest()); + return findReferencedVarDecl(AE->getDest()); - if (auto *D = findSimpleReferencedDecl(E)) + if (auto *D = findSimpleReferencedVarDecl(E)) return std::make_pair(nullptr, D); if (auto *MRE = dyn_cast(E)) { - if (auto *BaseDecl = findSimpleReferencedDecl(MRE->getBase())) - return std::make_pair(BaseDecl, MRE->getMember().getDecl()); + if (auto *BaseDecl = findSimpleReferencedVarDecl(MRE->getBase())) + return std::make_pair(BaseDecl, cast(MRE->getMember().getDecl())); } return std::make_pair(nullptr, nullptr); @@ -1519,10 +1542,12 @@ bool TypeChecker::diagnoseSelfAssignment(const Expr *expr) { auto *dstExpr = assignExpr->getDest(); auto *srcExpr = assignExpr->getSrc(); - auto dstDecl = findReferencedDecl(dstExpr); - auto srcDecl = findReferencedDecl(srcExpr); + auto dstDecl = findReferencedVarDecl(dstExpr); + auto srcDecl = findReferencedVarDecl(srcExpr); - if (dstDecl.second && dstDecl == srcDecl) { + if (dstDecl.second && + dstDecl.second->hasStorage() && + dstDecl == srcDecl) { auto &DE = dstDecl.second->getASTContext().Diags; DE.diagnose(expr->getLoc(), dstDecl.first ? diag::self_assignment_prop : diag::self_assignment_var) @@ -2150,6 +2175,7 @@ bool ContextualFailure::diagnoseAsError() { emitDiagnostic(diag::type_cannot_conform, /*isExistentialType=*/true, fromType, fromType->isEqual(toType), toType); + emitDiagnostic(diag::only_concrete_types_conform_to_protocols); return true; } @@ -2235,7 +2261,7 @@ bool ContextualFailure::diagnoseAsError() { return true; } - case ConstraintLocator::FunctionBuilderBodyResult: { + case ConstraintLocator::ResultBuilderBodyResult: { diagnostic = *getDiagnosticFor(CTP_Initialization, toType); break; } @@ -2442,9 +2468,6 @@ void ContextualFailure::tryFixIts(InFlightDiagnostic &diagnostic) const { } bool ContextualFailure::diagnoseMissingFunctionCall() const { - if (getLocator()->isLastElement()) - return false; - if (getLocator() ->isLastElement()) return false; @@ -4076,9 +4099,9 @@ bool MissingArgumentsFailure::diagnoseAsError() { interleave( SynthesizedArgs, - [&](const std::pair &e) { - const auto paramIdx = e.first; - const auto &arg = e.second; + [&](const SynthesizedArg &e) { + const auto paramIdx = e.paramIdx; + const auto &arg = e.param; if (arg.hasLabel()) { arguments << "'" << arg.getLabel().str() << "'"; @@ -4105,8 +4128,8 @@ bool MissingArgumentsFailure::diagnoseAsError() { llvm::raw_svector_ostream fixIt(scratch); interleave( SynthesizedArgs, - [&](const std::pair &arg) { - forFixIt(fixIt, arg.second); + [&](const SynthesizedArg &arg) { + forFixIt(fixIt, arg.param); }, [&] { fixIt << ", "; }); @@ -4155,8 +4178,8 @@ bool MissingArgumentsFailure::diagnoseSingleMissingArgument() const { return false; const auto &argument = SynthesizedArgs.front(); - auto position = argument.first; - auto label = argument.second.getLabel(); + auto position = argument.paramIdx; + auto label = argument.param.getLabel(); Expr *fnExpr = nullptr; Expr *argExpr = nullptr; @@ -4171,7 +4194,7 @@ bool MissingArgumentsFailure::diagnoseSingleMissingArgument() const { } // Will the parameter accept a trailing closure? - Type paramType = resolveType(argument.second.getPlainType()); + Type paramType = resolveType(argument.param.getPlainType()); bool paramAcceptsTrailingClosure = paramType ->lookThroughAllOptionalTypes()->is(); @@ -4187,7 +4210,7 @@ bool MissingArgumentsFailure::diagnoseSingleMissingArgument() const { else if (position != 0) insertText << ", "; - forFixIt(insertText, argument.second); + forFixIt(insertText, argument.param); if (position == 0 && numArgs > 0 && (!firstTrailingClosure || position < *firstTrailingClosure)) @@ -5249,14 +5272,10 @@ bool MissingGenericArgumentsFailure::diagnoseAsError() { scopedParameters[base].push_back(GP); }); - // FIXME: this code should be generalized now that we can anchor the - // fixes on the TypeRepr with the missing generic arg. if (!isScoped) { - assert(getAnchor().is() || getAnchor().is()); - if (auto *expr = getAsExpr(getAnchor())) - return diagnoseForAnchor(expr, Parameters); - - return diagnoseForAnchor(getAnchor().get(), Parameters); + auto anchor = getAnchor(); + assert(anchor.is() || anchor.is()); + return diagnoseForAnchor(anchor, Parameters); } bool diagnosed = false; @@ -5266,7 +5285,7 @@ bool MissingGenericArgumentsFailure::diagnoseAsError() { } bool MissingGenericArgumentsFailure::diagnoseForAnchor( - Anchor anchor, ArrayRef params) const { + ASTNode anchor, ArrayRef params) const { bool diagnosed = false; for (auto *GP : params) diagnosed |= diagnoseParameter(anchor, GP); @@ -5300,11 +5319,9 @@ bool MissingGenericArgumentsFailure::diagnoseForAnchor( } bool MissingGenericArgumentsFailure::diagnoseParameter( - Anchor anchor, GenericTypeParamType *GP) const { + ASTNode anchor, GenericTypeParamType *GP) const { auto &solution = getSolution(); - - auto loc = anchor.is() ? anchor.get()->getLoc() - : anchor.get()->getLoc(); + auto loc = ::getLoc(anchor); auto *locator = getLocator(); // Type variables associated with missing generic parameters are @@ -5350,7 +5367,7 @@ bool MissingGenericArgumentsFailure::diagnoseParameter( } void MissingGenericArgumentsFailure::emitGenericSignatureNote( - Anchor anchor) const { + ASTNode anchor) const { auto &solution = getSolution(); auto *paramDC = getDeclContext(); @@ -5486,7 +5503,7 @@ bool MissingGenericArgumentsFailure::findArgumentLocations( return associator.allParamsAssigned(); } -SourceLoc SkipUnhandledConstructInFunctionBuilderFailure::getLoc() const { +SourceLoc SkipUnhandledConstructInResultBuilderFailure::getLoc() const { if (auto stmt = unhandled.dyn_cast()) return stmt->getStartLoc(); @@ -5504,11 +5521,11 @@ static bool hasMissingElseInChain(IfStmt *ifStmt) { return false; } -void SkipUnhandledConstructInFunctionBuilderFailure::diagnosePrimary( +void SkipUnhandledConstructInResultBuilderFailure::diagnosePrimary( bool asNote) { if (auto stmt = unhandled.dyn_cast()) { - emitDiagnostic(asNote ? diag::note_function_builder_control_flow - : diag::function_builder_control_flow, + emitDiagnostic(asNote ? diag::note_result_builder_control_flow + : diag::result_builder_control_flow, builder->getName()); // Emit custom notes to help the user introduce the appropriate 'build' @@ -5517,74 +5534,74 @@ void SkipUnhandledConstructInFunctionBuilderFailure::diagnosePrimary( std::string stubIndent; Type componentType; std::tie(buildInsertionLoc, stubIndent, componentType) = - determineFunctionBuilderBuildFixItInfo(builder); + determineResultBuilderBuildFixItInfo(builder); if (buildInsertionLoc.isInvalid()) { // Do nothing. } else if (isa(stmt) && hasMissingElseInChain(cast(stmt))) { auto diag = emitDiagnosticAt( - builder->getLoc(), diag::function_builder_missing_build_optional, + builder->getLoc(), diag::result_builder_missing_build_optional, builder->getDeclaredInterfaceType()); std::string fixItString; { llvm::raw_string_ostream out(fixItString); - printFunctionBuilderBuildFunction( - builder, componentType, FunctionBuilderBuildFunction::BuildOptional, + printResultBuilderBuildFunction( + builder, componentType, ResultBuilderBuildFunction::BuildOptional, stubIndent, out); } diag.fixItInsert(buildInsertionLoc, fixItString); } else if (isa(stmt) || isa(stmt)) { auto diag = emitDiagnosticAt( - builder->getLoc(), diag::function_builder_missing_build_either, + builder->getLoc(), diag::result_builder_missing_build_either, builder->getDeclaredInterfaceType()); std::string fixItString; { llvm::raw_string_ostream out(fixItString); - printFunctionBuilderBuildFunction( + printResultBuilderBuildFunction( builder, componentType, - FunctionBuilderBuildFunction::BuildEitherFirst, + ResultBuilderBuildFunction::BuildEitherFirst, stubIndent, out); out << '\n'; - printFunctionBuilderBuildFunction( + printResultBuilderBuildFunction( builder, componentType, - FunctionBuilderBuildFunction::BuildEitherSecond, + ResultBuilderBuildFunction::BuildEitherSecond, stubIndent, out); } diag.fixItInsert(buildInsertionLoc, fixItString); } else if (isa(stmt)) { auto diag = emitDiagnosticAt( - builder->getLoc(), diag::function_builder_missing_build_array, + builder->getLoc(), diag::result_builder_missing_build_array, builder->getDeclaredInterfaceType()); std::string fixItString; { llvm::raw_string_ostream out(fixItString); - printFunctionBuilderBuildFunction( - builder, componentType, FunctionBuilderBuildFunction::BuildArray, + printResultBuilderBuildFunction( + builder, componentType, ResultBuilderBuildFunction::BuildArray, stubIndent, out); } diag.fixItInsert(buildInsertionLoc, fixItString); } } else { - emitDiagnostic(asNote ? diag::note_function_builder_decl - : diag::function_builder_decl, + emitDiagnostic(asNote ? diag::note_result_builder_decl + : diag::result_builder_decl, builder->getName()); } } -bool SkipUnhandledConstructInFunctionBuilderFailure::diagnoseAsError() { +bool SkipUnhandledConstructInResultBuilderFailure::diagnoseAsError() { diagnosePrimary(/*asNote=*/false); emitDiagnosticAt(builder, diag::kind_declname_declared_here, builder->getDescriptiveKind(), builder->getName()); return true; } -bool SkipUnhandledConstructInFunctionBuilderFailure::diagnoseAsNote() { +bool SkipUnhandledConstructInResultBuilderFailure::diagnoseAsNote() { diagnosePrimary(/*asNote=*/true); return true; } @@ -6021,7 +6038,7 @@ bool ArgumentMismatchFailure::diagnoseMisplacedMissingArgument() const { auto anchor = getRawAnchor(); MissingArgumentsFailure failure( - solution, {std::make_pair(0, param)}, + solution, {SynthesizedArg{0, param}}, getConstraintLocator(anchor, ConstraintLocator::ApplyArgument)); return failure.diagnoseSingleMissingArgument(); @@ -6947,3 +6964,43 @@ bool InvalidEmptyKeyPathFailure::diagnoseAsError() { emitDiagnostic(diag::expr_swift_keypath_empty); return true; } + +bool MissingContextualTypeForNil::diagnoseAsError() { + auto *expr = castToExpr(getAnchor()); + + // If this is a standalone `nil` literal expression e.g. + // `_ = nil`, let's diagnose it here because solver can't + // attempt any types for it. + auto *parentExpr = findParentExpr(expr); + + while (parentExpr && isa(parentExpr)) + parentExpr = findParentExpr(parentExpr); + + // In cases like `_ = nil?` AST would have `nil` + // wrapped in `BindOptionalExpr`. + if (parentExpr && isa(parentExpr)) + parentExpr = findParentExpr(parentExpr); + + if (parentExpr) { + // `_ = nil as? ...` + if (isa(parentExpr)) { + emitDiagnostic(diag::conditional_cast_from_nil); + return true; + } + + // `_ = nil!` + if (isa(parentExpr)) { + emitDiagnostic(diag::cannot_force_unwrap_nil_literal); + return true; + } + + // `_ = nil?` + if (isa(parentExpr)) { + emitDiagnostic(diag::unresolved_nil_literal); + return true; + } + } + + emitDiagnostic(diag::unresolved_nil_literal); + return true; +} diff --git a/lib/Sema/CSDiagnostics.h b/lib/Sema/CSDiagnostics.h index 980a8a51b753b..06339142bc818 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -16,9 +16,7 @@ #ifndef SWIFT_SEMA_CSDIAGNOSTICS_H #define SWIFT_SEMA_CSDIAGNOSTICS_H -#include "Constraint.h" -#include "ConstraintSystem.h" -#include "OverloadChoice.h" +#include "TypeChecker.h" #include "swift/AST/ASTContext.h" #include "swift/AST/ASTNode.h" #include "swift/AST/Decl.h" @@ -28,6 +26,8 @@ #include "swift/AST/OperatorNameLookup.h" #include "swift/AST/Types.h" #include "swift/Basic/SourceLoc.h" +#include "swift/Sema/ConstraintSystem.h" +#include "swift/Sema/OverloadChoice.h" #include "llvm/ADT/ArrayRef.h" #include @@ -1271,13 +1271,11 @@ class ImplicitInitOnNonConstMetatypeFailure final }; class MissingArgumentsFailure final : public FailureDiagnostic { - using SynthesizedParam = std::pair; - - SmallVector SynthesizedArgs; + SmallVector SynthesizedArgs; public: MissingArgumentsFailure(const Solution &solution, - ArrayRef synthesizedArgs, + ArrayRef synthesizedArgs, ConstraintLocator *locator) : FailureDiagnostic(solution, locator), SynthesizedArgs(synthesizedArgs.begin(), synthesizedArgs.end()) { @@ -1698,8 +1696,6 @@ class InOutConversionFailure final : public ContextualFailure { /// _ = S() /// ``` class MissingGenericArgumentsFailure final : public FailureDiagnostic { - using Anchor = llvm::PointerUnion; - SmallVector Parameters; public: @@ -1722,13 +1718,13 @@ class MissingGenericArgumentsFailure final : public FailureDiagnostic { bool diagnoseAsError() override; - bool diagnoseForAnchor(Anchor anchor, + bool diagnoseForAnchor(ASTNode anchor, ArrayRef params) const; - bool diagnoseParameter(Anchor anchor, GenericTypeParamType *GP) const; + bool diagnoseParameter(ASTNode anchor, GenericTypeParamType *GP) const; private: - void emitGenericSignatureNote(Anchor anchor) const; + void emitGenericSignatureNote(ASTNode anchor) const; /// Retrieve representative locations for associated generic prameters. /// @@ -1737,7 +1733,7 @@ class MissingGenericArgumentsFailure final : public FailureDiagnostic { llvm::function_ref callback); }; -class SkipUnhandledConstructInFunctionBuilderFailure final +class SkipUnhandledConstructInResultBuilderFailure final : public FailureDiagnostic { public: using UnhandledNode = llvm::PointerUnion; @@ -1748,7 +1744,7 @@ class SkipUnhandledConstructInFunctionBuilderFailure final void diagnosePrimary(bool asNote); public: - SkipUnhandledConstructInFunctionBuilderFailure(const Solution &solution, + SkipUnhandledConstructInResultBuilderFailure(const Solution &solution, UnhandledNode unhandled, NominalTypeDecl *builder, ConstraintLocator *locator) @@ -2268,6 +2264,23 @@ class InvalidEmptyKeyPathFailure final : public FailureDiagnostic { bool diagnoseAsError() override; }; +/// Diagnose situations where there is no context to determine a +/// type of `nil` literal e.g. +/// +/// \code +/// let _ = nil +/// let _ = try nil +/// let _ = nil! +/// \endcode +class MissingContextualTypeForNil final : public FailureDiagnostic { +public: + MissingContextualTypeForNil(const Solution &solution, + ConstraintLocator *locator) + : FailureDiagnostic(solution, locator) {} + + bool diagnoseAsError() override; +}; + } // end namespace constraints } // end namespace swift diff --git a/lib/Sema/CSFix.cpp b/lib/Sema/CSFix.cpp index 9808f8d63e7c8..9f31bb5e96973 100644 --- a/lib/Sema/CSFix.cpp +++ b/lib/Sema/CSFix.cpp @@ -16,16 +16,16 @@ // //===----------------------------------------------------------------------===// -#include "CSFix.h" #include "CSDiagnostics.h" -#include "ConstraintLocator.h" -#include "ConstraintSystem.h" -#include "OverloadChoice.h" #include "swift/AST/Expr.h" #include "swift/AST/ParameterList.h" #include "swift/AST/Type.h" #include "swift/AST/Types.h" #include "swift/Basic/SourceManager.h" +#include "swift/Sema/ConstraintLocator.h" +#include "swift/Sema/ConstraintSystem.h" +#include "swift/Sema/CSFix.h" +#include "swift/Sema/OverloadChoice.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/raw_ostream.h" #include @@ -748,9 +748,9 @@ bool AddMissingArguments::diagnose(const Solution &solution, AddMissingArguments * AddMissingArguments::create(ConstraintSystem &cs, - ArrayRef synthesizedArgs, + ArrayRef synthesizedArgs, ConstraintLocator *locator) { - unsigned size = totalSizeToAlloc(synthesizedArgs.size()); + unsigned size = totalSizeToAlloc(synthesizedArgs.size()); void *mem = cs.getAllocator().Allocate(size, alignof(AddMissingArguments)); return new (mem) AddMissingArguments(cs, synthesizedArgs, locator); } @@ -1003,18 +1003,18 @@ DefaultGenericArgument::create(ConstraintSystem &cs, GenericTypeParamType *param return new (cs.getAllocator()) DefaultGenericArgument(cs, param, locator); } -SkipUnhandledConstructInFunctionBuilder * -SkipUnhandledConstructInFunctionBuilder::create(ConstraintSystem &cs, +SkipUnhandledConstructInResultBuilder * +SkipUnhandledConstructInResultBuilder::create(ConstraintSystem &cs, UnhandledNode unhandled, NominalTypeDecl *builder, ConstraintLocator *locator) { return new (cs.getAllocator()) - SkipUnhandledConstructInFunctionBuilder(cs, unhandled, builder, locator); + SkipUnhandledConstructInResultBuilder(cs, unhandled, builder, locator); } -bool SkipUnhandledConstructInFunctionBuilder::diagnose(const Solution &solution, +bool SkipUnhandledConstructInResultBuilder::diagnose(const Solution &solution, bool asNote) const { - SkipUnhandledConstructInFunctionBuilderFailure failure(solution, unhandled, + SkipUnhandledConstructInResultBuilderFailure failure(solution, unhandled, builder, getLocator()); return failure.diagnose(asNote); } @@ -1552,14 +1552,14 @@ AllowKeyPathWithoutComponents::create(ConstraintSystem &cs, return new (cs.getAllocator()) AllowKeyPathWithoutComponents(cs, locator); } -bool IgnoreInvalidFunctionBuilderBody::diagnose(const Solution &solution, +bool IgnoreInvalidResultBuilderBody::diagnose(const Solution &solution, bool asNote) const { switch (Phase) { // Handled below case ErrorInPhase::PreCheck: break; case ErrorInPhase::ConstraintGeneration: - return true; // Already diagnosed by `matchFunctionBuilder`. + return true; // Already diagnosed by `matchResultBuilder`. } auto *S = getAnchor().get(); @@ -1582,7 +1582,7 @@ bool IgnoreInvalidFunctionBuilderBody::diagnose(const Solution &solution, return std::make_pair(true, S); } - // Ignore patterns because function builder pre-check does so as well. + // Ignore patterns because result builder pre-check does so as well. std::pair walkToPatternPre(Pattern *P) override { return std::make_pair(false, P); } @@ -1598,8 +1598,20 @@ bool IgnoreInvalidFunctionBuilderBody::diagnose(const Solution &solution, return walker.diagnosed(); } -IgnoreInvalidFunctionBuilderBody *IgnoreInvalidFunctionBuilderBody::create( +IgnoreInvalidResultBuilderBody *IgnoreInvalidResultBuilderBody::create( ConstraintSystem &cs, ErrorInPhase phase, ConstraintLocator *locator) { return new (cs.getAllocator()) - IgnoreInvalidFunctionBuilderBody(cs, phase, locator); + IgnoreInvalidResultBuilderBody(cs, phase, locator); +} + +bool SpecifyContextualTypeForNil::diagnose(const Solution &solution, + bool asNote) const { + MissingContextualTypeForNil failure(solution, getLocator()); + return failure.diagnose(asNote); +} + +SpecifyContextualTypeForNil * +SpecifyContextualTypeForNil::create(ConstraintSystem &cs, + ConstraintLocator *locator) { + return new (cs.getAllocator()) SpecifyContextualTypeForNil(cs, locator); } diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 9e33d851b6b34..eed2ce496123a 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -13,9 +13,8 @@ // This file implements constraint generation for the type checker. // //===----------------------------------------------------------------------===// -#include "ConstraintGraph.h" -#include "ConstraintSystem.h" #include "TypeCheckType.h" +#include "TypeChecker.h" #include "swift/AST/ASTVisitor.h" #include "swift/AST/ASTWalker.h" #include "swift/AST/Expr.h" @@ -24,6 +23,8 @@ #include "swift/AST/PrettyStackTrace.h" #include "swift/AST/SubstitutionMap.h" #include "swift/AST/TypeCheckRequests.h" +#include "swift/Sema/ConstraintGraph.h" +#include "swift/Sema/ConstraintSystem.h" #include "swift/Sema/IDETypeChecking.h" #include "swift/Subsystems.h" #include "llvm/ADT/APInt.h" @@ -588,6 +589,13 @@ namespace { // Determine whether the given declaration is favored. auto isFavoredDecl = [&](ValueDecl *value, Type type) -> bool { + // We want to consider all options for calls that might contain the code + // completion location, as missing arguments after the completion + // location are valid (since it might be that they just haven't been + // written yet). + if (CS.isForCodeCompletion()) + return false; + if (!type->is()) return false; @@ -990,7 +998,7 @@ namespace { : CS(CS), CurDC(DC ? DC : CS.DC), CurrPhase(CS.getPhase()) { // Although constraint system is initialized in `constraint // generation` phase, we have to set it here manually because e.g. - // function builders could generate constraints for its body + // result builders could generate constraints for its body // in the middle of the solving. CS.setPhase(ConstraintSystemPhase::ConstraintGeneration); } @@ -1002,8 +1010,21 @@ namespace { ConstraintSystem &getConstraintSystem() const { return CS; } virtual Type visitErrorExpr(ErrorExpr *E) { - // FIXME: Can we do anything with error expressions at this point? - return nullptr; + if (!CS.isForCodeCompletion()) + return nullptr; + + // For code completion, treat error expressions that don't contain + // the completion location itself as holes. If an ErrorExpr contains the + // code completion location, a fallback typecheck is called on the + // ErrorExpr's OriginalExpr (valid sub-expression) if it had one, + // independent of the wider expression containing the ErrorExpr, so + // there's no point attempting to produce a solution for it. + SourceRange range = E->getSourceRange(); + if (range.isInvalid() || + CS.getASTContext().SourceMgr.rangeContainsCodeCompletionLoc(range)) + return nullptr; + + return HoleType::get(CS.getASTContext(), E); } virtual Type visitCodeCompletionExpr(CodeCompletionExpr *E) { @@ -1015,80 +1036,13 @@ namespace { } Type visitNilLiteralExpr(NilLiteralExpr *expr) { - auto &DE = CS.getASTContext().Diags; - // If this is a standalone `nil` literal expression e.g. - // `_ = nil`, let's diagnose it here because solver can't - // attempt any types for it. - auto *parentExpr = CS.getParentExpr(expr); - bool hasContextualType = bool(CS.getContextualType(expr)); - - while (parentExpr) { - if (!isa(parentExpr)) - break; - - // If there is a parent, use it, otherwise we need - // to check whether the last parent node in the chain - // had a contextual type associated with it because - // in situations like: - // - // \code - // func foo() -> Int? { - // return (nil) - // } - // \endcode - // - // parentheses around `nil` are significant. - if (auto *nextParent = CS.getParentExpr(parentExpr)) { - parentExpr = nextParent; - } else { - hasContextualType |= bool(CS.getContextualType(parentExpr)); - // Since current expression is an identity expr - // and there are no more parents, let's pretend - // that `nil` don't have a parent since parens - // are not semantically significant for further checks. - parentExpr = nullptr; - } - } - - // In cases like `_ = nil?` AST would have `nil` - // wrapped in `BindOptionalExpr`. - if (parentExpr && isa(parentExpr)) - parentExpr = CS.getParentExpr(parentExpr); - - if (parentExpr) { - // `_ = nil as? ...` - if (isa(parentExpr)) { - DE.diagnose(expr->getLoc(), diag::conditional_cast_from_nil); - return Type(); - } - - // `_ = nil!` - if (isa(parentExpr)) { - DE.diagnose(expr->getLoc(), diag::cannot_force_unwrap_nil_literal); - return Type(); - } - - // `_ = nil?` - if (isa(parentExpr)) { - DE.diagnose(expr->getLoc(), diag::unresolved_nil_literal); - return Type(); - } + auto literalTy = visitLiteralExpr(expr); + // Allow `nil` to be a hole so we can diagnose it via a fix + // if it turns out that there is no contextual information. + if (auto *typeVar = literalTy->getAs()) + CS.recordPotentialHole(typeVar); - // `_ = nil` - if (auto *assignment = dyn_cast(parentExpr)) { - if (isa(assignment->getDest())) { - DE.diagnose(expr->getLoc(), diag::unresolved_nil_literal); - return Type(); - } - } - } - - if (!parentExpr && !hasContextualType) { - DE.diagnose(expr->getLoc(), diag::unresolved_nil_literal); - return Type(); - } - - return visitLiteralExpr(expr); + return literalTy; } Type visitFloatLiteralExpr(FloatLiteralExpr *expr) { @@ -1363,7 +1317,7 @@ namespace { type = CS.openUnboundGenericTypes(type, locator); } else if (CS.hasType(E)) { // If there's a type already set into the constraint system, honor it. - // FIXME: This supports the function builder transform, which sneakily + // FIXME: This supports the result builder transform, which sneakily // stashes a type in the constraint system through a TypeExpr in order // to pass it down to the rest of CSGen. This is a terribly // unprincipled thing to do. @@ -1492,9 +1446,6 @@ namespace { // The result of the last element of the chain must be convertible to the // whole chain, and the type of the whole chain must be equal to the base. - CS.addConstraint( - ConstraintKind::Conversion, memberTy, chainBaseTy, - CS.getConstraintLocator(tail, ConstraintLocator::RValueAdjustment)); CS.addConstraint(ConstraintKind::Conversion, memberTy, chainResultTy, locator); CS.addConstraint(ConstraintKind::Equal, chainBaseTy, chainResultTy, @@ -2552,6 +2503,24 @@ namespace { } } + // FIXME: We can see UnresolvedDeclRefExprs here because we have + // not yet run preCheckExpression() on the entire closure body + // yet. + // + // We could consider pre-checking more eagerly. + if (auto *declRef = dyn_cast(expr)) { + auto name = declRef->getName(); + auto loc = declRef->getLoc(); + if (name.isSimpleName() && loc.isValid()) { + auto *varDecl = dyn_cast_or_null( + ASTScope::lookupSingleLocalDecl(cs.DC->getParentSourceFile(), + name.getFullName(), loc)); + if (varDecl) + if (auto varType = cs.getTypeIfAvailable(varDecl)) + varType->getTypeVariables(varRefs); + } + } + return { true, expr }; } } collectVarRefs(CS); @@ -2622,9 +2591,9 @@ namespace { Type visitDynamicTypeExpr(DynamicTypeExpr *expr) { auto tv = CS.createTypeVariable(CS.getConstraintLocator(expr), TVO_CanBindToNoEscape); - CS.addConstraint(ConstraintKind::DynamicTypeOf, tv, - CS.getType(expr->getBase()), - CS.getConstraintLocator(expr, ConstraintLocator::RValueAdjustment)); + CS.addConstraint( + ConstraintKind::DynamicTypeOf, tv, CS.getType(expr->getBase()), + CS.getConstraintLocator(expr, ConstraintLocator::DynamicType)); return tv; } @@ -3031,11 +3000,19 @@ namespace { TVO_PrefersSubtypeBinding | TVO_CanBindToLValue | TVO_CanBindToNoEscape); - + + auto *valueExpr = expr->getSubExpr(); + // It's invalid to force unwrap `nil` literal e.g. `_ = nil!` or + // `_ = (try nil)!` and similar constructs. + if (auto *nilLiteral = dyn_cast( + valueExpr->getSemanticsProvidingExpr())) { + CS.recordFix(SpecifyContextualTypeForNil::create( + CS, CS.getConstraintLocator(nilLiteral))); + } + // The result is the object type of the optional subexpression. - CS.addConstraint(ConstraintKind::OptionalObject, - CS.getType(expr->getSubExpr()), objectTy, - locator); + CS.addConstraint(ConstraintKind::OptionalObject, CS.getType(valueExpr), + objectTy, locator); return objectTy; } @@ -3488,6 +3465,8 @@ namespace { // Generate constraints for each of the entries in the capture list. if (auto captureList = dyn_cast(expr)) { + TypeChecker::diagnoseDuplicateCaptureVars(captureList); + auto &CS = CG.getConstraintSystem(); for (const auto &capture : captureList->getCaptureList()) { SolutionApplicationTarget target(capture.Init); @@ -3825,9 +3804,8 @@ bool ConstraintSystem::generateConstraints( // Substitute type variables in for unresolved types. if (allowFreeTypeVariables == FreeTypeVariableBinding::UnresolvedType) { - bool isForSingleExprFunction = (ctp == CTP_ReturnSingleExpr); - auto *convertTypeLocator = getConstraintLocator( - expr, LocatorPathElt::ContextualType(isForSingleExprFunction)); + auto *convertTypeLocator = + getConstraintLocator(expr, LocatorPathElt::ContextualType()); convertType = convertType.transform([&](Type type) -> Type { if (type->is()) { diff --git a/lib/Sema/CSRanking.cpp b/lib/Sema/CSRanking.cpp index cc57a75def2c9..2e9ee713a6a6e 100644 --- a/lib/Sema/CSRanking.cpp +++ b/lib/Sema/CSRanking.cpp @@ -14,11 +14,12 @@ // constraint-based type checker. // //===----------------------------------------------------------------------===// -#include "ConstraintSystem.h" +#include "TypeChecker.h" #include "swift/AST/GenericSignature.h" #include "swift/AST/ParameterList.h" #include "swift/AST/ProtocolConformance.h" #include "swift/AST/TypeCheckRequests.h" +#include "swift/Sema/ConstraintSystem.h" #include "llvm/ADT/Statistic.h" #include "llvm/Support/Compiler.h" diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 104964b5ead2a..e61f78071d6f4 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -16,8 +16,6 @@ //===----------------------------------------------------------------------===// #include "CSDiagnostics.h" -#include "CSFix.h" -#include "ConstraintSystem.h" #include "swift/AST/ExistentialLayout.h" #include "swift/AST/GenericEnvironment.h" #include "swift/AST/GenericSignature.h" @@ -28,6 +26,8 @@ #include "swift/AST/ProtocolConformance.h" #include "swift/Basic/StringExtras.h" #include "swift/ClangImporter/ClangModule.h" +#include "swift/Sema/ConstraintSystem.h" +#include "swift/Sema/CSFix.h" #include "swift/Sema/IDETypeChecking.h" #include "llvm/ADT/SetVector.h" #include "llvm/Support/Compiler.h" @@ -40,7 +40,8 @@ MatchCallArgumentListener::~MatchCallArgumentListener() { } bool MatchCallArgumentListener::extraArgument(unsigned argIdx) { return true; } Optional -MatchCallArgumentListener::missingArgument(unsigned paramIdx) { +MatchCallArgumentListener::missingArgument(unsigned paramIdx, + unsigned argInsertIdx) { return None; } @@ -701,11 +702,14 @@ static bool matchCallArgumentsImpl( } // If we have any unfulfilled parameters, check them now. + Optional prevArgIdx; if (haveUnfulfilledParams) { for (auto paramIdx : indices(params)) { // If we have a binding for this parameter, we're done. - if (!parameterBindings[paramIdx].empty()) + if (!parameterBindings[paramIdx].empty()) { + prevArgIdx = parameterBindings[paramIdx].back(); continue; + } const auto ¶m = params[paramIdx]; @@ -717,7 +721,8 @@ static bool matchCallArgumentsImpl( if (paramInfo.hasDefaultArgument(paramIdx)) continue; - if (auto newArgIdx = listener.missingArgument(paramIdx)) { + unsigned argInsertIdx = prevArgIdx ? *prevArgIdx + 1 : 0; + if (auto newArgIdx = listener.missingArgument(paramIdx, argInsertIdx)) { parameterBindings[paramIdx].push_back(*newArgIdx); continue; } @@ -980,14 +985,47 @@ constraints::matchCallArguments( }; } +static Optional +getCompletionArgIndex(ASTNode anchor, SourceManager &SM) { + Expr *arg = nullptr; + if (auto *CE = getAsExpr(anchor)) + arg = CE->getArg(); + if (auto *SE = getAsExpr(anchor)) + arg = SE->getIndex(); + if (auto *OLE = getAsExpr(anchor)) + arg = OLE->getArg(); + + if (!arg) + return None; + + auto containsCompletion = [&](Expr *elem) { + if (!elem) + return false; + SourceRange range = elem->getSourceRange(); + return range.isValid() && SM.rangeContainsCodeCompletionLoc(range); + }; + + if (auto *TE = dyn_cast(arg)) { + auto elems = TE->getElements(); + auto idx = llvm::find_if(elems, containsCompletion); + if (idx != elems.end()) + return std::distance(elems.begin(), idx); + } else if (auto *PE = dyn_cast(arg)) { + if (containsCompletion(PE->getSubExpr())) + return 0; + } + return None; +} + class ArgumentFailureTracker : public MatchCallArgumentListener { ConstraintSystem &CS; SmallVectorImpl &Arguments; ArrayRef Parameters; ConstraintLocatorBuilder Locator; - SmallVector, 4> MissingArguments; + SmallVector MissingArguments; SmallVector, 4> ExtraArguments; + Optional CompletionArgIdx; public: ArgumentFailureTracker(ConstraintSystem &cs, @@ -1006,7 +1044,8 @@ class ArgumentFailureTracker : public MatchCallArgumentListener { } } - Optional missingArgument(unsigned paramIdx) override { + Optional missingArgument(unsigned paramIdx, + unsigned argInsertIdx) override { if (!CS.shouldAttemptFixes()) return None; @@ -1023,10 +1062,22 @@ class ArgumentFailureTracker : public MatchCallArgumentListener { TVO_CanBindToNoEscape | TVO_CanBindToHole); auto synthesizedArg = param.withType(argType); - - MissingArguments.push_back(std::make_pair(paramIdx, synthesizedArg)); Arguments.push_back(synthesizedArg); + if (CS.isForCodeCompletion()) { + // When solving for code completion, if any argument contains the + // completion location, later arguments shouldn't be considered missing + // (causing the solution to have a worse score) as the user just hasn't + // written them yet. Early exit to avoid recording them in this case. + SourceManager &SM = CS.getASTContext().SourceMgr; + if (!CompletionArgIdx) + CompletionArgIdx = getCompletionArgIndex(Locator.getAnchor(), SM); + if (CompletionArgIdx && *CompletionArgIdx < argInsertIdx) + return newArgIdx; + } + + MissingArguments.push_back(SynthesizedArg{paramIdx, synthesizedArg}); + return newArgIdx; } @@ -1190,12 +1241,11 @@ ConstraintSystem::TypeMatchResult constraints::matchCallArguments( argsWithLabels.pop_back(); // Let's make sure that labels associated with tuple elements // line up with what is expected by argument list. - SmallVector, 4> - synthesizedArgs; + SmallVector synthesizedArgs; for (unsigned i = 0, n = argTuple->getNumElements(); i != n; ++i) { const auto &elt = argTuple->getElement(i); AnyFunctionType::Param argument(elt.getType(), elt.getName()); - synthesizedArgs.push_back(std::make_pair(i, argument)); + synthesizedArgs.push_back(SynthesizedArg{i, argument}); argsWithLabels.push_back(argument); } @@ -1771,15 +1821,27 @@ static bool fixMissingArguments(ConstraintSystem &cs, ASTNode anchor, cs.createTypeVariable(argLoc, TVO_CanBindToNoEscape))); } - SmallVector, 4> synthesizedArgs; + SmallVector synthesizedArgs; synthesizedArgs.reserve(numMissing); for (unsigned i = args.size() - numMissing, n = args.size(); i != n; ++i) { - synthesizedArgs.push_back(std::make_pair(i, args[i])); + synthesizedArgs.push_back(SynthesizedArg{i, args[i]}); + } + + // Treat missing anonymous arguments as valid in closures containing the + // code completion location, since they may have just not been written yet. + if (cs.isForCodeCompletion()) { + if (auto *closure = getAsExpr(anchor)) { + SourceManager &SM = closure->getASTContext().SourceMgr; + SourceRange range = closure->getSourceRange(); + if (range.isValid() && SM.rangeContainsCodeCompletionLoc(range) && + (closure->hasAnonymousClosureVars() || + (args.empty() && closure->getInLoc().isInvalid()))) + return false; + } } auto *fix = AddMissingArguments::create(cs, synthesizedArgs, cs.getConstraintLocator(locator)); - if (cs.recordFix(fix)) return true; @@ -3966,7 +4028,7 @@ bool ConstraintSystem::repairFailures( // to diagnose this as a missing argument which can't be ignored. if (arg != getTypeVariables().end()) { conversionsOrFixes.push_back(AddMissingArguments::create( - *this, {std::make_pair(0, AnyFunctionType::Param(*arg))}, + *this, {SynthesizedArg{0, AnyFunctionType::Param(*arg)}}, getConstraintLocator(anchor, path))); break; } @@ -4309,9 +4371,6 @@ bool ConstraintSystem::repairFailures( break; } - case ConstraintLocator::RValueAdjustment: - return true; - case ConstraintLocator::UnresolvedMemberChainResult: { if (repairViaOptionalUnwrap(*this, lhs, rhs, matchKind, conversionsOrFixes, locator)) @@ -4432,7 +4491,7 @@ bool ConstraintSystem::repairFailures( getConstraintLocator(anchor, path)); } - case ConstraintLocator::FunctionBuilderBodyResult: { + case ConstraintLocator::ResultBuilderBodyResult: { // If result type of the body couldn't be determined // there is going to be other fix available to diagnose // the underlying issue. @@ -5256,9 +5315,15 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, } // Single expression function with implicit `return`. - if (elt->isResultOfSingleExprFunction()) { - increaseScore(SK_FunctionConversion); - return getTypeMatchSuccess(); + if (elt->is()) { + auto anchor = locator.getAnchor(); + auto contextualInfo = getContextualTypeInfo(anchor); + assert(contextualInfo && + "Found contextual type locator without additional information"); + if (contextualInfo->purpose == CTP_ReturnSingleExpr) { + increaseScore(SK_FunctionConversion); + return getTypeMatchSuccess(); + } } } } @@ -5560,7 +5625,8 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint( // This conformance may be conditional, in which case we need to consider // those requirements as constraints too. - if (conformance.isConcrete()) { + if (conformance.isConcrete() && + !isa(conformance.getConcrete())) { unsigned index = 0; for (const auto &req : conformance.getConditionalRequirements()) { addConstraint(req, @@ -5893,6 +5959,12 @@ ConstraintSystem::simplifyOptionalObjectConstraint( } + if (optTy->isHole()) { + if (auto *typeVar = second->getAs()) + recordPotentialHole(typeVar); + return SolutionKind::Solved; + } + Type objectTy = optTy->getOptionalObjectType(); // If the base type is not optional, let's attempt a fix (if possible) // and assume that `!` is just not there. @@ -7520,7 +7592,7 @@ ConstraintSystem::simplifyOneWayConstraint( secondSimplified, first, ConstraintKind::BindParam, flags, locator); } -static Type getOpenedFunctionBuilderTypeFor(ConstraintSystem &cs, +static Type getOpenedResultBuilderTypeFor(ConstraintSystem &cs, ConstraintLocatorBuilder locator) { auto lastElt = locator.last(); if (!lastElt) @@ -7546,7 +7618,7 @@ static Type getOpenedFunctionBuilderTypeFor(ConstraintSystem &cs, return Type(); auto *PD = getParameterAt(choice, argToParamElt->getParamIdx()); - auto builderType = PD->getFunctionBuilderType(); + auto builderType = PD->getResultBuilderType(); if (!builderType) return Type(); @@ -7583,15 +7655,15 @@ bool ConstraintSystem::resolveClosure(TypeVariableType *typeVar, auto *closure = castToExpr(closureLocator->getAnchor()); auto *inferredClosureType = getClosureType(closure); - // Determine whether a function builder will be applied. - auto functionBuilderType = getOpenedFunctionBuilderTypeFor(*this, locator); + // Determine whether a result builder will be applied. + auto resultBuilderType = getOpenedResultBuilderTypeFor(*this, locator); // Determine whether to introduce one-way constraints between the parameter's // type as seen in the body of the closure and the external parameter // type. bool oneWayConstraints = getASTContext().TypeCheckerOpts.EnableOneWayClosureParameters || - functionBuilderType; + resultBuilderType; auto *paramList = closure->getParameters(); SmallVector parameters; @@ -7656,10 +7728,10 @@ bool ConstraintSystem::resolveClosure(TypeVariableType *typeVar, inferredClosureType->getExtInfo()); assignFixedType(typeVar, closureType, closureLocator); - // If there is a function builder to apply, do so now. - if (functionBuilderType) { - if (auto result = matchFunctionBuilder( - closure, functionBuilderType, closureType->getResult(), + // If there is a result builder to apply, do so now. + if (resultBuilderType) { + if (auto result = matchResultBuilder( + closure, resultBuilderType, closureType->getResult(), ConstraintKind::Conversion, locator)) { return result->isSuccess(); } @@ -8790,14 +8862,6 @@ ConstraintSystem::simplifyApplicableFnConstraint( return false; }; - // If the types are obviously equivalent, we're done. This optimization - // is not valid for operators though, where an inout parameter does not - // have an explicit inout argument. - if (type1.getPointer() == desugar2) { - if (!isOperator || !hasInOut()) - return SolutionKind::Solved; - } - // Local function to form an unsolved result. auto formUnsolved = [&] { if (flags.contains(TMF_GenerateConstraints)) { @@ -8826,6 +8890,19 @@ ConstraintSystem::simplifyApplicableFnConstraint( ConstraintLocatorBuilder outerLocator = getConstraintLocator(anchor, parts, locator.getSummaryFlags()); + // If the types are obviously equivalent, we're done. This optimization + // is not valid for operators though, where an inout parameter does not + // have an explicit inout argument. + if (type1.getPointer() == desugar2) { + if (!isOperator || !hasInOut()) { + recordTrailingClosureMatch( + getConstraintLocator( + outerLocator.withPathElement(ConstraintLocator::ApplyArgument)), + TrailingClosureMatching::Forward); + return SolutionKind::Solved; + } + } + // Handle applications of types with `callAsFunction` methods. // Do this before stripping optional types below, when `shouldAttemptFixes()` // is true. @@ -9901,16 +9978,11 @@ bool ConstraintSystem::recordFix(ConstraintFix *fix, unsigned impact) { return false; } -void ConstraintSystem::recordPotentialHole(TypeVariableType *typeVar) { - assert(typeVar); - typeVar->getImpl().enableCanBindToHole(getSavedBindings()); -} - -void ConstraintSystem::recordPotentialHole(FunctionType *fnType) { - assert(fnType); - Type(fnType).visit([&](Type type) { +void ConstraintSystem::recordPotentialHole(Type type) { + assert(type->hasTypeVariable()); + type.visit([&](Type type) { if (auto *typeVar = type->getAs()) - recordPotentialHole(typeVar); + typeVar->getImpl().enableCanBindToHole(getSavedBindings()); }); } @@ -10012,7 +10084,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint( newTupleTypes.push_back(smallerElt); } else { if (largerElt.getType()->isTypeVariableOrMember()) - recordPotentialHole(largerElt.getType()->getAs()); + recordPotentialHole(largerElt.getType()); } } auto matchingType = @@ -10057,7 +10129,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint( case FixKind::RemoveAddressOf: case FixKind::AddMissingArguments: case FixKind::MoveOutOfOrderArgument: - case FixKind::SkipUnhandledConstructInFunctionBuilder: + case FixKind::SkipUnhandledConstructInResultBuilder: case FixKind::UsePropertyWrapper: case FixKind::UseWrappedValue: case FixKind::ExpandArrayIntoVarargs: @@ -10072,7 +10144,8 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint( case FixKind::SpecifyKeyPathRootType: case FixKind::SpecifyLabelToAssociateTrailingClosure: case FixKind::AllowKeyPathWithoutComponents: - case FixKind::IgnoreInvalidFunctionBuilderBody: { + case FixKind::IgnoreInvalidResultBuilderBody: + case FixKind::SpecifyContextualTypeForNil: { return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved; } @@ -10292,16 +10365,16 @@ ConstraintSystem::addArgumentConversionConstraintImpl( // If we have an unresolved closure argument, form an unsolved argument // conversion constraint, making sure to reference the type variables for - // a function builder if applicable. This ensures we properly connect the - // closure type variable with any type variables in the function builder, as + // a result builder if applicable. This ensures we properly connect the + // closure type variable with any type variables in the result builder, as // such type variables will be accessible within the body of the closure when // we open it. first = getFixedTypeRecursive(first, /*rvalue*/ false); if (auto *argTypeVar = first->getAs()) { if (argTypeVar->getImpl().isClosureType()) { - // Extract any type variables present in the parameter's function builder. + // Extract any type variables present in the parameter's result builder. SmallVector typeVars; - if (auto builderTy = getOpenedFunctionBuilderTypeFor(*this, locator)) + if (auto builderTy = getOpenedResultBuilderTypeFor(*this, locator)) builderTy->getTypeVariables(typeVars); auto *loc = getConstraintLocator(locator); @@ -10534,9 +10607,8 @@ void ConstraintSystem::addContextualConversionConstraint( } // Add the constraint. - bool isForSingleExprFunction = (purpose == CTP_ReturnSingleExpr); - auto *convertTypeLocator = getConstraintLocator( - expr, LocatorPathElt::ContextualType(isForSingleExprFunction)); + auto *convertTypeLocator = + getConstraintLocator(expr, LocatorPathElt::ContextualType()); addConstraint(constraintKind, getType(expr), conversionType, convertTypeLocator, /*isFavored*/ true); } diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index 81c86b550652a..eb75234cc4925 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -14,12 +14,13 @@ // //===----------------------------------------------------------------------===// #include "CSStep.h" -#include "ConstraintGraph.h" -#include "ConstraintSystem.h" -#include "SolutionResult.h" #include "TypeCheckType.h" +#include "TypeChecker.h" #include "swift/AST/ParameterList.h" #include "swift/AST/TypeWalker.h" +#include "swift/Sema/ConstraintGraph.h" +#include "swift/Sema/ConstraintSystem.h" +#include "swift/Sema/SolutionResult.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallSet.h" @@ -44,13 +45,13 @@ STATISTIC(TotalNumTypeVariables, "# of type variables created"); #define CS_STATISTIC(Name, Description) \ STATISTIC(Overall##Name, Description); -#include "ConstraintSolverStats.def" +#include "swift/Sema/ConstraintSolverStats.def" #undef DEBUG_TYPE #define DEBUG_TYPE "Constraint solver largest system" #define CS_STATISTIC(Name, Description) \ STATISTIC(Largest##Name, Description); -#include "ConstraintSolverStats.def" +#include "swift/Sema/ConstraintSolverStats.def" STATISTIC(LargestSolutionAttemptNumber, "# of the largest solution attempt"); TypeVariableType *ConstraintSystem::createTypeVariable( @@ -186,8 +187,8 @@ Solution ConstraintSystem::finalize() { for (auto &e : CheckedConformances) solution.Conformances.push_back({e.first, e.second}); - for (const auto &transformed : functionBuilderTransformed) { - solution.functionBuilderTransformed.insert(transformed); + for (const auto &transformed : resultBuilderTransformed) { + solution.resultBuilderTransformed.insert(transformed); } return solution; @@ -275,8 +276,8 @@ void ConstraintSystem::applySolution(const Solution &solution) { for (auto &conformance : solution.Conformances) CheckedConformances.push_back(conformance); - for (const auto &transformed : solution.functionBuilderTransformed) { - functionBuilderTransformed.push_back(transformed); + for (const auto &transformed : solution.resultBuilderTransformed) { + resultBuilderTransformed.push_back(transformed); } // Register any fixes produced along this path. @@ -444,7 +445,7 @@ ConstraintSystem::SolverState::~SolverState() { // Write our local statistics back to the overall statistics. #define CS_STATISTIC(Name, Description) JOIN2(Overall,Name) += Name; - #include "ConstraintSolverStats.def" + #include "swift/Sema/ConstraintSolverStats.def" #if LLVM_ENABLE_STATS // Update the "largest" statistics if this system is larger than the @@ -456,7 +457,7 @@ ConstraintSystem::SolverState::~SolverState() { #define CS_STATISTIC(Name, Description) \ JOIN2(Largest,Name) = Name-1; \ ++JOIN2(Largest,Name); - #include "ConstraintSolverStats.def" + #include "swift/Sema/ConstraintSolverStats.def" } #endif } @@ -478,7 +479,7 @@ ConstraintSystem::SolverScope::SolverScope(ConstraintSystem &cs) numCheckedConformances = cs.CheckedConformances.size(); numDisabledConstraints = cs.solverState->getNumDisabledConstraints(); numFavoredConstraints = cs.solverState->getNumFavoredConstraints(); - numFunctionBuilderTransformed = cs.functionBuilderTransformed.size(); + numResultBuilderTransformed = cs.resultBuilderTransformed.size(); numResolvedOverloads = cs.ResolvedOverloads.size(); numInferredClosureTypes = cs.ClosureTypes.size(); numContextualTypes = cs.contextualTypes.size(); @@ -557,9 +558,9 @@ ConstraintSystem::SolverScope::~SolverScope() { truncate(cs.CheckedConformances, numCheckedConformances); /// Remove any builder transformed closures. - truncate(cs.functionBuilderTransformed, numFunctionBuilderTransformed); + truncate(cs.resultBuilderTransformed, numResultBuilderTransformed); - // Remove any inferred closure types (e.g. used in function builder body). + // Remove any inferred closure types (e.g. used in result builder body). truncate(cs.ClosureTypes, numInferredClosureTypes); // Remove any contextual types. @@ -1928,30 +1929,6 @@ Constraint *ConstraintSystem::getUnboundBindOverloadDisjunction( return result->first; } -// Find a disjunction associated with an ApplicableFunction constraint -// where we have some information about all of the types of in the -// function application (even if we only know something about what the -// types conform to and not actually a concrete type). -Constraint *ConstraintSystem::selectApplyDisjunction() { - for (auto &constraint : InactiveConstraints) { - if (constraint.getKind() != ConstraintKind::ApplicableFunction) - continue; - - auto *applicable = &constraint; - if (haveTypeInformationForAllArguments( - applicable->getFirstType()->castTo())) { - auto *tyvar = applicable->getSecondType()->castTo(); - - // If we have created the disjunction for this apply, find it. - auto *disjunction = getUnboundBindOverloadDisjunction(tyvar); - if (disjunction) - return disjunction; - } - } - - return nullptr; -} - static bool isOperatorBindOverload(Constraint *bindOverload) { if (bindOverload->getKind() != ConstraintKind::BindOverload) return false; @@ -1964,165 +1941,6 @@ static bool isOperatorBindOverload(Constraint *bindOverload) { return funcDecl && funcDecl->getOperatorDecl(); } -// Given a bind overload constraint for an operator, return the -// protocol designated as the first place to look for overloads of the -// operator. -static ArrayRef -getOperatorDesignatedNominalTypes(Constraint *bindOverload) { - auto choice = bindOverload->getOverloadChoice(); - auto *funcDecl = cast(choice.getDecl()); - auto *operatorDecl = funcDecl->getOperatorDecl(); - return operatorDecl->getDesignatedNominalTypes(); -} - -void ConstraintSystem::sortDesignatedTypes( - SmallVectorImpl &nominalTypes, - Constraint *bindOverload) { - auto *tyvar = bindOverload->getFirstType()->castTo(); - auto applicableFns = getConstraintGraph().gatherConstraints( - tyvar, ConstraintGraph::GatheringKind::EquivalenceClass, - [](Constraint *match) { - return match->getKind() == ConstraintKind::ApplicableFunction; - }); - - // FIXME: This is not true when we run the constraint optimizer. - // assert(applicableFns.size() <= 1); - - // We have a disjunction for an operator but no application of it, - // so it's being passed as an argument. - if (applicableFns.size() == 0) - return; - - // FIXME: We have more than one applicable per disjunction as a - // result of merging disjunction type variables. We may want - // to rip that out at some point. - Constraint *foundApplicable = nullptr; - SmallVector, 2> argumentTypes; - for (auto *applicableFn : applicableFns) { - argumentTypes.clear(); - auto *fnTy = applicableFn->getFirstType()->castTo(); - ArgumentInfoCollector argInfo(*this, fnTy); - // Stop if we hit anything with concrete types or conformances to - // literals. - if (!argInfo.getTypes().empty() || !argInfo.getLiteralProtocols().empty()) { - foundApplicable = applicableFn; - break; - } - } - - if (!foundApplicable) - return; - - // FIXME: It would be good to avoid this redundancy. - auto *fnTy = foundApplicable->getFirstType()->castTo(); - ArgumentInfoCollector argInfo(*this, fnTy); - - size_t nextType = 0; - for (auto argType : argInfo.getTypes()) { - auto *nominal = argType->getAnyNominal(); - for (size_t i = nextType; i < nominalTypes.size(); ++i) { - if (nominal == nominalTypes[i]) { - std::swap(nominalTypes[nextType], nominalTypes[i]); - ++nextType; - break; - } else if (auto *protoDecl = dyn_cast(nominalTypes[i])) { - if (TypeChecker::conformsToProtocol(argType, protoDecl, DC)) { - std::swap(nominalTypes[nextType], nominalTypes[i]); - ++nextType; - break; - } - } - } - } - - if (nextType + 1 >= nominalTypes.size()) - return; - - for (auto *protocol : argInfo.getLiteralProtocols()) { - auto defaultType = TypeChecker::getDefaultType(protocol, DC); - // ExpressibleByNilLiteral does not have a default type. - if (!defaultType) - continue; - auto *nominal = defaultType->getAnyNominal(); - for (size_t i = nextType + 1; i < nominalTypes.size(); ++i) { - if (nominal == nominalTypes[i]) { - std::swap(nominalTypes[nextType], nominalTypes[i]); - ++nextType; - break; - } - } - } -} - -void ConstraintSystem::partitionForDesignatedTypes( - ArrayRef Choices, ConstraintMatchLoop forEachChoice, - PartitionAppendCallback appendPartition) { - - auto types = getOperatorDesignatedNominalTypes(Choices[0]); - if (types.empty()) - return; - - SmallVector designatedNominalTypes(types.begin(), - types.end()); - - if (designatedNominalTypes.size() > 1) - sortDesignatedTypes(designatedNominalTypes, Choices[0]); - - SmallVector, 4> definedInDesignatedType; - SmallVector, 4> definedInExtensionOfDesignatedType; - - auto examineConstraint = - [&](unsigned constraintIndex, Constraint *constraint) -> bool { - auto *decl = constraint->getOverloadChoice().getDecl(); - auto *funcDecl = cast(decl); - - auto *parentDC = funcDecl->getParent(); - auto *parentDecl = parentDC->getSelfNominalTypeDecl(); - - // Skip anything not defined in a nominal type. - if (!parentDecl) - return false; - - for (auto designatedTypeIndex : indices(designatedNominalTypes)) { - auto *designatedNominal = - designatedNominalTypes[designatedTypeIndex]; - - if (parentDecl != designatedNominal) - continue; - - auto &constraints = - isa(parentDC) - ? definedInExtensionOfDesignatedType[designatedTypeIndex] - : definedInDesignatedType[designatedTypeIndex]; - - constraints.push_back(constraintIndex); - return true; - } - - return false; - }; - - definedInDesignatedType.resize(designatedNominalTypes.size()); - definedInExtensionOfDesignatedType.resize(designatedNominalTypes.size()); - - forEachChoice(Choices, examineConstraint); - - // Now collect the overload choices that are defined within the type - // that was designated in the operator declaration. - // Add partitions for each of the overloads we found in types that - // were designated as part of the operator declaration. - for (auto designatedTypeIndex : indices(designatedNominalTypes)) { - if (designatedTypeIndex < definedInDesignatedType.size()) { - auto &primary = definedInDesignatedType[designatedTypeIndex]; - appendPartition(primary); - } - if (designatedTypeIndex < definedInExtensionOfDesignatedType.size()) { - auto &secondary = definedInExtensionOfDesignatedType[designatedTypeIndex]; - appendPartition(secondary); - } - } -} - // Performance hack: if there are two generic overloads, and one is // more specialized than the other, prefer the more-specialized one. static Constraint *tryOptimizeGenericDisjunction( @@ -2265,8 +2083,7 @@ void ConstraintSystem::partitionDisjunction( } // Partition SIMD operators. - if (!getASTContext().TypeCheckerOpts.SolverEnableOperatorDesignatedTypes && - isOperatorBindOverload(Choices[0])) { + if (isOperatorBindOverload(Choices[0])) { forEachChoice(Choices, [&](unsigned index, Constraint *constraint) -> bool { if (!isOperatorBindOverload(constraint)) return false; @@ -2290,11 +2107,6 @@ void ConstraintSystem::partitionDisjunction( } }; - if (getASTContext().TypeCheckerOpts.SolverEnableOperatorDesignatedTypes && - isOperatorBindOverload(Choices[0])) { - partitionForDesignatedTypes(Choices, forEachChoice, appendPartition); - } - SmallVector everythingElse; // Gather the remaining options. forEachChoice(Choices, [&](unsigned index, Constraint *constraint) -> bool { @@ -2319,17 +2131,6 @@ Constraint *ConstraintSystem::selectDisjunction() { if (disjunctions.empty()) return nullptr; - // Attempt apply disjunctions first. When we have operators with - // designated types, this is important, because it allows us to - // select all the preferred operator overloads prior to other - // disjunctions that we may not be able to short-circuit, allowing - // us to eliminate behavior that is exponential in the number of - // operators in the expression. - if (getASTContext().TypeCheckerOpts.SolverEnableOperatorDesignatedTypes) { - if (auto *disjunction = selectApplyDisjunction()) - return disjunction; - } - if (auto *disjunction = selectBestBindingDisjunction(*this, disjunctions)) return disjunction; diff --git a/lib/Sema/CSStep.cpp b/lib/Sema/CSStep.cpp index 8717fd15d469f..10636456a5a1c 100644 --- a/lib/Sema/CSStep.cpp +++ b/lib/Sema/CSStep.cpp @@ -16,8 +16,8 @@ //===----------------------------------------------------------------------===// #include "CSStep.h" -#include "ConstraintSystem.h" #include "swift/AST/Types.h" +#include "swift/Sema/ConstraintSystem.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/STLExtras.h" @@ -660,8 +660,7 @@ bool DisjunctionStep::shortCircuitDisjunctionAt( if (currentChoice->getKind() == ConstraintKind::BindOverload && isSIMDOperator(currentChoice->getOverloadChoice().getDecl()) && lastSuccessfulChoice->getKind() == ConstraintKind::BindOverload && - !isSIMDOperator(lastSuccessfulChoice->getOverloadChoice().getDecl()) && - !ctx.TypeCheckerOpts.SolverEnableOperatorDesignatedTypes) { + !isSIMDOperator(lastSuccessfulChoice->getOverloadChoice().getDecl())) { return true; } diff --git a/lib/Sema/CSStep.h b/lib/Sema/CSStep.h index 23c1f8aef642b..89b812ee53511 100644 --- a/lib/Sema/CSStep.h +++ b/lib/Sema/CSStep.h @@ -18,10 +18,10 @@ #ifndef SWIFT_SEMA_CSSTEP_H #define SWIFT_SEMA_CSSTEP_H -#include "Constraint.h" -#include "ConstraintGraph.h" -#include "ConstraintSystem.h" #include "swift/AST/Types.h" +#include "swift/Sema/Constraint.h" +#include "swift/Sema/ConstraintGraph.h" +#include "swift/Sema/ConstraintSystem.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/STLExtras.h" diff --git a/lib/Sema/CodeSynthesis.cpp b/lib/Sema/CodeSynthesis.cpp index 20e07a50489c0..947593e5fb226 100644 --- a/lib/Sema/CodeSynthesis.cpp +++ b/lib/Sema/CodeSynthesis.cpp @@ -16,7 +16,6 @@ #include "CodeSynthesis.h" -#include "ConstraintSystem.h" #include "TypeChecker.h" #include "TypeCheckDecl.h" #include "TypeCheckObjC.h" @@ -33,6 +32,7 @@ #include "swift/AST/TypeCheckRequests.h" #include "swift/Basic/Defer.h" #include "swift/ClangImporter/ClangModule.h" +#include "swift/Sema/ConstraintSystem.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" using namespace swift; @@ -261,6 +261,20 @@ static ConstructorDecl *createImplicitConstructor(NominalTypeDecl *decl, } } + Type resultBuilderType= var->getResultBuilderType(); + if (resultBuilderType) { + // If the variable's type is structurally a function type, use that + // type. Otherwise, form a non-escaping function type for the function + // parameter. + bool isStructuralFunctionType = + varInterfaceType->lookThroughAllOptionalTypes() + ->is(); + if (!isStructuralFunctionType) { + auto extInfo = ASTExtInfoBuilder().withNoEscape().build(); + varInterfaceType = FunctionType::get({ }, varInterfaceType, extInfo); + } + } + // Create the parameter. auto *arg = new (ctx) ParamDecl(SourceLoc(), Loc, @@ -273,6 +287,14 @@ static ConstructorDecl *createImplicitConstructor(NominalTypeDecl *decl, // Don't allow the parameter to accept temporary pointer conversions. arg->setNonEphemeralIfPossible(); + // Attach a result builder attribute if needed. + if (resultBuilderType) { + auto typeExpr = TypeExpr::createImplicit(resultBuilderType, ctx); + auto attr = CustomAttr::create( + ctx, SourceLoc(), typeExpr, /*implicit=*/true); + arg->getAttrs().add(attr); + } + maybeAddMemberwiseDefaultArg(arg, var, params.size(), ctx); params.push_back(arg); @@ -1054,12 +1076,11 @@ InheritsSuperclassInitializersRequest::evaluate(Evaluator &eval, if (decl->getAttrs().hasAttribute()) return true; - auto superclass = decl->getSuperclass(); - assert(superclass); + auto superclassDecl = decl->getSuperclassDecl(); + assert(superclassDecl); // If the superclass has known-missing designated initializers, inheriting // is unsafe. - auto *superclassDecl = superclass->getClassOrBoundGenericClass(); if (superclassDecl->getModuleContext() != decl->getParentModule() && superclassDecl->hasMissingDesignatedInitializers()) return false; @@ -1339,7 +1360,7 @@ HasDefaultInitRequest::evaluate(Evaluator &evaluator, // Don't synthesize a default for a subclass, it will attempt to inherit its // initializers from its superclass. if (auto *cd = dyn_cast(decl)) - if (cd->getSuperclass()) + if (cd->getSuperclassDecl()) return false; // If the user has already defined a designated initializer, then don't diff --git a/lib/Sema/Constraint.cpp b/lib/Sema/Constraint.cpp index 894427a24e89d..a26577dfbe276 100644 --- a/lib/Sema/Constraint.cpp +++ b/lib/Sema/Constraint.cpp @@ -15,10 +15,10 @@ // constraint that must be solved. // //===----------------------------------------------------------------------===// -#include "Constraint.h" -#include "ConstraintSystem.h" #include "swift/AST/Types.h" #include "swift/Basic/Compiler.h" +#include "swift/Sema/Constraint.h" +#include "swift/Sema/ConstraintSystem.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/raw_ostream.h" #include diff --git a/lib/Sema/ConstraintGraph.cpp b/lib/Sema/ConstraintGraph.cpp index 12194cb96a4e4..0d73ce5e2f095 100644 --- a/lib/Sema/ConstraintGraph.cpp +++ b/lib/Sema/ConstraintGraph.cpp @@ -15,10 +15,10 @@ // //===----------------------------------------------------------------------===// -#include "ConstraintGraph.h" -#include "ConstraintGraphScope.h" -#include "ConstraintSystem.h" #include "swift/Basic/Statistic.h" +#include "swift/Sema/ConstraintGraph.h" +#include "swift/Sema/ConstraintGraphScope.h" +#include "swift/Sema/ConstraintSystem.h" #include "llvm/ADT/SetVector.h" #include "llvm/Support/Debug.h" #include "llvm/Support/SaveAndRestore.h" diff --git a/lib/Sema/ConstraintLocator.cpp b/lib/Sema/ConstraintLocator.cpp index 708d90d7dc6de..3c871ae7fef20 100644 --- a/lib/Sema/ConstraintLocator.cpp +++ b/lib/Sema/ConstraintLocator.cpp @@ -15,11 +15,11 @@ // a particular constraint was derived. // //===----------------------------------------------------------------------===// -#include "ConstraintLocator.h" -#include "ConstraintSystem.h" #include "swift/AST/Decl.h" #include "swift/AST/Expr.h" #include "swift/AST/Types.h" +#include "swift/Sema/ConstraintLocator.h" +#include "swift/Sema/ConstraintSystem.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/raw_ostream.h" @@ -44,7 +44,7 @@ unsigned LocatorPathElt::getNewSummaryFlags() const { case ConstraintLocator::ClosureResult: case ConstraintLocator::ClosureBody: case ConstraintLocator::ConstructorMember: - case ConstraintLocator::FunctionBuilderBodyResult: + case ConstraintLocator::ResultBuilderBodyResult: case ConstraintLocator::InstanceType: case ConstraintLocator::AutoclosureResult: case ConstraintLocator::OptionalPayload: @@ -54,7 +54,7 @@ unsigned LocatorPathElt::getNewSummaryFlags() const { case ConstraintLocator::ParentType: case ConstraintLocator::ExistentialSuperclassType: case ConstraintLocator::LValueConversion: - case ConstraintLocator::RValueAdjustment: + case ConstraintLocator::DynamicType: case ConstraintLocator::SubscriptMember: case ConstraintLocator::OpenedGeneric: case ConstraintLocator::GenericParameter: @@ -97,12 +97,6 @@ unsigned LocatorPathElt::getNewSummaryFlags() const { llvm_unreachable("Unhandled PathElementKind in switch."); } -bool LocatorPathElt::isResultOfSingleExprFunction() const { - if (auto elt = getAs()) - return elt->isForSingleExprFunction(); - return false; -} - /// Determine whether given locator points to the subscript reference /// e.g. `foo[0]` or `\Foo.[0]` bool ConstraintLocator::isSubscriptMemberRef() const { @@ -204,8 +198,8 @@ bool ConstraintLocator::isForOptionalTry() const { return directlyAt(); } -bool ConstraintLocator::isForFunctionBuilderBodyResult() const { - return isFirstElement(); +bool ConstraintLocator::isForResultBuilderBodyResult() const { + return isFirstElement(); } GenericTypeParamType *ConstraintLocator::getGenericParameter() const { @@ -306,8 +300,8 @@ void ConstraintLocator::dump(SourceManager *sm, raw_ostream &out) const { out << "function result"; break; - case FunctionBuilderBodyResult: - out << "function builder body result"; + case ResultBuilderBodyResult: + out << "result builder body result"; break; case SequenceElementType: @@ -356,8 +350,8 @@ void ConstraintLocator::dump(SourceManager *sm, raw_ostream &out) const { out << "@lvalue-to-inout conversion"; break; - case RValueAdjustment: - out << "rvalue adjustment"; + case DynamicType: + out << "`.dynamicType` reference"; break; case SubscriptMember: @@ -412,10 +406,7 @@ void ConstraintLocator::dump(SourceManager *sm, raw_ostream &out) const { break; case ContextualType: - if (elt.isResultOfSingleExprFunction()) - out << "expected result type of the function with a single expression"; - else - out << "contextual type"; + out << "contextual type"; break; case SynthesizedArgument: { diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 22d9672b082c3..0cf40161c9f7d 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -15,17 +15,18 @@ // inference for expressions. // //===----------------------------------------------------------------------===// -#include "ConstraintSystem.h" -#include "ConstraintGraph.h" #include "CSDiagnostics.h" -#include "CSFix.h" -#include "SolutionResult.h" +#include "TypeChecker.h" #include "TypeCheckType.h" #include "swift/AST/Initializer.h" #include "swift/AST/GenericEnvironment.h" #include "swift/AST/ParameterList.h" #include "swift/AST/TypeCheckRequests.h" #include "swift/Basic/Statistic.h" +#include "swift/Sema/CSFix.h" +#include "swift/Sema/ConstraintGraph.h" +#include "swift/Sema/ConstraintSystem.h" +#include "swift/Sema/SolutionResult.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallString.h" @@ -1870,8 +1871,9 @@ static std::pair getTypeOfReferenceWithSpecialTypeCheckingSemantics( FunctionType::Param inputArg(input, CS.getASTContext().getIdentifier("of")); - CS.addConstraint(ConstraintKind::DynamicTypeOf, output, input, - CS.getConstraintLocator(locator, ConstraintLocator::RValueAdjustment)); + CS.addConstraint( + ConstraintKind::DynamicTypeOf, output, input, + CS.getConstraintLocator(locator, ConstraintLocator::DynamicType)); auto refType = FunctionType::get({inputArg}, output); return {refType, refType}; } @@ -1885,9 +1887,8 @@ static std::pair getTypeOfReferenceWithSpecialTypeCheckingSemantics( auto escapeClosure = CS.createTypeVariable( CS.getConstraintLocator(locator, ConstraintLocator::FunctionArgument), TVO_CanBindToNoEscape); - CS.addConstraint(ConstraintKind::EscapableFunctionOf, - escapeClosure, noescapeClosure, - CS.getConstraintLocator(locator, ConstraintLocator::RValueAdjustment)); + CS.addConstraint(ConstraintKind::EscapableFunctionOf, escapeClosure, + noescapeClosure, CS.getConstraintLocator(locator)); auto result = CS.createTypeVariable( CS.getConstraintLocator(locator, ConstraintLocator::FunctionResult), TVO_CanBindToNoEscape); @@ -1918,9 +1919,8 @@ static std::pair getTypeOfReferenceWithSpecialTypeCheckingSemantics( auto existentialTy = CS.createTypeVariable( CS.getConstraintLocator(locator, ConstraintLocator::FunctionArgument), TVO_CanBindToNoEscape); - CS.addConstraint(ConstraintKind::OpenedExistentialOf, - openedTy, existentialTy, - CS.getConstraintLocator(locator, ConstraintLocator::RValueAdjustment)); + CS.addConstraint(ConstraintKind::OpenedExistentialOf, openedTy, + existentialTy, CS.getConstraintLocator(locator)); auto result = CS.createTypeVariable( CS.getConstraintLocator(locator, ConstraintLocator::FunctionResult), TVO_CanBindToNoEscape); @@ -3931,7 +3931,7 @@ void constraints::simplifyLocator(ASTNode &anchor, case ConstraintLocator::AutoclosureResult: case ConstraintLocator::LValueConversion: - case ConstraintLocator::RValueAdjustment: + case ConstraintLocator::DynamicType: case ConstraintLocator::UnresolvedMember: case ConstraintLocator::ImplicitCallAsFunction: // Arguments in autoclosure positions, lvalue and rvalue adjustments, @@ -4078,7 +4078,7 @@ void constraints::simplifyLocator(ASTNode &anchor, break; } - case ConstraintLocator::FunctionBuilderBodyResult: { + case ConstraintLocator::ResultBuilderBodyResult: { path = path.slice(1); break; } @@ -5233,3 +5233,39 @@ Type ConstraintSystem::getVarType(const VarDecl *var) { return HoleType::get(Context, const_cast(var)); }); } + +bool ConstraintSystem::isReadOnlyKeyPathComponent( + const AbstractStorageDecl *storage, SourceLoc referenceLoc) { + // 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. + return true; + } + } 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. + return true; + } + + // If the setter is unavailable, then the keypath ought to be read-only + // in this context. + if (auto setter = storage->getOpaqueAccessor(AccessorKind::Set)) { + auto maybeUnavail = + TypeChecker::checkDeclarationAvailability(setter, referenceLoc, DC); + if (maybeUnavail.hasValue()) { + return true; + } + } + + return false; +} diff --git a/lib/Sema/DebuggerTestingTransform.cpp b/lib/Sema/DebuggerTestingTransform.cpp index 61484a57a0d77..d9057d6cb69d2 100644 --- a/lib/Sema/DebuggerTestingTransform.cpp +++ b/lib/Sema/DebuggerTestingTransform.cpp @@ -244,7 +244,8 @@ class DebuggerTestingTransform : public ASTWalker { // TODO: typeCheckExpression() seems to assign types to everything here, // but may not be sufficient in some cases. Expr *FinalExpr = ClosureCall; - if (!TypeChecker::typeCheckExpression(FinalExpr, getCurrentDeclContext())) + if (!TypeChecker::typeCheckExpression(FinalExpr, getCurrentDeclContext(), + /*contextualInfo=*/{})) llvm::report_fatal_error("Could not type-check instrumentation"); // Captures have to be computed after the closure is type-checked. This diff --git a/lib/Sema/DerivedConformanceActor.cpp b/lib/Sema/DerivedConformanceActor.cpp new file mode 100644 index 0000000000000..ad826a7734a01 --- /dev/null +++ b/lib/Sema/DerivedConformanceActor.cpp @@ -0,0 +1,293 @@ +//===--- DerivedConformanceActor.cpp - Derived Actor Conformance ----------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file implements implicit derivation of the Actor protocol. +// +//===----------------------------------------------------------------------===// +#include "DerivedConformances.h" +#include "TypeChecker.h" +#include "TypeCheckConcurrency.h" +#include "swift/AST/NameLookupRequests.h" +#include "swift/AST/ParameterList.h" + +using namespace swift; + +bool DerivedConformance::canDeriveActor( + NominalTypeDecl *nominal, DeclContext *dc) { + auto classDecl = dyn_cast(nominal); + return classDecl && classDecl->isActor() && dc == nominal; +} + +static DeclName getEnqueuePartialTaskName(ASTContext &ctx) { + return DeclName(ctx, ctx.Id_enqueue, { ctx.Id_partialTask }); +} + +static Type getPartialAsyncTaskType(ASTContext &ctx) { + auto concurrencyModule = ctx.getLoadedModule(ctx.Id_Concurrency); + if (!concurrencyModule) + return Type(); + + SmallVector decls; + concurrencyModule->lookupQualified( + concurrencyModule, DeclNameRef(ctx.Id_PartialAsyncTask), + NL_QualifiedDefault, decls); + for (auto decl : decls) { + if (auto typeDecl = dyn_cast(decl)) + return typeDecl->getDeclaredInterfaceType(); + } + + return Type(); +} + +/// Look for the default actor queue type. +static Type getDefaultActorQueueType(DeclContext *dc, SourceLoc loc) { + ASTContext &ctx = dc->getASTContext(); + UnqualifiedLookupOptions options; + options |= UnqualifiedLookupFlags::TypeLookup; + auto desc = UnqualifiedLookupDescriptor( + DeclNameRef(ctx.getIdentifier("_DefaultActorQueue")), dc, loc, options); + auto lookup = + evaluateOrDefault(ctx.evaluator, UnqualifiedLookupRequest{desc}, {}); + for (const auto &result : lookup) { + if (auto typeDecl = dyn_cast(result.getValueDecl())) + return typeDecl->getDeclaredInterfaceType(); + } + + return Type(); +} + +/// Look for the initialization function for the default actor storage. +static FuncDecl *getDefaultActorQueueCreate(DeclContext *dc, SourceLoc loc) { + ASTContext &ctx = dc->getASTContext(); + auto desc = UnqualifiedLookupDescriptor( + DeclNameRef(ctx.getIdentifier("_defaultActorQueueCreate")), dc, loc, + UnqualifiedLookupOptions()); + auto lookup = + evaluateOrDefault(ctx.evaluator, UnqualifiedLookupRequest{desc}, {}); + for (const auto &result : lookup) { + // FIXME: Validate this further, because we're assuming the exact type. + if (auto func = dyn_cast(result.getValueDecl())) + return func; + } + + return nullptr; +} + +/// Look for the default enqueue operation. +static FuncDecl *getDefaultActorQueueEnqueue(DeclContext *dc, SourceLoc loc) { + ASTContext &ctx = dc->getASTContext(); + auto desc = UnqualifiedLookupDescriptor( + DeclNameRef(ctx.getIdentifier("_defaultActorQueueEnqueuePartialTask")), + dc, loc, UnqualifiedLookupOptions()); + auto lookup = + evaluateOrDefault(ctx.evaluator, UnqualifiedLookupRequest{desc}, {}); + for (const auto &result : lookup) { + // FIXME: Validate this further, because we're assuming the exact type. + if (auto func = dyn_cast(result.getValueDecl())) + return func; + } + + return nullptr; +} + +static std::pair +deriveBodyActor_enqueuePartialTask( + AbstractFunctionDecl *enqueuePartialTask, void *) { + // func enqueue(partialTask: PartialAsyncTask) { + // _defaultActorQueueEnqueuePartialTask( + // actor: self, queue: &self.$__actor_storage, partialTask: partialTask) + // } + ASTContext &ctx = enqueuePartialTask->getASTContext(); + + // Dig out the $__actor_storage property. + auto classDecl = enqueuePartialTask->getDeclContext()->getSelfClassDecl(); + VarDecl *storageVar = nullptr; + for (auto decl : classDecl->lookupDirect(ctx.Id_actorStorage)) { + storageVar = dyn_cast(decl); + if (storageVar) + break; + } + + // Produce an empty brace statement on failure. + auto failure = [&]() -> std::pair { + auto body = BraceStmt::create( + ctx, SourceLoc(), { }, SourceLoc(), /*implicit=*/true); + return { body, /*isTypeChecked=*/true }; + }; + + if (!storageVar) { + classDecl->diagnose( + diag::concurrency_lib_missing, ctx.Id_actorStorage.str()); + return failure(); + } + + // Call into the runtime to enqueue the task. + auto fn = getDefaultActorQueueEnqueue(classDecl, classDecl->getLoc()); + if (!fn) { + classDecl->diagnose( + diag::concurrency_lib_missing, "_defaultActorQueueEnqueuePartialTask"); + return failure(); + } + + // Reference to _defaultActorQueueEnqueuePartialTask. + auto fnRef = new (ctx) DeclRefExpr(fn, DeclNameLoc(), /*Implicit=*/true); + fnRef->setType(fn->getInterfaceType()); + + // self argument to the function. + auto selfDecl = enqueuePartialTask->getImplicitSelfDecl(); + Type selfType = enqueuePartialTask->mapTypeIntoContext( + selfDecl->getValueInterfaceType()); + Expr *selfArg = new (ctx) DeclRefExpr( + selfDecl, DeclNameLoc(), /*Implicit=*/true, AccessSemantics::Ordinary, + selfType); + selfArg = ErasureExpr::create(ctx, selfArg, ctx.getAnyObjectType(), { }); + selfArg->setImplicit(); + + // Address of the actor storage. + auto module = classDecl->getModuleContext(); + Expr *selfBase = new (ctx) DeclRefExpr( + selfDecl, DeclNameLoc(), /*Implicit=*/true, AccessSemantics::Ordinary, + selfType); + SubstitutionMap storageVarSubs = classDecl->getDeclaredTypeInContext() + ->getMemberSubstitutionMap(module, storageVar); + ConcreteDeclRef storageVarDeclRef(storageVar, storageVarSubs); + Type storageVarType = classDecl->mapTypeIntoContext( + storageVar->getValueInterfaceType()); + Type storageVarRefType = LValueType::get(storageVarType); + Expr *storageVarRefExpr = new (ctx) MemberRefExpr( + selfBase, SourceLoc(), storageVarDeclRef, DeclNameLoc(), + /*Implicit=*/true); + storageVarRefExpr->setType(storageVarRefType); + storageVarRefExpr = new (ctx) InOutExpr( + SourceLoc(), storageVarRefExpr, storageVarType, /*isImplicit=*/true); + + // The partial asynchronous task. + auto partialTaskParam = enqueuePartialTask->getParameters()->get(0); + Expr *partialTask = new (ctx) DeclRefExpr( + partialTaskParam, DeclNameLoc(), /*Implicit=*/true, + AccessSemantics::Ordinary, + enqueuePartialTask->mapTypeIntoContext( + partialTaskParam->getValueInterfaceType())); + + // Form the call itself. + auto call = CallExpr::createImplicit( + ctx, fnRef, { selfArg, storageVarRefExpr, partialTask }, + { ctx.getIdentifier("actor"), ctx.getIdentifier("queue"), + ctx.Id_partialTask }); + call->setType(fn->getResultInterfaceType()); + call->setThrows(false); + + auto body = BraceStmt::create( + ctx, SourceLoc(), { call }, SourceLoc(), /*implicit=*/true); + return { body, /*isTypeChecked=*/true }; +} + +/// Derive the declaration of Actor's enqueue(partialTask:). +static ValueDecl *deriveActor_enqueuePartialTask(DerivedConformance &derived) { + ASTContext &ctx = derived.Context; + + // Retrieve the types and declarations we'll need to form this operation. + Type partialTaskType = getPartialAsyncTaskType(ctx); + if (!partialTaskType) { + derived.Nominal->diagnose( + diag::concurrency_lib_missing, ctx.Id_PartialAsyncTask.str()); + return nullptr; + } + + auto parentDC = derived.getConformanceContext(); + Type defaultActorQueueType = getDefaultActorQueueType( + parentDC, derived.ConformanceDecl->getLoc()); + if (!defaultActorQueueType) { + derived.Nominal->diagnose( + diag::concurrency_lib_missing, "_DefaultActorQueue"); + return nullptr; + } + + auto actorStorageCreateFn = getDefaultActorQueueCreate( + parentDC, derived.ConformanceDecl->getLoc()); + if (!actorStorageCreateFn) { + derived.Nominal->diagnose( + diag::concurrency_lib_missing, "_defaultActorQueueCreate"); + return nullptr; + } + + // Partial task parameter to enqueue(partialTask:). + auto partialTaskParamDecl = new (ctx) ParamDecl( + SourceLoc(), SourceLoc(), ctx.Id_partialTask, + SourceLoc(), ctx.Id_partialTask, parentDC); + partialTaskParamDecl->setInterfaceType(partialTaskType); + partialTaskParamDecl->setSpecifier(ParamSpecifier::Default); + + // enqueue(partialTask:) method. + ParameterList *params = ParameterList::createWithoutLoc(partialTaskParamDecl); + auto func = FuncDecl::createImplicit( + ctx, StaticSpellingKind::None, getEnqueuePartialTaskName(ctx), + SourceLoc(), /*Async=*/false, /*Throws=*/false, /*GenericParams=*/nullptr, + params, TupleType::getEmpty(ctx), parentDC); + func->copyFormalAccessFrom(derived.Nominal); + func->setBodySynthesizer(deriveBodyActor_enqueuePartialTask); + func->setSynthesized(); + // mark as @actorIndependent(unsafe) + func->getAttrs().add(new (ctx) ActorIndependentAttr( + ActorIndependentKind::Unsafe, /*IsImplicit=*/true)); + + // Actor storage property and its initialization. + auto actorStorage = new (ctx) VarDecl( + /*isStatic=*/false, VarDecl::Introducer::Var, SourceLoc(), + ctx.Id_actorStorage, parentDC); + actorStorage->setInterfaceType(defaultActorQueueType); + actorStorage->setImplicit(); + actorStorage->setAccess(AccessLevel::Private); + actorStorage->getAttrs().add(new (ctx) FinalAttr(/*Implicit=*/true)); + + // Pattern binding to initialize the actor storage. + Pattern *actorStoragePattern = NamedPattern::createImplicit( + ctx, actorStorage); + actorStoragePattern = TypedPattern::createImplicit( + ctx, actorStoragePattern, defaultActorQueueType); + + // Initialization expression. + // FIXME: We want the equivalent of type(of: self) here, but we cannot refer + // to self, so for now we use the static type instead. + Type nominalType = derived.Nominal->getDeclaredTypeInContext(); + Expr *metatypeArg = TypeExpr::createImplicit(nominalType, ctx); + Type anyObjectMetatype = ExistentialMetatypeType::get(ctx.getAnyObjectType()); + metatypeArg = ErasureExpr::create(ctx, metatypeArg, anyObjectMetatype, { }); + Expr *actorStorageCreateFnRef = new (ctx) DeclRefExpr( + actorStorageCreateFn, DeclNameLoc(), /*Implicit=*/true); + actorStorageCreateFnRef->setType(actorStorageCreateFn->getInterfaceType()); + + auto actorStorageInit = CallExpr::createImplicit( + ctx, actorStorageCreateFnRef, { metatypeArg}, { Identifier() }); + actorStorageInit->setType(actorStorageCreateFn->getResultInterfaceType()); + actorStorageInit->setThrows(false); + + auto actorStoragePatternBinding = PatternBindingDecl::createImplicit( + ctx, StaticSpellingKind::None, actorStoragePattern, actorStorageInit, + parentDC); + actorStoragePatternBinding->setInitializerChecked(0); + + derived.addMembersToConformanceContext( + { func, actorStorage, actorStoragePatternBinding }); + return func; +} + +ValueDecl *DerivedConformance::deriveActor(ValueDecl *requirement) { + auto func = dyn_cast(requirement); + if (!func) + return nullptr; + + if (FuncDecl::isEnqueuePartialTaskName(Context, func->getName())) + return deriveActor_enqueuePartialTask(*this); + + return nullptr; +} diff --git a/lib/Sema/DerivedConformanceCodable.cpp b/lib/Sema/DerivedConformanceCodable.cpp index 337afe9dd9946..75fbdaeb7555a 100644 --- a/lib/Sema/DerivedConformanceCodable.cpp +++ b/lib/Sema/DerivedConformanceCodable.cpp @@ -30,10 +30,15 @@ using namespace swift; /// Returns whether the type represented by the given ClassDecl inherits from a /// type which conforms to the given protocol. static bool superclassConformsTo(ClassDecl *target, KnownProtocolKind kpk) { - if (!target || !target->getSuperclass() || target->hasCircularInheritance()) { + if (!target) { return false; } - return !target->getSuperclassDecl() + + auto superclass = target->getSuperclassDecl(); + if (!superclass) + return false; + + return !superclass ->getModuleContext() ->lookupConformance(target->getSuperclass(), target->getASTContext().getProtocol(kpk)) @@ -229,7 +234,7 @@ static EnumDecl *synthesizeCodingKeysEnum(DerivedConformance &derived) { auto *codingKeyProto = C.getProtocol(KnownProtocolKind::CodingKey); auto codingKeyType = codingKeyProto->getDeclaredInterfaceType(); TypeLoc protoTypeLoc[1] = {TypeLoc::withoutLoc(codingKeyType)}; - MutableArrayRef inherited = C.AllocateCopy(protoTypeLoc); + ArrayRef inherited = C.AllocateCopy(protoTypeLoc); auto *enumDecl = new (C) EnumDecl(SourceLoc(), C.Id_CodingKeys, SourceLoc(), inherited, nullptr, target); @@ -794,8 +799,10 @@ deriveBodyDecodable_init(AbstractFunctionDecl *initDecl, void *) { diag::decodable_property_init_or_codingkeys_explicit, varDecl->getName()); } - varDecl->diagnose(diag::decodable_make_property_mutable) - .fixItReplace(varDecl->getAttributeInsertionLoc(true), "var"); + if (auto *PBD = varDecl->getParentPatternBinding()) { + varDecl->diagnose(diag::decodable_make_property_mutable) + .fixItReplace(PBD->getLoc(), "var"); + } continue; } diff --git a/lib/Sema/DerivedConformanceRawRepresentable.cpp b/lib/Sema/DerivedConformanceRawRepresentable.cpp index b8ecb3e61c3fa..39f21a42819a6 100644 --- a/lib/Sema/DerivedConformanceRawRepresentable.cpp +++ b/lib/Sema/DerivedConformanceRawRepresentable.cpp @@ -24,6 +24,7 @@ #include "swift/AST/Types.h" #include "llvm/ADT/APInt.h" #include "DerivedConformances.h" +#include "TypeCheckDecl.h" using namespace swift; @@ -401,13 +402,15 @@ deriveRawRepresentable_init(DerivedConformance &derived) { auto rawInterfaceType = enumDecl->getRawType(); auto rawType = parentDC->mapTypeIntoContext(rawInterfaceType); - auto equatableProto = TypeChecker::getProtocol(C, enumDecl->getLoc(), - KnownProtocolKind::Equatable); - assert(equatableProto); - assert( - TypeChecker::conformsToProtocol(rawType, equatableProto, enumDecl)); - (void)equatableProto; - (void)rawType; + + assert([&]() -> bool { + auto equatableProto = TypeChecker::getProtocol(C, enumDecl->getLoc(), + KnownProtocolKind::Equatable); + if (!equatableProto) { + return false; + } + return !TypeChecker::conformsToProtocol(rawType, equatableProto, enumDecl).isInvalid(); + }()); auto *rawDecl = new (C) ParamDecl(SourceLoc(), SourceLoc(), @@ -446,7 +449,10 @@ bool DerivedConformance::canDeriveRawRepresentable(DeclContext *DC, return false; Type rawType = enumDecl->getRawType(); - if (!rawType) + if (!rawType || rawType->hasError()) + return false; + + if (!computeAutomaticEnumValueKind(enumDecl)) return false; rawType = DC->mapTypeIntoContext(rawType); diff --git a/lib/Sema/DerivedConformances.cpp b/lib/Sema/DerivedConformances.cpp index eaffdea93af63..b27d89f35d92f 100644 --- a/lib/Sema/DerivedConformances.cpp +++ b/lib/Sema/DerivedConformances.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "TypeChecker.h" +#include "TypeCheckConcurrency.h" #include "swift/AST/ASTPrinter.h" #include "swift/AST/Decl.h" #include "swift/AST/Stmt.h" @@ -74,6 +75,10 @@ bool DerivedConformance::derivesProtocolConformance(DeclContext *DC, return canDeriveHashable(Nominal); } + if (*derivableKind == KnownDerivableProtocolKind::Actor) { + return canDeriveActor(Nominal, DC); + } + if (*derivableKind == KnownDerivableProtocolKind::AdditiveArithmetic) return canDeriveAdditiveArithmetic(Nominal, DC); @@ -426,6 +431,11 @@ ValueDecl *DerivedConformance::getDerivableRequirement(NominalTypeDecl *nominal, return getRequirement(KnownProtocolKind::Hashable); } + // Actor.enqueue(partialTask: PartialTask) + if (FuncDecl::isEnqueuePartialTaskName(ctx, name)) { + return getRequirement(KnownProtocolKind::Actor); + } + // SWIFT_ENABLE_TENSORFLOW // AdditiveArithmetic.+ // AdditiveArithmetic.- diff --git a/lib/Sema/DerivedConformances.h b/lib/Sema/DerivedConformances.h index 2321b59cc5967..18c585480f71a 100644 --- a/lib/Sema/DerivedConformances.h +++ b/lib/Sema/DerivedConformances.h @@ -18,15 +18,31 @@ #ifndef SWIFT_SEMA_DERIVEDCONFORMANCES_H #define SWIFT_SEMA_DERIVEDCONFORMANCES_H +#include "swift/Basic/LLVM.h" #include namespace swift { +class AbstractFunctionDecl; +class AccessorDecl; +class AssociatedTypeDecl; +class ASTContext; +struct ASTNode; class Decl; +class DeclContext; class DeclRefExpr; -class AccessorDecl; +class EnumDecl; +class EnumElementDecl; +class Expr; +class GuardStmt; +class Identifier; class NominalTypeDecl; +class ParamDecl; +class Pattern; class PatternBindingDecl; +class ProtocolDecl; +class StructDecl; class Type; +class TypeDecl; class ValueDecl; class VarDecl; @@ -277,6 +293,14 @@ class DerivedConformance { /// \returns the derived member, which will also be added to the type. ValueDecl *deriveDecodable(ValueDecl *requirement); + /// Whether we can derive the given Actor requirement in the given context. + static bool canDeriveActor(NominalTypeDecl *nominal, DeclContext *dc); + + /// Derive an Actor requirement for an actor class. + /// + /// \returns the derived member, which will also be added to the type. + ValueDecl *deriveActor(ValueDecl *requirement); + // SWIFT_ENABLE_TENSORFLOW /// Determine if a KeyPathIterable requirement can be derived for a type. /// diff --git a/lib/Sema/IDETypeCheckingRequests.cpp b/lib/Sema/IDETypeCheckingRequests.cpp index 739694f801205..5ba09dacb5706 100644 --- a/lib/Sema/IDETypeCheckingRequests.cpp +++ b/lib/Sema/IDETypeCheckingRequests.cpp @@ -15,11 +15,10 @@ #include "swift/AST/NameLookup.h" #include "swift/Basic/SourceManager.h" #include "swift/Frontend/Frontend.h" +#include "swift/Sema/ConstraintSystem.h" #include "swift/Sema/IDETypeCheckingRequests.h" #include "swift/Subsystems.h" #include "TypeChecker.h" -#include "ConstraintGraph.h" -#include "ConstraintSystem.h" using namespace swift; diff --git a/lib/Sema/ImportResolution.cpp b/lib/Sema/ImportResolution.cpp index 9b2a4362eaa3f..fbe90853e79d7 100644 --- a/lib/Sema/ImportResolution.cpp +++ b/lib/Sema/ImportResolution.cpp @@ -43,11 +43,6 @@ using namespace swift; // MARK: ImportResolver and supporting types //===----------------------------------------------------------------------===// -using ImportedModule = ModuleDecl::ImportedModule; -using ImportedModuleDesc = SourceFile::ImportedModuleDesc; -using ImportOptions = SourceFile::ImportOptions; -using ImportFlags = SourceFile::ImportFlags; - namespace { /// Represents an import which the ImportResolver knows exists, but which has /// not yet had its options checked, module loaded, or cross-imports found. @@ -56,44 +51,26 @@ namespace { /// source, or it may represent a cross-import overlay that has been found and /// needs to be loaded. struct UnboundImport { + /// Information about the import. Use this field, not \c getImportDecl(), to + /// determine the behavior expected for this import. + AttributedImport import; + /// The source location to use when diagnosing errors for this import. SourceLoc importLoc; - /// The options for this import, such as "exported" or - /// "implementation-only". Use this field, not \c attrs, to determine the - /// behavior expected for this import. - ImportOptions options; - - /// If \c options includes \c PrivateImport, the filename we should import - /// private declarations from. - StringRef privateImportFileName; - - /// The module names being imported. There will usually be just one for the - /// top-level module, but a submodule import will have more. - ImportPath::Module modulePath; - - /// If this is a scoped import, the names of the declaration being imported; - /// otherwise empty. (Currently the compiler doesn't support nested scoped - /// imports, so there should always be zero or one elements, but - /// \c ImportPath::Access is the common currency type for this.) - ImportPath::Access accessPath; - - // Names of explicitly imported SPI groups via @_spi. - ArrayRef spiGroups; - /// If this UnboundImport directly represents an ImportDecl, contains the /// ImportDecl it represents. This should only be used for diagnostics and /// for updating the AST; if you want to read information about the import, - /// get it from the other fields in \c UnboundImport rather than from the - /// \c ImportDecl. + /// get it from the \c import field rather than from the \c ImportDecl. /// /// If this UnboundImport represents a cross-import, contains the declaring /// module's \c ModuleDecl. - PointerUnion importOrUnderlyingModuleDecl; + PointerUnion, ModuleDecl *> + importOrUnderlyingModuleDecl; NullablePtr getImportDecl() const { - return importOrUnderlyingModuleDecl.is() ? - importOrUnderlyingModuleDecl.get() : nullptr; + return importOrUnderlyingModuleDecl.is>() ? + importOrUnderlyingModuleDecl.get>() : nullptr; } NullablePtr getUnderlyingModule() const { @@ -104,11 +81,14 @@ struct UnboundImport { /// Create an UnboundImport for a user-written import declaration. explicit UnboundImport(ImportDecl *ID); + /// Create an UnboundImport for an unloaded implicit import. + explicit UnboundImport(AttributedImport implicit); + /// Create an UnboundImport for a cross-import overlay. explicit UnboundImport(ASTContext &ctx, const UnboundImport &base, Identifier overlayName, - const ImportedModuleDesc &declaringImport, - const ImportedModuleDesc &bystandingImport); + const AttributedImport &declaringImport, + const AttributedImport &bystandingImport); /// Diagnoses if the import would simply load the module \p SF already /// belongs to, with no actual effect. @@ -138,11 +118,11 @@ struct UnboundImport { /// non-implementation-only import of a fragile library from a resilient one. void validateOptions(NullablePtr topLevelModule, SourceFile &SF); - /// Create an \c ImportedModuleDesc from the information in this + /// Create an \c AttributedImport from the information in this /// UnboundImport. - ImportedModuleDesc makeDesc(ModuleDecl *module) const { - return ImportedModuleDesc({ accessPath, module }, options, - privateImportFileName, spiGroups); + AttributedImport + makeAttributedImport(ModuleDecl *module) const { + return import.getLoaded(module); } private: @@ -170,7 +150,7 @@ class ImportResolver final : public DeclVisitor { SmallVector unboundImports; /// The list of fully bound imports. - SmallVector boundImports; + SmallVector, 16> boundImports; /// All imported modules which should be considered when cross-importing. /// This is basically the transitive import graph, but with only top-level @@ -179,14 +159,14 @@ class ImportResolver final : public DeclVisitor { /// We use a \c SmallSetVector here because this doubles as the worklist for /// cross-importing, so we want to keep it in order; this is feasible /// because this set is usually fairly small. - SmallSetVector crossImportableModules; + SmallSetVector, 64> crossImportableModules; /// The subset of \c crossImportableModules which may declare cross-imports. /// /// This is a performance optimization. Since most modules do not register /// any cross-imports, we can usually compare against this list, which is /// much, much smaller than \c crossImportableModules. - SmallVector crossImportDeclaringModules; + SmallVector, 16> crossImportDeclaringModules; /// The index of the next module in \c visibleModules that should be /// cross-imported. @@ -197,18 +177,10 @@ class ImportResolver final : public DeclVisitor { addImplicitImports(); } - void addImplicitImports() { - // TODO: Support cross-module imports. - for (auto &import : SF.getParentModule()->getImplicitImports()) { - assert(!(SF.Kind == SourceFileKind::SIL && - import.Module->isStdlibModule())); - ImportedModule importedMod{ImportPath::Access(), import.Module}; - boundImports.emplace_back(importedMod, import.Options); - } - } + void addImplicitImports(); /// Retrieve the finalized imports. - ArrayRef getFinishedImports() const { + ArrayRef> getFinishedImports() const { return boundImports; } @@ -224,6 +196,9 @@ class ImportResolver final : public DeclVisitor { return ctx.Diags.diagnose(std::forward(Args)...); } + /// Calls \c bindImport() on unbound imports until \c boundImports is drained. + void bindPendingImports(); + /// Check a single unbound import, bind it, add it to \c boundImports, /// and add its cross-import overlays to \c unboundImports. void bindImport(UnboundImport &&I); @@ -233,7 +208,7 @@ class ImportResolver final : public DeclVisitor { /// Adds \p desc and everything it re-exports to \c visibleModules using /// the settings from \c desc. - void addCrossImportableModules(ImportedModuleDesc desc); + void addCrossImportableModules(AttributedImport desc); /// * If \p I is a cross-import overlay, registers \p M as overlaying /// \p I.underlyingModule in \c SF. @@ -244,18 +219,19 @@ class ImportResolver final : public DeclVisitor { /// Discovers any cross-imports between \p newImport and /// \p oldImports and adds them to \c unboundImports, using source /// locations from \p I. - void findCrossImportsInLists(UnboundImport &I, - ArrayRef declaring, - ArrayRef bystanding, - bool shouldDiagnoseRedundantCrossImports); + void findCrossImportsInLists( + UnboundImport &I, + ArrayRef> declaring, + ArrayRef> bystanding, + bool shouldDiagnoseRedundantCrossImports); /// Discovers any cross-imports between \p declaringImport and /// \p bystandingImport and adds them to \c unboundImports, using source /// locations from \p I. void findCrossImports(UnboundImport &I, - const ImportedModuleDesc &declaringImport, - const ImportedModuleDesc &bystandingImport, - bool shouldDiagnoseRedundantCrossImports); + const AttributedImport &declaringImport, + const AttributedImport &bystandingImport, + bool shouldDiagnoseRedundantCrossImports); /// Load a module referenced by an import statement. /// @@ -315,6 +291,10 @@ void ImportResolver::visitImportDecl(ImportDecl *ID) { assert(unboundImports.empty()); unboundImports.emplace_back(ID); + bindPendingImports(); +} + +void ImportResolver::bindPendingImports() { while(!unboundImports.empty()) bindImport(unboundImports.pop_back_val()); } @@ -329,7 +309,7 @@ void ImportResolver::bindImport(UnboundImport &&I) { return; } - ModuleDecl *M = getModule(I.modulePath); + ModuleDecl *M = getModule(I.import.module.getModulePath()); if (!I.checkModuleLoaded(M, SF)) { // Can't process further. checkModuleLoaded() will have diagnosed this. if (ID) @@ -358,7 +338,7 @@ void ImportResolver::bindImport(UnboundImport &&I) { } void ImportResolver::addImport(const UnboundImport &I, ModuleDecl *M) { - auto importDesc = I.makeDesc(M); + auto importDesc = I.makeAttributedImport(M); addCrossImportableModules(importDesc); boundImports.push_back(importDesc); } @@ -367,14 +347,16 @@ void ImportResolver::addImport(const UnboundImport &I, ModuleDecl *M) { // MARK: Import module loading //===----------------------------------------------------------------------===// -ModuleDecl * -ImportResolver::getModule(ImportPath::Module modulePath) { +static ModuleDecl * +getModuleImpl(ImportPath::Module modulePath, ModuleDecl *loadingModule, + bool canImportBuiltin) { + ASTContext &ctx = loadingModule->getASTContext(); + assert(!modulePath.empty()); auto moduleID = modulePath[0]; // The Builtin module cannot be explicitly imported unless we're a .sil file. - if (SF.Kind == SourceFileKind::SIL && - moduleID.Item == ctx.TheBuiltinModule->getName()) + if (canImportBuiltin && moduleID.Item == ctx.TheBuiltinModule->getName()) return ctx.TheBuiltinModule; // If the imported module name is the same as the current module, @@ -383,8 +365,7 @@ ImportResolver::getModule(ImportPath::Module modulePath) { // // FIXME: We'd like to only use this in SIL mode, but unfortunately we use it // for clang overlays as well. - if (moduleID.Item == SF.getParentModule()->getName() && - modulePath.size() == 1) { + if (moduleID.Item == loadingModule->getName() && modulePath.size() == 1) { if (auto importer = ctx.getClangModuleLoader()) return importer->loadModule(moduleID.Loc, modulePath); return nullptr; @@ -393,13 +374,19 @@ ImportResolver::getModule(ImportPath::Module modulePath) { return ctx.getModule(modulePath); } +ModuleDecl * +ImportResolver::getModule(ImportPath::Module modulePath) { + return getModuleImpl(modulePath, SF.getParentModule(), + /*canImportBuiltin=*/SF.Kind == SourceFileKind::SIL); +} + NullablePtr UnboundImport::getTopLevelModule(ModuleDecl *M, SourceFile &SF) { - if (modulePath.size() == 1) + if (import.module.getModulePath().size() == 1) return M; // If we imported a submodule, import the top-level module as well. - Identifier topLevelName = modulePath.front().Item; + Identifier topLevelName = import.module.getModulePath().front().Item; ModuleDecl *topLevelModule = SF.getASTContext().getLoadedModule(topLevelName); if (!topLevelModule) { @@ -422,49 +409,66 @@ UnboundImport::getTopLevelModule(ModuleDecl *M, SourceFile &SF) { // MARK: Implicit imports //===----------------------------------------------------------------------===// -ArrayRef +static void diagnoseNoSuchModule(ModuleDecl *importingModule, + SourceLoc importLoc, + ImportPath::Module modulePath, + bool nonfatalInREPL) { + ASTContext &ctx = importingModule->getASTContext(); + + if (modulePath.size() == 1 && + importingModule->getName() == modulePath.front().Item) { + ctx.Diags.diagnose(importLoc, diag::error_underlying_module_not_found, + importingModule->getName()); + } else { + SmallString<64> modulePathStr; + modulePath.getString(modulePathStr); + + auto diagKind = diag::sema_no_import; + if (nonfatalInREPL && ctx.LangOpts.DebuggerSupport) + diagKind = diag::sema_no_import_repl; + ctx.Diags.diagnose(importLoc, diagKind, modulePathStr); + } + + if (ctx.SearchPathOpts.SDKPath.empty() && + llvm::Triple(llvm::sys::getProcessTriple()).isMacOSX()) { + ctx.Diags.diagnose(SourceLoc(), diag::sema_no_import_no_sdk); + ctx.Diags.diagnose(SourceLoc(), diag::sema_no_import_no_sdk_xcrun); + } +} + +ImplicitImportList ModuleImplicitImportsRequest::evaluate(Evaluator &evaluator, ModuleDecl *module) const { - SmallVector imports; + SmallVector, 4> imports; + SmallVector, 4> unloadedImports; auto &ctx = module->getASTContext(); auto &importInfo = module->getImplicitImportInfo(); // Add an implicit stdlib if needed. + ModuleDecl *stdlib; switch (importInfo.StdlibKind) { case ImplicitStdlibKind::None: + stdlib = nullptr; break; case ImplicitStdlibKind::Builtin: - imports.emplace_back(ctx.TheBuiltinModule); + stdlib = ctx.TheBuiltinModule; break; - case ImplicitStdlibKind::Stdlib: { - auto *stdlib = ctx.getStdlibModule(/*loadIfAbsent*/ true); + case ImplicitStdlibKind::Stdlib: + stdlib = ctx.getStdlibModule(/*loadIfAbsent*/ true); assert(stdlib && "Missing stdlib?"); - imports.emplace_back(stdlib); break; } - } + + if (stdlib) + imports.emplace_back(ImportedModule(stdlib)); // Add any modules we were asked to implicitly import. - for (auto moduleName : importInfo.ModuleNames) { - auto *importModule = ctx.getModuleByIdentifier(moduleName); - if (!importModule) { - ctx.Diags.diagnose(SourceLoc(), diag::sema_no_import, moduleName.str()); - if (ctx.SearchPathOpts.SDKPath.empty() && - llvm::Triple(llvm::sys::getProcessTriple()).isMacOSX()) { - ctx.Diags.diagnose(SourceLoc(), diag::sema_no_import_no_sdk); - ctx.Diags.diagnose(SourceLoc(), diag::sema_no_import_no_sdk_xcrun); - } - continue; - } - imports.emplace_back(importModule); - } + llvm::copy(importInfo.AdditionalUnloadedImports, + std::back_inserter(unloadedImports)); // Add any pre-loaded modules. - for (auto &module : importInfo.AdditionalModules) { - imports.emplace_back(module.first, module.second ? ImportFlags::Exported - : ImportOptions()); - } + llvm::copy(importInfo.AdditionalImports, std::back_inserter(imports)); auto *clangImporter = static_cast(ctx.getClangModuleLoader()); @@ -475,63 +479,82 @@ ModuleImplicitImportsRequest::evaluate(Evaluator &evaluator, !clangImporter->importBridgingHeader(bridgingHeaderPath, module)) { auto *headerModule = clangImporter->getImportedHeaderModule(); assert(headerModule && "Didn't load bridging header?"); - imports.emplace_back(headerModule, ImportFlags::Exported); + imports.emplace_back(ImportedModule(headerModule), ImportFlags::Exported); } // Implicitly import the underlying Clang half of this module if needed. if (importInfo.ShouldImportUnderlyingModule) { - auto *underlyingMod = clangImporter->loadModule( - SourceLoc(), ImportPath::Module::Builder(module->getName()).get()); - if (underlyingMod) { - imports.emplace_back(underlyingMod, ImportFlags::Exported); - } else { - ctx.Diags.diagnose(SourceLoc(), diag::error_underlying_module_not_found, - module->getName()); - } + // An @_exported self-import is loaded from ClangImporter instead of being + // rejected; see the special case in getModuleImpl() for details. + ImportPath::Builder importPath(module->getName()); + unloadedImports.emplace_back(UnloadedImportedModule(importPath.copyTo(ctx), + /*isScoped=*/false), + ImportFlags::Exported); + } + + return { ctx.AllocateCopy(imports), ctx.AllocateCopy(unloadedImports) }; +} + +void ImportResolver::addImplicitImports() { + auto implicitImports = SF.getParentModule()->getImplicitImports(); + + // TODO: Support cross-module imports. + for (auto &import : implicitImports.imports) { + assert(!(SF.Kind == SourceFileKind::SIL && + import.module.importedModule->isStdlibModule())); + boundImports.push_back(import); } - return ctx.AllocateCopy(imports); + for (auto &unloadedImport : implicitImports.unloadedImports) + unboundImports.emplace_back(unloadedImport); + + bindPendingImports(); } +UnboundImport::UnboundImport(AttributedImport implicit) + : import(implicit), importLoc(), + importOrUnderlyingModuleDecl(static_cast(nullptr)) {} + //===----------------------------------------------------------------------===// // MARK: Import validation (except for scoped imports) //===----------------------------------------------------------------------===// /// Create an UnboundImport for a user-written import declaration. UnboundImport::UnboundImport(ImportDecl *ID) - : importLoc(ID->getLoc()), options(), privateImportFileName(), - modulePath(ID->getModulePath()), accessPath(ID->getAccessPath()), - importOrUnderlyingModuleDecl(ID) + : import(UnloadedImportedModule(ID->getImportPath(), ID->getImportKind()), + {}), + importLoc(ID->getLoc()), importOrUnderlyingModuleDecl(ID) { if (ID->isExported()) - options |= ImportFlags::Exported; + import.options |= ImportFlags::Exported; if (ID->getAttrs().hasAttribute()) - options |= ImportFlags::Testable; + import.options |= ImportFlags::Testable; if (ID->getAttrs().hasAttribute()) - options |= ImportFlags::ImplementationOnly; + import.options |= ImportFlags::ImplementationOnly; if (auto *privateImportAttr = ID->getAttrs().getAttribute()) { - options |= ImportFlags::PrivateImport; - privateImportFileName = privateImportAttr->getSourceFile(); + import.options |= ImportFlags::PrivateImport; + import.sourceFileArg = privateImportAttr->getSourceFile(); } SmallVector spiGroups; for (auto attr : ID->getAttrs().getAttributes()) { - options |= SourceFile::ImportFlags::SPIAccessControl; + import.options |= ImportFlags::SPIAccessControl; auto attrSPIs = attr->getSPIGroups(); spiGroups.append(attrSPIs.begin(), attrSPIs.end()); } - this->spiGroups = ID->getASTContext().AllocateCopy(spiGroups); + import.spiGroups = ID->getASTContext().AllocateCopy(spiGroups); } bool UnboundImport::checkNotTautological(const SourceFile &SF) { // Exit early if this is not a self-import. + auto modulePath = import.module.getModulePath(); if (modulePath.front().Item != SF.getParentModule()->getName() || // Overlays use an @_exported self-import to load their clang module. - options.contains(ImportFlags::Exported) || + import.options.contains(ImportFlags::Exported) || // Imports of your own submodules are allowed in cross-language libraries. modulePath.size() != 1 || // SIL files self-import to get decls from the rest of the module. @@ -555,24 +578,8 @@ bool UnboundImport::checkModuleLoaded(ModuleDecl *M, SourceFile &SF) { if (M) return true; - ASTContext &ctx = SF.getASTContext(); - - SmallString<64> modulePathStr; - llvm::interleave(modulePath, [&](ImportPath::Element elem) { - modulePathStr += elem.Item.str(); - }, - [&] { modulePathStr += "."; }); - - auto diagKind = diag::sema_no_import; - if (ctx.LangOpts.DebuggerSupport) - diagKind = diag::sema_no_import_repl; - ctx.Diags.diagnose(importLoc, diagKind, modulePathStr); - - if (ctx.SearchPathOpts.SDKPath.empty() && - llvm::Triple(llvm::sys::getProcessTriple()).isMacOSX()) { - ctx.Diags.diagnose(SourceLoc(), diag::sema_no_import_no_sdk); - ctx.Diags.diagnose(SourceLoc(), diag::sema_no_import_no_sdk_xcrun); - } + diagnoseNoSuchModule(SF.getParentModule(), importLoc, + import.module.getModulePath(), /*nonfatalInREPL=*/true); return false; } @@ -602,7 +609,7 @@ void UnboundImport::validatePrivate(ModuleDecl *topLevelModule) { assert(topLevelModule); ASTContext &ctx = topLevelModule->getASTContext(); - if (!options.contains(ImportFlags::PrivateImport)) + if (!import.options.contains(ImportFlags::PrivateImport)) return; if (topLevelModule->arePrivateImportsEnabled()) @@ -610,16 +617,16 @@ void UnboundImport::validatePrivate(ModuleDecl *topLevelModule) { diagnoseInvalidAttr(DAK_PrivateImport, ctx.Diags, diag::module_not_compiled_for_private_import); - privateImportFileName = StringRef(); + import.sourceFileArg = StringRef(); } void UnboundImport::validateImplementationOnly(ASTContext &ctx) { - if (!options.contains(ImportFlags::ImplementationOnly) || - !options.contains(ImportFlags::Exported)) + if (!import.options.contains(ImportFlags::ImplementationOnly) || + !import.options.contains(ImportFlags::Exported)) return; // Remove one flag to maintain the invariant. - options -= ImportFlags::ImplementationOnly; + import.options -= ImportFlags::ImplementationOnly; diagnoseInvalidAttr(DAK_ImplementationOnly, ctx.Diags, diag::import_implementation_cannot_be_exported); @@ -629,7 +636,7 @@ void UnboundImport::validateTestable(ModuleDecl *topLevelModule) { assert(topLevelModule); ASTContext &ctx = topLevelModule->getASTContext(); - if (!options.contains(ImportFlags::Testable) || + if (!import.options.contains(ImportFlags::Testable) || topLevelModule->isTestingEnabled() || topLevelModule->isNonSwiftModule() || !ctx.LangOpts.EnableTestableAttrRequiresTestableModule) @@ -640,7 +647,7 @@ void UnboundImport::validateTestable(ModuleDecl *topLevelModule) { void UnboundImport::validateResilience(NullablePtr topLevelModule, SourceFile &SF) { - if (options.contains(ImportFlags::ImplementationOnly)) + if (import.options.contains(ImportFlags::ImplementationOnly)) return; // Per getTopLevelModule(), we'll only get nullptr here for non-Swift modules, @@ -653,7 +660,7 @@ void UnboundImport::validateResilience(NullablePtr topLevelModule, return; ASTContext &ctx = SF.getASTContext(); - ctx.Diags.diagnose(modulePath.front().Loc, + ctx.Diags.diagnose(import.module.getModulePath().front().Loc, diag::module_not_compiled_with_library_evolution, topLevelModule.get()->getName(), SF.getParentModule()->getName()); @@ -664,8 +671,8 @@ void UnboundImport::validateResilience(NullablePtr topLevelModule, void UnboundImport::diagnoseInvalidAttr(DeclAttrKind attrKind, DiagnosticEngine &diags, Diag diagID) { - auto diag = diags.diagnose(modulePath.front().Loc, diagID, - modulePath.front().Item); + auto diag = diags.diagnose(import.module.getModulePath().front().Loc, diagID, + import.module.getModulePath().front().Item); auto *ID = getImportDecl().getPtrOrNull(); if (!ID) return; @@ -861,7 +868,8 @@ ScopedImportLookupRequest::evaluate(Evaluator &evaluator, SmallVector decls; lookupInModule(topLevelModule, accessPath.front().Item, decls, NLKind::QualifiedLookup, ResolutionKind::Overloadable, - import->getDeclContext()->getModuleScopeContext()); + import->getDeclContext()->getModuleScopeContext(), + NL_QualifiedDefault); auto importLoc = import->getLoc(); if (decls.empty()) { @@ -917,29 +925,41 @@ ScopedImportLookupRequest::evaluate(Evaluator &evaluator, // MARK: Cross-import overlays //===----------------------------------------------------------------------===// -static bool canCrossImport(const ImportedModuleDesc &import) { - if (import.importOptions.contains(ImportFlags::Testable)) +static bool canCrossImport(const AttributedImport &import) { + if (import.options.contains(ImportFlags::Testable)) return false; - if (import.importOptions.contains(ImportFlags::PrivateImport)) + if (import.options.contains(ImportFlags::PrivateImport)) return false; return true; } +static UnloadedImportedModule makeUnimportedCrossImportOverlay( + ASTContext &ctx, + Identifier overlayName, + const UnboundImport &base, + const AttributedImport &declaringImport) { + ImportPath::Builder + builder(overlayName, base.import.module.getModulePath()[0].Loc); + + // If the declaring import was scoped, inherit that scope in the overlay's + // import. + llvm::copy(declaringImport.module.accessPath, std::back_inserter(builder)); + + // Cross-imports are not backed by an ImportDecl, so we need to provide + // our own storage for their module paths. + return UnloadedImportedModule(builder.copyTo(ctx), + /*isScoped=*/!declaringImport.module.accessPath.empty()); +} + /// Create an UnboundImport for a cross-import overlay. -UnboundImport::UnboundImport(ASTContext &ctx, const UnboundImport &base, - Identifier overlayName, - const ImportedModuleDesc &declaringImport, - const ImportedModuleDesc &bystandingImport) - : importLoc(base.importLoc), options(), privateImportFileName(), - // Cross-imports are not backed by an ImportDecl, so we need to provide - // our own storage for their module paths. - modulePath( - ImportPath::Module::Builder(overlayName, base.modulePath[0].Loc) - .copyTo(ctx)), - // If the declaring import was scoped, inherit that scope in the - // overlay's import. - accessPath(declaringImport.module.accessPath), +UnboundImport::UnboundImport( + ASTContext &ctx, const UnboundImport &base, Identifier overlayName, + const AttributedImport &declaringImport, + const AttributedImport &bystandingImport) + : import(makeUnimportedCrossImportOverlay(ctx, overlayName, base, + declaringImport), {}), + importLoc(base.importLoc), importOrUnderlyingModuleDecl(declaringImport.module.importedModule) { // A cross-import is never private or testable, and never comes from a private @@ -947,19 +967,19 @@ UnboundImport::UnboundImport(ASTContext &ctx, const UnboundImport &base, assert(canCrossImport(declaringImport)); assert(canCrossImport(bystandingImport)); - auto &declaringOptions = declaringImport.importOptions; - auto &bystandingOptions = bystandingImport.importOptions; + auto &declaringOptions = declaringImport.options; + auto &bystandingOptions = bystandingImport.options; // If both are exported, the cross-import is exported. if (declaringOptions.contains(ImportFlags::Exported) && bystandingOptions.contains(ImportFlags::Exported)) - options |= ImportFlags::Exported; + import.options |= ImportFlags::Exported; // If either are implementation-only, the cross-import is // implementation-only. if (declaringOptions.contains(ImportFlags::ImplementationOnly) || bystandingOptions.contains(ImportFlags::ImplementationOnly)) - options |= ImportFlags::ImplementationOnly; + import.options |= ImportFlags::ImplementationOnly; } void ImportResolver::crossImport(ModuleDecl *M, UnboundImport &I) { @@ -1037,8 +1057,8 @@ void ImportResolver::crossImport(ModuleDecl *M, UnboundImport &I) { } void ImportResolver::findCrossImportsInLists( - UnboundImport &I, ArrayRef declaring, - ArrayRef bystanding, + UnboundImport &I, ArrayRef> declaring, + ArrayRef> bystanding, bool shouldDiagnoseRedundantCrossImports) { for (auto &declaringImport : declaring) { if (!canCrossImport(declaringImport)) @@ -1055,8 +1075,9 @@ void ImportResolver::findCrossImportsInLists( } void ImportResolver::findCrossImports( - UnboundImport &I, const ImportedModuleDesc &declaringImport, - const ImportedModuleDesc &bystandingImport, + UnboundImport &I, + const AttributedImport &declaringImport, + const AttributedImport &bystandingImport, bool shouldDiagnoseRedundantCrossImports) { assert(&declaringImport != &bystandingImport); @@ -1109,7 +1130,7 @@ void ImportResolver::findCrossImports( name); LLVM_DEBUG({ - auto &crossImportOptions = unboundImports.back().options; + auto &crossImportOptions = unboundImports.back().import.options; llvm::dbgs() << " "; if (crossImportOptions.contains(ImportFlags::Exported)) llvm::dbgs() << "@_exported "; @@ -1125,7 +1146,8 @@ static bool isSubmodule(ModuleDecl* M) { return clangMod && clangMod->Parent; } -void ImportResolver::addCrossImportableModules(ImportedModuleDesc importDesc) { +void ImportResolver::addCrossImportableModules( + AttributedImport importDesc) { // FIXME: namelookup::getAllImports() doesn't quite do what we need (mainly // w.r.t. scoped imports), but it seems like we could extend it to do so, and // then eliminate most of this. diff --git a/lib/Sema/InstrumenterSupport.cpp b/lib/Sema/InstrumenterSupport.cpp index 1041651892ff2..fae3b3149f152 100644 --- a/lib/Sema/InstrumenterSupport.cpp +++ b/lib/Sema/InstrumenterSupport.cpp @@ -119,7 +119,7 @@ bool InstrumenterBase::doTypeCheckImpl(ASTContext &Ctx, DeclContext *DC, DiagnosticSuppression suppression(Ctx.Diags); ErrorGatherer errorGatherer(Ctx.Diags); - TypeChecker::typeCheckExpression(parsedExpr, DC); + TypeChecker::typeCheckExpression(parsedExpr, DC, /*contextualInfo=*/{}); if (parsedExpr) { ErrorFinder errorFinder; diff --git a/lib/Sema/InstrumenterSupport.h b/lib/Sema/InstrumenterSupport.h index 1197a950eefc5..dc009b40f1e99 100644 --- a/lib/Sema/InstrumenterSupport.h +++ b/lib/Sema/InstrumenterSupport.h @@ -16,7 +16,6 @@ //===----------------------------------------------------------------------===// #include "TypeChecker.h" - #include "swift/AST/ASTWalker.h" namespace swift { diff --git a/lib/Sema/LookupVisibleDecls.cpp b/lib/Sema/LookupVisibleDecls.cpp index ff55e59230569..e02dcbec499f1 100644 --- a/lib/Sema/LookupVisibleDecls.cpp +++ b/lib/Sema/LookupVisibleDecls.cpp @@ -693,11 +693,13 @@ static void lookupVisibleMemberDeclsImpl( } } + auto lookupTy = BaseTy; + const auto synthesizeAndLookupTypeMembers = [&](NominalTypeDecl *NTD) { synthesizeMemberDeclsForLookup(NTD, CurrDC); // Look in for members of a nominal type. - lookupTypeMembers(BaseTy, BaseTy, Consumer, CurrDC, LS, Reason); + lookupTypeMembers(BaseTy, lookupTy, Consumer, CurrDC, LS, Reason); }; llvm::SmallPtrSet Ancestors; @@ -725,7 +727,7 @@ static void lookupVisibleMemberDeclsImpl( Ancestors.insert(CD); Reason = getReasonForSuper(Reason); - BaseTy = CD->getSuperclass(); + lookupTy = CD->getSuperclass(); LS = LS.withOnSuperclass(); if (CD->inheritsSuperclassInitializers()) @@ -734,7 +736,7 @@ static void lookupVisibleMemberDeclsImpl( // Look into the inheritance chain. do { - const auto CurClass = BaseTy->getClassOrBoundGenericClass(); + const auto CurClass = lookupTy->getClassOrBoundGenericClass(); // FIXME: This path is no substitute for an actual circularity check. // The real fix is to check that the superclass doesn't introduce a @@ -744,10 +746,10 @@ static void lookupVisibleMemberDeclsImpl( synthesizeAndLookupTypeMembers(CurClass); - BaseTy = CurClass->getSuperclass(); + lookupTy = CurClass->getSuperclass(); if (!CurClass->inheritsSuperclassInitializers()) LS = LS.withoutInheritsSuperclassInitializers(); - } while (BaseTy); + } while (lookupTy); } swift::DynamicLookupInfo::DynamicLookupInfo( diff --git a/lib/Sema/MiscDiagnostics.cpp b/lib/Sema/MiscDiagnostics.cpp index a9b3996b90523..8b61dc4b21d7a 100644 --- a/lib/Sema/MiscDiagnostics.cpp +++ b/lib/Sema/MiscDiagnostics.cpp @@ -15,7 +15,6 @@ //===----------------------------------------------------------------------===// #include "MiscDiagnostics.h" -#include "ConstraintSystem.h" #include "TypeCheckAvailability.h" #include "TypeCheckConcurrency.h" #include "TypeChecker.h" @@ -29,6 +28,7 @@ #include "swift/Basic/StringExtras.h" #include "swift/Parse/Lexer.h" #include "swift/Parse/Parser.h" +#include "swift/Sema/ConstraintSystem.h" #include "swift/Sema/IDETypeChecking.h" #include "llvm/ADT/MapVector.h" #include "llvm/ADT/StringSwitch.h" @@ -2282,6 +2282,8 @@ bool swift::computeFixitsForOverridenDeclaration( namespace { class VarDeclUsageChecker : public ASTWalker { + DeclContext *DC; + DiagnosticEngine &Diags; // Keep track of some information about a variable. enum { @@ -2318,7 +2320,8 @@ class VarDeclUsageChecker : public ASTWalker { void operator=(const VarDeclUsageChecker &) = delete; public: - VarDeclUsageChecker(DiagnosticEngine &Diags) : Diags(Diags) {} + VarDeclUsageChecker(DeclContext *DC, + DiagnosticEngine &Diags) : DC(DC), Diags(Diags) {} // After we have scanned the entire region, diagnose variables that could be // declared with a narrower usage kind. @@ -3140,7 +3143,10 @@ std::pair VarDeclUsageChecker::walkToExprPre(Expr *E) { void VarDeclUsageChecker::handleIfConfig(IfConfigDecl *ICD) { struct ConservativeDeclMarker : public ASTWalker { VarDeclUsageChecker &VDUC; - ConservativeDeclMarker(VarDeclUsageChecker &VDUC) : VDUC(VDUC) {} + SourceFile *SF; + + ConservativeDeclMarker(VarDeclUsageChecker &VDUC) + : VDUC(VDUC), SF(VDUC.DC->getParentSourceFile()) {} Expr *walkToExprPost(Expr *E) override { // If we see a bound reference to a decl in an inactive #if block, then @@ -3148,6 +3154,16 @@ void VarDeclUsageChecker::handleIfConfig(IfConfigDecl *ICD) { // unused" and "could be marked let" warnings for it. if (auto *DRE = dyn_cast(E)) VDUC.addMark(DRE->getDecl(), RK_Read|RK_Written); + else if (auto *declRef = dyn_cast(E)) { + auto name = declRef->getName(); + auto loc = declRef->getLoc(); + if (name.isSimpleName() && loc.isValid()) { + auto *varDecl = dyn_cast_or_null( + ASTScope::lookupSingleLocalDecl(SF, name.getFullName(), loc)); + if (varDecl) + VDUC.addMark(varDecl, RK_Read|RK_Written); + } + } return E; } }; @@ -3166,7 +3182,7 @@ void VarDeclUsageChecker::handleIfConfig(IfConfigDecl *ICD) { void swift:: performTopLevelDeclDiagnostics(TopLevelCodeDecl *TLCD) { auto &ctx = TLCD->getDeclContext()->getASTContext(); - VarDeclUsageChecker checker(ctx.Diags); + VarDeclUsageChecker checker(TLCD, ctx.Diags); TLCD->walk(checker); } @@ -3181,7 +3197,7 @@ void swift::performAbstractFuncDeclDiagnostics(AbstractFunctionDecl *AFD) { // be checked as part of their parent function or TopLevelCodeDecl. if (!AFD->getDeclContext()->isLocalContext()) { auto &ctx = AFD->getDeclContext()->getASTContext(); - VarDeclUsageChecker checker(ctx.Diags); + VarDeclUsageChecker checker(AFD, ctx.Diags); AFD->walk(checker); } @@ -4591,7 +4607,9 @@ void swift::performSyntacticExprDiagnostics(const Expr *E, checkActorIsolation(E, DC); } -void swift::performStmtDiagnostics(ASTContext &ctx, const Stmt *S) { +void swift::performStmtDiagnostics(const Stmt *S, DeclContext *DC) { + auto &ctx = DC->getASTContext(); + TypeChecker::checkUnsupportedProtocolType(ctx, const_cast(S)); if (auto switchStmt = dyn_cast(S)) @@ -4603,6 +4621,9 @@ void swift::performStmtDiagnostics(ASTContext &ctx, const Stmt *S) { if (auto *lcs = dyn_cast(S)) for (const auto &elt : lcs->getCond()) checkImplicitPromotionsInCondition(elt, ctx); + + if (!ctx.LangOpts.DisableAvailabilityChecking) + diagAvailability(S, const_cast(DC)); } //===----------------------------------------------------------------------===// @@ -4846,7 +4867,8 @@ Optional TypeChecker::omitNeedlessWords(AbstractFunctionDecl *afd) { getTypeNameForOmission(resultType), getTypeNameForOmission(contextType), paramTypes, returnsSelf, false, - /*allPropertyNames=*/nullptr, scratch)) + /*allPropertyNames=*/nullptr, + None, None, scratch)) return None; /// Retrieve a replacement identifier. @@ -4901,7 +4923,7 @@ Optional TypeChecker::omitNeedlessWords(VarDecl *var) { OmissionTypeName contextTypeName = getTypeNameForOmission(contextType); if (::omitNeedlessWords(name, { }, "", typeName, contextTypeName, { }, /*returnsSelf=*/false, true, - /*allPropertyNames=*/nullptr, scratch)) { + /*allPropertyNames=*/nullptr, None, None, scratch)) { return Context.getIdentifier(name); } diff --git a/lib/Sema/MiscDiagnostics.h b/lib/Sema/MiscDiagnostics.h index 7c385c52ae913..2a5a1313ba80d 100644 --- a/lib/Sema/MiscDiagnostics.h +++ b/lib/Sema/MiscDiagnostics.h @@ -38,7 +38,7 @@ void performSyntacticExprDiagnostics(const Expr *E, const DeclContext *DC, bool isExprStmt); /// Emit diagnostics for a given statement. -void performStmtDiagnostics(ASTContext &ctx, const Stmt *S); +void performStmtDiagnostics(const Stmt *S, DeclContext *DC); void performAbstractFuncDeclDiagnostics(AbstractFunctionDecl *AFD); diff --git a/lib/Sema/PCMacro.cpp b/lib/Sema/PCMacro.cpp index 8750a9d968a33..75fb221cc3147 100644 --- a/lib/Sema/PCMacro.cpp +++ b/lib/Sema/PCMacro.cpp @@ -187,8 +187,8 @@ class Instrumenter : InstrumenterBase { transformStmtCondition(SC, GS->getStartLoc()); GS->setCond(SC); - if (Stmt *BS = GS->getBody()) - GS->setBody(transformStmt(BS)); + if (BraceStmt *BS = GS->getBody()) + GS->setBody(transformBraceStmt(BS)); return GS; } diff --git a/lib/Sema/PlaygroundTransform.cpp b/lib/Sema/PlaygroundTransform.cpp index 6bf259c4a1aeb..610f48b7e56cc 100644 --- a/lib/Sema/PlaygroundTransform.cpp +++ b/lib/Sema/PlaygroundTransform.cpp @@ -204,8 +204,8 @@ class Instrumenter : InstrumenterBase { } GuardStmt *transformGuardStmt(GuardStmt *GS) { - if (Stmt *BS = GS->getBody()) - GS->setBody(transformStmt(BS)); + if (BraceStmt *BS = GS->getBody()) + GS->setBody(transformBraceStmt(BS)); return GS; } diff --git a/lib/Sema/PreCheckExpr.cpp b/lib/Sema/PreCheckExpr.cpp new file mode 100644 index 0000000000000..32a05f091e5c9 --- /dev/null +++ b/lib/Sema/PreCheckExpr.cpp @@ -0,0 +1,1899 @@ +//===--- PreCheckExpr.cpp - Expression pre-checking pass ------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Pre-checking resolves unqualified name references, type expressions and +// operators. +// +//===----------------------------------------------------------------------===// + +#include "TypeChecker.h" +#include "TypeCheckType.h" +#include "TypoCorrection.h" +#include "swift/AST/ASTVisitor.h" +#include "swift/AST/ASTWalker.h" +#include "swift/AST/DiagnosticsParse.h" +#include "swift/AST/NameLookup.h" +#include "swift/AST/NameLookupRequests.h" +#include "swift/AST/ParameterList.h" +#include "swift/AST/PropertyWrappers.h" +#include "swift/AST/ProtocolConformance.h" +#include "swift/AST/SubstitutionMap.h" +#include "swift/AST/TypeCheckRequests.h" +#include "swift/Parse/Confusables.h" +#include "swift/Parse/Lexer.h" +#include "swift/Sema/ConstraintSystem.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallVector.h" + +using namespace swift; +using namespace constraints; + +//===----------------------------------------------------------------------===// +// High-level entry points. +//===----------------------------------------------------------------------===// + +static unsigned getNumArgs(ValueDecl *value) { + if (auto *func = dyn_cast(value)) + return func->getParameters()->size(); + return ~0U; +} + +static bool matchesDeclRefKind(ValueDecl *value, DeclRefKind refKind) { + switch (refKind) { + // An ordinary reference doesn't ignore anything. + case DeclRefKind::Ordinary: + return true; + + // A binary-operator reference only honors FuncDecls with a certain type. + case DeclRefKind::BinaryOperator: + return (getNumArgs(value) == 2); + + case DeclRefKind::PrefixOperator: + return (!value->getAttrs().hasAttribute() && + getNumArgs(value) == 1); + + case DeclRefKind::PostfixOperator: + return (value->getAttrs().hasAttribute() && + getNumArgs(value) == 1); + } + llvm_unreachable("bad declaration reference kind"); +} + +static bool containsDeclRefKind(LookupResult &lookupResult, + DeclRefKind refKind) { + for (auto candidate : lookupResult) { + ValueDecl *D = candidate.getValueDecl(); + if (!D) + continue; + if (matchesDeclRefKind(D, refKind)) + return true; + } + return false; +} + +/// Emit a diagnostic with a fixit hint for an invalid binary operator, showing +/// how to split it according to splitCandidate. +static void diagnoseBinOpSplit(ASTContext &Context, UnresolvedDeclRefExpr *UDRE, + std::pair splitCandidate, + Diag diagID) { + + unsigned splitLoc = splitCandidate.first; + bool isBinOpFirst = splitCandidate.second; + StringRef nameStr = UDRE->getName().getBaseIdentifier().str(); + auto startStr = nameStr.substr(0, splitLoc); + auto endStr = nameStr.drop_front(splitLoc); + + // One valid split found, it is almost certainly the right answer. + auto diag = Context.Diags.diagnose( + UDRE->getLoc(), diagID, Context.getIdentifier(startStr), + Context.getIdentifier(endStr), isBinOpFirst); + // Highlight the whole operator. + diag.highlight(UDRE->getLoc()); + // Insert whitespace on the left if the binop is at the start, or to the + // right if it is end. + if (isBinOpFirst) + diag.fixItInsert(UDRE->getLoc(), " "); + else + diag.fixItInsertAfter(UDRE->getLoc(), " "); + + // Insert a space between the operators. + diag.fixItInsert(UDRE->getLoc().getAdvancedLoc(splitLoc), " "); +} + +/// If we failed lookup of a binary operator, check to see it to see if +/// it is a binary operator juxtaposed with a unary operator (x*-4) that +/// needs whitespace. If so, emit specific diagnostics for it and return true, +/// otherwise return false. +static bool diagnoseOperatorJuxtaposition(UnresolvedDeclRefExpr *UDRE, + DeclContext *DC) { + Identifier name = UDRE->getName().getBaseIdentifier(); + StringRef nameStr = name.str(); + if (!name.isOperator() || nameStr.size() < 2) + return false; + + bool isBinOp = UDRE->getRefKind() == DeclRefKind::BinaryOperator; + + // If this is a binary operator, relex the token, to decide whether it has + // whitespace around it or not. If it does "x +++ y", then it isn't likely to + // be a case where a space was forgotten. + auto &Context = DC->getASTContext(); + if (isBinOp) { + auto tok = Lexer::getTokenAtLocation(Context.SourceMgr, UDRE->getLoc()); + if (tok.getKind() != tok::oper_binary_unspaced) + return false; + } + + // Okay, we have a failed lookup of a multicharacter operator. Check to see if + // lookup succeeds if part is split off, and record the matches found. + // + // In the case of a binary operator, the bool indicated is false if the + // first half of the split is the unary operator (x!*4) or true if it is the + // binary operator (x*+4). + std::vector> WorkableSplits; + + // Check all the potential splits. + for (unsigned splitLoc = 1, e = nameStr.size(); splitLoc != e; ++splitLoc) { + // For it to be a valid split, the start and end section must be valid + // operators, splitting a unicode code point isn't kosher. + auto startStr = nameStr.substr(0, splitLoc); + auto endStr = nameStr.drop_front(splitLoc); + if (!Lexer::isOperator(startStr) || !Lexer::isOperator(endStr)) + continue; + + DeclNameRef startName(Context.getIdentifier(startStr)); + DeclNameRef endName(Context.getIdentifier(endStr)); + + // Perform name lookup for the first and second pieces. If either fail to + // be found, then it isn't a valid split. + auto startLookup = TypeChecker::lookupUnqualified( + DC, startName, UDRE->getLoc(), defaultUnqualifiedLookupOptions); + if (!startLookup) continue; + auto endLookup = TypeChecker::lookupUnqualified(DC, endName, UDRE->getLoc(), + defaultUnqualifiedLookupOptions); + if (!endLookup) continue; + + // If the overall operator is a binary one, then we're looking at + // juxtaposed binary and unary operators. + if (isBinOp) { + // Look to see if the candidates found could possibly match. + if (containsDeclRefKind(startLookup, DeclRefKind::PostfixOperator) && + containsDeclRefKind(endLookup, DeclRefKind::BinaryOperator)) + WorkableSplits.push_back({ splitLoc, false }); + + if (containsDeclRefKind(startLookup, DeclRefKind::BinaryOperator) && + containsDeclRefKind(endLookup, DeclRefKind::PrefixOperator)) + WorkableSplits.push_back({ splitLoc, true }); + } else { + // Otherwise, it is two of the same kind, e.g. "!!x" or "!~x". + if (containsDeclRefKind(startLookup, UDRE->getRefKind()) && + containsDeclRefKind(endLookup, UDRE->getRefKind())) + WorkableSplits.push_back({ splitLoc, false }); + } + } + + switch (WorkableSplits.size()) { + case 0: + // No splits found, can't produce this diagnostic. + return false; + case 1: + // One candidate: produce an error with a fixit on it. + if (isBinOp) + diagnoseBinOpSplit(Context, UDRE, WorkableSplits[0], + diag::unspaced_binary_operator_fixit); + else + Context.Diags.diagnose( + UDRE->getLoc().getAdvancedLoc(WorkableSplits[0].first), + diag::unspaced_unary_operator); + return true; + + default: + // Otherwise, we have to produce a series of notes listing the various + // options. + Context.Diags + .diagnose(UDRE->getLoc(), isBinOp ? diag::unspaced_binary_operator + : diag::unspaced_unary_operator) + .highlight(UDRE->getLoc()); + + if (isBinOp) { + for (auto candidateSplit : WorkableSplits) + diagnoseBinOpSplit(Context, UDRE, candidateSplit, + diag::unspaced_binary_operators_candidate); + } + return true; + } +} + +static bool diagnoseRangeOperatorMisspell(DiagnosticEngine &Diags, + UnresolvedDeclRefExpr *UDRE) { + auto name = UDRE->getName().getBaseIdentifier(); + if (!name.isOperator()) + return false; + + auto corrected = StringRef(); + if (name.str() == ".." || name.str() == "...." || + name.str() == ".…" || name.str() == "…" || name.str() == "….") + corrected = "..."; + else if (name.str() == "...<" || name.str() == "....<" || + name.str() == "…<") + corrected = "..<"; + + if (!corrected.empty()) { + Diags + .diagnose(UDRE->getLoc(), diag::cannot_find_in_scope_corrected, + UDRE->getName(), true, corrected) + .highlight(UDRE->getSourceRange()) + .fixItReplace(UDRE->getSourceRange(), corrected); + + return true; + } + return false; +} + +static bool diagnoseIncDecOperator(DiagnosticEngine &Diags, + UnresolvedDeclRefExpr *UDRE) { + auto name = UDRE->getName().getBaseIdentifier(); + if (!name.isOperator()) + return false; + + auto corrected = StringRef(); + if (name.str() == "++") + corrected = "+= 1"; + else if (name.str() == "--") + corrected = "-= 1"; + + if (!corrected.empty()) { + Diags + .diagnose(UDRE->getLoc(), diag::cannot_find_in_scope_corrected, + UDRE->getName(), true, corrected) + .highlight(UDRE->getSourceRange()); + + return true; + } + return false; +} + +static bool findNonMembers(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 (!isValid(D)) + return false; + + if (matchesDeclRefKind(D, refKind)) + ResultValues.push_back(D); + } + + return AllDeclRefs; +} + +/// Find the next element in a chain of members. If this expression is (or +/// could be) the base of such a chain, this will return \c nullptr. +static Expr *getMemberChainSubExpr(Expr *expr) { + assert(expr && "getMemberChainSubExpr called with null expr!"); + if (auto *UDE = dyn_cast(expr)) { + return UDE->getBase(); + } else if (auto *CE = dyn_cast(expr)) { + return CE->getFn(); + } else if (auto *BOE = dyn_cast(expr)) { + return BOE->getSubExpr(); + } else if (auto *FVE = dyn_cast(expr)) { + return FVE->getSubExpr(); + } else if (auto *SE = dyn_cast(expr)) { + return SE->getBase(); + } else if (auto *CCE = dyn_cast(expr)) { + return CCE->getBase(); + } else { + return nullptr; + } +} + +UnresolvedMemberExpr *TypeChecker::getUnresolvedMemberChainBase(Expr *expr) { + if (auto *subExpr = getMemberChainSubExpr(expr)) + return getUnresolvedMemberChainBase(subExpr); + else + return dyn_cast(expr); +} + +/// Whether this expression is a member of a member chain. +static bool isMemberChainMember(Expr *expr) { + return getMemberChainSubExpr(expr) != nullptr; +} +/// Whether this expression sits at the end of a chain of member accesses. +static bool isMemberChainTail(Expr *expr, Expr *parent) { + assert(expr && "isMemberChainTail called with null expr!"); + // If this expression's parent is not itself part of a chain (or, this expr + // has no parent expr), this must be the tail of the chain. + return parent == nullptr || !isMemberChainMember(parent); +} + +/// Bind an UnresolvedDeclRefExpr by performing name lookup and +/// returning the resultant expression. Context is the DeclContext used +/// for the lookup. +Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, + DeclContext *DC, + bool replaceInvalidRefsWithErrors) { + // Process UnresolvedDeclRefExpr by doing an unqualified lookup. + DeclNameRef Name = UDRE->getName(); + SourceLoc Loc = UDRE->getLoc(); + + auto errorResult = [&]() -> Expr * { + if (replaceInvalidRefsWithErrors) + return new (DC->getASTContext()) ErrorExpr(UDRE->getSourceRange()); + return UDRE; + }; + + // Perform standard value name lookup. + NameLookupOptions lookupOptions = defaultUnqualifiedLookupOptions; + // TODO: Include all of the possible members to give a solver a + // chance to diagnose name shadowing which requires explicit + // name/module qualifier to access top-level name. + lookupOptions |= NameLookupFlags::IncludeOuterResults; + + LookupResult Lookup; + + bool AllDeclRefs = true; + SmallVector ResultValues; + + auto &Context = DC->getASTContext(); + + // First, look for a local binding in scope. + if (Loc.isValid() && !Name.isOperator()) { + SmallVector localDecls; + ASTScope::lookupLocalDecls(DC->getParentSourceFile(), + Name.getFullName(), Loc, + /*stopAfterInnermostBraceStmt=*/false, + ResultValues); + for (auto *localDecl : ResultValues) { + Lookup.add(LookupResultEntry(localDecl), /*isOuter=*/false); + } + } + + if (!Lookup) { + // Now, look for all local bindings, even forward references, as well + // as type members and top-level declarations. + if (Loc.isInvalid()) + DC = DC->getModuleScopeContext(); + + Lookup = TypeChecker::lookupUnqualified(DC, Name, Loc, lookupOptions); + + ValueDecl *localDeclAfterUse = nullptr; + auto isValid = [&](ValueDecl *D) { + // If we find something in the current context, it must be a forward + // reference, because otherwise if it was in scope, it would have + // been returned by the call to ASTScope::lookupLocalDecls() above. + if (D->getDeclContext()->isLocalContext() && + D->getDeclContext() == DC && + (Context.LangOpts.DisableParserLookup || + (Loc.isValid() && D->getLoc().isValid() && + Context.SourceMgr.isBeforeInBuffer(Loc, D->getLoc()) && + !isa(D)))) { + localDeclAfterUse = D; + return false; + } + return true; + }; + AllDeclRefs = + findNonMembers(Lookup.innerResults(), UDRE->getRefKind(), + /*breakOnMember=*/true, ResultValues, isValid); + + // If local declaration after use is found, check outer results for + // better matching candidates. + if (ResultValues.empty() && localDeclAfterUse) { + auto innerDecl = localDeclAfterUse; + while (localDeclAfterUse) { + if (Lookup.outerResults().empty()) { + Context.Diags.diagnose(Loc, diag::use_local_before_declaration, Name); + Context.Diags.diagnose(innerDecl, diag::decl_declared_here, + localDeclAfterUse->getName()); + Expr *error = new (Context) ErrorExpr(UDRE->getSourceRange()); + return error; + } + + Lookup.shiftDownResults(); + ResultValues.clear(); + localDeclAfterUse = nullptr; + AllDeclRefs = + findNonMembers(Lookup.innerResults(), UDRE->getRefKind(), + /*breakOnMember=*/true, ResultValues, isValid); + } + } + } + + if (!Lookup) { + // If we failed lookup of an operator, check to see if this is a range + // operator misspelling. Otherwise try to diagnose a juxtaposition + // e.g. (x*-4) that needs whitespace. + if (diagnoseRangeOperatorMisspell(Context.Diags, UDRE) || + diagnoseIncDecOperator(Context.Diags, UDRE) || + diagnoseOperatorJuxtaposition(UDRE, DC)) { + return errorResult(); + } + + // Try ignoring access control. + NameLookupOptions relookupOptions = lookupOptions; + relookupOptions |= NameLookupFlags::IgnoreAccessControl; + auto inaccessibleResults = + TypeChecker::lookupUnqualified(DC, Name, Loc, relookupOptions); + if (inaccessibleResults) { + // FIXME: What if the unviable candidates have different levels of access? + const ValueDecl *first = inaccessibleResults.front().getValueDecl(); + Context.Diags.diagnose( + Loc, diag::candidate_inaccessible, first->getBaseName(), + first->getFormalAccessScope().accessLevelForDiagnostics()); + + // FIXME: If any of the candidates (usually just one) are in the same + // module we could offer a fix-it. + for (auto lookupResult : inaccessibleResults) { + auto *VD = lookupResult.getValueDecl(); + VD->diagnose(diag::decl_declared_here, VD->getName()); + } + + // Don't try to recover here; we'll get more access-related diagnostics + // downstream if the type of the inaccessible decl is also inaccessible. + return errorResult(); + } + + // TODO: Name will be a compound name if it was written explicitly as + // one, but we should also try to propagate labels into this. + DeclNameLoc nameLoc = UDRE->getNameLoc(); + + Identifier simpleName = Name.getBaseIdentifier(); + const char *buffer = simpleName.get(); + llvm::SmallString<64> expectedIdentifier; + bool isConfused = false; + uint32_t codepoint; + uint32_t firstConfusableCodepoint = 0; + int totalCodepoints = 0; + int offset = 0; + while ((codepoint = validateUTF8CharacterAndAdvance(buffer, + buffer + + strlen(buffer))) + != ~0U) { + int length = (buffer - simpleName.get()) - offset; + if (auto expectedCodepoint = + confusable::tryConvertConfusableCharacterToASCII(codepoint)) { + if (firstConfusableCodepoint == 0) { + firstConfusableCodepoint = codepoint; + } + isConfused = true; + expectedIdentifier += expectedCodepoint; + } else { + expectedIdentifier += (char)codepoint; + } + + totalCodepoints++; + + offset += length; + } + + auto emitBasicError = [&] { + Context.Diags + .diagnose(Loc, diag::cannot_find_in_scope, Name, + Name.isOperator()) + .highlight(UDRE->getSourceRange()); + }; + + if (!isConfused) { + if (Name.isSimpleName(Context.Id_Self)) { + if (DeclContext *typeContext = DC->getInnermostTypeContext()){ + Type SelfType = typeContext->getSelfInterfaceType(); + + if (typeContext->getSelfClassDecl()) + SelfType = DynamicSelfType::get(SelfType, Context); + SelfType = DC->mapTypeIntoContext(SelfType); + return new (Context) + TypeExpr(new (Context) FixedTypeRepr(SelfType, Loc)); + } + } + + TypoCorrectionResults corrections(Name, nameLoc); + TypeChecker::performTypoCorrection(DC, UDRE->getRefKind(), Type(), + lookupOptions, corrections); + + if (auto typo = corrections.claimUniqueCorrection()) { + auto diag = Context.Diags.diagnose( + Loc, diag::cannot_find_in_scope_corrected, Name, + Name.isOperator(), typo->CorrectedName.getBaseIdentifier().str()); + diag.highlight(UDRE->getSourceRange()); + typo->addFixits(diag); + } else { + emitBasicError(); + } + + corrections.noteAllCandidates(); + } else { + emitBasicError(); + + if (totalCodepoints == 1) { + auto charNames = confusable::getConfusableAndBaseCodepointNames( + firstConfusableCodepoint); + Context.Diags + .diagnose(Loc, diag::single_confusable_character, + UDRE->getName().isOperator(), simpleName.str(), + charNames.first, expectedIdentifier, charNames.second) + .fixItReplace(Loc, expectedIdentifier); + } else { + Context.Diags + .diagnose(Loc, diag::confusable_character, + UDRE->getName().isOperator(), simpleName.str(), + expectedIdentifier) + .fixItReplace(Loc, expectedIdentifier); + } + } + + // TODO: consider recovering from here. We may want some way to suppress + // downstream diagnostics, though. + + return errorResult(); + } + + // FIXME: Need to refactor the way we build an AST node from a lookup result! + + // If we have an unambiguous reference to a type decl, form a TypeExpr. + if (Lookup.size() == 1 && UDRE->getRefKind() == DeclRefKind::Ordinary && + isa(Lookup[0].getValueDecl())) { + auto *D = cast(Lookup[0].getValueDecl()); + // FIXME: This is odd. + if (isa(D)) { + return new (Context) DeclRefExpr(D, UDRE->getNameLoc(), + /*Implicit=*/false, + AccessSemantics::Ordinary, + D->getInterfaceType()); + } + + auto *LookupDC = Lookup[0].getDeclContext(); + if (UDRE->isImplicit()) { + return TypeExpr::createImplicitForDecl( + UDRE->getNameLoc(), D, LookupDC, + LookupDC->mapTypeIntoContext(D->getInterfaceType())); + } else { + return TypeExpr::createForDecl(UDRE->getNameLoc(), D, LookupDC); + } + } + + if (AllDeclRefs) { + // Diagnose uses of operators that found no matching candidates. + if (ResultValues.empty()) { + assert(UDRE->getRefKind() != DeclRefKind::Ordinary); + Context.Diags.diagnose( + Loc, diag::use_nonmatching_operator, Name, + UDRE->getRefKind() == DeclRefKind::BinaryOperator + ? 0 + : UDRE->getRefKind() == DeclRefKind::PrefixOperator ? 1 : 2); + return new (Context) ErrorExpr(UDRE->getSourceRange()); + } + + // For operators, sort the results so that non-generic operations come + // first. + // Note: this is part of a performance hack to prefer non-generic operators + // to generic operators, because the former is far more efficient to check. + if (UDRE->getRefKind() != DeclRefKind::Ordinary) { + std::stable_sort(ResultValues.begin(), ResultValues.end(), + [&](ValueDecl *x, ValueDecl *y) -> bool { + auto xGeneric = x->getInterfaceType()->getAs(); + auto yGeneric = y->getInterfaceType()->getAs(); + if (static_cast(xGeneric) != static_cast(yGeneric)) { + return xGeneric? false : true; + } + + if (!xGeneric) + return false; + + unsigned xDepth = xGeneric->getGenericParams().back()->getDepth(); + unsigned yDepth = yGeneric->getGenericParams().back()->getDepth(); + return xDepth < yDepth; + }); + } + + return buildRefExpr(ResultValues, DC, UDRE->getNameLoc(), + UDRE->isImplicit(), UDRE->getFunctionRefKind()); + } + + ResultValues.clear(); + bool AllMemberRefs = true; + ValueDecl *Base = nullptr; + DeclContext *BaseDC = nullptr; + for (auto Result : Lookup) { + auto ThisBase = Result.getBaseDecl(); + + // Track the base for member declarations. + if (ThisBase && !isa(ThisBase)) { + auto Value = Result.getValueDecl(); + ResultValues.push_back(Value); + if (Base && ThisBase != Base) { + AllMemberRefs = false; + break; + } + + Base = ThisBase; + BaseDC = Result.getDeclContext(); + continue; + } + + AllMemberRefs = false; + break; + } + + if (AllMemberRefs) { + Expr *BaseExpr; + if (auto PD = dyn_cast(Base)) { + auto selfParam = PD->getGenericParams()->getParams().front(); + BaseExpr = TypeExpr::createImplicitForDecl( + UDRE->getNameLoc(), selfParam, + /*DC*/ nullptr, + DC->mapTypeIntoContext(selfParam->getInterfaceType())); + } else if (auto NTD = dyn_cast(Base)) { + BaseExpr = TypeExpr::createImplicitForDecl( + UDRE->getNameLoc(), NTD, BaseDC, + DC->mapTypeIntoContext(NTD->getInterfaceType())); + } else { + BaseExpr = new (Context) DeclRefExpr(Base, UDRE->getNameLoc(), + /*Implicit=*/true); + } + + llvm::SmallVector outerAlternatives; + (void)findNonMembers(Lookup.outerResults(), UDRE->getRefKind(), + /*breakOnMember=*/false, outerAlternatives, + /*isValid=*/[](ValueDecl *choice) -> bool { + return !choice->isInvalid(); + }); + + // Otherwise, form an UnresolvedDotExpr and sema will resolve it based on + // type information. + 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 + // very broken, but it's still conceivable that this may happen due to + // invalid shadowed declarations. + // + // Make sure we emit a diagnostic, since returning an ErrorExpr without + // producing one will break things downstream. + Context.Diags.diagnose(Loc, diag::ambiguous_decl_ref, Name); + for (auto Result : Lookup) { + auto *Decl = Result.getValueDecl(); + Context.Diags.diagnose(Decl, diag::decl_declared_here, Decl->getName()); + } + return new (Context) ErrorExpr(UDRE->getSourceRange()); +} + +/// If an expression references 'self.init' or 'super.init' in an +/// initializer context, returns the implicit 'self' decl of the constructor. +/// Otherwise, return nil. +VarDecl * +TypeChecker::getSelfForInitDelegationInConstructor(DeclContext *DC, + UnresolvedDotExpr *ctorRef) { + // If the reference isn't to a constructor, we're done. + if (ctorRef->getName().getBaseName() != DeclBaseName::createConstructor()) + return nullptr; + + if (auto ctorContext = + dyn_cast_or_null(DC->getInnermostMethodContext())) { + auto nestedArg = ctorRef->getBase(); + if (auto inout = dyn_cast(nestedArg)) + nestedArg = inout->getSubExpr(); + if (nestedArg->isSuperExpr()) + return ctorContext->getImplicitSelfDecl(); + if (auto declRef = dyn_cast(nestedArg)) + if (declRef->getDecl()->getName() == DC->getASTContext().Id_self) + return ctorContext->getImplicitSelfDecl(); + } + return nullptr; +} + +namespace { + /// Update the function reference kind based on adding a direct call to a + /// callee with this kind. + FunctionRefKind addingDirectCall(FunctionRefKind kind) { + switch (kind) { + case FunctionRefKind::Unapplied: + return FunctionRefKind::SingleApply; + + case FunctionRefKind::SingleApply: + case FunctionRefKind::DoubleApply: + return FunctionRefKind::DoubleApply; + + case FunctionRefKind::Compound: + return FunctionRefKind::Compound; + } + + llvm_unreachable("Unhandled FunctionRefKind in switch."); + } + + /// Update a direct callee expression node that has a function reference kind + /// based on seeing a call to this callee. + templategetFunctionRefKind())> + void tryUpdateDirectCalleeImpl(E *callee, int) { + callee->setFunctionRefKind(addingDirectCall(callee->getFunctionRefKind())); + } + + /// Version of tryUpdateDirectCalleeImpl for when the callee + /// expression type doesn't carry a reference. + template + void tryUpdateDirectCalleeImpl(E *callee, ...) { } + + /// The given expression is the direct callee of a call expression; mark it to + /// indicate that it has been called. + void markDirectCallee(Expr *callee) { + while (true) { + // Look through identity expressions. + if (auto identity = dyn_cast(callee)) { + callee = identity->getSubExpr(); + continue; + } + + // Look through unresolved 'specialize' expressions. + if (auto specialize = dyn_cast(callee)) { + callee = specialize->getSubExpr(); + continue; + } + + // Look through optional binding. + if (auto bindOptional = dyn_cast(callee)) { + callee = bindOptional->getSubExpr(); + continue; + } + + // Look through forced binding. + if (auto force = dyn_cast(callee)) { + callee = force->getSubExpr(); + continue; + } + + // Calls compose. + if (auto call = dyn_cast(callee)) { + callee = call->getFn(); + continue; + } + + // We're done. + break; + } + + // Cast the callee to its most-specific class, then try to perform an + // update. If the expression node has a declaration reference in it, the + // update will succeed. Otherwise, we're done propagating. + switch (callee->getKind()) { +#define EXPR(Id, Parent) \ + case ExprKind::Id: \ + tryUpdateDirectCalleeImpl(cast(callee), 0); \ + break; +#include "swift/AST/ExprNodes.def" + } + } + + class PreCheckExpression : public ASTWalker { + ASTContext &Ctx; + DeclContext *DC; + + Expr *ParentExpr; + + /// Indicates whether pre-check is allowed to insert + /// implicit `ErrorExpr` in place of invalid references. + bool UseErrorExprs; + + /// A stack of expressions being walked, used to determine where to + /// insert RebindSelfInConstructorExpr nodes. + llvm::SmallVector ExprStack; + + /// The 'self' variable to use when rebinding 'self' in a constructor. + VarDecl *UnresolvedCtorSelf = nullptr; + + /// The expression that will be wrapped by a RebindSelfInConstructorExpr + /// node when visited. + Expr *UnresolvedCtorRebindTarget = nullptr; + + /// The expressions that are direct arguments of call expressions. + llvm::SmallPtrSet CallArgs; + + /// Simplify expressions which are type sugar productions that got parsed + /// as expressions due to the parser not knowing which identifiers are + /// type names. + TypeExpr *simplifyTypeExpr(Expr *E); + + /// Simplify unresolved dot expressions which are nested type productions. + TypeExpr *simplifyNestedTypeExpr(UnresolvedDotExpr *UDE); + + TypeExpr *simplifyUnresolvedSpecializeExpr(UnresolvedSpecializeExpr *USE); + + /// Simplify a key path expression into a canonical form. + void resolveKeyPathExpr(KeyPathExpr *KPE); + + /// Simplify constructs like `UInt32(1)` into `1 as UInt32` if + /// the type conforms to the expected literal protocol. + Expr *simplifyTypeConstructionWithLiteralArg(Expr *E); + + /// In Swift < 5, diagnose and correct invalid multi-argument or + /// argument-labeled interpolations. + void correctInterpolationIfStrange(InterpolatedStringLiteralExpr *ISLE) { + // These expressions are valid in Swift 5+. + if (getASTContext().isSwiftVersionAtLeast(5)) + return; + + /// Diagnoses appendInterpolation(...) calls with multiple + /// arguments or argument labels and corrects them. + class StrangeInterpolationRewriter : public ASTWalker { + ASTContext &Context; + + public: + StrangeInterpolationRewriter(ASTContext &Ctx) : Context(Ctx) {} + + virtual bool walkToDeclPre(Decl *D) override { + // We don't want to look inside decls. + return false; + } + + virtual std::pair walkToExprPre(Expr *E) override { + // One InterpolatedStringLiteralExpr should never be nested inside + // another except as a child of a CallExpr, and we don't recurse into + // the children of CallExprs. + assert(!isa(E) && + "StrangeInterpolationRewriter found nested interpolation?"); + + // We only care about CallExprs. + if (!isa(E)) + return { true, E }; + + auto call = cast(E); + if (auto callee = dyn_cast(call->getFn())) { + if (callee->getName().getBaseName() == + Context.Id_appendInterpolation) { + Expr *newArg = nullptr; + SourceLoc lParen, rParen; + + if (call->getNumArguments() > 1) { + auto *args = cast(call->getArg()); + + lParen = args->getLParenLoc(); + rParen = args->getRParenLoc(); + Expr *secondArg = args->getElement(1); + + Context.Diags + .diagnose(secondArg->getLoc(), + diag::string_interpolation_list_changing) + .highlightChars(secondArg->getLoc(), rParen); + Context.Diags + .diagnose(secondArg->getLoc(), + diag::string_interpolation_list_insert_parens) + .fixItInsertAfter(lParen, "(") + .fixItInsert(rParen, ")"); + + newArg = args; + } + else if(call->getNumArguments() == 1 && + call->getArgumentLabels().front() != Identifier()) { + auto *args = cast(call->getArg()); + newArg = args->getElement(0); + + lParen = args->getLParenLoc(); + rParen = args->getRParenLoc(); + + SourceLoc argLabelLoc = call->getArgumentLabelLoc(0), + argLoc = newArg->getStartLoc(); + + Context.Diags + .diagnose(argLabelLoc, + diag::string_interpolation_label_changing) + .highlightChars(argLabelLoc, argLoc); + Context.Diags + .diagnose(argLabelLoc, + diag::string_interpolation_remove_label, + call->getArgumentLabels().front()) + .fixItRemoveChars(argLabelLoc, argLoc); + } + + // If newArg is no longer null, we need to build a new + // appendInterpolation(_:) call that takes it to replace the bad + // appendInterpolation(...) call. + if (newArg) { + auto newCallee = new (Context) UnresolvedDotExpr( + callee->getBase(), /*dotloc=*/SourceLoc(), + DeclNameRef(Context.Id_appendInterpolation), + /*nameloc=*/DeclNameLoc(), /*Implicit=*/true); + + E = CallExpr::create(Context, newCallee, lParen, {newArg}, + {Identifier()}, {SourceLoc()}, rParen, + /*trailingClosures=*/{}, + /*implicit=*/false); + } + } + } + + // There is never a CallExpr between an InterpolatedStringLiteralExpr + // and an un-typechecked appendInterpolation(...) call, so whether we + // changed E or not, we don't need to recurse any deeper. + return { false, E }; + } + }; + + ISLE->getAppendingExpr()->walk( + StrangeInterpolationRewriter(getASTContext())); + } + + public: + PreCheckExpression(DeclContext *dc, Expr *parent, + bool replaceInvalidRefsWithErrors) + : Ctx(dc->getASTContext()), DC(dc), ParentExpr(parent), + UseErrorExprs(replaceInvalidRefsWithErrors) {} + + ASTContext &getASTContext() const { return Ctx; } + + bool walkToClosureExprPre(ClosureExpr *expr); + + bool shouldWalkCaptureInitializerExpressions() override { return true; } + + VarDecl *getImplicitSelfDeclForSuperContext(SourceLoc Loc) { + auto *methodContext = DC->getInnermostMethodContext(); + if (!methodContext) { + Ctx.Diags.diagnose(Loc, diag::super_not_in_class_method); + return nullptr; + } + + // Do an actual lookup for 'self' in case it shows up in a capture list. + auto *methodSelf = methodContext->getImplicitSelfDecl(); + auto *lookupSelf = ASTScope::lookupSingleLocalDecl(DC->getParentSourceFile(), + Ctx.Id_self, Loc); + if (lookupSelf && lookupSelf != methodSelf) { + // FIXME: This is the wrong diagnostic for if someone manually declares a + // variable named 'self' using backticks. + Ctx.Diags.diagnose(Loc, diag::super_in_closure_with_capture); + Ctx.Diags.diagnose(lookupSelf->getLoc(), + diag::super_in_closure_with_capture_here); + return nullptr; + } + + return methodSelf; + } + + std::pair walkToExprPre(Expr *expr) override { + // If this is a call, record the argument expression. + if (auto call = dyn_cast(expr)) { + if (!isa(expr)) { + CallArgs.insert(call->getArg()); + } + } + + // FIXME(diagnostics): `InOutType` could appear here as a result + // of successful re-typecheck of the one of the sub-expressions e.g. + // `let _: Int = { (s: inout S) in s.bar() }`. On the first + // attempt to type-check whole expression `s.bar()` - is going + // to have a base which points directly to declaration of `S`. + // But when diagnostics attempts to type-check `s.bar()` standalone + // its base would be tranformed into `InOutExpr -> DeclRefExr`, + // and `InOutType` is going to be recorded in constraint system. + // One possible way to fix this (if diagnostics still use typecheck) + // might be to make it so self is not wrapped into `InOutExpr` + // but instead used as @lvalue type in some case of mutable members. + if (!expr->isImplicit()) { + if (isa(expr) || isa(expr)) { + auto *LE = cast(expr); + if (auto *IOE = dyn_cast(LE->getBase())) + LE->setBase(IOE->getSubExpr()); + } + + if (auto *DSCE = dyn_cast(expr)) { + if (auto *IOE = dyn_cast(DSCE->getBase())) + DSCE->setBase(IOE->getSubExpr()); + } + } + + // Local function used to finish up processing before returning. Every + // return site should call through here. + auto finish = [&](bool recursive, Expr *expr) { + // If we're going to recurse, record this expression on the stack. + if (recursive) + ExprStack.push_back(expr); + + return std::make_pair(recursive, expr); + }; + + // Resolve 'super' references. + if (auto *superRef = dyn_cast(expr)) { + auto loc = superRef->getLoc(); + auto *selfDecl = getImplicitSelfDeclForSuperContext(loc); + if (selfDecl == nullptr) + return finish(true, new (Ctx) ErrorExpr(loc)); + + superRef->setSelf(selfDecl); + return finish(true, superRef); + } + + // For closures, type-check the patterns and result type as written, + // but do not walk into the body. That will be type-checked after + // we've determine the complete function type. + if (auto closure = dyn_cast(expr)) + return finish(walkToClosureExprPre(closure), expr); + + if (auto unresolved = dyn_cast(expr)) { + TypeChecker::checkForForbiddenPrefix( + getASTContext(), unresolved->getName().getBaseName()); + return finish(true, TypeChecker::resolveDeclRefExpr(unresolved, DC, + UseErrorExprs)); + } + + // Let's try to figure out if `InOutExpr` is out of place early + // otherwise there is a risk of producing solutions which can't + // be later applied to AST and would result in the crash in some + // cases. Such expressions are only allowed in argument positions + // of function/operator calls. + if (isa(expr)) { + // If this is an implicit `inout` expression we assume that + // compiler knowns what it's doing. + if (expr->isImplicit()) + return finish(true, expr); + + auto parents = ParentExpr->getParentMap(); + + auto result = parents.find(expr); + if (result != parents.end()) { + auto *parent = result->getSecond(); + + if (isa(parent)) + return finish(true, expr); + + if (isa(parent) || isa(parent)) { + auto call = parents.find(parent); + if (call != parents.end()) { + if (isa(call->getSecond()) || + isa(call->getSecond())) + return finish(true, expr); + + if (isa(call->getSecond())) { + getASTContext().Diags.diagnose( + expr->getStartLoc(), + diag::cannot_pass_inout_arg_to_subscript); + return finish(false, nullptr); + } + } + } + } + + getASTContext().Diags.diagnose(expr->getStartLoc(), + diag::extraneous_address_of); + return finish(false, nullptr); + } + + if (auto *ISLE = dyn_cast(expr)) + correctInterpolationIfStrange(ISLE); + + return finish(true, expr); + } + + Expr *walkToExprPost(Expr *expr) override { + // Remove this expression from the stack. + assert(ExprStack.back() == expr); + ExprStack.pop_back(); + + // Mark the direct callee as being a callee. + if (auto *call = dyn_cast(expr)) + markDirectCallee(call->getFn()); + + // Fold sequence expressions. + if (auto *seqExpr = dyn_cast(expr)) { + auto result = TypeChecker::foldSequence(seqExpr, DC); + return result->walk(*this); + } + + // Type check the type parameters in an UnresolvedSpecializeExpr. + if (auto *us = dyn_cast(expr)) { + if (auto *typeExpr = simplifyUnresolvedSpecializeExpr(us)) + return typeExpr; + } + + // If we're about to step out of a ClosureExpr, restore the DeclContext. + if (auto *ce = dyn_cast(expr)) { + assert(DC == ce && "DeclContext imbalance"); + DC = ce->getParent(); + } + + // A 'self.init' or 'super.init' application inside a constructor will + // evaluate to void, with the initializer's result implicitly rebound + // to 'self'. Recognize the unresolved constructor expression and + // determine where to place the RebindSelfInConstructorExpr node. + // When updating this logic, also update + // RebindSelfInConstructorExpr::getCalledConstructor. + auto &ctx = getASTContext(); + if (auto unresolvedDot = dyn_cast(expr)) { + if (auto self = TypeChecker::getSelfForInitDelegationInConstructor( + DC, unresolvedDot)) { + // Walk our ancestor expressions looking for the appropriate place + // to insert the RebindSelfInConstructorExpr. + Expr *target = nullptr; + bool foundApply = false; + bool foundRebind = false; + for (auto ancestor : llvm::reverse(ExprStack)) { + if (isa(ancestor)) { + // If we already have a rebind, then we're re-typechecking an + // expression and are done. + foundRebind = true; + break; + } + + // Recognize applications. + if (auto apply = dyn_cast(ancestor)) { + // If we already saw an application, we're done. + if (foundApply) + break; + + // If the function being called is not our unresolved initializer + // reference, we're done. + if (apply->getSemanticFn() != unresolvedDot) + break; + + foundApply = true; + target = ancestor; + continue; + } + + // Look through identity, force-value, and 'try' expressions. + if (isa(ancestor) || + isa(ancestor) || + isa(ancestor)) { + if (!CallArgs.count(ancestor)) { + if (target) + target = ancestor; + continue; + } + } + + // No other expression kinds are permitted. + break; + } + + // If we found a rebind target, note the insertion point. + if (target && !foundRebind) { + UnresolvedCtorRebindTarget = target; + UnresolvedCtorSelf = self; + } + } + } + + // If the expression we've found is the intended target of an + // RebindSelfInConstructorExpr, wrap it in the + // RebindSelfInConstructorExpr. + if (expr == UnresolvedCtorRebindTarget) { + expr = new (ctx) + RebindSelfInConstructorExpr(expr, UnresolvedCtorSelf); + UnresolvedCtorRebindTarget = nullptr; + return expr; + } + + // Double check if there are any BindOptionalExpr remaining in the + // tree (see comment below for more details), if there are no BOE + // expressions remaining remove OptionalEvaluationExpr from the tree. + if (auto OEE = dyn_cast(expr)) { + bool hasBindOptional = false; + OEE->forEachChildExpr([&](Expr *expr) -> Expr * { + if (isa(expr)) + hasBindOptional = true; + // If at least a single BOE was found, no reason + // to walk any further in the tree. + return hasBindOptional ? nullptr : expr; + }); + + return hasBindOptional ? OEE : OEE->getSubExpr(); + } + + // Check if there are any BindOptionalExpr in the tree which + // wrap DiscardAssignmentExpr, such situation corresponds to syntax + // like - `_? = `, since it doesn't really make + // sense to have optional assignment to discarded LValue which can + // never be optional, we can remove BOE from the tree and avoid + // generating any of the unnecessary constraints. + if (auto BOE = dyn_cast(expr)) { + if (auto DAE = dyn_cast(BOE->getSubExpr())) + return DAE; + } + + // If this is a sugared type that needs to be folded into a single + // TypeExpr, do it. + if (auto *simplified = simplifyTypeExpr(expr)) + return simplified; + + if (auto KPE = dyn_cast(expr)) { + resolveKeyPathExpr(KPE); + return KPE; + } + + if (auto *simplified = simplifyTypeConstructionWithLiteralArg(expr)) + return simplified; + + // If we find an unresolved member chain, wrap it in an + // UnresolvedMemberChainResultExpr (unless this has already been done). + auto *parent = Parent.getAsExpr(); + if (isMemberChainTail(expr, parent)) + if (auto *UME = TypeChecker::getUnresolvedMemberChainBase(expr)) + if (!parent || !isa(parent)) + return new (ctx) UnresolvedMemberChainResultExpr(expr, UME); + + return expr; + } + + std::pair walkToStmtPre(Stmt *stmt) override { + return { true, stmt }; + } + }; +} // end anonymous namespace + +/// Perform prechecking of a ClosureExpr before we dive into it. This returns +/// true when we want the body to be considered part of this larger expression. +bool PreCheckExpression::walkToClosureExprPre(ClosureExpr *closure) { + auto *PL = closure->getParameters(); + + // Validate the parameters. + bool hadParameterError = false; + + // If we encounter an error validating the parameter list, don't bail. + // Instead, go on to validate any potential result type, and bail + // afterwards. This allows for better diagnostics, and keeps the + // closure expression type well-formed. + for (auto param : *PL) { + hadParameterError |= param->isInvalid(); + } + + if (hadParameterError) + return false; + + // If we won't be checking the body of the closure, don't walk into it here. + if (!shouldTypeCheckInEnclosingExpression(closure)) + return false; + + // Update the current DeclContext to be the closure we're about to + // recurse into. + assert((closure->getParent() == DC || + closure->getParent()->isChildContextOf(DC)) && + "Decl context isn't correct"); + DC = closure; + return true; +} + +TypeExpr *PreCheckExpression::simplifyNestedTypeExpr(UnresolvedDotExpr *UDE) { + if (!UDE->getName().isSimpleName() || + UDE->getName().isSpecial()) + return nullptr; + + auto Name = UDE->getName(); + auto NameLoc = UDE->getNameLoc().getBaseNameLoc(); + + // Qualified type lookup with a module base is represented as a DeclRefExpr + // and not a TypeExpr. + if (auto *DRE = dyn_cast(UDE->getBase())) { + if (auto *TD = dyn_cast(DRE->getDecl())) { + // See if the type has a member type with this name. + auto Result = TypeChecker::lookupMemberType( + DC, TD->getDeclaredInterfaceType(), Name, + defaultMemberLookupOptions); + + // If there is no nested type with this name, we have a lookup of + // a non-type member, so leave the expression as-is. + if (Result.size() == 1) { + return TypeExpr::createForMemberDecl( + DRE->getNameLoc(), TD, UDE->getNameLoc(), Result.front().Member); + } + } + + return nullptr; + } + + auto *TyExpr = dyn_cast(UDE->getBase()); + if (!TyExpr) + return nullptr; + + auto *InnerTypeRepr = TyExpr->getTypeRepr(); + if (!InnerTypeRepr) + return nullptr; + + // Fold 'T.Protocol' into a protocol metatype. + if (Name.isSimpleName(getASTContext().Id_Protocol)) { + auto *NewTypeRepr = + new (getASTContext()) ProtocolTypeRepr(InnerTypeRepr, NameLoc); + return new (getASTContext()) TypeExpr(NewTypeRepr); + } + + // Fold 'T.Type' into an existential metatype if 'T' is a protocol, + // or an ordinary metatype otherwise. + if (Name.isSimpleName(getASTContext().Id_Type)) { + auto *NewTypeRepr = + new (getASTContext()) MetatypeTypeRepr(InnerTypeRepr, NameLoc); + return new (getASTContext()) TypeExpr(NewTypeRepr); + } + + // Fold 'T.U' into a nested type. + if (auto *ITR = dyn_cast(InnerTypeRepr)) { + // Resolve the TypeRepr to get the base type for the lookup. + const auto options = + TypeResolutionOptions(TypeResolverContext::InExpression); + const auto resolution = + TypeResolution::forContextual(DC, options, [](auto unboundTy) { + // FIXME: Don't let unbound generic types escape type resolution. + // For now, just return the unbound generic type. + return unboundTy; + }); + const auto BaseTy = resolution.resolveType(InnerTypeRepr); + + if (BaseTy->mayHaveMembers()) { + // See if there is a member type with this name. + auto Result = + TypeChecker::lookupMemberType(DC, BaseTy, Name, + defaultMemberLookupOptions); + + // If there is no nested type with this name, we have a lookup of + // a non-type member, so leave the expression as-is. + if (Result.size() == 1) { + return TypeExpr::createForMemberDecl(ITR, UDE->getNameLoc(), + Result.front().Member); + } + } + } + + return nullptr; +} + +TypeExpr *PreCheckExpression::simplifyUnresolvedSpecializeExpr( + UnresolvedSpecializeExpr *us) { + // If this is a reference type a specialized type, form a TypeExpr. + // The base should be a TypeExpr that we already resolved. + if (auto *te = dyn_cast(us->getSubExpr())) { + if (auto *ITR = dyn_cast_or_null(te->getTypeRepr())) { + return TypeExpr::createForSpecializedDecl(ITR, + us->getUnresolvedParams(), + SourceRange(us->getLAngleLoc(), + us->getRAngleLoc()), + getASTContext()); + } + } + + return nullptr; +} + +/// Simplify expressions which are type sugar productions that got parsed +/// as expressions due to the parser not knowing which identifiers are +/// type names. +TypeExpr *PreCheckExpression::simplifyTypeExpr(Expr *E) { + // Don't try simplifying a call argument, because we don't want to + // simplify away the required ParenExpr/TupleExpr. + if (CallArgs.count(E) > 0) return nullptr; + + // Fold member types. + if (auto *UDE = dyn_cast(E)) { + return simplifyNestedTypeExpr(UDE); + } + + // Fold T? into an optional type when T is a TypeExpr. + if (isa(E) || isa(E)) { + TypeExpr *TyExpr; + SourceLoc QuestionLoc; + if (auto *OOE = dyn_cast(E)) { + TyExpr = dyn_cast(OOE->getSubExpr()); + QuestionLoc = OOE->getLoc(); + } else { + TyExpr = dyn_cast(cast(E)->getSubExpr()); + QuestionLoc = cast(E)->getQuestionLoc(); + } + if (!TyExpr) return nullptr; + + auto *InnerTypeRepr = TyExpr->getTypeRepr(); + assert(!TyExpr->isImplicit() && InnerTypeRepr && + "This doesn't work on implicit TypeExpr's, " + "the TypeExpr should have been built correctly in the first place"); + + // The optional evaluation is passed through. + if (isa(E)) + return TyExpr; + + auto *NewTypeRepr = + new (getASTContext()) OptionalTypeRepr(InnerTypeRepr, QuestionLoc); + return new (getASTContext()) TypeExpr(NewTypeRepr); + } + + // Fold T! into an IUO type when T is a TypeExpr. + if (auto *FVE = dyn_cast(E)) { + auto *TyExpr = dyn_cast(FVE->getSubExpr()); + if (!TyExpr) return nullptr; + + auto *InnerTypeRepr = TyExpr->getTypeRepr(); + assert(!TyExpr->isImplicit() && InnerTypeRepr && + "This doesn't work on implicit TypeExpr's, " + "the TypeExpr should have been built correctly in the first place"); + + auto *NewTypeRepr = new (getASTContext()) + ImplicitlyUnwrappedOptionalTypeRepr(InnerTypeRepr, + FVE->getExclaimLoc()); + return new (getASTContext()) TypeExpr(NewTypeRepr); + } + + // Fold (T) into a type T with parens around it. + if (auto *PE = dyn_cast(E)) { + auto *TyExpr = dyn_cast(PE->getSubExpr()); + if (!TyExpr) return nullptr; + + TupleTypeReprElement InnerTypeRepr[] = { TyExpr->getTypeRepr() }; + assert(!TyExpr->isImplicit() && InnerTypeRepr[0].Type && + "SubscriptExpr doesn't work on implicit TypeExpr's, " + "the TypeExpr should have been built correctly in the first place"); + + auto *NewTypeRepr = TupleTypeRepr::create(getASTContext(), InnerTypeRepr, + PE->getSourceRange()); + return new (getASTContext()) TypeExpr(NewTypeRepr); + } + + // Fold a tuple expr like (T1,T2) into a tuple type (T1,T2). + if (auto *TE = dyn_cast(E)) { + if (TE->hasTrailingClosure() || + // FIXME: Decide what to do about (). It could be a type or an expr. + TE->getNumElements() == 0) + return nullptr; + + SmallVector Elts; + unsigned EltNo = 0; + for (auto Elt : TE->getElements()) { + auto *eltTE = dyn_cast(Elt); + if (!eltTE) return nullptr; + TupleTypeReprElement elt; + assert(eltTE->getTypeRepr() && !eltTE->isImplicit() && + "This doesn't work on implicit TypeExpr's, the " + "TypeExpr should have been built correctly in the first place"); + + // If the tuple element has a label, propagate it. + elt.Type = eltTE->getTypeRepr(); + Identifier name = TE->getElementName(EltNo); + if (!name.empty()) { + elt.Name = name; + elt.NameLoc = TE->getElementNameLoc(EltNo); + } + + Elts.push_back(elt); + ++EltNo; + } + auto *NewTypeRepr = TupleTypeRepr::create( + getASTContext(), Elts, TE->getSourceRange(), SourceLoc(), Elts.size()); + return new (getASTContext()) TypeExpr(NewTypeRepr); + } + + + // Fold [T] into an array type. + if (auto *AE = dyn_cast(E)) { + if (AE->getElements().size() != 1) + return nullptr; + + auto *TyExpr = dyn_cast(AE->getElement(0)); + if (!TyExpr) + return nullptr; + + auto *NewTypeRepr = new (getASTContext()) + ArrayTypeRepr(TyExpr->getTypeRepr(), + SourceRange(AE->getLBracketLoc(), AE->getRBracketLoc())); + return new (getASTContext()) TypeExpr(NewTypeRepr); + } + + // Fold [K : V] into a dictionary type. + if (auto *DE = dyn_cast(E)) { + if (DE->getElements().size() != 1) + return nullptr; + + TypeRepr *keyTypeRepr, *valueTypeRepr; + + if (auto EltTuple = dyn_cast(DE->getElement(0))) { + auto *KeyTyExpr = dyn_cast(EltTuple->getElement(0)); + if (!KeyTyExpr) + return nullptr; + + auto *ValueTyExpr = dyn_cast(EltTuple->getElement(1)); + if (!ValueTyExpr) + return nullptr; + + keyTypeRepr = KeyTyExpr->getTypeRepr(); + valueTypeRepr = ValueTyExpr->getTypeRepr(); + } else { + auto *TE = dyn_cast(DE->getElement(0)); + if (!TE) return nullptr; + + auto *TRE = dyn_cast_or_null(TE->getTypeRepr()); + if (!TRE || TRE->getEllipsisLoc().isValid()) return nullptr; + while (TRE->isParenType()) { + TRE = dyn_cast_or_null(TRE->getElementType(0)); + if (!TRE || TRE->getEllipsisLoc().isValid()) return nullptr; + } + + assert(TRE->getElements().size() == 2); + keyTypeRepr = TRE->getElementType(0); + valueTypeRepr = TRE->getElementType(1); + } + + auto *NewTypeRepr = new (getASTContext()) DictionaryTypeRepr( + keyTypeRepr, valueTypeRepr, + /*FIXME:colonLoc=*/SourceLoc(), + SourceRange(DE->getLBracketLoc(), DE->getRBracketLoc())); + return new (getASTContext()) TypeExpr(NewTypeRepr); + } + + // Reinterpret arrow expr T1 -> T2 as function type. + // FIXME: support 'inout', etc. + if (auto *AE = dyn_cast(E)) { + if (!AE->isFolded()) return nullptr; + + auto diagnoseMissingParens = [](ASTContext &ctx, TypeRepr *tyR) { + bool isVoid = false; + if (const auto Void = dyn_cast(tyR)) { + if (Void->getNameRef().isSimpleName(ctx.Id_Void)) { + isVoid = true; + } + } + + if (isVoid) { + ctx.Diags.diagnose(tyR->getStartLoc(), diag::function_type_no_parens) + .fixItReplace(tyR->getStartLoc(), "()"); + } else { + ctx.Diags.diagnose(tyR->getStartLoc(), diag::function_type_no_parens) + .highlight(tyR->getSourceRange()) + .fixItInsert(tyR->getStartLoc(), "(") + .fixItInsertAfter(tyR->getEndLoc(), ")"); + } + }; + + auto &ctx = getASTContext(); + auto extractInputTypeRepr = [&](Expr *E) -> TupleTypeRepr * { + if (!E) + return nullptr; + if (auto *TyE = dyn_cast(E)) { + auto ArgRepr = TyE->getTypeRepr(); + if (auto *TTyRepr = dyn_cast(ArgRepr)) + return TTyRepr; + diagnoseMissingParens(ctx, ArgRepr); + return TupleTypeRepr::create(ctx, {ArgRepr}, ArgRepr->getSourceRange()); + } + if (auto *TE = dyn_cast(E)) + if (TE->getNumElements() == 0) + return TupleTypeRepr::createEmpty(getASTContext(), + TE->getSourceRange()); + + // When simplifying a type expr like "(P1 & P2) -> (P3 & P4) -> Int", + // it may have been folded at the same time; recursively simplify it. + if (auto ArgsTypeExpr = simplifyTypeExpr(E)) { + auto ArgRepr = ArgsTypeExpr->getTypeRepr(); + if (auto *TTyRepr = dyn_cast(ArgRepr)) + return TTyRepr; + diagnoseMissingParens(ctx, ArgRepr); + return TupleTypeRepr::create(ctx, {ArgRepr}, ArgRepr->getSourceRange()); + } + return nullptr; + }; + + auto extractTypeRepr = [&](Expr *E) -> TypeRepr * { + if (!E) + return nullptr; + if (auto *TyE = dyn_cast(E)) + return TyE->getTypeRepr(); + if (auto *TE = dyn_cast(E)) + if (TE->getNumElements() == 0) + return TupleTypeRepr::createEmpty(ctx, TE->getSourceRange()); + + // When simplifying a type expr like "P1 & P2 -> P3 & P4 -> Int", + // it may have been folded at the same time; recursively simplify it. + if (auto ArgsTypeExpr = simplifyTypeExpr(E)) + return ArgsTypeExpr->getTypeRepr(); + return nullptr; + }; + + TupleTypeRepr *ArgsTypeRepr = extractInputTypeRepr(AE->getArgsExpr()); + if (!ArgsTypeRepr) { + ctx.Diags.diagnose(AE->getArgsExpr()->getLoc(), + diag::expected_type_before_arrow); + auto ArgRange = AE->getArgsExpr()->getSourceRange(); + auto ErrRepr = new (ctx) ErrorTypeRepr(ArgRange); + ArgsTypeRepr = + TupleTypeRepr::create(ctx, {ErrRepr}, ArgRange); + } + + TypeRepr *ResultTypeRepr = extractTypeRepr(AE->getResultExpr()); + if (!ResultTypeRepr) { + ctx.Diags.diagnose(AE->getResultExpr()->getLoc(), + diag::expected_type_after_arrow); + ResultTypeRepr = new (ctx) + ErrorTypeRepr(AE->getResultExpr()->getSourceRange()); + } + + auto NewTypeRepr = new (ctx) + FunctionTypeRepr(nullptr, ArgsTypeRepr, AE->getAsyncLoc(), + AE->getThrowsLoc(), AE->getArrowLoc(), ResultTypeRepr); + return new (ctx) TypeExpr(NewTypeRepr); + } + + // Fold 'P & Q' into a composition type + if (auto *binaryExpr = dyn_cast(E)) { + bool isComposition = false; + // look at the name of the operator, if it is a '&' we can create the + // composition TypeExpr + auto fn = binaryExpr->getFn(); + if (auto Overload = dyn_cast(fn)) { + for (auto Decl : Overload->getDecls()) + if (Decl->getBaseName() == "&") { + isComposition = true; + break; + } + } else if (auto *Decl = dyn_cast(fn)) { + if (Decl->getName().isSimpleName() && + Decl->getName().getBaseName() == "&") + isComposition = true; + } + + if (isComposition) { + // The protocols we are composing + SmallVector Types; + + auto lhsExpr = binaryExpr->getArg()->getElement(0); + if (auto *lhs = dyn_cast(lhsExpr)) { + Types.push_back(lhs->getTypeRepr()); + } else if (isa(lhsExpr)) { + // If the lhs is another binary expression, we have a multi element + // composition: 'A & B & C' is parsed as ((A & B) & C); we get + // the protocols from the lhs here + if (auto expr = simplifyTypeExpr(lhsExpr)) + if (auto *repr = dyn_cast(expr->getTypeRepr())) + // add the protocols to our list + for (auto proto : repr->getTypes()) + Types.push_back(proto); + else + return nullptr; + else + return nullptr; + } else + return nullptr; + + // Add the rhs which is just a TypeExpr + auto *rhs = dyn_cast(binaryExpr->getArg()->getElement(1)); + if (!rhs) return nullptr; + Types.push_back(rhs->getTypeRepr()); + + auto CompRepr = CompositionTypeRepr::create(getASTContext(), Types, + lhsExpr->getStartLoc(), + binaryExpr->getSourceRange()); + return new (getASTContext()) TypeExpr(CompRepr); + } + } + + return nullptr; +} + +void PreCheckExpression::resolveKeyPathExpr(KeyPathExpr *KPE) { + if (KPE->isObjC()) + return; + + if (!KPE->getComponents().empty()) + return; + + TypeRepr *rootType = nullptr; + SmallVector components; + auto &DE = getASTContext().Diags; + + // Pre-order visit of a sequence foo.bar[0]?.baz, which means that the + // components are pushed in reverse order. + auto traversePath = [&](Expr *expr, bool isInParsedPath, + bool emitErrors = true) { + Expr *outermostExpr = expr; + // We can end up in scenarios where the key path has contextual type, + // but is missing a leading dot. This can happen when we have an + // implicit TypeExpr or an implicit DeclRefExpr. + auto diagnoseMissingDot = [&]() { + DE.diagnose(expr->getLoc(), + diag::expr_swift_keypath_not_starting_with_dot) + .fixItInsert(expr->getStartLoc(), "."); + }; + while (1) { + // Base cases: we've reached the top. + if (auto TE = dyn_cast(expr)) { + assert(!isInParsedPath); + rootType = TE->getTypeRepr(); + if (TE->isImplicit() && !KPE->expectsContextualRoot()) + diagnoseMissingDot(); + return; + } else if (isa(expr)) { + assert(isInParsedPath); + // Nothing here: the type is either the root, or is inferred. + return; + } else if (!KPE->expectsContextualRoot() && expr->isImplicit() && + isa(expr)) { + assert(!isInParsedPath); + diagnoseMissingDot(); + return; + } + + // Recurring cases: + if (auto SE = dyn_cast(expr)) { + // .self, the identity component. + components.push_back(KeyPathExpr::Component::forIdentity( + SE->getSelfLoc())); + expr = SE->getSubExpr(); + } else if (auto UDE = dyn_cast(expr)) { + // .foo + components.push_back(KeyPathExpr::Component::forUnresolvedProperty( + UDE->getName(), UDE->getLoc())); + + expr = UDE->getBase(); + } else if (auto SE = dyn_cast(expr)) { + // .[0] or just plain [0] + components.push_back( + KeyPathExpr::Component::forUnresolvedSubscriptWithPrebuiltIndexExpr( + getASTContext(), SE->getIndex(), SE->getArgumentLabels(), + SE->getLoc())); + + expr = SE->getBase(); + } else if (auto BOE = dyn_cast(expr)) { + // .? or ? + components.push_back(KeyPathExpr::Component::forUnresolvedOptionalChain( + BOE->getQuestionLoc())); + + expr = BOE->getSubExpr(); + } else if (auto FVE = dyn_cast(expr)) { + // .! or ! + components.push_back(KeyPathExpr::Component::forUnresolvedOptionalForce( + FVE->getExclaimLoc())); + + expr = FVE->getSubExpr(); + } else if (auto OEE = dyn_cast(expr)) { + // Do nothing: this is implied to exist as the last expression, by the + // BindOptionalExprs, but is irrelevant to the components. + (void)outermostExpr; + assert(OEE == outermostExpr); + expr = OEE->getSubExpr(); + } else { + if (emitErrors) { + // \() may be an attempt to write a string interpolation outside + // of a string literal; diagnose this case specially. + if (isa(expr) || isa(expr)) { + DE.diagnose(expr->getLoc(), + diag::expr_string_interpolation_outside_string); + } else { + DE.diagnose(expr->getLoc(), + diag::expr_swift_keypath_invalid_component); + } + } + components.push_back(KeyPathExpr::Component()); + return; + } + } + }; + + auto root = KPE->getParsedRoot(); + auto path = KPE->getParsedPath(); + + if (path) { + traversePath(path, /*isInParsedPath=*/true); + + // This path looks like \Foo.Bar.[0].baz, which means Foo.Bar has to be a + // type. + if (root) { + if (auto TE = dyn_cast(root)) { + rootType = TE->getTypeRepr(); + } else { + // FIXME: Probably better to catch this case earlier and force-eval as + // TypeExpr. + DE.diagnose(root->getLoc(), + diag::expr_swift_keypath_not_starting_with_type); + + // Traverse this path for recovery purposes: it may be a typo like + // \Foo.property.[0]. + traversePath(root, /*isInParsedPath=*/false, + /*emitErrors=*/false); + } + } + } else { + traversePath(root, /*isInParsedPath=*/false); + } + + // Key paths must be spelled with at least one component. + if (components.empty()) { + // Passes further down the pipeline expect keypaths to always have at least + // one component, so stuff an invalid component in the AST for recovery. + components.push_back(KeyPathExpr::Component()); + } + + std::reverse(components.begin(), components.end()); + + KPE->setRootType(rootType); + KPE->resolveComponents(getASTContext(), components); +} + +Expr *PreCheckExpression::simplifyTypeConstructionWithLiteralArg(Expr *E) { + // If constructor call is expected to produce an optional let's not attempt + // this optimization because literal initializers aren't failable. + if (!getASTContext().LangOpts.isSwiftVersionAtLeast(5)) { + if (!ExprStack.empty()) { + auto *parent = ExprStack.back(); + if (isa(parent) || isa(parent)) + return nullptr; + } + } + + auto *call = dyn_cast(E); + if (!call || call->getNumArguments() != 1) + return nullptr; + + auto *typeExpr = dyn_cast(call->getFn()); + if (!typeExpr) + return nullptr; + + auto *argExpr = call->getArg()->getSemanticsProvidingExpr(); + auto *literal = dyn_cast(argExpr); + if (!literal) + return nullptr; + + auto *protocol = TypeChecker::getLiteralProtocol(getASTContext(), literal); + if (!protocol) + return nullptr; + + Type castTy; + if (auto precheckedTy = typeExpr->getInstanceType()) { + castTy = precheckedTy; + } else { + const auto options = + TypeResolutionOptions(TypeResolverContext::InExpression) | + TypeResolutionFlags::SilenceErrors; + + const auto resolution = + TypeResolution::forContextual(DC, options, [](auto unboundTy) { + // FIXME: Don't let unbound generic types escape type resolution. + // For now, just return the unbound generic type. + return unboundTy; + }); + const auto result = resolution.resolveType(typeExpr->getTypeRepr()); + if (result->hasError()) + return nullptr; + castTy = result; + } + + if (!castTy || !castTy->getAnyNominal()) + return nullptr; + + // Don't bother to convert deprecated selector syntax. + if (auto selectorTy = getASTContext().getSelectorType()) { + if (castTy->isEqual(selectorTy)) + return nullptr; + } + + SmallVector conformances; + return castTy->getAnyNominal()->lookupConformance(DC->getParentModule(), + protocol, conformances) + ? CoerceExpr::forLiteralInit(getASTContext(), argExpr, + call->getSourceRange(), + typeExpr->getTypeRepr()) + : nullptr; +} + +/// Pre-check the expression, validating any types that occur in the +/// expression and folding sequence expressions. +bool ConstraintSystem::preCheckExpression(Expr *&expr, DeclContext *dc, + bool replaceInvalidRefsWithErrors) { + PreCheckExpression preCheck(dc, expr, replaceInvalidRefsWithErrors); + // Perform the pre-check. + if (auto result = expr->walk(preCheck)) { + expr = result; + return false; + } + return true; +} diff --git a/lib/Sema/ResilienceDiagnostics.cpp b/lib/Sema/ResilienceDiagnostics.cpp index 3a26433ce59c3..dfade1e1094aa 100644 --- a/lib/Sema/ResilienceDiagnostics.cpp +++ b/lib/Sema/ResilienceDiagnostics.cpp @@ -27,54 +27,24 @@ using namespace swift; -/// A uniquely-typed boolean to reduce the chances of accidentally inverting -/// a check. -enum class DowngradeToWarning: bool { - No, - Yes -}; - -bool TypeChecker::diagnoseInlinableDeclRef(SourceLoc loc, - ConcreteDeclRef declRef, - const DeclContext *DC, - FragileFunctionKind Kind) { - assert(Kind.kind != FragileFunctionKind::None); - - const ValueDecl *D = declRef.getDecl(); - // Do some important fast-path checks that apply to all cases. - - // Type parameters are OK. - if (isa(D)) - return false; - - // Check whether the declaration is accessible. - if (diagnoseInlinableDeclRefAccess(loc, D, DC, Kind)) - return true; - - // Check whether the declaration comes from a publically-imported module. - // Skip this check for accessors because the associated property or subscript - // will also be checked, and will provide a better error message. - if (!isa(D)) - if (diagnoseDeclRefExportability(loc, declRef, DC, Kind)) - return true; - - return false; -} - bool TypeChecker::diagnoseInlinableDeclRefAccess(SourceLoc loc, - const ValueDecl *D, - const DeclContext *DC, - FragileFunctionKind Kind) { - assert(Kind.kind != FragileFunctionKind::None); + const ValueDecl *D, + ExportContext where) { + auto fragileKind = where.getFragileFunctionKind(); + if (fragileKind.kind == FragileFunctionKind::None) + return false; // Local declarations are OK. if (D->getDeclContext()->isLocalContext()) return false; - // Public declarations or SPI used from SPI are OK. + auto *DC = where.getDeclContext(); + + // Public declarations are OK, even if they're SPI or came from an + // implementation-only import. We'll diagnose exportability violations + // from diagnoseDeclRefExportability(). if (D->getFormalAccessScope(/*useDC=*/nullptr, - Kind.allowUsableFromInline).isPublic() && - !(D->isSPI() && !DC->getInnermostDeclarationDeclContext()->isSPI())) + fragileKind.allowUsableFromInline).isPublic()) return false; auto &Context = DC->getASTContext(); @@ -87,14 +57,6 @@ bool TypeChecker::diagnoseInlinableDeclRefAccess(SourceLoc loc, return false; } - // Property initializers that are not exposed to clients are OK. - if (auto pattern = dyn_cast(DC)) { - auto bindingIndex = pattern->getBindingIndex(); - auto *varDecl = pattern->getBinding()->getAnchoringVarDecl(bindingIndex); - if (!varDecl->isInitExposedToClients()) - return false; - } - DowngradeToWarning downgradeToWarning = DowngradeToWarning::No; // Swift 4.2 did not perform any checks for type aliases. @@ -133,10 +95,10 @@ bool TypeChecker::diagnoseInlinableDeclRefAccess(SourceLoc loc, loc, diagID, D->getDescriptiveKind(), diagName, D->getFormalAccessScope().accessLevelForDiagnostics(), - static_cast(Kind.kind), + static_cast(fragileKind.kind), isAccessor); - if (Kind.allowUsableFromInline) { + if (fragileKind.allowUsableFromInline) { Context.Diags.diagnose(D, diag::resilience_decl_declared_here, D->getDescriptiveKind(), diagName, isAccessor); } else { @@ -147,99 +109,79 @@ bool TypeChecker::diagnoseInlinableDeclRefAccess(SourceLoc loc, return (downgradeToWarning == DowngradeToWarning::No); } -static bool diagnoseDeclExportability(SourceLoc loc, const ValueDecl *D, - const SourceFile &userSF, - const DeclContext *userDC, - FragileFunctionKind fragileKind) { - assert(fragileKind.kind != FragileFunctionKind::None); +bool +TypeChecker::diagnoseDeclRefExportability(SourceLoc loc, + const ValueDecl *D, + ExportContext where) { + // Accessors cannot have exportability that's different than the storage, + // so skip them for now. + if (isa(D)) + return false; + + if (!where.mustOnlyReferenceExportedDecls()) + return false; auto definingModule = D->getModuleContext(); + auto downgradeToWarning = DowngradeToWarning::No; + auto originKind = getDisallowedOriginKind( - D, userSF, userDC->getInnermostDeclarationDeclContext()); + D, where, downgradeToWarning); if (originKind == DisallowedOriginKind::None) return false; - // TODO: different diagnostics ASTContext &ctx = definingModule->getASTContext(); - ctx.Diags.diagnose(loc, diag::inlinable_decl_ref_from_hidden_module, - D->getDescriptiveKind(), D->getName(), - static_cast(fragileKind.kind), - definingModule->getName(), - static_cast(originKind)); - return true; -} -static bool -diagnoseGenericArgumentsExportability(SourceLoc loc, - SubstitutionMap subs, - const SourceFile &userSF, - const DeclContext *userDC) { - bool hadAnyIssues = false; - for (ProtocolConformanceRef conformance : subs.getConformances()) { - if (!conformance.isConcrete()) - continue; - const ProtocolConformance *concreteConf = conformance.getConcrete(); - - SubstitutionMap subConformanceSubs = - concreteConf->getSubstitutions(userSF.getParentModule()); - diagnoseGenericArgumentsExportability(loc, subConformanceSubs, userSF, userDC); - - const RootProtocolConformance *rootConf = - concreteConf->getRootConformance(); - ModuleDecl *M = rootConf->getDeclContext()->getParentModule(); - - auto originKind = getDisallowedOriginKind( - rootConf->getDeclContext()->getAsDecl(), - userSF, userDC->getInnermostDeclarationDeclContext()); - if (originKind == DisallowedOriginKind::None) - continue; - - ASTContext &ctx = M->getASTContext(); - ctx.Diags.diagnose(loc, diag::conformance_from_implementation_only_module, - rootConf->getType(), - rootConf->getProtocol()->getName(), 0, M->getName(), + auto fragileKind = where.getFragileFunctionKind(); + auto reason = where.getExportabilityReason(); + + if (fragileKind.kind == FragileFunctionKind::None) { + auto errorOrWarning = downgradeToWarning == DowngradeToWarning::Yes? + diag::decl_from_hidden_module_warn: + diag::decl_from_hidden_module; + ctx.Diags.diagnose(loc, errorOrWarning, + D->getDescriptiveKind(), + D->getName(), + static_cast(*reason), + definingModule->getName(), static_cast(originKind)); - hadAnyIssues = true; - } - return hadAnyIssues; -} -void TypeChecker::diagnoseGenericTypeExportability(SourceLoc Loc, Type T, - const DeclContext *DC) { - const SourceFile *SF = DC->getParentSourceFile(); - if (!SF) - return; - - // FIXME: It would be nice to highlight just the part of the type that's - // problematic, but unfortunately the TypeRepr doesn't have the - // information we need and the Type doesn't easily map back to it. - if (auto *BGT = dyn_cast(T.getPointer())) { - ModuleDecl *useModule = SF->getParentModule(); - auto subs = T->getContextSubstitutionMap(useModule, BGT->getDecl()); - (void)diagnoseGenericArgumentsExportability(Loc, subs, *SF, DC); - } else if (auto *TAT = dyn_cast(T.getPointer())) { - auto subs = TAT->getSubstitutionMap(); - (void)diagnoseGenericArgumentsExportability(Loc, subs, *SF, DC); + D->diagnose(diag::kind_declared_here, DescriptiveDeclKind::Type); + } else { + ctx.Diags.diagnose(loc, diag::inlinable_decl_ref_from_hidden_module, + D->getDescriptiveKind(), D->getName(), + static_cast(fragileKind.kind), + definingModule->getName(), + static_cast(originKind)); } + return true; } bool -TypeChecker::diagnoseDeclRefExportability(SourceLoc loc, - ConcreteDeclRef declRef, - const DeclContext *DC, - FragileFunctionKind fragileKind) { - // We're only interested in diagnosing uses from source files. - auto userSF = DC->getParentSourceFile(); - if (!userSF) +TypeChecker::diagnoseConformanceExportability(SourceLoc loc, + const RootProtocolConformance *rootConf, + ExportContext where) { + if (!where.mustOnlyReferenceExportedDecls()) return false; - const ValueDecl *D = declRef.getDecl(); - if (diagnoseDeclExportability(loc, D, *userSF, DC, fragileKind)) - return true; - if (diagnoseGenericArgumentsExportability(loc, declRef.getSubstitutions(), - *userSF, DC)) { - return true; - } - return false; + auto originKind = getDisallowedOriginKind( + rootConf->getDeclContext()->getAsDecl(), + where); + if (originKind == DisallowedOriginKind::None) + return false; + + ModuleDecl *M = rootConf->getDeclContext()->getParentModule(); + ASTContext &ctx = M->getASTContext(); + + auto reason = where.getExportabilityReason(); + if (!reason.hasValue()) + reason = ExportabilityReason::General; + + ctx.Diags.diagnose(loc, diag::conformance_from_implementation_only_module, + rootConf->getType(), + rootConf->getProtocol()->getName(), + static_cast(*reason), + M->getName(), + static_cast(originKind)); + return true; } diff --git a/lib/Sema/TypeCheckAccess.cpp b/lib/Sema/TypeCheckAccess.cpp index 67f15ea3f539b..dd6cc34657844 100644 --- a/lib/Sema/TypeCheckAccess.cpp +++ b/lib/Sema/TypeCheckAccess.cpp @@ -15,17 +15,16 @@ //===----------------------------------------------------------------------===// #include "TypeCheckAccess.h" +#include "TypeChecker.h" +#include "TypeCheckAvailability.h" #include "TypeAccessScopeChecker.h" #include "swift/AST/ASTVisitor.h" #include "swift/AST/ASTWalker.h" -#include "swift/AST/ClangModuleLoader.h" #include "swift/AST/DiagnosticsSema.h" #include "swift/AST/ExistentialLayout.h" #include "swift/AST/Pattern.h" #include "swift/AST/ParameterList.h" -#include "swift/AST/ProtocolConformance.h" #include "swift/AST/TypeCheckRequests.h" -#include "swift/AST/TypeDeclFinder.h" using namespace swift; @@ -33,15 +32,6 @@ using namespace swift; namespace { -/// A uniquely-typed boolean to reduce the chances of accidentally inverting -/// a check. -/// -/// \see checkTypeAccess -enum class DowngradeToWarning: bool { - No, - Yes -}; - /// Calls \p callback for each type in each requirement provided by /// \p source. static void forAllRequirementTypes( @@ -321,11 +311,10 @@ void AccessControlCheckerBase::checkGenericParamAccess( const Decl *ownerDecl, AccessScope accessScope, AccessLevel contextAccess) { - auto params = ownerCtx->getGenericParams(); - if (!params) + if (!ownerCtx->isGenericContext()) return; - // This must stay in sync with diag::generic_param_access. + // This must stay in sync with diag::generic_param_access. enum class ACEK { Parameter = 0, Requirement @@ -353,20 +342,25 @@ void AccessControlCheckerBase::checkGenericParamAccess( auto *DC = ownerDecl->getDeclContext(); - for (auto param : *params) { - if (param->getInherited().empty()) - continue; - assert(param->getInherited().size() == 1); - checkTypeAccessImpl(param->getInherited().front().getType(), - param->getInherited().front().getTypeRepr(), - accessScope, DC, /*mayBeInferred*/false, - callback); + if (auto params = ownerCtx->getGenericParams()) { + for (auto param : *params) { + if (param->getInherited().empty()) + continue; + assert(param->getInherited().size() == 1); + checkTypeAccessImpl(param->getInherited().front().getType(), + param->getInherited().front().getTypeRepr(), + accessScope, DC, /*mayBeInferred*/false, + callback); + } } + callbackACEK = ACEK::Requirement; - checkRequirementAccess(WhereClauseOwner( - const_cast(ownerCtx)), - accessScope, DC, callback); + if (ownerCtx->getTrailingWhereClause()) { + checkRequirementAccess(WhereClauseOwner( + const_cast(ownerCtx)), + accessScope, DC, callback); + } if (minAccessScope.isPublic()) return; @@ -418,6 +412,10 @@ namespace { class AccessControlChecker : public AccessControlCheckerBase, public DeclVisitor { public: + + AccessControlChecker(bool allowUsableFromInline) + : AccessControlCheckerBase(allowUsableFromInline) {} + AccessControlChecker() : AccessControlCheckerBase(/*checkUsableFromInline=*/false) {} @@ -1053,29 +1051,26 @@ class UsableFromInlineChecker : public AccessControlCheckerBase, UNINTERESTING(Accessor) // Handled by the Var or Subscript. UNINTERESTING(OpaqueType) // Handled by the Var or Subscript. - /// If \p PBD declared stored instance properties in a fixed-contents struct, - /// return said struct. - static const StructDecl * - getFixedLayoutStructContext(const PatternBindingDecl *PBD) { - auto *parentStruct = dyn_cast(PBD->getDeclContext()); - if (!parentStruct) - return nullptr; - if (!(parentStruct->getAttrs().hasAttribute() || - parentStruct->getAttrs().hasAttribute()) || - PBD->isStatic() || !PBD->hasStorage()) { - return nullptr; - } - // We don't check for "in resilient modules" because there's no reason to - // write '@_fixedLayout' on a struct in a non-resilient module. - return parentStruct; + /// If \p VD's layout is exposed by a @frozen struct or class, return said + /// struct or class. + /// + /// Stored instance properties in @frozen structs and classes must always use + /// public/@usableFromInline types. In these cases, check the access against + /// the struct instead of the VarDecl, and customize the diagnostics. + static const ValueDecl * + getFixedLayoutStructContext(const VarDecl *VD) { + if (VD->isLayoutExposedToClients()) + return dyn_cast(VD->getDeclContext()); + + return nullptr; } /// \see visitPatternBindingDecl void checkNamedPattern(const NamedPattern *NP, - const ValueDecl *fixedLayoutStructContext, bool isTypeContext, const llvm::DenseSet &seenVars) { const VarDecl *theVar = NP->getDecl(); + auto *fixedLayoutStructContext = getFixedLayoutStructContext(theVar); if (!fixedLayoutStructContext && shouldSkipChecking(theVar)) return; // Only check individual variables if we didn't check an enclosing @@ -1103,7 +1098,6 @@ class UsableFromInlineChecker : public AccessControlCheckerBase, /// \see visitPatternBindingDecl void checkTypedPattern(const TypedPattern *TP, - const ValueDecl *fixedLayoutStructContext, bool isTypeContext, llvm::DenseSet &seenVars) { // FIXME: We need an access level to check against, so we pull one out @@ -1116,6 +1110,7 @@ class UsableFromInlineChecker : public AccessControlCheckerBase, }); if (!anyVar) return; + auto *fixedLayoutStructContext = getFixedLayoutStructContext(anyVar); if (!fixedLayoutStructContext && shouldSkipChecking(anyVar)) return; @@ -1156,27 +1151,18 @@ class UsableFromInlineChecker : public AccessControlCheckerBase, void visitPatternBindingDecl(PatternBindingDecl *PBD) { bool isTypeContext = PBD->getDeclContext()->isTypeContext(); - // Stored instance properties in public/@usableFromInline fixed-contents - // structs in resilient modules must always use public/@usableFromInline - // types. In these cases, check the access against the struct instead of the - // VarDecl, and customize the diagnostics. - const ValueDecl *fixedLayoutStructContext = - getFixedLayoutStructContext(PBD); - llvm::DenseSet seenVars; for (auto idx : range(PBD->getNumPatternEntries())) { PBD->getPattern(idx)->forEachNode([&](const Pattern *P) { if (auto *NP = dyn_cast(P)) { - checkNamedPattern(NP, fixedLayoutStructContext, isTypeContext, - seenVars); + checkNamedPattern(NP, isTypeContext, seenVars); return; } auto *TP = dyn_cast(P); if (!TP) return; - checkTypedPattern(TP, fixedLayoutStructContext, isTypeContext, - seenVars); + checkTypedPattern(TP, isTypeContext, seenVars); }); seenVars.clear(); } @@ -1454,28 +1440,30 @@ class UsableFromInlineChecker : public AccessControlCheckerBase, } }; +} // end anonymous namespace + /// Returns the kind of origin, implementation-only import or SPI declaration, /// that restricts exporting \p decl from the given file and context. /// /// Local variant to swift::getDisallowedOriginKind for downgrade to warnings. DisallowedOriginKind -getDisallowedOriginKind(const Decl *decl, - const SourceFile &userSF, - const Decl *userContext, - DowngradeToWarning &downgradeToWarning) { +swift::getDisallowedOriginKind(const Decl *decl, + ExportContext where, + DowngradeToWarning &downgradeToWarning) { downgradeToWarning = DowngradeToWarning::No; ModuleDecl *M = decl->getModuleContext(); - if (userSF.isImportedImplementationOnly(M)) { + auto *SF = where.getDeclContext()->getParentSourceFile(); + if (SF->isImportedImplementationOnly(M)) { // Temporarily downgrade implementation-only exportability in SPI to // a warning. - if (userContext->isSPI()) + if (where.isSPI()) downgradeToWarning = DowngradeToWarning::Yes; // Implementation-only imported, cannot be reexported. return DisallowedOriginKind::ImplementationOnly; - } else if (decl->isSPI() && !userContext->isSPI()) { + } else if (decl->isSPI() && !where.isSPI()) { // SPI can only be exported in SPI. - return userContext->getModuleContext() == M ? + return where.getDeclContext()->getParentModule() == M ? DisallowedOriginKind::SPILocal : DisallowedOriginKind::SPIImported; } @@ -1483,293 +1471,66 @@ getDisallowedOriginKind(const Decl *decl, return DisallowedOriginKind::None; }; -// Diagnose public APIs exposing types that are either imported as -// implementation-only or declared as SPI. -class ExportabilityChecker : public DeclVisitor { - class Diagnoser; +namespace { - void checkTypeImpl( - Type type, const TypeRepr *typeRepr, const SourceFile &SF, - const Decl *context, - const Diagnoser &diagnoser) { +/// Diagnose declarations whose signatures refer to unavailable types. +class DeclAvailabilityChecker : public DeclVisitor { + ExportContext Where; + + void checkType(Type type, const TypeRepr *typeRepr, const Decl *context, + ExportabilityReason reason=ExportabilityReason::General, + bool allowUnavailableProtocol=false) { // Don't bother checking errors. if (type && type->hasError()) return; - bool foundAnyIssues = false; - - // Check the TypeRepr first (if present), because that will give us a - // better diagnostic. - if (typeRepr) { - const_cast(typeRepr)->walk(TypeReprIdentFinder( - [&](const ComponentIdentTypeRepr *component) { - TypeDecl *typeDecl = component->getBoundDecl(); - auto downgradeToWarning = DowngradeToWarning::No; - auto originKind = getDisallowedOriginKind(typeDecl, SF, context, downgradeToWarning); - if (originKind != DisallowedOriginKind::None) { - diagnoser.diagnoseType(typeDecl, component, originKind, downgradeToWarning); - foundAnyIssues = true; - } - - // We still continue even in the diagnostic case to report multiple - // violations. - return true; - })); - } + DeclAvailabilityFlags flags = None; - // Note that if we have a type, we can't skip checking it even if the - // TypeRepr is okay, because that's how we check what conformances are - // being used. + // We allow a type to conform to a protocol that is less available than + // the type itself. This enables a type to retroactively model or directly + // conform to a protocol only available on newer OSes and yet still be used on + // older OSes. // - // We still don't want to do this if we found issues with the TypeRepr, - // though, because that would result in some issues being reported twice. - if (foundAnyIssues || !type) - return; + // To support this, inside inheritance clauses we allow references to + // protocols that are unavailable in the current type refinement context. + if (allowUnavailableProtocol) + flags |= DeclAvailabilityFlag::AllowPotentiallyUnavailableProtocol; - class ProblematicTypeFinder : public TypeDeclFinder { - const SourceFile &SF; - const Decl *context; - const Diagnoser &diagnoser; - public: - ProblematicTypeFinder(const SourceFile &SF, const Decl *context, const Diagnoser &diagnoser) - : SF(SF), context(context), diagnoser(diagnoser) {} - - void visitTypeDecl(const TypeDecl *typeDecl) { - auto downgradeToWarning = DowngradeToWarning::No; - auto originKind = getDisallowedOriginKind(typeDecl, SF, context, downgradeToWarning); - if (originKind != DisallowedOriginKind::None) - diagnoser.diagnoseType(typeDecl, /*typeRepr*/nullptr, originKind, downgradeToWarning); - } + auto loc = context->getLoc(); + if (auto *varDecl = dyn_cast(context)) + loc = varDecl->getNameLoc(); - void visitSubstitutionMap(SubstitutionMap subs) { - for (ProtocolConformanceRef conformance : subs.getConformances()) { - if (!conformance.isConcrete()) - continue; - const ProtocolConformance *concreteConf = conformance.getConcrete(); - - SubstitutionMap subConformanceSubs = - concreteConf->getSubstitutions(SF.getParentModule()); - visitSubstitutionMap(subConformanceSubs); - - const RootProtocolConformance *rootConf = - concreteConf->getRootConformance(); - auto originKind = getDisallowedOriginKind( - rootConf->getDeclContext()->getAsDecl(), - SF, context); - if (originKind == DisallowedOriginKind::None) - continue; - diagnoser.diagnoseConformance(rootConf, originKind); - } - } - - Action visitNominalType(NominalType *ty) override { - visitTypeDecl(ty->getDecl()); - return Action::Continue; - } - - Action visitBoundGenericType(BoundGenericType *ty) override { - visitTypeDecl(ty->getDecl()); - SubstitutionMap subs = - ty->getContextSubstitutionMap(SF.getParentModule(), ty->getDecl()); - visitSubstitutionMap(subs); - return Action::Continue; - } - - Action visitTypeAliasType(TypeAliasType *ty) override { - visitTypeDecl(ty->getDecl()); - visitSubstitutionMap(ty->getSubstitutionMap()); - return Action::Continue; - } - - // We diagnose unserializable Clang function types in the - // post-visitor so that we diagnose any unexportable component - // types first. - Action walkToTypePost(Type T) override { - if (auto fnType = T->getAs()) { - if (auto clangType = fnType->getClangTypeInfo().getType()) { - auto loader = T->getASTContext().getClangModuleLoader(); - // Serialization will serialize the sugared type if it can, - // but we need the canonical type to be serializable or else - // canonicalization (e.g. in SIL) might break things. - if (!loader->isSerializable(clangType, /*check canonical*/ true)) { - diagnoser.diagnoseClangFunctionType(T, clangType); - } - } - } - return TypeDeclFinder::walkToTypePost(T); - } - }; - - type.walk(ProblematicTypeFinder(SF, context, diagnoser)); - } - - void checkType( - Type type, const TypeRepr *typeRepr, const Decl *context, - const Diagnoser &diagnoser) { - auto *SF = context->getDeclContext()->getParentSourceFile(); - assert(SF && "checking a non-source declaration?"); - return checkTypeImpl(type, typeRepr, *SF, context, diagnoser); - } - - void checkType( - const TypeLoc &TL, const Decl *context, const Diagnoser &diagnoser) { - checkType(TL.getType(), TL.getTypeRepr(), context, diagnoser); + diagnoseTypeAvailability(typeRepr, type, loc, + Where.withReason(reason), flags); } void checkGenericParams(const GenericContext *ownerCtx, const ValueDecl *ownerDecl) { - const auto params = ownerCtx->getGenericParams(); - if (!params) + if (!ownerCtx->isGenericContext()) return; - for (auto param : *params) { - if (param->getInherited().empty()) - continue; - assert(param->getInherited().size() == 1); - checkType(param->getInherited().front(), ownerDecl, - getDiagnoser(ownerDecl)); - } - - forAllRequirementTypes(WhereClauseOwner( - const_cast(ownerCtx)), - [&](Type type, TypeRepr *typeRepr) { - checkType(type, typeRepr, ownerDecl, getDiagnoser(ownerDecl)); - }); - } - - // This enum must be kept in sync with - // diag::decl_from_hidden_module and - // diag::conformance_from_implementation_only_module. - enum class Reason : unsigned { - General, - PropertyWrapper, - ExtensionWithPublicMembers, - ExtensionWithConditionalConformances - }; - - class Diagnoser { - const Decl *D; - Reason reason; - public: - Diagnoser(const Decl *D, Reason reason) : D(D), reason(reason) {} - - void diagnoseType(const TypeDecl *offendingType, - const TypeRepr *complainRepr, - DisallowedOriginKind originKind, - DowngradeToWarning downgradeToWarning) const { - ModuleDecl *M = offendingType->getModuleContext(); - auto errorOrWarning = downgradeToWarning == DowngradeToWarning::Yes? - diag::decl_from_hidden_module_warn: - diag::decl_from_hidden_module; - auto diag = D->diagnose(errorOrWarning, - offendingType->getDescriptiveKind(), - offendingType->getName(), - static_cast(reason), M->getName(), - static_cast(originKind)); - highlightOffendingType(diag, complainRepr); - } - - void diagnoseConformance(const ProtocolConformance *offendingConformance, - DisallowedOriginKind originKind) const { - ModuleDecl *M = offendingConformance->getDeclContext()->getParentModule(); - D->diagnose(diag::conformance_from_implementation_only_module, - offendingConformance->getType(), - offendingConformance->getProtocol()->getName(), - static_cast(reason), M->getName(), - static_cast(originKind)); + if (auto params = ownerCtx->getGenericParams()) { + for (auto param : *params) { + if (param->getInherited().empty()) + continue; + assert(param->getInherited().size() == 1); + auto inherited = param->getInherited().front(); + checkType(inherited.getType(), inherited.getTypeRepr(), ownerDecl); + } } - void diagnoseClangFunctionType(Type fnType, const clang::Type *type) const { - D->diagnose(diag::unexportable_clang_function_type, fnType); + if (ownerCtx->getTrailingWhereClause()) { + forAllRequirementTypes(WhereClauseOwner( + const_cast(ownerCtx)), + [&](Type type, TypeRepr *typeRepr) { + checkType(type, typeRepr, ownerDecl); + }); } - }; - - Diagnoser getDiagnoser(const Decl *D, Reason reason = Reason::General) { - return Diagnoser(D, reason); } public: - ExportabilityChecker() {} - - static bool shouldSkipChecking(const ValueDecl *VD) { - if (VD->getAttrs().hasAttribute()) - return true; - - // Accessors are handled as part of their Var or Subscript, and we don't - // want to redo extension signature checking for them. - if (isa(VD)) - return true; - - // Is this part of the module's API or ABI? - AccessScope accessScope = - VD->getFormalAccessScope(nullptr, - /*treatUsableFromInlineAsPublic*/true); - if (accessScope.isPublic()) - return false; - - // Is this a stored property in a non-resilient struct or class? - auto *property = dyn_cast(VD); - if (!property || !property->hasStorage() || property->isStatic()) - return true; - auto *parentNominal = dyn_cast(property->getDeclContext()); - if (!parentNominal || parentNominal->isResilient()) - return true; - - // Is that struct or class part of the module's API or ABI? - AccessScope parentAccessScope = parentNominal->getFormalAccessScope( - nullptr, /*treatUsableFromInlineAsPublic*/true); - if (parentAccessScope.isPublic()) - return false; - - return true; - } - - void checkOverride(const ValueDecl *VD) { - const ValueDecl *overridden = VD->getOverriddenDecl(); - if (!overridden) - return; - - auto *SF = VD->getDeclContext()->getParentSourceFile(); - assert(SF && "checking a non-source declaration?"); - - ModuleDecl *M = overridden->getModuleContext(); - if (SF->isImportedImplementationOnly(M)) { - VD->diagnose(diag::implementation_only_override_import_without_attr, - overridden->getDescriptiveKind()) - .fixItInsert(VD->getAttributeInsertionLoc(false), - "@_implementationOnly "); - overridden->diagnose(diag::overridden_here); - return; - } - - if (overridden->getAttrs().hasAttribute()) { - VD->diagnose(diag::implementation_only_override_without_attr, - overridden->getDescriptiveKind()) - .fixItInsert(VD->getAttributeInsertionLoc(false), - "@_implementationOnly "); - overridden->diagnose(diag::overridden_here); - return; - } - - // FIXME: Check storage decls where the setter is in a separate module from - // the getter, which is a thing Objective-C can do. The ClangImporter - // doesn't make this easy, though, because it just gives the setter the same - // DeclContext as the property or subscript, which means we've lost the - // information about whether its module was implementation-only imported. - } - - void visit(Decl *D) { - if (D->isInvalid() || D->isImplicit()) - return; - - if (auto *VD = dyn_cast(D)) { - if (shouldSkipChecking(VD)) - return; - checkOverride(VD); - } - - DeclVisitor::visit(D); - } + explicit DeclAvailabilityChecker(ExportContext where) + : Where(where) {} // Force all kinds to be handled at a lower level. void visitDecl(Decl *D) = delete; @@ -1808,22 +1569,18 @@ class ExportabilityChecker : public DeclVisitor { void checkNamedPattern(const NamedPattern *NP, const llvm::DenseSet &seenVars) { const VarDecl *theVar = NP->getDecl(); - if (shouldSkipChecking(theVar)) - return; - - checkOverride(theVar); // Only check the type of individual variables if we didn't check an // enclosing TypedPattern. - if (seenVars.count(theVar) || theVar->isInvalid()) + if (seenVars.count(theVar)) return; - checkType(theVar->getInterfaceType(), /*typeRepr*/nullptr, theVar, - getDiagnoser(theVar)); + checkType(theVar->getValueInterfaceType(), /*typeRepr*/nullptr, theVar); } /// \see visitPatternBindingDecl - void checkTypedPattern(const TypedPattern *TP, + void checkTypedPattern(PatternBindingDecl *PBD, + const TypedPattern *TP, llvm::DenseSet &seenVars) { // FIXME: We need to figure out if this is a stored or computed property, // so we pull out some random VarDecl in the pattern. They're all going to @@ -1833,18 +1590,15 @@ class ExportabilityChecker : public DeclVisitor { seenVars.insert(V); anyVar = V; }); - if (!anyVar) - return; - if (shouldSkipChecking(anyVar)) - return; checkType(TP->hasType() ? TP->getType() : Type(), - TP->getTypeRepr(), anyVar, getDiagnoser(anyVar)); + TP->getTypeRepr(), anyVar ? (Decl *)anyVar : (Decl *)PBD); // Check the property wrapper types. - for (auto attr : anyVar->getAttachedPropertyWrappers()) - checkType(attr->getType(), attr->getTypeRepr(), anyVar, - getDiagnoser(anyVar, Reason::PropertyWrapper)); + if (anyVar) + for (auto attr : anyVar->getAttachedPropertyWrappers()) + checkType(attr->getType(), attr->getTypeRepr(), anyVar, + ExportabilityReason::PropertyWrapper); } void visitPatternBindingDecl(PatternBindingDecl *PBD) { @@ -1859,7 +1613,7 @@ class ExportabilityChecker : public DeclVisitor { auto *TP = dyn_cast(P); if (!TP) return; - checkTypedPattern(TP, seenVars); + checkTypedPattern(PBD, TP, seenVars); }); seenVars.clear(); } @@ -1868,22 +1622,22 @@ class ExportabilityChecker : public DeclVisitor { void visitTypeAliasDecl(TypeAliasDecl *TAD) { checkGenericParams(TAD, TAD); checkType(TAD->getUnderlyingType(), - TAD->getUnderlyingTypeRepr(), TAD, getDiagnoser(TAD)); + TAD->getUnderlyingTypeRepr(), TAD); } void visitAssociatedTypeDecl(AssociatedTypeDecl *assocType) { llvm::for_each(assocType->getInherited(), [&](TypeLoc requirement) { - checkType(requirement, assocType, getDiagnoser(assocType)); + checkType(requirement.getType(), requirement.getTypeRepr(), + assocType); }); checkType(assocType->getDefaultDefinitionType(), - assocType->getDefaultDefinitionTypeRepr(), assocType, - getDiagnoser(assocType)); + assocType->getDefaultDefinitionTypeRepr(), assocType); if (assocType->getTrailingWhereClause()) { forAllRequirementTypes(assocType, [&](Type type, TypeRepr *typeRepr) { - checkType(type, typeRepr, assocType, getDiagnoser(assocType)); + checkType(type, typeRepr, assocType); }); } } @@ -1892,20 +1646,24 @@ class ExportabilityChecker : public DeclVisitor { checkGenericParams(nominal, nominal); llvm::for_each(nominal->getInherited(), - [&](TypeLoc nextInherited) { - checkType(nextInherited, nominal, getDiagnoser(nominal)); + [&](TypeLoc inherited) { + checkType(inherited.getType(), inherited.getTypeRepr(), + nominal, ExportabilityReason::General, + /*allowUnavailableProtocol=*/true); }); } void visitProtocolDecl(ProtocolDecl *proto) { llvm::for_each(proto->getInherited(), [&](TypeLoc requirement) { - checkType(requirement, proto, getDiagnoser(proto)); + checkType(requirement.getType(), requirement.getTypeRepr(), proto, + ExportabilityReason::General, + /*allowUnavailableProtocol=*/false); }); if (proto->getTrailingWhereClause()) { forAllRequirementTypes(proto, [&](Type type, TypeRepr *typeRepr) { - checkType(type, typeRepr, proto, getDiagnoser(proto)); + checkType(type, typeRepr, proto); }); } } @@ -1914,76 +1672,82 @@ class ExportabilityChecker : public DeclVisitor { checkGenericParams(SD, SD); for (auto &P : *SD->getIndices()) { - checkType(P->getInterfaceType(), P->getTypeRepr(), SD, - getDiagnoser(SD)); + checkType(P->getInterfaceType(), P->getTypeRepr(), SD); } - checkType(SD->getElementInterfaceType(), SD->getElementTypeRepr(), SD, - getDiagnoser(SD)); + checkType(SD->getElementInterfaceType(), SD->getElementTypeRepr(), SD); } void visitAbstractFunctionDecl(AbstractFunctionDecl *fn) { checkGenericParams(fn, fn); for (auto *P : *fn->getParameters()) - checkType(P->getInterfaceType(), P->getTypeRepr(), fn, - getDiagnoser(fn)); + checkType(P->getInterfaceType(), P->getTypeRepr(), fn); } void visitFuncDecl(FuncDecl *FD) { visitAbstractFunctionDecl(FD); - checkType(FD->getResultInterfaceType(), FD->getResultTypeRepr(), FD, - getDiagnoser(FD)); + checkType(FD->getResultInterfaceType(), FD->getResultTypeRepr(), FD); } void visitEnumElementDecl(EnumElementDecl *EED) { if (!EED->hasAssociatedValues()) return; for (auto &P : *EED->getParameterList()) - checkType(P->getInterfaceType(), P->getTypeRepr(), EED, - getDiagnoser(EED)); + checkType(P->getInterfaceType(), P->getTypeRepr(), EED); } void checkConstrainedExtensionRequirements(ExtensionDecl *ED, - Reason reason) { + bool hasExportedMembers) { if (!ED->getTrailingWhereClause()) return; + + ExportabilityReason reason = + hasExportedMembers ? ExportabilityReason::ExtensionWithPublicMembers + : ExportabilityReason::ExtensionWithConditionalConformances; + forAllRequirementTypes(ED, [&](Type type, TypeRepr *typeRepr) { - checkType(type, typeRepr, ED, getDiagnoser(ED, reason)); + checkType(type, typeRepr, ED, reason); }); } void visitExtensionDecl(ExtensionDecl *ED) { auto extendedType = ED->getExtendedNominal(); assert(extendedType && "valid extension with no extended type?"); - if (!extendedType || shouldSkipChecking(extendedType)) + if (!extendedType) return; - // FIXME: We should allow conforming to implementation-only protocols, - // but just hide that from interfaces. + // The rules here are tricky. + // + // 1) If the extension defines conformances, the conformed-to protocols + // must be exported. llvm::for_each(ED->getInherited(), - [&](TypeLoc nextInherited) { - checkType(nextInherited, ED, getDiagnoser(ED)); + [&](TypeLoc inherited) { + checkType(inherited.getType(), inherited.getTypeRepr(), + ED, ExportabilityReason::General, + /*allowUnavailableProtocol=*/true); }); - bool hasPublicMembers = llvm::any_of(ED->getMembers(), - [](const Decl *member) -> bool { + auto wasWhere = Where; + + // 2) If the extension contains exported members, the as-written + // extended type should be exportable. + bool hasExportedMembers = llvm::any_of(ED->getMembers(), + [](const Decl *member) -> bool { auto *valueMember = dyn_cast(member); if (!valueMember) return false; - return !shouldSkipChecking(valueMember); + return isExported(valueMember); }); - if (hasPublicMembers) { - checkType(ED->getExtendedType(), ED->getExtendedTypeRepr(), ED, - getDiagnoser(ED, Reason::ExtensionWithPublicMembers)); - } + Where = wasWhere.withExported(hasExportedMembers); + checkType(ED->getExtendedType(), ED->getExtendedTypeRepr(), ED, + ExportabilityReason::ExtensionWithPublicMembers); - if (hasPublicMembers || !ED->getInherited().empty()) { - Reason reason = - hasPublicMembers ? Reason::ExtensionWithPublicMembers - : Reason::ExtensionWithConditionalConformances; - checkConstrainedExtensionRequirements(ED, reason); - } + // 3) If the extension contains exported members or defines conformances, + // the 'where' clause must only name exported types. + Where = wasWhere.withExported(hasExportedMembers || + !ED->getInherited().empty()); + checkConstrainedExtensionRequirements(ED, hasExportedMembers); } void checkPrecedenceGroup(const PrecedenceGroupDecl *PGD, @@ -1998,7 +1762,7 @@ class ExportabilityChecker : public DeclVisitor { auto diag = DE.diagnose(diagLoc, diag::decl_from_hidden_module, PGD->getDescriptiveKind(), PGD->getName(), - static_cast(Reason::General), M->getName(), + static_cast(ExportabilityReason::General), M->getName(), static_cast(DisallowedOriginKind::ImplementationOnly) ); if (refRange.isValid()) @@ -2031,6 +1795,7 @@ class ExportabilityChecker : public DeclVisitor { }); } }; + } // end anonymous namespace static void checkExtensionGenericParamAccess(const ExtensionDecl *ED) { @@ -2065,19 +1830,27 @@ static void checkExtensionGenericParamAccess(const ExtensionDecl *ED) { } DisallowedOriginKind swift::getDisallowedOriginKind(const Decl *decl, - const SourceFile &userSF, - const Decl *declContext) { + ExportContext where) { auto downgradeToWarning = DowngradeToWarning::No; - return getDisallowedOriginKind(decl, userSF, declContext, downgradeToWarning); + return getDisallowedOriginKind(decl, where, downgradeToWarning); } void swift::checkAccessControl(Decl *D) { if (isa(D) || isa(D)) { - AccessControlChecker().visit(D); + bool allowInlineable = + D->getDeclContext()->isInSpecializeExtensionContext(); + AccessControlChecker(allowInlineable).visit(D); UsableFromInlineChecker().visit(D); } else if (auto *ED = dyn_cast(D)) { checkExtensionGenericParamAccess(ED); } - ExportabilityChecker().visit(D); + if (isa(D)) + return; + + auto where = ExportContext::forDeclSignature(D); + if (where.isImplicit()) + return; + + DeclAvailabilityChecker(where).visit(D); } diff --git a/lib/Sema/TypeCheckAccess.h b/lib/Sema/TypeCheckAccess.h index d29523ef39a8d..29d82411bba5c 100644 --- a/lib/Sema/TypeCheckAccess.h +++ b/lib/Sema/TypeCheckAccess.h @@ -22,6 +22,7 @@ namespace swift { class Decl; +class ExportContext; class SourceFile; /// Performs access-related checks for \p D. @@ -43,11 +44,23 @@ enum class DisallowedOriginKind : uint8_t { None }; +/// A uniquely-typed boolean to reduce the chances of accidentally inverting +/// a check. +/// +/// \see checkTypeAccess +enum class DowngradeToWarning: bool { + No, + Yes +}; + /// Returns the kind of origin, implementation-only import or SPI declaration, /// that restricts exporting \p decl from the given file and context. DisallowedOriginKind getDisallowedOriginKind(const Decl *decl, - const SourceFile &userSF, - const Decl *userContext); + ExportContext where); + +DisallowedOriginKind getDisallowedOriginKind(const Decl *decl, + ExportContext where, + DowngradeToWarning &downgradeToWarning); } // end namespace swift diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index caf32da42bc95..77409a2fe2fde 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -15,6 +15,8 @@ //===----------------------------------------------------------------------===// #include "MiscDiagnostics.h" +#include "TypeCheckAvailability.h" +#include "TypeCheckConcurrency.h" #include "TypeCheckObjC.h" #include "TypeCheckType.h" #include "TypeChecker.h" @@ -118,6 +120,7 @@ class AttributeChecker : public AttributeVisitor { IGNORED_ATTR(ReferenceOwnership) IGNORED_ATTR(OriginallyDefinedIn) IGNORED_ATTR(NoDerivative) + IGNORED_ATTR(SpecializeExtension) #undef IGNORED_ATTR void visitAlignmentAttr(AlignmentAttr *attr) { @@ -248,7 +251,7 @@ class AttributeChecker : public AttributeVisitor { void visitCustomAttr(CustomAttr *attr); void visitPropertyWrapperAttr(PropertyWrapperAttr *attr); - void visitFunctionBuilderAttr(FunctionBuilderAttr *attr); + void visitResultBuilderAttr(ResultBuilderAttr *attr); void visitImplementationOnlyAttr(ImplementationOnlyAttr *attr); void visitNonEphemeralAttr(NonEphemeralAttr *attr); @@ -287,19 +290,26 @@ class AttributeChecker : public AttributeVisitor { if (auto var = dyn_cast(D)) { // @actorIndependent is meaningless on a `let`. if (var->isLet()) { - diagnoseAndRemoveAttr(attr, diag::actorisolated_let); + diagnoseAndRemoveAttr(attr, diag::actorindependent_let); return; } - // @actorIndependent can not be applied to stored properties. + // @actorIndependent can not be applied to stored properties, unless if + // the 'unsafe' option was specified if (var->hasStorage()) { - diagnoseAndRemoveAttr(attr, diag::actorisolated_mutable_storage); - return; + switch (attr->getKind()) { + case ActorIndependentKind::Safe: + diagnoseAndRemoveAttr(attr, diag::actorindependent_mutable_storage); + return; + + case ActorIndependentKind::Unsafe: + break; + } } // @actorIndependent can not be applied to local properties. if (dc->isLocalContext()) { - diagnoseAndRemoveAttr(attr, diag::actorisolated_local_var); + diagnoseAndRemoveAttr(attr, diag::actorindependent_local_var); return; } @@ -316,15 +326,26 @@ class AttributeChecker : public AttributeVisitor { // @actorIndependent only makes sense on an actor instance member. if (!dc->getSelfClassDecl() || !dc->getSelfClassDecl()->isActor()) { - diagnoseAndRemoveAttr(attr, diag::actorisolated_not_actor_member); + diagnoseAndRemoveAttr(attr, diag::actorindependent_not_actor_member); return; } - if (!cast(D)->isInstanceMember()) { + auto VD = cast(D); + if (!VD->isInstanceMember()) { diagnoseAndRemoveAttr( - attr, diag::actorisolated_not_actor_instance_member); + attr, diag::actorindependent_not_actor_instance_member); return; } + + (void)getActorIsolation(VD); + } + + void visitGlobalActorAttr(GlobalActorAttr *attr) { + auto nominal = dyn_cast(D); + if (!nominal) + return; // already diagnosed + + (void)nominal->isGlobalActor(); } }; } // end anonymous namespace @@ -949,14 +970,15 @@ void AttributeChecker::visitSPIAccessControlAttr(SPIAccessControlAttr *attr) { } // Forbid stored properties marked SPI in frozen types. - if (auto property = dyn_cast(VD)) - if (auto DC = dyn_cast(D->getDeclContext())) - if (property->hasStorage() && - !DC->isFormallyResilient() && - !DC->isSPI()) + if (auto property = dyn_cast(VD)) { + if (auto NTD = dyn_cast(D->getDeclContext())) { + if (property->isLayoutExposedToClients() && !NTD->isSPI()) { diagnoseAndRemoveAttr(attr, diag::spi_attribute_on_frozen_stored_properties, VD->getName()); + } + } + } } } @@ -1783,7 +1805,7 @@ void AttributeChecker::checkApplicationMainAttribute(DeclAttribute *attr, namelookup::lookupInModule(KitModule, Id_ApplicationDelegate, decls, NLKind::QualifiedLookup, namelookup::ResolutionKind::TypesOnly, - SF); + SF, NL_QualifiedDefault); if (decls.size() == 1) ApplicationDelegateProto = dyn_cast(decls[0]); } @@ -1970,6 +1992,9 @@ SynthesizeMainFunctionRequest::evaluate(Evaluator &evaluator, mainFunction = viableCandidates[0]; } + auto where = ExportContext::forDeclSignature(D); + diagnoseDeclAvailability(mainFunction, attr->getRange(), where, None); + auto *const func = FuncDecl::createImplicit( context, StaticSpellingKind::KeywordStatic, DeclName(context, DeclBaseName(context.Id_MainEntryPoint), @@ -2301,6 +2326,9 @@ void AttributeChecker::visitSpecializeAttr(SpecializeAttr *attr) { attr->getLocation(), /*allowConcreteGenericParams=*/true); attr->setSpecializedSignature(specializedSig); + + // Check the target function if there is one. + attr->getTargetFunctionDecl(FD); } void AttributeChecker::visitFixedLayoutAttr(FixedLayoutAttr *attr) { @@ -2414,7 +2442,7 @@ void AttributeChecker::visitDiscardableResultAttr(DiscardableResultAttr *attr) { /// Lookup the replaced decl in the replacments scope. static void lookupReplacedDecl(DeclNameRef replacedDeclName, - const DynamicReplacementAttr *attr, + const DeclAttribute *attr, const ValueDecl *replacement, SmallVectorImpl &results) { auto *declCtxt = replacement->getDeclContext(); @@ -2443,9 +2471,13 @@ static void lookupReplacedDecl(DeclNameRef replacedDeclName, if (!typeCtx) typeCtx = cast(declCtxt->getAsDecl())->getExtendedNominal(); + auto options = NL_QualifiedDefault; + if (declCtxt->isInSpecializeExtensionContext()) + options |= NL_IncludeUsableFromInline; + if (typeCtx) - moduleScopeCtxt->lookupQualified({typeCtx}, replacedDeclName, - NL_QualifiedDefault, results); + moduleScopeCtxt->lookupQualified({typeCtx}, replacedDeclName, options, + results); } /// Remove any argument labels from the interface type of the given value that @@ -2467,10 +2499,10 @@ static Type getDynamicComparisonType(ValueDecl *value) { return interfaceType->removeArgumentLabels(numArgumentLabels); } -static FuncDecl *findReplacedAccessor(DeclNameRef replacedVarName, - AccessorDecl *replacement, - DynamicReplacementAttr *attr, - ASTContext &ctx) { +static FuncDecl *findSimilarAccessor(DeclNameRef replacedVarName, + const AccessorDecl *replacement, + DeclAttribute *attr, ASTContext &ctx, + bool forDynamicReplacement) { // Retrieve the replaced abstract storage decl. SmallVector results; @@ -2528,7 +2560,7 @@ static FuncDecl *findReplacedAccessor(DeclNameRef replacedVarName, assert(!isa(results[0])); auto *origStorage = cast(results[0]); - if (!origStorage->isDynamic()) { + if (forDynamicReplacement && !origStorage->isDynamic()) { Diags.diagnose(attr->getLocation(), diag::dynamic_replacement_accessor_not_dynamic, origStorage->getName()); @@ -2556,31 +2588,47 @@ static FuncDecl *findReplacedAccessor(DeclNameRef replacedVarName, return origAccessor; } +static FuncDecl *findReplacedAccessor(DeclNameRef replacedVarName, + const AccessorDecl *replacement, + DeclAttribute *attr, + ASTContext &ctx) { + return findSimilarAccessor(replacedVarName, replacement, attr, ctx, + /*forDynamicReplacement*/ true); +} + +static FuncDecl *findTargetAccessor(DeclNameRef replacedVarName, + const AccessorDecl *replacement, + DeclAttribute *attr, + ASTContext &ctx) { + return findSimilarAccessor(replacedVarName, replacement, attr, ctx, + /*forDynamicReplacement*/ false); +} + static AbstractFunctionDecl * -findReplacedFunction(DeclNameRef replacedFunctionName, - const AbstractFunctionDecl *replacement, - DynamicReplacementAttr *attr, DiagnosticEngine *Diags) { +findSimilarFunction(DeclNameRef replacedFunctionName, + const AbstractFunctionDecl *base, DeclAttribute *attr, + DiagnosticEngine *Diags, bool forDynamicReplacement) { // Note: we might pass a constant attribute when typechecker is nullptr. // Any modification to attr must be guarded by a null check on TC. // SmallVector results; - lookupReplacedDecl(replacedFunctionName, attr, replacement, results); + lookupReplacedDecl(replacedFunctionName, attr, base, results); for (auto *result : results) { // Protocol requirements are not replaceable. if (isa(result->getDeclContext())) continue; // Check for static/instance mismatch. - if (result->isStatic() != replacement->isStatic()) + if (result->isStatic() != base->isStatic()) continue; auto resultTy = result->getInterfaceType(); - auto replaceTy = replacement->getInterfaceType(); + auto replaceTy = base->getInterfaceType(); TypeMatchOptions matchMode = TypeMatchFlags::AllowABICompatible; matchMode |= TypeMatchFlags::AllowCompatibleOpaqueTypeArchetypes; if (resultTy->matches(replaceTy, matchMode)) { - if (!result->isDynamic()) { + if (forDynamicReplacement && !result->isDynamic()) { if (Diags) { Diags->diagnose(attr->getLocation(), diag::dynamic_replacement_function_not_dynamic, @@ -2598,17 +2646,23 @@ findReplacedFunction(DeclNameRef replacedFunctionName, if (results.empty()) { Diags->diagnose(attr->getLocation(), - diag::dynamic_replacement_function_not_found, + forDynamicReplacement + ? diag::dynamic_replacement_function_not_found + : diag::specialize_target_function_not_found, replacedFunctionName); } else { Diags->diagnose(attr->getLocation(), - diag::dynamic_replacement_function_of_type_not_found, + forDynamicReplacement + ? diag::dynamic_replacement_function_of_type_not_found + : diag::specialize_target_function_of_type_not_found, replacedFunctionName, - replacement->getInterfaceType()->getCanonicalType()); + base->getInterfaceType()->getCanonicalType()); for (auto *result : results) { Diags->diagnose(SourceLoc(), - diag::dynamic_replacement_found_function_of_type, + forDynamicReplacement + ? diag::dynamic_replacement_found_function_of_type + : diag::specialize_found_function_of_type, result->getName(), result->getInterfaceType()->getCanonicalType()); } @@ -2617,6 +2671,22 @@ findReplacedFunction(DeclNameRef replacedFunctionName, return nullptr; } +static AbstractFunctionDecl * +findReplacedFunction(DeclNameRef replacedFunctionName, + const AbstractFunctionDecl *replacement, + DynamicReplacementAttr *attr, DiagnosticEngine *Diags) { + return findSimilarFunction(replacedFunctionName, replacement, attr, Diags, + true /*forDynamicReplacement*/); +} + +static AbstractFunctionDecl * +findTargetFunction(DeclNameRef targetFunctionName, + const AbstractFunctionDecl *base, + SpecializeAttr * attr, DiagnosticEngine *diags) { + return findSimilarFunction(targetFunctionName, base, attr, diags, + false /*forDynamicReplacement*/); +} + static AbstractStorageDecl * findReplacedStorageDecl(DeclNameRef replacedFunctionName, const AbstractStorageDecl *replacement, @@ -2979,9 +3049,9 @@ void AttributeChecker::visitCustomAttr(CustomAttr *attr) { return; } - // If the nominal type is a function builder type, verify that D is a + // If the nominal type is a result builder type, verify that D is a // function, storage with an explicit getter, or parameter of function type. - if (nominal->getAttrs().hasAttribute()) { + if (nominal->getAttrs().hasAttribute()) { ValueDecl *decl; if (auto param = dyn_cast(D)) { decl = param; @@ -2990,8 +3060,19 @@ void AttributeChecker::visitCustomAttr(CustomAttr *attr) { } else if (auto storage = dyn_cast(D)) { decl = storage; - // Check whether this is a property without an explicit getter. + // Check whether this is a storage declaration that is not permitted + // to have a result builder attached. auto shouldDiagnose = [&]() -> bool { + // An uninitialized stored property in a struct can have a function + // builder attached. + if (auto var = dyn_cast(decl)) { + if (var->isInstanceMember() && + isa(var->getDeclContext()) && + !var->getParentInitializer()) { + return false; + } + } + auto getter = storage->getParsedAccessor(AccessorKind::Get); if (!getter) return true; @@ -3010,7 +3091,7 @@ void AttributeChecker::visitCustomAttr(CustomAttr *attr) { if (shouldDiagnose()) { diagnose(attr->getLocation(), - diag::function_builder_attribute_on_storage_without_getter, + diag::result_builder_attribute_on_storage_without_getter, nominal->getName(), isa(storage) ? 0 : storage->getDeclContext()->isTypeContext() ? 1 @@ -3020,7 +3101,7 @@ void AttributeChecker::visitCustomAttr(CustomAttr *attr) { } } else { diagnose(attr->getLocation(), - diag::function_builder_attribute_not_allowed_here, + diag::result_builder_attribute_not_allowed_here, nominal->getName()); attr->setInvalid(); return; @@ -3028,27 +3109,36 @@ void AttributeChecker::visitCustomAttr(CustomAttr *attr) { // Diagnose and ignore arguments. if (attr->getArg()) { - diagnose(attr->getLocation(), diag::function_builder_arguments) + diagnose(attr->getLocation(), diag::result_builder_arguments) .highlight(attr->getArg()->getSourceRange()); } - // Complain if this isn't the primary function-builder attribute. - auto attached = decl->getAttachedFunctionBuilder(); + // Complain if this isn't the primary result-builder attribute. + auto attached = decl->getAttachedResultBuilder(); if (attached != attr) { - diagnose(attr->getLocation(), diag::function_builder_multiple, + diagnose(attr->getLocation(), diag::result_builder_multiple, isa(decl)); - diagnose(attached->getLocation(), diag::previous_function_builder_here); + diagnose(attached->getLocation(), diag::previous_result_builder_here); attr->setInvalid(); return; } else { - // Force any diagnostics associated with computing the function-builder + // Force any diagnostics associated with computing the result-builder // type. - (void) decl->getFunctionBuilderType(); + (void) decl->getResultBuilderType(); } return; } + // If the nominal type is a global actor, let the global actor attribute + // retrieval request perform checking for us. + if (nominal->isGlobalActor()) { + (void)D->getGlobalActorAttr(); + if (auto value = dyn_cast(D)) + (void)getActorIsolation(value); + return; + } + diagnose(attr->getLocation(), diag::nominal_type_not_attribute, nominal->getDescriptiveKind(), nominal->getName()); nominal->diagnose(diag::decl_declared_here, nominal->getName()); @@ -3065,7 +3155,7 @@ void AttributeChecker::visitPropertyWrapperAttr(PropertyWrapperAttr *attr) { (void)nominal->getPropertyWrapperTypeInfo(); } -void AttributeChecker::visitFunctionBuilderAttr(FunctionBuilderAttr *attr) { +void AttributeChecker::visitResultBuilderAttr(ResultBuilderAttr *attr) { auto *nominal = dyn_cast(D); SmallVector potentialMatches; bool supportsBuildBlock = TypeChecker::typeSupportsBuilderOp( @@ -3075,21 +3165,21 @@ void AttributeChecker::visitFunctionBuilderAttr(FunctionBuilderAttr *attr) { if (!supportsBuildBlock) { { auto diag = diagnose( - nominal->getLoc(), diag::function_builder_static_buildblock); + nominal->getLoc(), diag::result_builder_static_buildblock); // If there were no close matches, propose adding a stub. SourceLoc buildInsertionLoc; std::string stubIndent; Type componentType; std::tie(buildInsertionLoc, stubIndent, componentType) = - determineFunctionBuilderBuildFixItInfo(nominal); + determineResultBuilderBuildFixItInfo(nominal); if (buildInsertionLoc.isValid() && potentialMatches.empty()) { std::string fixItString; { llvm::raw_string_ostream out(fixItString); - printFunctionBuilderBuildFunction( + printResultBuilderBuildFunction( nominal, componentType, - FunctionBuilderBuildFunction::BuildBlock, + ResultBuilderBuildFunction::BuildBlock, stubIndent, out); } @@ -3105,13 +3195,13 @@ void AttributeChecker::visitFunctionBuilderAttr(FunctionBuilderAttr *attr) { if (isa(member) && member->getDeclContext()->getSelfNominalTypeDecl() == nominal) - diagnose(member->getLoc(), diag::function_builder_non_static_buildblock) + diagnose(member->getLoc(), diag::result_builder_non_static_buildblock) .fixItInsert(member->getAttributeInsertionLoc(true), "static "); else if (isa(member)) - diagnose(member->getLoc(), diag::function_builder_buildblock_enum_case); + diagnose(member->getLoc(), diag::result_builder_buildblock_enum_case); else diagnose(member->getLoc(), - diag::function_builder_buildblock_not_static_method); + diag::result_builder_buildblock_not_static_method); } } } @@ -3198,12 +3288,6 @@ void AttributeChecker::visitNonEphemeralAttr(NonEphemeralAttr *attr) { attr->setInvalid(); } -void TypeChecker::checkParameterAttributes(ParameterList *params) { - for (auto param: *params) { - checkDeclAttributes(param); - } -} - void AttributeChecker::checkOriginalDefinedInAttrs(Decl *D, ArrayRef Attrs) { if (Attrs.empty()) @@ -3494,6 +3578,34 @@ DynamicallyReplacedDeclRequest::evaluate(Evaluator &evaluator, return nullptr; } +ValueDecl * +SpecializeAttrTargetDeclRequest::evaluate(Evaluator &evaluator, + const ValueDecl *vd, + SpecializeAttr *attr) const { + if (auto *lazyResolver = attr->resolver) { + auto *decl = + lazyResolver->loadTargetFunctionDecl(attr, attr->resolverContextData); + attr->resolver = nullptr; + return decl; + } + + auto &ctx = vd->getASTContext(); + + auto targetFunctionName = attr->getTargetFunctionName(); + if (!targetFunctionName) + return nullptr; + + if (auto *ad = dyn_cast(vd)) { + return findTargetAccessor(targetFunctionName, ad, attr, ctx); + } + + if (auto *afd = dyn_cast(vd)) { + return findTargetFunction(targetFunctionName, afd, attr, &ctx.Diags); + } + + return nullptr; + +} /// Returns true if the given type conforms to `Differentiable` in the given /// context. If `tangentVectorEqualsSelf` is true, also check whether the given /// type satisfies `TangentVector == Self`. @@ -3723,18 +3835,23 @@ enum class AbstractFunctionDeclLookupErrorKind { CandidateNotFunctionDeclaration }; -/// Returns the function declaration corresponding to the given base type -/// (optional), function name, and lookup context. +/// Returns the original function (in the context of a derivative or transpose +/// function) declaration corresponding to the given base type (optional), +/// function name, lookup context, and the expected original function type. /// /// If the base type of the function is specified, member lookup is performed. /// Otherwise, unqualified lookup is performed. /// +/// If the expected original function type has a generic signature, any +/// candidate with a less constrained type signature than the expected original +/// function type will be treated as a viable candidate. +/// /// If the function declaration cannot be resolved, emits a diagnostic and /// returns nullptr. /// /// Used for resolving the referenced declaration in `@derivative` and /// `@transpose` attributes. -static AbstractFunctionDecl *findAbstractFunctionDecl( +static AbstractFunctionDecl *findAutoDiffOriginalFunctionDecl( DeclAttribute *attr, Type baseType, DeclNameRefWithLoc funcNameWithLoc, DeclContext *lookupContext, NameLookupOptions lookupOptions, const llvm::function_ref( @@ -4662,7 +4779,7 @@ static bool typeCheckDerivativeAttr(ASTContext &Ctx, Decl *D, } // Look up original function. - auto *originalAFD = findAbstractFunctionDecl( + auto *originalAFD = findAutoDiffOriginalFunctionDecl( attr, baseType, originalName, derivativeTypeCtx, lookupOptions, isValidOriginalCandidate, originalFnType); if (!originalAFD) { @@ -5221,7 +5338,7 @@ void AttributeChecker::visitTransposeAttr(TransposeAttr *attr) { auto funcLoc = originalName.Loc.getBaseNameLoc(); if (attr->getBaseTypeRepr()) funcLoc = attr->getBaseTypeRepr()->getLoc(); - auto *originalAFD = findAbstractFunctionDecl( + auto *originalAFD = findAutoDiffOriginalFunctionDecl( attr, baseType, originalName, transposeTypeCtx, lookupOptions, isValidOriginalCandidate, expectedOriginalFnType); if (!originalAFD) { diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index b8784d9e62978..517a0f44482b0 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -22,7 +22,9 @@ #include "swift/AST/Initializer.h" #include "swift/AST/NameLookup.h" #include "swift/AST/Pattern.h" +#include "swift/AST/ProtocolConformance.h" #include "swift/AST/SourceFile.h" +#include "swift/AST/TypeDeclFinder.h" #include "swift/AST/TypeRefinementContext.h" #include "swift/Basic/Defer.h" #include "swift/Basic/SourceManager.h" @@ -35,6 +37,208 @@ #include "llvm/Support/SaveAndRestore.h" using namespace swift; +ExportContext::ExportContext(DeclContext *DC, FragileFunctionKind kind, + bool spi, bool exported, bool implicit, bool deprecated, + Optional unavailablePlatformKind) + : DC(DC), FragileKind(kind) { + SPI = spi; + Exported = exported; + Implicit = implicit; + Deprecated = deprecated; + if (unavailablePlatformKind) { + Unavailable = 1; + Platform = unsigned(*unavailablePlatformKind); + } else { + Unavailable = 0; + Platform = 0; + } + + Reason = unsigned(ExportabilityReason::General); +} + +bool swift::isExported(const ValueDecl *VD) { + if (VD->getAttrs().hasAttribute()) + return false; + + // Is this part of the module's API or ABI? + AccessScope accessScope = + VD->getFormalAccessScope(nullptr, + /*treatUsableFromInlineAsPublic*/true); + if (accessScope.isPublic()) + return true; + + // Is this a stored property in a @frozen struct or class? + if (auto *property = dyn_cast(VD)) + if (property->isLayoutExposedToClients()) + return true; + + return false; +} + +bool swift::isExported(const Decl *D) { + if (auto *VD = dyn_cast(D)) { + return isExported(VD); + } + if (auto *PBD = dyn_cast(D)) { + for (unsigned i = 0, e = PBD->getNumPatternEntries(); i < e; ++i) { + if (auto *VD = PBD->getAnchoringVarDecl(i)) + return isExported(VD); + } + + return false; + } + if (auto *ED = dyn_cast(D)) { + if (auto *NTD = ED->getExtendedNominal()) + return isExported(NTD); + + return false; + } + + return true; +} + +template +static void forEachOuterDecl(DeclContext *DC, Fn fn) { + for (; !DC->isModuleScopeContext(); DC = DC->getParent()) { + switch (DC->getContextKind()) { + case DeclContextKind::AbstractClosureExpr: + case DeclContextKind::TopLevelCodeDecl: + case DeclContextKind::SerializedLocal: + case DeclContextKind::Module: + case DeclContextKind::FileUnit: + break; + + case DeclContextKind::Initializer: + if (auto *PBI = dyn_cast(DC)) + fn(PBI->getBinding()); + break; + + case DeclContextKind::SubscriptDecl: + fn(cast(DC)); + break; + + case DeclContextKind::EnumElementDecl: + fn(cast(DC)); + break; + + case DeclContextKind::AbstractFunctionDecl: + fn(cast(DC)); + + if (auto *AD = dyn_cast(DC)) + fn(AD->getStorage()); + break; + + case DeclContextKind::GenericTypeDecl: + fn(cast(DC)); + break; + + case DeclContextKind::ExtensionDecl: + fn(cast(DC)); + break; + } + } +} + +static void computeExportContextBits(ASTContext &Ctx, Decl *D, + bool *spi, bool *implicit, bool *deprecated, + Optional *unavailablePlatformKind) { + if (D->isSPI()) + *spi = true; + + if (D->isImplicit()) + *implicit = true; + + if (D->getAttrs().getDeprecated(Ctx)) + *deprecated = true; + + if (auto *A = D->getAttrs().getUnavailable(Ctx)) { + *unavailablePlatformKind = A->Platform; + } + + if (auto *PBD = dyn_cast(D)) { + for (unsigned i = 0, e = PBD->getNumPatternEntries(); i < e; ++i) { + if (auto *VD = PBD->getAnchoringVarDecl(i)) + computeExportContextBits(Ctx, VD, spi, implicit, deprecated, + unavailablePlatformKind); + } + } +} + +ExportContext ExportContext::forDeclSignature(Decl *D) { + auto &Ctx = D->getASTContext(); + + auto *DC = D->getInnermostDeclContext(); + auto fragileKind = DC->getFragileFunctionKind(); + + bool spi = false; + bool implicit = false; + bool deprecated = false; + Optional unavailablePlatformKind; + computeExportContextBits(Ctx, D, &spi, &implicit, &deprecated, + &unavailablePlatformKind); + forEachOuterDecl(D->getDeclContext(), + [&](Decl *D) { + computeExportContextBits(Ctx, D, + &spi, &implicit, &deprecated, + &unavailablePlatformKind); + }); + + bool exported = ::isExported(D); + + return ExportContext(DC, fragileKind, spi, exported, implicit, deprecated, + unavailablePlatformKind); +} + +ExportContext ExportContext::forFunctionBody(DeclContext *DC) { + auto &Ctx = DC->getASTContext(); + + auto fragileKind = DC->getFragileFunctionKind(); + + bool spi = false; + bool implicit = false; + bool deprecated = false; + Optional unavailablePlatformKind; + forEachOuterDecl(DC, + [&](Decl *D) { + computeExportContextBits(Ctx, D, + &spi, &implicit, &deprecated, + &unavailablePlatformKind); + }); + + bool exported = false; + + return ExportContext(DC, fragileKind, spi, exported, implicit, deprecated, + unavailablePlatformKind); +} + +ExportContext ExportContext::withReason(ExportabilityReason reason) const { + auto copy = *this; + copy.Reason = unsigned(reason); + return copy; +} + +ExportContext ExportContext::withExported(bool exported) const { + auto copy = *this; + copy.Exported = isExported() && exported; + return copy; +} + +Optional ExportContext::getUnavailablePlatformKind() const { + if (Unavailable) + return PlatformKind(Platform); + return None; +} + +bool ExportContext::mustOnlyReferenceExportedDecls() const { + return Exported || FragileKind.kind != FragileFunctionKind::None; +} + +Optional ExportContext::getExportabilityReason() const { + if (Exported) + return ExportabilityReason(Reason); + return None; +} + /// Returns the first availability attribute on the declaration that is active /// on the target platform. static const AvailableAttr *getActiveAvailableAttribute(const Decl *D, @@ -710,29 +914,6 @@ TypeChecker::overApproximateAvailabilityAtLocation(SourceLoc loc, return OverApproximateContext; } -bool TypeChecker::isDeclAvailable(const Decl *D, SourceLoc referenceLoc, - const DeclContext *referenceDC, - AvailabilityContext &OutAvailableInfo) { - ASTContext &Context = referenceDC->getASTContext(); - - AvailabilityContext safeRangeUnderApprox{ - AvailabilityInference::availableRange(D, Context)}; - AvailabilityContext runningOSOverApprox = - overApproximateAvailabilityAtLocation(referenceLoc, referenceDC); - - // The reference is safe if an over-approximation of the running OS - // versions is fully contained within an under-approximation - // of the versions on which the declaration is available. If this - // containment cannot be guaranteed, we say the reference is - // not available. - if (!(runningOSOverApprox.isContainedIn(safeRangeUnderApprox))) { - OutAvailableInfo = safeRangeUnderApprox; - return false; - } - - return true; -} - Optional TypeChecker::checkDeclarationAvailability(const Decl *D, SourceLoc referenceLoc, const DeclContext *referenceDC) { @@ -747,24 +928,24 @@ TypeChecker::checkDeclarationAvailability(const Decl *D, SourceLoc referenceLoc, return None; } - auto safeRangeUnderApprox = AvailabilityContext::neverAvailable(); - if (isDeclAvailable(D, referenceLoc, referenceDC, safeRangeUnderApprox)) { + AvailabilityContext runningOSOverApprox = + overApproximateAvailabilityAtLocation(referenceLoc, referenceDC); + + AvailabilityContext safeRangeUnderApprox{ + AvailabilityInference::availableRange(D, Context)}; + + // The reference is safe if an over-approximation of the running OS + // versions is fully contained within an under-approximation + // of the versions on which the declaration is available. If this + // containment cannot be guaranteed, we say the reference is + // not available. + if (runningOSOverApprox.isContainedIn(safeRangeUnderApprox)) return None; - } - // safeRangeUnderApprox now holds the safe range. VersionRange version = safeRangeUnderApprox.getOSVersion(); return UnavailabilityReason::requiresVersionRange(version); } -void TypeChecker::diagnosePotentialUnavailability( - const ValueDecl *D, SourceRange ReferenceRange, - const DeclContext *ReferenceDC, - const UnavailabilityReason &Reason) { - diagnosePotentialUnavailability(D, D->getName(), ReferenceRange, - ReferenceDC, Reason); -} - /// A class that walks the AST to find the innermost (i.e., deepest) node that /// contains a target SourceRange and matches a particular criterion. /// This class finds the innermost nodes of interest by walking @@ -1395,8 +1576,9 @@ void TypeChecker::diagnosePotentialOpaqueTypeUnavailability( } void TypeChecker::diagnosePotentialUnavailability( - const Decl *D, DeclName Name, SourceRange ReferenceRange, - const DeclContext *ReferenceDC, const UnavailabilityReason &Reason) { + const ValueDecl *D, SourceRange ReferenceRange, + const DeclContext *ReferenceDC, + const UnavailabilityReason &Reason) { ASTContext &Context = ReferenceDC->getASTContext(); // We only emit diagnostics for API unavailability, not for explicitly @@ -1411,7 +1593,7 @@ void TypeChecker::diagnosePotentialUnavailability( auto Err = Context.Diags.diagnose( ReferenceRange.Start, diag::availability_decl_only_version_newer, - Name, prettyPlatformString(targetPlatform(Context.LangOpts)), + D->getName(), prettyPlatformString(targetPlatform(Context.LangOpts)), Reason.getRequiredOSVersionRange().getLowerEndpoint()); // Direct a fixit to the error if an existing guard is nearly-correct @@ -1472,85 +1654,15 @@ const AvailableAttr *TypeChecker::getDeprecated(const Decl *D) { return nullptr; } -/// Returns true if some declaration lexically enclosing the reference -/// matches the passed in predicate and false otherwise. -static bool -someEnclosingDeclMatches(SourceRange ReferenceRange, - const DeclContext *ReferenceDC, - llvm::function_ref Pred) { - // Climb the DeclContext hierarchy to see if any of the containing - // declarations matches the predicate. - const DeclContext *DC = ReferenceDC; - while (true) { - auto *D = DC->getInnermostDeclarationDeclContext(); - if (!D) - break; - - if (Pred(D)) { - return true; - } - - // If we are in an accessor, check to see if the associated - // property matches the predicate. - if (auto accessor = dyn_cast(D)) { - if (Pred(accessor->getStorage())) - return true; - } - - DC = D->getDeclContext(); - } - - // Search the AST starting from our innermost declaration context to see if - // if the reference is inside a property declaration but not inside an - // accessor (this can happen for the TypeRepr for the declared type of a - // property, for example). - // We can't rely on the DeclContext hierarchy climb above because properties - // do not introduce a new DeclContext. - - // Don't search for a containing declaration if we don't have a source range. - if (ReferenceRange.isInvalid()) - return false; - - ASTContext &Ctx = ReferenceDC->getASTContext(); - const Decl *DeclToSearch = - findContainingDeclaration(ReferenceRange, ReferenceDC, Ctx.SourceMgr); - - // We may not be able to find a declaration to search if the ReferenceRange - // isn't useful (i.e., we are in synthesized code). - if (!DeclToSearch) - return false; - - return Pred(abstractSyntaxDeclForAvailableAttribute(DeclToSearch)); -} - -/// Returns true if the reference or any of its parents is an -/// implicit function. -static bool isInsideImplicitFunction(SourceRange ReferenceRange, - const DeclContext *DC) { - auto IsInsideImplicitFunc = [](const Decl *D) { - auto *AFD = dyn_cast(D); - return AFD && AFD->isImplicit(); - }; - - return someEnclosingDeclMatches(ReferenceRange, DC, IsInsideImplicitFunc); -} - -/// Returns true if the reference or any of its parents is an -/// unavailable (or obsoleted) declaration. -static bool isInsideUnavailableDeclaration(SourceRange ReferenceRange, - const DeclContext *ReferenceDC) { - auto IsUnavailable = [](const Decl *D) { - return D->getAttrs().getUnavailable(D->getASTContext()); - }; - - return someEnclosingDeclMatches(ReferenceRange, ReferenceDC, IsUnavailable); -} - /// Returns true if the reference or any of its parents is an /// unconditional unavailable declaration for the same platform. static bool isInsideCompatibleUnavailableDeclaration( - const ValueDecl *referencedD, SourceRange ReferenceRange, - const DeclContext *ReferenceDC, const AvailableAttr *attr) { + const ValueDecl *D, ExportContext where, + const AvailableAttr *attr) { + auto referencedPlatform = where.getUnavailablePlatformKind(); + if (!referencedPlatform) + return false; + if (!attr->isUnconditionallyUnavailable()) { return false; } @@ -1559,28 +1671,13 @@ static bool isInsideCompatibleUnavailableDeclaration( // but allow the use of types. PlatformKind platform = attr->Platform; if (platform == PlatformKind::none && - !isa(referencedD)) { + !isa(D)) { return false; } - auto IsUnavailable = [platform](const Decl *D) { - auto EnclosingUnavailable = - D->getAttrs().getUnavailable(D->getASTContext()); - return EnclosingUnavailable && EnclosingUnavailable->Platform == platform; - }; - - return someEnclosingDeclMatches(ReferenceRange, ReferenceDC, IsUnavailable); -} - -/// Returns true if the reference is lexically contained in a declaration -/// that is deprecated on all deployment targets. -static bool isInsideDeprecatedDeclaration(SourceRange ReferenceRange, - const DeclContext *ReferenceDC){ - auto IsDeprecated = [](const Decl *D) { - return D->getAttrs().getDeprecated(D->getASTContext()); - }; - - return someEnclosingDeclMatches(ReferenceRange, ReferenceDC, IsDeprecated); + return (*referencedPlatform == platform || + inheritsAvailabilityFromPlatform(platform, + *referencedPlatform)); } static void fixItAvailableAttrRename(InFlightDiagnostic &diag, @@ -1959,26 +2056,21 @@ getAccessorKindAndNameForDiagnostics(const ValueDecl *D) { } void TypeChecker::diagnoseIfDeprecated(SourceRange ReferenceRange, - const DeclContext *ReferenceDC, + ExportContext Where, const ValueDecl *DeprecatedDecl, const ApplyExpr *Call) { const AvailableAttr *Attr = TypeChecker::getDeprecated(DeprecatedDecl); if (!Attr) return; - // We don't want deprecated declarations to trigger warnings - // in synthesized code. - if (ReferenceRange.isInvalid() && - isInsideImplicitFunction(ReferenceRange, ReferenceDC)) { - return; - } // We match the behavior of clang to not report deprecation warnings // inside declarations that are themselves deprecated on all deployment // targets. - if (isInsideDeprecatedDeclaration(ReferenceRange, ReferenceDC)) { + if (Where.isDeprecated()) { return; } + auto *ReferenceDC = Where.getDeclContext(); auto &Context = ReferenceDC->getASTContext(); if (!Context.LangOpts.DisableAvailabilityChecking) { AvailabilityContext RunningOSVersions = @@ -2065,8 +2157,8 @@ void swift::diagnoseUnavailableOverride(ValueDecl *override, return; } - diagnoseExplicitUnavailability(base, override->getLoc(), - override->getDeclContext(), + ExportContext where = ExportContext::forDeclSignature(override); + diagnoseExplicitUnavailability(base, override->getLoc(), where, /*Flags*/None, [&](InFlightDiagnostic &diag) { ParsedDeclName parsedName = parseDeclName(attr->Rename); @@ -2099,10 +2191,10 @@ void swift::diagnoseUnavailableOverride(ValueDecl *override, /// marked as unavailable, either through "unavailable" or "obsoleted:". bool swift::diagnoseExplicitUnavailability(const ValueDecl *D, SourceRange R, - const DeclContext *DC, + ExportContext Where, const ApplyExpr *call, DeclAvailabilityFlags Flags) { - return diagnoseExplicitUnavailability(D, R, DC, Flags, + return diagnoseExplicitUnavailability(D, R, Where, Flags, [=](InFlightDiagnostic &diag) { fixItAvailableAttrRename(diag, R, D, AvailableAttr::isUnavailable(D), call); @@ -2173,30 +2265,19 @@ bool isSubscriptReturningString(const ValueDecl *D, ASTContext &Context) { bool swift::diagnoseExplicitUnavailability( const ValueDecl *D, SourceRange R, - const DeclContext *DC, + ExportContext Where, DeclAvailabilityFlags Flags, llvm::function_ref attachRenameFixIts) { auto *Attr = AvailableAttr::isUnavailable(D); if (!Attr) return false; - // Suppress the diagnostic if we are in synthesized code inside - // a synthesized function and the reference is lexically - // contained in a declaration that is itself marked unavailable. - // The right thing to do here is to not synthesize that code in the - // first place. rdar://problem/20491640 - if (R.isInvalid() && isInsideImplicitFunction(R, DC) && - isInsideUnavailableDeclaration(R, DC)) { - return false; - } - // Calling unavailable code from within code with the same // unavailability is OK -- the eventual caller can't call the // enclosing code in the same situations it wouldn't be able to // call this code. - if (isInsideCompatibleUnavailableDeclaration(D, R, DC, Attr)) { + if (isInsideCompatibleUnavailableDeclaration(D, Where, Attr)) return false; - } SourceLoc Loc = R.Start; DeclName Name; @@ -2337,33 +2418,13 @@ class AvailabilityWalker : public ASTWalker { }; ASTContext &Context; - DeclContext *DC; MemberAccessContext AccessContext = MemberAccessContext::Getter; SmallVector ExprStack; - FragileFunctionKind FragileKind; - - /// Returns true if DC is an \c init(rawValue:) declaration and it is marked - /// implicit. - bool inSynthesizedInitRawValue() { - auto init = dyn_cast_or_null( - DC->getInnermostDeclarationDeclContext()); - - return init && - init->isImplicit() && - init->getParameters()->size() == 1 && - init->getParameters()->get(0)->getArgumentName() == - Context.Id_rawValue; - } + ExportContext Where; public: - AvailabilityWalker(DeclContext *DC) : Context(DC->getASTContext()), DC(DC) { - FragileKind = DC->getFragileFunctionKind(); - } - - // FIXME: Remove this - bool shouldWalkAccessorsTheOldWay() override { - return true; - } + AvailabilityWalker(ExportContext Where) + : Context(Where.getDeclContext()->getASTContext()), Where(Where) {} bool shouldWalkIntoSeparatelyCheckedClosure(ClosureExpr *expr) override { return false; @@ -2372,6 +2433,8 @@ class AvailabilityWalker : public ASTWalker { bool shouldWalkIntoTapExpression() override { return false; } std::pair walkToExprPre(Expr *E) override { + auto *DC = Where.getDeclContext(); + ExprStack.push_back(E); auto visitChildren = [&]() { return std::make_pair(true, E); }; @@ -2381,20 +2444,8 @@ class AvailabilityWalker : public ASTWalker { }; if (auto DR = dyn_cast(E)) { - DeclAvailabilityFlags flags = None; - if (inSynthesizedInitRawValue()) - // HACK: If a raw-value enum has cases with `@available(introduced:)` - // attributes, the auto-derived `init(rawValue:)` will contain - // DeclRefExprs which reference those cases. It will also contain - // appropriate `guard #available` statements to keep them from running - // on older versions, but the availability checker can't verify that - // because the synthesized code uses invalid SourceLocs. Don't diagnose - // these errors; instead, take it on faith that - // DerivedConformanceRawRepresentable will do the right thing. - flags |= DeclAvailabilityFlag::AllowPotentiallyUnavailable; - diagAvailability(DR->getDeclRef(), DR->getSourceRange(), - getEnclosingApplyExpr(), flags); + getEnclosingApplyExpr(), None); maybeDiagStorageAccess(DR->getDecl(), DR->getSourceRange(), DC); } if (auto MR = dyn_cast(E)) { @@ -2428,6 +2479,26 @@ class AvailabilityWalker : public ASTWalker { walkInOutExpr(IO); return skipChildren(); } + if (auto T = dyn_cast(E)) { + if (!T->isImplicit()) { + diagnoseTypeAvailability(T->getTypeRepr(), T->getType(), E->getLoc(), + Where); + } + } + if (auto CE = dyn_cast(E)) { + for (auto *param : *CE->getParameters()) { + diagnoseTypeAvailability(param->getTypeRepr(), param->getInterfaceType(), + E->getLoc(), Where); + } + diagnoseTypeAvailability(CE->hasExplicitResultType() + ? CE->getExplicitResultTypeRepr() + : nullptr, + CE->getResultType(), E->getLoc(), Where); + } + if (auto CE = dyn_cast(E)) { + diagnoseTypeAvailability(CE->getCastTypeRepr(), CE->getCastType(), + E->getLoc(), Where); + } return visitChildren(); } @@ -2510,6 +2581,7 @@ class AvailabilityWalker : public ASTWalker { return; // Diagnose for appropriate accessors, given the access context. + auto *DC = Where.getDeclContext(); maybeDiagStorageAccess(D, E->getSourceRange(), DC); } @@ -2625,6 +2697,10 @@ AvailabilityWalker::diagAvailability(ConcreteDeclRef declRef, SourceRange R, return false; const ValueDecl *D = declRef.getDecl(); + // Generic parameters are always available. + if (isa(D)) + return false; + if (auto *attr = AvailableAttr::isUnavailable(D)) { if (diagnoseIncDecRemoval(D, R, attr)) return true; @@ -2642,12 +2718,22 @@ AvailabilityWalker::diagAvailability(ConcreteDeclRef declRef, SourceRange R, return false; } - if (FragileKind.kind != FragileFunctionKind::None) - if (R.isValid()) - if (TypeChecker::diagnoseInlinableDeclRef(R.Start, declRef, DC, FragileKind)) - return true; + if (R.isValid()) { + if (TypeChecker::diagnoseInlinableDeclRefAccess(R.Start, D, Where)) + return true; - if (diagnoseExplicitUnavailability(D, R, DC, call, Flags)) + if (TypeChecker::diagnoseDeclRefExportability(R.Start, D, Where)) + return true; + } + + if (R.isValid()) { + if (diagnoseSubstitutionMapAvailability(R.Start, declRef.getSubstitutions(), + Where)) { + return true; + } + } + + if (diagnoseExplicitUnavailability(D, R, Where, call, Flags)) return true; // Make sure not to diagnose an accessor's deprecation if we already @@ -2657,15 +2743,14 @@ AvailabilityWalker::diagAvailability(ConcreteDeclRef declRef, SourceRange R, // Diagnose for deprecation if (!isAccessorWithDeprecatedStorage) - TypeChecker::diagnoseIfDeprecated(R, DC, D, call); - - if (Flags.contains(DeclAvailabilityFlag::AllowPotentiallyUnavailable)) - return false; + TypeChecker::diagnoseIfDeprecated(R, Where, D, call); if (Flags.contains(DeclAvailabilityFlag::AllowPotentiallyUnavailableProtocol) && isa(D)) return false; + auto *DC = Where.getDeclContext(); + // Diagnose (and possibly signal) for potential unavailability auto maybeUnavail = TypeChecker::checkDeclarationAvailability(D, R.Start, DC); if (maybeUnavail.hasValue()) { @@ -2723,6 +2808,7 @@ AvailabilityWalker::diagnoseIncDecRemoval(const ValueDecl *D, SourceRange R, // If the expression type is integer or floating point, then we can rewrite it // to "lvalue += 1". + auto *DC = Where.getDeclContext(); std::string replacement; if (isIntegerOrFloatingPointType(call->getType(), DC, Context)) replacement = isInc ? " += 1" : " -= 1"; @@ -2838,19 +2924,268 @@ AvailabilityWalker::diagnoseMemoryLayoutMigration(const ValueDecl *D, /// Diagnose uses of unavailable declarations. void swift::diagAvailability(const Expr *E, DeclContext *DC) { - AvailabilityWalker walker(DC); + auto where = ExportContext::forFunctionBody(DC); + if (where.isImplicit()) + return; + AvailabilityWalker walker(where); const_cast(E)->walk(walker); } +namespace { + +class StmtAvailabilityWalker : public ASTWalker { + ExportContext Where; + +public: + explicit StmtAvailabilityWalker(ExportContext where) + : Where(where) {} + + /// We'll visit the expression from performSyntacticExprDiagnostics(). + std::pair walkToExprPre(Expr *E) override { + return std::make_pair(false, E); + } + + bool walkToTypeReprPre(TypeRepr *T) override { + diagnoseTypeReprAvailability(T, Where); + return false; + } + + std::pair walkToPatternPre(Pattern *P) override { + if (auto *IP = dyn_cast(P)) + diagnoseTypeAvailability(IP->getCastType(), P->getLoc(), Where); + + return std::make_pair(true, P); + } +}; + +} + +void swift::diagAvailability(const Stmt *S, DeclContext *DC) { + // We'll visit the individual statements when we check them. + if (isa(S)) + return; + + auto where = ExportContext::forFunctionBody(DC); + if (where.isImplicit()) + return; + + StmtAvailabilityWalker walker(where); + const_cast(S)->walk(walker); +} + +namespace { + +class TypeReprAvailabilityWalker : public ASTWalker { + ExportContext where; + DeclAvailabilityFlags flags; + + bool checkComponentIdentTypeRepr(ComponentIdentTypeRepr *ITR) { + if (auto *typeDecl = ITR->getBoundDecl()) { + auto range = ITR->getNameLoc().getSourceRange(); + if (diagnoseDeclAvailability(typeDecl, range, where, flags)) + return true; + } + + bool foundAnyIssues = false; + + if (auto *GTR = dyn_cast(ITR)) { + auto genericFlags = flags; + genericFlags -= DeclAvailabilityFlag::AllowPotentiallyUnavailableProtocol; + + for (auto *genericArg : GTR->getGenericArgs()) { + if (diagnoseTypeReprAvailability(genericArg, where, genericFlags)) + foundAnyIssues = true; + } + } + + return foundAnyIssues; + } + +public: + bool foundAnyIssues = false; + + TypeReprAvailabilityWalker(ExportContext where, + DeclAvailabilityFlags flags) + : where(where), flags(flags) {} + + bool walkToTypeReprPre(TypeRepr *T) override { + if (auto *ITR = dyn_cast(T)) { + if (auto *CTR = dyn_cast(ITR)) { + for (auto *comp : CTR->getComponents()) { + // If a parent type is unavailable, don't go on to diagnose + // the member since that will just produce a redundant + // diagnostic. + if (checkComponentIdentTypeRepr(comp)) { + foundAnyIssues = true; + break; + } + } + } else if (auto *GTR = dyn_cast(T)) { + if (checkComponentIdentTypeRepr(GTR)) + foundAnyIssues = true; + } else if (auto *STR = dyn_cast(T)) { + if (checkComponentIdentTypeRepr(STR)) + foundAnyIssues = true; + } + + // We've already visited all the children above, so we don't + // need to recurse. + return false; + } + + return true; + } +}; + +} + +bool swift::diagnoseTypeReprAvailability(const TypeRepr *T, ExportContext where, + DeclAvailabilityFlags flags) { + if (!T) + return false; + TypeReprAvailabilityWalker walker(where, flags); + const_cast(T)->walk(walker); + return walker.foundAnyIssues; +} + +namespace { + +class ProblematicTypeFinder : public TypeDeclFinder { + SourceLoc Loc; + ExportContext Where; + DeclAvailabilityFlags Flags; + +public: + ProblematicTypeFinder(SourceLoc Loc, ExportContext Where, + DeclAvailabilityFlags Flags) + : Loc(Loc), Where(Where), Flags(Flags) {} + + void visitTypeDecl(TypeDecl *decl) { + // We only need to diagnose exportability here. Availability was + // already checked on the TypeRepr. + if (Where.mustOnlyReferenceExportedDecls()) + TypeChecker::diagnoseDeclRefExportability(Loc, decl, Where); + } + + Action visitNominalType(NominalType *ty) override { + visitTypeDecl(ty->getDecl()); + + // If some generic parameters are missing, don't check conformances. + if (ty->hasUnboundGenericType()) + return Action::Continue; + + // When the DeclContext parameter to getContextSubstitutionMap() + // is a protocol declaration, the receiver must be a concrete + // type, so it doesn't make sense to perform this check on + // protocol types. + if (isa(ty)) + return Action::Continue; + + ModuleDecl *useModule = Where.getDeclContext()->getParentModule(); + auto subs = ty->getContextSubstitutionMap(useModule, ty->getDecl()); + (void) diagnoseSubstitutionMapAvailability(Loc, subs, Where); + return Action::Continue; + } + + Action visitBoundGenericType(BoundGenericType *ty) override { + visitTypeDecl(ty->getDecl()); + + ModuleDecl *useModule = Where.getDeclContext()->getParentModule(); + auto subs = ty->getContextSubstitutionMap(useModule, ty->getDecl()); + (void) diagnoseSubstitutionMapAvailability(Loc, subs, Where); + return Action::Continue; + } + + Action visitTypeAliasType(TypeAliasType *ty) override { + visitTypeDecl(ty->getDecl()); + + auto subs = ty->getSubstitutionMap(); + (void) diagnoseSubstitutionMapAvailability(Loc, subs, Where); + return Action::Continue; + } + + // We diagnose unserializable Clang function types in the + // post-visitor so that we diagnose any unexportable component + // types first. + Action walkToTypePost(Type T) override { + if (Where.mustOnlyReferenceExportedDecls()) { + if (auto fnType = T->getAs()) { + if (auto clangType = fnType->getClangTypeInfo().getType()) { + auto *DC = Where.getDeclContext(); + auto &ctx = DC->getASTContext(); + auto loader = ctx.getClangModuleLoader(); + // Serialization will serialize the sugared type if it can, + // but we need the canonical type to be serializable or else + // canonicalization (e.g. in SIL) might break things. + if (!loader->isSerializable(clangType, /*check canonical*/ true)) { + ctx.Diags.diagnose(Loc, diag::unexportable_clang_function_type, T); + } + } + } + } + + return TypeDeclFinder::walkToTypePost(T); + } +}; + +} + +void swift::diagnoseTypeAvailability(Type T, SourceLoc loc, ExportContext where, + DeclAvailabilityFlags flags) { + if (!T) + return; + T.walk(ProblematicTypeFinder(loc, where, flags)); +} + +void swift::diagnoseTypeAvailability(const TypeRepr *TR, Type T, SourceLoc loc, + ExportContext where, + DeclAvailabilityFlags flags) { + if (diagnoseTypeReprAvailability(TR, where, flags)) + return; + diagnoseTypeAvailability(T, loc, where, flags); +} + +bool +swift::diagnoseConformanceAvailability(SourceLoc loc, + ProtocolConformanceRef conformance, + ExportContext where) { + if (!conformance.isConcrete()) + return false; + const ProtocolConformance *concreteConf = conformance.getConcrete(); + + auto *DC = where.getDeclContext(); + SubstitutionMap subConformanceSubs = + concreteConf->getSubstitutions(DC->getParentModule()); + diagnoseSubstitutionMapAvailability(loc, subConformanceSubs, where); + const RootProtocolConformance *rootConf = + concreteConf->getRootConformance(); + + return TypeChecker::diagnoseConformanceExportability( + loc, rootConf, where); +} + +bool +swift::diagnoseSubstitutionMapAvailability(SourceLoc loc, + SubstitutionMap subs, + ExportContext where) { + bool hadAnyIssues = false; + for (ProtocolConformanceRef conformance : subs.getConformances()) { + if (diagnoseConformanceAvailability(loc, conformance, where)) + hadAnyIssues = true; + } + return hadAnyIssues; +} + /// Run the Availability-diagnostics algorithm otherwise used in an expr /// context, but for non-expr contexts such as TypeDecls referenced from /// TypeReprs. bool swift::diagnoseDeclAvailability(const ValueDecl *Decl, - DeclContext *DC, SourceRange R, + ExportContext Where, DeclAvailabilityFlags Flags) { - AvailabilityWalker AW(DC); + assert(!Where.isImplicit()); + AvailabilityWalker AW(Where); return AW.diagAvailability(const_cast(Decl), R, nullptr, Flags); } @@ -2866,9 +3201,10 @@ static bool declNeedsExplicitAvailability(const Decl *decl) { return false; } - // Skip functions emitted into clients or SPI. + // Skip functions emitted into clients, SPI or implicit. if (decl->getAttrs().hasAttribute() || - decl->isSPI()) + decl->isSPI() || + decl->isImplicit()) return false; // Warn on decls without an introduction version. diff --git a/lib/Sema/TypeCheckAvailability.h b/lib/Sema/TypeCheckAvailability.h index 526db08e892a8..9d23c4efbb80e 100644 --- a/lib/Sema/TypeCheckAvailability.h +++ b/lib/Sema/TypeCheckAvailability.h @@ -13,6 +13,7 @@ #ifndef SWIFT_SEMA_TYPE_CHECK_AVAILABILITY_H #define SWIFT_SEMA_TYPE_CHECK_AVAILABILITY_H +#include "swift/AST/DeclContext.h" #include "swift/AST/AttrKind.h" #include "swift/AST/Identifier.h" #include "swift/Basic/LLVM.h" @@ -24,15 +25,16 @@ namespace swift { class ApplyExpr; class AvailableAttr; - class DeclContext; class Expr; class InFlightDiagnostic; class Decl; + class ProtocolConformanceRef; + class Stmt; + class SubstitutionMap; + class Type; + class TypeRepr; class ValueDecl; -/// Diagnose uses of unavailable declarations. -void diagAvailability(const Expr *E, DeclContext *DC); - enum class DeclAvailabilityFlag : uint8_t { /// Do not diagnose uses of protocols in versions before they were introduced. /// Used when type-checking protocol conformances, since conforming to a @@ -47,23 +49,169 @@ enum class DeclAvailabilityFlag : uint8_t { /// is inout and both the getter and setter must be available. ForInout = 1 << 2, - /// Do not diagnose uses of declarations in versions before they were - /// introduced. Used to work around availability-checker bugs. - AllowPotentiallyUnavailable = 1 << 3, - /// If an error diagnostic would normally be emitted, demote the error to a /// warning. Used for ObjC key path components. - ForObjCKeyPath = 1 << 4 + ForObjCKeyPath = 1 << 3 }; using DeclAvailabilityFlags = OptionSet; +// This enum must be kept in sync with +// diag::decl_from_hidden_module and +// diag::conformance_from_implementation_only_module. +enum class ExportabilityReason : unsigned { + General, + PropertyWrapper, + ExtensionWithPublicMembers, + ExtensionWithConditionalConformances +}; + +/// A description of the restrictions on what declarations can be referenced +/// from the signature or body of a declaration. +/// +/// We say a declaration is "exported" if all of the following holds: +/// +/// - the declaration is `public` or `@usableFromInline` +/// - the declaration is not `@_spi` +/// - the declaration was not imported from an `@_implementationOnly` import +/// +/// The "signature" of a declaration is the set of all types written in the +/// declaration (such as function parameter and return types), but not +/// including the function body. +/// +/// The signature of an exported declaration can only reference other +/// exported types. +/// +/// The body of an inlinable function can only reference other `public` and +/// `@usableFromInline` declarations; furthermore, if the inlinable +/// function is not `@_spi`, its body can only reference other exported +/// declarations. +/// +/// The ExportContext also stores if the location in the program is inside +/// of a function or type body with deprecated or unavailable availability. +/// This allows referencing other deprecated and unavailable declarations, +/// without producing a warning or error, respectively. +class ExportContext { + DeclContext *DC; + FragileFunctionKind FragileKind; + unsigned SPI : 1; + unsigned Exported : 1; + unsigned Deprecated : 1; + unsigned Implicit : 1; + unsigned Unavailable : 1; + unsigned Platform : 8; + unsigned Reason : 2; + + ExportContext(DeclContext *DC, FragileFunctionKind kind, + bool spi, bool exported, bool implicit, bool deprecated, + Optional unavailablePlatformKind); + +public: + + /// Create an instance describing the types that can be referenced from the + /// given declaration's signature. + /// + /// If the declaration is exported, the resulting context is restricted to + /// referencing exported types only. Otherwise it can reference anything. + static ExportContext forDeclSignature(Decl *D); + + /// Create an instance describing the declarations that can be referenced + /// from the given function's body. + /// + /// If the function is inlinable, the resulting context is restricted to + /// referencing ABI-public declarations only. Furthermore, if the function + /// is exported, referenced declarations must also be exported. Otherwise + /// it can reference anything. + static ExportContext forFunctionBody(DeclContext *DC); + + /// Produce a new context with the same properties as this one, except + /// changing the ExportabilityReason. This only affects diagnostics. + ExportContext withReason(ExportabilityReason reason) const; + + /// Produce a new context with the same properties as this one, except + /// that if 'exported' is false, the resulting context can reference + /// declarations that are not exported. If 'exported' is true, the + /// resulting context is indentical to this one. + /// + /// That is, this will perform a 'bitwise and' on the 'exported' bit. + ExportContext withExported(bool exported) const; + + DeclContext *getDeclContext() const { return DC; } + + /// If not 'None', the context has the inlinable function body restriction. + FragileFunctionKind getFragileFunctionKind() const { return FragileKind; } + + /// If true, the context is part of a synthesized declaration, and + /// availability checking should be disabled. + bool isImplicit() const { return Implicit; } + + /// If true, the context is SPI and can reference SPI declarations. + bool isSPI() const { return SPI; } + + /// If true, the context is exported and cannot reference SPI declarations + /// or declarations from `@_implementationOnly` imports. + bool isExported() const { return Exported; } + + /// If true, the context is part of a deprecated declaration and can + /// reference other deprecated declarations without warning. + bool isDeprecated() const { return Deprecated; } + + Optional getUnavailablePlatformKind() const; + + /// If true, the context can only reference exported declarations, either + /// because it is the signature context of an exported declaration, or + /// because it is the function body context of an inlinable function. + bool mustOnlyReferenceExportedDecls() const; + + /// Get the ExportabilityReason for diagnostics. If this is 'None', there + /// are no restrictions on referencing unexported declarations. + Optional getExportabilityReason() const; +}; + +/// Check if a public declaration is part of a module's API; that is, this +/// will return false if the declaration is @_spi or @_implementationOnly. +bool isExported(const ValueDecl *VD); +bool isExported(const Decl *D); + +/// Diagnose uses of unavailable declarations in expressions. +void diagAvailability(const Expr *E, DeclContext *DC); + +/// Diagnose uses of unavailable declarations in statements (via patterns, etc), +/// without walking into expressions. +void diagAvailability(const Stmt *S, DeclContext *DC); + +/// Diagnose uses of unavailable declarations in types. +bool diagnoseTypeReprAvailability(const TypeRepr *T, + ExportContext context, + DeclAvailabilityFlags flags = None); + +/// Diagnose uses of unavailable conformances in types. +void diagnoseTypeAvailability(Type T, SourceLoc loc, + ExportContext context, + DeclAvailabilityFlags flags = None); + +/// Checks both a TypeRepr and a Type, but avoids emitting duplicate +/// diagnostics by only checking the Type if the TypeRepr succeeded. +void diagnoseTypeAvailability(const TypeRepr *TR, Type T, SourceLoc loc, + ExportContext context, + DeclAvailabilityFlags flags = None); + +bool +diagnoseConformanceAvailability(SourceLoc loc, + ProtocolConformanceRef conformance, + ExportContext context); + +bool +diagnoseSubstitutionMapAvailability(SourceLoc loc, + SubstitutionMap subs, + ExportContext context); + /// Run the Availability-diagnostics algorithm otherwise used in an expr /// context, but for non-expr contexts such as TypeDecls referenced from /// TypeReprs. bool diagnoseDeclAvailability(const ValueDecl *Decl, - DeclContext *DC, SourceRange R, - DeclAvailabilityFlags Options); + ExportContext context, + DeclAvailabilityFlags flags = None); void diagnoseUnavailableOverride(ValueDecl *override, const ValueDecl *base, @@ -73,7 +221,7 @@ void diagnoseUnavailableOverride(ValueDecl *override, /// marked as unavailable, either through "unavailable" or "obsoleted:". bool diagnoseExplicitUnavailability(const ValueDecl *D, SourceRange R, - const DeclContext *DC, + ExportContext Where, const ApplyExpr *call, DeclAvailabilityFlags Flags = None); @@ -82,7 +230,7 @@ bool diagnoseExplicitUnavailability(const ValueDecl *D, bool diagnoseExplicitUnavailability( const ValueDecl *D, SourceRange R, - const DeclContext *DC, + ExportContext Where, DeclAvailabilityFlags Flags, llvm::function_ref attachRenameFixIts); diff --git a/lib/Sema/TypeCheckCaptures.cpp b/lib/Sema/TypeCheckCaptures.cpp index c83ed135bcc9a..bb817f58b88fc 100644 --- a/lib/Sema/TypeCheckCaptures.cpp +++ b/lib/Sema/TypeCheckCaptures.cpp @@ -572,8 +572,10 @@ class FindCapturedVars : public ASTWalker { // When we see a reference to the 'super' expression, capture 'self' decl. if (auto *superE = dyn_cast(E)) { - if (CurDC->isChildContextOf(superE->getSelf()->getDeclContext())) - addCapture(CapturedValue(superE->getSelf(), 0, superE->getLoc())); + if (auto *selfDecl = superE->getSelf()) { + if (CurDC->isChildContextOf(selfDecl->getDeclContext())) + addCapture(CapturedValue(selfDecl, 0, superE->getLoc())); + } return { false, superE }; } diff --git a/lib/Sema/TypeCheckCodeCompletion.cpp b/lib/Sema/TypeCheckCodeCompletion.cpp index 789db2a2bb485..ef3746a6db34e 100644 --- a/lib/Sema/TypeCheckCodeCompletion.cpp +++ b/lib/Sema/TypeCheckCodeCompletion.cpp @@ -15,7 +15,6 @@ //===----------------------------------------------------------------------===// #include "swift/Subsystems.h" -#include "ConstraintSystem.h" #include "TypeChecker.h" #include "TypeCheckObjC.h" #include "TypeCheckType.h" @@ -42,6 +41,7 @@ #include "swift/Parse/Lexer.h" #include "swift/Sema/IDETypeChecking.h" #include "swift/Sema/CodeCompletionTypeChecking.h" +#include "swift/Sema/ConstraintSystem.h" #include "swift/Strings.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/PointerUnion.h" @@ -593,176 +593,277 @@ TypeChecker::getTypeOfCompletionOperator(DeclContext *DC, Expr *LHS, } } -bool TypeChecker::typeCheckForCodeCompletion( - SolutionApplicationTarget &target, - llvm::function_ref callback) { - auto *DC = target.getDeclContext(); - auto &Context = DC->getASTContext(); - - auto *expr = target.getAsExpr(); - if (!expr) - return false; - - // First of all, let's check whether given target expression - // does indeed have the code completion location in it. - { - auto range = expr->getSourceRange(); - if (range.isInvalid() || - !Context.SourceMgr.rangeContainsCodeCompletionLoc(range)) - return false; - } - - FrontendStatsTracer StatsTracer(Context.Stats, - "typecheck-for-code-completion", expr); - PrettyStackTraceExpr stackTrace(Context, "code-completion", expr); - - expr = expr->walk(SanitizeExpr(Context, - /*shouldReusePrecheckedType=*/false)); +namespace { +class CompletionContextFinder : public ASTWalker { enum class ContextKind { - Expression, - Application, + FallbackExpression, StringInterpolation, SingleStmtClosure, MultiStmtClosure, ErrorExpression }; - class ContextFinder : public ASTWalker { - using Context = std::pair; + struct Context { + ContextKind Kind; + Expr * E; + }; + + /// Stack of all "interesting" contexts up to code completion expression. + llvm::SmallVector Contexts; + CodeCompletionExpr *CompletionExpr = nullptr; + Expr *InitialExpr = nullptr; + DeclContext *InitialDC; - // Stack of all "interesting" contexts up to code completion expression. - llvm::SmallVector Contexts; +public: + /// Finder for completion contexts within the provided initial expression. + CompletionContextFinder(Expr *initialExpr, DeclContext *DC) + : InitialExpr(initialExpr), InitialDC(DC) { + assert(DC); + initialExpr->walk(*this); + }; - Expr *CompletionExpr = nullptr; + /// Finder for completion contexts within the outermost non-closure context of + /// the code completion expression's direct context. + CompletionContextFinder(DeclContext *completionDC): InitialDC(completionDC) { + while (auto *ACE = dyn_cast(InitialDC)) + InitialDC = ACE->getParent(); + InitialDC->walkContext(*this); + } - public: - ContextFinder(Expr *E) { - Contexts.push_back(std::make_pair(ContextKind::Expression, E)); + std::pair walkToExprPre(Expr *E) override { + if (auto *closure = dyn_cast(E)) { + Contexts.push_back({closure->hasSingleExpressionBody() + ? ContextKind::SingleStmtClosure + : ContextKind::MultiStmtClosure, + closure}); } - std::pair walkToExprPre(Expr *E) override { - if (auto *closure = dyn_cast(E)) { - Contexts.push_back(std::make_pair(closure->hasSingleExpressionBody() - ? ContextKind::SingleStmtClosure - : ContextKind::MultiStmtClosure, - closure)); - } + if (isa(E)) { + Contexts.push_back({ContextKind::StringInterpolation, E}); + } - if (isa(E)) { - Contexts.push_back(std::make_pair(ContextKind::StringInterpolation, E)); - } + if (isa(E) || isa(E)) { + Contexts.push_back({ContextKind::FallbackExpression, E}); + } - if (isa(E)) { - Contexts.push_back(std::make_pair(ContextKind::Application, E)); + if (auto *Error = dyn_cast(E)) { + Contexts.push_back({ContextKind::ErrorExpression, E}); + if (auto *OrigExpr = Error->getOriginalExpr()) { + OrigExpr->walk(*this); + if (hasCompletionExpr()) + return std::make_pair(false, nullptr); } + } - if (isa(E)) { - CompletionExpr = E; - return std::make_pair(false, nullptr); - } + if (auto *CCE = dyn_cast(E)) { + CompletionExpr = CCE; + return std::make_pair(false, nullptr); + } - if (auto *Error = dyn_cast(E)) { - Contexts.push_back(std::make_pair(ContextKind::ErrorExpression, E)); - if (auto *OrigExpr = Error->getOriginalExpr()) { - OrigExpr->walk(*this); - return std::make_pair(false, hasCompletionExpr() ? nullptr : E); - } - } + return std::make_pair(true, E); + } - return std::make_pair(true, E); + Expr *walkToExprPost(Expr *E) override { + if (isa(E) || isa(E) || + isa(E) || isa(E) || isa(E)) { + assert(Contexts.back().E == E); + Contexts.pop_back(); } + return E; + } - Expr *walkToExprPost(Expr *E) override { - if (isa(E) || isa(E) || - isa(E) || isa(E)) - Contexts.pop_back(); - return E; - } + /// Check whether code completion expression is located inside of a + /// multi-statement closure. + bool locatedInMultiStmtClosure() const { + return hasContext(ContextKind::MultiStmtClosure); + } - /// Check whether code completion expression is located inside of a - /// multi-statement closure. - bool locatedInMultiStmtClosure() const { - return hasContext(ContextKind::MultiStmtClosure); - } + bool locatedInStringIterpolation() const { + return hasContext(ContextKind::StringInterpolation); + } - bool locatedInStringIterpolation() const { - return hasContext(ContextKind::StringInterpolation); - } + bool hasCompletionExpr() const { + return CompletionExpr; + } - bool hasCompletionExpr() const { - return CompletionExpr; - } + CodeCompletionExpr *getCompletionExpr() const { + assert(CompletionExpr); + return CompletionExpr; + } - Expr *getCompletionExpr() const { - assert(CompletionExpr); - return CompletionExpr; - } + struct Fallback { + Expr *E; ///< The fallback expression. + DeclContext *DC; ///< The fallback expression's decl context. + bool SeparatePrecheck; ///< True if the fallback may require prechecking. + }; - ErrorExpr *getInnermostErrorExpr() const { - for (const Context &curr : llvm::reverse(Contexts)) { - if (curr.first == ContextKind::ErrorExpression) - return cast(curr.second); - } - return nullptr; - } + /// As a fallback sometimes its useful to not only type-check + /// code completion expression directly but instead add some + /// of the enclosing context e.g. when completion is an argument + /// to a call. + Optional getFallbackCompletionExpr() const { + assert(CompletionExpr); + + Optional fallback; + bool separatePrecheck = false; + DeclContext *fallbackDC = InitialDC; + + // Find the outermost fallback expression within the innermost error + // expression or multi-statement closure, keeping track of its decl context. + for (auto context: Contexts) { + switch (context.Kind) { + case ContextKind::StringInterpolation: + LLVM_FALLTHROUGH; + case ContextKind::FallbackExpression: + if (!fallback && context.E != InitialExpr) + fallback = Fallback{context.E, fallbackDC, separatePrecheck}; + continue; + + case ContextKind::SingleStmtClosure: + if (!fallback && context.E != InitialExpr) + fallback = Fallback{context.E, fallbackDC, separatePrecheck}; + fallbackDC = cast(context.E); + continue; - ClosureExpr *getOutermostMultiStmtClosure() const { - for (const Context &curr : Contexts) { - if (curr.first == ContextKind::MultiStmtClosure) - return cast(curr.second); + case ContextKind::MultiStmtClosure: + fallbackDC = cast(context.E); + LLVM_FALLTHROUGH; + case ContextKind::ErrorExpression:; + fallback = None; + separatePrecheck = true; + continue; } - return nullptr; } - /// As a fallback sometimes its useful to not only type-check - /// code completion expression directly but instead add some - /// of the enclosing context e.g. when completion is an argument - /// to a call. - Expr *getCompletionExprInContext() const { - assert(CompletionExpr); - - auto &innerContext = Contexts.back(); - return innerContext.first == ContextKind::Application - ? innerContext.second - : CompletionExpr; - } + if (fallback) + return fallback; + + if (CompletionExpr->getBase() && CompletionExpr != InitialExpr) + return Fallback{CompletionExpr, fallbackDC, separatePrecheck}; + return None; + } + +private: + bool hasContext(ContextKind kind) const { + return llvm::find_if(Contexts, [&kind](const Context &currContext) { + return currContext.Kind == kind; + }) != Contexts.end(); + } +}; - private: - bool hasContext(ContextKind kind) const { - return llvm::find_if(Contexts, [&kind](const Context &currContext) { - return currContext.first == kind; - }) != Contexts.end(); +} // end namespace + +// Determine if the target expression is the implicit BinaryExpr generated for +// pattern-matching in a switch/if/guard case ( ~= matchValue). +static bool isForPatternMatch(SolutionApplicationTarget &target) { + if (target.getExprContextualTypePurpose() != CTP_Condition) + return false; + Expr *condition = target.getAsExpr(); + if (!condition->isImplicit()) + return false; + if (auto *BE = dyn_cast(condition)) { + Identifier id; + if (auto *ODRE = dyn_cast(BE->getFn())) { + id = ODRE->getDecls().front()->getBaseIdentifier(); + } else if (auto *DRE = dyn_cast(BE->getFn())) { + id = DRE->getDecl()->getBaseIdentifier(); } - }; + if (id != target.getDeclContext()->getASTContext().Id_MatchOperator) + return false; + return isa(BE->getArg()->getElement(0)); + } + return false; +} + +/// Remove any solutions from the provided vector with a score less than the best solution's. +static void filterSolutions(SolutionApplicationTarget &target, + SmallVectorImpl &solutions) { + // FIXME: this is only needed because in pattern matching position, the + // code completion expression always becomes an expression pattern, which + // requires the ~= operator to be defined on the type being matched against. + // Pattern matching against an enum doesn't require that however, so valid + // solutions always end up having fixes. This is a problem because there will + // always be a valid solution as well. Optional defines ~= between Optional + // and _OptionalNilComparisonType (which defines a nilLiteral initializer), + // and the matched-against value can implicitly be made Optional if it isn't + // already, so _OptionalNilComparisonType is always a valid solution for the + // completion. That only generates the 'nil' completion, which is rarely what + // the user intends to write in this position and shouldn't be preferred over + // the other formed solutions (which require fixes). We should generate enum + // pattern completions separately. + if (isForPatternMatch(target)) + return; + + if (solutions.size() <= 1) + return; + + auto min = std::min_element(solutions.begin(), solutions.end(), + [](const Solution &a, const Solution &b) { + return a.getFixedScore() < b.getFixedScore(); + }); + auto fixStart = llvm::partition(solutions, [&](const Solution &S) { + return S.getFixedScore() == min->getFixedScore(); + }); + + if (fixStart != solutions.begin() && fixStart != solutions.end()) + solutions.erase(fixStart, solutions.end()); +} + +/// When solving for code completion we still consider solutions with holes as +/// valid. Regular type-checking does not. This is intended to return true only +/// if regular type-checking would consider this solution viable. +static bool isViableForReTypeCheck(const Solution &S) { + if (llvm::any_of(S.Fixes, [&](const ConstraintFix *CF) { + return !CF->isWarning(); + })) + return false; + using Binding = std::pair; + return llvm::none_of(S.typeBindings, [&](const Binding& binding) { + return binding.second->hasHole(); + }); +} + +bool TypeChecker::typeCheckForCodeCompletion( + SolutionApplicationTarget &target, + llvm::function_ref callback) { + auto *DC = target.getDeclContext(); + auto &Context = DC->getASTContext(); + + auto *expr = target.getAsExpr(); + if (!expr) + return false; + + // First of all, let's check whether given target expression + // does indeed have the code completion location in it. + { + auto range = expr->getSourceRange(); + if (range.isInvalid() || + !Context.SourceMgr.rangeContainsCodeCompletionLoc(range)) + return false; + } + + FrontendStatsTracer StatsTracer(Context.Stats, + "typecheck-for-code-completion", expr); + PrettyStackTraceExpr stackTrace(Context, "code-completion", expr); - ContextFinder contextAnalyzer(expr); - expr->walk(contextAnalyzer); + expr = expr->walk(SanitizeExpr(Context, + /*shouldReusePrecheckedType=*/false)); + target.setExpr(expr); + + CompletionContextFinder contextAnalyzer(expr, DC); // If there was no completion expr (e.g. if the code completion location was // among tokens that were skipped over during parser error recovery) bail. if (!contextAnalyzer.hasCompletionExpr()) return false; - // If the completion expression is in a valid subexpression of an ErrorExpr, - // fallback to trying the valid subexpression without any context. This can - // happen for cases like `expectsBoolArg(foo.).` which becomes an - // ErrorExpr due to the missing member name after the final dot. - if (auto *errorExpr = contextAnalyzer.getInnermostErrorExpr()) { - if (auto *origExpr = errorExpr->getOriginalExpr()) { - SolutionApplicationTarget completionTarget(origExpr, DC, CTP_Unused, - /*contextualType=*/Type(), - /*isDiscarded=*/true); - return typeCheckForCodeCompletion(completionTarget, callback); - } - } - // Interpolation components are type-checked separately. if (contextAnalyzer.locatedInStringIterpolation()) return false; // FIXME: There is currently no way to distinguish between - // multi-statement closures which are function builder bodies + // multi-statement closures which are result builder bodies // (that are type-checked together with enclosing context) // and regular closures which are type-checked separately. @@ -786,47 +887,47 @@ bool TypeChecker::typeCheckForCodeCompletion( ConstraintSystemOptions options; options |= ConstraintSystemFlags::AllowFixes; options |= ConstraintSystemFlags::SuppressDiagnostics; + options |= ConstraintSystemFlags::ForCodeCompletion; ConstraintSystem cs(DC, options); llvm::SmallVector solutions; // If solve failed to generate constraints or with some other - // issue, we need to fallback to type-checking code completion - // expression directly. + // issue, we need to fallback to type-checking a sub-expression. if (!cs.solveForCodeCompletion(target, solutions)) return CompletionResult::Fallback; - // If case type-check didn't produce any solutions, let's - // attempt to type-check code completion expression without - // enclosing context. + // FIXME: instead of filtering, expose the score and viability to clients. + // Only keep the solution(s) with the best score. + filterSolutions(target, solutions); + + // Similarly, if the type-check didn't produce any solutions, fall back + // to type-checking a sub-expression in isolation. if (solutions.empty()) return CompletionResult::Fallback; // If code completion expression resides inside of multi-statement - // closure body it code either be type-checker together with context - // or not, it's impossible to say without trying. If solution - // doesn't have a type for a code completion expression it means that - // we have to wait until body of the closure is type-checked. + // closure body it could either be type-checked together with the context + // or not, it's impossible to say without checking. if (contextAnalyzer.locatedInMultiStmtClosure()) { auto &solution = solutions.front(); - // Let's check whether closure participated in the type-check. if (solution.hasType(contextAnalyzer.getCompletionExpr())) { llvm::for_each(solutions, callback); return CompletionResult::Ok; } - if (solutions.size() > 1) - return CompletionResult::Fallback; - - auto *closure = contextAnalyzer.getOutermostMultiStmtClosure(); - auto closureType = solution.getResolvedType(closure); + // At this point we know the code completion expression wasn't checked + // with the closure's surrounding context. If a single valid solution + // was formed we can wait until the body of the closure is type-checked + // and gather completions then. + if (solutions.size() == 1 && isViableForReTypeCheck(solution)) + return CompletionResult::NotApplicable; - if (closureType->hasUnresolvedType()) - return CompletionResult::Fallback; - - return CompletionResult::NotApplicable; + // Otherwise, it's unlikely the body will ever be type-checked, so fall + // back to manually checking a sub-expression within the closure body. + return CompletionResult::Fallback; } llvm::for_each(solutions, callback); @@ -844,33 +945,25 @@ bool TypeChecker::typeCheckForCodeCompletion( break; } - { - auto *completionExpr = contextAnalyzer.getCompletionExpr(); - - if (contextAnalyzer.locatedInMultiStmtClosure()) { - auto completionInContext = contextAnalyzer.getCompletionExprInContext(); - // If pre-check fails, let's switch to code completion - // expression without any enclosing context. - if (ConstraintSystem::preCheckExpression( - completionInContext, DC, /*replaceInvalidRefsWithErrors=*/true)) { - completionExpr = contextAnalyzer.getCompletionExpr(); - } else { - completionExpr = completionInContext; - } - } - - // If initial solve failed, let's fallback to checking only code completion - // expresion without any context. - SolutionApplicationTarget completionTarget(completionExpr, DC, CTP_Unused, + // Determine the best subexpression to use based on the collected context + // of the code completion expression. + if (auto fallback = contextAnalyzer.getFallbackCompletionExpr()) { + assert(fallback->E != expr); + SolutionApplicationTarget completionTarget(fallback->E, + fallback->DC, CTP_Unused, /*contextualType=*/Type(), /*isDiscarded=*/true); + if (fallback->SeparatePrecheck) { + typeCheckForCodeCompletion(completionTarget, callback); + return true; + } switch (solveForCodeCompletion(completionTarget)) { case CompletionResult::Ok: case CompletionResult::Fallback: break; case CompletionResult::NotApplicable: - llvm_unreachable("solve on CodeCompletionExpr produced not applicable?"); + llvm_unreachable("fallback expr not applicable?"); } } return true; @@ -962,8 +1055,7 @@ bool swift::typeCheckExpression(DeclContext *DC, Expr *&parsedExpr) { parsedExpr = parsedExpr->walk(SanitizeExpr(ctx, /*shouldReusePrecheckedType=*/false)); DiagnosticSuppression suppression(ctx.Diags); - auto resultTy = TypeChecker::typeCheckExpression(parsedExpr, DC, Type(), - CTP_Unused); + auto resultTy = TypeChecker::typeCheckExpression(parsedExpr, DC); return !resultTy; } @@ -974,8 +1066,23 @@ swift::lookupSemanticMember(DeclContext *DC, Type ty, DeclName name) { void DotExprTypeCheckCompletionCallback::fallbackTypeCheck() { assert(!gotCallback()); - SolutionApplicationTarget completionTarget(CompletionExpr, DC, CTP_Unused, - Type(), /*isDiscared=*/true); + + // Default to checking the completion expression in isolation. + Expr *fallbackExpr = CompletionExpr; + DeclContext *fallbackDC = DC; + + CompletionContextFinder finder(DC); + if (finder.hasCompletionExpr()) { + if (auto fallback = finder.getFallbackCompletionExpr()) { + fallbackExpr = fallback->E; + fallbackDC = fallback->DC; + } + } + + SolutionApplicationTarget completionTarget(fallbackExpr, fallbackDC, + CTP_Unused, Type(), + /*isDiscared=*/true); + TypeChecker::typeCheckForCodeCompletion( completionTarget, [&](const Solution &S) { sawSolution(S); }); } diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index e1c725979909e..379add233a823 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -15,14 +15,42 @@ //===----------------------------------------------------------------------===// #include "TypeCheckConcurrency.h" #include "TypeChecker.h" +#include "TypeCheckType.h" #include "swift/AST/ASTWalker.h" +#include "swift/AST/Initializer.h" #include "swift/AST/ParameterList.h" #include "swift/AST/ProtocolConformance.h" +#include "swift/AST/NameLookupRequests.h" #include "swift/AST/TypeCheckRequests.h" using namespace swift; -bool swift::checkAsyncHandler(FuncDecl *func, bool diagnose) { +/// Determine whether it makes sense to infer an attribute in the given +/// context. +static bool shouldInferAttributeInContext(const DeclContext *dc) { + auto sourceFile = dc->getParentSourceFile(); + if (!sourceFile) + return false; + + switch (sourceFile->Kind) { + case SourceFileKind::Interface: + case SourceFileKind::SIL: + return false; + + case SourceFileKind::Library: + case SourceFileKind::Main: + return true; + } +} + +/// Check whether the @asyncHandler attribute can be applied to the given +/// function declaration. +/// +/// \param diagnose Whether to emit a diagnostic when a problem is encountered. +/// +/// \returns \c true if there was a problem with adding the attribute, \c false +/// otherwise. +static bool checkAsyncHandler(FuncDecl *func, bool diagnose) { if (!func->getResultInterfaceType()->isVoid()) { if (diagnose) { func->diagnose(diag::asynchandler_returns_value) @@ -78,7 +106,7 @@ bool swift::checkAsyncHandler(FuncDecl *func, bool diagnose) { void swift::addAsyncNotes(FuncDecl *func) { func->diagnose(diag::note_add_async_to_function, func->getName()); - if (!checkAsyncHandler(func, /*diagnose=*/false)) { + if (func->canBeAsyncHandler()) { func->diagnose( diag::note_add_asynchandler_to_function, func->getName()) .fixItInsert(func->getAttributeInsertionLoc(false), "@asyncHandler "); @@ -98,17 +126,19 @@ bool IsAsyncHandlerRequest::evaluate( return true; } - if (!func->getASTContext().LangOpts.EnableExperimentalConcurrency) + if (!shouldInferAttributeInContext(func->getDeclContext())) return false; // Are we in a context where inference is possible? auto dc = func->getDeclContext(); - if (!dc->isTypeContext() || !dc->getParentSourceFile() || - isa(dc) || !func->hasBody()) + if (!dc->getSelfClassDecl() || !dc->getParentSourceFile() || !func->hasBody()) return false; // Is it possible to infer @asyncHandler for this function at all? - if (checkAsyncHandler(func, /*diagnose=*/false)) + if (!func->canBeAsyncHandler()) + return false; + + if (!dc->getSelfClassDecl()->isActor()) return false; // Add an implicit @asyncHandler attribute and return true. We're done. @@ -157,6 +187,11 @@ bool IsAsyncHandlerRequest::evaluate( return false; } +bool CanBeAsyncHandlerRequest::evaluate( + Evaluator &evaluator, FuncDecl *func) const { + return !checkAsyncHandler(func, /*diagnose=*/false); +} + bool IsActorRequest::evaluate( Evaluator &evaluator, ClassDecl *classDecl) const { // If concurrency is not enabled, we don't have actors. @@ -168,6 +203,14 @@ bool IsActorRequest::evaluate( if (superclassDecl->isActor()) return true; + // The superclass is 'NSObject', which is known to have no state and no + // superclass. + if (superclassDecl->hasClangNode() && + superclassDecl->getName().is("NSObject") && + superclassDecl->getModuleContext()->getName().is("ObjectiveC") && + actorAttr != nullptr) + return true; + // This class cannot be an actor; complain if the 'actor' modifier was // provided. if (actorAttr) { @@ -182,160 +225,290 @@ bool IsActorRequest::evaluate( return actorAttr != nullptr; } -namespace { +static bool isDeclNotAsAccessibleAsParent(ValueDecl *decl, + NominalTypeDecl *parent) { + return decl->getFormalAccess() < + std::min(parent->getFormalAccess(), AccessLevel::Public); +} -/// The isolation restriction in effect for a given declaration that is -/// referenced from source. -class IsolationRestriction { -public: - enum Kind { - /// There is no restriction on references to the given declaration, - /// e.g., because it's immutable. - Unrestricted, - - /// Access to the declaration is unsafe in a concurrent context. - Unsafe, - - /// The declaration is a local entity whose capture could introduce - /// data races. The context in which the local was defined is provided. - LocalCapture, - - /// References to this local variable that can only be made from the - /// References to this member of an actor are only permitted on 'self'. - ActorSelf, - }; +VarDecl *GlobalActorInstanceRequest::evaluate( + Evaluator &evaluator, NominalTypeDecl *nominal) const { + auto globalActorAttr = nominal->getAttrs().getAttribute(); + if (!globalActorAttr) + return nullptr; + + // Ensure that the actor protocol has been loaded. + ASTContext &ctx = nominal->getASTContext(); + auto actorProto = ctx.getProtocol(KnownProtocolKind::Actor); + if (!actorProto) { + nominal->diagnose(diag::concurrency_lib_missing, "Actor"); + return nullptr; + } -private: - /// The kind of restriction - Kind kind; + // Global actors have a static property "shared" that provides an actor + // instance. The value must + SmallVector decls; + nominal->lookupQualified( + nominal, DeclNameRef(ctx.Id_shared), NL_QualifiedDefault, decls); + VarDecl *sharedVar = nullptr; + llvm::TinyPtrVector candidates; + for (auto decl : decls) { + auto var = dyn_cast(decl); + if (!var) + continue; + + auto varDC = var->getDeclContext(); + if (var->isStatic() && + !isDeclNotAsAccessibleAsParent(var, nominal) && + !(isa(varDC) && + cast(varDC)->isConstrainedExtension()) && + TypeChecker::conformsToProtocol( + varDC->mapTypeIntoContext(var->getValueInterfaceType()), + actorProto, nominal)) { + sharedVar = var; + break; + } - union { - /// The local context that an entity is tied to. - DeclContext *localContext; + candidates.push_back(var); + } - /// The actor class that the entity is declared in. - ClassDecl *actorClass; - } data; + // If we found a suitable candidate, we're done. + if (sharedVar) + return sharedVar; - explicit IsolationRestriction(Kind kind) : kind(kind) { } + // Complain about the lack of a suitable 'shared' property. + { + auto primaryDiag = nominal->diagnose( + diag::global_actor_missing_shared, nominal->getName()); -public: - Kind getKind() const { return kind; } + // If there were no candidates, provide a Fix-It with a prototype. + if (candidates.empty() && nominal->getBraces().Start.isValid()) { + // Figure out the indentation we need. + SourceLoc sharedInsertionLoc = Lexer::getLocForEndOfToken( + ctx.SourceMgr, nominal->getBraces().Start); - /// Retrieve the declaration context in which a local was defined. - DeclContext *getLocalContext() const { - assert(kind == LocalCapture); - return data.localContext; - } + StringRef extraIndent; + StringRef currentIndent = Lexer::getIndentationForLine( + ctx.SourceMgr, sharedInsertionLoc, &extraIndent); + std::string stubIndent = (currentIndent + extraIndent).str(); - /// Retrieve the actor class that the declaration is within. - ClassDecl *getActorClass() const { - assert(kind == ActorSelf); - return data.actorClass; - } + // From the string to add the declaration. + std::string sharedDeclString = "\n" + stubIndent; + if (nominal->getFormalAccess() >= AccessLevel::Public) + sharedDeclString += "public "; - /// There are no restrictions on the use of the entity. - static IsolationRestriction forUnrestricted() { - return IsolationRestriction(Unrestricted); - } + sharedDeclString += "static let shared = <#actor instance#>"; - /// Accesses to the given declaration are unsafe. - static IsolationRestriction forUnsafe() { - return IsolationRestriction(Unsafe); + primaryDiag.fixItInsert(sharedInsertionLoc, sharedDeclString); + } } - /// Accesses to the given declaration can only be made via the 'self' of - /// the current actor. - static IsolationRestriction forActorSelf(ClassDecl *actorClass) { - IsolationRestriction result(ActorSelf); - result.data.actorClass = actorClass; - return result; + // Remark about all of the candidates that failed (and why). + for (auto candidate : candidates) { + if (!candidate->isStatic()) { + candidate->diagnose(diag::global_actor_shared_not_static) + .fixItInsert(candidate->getAttributeInsertionLoc(true), "static "); + continue; + } + + if (isDeclNotAsAccessibleAsParent(candidate, nominal)) { + AccessLevel needAccessLevel = std::min( + nominal->getFormalAccess(), AccessLevel::Public); + auto diag = candidate->diagnose( + diag::global_actor_shared_inaccessible, + getAccessLevelSpelling(candidate->getFormalAccess()), + getAccessLevelSpelling(needAccessLevel)); + if (auto attr = candidate->getAttrs().getAttribute()) { + if (needAccessLevel == AccessLevel::Internal) { + diag.fixItRemove(attr->getRange()); + } else { + diag.fixItReplace( + attr->getRange(), getAccessLevelSpelling(needAccessLevel)); + } + } else { + diag.fixItInsert( + candidate->getAttributeInsertionLoc(true), + getAccessLevelSpelling(needAccessLevel)); + } + continue; + } + + if (auto ext = dyn_cast(candidate->getDeclContext())) { + if (ext->isConstrainedExtension()) { + candidate->diagnose(diag::global_actor_shared_constrained_extension); + continue; + } + } + + Type varType = candidate->getDeclContext()->mapTypeIntoContext( + candidate->getValueInterfaceType()); + candidate->diagnose(diag::global_actor_shared_non_actor_type, varType); } - /// Access is restricted to code running within the given local context. - static IsolationRestriction forLocalCapture(DeclContext *dc) { - IsolationRestriction result(LocalCapture); - result.data.localContext = dc; - return result; + return nullptr; +} + +Optional> +GlobalActorAttributeRequest::evaluate( + Evaluator &evaluator, Decl *decl) const { + ASTContext &ctx = decl->getASTContext(); + auto dc = decl->getDeclContext(); + CustomAttr *globalActorAttr = nullptr; + NominalTypeDecl *globalActorNominal = nullptr; + + for (auto attr : decl->getAttrs().getAttributes()) { + auto mutableAttr = const_cast(attr); + // Figure out which nominal declaration this custom attribute refers to. + auto nominal = evaluateOrDefault(ctx.evaluator, + CustomAttrNominalRequest{mutableAttr, dc}, + nullptr); + + // Ignore unresolvable custom attributes. + if (!nominal) + continue; + + // We are only interested in global actor types. + if (!nominal->isGlobalActor()) + continue; + + // Only a single global actor can be applied to a given declaration. + if (globalActorAttr) { + decl->diagnose( + diag::multiple_global_actors, globalActorNominal->getName(), + nominal->getName()); + continue; + } + + globalActorAttr = const_cast(attr); + globalActorNominal = nominal; } - /// Determine the isolation rules for a given declaration. - static IsolationRestriction forDeclaration(Decl *decl) { - switch (decl->getKind()) { - case DeclKind::AssociatedType: - case DeclKind::Class: - case DeclKind::Enum: - case DeclKind::Extension: - case DeclKind::GenericTypeParam: - case DeclKind::OpaqueType: - case DeclKind::Protocol: - case DeclKind::Struct: - case DeclKind::TypeAlias: - // Types are always available. - return forUnrestricted(); + if (!globalActorAttr) + return None; - case DeclKind::Constructor: - case DeclKind::EnumCase: - case DeclKind::EnumElement: - // Type-level entities don't require isolation. - return forUnrestricted(); + // Check that a global actor attribute makes sense on this kind of + // declaration. + if (auto nominal = dyn_cast(decl)) { + // Nominal types are okay... + if (auto classDecl = dyn_cast(nominal)){ + if (classDecl->isActor()) { + // ... except for actor classes. + nominal->diagnose(diag::global_actor_on_actor_class, nominal->getName()) + .highlight(globalActorAttr->getRangeWithAt()); + return None; + } + } + } else if (auto storage = dyn_cast(decl)) { + // Subscripts and properties are fine... + if (auto var = dyn_cast(storage)) { + if (var->getDeclContext()->isLocalContext()) { + var->diagnose(diag::global_actor_on_local_variable, var->getName()) + .highlight(globalActorAttr->getRangeWithAt()); + return None; + } + } + } else if (isa(decl)) { + // Extensions are okay. + } else if (isa(decl) || isa(decl)) { + // Functions are okay. + } else { + // Everything else is disallowed. + decl->diagnose(diag::global_actor_disallowed, decl->getDescriptiveKind()); + return None; + } - case DeclKind::IfConfig: - case DeclKind::Import: - case DeclKind::InfixOperator: - case DeclKind::MissingMember: - case DeclKind::Module: - case DeclKind::PatternBinding: - case DeclKind::PostfixOperator: - case DeclKind::PoundDiagnostic: - case DeclKind::PrecedenceGroup: - case DeclKind::PrefixOperator: - case DeclKind::TopLevelCode: - // Non-value entities don't require isolation. - return forUnrestricted(); + return std::make_pair(globalActorAttr, globalActorNominal); +} - case DeclKind::Destructor: - // Destructors don't require isolation. +/// Determine the isolation rules for a given declaration. +ActorIsolationRestriction ActorIsolationRestriction::forDeclaration( + ConcreteDeclRef declRef) { + auto decl = declRef.getDecl(); + + switch (decl->getKind()) { + case DeclKind::AssociatedType: + case DeclKind::Class: + case DeclKind::Enum: + case DeclKind::Extension: + case DeclKind::GenericTypeParam: + case DeclKind::OpaqueType: + case DeclKind::Protocol: + case DeclKind::Struct: + case DeclKind::TypeAlias: + // Types are always available. + return forUnrestricted(); + + case DeclKind::Constructor: + case DeclKind::EnumCase: + case DeclKind::EnumElement: + // Type-level entities don't require isolation. + return forUnrestricted(); + + case DeclKind::IfConfig: + case DeclKind::Import: + case DeclKind::InfixOperator: + case DeclKind::MissingMember: + case DeclKind::Module: + case DeclKind::PatternBinding: + case DeclKind::PostfixOperator: + case DeclKind::PoundDiagnostic: + case DeclKind::PrecedenceGroup: + case DeclKind::PrefixOperator: + case DeclKind::TopLevelCode: + // Non-value entities don't require isolation. + return forUnrestricted(); + + case DeclKind::Destructor: + // Destructors don't require isolation. + return forUnrestricted(); + + case DeclKind::Param: + case DeclKind::Var: + // 'let' declarations are immutable, so there are no restrictions on + // their access. + if (cast(decl)->isLet()) return forUnrestricted(); - case DeclKind::Param: - case DeclKind::Var: - // 'let' declarations are immutable, so there are no restrictions on - // their access. - if (cast(decl)->isLet()) - return forUnrestricted(); + LLVM_FALLTHROUGH; - LLVM_FALLTHROUGH; + case DeclKind::Accessor: + case DeclKind::Func: + case DeclKind::Subscript: + // A function that provides an asynchronous context has no restrictions + // on its access. + if (auto func = dyn_cast(decl)) { + if (func->isAsyncContext()) + return forUnrestricted(); + } - case DeclKind::Accessor: - case DeclKind::Func: - case DeclKind::Subscript: - // Local captures can only be referenced in their local context or a - // context that is guaranteed not to run concurrently with it. - if (cast(decl)->isLocalCapture()) - return forLocalCapture(decl->getDeclContext()); + // Local captures can only be referenced in their local context or a + // context that is guaranteed not to run concurrently with it. + if (cast(decl)->isLocalCapture()) + return forLocalCapture(decl->getDeclContext()); - // Determine the actor isolation of the given declaration. - switch (auto isolation = getActorIsolation(cast(decl))) { - case ActorIsolation::ActorInstance: - // Protected actor instance members can only be accessed on 'self'. - return forActorSelf(isolation.getActor()); + // Determine the actor isolation of the given declaration. + switch (auto isolation = getActorIsolation(cast(decl))) { + case ActorIsolation::ActorInstance: + // Protected actor instance members can only be accessed on 'self'. + return forActorSelf(isolation.getActor()); - case ActorIsolation::Independent: - case ActorIsolation::ActorPrivileged: - // Actor-independent and actor-privileged declarations have no - // restrictions on their access. - return forUnrestricted(); + case ActorIsolation::GlobalActor: { + Type actorType = isolation.getGlobalActor(); + if (auto subs = declRef.getSubstitutions()) + actorType = actorType.subst(subs); - case ActorIsolation::Unspecified: - return forUnsafe(); - } + return forGlobalActor(actorType); } - } - operator Kind() const { return kind; }; -}; + case ActorIsolation::Independent: + case ActorIsolation::IndependentUnsafe: + // Actor-independent have no restrictions on their access. + return forUnrestricted(); + case ActorIsolation::Unspecified: + return forUnsafe(); + } + } } namespace { @@ -374,14 +547,13 @@ static Optional decomposePartialApplyThunk( } /// Find the immediate member reference in the given expression. -static Optional> +static Optional> findMemberReference(Expr *expr) { if (auto declRef = dyn_cast(expr)) - return std::make_pair(declRef->getDecl(), declRef->getLoc()); + return std::make_pair(declRef->getDeclRef(), declRef->getLoc()); if (auto otherCtor = dyn_cast(expr)) { - return std::make_pair( - static_cast(otherCtor->getDecl()), otherCtor->getLoc()); + return std::make_pair(otherCtor->getDeclRef(), otherCtor->getLoc()); } return None; @@ -416,13 +588,13 @@ void swift::checkActorIsolation(const Expr *expr, const DeclContext *dc) { } if (auto lookup = dyn_cast(expr)) { - checkMemberReference(lookup->getBase(), lookup->getMember().getDecl(), + checkMemberReference(lookup->getBase(), lookup->getMember(), lookup->getLoc()); return { true, expr }; } if (auto declRef = dyn_cast(expr)) { - checkNonMemberReference(declRef->getDecl(), declRef->getLoc()); + checkNonMemberReference(declRef->getDeclRef(), declRef->getLoc()); return { true, expr }; } @@ -495,6 +667,8 @@ void swift::checkActorIsolation(const Expr *expr, const DeclContext *dc) { /// Note that the given actor member is isolated. static void noteIsolatedActorMember(ValueDecl *decl) { + // FIXME: Make this diagnostic more sensitive to the isolation context + // of the declaration. if (auto func = dyn_cast(decl)) { // FIXME: We'd like to insert 'async' at the appropriate place, but // FuncDecl/AbstractFunctionDecl doesn't have the right source-location @@ -576,19 +750,125 @@ void swift::checkActorIsolation(const Expr *expr, const DeclContext *dc) { return true; } + /// Get the actor isolation of the innermost relevant context. + ActorIsolation getInnermostIsolatedContext(const DeclContext *constDC) { + // Retrieve the actor isolation for a declaration somewhere in our + // declaration context chain and map it into our own context so that + // the types can be compared. + auto getActorIsolation = [constDC](ValueDecl *value) { + switch (auto isolation = swift::getActorIsolation(value)) { + case ActorIsolation::ActorInstance: + case ActorIsolation::Independent: + case ActorIsolation::IndependentUnsafe: + case ActorIsolation::Unspecified: + return isolation; + + case ActorIsolation::GlobalActor: + return ActorIsolation::forGlobalActor( + constDC->mapTypeIntoContext(isolation.getGlobalActor())); + } + }; + + auto dc = const_cast(constDC); + while (!dc->isModuleScopeContext()) { + // Look through non-escaping closures. + if (auto closure = dyn_cast(dc)) { + if (auto type = closure->getType()) { + if (auto fnType = type->getAs()) { + if (fnType->isNoEscape()) { + dc = closure->getParent(); + continue; + } + } + } + } + + // Functions have actor isolation defined on them. + if (auto func = dyn_cast(dc)) + return getActorIsolation(func); + + // Subscripts have actor isolation defined on them. + if (auto subscript = dyn_cast(dc)) + return getActorIsolation(subscript); + + // Pattern binding declarations have actor isolation defined on their + // properties, if any. + if (auto init = dyn_cast(dc)) { + auto var = init->getBinding()->getAnchoringVarDecl( + init->getBindingIndex()); + if (var) + return getActorIsolation(var); + + return ActorIsolation::forUnspecified(); + } + + return ActorIsolation::forUnspecified(); + } + + // At module scope, actor independence with safety is assumed. + return ActorIsolation::forIndependent(ActorIndependentKind::Safe); + } + + /// Check a reference to an entity within a global actor. + bool checkGlobalActorReference( + ValueDecl *value, SourceLoc loc, Type globalActor) { + switch (auto contextIsolation = + getInnermostIsolatedContext(getDeclContext())) { + case ActorIsolation::ActorInstance: + ctx.Diags.diagnose( + loc, diag::global_actor_from_instance_actor_context, + value->getDescriptiveKind(), value->getName(), globalActor, + contextIsolation.getActor()->getName()); + noteIsolatedActorMember(value); + return true; + + case ActorIsolation::GlobalActor: { + // If the global actor types are the same, we're done. + if (contextIsolation.getGlobalActor()->isEqual(globalActor)) + return false; + + ctx.Diags.diagnose( + loc, diag::global_actor_from_other_global_actor_context, + value->getDescriptiveKind(), value->getName(), globalActor, + contextIsolation.getGlobalActor()); + noteIsolatedActorMember(value); + return true; + } + + case ActorIsolation::Independent: + case ActorIsolation::IndependentUnsafe: + ctx.Diags.diagnose( + loc, diag::global_actor_from_independent_context, + value->getDescriptiveKind(), value->getName(), globalActor); + noteIsolatedActorMember(value); + return true; + + case ActorIsolation::Unspecified: + // Okay. + return false; + } + llvm_unreachable("unhandled actor isolation kind!"); + } + /// Check a reference to a local or global. - bool checkNonMemberReference(ValueDecl *value, SourceLoc loc) { - if (!value) + bool checkNonMemberReference(ConcreteDeclRef valueRef, SourceLoc loc) { + if (!valueRef) return false; - switch (auto isolation = IsolationRestriction::forDeclaration(value)) { - case IsolationRestriction::Unrestricted: + auto value = valueRef.getDecl(); + switch (auto isolation = + ActorIsolationRestriction::forDeclaration(valueRef)) { + case ActorIsolationRestriction::Unrestricted: return false; - case IsolationRestriction::ActorSelf: + case ActorIsolationRestriction::ActorSelf: llvm_unreachable("non-member reference into an actor"); - case IsolationRestriction::LocalCapture: + case ActorIsolationRestriction::GlobalActor: + return checkGlobalActorReference( + value, loc, isolation.getGlobalActor()); + + case ActorIsolationRestriction::LocalCapture: // Only diagnose unsafe concurrent accesses within the context of an // actor. This is globally unsafe, but locally enforceable. if (!getNearestEnclosingActorContext(getDeclContext())) @@ -608,23 +888,26 @@ void swift::checkActorIsolation(const Expr *expr, const DeclContext *dc) { return false; - case IsolationRestriction::Unsafe: + case ActorIsolationRestriction::Unsafe: return diagnoseReferenceToUnsafe(value, loc); } + llvm_unreachable("unhandled actor isolation kind!"); } /// Check a reference with the given base expression to the given member. bool checkMemberReference( - Expr *base, ValueDecl *member, SourceLoc memberLoc, + Expr *base, ConcreteDeclRef memberRef, SourceLoc memberLoc, bool isEscapingPartialApply = false) { - if (!base || !member) + if (!base || !memberRef) return false; - switch (auto isolation = IsolationRestriction::forDeclaration(member)) { - case IsolationRestriction::Unrestricted: + auto member = memberRef.getDecl(); + switch (auto isolation = + ActorIsolationRestriction::forDeclaration(memberRef)) { + case ActorIsolationRestriction::Unrestricted: return false; - case IsolationRestriction::ActorSelf: { + case ActorIsolationRestriction::ActorSelf: { // Must reference actor-isolated state on 'self'. auto selfVar = getSelfReference(base); if (!selfVar) { @@ -639,10 +922,9 @@ void swift::checkActorIsolation(const Expr *expr, const DeclContext *dc) { } // Check whether the context of 'self' is actor-isolated. - switch (getActorIsolation( + switch (auto contextIsolation = getActorIsolation( cast(selfVar->getDeclContext()->getAsDecl()))) { case ActorIsolation::ActorInstance: - case ActorIsolation::ActorPrivileged: // An escaping partial application of something that is part of // the actor's isolated state is never permitted. if (isEscapingPartialApply) { @@ -655,6 +937,7 @@ void swift::checkActorIsolation(const Expr *expr, const DeclContext *dc) { } break; + case ActorIsolation::IndependentUnsafe: case ActorIsolation::Unspecified: // Okay break; @@ -668,6 +951,17 @@ void swift::checkActorIsolation(const Expr *expr, const DeclContext *dc) { member->getName()); noteIsolatedActorMember(member); return true; + + case ActorIsolation::GlobalActor: + // The 'self' is for a member that's part of a global actor, which + // means we cannot refer to actor-isolated state. + ctx.Diags.diagnose( + memberLoc, diag::actor_isolated_global_actor_context, + member->getDescriptiveKind(), + member->getName(), + contextIsolation.getGlobalActor()); + noteIsolatedActorMember(member); + return true; } // Check whether we are in a context that will not execute concurrently @@ -685,12 +979,17 @@ void swift::checkActorIsolation(const Expr *expr, const DeclContext *dc) { return false; } - case IsolationRestriction::LocalCapture: + case ActorIsolationRestriction::GlobalActor: + return checkGlobalActorReference( + member, memberLoc, isolation.getGlobalActor()); + + case ActorIsolationRestriction::LocalCapture: llvm_unreachable("Locals cannot be referenced with member syntax"); - case IsolationRestriction::Unsafe: + case ActorIsolationRestriction::Unsafe: return diagnoseReferenceToUnsafe(member, memberLoc); } + llvm_unreachable("unhandled actor isolation kind!"); } }; @@ -698,37 +997,273 @@ void swift::checkActorIsolation(const Expr *expr, const DeclContext *dc) { const_cast(expr)->walk(walker); } -ActorIsolation ActorIsolationRequest::evaluate( - Evaluator &evaluator, ValueDecl *value) const { - // If the attribute is explicitly marked @actorIndependent, report it as +/// Determine actor isolation solely from attributes. +/// +/// \returns the actor isolation determined from attributes alone (with no +/// inference rules). Returns \c None if there were no attributes on this +/// declaration. +static Optional getIsolationFromAttributes(Decl *decl) { + // Look up attributes on the declaration that can affect its actor isolation. + // If any of them are present, use that attribute. + auto independentAttr = decl->getAttrs().getAttribute(); + auto globalActorAttr = decl->getGlobalActorAttr(); + unsigned numIsolationAttrs = + (independentAttr ? 1 : 0) + (globalActorAttr ? 1 : 0); + if (numIsolationAttrs == 0) + return None; + + // Only one such attribute is valid. + if (numIsolationAttrs > 1) { + DeclName name; + if (auto value = dyn_cast(decl)) { + name = value->getName(); + } else if (auto ext = dyn_cast(decl)) { + if (auto selfTypeDecl = ext->getSelfNominalTypeDecl()) + name = selfTypeDecl->getName(); + } + + decl->diagnose( + diag::actor_isolation_multiple_attr, decl->getDescriptiveKind(), + name, independentAttr->getAttrName(), + globalActorAttr->second->getName().str()) + .highlight(independentAttr->getRangeWithAt()) + .highlight(globalActorAttr->first->getRangeWithAt()); + } + + // If the declaration is explicitly marked @actorIndependent, report it as // independent. - if (value->getAttrs().hasAttribute()) { - return ActorIsolation::forIndependent(); + if (independentAttr) { + return ActorIsolation::forIndependent(independentAttr->getKind()); } - // If the declaration overrides another declaration, it must have the same - // actor isolation. - if (auto overriddenValue = value->getOverriddenDecl()) { - if (auto isolation = getActorIsolation(overriddenValue)) - return isolation; + // If the declaration is marked with a global actor, report it as being + // part of that global actor. + if (globalActorAttr) { + ASTContext &ctx = decl->getASTContext(); + auto dc = decl->getInnermostDeclContext(); + Type globalActorType = evaluateOrDefault( + ctx.evaluator, + CustomAttrTypeRequest{ + globalActorAttr->first, dc, CustomAttrTypeKind::GlobalActor}, + Type()); + if (!globalActorType || globalActorType->hasError()) + return ActorIsolation::forUnspecified(); + + return ActorIsolation::forGlobalActor( + globalActorType->mapTypeOutOfContext()); + } + + llvm_unreachable("Forgot about an attribute?"); +} + +/// Infer isolation from witnessed protocol requirements. +static Optional getIsolationFromWitnessedRequirements( + ValueDecl *value) { + auto dc = value->getDeclContext(); + auto idc = dyn_cast_or_null(dc->getAsDecl()); + if (!idc) + return None; + + if (dc->getSelfProtocolDecl()) + return None; + + // Walk through each of the conformances in this context, collecting any + // requirements that have actor isolation. + auto conformances = evaluateOrDefault( + dc->getASTContext().evaluator, + LookupAllConformancesInContextRequest{idc}, { }); + using IsolatedRequirement = + std::tuple; + SmallVector isolatedRequirements; + for (auto conformance : conformances) { + auto protocol = conformance->getProtocol(); + for (auto found : protocol->lookupDirect(value->getName())) { + if (!isa(found->getDeclContext())) + continue; + + auto requirement = dyn_cast(found); + if (!requirement || isa(requirement)) + continue; + + auto requirementIsolation = getActorIsolation(requirement); + if (requirementIsolation.isUnspecified()) + continue; + + auto witness = conformance->getWitnessDecl(requirement); + if (witness != value) + continue; + + isolatedRequirements.push_back( + IsolatedRequirement{conformance, requirementIsolation, requirement}); + } + } + + // Filter out duplicate actors. + SmallPtrSet globalActorTypes; + bool sawActorIndependent = false; + isolatedRequirements.erase( + std::remove_if(isolatedRequirements.begin(), isolatedRequirements.end(), + [&](IsolatedRequirement &isolated) { + auto isolation = std::get<1>(isolated); + switch (isolation) { + case ActorIsolation::ActorInstance: + llvm_unreachable("protocol requirements cannot be actor instances"); + + case ActorIsolation::Independent: + case ActorIsolation::IndependentUnsafe: + // We only need one @actorIndependent. + if (sawActorIndependent) + return true; + + sawActorIndependent = true; + return false; + + case ActorIsolation::Unspecified: + return true; + + case ActorIsolation::GlobalActor: { + // Substitute into the global actor type. + auto conformance = std::get<0>(isolated); + auto requirementSubs = SubstitutionMap::getProtocolSubstitutions( + conformance->getProtocol(), dc->getSelfTypeInContext(), + ProtocolConformanceRef(conformance)); + Type globalActor = isolation.getGlobalActor().subst(requirementSubs); + if (!globalActorTypes.insert(globalActor->getCanonicalType()).second) + return true; + + // Update the global actor type, now that we've done this substitution. + std::get<1>(isolated) = ActorIsolation::forGlobalActor(globalActor); + return false; + } + } + }), + isolatedRequirements.end()); + + if (isolatedRequirements.size() != 1) + return None; + + return std::get<1>(isolatedRequirements.front()); +} + +ActorIsolation ActorIsolationRequest::evaluate( + Evaluator &evaluator, ValueDecl *value) const { + // If this declaration has one of the actor isolation attributes, report + // that. + if (auto isolationFromAttr = getIsolationFromAttributes(value)) { + return *isolationFromAttr; } + // Determine the default isolation for this declaration, which may still be + // overridden by other inference rules. + ActorIsolation defaultIsolation = ActorIsolation::forUnspecified(); + // Check for instance members of actor classes, which are part of // actor-isolated state. auto classDecl = value->getDeclContext()->getSelfClassDecl(); if (classDecl && classDecl->isActor() && value->isInstanceMember()) { - // A function that is an asynchronous context is actor-privileged. - if (auto func = dyn_cast(value)) { - if (func->isAsyncContext()) - return ActorIsolation::forActorPrivileged(classDecl); + defaultIsolation = ActorIsolation::forActorInstance(classDecl); + } + + // Disable inference of actor attributes outside of normal Swift source files. + if (!shouldInferAttributeInContext(value->getDeclContext())) + return defaultIsolation; + + // Function used when returning an inferred isolation. + auto inferredIsolation = [&](ActorIsolation inferred) { + // Add an implicit attribute to capture the actor isolation that was + // inferred, so that (e.g.) it will be printed and serialized. + ASTContext &ctx = value->getASTContext(); + switch (inferred) { + // FIXME: if the context is 'unsafe', is it fine to infer the 'safe' one? + case ActorIsolation::IndependentUnsafe: + case ActorIsolation::Independent: + value->getAttrs().add(new (ctx) ActorIndependentAttr( + ActorIndependentKind::Safe, /*IsImplicit=*/true)); + break; + + case ActorIsolation::GlobalActor: { + auto typeExpr = TypeExpr::createImplicit(inferred.getGlobalActor(), ctx); + auto attr = CustomAttr::create( + ctx, SourceLoc(), typeExpr, /*implicit=*/true); + value->getAttrs().add(attr); + break; + } + + case ActorIsolation::ActorInstance: + case ActorIsolation::Unspecified: + // Nothing to do. + break; } + return inferred; + }; + + // If the declaration overrides another declaration, it must have the same + // actor isolation. + if (auto overriddenValue = value->getOverriddenDecl()) { + if (auto isolation = getActorIsolation(overriddenValue)) { + SubstitutionMap subs; + if (auto env = value->getInnermostDeclContext() + ->getGenericEnvironmentOfContext()) { + subs = SubstitutionMap::getOverrideSubstitutions( + overriddenValue, value, subs); + } + + return inferredIsolation(isolation.subst(subs)); + } + } + + // If this is an accessor, use the actor isolation of its storage + // declaration. + if (auto accessor = dyn_cast(value)) { + return getActorIsolation(accessor->getStorage()); + } - // Everything else is part of the actor's isolated state. - return ActorIsolation::forActorInstance(classDecl); + // If the declaration witnesses a protocol requirement that is isolated, + // use that. + if (auto witnessedIsolation = getIsolationFromWitnessedRequirements(value)) { + return inferredIsolation(*witnessedIsolation); } - // Everything else is unspecified. - return ActorIsolation::forUnspecified(); + // If the declaration is a class with a superclass that has specified + // isolation, use that. + if (auto classDecl = dyn_cast(value)) { + if (auto superclassDecl = classDecl->getSuperclassDecl()) { + auto superclassIsolation = getActorIsolation(superclassDecl); + if (!superclassIsolation.isUnspecified()) { + if (superclassIsolation.requiresSubstitution()) { + Type superclassType = classDecl->getSuperclass(); + if (!superclassType) + return ActorIsolation::forUnspecified(); + + SubstitutionMap subs = superclassType->getMemberSubstitutionMap( + classDecl->getModuleContext(), classDecl); + superclassIsolation = superclassIsolation.subst(subs); + } + + return inferredIsolation(superclassIsolation); + } + } + } + + // If the declaration is in an extension that has one of the isolation + // attributes, use that. + if (auto ext = dyn_cast(value->getDeclContext())) { + if (auto isolationFromAttr = getIsolationFromAttributes(ext)) { + return inferredIsolation(*isolationFromAttr); + } + } + + // If the declaration is in a nominal type (or extension thereof) that + // has isolation, use that. + if (auto selfTypeDecl = value->getDeclContext()->getSelfNominalTypeDecl()) { + auto selfTypeIsolation = getActorIsolation(selfTypeDecl); + if (!selfTypeIsolation.isUnspecified()) { + return inferredIsolation(selfTypeIsolation); + } + } + + // Default isolation for this member. + return defaultIsolation; } ActorIsolation swift::getActorIsolation(ValueDecl *value) { @@ -737,3 +1272,37 @@ ActorIsolation swift::getActorIsolation(ValueDecl *value) { ctx.evaluator, ActorIsolationRequest{value}, ActorIsolation::forUnspecified()); } + +void swift::checkOverrideActorIsolation(ValueDecl *value) { + if (isa(value)) + return; + + auto overridden = value->getOverriddenDecl(); + if (!overridden) + return; + + // Determine the actor isolation of this declaration. + auto isolation = getActorIsolation(value); + + // Determine the actor isolation of the overridden function.= + auto overriddenIsolation = getActorIsolation(overridden); + + if (overriddenIsolation.requiresSubstitution()) { + SubstitutionMap subs; + if (auto env = value->getInnermostDeclContext() + ->getGenericEnvironmentOfContext()) { + subs = SubstitutionMap::getOverrideSubstitutions(overridden, value, subs); + overriddenIsolation = overriddenIsolation.subst(subs); + } + } + + // If the isolation matches, we're done. + if (isolation == overriddenIsolation) + return; + + // Isolation mismatch. Diagnose it. + value->diagnose( + diag::actor_isolation_override_mismatch, isolation, + value->getDescriptiveKind(), value->getName(), overriddenIsolation); + overridden->diagnose(diag::overridden_here); +} diff --git a/lib/Sema/TypeCheckConcurrency.h b/lib/Sema/TypeCheckConcurrency.h index 030b466537a8c..9191f815650f1 100644 --- a/lib/Sema/TypeCheckConcurrency.h +++ b/lib/Sema/TypeCheckConcurrency.h @@ -17,24 +17,22 @@ #ifndef SWIFT_SEMA_TYPECHECKCONCURRENCY_H #define SWIFT_SEMA_TYPECHECKCONCURRENCY_H +#include "swift/AST/Type.h" +#include + namespace swift { class ActorIsolation; +class ASTContext; class ClassDecl; +class ConcreteDeclRef; +class Decl; class DeclContext; class Expr; class FuncDecl; +class TypeBase; class ValueDecl; -/// Check whether the @asyncHandler attribute can be applied to the given -/// function declaration. -/// -/// \param diagnose Whether to emit a diagnostic when a problem is encountered. -/// -/// \returns \c true if there was a problem with adding the attribute, \c false -/// otherwise. -bool checkAsyncHandler(FuncDecl *func, bool diagnose); - /// Add notes suggesting the addition of 'async' or '@asyncHandler', as /// appropriate, to a diagnostic for a function that isn't an async context. void addAsyncNotes(FuncDecl *func); @@ -45,6 +43,111 @@ void checkActorIsolation(const Expr *expr, const DeclContext *dc); /// Determine how the given value declaration is isolated. ActorIsolation getActorIsolation(ValueDecl *value); +/// The isolation restriction in effect for a given declaration that is +/// referenced from source. +class ActorIsolationRestriction { +public: + enum Kind { + /// There is no restriction on references to the given declaration, + /// e.g., because it's immutable. + Unrestricted, + + /// Access to the declaration is unsafe in a concurrent context. + Unsafe, + + /// The declaration is a local entity whose capture could introduce + /// data races. The context in which the local was defined is provided. + LocalCapture, + + /// References to this member of an actor are only permitted on 'self'. + ActorSelf, + + /// References to a declaration that is part of a global actor are only + /// permitted from other declarations with that same global actor. + GlobalActor, + }; + +private: + /// The kind of restriction + Kind kind; + + union { + /// The local context that an entity is tied to. + DeclContext *localContext; + + /// The actor class that the entity is declared in. + ClassDecl *actorClass; + + /// The global actor type. + TypeBase *globalActor; + } data; + + explicit ActorIsolationRestriction(Kind kind) : kind(kind) { } + +public: + Kind getKind() const { return kind; } + + /// Retrieve the declaration context in which a local was defined. + DeclContext *getLocalContext() const { + assert(kind == LocalCapture); + return data.localContext; + } + + /// Retrieve the actor class that the declaration is within. + ClassDecl *getActorClass() const { + assert(kind == ActorSelf); + return data.actorClass; + } + + /// Retrieve the actor class that the declaration is within. + Type getGlobalActor() const { + assert(kind == GlobalActor); + return Type(data.globalActor); + } + + /// There are no restrictions on the use of the entity. + static ActorIsolationRestriction forUnrestricted() { + return ActorIsolationRestriction(Unrestricted); + } + + /// Accesses to the given declaration are unsafe. + static ActorIsolationRestriction forUnsafe() { + return ActorIsolationRestriction(Unsafe); + } + + /// Accesses to the given declaration can only be made via the 'self' of + /// the current actor. + static ActorIsolationRestriction forActorSelf(ClassDecl *actorClass) { + ActorIsolationRestriction result(ActorSelf); + result.data.actorClass = actorClass; + return result; + } + + /// Access is restricted to code running within the given local context. + static ActorIsolationRestriction forLocalCapture(DeclContext *dc) { + ActorIsolationRestriction result(LocalCapture); + result.data.localContext = dc; + return result; + } + + /// Accesses to the given declaration can only be made via this particular + /// global actor. + static ActorIsolationRestriction forGlobalActor(Type globalActor) { + ActorIsolationRestriction result(GlobalActor); + result.data.globalActor = globalActor.getPointer(); + return result; + } + + /// Determine the isolation rules for a given declaration. + static ActorIsolationRestriction forDeclaration(ConcreteDeclRef declRef); + + operator Kind() const { return kind; }; +}; + +/// Check that the actor isolation of an override matches that of its +/// overridden declaration. +void checkOverrideActorIsolation(ValueDecl *value); + } // end namespace swift #endif /* SWIFT_SEMA_TYPECHECKCONCURRENCY_H */ diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 5e3728222ca5b..0af4245017c8f 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -16,34 +16,21 @@ // //===----------------------------------------------------------------------===// -#include "ConstraintSystem.h" #include "MiscDiagnostics.h" -#include "SolutionResult.h" #include "TypeChecker.h" -#include "TypeCheckType.h" -#include "TypoCorrection.h" #include "swift/AST/ASTVisitor.h" #include "swift/AST/ASTWalker.h" -#include "swift/AST/DiagnosticsParse.h" #include "swift/AST/DiagnosticSuppression.h" #include "swift/AST/ExistentialLayout.h" #include "swift/AST/Initializer.h" -#include "swift/AST/NameLookup.h" -#include "swift/AST/NameLookupRequests.h" -#include "swift/AST/ParameterList.h" #include "swift/AST/PrettyStackTrace.h" -#include "swift/AST/PropertyWrappers.h" -#include "swift/AST/ProtocolConformance.h" #include "swift/AST/SubstitutionMap.h" #include "swift/AST/TypeCheckRequests.h" #include "swift/Basic/Statistic.h" -#include "swift/Parse/Confusables.h" -#include "swift/Parse/Lexer.h" #include "swift/Sema/CodeCompletionTypeChecking.h" -#include "llvm/ADT/APInt.h" +#include "swift/Sema/ConstraintSystem.h" +#include "swift/Sema/SolutionResult.h" #include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/FoldingSet.h" -#include "llvm/ADT/MapVector.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" @@ -224,1816 +211,6 @@ Expr *ConstraintLocatorBuilder::trySimplifyToExpr() const { return (path.empty() ? getAsExpr(anchor) : nullptr); } -//===----------------------------------------------------------------------===// -// High-level entry points. -//===----------------------------------------------------------------------===// - -static unsigned getNumArgs(ValueDecl *value) { - if (auto *func = dyn_cast(value)) - return func->getParameters()->size(); - return ~0U; -} - -static bool matchesDeclRefKind(ValueDecl *value, DeclRefKind refKind) { - switch (refKind) { - // An ordinary reference doesn't ignore anything. - case DeclRefKind::Ordinary: - return true; - - // A binary-operator reference only honors FuncDecls with a certain type. - case DeclRefKind::BinaryOperator: - return (getNumArgs(value) == 2); - - case DeclRefKind::PrefixOperator: - return (!value->getAttrs().hasAttribute() && - getNumArgs(value) == 1); - - case DeclRefKind::PostfixOperator: - return (value->getAttrs().hasAttribute() && - getNumArgs(value) == 1); - } - llvm_unreachable("bad declaration reference kind"); -} - -static bool containsDeclRefKind(LookupResult &lookupResult, - DeclRefKind refKind) { - for (auto candidate : lookupResult) { - ValueDecl *D = candidate.getValueDecl(); - if (!D) - continue; - if (matchesDeclRefKind(D, refKind)) - return true; - } - return false; -} - -/// Emit a diagnostic with a fixit hint for an invalid binary operator, showing -/// how to split it according to splitCandidate. -static void diagnoseBinOpSplit(ASTContext &Context, UnresolvedDeclRefExpr *UDRE, - std::pair splitCandidate, - Diag diagID) { - - unsigned splitLoc = splitCandidate.first; - bool isBinOpFirst = splitCandidate.second; - StringRef nameStr = UDRE->getName().getBaseIdentifier().str(); - auto startStr = nameStr.substr(0, splitLoc); - auto endStr = nameStr.drop_front(splitLoc); - - // One valid split found, it is almost certainly the right answer. - auto diag = Context.Diags.diagnose( - UDRE->getLoc(), diagID, Context.getIdentifier(startStr), - Context.getIdentifier(endStr), isBinOpFirst); - // Highlight the whole operator. - diag.highlight(UDRE->getLoc()); - // Insert whitespace on the left if the binop is at the start, or to the - // right if it is end. - if (isBinOpFirst) - diag.fixItInsert(UDRE->getLoc(), " "); - else - diag.fixItInsertAfter(UDRE->getLoc(), " "); - - // Insert a space between the operators. - diag.fixItInsert(UDRE->getLoc().getAdvancedLoc(splitLoc), " "); -} - -/// If we failed lookup of a binary operator, check to see it to see if -/// it is a binary operator juxtaposed with a unary operator (x*-4) that -/// needs whitespace. If so, emit specific diagnostics for it and return true, -/// otherwise return false. -static bool diagnoseOperatorJuxtaposition(UnresolvedDeclRefExpr *UDRE, - DeclContext *DC) { - Identifier name = UDRE->getName().getBaseIdentifier(); - StringRef nameStr = name.str(); - if (!name.isOperator() || nameStr.size() < 2) - return false; - - bool isBinOp = UDRE->getRefKind() == DeclRefKind::BinaryOperator; - - // If this is a binary operator, relex the token, to decide whether it has - // whitespace around it or not. If it does "x +++ y", then it isn't likely to - // be a case where a space was forgotten. - auto &Context = DC->getASTContext(); - if (isBinOp) { - auto tok = Lexer::getTokenAtLocation(Context.SourceMgr, UDRE->getLoc()); - if (tok.getKind() != tok::oper_binary_unspaced) - return false; - } - - // Okay, we have a failed lookup of a multicharacter operator. Check to see if - // lookup succeeds if part is split off, and record the matches found. - // - // In the case of a binary operator, the bool indicated is false if the - // first half of the split is the unary operator (x!*4) or true if it is the - // binary operator (x*+4). - std::vector> WorkableSplits; - - // Check all the potential splits. - for (unsigned splitLoc = 1, e = nameStr.size(); splitLoc != e; ++splitLoc) { - // For it to be a valid split, the start and end section must be valid - // operators, splitting a unicode code point isn't kosher. - auto startStr = nameStr.substr(0, splitLoc); - auto endStr = nameStr.drop_front(splitLoc); - if (!Lexer::isOperator(startStr) || !Lexer::isOperator(endStr)) - continue; - - DeclNameRef startName(Context.getIdentifier(startStr)); - DeclNameRef endName(Context.getIdentifier(endStr)); - - // Perform name lookup for the first and second pieces. If either fail to - // be found, then it isn't a valid split. - auto startLookup = TypeChecker::lookupUnqualified( - DC, startName, UDRE->getLoc(), defaultUnqualifiedLookupOptions); - if (!startLookup) continue; - auto endLookup = TypeChecker::lookupUnqualified(DC, endName, UDRE->getLoc(), - defaultUnqualifiedLookupOptions); - if (!endLookup) continue; - - // If the overall operator is a binary one, then we're looking at - // juxtaposed binary and unary operators. - if (isBinOp) { - // Look to see if the candidates found could possibly match. - if (containsDeclRefKind(startLookup, DeclRefKind::PostfixOperator) && - containsDeclRefKind(endLookup, DeclRefKind::BinaryOperator)) - WorkableSplits.push_back({ splitLoc, false }); - - if (containsDeclRefKind(startLookup, DeclRefKind::BinaryOperator) && - containsDeclRefKind(endLookup, DeclRefKind::PrefixOperator)) - WorkableSplits.push_back({ splitLoc, true }); - } else { - // Otherwise, it is two of the same kind, e.g. "!!x" or "!~x". - if (containsDeclRefKind(startLookup, UDRE->getRefKind()) && - containsDeclRefKind(endLookup, UDRE->getRefKind())) - WorkableSplits.push_back({ splitLoc, false }); - } - } - - switch (WorkableSplits.size()) { - case 0: - // No splits found, can't produce this diagnostic. - return false; - case 1: - // One candidate: produce an error with a fixit on it. - if (isBinOp) - diagnoseBinOpSplit(Context, UDRE, WorkableSplits[0], - diag::unspaced_binary_operator_fixit); - else - Context.Diags.diagnose( - UDRE->getLoc().getAdvancedLoc(WorkableSplits[0].first), - diag::unspaced_unary_operator); - return true; - - default: - // Otherwise, we have to produce a series of notes listing the various - // options. - Context.Diags - .diagnose(UDRE->getLoc(), isBinOp ? diag::unspaced_binary_operator - : diag::unspaced_unary_operator) - .highlight(UDRE->getLoc()); - - if (isBinOp) { - for (auto candidateSplit : WorkableSplits) - diagnoseBinOpSplit(Context, UDRE, candidateSplit, - diag::unspaced_binary_operators_candidate); - } - return true; - } -} - -static bool diagnoseRangeOperatorMisspell(DiagnosticEngine &Diags, - UnresolvedDeclRefExpr *UDRE) { - auto name = UDRE->getName().getBaseIdentifier(); - if (!name.isOperator()) - return false; - - auto corrected = StringRef(); - if (name.str() == ".." || name.str() == "...." || - name.str() == ".…" || name.str() == "…" || name.str() == "….") - corrected = "..."; - else if (name.str() == "...<" || name.str() == "....<" || - name.str() == "…<") - corrected = "..<"; - - if (!corrected.empty()) { - Diags - .diagnose(UDRE->getLoc(), diag::cannot_find_in_scope_corrected, - UDRE->getName(), true, corrected) - .highlight(UDRE->getSourceRange()) - .fixItReplace(UDRE->getSourceRange(), corrected); - - return true; - } - return false; -} - -static bool diagnoseIncDecOperator(DiagnosticEngine &Diags, - UnresolvedDeclRefExpr *UDRE) { - auto name = UDRE->getName().getBaseIdentifier(); - if (!name.isOperator()) - return false; - - auto corrected = StringRef(); - if (name.str() == "++") - corrected = "+= 1"; - else if (name.str() == "--") - corrected = "-= 1"; - - if (!corrected.empty()) { - Diags - .diagnose(UDRE->getLoc(), diag::cannot_find_in_scope_corrected, - UDRE->getName(), true, corrected) - .highlight(UDRE->getSourceRange()); - - return true; - } - return false; -} - -static bool findNonMembers(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 (!isValid(D)) - return false; - - if (matchesDeclRefKind(D, refKind)) - ResultValues.push_back(D); - } - - return AllDeclRefs; -} - -/// Find the next element in a chain of members. If this expression is (or -/// could be) the base of such a chain, this will return \c nullptr. -static Expr *getMemberChainSubExpr(Expr *expr) { - assert(expr && "getMemberChainSubExpr called with null expr!"); - if (auto *UDE = dyn_cast(expr)) { - return UDE->getBase(); - } else if (auto *CE = dyn_cast(expr)) { - return CE->getFn(); - } else if (auto *BOE = dyn_cast(expr)) { - return BOE->getSubExpr(); - } else if (auto *FVE = dyn_cast(expr)) { - return FVE->getSubExpr(); - } else if (auto *SE = dyn_cast(expr)) { - return SE->getBase(); - } else if (auto *CCE = dyn_cast(expr)) { - return CCE->getBase(); - } else { - return nullptr; - } -} - -UnresolvedMemberExpr *TypeChecker::getUnresolvedMemberChainBase(Expr *expr) { - if (auto *subExpr = getMemberChainSubExpr(expr)) - return getUnresolvedMemberChainBase(subExpr); - else - return dyn_cast(expr); -} - -/// Whether this expression is a member of a member chain. -static bool isMemberChainMember(Expr *expr) { - return getMemberChainSubExpr(expr) != nullptr; -} -/// Whether this expression sits at the end of a chain of member accesses. -static bool isMemberChainTail(Expr *expr, Expr *parent) { - assert(expr && "isMemberChainTail called with null expr!"); - // If this expression's parent is not itself part of a chain (or, this expr - // has no parent expr), this must be the tail of the chain. - return parent == nullptr || !isMemberChainMember(parent); -} - -/// Bind an UnresolvedDeclRefExpr by performing name lookup and -/// returning the resultant expression. Context is the DeclContext used -/// for the lookup. -Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, - DeclContext *DC, - bool replaceInvalidRefsWithErrors) { - // Process UnresolvedDeclRefExpr by doing an unqualified lookup. - DeclNameRef Name = UDRE->getName(); - SourceLoc Loc = UDRE->getLoc(); - - auto errorResult = [&]() -> Expr * { - if (replaceInvalidRefsWithErrors) - return new (DC->getASTContext()) ErrorExpr(UDRE->getSourceRange()); - return UDRE; - }; - - // Perform standard value name lookup. - NameLookupOptions lookupOptions = defaultUnqualifiedLookupOptions; - // TODO: Include all of the possible members to give a solver a - // chance to diagnose name shadowing which requires explicit - // name/module qualifier to access top-level name. - lookupOptions |= NameLookupFlags::IncludeOuterResults; - - if (Loc.isInvalid()) - DC = DC->getModuleScopeContext(); - - auto Lookup = TypeChecker::lookupUnqualified(DC, Name, Loc, lookupOptions); - - auto &Context = DC->getASTContext(); - if (!Lookup) { - // If we failed lookup of an operator, check to see if this is a range - // operator misspelling. Otherwise try to diagnose a juxtaposition - // e.g. (x*-4) that needs whitespace. - if (diagnoseRangeOperatorMisspell(Context.Diags, UDRE) || - diagnoseIncDecOperator(Context.Diags, UDRE) || - diagnoseOperatorJuxtaposition(UDRE, DC)) { - return errorResult(); - } - - // Try ignoring access control. - NameLookupOptions relookupOptions = lookupOptions; - relookupOptions |= NameLookupFlags::IgnoreAccessControl; - auto inaccessibleResults = - TypeChecker::lookupUnqualified(DC, Name, Loc, relookupOptions); - if (inaccessibleResults) { - // FIXME: What if the unviable candidates have different levels of access? - const ValueDecl *first = inaccessibleResults.front().getValueDecl(); - Context.Diags.diagnose( - Loc, diag::candidate_inaccessible, first->getBaseName(), - first->getFormalAccessScope().accessLevelForDiagnostics()); - - // FIXME: If any of the candidates (usually just one) are in the same - // module we could offer a fix-it. - for (auto lookupResult : inaccessibleResults) { - auto *VD = lookupResult.getValueDecl(); - VD->diagnose(diag::decl_declared_here, VD->getName()); - } - - // Don't try to recover here; we'll get more access-related diagnostics - // downstream if the type of the inaccessible decl is also inaccessible. - return errorResult(); - } - - // TODO: Name will be a compound name if it was written explicitly as - // one, but we should also try to propagate labels into this. - DeclNameLoc nameLoc = UDRE->getNameLoc(); - - Identifier simpleName = Name.getBaseIdentifier(); - const char *buffer = simpleName.get(); - llvm::SmallString<64> expectedIdentifier; - bool isConfused = false; - uint32_t codepoint; - uint32_t firstConfusableCodepoint = 0; - int totalCodepoints = 0; - int offset = 0; - while ((codepoint = validateUTF8CharacterAndAdvance(buffer, - buffer + - strlen(buffer))) - != ~0U) { - int length = (buffer - simpleName.get()) - offset; - if (auto expectedCodepoint = - confusable::tryConvertConfusableCharacterToASCII(codepoint)) { - if (firstConfusableCodepoint == 0) { - firstConfusableCodepoint = codepoint; - } - isConfused = true; - expectedIdentifier += expectedCodepoint; - } else { - expectedIdentifier += (char)codepoint; - } - - totalCodepoints++; - - offset += length; - } - - auto emitBasicError = [&] { - Context.Diags - .diagnose(Loc, diag::cannot_find_in_scope, Name, - Name.isOperator()) - .highlight(UDRE->getSourceRange()); - }; - - if (!isConfused) { - if (Name.isSimpleName(Context.Id_Self)) { - if (DeclContext *typeContext = DC->getInnermostTypeContext()){ - Type SelfType = typeContext->getSelfInterfaceType(); - - if (typeContext->getSelfClassDecl()) - SelfType = DynamicSelfType::get(SelfType, Context); - SelfType = DC->mapTypeIntoContext(SelfType); - return new (Context) - TypeExpr(new (Context) FixedTypeRepr(SelfType, Loc)); - } - } - - TypoCorrectionResults corrections(Name, nameLoc); - TypeChecker::performTypoCorrection(DC, UDRE->getRefKind(), Type(), - lookupOptions, corrections); - - if (auto typo = corrections.claimUniqueCorrection()) { - auto diag = Context.Diags.diagnose( - Loc, diag::cannot_find_in_scope_corrected, Name, - Name.isOperator(), typo->CorrectedName.getBaseIdentifier().str()); - diag.highlight(UDRE->getSourceRange()); - typo->addFixits(diag); - } else { - emitBasicError(); - } - - corrections.noteAllCandidates(); - } else { - emitBasicError(); - - if (totalCodepoints == 1) { - auto charNames = confusable::getConfusableAndBaseCodepointNames( - firstConfusableCodepoint); - Context.Diags - .diagnose(Loc, diag::single_confusable_character, - UDRE->getName().isOperator(), simpleName.str(), - charNames.first, expectedIdentifier, charNames.second) - .fixItReplace(Loc, expectedIdentifier); - } else { - Context.Diags - .diagnose(Loc, diag::confusable_character, - UDRE->getName().isOperator(), simpleName.str(), - expectedIdentifier) - .fixItReplace(Loc, expectedIdentifier); - } - } - - // TODO: consider recovering from here. We may want some way to suppress - // downstream diagnostics, though. - - return errorResult(); - } - - // FIXME: Need to refactor the way we build an AST node from a lookup result! - - SmallVector ResultValues; - ValueDecl *localDeclAfterUse = nullptr; - auto isValid = [&](ValueDecl *D) { - // FIXME: The source-location checks won't make sense once - // EnableASTScopeLookup is the default. - // - // Note that we allow forward references to types, because they cannot - // capture. - if (Loc.isValid() && D->getLoc().isValid() && - D->getDeclContext()->isLocalContext() && - D->getDeclContext() == DC && - Context.SourceMgr.isBeforeInBuffer(Loc, D->getLoc()) && - !isa(D)) { - localDeclAfterUse = D; - return false; - } - return true; - }; - bool AllDeclRefs = - findNonMembers(Lookup.innerResults(), UDRE->getRefKind(), - /*breakOnMember=*/true, ResultValues, isValid); - - // If local declaration after use is found, check outer results for - // better matching candidates. - if (localDeclAfterUse) { - auto innerDecl = localDeclAfterUse; - while (localDeclAfterUse) { - if (Lookup.outerResults().empty()) { - Context.Diags.diagnose(Loc, diag::use_local_before_declaration, Name); - Context.Diags.diagnose(innerDecl, diag::decl_declared_here, - localDeclAfterUse->getName()); - Expr *error = new (Context) ErrorExpr(UDRE->getSourceRange()); - return error; - } - - Lookup.shiftDownResults(); - ResultValues.clear(); - localDeclAfterUse = nullptr; - AllDeclRefs = - findNonMembers(Lookup.innerResults(), UDRE->getRefKind(), - /*breakOnMember=*/true, ResultValues, isValid); - } - } - - // If we have an unambiguous reference to a type decl, form a TypeExpr. - if (Lookup.size() == 1 && UDRE->getRefKind() == DeclRefKind::Ordinary && - isa(Lookup[0].getValueDecl())) { - auto *D = cast(Lookup[0].getValueDecl()); - // FIXME: This is odd. - if (isa(D)) { - return new (Context) DeclRefExpr(D, UDRE->getNameLoc(), - /*Implicit=*/false, - AccessSemantics::Ordinary, - D->getInterfaceType()); - } - - auto *LookupDC = Lookup[0].getDeclContext(); - if (UDRE->isImplicit()) { - return TypeExpr::createImplicitForDecl( - UDRE->getNameLoc(), D, LookupDC, - LookupDC->mapTypeIntoContext(D->getInterfaceType())); - } else { - return TypeExpr::createForDecl(UDRE->getNameLoc(), D, LookupDC); - } - } - - if (AllDeclRefs) { - // Diagnose uses of operators that found no matching candidates. - if (ResultValues.empty()) { - assert(UDRE->getRefKind() != DeclRefKind::Ordinary); - Context.Diags.diagnose( - Loc, diag::use_nonmatching_operator, Name, - UDRE->getRefKind() == DeclRefKind::BinaryOperator - ? 0 - : UDRE->getRefKind() == DeclRefKind::PrefixOperator ? 1 : 2); - return new (Context) ErrorExpr(UDRE->getSourceRange()); - } - - // For operators, sort the results so that non-generic operations come - // first. - // Note: this is part of a performance hack to prefer non-generic operators - // to generic operators, because the former is far more efficient to check. - if (UDRE->getRefKind() != DeclRefKind::Ordinary) { - std::stable_sort(ResultValues.begin(), ResultValues.end(), - [&](ValueDecl *x, ValueDecl *y) -> bool { - auto xGeneric = x->getInterfaceType()->getAs(); - auto yGeneric = y->getInterfaceType()->getAs(); - if (static_cast(xGeneric) != static_cast(yGeneric)) { - return xGeneric? false : true; - } - - if (!xGeneric) - return false; - - unsigned xDepth = xGeneric->getGenericParams().back()->getDepth(); - unsigned yDepth = yGeneric->getGenericParams().back()->getDepth(); - return xDepth < yDepth; - }); - } - - return buildRefExpr(ResultValues, DC, UDRE->getNameLoc(), - UDRE->isImplicit(), UDRE->getFunctionRefKind()); - } - - ResultValues.clear(); - bool AllMemberRefs = true; - ValueDecl *Base = nullptr; - DeclContext *BaseDC = nullptr; - for (auto Result : Lookup) { - auto ThisBase = Result.getBaseDecl(); - - // Track the base for member declarations. - if (ThisBase && !isa(ThisBase)) { - auto Value = Result.getValueDecl(); - ResultValues.push_back(Value); - if (Base && ThisBase != Base) { - AllMemberRefs = false; - break; - } - - Base = ThisBase; - BaseDC = Result.getDeclContext(); - continue; - } - - AllMemberRefs = false; - break; - } - - if (AllMemberRefs) { - Expr *BaseExpr; - if (auto PD = dyn_cast(Base)) { - auto selfParam = PD->getGenericParams()->getParams().front(); - BaseExpr = TypeExpr::createImplicitForDecl( - UDRE->getNameLoc(), selfParam, - /*DC*/ nullptr, - DC->mapTypeIntoContext(selfParam->getInterfaceType())); - } else if (auto NTD = dyn_cast(Base)) { - BaseExpr = TypeExpr::createImplicitForDecl( - UDRE->getNameLoc(), NTD, BaseDC, - DC->mapTypeIntoContext(NTD->getInterfaceType())); - } else { - BaseExpr = new (Context) DeclRefExpr(Base, UDRE->getNameLoc(), - /*Implicit=*/true); - } - - llvm::SmallVector outerAlternatives; - (void)findNonMembers(Lookup.outerResults(), UDRE->getRefKind(), - /*breakOnMember=*/false, outerAlternatives, - /*isValid=*/[](ValueDecl *choice) -> bool { - return !choice->isInvalid(); - }); - - // Otherwise, form an UnresolvedDotExpr and sema will resolve it based on - // type information. - 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 - // very broken, but it's still conceivable that this may happen due to - // invalid shadowed declarations. - // - // Make sure we emit a diagnostic, since returning an ErrorExpr without - // producing one will break things downstream. - Context.Diags.diagnose(Loc, diag::ambiguous_decl_ref, Name); - for (auto Result : Lookup) { - auto *Decl = Result.getValueDecl(); - Context.Diags.diagnose(Decl, diag::decl_declared_here, Decl->getName()); - } - return new (Context) ErrorExpr(UDRE->getSourceRange()); -} - -/// If an expression references 'self.init' or 'super.init' in an -/// initializer context, returns the implicit 'self' decl of the constructor. -/// Otherwise, return nil. -VarDecl * -TypeChecker::getSelfForInitDelegationInConstructor(DeclContext *DC, - UnresolvedDotExpr *ctorRef) { - // If the reference isn't to a constructor, we're done. - if (ctorRef->getName().getBaseName() != DeclBaseName::createConstructor()) - return nullptr; - - if (auto ctorContext = - dyn_cast_or_null(DC->getInnermostMethodContext())) { - auto nestedArg = ctorRef->getBase(); - if (auto inout = dyn_cast(nestedArg)) - nestedArg = inout->getSubExpr(); - if (nestedArg->isSuperExpr()) - return ctorContext->getImplicitSelfDecl(); - if (auto declRef = dyn_cast(nestedArg)) - if (declRef->getDecl()->getName() == DC->getASTContext().Id_self) - return ctorContext->getImplicitSelfDecl(); - } - return nullptr; -} - -namespace { - /// Update the function reference kind based on adding a direct call to a - /// callee with this kind. - FunctionRefKind addingDirectCall(FunctionRefKind kind) { - switch (kind) { - case FunctionRefKind::Unapplied: - return FunctionRefKind::SingleApply; - - case FunctionRefKind::SingleApply: - case FunctionRefKind::DoubleApply: - return FunctionRefKind::DoubleApply; - - case FunctionRefKind::Compound: - return FunctionRefKind::Compound; - } - - llvm_unreachable("Unhandled FunctionRefKind in switch."); - } - - /// Update a direct callee expression node that has a function reference kind - /// based on seeing a call to this callee. - templategetFunctionRefKind())> - void tryUpdateDirectCalleeImpl(E *callee, int) { - callee->setFunctionRefKind(addingDirectCall(callee->getFunctionRefKind())); - } - - /// Version of tryUpdateDirectCalleeImpl for when the callee - /// expression type doesn't carry a reference. - template - void tryUpdateDirectCalleeImpl(E *callee, ...) { } - - /// The given expression is the direct callee of a call expression; mark it to - /// indicate that it has been called. - void markDirectCallee(Expr *callee) { - while (true) { - // Look through identity expressions. - if (auto identity = dyn_cast(callee)) { - callee = identity->getSubExpr(); - continue; - } - - // Look through unresolved 'specialize' expressions. - if (auto specialize = dyn_cast(callee)) { - callee = specialize->getSubExpr(); - continue; - } - - // Look through optional binding. - if (auto bindOptional = dyn_cast(callee)) { - callee = bindOptional->getSubExpr(); - continue; - } - - // Look through forced binding. - if (auto force = dyn_cast(callee)) { - callee = force->getSubExpr(); - continue; - } - - // Calls compose. - if (auto call = dyn_cast(callee)) { - callee = call->getFn(); - continue; - } - - // We're done. - break; - } - - // Cast the callee to its most-specific class, then try to perform an - // update. If the expression node has a declaration reference in it, the - // update will succeed. Otherwise, we're done propagating. - switch (callee->getKind()) { -#define EXPR(Id, Parent) \ - case ExprKind::Id: \ - tryUpdateDirectCalleeImpl(cast(callee), 0); \ - break; -#include "swift/AST/ExprNodes.def" - } - } - - class PreCheckExpression : public ASTWalker { - ASTContext &Ctx; - DeclContext *DC; - - Expr *ParentExpr; - - /// Indicates whether pre-check is allowed to insert - /// implicit `ErrorExpr` in place of invalid references. - bool UseErrorExprs; - - /// A stack of expressions being walked, used to determine where to - /// insert RebindSelfInConstructorExpr nodes. - llvm::SmallVector ExprStack; - - /// The 'self' variable to use when rebinding 'self' in a constructor. - VarDecl *UnresolvedCtorSelf = nullptr; - - /// The expression that will be wrapped by a RebindSelfInConstructorExpr - /// node when visited. - Expr *UnresolvedCtorRebindTarget = nullptr; - - /// The expressions that are direct arguments of call expressions. - llvm::SmallPtrSet CallArgs; - - /// Simplify expressions which are type sugar productions that got parsed - /// as expressions due to the parser not knowing which identifiers are - /// type names. - TypeExpr *simplifyTypeExpr(Expr *E); - - /// Simplify unresolved dot expressions which are nested type productions. - TypeExpr *simplifyNestedTypeExpr(UnresolvedDotExpr *UDE); - - TypeExpr *simplifyUnresolvedSpecializeExpr(UnresolvedSpecializeExpr *USE); - - /// Simplify a key path expression into a canonical form. - void resolveKeyPathExpr(KeyPathExpr *KPE); - - /// Simplify constructs like `UInt32(1)` into `1 as UInt32` if - /// the type conforms to the expected literal protocol. - Expr *simplifyTypeConstructionWithLiteralArg(Expr *E); - - /// In Swift < 5, diagnose and correct invalid multi-argument or - /// argument-labeled interpolations. - void correctInterpolationIfStrange(InterpolatedStringLiteralExpr *ISLE) { - // These expressions are valid in Swift 5+. - if (getASTContext().isSwiftVersionAtLeast(5)) - return; - - /// Diagnoses appendInterpolation(...) calls with multiple - /// arguments or argument labels and corrects them. - class StrangeInterpolationRewriter : public ASTWalker { - ASTContext &Context; - - public: - StrangeInterpolationRewriter(ASTContext &Ctx) : Context(Ctx) {} - - virtual bool walkToDeclPre(Decl *D) override { - // We don't want to look inside decls. - return false; - } - - virtual std::pair walkToExprPre(Expr *E) override { - // One InterpolatedStringLiteralExpr should never be nested inside - // another except as a child of a CallExpr, and we don't recurse into - // the children of CallExprs. - assert(!isa(E) && - "StrangeInterpolationRewriter found nested interpolation?"); - - // We only care about CallExprs. - if (!isa(E)) - return { true, E }; - - auto call = cast(E); - if (auto callee = dyn_cast(call->getFn())) { - if (callee->getName().getBaseName() == - Context.Id_appendInterpolation) { - Expr *newArg = nullptr; - SourceLoc lParen, rParen; - - if (call->getNumArguments() > 1) { - auto *args = cast(call->getArg()); - - lParen = args->getLParenLoc(); - rParen = args->getRParenLoc(); - Expr *secondArg = args->getElement(1); - - Context.Diags - .diagnose(secondArg->getLoc(), - diag::string_interpolation_list_changing) - .highlightChars(secondArg->getLoc(), rParen); - Context.Diags - .diagnose(secondArg->getLoc(), - diag::string_interpolation_list_insert_parens) - .fixItInsertAfter(lParen, "(") - .fixItInsert(rParen, ")"); - - newArg = args; - } - else if(call->getNumArguments() == 1 && - call->getArgumentLabels().front() != Identifier()) { - auto *args = cast(call->getArg()); - newArg = args->getElement(0); - - lParen = args->getLParenLoc(); - rParen = args->getRParenLoc(); - - SourceLoc argLabelLoc = call->getArgumentLabelLoc(0), - argLoc = newArg->getStartLoc(); - - Context.Diags - .diagnose(argLabelLoc, - diag::string_interpolation_label_changing) - .highlightChars(argLabelLoc, argLoc); - Context.Diags - .diagnose(argLabelLoc, - diag::string_interpolation_remove_label, - call->getArgumentLabels().front()) - .fixItRemoveChars(argLabelLoc, argLoc); - } - - // If newArg is no longer null, we need to build a new - // appendInterpolation(_:) call that takes it to replace the bad - // appendInterpolation(...) call. - if (newArg) { - auto newCallee = new (Context) UnresolvedDotExpr( - callee->getBase(), /*dotloc=*/SourceLoc(), - DeclNameRef(Context.Id_appendInterpolation), - /*nameloc=*/DeclNameLoc(), /*Implicit=*/true); - - E = CallExpr::create(Context, newCallee, lParen, {newArg}, - {Identifier()}, {SourceLoc()}, rParen, - /*trailingClosures=*/{}, - /*implicit=*/false); - } - } - } - - // There is never a CallExpr between an InterpolatedStringLiteralExpr - // and an un-typechecked appendInterpolation(...) call, so whether we - // changed E or not, we don't need to recurse any deeper. - return { false, E }; - } - }; - - ISLE->getAppendingExpr()->walk( - StrangeInterpolationRewriter(getASTContext())); - } - - public: - PreCheckExpression(DeclContext *dc, Expr *parent, - bool replaceInvalidRefsWithErrors) - : Ctx(dc->getASTContext()), DC(dc), ParentExpr(parent), - UseErrorExprs(replaceInvalidRefsWithErrors) {} - - ASTContext &getASTContext() const { return Ctx; } - - bool walkToClosureExprPre(ClosureExpr *expr); - - bool shouldWalkCaptureInitializerExpressions() override { return true; } - - std::pair walkToExprPre(Expr *expr) override { - // If this is a call, record the argument expression. - if (auto call = dyn_cast(expr)) { - if (!isa(expr)) { - CallArgs.insert(call->getArg()); - } - } - - // FIXME(diagnostics): `InOutType` could appear here as a result - // of successful re-typecheck of the one of the sub-expressions e.g. - // `let _: Int = { (s: inout S) in s.bar() }`. On the first - // attempt to type-check whole expression `s.bar()` - is going - // to have a base which points directly to declaration of `S`. - // But when diagnostics attempts to type-check `s.bar()` standalone - // its base would be tranformed into `InOutExpr -> DeclRefExr`, - // and `InOutType` is going to be recorded in constraint system. - // One possible way to fix this (if diagnostics still use typecheck) - // might be to make it so self is not wrapped into `InOutExpr` - // but instead used as @lvalue type in some case of mutable members. - if (!expr->isImplicit()) { - if (isa(expr) || isa(expr)) { - auto *LE = cast(expr); - if (auto *IOE = dyn_cast(LE->getBase())) - LE->setBase(IOE->getSubExpr()); - } - - if (auto *DSCE = dyn_cast(expr)) { - if (auto *IOE = dyn_cast(DSCE->getBase())) - DSCE->setBase(IOE->getSubExpr()); - } - } - - // Local function used to finish up processing before returning. Every - // return site should call through here. - auto finish = [&](bool recursive, Expr *expr) { - // If we're going to recurse, record this expression on the stack. - if (recursive) - ExprStack.push_back(expr); - - return std::make_pair(recursive, expr); - }; - - // For closures, type-check the patterns and result type as written, - // but do not walk into the body. That will be type-checked after - // we've determine the complete function type. - if (auto closure = dyn_cast(expr)) - return finish(walkToClosureExprPre(closure), expr); - - if (auto unresolved = dyn_cast(expr)) { - TypeChecker::checkForForbiddenPrefix( - getASTContext(), unresolved->getName().getBaseName()); - return finish(true, TypeChecker::resolveDeclRefExpr(unresolved, DC, - UseErrorExprs)); - } - - // Let's try to figure out if `InOutExpr` is out of place early - // otherwise there is a risk of producing solutions which can't - // be later applied to AST and would result in the crash in some - // cases. Such expressions are only allowed in argument positions - // of function/operator calls. - if (isa(expr)) { - // If this is an implicit `inout` expression we assume that - // compiler knowns what it's doing. - if (expr->isImplicit()) - return finish(true, expr); - - auto parents = ParentExpr->getParentMap(); - - auto result = parents.find(expr); - if (result != parents.end()) { - auto *parent = result->getSecond(); - - if (isa(parent)) - return finish(true, expr); - - if (isa(parent) || isa(parent)) { - auto call = parents.find(parent); - if (call != parents.end()) { - if (isa(call->getSecond()) || - isa(call->getSecond())) - return finish(true, expr); - - if (isa(call->getSecond())) { - getASTContext().Diags.diagnose( - expr->getStartLoc(), - diag::cannot_pass_inout_arg_to_subscript); - return finish(false, nullptr); - } - } - } - } - - getASTContext().Diags.diagnose(expr->getStartLoc(), - diag::extraneous_address_of); - return finish(false, nullptr); - } - - if (auto *ISLE = dyn_cast(expr)) - correctInterpolationIfStrange(ISLE); - - return finish(true, expr); - } - - Expr *walkToExprPost(Expr *expr) override { - // Remove this expression from the stack. - assert(ExprStack.back() == expr); - ExprStack.pop_back(); - - // Mark the direct callee as being a callee. - if (auto *call = dyn_cast(expr)) - markDirectCallee(call->getFn()); - - // Fold sequence expressions. - if (auto *seqExpr = dyn_cast(expr)) { - auto result = TypeChecker::foldSequence(seqExpr, DC); - return result->walk(*this); - } - - // Type check the type parameters in an UnresolvedSpecializeExpr. - if (auto *us = dyn_cast(expr)) { - if (auto *typeExpr = simplifyUnresolvedSpecializeExpr(us)) - return typeExpr; - } - - // If we're about to step out of a ClosureExpr, restore the DeclContext. - if (auto *ce = dyn_cast(expr)) { - assert(DC == ce && "DeclContext imbalance"); - DC = ce->getParent(); - } - - // A 'self.init' or 'super.init' application inside a constructor will - // evaluate to void, with the initializer's result implicitly rebound - // to 'self'. Recognize the unresolved constructor expression and - // determine where to place the RebindSelfInConstructorExpr node. - // When updating this logic, also update - // RebindSelfInConstructorExpr::getCalledConstructor. - auto &ctx = getASTContext(); - if (auto unresolvedDot = dyn_cast(expr)) { - if (auto self = TypeChecker::getSelfForInitDelegationInConstructor( - DC, unresolvedDot)) { - // Walk our ancestor expressions looking for the appropriate place - // to insert the RebindSelfInConstructorExpr. - Expr *target = nullptr; - bool foundApply = false; - bool foundRebind = false; - for (auto ancestor : llvm::reverse(ExprStack)) { - if (isa(ancestor)) { - // If we already have a rebind, then we're re-typechecking an - // expression and are done. - foundRebind = true; - break; - } - - // Recognize applications. - if (auto apply = dyn_cast(ancestor)) { - // If we already saw an application, we're done. - if (foundApply) - break; - - // If the function being called is not our unresolved initializer - // reference, we're done. - if (apply->getSemanticFn() != unresolvedDot) - break; - - foundApply = true; - target = ancestor; - continue; - } - - // Look through identity, force-value, and 'try' expressions. - if (isa(ancestor) || - isa(ancestor) || - isa(ancestor)) { - if (!CallArgs.count(ancestor)) { - if (target) - target = ancestor; - continue; - } - } - - // No other expression kinds are permitted. - break; - } - - // If we found a rebind target, note the insertion point. - if (target && !foundRebind) { - UnresolvedCtorRebindTarget = target; - UnresolvedCtorSelf = self; - } - } - } - - // If the expression we've found is the intended target of an - // RebindSelfInConstructorExpr, wrap it in the - // RebindSelfInConstructorExpr. - if (expr == UnresolvedCtorRebindTarget) { - expr = new (ctx) - RebindSelfInConstructorExpr(expr, UnresolvedCtorSelf); - UnresolvedCtorRebindTarget = nullptr; - return expr; - } - - // Double check if there are any BindOptionalExpr remaining in the - // tree (see comment below for more details), if there are no BOE - // expressions remaining remove OptionalEvaluationExpr from the tree. - if (auto OEE = dyn_cast(expr)) { - bool hasBindOptional = false; - OEE->forEachChildExpr([&](Expr *expr) -> Expr * { - if (isa(expr)) - hasBindOptional = true; - // If at least a single BOE was found, no reason - // to walk any further in the tree. - return hasBindOptional ? nullptr : expr; - }); - - return hasBindOptional ? OEE : OEE->getSubExpr(); - } - - // Check if there are any BindOptionalExpr in the tree which - // wrap DiscardAssignmentExpr, such situation corresponds to syntax - // like - `_? = `, since it doesn't really make - // sense to have optional assignment to discarded LValue which can - // never be optional, we can remove BOE from the tree and avoid - // generating any of the unnecessary constraints. - if (auto BOE = dyn_cast(expr)) { - if (auto DAE = dyn_cast(BOE->getSubExpr())) - return DAE; - } - - // If this is a sugared type that needs to be folded into a single - // TypeExpr, do it. - if (auto *simplified = simplifyTypeExpr(expr)) - return simplified; - - if (auto KPE = dyn_cast(expr)) { - resolveKeyPathExpr(KPE); - return KPE; - } - - if (auto *simplified = simplifyTypeConstructionWithLiteralArg(expr)) - return simplified; - - // If we find an unresolved member chain, wrap it in an - // UnresolvedMemberChainResultExpr (unless this has already been done). - auto *parent = Parent.getAsExpr(); - if (isMemberChainTail(expr, parent)) - if (auto *UME = TypeChecker::getUnresolvedMemberChainBase(expr)) - if (!parent || !isa(parent)) - return new (ctx) UnresolvedMemberChainResultExpr(expr, UME); - - return expr; - } - - std::pair walkToStmtPre(Stmt *stmt) override { - return { true, stmt }; - } - }; -} // end anonymous namespace - -/// Perform prechecking of a ClosureExpr before we dive into it. This returns -/// true when we want the body to be considered part of this larger expression. -bool PreCheckExpression::walkToClosureExprPre(ClosureExpr *closure) { - auto *PL = closure->getParameters(); - - // Validate the parameters. - bool hadParameterError = false; - - // If we encounter an error validating the parameter list, don't bail. - // Instead, go on to validate any potential result type, and bail - // afterwards. This allows for better diagnostics, and keeps the - // closure expression type well-formed. - for (auto param : *PL) { - hadParameterError |= param->isInvalid(); - } - - if (hadParameterError) - return false; - - // If we won't be checking the body of the closure, don't walk into it here. - if (!shouldTypeCheckInEnclosingExpression(closure)) - return false; - - // Update the current DeclContext to be the closure we're about to - // recurse into. - assert((closure->getParent() == DC || - closure->getParent()->isChildContextOf(DC)) && - "Decl context isn't correct"); - DC = closure; - return true; -} - -TypeExpr *PreCheckExpression::simplifyNestedTypeExpr(UnresolvedDotExpr *UDE) { - if (!UDE->getName().isSimpleName() || - UDE->getName().isSpecial()) - return nullptr; - - auto Name = UDE->getName(); - auto NameLoc = UDE->getNameLoc().getBaseNameLoc(); - - // Qualified type lookup with a module base is represented as a DeclRefExpr - // and not a TypeExpr. - if (auto *DRE = dyn_cast(UDE->getBase())) { - if (auto *TD = dyn_cast(DRE->getDecl())) { - // See if the type has a member type with this name. - auto Result = TypeChecker::lookupMemberType( - DC, TD->getDeclaredInterfaceType(), Name, - defaultMemberLookupOptions); - - // If there is no nested type with this name, we have a lookup of - // a non-type member, so leave the expression as-is. - if (Result.size() == 1) { - return TypeExpr::createForMemberDecl( - DRE->getNameLoc(), TD, UDE->getNameLoc(), Result.front().Member); - } - } - - return nullptr; - } - - auto *TyExpr = dyn_cast(UDE->getBase()); - if (!TyExpr) - return nullptr; - - auto *InnerTypeRepr = TyExpr->getTypeRepr(); - if (!InnerTypeRepr) - return nullptr; - - // Fold 'T.Protocol' into a protocol metatype. - if (Name.isSimpleName(getASTContext().Id_Protocol)) { - auto *NewTypeRepr = - new (getASTContext()) ProtocolTypeRepr(InnerTypeRepr, NameLoc); - return new (getASTContext()) TypeExpr(NewTypeRepr); - } - - // Fold 'T.Type' into an existential metatype if 'T' is a protocol, - // or an ordinary metatype otherwise. - if (Name.isSimpleName(getASTContext().Id_Type)) { - auto *NewTypeRepr = - new (getASTContext()) MetatypeTypeRepr(InnerTypeRepr, NameLoc); - return new (getASTContext()) TypeExpr(NewTypeRepr); - } - - // Fold 'T.U' into a nested type. - if (auto *ITR = dyn_cast(InnerTypeRepr)) { - // Resolve the TypeRepr to get the base type for the lookup. - // Disable availability diagnostics here, because the final - // TypeRepr will be resolved again when generating constraints. - const auto options = - TypeResolutionOptions(TypeResolverContext::InExpression) | - TypeResolutionFlags::AllowUnavailable; - const auto resolution = - TypeResolution::forContextual(DC, options, [](auto unboundTy) { - // FIXME: Don't let unbound generic types escape type resolution. - // For now, just return the unbound generic type. - return unboundTy; - }); - const auto BaseTy = resolution.resolveType(InnerTypeRepr); - - if (BaseTy->mayHaveMembers()) { - // See if there is a member type with this name. - auto Result = - TypeChecker::lookupMemberType(DC, BaseTy, Name, - defaultMemberLookupOptions); - - // If there is no nested type with this name, we have a lookup of - // a non-type member, so leave the expression as-is. - if (Result.size() == 1) { - return TypeExpr::createForMemberDecl(ITR, UDE->getNameLoc(), - Result.front().Member); - } - } - } - - return nullptr; -} - -TypeExpr *PreCheckExpression::simplifyUnresolvedSpecializeExpr( - UnresolvedSpecializeExpr *us) { - // If this is a reference type a specialized type, form a TypeExpr. - // The base should be a TypeExpr that we already resolved. - if (auto *te = dyn_cast(us->getSubExpr())) { - if (auto *ITR = dyn_cast_or_null(te->getTypeRepr())) { - return TypeExpr::createForSpecializedDecl(ITR, - us->getUnresolvedParams(), - SourceRange(us->getLAngleLoc(), - us->getRAngleLoc()), - getASTContext()); - } - } - - return nullptr; -} - -/// Simplify expressions which are type sugar productions that got parsed -/// as expressions due to the parser not knowing which identifiers are -/// type names. -TypeExpr *PreCheckExpression::simplifyTypeExpr(Expr *E) { - // Don't try simplifying a call argument, because we don't want to - // simplify away the required ParenExpr/TupleExpr. - if (CallArgs.count(E) > 0) return nullptr; - - // Fold member types. - if (auto *UDE = dyn_cast(E)) { - return simplifyNestedTypeExpr(UDE); - } - - // Fold T? into an optional type when T is a TypeExpr. - if (isa(E) || isa(E)) { - TypeExpr *TyExpr; - SourceLoc QuestionLoc; - if (auto *OOE = dyn_cast(E)) { - TyExpr = dyn_cast(OOE->getSubExpr()); - QuestionLoc = OOE->getLoc(); - } else { - TyExpr = dyn_cast(cast(E)->getSubExpr()); - QuestionLoc = cast(E)->getQuestionLoc(); - } - if (!TyExpr) return nullptr; - - auto *InnerTypeRepr = TyExpr->getTypeRepr(); - assert(!TyExpr->isImplicit() && InnerTypeRepr && - "This doesn't work on implicit TypeExpr's, " - "the TypeExpr should have been built correctly in the first place"); - - // The optional evaluation is passed through. - if (isa(E)) - return TyExpr; - - auto *NewTypeRepr = - new (getASTContext()) OptionalTypeRepr(InnerTypeRepr, QuestionLoc); - return new (getASTContext()) TypeExpr(NewTypeRepr); - } - - // Fold T! into an IUO type when T is a TypeExpr. - if (auto *FVE = dyn_cast(E)) { - auto *TyExpr = dyn_cast(FVE->getSubExpr()); - if (!TyExpr) return nullptr; - - auto *InnerTypeRepr = TyExpr->getTypeRepr(); - assert(!TyExpr->isImplicit() && InnerTypeRepr && - "This doesn't work on implicit TypeExpr's, " - "the TypeExpr should have been built correctly in the first place"); - - auto *NewTypeRepr = new (getASTContext()) - ImplicitlyUnwrappedOptionalTypeRepr(InnerTypeRepr, - FVE->getExclaimLoc()); - return new (getASTContext()) TypeExpr(NewTypeRepr); - } - - // Fold (T) into a type T with parens around it. - if (auto *PE = dyn_cast(E)) { - auto *TyExpr = dyn_cast(PE->getSubExpr()); - if (!TyExpr) return nullptr; - - TupleTypeReprElement InnerTypeRepr[] = { TyExpr->getTypeRepr() }; - assert(!TyExpr->isImplicit() && InnerTypeRepr[0].Type && - "SubscriptExpr doesn't work on implicit TypeExpr's, " - "the TypeExpr should have been built correctly in the first place"); - - auto *NewTypeRepr = TupleTypeRepr::create(getASTContext(), InnerTypeRepr, - PE->getSourceRange()); - return new (getASTContext()) TypeExpr(NewTypeRepr); - } - - // Fold a tuple expr like (T1,T2) into a tuple type (T1,T2). - if (auto *TE = dyn_cast(E)) { - if (TE->hasTrailingClosure() || - // FIXME: Decide what to do about (). It could be a type or an expr. - TE->getNumElements() == 0) - return nullptr; - - SmallVector Elts; - unsigned EltNo = 0; - for (auto Elt : TE->getElements()) { - auto *eltTE = dyn_cast(Elt); - if (!eltTE) return nullptr; - TupleTypeReprElement elt; - assert(eltTE->getTypeRepr() && !eltTE->isImplicit() && - "This doesn't work on implicit TypeExpr's, the " - "TypeExpr should have been built correctly in the first place"); - - // If the tuple element has a label, propagate it. - elt.Type = eltTE->getTypeRepr(); - Identifier name = TE->getElementName(EltNo); - if (!name.empty()) { - elt.Name = name; - elt.NameLoc = TE->getElementNameLoc(EltNo); - } - - Elts.push_back(elt); - ++EltNo; - } - auto *NewTypeRepr = TupleTypeRepr::create( - getASTContext(), Elts, TE->getSourceRange(), SourceLoc(), Elts.size()); - return new (getASTContext()) TypeExpr(NewTypeRepr); - } - - - // Fold [T] into an array type. - if (auto *AE = dyn_cast(E)) { - if (AE->getElements().size() != 1) - return nullptr; - - auto *TyExpr = dyn_cast(AE->getElement(0)); - if (!TyExpr) - return nullptr; - - auto *NewTypeRepr = new (getASTContext()) - ArrayTypeRepr(TyExpr->getTypeRepr(), - SourceRange(AE->getLBracketLoc(), AE->getRBracketLoc())); - return new (getASTContext()) TypeExpr(NewTypeRepr); - } - - // Fold [K : V] into a dictionary type. - if (auto *DE = dyn_cast(E)) { - if (DE->getElements().size() != 1) - return nullptr; - - TypeRepr *keyTypeRepr, *valueTypeRepr; - - if (auto EltTuple = dyn_cast(DE->getElement(0))) { - auto *KeyTyExpr = dyn_cast(EltTuple->getElement(0)); - if (!KeyTyExpr) - return nullptr; - - auto *ValueTyExpr = dyn_cast(EltTuple->getElement(1)); - if (!ValueTyExpr) - return nullptr; - - keyTypeRepr = KeyTyExpr->getTypeRepr(); - valueTypeRepr = ValueTyExpr->getTypeRepr(); - } else { - auto *TE = dyn_cast(DE->getElement(0)); - if (!TE) return nullptr; - - auto *TRE = dyn_cast_or_null(TE->getTypeRepr()); - if (!TRE || TRE->getEllipsisLoc().isValid()) return nullptr; - while (TRE->isParenType()) { - TRE = dyn_cast_or_null(TRE->getElementType(0)); - if (!TRE || TRE->getEllipsisLoc().isValid()) return nullptr; - } - - assert(TRE->getElements().size() == 2); - keyTypeRepr = TRE->getElementType(0); - valueTypeRepr = TRE->getElementType(1); - } - - auto *NewTypeRepr = new (getASTContext()) DictionaryTypeRepr( - keyTypeRepr, valueTypeRepr, - /*FIXME:colonLoc=*/SourceLoc(), - SourceRange(DE->getLBracketLoc(), DE->getRBracketLoc())); - return new (getASTContext()) TypeExpr(NewTypeRepr); - } - - // Reinterpret arrow expr T1 -> T2 as function type. - // FIXME: support 'inout', etc. - if (auto *AE = dyn_cast(E)) { - if (!AE->isFolded()) return nullptr; - - auto diagnoseMissingParens = [](ASTContext &ctx, TypeRepr *tyR) { - bool isVoid = false; - if (const auto Void = dyn_cast(tyR)) { - if (Void->getNameRef().isSimpleName(ctx.Id_Void)) { - isVoid = true; - } - } - - if (isVoid) { - ctx.Diags.diagnose(tyR->getStartLoc(), diag::function_type_no_parens) - .fixItReplace(tyR->getStartLoc(), "()"); - } else { - ctx.Diags.diagnose(tyR->getStartLoc(), diag::function_type_no_parens) - .highlight(tyR->getSourceRange()) - .fixItInsert(tyR->getStartLoc(), "(") - .fixItInsertAfter(tyR->getEndLoc(), ")"); - } - }; - - auto &ctx = getASTContext(); - auto extractInputTypeRepr = [&](Expr *E) -> TupleTypeRepr * { - if (!E) - return nullptr; - if (auto *TyE = dyn_cast(E)) { - auto ArgRepr = TyE->getTypeRepr(); - if (auto *TTyRepr = dyn_cast(ArgRepr)) - return TTyRepr; - diagnoseMissingParens(ctx, ArgRepr); - return TupleTypeRepr::create(ctx, {ArgRepr}, ArgRepr->getSourceRange()); - } - if (auto *TE = dyn_cast(E)) - if (TE->getNumElements() == 0) - return TupleTypeRepr::createEmpty(getASTContext(), - TE->getSourceRange()); - - // When simplifying a type expr like "(P1 & P2) -> (P3 & P4) -> Int", - // it may have been folded at the same time; recursively simplify it. - if (auto ArgsTypeExpr = simplifyTypeExpr(E)) { - auto ArgRepr = ArgsTypeExpr->getTypeRepr(); - if (auto *TTyRepr = dyn_cast(ArgRepr)) - return TTyRepr; - diagnoseMissingParens(ctx, ArgRepr); - return TupleTypeRepr::create(ctx, {ArgRepr}, ArgRepr->getSourceRange()); - } - return nullptr; - }; - - auto extractTypeRepr = [&](Expr *E) -> TypeRepr * { - if (!E) - return nullptr; - if (auto *TyE = dyn_cast(E)) - return TyE->getTypeRepr(); - if (auto *TE = dyn_cast(E)) - if (TE->getNumElements() == 0) - return TupleTypeRepr::createEmpty(ctx, TE->getSourceRange()); - - // When simplifying a type expr like "P1 & P2 -> P3 & P4 -> Int", - // it may have been folded at the same time; recursively simplify it. - if (auto ArgsTypeExpr = simplifyTypeExpr(E)) - return ArgsTypeExpr->getTypeRepr(); - return nullptr; - }; - - TupleTypeRepr *ArgsTypeRepr = extractInputTypeRepr(AE->getArgsExpr()); - if (!ArgsTypeRepr) { - ctx.Diags.diagnose(AE->getArgsExpr()->getLoc(), - diag::expected_type_before_arrow); - auto ArgRange = AE->getArgsExpr()->getSourceRange(); - auto ErrRepr = new (ctx) ErrorTypeRepr(ArgRange); - ArgsTypeRepr = - TupleTypeRepr::create(ctx, {ErrRepr}, ArgRange); - } - - TypeRepr *ResultTypeRepr = extractTypeRepr(AE->getResultExpr()); - if (!ResultTypeRepr) { - ctx.Diags.diagnose(AE->getResultExpr()->getLoc(), - diag::expected_type_after_arrow); - ResultTypeRepr = new (ctx) - ErrorTypeRepr(AE->getResultExpr()->getSourceRange()); - } - - auto NewTypeRepr = new (ctx) - FunctionTypeRepr(nullptr, ArgsTypeRepr, AE->getAsyncLoc(), - AE->getThrowsLoc(), AE->getArrowLoc(), ResultTypeRepr); - return new (ctx) TypeExpr(NewTypeRepr); - } - - // Fold 'P & Q' into a composition type - if (auto *binaryExpr = dyn_cast(E)) { - bool isComposition = false; - // look at the name of the operator, if it is a '&' we can create the - // composition TypeExpr - auto fn = binaryExpr->getFn(); - if (auto Overload = dyn_cast(fn)) { - for (auto Decl : Overload->getDecls()) - if (Decl->getBaseName() == "&") { - isComposition = true; - break; - } - } else if (auto *Decl = dyn_cast(fn)) { - if (Decl->getName().isSimpleName() && - Decl->getName().getBaseName() == "&") - isComposition = true; - } - - if (isComposition) { - // The protocols we are composing - SmallVector Types; - - auto lhsExpr = binaryExpr->getArg()->getElement(0); - if (auto *lhs = dyn_cast(lhsExpr)) { - Types.push_back(lhs->getTypeRepr()); - } else if (isa(lhsExpr)) { - // If the lhs is another binary expression, we have a multi element - // composition: 'A & B & C' is parsed as ((A & B) & C); we get - // the protocols from the lhs here - if (auto expr = simplifyTypeExpr(lhsExpr)) - if (auto *repr = dyn_cast(expr->getTypeRepr())) - // add the protocols to our list - for (auto proto : repr->getTypes()) - Types.push_back(proto); - else - return nullptr; - else - return nullptr; - } else - return nullptr; - - // Add the rhs which is just a TypeExpr - auto *rhs = dyn_cast(binaryExpr->getArg()->getElement(1)); - if (!rhs) return nullptr; - Types.push_back(rhs->getTypeRepr()); - - auto CompRepr = CompositionTypeRepr::create(getASTContext(), Types, - lhsExpr->getStartLoc(), - binaryExpr->getSourceRange()); - return new (getASTContext()) TypeExpr(CompRepr); - } - } - - return nullptr; -} - -void PreCheckExpression::resolveKeyPathExpr(KeyPathExpr *KPE) { - if (KPE->isObjC()) - return; - - if (!KPE->getComponents().empty()) - return; - - TypeRepr *rootType = nullptr; - SmallVector components; - auto &DE = getASTContext().Diags; - - // Pre-order visit of a sequence foo.bar[0]?.baz, which means that the - // components are pushed in reverse order. - auto traversePath = [&](Expr *expr, bool isInParsedPath, - bool emitErrors = true) { - Expr *outermostExpr = expr; - // We can end up in scenarios where the key path has contextual type, - // but is missing a leading dot. This can happen when we have an - // implicit TypeExpr or an implicit DeclRefExpr. - auto diagnoseMissingDot = [&]() { - DE.diagnose(expr->getLoc(), - diag::expr_swift_keypath_not_starting_with_dot) - .fixItInsert(expr->getStartLoc(), "."); - }; - while (1) { - // Base cases: we've reached the top. - if (auto TE = dyn_cast(expr)) { - assert(!isInParsedPath); - rootType = TE->getTypeRepr(); - if (TE->isImplicit() && !KPE->expectsContextualRoot()) - diagnoseMissingDot(); - return; - } else if (isa(expr)) { - assert(isInParsedPath); - // Nothing here: the type is either the root, or is inferred. - return; - } else if (!KPE->expectsContextualRoot() && expr->isImplicit() && - isa(expr)) { - assert(!isInParsedPath); - diagnoseMissingDot(); - return; - } - - // Recurring cases: - if (auto SE = dyn_cast(expr)) { - // .self, the identity component. - components.push_back(KeyPathExpr::Component::forIdentity( - SE->getSelfLoc())); - expr = SE->getSubExpr(); - } else if (auto UDE = dyn_cast(expr)) { - // .foo - components.push_back(KeyPathExpr::Component::forUnresolvedProperty( - UDE->getName(), UDE->getLoc())); - - expr = UDE->getBase(); - } else if (auto SE = dyn_cast(expr)) { - // .[0] or just plain [0] - components.push_back( - KeyPathExpr::Component::forUnresolvedSubscriptWithPrebuiltIndexExpr( - getASTContext(), SE->getIndex(), SE->getArgumentLabels(), - SE->getLoc())); - - expr = SE->getBase(); - } else if (auto BOE = dyn_cast(expr)) { - // .? or ? - components.push_back(KeyPathExpr::Component::forUnresolvedOptionalChain( - BOE->getQuestionLoc())); - - expr = BOE->getSubExpr(); - } else if (auto FVE = dyn_cast(expr)) { - // .! or ! - components.push_back(KeyPathExpr::Component::forUnresolvedOptionalForce( - FVE->getExclaimLoc())); - - expr = FVE->getSubExpr(); - } else if (auto OEE = dyn_cast(expr)) { - // Do nothing: this is implied to exist as the last expression, by the - // BindOptionalExprs, but is irrelevant to the components. - (void)outermostExpr; - assert(OEE == outermostExpr); - expr = OEE->getSubExpr(); - } else { - if (emitErrors) { - // \() may be an attempt to write a string interpolation outside - // of a string literal; diagnose this case specially. - if (isa(expr) || isa(expr)) { - DE.diagnose(expr->getLoc(), - diag::expr_string_interpolation_outside_string); - } else { - DE.diagnose(expr->getLoc(), - diag::expr_swift_keypath_invalid_component); - } - } - components.push_back(KeyPathExpr::Component()); - return; - } - } - }; - - auto root = KPE->getParsedRoot(); - auto path = KPE->getParsedPath(); - - if (path) { - traversePath(path, /*isInParsedPath=*/true); - - // This path looks like \Foo.Bar.[0].baz, which means Foo.Bar has to be a - // type. - if (root) { - if (auto TE = dyn_cast(root)) { - rootType = TE->getTypeRepr(); - } else { - // FIXME: Probably better to catch this case earlier and force-eval as - // TypeExpr. - DE.diagnose(root->getLoc(), - diag::expr_swift_keypath_not_starting_with_type); - - // Traverse this path for recovery purposes: it may be a typo like - // \Foo.property.[0]. - traversePath(root, /*isInParsedPath=*/false, - /*emitErrors=*/false); - } - } - } else { - traversePath(root, /*isInParsedPath=*/false); - } - - // Key paths must be spelled with at least one component. - if (components.empty()) { - // Passes further down the pipeline expect keypaths to always have at least - // one component, so stuff an invalid component in the AST for recovery. - components.push_back(KeyPathExpr::Component()); - } - - std::reverse(components.begin(), components.end()); - - KPE->setRootType(rootType); - KPE->resolveComponents(getASTContext(), components); -} - -Expr *PreCheckExpression::simplifyTypeConstructionWithLiteralArg(Expr *E) { - // If constructor call is expected to produce an optional let's not attempt - // this optimization because literal initializers aren't failable. - if (!getASTContext().LangOpts.isSwiftVersionAtLeast(5)) { - if (!ExprStack.empty()) { - auto *parent = ExprStack.back(); - if (isa(parent) || isa(parent)) - return nullptr; - } - } - - auto *call = dyn_cast(E); - if (!call || call->getNumArguments() != 1) - return nullptr; - - auto *typeExpr = dyn_cast(call->getFn()); - if (!typeExpr) - return nullptr; - - auto *argExpr = call->getArg()->getSemanticsProvidingExpr(); - auto *literal = dyn_cast(argExpr); - if (!literal) - return nullptr; - - auto *protocol = TypeChecker::getLiteralProtocol(getASTContext(), literal); - if (!protocol) - return nullptr; - - Type castTy; - if (auto precheckedTy = typeExpr->getInstanceType()) { - castTy = precheckedTy; - } else { - const auto options = - TypeResolutionOptions(TypeResolverContext::InExpression) | - TypeResolutionFlags::SilenceErrors; - - const auto resolution = - TypeResolution::forContextual(DC, options, [](auto unboundTy) { - // FIXME: Don't let unbound generic types escape type resolution. - // For now, just return the unbound generic type. - return unboundTy; - }); - const auto result = resolution.resolveType(typeExpr->getTypeRepr()); - if (result->hasError()) - return nullptr; - castTy = result; - } - - if (!castTy || !castTy->getAnyNominal()) - return nullptr; - - // Don't bother to convert deprecated selector syntax. - if (auto selectorTy = getASTContext().getSelectorType()) { - if (castTy->isEqual(selectorTy)) - return nullptr; - } - - SmallVector conformances; - return castTy->getAnyNominal()->lookupConformance(DC->getParentModule(), - protocol, conformances) - ? CoerceExpr::forLiteralInit(getASTContext(), argExpr, - call->getSourceRange(), - typeExpr->getTypeRepr()) - : nullptr; -} - -/// Pre-check the expression, validating any types that occur in the -/// expression and folding sequence expressions. -bool ConstraintSystem::preCheckExpression(Expr *&expr, DeclContext *dc, - bool replaceInvalidRefsWithErrors) { - PreCheckExpression preCheck(dc, expr, replaceInvalidRefsWithErrors); - // Perform the pre-check. - if (auto result = expr->walk(preCheck)) { - expr = result; - return false; - } - return true; -} - void ParentConditionalConformance::diagnoseConformanceStack( DiagnosticEngine &diags, SourceLoc loc, ArrayRef conformances) { @@ -2045,7 +222,7 @@ void ParentConditionalConformance::diagnoseConformanceStack( namespace { /// Produce any additional syntactic diagnostics for the body of a function -/// that had a function builder applied. +/// that had a result builder applied. class FunctionSyntacticDiagnosticWalker : public ASTWalker { SmallVector dcStack; @@ -2077,7 +254,7 @@ class FunctionSyntacticDiagnosticWalker : public ASTWalker { } std::pair walkToStmtPre(Stmt *stmt) override { - performStmtDiagnostics(dcStack.back()->getASTContext(), stmt); + performStmtDiagnostics(stmt, dcStack.back()); return {true, stmt}; } @@ -2123,11 +300,10 @@ void constraints::performSyntacticDiagnosticsForTarget( #pragma mark High-level entry points Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc, - Type convertType, - ContextualTypePurpose convertTypePurpose, + ContextualTypeInfo contextualInfo, TypeCheckExprOptions options) { SolutionApplicationTarget target( - expr, dc, convertTypePurpose, convertType, + expr, dc, contextualInfo.purpose, contextualInfo.getType(), options.contains(TypeCheckExprFlags::IsDiscarded)); auto resultTarget = typeCheckExpression(target, options); if (!resultTarget) { @@ -2245,9 +421,10 @@ Type TypeChecker::typeCheckParameterDefault(Expr *&defaultValue, DeclContext *DC, Type paramType, bool isAutoClosure) { assert(paramType && !paramType->hasError()); - return typeCheckExpression( - defaultValue, DC, paramType, - isAutoClosure ? CTP_AutoclosureDefaultParameter : CTP_DefaultParameter); + return typeCheckExpression(defaultValue, DC, /*contextualInfo=*/ + {paramType, isAutoClosure + ? CTP_AutoclosureDefaultParameter + : CTP_DefaultParameter}); } bool TypeChecker::typeCheckBinding( @@ -2416,7 +593,8 @@ bool TypeChecker::typeCheckCondition(Expr *&expr, DeclContext *dc) { // If this expression is already typechecked and has type Bool, then just // re-typecheck it. if (expr->getType() && expr->getType()->isBool()) { - auto resultTy = TypeChecker::typeCheckExpression(expr, dc); + auto resultTy = + TypeChecker::typeCheckExpression(expr, dc); return !resultTy; } @@ -2425,8 +603,8 @@ bool TypeChecker::typeCheckCondition(Expr *&expr, DeclContext *dc) { return true; auto resultTy = TypeChecker::typeCheckExpression( - expr, dc, boolDecl->getDeclaredInterfaceType(), - CTP_Condition); + expr, dc, + /*contextualInfo=*/{boolDecl->getDeclaredInterfaceType(), CTP_Condition}); return !resultTy; } @@ -2957,7 +1135,7 @@ void ConstraintSystem::print(raw_ostream &out) const { out << " as "; Type(fixed).print(out, PO); } else { - inferBindingsFor(tv).dump(out, 1); + const_cast(this)->inferBindingsFor(tv).dump(out, 1); } } else { out << " equivalent to "; diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index c0b80def44141..d0ac96b22ea22 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -15,7 +15,6 @@ //===----------------------------------------------------------------------===// #include "CodeSynthesis.h" -#include "ConstraintSystem.h" #include "DerivedConformances.h" #include "TypeChecker.h" #include "TypeCheckAccess.h" @@ -233,24 +232,6 @@ static bool canSkipCircularityCheck(NominalTypeDecl *decl) { return decl->hasClangNode() || decl->wasDeserialized(); } -bool -HasCircularInheritanceRequest::evaluate(Evaluator &evaluator, - ClassDecl *decl) const { - if (canSkipCircularityCheck(decl) || !decl->hasSuperclass()) - return false; - - auto *superclass = decl->getSuperclassDecl(); - auto result = evaluator(HasCircularInheritanceRequest{superclass}); - - // If we have a cycle, handle it and return true. - if (!result) { - using Error = CyclicalRequestError; - llvm::handleAllErrors(result.takeError(), [](const Error &E) {}); - return true; - } - return result.get(); -} - bool HasCircularInheritedProtocolsRequest::evaluate(Evaluator &evaluator, ProtocolDecl *decl) const { @@ -472,6 +453,160 @@ InitKindRequest::evaluate(Evaluator &evaluator, ConstructorDecl *decl) const { return CtorInitializerKind::Designated; } +BodyInitKindAndExpr +BodyInitKindRequest::evaluate(Evaluator &evaluator, + ConstructorDecl *decl) const { + + struct FindReferenceToInitializer : ASTWalker { + const ConstructorDecl *Decl; + BodyInitKind Kind = BodyInitKind::None; + ApplyExpr *InitExpr = nullptr; + ASTContext &ctx; + + FindReferenceToInitializer(const ConstructorDecl *decl, + ASTContext &ctx) + : Decl(decl), ctx(ctx) { } + + bool walkToDeclPre(class Decl *D) override { + // Don't walk into further nominal decls. + return !isa(D); + } + + std::pair walkToExprPre(Expr *E) override { + // Don't walk into closures. + if (isa(E)) + return { false, E }; + + // Look for calls of a constructor on self or super. + auto apply = dyn_cast(E); + if (!apply) + return { true, E }; + + auto Callee = apply->getSemanticFn(); + + Expr *arg; + + if (isa(Callee)) { + arg = apply->getArg(); + } else if (auto *CRE = dyn_cast(Callee)) { + arg = CRE->getArg(); + } else if (auto *dotExpr = dyn_cast(Callee)) { + if (dotExpr->getName().getBaseName() != DeclBaseName::createConstructor()) + return { true, E }; + + arg = dotExpr->getBase(); + } else { + // Not a constructor call. + return { true, E }; + } + + // Look for a base of 'self' or 'super'. + arg = arg->getSemanticsProvidingExpr(); + + auto myKind = BodyInitKind::None; + if (arg->isSuperExpr()) + myKind = BodyInitKind::Chained; + else if (arg->isSelfExprOf(Decl, /*sameBase*/true)) + myKind = BodyInitKind::Delegating; + else if (auto *declRef = dyn_cast(arg)) { + // FIXME: We can see UnresolvedDeclRefExprs here because we have + // not yet run preCheckExpression() on the entire function body + // yet. + // + // We could consider pre-checking more eagerly. + auto name = declRef->getName(); + auto loc = declRef->getLoc(); + if (name.isSimpleName(ctx.Id_self)) { + auto *otherSelfDecl = + ASTScope::lookupSingleLocalDecl(Decl->getParentSourceFile(), + name.getFullName(), loc); + if (otherSelfDecl == Decl->getImplicitSelfDecl()) + myKind = BodyInitKind::Delegating; + } + } + + if (myKind == BodyInitKind::None) + return { true, E }; + + if (Kind == BodyInitKind::None) { + Kind = myKind; + + InitExpr = apply; + return { true, E }; + } + + // If the kind changed, complain. + if (Kind != myKind) { + // The kind changed. Complain. + ctx.Diags.diagnose(E->getLoc(), diag::init_delegates_and_chains); + ctx.Diags.diagnose(InitExpr->getLoc(), diag::init_delegation_or_chain, + Kind == BodyInitKind::Chained); + } + + return { true, E }; + } + }; + + auto &ctx = decl->getASTContext(); + FindReferenceToInitializer finder(decl, ctx); + decl->getBody()->walk(finder); + + // get the kind out of the finder. + auto Kind = finder.Kind; + + auto *NTD = decl->getDeclContext()->getSelfNominalTypeDecl(); + + // Protocol extension and enum initializers are always delegating. + if (Kind == BodyInitKind::None) { + if (isa(NTD) || isa(NTD)) { + Kind = BodyInitKind::Delegating; + } + } + + // Struct initializers that cannot see the layout of the struct type are + // always delegating. This occurs if the struct type is not fixed layout, + // and the constructor is either inlinable or defined in another module. + if (Kind == BodyInitKind::None && isa(NTD)) { + // Note: This is specifically not using isFormallyResilient. We relax this + // rule for structs in non-resilient modules so that they can have inlinable + // constructors, as long as those constructors don't reference private + // declarations. + if (NTD->isResilient() && + decl->getResilienceExpansion() == ResilienceExpansion::Minimal) { + Kind = BodyInitKind::Delegating; + + } else if (isa(decl->getDeclContext())) { + // Prior to Swift 5, cross-module initializers were permitted to be + // non-delegating. However, if the struct isn't fixed-layout, we have to + // be delegating because, well, we don't know the layout. + // A dynamic replacement is permitted to be non-delegating. + if (NTD->isResilient() || + (ctx.isSwiftVersionAtLeast(5) && + !decl->getAttrs().getAttribute())) { + if (decl->getParentModule() != NTD->getParentModule()) + Kind = BodyInitKind::Delegating; + } + } + } + + // If we didn't find any delegating or chained initializers, check whether + // the initializer was explicitly marked 'convenience'. + if (Kind == BodyInitKind::None && + decl->getAttrs().hasAttribute()) + Kind = BodyInitKind::Delegating; + + // If we still don't know, check whether we have a class with a superclass: it + // gets an implicit chained initializer. + if (Kind == BodyInitKind::None) { + if (auto classDecl = decl->getDeclContext()->getSelfClassDecl()) { + if (classDecl->hasSuperclass()) + Kind = BodyInitKind::ImplicitChained; + } + } + + return BodyInitKindAndExpr(Kind, finder.InitExpr); +} + bool ProtocolRequiresClassRequest::evaluate(Evaluator &evaluator, ProtocolDecl *decl) const { @@ -1027,7 +1162,7 @@ EnumRawValuesRequest::evaluate(Evaluator &eval, EnumDecl *ED, if (uncheckedRawValueOf(elt)) { if (!uncheckedRawValueOf(elt)->isImplicit()) lastExplicitValueElt = elt; - } else if (!ED->LazySemanticInfo.hasFixedRawValues()) { + } else if (!ED->SemanticFlags.contains(EnumDecl::HasFixedRawValues)) { // Try to pull out the automatic enum value kind. If that fails, bail. if (!valueKind) { valueKind = computeAutomaticEnumValueKind(ED); @@ -1063,9 +1198,9 @@ EnumRawValuesRequest::evaluate(Evaluator &eval, EnumDecl *ED, { Expr *exprToCheck = prevValue; - if (TypeChecker::typeCheckExpression(exprToCheck, ED, - rawTy, - CTP_EnumCaseRawValue)) { + if (TypeChecker::typeCheckExpression( + exprToCheck, ED, + /*contextualInfo=*/{rawTy, CTP_EnumCaseRawValue})) { TypeChecker::checkEnumElementEffects(elt, exprToCheck); } } @@ -1082,7 +1217,7 @@ EnumRawValuesRequest::evaluate(Evaluator &eval, EnumDecl *ED, // to have set things up correctly. This comes up with imported enums // and deserialized @objc enums which always have their raw values setup // beforehand. - if (ED->LazySemanticInfo.hasFixedRawValues()) + if (ED->SemanticFlags.contains(EnumDecl::HasFixedRawValues)) continue; // Using magic literals like #file as raw value is not supported right now. @@ -1138,73 +1273,6 @@ EnumRawValuesRequest::evaluate(Evaluator &eval, EnumDecl *ED, return std::make_tuple<>(); } -bool SimpleDidSetRequest::evaluate(Evaluator &evaluator, - AccessorDecl *decl) const { - - class OldValueFinder : public ASTWalker { - const ParamDecl *OldValueParam; - bool foundOldValueRef = false; - - public: - OldValueFinder(const ParamDecl *param) : OldValueParam(param) {} - - virtual std::pair walkToExprPre(Expr *E) override { - if (!E) - return {true, E}; - if (auto DRE = dyn_cast(E)) { - if (auto decl = DRE->getDecl()) { - if (decl == OldValueParam) { - foundOldValueRef = true; - return {false, nullptr}; - } - } - } - - return {true, E}; - } - - bool didFindOldValueRef() { return foundOldValueRef; } - }; - - // If this is not a didSet accessor, bail out. - if (decl->getAccessorKind() != AccessorKind::DidSet) { - return false; - } - - // Always assume non-simple 'didSet' in code completion mode. - if (decl->getASTContext().SourceMgr.hasCodeCompletionBuffer()) - return false; - - // didSet must have a single parameter. - if (decl->getParameters()->size() != 1) { - return false; - } - - auto param = decl->getParameters()->get(0); - // If this parameter is not implicit, then it means it has been explicitly - // provided by the user (i.e. 'didSet(oldValue)'). This means we cannot - // consider this a "simple" didSet because we have to fetch the oldValue - // regardless of whether it's referenced in the body or not. - if (!param->isImplicit()) { - return false; - } - - // If we find a reference to the implicit 'oldValue' parameter, then it is - // not a "simple" didSet because we need to fetch it. - auto walker = OldValueFinder(param); - decl->getTypecheckedBody()->walk(walker); - auto hasOldValueRef = walker.didFindOldValueRef(); - if (!hasOldValueRef) { - // If the body does not refer to implicit 'oldValue', it means we can - // consider this as a "simple" didSet. Let's also erase the implicit - // oldValue as it is never used. - auto &ctx = decl->getASTContext(); - decl->setParameters(ParameterList::createEmpty(ctx)); - return true; - } - return false; -} - const ConstructorDecl * swift::findNonImplicitRequiredInit(const ConstructorDecl *CD) { while (CD->isImplicit()) { @@ -2121,6 +2189,9 @@ static Type validateParameterType(ParamDecl *decl) { TypeResolverContext::FunctionInput); options |= TypeResolutionFlags::Direct; + if (dc->isInSpecializeExtensionContext()) + options |= TypeResolutionFlags::AllowUsableFromInline; + const auto resolution = TypeResolution::forInterface(dc, options, unboundTyOpener); auto Ty = resolution.resolveType(decl->getTypeRepr()); @@ -2550,6 +2621,15 @@ SemanticMembersRequest::evaluate(Evaluator &evaluator, for (auto *member : idc->getMembers()) { if (auto *afd = dyn_cast(member)) { + // If this is a witness to Actor.enqueue(partialTask:), put it at the + // beginning of the vtable. + if (auto func = dyn_cast(afd)) { + if (func->isActorEnqueuePartialTaskWitness()) { + result.insert(result.begin(), func); + continue; + } + } + // Add synthesized members to a side table and sort them by their mangled // name, since they could have been added to the class in any order. if (afd->isSynthesized()) { @@ -2645,9 +2725,11 @@ ExtendedTypeRequest::evaluate(Evaluator &eval, ExtensionDecl *ext) const { return error(); // Compute the extended type. - const TypeResolutionOptions options(TypeResolverContext::ExtensionBinding); + TypeResolutionOptions options(TypeResolverContext::ExtensionBinding); + if (ext->isInSpecializeExtensionContext()) + options |= TypeResolutionFlags::AllowUsableFromInline; const auto resolution = TypeResolution::forStructural( - ext->getDeclContext(), TypeResolverContext::ExtensionBinding, + ext->getDeclContext(), options, [](auto unboundTy) { // FIXME: Don't let unbound generic types escape type resolution. // For now, just return the unbound generic type. diff --git a/lib/Sema/TypeCheckDecl.h b/lib/Sema/TypeCheckDecl.h index 5f3a01c8348fb..5bb835b9e3585 100644 --- a/lib/Sema/TypeCheckDecl.h +++ b/lib/Sema/TypeCheckDecl.h @@ -33,6 +33,7 @@ const ConstructorDecl *findNonImplicitRequiredInit(const ConstructorDecl *CD); // Implemented in TypeCheckDeclOverride.cpp bool checkOverrides(ValueDecl *decl); +void checkImplementationOnlyOverride(const ValueDecl *VD); // Implemented in TypeCheckStorage.cpp void setBoundVarsTypeError(Pattern *pattern, ASTContext &ctx); diff --git a/lib/Sema/TypeCheckDeclObjC.cpp b/lib/Sema/TypeCheckDeclObjC.cpp index 1d99cc1b9cc91..287acfc7708dd 100644 --- a/lib/Sema/TypeCheckDeclObjC.cpp +++ b/lib/Sema/TypeCheckDeclObjC.cpp @@ -16,6 +16,7 @@ //===----------------------------------------------------------------------===// #include "TypeCheckObjC.h" #include "TypeChecker.h" +#include "TypeCheckConcurrency.h" #include "TypeCheckProtocol.h" #include "swift/AST/ASTContext.h" #include "swift/AST/Decl.h" @@ -368,6 +369,36 @@ static bool checkObjCInForeignClassContext(const ValueDecl *VD, return true; } +/// Actor-isolated declarations cannot be @objc. +static bool checkObjCActorIsolation(const ValueDecl *VD, + ObjCReason Reason) { + // Check actor isolation. + bool Diagnose = shouldDiagnoseObjCReason(Reason, VD->getASTContext()); + + switch (auto restriction = ActorIsolationRestriction::forDeclaration( + const_cast(VD))) { + case ActorIsolationRestriction::ActorSelf: + // Actor-isolated functions cannot be @objc. + if (Diagnose) { + VD->diagnose( + diag::actor_isolated_objc, VD->getDescriptiveKind(), VD->getName()); + describeObjCReason(VD, Reason); + if (auto FD = dyn_cast(VD)) { + addAsyncNotes(const_cast(FD)); + } + } + return true; + + case ActorIsolationRestriction::GlobalActor: + // FIXME: Consider whether to limit @objc on global-actor-qualified + // declarations. + case ActorIsolationRestriction::Unrestricted: + case ActorIsolationRestriction::LocalCapture: + case ActorIsolationRestriction::Unsafe: + return false; + } +} + static VersionRange getMinOSVersionForClassStubs(const llvm::Triple &target) { if (target.isMacOSX()) return VersionRange::allGTE(llvm::VersionTuple(10, 15, 0)); @@ -512,6 +543,8 @@ bool swift::isRepresentableInObjC( return false; if (checkObjCInExtensionContext(AFD, Diagnose)) return false; + if (checkObjCActorIsolation(AFD, Reason)) + return false; if (AFD->isOperator()) { AFD->diagnose((isa(AFD->getDeclContext()) @@ -686,10 +719,12 @@ bool swift::isRepresentableInObjC( Type resultType = FD->mapTypeIntoContext(FD->getResultInterfaceType()); if (auto tupleType = resultType->getAs()) { for (const auto &tupleElt : tupleType->getElements()) { - addCompletionHandlerParam(tupleElt.getType()); + if (addCompletionHandlerParam(tupleElt.getType())) + return false; } } else { - addCompletionHandlerParam(resultType); + if (addCompletionHandlerParam(resultType)) + return false; } // For a throwing asynchronous function, an Error? parameter is added @@ -939,6 +974,8 @@ bool swift::isRepresentableInObjC(const VarDecl *VD, ObjCReason Reason) { if (checkObjCInForeignClassContext(VD, Reason)) return false; + if (checkObjCActorIsolation(VD, Reason)) + return false; if (!Diagnose || Result) return Result; @@ -967,6 +1004,8 @@ bool swift::isRepresentableInObjC(const SubscriptDecl *SD, ObjCReason Reason) { return false; if (checkObjCWithGenericParams(SD, Reason)) return false; + if (checkObjCActorIsolation(SD, Reason)) + return false; // ObjC doesn't support class subscripts. if (!SD->isInstanceMember()) { @@ -2337,12 +2376,22 @@ bool swift::diagnoseObjCUnsatisfiedOptReqConflicts(SourceFile &sf) { if (conflicts.empty()) continue; + auto bestConflict = conflicts[0]; + for (auto conflict : conflicts) { + if (conflict->getName().isCompoundName() && + conflict->getName().getArgumentNames().size() == + req->getName().getArgumentNames().size()) { + bestConflict = conflict; + break; + } + } + // Diagnose the conflict. auto reqDiagInfo = getObjCMethodDiagInfo(unsatisfied.second); - auto conflictDiagInfo = getObjCMethodDiagInfo(conflicts[0]); + auto conflictDiagInfo = getObjCMethodDiagInfo(bestConflict); auto protocolName = cast(req->getDeclContext())->getName(); - Ctx.Diags.diagnose(conflicts[0], + Ctx.Diags.diagnose(bestConflict, diag::objc_optional_requirement_conflict, conflictDiagInfo.first, conflictDiagInfo.second, @@ -2352,9 +2401,9 @@ bool swift::diagnoseObjCUnsatisfiedOptReqConflicts(SourceFile &sf) { protocolName); // Fix the name of the witness, if we can. - if (req->getName() != conflicts[0]->getName() && - req->getKind() == conflicts[0]->getKind() && - isa(req) == isa(conflicts[0])) { + if (req->getName() != bestConflict->getName() && + req->getKind() == bestConflict->getKind() && + isa(req) == isa(bestConflict)) { // They're of the same kind: fix the name. unsigned kind; if (isa(req)) @@ -2367,29 +2416,29 @@ bool swift::diagnoseObjCUnsatisfiedOptReqConflicts(SourceFile &sf) { llvm_unreachable("unhandled @objc declaration kind"); } - auto diag = Ctx.Diags.diagnose(conflicts[0], + auto diag = Ctx.Diags.diagnose(bestConflict, diag::objc_optional_requirement_swift_rename, kind, req->getName()); // Fix the Swift name. - fixDeclarationName(diag, conflicts[0], req->getName()); + fixDeclarationName(diag, bestConflict, req->getName()); // Fix the '@objc' attribute, if needed. - if (!conflicts[0]->canInferObjCFromRequirement(req)) - fixDeclarationObjCName(diag, conflicts[0], - conflicts[0]->getObjCRuntimeName(), + if (!bestConflict->canInferObjCFromRequirement(req)) + fixDeclarationObjCName(diag, bestConflict, + bestConflict->getObjCRuntimeName(), req->getObjCRuntimeName(), /*ignoreImpliedName=*/true); } // @nonobjc will silence this warning. bool hasExplicitObjCAttribute = false; - if (auto objcAttr = conflicts[0]->getAttrs().getAttribute()) + if (auto objcAttr = bestConflict->getAttrs().getAttribute()) hasExplicitObjCAttribute = !objcAttr->isImplicit(); if (!hasExplicitObjCAttribute) - Ctx.Diags.diagnose(conflicts[0], diag::req_near_match_nonobjc, true) + Ctx.Diags.diagnose(bestConflict, diag::req_near_match_nonobjc, true) .fixItInsert( - conflicts[0]->getAttributeInsertionLoc(/*forModifier=*/false), + bestConflict->getAttributeInsertionLoc(/*forModifier=*/false), "@nonobjc "); Ctx.Diags.diagnose(getDeclContextLoc(unsatisfied.first), diff --git a/lib/Sema/TypeCheckDeclOverride.cpp b/lib/Sema/TypeCheckDeclOverride.cpp index 6be42e50c9e10..7075f475f8dbf 100644 --- a/lib/Sema/TypeCheckDeclOverride.cpp +++ b/lib/Sema/TypeCheckDeclOverride.cpp @@ -1472,6 +1472,7 @@ namespace { UNINTERESTING_ATTR(SwiftNativeObjCRuntimeBase) UNINTERESTING_ATTR(ShowInInterface) UNINTERESTING_ATTR(Specialize) + UNINTERESTING_ATTR(SpecializeExtension) UNINTERESTING_ATTR(DynamicReplacement) UNINTERESTING_ATTR(PrivateImport) UNINTERESTING_ATTR(MainType) @@ -1513,11 +1514,12 @@ namespace { UNINTERESTING_ATTR(Custom) UNINTERESTING_ATTR(PropertyWrapper) UNINTERESTING_ATTR(DisfavoredOverload) - UNINTERESTING_ATTR(FunctionBuilder) + UNINTERESTING_ATTR(ResultBuilder) UNINTERESTING_ATTR(ProjectedValueProperty) UNINTERESTING_ATTR(OriginallyDefinedIn) UNINTERESTING_ATTR(Actor) UNINTERESTING_ATTR(ActorIndependent) + UNINTERESTING_ATTR(GlobalActor) #undef UNINTERESTING_ATTR void visitAvailableAttr(AvailableAttr *attr) { @@ -2158,3 +2160,53 @@ bool IsABICompatibleOverrideRequest::evaluate(Evaluator &evaluator, return derivedInterfaceTy->matches(overrideInterfaceTy, TypeMatchFlags::AllowABICompatible); } + +void swift::checkImplementationOnlyOverride(const ValueDecl *VD) { + if (VD->isImplicit()) + return; + + if (VD->getAttrs().hasAttribute()) + return; + + if (isa(VD)) + return; + + // Is this part of the module's API or ABI? + AccessScope accessScope = + VD->getFormalAccessScope(nullptr, + /*treatUsableFromInlineAsPublic*/true); + if (!accessScope.isPublic()) + return; + + const ValueDecl *overridden = VD->getOverriddenDecl(); + if (!overridden) + return; + + auto *SF = VD->getDeclContext()->getParentSourceFile(); + assert(SF && "checking a non-source declaration?"); + + ModuleDecl *M = overridden->getModuleContext(); + if (SF->isImportedImplementationOnly(M)) { + VD->diagnose(diag::implementation_only_override_import_without_attr, + overridden->getDescriptiveKind()) + .fixItInsert(VD->getAttributeInsertionLoc(false), + "@_implementationOnly "); + overridden->diagnose(diag::overridden_here); + return; + } + + if (overridden->getAttrs().hasAttribute()) { + VD->diagnose(diag::implementation_only_override_without_attr, + overridden->getDescriptiveKind()) + .fixItInsert(VD->getAttributeInsertionLoc(false), + "@_implementationOnly "); + overridden->diagnose(diag::overridden_here); + return; + } + + // FIXME: Check storage decls where the setter is in a separate module from + // the getter, which is a thing Objective-C can do. The ClangImporter + // doesn't make this easy, though, because it just gives the setter the same + // DeclContext as the property or subscript, which means we've lost the + // information about whether its module was implementation-only imported. +} diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp index ead29eca35c4b..fa1163a9036e9 100644 --- a/lib/Sema/TypeCheckDeclPrimary.cpp +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -17,10 +17,10 @@ //===----------------------------------------------------------------------===// #include "CodeSynthesis.h" -#include "ConstraintSystem.h" #include "DerivedConformances.h" #include "TypeChecker.h" #include "TypeCheckAccess.h" +#include "TypeCheckConcurrency.h" #include "TypeCheckDecl.h" #include "TypeCheckAvailability.h" #include "TypeCheckObjC.h" @@ -397,6 +397,28 @@ static void checkForEmptyOptionSet(const VarDecl *VD) { .fixItReplace(args->getSourceRange(), "([])"); } +template +static void diagnoseDuplicateDecls(const T &decls) { + llvm::SmallDenseMap names; + for (auto *current : decls) { + if (!current->getASTContext().LangOpts.DisableParserLookup) + return; + + if (!current->hasName() || current->isImplicit()) + continue; + + auto found = names.insert(std::make_pair(current->getBaseName(), current)); + if (!found.second) { + auto *other = found.first->second; + + current->getASTContext().Diags.diagnoseWithNotes( + current->diagnose(diag::invalid_redecl, + current->getName()), [&]() { + other->diagnose(diag::invalid_redecl_prev, other->getName()); + }); + } + } +} /// Check the inheritance clauses generic parameters along with any /// requirements stored within the generic parameter list. @@ -410,10 +432,13 @@ static void checkGenericParams(GenericContext *ownerCtx) { checkInheritanceClause(gp); } - // Force visitation of each of the requirements here. + // Force resolution of interface types written in requirements here. WhereClauseOwner(ownerCtx) .visitRequirements(TypeResolutionStage::Interface, [](Requirement, RequirementRepr *) { return false; }); + + // Check for duplicate generic parameter names. + diagnoseDuplicateDecls(*genericParams); } template @@ -489,9 +514,11 @@ CheckRedeclarationRequest::evaluate(Evaluator &eval, ValueDecl *current) const { // FIXME: Should restrict this to the source file we care about. DeclContext *currentDC = current->getDeclContext(); SourceFile *currentFile = currentDC->getParentSourceFile(); - if (!currentFile || currentDC->isLocalContext()) + if (!currentFile) return std::make_tuple<>(); + auto &ctx = current->getASTContext(); + // Find other potential definitions. SmallVector otherDefinitions; if (currentDC->isTypeContext()) { @@ -500,7 +527,17 @@ CheckRedeclarationRequest::evaluate(Evaluator &eval, ValueDecl *current) const { auto found = nominal->lookupDirect(current->getBaseName()); otherDefinitions.append(found.begin(), found.end()); } + } else if (currentDC->isLocalContext()) { + if (ctx.LangOpts.DisableParserLookup) { + if (!current->isImplicit()) { + ASTScope::lookupLocalDecls(currentFile, current->getBaseName(), + current->getLoc(), + /*stopAfterInnermostBraceStmt=*/true, + otherDefinitions); + } + } } else { + assert(currentDC->isModuleScopeContext()); // Look within a module context. currentFile->getParentModule()->lookupValue(current->getBaseName(), NLKind::QualifiedLookup, @@ -512,7 +549,6 @@ CheckRedeclarationRequest::evaluate(Evaluator &eval, ValueDecl *current) const { OverloadSignature currentSig = current->getOverloadSignature(); CanType currentSigType = current->getOverloadSignatureType(); ModuleDecl *currentModule = current->getModuleContext(); - auto &ctx = current->getASTContext(); for (auto other : otherDefinitions) { // Skip invalid declarations and ourselves. // @@ -536,6 +572,11 @@ CheckRedeclarationRequest::evaluate(Evaluator &eval, ValueDecl *current) const { if (currentDC->isTypeContext() != other->getDeclContext()->isTypeContext()) continue; + // In local context, only consider exact name matches. + if (currentDC->isLocalContext() && + current->getName() != other->getName()) + continue; + // Check whether the overload signatures conflict (ignoring the type for // now). auto otherSig = other->getOverloadSignature(); @@ -547,7 +588,8 @@ CheckRedeclarationRequest::evaluate(Evaluator &eval, ValueDecl *current) const { // shadows a non-private one, but only in the file where the shadowing // happens. We will warn on conflicting non-private declarations in both // files. - if (!other->isAccessibleFrom(currentDC)) + if (!currentDC->isLocalContext() && + !other->isAccessibleFrom(currentDC)) continue; // Skip invalid declarations. @@ -573,18 +615,6 @@ CheckRedeclarationRequest::evaluate(Evaluator &eval, ValueDecl *current) const { &wouldBeSwift5Redeclaration); // If there is another conflict, complain. if (isRedeclaration || wouldBeSwift5Redeclaration) { - // If the two declarations occur in the same source file, make sure - // we get the diagnostic ordering to be sensible. - if (auto otherFile = other->getDeclContext()->getParentSourceFile()) { - if (currentFile == otherFile && - current->getLoc().isValid() && - other->getLoc().isValid() && - ctx.SourceMgr.isBeforeInBuffer(current->getLoc(), - other->getLoc())) { - std::swap(current, other); - } - } - // If we're currently looking at a .sil and the conflicting declaration // comes from a .sib, don't error since we won't be considering the sil // from the .sib. So it's fine for the .sil to shadow it, since that's the @@ -798,11 +828,6 @@ CheckRedeclarationRequest::evaluate(Evaluator &eval, ValueDecl *current) const { } } - // Make sure we don't do this checking again for the same decl. We also - // set this at the beginning of the function, but we might have swapped - // the decls for diagnostics; so ensure we also set this for the actual - // decl we diagnosed on. - current->setCheckedRedeclaration(); break; } } @@ -1293,6 +1318,38 @@ static void maybeDiagnoseClassWithoutInitializers(ClassDecl *classDecl) { diagnoseClassWithoutInitializers(classDecl); } +void TypeChecker::checkParameterList(ParameterList *params, + DeclContext *owner) { + for (auto param: *params) { + checkDeclAttributes(param); + } + + // For source compatibilty, allow duplicate internal parameter names + // on protocol requirements. + // + // FIXME: Consider turning this into a warning or error if we do + // another -swift-version. + if (!isa(owner->getParent())) { + // Check for duplicate parameter names. + diagnoseDuplicateDecls(*params); + } +} + +void TypeChecker::diagnoseDuplicateBoundVars(Pattern *pattern) { + SmallVector boundVars; + pattern->collectVariables(boundVars); + + diagnoseDuplicateDecls(boundVars); +} + +void TypeChecker::diagnoseDuplicateCaptureVars(CaptureListExpr *expr) { + SmallVector captureListVars; + for (auto &capture : expr->getCaptureList()) + captureListVars.push_back(capture.Var); + + diagnoseDuplicateDecls(captureListVars); +} + namespace { class DeclChecker : public DeclVisitor { public: @@ -1333,7 +1390,8 @@ class DeclChecker : public DeclVisitor { (void) VD->getFormalAccess(); // Compute overrides. - (void) VD->getOverriddenDecls(); + if (!VD->getOverriddenDecls().empty()) + checkOverrideActorIsolation(VD); // Check whether the member is @objc or dynamic. (void) VD->isObjC(); @@ -1418,6 +1476,11 @@ class DeclChecker : public DeclVisitor { (void) VD->getPropertyWrapperBackingProperty(); (void) VD->getImplInfo(); + // Visit auxiliary decls first + VD->visitAuxiliaryDecls([&](VarDecl *var) { + this->visitBoundVariable(var); + }); + // Add the '@_hasStorage' attribute if this property is stored. if (VD->hasStorage() && !VD->getAttrs().hasAttribute()) VD->getAttrs().add(new (getASTContext()) @@ -1488,6 +1551,8 @@ class DeclChecker : public DeclVisitor { } } + checkImplementationOnlyOverride(VD); + if (VD->getDeclContext()->getSelfClassDecl()) { if (VD->getValueInterfaceType()->hasDynamicSelfType()) { if (VD->hasStorage()) @@ -1709,12 +1774,14 @@ class DeclChecker : public DeclVisitor { } } + checkImplementationOnlyOverride(SD); + // Compute these requests in case they emit diagnostics. (void) SD->isGetterMutating(); (void) SD->isSetterMutating(); (void) SD->getImplInfo(); - TypeChecker::checkParameterAttributes(SD->getIndices()); + TypeChecker::checkParameterList(SD->getIndices(), SD); checkDefaultArguments(SD->getIndices()); @@ -1740,6 +1807,7 @@ class DeclChecker : public DeclVisitor { TypeChecker::checkDeclAttributes(TAD); checkAccessControl(TAD); + checkGenericParams(TAD); } void visitOpaqueTypeDecl(OpaqueTypeDecl *OTD) { @@ -1860,7 +1928,6 @@ class DeclChecker : public DeclVisitor { if (!rawTy->is()) { DE.diagnose(ED->getInherited().front().getSourceRange().Start, diag::raw_type_not_literal_convertible, rawTy); - ED->getInherited().front().setType(ErrorType::get(getASTContext())); } } @@ -2011,7 +2078,7 @@ class DeclChecker : public DeclVisitor { checkGenericParams(CD); // Check for circular inheritance. - (void)CD->hasCircularInheritance(); + (void)CD->getSuperclassDecl(); // Force lowering of stored properties. (void) CD->getStoredProperties(); @@ -2265,15 +2332,18 @@ class DeclChecker : public DeclVisitor { (void) FD->getOperatorDecl(); (void) FD->getDynamicallyReplacedDecl(); - if (!FD->isInvalid()) { - checkGenericParams(FD); - TypeChecker::checkReferencedGenericParams(FD); - TypeChecker::checkProtocolSelfRequirements(FD); - } + if (!isa(FD)) { + if (!FD->isInvalid()) { + checkGenericParams(FD); + TypeChecker::checkReferencedGenericParams(FD); + TypeChecker::checkProtocolSelfRequirements(FD); + } + + checkAccessControl(FD); - checkAccessControl(FD); + TypeChecker::checkParameterList(FD->getParameters(), FD); + } - TypeChecker::checkParameterAttributes(FD->getParameters()); TypeChecker::checkDeclAttributes(FD); if (!checkOverrides(FD)) { @@ -2298,12 +2368,15 @@ class DeclChecker : public DeclVisitor { } } + checkImplementationOnlyOverride(FD); + if (requiresDefinition(FD) && !FD->hasBody()) { // Complain if we should have a body. FD->diagnose(diag::func_decl_without_brace); } else if (FD->getDeclContext()->isLocalContext()) { // Check local function bodies right away. (void)FD->getTypecheckedBody(); + TypeChecker::computeCaptures(FD); } else if (shouldSkipBodyTypechecking(FD)) { FD->setBodySkipped(FD->getBodySourceRange()); } else { @@ -2381,7 +2454,7 @@ class DeclChecker : public DeclVisitor { TypeChecker::checkDeclAttributes(EED); if (auto *PL = EED->getParameterList()) { - TypeChecker::checkParameterAttributes(PL); + TypeChecker::checkParameterList(PL, EED); checkDefaultArguments(PL); } @@ -2537,7 +2610,7 @@ class DeclChecker : public DeclVisitor { } TypeChecker::checkDeclAttributes(CD); - TypeChecker::checkParameterAttributes(CD->getParameters()); + TypeChecker::checkParameterList(CD->getParameters(), CD); // Check whether this initializer overrides an initializer in its // superclass. @@ -2605,6 +2678,8 @@ class DeclChecker : public DeclVisitor { } } + checkImplementationOnlyOverride(CD); + // If this initializer overrides a 'required' initializer, it must itself // be marked 'required'. if (!CD->getAttrs().hasAttribute()) { diff --git a/lib/Sema/TypeCheckEffects.cpp b/lib/Sema/TypeCheckEffects.cpp index a21c6ba1d2146..1e9a54ee2b872 100644 --- a/lib/Sema/TypeCheckEffects.cpp +++ b/lib/Sema/TypeCheckEffects.cpp @@ -878,6 +878,13 @@ class Context { HandlesErrors(handlesErrors), HandlesAsync(handlesAsync) { } public: + bool shouldDiagnoseErrorOnTry() const { + return DiagnoseErrorOnTry; + } + void setDiagnoseErrorOnTry(bool b) { + DiagnoseErrorOnTry = b; + } + /// Whether this is a function that rethrows. bool isRethrows() const { if (!HandlesErrors) @@ -1375,6 +1382,7 @@ class CheckEffectsCoverage : public EffectsHandlingWalker DeclContext *OldRethrowsDC; ContextFlags OldFlags; ThrowingKind OldMaxThrowingKind; + public: ContextScope(CheckEffectsCoverage &self, Optional newContext) : Self(self), OldContext(self.CurContext), @@ -1468,7 +1476,15 @@ class CheckEffectsCoverage : public EffectsHandlingWalker } ~ContextScope() { + // The "DiagnoseErrorOnTry" flag is a bit of mutable state + // in the Context itself, used to postpone diagnostic emission + // to a parent "try" expression. If something was diagnosed + // during this ContextScope, the flag may have been set, and + // we need to preseve its value when restoring the old Context. + bool DiagnoseErrorOnTry = Self.CurContext.shouldDiagnoseErrorOnTry(); Self.CurContext = OldContext; + Self.CurContext.setDiagnoseErrorOnTry(DiagnoseErrorOnTry); + Self.RethrowsDC = OldRethrowsDC; Self.Flags = OldFlags; Self.MaxThrowingKind = OldMaxThrowingKind; diff --git a/lib/Sema/TypeCheckExpr.cpp b/lib/Sema/TypeCheckExpr.cpp index 7e8135083ea34..8eb3a80c34bca 100644 --- a/lib/Sema/TypeCheckExpr.cpp +++ b/lib/Sema/TypeCheckExpr.cpp @@ -25,7 +25,6 @@ #include "swift/AST/SourceFile.h" #include "swift/AST/TypeCheckRequests.h" #include "swift/Parse/Lexer.h" -#include "ConstraintSystem.h" using namespace swift; @@ -601,10 +600,10 @@ Expr *TypeChecker::buildRefExpr(ArrayRef Decls, assert(!Decls.empty() && "Must have at least one declaration"); auto &Context = UseDC->getASTContext(); - if (Decls.size() == 1 && !isa(Decls[0]->getDeclContext())) { - auto semantics = Decls[0]->getAccessSemanticsFromContext(UseDC, - /*isAccessOnSelf*/false); - return new (Context) DeclRefExpr(Decls[0], NameLoc, Implicit, semantics); + + if (Decls.size() == 1) { + return new (Context) DeclRefExpr(Decls[0], NameLoc, Implicit, + AccessSemantics::Ordinary); } Decls = Context.AllocateCopy(Decls); diff --git a/lib/Sema/TypeCheckGeneric.cpp b/lib/Sema/TypeCheckGeneric.cpp index 9c69020d726b0..fb07113c6c560 100644 --- a/lib/Sema/TypeCheckGeneric.cpp +++ b/lib/Sema/TypeCheckGeneric.cpp @@ -903,8 +903,9 @@ RequirementRequest::evaluate(Evaluator &evaluator, unsigned index, TypeResolutionStage stage) const { // Figure out the type resolution. - const auto options = - TypeResolutionOptions(TypeResolverContext::GenericRequirement); + auto options = TypeResolutionOptions(TypeResolverContext::GenericRequirement); + if (owner.dc->isInSpecializeExtensionContext()) + options |= TypeResolutionFlags::AllowUsableFromInline; Optional resolution; switch (stage) { case TypeResolutionStage::Structural: diff --git a/lib/Sema/TypeCheckNameLookup.cpp b/lib/Sema/TypeCheckNameLookup.cpp index 62347f886bbec..2fc3f814704bd 100644 --- a/lib/Sema/TypeCheckNameLookup.cpp +++ b/lib/Sema/TypeCheckNameLookup.cpp @@ -211,6 +211,8 @@ convertToUnqualifiedLookupOptions(NameLookupOptions options) { newOptions |= UnqualifiedLookupFlags::IgnoreAccessControl; if (options.contains(NameLookupFlags::IncludeOuterResults)) newOptions |= UnqualifiedLookupFlags::IncludeOuterResults; + if (options.contains(NameLookupFlags::IncludeUsableFromInline)) + newOptions |= UnqualifiedLookupFlags::IncludeUsableFromInline; return newOptions; } @@ -386,6 +388,8 @@ LookupTypeResult TypeChecker::lookupMemberType(DeclContext *dc, if (options.contains(NameLookupFlags::IgnoreAccessControl)) subOptions |= NL_IgnoreAccessControl; + if (options.contains(NameLookupFlags::IncludeUsableFromInline)) + subOptions |= NL_IncludeUsableFromInline; // Make sure we've resolved implicit members, if we need them. if (auto *current = type->getAnyNominal()) { diff --git a/lib/Sema/TypeCheckPattern.cpp b/lib/Sema/TypeCheckPattern.cpp index 459b7df7636c8..27a6c0ae1706e 100644 --- a/lib/Sema/TypeCheckPattern.cpp +++ b/lib/Sema/TypeCheckPattern.cpp @@ -37,7 +37,8 @@ using namespace swift; static EnumElementDecl * extractEnumElement(DeclContext *DC, SourceLoc UseLoc, const VarDecl *constant) { - diagnoseExplicitUnavailability(constant, UseLoc, DC, nullptr); + ExportContext where = ExportContext::forFunctionBody(DC); + diagnoseExplicitUnavailability(constant, UseLoc, where, nullptr); const FuncDecl *getter = constant->getAccessor(AccessorKind::Get); if (!getter) diff --git a/lib/Sema/TypeCheckPropertyWrapper.cpp b/lib/Sema/TypeCheckPropertyWrapper.cpp index 0ff4e76afc5c3..36db5a9203da4 100644 --- a/lib/Sema/TypeCheckPropertyWrapper.cpp +++ b/lib/Sema/TypeCheckPropertyWrapper.cpp @@ -13,7 +13,6 @@ // This file implements semantic analysis for property wrappers. // //===----------------------------------------------------------------------===// -#include "ConstraintSystem.h" #include "TypeChecker.h" #include "TypeCheckType.h" #include "swift/AST/ASTContext.h" @@ -414,12 +413,6 @@ AttachedPropertyWrappersRequest::evaluate(Evaluator &evaluator, // Check various restrictions on which properties can have wrappers // attached to them. - // Local properties do not yet support wrappers. - if (var->getDeclContext()->isLocalContext()) { - ctx.Diags.diagnose(attr->getLocation(), diag::property_wrapper_local); - continue; - } - // Nor does top-level code. if (var->getDeclContext()->isModuleScopeContext()) { ctx.Diags.diagnose(attr->getLocation(), diag::property_wrapper_top_level); @@ -519,7 +512,7 @@ Type AttachedPropertyWrapperTypeRequest::evaluate(Evaluator &evaluator, auto ty = evaluateOrDefault( evaluator, CustomAttrTypeRequest{customAttr, var->getDeclContext(), - CustomAttrTypeKind::PropertyDelegate}, + CustomAttrTypeKind::PropertyWrapper}, Type()); if (!ty || ty->hasError()) { return ErrorType::get(var->getASTContext()); diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index 6a89c151fc8bd..676b1e852f5a6 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -15,7 +15,6 @@ //===----------------------------------------------------------------------===// #include "TypeCheckProtocol.h" -#include "ConstraintSystem.h" #include "DerivedConformances.h" #include "MiscDiagnostics.h" #include "TypeAccessScopeChecker.h" @@ -737,18 +736,6 @@ swift::matchWitness( } } - // Check for actor-isolation consistency. - switch (getActorIsolation(witness)) { - case ActorIsolation::ActorInstance: - // Actor-isolated witnesses cannot conform to protocol requirements. - return RequirementMatch(witness, MatchKind::ActorIsolatedWitness); - - case ActorIsolation::ActorPrivileged: - case ActorIsolation::Independent: - case ActorIsolation::Unspecified: - break; - } - // Now finalize the match. auto result = finalize(anyRenaming, optionalAdjustments); // Check if the requirement's `@differentiable` attributes are satisfied by @@ -926,10 +913,9 @@ swift::matchWitness(WitnessChecker::RequirementEnvironmentCache &reqEnvCache, // defining a non-final class conforming to 'Collection' which uses // the default witness for 'Collection.Iterator', which is defined // as 'IndexingIterator'. - auto selfKind = proto->findProtocolSelfReferences(req, - /*allowCovariantParameters=*/false, - /*skipAssocTypes=*/false); - if (!selfKind.other) { + const auto selfRefInfo = proto->findProtocolSelfReferences( + req, /*treatNonResultCovariantSelfAsInvariant=*/true); + if (!selfRefInfo.assocTypeRef) { covariantSelf = classDecl; } } @@ -1525,7 +1511,9 @@ class swift::MultiConformanceChecker { NormalProtocolConformance *conformance, bool issueFixit); /// Determine whether the given requirement was left unsatisfied. - bool isUnsatisfiedReq(NormalProtocolConformance *conformance, ValueDecl *req); + bool isUnsatisfiedReq( + ConformanceChecker &checker, NormalProtocolConformance *conformance, + ValueDecl *req); public: MultiConformanceChecker(ASTContext &ctx) : Context(ctx) {} @@ -1551,7 +1539,8 @@ class swift::MultiConformanceChecker { }; bool MultiConformanceChecker:: -isUnsatisfiedReq(NormalProtocolConformance *conformance, ValueDecl *req) { +isUnsatisfiedReq(ConformanceChecker &checker, + NormalProtocolConformance *conformance, ValueDecl *req) { if (conformance->isInvalid()) return false; if (isa(req)) return false; @@ -1559,9 +1548,25 @@ isUnsatisfiedReq(NormalProtocolConformance *conformance, ValueDecl *req) { ? conformance->getWitnessUncached(req).getDecl() : nullptr; - // An optional requirement might not have a witness... - if (!witness) + if (!witness) { + // If another @objc requirement refers to the same Objective-C + // method, this requirement isn't unsatisfied. + if (conformance->getProtocol()->isObjC() && + isa(req)) { + auto funcReq = cast(req); + auto key = checker.getObjCMethodKey(funcReq); + for (auto otherReq : checker.getObjCRequirements(key)) { + if (otherReq == req) + continue; + + if (conformance->hasWitness(otherReq)) + return false; + } + } + + // An optional requirement might not have a witness. return req->getAttrs().hasAttribute(); + } // If the witness lands within the declaration context of the conformance, // record it as a "covered" member. @@ -1586,13 +1591,26 @@ void MultiConformanceChecker::checkAllConformances() { continue; // Check whether there are any unsatisfied requirements. auto proto = conformance->getProtocol(); + Optional checker; + auto getChecker = [&] () -> ConformanceChecker& { + if (checker) + return *checker; + + if (!AllUsedCheckers.empty() && + AllUsedCheckers.back().Conformance == conformance) + return AllUsedCheckers.back(); + + checker.emplace(getASTContext(), conformance, MissingWitnesses); + return *checker; + }; + for (auto member : proto->getMembers()) { auto req = dyn_cast(member); if (!req || !req->isProtocolRequirement()) continue; // If the requirement is unsatisfied, we might want to warn // about near misses; record it. - if (isUnsatisfiedReq(conformance, req)) { + if (isUnsatisfiedReq(getChecker(), conformance, req)) { UnsatisfiedReqs.push_back(req); continue; } @@ -1607,7 +1625,8 @@ void MultiConformanceChecker::checkAllConformances() { for (auto It = AllUsedCheckers.rbegin(); It != AllUsedCheckers.rend(); ++It) { if (!It->getLocalMissingWitness().empty()) { - It->diagnoseMissingWitnesses(MissingWitnessDiagnosisKind::FixItOnly); + if (It->diagnoseMissingWitnesses(MissingWitnessDiagnosisKind::FixItOnly)) + break; } } } @@ -2092,7 +2111,7 @@ static Type getRequirementTypeForDisplay(ModuleDecl *module, return FunctionType::get(params, result, fnTy->getExtInfo()); } - return substType(type, /*result*/false); + return substType(type, /*result*/ true); } diag::RequirementKind @@ -2454,24 +2473,6 @@ diagnoseMatch(ModuleDecl *module, NormalProtocolConformance *conformance, case MatchKind::NonObjC: diags.diagnose(match.Witness, diag::protocol_witness_not_objc); break; - case MatchKind::ActorIsolatedWitness: { - bool canBeAsyncHandler = false; - if (auto witnessFunc = dyn_cast(match.Witness)) { - canBeAsyncHandler = !witnessFunc->isAsyncHandler() && - !checkAsyncHandler(witnessFunc, /*diagnose=*/false); - } - auto diag = match.Witness->diagnose( - canBeAsyncHandler ? diag::actor_isolated_witness_could_be_async_handler - : diag::actor_isolated_witness, - match.Witness->getDescriptiveKind(), match.Witness->getName()); - - if (canBeAsyncHandler) { - diag.fixItInsert( - match.Witness->getAttributeInsertionLoc(false), "@asyncHandler "); - } - break; - } - case MatchKind::MissingDifferentiableAttr: { auto *witness = match.Witness; // Emit a note and fix-it showing the missing requirement `@differentiable` @@ -2653,6 +2654,115 @@ static void emitDeclaredHereIfNeeded(DiagnosticEngine &diags, diags.diagnose(value, diag::decl_declared_here, value->getName()); } +bool ConformanceChecker::checkActorIsolation( + ValueDecl *requirement, ValueDecl *witness) { + // Ensure that the witness is not actor-isolated in a manner that makes it + // unsuitable as a witness. + Type witnessGlobalActor; + switch (auto witnessRestriction = + ActorIsolationRestriction::forDeclaration(witness)) { + case ActorIsolationRestriction::ActorSelf: { + // Actor-isolated witnesses cannot conform to protocol requirements. + bool canBeAsyncHandler = false; + if (auto witnessFunc = dyn_cast(witness)) { + canBeAsyncHandler = !witnessFunc->isAsyncHandler() && + witnessFunc->canBeAsyncHandler(); + } + auto diag = witness->diagnose( + canBeAsyncHandler + ? diag::actor_isolated_witness_could_be_async_handler + : diag::actor_isolated_witness, + witness->getDescriptiveKind(), witness->getName()); + + if (canBeAsyncHandler) { + diag.fixItInsert( + witness->getAttributeInsertionLoc(false), "@asyncHandler "); + } + + return true; + } + + case ActorIsolationRestriction::GlobalActor: { + // Hang on to the global actor that's used for the witness. It will need + // to match that of the requirement. + witnessGlobalActor = witness->getDeclContext()->mapTypeIntoContext( + witnessRestriction.getGlobalActor()); + break; + } + + case ActorIsolationRestriction::Unsafe: + case ActorIsolationRestriction::LocalCapture: + break; + + case ActorIsolationRestriction::Unrestricted: + // The witness is completely unrestricted, so ignore any annotations on + // the requirement. + return false; + } + + // Check whether the requirement requires some particular actor isolation. + Type requirementGlobalActor; + switch (auto requirementIsolation = getActorIsolation(requirement)) { + case ActorIsolation::ActorInstance: + llvm_unreachable("There are not actor protocols"); + + case ActorIsolation::GlobalActor: { + auto requirementSubs = SubstitutionMap::getProtocolSubstitutions( + Proto, Adoptee, ProtocolConformanceRef(Conformance)); + requirementGlobalActor = requirementIsolation.getGlobalActor() + .subst(requirementSubs); + break; + } + + case ActorIsolation::Independent: + case ActorIsolation::IndependentUnsafe: + case ActorIsolation::Unspecified: + break; + } + + // If neither has a global actor, we're done. + if (!witnessGlobalActor && !requirementGlobalActor) + return false; + + // If the witness has a global actor but the requirement does not, we have + // an isolation error. + if (witnessGlobalActor && !requirementGlobalActor) { + witness->diagnose( + diag::global_actor_isolated_witness, witness->getDescriptiveKind(), + witness->getName(), witnessGlobalActor, Proto->getName()); + requirement->diagnose(diag::decl_declared_here, requirement->getName()); + return true; + } + + // If the requirement has a global actor but the witness does not, we have + // an isolation error. + // + // FIXME: Within a module, this will be an inference rule. + if (requirementGlobalActor && !witnessGlobalActor) { + witness->diagnose( + diag::global_actor_isolated_requirement, witness->getDescriptiveKind(), + witness->getName(), requirementGlobalActor, Proto->getName()) + .fixItInsert( + witness->getAttributeInsertionLoc(/*forModifier=*/false), + "@" + requirementGlobalActor.getString()); + requirement->diagnose(diag::decl_declared_here, requirement->getName()); + return true; + } + + // If both have global actors but they differ, this is an isolation error. + if (!witnessGlobalActor->isEqual(requirementGlobalActor)) { + witness->diagnose( + diag::global_actor_isolated_requirement_witness_conflict, + witness->getDescriptiveKind(), witness->getName(), witnessGlobalActor, + Proto->getName(), requirementGlobalActor); + requirement->diagnose(diag::decl_declared_here, requirement->getName()); + return true; + } + + // Everything is okay. + return false; +} + bool ConformanceChecker::checkObjCTypeErasedGenerics( AssociatedTypeDecl *assocType, Type type, @@ -3099,13 +3209,128 @@ filterProtocolRequirements( return Filtered; } -void ConformanceChecker:: +/// Prune the set of missing witnesses for the given conformance, eliminating +/// any requirements that do not actually need to satisfied. +static ArrayRef pruneMissingWitnesses( + ConformanceChecker &checker, + ProtocolDecl *proto, + NormalProtocolConformance *conformance, + ArrayRef missingWitnesses, + SmallVectorImpl &scratch) { + if (missingWitnesses.empty()) + return missingWitnesses; + + // For an @objc protocol defined in Objective-C, the Clang importer might + // have imported the same underlying Objective-C declaration as more than + // one Swift declaration. If we aren't in an imported @objc protocol, there + // is nothing to do. + if (!proto->isObjC()) + return missingWitnesses; + + // Consider each of the missing witnesses to remove any that should not + // longer be considered "missing". + llvm::SmallDenseSet + alreadyReportedAsMissing; + bool removedAny = false; + for (unsigned missingWitnessIdx : indices(missingWitnesses)) { + const auto &missingWitness = missingWitnesses[missingWitnessIdx]; + + // Local function to skip this particular witness. + auto skipWitness = [&] { + if (removedAny) + return; + + // This is the first witness we skipped. Copy all of the earlier + // missing witnesses over. + scratch.clear(); + scratch.append( + missingWitnesses.begin(), + missingWitnesses.begin() + missingWitnessIdx); + removedAny = true; + }; + + // Local function to add this particular witness. + auto addWitness = [&] { + if (removedAny) + scratch.push_back(missingWitness); + }; + + // We only care about functions + auto funcRequirement = dyn_cast( + missingWitness.requirement); + if (!funcRequirement) { + addWitness(); + continue; + } + + // ... whose selector is one that maps to multiple requirement declarations. + auto key = checker.getObjCMethodKey(funcRequirement); + auto matchingRequirements = checker.getObjCRequirements(key); + if (matchingRequirements.size() < 2) { + addWitness(); + continue; + } + + // If we have already reported a function with this selector as missing, + // don't do it again. + if (!alreadyReportedAsMissing.insert(key).second) { + skipWitness(); + continue; + } + + // If there is a witness for any of the *other* requirements with this + // same selector, don't report it. + bool foundOtherWitness = false; + for (auto otherReq : matchingRequirements) { + if (otherReq == funcRequirement) + continue; + + if (conformance->getWitness(otherReq)) { + foundOtherWitness = true; + break; + } + } + + if (foundOtherWitness) + skipWitness(); + else + addWitness(); + } + + if (removedAny) + return scratch; + + return missingWitnesses; +} + +bool ConformanceChecker:: diagnoseMissingWitnesses(MissingWitnessDiagnosisKind Kind) { auto LocalMissing = getLocalMissingWitness(); + SmallVector MissingWitnessScratch; + LocalMissing = pruneMissingWitnesses( + *this, Proto, Conformance, LocalMissing, MissingWitnessScratch); + // If this conformance has nothing to complain, return. if (LocalMissing.empty()) - return; + return false; + + // Diagnose the missing witnesses. + for (auto &Missing : LocalMissing) { + auto requirement = Missing.requirement; + auto matches = Missing.matches; + auto nominal = Adoptee->getAnyNominal(); + diagnoseOrDefer(requirement, true, + [requirement, matches, nominal](NormalProtocolConformance *conformance) { + auto dc = conformance->getDeclContext(); + auto *protocol = conformance->getProtocol(); + // Possibly diagnose reason for automatic derivation failure + DerivedConformance::tryDiagnoseFailedDerivation(dc, nominal, protocol); + // Diagnose each of the matches. + for (const auto &match : matches) + diagnoseMatch(dc->getParentModule(), conformance, requirement, match); + }); + } const auto InsertFixit = []( NormalProtocolConformance *Conf, SourceLoc ComplainLoc, bool EditorMode, @@ -3226,22 +3451,46 @@ diagnoseMissingWitnesses(MissingWitnessDiagnosisKind Kind) { }); } clearGlobalMissingWitnesses(); - return; + return true; } case MissingWitnessDiagnosisKind::ErrorOnly: { diagnoseOrDefer( LocalMissing[0].requirement, true, [](NormalProtocolConformance *) {}); - return; + return true; } case MissingWitnessDiagnosisKind::FixItOnly: InsertFixit(Conformance, Loc, IsEditorMode, filterProtocolRequirements(GlobalMissingWitnesses.getArrayRef(), Adoptee)); clearGlobalMissingWitnesses(); - return; + return true; } } +/// Whether the given protocol requirement has a "Self ==" constraint. +static bool hasSelfSameTypeConstraint(const ValueDecl *req) { + const auto *proto = cast(req->getDeclContext()); + const auto *genCtx = req->getAsGenericContext(); + if (!genCtx) + return false; + + const auto genericSig = genCtx->getGenericSignature(); + if (!genericSig) + return false; + + const auto selfTy = proto->getSelfInterfaceType(); + for (const auto &constr : genericSig->getRequirements()) { + if (constr.getKind() != RequirementKind::SameType) + continue; + + if (constr.getFirstType()->isEqual(selfTy) || + constr.getSecondType()->isEqual(selfTy)) + return true; + } + + return false; +} + /// Determine the given witness has a same-type constraint constraining the /// given 'Self' type, and return the requirement that does. /// @@ -3341,11 +3590,10 @@ void ConformanceChecker::checkNonFinalClassWitness(ValueDecl *requirement, // Check whether this requirement uses Self in a way that might // prevent conformance from succeeding. - auto selfKind = Proto->findProtocolSelfReferences(requirement, - /*allowCovariantParameters=*/false, - /*skipAssocTypes=*/true); + const auto selfRefInfo = Proto->findProtocolSelfReferences( + requirement, /*treatNonResultCovariantSelfAsInvariant=*/true); - if (selfKind.other) { + if (selfRefInfo.selfRef == SelfReferencePosition::Invariant) { // References to Self in a position where subclasses cannot do // the right thing. Complain if the adoptee is a non-final // class. @@ -3360,44 +3608,39 @@ void ConformanceChecker::checkNonFinalClassWitness(ValueDecl *requirement, conformance->getType()); emitDeclaredHereIfNeeded(diags, diagLoc, witness); }); - } else if (selfKind.result) { - // The reference to Self occurs in the result type. A non-final class - // can satisfy this requirement with a method that returns Self. + } else if (selfRefInfo.hasCovariantSelfResult) { + // The reference to Self occurs in the result type of a method/subscript + // or the type of a property. A non-final class can satisfy this requirement + // by holding onto Self accordingly. + if (witness->getDeclContext()->getSelfClassDecl()) { + const bool hasDynamicSelfResult = [&] { + if (auto func = dyn_cast(witness)) { + return func->hasDynamicSelfResult(); + } else if (auto var = dyn_cast(witness)) { + return var->getInterfaceType()->hasDynamicSelfType(); + } - // If the function has a dynamic Self, it's okay. - if (auto func = dyn_cast(witness)) { - if (func->getDeclContext()->getSelfClassDecl() && - !func->hasDynamicSelfResult()) { + return cast(witness) + ->getElementInterfaceType() + ->hasDynamicSelfType(); + }(); + + if (!hasDynamicSelfResult) { diagnoseOrDefer(requirement, false, [witness, requirement](NormalProtocolConformance *conformance) { auto proto = conformance->getProtocol(); auto &diags = proto->getASTContext().Diags; SourceLoc diagLoc = getLocForDiagnosingWitness(conformance,witness); - diags.diagnose(diagLoc, - diag::witness_requires_dynamic_self, + diags.diagnose(diagLoc, diag::witness_requires_dynamic_self, + getProtocolRequirementKind(requirement), requirement->getName(), conformance->getType(), proto->getDeclaredInterfaceType()); emitDeclaredHereIfNeeded(diags, diagLoc, witness); }); } - - // Constructors conceptually also have a dynamic Self - // return type, so they're okay. - } else if (!isa(witness)) { - diagnoseOrDefer(requirement, false, - [witness, requirement](NormalProtocolConformance *conformance) { - auto proto = conformance->getProtocol(); - auto &diags = proto->getASTContext().Diags; - SourceLoc diagLoc = getLocForDiagnosingWitness(conformance, witness); - diags.diagnose(diagLoc, diag::witness_self_non_subtype, - proto->getDeclaredInterfaceType(), - requirement->getName(), - conformance->getType()); - emitDeclaredHereIfNeeded(diags, diagLoc, witness); - }); } - } else if (selfKind.requirement) { + } else if (hasSelfSameTypeConstraint(requirement)) { if (auto targetPair = getAdopteeSelfSameTypeConstraint(classDecl, witness)) { // A "Self ==" constraint works incorrectly with subclasses. Complain. @@ -3431,20 +3674,16 @@ void ConformanceChecker::checkNonFinalClassWitness(ValueDecl *requirement, // constraint that either the requirement not produce 'Self' in a // covariant position, or the type of the requirement does not involve // associated types. - if (auto func = dyn_cast(witness)) { - if (func->getDeclContext()->getExtendedProtocolDecl()) { - auto selfKindWithAssocTypes = Proto->findProtocolSelfReferences( - requirement, - /*allowCovariantParameters=*/false, - /*skipAssocTypes=*/false); - if (selfKindWithAssocTypes.other && - selfKindWithAssocTypes.result) { + if (isa(witness) || isa(witness)) { + if (witness->getDeclContext()->getExtendedProtocolDecl()) { + if (selfRefInfo.hasCovariantSelfResult && selfRefInfo.assocTypeRef) { diagnoseOrDefer(requirement, false, [witness, requirement](NormalProtocolConformance *conformance) { auto proto = conformance->getProtocol(); auto &diags = proto->getASTContext().Diags; diags.diagnose(conformance->getLoc(), diag::witness_requires_class_implementation, + getProtocolRequirementKind(requirement), requirement->getName(), conformance->getType()); diags.diagnose(witness, diag::decl_declared_here, @@ -3748,17 +3987,7 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) { if (!numViable) { // Save the missing requirement for later diagnosis. GlobalMissingWitnesses.insert({requirement, matches}); - diagnoseOrDefer(requirement, true, - [requirement, matches, nominal](NormalProtocolConformance *conformance) { - auto dc = conformance->getDeclContext(); - auto *protocol = conformance->getProtocol(); - // Possibly diagnose reason for automatic derivation failure - DerivedConformance::tryDiagnoseFailedDerivation(dc, nominal, protocol); - // Diagnose each of the matches. - for (const auto &match : matches) - diagnoseMatch(dc->getParentModule(), conformance, requirement, match); - }); - return ResolveWitnessResult::ExplicitFailed; + return ResolveWitnessResult::Missing; } diagnoseOrDefer(requirement, true, @@ -4109,9 +4338,11 @@ static void checkExportability(Type depTy, Type replacementTy, conformance->getRootConformance(); ModuleDecl *M = rootConformance->getDeclContext()->getParentModule(); + auto where = ExportContext::forDeclSignature( + DC->getInnermostDeclarationDeclContext()); auto originKind = getDisallowedOriginKind( rootConformance->getDeclContext()->getAsDecl(), - *SF, DC->getAsDecl()); + where); if (originKind == DisallowedOriginKind::None) return; @@ -4220,8 +4451,23 @@ void ConformanceChecker::resolveValueWitnesses() { auto witness = Conformance->getWitnessUncached(requirement).getDecl(); if (!witness) return; - // Objective-C checking for @objc requirements. auto &C = witness->getASTContext(); + + // Ensure that Actor.enqueue(partialTask:) is implemented within the + // actor class itself. + if (FuncDecl::isEnqueuePartialTaskName(C, requirement->getName()) && + Proto->isSpecificProtocol(KnownProtocolKind::Actor) && + DC != witness->getDeclContext() && + Adoptee->getClassOrBoundGenericClass() && + Adoptee->getClassOrBoundGenericClass()->isActor()) { + witness->diagnose(diag::enqueue_partial_task_not_in_context, Adoptee); + return; + } + + if (checkActorIsolation(requirement, witness)) + return; + + // Objective-C checking for @objc requirements. if (requirement->isObjC() && requirement->getName() == witness->getName() && !requirement->getAttrs().isUnavailable(getASTContext())) { @@ -4435,6 +4681,41 @@ void ConformanceChecker::checkConformance(MissingWitnessDiagnosisKind Kind) { } } +/// Retrieve the Objective-C method key from the given function. +auto ConformanceChecker::getObjCMethodKey(AbstractFunctionDecl *func) + -> ObjCMethodKey { + return ObjCMethodKey(func->getObjCSelector(), func->isInstanceMember()); +} + +/// Retrieve the Objective-C requirements in this protocol that have the +/// given Objective-C method key. +ArrayRef +ConformanceChecker::getObjCRequirements(ObjCMethodKey key) { + auto proto = Conformance->getProtocol(); + if (!proto->isObjC()) + return { }; + + // Fill in the data structure if we haven't done so yet. + if (!computedObjCMethodRequirements) { + for (auto requirement : proto->getSemanticMembers()) { + auto funcRequirement = dyn_cast(requirement); + if (!funcRequirement) + continue; + + objcMethodRequirements[getObjCMethodKey(funcRequirement)] + .push_back(funcRequirement); + } + + computedObjCMethodRequirements = true; + } + + auto known = objcMethodRequirements.find(key); + if (known == objcMethodRequirements.end()) + return { }; + + return known->second; +} + void swift::diagnoseConformanceFailure(Type T, ProtocolDecl *Proto, DeclContext *DC, @@ -4454,6 +4735,8 @@ void swift::diagnoseConformanceFailure(Type T, diags.diagnose(ComplainLoc, diag::type_cannot_conform, true, T, T->isEqual(Proto->getDeclaredInterfaceType()), Proto->getDeclaredInterfaceType()); + diags.diagnose(ComplainLoc, + diag::only_concrete_types_conform_to_protocols); return; } @@ -5177,14 +5460,13 @@ static void inferStaticInitializeObjCMetadata(ClassDecl *classDecl) { // only statically initialize the Objective-C metadata when running on // a new-enough OS. if (auto sourceFile = classDecl->getParentSourceFile()) { - AvailabilityContext availableInfo = AvailabilityContext::alwaysAvailable(); - for (Decl *enclosingDecl = classDecl; enclosingDecl; - enclosingDecl = enclosingDecl->getDeclContext() - ->getInnermostDeclarationDeclContext()) { - if (!TypeChecker::isDeclAvailable(enclosingDecl, SourceLoc(), sourceFile, - availableInfo)) - return; - } + AvailabilityContext safeRangeUnderApprox{ + AvailabilityInference::availableRange(classDecl, ctx)}; + AvailabilityContext runningOSOverApprox = + AvailabilityContext::forDeploymentTarget(ctx); + + if (!runningOSOverApprox.isContainedIn(safeRangeUnderApprox)) + return; } // Infer @_staticInitializeObjCMetadata. @@ -5808,6 +6090,9 @@ ValueDecl *TypeChecker::deriveProtocolRequirement(DeclContext *DC, case KnownDerivableProtocolKind::Differentiable: return derived.deriveDifferentiable(Requirement); + case KnownDerivableProtocolKind::Actor: + return derived.deriveActor(Requirement); + case KnownDerivableProtocolKind::OptionSet: llvm_unreachable( "When possible, OptionSet is derived via memberwise init synthesis"); diff --git a/lib/Sema/TypeCheckProtocol.h b/lib/Sema/TypeCheckProtocol.h index 0735417f8ac49..8e6fb5b38fb76 100644 --- a/lib/Sema/TypeCheckProtocol.h +++ b/lib/Sema/TypeCheckProtocol.h @@ -1,4 +1,4 @@ -//===--- ConstraintSystem.h - Constraint-based Type Checking ----*- C++ -*-===// +//===--- TypeCheckProtocol.h - Constraint-based Type Checking ----*- C++ -*-===// // // This source file is part of the Swift.org open source project // @@ -25,6 +25,7 @@ #include "swift/AST/Types.h" #include "swift/AST/Witness.h" #include "swift/Basic/Debug.h" +#include "swift/Sema/ConstraintSystem.h" #include "llvm/ADT/ScopedHashTable.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallPtrSet.h" @@ -268,9 +269,6 @@ enum class MatchKind : uint8_t { /// The witness is explicitly @nonobjc but the requirement is @objc. NonObjC, - /// The witness is part of actor-isolated state. - ActorIsolatedWitness, - /// The witness is missing a `@differentiable` attribute from the requirement. MissingDifferentiableAttr, @@ -503,7 +501,6 @@ struct RequirementMatch { case MatchKind::AsyncConflict: case MatchKind::ThrowsConflict: case MatchKind::NonObjC: - case MatchKind::ActorIsolatedWitness: case MatchKind::MissingDifferentiableAttr: case MatchKind::EnumCaseWithAssociatedValues: return false; @@ -536,7 +533,6 @@ struct RequirementMatch { case MatchKind::AsyncConflict: case MatchKind::ThrowsConflict: case MatchKind::NonObjC: - case MatchKind::ActorIsolatedWitness: case MatchKind::MissingDifferentiableAttr: case MatchKind::EnumCaseWithAssociatedValues: return false; @@ -678,6 +674,12 @@ class DelayedMissingWitnesses : public MissingWitnessesBase { /// This helper class handles most of the details of checking whether a /// given type (\c Adoptee) conforms to a protocol (\c Proto). class ConformanceChecker : public WitnessChecker { +public: + /// Key that can be used to uniquely identify a particular Objective-C + /// method. + using ObjCMethodKey = std::pair; + +private: friend class MultiConformanceChecker; friend class AssociatedTypeInference; @@ -712,6 +714,14 @@ class ConformanceChecker : public WitnessChecker { /// Whether we checked the requirement signature already. bool CheckedRequirementSignature = false; + /// Mapping from Objective-C methods to the set of requirements within this + /// protocol that have the same selector and instance/class designation. + llvm::SmallDenseMap, 4> + objcMethodRequirements; + + /// Whether objcMethodRequirements has been computed. + bool computedObjCMethodRequirements = false; + /// Retrieve the associated types that are referenced by the given /// requirement with a base of 'Self'. ArrayRef getReferencedAssociatedTypes(ValueDecl *req); @@ -730,6 +740,11 @@ class ConformanceChecker : public WitnessChecker { Type type, TypeDecl *typeDecl); + /// Check that the witness and requirement have compatible actor contexts. + /// + /// \returns true if an error occurred, false otherwise. + bool checkActorIsolation(ValueDecl *requirement, ValueDecl *witness); + /// Record a type witness. /// /// \param assocType The associated type whose witness is being recorded. @@ -789,7 +804,10 @@ class ConformanceChecker : public WitnessChecker { public: /// Call this to diagnose currently known missing witnesses. - void diagnoseMissingWitnesses(MissingWitnessDiagnosisKind Kind); + /// + /// \returns true if any witnesses were diagnosed. + bool diagnoseMissingWitnesses(MissingWitnessDiagnosisKind Kind); + /// Emit any diagnostics that have been delayed. void emitDelayedDiags(); @@ -824,6 +842,13 @@ class ConformanceChecker : public WitnessChecker { /// Check the entire protocol conformance, ensuring that all /// witnesses are resolved and emitting any diagnostics. void checkConformance(MissingWitnessDiagnosisKind Kind); + + /// Retrieve the Objective-C method key from the given function. + ObjCMethodKey getObjCMethodKey(AbstractFunctionDecl *func); + + /// Retrieve the Objective-C requirements in this protocol that have the + /// given Objective-C method key. + ArrayRef getObjCRequirements(ObjCMethodKey key); }; /// Captures the state needed to infer associated types. diff --git a/lib/Sema/TypeCheckRequestFunctions.cpp b/lib/Sema/TypeCheckRequestFunctions.cpp index f841882dc3893..06c4e510dd7cf 100644 --- a/lib/Sema/TypeCheckRequestFunctions.cpp +++ b/lib/Sema/TypeCheckRequestFunctions.cpp @@ -30,31 +30,27 @@ Type InheritedTypeRequest::evaluate( llvm::PointerUnion decl, unsigned index, TypeResolutionStage stage) const { // Figure out how to resolve types. - TypeResolutionOptions options = None; DeclContext *dc; if (auto typeDecl = decl.dyn_cast()) { if (auto nominal = dyn_cast(typeDecl)) { dc = (DeclContext *)nominal; - - options |= TypeResolutionFlags::AllowUnavailableProtocol; } else { dc = typeDecl->getDeclContext(); } } else { dc = (DeclContext *)decl.get(); - options |= TypeResolutionFlags::AllowUnavailableProtocol; } Optional resolution; switch (stage) { case TypeResolutionStage::Structural: resolution = - TypeResolution::forStructural(dc, options, /*unboundTyOpener*/ nullptr); + TypeResolution::forStructural(dc, None, /*unboundTyOpener*/ nullptr); break; case TypeResolutionStage::Interface: resolution = - TypeResolution::forInterface(dc, options, /*unboundTyOpener*/ nullptr); + TypeResolution::forInterface(dc, None, /*unboundTyOpener*/ nullptr); break; case TypeResolutionStage::Contextual: { @@ -93,6 +89,12 @@ SuperclassTypeRequest::evaluate(Evaluator &evaluator, return proto->getGenericSignature() ->getSuperclassBound(proto->getSelfInterfaceType()); } + + if (!proto->getSuperclassDecl()) + return Type(); + } else if (auto classDecl = dyn_cast(nominalDecl)) { + if (!classDecl->getSuperclassDecl()) + return Type(); } for (unsigned int idx : indices(nominalDecl->getInherited())) { @@ -130,13 +132,12 @@ SuperclassTypeRequest::evaluate(Evaluator &evaluator, return Type(); } -Type -EnumRawTypeRequest::evaluate(Evaluator &evaluator, EnumDecl *enumDecl, - TypeResolutionStage stage) const { +Type EnumRawTypeRequest::evaluate(Evaluator &evaluator, + EnumDecl *enumDecl) const { for (unsigned int idx : indices(enumDecl->getInherited())) { - auto inheritedTypeResult = - evaluator(InheritedTypeRequest{enumDecl, idx, stage}); - + auto inheritedTypeResult = evaluator( + InheritedTypeRequest{enumDecl, idx, TypeResolutionStage::Interface}); + if (auto err = inheritedTypeResult.takeError()) { llvm::handleAllErrors(std::move(err), [](const CyclicalRequestError &E) { @@ -160,7 +161,7 @@ EnumRawTypeRequest::evaluate(Evaluator &evaluator, EnumDecl *enumDecl, } CustomAttr * -AttachedFunctionBuilderRequest::evaluate(Evaluator &evaluator, +AttachedResultBuilderRequest::evaluate(Evaluator &evaluator, ValueDecl *decl) const { ASTContext &ctx = decl->getASTContext(); auto dc = decl->getDeclContext(); @@ -175,16 +176,16 @@ AttachedFunctionBuilderRequest::evaluate(Evaluator &evaluator, if (!nominal) continue; - // Return the first custom attribute that is a function builder type. - if (nominal->getAttrs().hasAttribute()) + // Return the first custom attribute that is a result builder type. + if (nominal->getAttrs().hasAttribute()) return mutableAttr; } return nullptr; } -/// Attempt to infer the function builder type for a declaration. -static Type inferFunctionBuilderType(ValueDecl *decl) { +/// Attempt to infer the result builder type for a declaration. +static Type inferResultBuilderType(ValueDecl *decl) { auto dc = decl->getDeclContext(); if (!dc->isTypeContext() || isa(dc)) return Type(); @@ -195,12 +196,12 @@ static Type inferFunctionBuilderType(ValueDecl *decl) { return Type(); // Check whether there are any return statements in the function's body. - // If there are, the function builder transform will be disabled, - // so don't infer a function builder. + // If there are, the result builder transform will be disabled, + // so don't infer a result builder. if (!TypeChecker::findReturnStatements(funcDecl).empty()) return Type(); - // Only getters can have function builders. When we find one, look at + // Only getters can have result builders. When we find one, look at // the storage declaration for the purposes of witness matching. auto lookupDecl = decl; if (auto accessor = dyn_cast(funcDecl)) { @@ -210,7 +211,7 @@ static Type inferFunctionBuilderType(ValueDecl *decl) { lookupDecl = accessor->getStorage(); } - // Find all of the potentially inferred function builder types. + // Find all of the potentially inferred result builder types. struct Match { enum Kind { Conformance, @@ -226,26 +227,26 @@ static Type inferFunctionBuilderType(ValueDecl *decl) { ValueDecl *dynamicReplacement; }; - Type functionBuilderType; + Type resultBuilderType; static Match forConformance( ProtocolConformance *conformance, ValueDecl *requirement, - Type functionBuilderType) { + Type resultBuilderType) { Match match; match.kind = Conformance; match.conformanceMatch.conformance = conformance; match.conformanceMatch.requirement = requirement; - match.functionBuilderType = functionBuilderType; + match.resultBuilderType = resultBuilderType; return match; } static Match forDynamicReplacement( - ValueDecl *dynamicReplacement, Type functionBuilderType) { + ValueDecl *dynamicReplacement, Type resultBuilderType) { Match match; match.kind = DynamicReplacement; match.dynamicReplacement = dynamicReplacement; - match.functionBuilderType = functionBuilderType; + match.resultBuilderType = resultBuilderType; return match; } @@ -257,16 +258,17 @@ static Type inferFunctionBuilderType(ValueDecl *decl) { case DynamicReplacement: return dynamicReplacement->getName(); } + llvm_unreachable("unhandled decl name kind!"); } }; - // The set of matches from which we can infer function builder types. + // The set of matches from which we can infer result builder types. SmallVector matches; // Determine all of the conformances within the same context as // this declaration. If this declaration is a witness to any - // requirement within one of those protocols that has a function builder - // attached, use that function builder type. + // requirement within one of those protocols that has a result builder + // attached, use that result builder type. auto addConformanceMatches = [&matches](ValueDecl *lookupDecl) { DeclContext *dc = lookupDecl->getDeclContext(); auto idc = cast(dc->getAsDecl()); @@ -284,33 +286,33 @@ static Type inferFunctionBuilderType(ValueDecl *decl) { if (!requirement) continue; - Type functionBuilderType = requirement->getFunctionBuilderType(); - if (!functionBuilderType) + Type resultBuilderType = requirement->getResultBuilderType(); + if (!resultBuilderType) continue; auto witness = conformance->getWitnessDecl(requirement); if (witness != lookupDecl) continue; - // Substitute into the function builder type. + // Substitute into the result builder type. auto subs = conformance->getSubstitutions(lookupDecl->getModuleContext()); - Type subFunctionBuilderType = functionBuilderType.subst(subs); + Type subResultBuilderType = resultBuilderType.subst(subs); matches.push_back( Match::forConformance( - conformance, requirement, subFunctionBuilderType)); + conformance, requirement, subResultBuilderType)); } } }; addConformanceMatches(lookupDecl); - // Look for function builder types inferred through dynamic replacements. + // Look for result builder types inferred through dynamic replacements. if (auto replaced = lookupDecl->getDynamicallyReplacedDecl()) { - if (auto functionBuilderType = replaced->getFunctionBuilderType()) { + if (auto resultBuilderType = replaced->getResultBuilderType()) { matches.push_back( - Match::forDynamicReplacement(replaced, functionBuilderType)); + Match::forDynamicReplacement(replaced, resultBuilderType)); } else { addConformanceMatches(replaced); } @@ -319,44 +321,44 @@ static Type inferFunctionBuilderType(ValueDecl *decl) { if (matches.size() == 0) return Type(); - // Determine whether there is more than one actual function builder type. - Type functionBuilderType = matches[0].functionBuilderType; + // Determine whether there is more than one actual result builder type. + Type resultBuilderType = matches[0].resultBuilderType; for (const auto &match : matches) { // If the types were the same anyway, there's nothing to do. - Type otherFunctionBuilderType = match.functionBuilderType; - if (functionBuilderType->isEqual(otherFunctionBuilderType)) + Type otherResultBuilderType = match.resultBuilderType; + if (resultBuilderType->isEqual(otherResultBuilderType)) continue; - // We have at least two different function builder types. + // We have at least two different result builder types. // Diagnose the ambiguity and provide potential solutions. decl->diagnose( - diag::function_builder_infer_ambig, lookupDecl->getName(), - functionBuilderType, otherFunctionBuilderType); - decl->diagnose(diag::function_builder_infer_add_return) + diag::result_builder_infer_ambig, lookupDecl->getName(), + resultBuilderType, otherResultBuilderType); + decl->diagnose(diag::result_builder_infer_add_return) .fixItInsert(funcDecl->getBodySourceRange().End, "return <#expr#>\n"); for (const auto &match : matches) { decl->diagnose( - diag::function_builder_infer_pick_specific, - match.functionBuilderType, + diag::result_builder_infer_pick_specific, + match.resultBuilderType, static_cast(match.kind), match.getSourceName()) .fixItInsert( lookupDecl->getAttributeInsertionLoc(false), - "@" + match.functionBuilderType.getString() + " "); + "@" + match.resultBuilderType.getString() + " "); } return Type(); } - return functionBuilderType; + return resultBuilderType; } -Type FunctionBuilderTypeRequest::evaluate(Evaluator &evaluator, +Type ResultBuilderTypeRequest::evaluate(Evaluator &evaluator, ValueDecl *decl) const { - // Look for a function-builder custom attribute. - auto attr = decl->getAttachedFunctionBuilder(); + // Look for a result-builder custom attribute. + auto attr = decl->getAttachedResultBuilder(); if (!attr) - return inferFunctionBuilderType(decl); + return inferResultBuilderType(decl); // Resolve a type for the attribute. auto mutableAttr = const_cast(attr); @@ -384,7 +386,7 @@ Type FunctionBuilderTypeRequest::evaluate(Evaluator &evaluator, // Require the parameter to be an interface type. if (!paramFnType) { ctx.Diags.diagnose(attr->getLocation(), - diag::function_builder_parameter_not_of_function_type, + diag::result_builder_parameter_not_of_function_type, nominal->getName()); mutableAttr->setInvalid(); return Type(); @@ -393,7 +395,7 @@ Type FunctionBuilderTypeRequest::evaluate(Evaluator &evaluator, // Forbid the parameter to be an autoclosure. if (param->isAutoClosure()) { ctx.Diags.diagnose(attr->getLocation(), - diag::function_builder_parameter_autoclosure, + diag::result_builder_parameter_autoclosure, nominal->getName()); mutableAttr->setInvalid(); return Type(); diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index 0c23c18ff41c8..ada5ba0783f2b 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -18,9 +18,9 @@ #include "TypeCheckAvailability.h" #include "TypeCheckType.h" #include "MiscDiagnostics.h" -#include "ConstraintSystem.h" #include "swift/Subsystems.h" #include "swift/AST/ASTPrinter.h" +#include "swift/AST/ASTScope.h" #include "swift/AST/ASTWalker.h" #include "swift/AST/ASTVisitor.h" #include "swift/AST/DiagnosticsSema.h" @@ -465,6 +465,24 @@ static bool typeCheckConditionForStatement(LabeledConditionalStmt *stmt, for (auto &elt : cond) { if (elt.getKind() == StmtConditionElement::CK_Availability) { hadAnyFalsable = true; + + // Reject inlinable code using availability macros. + PoundAvailableInfo *info = elt.getAvailability(); + if (auto *decl = dc->getAsDecl()) { + if (decl->getAttrs().hasAttribute() || + decl->getAttrs().hasAttribute()) + for (auto queries : info->getQueries()) + if (auto availSpec = + dyn_cast(queries)) + if (availSpec->getMacroLoc().isValid()) { + Context.Diags.diagnose( + availSpec->getMacroLoc(), + swift::diag::availability_macro_in_inlinable, + decl->getDescriptiveKind()); + break; + } + } + continue; } @@ -504,6 +522,8 @@ static bool typeCheckConditionForStatement(LabeledConditionalStmt *stmt, } elt.setPattern(pattern); + TypeChecker::diagnoseDuplicateBoundVars(pattern); + // Check the pattern, it allows unspecified types because the pattern can // provide type information. auto contextualPattern = ContextualPattern::forRawPattern(pattern, dc); @@ -657,7 +677,7 @@ class StmtChecker : public StmtVisitor { if (S2 == nullptr) return true; S = S2; - performStmtDiagnostics(getASTContext(), S); + performStmtDiagnostics(S, DC); return false; } @@ -779,9 +799,8 @@ class StmtChecker : public StmtVisitor { } } - auto exprTy = TypeChecker::typeCheckExpression(E, DC, - ResultTy, - ctp, options); + auto exprTy = + TypeChecker::typeCheckExpression(E, DC, {ResultTy, ctp}, options); RS->setResult(E); if (!exprTy) { @@ -842,8 +861,7 @@ class StmtChecker : public StmtVisitor { } TypeChecker::typeCheckExpression(exprToCheck, DC, - contextType, - contextTypePurpose); + {contextType, contextTypePurpose}); // Propagate the change into the inout expression we stripped before. if (inout) { @@ -865,10 +883,9 @@ class StmtChecker : public StmtVisitor { Type exnType = getASTContext().getErrorDecl()->getDeclaredInterfaceType(); if (!exnType) return TS; - TypeChecker::typeCheckExpression(E, DC, exnType, - CTP_ThrowStmt); + TypeChecker::typeCheckExpression(E, DC, {exnType, CTP_ThrowStmt}); TS->setSubExpr(E); - + return TS; } @@ -910,7 +927,7 @@ class StmtChecker : public StmtVisitor { Stmt *visitGuardStmt(GuardStmt *GS) { typeCheckConditionForStatement(GS, DC); - Stmt *S = GS->getBody(); + BraceStmt *S = GS->getBody(); typeCheckStmt(S); GS->setBody(S); return GS; @@ -956,6 +973,8 @@ class StmtChecker : public StmtVisitor { if (TypeChecker::typeCheckForEachBinding(DC, S)) return nullptr; + TypeChecker::diagnoseDuplicateBoundVars(S->getPattern()); + // Type-check the body of the loop. auto sourceFile = DC->getParentSourceFile(); checkLabeledStmtShadowing(getASTContext(), sourceFile, S); @@ -1032,6 +1051,8 @@ class StmtChecker : public StmtVisitor { } labelItem.setPattern(pattern, /*resolved=*/true); + TypeChecker::diagnoseDuplicateBoundVars(pattern); + // Otherwise for each variable in the pattern, make sure its type is // identical to the initial case decl and stash the previous case decl as // the parent of the decl. @@ -1144,7 +1165,7 @@ class StmtChecker : public StmtVisitor { getASTContext(), caseBlock, limitExhaustivityChecks); } - Stmt *body = caseBlock->getBody(); + BraceStmt *body = caseBlock->getBody(); limitExhaustivityChecks |= typeCheckStmt(body); caseBlock->setBody(body); } @@ -1513,7 +1534,7 @@ void StmtChecker::typeCheckASTNode(ASTNode &node) { } auto resultTy = - TypeChecker::typeCheckExpression(E, DC, Type(), CTP_Unused, options); + TypeChecker::typeCheckExpression(E, DC, /*contextualInfo=*/{}, options); // If a closure expression is unused, the user might have intended to write // "do { ... }". @@ -1586,14 +1607,14 @@ void TypeChecker::typeCheckASTNode(ASTNode &node, DeclContext *DC, stmtChecker.typeCheckASTNode(node); } -static Type getFunctionBuilderType(FuncDecl *FD) { - Type builderType = FD->getFunctionBuilderType(); +static Type getResultBuilderType(FuncDecl *FD) { + Type builderType = FD->getResultBuilderType(); // For getters, fall back on looking on the attribute on the storage. if (!builderType) { auto accessor = dyn_cast(FD); if (accessor && accessor->getAccessorKind() == AccessorKind::Get) { - builderType = accessor->getStorage()->getFunctionBuilderType(); + builderType = accessor->getStorage()->getResultBuilderType(); } } @@ -1613,9 +1634,8 @@ static Expr* constructCallToSuperInit(ConstructorDecl *ctor, r = new (Context) TryExpr(SourceLoc(), r, Type(), /*implicit=*/true); DiagnosticSuppression suppression(ctor->getASTContext().Diags); - auto resultTy = - TypeChecker::typeCheckExpression(r, ctor, Type(), CTP_Unused, - TypeCheckExprFlags::IsDiscarded); + auto resultTy = TypeChecker::typeCheckExpression( + r, ctor, /*contextualInfo=*/{}, TypeCheckExprFlags::IsDiscarded); if (!resultTy) return nullptr; @@ -1666,7 +1686,7 @@ static bool checkSuperInit(ConstructorDecl *fromCtor, for (auto decl : lookupResults) { auto superclassCtor = dyn_cast(decl); - if (!superclassCtor || !superclassCtor->isDesignatedInit() || + if (!superclassCtor || !superclassCtor->isDesignatedInit() || superclassCtor == ctor) continue; @@ -1676,12 +1696,9 @@ static bool checkSuperInit(ConstructorDecl *fromCtor, } // Make sure we can reference the designated initializer correctly. - auto fragileKind = fromCtor->getFragileFunctionKind(); - if (fragileKind.kind != FragileFunctionKind::None) { - TypeChecker::diagnoseInlinableDeclRef( - fromCtor->getLoc(), ctor, fromCtor, - fragileKind); - } + diagnoseDeclAvailability( + ctor, fromCtor->getLoc(), + ExportContext::forFunctionBody(fromCtor)); } @@ -1704,30 +1721,30 @@ static void checkClassConstructorBody(ClassDecl *classDecl, ASTContext &ctx = classDecl->getASTContext(); bool wantSuperInitCall = false; bool isDelegating = false; - ApplyExpr *initExpr = nullptr; - switch (ctor->getDelegatingOrChainedInitKind(&ctx.Diags, &initExpr)) { - case ConstructorDecl::BodyInitKind::Delegating: + auto initKindAndExpr = ctor->getDelegatingOrChainedInitKind(); + switch (initKindAndExpr.initKind) { + case BodyInitKind::Delegating: isDelegating = true; wantSuperInitCall = false; break; - case ConstructorDecl::BodyInitKind::Chained: - checkSuperInit(ctor, initExpr, false); + case BodyInitKind::Chained: + checkSuperInit(ctor, initKindAndExpr.initExpr, false); /// A convenience initializer cannot chain to a superclass constructor. if (ctor->isConvenienceInit()) { - ctx.Diags.diagnose(initExpr->getLoc(), + ctx.Diags.diagnose(initKindAndExpr.initExpr->getLoc(), diag::delegating_convenience_super_init, ctor->getDeclContext()->getDeclaredInterfaceType()); } LLVM_FALLTHROUGH; - case ConstructorDecl::BodyInitKind::None: + case BodyInitKind::None: wantSuperInitCall = false; break; - case ConstructorDecl::BodyInitKind::ImplicitChained: + case BodyInitKind::ImplicitChained: wantSuperInitCall = true; break; } @@ -1743,7 +1760,7 @@ static void checkClassConstructorBody(ClassDecl *classDecl, .fixItInsert(ctor->getLoc(), "convenience "); } - ctx.Diags.diagnose(initExpr->getLoc(), diag::delegation_here); + ctx.Diags.diagnose(initKindAndExpr.initExpr->getLoc(), diag::delegation_here); } // An inlinable constructor in a class must always be delegating, @@ -1891,13 +1908,15 @@ bool TypeCheckASTNodeAtLocRequest::evaluate(Evaluator &evaluator, if (auto *AFD = dyn_cast(DC)) { if (AFD->isBodyTypeChecked()) return false; + + ASTScope::expandFunctionBody(AFD); } // Function builder function doesn't support partial type checking. if (auto *func = dyn_cast(DC)) { - if (Type builderType = getFunctionBuilderType(func)) { + if (Type builderType = getResultBuilderType(func)) { auto optBody = - TypeChecker::applyFunctionBuilderBodyTransform(func, builderType); + TypeChecker::applyResultBuilderBodyTransform(func, builderType); if (!optBody || !*optBody) return true; // Wire up the function body now. @@ -1956,9 +1975,9 @@ TypeCheckFunctionBodyRequest::evaluate(Evaluator &evaluator, bool alreadyTypeChecked = false; if (auto *func = dyn_cast(AFD)) { - if (Type builderType = getFunctionBuilderType(func)) { + if (Type builderType = getResultBuilderType(func)) { if (auto optBody = - TypeChecker::applyFunctionBuilderBodyTransform( + TypeChecker::applyResultBuilderBodyTransform( func, builderType)) { if (!*optBody) return errorBody(); @@ -2046,7 +2065,7 @@ TypeCheckFunctionBodyRequest::evaluate(Evaluator &evaluator, } bool TypeChecker::typeCheckClosureBody(ClosureExpr *closure) { - checkParameterAttributes(closure->getParameters()); + TypeChecker::checkParameterList(closure->getParameters(), closure); BraceStmt *body = closure->getBody(); diff --git a/lib/Sema/TypeCheckStorage.cpp b/lib/Sema/TypeCheckStorage.cpp index 69525ed1881e7..5194d610c16f2 100644 --- a/lib/Sema/TypeCheckStorage.cpp +++ b/lib/Sema/TypeCheckStorage.cpp @@ -30,6 +30,7 @@ #include "swift/AST/ParameterList.h" #include "swift/AST/Pattern.h" #include "swift/AST/PropertyWrappers.h" +#include "swift/AST/ProtocolConformance.h" #include "swift/AST/SourceFile.h" #include "swift/AST/TypeCheckRequests.h" #include "swift/AST/Types.h" @@ -113,6 +114,21 @@ static void computeLoweredStoredProperties(NominalTypeDecl *decl) { if (var->hasAttachedPropertyWrapper()) (void) var->getPropertyWrapperBackingProperty(); } + + // If this is an actor class, check conformance to the Actor protocol to + // ensure that the actor storage will get created (if needed). + if (auto classDecl = dyn_cast(decl)) { + if (classDecl->isActor()) { + ASTContext &ctx = decl->getASTContext(); + if (auto actorProto = ctx.getProtocol(KnownProtocolKind::Actor)) { + SmallVector conformances; + classDecl->lookupConformance( + decl->getModuleContext(), actorProto, conformances); + for (auto conformance : conformances) + TypeChecker::checkConformance(conformance->getRootNormalConformance()); + } + } + } } ArrayRef @@ -128,10 +144,16 @@ StoredPropertiesRequest::evaluate(Evaluator &evaluator, if (isa(decl->getModuleScopeContext())) computeLoweredStoredProperties(decl); + ASTContext &ctx = decl->getASTContext(); for (auto *member : decl->getMembers()) { if (auto *var = dyn_cast(member)) - if (!var->isStatic() && var->hasStorage()) - results.push_back(var); + if (!var->isStatic() && var->hasStorage()) { + // Actor storage always goes at the beginning. + if (var->getName() == ctx.Id_actorStorage) + results.insert(results.begin(), var); + else + results.push_back(var); + } } return decl->getASTContext().AllocateCopy(results); @@ -763,10 +785,11 @@ static Expr *buildStorageReference(AccessorDecl *accessor, if (accessor->getAccessorKind() == AccessorKind::Get || accessor->getAccessorKind() == AccessorKind::Read) { if (wrappedValue->getAttrs().getUnavailable(ctx)) { + ExportContext where = ExportContext::forDeclSignature(var); diagnoseExplicitUnavailability( wrappedValue, var->getAttachedPropertyWrappers()[i]->getRangeWithAt(), - var->getDeclContext(), nullptr); + where, nullptr); } } @@ -808,12 +831,32 @@ static Expr *buildStorageReference(AccessorDecl *accessor, } } + // If the base is not 'self', default get access to nonmutating and set access to mutating. + bool getterMutatesBase = selfDecl && storage->isGetterMutating(); + bool setterMutatesBase = !selfDecl || storage->isSetterMutating(); + // If we're not accessing via a property wrapper, we don't need to adjust + // the mutability. + if (target == TargetImpl::Wrapper || target == TargetImpl::WrapperStorage) { + auto var = cast(accessor->getStorage()); + auto mutability = var->getPropertyWrapperMutability(); + // Only adjust mutability if it's possible to mutate the base. + if (mutability && !var->isStatic() && + !(selfDecl && selfTypeForAccess->hasReferenceSemantics())) { + getterMutatesBase = (mutability->Getter == PropertyWrapperMutability::Mutating); + setterMutatesBase = (mutability->Setter == PropertyWrapperMutability::Mutating); + } + } + + // If the accessor is mutating, then the base should be referred as an l-value + bool isBaseLValue = (getterMutatesBase && isUsedForGetAccess) || + (setterMutatesBase && isUsedForSetAccess); + if (!selfDecl) { assert(target != TargetImpl::Super); auto *storageDRE = new (ctx) DeclRefExpr(storage, DeclNameLoc(), /*IsImplicit=*/true, semantics); auto type = storage->getValueInterfaceType().subst(subs); - if (isLValue) + if (isBaseLValue) type = LValueType::get(type); storageDRE->setType(type); @@ -821,32 +864,9 @@ static Expr *buildStorageReference(AccessorDecl *accessor, } // Build self - - bool isGetterMutating = storage->isGetterMutating(); - bool isSetterMutating = storage->isSetterMutating(); - // If we're not accessing via a property wrapper, we don't need to adjust - // the mutability. - if (target == TargetImpl::Wrapper || target == TargetImpl::WrapperStorage) { - auto var = cast(accessor->getStorage()); - if (auto mutability = var->getPropertyWrapperMutability()) { - // We consider the storage's mutability too because the wrapped property - // might be part of a class, in case of which nothing is mutating. - isGetterMutating = (mutability->Getter == PropertyWrapperMutability::Mutating) - ? (storage->isGetterMutating() || storage->isSetterMutating()) - : storage->isGetterMutating(); - isSetterMutating = (mutability->Setter == PropertyWrapperMutability::Mutating) - ? (storage->isGetterMutating() || storage->isSetterMutating()) - : storage->isGetterMutating(); - } - } - - // If the accessor is mutating, then self should be referred as an l-value - bool isSelfLValue = (isGetterMutating && isUsedForGetAccess) || - (isSetterMutating && isUsedForSetAccess); - - Expr *selfDRE = buildSelfReference(selfDecl, selfAccessKind, isSelfLValue, + Expr *selfDRE = buildSelfReference(selfDecl, selfAccessKind, isBaseLValue, /*convertTy*/ selfTypeForAccess); - if (isSelfLValue) + if (isBaseLValue) selfTypeForAccess = LValueType::get(selfTypeForAccess); if (!selfDRE->getType()->isEqual(selfTypeForAccess)) { @@ -2735,6 +2755,8 @@ PropertyWrapperBackingPropertyInfoRequest::evaluate(Evaluator &evaluator, initializer); pbd->setInit(0, initializer); pbd->setInitializerChecked(0); + } else if (var->hasObservers() && !dc->isTypeContext()) { + var->diagnose(diag::observingprop_requires_initializer); } if (var->getOpaqueResultTypeDecl()) { @@ -3165,3 +3187,71 @@ StorageImplInfoRequest::evaluate(Evaluator &evaluator, return info; } + +bool SimpleDidSetRequest::evaluate(Evaluator &evaluator, + AccessorDecl *decl) const { + + class OldValueFinder : public ASTWalker { + const ParamDecl *OldValueParam; + bool foundOldValueRef = false; + + public: + OldValueFinder(const ParamDecl *param) : OldValueParam(param) {} + + virtual std::pair walkToExprPre(Expr *E) override { + if (!E) + return {true, E}; + if (auto DRE = dyn_cast(E)) { + if (auto decl = DRE->getDecl()) { + if (decl == OldValueParam) { + foundOldValueRef = true; + return {false, nullptr}; + } + } + } + + return {true, E}; + } + + bool didFindOldValueRef() { return foundOldValueRef; } + }; + + // If this is not a didSet accessor, bail out. + if (decl->getAccessorKind() != AccessorKind::DidSet) { + return false; + } + + // Always assume non-simple 'didSet' in code completion mode. + if (decl->getASTContext().SourceMgr.hasCodeCompletionBuffer()) + return false; + + // didSet must have a single parameter. + if (decl->getParameters()->size() != 1) { + return false; + } + + auto param = decl->getParameters()->get(0); + // If this parameter is not implicit, then it means it has been explicitly + // provided by the user (i.e. 'didSet(oldValue)'). This means we cannot + // consider this a "simple" didSet because we have to fetch the oldValue + // regardless of whether it's referenced in the body or not. + if (!param->isImplicit()) { + return false; + } + + // If we find a reference to the implicit 'oldValue' parameter, then it is + // not a "simple" didSet because we need to fetch it. + auto walker = OldValueFinder(param); + decl->getTypecheckedBody()->walk(walker); + auto hasOldValueRef = walker.didFindOldValueRef(); + if (!hasOldValueRef) { + // If the body does not refer to implicit 'oldValue', it means we can + // consider this as a "simple" didSet. Let's also erase the implicit + // oldValue as it is never used. + auto &ctx = decl->getASTContext(); + decl->setParameters(ParameterList::createEmpty(ctx)); + return true; + } + return false; +} + diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index 06d0a3ba8eb1e..7a0222ed73b51 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -575,7 +575,6 @@ static TypeResolutionOptions adjustOptionsForGenericArgs(TypeResolutionOptions options) { options.setContext(None); options -= TypeResolutionFlags::SILType; - options -= TypeResolutionFlags::AllowUnavailableProtocol; return options; } @@ -813,13 +812,6 @@ static Type applyGenericArguments(Type type, TypeResolution resolution, const auto result = TypeChecker::applyUnboundGenericArguments( decl, unboundType->getParent(), loc, resolution, args); - const auto genericOptions = genericResolution.getOptions(); - if (!genericOptions.contains(TypeResolutionFlags::AllowUnavailable)) { - if (genericOptions.isAnyExpr() || dc->getParent()->isLocalContext()) - if (dc->getResilienceExpansion() == ResilienceExpansion::Minimal) - TypeChecker::diagnoseGenericTypeExportability(loc, result, dc); - } - // Migration hack. bool isMutablePointer; if (isPointerToVoid(dc->getASTContext(), result, isMutablePointer)) { @@ -1321,8 +1313,11 @@ static Type resolveTopLevelIdentTypeComponent(TypeResolution resolution, } } + NameLookupOptions lookupOptions = defaultUnqualifiedLookupOptions; + if (options.contains(TypeResolutionFlags::AllowUsableFromInline)) + lookupOptions |= NameLookupFlags::IncludeUsableFromInline; auto globals = TypeChecker::lookupUnqualifiedType(DC, id, comp->getLoc(), - defaultUnqualifiedLookupOptions); + lookupOptions); // Process the names we found. Type current; @@ -1400,7 +1395,7 @@ static Type resolveTopLevelIdentTypeComponent(TypeResolution resolution, return ErrorType::get(ctx); return diagnoseUnknownType(resolution, nullptr, SourceRange(), comp, - defaultUnqualifiedLookupOptions); + lookupOptions); } comp->setValue(currentDecl, currentDC); @@ -1522,11 +1517,13 @@ static Type resolveNestedIdentTypeComponent(TypeResolution resolution, // Phase 1: Find and bind the component decl. // Look for member types with the given name. + NameLookupOptions lookupOptions = defaultMemberLookupOptions; + if (options.contains(TypeResolutionFlags::AllowUsableFromInline)) + lookupOptions |= NameLookupFlags::IncludeUsableFromInline; LookupTypeResult memberTypes; if (parentTy->mayHaveMembers()) - memberTypes = TypeChecker::lookupMemberType(DC, parentTy, - comp->getNameRef(), - defaultMemberLookupOptions); + memberTypes = TypeChecker::lookupMemberType( + DC, parentTy, comp->getNameRef(), lookupOptions); // Name lookup was ambiguous. Complain. // FIXME: Could try to apply generic arguments first, and see whether @@ -1549,7 +1546,7 @@ static Type resolveNestedIdentTypeComponent(TypeResolution resolution, return ErrorType::get(ctx); memberType = diagnoseUnknownType(resolution, parentTy, parentRange, comp, - defaultMemberLookupOptions); + lookupOptions); member = comp->getBoundDecl(); if (!member) return ErrorType::get(ctx); @@ -1593,26 +1590,6 @@ resolveIdentTypeComponent(TypeResolution resolution, comp); } -static bool diagnoseAvailability(IdentTypeRepr *IdType, - DeclContext *DC, - bool AllowPotentiallyUnavailableProtocol) { - DeclAvailabilityFlags flags = - DeclAvailabilityFlag::ContinueOnPotentialUnavailability; - if (AllowPotentiallyUnavailableProtocol) - flags |= DeclAvailabilityFlag::AllowPotentiallyUnavailableProtocol; - auto componentRange = IdType->getComponentRange(); - for (auto comp : componentRange) { - if (auto *typeDecl = comp->getBoundDecl()) { - if (diagnoseDeclAvailability(typeDecl, DC, - comp->getNameLoc().getSourceRange(), flags)) { - return true; - } - } - } - - return false; -} - // Hack to apply context-specific @escaping to an AST function type. static Type applyNonEscapingIfNecessary(Type ty, TypeResolutionOptions options) { @@ -1900,9 +1877,6 @@ NeverNullType TypeResolver::resolveType(TypeRepr *repr, options.setContext(None); } - if (getASTContext().LangOpts.DisableAvailabilityChecking) - options |= TypeResolutionFlags::AllowUnavailable; - bool isDirect = false; if ((options & TypeResolutionFlags::Direct) && !isa(repr)){ isDirect = true; @@ -3325,24 +3299,6 @@ Type TypeResolver::resolveIdentifierType(IdentTypeRepr *IdType, if (result->is()) result = applyNonEscapingIfNecessary(result, options); - // Check the availability of the type. - - // We allow a type to conform to a protocol that is less available than - // the type itself. This enables a type to retroactively model or directly - // conform to a protocol only available on newer OSes and yet still be used on - // older OSes. - // To support this, inside inheritance clauses we allow references to - // protocols that are unavailable in the current type refinement context. - - if (!options.contains(TypeResolutionFlags::SilenceErrors) && - !options.contains(TypeResolutionFlags::AllowUnavailable) && - diagnoseAvailability( - IdType, getDeclContext(), - options.contains(TypeResolutionFlags::AllowUnavailableProtocol))) { - Components.back()->setInvalid(); - return ErrorType::get(getASTContext()); - } - return result; } @@ -3973,7 +3929,7 @@ Type CustomAttrTypeRequest::evaluate(Evaluator &eval, CustomAttr *attr, OpenUnboundGenericTypeFn unboundTyOpener = nullptr; // Property delegates allow their type to be an unbound generic. - if (typeKind == CustomAttrTypeKind::PropertyDelegate) { + if (typeKind == CustomAttrTypeKind::PropertyWrapper) { unboundTyOpener = [](auto unboundTy) { // FIXME: Don't let unbound generic types // escape type resolution. For now, just diff --git a/lib/Sema/TypeCheckType.h b/lib/Sema/TypeCheckType.h index ab6f58b1de4b6..82906e00544d3 100644 --- a/lib/Sema/TypeCheckType.h +++ b/lib/Sema/TypeCheckType.h @@ -17,6 +17,7 @@ #define SWIFT_SEMA_TYPE_CHECK_TYPE_H #include "swift/AST/Type.h" +#include "swift/AST/Types.h" #include "swift/AST/TypeResolutionStage.h" #include "llvm/ADT/None.h" @@ -35,34 +36,31 @@ enum class TypeResolutionFlags : uint16_t { /// Whether to allow unspecified types within a pattern. AllowUnspecifiedTypes = 1 << 0, - /// Whether an unavailable protocol can be referenced. - AllowUnavailableProtocol = 1 << 1, - - /// Whether we should allow references to unavailable types. - AllowUnavailable = 1 << 2, - /// Whether the given type can override the type of a typed pattern. - OverrideType = 1 << 3, + OverrideType = 1 << 1, /// Whether we are validating the type for SIL. // FIXME: Move this flag to TypeResolverContext. - SILType = 1 << 4, + SILType = 1 << 2, /// Whether we are parsing a SIL file. Not the same as SILType, /// because the latter is not set if we're parsing an AST type. - SILMode = 1 << 5, + SILMode = 1 << 3, /// Whether this is a resolution based on a non-inferred type pattern. - FromNonInferredPattern = 1 << 6, + FromNonInferredPattern = 1 << 4, /// Whether we are at the direct base of a type expression. - Direct = 1 << 7, + Direct = 1 << 5, /// Whether we should not produce diagnostics if the type is invalid. - SilenceErrors = 1 << 8, + SilenceErrors = 1 << 6, /// Whether to allow module declaration types. - AllowModule = 1 << 9, + AllowModule = 1 << 7, + + /// Make internal @usableFromInline and @inlinable decls visible. + AllowUsableFromInline = 1 << 8, }; /// Type resolution contexts that require special handling. diff --git a/lib/Sema/TypeChecker.cpp b/lib/Sema/TypeChecker.cpp index 8f0fb2626f421..9d6f9b02d6753 100644 --- a/lib/Sema/TypeChecker.cpp +++ b/lib/Sema/TypeChecker.cpp @@ -16,7 +16,6 @@ //===----------------------------------------------------------------------===// #include "swift/Subsystems.h" -#include "ConstraintSystem.h" #include "TypeChecker.h" #include "TypeCheckObjC.h" #include "TypeCheckType.h" diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index e907fcc73a8da..69cd68a675482 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -22,12 +22,14 @@ #include "swift/AST/AnyFunctionRef.h" #include "swift/AST/Availability.h" #include "swift/AST/DiagnosticsSema.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/KnownProtocols.h" #include "swift/AST/LazyResolver.h" #include "swift/AST/NameLookup.h" #include "swift/AST/TypeRefinementContext.h" #include "swift/Parse/Lexer.h" #include "swift/Basic/OptionSet.h" +#include "swift/Sema/ConstraintSystem.h" #include "swift/Config.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/TinyPtrVector.h" @@ -35,14 +37,17 @@ namespace swift { +class ExportContext; class GenericSignatureBuilder; class NominalTypeDecl; class NormalProtocolConformance; +class RootProtocolConformance; class TypeResolution; class TypeResolutionOptions; class TypoCorrectionResults; class ExprPattern; enum class TypeResolutionStage : uint8_t; +enum class ExportabilityReason : unsigned; namespace constraints { enum class ConstraintKind : char; @@ -111,48 +116,6 @@ class LookupTypeResult { } }; -/// This specifies the purpose of the contextual type, when specified to -/// typeCheckExpression. This is used for diagnostic generation to produce more -/// specified error messages when the conversion fails. -/// -enum ContextualTypePurpose { - CTP_Unused, ///< No contextual type is specified. - CTP_Initialization, ///< Pattern binding initialization. - CTP_ReturnStmt, ///< Value specified to a 'return' statement. - CTP_ReturnSingleExpr, ///< Value implicitly returned from a function. - CTP_YieldByValue, ///< By-value yield operand. - CTP_YieldByReference, ///< By-reference yield operand. - CTP_ThrowStmt, ///< Value specified to a 'throw' statement. - CTP_EnumCaseRawValue, ///< Raw value specified for "case X = 42" in enum. - CTP_DefaultParameter, ///< Default value in parameter 'foo(a : Int = 42)'. - - /// Default value in @autoclosure parameter - /// 'foo(a : @autoclosure () -> Int = 42)'. - CTP_AutoclosureDefaultParameter, - - CTP_CalleeResult, ///< Constraint is placed on the result of a callee. - CTP_CallArgument, ///< Call to function or operator requires type. - CTP_ClosureResult, ///< Closure result expects a specific type. - CTP_ArrayElement, ///< ArrayExpr wants elements to have a specific type. - CTP_DictionaryKey, ///< DictionaryExpr keys should have a specific type. - CTP_DictionaryValue, ///< DictionaryExpr values should have a specific type. - CTP_CoerceOperand, ///< CoerceExpr operand coerced to specific type. - CTP_AssignSource, ///< AssignExpr source operand coerced to result type. - CTP_SubscriptAssignSource, ///< AssignExpr source operand coerced to subscript - ///< result type. - CTP_Condition, ///< Condition expression of various statements e.g. - ///< `if`, `for`, `while` etc. - CTP_ForEachStmt, ///< "expression/sequence" associated with 'for-in' loop - ///< is expected to conform to 'Sequence' protocol. - CTP_WrappedProperty, ///< Property type expected to match 'wrappedValue' type - CTP_ComposedPropertyWrapper, ///< Composed wrapper type expected to match - ///< former 'wrappedValue' type - - CTP_CannotFail, ///< Conversion can never fail. abort() if it does. -}; - - - /// Flags that can be used to control type checking. enum class TypeCheckExprFlags { /// Whether we know that the result of the expression is discarded. This @@ -192,6 +155,8 @@ enum class NameLookupFlags { /// Whether to include results from outside the innermost scope that has a /// result. IncludeOuterResults = 1 << 1, + // Whether to include results that are marked @inlinable or @usableFromInline. + IncludeUsableFromInline = 1 << 2, }; /// A set of options that control name lookup. @@ -222,17 +187,6 @@ enum class Comparison { Worse }; -/// Specify how we handle the binding of underconstrained (free) type variables -/// within a solution to a constraint system. -enum class FreeTypeVariableBinding { - /// Disallow any binding of such free type variables. - Disallow, - /// Allow the free type variables to persist in the solution. - Allow, - /// Bind the type variables to UnresolvedType to represent the ambiguity. - UnresolvedType -}; - /// A conditional conformance that implied some other requirements. That is, \c /// ConformingType conforming to \c Protocol may have required additional /// requirements to be satisfied. @@ -471,14 +425,14 @@ Expr *substituteInputSugarTypeForResult(ApplyExpr *E); void typeCheckASTNode(ASTNode &node, DeclContext *DC, bool LeaveBodyUnchecked = false); -/// Try to apply the function builder transform of the given builder type +/// Try to apply the result builder transform of the given builder type /// to the body of the function. /// /// \returns \c None if the builder transformation cannot be applied at all, /// e.g., because of a \c return statement. Otherwise, returns either the /// fully type-checked body of the function (on success) or a \c nullptr /// value if an error occurred while type checking the transformed body. -Optional applyFunctionBuilderBodyTransform(FuncDecl *func, +Optional applyResultBuilderBodyTransform(FuncDecl *func, Type builderType); /// Find the return statements within the body of the given function. @@ -497,7 +451,11 @@ void typeCheckDecl(Decl *D); void addImplicitDynamicAttribute(Decl *D); void checkDeclAttributes(Decl *D); -void checkParameterAttributes(ParameterList *params); +void checkParameterList(ParameterList *params, DeclContext *owner); + +void diagnoseDuplicateBoundVars(Pattern *pattern); + +void diagnoseDuplicateCaptureVars(CaptureListExpr *expr); Type checkReferenceOwnershipAttr(VarDecl *D, Type interfaceType, ReferenceOwnershipAttr *attr); @@ -597,21 +555,18 @@ Expr *findLHS(DeclContext *DC, Expr *E, Identifier name); /// \param expr The expression to type-check, which will be modified in /// place. /// -/// \param convertTypePurpose When convertType is specified, this indicates +/// \param contextualInfo The type that the expression is being converted to, +/// or null if the expression is standalone. When convertType is specified, this indicates /// what the conversion is doing. This allows diagnostics generation to /// produce more specific and helpful error messages when the conversion fails /// to be possible. /// -/// \param convertType The type that the expression is being converted to, -/// or null if the expression is standalone. -/// /// \param options Options that control how type checking is performed. /// /// \returns The type of the top-level expression, or Type() if an /// error occurred. Type typeCheckExpression(Expr *&expr, DeclContext *dc, - Type convertType = Type(), - ContextualTypePurpose convertTypePurpose = CTP_Unused, + constraints::ContextualTypeInfo contextualInfo = {}, TypeCheckExprOptions options = TypeCheckExprOptions()); Optional @@ -992,32 +947,26 @@ DeclName getObjectLiteralConstructorName(ASTContext &ctx, /// we're parsing the standard library. ModuleDecl *getStdlibModule(const DeclContext *dc); -/// \name Resilience diagnostics -bool diagnoseInlinableDeclRef(SourceLoc loc, ConcreteDeclRef declRef, - const DeclContext *DC, FragileFunctionKind Kind); - Expr *buildDefaultInitializer(Type type); +/// \name Resilience diagnostics + bool diagnoseInlinableDeclRefAccess(SourceLoc loc, const ValueDecl *D, - const DeclContext *DC, - FragileFunctionKind Kind); + ExportContext where); /// Given that a declaration is used from a particular context which /// exposes it in the interface of the current module, diagnose if it cannot /// reasonably be shared. -bool diagnoseDeclRefExportability(SourceLoc loc, ConcreteDeclRef declRef, - const DeclContext *DC, - FragileFunctionKind fragileKind); +bool diagnoseDeclRefExportability(SourceLoc loc, + const ValueDecl *D, + ExportContext where); -/// Given that a type is used from a particular context which -/// exposes it in the interface of the current module, diagnose if its -/// generic arguments require the use of conformances that cannot reasonably -/// be shared. -/// -/// This method \e only checks how generic arguments are used; it is assumed -/// that the declarations involved have already been checked elsewhere. -void diagnoseGenericTypeExportability(SourceLoc loc, Type type, - const DeclContext *DC); +/// Given that a conformance is used from a particular context which +/// exposes it in the interface of the current module, diagnose if the +/// conformance is SPI or visible via an implementation-only import. +bool diagnoseConformanceExportability(SourceLoc loc, + const RootProtocolConformance *rootConf, + ExportContext where); /// \name Availability checking /// @@ -1056,17 +1005,6 @@ TypeRefinementContext *getOrBuildTypeRefinementContext(SourceFile *SF); Optional> diagnosticIfDeclCannotBePotentiallyUnavailable(const Decl *D); -/// Checks whether a declaration is available when referred to at the given -/// location (this reference location must be in the passed-in -/// reference DeclContext). -/// If the declaration is available, return true. -/// If the declaration is not available, return false and write the -/// declaration's availability info to the out parameter -/// \p OutAvailableRange. -bool isDeclAvailable(const Decl *D, SourceLoc referenceLoc, - const DeclContext *referenceDC, - AvailabilityContext &OutAvailableRange); - /// Checks whether a declaration should be considered unavailable when /// referred to at the given location and, if so, returns the reason why the /// declaration is unavailable. Returns None is the declaration is @@ -1088,14 +1026,6 @@ void diagnosePotentialUnavailability(const ValueDecl *D, const DeclContext *ReferenceDC, const UnavailabilityReason &Reason); -// Emits a diagnostic, if necessary, for a reference to a declaration -// that is potentially unavailable at the given source location, using -// Name as the diagnostic name. -void diagnosePotentialUnavailability(const Decl *D, DeclName Name, - SourceRange ReferenceRange, - const DeclContext *ReferenceDC, - const UnavailabilityReason &Reason); - void diagnosePotentialOpaqueTypeUnavailability(SourceRange ReferenceRange, const DeclContext *ReferenceDC, @@ -1116,7 +1046,7 @@ const AvailableAttr *getDeprecated(const Decl *D); /// Callers can provide a lambda that adds additional information (such as a /// fixit hint) to the deprecation diagnostic, if it is emitted. void diagnoseIfDeprecated(SourceRange SourceRange, - const DeclContext *ReferenceDC, + ExportContext Where, const ValueDecl *DeprecatedDecl, const ApplyExpr *Call); /// @} @@ -1221,7 +1151,7 @@ bool requireArrayLiteralIntrinsics(ASTContext &ctx, SourceLoc loc); /// an \c UnresolvedMemberExpr, \c nullptr is returned. UnresolvedMemberExpr *getUnresolvedMemberChainBase(Expr *expr); -/// Checks whether a function builder type has a well-formed function builder +/// Checks whether a result builder type has a well-formed result builder /// method with the given name. If provided and non-empty, the argument labels /// are verified against any candidates. bool typeSupportsBuilderOp(Type builderType, DeclContext *dc, Identifier fnName, diff --git a/lib/Serialization/CMakeLists.txt b/lib/Serialization/CMakeLists.txt index 9a795e3a9b64d..c10fa09a159f4 100644 --- a/lib/Serialization/CMakeLists.txt +++ b/lib/Serialization/CMakeLists.txt @@ -1,3 +1,4 @@ +set_swift_llvm_is_available() add_swift_host_library(swiftSerialization STATIC Deserialization.cpp DeserializeSIL.cpp diff --git a/lib/Serialization/DeclTypeRecordNodes.def b/lib/Serialization/DeclTypeRecordNodes.def index 2ab062799f926..783a1da57d4af 100644 --- a/lib/Serialization/DeclTypeRecordNodes.def +++ b/lib/Serialization/DeclTypeRecordNodes.def @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -194,6 +194,8 @@ OTHER(CLANG_TYPE, 253) OTHER(DERIVATIVE_FUNCTION_CONFIGURATION, 254) +OTHER(BUILTIN_PROTOCOL_CONFORMANCE, 255) + #undef RECORD #undef DECLTYPERECORDNODES_HAS_RECORD_VAL #undef RECORD_VAL diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index 96789352635da..077124df3dc5c 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -562,6 +562,34 @@ ModuleFile::readConformanceChecked(llvm::BitstreamCursor &Cursor, return ProtocolConformanceRef(conformance); } + case BUILTIN_PROTOCOL_CONFORMANCE: { + TypeID conformingTypeID; + DeclID protoID; + size_t numConformances; + BuiltinProtocolConformanceLayout::readRecord(scratch, conformingTypeID, + protoID, numConformances); + + Type conformingType = getType(conformingTypeID); + + auto decl = getDeclChecked(protoID); + if (!decl) + return decl.takeError(); + + auto proto = cast(decl.get()); + + // Read the conformances. + SmallVector conformances; + conformances.reserve(numConformances); + for (unsigned i : range(numConformances)) { + (void)i; + conformances.push_back(readConformance(Cursor)); + } + + auto conformance = getContext().getBuiltinConformance(conformingType, proto, + conformances); + return ProtocolConformanceRef(conformance); + } + case NORMAL_PROTOCOL_CONFORMANCE_ID: { NormalConformanceID conformanceID; NormalProtocolConformanceIdLayout::readRecord(scratch, conformanceID); @@ -4136,6 +4164,14 @@ llvm::Error DeclDeserializer::deserializeDeclAttributes() { break; } + case decls_block::ActorIndependent_DECL_ATTR: { + unsigned kind; + serialization::decls_block::ActorIndependentDeclAttrLayout::readRecord( + scratch, kind); + Attr = new (ctx) ActorIndependentAttr((ActorIndependentKind)kind); + break; + } + case decls_block::Optimize_DECL_ATTR: { unsigned kind; serialization::decls_block::OptimizeDeclAttrLayout::readRecord( @@ -4255,18 +4291,48 @@ llvm::Error DeclDeserializer::deserializeDeclAttributes() { unsigned specializationKindVal; GenericSignatureID specializedSigID; + ArrayRef rawPieceIDs; + uint64_t numArgs; + uint64_t numSPIGroups; + DeclID targetFunID; + serialization::decls_block::SpecializeDeclAttrLayout::readRecord( - scratch, exported, specializationKindVal, specializedSigID); + scratch, exported, specializationKindVal, specializedSigID, + targetFunID, numArgs, numSPIGroups, rawPieceIDs); + assert(rawPieceIDs.size() == numArgs + numSPIGroups || + rawPieceIDs.size() == (numArgs - 1 + numSPIGroups)); specializationKind = specializationKindVal ? SpecializeAttr::SpecializationKind::Partial : SpecializeAttr::SpecializationKind::Full; + // The 'target' parameter. + DeclNameRef replacedFunctionName; + if (numArgs) { + bool numArgumentLabels = (numArgs == 1) ? 0 : numArgs - 2; + auto baseName = MF.getDeclBaseName(rawPieceIDs[0]); + SmallVector pieces; + if (numArgumentLabels) { + for (auto pieceID : rawPieceIDs.slice(1, numArgumentLabels)) + pieces.push_back(MF.getIdentifier(pieceID)); + } + replacedFunctionName = (numArgs == 1) + ? DeclNameRef({baseName}) // simple name + : DeclNameRef({ctx, baseName, pieces}); + } + + SmallVector spis; + if (numSPIGroups) { + auto numTargetFunctionPiecesToSkip = + (rawPieceIDs.size() == numArgs + numSPIGroups) ? numArgs + : numArgs - 1; + for (auto id : rawPieceIDs.slice(numTargetFunctionPiecesToSkip)) + spis.push_back(MF.getIdentifier(id)); + } auto specializedSig = MF.getGenericSignature(specializedSigID); - Attr = SpecializeAttr::create(ctx, SourceLoc(), SourceRange(), - nullptr, exported != 0, - specializationKind, - specializedSig); + Attr = SpecializeAttr::create(ctx, exported != 0, specializationKind, + spis, specializedSig, + replacedFunctionName, &MF, targetFunID); break; } @@ -5799,6 +5865,9 @@ class SwiftToClangBasicReader : llvm::Expected ModuleFile::getClangType(ClangTypeID TID) { + if (!getContext().LangOpts.UseClangFunctionTypes) + return nullptr; + if (TID == 0) return nullptr; @@ -6025,6 +6094,13 @@ ModuleFile::loadReferencedFunctionDecl(const DerivativeAttr *DA, return cast(getDecl(contextData)); } +ValueDecl *ModuleFile::loadTargetFunctionDecl(const SpecializeAttr *attr, + uint64_t contextData) { + if (contextData == 0) + return nullptr; + return cast(getDecl(contextData)); +} + Type ModuleFile::loadTypeEraserType(const TypeEraserAttr *TRA, uint64_t contextData) { return getType(contextData); diff --git a/lib/Serialization/DeserializeSIL.cpp b/lib/Serialization/DeserializeSIL.cpp index b3024b0939170..6b5cff8a157b3 100644 --- a/lib/Serialization/DeserializeSIL.cpp +++ b/lib/Serialization/DeserializeSIL.cpp @@ -683,6 +683,7 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn, } // Read and instantiate the specialize attributes. + bool shouldAddAtttributes = fn->getSpecializeAttrs().empty(); while (numSpecAttrs--) { llvm::Expected maybeNext = SILCursor.advance(AF_DontPopBlockAtEnd); @@ -701,18 +702,37 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn, unsigned exported; unsigned specializationKindVal; GenericSignatureID specializedSigID; - SILSpecializeAttrLayout::readRecord(scratch, exported, - specializationKindVal, - specializedSigID); + IdentifierID targetFunctionID; + IdentifierID spiGroupID; + ModuleID spiModuleID; + SILSpecializeAttrLayout::readRecord( + scratch, exported, specializationKindVal, specializedSigID, + targetFunctionID, spiGroupID, spiModuleID); + + SILFunction *target = nullptr; + if (targetFunctionID) { + target = getFuncForReference(MF->getIdentifier(targetFunctionID).str()); + } + + Identifier spiGroup; + const ModuleDecl *spiModule = nullptr; + if (spiGroupID) { + spiGroup = MF->getIdentifier(spiGroupID); + spiModule = MF->getModule(spiModuleID); + } + SILSpecializeAttr::SpecializationKind specializationKind = specializationKindVal ? SILSpecializeAttr::SpecializationKind::Partial : SILSpecializeAttr::SpecializationKind::Full; auto specializedSig = MF->getGenericSignature(specializedSigID); - - // Read the substitution list and construct a SILSpecializeAttr. - fn->addSpecializeAttr(SILSpecializeAttr::create( - SILMod, specializedSig, exported != 0, specializationKind)); + // Only add the specialize attributes once. + if (shouldAddAtttributes) { + // Read the substitution list and construct a SILSpecializeAttr. + fn->addSpecializeAttr(SILSpecializeAttr::create( + SILMod, specializedSig, exported != 0, specializationKind, target, + spiGroup, spiModule)); + } } GenericEnvironment *genericEnv = nullptr; @@ -1202,6 +1222,21 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, ResultInst = Builder.createMetatype( Loc, getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn)); break; + + case SILInstructionKind::GetAsyncContinuationInst: + assert(RecordKind == SIL_ONE_TYPE && "Layout should be OneType."); + ResultInst = Builder.createGetAsyncContinuation(Loc, + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn)); + break; + + case SILInstructionKind::GetAsyncContinuationAddrInst: + assert(RecordKind == SIL_ONE_TYPE_ONE_OPERAND + && "Layout should be OneTypeOneOperand."); + ResultInst = Builder.createGetAsyncContinuationAddr(Loc, + getLocalValue(ValID, getSILType(MF->getType(TyID2), + (SILValueCategory)TyCategory2, Fn)), + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn)); + break; #define ONETYPE_ONEOPERAND_INST(ID) \ case SILInstructionKind::ID##Inst: \ @@ -2157,6 +2192,21 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, getBBForReference(Fn, ListOfValues[2]), FalseArgs); break; } + case SILInstructionKind::AwaitAsyncContinuationInst: { + // Format: continuation, resume block ID, error block ID if given + SILValue Cont = getLocalValue( + ListOfValues[0], + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn)); + + SILBasicBlock *resultBB = getBBForReference(Fn, ListOfValues[1]); + SILBasicBlock *errorBB = nullptr; + if (ListOfValues.size() >= 3) { + errorBB = getBBForReference(Fn, ListOfValues[2]); + } + + ResultInst = Builder.createAwaitAsyncContinuation(Loc, Cont, resultBB, errorBB); + break; + } case SILInstructionKind::SwitchEnumInst: case SILInstructionKind::SwitchEnumAddrInst: { // Format: condition, a list of cases (EnumElementDecl + Basic Block ID), diff --git a/lib/Serialization/ModuleDependencyScanner.cpp b/lib/Serialization/ModuleDependencyScanner.cpp index 6e4f82b6178ed..a24cb856a3d63 100644 --- a/lib/Serialization/ModuleDependencyScanner.cpp +++ b/lib/Serialization/ModuleDependencyScanner.cpp @@ -98,8 +98,7 @@ std::error_code PlaceholderSwiftModuleScanner::findModuleFilesInDirectory( static std::vector getCompiledCandidates(ASTContext &ctx, StringRef moduleName, StringRef interfacePath) { - return static_cast(ctx - .getModuleInterfaceLoader())->getCompiledModuleCandidatesForInterface( + return ctx.getModuleInterfaceChecker()->getCompiledModuleCandidatesForInterface( moduleName.str(), interfacePath); } @@ -150,8 +149,8 @@ ErrorOr ModuleDependencyScanner::scanInterfaceFile( // Collect implicitly imported modules in case they are not explicitly // printed in the interface file, e.g. SwiftOnoneSupport. auto &imInfo = mainMod->getImplicitImportInfo(); - for (auto name: imInfo.ModuleNames) { - Result->addModuleDependency(name.str(), &alreadyAddedModules); + for (auto import: imInfo.AdditionalUnloadedImports) { + Result->addModuleDependency(import.module.getModulePath(), &alreadyAddedModules); } return std::error_code(); }); @@ -167,10 +166,13 @@ Optional SerializedModuleLoaderBase::getModuleDependencies( InterfaceSubContextDelegate &delegate) { // Check whether we've cached this result. if (auto found = cache.findDependencies( - moduleName, ModuleDependenciesKind::Swift)) + moduleName, ModuleDependenciesKind::SwiftTextual)) return found; - if (auto found = - cache.findDependencies(moduleName, ModuleDependenciesKind::SwiftPlaceholder)) + if (auto found = cache.findDependencies( + moduleName, ModuleDependenciesKind::SwiftBinary)) + return found; + if (auto found = cache.findDependencies( + moduleName, ModuleDependenciesKind::SwiftPlaceholder)) return found; auto moduleId = Ctx.getIdentifier(moduleName); @@ -192,8 +194,7 @@ Optional SerializedModuleLoaderBase::getModuleDependencies( for (auto &scanner : scanners) { if (scanner->canImportModule({moduleId, SourceLoc()})) { // Record the dependencies. - cache.recordDependencies(moduleName, *(scanner->dependencies), - scanner->dependencyKind); + cache.recordDependencies(moduleName, *(scanner->dependencies)); return std::move(scanner->dependencies); } } diff --git a/lib/Serialization/ModuleFile.cpp b/lib/Serialization/ModuleFile.cpp index 5bd056555121f..31e559be687bf 100644 --- a/lib/Serialization/ModuleFile.cpp +++ b/lib/Serialization/ModuleFile.cpp @@ -167,8 +167,8 @@ Status ModuleFile::associateWithFileContext(FileUnit *file, SourceLoc diagLoc) { return error(Status::FailedToLoadBridgingHeader); } ModuleDecl *importedHeaderModule = clangImporter->getImportedHeaderModule(); - dependency.Import = ModuleDecl::ImportedModule{ImportPath::Access(), - importedHeaderModule}; + dependency.Import = ImportedModule{ImportPath::Access(), + importedHeaderModule}; continue; } @@ -212,7 +212,7 @@ Status ModuleFile::associateWithFileContext(FileUnit *file, SourceLoc diagLoc) { continue; } - dependency.Import = ModuleDecl::ImportedModule{accessPath, module}; + dependency.Import = ImportedModule{accessPath, module}; // SPI StringRef spisStr = dependency.Core.RawSPIs; @@ -420,9 +420,8 @@ PrecedenceGroupDecl *ModuleFile::lookupPrecedenceGroup(Identifier name) { return cast(getDecl(data[0].second)); } -void ModuleFile::getImportedModules( - SmallVectorImpl &results, - ModuleDecl::ImportFilter filter) { +void ModuleFile::getImportedModules(SmallVectorImpl &results, + ModuleDecl::ImportFilter filter) { PrettyStackTraceModuleFile stackEntry(*this); for (auto &dep : Dependencies) { diff --git a/lib/Serialization/ModuleFile.h b/lib/Serialization/ModuleFile.h index baec8af6adcba..3a5b3d5a0a5bd 100644 --- a/lib/Serialization/ModuleFile.h +++ b/lib/Serialization/ModuleFile.h @@ -90,7 +90,7 @@ class ModuleFile public: const ModuleFileSharedCore::Dependency &Core; - llvm::Optional Import = llvm::None; + llvm::Optional Import = llvm::None; SmallVector spiGroups; Dependency(const ModuleFileSharedCore::Dependency &coreDependency) @@ -465,6 +465,9 @@ class ModuleFile return Core->Bits.IsImplicitDynamicEnabled; } + /// \c true if this module has incremental dependency information. + bool hasIncrementalInfo() const { return Core->hasIncrementalInfo(); } + /// Associates this module file with the AST node representing it. /// /// Checks that the file is compatible with the AST module it's being loaded @@ -524,7 +527,7 @@ class ModuleFile PrecedenceGroupDecl *lookupPrecedenceGroup(Identifier name); /// Adds any imported modules to the given vector. - void getImportedModules(SmallVectorImpl &results, + void getImportedModules(SmallVectorImpl &results, ModuleDecl::ImportFilter filter); void getImportDecls(SmallVectorImpl &Results); @@ -655,6 +658,8 @@ class ModuleFile loadDynamicallyReplacedFunctionDecl(const DynamicReplacementAttr *DRA, uint64_t contextData) override; + virtual ValueDecl *loadTargetFunctionDecl(const SpecializeAttr *attr, + uint64_t contextData) override; virtual AbstractFunctionDecl * loadReferencedFunctionDecl(const DerivativeAttr *DA, uint64_t contextData) override; diff --git a/lib/Serialization/ModuleFileSharedCore.cpp b/lib/Serialization/ModuleFileSharedCore.cpp index f72b3924923c6..2d7c4c5f249d4 100644 --- a/lib/Serialization/ModuleFileSharedCore.cpp +++ b/lib/Serialization/ModuleFileSharedCore.cpp @@ -1427,6 +1427,17 @@ ModuleFileSharedCore::ModuleFileSharedCore( break; } + case INCREMENTAL_INFORMATION_BLOCK_ID: { + HasIncrementalInfo = true; + // Skip incremental info if present. The Frontend currently doesn't do + // anything with this. + if (cursor.SkipBlock()) { + info.status = error(Status::Malformed); + return; + } + break; + } + default: // Unknown top-level block, possibly for use by a future version of the // module format. diff --git a/lib/Serialization/ModuleFileSharedCore.h b/lib/Serialization/ModuleFileSharedCore.h index 17e60d85e687d..0bdceb68303f9 100644 --- a/lib/Serialization/ModuleFileSharedCore.h +++ b/lib/Serialization/ModuleFileSharedCore.h @@ -69,10 +69,13 @@ class ModuleFileSharedCore { /// The data blob containing all of the module's identifiers. StringRef IdentifierData; - // Full blob from the misc. version field of the metadata block. This should - // include the version string of the compiler that built the module. + /// Full blob from the misc. version field of the metadata block. This should + /// include the version string of the compiler that built the module. StringRef MiscVersion; + /// \c true if this module has incremental dependency information. + bool HasIncrementalInfo = false; + public: /// Represents another module that has been imported as a dependency. class Dependency { @@ -490,6 +493,10 @@ class ModuleFileSharedCore { ArrayRef getDependencies() const { return Dependencies; } + + /// Returns \c true if this module file contains a section with incremental + /// information. + bool hasIncrementalInfo() const { return HasIncrementalInfo; } }; template diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 9213caae40c06..eb6f35d1a66fe 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -20,10 +20,11 @@ #define SWIFT_SERIALIZATION_MODULEFORMAT_H #include "swift/AST/Decl.h" +#include "swift/AST/FineGrainedDependencyFormat.h" #include "swift/AST/Types.h" +#include "llvm/ADT/PointerEmbeddedInt.h" #include "llvm/Bitcode/RecordLayout.h" #include "llvm/Bitstream/BitCodes.h" -#include "llvm/ADT/PointerEmbeddedInt.h" namespace swift { namespace serialization { @@ -55,7 +56,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0; /// describe what change you made. The content of this comment isn't important; /// it just ensures a conflict if two people change the module format. /// Don't worry about adhering to the 80-column limit for this line. -const uint16_t SWIFTMODULE_VERSION_MINOR = 578; // Remove hasNonPatternBindingInit +const uint16_t SWIFTMODULE_VERSION_MINOR = 584; // builtin protocol conformances /// A standard hash seed used for all string hashes in a serialized module. /// @@ -724,11 +725,19 @@ enum BlockID { /// This is part of a stable format and should not be renumbered. /// /// Though we strive to keep the format stable, breaking the format of - /// .swiftsourceinfo doesn't have consequences as serious as breaking the format - /// of .swiftdoc because .swiftsourceinfo file is for local development use only. + /// .swiftsourceinfo doesn't have consequences as serious as breaking the + /// format + /// of .swiftdoc because .swiftsourceinfo file is for local development use + /// only. /// /// \sa decl_locs_block DECL_LOCS_BLOCK_ID, + + /// The incremental dependency information block. + /// + /// This is part of a stable format and should not be renumbered. + INCREMENTAL_INFORMATION_BLOCK_ID = + fine_grained_dependencies::INCREMENTAL_INFORMATION_BLOCK_ID, }; /// The record types within the control block. @@ -1619,6 +1628,14 @@ namespace decls_block { TypeIDField // the conforming type >; + using BuiltinProtocolConformanceLayout = BCRecordLayout< + BUILTIN_PROTOCOL_CONFORMANCE, + TypeIDField, // the conforming type + DeclIDField, // the protocol + BCVBR<5> // the number of element conformances + // the (optional) element conformances follow + >; + // Refers to a normal protocol conformance in the given module via its id. using NormalProtocolConformanceIdLayout = BCRecordLayout< NORMAL_PROTOCOL_CONFORMANCE_ID, @@ -1798,6 +1815,11 @@ namespace decls_block { BCFixed<2> // inline value >; + using ActorIndependentDeclAttrLayout = BCRecordLayout< + ActorIndependent_DECL_ATTR, + BCFixed<1> // unsafe flag + >; + using OptimizeDeclAttrLayout = BCRecordLayout< Optimize_DECL_ATTR, BCFixed<2> // optimize value @@ -1839,7 +1861,11 @@ namespace decls_block { Specialize_DECL_ATTR, BCFixed<1>, // exported flag BCFixed<1>, // specialization kind - GenericSignatureIDField // specialized signature + GenericSignatureIDField, // specialized signature + DeclIDField, // target function + BCVBR<4>, // # of arguments (+1) or 1 if simple decl name, 0 if no target + BCVBR<4>, // # of SPI groups + BCArray // target function pieces, spi groups >; using DifferentiableDeclAttrLayout = BCRecordLayout< diff --git a/lib/Serialization/SILFormat.h b/lib/Serialization/SILFormat.h index 696f06b0d82fa..28b60b3264c31 100644 --- a/lib/Serialization/SILFormat.h +++ b/lib/Serialization/SILFormat.h @@ -302,7 +302,10 @@ namespace sil_block { BCRecordLayout, // exported BCFixed<1>, // specialization kind - GenericSignatureIDField // specialized signature + GenericSignatureIDField, // specialized signature + DeclIDField, // Target SILFunction name or 0. + DeclIDField, // SPIGroup or 0. + DeclIDField // SPIGroup Module name id. >; // Has an optional argument list where each argument is a typed valueref. diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index 5d2e5f58ecab7..0dd961d3e5b59 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -689,6 +689,16 @@ IdentifierID Serializer::addContainingModuleRef(const DeclContext *DC) { return addDeclBaseNameRef(exportedModuleID); } +IdentifierID Serializer::addModuleRef(const ModuleDecl *module) { + if (module == this->M) + return CURRENT_MODULE_ID; + if (module == this->M->getASTContext().TheBuiltinModule) + return BUILTIN_MODULE_ID; + auto moduleName = + module->getASTContext().getIdentifier(module->getName().str()); + return addDeclBaseNameRef(moduleName); +} + SILLayoutID Serializer::addSILLayoutRef(const SILLayout *layout) { return SILLayoutsToSerialize.addRef(layout); } @@ -842,6 +852,8 @@ void Serializer::writeBlockInfoBlock() { decls_block::SPECIALIZED_PROTOCOL_CONFORMANCE); BLOCK_RECORD_WITH_NAMESPACE(sil_block, decls_block::INHERITED_PROTOCOL_CONFORMANCE); + BLOCK_RECORD_WITH_NAMESPACE(sil_block, + decls_block::BUILTIN_PROTOCOL_CONFORMANCE); BLOCK_RECORD_WITH_NAMESPACE(sil_block, decls_block::NORMAL_PROTOCOL_CONFORMANCE_ID); BLOCK_RECORD_WITH_NAMESPACE(sil_block, @@ -868,6 +880,13 @@ void Serializer::writeBlockInfoBlock() { BLOCK_RECORD(sil_index_block, SIL_DIFFERENTIABILITY_WITNESS_NAMES); BLOCK_RECORD(sil_index_block, SIL_DIFFERENTIABILITY_WITNESS_OFFSETS); + BLOCK(INCREMENTAL_INFORMATION_BLOCK); + BLOCK_RECORD(fine_grained_dependencies::record_block, METADATA); + BLOCK_RECORD(fine_grained_dependencies::record_block, SOURCE_FILE_DEP_GRAPH_NODE); + BLOCK_RECORD(fine_grained_dependencies::record_block, FINGERPRINT_NODE); + BLOCK_RECORD(fine_grained_dependencies::record_block, DEPENDS_ON_DEFINITION_NODE); + BLOCK_RECORD(fine_grained_dependencies::record_block, IDENTIFIER_NODE); + #undef BLOCK #undef BLOCK_RECORD } @@ -953,7 +972,7 @@ void Serializer::writeHeader(const SerializationOptions &options) { } } -static void flattenImportPath(const ModuleDecl::ImportedModule &import, +static void flattenImportPath(const ImportedModule &import, SmallVectorImpl &out) { llvm::raw_svector_ostream outStream(out); import.importedModule->getReverseFullModuleName().printForward( @@ -974,11 +993,10 @@ uint64_t getRawModTimeOrHash(const SerializationOptions::FileDependency &dep) { return dep.getModificationTime(); } -using ImportSet = llvm::SmallSet; +using ImportSet = llvm::SmallSet; static ImportSet getImportsAsSet(const ModuleDecl *M, ModuleDecl::ImportFilter filter) { - SmallVector imports; + SmallVector imports; M->getImportedModules(imports, filter); ImportSet importSet; importSet.insert(imports.begin(), imports.end()); @@ -987,7 +1005,7 @@ static ImportSet getImportsAsSet(const ModuleDecl *M, void Serializer::writeInputBlock(const SerializationOptions &options) { BCBlockRAII restoreBlock(Out, INPUT_BLOCK_ID, 4); - input_block::ImportedModuleLayout ImportedModule(Out); + input_block::ImportedModuleLayout importedModule(Out); input_block::ImportedModuleLayoutSPI ImportedModuleSPI(Out); input_block::LinkLibraryLayout LinkLibrary(Out); input_block::ImportedHeaderLayout ImportedHeader(Out); @@ -1031,13 +1049,13 @@ void Serializer::writeInputBlock(const SerializationOptions &options) { if (!options.ModuleInterface.empty()) ModuleInterface.emit(ScratchRecord, options.ModuleInterface); - SmallVector allImports; + SmallVector allImports; M->getImportedModules(allImports, {ModuleDecl::ImportFilterKind::Exported, ModuleDecl::ImportFilterKind::Default, ModuleDecl::ImportFilterKind::ImplementationOnly, ModuleDecl::ImportFilterKind::SPIAccessControl}); - ModuleDecl::removeDuplicateImports(allImports); + ImportedModule::removeDuplicates(allImports); // Collect the public and private imports as a subset so that we can // distinguish them. @@ -1046,17 +1064,13 @@ void Serializer::writeInputBlock(const SerializationOptions &options) { ImportSet privateImportSet = getImportsAsSet(M, ModuleDecl::ImportFilterKind::Default); ImportSet spiImportSet = - getImportsAsSet(M, { - ModuleDecl::ImportFilterKind::Exported, - ModuleDecl::ImportFilterKind::Default, - ModuleDecl::ImportFilterKind::SPIAccessControl - }); + getImportsAsSet(M, ModuleDecl::ImportFilterKind::SPIAccessControl); auto clangImporter = static_cast(M->getASTContext().getClangModuleLoader()); ModuleDecl *bridgingHeaderModule = clangImporter->getImportedHeaderModule(); - ModuleDecl::ImportedModule bridgingHeaderImport{ImportPath::Access(), - bridgingHeaderModule}; + ImportedModule bridgingHeaderImport{ImportPath::Access(), + bridgingHeaderModule}; // Make sure the bridging header module is always at the top of the import // list, mimicking how it is processed before any module imports when @@ -1104,7 +1118,7 @@ void Serializer::writeInputBlock(const SerializationOptions &options) { llvm::SmallSetVector spis; M->lookupImportedSPIGroups(import.importedModule, spis); - ImportedModule.emit(ScratchRecord, + importedModule.emit(ScratchRecord, static_cast(stableImportControl), !import.accessPath.empty(), !spis.empty(), importPath); @@ -1497,6 +1511,17 @@ Serializer::writeConformance(ProtocolConformanceRef conformanceRef, writeConformance(conf->getInheritedConformance(), abbrCodes, genericEnv); break; } + case ProtocolConformanceKind::Builtin: + auto builtin = cast(conformance); + unsigned abbrCode = abbrCodes[BuiltinProtocolConformanceLayout::Code]; + auto typeID = addTypeRef(builtin->getType()); + auto protocolID = addDeclRef(builtin->getProtocol()); + BuiltinProtocolConformanceLayout::emitRecord(Out, ScratchRecord, abbrCode, + typeID, protocolID, + builtin->getConformances().size()); + + writeConformances(builtin->getConformances(), abbrCodes); + break; } } @@ -2285,6 +2310,14 @@ class Serializer::DeclSerializer : public DeclVisitor { return; } + case DAK_ActorIndependent: { + auto *theAttr = cast(DA); + auto abbrCode = S.DeclTypeAbbrCodes[ActorIndependentDeclAttrLayout::Code]; + ActorIndependentDeclAttrLayout::emitRecord(S.Out, S.ScratchRecord, + abbrCode, (unsigned)theAttr->getKind()); + return; + } + case DAK_Optimize: { auto *theAttr = cast(DA); auto abbrCode = S.DeclTypeAbbrCodes[OptimizeDeclAttrLayout::Code]; @@ -2363,13 +2396,39 @@ class Serializer::DeclSerializer : public DeclVisitor { case DAK_Specialize: { auto abbrCode = S.DeclTypeAbbrCodes[SpecializeDeclAttrLayout::Code]; - auto SA = cast(DA); + auto attr = cast(DA); + auto targetFun = attr->getTargetFunctionName(); + auto *targetFunDecl = attr->getTargetFunctionDecl(cast(D)); + + SmallVector pieces; + + // encodes whether this a a simple or compound name by adding one. + size_t numArgs = 0; + if (targetFun) { + pieces.push_back(S.addDeclBaseNameRef(targetFun.getBaseName())); + for (auto argName : targetFun.getArgumentNames()) + pieces.push_back(S.addDeclBaseNameRef(argName)); + if (targetFun.isSimpleName()) { + assert(pieces.size() == 1); + numArgs = 1; + } else + numArgs = pieces.size() + 1; + } + + for (auto spi : attr->getSPIGroups()) { + assert(!spi.empty() && "Empty SPI name"); + pieces.push_back(S.addDeclBaseNameRef(spi)); + } + + auto numSPIGroups = attr->getSPIGroups().size(); + assert(pieces.size() == numArgs + numSPIGroups || + pieces.size() == (numArgs - 1 + numSPIGroups)); SpecializeDeclAttrLayout::emitRecord( - S.Out, S.ScratchRecord, abbrCode, - (unsigned)SA->isExported(), - (unsigned)SA->getSpecializationKind(), - S.addGenericSignatureRef(SA->getSpecializedSignature())); + S.Out, S.ScratchRecord, abbrCode, (unsigned)attr->isExported(), + (unsigned)attr->getSpecializationKind(), + S.addGenericSignatureRef(attr->getSpecializedSignature()), + S.addDeclRef(targetFunDecl), numArgs, numSPIGroups, pieces); return; } @@ -2496,11 +2555,11 @@ class Serializer::DeclSerializer : public DeclVisitor { auto *storage = dyn_cast(value); auto access = value->getFormalAccess(); - // Emit the private descriminator for private decls. + // Emit the private discriminator for private decls. // FIXME: We shouldn't need to encode this for /all/ private decls. // In theory we can follow the same rules as mangling and only include // the outermost private context. - bool shouldEmitPrivateDescriminator = + bool shouldEmitPrivateDiscriminator = access <= swift::AccessLevel::FilePrivate && !value->getDeclContext()->isLocalContext(); @@ -2514,10 +2573,10 @@ class Serializer::DeclSerializer : public DeclVisitor { storage->getFormalAccess() >= swift::AccessLevel::Internal && storage->hasPrivateAccessor())); - if (shouldEmitFilenameForPrivate || shouldEmitPrivateDescriminator) { + if (shouldEmitFilenameForPrivate || shouldEmitPrivateDiscriminator) { auto topLevelContext = value->getDeclContext()->getModuleScopeContext(); if (auto *enclosingFile = dyn_cast(topLevelContext)) { - if (shouldEmitPrivateDescriminator) { + if (shouldEmitPrivateDiscriminator) { Identifier discriminator = enclosingFile->getDiscriminatorForPrivateValue(value); unsigned abbrCode = @@ -4147,7 +4206,10 @@ class Serializer::TypeSerializer : public TypeVisitor { using namespace decls_block; auto resultType = S.addTypeRef(fnTy->getResult()); - auto clangType = S.addClangTypeRef(fnTy->getClangTypeInfo().getType()); + auto clangType = + S.getASTContext().LangOpts.UseClangFunctionTypes + ? S.addClangTypeRef(fnTy->getClangTypeInfo().getType()) + : ClangTypeID(0); unsigned abbrCode = S.DeclTypeAbbrCodes[FunctionTypeLayout::Code]; FunctionTypeLayout::emitRecord(S.Out, S.ScratchRecord, abbrCode, @@ -4542,6 +4604,7 @@ void Serializer::writeAllDeclsAndTypes() { registerDeclTypeAbbr(); registerDeclTypeAbbr(); registerDeclTypeAbbr(); + registerDeclTypeAbbr(); registerDeclTypeAbbr(); registerDeclTypeAbbr(); @@ -5219,9 +5282,11 @@ SerializerBase::SerializerBase(ArrayRef signature, this->SF = DC.dyn_cast(); } -void Serializer::writeToStream(raw_ostream &os, ModuleOrSourceFile DC, - const SILModule *SILMod, - const SerializationOptions &options) { +void Serializer::writeToStream( + raw_ostream &os, ModuleOrSourceFile DC, + const SILModule *SILMod, + const SerializationOptions &options, + const fine_grained_dependencies::SourceFileDepGraph *DepGraph) { Serializer S{SWIFTMODULE_SIGNATURE, DC}; // FIXME: This is only really needed for debugging. We don't actually use it. @@ -5233,6 +5298,10 @@ void Serializer::writeToStream(raw_ostream &os, ModuleOrSourceFile DC, S.writeInputBlock(options); S.writeSIL(SILMod, options.SerializeAllSIL); S.writeAST(DC); + if (options.ExperimentalCrossModuleIncrementalInfo && DepGraph) { + fine_grained_dependencies::writeFineGrainedDependencyGraph( + S.Out, *DepGraph, fine_grained_dependencies::Purpose::ForSwiftModule); + } } S.writeToStream(os); @@ -5251,7 +5320,8 @@ void swift::serializeToBuffers( "Serialization, swiftmodule, to buffer"); llvm::SmallString<1024> buf; llvm::raw_svector_ostream stream(buf); - Serializer::writeToStream(stream, DC, M, options); + Serializer::writeToStream(stream, DC, M, options, + /*dependency info*/ nullptr); bool hadError = withOutputFile(getContext(DC).Diags, options.OutputPath, [&](raw_ostream &out) { @@ -5310,7 +5380,7 @@ void swift::serializeToMemory( llvm::NamedRegionTimer timer(name, name, "Swift", "Swift compilation"); llvm::SmallString<1024> buf; llvm::raw_svector_ostream stream(buf); - Serializer::writeToStream(stream, DC, M, options); + Serializer::writeToStream(stream, DC, M, options, nullptr); *moduleBuffer = std::make_unique(std::move(buf)); } @@ -5328,12 +5398,13 @@ void swift::serializeToMemory( void swift::serialize(ModuleOrSourceFile DC, const SerializationOptions &options, - const SILModule *M) { + const SILModule *M, + const fine_grained_dependencies::SourceFileDepGraph *DG) { assert(!StringRef::withNullAsEmpty(options.OutputPath).empty()); if (StringRef(options.OutputPath) == "-") { // Special-case writing to stdout. - Serializer::writeToStream(llvm::outs(), DC, M, options); + Serializer::writeToStream(llvm::outs(), DC, M, options, DG); assert(StringRef::withNullAsEmpty(options.DocOutputPath).empty()); return; } @@ -5343,7 +5414,7 @@ void swift::serialize(ModuleOrSourceFile DC, [&](raw_ostream &out) { FrontendStatsTracer tracer(getContext(DC).Stats, "Serialization, swiftmodule"); - Serializer::writeToStream(out, DC, M, options); + Serializer::writeToStream(out, DC, M, options, DG); return false; }); if (hadError) diff --git a/lib/Serialization/Serialization.h b/lib/Serialization/Serialization.h index 8529620ae9957..47ca711966245 100644 --- a/lib/Serialization/Serialization.h +++ b/lib/Serialization/Serialization.h @@ -34,6 +34,10 @@ namespace clang { namespace swift { class SILModule; + namespace fine_grained_dependencies { + class SourceFileDepGraph; + } + namespace serialization { using FilenamesTy = ArrayRef; @@ -389,14 +393,21 @@ class Serializer : public SerializerBase { /// Top-level entry point for serializing a module. void writeAST(ModuleOrSourceFile DC); + /// Serializes the given dependnecy graph into the incremental information + /// section of this swift module. + void writeIncrementalInfo( + const fine_grained_dependencies::SourceFileDepGraph *DepGraph); + using SerializerBase::SerializerBase; using SerializerBase::writeToStream; public: /// Serialize a module to the given stream. - static void writeToStream(raw_ostream &os, ModuleOrSourceFile DC, - const SILModule *M, - const SerializationOptions &options); + static void + writeToStream(raw_ostream &os, ModuleOrSourceFile DC, + const SILModule *M, + const SerializationOptions &options, + const fine_grained_dependencies::SourceFileDepGraph *DepGraph); /// Records the use of the given Type. /// @@ -493,6 +504,9 @@ class Serializer : public SerializerBase { /// \see FileUnit::getExportedModuleName IdentifierID addContainingModuleRef(const DeclContext *DC); + /// Records the module \m. + IdentifierID addModuleRef(const ModuleDecl *m); + /// Write a normal protocol conformance. void writeASTBlockEntity(const NormalProtocolConformance *conformance); diff --git a/lib/Serialization/SerializeSIL.cpp b/lib/Serialization/SerializeSIL.cpp index 7c81a0c7bf579..1f3d1aae2051c 100644 --- a/lib/Serialization/SerializeSIL.cpp +++ b/lib/Serialization/SerializeSIL.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 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -452,11 +452,23 @@ void SILSerializer::writeSILFunction(const SILFunction &F, bool DeclOnly) { for (auto *SA : F.getSpecializeAttrs()) { unsigned specAttrAbbrCode = SILAbbrCodes[SILSpecializeAttrLayout::Code]; + IdentifierID targetFunctionNameID = 0; + if (auto *target = SA->getTargetFunction()) { + addReferencedSILFunction(target, true); + targetFunctionNameID = S.addUniquedStringRef(target->getName()); + } + IdentifierID spiGroupID = 0; + IdentifierID spiModuleDeclID = 0; + auto ident = SA->getSPIGroup(); + if (!ident.empty()) { + spiGroupID = S.addUniquedStringRef(ident.str()); + spiModuleDeclID = S.addModuleRef(SA->getSPIModule()); + } SILSpecializeAttrLayout::emitRecord( - Out, ScratchRecord, specAttrAbbrCode, - (unsigned)SA->isExported(), + Out, ScratchRecord, specAttrAbbrCode, (unsigned)SA->isExported(), (unsigned)SA->getSpecializationKind(), - S.addGenericSignatureRef(SA->getSpecializedSignature())); + S.addGenericSignatureRef(SA->getSpecializedSignature()), + targetFunctionNameID, spiGroupID, spiModuleDeclID); } // Assign a unique ID to each basic block of the SILFunction. @@ -1148,6 +1160,26 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { ListOfValues); break; } + case SILInstructionKind::AwaitAsyncContinuationInst: { + const AwaitAsyncContinuationInst *AACI + = cast(&SI); + + // Format: continuation, resume block ID, error block ID if given + SmallVector ListOfValues; + + ListOfValues.push_back(addValueRef(AACI->getOperand())); + ListOfValues.push_back(BasicBlockMap[AACI->getResumeBB()]); + if (auto errorBB = AACI->getErrorBB()) { + ListOfValues.push_back(BasicBlockMap[errorBB]); + } + SILOneTypeValuesLayout::emitRecord(Out, ScratchRecord, + SILAbbrCodes[SILOneTypeValuesLayout::Code], + (unsigned)SI.getKind(), + S.addTypeRef(AACI->getOperand()->getType().getASTType()), + (unsigned)AACI->getOperand()->getType().getCategory(), + ListOfValues); + break; + } case SILInstructionKind::SwitchEnumInst: case SILInstructionKind::SwitchEnumAddrInst: { // Format: condition, a list of cases (EnumElementDecl + Basic Block ID), @@ -1546,6 +1578,17 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { open.getOperand()); break; } + case SILInstructionKind::GetAsyncContinuationAddrInst: { + auto &gaca = cast(SI); + writeOneTypeOneOperandLayout(gaca.getKind(), 0, gaca.getType(), + gaca.getOperand()); + break; + } + case SILInstructionKind::GetAsyncContinuationInst: { + auto &gaca = cast(SI); + writeOneTypeLayout(gaca.getKind(), 0, gaca.getType()); + break; + } // Conversion instructions (and others of similar form). #define LOADABLE_REF_STORAGE(Name, ...) \ case SILInstructionKind::RefTo##Name##Inst: \ @@ -1902,11 +1945,11 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { default: llvm_unreachable("Out of sync with parent switch"); case SILInstructionKind::TupleElementAddrInst: operand = cast(&SI)->getOperand(); - FieldNo = cast(&SI)->getFieldNo(); + FieldNo = cast(&SI)->getFieldIndex(); break; case SILInstructionKind::TupleExtractInst: operand = cast(&SI)->getOperand(); - FieldNo = cast(&SI)->getFieldNo(); + FieldNo = cast(&SI)->getFieldIndex(); break; } @@ -2689,6 +2732,7 @@ void SILSerializer::writeSILBlock(const SILModule *SILMod) { registerSILAbbr(); registerSILAbbr(); registerSILAbbr(); + registerSILAbbr(); registerSILAbbr(); registerSILAbbr(); registerSILAbbr(); diff --git a/lib/Serialization/SerializedModuleLoader.cpp b/lib/Serialization/SerializedModuleLoader.cpp index b35b2f6561889..11a7d962c1f43 100644 --- a/lib/Serialization/SerializedModuleLoader.cpp +++ b/lib/Serialization/SerializedModuleLoader.cpp @@ -394,8 +394,13 @@ llvm::ErrorOr SerializedModuleLoaderBase::scanModuleFile( nullptr, isFramework, loadedModuleFile); + const std::string moduleDocPath; + const std::string sourceInfoPath; // Map the set of dependencies over to the "module dependencies". - auto dependencies = ModuleDependencies::forSwiftModule(modulePath.str(), isFramework); + auto dependencies = ModuleDependencies::forSwiftBinaryModule(modulePath.str(), + moduleDocPath, + sourceInfoPath, + isFramework); llvm::StringSet<> addedModuleNames; for (const auto &dependency : loadedModuleFile->getDependencies()) { // FIXME: Record header dependency? @@ -714,6 +719,8 @@ LoadedFile *SerializedModuleLoaderBase::loadAST( M.setPrivateImportsEnabled(); if (loadedModuleFile->isImplicitDynamicEnabled()) M.setImplicitDynamicEnabled(); + if (loadedModuleFile->hasIncrementalInfo()) + M.setHasIncrementalInfo(); auto diagLocOrInvalid = diagLoc.getValueOr(SourceLoc()); loadInfo.status = @@ -971,13 +978,6 @@ SerializedModuleLoaderBase::loadModule(SourceLoc importLoc, isFramework, isSystemModule)) { return nullptr; } - if (dependencyTracker) { - // Don't record cached artifacts as dependencies. - StringRef DepPath = moduleInputBuffer->getBufferIdentifier(); - if (!isCached(DepPath)) { - dependencyTracker->addDependency(DepPath, /*isSystem=*/false); - } - } assert(moduleInputBuffer); @@ -995,6 +995,18 @@ SerializedModuleLoaderBase::loadModule(SourceLoc importLoc, } else { M->setFailedToLoad(); } + + if (dependencyTracker && file) { + auto DepPath = file->getFilename(); + // Don't record cached artifacts as dependencies. + if (!isCached(DepPath)) { + if (M->hasIncrementalInfo()) { + dependencyTracker->addIncrementalDependency(DepPath); + } else { + dependencyTracker->addDependency(DepPath, /*isSystem=*/false); + } + } + } return M; } @@ -1102,14 +1114,14 @@ void SerializedModuleLoaderBase::verifyAllModules() { //----------------------------------------------------------------------------- void SerializedASTFile::getImportedModules( - SmallVectorImpl &imports, + SmallVectorImpl &imports, ModuleDecl::ImportFilter filter) const { File.getImportedModules(imports, filter); } void SerializedASTFile::collectLinkLibrariesFromImports( ModuleDecl::LinkLibraryCallback callback) const { - llvm::SmallVector Imports; + llvm::SmallVector Imports; File.getImportedModules(Imports, {ModuleDecl::ImportFilterKind::Exported, ModuleDecl::ImportFilterKind::Default}); diff --git a/lib/SyntaxParse/CMakeLists.txt b/lib/SyntaxParse/CMakeLists.txt index b1b1c562988aa..230f230ebd68f 100644 --- a/lib/SyntaxParse/CMakeLists.txt +++ b/lib/SyntaxParse/CMakeLists.txt @@ -1,3 +1,4 @@ +set_swift_llvm_is_available() add_swift_host_library(swiftSyntaxParse STATIC RawSyntaxTokenCache.cpp SyntaxTreeCreator.cpp) diff --git a/lib/TBDGen/CMakeLists.txt b/lib/TBDGen/CMakeLists.txt index 06181211d4a67..60796eab5e73c 100644 --- a/lib/TBDGen/CMakeLists.txt +++ b/lib/TBDGen/CMakeLists.txt @@ -1,3 +1,4 @@ +set_swift_llvm_is_available() add_swift_host_library(swiftTBDGen STATIC TBDGen.cpp TBDGenRequests.cpp diff --git a/lib/TBDGen/TBDGen.cpp b/lib/TBDGen/TBDGen.cpp index e3523a683456a..76f84c9d51cae 100644 --- a/lib/TBDGen/TBDGen.cpp +++ b/lib/TBDGen/TBDGen.cpp @@ -664,6 +664,19 @@ void TBDGenVisitor::visitAbstractFunctionDecl(AbstractFunctionDecl *AFD) { return; } + // Add exported prespecialized symbols. + for (auto *attr : AFD->getAttrs().getAttributes()) { + if (!attr->isExported()) + continue; + if (auto *targetFun = attr->getTargetFunctionDecl(AFD)) { + auto declRef = SILDeclRef(targetFun, attr->getSpecializedSignature()); + addSymbol(declRef.mangle(), SymbolSource::forSILDeclRef(declRef)); + } else { + auto declRef = SILDeclRef(AFD, attr->getSpecializedSignature()); + addSymbol(declRef.mangle(), SymbolSource::forSILDeclRef(declRef)); + } + } + addSymbol(SILDeclRef(AFD)); // Add the global function pointer for a dynamically replaceable function. diff --git a/localization/CMakeLists.txt b/localization/CMakeLists.txt index 53c7e05e2a757..221fb264ee62d 100644 --- a/localization/CMakeLists.txt +++ b/localization/CMakeLists.txt @@ -19,4 +19,8 @@ add_dependencies(diagnostic-database swift-def-to-yaml-converter) swift_install_in_component( DIRECTORY ${CMAKE_BINARY_DIR}/share/swift/diagnostics/ DESTINATION "share/swift/diagnostics" - COMPONENT compiler) + COMPONENT compiler + FILES_MATCHING + PATTERN "*.db" + PATTERN "*.yaml" +) diff --git a/stdlib/CMakeLists.txt b/stdlib/CMakeLists.txt index 6a39ac804c01a..edf7c19f4b7a6 100644 --- a/stdlib/CMakeLists.txt +++ b/stdlib/CMakeLists.txt @@ -9,7 +9,7 @@ project(swift-stdlib LANGUAGES C CXX) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules") -if("${SWIFT_HOST_VARIANT_SDK}" STREQUAL "OSX") +if("${SWIFT_HOST_VARIANT_SDK}" MATCHES "(OSX|IOS*|TVOS*|WATCHOS*)") # All Darwin platforms have ABI stability. set(SWIFT_STDLIB_STABLE_ABI_default TRUE) elseif("${SWIFT_HOST_VARIANT_SDK}" STREQUAL "LINUX") @@ -38,33 +38,46 @@ else() set(SWIFT_STDLIB_STABLE_ABI_default FALSE) endif() +if(SWIFT_BUILD_SDK_OVERLAY OR SWIFT_INCLUDE_TESTS) + set(SWIFT_BUILD_TEST_SUPPORT_MODULES_default TRUE) +else() + set(SWIFT_BUILD_TEST_SUPPORT_MODULES_default FALSE) +endif() + # # User-configurable options for the standard library. # +# NOTE: Some of these variables are also initialized in StandaloneOverlay.cmake +# so that interfaces are emitted when overlays are separately built. + option(SWIFT_STDLIB_STABLE_ABI - "Should stdlib be built with stable ABI (library evolution, resilience)." - "${SWIFT_STDLIB_STABLE_ABI_default}") + "Should stdlib be built with stable ABI (library evolution, resilience)." + "${SWIFT_STDLIB_STABLE_ABI_default}") + +option(SWIFT_ENABLE_MODULE_INTERFACES + "Generate .swiftinterface files alongside .swiftmodule files" + "${SWIFT_STDLIB_STABLE_ABI}") option(SWIFT_ENABLE_COMPATIBILITY_OVERRIDES - "Support back-deploying compatibility fixes for newer apps running on older runtimes." - TRUE) + "Support back-deploying compatibility fixes for newer apps running on older runtimes." + TRUE) option(SWIFT_RUNTIME_MACHO_NO_DYLD - "Build stdlib assuming the runtime environment uses Mach-O but does not support dynamic modules." - FALSE) + "Build stdlib assuming the runtime environment uses Mach-O but does not support dynamic modules." + FALSE) option(SWIFT_STDLIB_SINGLE_THREADED_RUNTIME - "Build the standard libraries assuming that they will be used in an environment with only a single thread." - FALSE) + "Build the standard libraries assuming that they will be used in an environment with only a single thread." + FALSE) option(SWIFT_STDLIB_OS_VERSIONING - "Build stdlib with availability based on OS versions (Darwin only)." - TRUE) + "Build stdlib with availability based on OS versions (Darwin only)." + TRUE) -option(SWIFT_ENABLE_MODULE_INTERFACES - "Generate .swiftinterface files alongside .swiftmodule files" - "${SWIFT_STDLIB_STABLE_ABI}") +option(SWIFT_BUILD_TEST_SUPPORT_MODULES + "Whether to build StdlibUnittest and other test support modules. Defaults to On when SWIFT_BUILD_SDK_OVERLAY is On, or when SWIFT_INCLUDE_TESTS is On." + "${SWIFT_BUILD_TEST_SUPPORT_MODULES_default}") # # End of user-configurable options. diff --git a/stdlib/cmake/modules/AddSwiftStdlib.cmake b/stdlib/cmake/modules/AddSwiftStdlib.cmake index e7cfa587ece40..1d9f36e983182 100644 --- a/stdlib/cmake/modules/AddSwiftStdlib.cmake +++ b/stdlib/cmake/modules/AddSwiftStdlib.cmake @@ -998,7 +998,7 @@ function(_add_swift_target_library_single target name) elseif("${SWIFTLIB_SINGLE_SDK}" STREQUAL "LINUX") set_target_properties("${target}" PROPERTIES - INSTALL_RPATH "$ORIGIN:/usr/lib/swift/linux") + INSTALL_RPATH "$ORIGIN") elseif("${SWIFTLIB_SINGLE_SDK}" STREQUAL "CYGWIN") set_target_properties("${target}" PROPERTIES @@ -1438,6 +1438,9 @@ endfunction() # SWIFT_MODULE_DEPENDS_WATCHOS # Swift modules this library depends on when built for watchOS. # +# SWIFT_MODULE_DEPENDS_FREESTANDING +# Swift modules this library depends on when built for Freestanding. +# # SWIFT_MODULE_DEPENDS_FREEBSD # Swift modules this library depends on when built for FreeBSD. # @@ -1562,6 +1565,7 @@ function(add_swift_target_library name) SWIFT_MODULE_DEPENDS SWIFT_MODULE_DEPENDS_CYGWIN SWIFT_MODULE_DEPENDS_FREEBSD + SWIFT_MODULE_DEPENDS_FREESTANDING SWIFT_MODULE_DEPENDS_OPENBSD SWIFT_MODULE_DEPENDS_HAIKU SWIFT_MODULE_DEPENDS_IOS @@ -1719,6 +1723,9 @@ function(add_swift_target_library name) elseif(${sdk} STREQUAL WATCHOS OR ${sdk} STREQUAL WATCHOS_SIMULATOR) list(APPEND swiftlib_module_depends_flattened ${SWIFTLIB_SWIFT_MODULE_DEPENDS_WATCHOS}) + elseif(${sdk} STREQUAL FREESTANDING) + list(APPEND swiftlib_module_depends_flattened + ${SWIFTLIB_SWIFT_MODULE_DEPENDS_FREESTANDING}) elseif(${sdk} STREQUAL FREEBSD) list(APPEND swiftlib_module_depends_flattened ${SWIFTLIB_SWIFT_MODULE_DEPENDS_FREEBSD}) diff --git a/stdlib/include/llvm/Support/MathExtras.h b/stdlib/include/llvm/Support/MathExtras.h index 9f759dd869cf4..8b40e95f55f1f 100644 --- a/stdlib/include/llvm/Support/MathExtras.h +++ b/stdlib/include/llvm/Support/MathExtras.h @@ -881,7 +881,7 @@ extern const float huge_valf; /// Add two signed integers, computing the two's complement truncated result, -/// returning true if overflow occured. +/// returning true if overflow occurred. template std::enable_if_t::value, T> AddOverflow(T X, T Y, T &Result) { #if __has_builtin(__builtin_add_overflow) diff --git a/stdlib/private/CMakeLists.txt b/stdlib/private/CMakeLists.txt index b1cf2ed6b1b2f..1007fabd79b46 100644 --- a/stdlib/private/CMakeLists.txt +++ b/stdlib/private/CMakeLists.txt @@ -8,6 +8,9 @@ if(SWIFT_BUILD_SDK_OVERLAY) if(SWIFT_ENABLE_EXPERIMENTAL_DIFFERENTIABLE_PROGRAMMING) add_subdirectory(DifferentiationUnittest) endif() +endif() + +if(SWIFT_BUILD_SDK_OVERLAY OR SWIFT_BUILD_TEST_SUPPORT_MODULES) add_subdirectory(RuntimeUnittest) add_subdirectory(StdlibUnicodeUnittest) add_subdirectory(StdlibCollectionUnittest) @@ -18,7 +21,9 @@ if(SWIFT_BUILD_SDK_OVERLAY) # SwiftPrivateThreadExtras to ensure that the dependency targets are setup in # the correct order for Windows. add_subdirectory(StdlibUnittest) +endif() +if(SWIFT_BUILD_SDK_OVERLAY) add_subdirectory(OSLog) if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") diff --git a/stdlib/private/OSLog/CMakeLists.txt b/stdlib/private/OSLog/CMakeLists.txt index 7241543470fe8..2e37b642fb6c0 100644 --- a/stdlib/private/OSLog/CMakeLists.txt +++ b/stdlib/private/OSLog/CMakeLists.txt @@ -10,6 +10,9 @@ add_swift_target_library(swiftOSLogTestHelper OSLogStringTypes.swift OSLogNSObjectType.swift OSLogFloatingPointTypes.swift + OSLogSwiftProtocols.swift + OSLogPrivacy.swift + OSLogFloatFormatting.swift SWIFT_MODULE_DEPENDS_IOS Darwin ObjectiveC SWIFT_MODULE_DEPENDS_OSX Darwin ObjectiveC diff --git a/stdlib/private/OSLog/OSLogFloatFormatting.swift b/stdlib/private/OSLog/OSLogFloatFormatting.swift new file mode 100644 index 0000000000000..8397f849e5abb --- /dev/null +++ b/stdlib/private/OSLog/OSLogFloatFormatting.swift @@ -0,0 +1,426 @@ +//===--------------------------- OSLogFloatFormatting.swift ------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===------------------------------------------------------------------------------===// + +// This file defines types and functions for specifying formatting of +// floating-point typed interpolations passed to the os log APIs. + +@frozen +public struct OSLogFloatFormatting { + /// When set, a `+` will be printed for all non-negative floats. + @usableFromInline + internal var explicitPositiveSign: Bool + + /// Whether to use uppercase letters to represent numerals greater than 9 + /// (default is to use lowercase). This applies to hexadecimal digits, NaN, Inf, + /// the symbols E and X used to denote exponent and hex format. + @usableFromInline + internal var uppercase: Bool + + // Note: includePrefix is not supported for FloatFormatting. The format specifier %a + // always prints a prefix, %efg don't need one. + + /// Number of digits to display following the radix point. Hex notation does not accept + /// a precision. For non-hex notations, precision can be a dynamic value. The default + /// precision is 6 for non-hex notations. + @usableFromInline + internal var precision: (() -> Int)? + + @usableFromInline + internal enum Notation { + /// Hexadecimal formatting. + case hex + + /// fprintf's `%f` formatting. + /// + /// Prints all digits before the radix point, and `precision` digits following + /// the radix point. If `precision` is zero, the radix point is omitted. + /// + /// Note that very large floating-point values may print quite a lot of digits + /// when using this format, even if `precision` is zero--up to hundreds for + /// `Double`, and thousands for `Float80`. Note also that this format is + /// very likely to print non-zero values as all-zero. If these are a concern, use + /// `.exponential` or `.hybrid` instead. + /// + /// Systems may impose an upper bound on the number of digits that are + /// supported following the radix point. + case fixed + + /// fprintf's `%e` formatting. + /// + /// Prints the number in the form [-]d.ddd...dde±dd, with `precision` significant + /// digits following the radix point. Systems may impose an upper bound on the number + /// of digits that are supported. + case exponential + + /// fprintf's `%g` formatting. + /// + /// Behaves like `.fixed` when the number is scaled close to 1.0, and like + /// `.exponential` if it has a very large or small exponent. + case hybrid + } + + @usableFromInline + internal var notation: Notation + + @_transparent + @usableFromInline + internal init( + explicitPositiveSign: Bool = false, + uppercase: Bool = false, + precision: (() -> Int)?, + notation: Notation + ) { + self.explicitPositiveSign = explicitPositiveSign + self.uppercase = uppercase + self.precision = precision + self.notation = notation + } + + /// Displays an interpolated floating-point value in fprintf's `%f` format with + /// default precision. + /// + /// Prints all digits before the radix point, and 6 digits following the radix point. + /// Note also that this format is very likely to print non-zero values as all-zero. + /// + /// Note that very large floating-point values may print quite a lot of digits + /// when using this format --up to hundreds for `Double`. Note also that this + /// format is very likely to print non-zero values as all-zero. If these are a concern, + /// use `.exponential` or `.hybrid` instead. + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + public static var fixed: OSLogFloatFormatting { .fixed() } + + /// Displays an interpolated floating-point value in fprintf's `%f` format with + /// specified precision, and optional sign and case. + /// + /// Prints all digits before the radix point, and `precision` digits following + /// the radix point. If `precision` is zero, the radix point is omitted. + /// + /// Note that very large floating-point values may print quite a lot of digits + /// when using this format, even if `precision` is zero--up to hundreds for + /// `Double`. Note also that this format is very likely to print non-zero values as + /// all-zero. If these are a concern, use `.exponential` or `.hybrid` instead. + /// + /// Systems may impose an upper bound on the number of digits that are + /// supported following the radix point. + /// + /// All parameters to this function except `precision` must be boolean literals. + /// + /// - Parameters: + /// - precision: Number of digits to display after the radix point. + /// - explicitPositiveSign: Pass `true` to add a + sign to non-negative + /// numbers. + /// - uppercase: Pass `true` to use uppercase letters or `false` to use + /// lowercase letters. The default is `false`. + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + public static func fixed( + precision: @escaping @autoclosure () -> Int, + explicitPositiveSign: Bool = false, + uppercase: Bool = false + ) -> OSLogFloatFormatting { + return OSLogFloatFormatting( + explicitPositiveSign: explicitPositiveSign, + uppercase: uppercase, + precision: precision, + notation: .fixed + ) + } + + /// Displays an interpolated floating-point value in fprintf's `%f` format with + /// default precision, and optional sign and case. + /// + /// Prints all digits before the radix point, and 6 digits following the radix point. + /// Note also that this format is very likely to print non-zero values as all-zero. + /// + /// Note that very large floating-point values may print quite a lot of digits + /// when using this format, even if `precision` is zero--up to hundreds for + /// `Double`. Note also that this format is very likely to print non-zero values as + /// all-zero. If these are a concern, use `.exponential` or `.hybrid` instead. + /// + /// Systems may impose an upper bound on the number of digits that are + /// supported following the radix point. + /// + /// All parameters to this function must be boolean literals. + /// - Parameters: + /// - explicitPositiveSign: Pass `true` to add a + sign to non-negative + /// numbers. + /// - uppercase: Pass `true` to use uppercase letters or `false` to use + /// lowercase letters. The default is `false`. + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + public static func fixed( + explicitPositiveSign: Bool = false, + uppercase: Bool = false + ) -> OSLogFloatFormatting { + return OSLogFloatFormatting( + explicitPositiveSign: explicitPositiveSign, + uppercase: uppercase, + precision: nil, + notation: .fixed + ) + } + + /// Displays an interpolated floating-point value in hexadecimal format. + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + public static var hex: OSLogFloatFormatting { .hex() } + + /// Displays an interpolated floating-point value in hexadecimal format with + /// optional sign and case. + /// + /// All parameters to this function must be boolean literals. + /// + /// - Parameters: + /// - explicitPositiveSign: Pass `true` to add a + sign to non-negative + /// numbers. + /// - uppercase: Pass `true` to use uppercase letters or `false` to use + /// lowercase letters. The default is `false`. + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + public static func hex( + explicitPositiveSign: Bool = false, + uppercase: Bool = false + ) -> OSLogFloatFormatting { + return OSLogFloatFormatting( + explicitPositiveSign: explicitPositiveSign, + uppercase: uppercase, + precision: nil, + notation: .hex + ) + } + + /// Displays an interpolated floating-point value in fprintf's `%e` format. + /// + /// Prints the number in the form [-]d.ddd...dde±dd. + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + public static var exponential: OSLogFloatFormatting { .exponential() } + + /// Displays an interpolated floating-point value in fprintf's `%e` format with + /// specified precision, and optional sign and case. + /// + /// Prints the number in the form [-]d.ddd...dde±dd, with `precision` significant + /// digits following the radix point. Systems may impose an upper bound on the number + /// of digits that are supported. + /// + /// All parameters except `precision` must be boolean literals. + /// + /// - Parameters: + /// - precision: Number of digits to display after the radix point. + /// - explicitPositiveSign: Pass `true` to add a + sign to non-negative + /// numbers. + /// - uppercase: Pass `true` to use uppercase letters or `false` to use + /// lowercase letters. The default is `false`. + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + public static func exponential( + precision: @escaping @autoclosure () -> Int, + explicitPositiveSign: Bool = false, + uppercase: Bool = false + ) -> OSLogFloatFormatting { + return OSLogFloatFormatting( + explicitPositiveSign: explicitPositiveSign, + uppercase: uppercase, + precision: precision, + notation: .exponential + ) + } + + /// Displays an interpolated floating-point value in fprintf's `%e` format with + /// an optional sign and case. + /// + /// Prints the number in the form [-]d.ddd...dde±dd. + /// + /// All parameters to this function must be boolean literals. + /// + /// - Parameters: + /// - explicitPositiveSign: Pass `true` to add a + sign to non-negative + /// numbers. + /// - uppercase: Pass `true` to use uppercase letters or `false` to use + /// lowercase letters. The default is `false`. + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + public static func exponential( + explicitPositiveSign: Bool = false, + uppercase: Bool = false + ) -> OSLogFloatFormatting { + return OSLogFloatFormatting( + explicitPositiveSign: explicitPositiveSign, + uppercase: uppercase, + precision: nil, + notation: .exponential + ) + } + + /// Displays an interpolated floating-point value in fprintf's `%g` format. + /// + /// Behaves like `.fixed` when the number is scaled close to 1.0, and like + /// `.exponential` if it has a very large or small exponent. + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + public static var hybrid: OSLogFloatFormatting { .hybrid() } + + /// Displays an interpolated floating-point value in fprintf's `%g` format with the + /// specified precision, and optional sign and case. + /// + /// Behaves like `.fixed` when the number is scaled close to 1.0, and like + /// `.exponential` if it has a very large or small exponent. + /// + /// All parameters except `precision` must be boolean literals. + /// + /// - Parameters: + /// - precision: Number of digits to display after the radix point. + /// - explicitPositiveSign: Pass `true` to add a + sign to non-negative + /// numbers. + /// - uppercase: Pass `true` to use uppercase letters or `false` to use + /// lowercase letters. The default is `false`. + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + public static func hybrid( + precision: @escaping @autoclosure () -> Int, + explicitPositiveSign: Bool = false, + uppercase: Bool = false + ) -> OSLogFloatFormatting { + return OSLogFloatFormatting( + explicitPositiveSign: explicitPositiveSign, + uppercase: uppercase, + precision: precision, + notation: .hybrid + ) + } + + /// Displays an interpolated floating-point value in fprintf's `%g` format with + /// optional sign and case. + /// + /// Behaves like `.fixed` when the number is scaled close to 1.0, and like + /// `.exponential` if it has a very large or small exponent. + /// + /// All parameters to this function must be boolean literals. + /// + /// - Parameters: + /// - explicitPositiveSign: Pass `true` to add a + sign to non-negative + /// numbers. + /// - uppercase: Pass `true` to use uppercase letters or `false` to use + /// lowercase letters. The default is `false`. + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + public static func hybrid( + explicitPositiveSign: Bool = false, + uppercase: Bool = false + ) -> OSLogFloatFormatting { + return OSLogFloatFormatting( + explicitPositiveSign: explicitPositiveSign, + uppercase: uppercase, + precision: nil, + notation: .hybrid + ) + } +} + +extension OSLogFloatFormatting { + /// Returns a fprintf-compatible length modifier for a given argument type + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + internal static func _formatStringLengthModifier( + _ type: I.Type + ) -> String? { + switch type { + // fprintf formatters promote Float to Double + case is Float.Type: return "" + case is Double.Type: return "" +#if !os(Windows) && (arch(i386) || arch(x86_64)) + // fprintf formatters use L for Float80 + case is Float80.Type: return "L" +#endif + default: return nil + } + } + + /// Constructs an os_log format specifier for the given type `type` + /// using the specified alignment `align` and privacy qualifier `privacy`. + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + internal func formatSpecifier( + for type: I.Type, + align: OSLogStringAlignment, + privacy: OSLogPrivacy + ) -> String { + var specification = "%" + // Add privacy qualifier after % sign within curly braces. This is an + // os log specific flag. + if let privacySpecifier = privacy.privacySpecifier { + specification += "{" + specification += privacySpecifier + specification += "}" + } + + // 1. Flags + // IEEE: `+` The result of a signed conversion shall always begin with a sign + // ( '+' or '-' ) + if explicitPositiveSign { + specification += "+" + } + + // IEEE: `-` The result of the conversion shall be left-justified within the field. + // The conversion is right-justified if this flag is not specified. + if case .start = align.anchor { + specification += "-" + } + + if let _ = align.minimumColumnWidth { + // The alignment could be a dynamic value. Therefore, use a star here and pass it + // as an additional argument. + specification += "*" + } + + if let _ = precision { + specification += ".*" + } + + guard let lengthModifier = + OSLogFloatFormatting._formatStringLengthModifier(type) else { + fatalError("Float type has unknown length") + } + specification += lengthModifier + + // 3. Precision and conversion specifier. + switch notation { + case .fixed: + specification += (uppercase ? "F" : "f") + case .exponential: + specification += (uppercase ? "E" : "e") + case .hybrid: + specification += (uppercase ? "G" : "g") + case .hex: + //guard type.radix == 2 else { return nil } + specification += (uppercase ? "A" : "a") + default: + fatalError("Unknown float notation") + } + return specification + } +} + diff --git a/stdlib/private/OSLog/OSLogFloatingPointTypes.swift b/stdlib/private/OSLog/OSLogFloatingPointTypes.swift index b7616b98215b8..e135911d2380b 100644 --- a/stdlib/private/OSLog/OSLogFloatingPointTypes.swift +++ b/stdlib/private/OSLog/OSLogFloatingPointTypes.swift @@ -17,46 +17,88 @@ // // The `appendInterpolation` functions defined in this file accept privacy // options along with the interpolated expression as shown below: -// TODO: support floating-point formatting options. // -// "\(x, privacy: .private\)" +// "\(x, format: .fixed(precision: 10), privacy: .private\)" extension OSLogInterpolation { - /// Define interpolation for expressions of type Float. + /// Defines interpolation for expressions of type Float. + /// + /// Do not call this function directly. It will be called automatically when interpolating + /// a value of type `Float` in the string interpolations passed to the log APIs. + /// /// - Parameters: - /// - number: the interpolated expression of type Float, which is autoclosured. - /// - privacy: a privacy qualifier which is either private or public. - /// It is auto-inferred by default. + /// - number: The interpolated expression of type Float, which is autoclosured. + /// - format: A formatting option available for float types, defined by the + /// type`OSLogFloatFormatting`. The default is `.fixed`. + /// - align: Left or right alignment with the minimum number of columns as + /// defined by the type `OSLogStringAlignment`. + /// - privacy: A privacy qualifier which is either private or public. + /// It is auto-inferred by default. @_semantics("constant_evaluable") - @_semantics("oslog.requires_constant_arguments") @inlinable @_optimize(none) + @_semantics("oslog.requires_constant_arguments") public mutating func appendInterpolation( _ number: @autoclosure @escaping () -> Float, + format: OSLogFloatFormatting = .fixed, + align: OSLogStringAlignment = .none, privacy: OSLogPrivacy = .auto ) { - appendInterpolation(Double(number()), privacy: privacy) + appendInterpolation( + Double(number()), + format: format, + align: align, + privacy: privacy) } /// Define interpolation for expressions of type Double. + /// + /// Do not call this function directly. It will be called automatically when interpolating + /// a value of type `Double` in the string interpolations passed to the log APIs. + /// /// - Parameters: - /// - number: the interpolated expression of type Double, which is autoclosured. - /// - privacy: a privacy qualifier which is either private or public. - /// It is auto-inferred by default. + /// - number: The interpolated expression of type Double, which is autoclosured. + /// - format: A formatting option available for float types, defined by the + /// type`OSLogFloatFormatting`. The default is `.fixed`. + /// - align: Left or right alignment with the minimum number of columns as + /// defined by the type `OSLogStringAlignment`. + /// - privacy: A privacy qualifier which is either private or public. + /// It is auto-inferred by default. @_semantics("constant_evaluable") - @_semantics("oslog.requires_constant_arguments") @inlinable @_optimize(none) + @_semantics("oslog.requires_constant_arguments") public mutating func appendInterpolation( _ number: @autoclosure @escaping () -> Double, + format: OSLogFloatFormatting = .fixed, + align: OSLogStringAlignment = .none, privacy: OSLogPrivacy = .auto ) { guard argumentCount < maxOSLogArgumentCount else { return } + formatString += + format.formatSpecifier(for: Double.self, align: align, privacy: privacy) - formatString += getDoubleFormatSpecifier(privacy: privacy) - addDoubleHeaders(privacy) + // If minimum column width is specified, append this value first. Note that + // the format specifier would use a '*' for width e.g. %*f. + if let minColumns = align.minimumColumnWidth { + appendAlignmentArgument(minColumns) + } + + // If the privacy has a mask, append the mask argument, which is a constant payload. + // Note that this should come after the width but before the precision. + if privacy.hasMask { + appendMaskArgument(privacy) + } + // If minimum number of digits (precision) is specified, append the + // precision before the argument. Note that the format specifier would use + // a '*' for precision: %.*f. + if let precision = format.precision { + appendPrecisionArgument(precision) + } + // Append the double. + addDoubleHeaders(privacy) arguments.append(number) argumentCount += 1 } @@ -82,29 +124,6 @@ extension OSLogInterpolation { preamble = getUpdatedPreamble(privacy: privacy, isScalar: true) } - - /// Construct an os_log format specifier from the given parameters. - /// This function must be constant evaluable and all its arguments - /// must be known at compile time. - @inlinable - @_semantics("constant_evaluable") - @_effects(readonly) - @_optimize(none) - internal func getDoubleFormatSpecifier(privacy: OSLogPrivacy) -> String { - // TODO: this will become more sophisticated when floating-point formatting - // options are supported. - var specifier = "%" - switch privacy { - case .private: - specifier += "{private}" - case .public: - specifier += "{public}" - default: - break - } - specifier += "f" - return specifier - } } extension OSLogArguments { @@ -115,7 +134,7 @@ extension OSLogArguments { @inlinable @_optimize(none) internal mutating func append(_ value: @escaping () -> Double) { - argumentClosures.append({ (position, _) in + argumentClosures.append({ (position, _, _) in serialize(value(), at: &position) }) } @@ -125,14 +144,13 @@ extension OSLogArguments { /// specified by os_log. Note that this is marked transparent instead of /// @inline(__always) as it is used in optimize(none) functions. @_transparent -@usableFromInline +@_alwaysEmitIntoClient internal func doubleSizeInBytes() -> Int { return 8 } /// Serialize a double at the buffer location that `position` points to and /// increment `position` by the byte size of the double. -@inlinable @_alwaysEmitIntoClient @inline(__always) internal func serialize( @@ -145,3 +163,4 @@ internal func serialize( withUnsafeBytes(of: value) { dest.copyMemory(from: $0) } bufferPosition += byteCount } + diff --git a/stdlib/private/OSLog/OSLogIntegerFormatting.swift b/stdlib/private/OSLog/OSLogIntegerFormatting.swift index 9dc1389efd208..2d9a53563c3bc 100644 --- a/stdlib/private/OSLog/OSLogIntegerFormatting.swift +++ b/stdlib/private/OSLog/OSLogIntegerFormatting.swift @@ -17,23 +17,30 @@ public struct OSLogIntegerFormatting { /// The base to use for the string representation. `radix` must be at least 2 /// and at most 36. The default is 10. - public var radix: Int + @usableFromInline + internal var radix: Int /// When set, a `+` will be printed for all non-negative integers. - public var explicitPositiveSign: Bool + @usableFromInline + internal var explicitPositiveSign: Bool /// When set, a prefix: 0b or 0o or 0x will be added when the radix is 2, 8 or /// 16 respectively. - public var includePrefix: Bool + @usableFromInline + internal var includePrefix: Bool /// Whether to use uppercase letters to represent numerals /// greater than 9 (default is to use lowercase). - public var uppercase: Bool + @usableFromInline + internal var uppercase: Bool /// Minimum number of digits to display. Numbers having fewer digits than /// minDigits will be displayed with leading zeros. - public var minDigits: (() -> Int)? + @usableFromInline + internal var minDigits: (() -> Int)? + /// Initializes all stored properties. + /// /// - Parameters: /// - radix: The base to use for the string representation. `radix` must be /// at least 2 and at most 36. The default is 10. @@ -46,9 +53,8 @@ public struct OSLogIntegerFormatting { /// `false`. /// - minDigits: minimum number of digits to display. Numbers will be /// prefixed with zeros if necessary to meet the minimum. The default is 1. - @_semantics("constant_evaluable") - @inlinable - @_optimize(none) + @_transparent + @usableFromInline internal init( radix: Int = 10, explicitPositiveSign: Bool = false, @@ -56,8 +62,6 @@ public struct OSLogIntegerFormatting { uppercase: Bool = false, minDigits: (() -> Int)? ) { - precondition(radix >= 2 && radix <= 36) - self.radix = radix self.explicitPositiveSign = explicitPositiveSign self.includePrefix = includePrefix @@ -65,11 +69,17 @@ public struct OSLogIntegerFormatting { self.minDigits = minDigits } + /// Displays an interpolated integer as a decimal number with the specified number + /// of digits and an optional sign. + /// + /// The parameter `explicitPositiveSign` must be a boolean literal. The + /// parameter `minDigits` can be an arbitrary expression. + /// /// - Parameters: /// - explicitPositiveSign: Pass `true` to add a + sign to non-negative - /// numbers. Default is `false`. + /// numbers. /// - minDigits: minimum number of digits to display. Numbers will be - /// prefixed with zeros if necessary to meet the minimum. The default is 1. + /// prefixed with zeros if necessary to meet the minimum. @_semantics("constant_evaluable") @inlinable @_optimize(none) @@ -83,9 +93,13 @@ public struct OSLogIntegerFormatting { minDigits: minDigits) } + /// Displays an interpolated integer as a decimal number with an optional sign. + /// + /// The parameter `explicitPositiveSign` must be a boolean literal. + /// /// - Parameters: /// - explicitPositiveSign: Pass `true` to add a + sign to non-negative - /// numbers. Default is `false`. + /// numbers. @_semantics("constant_evaluable") @inlinable @_optimize(none) @@ -98,22 +112,28 @@ public struct OSLogIntegerFormatting { minDigits: nil) } - /// Default decimal format. + /// Displays an interpolated integer as a decimal number. This is the default format for + /// integers. @_semantics("constant_evaluable") @inlinable @_optimize(none) public static var decimal: OSLogIntegerFormatting { .decimal() } + /// Displays an interpolated unsigned integer as a hexadecimal number with the + /// specified parameters. This formatting option should be used only with unsigned + /// integers. + /// + /// All parameters except `minDigits` should be boolean literals. `minDigits` + /// can be an arbitrary expression. + /// /// - Parameters: /// - explicitPositiveSign: Pass `true` to add a + sign to non-negative - /// numbers. Default is `false`. - /// - includePrefix: Pass `true` to add a prefix: 0b, 0o, 0x to corresponding - /// radices. Default is `false`. + /// numbers. + /// - includePrefix: Pass `true` to add a prefix 0x. /// - uppercase: Pass `true` to use uppercase letters to represent numerals - /// greater than 9, or `false` to use lowercase letters. The default is - /// `false`. + /// greater than 9, or `false` to use lowercase letters. The default is `false`. /// - minDigits: minimum number of digits to display. Numbers will be - /// prefixed with zeros if necessary to meet the minimum. The default is 1. + /// prefixed with zeros if necessary to meet the minimum. @_semantics("constant_evaluable") @inlinable @_optimize(none) @@ -131,14 +151,17 @@ public struct OSLogIntegerFormatting { minDigits: minDigits) } + /// Displays an interpolated unsigned integer as a hexadecimal number with the specified + /// parameters. This formatting option should be used only with unsigned integers. + /// + /// All parameters should be boolean literals. + /// /// - Parameters: /// - explicitPositiveSign: Pass `true` to add a + sign to non-negative - /// numbers. Default is `false`. - /// - includePrefix: Pass `true` to add a prefix: 0b, 0o, 0x to corresponding - /// radices. Default is `false`. + /// numbers. + /// - includePrefix: Pass `true` to add a prefix 0x. /// - uppercase: Pass `true` to use uppercase letters to represent numerals - /// greater than 9, or `false` to use lowercase letters. The default is - /// `false`. + /// greater than 9, or `false` to use lowercase letters. The default is `false`. @_semantics("constant_evaluable") @inlinable @_optimize(none) @@ -155,22 +178,28 @@ public struct OSLogIntegerFormatting { minDigits: nil) } - /// Default hexadecimal format. + /// Displays an interpolated unsigned integer as a hexadecimal number. + /// This formatting option should be used only with unsigned integers. @_semantics("constant_evaluable") @inlinable @_optimize(none) public static var hex: OSLogIntegerFormatting { .hex() } + /// Displays an interpolated unsigned integer as an octal number with the specified + /// parameters. This formatting option should be used only with unsigned + /// integers. + /// + /// All parameters except `minDigits` should be boolean literals. `minDigits` + /// can be an arbitrary expression. + /// /// - Parameters: /// - explicitPositiveSign: Pass `true` to add a + sign to non-negative - /// numbers. Default is `false`. - /// - includePrefix: Pass `true` to add a prefix: 0b, 0o, 0x to corresponding - /// radices. Default is `false`. + /// numbers. + /// - includePrefix: Pass `true` to add a prefix 0o. /// - uppercase: Pass `true` to use uppercase letters to represent numerals - /// greater than 9, or `false` to use lowercase letters. The default is - /// `false`. + /// greater than 9, or `false` to use lowercase letters. The default is `false`. /// - minDigits: minimum number of digits to display. Numbers will be - /// prefixed with zeros if necessary to meet the minimum. The default is 1. + /// prefixed with zeros if necessary to meet the minimum. @_semantics("constant_evaluable") @inlinable @_optimize(none) @@ -188,14 +217,17 @@ public struct OSLogIntegerFormatting { minDigits: minDigits) } + /// Displays an interpolated unsigned integer as an octal number with the specified parameters. + /// This formatting option should be used only with unsigned integers. + /// + /// All parameters must be boolean literals. + /// /// - Parameters: /// - explicitPositiveSign: Pass `true` to add a + sign to non-negative - /// numbers. Default is `false`. - /// - includePrefix: Pass `true` to add a prefix: 0b, 0o, 0x to corresponding - /// radices. Default is `false`. + /// numbers. + /// - includePrefix: Pass `true` to add a prefix 0o. /// - uppercase: Pass `true` to use uppercase letters to represent numerals - /// greater than 9, or `false` to use lowercase letters. The default is - /// `false`. + /// greater than 9, or `false` to use lowercase letters. @_semantics("constant_evaluable") @inlinable @_optimize(none) @@ -212,7 +244,8 @@ public struct OSLogIntegerFormatting { minDigits: nil) } - /// Default octal format. + /// Displays an interpolated unsigned integer as an octal number. + /// This formatting option should be used only with unsigned integers. @_semantics("constant_evaluable") @inlinable @_optimize(none) @@ -311,13 +344,10 @@ extension OSLogIntegerFormatting { // Add privacy qualifier after % sign within curly braces. This is an // os log specific flag. - switch privacy { - case .private: - specification += "{private}" - case .public: - specification += "{public}" - default: - break + if let privacySpecifier = privacy.privacySpecifier { + specification += "{" + specification += privacySpecifier + specification += "}" } // diff --git a/stdlib/private/OSLog/OSLogIntegerTypes.swift b/stdlib/private/OSLog/OSLogIntegerTypes.swift index 248e90e90c57a..dc24531648d42 100644 --- a/stdlib/private/OSLog/OSLogIntegerTypes.swift +++ b/stdlib/private/OSLog/OSLogIntegerTypes.swift @@ -24,19 +24,23 @@ extension OSLogInterpolation { - /// Define interpolation for expressions of type Int. + /// Defines interpolation for expressions of type Int. + /// + /// Do not call this function directly. It will be called automatically when interpolating + /// a value of type `Int` in the string interpolations passed to the log APIs. + /// /// - Parameters: - /// - number: the interpolated expression of type Int, which is autoclosured. - /// - format: a formatting option available for integer types, defined by the - /// type`OSLogIntegerFormatting`. The default is .decimal. - /// - align: left or right alignment with the minimum number of columns as - /// defined by the type `OSLogStringAlignment`. - /// - privacy: a privacy qualifier which is either private or public. - /// It is auto-inferred by default. + /// - number: The interpolated expression of type Int, which is autoclosured. + /// - format: A formatting option available for integer types, defined by the + /// type: `OSLogIntegerFormatting`. The default is `.decimal`. + /// - align: Left or right alignment with the minimum number of columns as + /// defined by the type `OSLogStringAlignment`. + /// - privacy: A privacy qualifier which is either private or public. + /// It is auto-inferred by default. @_semantics("constant_evaluable") - @_semantics("oslog.requires_constant_arguments") @inlinable @_optimize(none) + @_semantics("oslog.requires_constant_arguments") public mutating func appendInterpolation( _ number: @autoclosure @escaping () -> Int, format: OSLogIntegerFormatting = .decimal, @@ -46,18 +50,12 @@ extension OSLogInterpolation { appendInteger(number, format: format, align: align, privacy: privacy) } - /// Define interpolation for expressions of type Int32. - /// - Parameters: - /// - number: the interpolated expression of type Int32, which is autoclosured. - /// - format: a formatting option available for integer types, defined by the - /// type `OSLogIntegerFormatting`. - /// - align: left or right alignment with the minimum number of columns as - /// defined by the type `OSLogStringAlignment`. - /// - privacy: a privacy qualifier which is either private or public. - /// It is auto-inferred by default. + // Define appendInterpolation overloads for fixed-size integers. + @_semantics("constant_evaluable") @inlinable @_optimize(none) + @_semantics("oslog.requires_constant_arguments") public mutating func appendInterpolation( _ number: @autoclosure @escaping () -> Int32, format: OSLogIntegerFormatting = .decimal, @@ -67,19 +65,23 @@ extension OSLogInterpolation { appendInteger(number, format: format, align: align, privacy: privacy) } - /// Define interpolation for expressions of type UInt. + /// Defines interpolation for expressions of type UInt. + /// + /// Do not call this function directly. It will be called automatically when interpolating + /// a value of type `Int` in the string interpolations passed to the log APIs. + /// /// - Parameters: - /// - number: the interpolated expression of type UInt, which is autoclosured. - /// - format: a formatting option available for integer types, defined by the - /// type `OSLogIntegerFormatting`. - /// - align: left or right alignment with the minimum number of columns as - /// defined by the type `OSLogStringAlignment`. - /// - privacy: a privacy qualifier which is either private or public. - /// It is auto-inferred by default. + /// - number: The interpolated expression of type UInt, which is autoclosured. + /// - format: A formatting option available for integer types, defined by the + /// type `OSLogIntegerFormatting`. + /// - align: Left or right alignment with the minimum number of columns as + /// defined by the type `OSLogStringAlignment`. + /// - privacy: A privacy qualifier which is either private or public. + /// It is auto-inferred by default. @_semantics("constant_evaluable") - @_semantics("oslog.requires_constant_arguments") @inlinable @_optimize(none) + @_semantics("oslog.requires_constant_arguments") public mutating func appendInterpolation( _ number: @autoclosure @escaping () -> UInt, format: OSLogIntegerFormatting = .decimal, @@ -104,18 +106,27 @@ extension OSLogInterpolation { guard argumentCount < maxOSLogArgumentCount else { return } formatString += format.formatSpecifier(for: T.self, align: align, privacy: privacy) - // If minimum column width is specified, append this value first. Note that the - // format specifier would use a '*' for width e.g. %*d. + + // If minimum column width is specified, append this value first. Note that + // the format specifier would use a '*' for width e.g. %*d. if let minColumns = align.minimumColumnWidth { - appendPrecisionArgument(minColumns) + appendAlignmentArgument(minColumns) } - // If minimum number of digits (precision) is specified, append the precision before - // the argument. Note that the format specifier would use a '*' for precision: %.*d. + // If the privacy has a mask, append the mask argument, which is a constant payload. + // Note that this should come after the width but before the precision. + if privacy.hasMask { + appendMaskArgument(privacy) + } + + // If minimum number of digits (precision) is specified, append the + // precision before the argument. Note that the format specifier would use + // a '*' for precision: %.*d. if let minDigits = format.minDigits { appendPrecisionArgument(minDigits) } + // Append the integer. addIntHeaders(privacy, sizeForEncoding(T.self)) arguments.append(number) argumentCount += 1 @@ -151,19 +162,57 @@ extension OSLogInterpolation { @inlinable @_optimize(none) internal mutating func appendPrecisionArgument(_ count: @escaping () -> Int) { - // Note that we don't have to update the preamble here. - let argumentHeader = getArgumentHeader(privacy: .auto, type: .count) + appendPrecisionAlignCount( + count, + getArgumentHeader(privacy: .auto, type: .count)) + } + + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + internal mutating func appendAlignmentArgument(_ count: @escaping () -> Int) { + appendPrecisionAlignCount( + count, + getArgumentHeader(privacy: .auto, type: .scalar)) + } + + // This is made transparent to minimize compile time overheads. The function's + // implementation also uses literals whenever possible for the same reason. + @_transparent + @inlinable + internal mutating func appendPrecisionAlignCount( + _ count: @escaping () -> Int, + _ argumentHeader: UInt8 + ) { arguments.append(argumentHeader) // Append number of bytes needed to serialize the argument. - let byteCount = sizeForEncoding(CInt.self) - arguments.append(UInt8(byteCount)) + arguments.append(4) // Increment total byte size by the number of bytes needed for this // argument, which is the sum of the byte size of the argument and // two bytes needed for the headers. - totalBytesForSerializingArguments += 2 + byteCount + totalBytesForSerializingArguments += 6 // The count is expected to be a CInt. arguments.append({ CInt(count()) }) argumentCount += 1 + // Note that we don't have to update the preamble here. + } + + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + internal mutating func appendMaskArgument(_ privacy: OSLogPrivacy) { + arguments.append(getArgumentHeader(privacy: .auto, type: .mask)) + // Append number of bytes needed to serialize the mask. Mask is 64 bit payload. + arguments.append(8) + // Increment total byte size by the number of bytes needed for this + // argument, which is the sum of the byte size of the argument and + // two bytes needed for the headers. + totalBytesForSerializingArguments += 10 + // Append the mask value. This is a compile-time constant. + let maskValue = privacy.maskValue + arguments.append({ maskValue }) + argumentCount += 1 + // Note that we don't have to update the preamble here. } } @@ -177,7 +226,7 @@ extension OSLogArguments { internal mutating func append( _ value: @escaping () -> T ) where T: FixedWidthInteger { - argumentClosures.append({ (position, _) in + argumentClosures.append({ (position, _, _) in serialize(value(), at: &position) }) } @@ -188,7 +237,7 @@ extension OSLogArguments { /// it is marked transparent instead of @inline(__always) as it is used in /// optimize(none) functions. @_transparent -@usableFromInline +@_alwaysEmitIntoClient internal func sizeForEncoding( _ type: T.Type ) -> Int where T : FixedWidthInteger { @@ -197,7 +246,6 @@ internal func sizeForEncoding( /// Serialize an integer at the buffer location that `position` points to and /// increment `position` by the byte size of `T`. -@inlinable @_alwaysEmitIntoClient @inline(__always) internal func serialize( diff --git a/stdlib/private/OSLog/OSLogMessage.swift b/stdlib/private/OSLog/OSLogMessage.swift index e49b2a1aa46be..99fd0e0bf3c1f 100644 --- a/stdlib/private/OSLog/OSLogMessage.swift +++ b/stdlib/private/OSLog/OSLogMessage.swift @@ -11,47 +11,31 @@ //===----------------------------------------------------------------------===// // This file contains data structures and helper functions that are used by -// the new OS log APIs. These are prototype implementations and should not be -// used outside of tests. - -/// Privacy qualifiers for indicating the privacy level of the logged data -/// to the logging system. These can be specified in the string interpolation -/// passed to the log APIs. -/// For Example, -/// log.info("Login request from user id \(userid, privacy: .private)") -/// -/// See `OSLogInterpolation.appendInterpolation` definitions for default options -/// for each supported type. -public enum OSLogPrivacy { - case `private` - case `public` - case auto -} +// the new OS log APIs. + +import ObjectiveC /// Maximum number of arguments i.e., interpolated expressions that can /// be used in the string interpolations passed to the log APIs. -/// This limit is imposed by the ABI of os_log. +/// This limit is imposed by the logging system. @_semantics("constant_evaluable") @inlinable @_optimize(none) public var maxOSLogArgumentCount: UInt8 { return 48 } -/// Note that this is marked transparent instead of @inline(__always) as it is -/// used in optimize(none) functions. +// Note that this is marked transparent instead of @inline(__always) as it is +// used in optimize(none) functions. @_transparent -@usableFromInline +@_alwaysEmitIntoClient internal var logBitsPerByte: Int { return 3 } /// Represents a string interpolation passed to the log APIs. /// /// This type converts (through its methods) the given string interpolation into -/// a C-style format string and a sequence of arguments, which is represented -/// by the type `OSLogArguments`. +/// a C-style format string and a sequence of arguments. /// -/// Do not create an instance of this type directly. It is used by the compiler -/// when you pass a string interpolation to the log APIs. -/// Extend this type with more `appendInterpolation` overloads to enable -/// interpolating additional types. +/// - Warning: Do not explicitly refer to this type. It will be implicitly created +/// by the compiler when you pass a string interpolation to the log APIs. @frozen public struct OSLogInterpolation : StringInterpolationProtocol { /// A format string constructed from the given string interpolation to be @@ -82,39 +66,14 @@ public struct OSLogInterpolation : StringInterpolationProtocol { @usableFromInline internal var arguments: OSLogArguments - /// The possible values for the argument flag, as defined by the os_log ABI, - /// which occupies four least significant bits of the first byte of the - /// argument header. The first two bits are used to indicate privacy and - /// the other two are reserved. - @usableFromInline - @frozen - internal enum ArgumentFlag { - case autoFlag - case privateFlag - case publicFlag - - @inlinable - internal var rawValue: UInt8 { - switch self { - case .autoFlag: - return 0 - case .privateFlag: - return 0x1 - case .publicFlag: - return 0x2 - } - } - } - /// The possible values for the argument type, as defined by the os_log ABI, /// which occupies four most significant bits of the first byte of the /// argument header. The rawValue of this enum must be constant evaluable. /// (Note that an auto-generated rawValue is not constant evaluable because /// it cannot be annotated so.) @usableFromInline - @frozen internal enum ArgumentType { - case scalar, count, string, pointer, object + case scalar, count, string, pointer, object, mask @inlinable internal var rawValue: UInt8 { @@ -127,7 +86,9 @@ public struct OSLogInterpolation : StringInterpolationProtocol { return 2 case .pointer: return 3 - case .object: + case .mask: + return 7 + default: //.object return 4 } } @@ -138,25 +99,18 @@ public struct OSLogInterpolation : StringInterpolationProtocol { @usableFromInline internal var preamble: UInt8 - /// Bit mask for setting bits in the peamble. The bits denoted by the bit - /// mask indicate whether there is an argument that is private, and whether - /// there is an argument that is non-scalar: String, NSObject or Pointer. - @usableFromInline - @frozen - internal enum PreambleBitMask { - case privateBitMask - case nonScalarBitMask + /// Denotes the bit that indicates whether there is private argument. + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + internal var privateBitMask: UInt8 { 0x1 } - @inlinable - internal var rawValue: UInt8 { - switch self { - case .privateBitMask: - return 0x1 - case .nonScalarBitMask: - return 0x2 - } - } - } + /// Denotes the bit that indicates whether there is non-scalar argument: + /// String, NSObject or Pointer. + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + internal var nonScalarBitMask: UInt8 { 0x2 } /// The second summary byte that denotes the number of arguments, which is /// also the number of interpolated expressions. This will be determined @@ -170,11 +124,24 @@ public struct OSLogInterpolation : StringInterpolationProtocol { @usableFromInline internal var totalBytesForSerializingArguments: Int + /// The number of arguments that are Strings. This count is used to create + /// auxiliary storage meant for extending the lifetime of the string arguments + /// until the log call completes. + @usableFromInline + internal var stringArgumentCount: Int + + /// The number of arguments that are NSObjects. This count is used to create + /// auxiliary storage meant for extending the lifetime of the NSObject + /// arguments until the log call completes. + @usableFromInline + internal var objectArgumentCount: Int + // Some methods defined below are marked @_optimize(none) to prevent inlining // of string internals (such as String._StringGuts) which will interfere with // constant evaluation and folding. Note that these methods will be inlined, // constant evaluated/folded and optimized in the context of a caller. + @_semantics("oslog.interpolation.init") @_semantics("constant_evaluable") @inlinable @_optimize(none) @@ -186,6 +153,8 @@ public struct OSLogInterpolation : StringInterpolationProtocol { preamble = 0 argumentCount = 0 totalBytesForSerializingArguments = 0 + stringArgumentCount = 0 + objectArgumentCount = 0 } @_semantics("constant_evaluable") @@ -197,23 +166,6 @@ public struct OSLogInterpolation : StringInterpolationProtocol { /// `appendInterpolation` conformances will be added by extensions to this type. - /// Return true if and only if the parameter is .private. - /// This function must be constant evaluable. - @inlinable - @_semantics("constant_evaluable") - @_effects(readonly) - @_optimize(none) - internal func getArugmentFlag(_ privacy: OSLogPrivacy) -> ArgumentFlag { - switch privacy { - case .public: - return .publicFlag - case .private: - return .privateFlag - default: - return .autoFlag - } - } - /// Compute a byte-sized argument header consisting of flag and type. /// Flag and type take up the least and most significant four bits /// of the header byte, respectively. @@ -226,9 +178,7 @@ public struct OSLogInterpolation : StringInterpolationProtocol { privacy: OSLogPrivacy, type: ArgumentType ) -> UInt8 { - let flag = getArugmentFlag(privacy) - let flagAndType: UInt8 = (type.rawValue &<< 4) | flag.rawValue - return flagAndType + return (type.rawValue &<< 4) | privacy.argumentFlag } /// Compute the new preamble based whether the current argument is private @@ -242,13 +192,11 @@ public struct OSLogInterpolation : StringInterpolationProtocol { isScalar: Bool ) -> UInt8 { var preamble = self.preamble - // Equality comparisions on enums is not yet supported by the constant - // evaluator. - if case .private = privacy { - preamble |= PreambleBitMask.privateBitMask.rawValue + if privacy.isAtleastPrivate { + preamble |= privateBitMask } - if !isScalar { - preamble |= PreambleBitMask.nonScalarBitMask.rawValue + if !isScalar || privacy.hasMask { + preamble |= nonScalarBitMask } return preamble } @@ -258,7 +206,8 @@ extension String { /// Replace all percents "%" in the string by "%%" so that the string can be /// interpreted as a C format string. This function is constant evaluable /// and its semantics is modeled within the evaluator. - public var percentEscapedString: String { + @inlinable + internal var percentEscapedString: String { @_semantics("string.escapePercent.get") @_effects(readonly) @_optimize(none) @@ -270,14 +219,17 @@ extension String { } } +/// Represents a message passed to the log APIs. This type should be created +/// from a string interpolation or a string literal. +/// +/// Do not explicitly refer to this type. It will be implicitly created +/// by the compiler when you pass a string interpolation to the log APIs. @frozen public struct OSLogMessage : ExpressibleByStringInterpolation, ExpressibleByStringLiteral { public let interpolation: OSLogInterpolation - /// Initializer for accepting string interpolations. This function must be - /// constant evaluable. @inlinable @_optimize(none) @_semantics("oslog.message.init_interpolation") @@ -286,8 +238,6 @@ public struct OSLogMessage : self.interpolation = stringInterpolation } - /// Initializer for accepting string literals. This function must be - /// constant evaluable. @inlinable @_optimize(none) @_semantics("oslog.message.init_stringliteral") @@ -298,19 +248,25 @@ public struct OSLogMessage : self.interpolation = s } - /// The byte size of the buffer that will be passed to the C os_log ABI. - /// It will contain the elements of `interpolation.arguments` and the two - /// summary bytes: preamble and argument count. + /// The byte size of the buffer that will be passed to the logging system. @_semantics("constant_evaluable") @inlinable @_optimize(none) public var bufferSize: Int { + // The two additional bytes is for the preamble and argument count. return interpolation.totalBytesForSerializingArguments + 2 } } -public typealias ByteBufferPointer = UnsafeMutablePointer -public typealias StorageObjects = [AnyObject] +@usableFromInline +internal typealias ByteBufferPointer = UnsafeMutablePointer +@usableFromInline +internal typealias ObjectStorage = UnsafeMutablePointer? +@usableFromInline +internal typealias ArgumentClosures = + [(inout ByteBufferPointer, + inout ObjectStorage, + inout ObjectStorage) -> ()] /// A representation of a sequence of arguments and headers (of possibly /// different types) that have to be serialized to a byte buffer. The arguments @@ -326,8 +282,7 @@ internal struct OSLogArguments { /// array of AnyObject to store references to auxiliary storage created during /// serialization. @usableFromInline - internal var argumentClosures: [(inout ByteBufferPointer, - inout StorageObjects) -> ()] + internal var argumentClosures: ArgumentClosures @_semantics("constant_evaluable") @inlinable @@ -342,7 +297,7 @@ internal struct OSLogArguments { @inlinable @_optimize(none) internal mutating func append(_ header: UInt8) { - argumentClosures.append({ (position, _) in + argumentClosures.append({ (position, _, _) in serialize(header, at: &position) }) } @@ -352,7 +307,6 @@ internal struct OSLogArguments { /// Serialize a UInt8 value at the buffer location pointed to by `bufferPosition`, /// and increment the `bufferPosition` with the byte size of the serialized value. -@inlinable @_alwaysEmitIntoClient @inline(__always) internal func serialize( @@ -362,3 +316,43 @@ internal func serialize( bufferPosition[0] = value bufferPosition += 1 } + +// The following code defines helper functions for creating and maintaining +// a buffer for holding a fixed number for instances of a type T. Such buffers +// are used to hold onto NSObjects and Strings that are interpolated in the log +// message until the end of the log call. + +@_alwaysEmitIntoClient +@inline(__always) +internal func createStorage( + capacity: Int, + type: T.Type +) -> ObjectStorage { + return + capacity == 0 ? + nil : + UnsafeMutablePointer.allocate(capacity: capacity) +} + +@_alwaysEmitIntoClient +@inline(__always) +internal func initializeAndAdvance( + _ storageOpt: inout ObjectStorage, + to value: T +) { + // This if statement should get optimized away. + if let storage = storageOpt { + storage.initialize(to: value) + storageOpt = storage.advanced(by: 1) + } +} + +@_alwaysEmitIntoClient +@inline(__always) +internal func destroyStorage(_ storageOpt: ObjectStorage, count: Int) { + // This if statement should get optimized away. + if let storage = storageOpt { + storage.deinitialize(count: count) + storage.deallocate() + } +} diff --git a/stdlib/private/OSLog/OSLogNSObjectType.swift b/stdlib/private/OSLog/OSLogNSObjectType.swift index 3942999a9c682..bcf1c7222f2f4 100644 --- a/stdlib/private/OSLog/OSLogNSObjectType.swift +++ b/stdlib/private/OSLog/OSLogNSObjectType.swift @@ -24,15 +24,18 @@ import ObjectiveC extension OSLogInterpolation { - /// Define interpolation for expressions of type NSObject. + /// Defines interpolation for expressions of type NSObject. + /// + /// Do not call this function directly. It will be called automatically when interpolating + /// a value of type `NSObject` in the string interpolations passed to the log APIs. + /// /// - Parameters: - /// - argumentObject: the interpolated expression of type NSObject, which is autoclosured. - /// - privacy: a privacy qualifier which is either private or public. - /// It is auto-inferred by default. + /// - argumentObject: The interpolated expression of type NSObject, which is autoclosured. + /// - privacy: A privacy qualifier which is either private or public. It is auto-inferred by default. @_semantics("constant_evaluable") - @_semantics("oslog.requires_constant_arguments") @inlinable @_optimize(none) + @_semantics("oslog.requires_constant_arguments") public mutating func appendInterpolation( _ argumentObject: @autoclosure @escaping () -> NSObject, privacy: OSLogPrivacy = .auto @@ -40,10 +43,14 @@ extension OSLogInterpolation { guard argumentCount < maxOSLogArgumentCount else { return } formatString += getNSObjectFormatSpecifier(privacy) + // If the privacy has a mask, append the mask argument, which is a constant payload. + if privacy.hasMask { + appendMaskArgument(privacy) + } addNSObjectHeaders(privacy) - arguments.append(argumentObject) argumentCount += 1 + objectArgumentCount += 1 } /// Update preamble and append argument headers based on the parameters of @@ -76,14 +83,14 @@ extension OSLogInterpolation { @_effects(readonly) @_optimize(none) internal func getNSObjectFormatSpecifier(_ privacy: OSLogPrivacy) -> String { - switch privacy { - case .private: - return "%{private}@" - case .public: - return "%{public}@" - default: - return "%@" + var specifier = "%" + if let privacySpecifier = privacy.privacySpecifier { + specifier += "{" + specifier += privacySpecifier + specifier += "}" } + specifier += "@" + return specifier } } @@ -95,20 +102,20 @@ extension OSLogArguments { @inlinable @_optimize(none) internal mutating func append(_ value: @escaping () -> NSObject) { - argumentClosures.append({ (position, _) in - serialize(value(), at: &position) + argumentClosures.append({ (position, objectArguments, _) in + serialize(value(), at: &position, storingObjectsIn: &objectArguments) }) } } /// Serialize an NSObject pointer at the buffer location pointed by /// `bufferPosition`. -@inlinable @_alwaysEmitIntoClient @inline(__always) internal func serialize( _ object: NSObject, - at bufferPosition: inout ByteBufferPointer + at bufferPosition: inout ByteBufferPointer, + storingObjectsIn objectArguments: inout ObjectStorage ) { let byteCount = pointerSizeInBytes(); let dest = @@ -116,9 +123,11 @@ internal func serialize( // Get the address of this NSObject as an UnsafeRawPointer. let objectAddress = Unmanaged.passUnretained(object).toOpaque() // Copy the address into the destination buffer. Note that the input NSObject - // is an interpolated expression and is guaranteed to be alive until the - // os_log ABI call is completed by the implementation. Therefore, passing - // this address to the os_log ABI is safe. + // is kept alive until the os_log ABI call completes by storing in the + // objectArguments. withUnsafeBytes(of: objectAddress) { dest.copyMemory(from: $0) } bufferPosition += byteCount + // This object could be newly created by the auto-closure. Therefore, make + // sure it is alive until the log call completes. + initializeAndAdvance(&objectArguments, to: object) } diff --git a/stdlib/private/OSLog/OSLogPrivacy.swift b/stdlib/private/OSLog/OSLogPrivacy.swift new file mode 100644 index 0000000000000..e0646ed6395b7 --- /dev/null +++ b/stdlib/private/OSLog/OSLogPrivacy.swift @@ -0,0 +1,220 @@ +//===----------------- OSLogPrivacy.swift ---------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +// This file defines the APIs for specifying privacy in the log messages and also +// the logic for encoding them in the byte buffer passed to the libtrace library. + +/// Privacy options for specifying privacy level of the interpolated expressions +/// in the string interpolations passed to the log APIs. +@frozen +public struct OSLogPrivacy { + + @usableFromInline + internal enum PrivacyOption { + case `private` + case `public` + case auto + } + + public enum Mask { + /// Applies a salted hashing transformation to an interpolated value to redact it in the logs. + /// + /// Its purpose is to permit the correlation of identical values across multiple log lines + /// without revealing the value itself. + case hash + case none + } + + @usableFromInline + internal var privacy: PrivacyOption + + @usableFromInline + internal var mask: Mask + + @_transparent + @usableFromInline + internal init(privacy: PrivacyOption, mask: Mask) { + self.privacy = privacy + self.mask = mask + } + + /// Sets the privacy level of an interpolated value to public. + /// + /// When the privacy level is public, the value will be displayed + /// normally without any redaction in the logs. + @_semantics("constant_evaluable") + @_optimize(none) + @inlinable + public static var `public`: OSLogPrivacy { + OSLogPrivacy(privacy: .public, mask: .none) + } + + /// Sets the privacy level of an interpolated value to private. + /// + /// When the privacy level is private, the value will be redacted in the logs, + /// subject to the privacy configuration of the logging system. + @_semantics("constant_evaluable") + @_optimize(none) + @inlinable + public static var `private`: OSLogPrivacy { + OSLogPrivacy(privacy: .private, mask: .none) + } + + /// Sets the privacy level of an interpolated value to private and + /// applies a `mask` to the interpolated value to redacted it. + /// + /// When the privacy level is private, the value will be redacted in the logs, + /// subject to the privacy configuration of the logging system. + /// + /// If the value need not be redacted in the logs, its full value is captured as normal. + /// Otherwise (i.e. if the value would be redacted) the `mask` is applied to + /// the argument value and the result of the transformation is recorded instead. + /// + /// - Parameters: + /// - mask: Mask to use with the privacy option. + @_semantics("constant_evaluable") + @_optimize(none) + @inlinable + public static func `private`(mask: Mask) -> OSLogPrivacy { + OSLogPrivacy(privacy: .private, mask: mask) + } + + /// Auto-infers a privacy level for an interpolated value. + /// + /// The system will automatically decide whether the value should + /// be captured fully in the logs or should be redacted. + @_semantics("constant_evaluable") + @_optimize(none) + @inlinable + public static var auto: OSLogPrivacy { + OSLogPrivacy(privacy: .auto, mask: .none) + } + + /// Auto-infers a privacy level for an interpolated value and applies a `mask` + /// to the interpolated value to redacted it when necessary. + /// + /// The system will automatically decide whether the value should + /// be captured fully in the logs or should be redacted. + /// If the value need not be redacted in the logs, its full value is captured as normal. + /// Otherwise (i.e. if the value would be redacted) the `mask` is applied to + /// the argument value and the result of the transformation is recorded instead. + /// + /// - Parameters: + /// - mask: Mask to use with the privacy option. + @_semantics("constant_evaluable") + @_optimize(none) + @inlinable + public static func auto(mask: Mask) -> OSLogPrivacy { + OSLogPrivacy(privacy: .auto, mask: mask) + } + + /// Return an argument flag for the privacy option., as defined by the + /// os_log ABI, which occupies four least significant bits of the first byte of the + /// argument header. The first two bits are used to indicate privacy and + /// the other two are reserved. + @inlinable + @_semantics("constant_evaluable") + @_optimize(none) + internal var argumentFlag: UInt8 { + switch privacy { + case .private: + return 0x1 + case .public: + return 0x2 + default: + return 0 + } + } + + @inlinable + @_semantics("constant_evaluable") + @_optimize(none) + internal var isAtleastPrivate: Bool { + switch privacy { + case .public: + return false + case .auto: + return false + default: + return true + } + } + + @inlinable + @_semantics("constant_evaluable") + @_optimize(none) + internal var needsPrivacySpecifier: Bool { + if case .hash = mask { + return true + } + switch privacy { + case .auto: + return false + default: + return true + } + } + + @inlinable + @_transparent + internal var hasMask: Bool { + if case .hash = mask { + return true + } + return false + } + + /// A 64-bit value obtained by interpreting the mask name as a little-endian unsigned + /// integer. + @inlinable + @_transparent + internal var maskValue: UInt64 { + // Return the value of + // 'h' | 'a' << 8 | 's' << 16 | 'h' << 24 which equals + // 104 | (97 << 8) | (115 << 16) | (104 << 24) + 1752392040 + } + + /// Return an os log format specifier for this `privacy` level. The + /// format specifier goes within curly braces e.g. %{private} in the format + /// string passed to the os log ABI. + @inlinable + @_semantics("constant_evaluable") + @_optimize(none) + internal var privacySpecifier: String? { + let hasMask = self.hasMask + var isAuto = false + if case .auto = privacy { + isAuto = true + } + if isAuto, !hasMask { + return nil + } + var specifier: String + switch privacy { + case .public: + specifier = "public" + case .private: + specifier = "private" + default: + specifier = "" + } + if hasMask { + if !isAuto { + specifier += "," + } + specifier += "mask.hash" + } + return specifier + } +} + diff --git a/stdlib/private/OSLog/OSLogStringAlignment.swift b/stdlib/private/OSLog/OSLogStringAlignment.swift index 05afc4f430964..a93388a9cfef9 100644 --- a/stdlib/private/OSLog/OSLogStringAlignment.swift +++ b/stdlib/private/OSLog/OSLogStringAlignment.swift @@ -13,8 +13,8 @@ // This file defines types and functions for specifying alignment of the // interpolations passed to the os log APIs. -@frozen -public enum OSLogCollectionBound { +@usableFromInline +internal enum OSLogCollectionBound { case start case end } @@ -23,20 +23,24 @@ public enum OSLogCollectionBound { public struct OSLogStringAlignment { /// Minimum number of characters to be displayed. If the value to be printed /// is shorter than this number, the result is padded with spaces. The value - /// is not truncated even if the result is larger.This value need not be a + /// is not truncated even if the result is larger. This value need not be a /// compile-time constant, and is therefore an autoclosure. - public var minimumColumnWidth: (() -> Int)? + @usableFromInline + internal var minimumColumnWidth: (() -> Int)? + /// This captures right/left alignment. - public var anchor: OSLogCollectionBound + @usableFromInline + internal var anchor: OSLogCollectionBound + /// Initiailzes stored properties. + /// /// - Parameters: /// - minimumColumnWidth: Minimum number of characters to be displayed. If the value to be /// printed is shorter than this number, the result is padded with spaces. The value is not truncated /// even if the result is larger. /// - anchor: Use `.end` for right alignment and `.start` for left. - @_semantics("constant_evaluable") - @inlinable - @_optimize(none) + @_transparent + @usableFromInline internal init( minimumColumnWidth: (() -> Int)? = nil, anchor: OSLogCollectionBound = .end @@ -45,29 +49,18 @@ public struct OSLogStringAlignment { self.anchor = anchor } - /// Right alignment formatter. - @_semantics("constant_evaluable") - @inlinable - @_optimize(none) - public static var right: OSLogStringAlignment { - OSLogStringAlignment(anchor: .end) - } - - /// Left alignment formatter. - @_semantics("constant_evaluable") - @inlinable - @_optimize(none) - public static var left: OSLogStringAlignment { - OSLogStringAlignment(anchor: .start) - } - - /// Use default alignment, which is right alignment. + /// Indicates no alignment. @_semantics("constant_evaluable") @inlinable @_optimize(none) - public static var none: OSLogStringAlignment { .right } + public static var none: OSLogStringAlignment { OSLogStringAlignment(anchor: .end) } - /// Right align and display at least`columns` characters. + /// Right align and display at least `columns` characters. + /// + /// The interpolated value would be padded with spaces, if necessary, to + /// meet the specified `columns` characters. + /// + /// - Parameter columns: minimum number of characters to display. @_semantics("constant_evaluable") @inlinable @_optimize(none) @@ -77,7 +70,12 @@ public struct OSLogStringAlignment { OSLogStringAlignment(minimumColumnWidth: columns, anchor: .end) } - /// Left align and display at least`columns` characters. + /// Left align and display at least `columns` characters. + /// + /// The interpolated value would be padded with spaces, if necessary, to + /// meet the specified `columns` characters. + /// + /// - Parameter columns: minimum number of characters to display. @_semantics("constant_evaluable") @inlinable @_optimize(none) diff --git a/stdlib/private/OSLog/OSLogStringTypes.swift b/stdlib/private/OSLog/OSLogStringTypes.swift index 8a90b03c40598..ef0a7961916ec 100644 --- a/stdlib/private/OSLog/OSLogStringTypes.swift +++ b/stdlib/private/OSLog/OSLogStringTypes.swift @@ -14,8 +14,7 @@ // It defines `appendInterpolation` function for String type. It also defines // extensions for serializing strings into the argument buffer passed to // os_log ABIs. Note that os_log requires passing a stable pointer to an -// interpolated string. The SPI: `_convertConstStringToUTF8PointerArgument` -// is used to construct a stable pointer to a (dynamic) string. +// interpolated string. // // The `appendInterpolation` function defined in this file accept privacy and // alignment options along with the interpolated expression as shown below: @@ -25,17 +24,21 @@ extension OSLogInterpolation { - /// Define interpolation for expressions of type String. + /// Defines interpolation for expressions of type String. + /// + /// Do not call this function directly. It will be called automatically when interpolating + /// a value of type `String` in the string interpolations passed to the log APIs. + /// /// - Parameters: - /// - argumentString: the interpolated expression of type String, which is autoclosured. - /// - align: left or right alignment with the minimum number of columns as - /// defined by the type `OSLogStringAlignment`. - /// - privacy: a privacy qualifier which is either private or public. - /// It is auto-inferred by default. + /// - argumentString: The interpolated expression of type String, which is autoclosured. + /// - align: Left or right alignment with the minimum number of columns as + /// defined by the type `OSLogStringAlignment`. + /// - privacy: A privacy qualifier which is either private or public. + /// It is auto-inferred by default. @_semantics("constant_evaluable") - @_semantics("oslog.requires_constant_arguments") @inlinable @_optimize(none) + @_semantics("oslog.requires_constant_arguments") public mutating func appendInterpolation( _ argumentString: @autoclosure @escaping () -> String, align: OSLogStringAlignment = .none, @@ -48,12 +51,20 @@ extension OSLogInterpolation { // If minimum column width is specified, append this value first. Note that the // format specifier would use a '*' for width e.g. %*s. if let minColumns = align.minimumColumnWidth { - appendPrecisionArgument(minColumns) + appendAlignmentArgument(minColumns) + } + + // If the privacy has a mask, append the mask argument, which is a constant payload. + // Note that this should come after the width but before the precision. + if privacy.hasMask { + appendMaskArgument(privacy) } + // Append the string argument. addStringHeaders(privacy) arguments.append(argumentString) argumentCount += 1 + stringArgumentCount += 1 } /// Update preamble and append argument headers based on the parameters of @@ -90,13 +101,10 @@ extension OSLogInterpolation { _ privacy: OSLogPrivacy ) -> String { var specifier = "%" - switch privacy { - case .private: - specifier += "{private}" - case .public: - specifier += "{public}" - default: - break + if let privacySpecifier = privacy.privacySpecifier { + specifier += "{" + specifier += privacySpecifier + specifier += "}" } if case .start = align.anchor { specifier += "-" @@ -117,7 +125,12 @@ extension OSLogArguments { @inlinable @_optimize(none) internal mutating func append(_ value: @escaping () -> String) { - argumentClosures.append({ serialize(value(), at: &$0, using: &$1) }) + argumentClosures.append({ (position, _, stringArgumentOwners) in + serialize( + value(), + at: &position, + storingStringOwnersIn: &stringArgumentOwners) + }) } } @@ -129,34 +142,48 @@ extension OSLogArguments { /// This function must be constant evaluable. Note that it is marked transparent /// instead of @inline(__always) as it is used in optimize(none) functions. @_transparent -@usableFromInline +@_alwaysEmitIntoClient internal func pointerSizeInBytes() -> Int { return Int.bitWidth &>> logBitsPerByte } /// Serialize a stable pointer to the string `stringValue` at the buffer location -/// pointed by `bufferPosition`. When necessary, this function would copy the -/// string contents to a storage with a stable pointer. If that happens, a reference -/// to the storage will be added to `storageObjects`. -@inlinable +/// pointed to by `bufferPosition`. @_alwaysEmitIntoClient @inline(__always) internal func serialize( _ stringValue: String, - at bufferPosition: inout ByteBufferPointer, - using storageObjects: inout StorageObjects + at bufferPosition: inout UnsafeMutablePointer, + storingStringOwnersIn stringArgumentOwners: inout ObjectStorage ) { - let (optionalStorage, bytePointer): (AnyObject?, UnsafeRawPointer) = - _convertConstStringToUTF8PointerArgument( - stringValue) - - if let storage = optionalStorage { - storageObjects.append(storage) - } + let stringPointer = + getNullTerminatedUTF8Pointer( + stringValue, + storingStringOwnersIn: &stringArgumentOwners) let byteCount = pointerSizeInBytes() let dest = UnsafeMutableRawBufferPointer(start: bufferPosition, count: byteCount) - withUnsafeBytes(of: bytePointer) { dest.copyMemory(from: $0) } + withUnsafeBytes(of: stringPointer) { dest.copyMemory(from: $0) } bufferPosition += byteCount } + +/// Return a pointer that points to a contiguous sequence of null-terminated, +/// UTF8 charcters. If necessary, extends the lifetime of `stringValue` by +/// using `stringArgumentOwners`. +@_alwaysEmitIntoClient +@inline(never) +internal func getNullTerminatedUTF8Pointer( + _ stringValue: String, + storingStringOwnersIn stringArgumentOwners: inout ObjectStorage +) -> UnsafeRawPointer { + let (optStorage, bytePointer, _, _, _): + (AnyObject?, UnsafeRawPointer, Int, Bool, Bool) = + stringValue._deconstructUTF8(scratch: nil) + if let storage = optStorage { + initializeAndAdvance(&stringArgumentOwners, to: storage) + } else { + initializeAndAdvance(&stringArgumentOwners, to: stringValue._guts) + } + return bytePointer +} diff --git a/stdlib/private/OSLog/OSLogSwiftProtocols.swift b/stdlib/private/OSLog/OSLogSwiftProtocols.swift new file mode 100644 index 0000000000000..f14ee3c99bef8 --- /dev/null +++ b/stdlib/private/OSLog/OSLogSwiftProtocols.swift @@ -0,0 +1,72 @@ +//===----------------- OSLogSwiftProtocols.swift -----------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +// This file defines extensions for interpolating types conforming to common +// Swift protocols. It defines `appendInterpolation` overloads for these protocols. +// All overloads defined in this file, delegate to other appendInterpolation +// functions for types natively supported by os_log. + +extension OSLogInterpolation { + + /// Defines interpolation for values conforming to CustomStringConvertible. The values + /// are displayed using the description methods on them. + /// + /// Do not call this function directly. It will be called automatically when interpolating + /// a value conforming to CustomStringConvertible in the string interpolations passed + /// to the log APIs. + /// + /// - Parameters: + /// - value: The interpolated expression conforming to CustomStringConvertible. + /// - align: Left or right alignment with the minimum number of columns as + /// defined by the type `OSLogStringAlignment`. + /// - privacy: A privacy qualifier which is either private or public. + /// It is auto-inferred by default. + @_optimize(none) + @_transparent + @_semantics("oslog.requires_constant_arguments") + public mutating func appendInterpolation( + _ value: @autoclosure @escaping () -> T, + align: OSLogStringAlignment = .none, + privacy: OSLogPrivacy = .auto + ) { + // TODO: Dead code elimination does not remove the call to the default value + // of alignment: .none. This function is made @_transparent to work around + // that. + appendInterpolation(value().description, align: align, privacy: privacy) + } + + /// Defines interpolation for meta-types. + /// + /// Do not call this function directly. It will be called automatically when interpolating + /// a value of type `Any.Type` in the string interpolations passed to the log APIs. + /// + /// - Parameters: + /// - value: An interpolated expression of type Any.Type, which is autoclosured. + /// - align: Left or right alignment with the minimum number of columns as + /// defined by the type `OSLogStringAlignment`. + /// - privacy: A privacy qualifier which is either private or public. + /// It is auto-inferred by default. + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + @_semantics("oslog.requires_constant_arguments") + public mutating func appendInterpolation( + _ value: @autoclosure @escaping () -> Any.Type, + align: OSLogStringAlignment = .none, + privacy: OSLogPrivacy = .auto + ) { + appendInterpolation( + _typeName(value(), qualified: false), + align: align, + privacy: privacy) + } +} diff --git a/stdlib/private/OSLog/OSLogTestHelper.swift b/stdlib/private/OSLog/OSLogTestHelper.swift index 85dde5ca81055..3d6e98dbaaad1 100644 --- a/stdlib/private/OSLog/OSLogTestHelper.swift +++ b/stdlib/private/OSLog/OSLogTestHelper.swift @@ -10,6 +10,8 @@ // //===----------------------------------------------------------------------===// +import ObjectiveC + // This file contains test helpers for testing the compiler diagnostics and optimizations // of the new swift APIs for os log that accept string interpolations. @@ -51,41 +53,47 @@ func _osLogTestHelper( let preamble = message.interpolation.preamble let argumentCount = message.interpolation.argumentCount let bufferSize = message.bufferSize + let objectCount = message.interpolation.objectArgumentCount + let stringCount = message.interpolation.stringArgumentCount + let uint32bufferSize = UInt32(bufferSize) let argumentClosures = message.interpolation.arguments.argumentClosures + let formatStringPointer = _getGlobalStringTablePointer(formatString) // Code that will execute at runtime. if (!isLoggingEnabled()) { return } - - // Allocate a byte buffer to store the arguments. The buffer could be stack - // allocated as it is local to this function and also its size is a - // compile-time constant. let bufferMemory = UnsafeMutablePointer.allocate(capacity: bufferSize) - // Array of references to auxiliary storage created during serialization of - // strings. This array can be stack allocated. - var stringStorageObjects: [AnyObject] = [] + // Buffer for storing NSObjects and strings to keep them alive until the + // _os_log_impl_test call completes. + let objectArguments = createStorage(capacity: objectCount, type: NSObject.self) + let stringArgumentOwners = createStorage(capacity: stringCount, type: Any.self) var currentBufferPosition = bufferMemory + var objectArgumentsPosition = objectArguments + var stringArgumentOwnersPosition = stringArgumentOwners serialize(preamble, at: ¤tBufferPosition) serialize(argumentCount, at: ¤tBufferPosition) - argumentClosures.forEach { $0(¤tBufferPosition, &stringStorageObjects) } + argumentClosures.forEach { + $0(¤tBufferPosition, + &objectArgumentsPosition, + &stringArgumentOwnersPosition) + } _os_log_impl_test( assertion, formatString, formatStringPointer, bufferMemory, - UInt32(bufferSize)) + uint32bufferSize) - // The following operation extends the lifetime of argumentClosures, - // stringStorageObjects, and also of the objects stored in them, till this - // point. This is necessary because the assertion is passed internal pointers - // to the objects/strings stored in these arrays, as in the actual os log - // implementation. - _fixLifetime(argumentClosures) - _fixLifetime(stringStorageObjects) + // The following operation extends the lifetime of objectArguments and + // stringArgumentOwners till this point. This is necessary because the + // assertion is passed internal pointers to the objects/strings stored + // in these arrays, as in the actual os log implementation. + destroyStorage(objectArguments, count: objectCount) + destroyStorage(stringArgumentOwners, count: stringCount) bufferMemory.deallocate() } diff --git a/stdlib/private/RuntimeUnittest/CMakeLists.txt b/stdlib/private/RuntimeUnittest/CMakeLists.txt index 3f042f605613a..538ff3c4cf2e5 100644 --- a/stdlib/private/RuntimeUnittest/CMakeLists.txt +++ b/stdlib/private/RuntimeUnittest/CMakeLists.txt @@ -13,7 +13,7 @@ add_swift_target_library(swiftRuntimeUnittest ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES SWIFT_MODULE_DEPENDS_OPENBSD Glibc SWIFT_MODULE_DEPENDS_CYGWIN Glibc SWIFT_MODULE_DEPENDS_HAIKU Glibc - SWIFT_MODULE_DEPENDS_WINDOWS MSVCRT + SWIFT_MODULE_DEPENDS_WINDOWS CRT SWIFT_COMPILE_FLAGS ${swift_stdlib_unittest_compile_flags} ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} INSTALL_IN_COMPONENT stdlib-experimental DARWIN_INSTALL_NAME_DIR "${SWIFT_DARWIN_STDLIB_PRIVATE_INSTALL_NAME_DIR}") diff --git a/stdlib/private/StdlibCollectionUnittest/CMakeLists.txt b/stdlib/private/StdlibCollectionUnittest/CMakeLists.txt index 0efaf5ab37f0b..f0eb6fe467cff 100644 --- a/stdlib/private/StdlibCollectionUnittest/CMakeLists.txt +++ b/stdlib/private/StdlibCollectionUnittest/CMakeLists.txt @@ -24,7 +24,7 @@ add_swift_target_library(swiftStdlibCollectionUnittest ${SWIFT_STDLIB_LIBRARY_BU SWIFT_MODULE_DEPENDS_OPENBSD Glibc SWIFT_MODULE_DEPENDS_CYGWIN Glibc SWIFT_MODULE_DEPENDS_HAIKU Glibc - SWIFT_MODULE_DEPENDS_WINDOWS MSVCRT + SWIFT_MODULE_DEPENDS_WINDOWS CRT SWIFT_COMPILE_FLAGS ${swift_stdlib_unittest_compile_flags} ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} INSTALL_IN_COMPONENT stdlib-experimental DARWIN_INSTALL_NAME_DIR "${SWIFT_DARWIN_STDLIB_PRIVATE_INSTALL_NAME_DIR}") diff --git a/stdlib/private/StdlibUnicodeUnittest/CMakeLists.txt b/stdlib/private/StdlibUnicodeUnittest/CMakeLists.txt index 20aaa0a81bfa8..979cc9dfccb69 100644 --- a/stdlib/private/StdlibUnicodeUnittest/CMakeLists.txt +++ b/stdlib/private/StdlibUnicodeUnittest/CMakeLists.txt @@ -12,7 +12,7 @@ add_swift_target_library(swiftStdlibUnicodeUnittest ${SWIFT_STDLIB_LIBRARY_BUILD SWIFT_MODULE_DEPENDS_OPENBSD Glibc SWIFT_MODULE_DEPENDS_CYGWIN Glibc SWIFT_MODULE_DEPENDS_HAIKU Glibc - SWIFT_MODULE_DEPENDS_WINDOWS MSVCRT + SWIFT_MODULE_DEPENDS_WINDOWS CRT SWIFT_COMPILE_FLAGS ${swift_stdlib_unittest_compile_flags} ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} INSTALL_IN_COMPONENT stdlib-experimental DARWIN_INSTALL_NAME_DIR "${SWIFT_DARWIN_STDLIB_PRIVATE_INSTALL_NAME_DIR}") diff --git a/stdlib/private/StdlibUnittest/CMakeLists.txt b/stdlib/private/StdlibUnittest/CMakeLists.txt index d1f61270a89b6..0e033b97f131b 100644 --- a/stdlib/private/StdlibUnittest/CMakeLists.txt +++ b/stdlib/private/StdlibUnittest/CMakeLists.txt @@ -36,12 +36,13 @@ add_swift_target_library(swiftStdlibUnittest ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} SWIFT_MODULE_DEPENDS_OSX Darwin Foundation SWIFT_MODULE_DEPENDS_TVOS Darwin Foundation SWIFT_MODULE_DEPENDS_WATCHOS Darwin Foundation + SWIFT_MODULE_DEPENDS_FREESTANDING Darwin SWIFT_MODULE_DEPENDS_LINUX Glibc SWIFT_MODULE_DEPENDS_FREEBSD Glibc SWIFT_MODULE_DEPENDS_OPENBSD Glibc SWIFT_MODULE_DEPENDS_CYGWIN Glibc SWIFT_MODULE_DEPENDS_HAIKU Glibc - SWIFT_MODULE_DEPENDS_WINDOWS MSVCRT WinSDK + SWIFT_MODULE_DEPENDS_WINDOWS CRT WinSDK SWIFT_COMPILE_FLAGS ${swift_stdlib_unittest_compile_flags} ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} INSTALL_IN_COMPONENT stdlib-experimental DARWIN_INSTALL_NAME_DIR "${SWIFT_DARWIN_STDLIB_PRIVATE_INSTALL_NAME_DIR}") diff --git a/stdlib/private/StdlibUnittest/RaceTest.swift b/stdlib/private/StdlibUnittest/RaceTest.swift index ef25992e8c54d..58b415965b6d8 100644 --- a/stdlib/private/StdlibUnittest/RaceTest.swift +++ b/stdlib/private/StdlibUnittest/RaceTest.swift @@ -44,7 +44,7 @@ import Darwin #elseif canImport(Glibc) import Glibc #elseif os(Windows) -import MSVCRT +import CRT import WinSDK #endif diff --git a/stdlib/private/StdlibUnittest/StdlibCoreExtras.swift b/stdlib/private/StdlibUnittest/StdlibCoreExtras.swift index a734f9fc0e047..b76c1140f6d7b 100644 --- a/stdlib/private/StdlibUnittest/StdlibCoreExtras.swift +++ b/stdlib/private/StdlibUnittest/StdlibCoreExtras.swift @@ -17,7 +17,7 @@ import Darwin #elseif canImport(Glibc) import Glibc #elseif os(Windows) -import MSVCRT +import CRT #endif #if _runtime(_ObjC) diff --git a/stdlib/private/StdlibUnittest/StdlibUnittest.swift b/stdlib/private/StdlibUnittest/StdlibUnittest.swift index f8841795eee92..7e6e82c47182d 100644 --- a/stdlib/private/StdlibUnittest/StdlibUnittest.swift +++ b/stdlib/private/StdlibUnittest/StdlibUnittest.swift @@ -16,12 +16,14 @@ import SwiftPrivateThreadExtras import SwiftPrivateLibcExtras #if canImport(Darwin) +#if _runtime(_ObjC) import Foundation +#endif import Darwin #elseif canImport(Glibc) import Glibc #elseif os(Windows) -import MSVCRT +import CRT import WinSDK #endif @@ -1729,9 +1731,19 @@ public final class TestSuite { } #if canImport(Darwin) +#if _runtime(_ObjC) func _getSystemVersionPlistProperty(_ propertyName: String) -> String? { return NSDictionary(contentsOfFile: "/System/Library/CoreServices/SystemVersion.plist")?[propertyName] as? String } +#else +func _getSystemVersionPlistProperty(_ propertyName: String) -> String? { + var count = 0 + sysctlbyname("kern.osproductversion", nil, &count, nil, 0) + var s = [CChar](repeating: 0, count: count) + sysctlbyname("kern.osproductversion", &s, &count, nil, 0) + return String(cString: &s) +} +#endif #endif public enum OSVersion : CustomStringConvertible { diff --git a/stdlib/private/StdlibUnittest/SymbolLookup.swift b/stdlib/private/StdlibUnittest/SymbolLookup.swift index 2d99cc4f6f3da..5cab6a1ac5c1b 100644 --- a/stdlib/private/StdlibUnittest/SymbolLookup.swift +++ b/stdlib/private/StdlibUnittest/SymbolLookup.swift @@ -15,7 +15,7 @@ #elseif canImport(Glibc) import Glibc #elseif os(Windows) - import MSVCRT + import CRT import WinSDK #else #error("Unsupported platform") diff --git a/stdlib/private/SwiftPrivate/CMakeLists.txt b/stdlib/private/SwiftPrivate/CMakeLists.txt index e0ab051ba48a2..c68670bce1814 100644 --- a/stdlib/private/SwiftPrivate/CMakeLists.txt +++ b/stdlib/private/SwiftPrivate/CMakeLists.txt @@ -15,7 +15,7 @@ add_swift_target_library(swiftSwiftPrivate ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} I GYB_SOURCES AtomicInt.swift.gyb - SWIFT_MODULE_DEPENDS_WINDOWS MSVCRT WinSDK + SWIFT_MODULE_DEPENDS_WINDOWS CRT WinSDK SWIFT_COMPILE_FLAGS ${swift_swiftprivate_compile_flags} ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} INSTALL_IN_COMPONENT stdlib-experimental DARWIN_INSTALL_NAME_DIR "${SWIFT_DARWIN_STDLIB_PRIVATE_INSTALL_NAME_DIR}") diff --git a/stdlib/private/SwiftPrivate/IO.swift b/stdlib/private/SwiftPrivate/IO.swift index 152ae9a0bc1a8..f973261d4c920 100644 --- a/stdlib/private/SwiftPrivate/IO.swift +++ b/stdlib/private/SwiftPrivate/IO.swift @@ -14,7 +14,7 @@ import Swift import SwiftShims #if os(Windows) -import MSVCRT +import CRT import WinSDK #endif diff --git a/stdlib/private/SwiftPrivateLibcExtras/CMakeLists.txt b/stdlib/private/SwiftPrivateLibcExtras/CMakeLists.txt index b923d3f1d5e99..29a377932b99c 100644 --- a/stdlib/private/SwiftPrivateLibcExtras/CMakeLists.txt +++ b/stdlib/private/SwiftPrivateLibcExtras/CMakeLists.txt @@ -13,11 +13,12 @@ add_swift_target_library(swiftSwiftPrivateLibcExtras ${SWIFT_STDLIB_LIBRARY_BUIL SWIFT_MODULE_DEPENDS_IOS Darwin SWIFT_MODULE_DEPENDS_TVOS Darwin SWIFT_MODULE_DEPENDS_WATCHOS Darwin + SWIFT_MODULE_DEPENDS_FREESTANDING Darwin SWIFT_MODULE_DEPENDS_LINUX Glibc SWIFT_MODULE_DEPENDS_FREEBSD Glibc SWIFT_MODULE_DEPENDS_OPENBSD Glibc SWIFT_MODULE_DEPENDS_CYGWIN Glibc SWIFT_MODULE_DEPENDS_HAIKU Glibc - SWIFT_MODULE_DEPENDS_WINDOWS MSVCRT WinSDK + SWIFT_MODULE_DEPENDS_WINDOWS CRT WinSDK INSTALL_IN_COMPONENT stdlib-experimental DARWIN_INSTALL_NAME_DIR "${SWIFT_DARWIN_STDLIB_PRIVATE_INSTALL_NAME_DIR}") diff --git a/stdlib/private/SwiftPrivateLibcExtras/Subprocess.swift b/stdlib/private/SwiftPrivateLibcExtras/Subprocess.swift index 0b9aeb8466b65..e28c46872ff42 100644 --- a/stdlib/private/SwiftPrivateLibcExtras/Subprocess.swift +++ b/stdlib/private/SwiftPrivateLibcExtras/Subprocess.swift @@ -16,7 +16,7 @@ import Darwin #elseif canImport(Glibc) import Glibc #elseif os(Windows) -import MSVCRT +import CRT import WinSDK #endif diff --git a/stdlib/private/SwiftPrivateLibcExtras/SwiftPrivateLibcExtras.swift b/stdlib/private/SwiftPrivateLibcExtras/SwiftPrivateLibcExtras.swift index d9dd93277e471..b5e76d6be64f8 100644 --- a/stdlib/private/SwiftPrivateLibcExtras/SwiftPrivateLibcExtras.swift +++ b/stdlib/private/SwiftPrivateLibcExtras/SwiftPrivateLibcExtras.swift @@ -16,7 +16,7 @@ import Darwin #elseif canImport(Glibc) import Glibc #elseif os(Windows) -import MSVCRT +import CRT #endif public func _stdlib_mkstemps(_ template: inout String, _ suffixlen: CInt) -> CInt { diff --git a/stdlib/private/SwiftPrivateThreadExtras/CMakeLists.txt b/stdlib/private/SwiftPrivateThreadExtras/CMakeLists.txt index 76487eb500314..e2c7b198ecb73 100644 --- a/stdlib/private/SwiftPrivateThreadExtras/CMakeLists.txt +++ b/stdlib/private/SwiftPrivateThreadExtras/CMakeLists.txt @@ -10,14 +10,14 @@ add_swift_target_library(swiftSwiftPrivateThreadExtras ${SWIFT_STDLIB_LIBRARY_BU SWIFT_MODULE_DEPENDS_OSX Darwin SWIFT_MODULE_DEPENDS_TVOS Darwin SWIFT_MODULE_DEPENDS_WATCHOS Darwin + SWIFT_MODULE_DEPENDS_FREESTANDING Darwin SWIFT_MODULE_DEPENDS_LINUX Glibc SWIFT_MODULE_DEPENDS_FREEBSD Glibc SWIFT_MODULE_DEPENDS_OPENBSD Glibc SWIFT_MODULE_DEPENDS_CYGWIN Glibc SWIFT_MODULE_DEPENDS_HAIKU Glibc - SWIFT_MODULE_DEPENDS_WINDOWS MSVCRT WinSDK + SWIFT_MODULE_DEPENDS_WINDOWS CRT WinSDK SWIFT_COMPILE_FLAGS ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} - TARGET_SDKS ALL_APPLE_PLATFORMS CYGWIN FREEBSD OPENBSD HAIKU LINUX WINDOWS ANDROID INSTALL_IN_COMPONENT stdlib-experimental DARWIN_INSTALL_NAME_DIR "${SWIFT_DARWIN_STDLIB_PRIVATE_INSTALL_NAME_DIR}") diff --git a/stdlib/private/SwiftPrivateThreadExtras/SwiftPrivateThreadExtras.swift b/stdlib/private/SwiftPrivateThreadExtras/SwiftPrivateThreadExtras.swift index 9b044cbd55457..fa802a44cc49c 100644 --- a/stdlib/private/SwiftPrivateThreadExtras/SwiftPrivateThreadExtras.swift +++ b/stdlib/private/SwiftPrivateThreadExtras/SwiftPrivateThreadExtras.swift @@ -20,7 +20,7 @@ import Darwin #elseif canImport(Glibc) import Glibc #elseif os(Windows) -import MSVCRT +import CRT import WinSDK #endif diff --git a/stdlib/private/SwiftPrivateThreadExtras/ThreadBarriers.swift b/stdlib/private/SwiftPrivateThreadExtras/ThreadBarriers.swift index 73b989377c578..4138ddc7bfa84 100644 --- a/stdlib/private/SwiftPrivateThreadExtras/ThreadBarriers.swift +++ b/stdlib/private/SwiftPrivateThreadExtras/ThreadBarriers.swift @@ -15,7 +15,7 @@ import Darwin #elseif canImport(Glibc) import Glibc #elseif os(Windows) -import MSVCRT +import CRT import WinSDK #endif diff --git a/stdlib/private/SwiftReflectionTest/CMakeLists.txt b/stdlib/private/SwiftReflectionTest/CMakeLists.txt index 7c33850c96a2c..f07e50f13e2b6 100644 --- a/stdlib/private/SwiftReflectionTest/CMakeLists.txt +++ b/stdlib/private/SwiftReflectionTest/CMakeLists.txt @@ -9,7 +9,7 @@ if (SWIFT_INCLUDE_TESTS) SWIFT_MODULE_DEPENDS_TVOS Darwin SWIFT_MODULE_DEPENDS_WATCHOS Darwin SWIFT_MODULE_DEPENDS_LINUX Glibc - SWIFT_MODULE_DEPENDS_WINDOWS MSVCRT + SWIFT_MODULE_DEPENDS_WINDOWS CRT INSTALL_IN_COMPONENT stdlib-experimental DARWIN_INSTALL_NAME_DIR "${SWIFT_DARWIN_STDLIB_PRIVATE_INSTALL_NAME_DIR}") diff --git a/stdlib/private/SwiftReflectionTest/SwiftReflectionTest.swift b/stdlib/private/SwiftReflectionTest/SwiftReflectionTest.swift index b077bd4923a2e..cb2350b845f2c 100644 --- a/stdlib/private/SwiftReflectionTest/SwiftReflectionTest.swift +++ b/stdlib/private/SwiftReflectionTest/SwiftReflectionTest.swift @@ -216,7 +216,7 @@ internal struct ReflectionInfo : Sequence { } internal func sendBytes(from address: UnsafePointer, count: Int) { - var source = address + var source = UnsafeRawPointer(address) var bytesLeft = count debugLog("BEGIN \(#function)"); defer { debugLog("END \(#function)") } while bytesLeft > 0 { diff --git a/stdlib/public/CMakeLists.txt b/stdlib/public/CMakeLists.txt index 9141a0db2d466..f32785db29943 100644 --- a/stdlib/public/CMakeLists.txt +++ b/stdlib/public/CMakeLists.txt @@ -15,7 +15,11 @@ if(SWIFT_RUNTIME_USE_SANITIZERS) endif() endif() -list(APPEND SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS "-Xfrontend" "-verify-syntax-tree") +list(APPEND SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS "-Xfrontend" "-verify-syntax-tree") + +if(SWIFT_STDLIB_SIL_DEBUGGING) + list(APPEND SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS "-Xfrontend" "-gsil") +endif() # Build the runtime with -Wall to catch, e.g., uninitialized variables # warnings. @@ -100,7 +104,7 @@ if(SWIFT_BUILD_STDLIB OR SWIFT_BUILD_REMOTE_MIRROR) add_subdirectory(SwiftRemoteMirror) endif() -if(SWIFT_BUILD_SDK_OVERLAY) +if(SWIFT_BUILD_SDK_OVERLAY OR SWIFT_BUILD_TEST_SUPPORT_MODULES) add_subdirectory(Platform) endif() diff --git a/stdlib/public/Concurrency/Actor.swift b/stdlib/public/Concurrency/Actor.swift new file mode 100644 index 0000000000000..212651ecfb6b3 --- /dev/null +++ b/stdlib/public/Concurrency/Actor.swift @@ -0,0 +1,55 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import Swift +@_implementationOnly import _SwiftConcurrencyShims + +/// Common protocol to which all actor classes conform. +/// +/// The \c Actor protocol provides the core functionality of an actor class, +/// which involves enqueuing new partial tasks to be executed at some +/// point. Actor classes implicitly conform to this protocol as part of their +/// primary class definition. +public protocol Actor: AnyObject { + /// Enqueue a new partial task that will be executed in the actor's context. + func enqueue(partialTask: PartialAsyncTask) +} + +/// A native actor queue, which schedules partial tasks onto a serial queue. +public struct _NativeActorQueue { + // TODO: This is just a stub for now +} + +/// The default type to be used for an actor's queue when an actor does not +/// provide its own implementation of `enqueue(partialTask:)`. +public typealias _DefaultActorQueue = _NativeActorQueue + +/// Called to create a new default actor queue instance for a class of the given +/// type. The implementation will call this within the actor's initializer to +/// initialize the actor queue. +public func _defaultActorQueueCreate( + _ actorClass: AnyObject.Type +) -> _DefaultActorQueue { + _DefaultActorQueue() +} + +/// Called by the synthesized implementation of enqueue(partialTask:). +/// +/// The implementation is provided with the address of the synthesized instance +/// property for the actor queue, so that it need not be at a fixed offset. +public func _defaultActorQueueEnqueuePartialTask( + actor: AnyObject, + queue: inout _DefaultActorQueue, + partialTask: PartialAsyncTask +) { + // TODO: Implement queueing. +} diff --git a/stdlib/public/Concurrency/CMakeLists.txt b/stdlib/public/Concurrency/CMakeLists.txt index fa491ec431295..9ad4cfebddb58 100644 --- a/stdlib/public/Concurrency/CMakeLists.txt +++ b/stdlib/public/Concurrency/CMakeLists.txt @@ -11,7 +11,13 @@ #===----------------------------------------------------------------------===# add_swift_target_library(swift_Concurrency ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_STDLIB + Actor.swift PartialAsyncTask.swift + Task.cpp + Task.swift + TaskAlloc.cpp + TaskStatus.cpp + Mutex.cpp SWIFT_MODULE_DEPENDS_OSX Darwin SWIFT_MODULE_DEPENDS_IOS Darwin @@ -22,11 +28,16 @@ add_swift_target_library(swift_Concurrency ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} I SWIFT_MODULE_DEPENDS_OPENBSD Glibc SWIFT_MODULE_DEPENDS_CYGWIN Glibc SWIFT_MODULE_DEPENDS_HAIKU Glibc - SWIFT_MODULE_DEPENDS_WINDOWS MSVCRT + SWIFT_MODULE_DEPENDS_WINDOWS CRT + LINK_LIBRARIES swiftCore + + C_COMPILE_FLAGS + -Dswift_Concurrency_EXPORTS SWIFT_COMPILE_FLAGS ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} -parse-stdlib + -Xfrontend -enable-experimental-concurrency LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" - DARWIN_INSTALL_NAME_DIR "@rpath" - INSTALL_IN_COMPONENT stdlib) + INSTALL_IN_COMPONENT stdlib +) diff --git a/stdlib/public/Concurrency/Mutex.cpp b/stdlib/public/Concurrency/Mutex.cpp new file mode 100644 index 0000000000000..95dd58e4e05d8 --- /dev/null +++ b/stdlib/public/Concurrency/Mutex.cpp @@ -0,0 +1,21 @@ +//===--- Mutex.cpp - Mutex support code -----------------------------------===// +// +// 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 the runtime's mutex support code. +// FIXME: figure out some reasonable way to share this stuff + +#include "../runtime/MutexPThread.cpp" +#include "../runtime/MutexWin32.cpp" + +SWIFT_NORETURN void swift::fatalError(uint32_t flags, const char *format, ...) { + abort(); +} \ No newline at end of file diff --git a/stdlib/public/Concurrency/PartialAsyncTask.swift b/stdlib/public/Concurrency/PartialAsyncTask.swift index fe55edc48a189..46e10801803c9 100644 --- a/stdlib/public/Concurrency/PartialAsyncTask.swift +++ b/stdlib/public/Concurrency/PartialAsyncTask.swift @@ -13,8 +13,25 @@ import Swift @_implementationOnly import _SwiftConcurrencyShims +/// A partial task is a unit of scheduleable work. public struct PartialAsyncTask { private var context: UnsafeMutablePointer<_SwiftContext> public func run() { } } + + +public struct UnsafeContinuation { + private var context: UnsafeRawPointer + + public func resume(_: T) { } +} + +public struct UnsafeThrowingContinuation { + private var context: UnsafeRawPointer + + public func resume(_: T) { } + public func fail(_: Error) { } +} + + diff --git a/stdlib/public/Concurrency/Task.cpp b/stdlib/public/Concurrency/Task.cpp new file mode 100644 index 0000000000000..c163f9791fb47 --- /dev/null +++ b/stdlib/public/Concurrency/Task.cpp @@ -0,0 +1,134 @@ +//===--- Task.cpp - Task object and management ----------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Object management routines for asynchronous task objects. +// +//===----------------------------------------------------------------------===// + +#include "swift/Runtime/Concurrency.h" +#include "swift/ABI/Task.h" +#include "swift/ABI/Metadata.h" +#include "swift/Runtime/HeapObject.h" +#include "TaskPrivate.h" + +using namespace swift; + +SWIFT_CC(swift) +static void destroySimpleTask(SWIFT_CONTEXT HeapObject *_obj) { + auto obj = static_cast(_obj); + + assert(!obj->isFuture()); + + // The task execution itself should always hold a reference to it, so + // if we get here, we know the task has finished running, which means + // swift_task_complete should have been run, which will have torn down + // the task-local allocator. There's actually nothing else to clean up + // here. + + free(obj); +} + +/// Heap metadata for a simple asynchronous task that does not +/// include a future. +static FullMetadata simpleTaskHeapMetadata = { + { + { + &destroySimpleTask + }, + { + /*value witness table*/ nullptr + } + }, + { + MetadataKind::SimpleTask + } +}; + +/// The function that we put in the context of a simple task +/// (one with no future) to handle the final return. +SWIFT_CC(swift) +static void completeTask(AsyncTask *task, ExecutorRef executor, + AsyncContext *context) { + // Tear down the task-local allocator immediately; there's no need + // to wait for the object to be destroyed. + _swift_task_alloc_destroy(task); + + // TODO: set something in the status? + // TODO: notify the parent somehow? + // TODO: remove this task from the child-task chain? + // TODO: notify tasks waiting on the future? + + // Release the task, balancing the retain that a running task + // has on itself. + swift_release(task); +} + +AsyncTaskAndContext +swift::swift_task_create(JobFlags flags, AsyncTask *parent, + const AsyncFunctionPointer *function) { + return swift_task_create_f(flags, parent, function->Function.get(), + function->ExpectedContextSize); +} + +AsyncTaskAndContext +swift::swift_task_create_f(JobFlags flags, AsyncTask *parent, + AsyncFunctionType *function, + size_t initialContextSize) { + assert(!flags.task_isFuture() && "function doesn't support creating futures"); + assert((parent != nullptr) == flags.task_isChildTask()); + + // Figure out the size of the header. + size_t headerSize = sizeof(AsyncTask); + if (parent) headerSize += sizeof(AsyncTask::ChildFragment); + + // Allocate the initial context together with the job. + // This means that we never get rid of this allocation. + size_t amountToAllocate = headerSize + initialContextSize; + + assert(amountToAllocate % MaximumAlignment == 0); + + void *allocation = malloc(amountToAllocate); + + AsyncContext *initialContext = + reinterpret_cast( + reinterpret_cast(allocation) + headerSize); + + // Initialize the task so that resuming it will run the given + // function on the initial context. + AsyncTask *task = + new(allocation) AsyncTask(&simpleTaskHeapMetadata, flags, + function, initialContext); + + // Initialize the child fragment if applicable. + // TODO: propagate information from the parent? + if (parent) { + auto childFragment = task->childFragment(); + new (childFragment) AsyncTask::ChildFragment(parent); + } + + // Configure the initial context. + // + // FIXME: if we store a null pointer here using the standard ABI for + // signed null pointers, then we'll have to authenticate context pointers + // as if they might be null, even though the only time they ever might + // be is the final hop. Store a signed null instead. + initialContext->Parent = nullptr; + initialContext->ResumeParent = &completeTask; + initialContext->ResumeParentExecutor = ExecutorRef::noPreference(); + initialContext->Flags = AsyncContextKind::Ordinary; + initialContext->Flags.setShouldNotDeallocateInCallee(true); + + // Initialize the task-local allocator. + _swift_task_alloc_initialize(task); + + return {task, initialContext}; +} diff --git a/stdlib/public/Concurrency/Task.swift b/stdlib/public/Concurrency/Task.swift new file mode 100644 index 0000000000000..ce6551cae40bb --- /dev/null +++ b/stdlib/public/Concurrency/Task.swift @@ -0,0 +1,264 @@ +////===----------------------------------------------------------------------===// +//// +//// This source file is part of the Swift.org open source project +//// +//// Copyright (c) 2020 Apple Inc. and the Swift project authors +//// Licensed under Apache License v2.0 with Runtime Library Exception +//// +//// See https://swift.org/LICENSE.txt for license information +//// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +//// +////===----------------------------------------------------------------------===// + +import Swift +@_implementationOnly import _SwiftConcurrencyShims + +// ==== Task ------------------------------------------------------------------- + +/// An asynchronous task (just "Task" hereafter) is the analogue of a thread for +/// asynchronous functions. All asynchronous functions run as part of some task. +/// +/// A task's execution can be seen as a series of periods where the task was +/// running. Each such period ends at a suspension point or -- finally -- the +/// completion of the task. +/// +/// These partial periods towards the task's completion are `PartialAsyncTask`. +/// Partial tasks are generally not interacted with by end-users directly, +/// unless implementing a scheduler. +public struct Task { +} + +// ==== Current Task ----------------------------------------------------------- + +extension Task { + /// Returns the currently executing `Task`. + /// + /// As invoking this function is only possible from an asynchronous context + /// it is always able to return the current `Task` in which we are currently + /// running. + public static func current() async -> Task { + fatalError("\(#function) not implemented yet.") // TODO: needs a built-in function + } +} + +// ==== Task Priority ---------------------------------------------------------- + +extension Task { + /// Task priority may inform decisions an `Executor` makes about how and when + /// to schedule tasks submitted to it. + /// + /// ### Priority scheduling + /// An executor MAY utilize priority information to attempt running higher + /// priority tasks first, and then continuing to serve lower priority tasks. + /// + /// The exact semantics of how priority is treated are left up to each + /// platform and `Executor` implementation. + /// + /// ### Priority inheritance + /// Child tasks automatically inherit their parent task's priority. + /// + /// Detached tasks (created by `Task.runDetached`) DO NOT inherit task priority, + /// as they are "detached" from their parent tasks after all. + /// + /// ### Priority elevation + /// In some situations the priority of a task must be elevated ("raised"): + /// + /// - if a `Task` running on behalf of an actor, and a new higher-priority + /// task is enqueued to the actor, its current task must be temporarily + /// elevated to the priority of the enqueued task, in order to allow the new + /// task to be processed at--effectively-- the priority it was enqueued with. + /// - this DOES NOT affect `Task.currentPriority()`. + /// - if a task is created with a `Task.Handle`, and a higher-priority task + /// calls the `await handle.get()` function the priority of this task must be + /// permanently increased until the task completes. + /// - this DOES affect `Task.currentPriority()`. + /// + /// TODO: Define the details of task priority; It is likely to be a concept + /// similar to Darwin Dispatch's QoS; bearing in mind that priority is not as + /// much of a thing on other platforms (i.e. server side Linux systems). + public struct Priority: Comparable { + public static let `default`: Task.Priority = .init() // TODO: replace with actual values + + // TODO: specifics of implementation are not decided yet + private let __value: Int = 0 + + public static func < (lhs: Self, rhs: Self) -> Bool { + lhs.__value < rhs.__value + } + } +} + +// ==== Task Handle ------------------------------------------------------------ + +extension Task { + + /// A task handle refers to an in-flight `Task`, + /// allowing for potentially awaiting for its result or canceling it. + /// + /// It is not a programming error to drop a handle without awaiting or canceling it, + /// i.e. the task will run regardless of the handle still being present or not. + /// Dropping a handle however means losing the ability to await on the task's result + /// and losing the ability to cancel it. + public final class Handle { + /// Wait for the task to complete, returning (or throwing) its result. + /// + /// ### Priority + /// If the task has not completed yet, its priority will be elevated to the + /// priority of the current task. Note that this may not be as effective as + /// creating the task with the "right" priority to in the first place. + /// + /// ### Cancellation + /// If the awaited on task gets cancelled the `get()` will throw a cancellation error. + public func get() async throws -> Success { + fatalError("\(#function) not implemented yet.") + } + + /// Attempt to cancel the task. + /// + /// Whether this function has any effect is task-dependent. + /// + /// For a task to respect cancellation it must cooperatively check for it + /// while running. Many tasks will check for cancellation before beginning + /// their "actual work", however this is not a requirement nor is it guaranteed + /// how and when tasks check for cancellation in general. + public func cancel() { + fatalError("\(#function) not implemented yet.") + } + } +} + +// ==== Detached Tasks --------------------------------------------------------- + +extension Task { + /// Run given `operation` as part of a new top-level task. + /// + /// Creating detached tasks should, generally, be avoided in favor of using + /// `async` functions, `async let` declarations and `await` expressions - as + /// those benefit from structured, bounded concurrency which is easier to reason + /// about, as well as automatically inheriting the parent tasks priority, + /// task-local storage, deadlines, as well as being cancelled automatically + /// when their parent task is cancelled. Detached tasks do not get any of those + /// benefits, and thus should only be used when an operation is impossible to + /// be modelled with child tasks. + /// + /// ### Cancellation + /// A detached task always runs to completion unless it is explicitly cancelled. + /// Specifically, dropping a detached tasks `Task.Handle` does _not_ automatically + /// cancel given task. + /// + /// Canceling a task must be performed explicitly via `handle.cancel()`. + /// + /// - Parameters: + /// - priority: priority of the task TODO: reword and define more explicitly once we have priorities well-defined + /// - operation: + /// - Returns: handle to the task, allowing to `await handle.get()` on the + /// tasks result or `cancel` it. + /// + /// - Note: it is generally preferable to use child tasks rather than detached + /// tasks. Child tasks automatically carry priorities, task-local state, + /// deadlines and have other benefits resulting from the structured + /// concurrency concepts that they model. Consider using detached tasks only + /// when strictly necessary and impossible to model operations otherwise. + public static func runDetached( + priority: Priority = .default, + operation: () async -> T + ) -> Handle { + fatalError("\(#function) not implemented yet.") + } + + /// Run given throwing `operation` as part of a new top-level task. + /// + /// Creating detached tasks should, generally, be avoided in favor of using + /// `async` functions, `async let` declarations and `await` expressions - as + /// those benefit from structured, bounded concurrency which is easier to reason + /// about, as well as automatically inheriting the parent tasks priority, + /// task-local storage, deadlines, as well as being cancelled automatically + /// when their parent task is cancelled. Detached tasks do not get any of those + /// benefits, and thus should only be used when an operation is impossible to + /// be modelled with child tasks. + /// + /// ### Cancellation + /// A detached task always runs to completion unless it is explicitly cancelled. + /// Specifically, dropping a detached tasks `Task.Handle` does _not_ automatically + /// cancel given task. + /// + /// Canceling a task must be performed explicitly via `handle.cancel()`. + /// + /// - Parameters: + /// - priority: priority of the task TODO: reword and define more explicitly once we have priorities well-defined + /// - operation: + /// - Returns: handle to the task, allowing to `await handle.get()` on the + /// tasks result or `cancel` it. If the operation fails the handle will + /// throw the error the operation has thrown when awaited on. + /// + /// - Note: it is generally preferable to use child tasks rather than detached + /// tasks. Child tasks automatically carry priorities, task-local state, + /// deadlines and have other benefits resulting from the structured + /// concurrency concepts that they model. Consider using detached tasks only + /// when strictly necessary and impossible to model operations otherwise. + public static func runDetached( + priority: Priority = .default, + operation: () async throws -> T + ) -> Handle { + fatalError("\(#function) not implemented yet.") + } +} + +// ==== UnsafeContinuation ----------------------------------------------------- + +extension Task { + public struct UnsafeContinuation { + /// Return a value into the continuation and make the task schedulable. + /// + /// The task will never run synchronously, even if the task does not + /// need to be resumed on a specific executor. + /// + /// This is appropriate when the caller is something "busy", like an event + /// loop, and doesn't want to be potentially delayed by arbitrary work. + public func resume(returning: T) { + fatalError("\(#function) not implemented yet.") + } + } + + public struct UnsafeThrowingContinuation { + /// Return a value into the continuation and make the task schedulable. + /// + /// The task will never run synchronously, even if the task does not + /// need to be resumed on a specific executor. + /// + /// This is appropriate when the caller is something "busy", like an event + /// loop, and doesn't want to be potentially delayed by arbitrary work. + public func resume(returning: T) { + fatalError("\(#function) not implemented yet.") + } + + /// Resume the continuation with an error and make the task schedulable. + /// + /// The task will never run synchronously, even if the task does not + /// need to be resumed on a specific executor. + /// + /// This is appropriate when the caller is something "busy", like an event + /// loop, and doesn't want to be potentially delayed by arbitrary work. + public func resume(throwing: E) { + fatalError("\(#function) not implemented yet.") + } + } + + /// The operation functions must resume the continuation *exactly once*. + /// + /// The continuation will not begin executing until the operation function returns. + public static func withUnsafeContinuation( + operation: (UnsafeContinuation) -> Void + ) async -> T { + fatalError("\(#function) not implemented yet.") + } + + /// The operation functions must resume the continuation *exactly once*. + /// + /// The continuation will not begin executing until the operation function returns. + public static func withUnsafeThrowingContinuation( + operation: (UnsafeThrowingContinuation) -> Void + ) async throws -> T { + fatalError("\(#function) not implemented yet.") + } +} diff --git a/stdlib/public/Concurrency/TaskAlloc.cpp b/stdlib/public/Concurrency/TaskAlloc.cpp new file mode 100644 index 0000000000000..37858b9312e80 --- /dev/null +++ b/stdlib/public/Concurrency/TaskAlloc.cpp @@ -0,0 +1,87 @@ +//===--- TaskAlloc.cpp - Task-local stack allocator -----------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// A task-local allocator that obeys a stack discipline. +// +// Because allocation is task-local, and there's at most one thread +// running a task at once, no synchronization is required. +// +//===----------------------------------------------------------------------===// + +#include "TaskPrivate.h" +#include "swift/Runtime/Concurrency.h" +#include "swift/Runtime/Debug.h" +#include +#include + +using namespace swift; + +namespace { + +class TaskAllocator { + // Just keep track of all allocations in a vector so that we can + // verify stack discipline. We should make sure the allocator + // implementation strictly verifies allocation order at least + // until we've stabilized the compiler implementation. + std::vector Allocations; + +public: + void *alloc(size_t size) { + void *ptr = malloc(size); + Allocations.push_back(ptr); + return ptr; + } + + void dealloc(void *ptr) { + if (Allocations.empty() || Allocations.back() != ptr) + fatalError(0, "pointer was not the last allocation on this task"); + + Allocations.pop_back(); + free(ptr); + } +}; + +static_assert(sizeof(TaskAllocator) <= sizeof(AsyncTask::AllocatorPrivate), + "task allocator must fit in allocator-private slot"); + +static_assert(alignof(TaskAllocator) <= alignof(decltype(AsyncTask::AllocatorPrivate)), + "task allocator must not be more aligned than " + "allocator-private slot"); + +} // end anonymous namespace + +void swift::_swift_task_alloc_initialize(AsyncTask *task) { + new (task->AllocatorPrivate) TaskAllocator(); +} + +static TaskAllocator &allocator(AsyncTask *task) { + if (task) + return reinterpret_cast(task->AllocatorPrivate); + + // FIXME: this fall-back shouldn't be necessary, but it's useful + // for now, since the current execution tests aren't setting up a task + // properly. + static TaskAllocator global; + return global; +} + +void swift::_swift_task_alloc_destroy(AsyncTask *task) { + allocator(task).~TaskAllocator(); +} + +void *swift::swift_task_alloc(AsyncTask *task, size_t size) { + return allocator(task).alloc(size); +} + +void swift::swift_task_dealloc(AsyncTask *task, void *ptr) { + return allocator(task).dealloc(ptr); +} diff --git a/stdlib/public/Concurrency/TaskPrivate.h b/stdlib/public/Concurrency/TaskPrivate.h new file mode 100644 index 0000000000000..13d130ff44a77 --- /dev/null +++ b/stdlib/public/Concurrency/TaskPrivate.h @@ -0,0 +1,32 @@ +//===--- TaskPrivate.h - Concurrency library internal interface -*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Internal functions for the concurrency library. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_CONCURRENCY_TASKPRIVATE_H +#define SWIFT_CONCURRENCY_TASKPRIVATE_H + +namespace swift { + +class AsyncTask; + +/// Initialize the task-local allocator in the given task. +void _swift_task_alloc_initialize(AsyncTask *task); + +/// Destsroy the task-local allocator in the given task. +void _swift_task_alloc_destroy(AsyncTask *task); + +} // end namespace swift + +#endif diff --git a/stdlib/public/Concurrency/TaskStatus.cpp b/stdlib/public/Concurrency/TaskStatus.cpp new file mode 100644 index 0000000000000..453f98f5bc893 --- /dev/null +++ b/stdlib/public/Concurrency/TaskStatus.cpp @@ -0,0 +1,589 @@ +//===--- TaskStatus.cpp - Asynchronous task status tracking ---------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Routines for maintaining and interacting with the current state of a +// task, including tracking child tasks, deadlines, and cancellation. +// +//===----------------------------------------------------------------------===// + +#include "swift/Runtime/Concurrency.h" +#include "swift/Runtime/Mutex.h" +#include "swift/ABI/TaskStatus.h" +#include + +using namespace swift; + +/**************************************************************************/ +/************************* RECORD LOCK MANAGEMENT *************************/ +/**************************************************************************/ + +/// A lock used to protect management of task-specific status +/// record locks. +static StaticMutex StatusRecordLockLock; + +namespace { + +/// A lock record which can be used to protect a task's active +/// status records. +/// +/// For the most part, the active task status records of a task are +/// only accessed by the task itself. If that were always true, +/// no synchronization would be required to change them. However, +/// cancellation and escalation can occur asynchronously, and they +/// must be able to inspect the status records without worrying about +/// their concurrent modification or destruction of the records. +/// Therefore, these operations freeze the active status records +/// for their duration. They do this by (1) setting a bit in the +/// task's `Status` field state which says that the records are +/// locked and (2) creating a lock record as the new innermost +/// status record. When the operation is complete, it removes this +/// record and clears the lock bit, then notifies the lock record that +/// the locking operation is complete. +/// +/// When a task wants to change its active status record, but +/// it sees that the locked bit is set in the `Status` field, it +/// must acquire the global status-record lock, find this record +/// (which should be the innermost record), and wait for an unlock. +class StatusRecordLockRecord : public TaskStatusRecord { + /// A lock held by the locking thread for the duration of some + /// operation. The real lock for the status record state is the + /// isLocked() bit in the active state; this lock is just a + /// mechanism to allow threads to wait for that lock. This is + /// rather unfortunately heavyweight, but we're willing make + /// locking expensive if it makes a task's normal record + /// manipulations as cheap as possible. + Mutex LockingThreadLock; + + /// A condition variable that the locking thread waits for if + /// there are active unlock waiters when it tries to unlock. + ConditionVariable LockerQueue; + + // These fields are protected by StatusRecordLockLock, + // not LockingThreadLock. + + /// The number of threads waiting for Locked to become false. + size_t NumUnlockWaiters : CHAR_BIT * sizeof(size_t) - 1; + + /// True if the lock has been cleared. + size_t Locked : 1; + +public: + StatusRecordLockRecord(TaskStatusRecord *parent) + : TaskStatusRecord(TaskStatusRecordKind::Private_RecordLock, parent), + NumUnlockWaiters(0), Locked(true) { + // This is always initialized on the locking thread, and + // the private lock starts off locked. + LockingThreadLock.lock(); + } + + ~StatusRecordLockRecord() { + // Unlock the lock before destroying it. + if (Locked) LockingThreadLock.unlock(); + } + + /// Wait on the queue until there's an unlock. + void waitForUnlock(StaticScopedLock &globalLock) { + assert(Locked); + + // Flag that we're waiting, then drop the global lock. + NumUnlockWaiters++; + { + StaticScopedLock globalUnlock(StatusRecordLockLock); + + // Attempt to acquire the locking-thread lock, thereby + // waiting until the locking thread unlocks the record. + { + ScopedLock acquirePrivateLock(LockingThreadLock); + } + + // Now reacquire the global lock. + } + + // The record should always be unlocked now. + assert(!Locked); + + // Remove ourselves from the count, and if the count is zero, + // wake the locking thread. + NumUnlockWaiters--; + if (NumUnlockWaiters == 0) + LockerQueue.notifyAll(); + } + + /// Wake up any threads that were waiting for unlock. Must be + /// called by the locking thread. + void unlock() { + StaticScopedLock globalLock(StatusRecordLockLock); + assert(Locked); + Locked = false; + + // Unlock the locking-thread lock, balancing out the lock() + // call in the constructor. This allows any unlock waiters + // to wake up. + LockingThreadLock.unlock(); + + // As soon as we don't have any unlock waiters, we're done. + while (NumUnlockWaiters) { + // In the meantime, wait on the locker queue, temporarily + // releasing the global lock. + // FIXME: this is a priority inversion; we really want to + // escalate the priority of the waiting threads. + StatusRecordLockLock.wait(LockerQueue); + } + } + + static bool classof(const TaskStatusRecord *record) { + return record->getKind() == TaskStatusRecordKind::Private_RecordLock; + } +}; + +} + +/// Wait for a task's status record lock to be unlocked. +/// +/// When this function returns, `oldStatus` will have been updated +/// to the last value read and `isLocked()` will be false. +/// Of course, another thread may still be concurrently trying +/// to acquire the record lock. +static void waitForStatusRecordUnlock(AsyncTask *task, + ActiveTaskStatus &oldStatus) { + assert(oldStatus.isLocked()); + + // Acquire the lock. + StaticScopedLock globalLock(StatusRecordLockLock); + + while (true) { + // Check that oldStatus is still correct. + oldStatus = task->Status.load(std::memory_order_acquire); + if (!oldStatus.isLocked()) + return; + + // The innermost entry should be a record lock record; wait + // for it to be unlocked. + auto record = oldStatus.getInnermostRecord(); + auto recordLockRecord = cast(record); + recordLockRecord->waitForUnlock(globalLock); + } +} + +/// Acquire a task's status record lock and return the +/// previous value of its status record state. +/// +/// If `forCancellation` is true, the cancelled bit will be set in the +/// state, and the lock will not be acquired if the task is already +/// cancelled or can be cancelled without the lock. If this occurs, +/// `isCancelled()` will be true for the return value. +static ActiveTaskStatus +acquireStatusRecordLock(AsyncTask *task, + Optional &recordLockRecord, + bool forCancellation) { + auto loadOrdering = forCancellation + ? std::memory_order_acquire + : std::memory_order_relaxed; + + // Load the current state. We can use relaxed loads if this isn't + // for cancellation because (1) this operation should be synchronous + // with the task, so the only thing that can modify it asynchronously + // is a cancelling thread, and (2) we'll reload with acquire ordering + // if a cancelling thread forces us to wait for an unlock. + auto oldStatus = task->Status.load(loadOrdering); + + while (true) { + // Cancellation should be idempotent: if the task has already + // been cancelled (or is being cancelled concurrently), there + // shouldn't be any need to do this work again. + if (oldStatus.isCancelled() && forCancellation) + return oldStatus; + + // If the old info says we're locked, wait for the lock to clear. + if (oldStatus.isLocked()) { + waitForStatusRecordUnlock(task, oldStatus); + continue; + } + + // If we're cancelling and the task has no active status records, + // try to just set the cancelled bit and return. + auto oldRecord = oldStatus.getInnermostRecord(); + if (!oldRecord && forCancellation) { + ActiveTaskStatus newStatus(nullptr, + /*cancelled*/ true, + /*locked*/ false); + if (task->Status.compare_exchange_weak(oldStatus, newStatus, + /*success*/ std::memory_order_relaxed, + /*failure*/ loadOrdering)) + return newStatus; + + // If that failed, just restart. + continue; + } + + // Make (or reconfigure) a lock record. + if (!recordLockRecord) { + recordLockRecord.emplace(oldRecord); + } else { + recordLockRecord->resetParent(oldRecord); + } + + // Install the lock record as the active cancellation info, or + // restart if that fails. + bool newIsCancelled = forCancellation || oldStatus.isCancelled(); + ActiveTaskStatus newStatus(&*recordLockRecord, + /*cancelled*/ newIsCancelled, + /*locked*/ true); + if (task->Status.compare_exchange_weak(oldStatus, newStatus, + /*success*/ std::memory_order_release, + /*failure*/ loadOrdering)) + return oldStatus; + } +} + +/// Release a task's status record lock that was previously +/// acquired on this thread. +static void releaseStatusRecordLock(AsyncTask *task, + ActiveTaskStatus newStatus, + Optional &recordLockRecord) { + assert(!newStatus.isLocked()); + + // We can just unconditionally store because nobody can be modifying + // the state while we've locked it. The task shouldn't depend + // on memory-ordering with anything we've done, so we can use a + // relaxed store. + task->Status.store(newStatus, std::memory_order_relaxed); + + // Unlock the record lock. + recordLockRecord->unlock(); +} + +/**************************************************************************/ +/*************************** RECORD MANAGEMENT ****************************/ +/**************************************************************************/ + +bool swift::swift_task_addStatusRecord(AsyncTask *task, + TaskStatusRecord *newRecord) { + // Load the current state. We can use a relaxed load because we're + // synchronous with the task. + auto oldStatus = task->Status.load(std::memory_order_relaxed); + + while (true) { + // Wait for any active lock to be released. + if (oldStatus.isLocked()) + waitForStatusRecordUnlock(task, oldStatus); + + // Reset the parent of the new record. + newRecord->resetParent(oldStatus.getInnermostRecord()); + + // Set the record as the new innermost record. + // We have to use a release on success to make the initialization of + // the new record visible to the cancelling thread. + ActiveTaskStatus newStatus(newRecord, + oldStatus.isCancelled(), + /*locked*/ false); + if (task->Status.compare_exchange_weak(oldStatus, newStatus, + /*success*/ std::memory_order_release, + /*failure*/ std::memory_order_relaxed)) + return !oldStatus.isCancelled(); + } +} + +bool swift::swift_task_tryAddStatusRecord(AsyncTask *task, + TaskStatusRecord *newRecord) { + // Load the current state. We can use a relaxed load because we're + // synchronous with the task. + auto oldStatus = task->Status.load(std::memory_order_relaxed); + + while (true) { + // If the old info is already cancelled, do nothing. + if (oldStatus.isCancelled()) + return false; + + // Wait for any active lock to be released. + if (oldStatus.isLocked()) { + waitForStatusRecordUnlock(task, oldStatus); + + if (oldStatus.isCancelled()) + return false; + } + + // Reset the parent of the new record. + newRecord->resetParent(oldStatus.getInnermostRecord()); + + // Set the record as the new innermost record. + // We have to use a release on success to make the initialization of + // the new record visible to the cancelling thread. + ActiveTaskStatus newStatus(newRecord, + /*cancelled*/ false, + /*locked*/ false); + if (task->Status.compare_exchange_weak(oldStatus, newStatus, + /*success*/ std::memory_order_release, + /*failure*/ std::memory_order_relaxed)) + return true; + } +} + +bool swift::swift_task_removeStatusRecord(AsyncTask *task, + TaskStatusRecord *record) { + // Load the current state. + auto oldStatus = task->Status.load(std::memory_order_relaxed); + + while (true) { + // Wait for any active lock to be released. + if (oldStatus.isLocked()) + waitForStatusRecordUnlock(task, oldStatus); + + // If the record is the innermost record, try to just pop it off. + if (oldStatus.getInnermostRecord() == record) { + ActiveTaskStatus newStatus(record->getParent(), + oldStatus.isCancelled(), + /*locked*/ false); + if (task->Status.compare_exchange_weak(oldStatus, newStatus, + /*success*/ std::memory_order_release, + /*failure*/ std::memory_order_relaxed)) + return !oldStatus.isCancelled(); + + // Otherwise, restart. + continue; + } + + // If the record is not the innermost record, we need to acquire the + // record lock; there's no way to splice the record list safely with + // a thread that's attempting to acquire the lock. + break; + } + + // Acquire the status record lock. + Optional recordLockRecord; + oldStatus = acquireStatusRecordLock(task, recordLockRecord, + /*forCancellation*/ false); + assert(!oldStatus.isLocked()); + + // We can't observe the record to be the innermost record here because + // that would require some other thread to be concurrently structurally + // changing the set of status records, but we're running + // synchronously with the task. + auto cur = oldStatus.getInnermostRecord(); + assert(cur != record); + + // Splice the record out. + while (true) { + auto next = cur->getParent(); + if (next == record) { + cur->spliceParent(record->getParent()); + break; + } + } + + // Release the lock. Since the record can't be the root, we don't + // have to worry about replacing the root, and oldStatus is always + // exactly what we want to restore. + releaseStatusRecordLock(task, oldStatus, recordLockRecord); + + return !oldStatus.isCancelled(); +} + +/**************************************************************************/ +/****************************** CANCELLATION ******************************/ +/**************************************************************************/ + +/// Perform any cancellation actions required by the given record. +static void performCancellationAction(TaskStatusRecord *record) { + switch (record->getKind()) { + // Deadlines don't require any special support. + case TaskStatusRecordKind::Deadline: + return; + + // Child tasks need to be recursively cancelled. + case TaskStatusRecordKind::ChildTask: { + auto childRecord = cast(record); + for (AsyncTask *child: childRecord->children()) + swift_task_cancel(child); + return; + } + + // Cancellation notifications need to be called. + case TaskStatusRecordKind::CancellationNotification: { + auto notification = + cast(record); + notification->run(); + return; + } + + // Escalation notifications can be ignored. + case TaskStatusRecordKind::EscalationNotification: + return; + + // Record locks shouldn't be found this way, but they don't have + // anything to do anyway. + case TaskStatusRecordKind::Private_RecordLock: + return; + } + + // Other cases can fall through here and be ignored. + // FIXME: allow dynamic extension/correction? +} + +void swift::swift_task_cancel(AsyncTask *task) { + Optional recordLockRecord; + + // Acquire the status record lock. + auto oldStatus = acquireStatusRecordLock(task, recordLockRecord, + /*forCancellation*/ true); + assert(!oldStatus.isLocked()); + + // If we were already cancelled or were able to cancel without acquiring + // the lock, there's nothing else to do. + if (oldStatus.isCancelled()) + return; + + // Otherwise, we've installed the lock record and are now the + // locking thread. + + // Carry out the cancellation operations associated with all + // the active records. + for (auto cur: oldStatus.records()) { + performCancellationAction(cur); + } + + // Release the status record lock, being sure to flag that + // the task is now cancelled. + ActiveTaskStatus cancelledStatus(oldStatus.getInnermostRecord(), + /*cancelled*/ true, + /*locked*/ false); + releaseStatusRecordLock(task, cancelledStatus, recordLockRecord); +} + +/**************************************************************************/ +/******************************* ESCALATION *******************************/ +/**************************************************************************/ + +/// Perform any escalation actions required by the given record. +static void performEscalationAction(TaskStatusRecord *record, + JobPriority newPriority) { + switch (record->getKind()) { + // Deadlines don't require any special support. + case TaskStatusRecordKind::Deadline: + return; + + // Child tasks need to be recursively escalated. + case TaskStatusRecordKind::ChildTask: { + auto childRecord = cast(record); + for (AsyncTask *child: childRecord->children()) + swift_task_escalate(child, newPriority); + return; + } + + // Cancellation notifications can be ignore. + case TaskStatusRecordKind::CancellationNotification: + return; + + // Escalation notifications need to be called. + case TaskStatusRecordKind::EscalationNotification: { + auto notification = + cast(record); + notification->run(newPriority); + return; + } + + // Record locks shouldn't be found this way, but they don't have + // anything to do anyway. + case TaskStatusRecordKind::Private_RecordLock: + return; + } + + // Other cases can fall through here and be ignored. + // FIXME: allow dynamic extension/correction? +} + +JobPriority +swift::swift_task_escalate(AsyncTask *task, JobPriority newPriority) { + Optional recordLockRecord; + + // Fast path: check that the task's priority is not already at laest + // as high as the target. The task's priority can only be modified + // under the status record lock; it's possible that the priority could + // be getting simultaneously escalated, but it's okay for us to return + // before that's complete. + if (task->Flags.getPriority() >= newPriority) + return task->Flags.getPriority(); + + // Acquire the status record lock. + auto oldStatus = acquireStatusRecordLock(task, recordLockRecord, + /*forCancellation*/ false); + assert(!oldStatus.isLocked()); + + // Now that we have the task's status lock, check again that the + // priority is still too low. + auto priorityToReturn = task->Flags.getPriority(); + if (priorityToReturn < newPriority) { + // Change the priority. + task->Flags.setPriority(newPriority); + priorityToReturn = newPriority; + + // TODO: attempt to escalate the thread running the task, if it's + // currently running. This probably requires the task to be enqueued + // on a standard executor. + + // Perform escalation operations for all the status records. + for (auto cur: oldStatus.records()) { + performEscalationAction(cur, newPriority); + } + } + + // Release the status record lock, restoring the old status. + releaseStatusRecordLock(task, oldStatus, recordLockRecord); + + return priorityToReturn; +} + +/**************************************************************************/ +/******************************** DEADLINE ********************************/ +/**************************************************************************/ + +NearestTaskDeadline swift::swift_task_getNearestDeadline(AsyncTask *task) { + // We don't have to worry about the deadline records being + // concurrently modified, so we can just walk the record chain, + // ignoring the possibility of a concurrent cancelling task. + + // Load the current state. + auto oldStatus = task->Status.load(std::memory_order_relaxed); + + NearestTaskDeadline result; + + // If it's already cancelled, we're done. + if (oldStatus.isCancelled()) { + result.ValueKind = NearestTaskDeadline::AlreadyCancelled; + return result; + } + + // If it's locked, wait for the lock; we can't safely step through + // the RecordLockStatusRecord on a different thread. + if (oldStatus.isLocked()) { + waitForStatusRecordUnlock(task, oldStatus); + assert(!oldStatus.isLocked()); + } + + // Walk all the records looking for deadlines. + result.ValueKind = NearestTaskDeadline::None; + for (const auto *record: oldStatus.records()) { + auto deadlineRecord = dyn_cast(record); + if (!deadlineRecord) continue; + auto recordDeadline = deadlineRecord->getDeadline(); + + // If we already have a deadline, pick the earlier. + if (result.ValueKind == NearestTaskDeadline::Active) { + if (recordDeadline < result.Value) + result.Value = recordDeadline; + } else { + result.Value = recordDeadline; + result.ValueKind = NearestTaskDeadline::Active; + } + } + return result; +} diff --git a/stdlib/public/Darwin/Foundation/URL.swift b/stdlib/public/Darwin/Foundation/URL.swift index cef7f3a4d4f42..76b264398e055 100644 --- a/stdlib/public/Darwin/Foundation/URL.swift +++ b/stdlib/public/Darwin/Foundation/URL.swift @@ -827,7 +827,8 @@ public struct URL : ReferenceConvertible, Equatable { } else { // Now we need to do something more expensive if var c = URLComponents(url: self, resolvingAgainstBaseURL: true) { - c.path = (c.path as NSString).appendingPathComponent(pathComponent) + let path = (c.path as NSString).appendingPathComponent(pathComponent) + c.path = isDirectory ? path + "/" : path if let result = c.url { return result diff --git a/stdlib/public/Differentiation/CMakeLists.txt b/stdlib/public/Differentiation/CMakeLists.txt index 0b44efa504a56..6e843661e0a44 100644 --- a/stdlib/public/Differentiation/CMakeLists.txt +++ b/stdlib/public/Differentiation/CMakeLists.txt @@ -32,11 +32,10 @@ add_swift_target_library(swift_Differentiation ${SWIFT_STDLIB_LIBRARY_BUILD_TYPE SWIFT_MODULE_DEPENDS_OPENBSD Glibc SWIFT_MODULE_DEPENDS_CYGWIN Glibc SWIFT_MODULE_DEPENDS_HAIKU Glibc - SWIFT_MODULE_DEPENDS_WINDOWS MSVCRT + SWIFT_MODULE_DEPENDS_WINDOWS CRT SWIFT_COMPILE_FLAGS ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} -parse-stdlib LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" - DARWIN_INSTALL_NAME_DIR "@rpath" INSTALL_IN_COMPONENT stdlib) diff --git a/stdlib/public/Differentiation/TgmathDerivatives.swift.gyb b/stdlib/public/Differentiation/TgmathDerivatives.swift.gyb index f7f7df00c23b6..1b3bcd01bc01b 100644 --- a/stdlib/public/Differentiation/TgmathDerivatives.swift.gyb +++ b/stdlib/public/Differentiation/TgmathDerivatives.swift.gyb @@ -26,7 +26,7 @@ import Swift #elseif os(Linux) || os(FreeBSD) || os(OpenBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) import Glibc #elseif os(Windows) - import MSVCRT + import CRT #else #error("Unsupported platform") #endif diff --git a/stdlib/public/Platform/CMakeLists.txt b/stdlib/public/Platform/CMakeLists.txt index 540e750c7e6ab..2d197f077ea1a 100644 --- a/stdlib/public/Platform/CMakeLists.txt +++ b/stdlib/public/Platform/CMakeLists.txt @@ -30,7 +30,7 @@ add_swift_target_library(swiftDarwin ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_SDK_ ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} -Xfrontend -disable-objc-attr-requires-foundation-module LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" - TARGET_SDKS ALL_APPLE_PLATFORMS + TARGET_SDKS ALL_APPLE_PLATFORMS FREESTANDING INSTALL_IN_COMPONENT sdk-overlay DEPENDS ${darwin_depends}) @@ -51,8 +51,8 @@ add_swift_target_library(swiftGlibc ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_SDK_O INSTALL_IN_COMPONENT sdk-overlay DEPENDS glibc_modulemap) -add_swift_target_library(swiftMSVCRT ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_SDK_OVERLAY - msvcrt.swift +add_swift_target_library(swiftCRT ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_SDK_OVERLAY + ucrt.swift ${swift_platform_sources} POSIXError.swift diff --git a/stdlib/public/Platform/MachError.swift b/stdlib/public/Platform/MachError.swift index 5d506dcefcbc5..d79fb7382a456 100644 --- a/stdlib/public/Platform/MachError.swift +++ b/stdlib/public/Platform/MachError.swift @@ -10,7 +10,7 @@ // //===----------------------------------------------------------------------===// -#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) +#if (os(macOS) || os(iOS) || os(tvOS) || os(watchOS)) && _runtime(_ObjC) /// Enumeration describing Mach error codes. @objc public enum MachErrorCode : Int32 { @@ -202,4 +202,4 @@ public enum MachErrorCode : Int32 { /// The requested property cannot be changed at this time. case policyStatic = 51 } -#endif // os(macOS) || os(iOS) || os(tvOS) || os(watchOS) +#endif // (os(macOS) || os(iOS) || os(tvOS) || os(watchOS)) && _runtime(_ObjC) diff --git a/stdlib/public/Platform/POSIXError.swift b/stdlib/public/Platform/POSIXError.swift index 5e644ae7a7237..fe981cc55d557 100644 --- a/stdlib/public/Platform/POSIXError.swift +++ b/stdlib/public/Platform/POSIXError.swift @@ -10,7 +10,7 @@ // //===----------------------------------------------------------------------===// -#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) +#if (os(macOS) || os(iOS) || os(tvOS) || os(watchOS)) && _runtime(_ObjC) /// Enumeration describing POSIX error codes. @objc diff --git a/stdlib/public/Platform/Platform.swift b/stdlib/public/Platform/Platform.swift index 5243b516379f9..eebde99006153 100644 --- a/stdlib/public/Platform/Platform.swift +++ b/stdlib/public/Platform/Platform.swift @@ -13,10 +13,6 @@ import SwiftShims import SwiftOverlayShims -#if os(Windows) -import ucrt -#endif - #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) //===----------------------------------------------------------------------===// // MacTypes.h diff --git a/stdlib/public/Platform/msvcrt.swift b/stdlib/public/Platform/ucrt.swift similarity index 99% rename from stdlib/public/Platform/msvcrt.swift rename to stdlib/public/Platform/ucrt.swift index 5030fc35061b7..80181db44d5cf 100644 --- a/stdlib/public/Platform/msvcrt.swift +++ b/stdlib/public/Platform/ucrt.swift @@ -11,7 +11,6 @@ //===----------------------------------------------------------------------===// @_exported import ucrt // Clang module -@_exported import visualc // Clang module @available(swift, deprecated: 3.0, message: "Please use 'Double.pi' or '.pi' to get the value of correct type and avoid casting.") public let M_PI = Double.pi diff --git a/stdlib/public/Platform/winsdk.modulemap b/stdlib/public/Platform/winsdk.modulemap index 8d7d464d27ffd..b30c27f89917a 100644 --- a/stdlib/public/Platform/winsdk.modulemap +++ b/stdlib/public/Platform/winsdk.modulemap @@ -102,6 +102,8 @@ module WinSDK [system] { module path { header "PathCch.h" export * + + link "pathcch.lib" } // api-ms-win-core-processthreads-l1-1-2.dll @@ -127,13 +129,14 @@ module WinSDK [system] { header "timezoneapi.h" export * } - } - module AuthZ { - header "AuthZ.h" - export * + // api-ms-win-core-version-l1-1-0.dll + module version { + header "winver.h" + export * - link "AuthZ.Lib" + link "Version.Lib" + } } module Controls { @@ -164,13 +167,6 @@ module WinSDK [system] { link "NetAPI32.Lib" } - module DbgHelp { - header "DbgHelp.h" - export * - - link "DbgHelp.Lib" - } - module DWM { header "dwmapi.h" export * @@ -193,6 +189,7 @@ module WinSDK [system] { module IMM { header "immdev.h" + header "imm.h" export * link "Imm32.lib" @@ -223,6 +220,36 @@ module WinSDK [system] { link "WinMM.Lib" } + module Networking { + header "winnetwk.h" + export * + + link "Mpr.Lib" + } + + module Security { + module AuthZ { + header "AuthZ.h" + export * + + link "AuthZ.Lib" + } + + module SmartCard { + header "winscard.h" + export * + + link "winscard.lib" + } + + module WinCrypt { + header "wincrypt.h" + export * + + link "Crypt32.Lib" + } + } + module Shell { header "ShlObj.h" export * @@ -244,6 +271,25 @@ module WinSDK [system] { link "shcore.lib" } + module System { + module DbgHelp { + header "DbgHelp.h" + export * + + link "DbgHelp.Lib" + } + + module IOCTL { + header "winioctl.h" + export * + } + + module MCX { + header "mcx.h" + export * + } + } + module OLE32 { header "oaidl.h" export * @@ -251,6 +297,25 @@ module WinSDK [system] { link "OleAut32.Lib" } + module Performance { + module PerfLib { + header "perflib.h" + export * + + link "AdvAPI32.Lib" + } + + module PDH { + header "Pdh.h" + export * + + link "Pdh.Lib" + } + + header "winperf.h" + export * + } + module Printing { header "winspool.h" export * @@ -273,13 +338,6 @@ module WinSDK [system] { export * } - module WinCrypt { - header "wincrypt.h" - export * - - link "Crypt32.Lib" - } - module WinDNS { header "WinDNS.h" export * @@ -294,6 +352,11 @@ module WinSDK [system] { link "Gdi32.Lib" } + module WinNT { + header "winnt.h" + export * + } + module WinReg { header "winreg.h" export * @@ -315,5 +378,15 @@ module WinSDK [system] { link "AdvAPI32.Lib" } + + // TODO(compnerd) does it make sense to implicitly export this API surface? + // It seems to be meant for device drivers. + module WLANAPI { + header "wlanapi.h" + header "wlanihv.h" + header "wlclient.h" + + link "wlanapi.lib" + } } diff --git a/stdlib/public/Reflection/TypeLowering.cpp b/stdlib/public/Reflection/TypeLowering.cpp index 729765edd1893..09bc0b6b258b4 100644 --- a/stdlib/public/Reflection/TypeLowering.cpp +++ b/stdlib/public/Reflection/TypeLowering.cpp @@ -23,7 +23,7 @@ #include "swift/Reflection/TypeLowering.h" #include "swift/Reflection/TypeRef.h" #include "swift/Reflection/TypeRefBuilder.h" -#include "swift/Runtime/Unreachable.h" +#include "swift/Basic/Unreachable.h" #ifdef DEBUG_TYPE_LOWERING #define DEBUG_LOG(expr) expr; @@ -212,7 +212,7 @@ class PrintTypeInfo { } } - swift_runtime_unreachable("Bad TypeInfo kind"); + swift_unreachable("Bad TypeInfo kind"); } }; @@ -2007,7 +2007,7 @@ class LowerType return nullptr; } - swift_runtime_unreachable("Unhandled FieldDescriptorKind in switch."); + swift_unreachable("Unhandled FieldDescriptorKind in switch."); } const TypeInfo *visitNominalTypeRef(const NominalTypeRef *N) { @@ -2039,7 +2039,7 @@ class LowerType return TC.getTypeInfo(TC.getThinFunctionTypeRef(), ExternalTypeInfo); } - swift_runtime_unreachable("Unhandled FunctionMetadataConvention in switch."); + swift_unreachable("Unhandled FunctionMetadataConvention in switch."); } const TypeInfo * @@ -2060,7 +2060,7 @@ class LowerType return TC.getTypeInfo(TC.getAnyMetatypeTypeRef(), ExternalTypeInfo); } - swift_runtime_unreachable("Unhandled MetatypeRepresentation in switch."); + swift_unreachable("Unhandled MetatypeRepresentation in switch."); } const TypeInfo * @@ -2256,7 +2256,7 @@ const TypeInfo *TypeConverter::getClassInstanceTypeInfo( return nullptr; } - swift_runtime_unreachable("Unhandled FieldDescriptorKind in switch."); + swift_unreachable("Unhandled FieldDescriptorKind in switch."); } } // namespace reflection diff --git a/stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp b/stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp index 9d5afad1cf41f..0af1e53b3f9d7 100644 --- a/stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp +++ b/stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp @@ -23,7 +23,7 @@ unsigned long long swift_reflection_classIsSwiftMask = 2; #include "swift/Reflection/ReflectionContext.h" #include "swift/Reflection/TypeLowering.h" #include "swift/Remote/CMemoryReader.h" -#include "swift/Runtime/Unreachable.h" +#include "swift/Basic/Unreachable.h" #if defined(__APPLE__) && defined(__MACH__) #include @@ -406,7 +406,7 @@ swift_layout_kind_t getTypeInfoKind(const TypeInfo &TI) { } } - swift_runtime_unreachable("Unhandled TypeInfoKind in switch"); + swift_unreachable("Unhandled TypeInfoKind in switch"); } static swift_typeinfo_t convertTypeInfo(const TypeInfo *TI) { diff --git a/stdlib/public/SwiftShims/CMakeLists.txt b/stdlib/public/SwiftShims/CMakeLists.txt index 4c2b3c4e59588..33c49a3f8861e 100644 --- a/stdlib/public/SwiftShims/CMakeLists.txt +++ b/stdlib/public/SwiftShims/CMakeLists.txt @@ -223,8 +223,8 @@ endif() # need to use a different version of the headers than the installed Clang. This # should be used in conjunction with clang-resource-dir-symlink. file(TO_CMAKE_PATH "${LLVM_LIBRARY_OUTPUT_INTDIR}" - _SWIFT_SHIMS_PATH_TO_CLANG_BUILD) -swift_install_in_component(DIRECTORY "${_SWIFT_SHIMS_PATH_TO_CLANG_BUILD}/lib/clang" + _SWIFT_SHIMS_PATH_TO_CLANG_LIB_BUILD) +swift_install_in_component(DIRECTORY "${_SWIFT_SHIMS_PATH_TO_CLANG_LIB_BUILD}/clang" DESTINATION "lib" COMPONENT clang-builtin-headers-in-clang-resource-dir PATTERN "*.h") diff --git a/stdlib/public/SwiftShims/DispatchOverlayShims.h b/stdlib/public/SwiftShims/DispatchOverlayShims.h index e9eeebfeb75f6..d49e9ad38263c 100644 --- a/stdlib/public/SwiftShims/DispatchOverlayShims.h +++ b/stdlib/public/SwiftShims/DispatchOverlayShims.h @@ -215,8 +215,8 @@ static inline unsigned int _swift_dispatch_data_apply( __swift_shims_dispatch_data_t data, __swift_shims_dispatch_data_applier SWIFT_DISPATCH_NOESCAPE applier) { - return dispatch_data_apply(data, ^bool(dispatch_data_t data, size_t off, const void *loc, size_t size){ - return applier(data, off, loc, size); + return dispatch_data_apply((dispatch_data_t)data, ^bool(dispatch_data_t data, size_t off, const void *loc, size_t size){ + return applier((__swift_shims_dispatch_data_t)data, off, loc, size); }); } diff --git a/stdlib/public/SwiftShims/RuntimeShims.h b/stdlib/public/SwiftShims/RuntimeShims.h index 69fd8ee83036c..b5ffb04c81958 100644 --- a/stdlib/public/SwiftShims/RuntimeShims.h +++ b/stdlib/public/SwiftShims/RuntimeShims.h @@ -31,22 +31,19 @@ SWIFT_RUNTIME_STDLIB_API void *_swift_objCMirrorSummary(const void * nsObject); /// Call strtold_l with the C locale, swapping argument and return -/// types so we can operate on Float80. Return NULL on overflow. +/// types so we can operate on Float80. SWIFT_RUNTIME_STDLIB_API const char *_swift_stdlib_strtold_clocale(const char *nptr, void *outResult); /// Call strtod_l with the C locale, swapping argument and return -/// types so we can operate consistently on Float80. Return NULL on -/// overflow. +/// types so we can operate consistently on Float80. SWIFT_RUNTIME_STDLIB_API const char *_swift_stdlib_strtod_clocale(const char *nptr, double *outResult); /// Call strtof_l with the C locale, swapping argument and return -/// types so we can operate consistently on Float80. Return NULL on -/// overflow. +/// types so we can operate consistently on Float80. SWIFT_RUNTIME_STDLIB_API const char *_swift_stdlib_strtof_clocale(const char *nptr, float *outResult); /// Call strtof_l with the C locale, swapping argument and return -/// types so we can operate consistently on Float80. Return NULL on -/// overflow. +/// types so we can operate consistently on Float80. SWIFT_RUNTIME_STDLIB_API const char *_swift_stdlib_strtof16_clocale(const char *nptr, __fp16 *outResult); diff --git a/stdlib/public/SwiftShims/Visibility.h b/stdlib/public/SwiftShims/Visibility.h index 708e595975502..4ddd34a12eaf7 100644 --- a/stdlib/public/SwiftShims/Visibility.h +++ b/stdlib/public/SwiftShims/Visibility.h @@ -52,6 +52,12 @@ # define SWIFT_END_NULLABILITY_ANNOTATIONS #endif +#define SWIFT_MACRO_CONCAT(A, B) A ## B +#define SWIFT_MACRO_IF_0(IF_TRUE, IF_FALSE) IF_FALSE +#define SWIFT_MACRO_IF_1(IF_TRUE, IF_FALSE) IF_TRUE +#define SWIFT_MACRO_IF(COND, IF_TRUE, IF_FALSE) \ + SWIFT_MACRO_CONCAT(SWIFT_MACRO_IF_, COND)(IF_TRUE, IF_FALSE) + #if __has_attribute(pure) #define SWIFT_READONLY __attribute__((__pure__)) #else @@ -94,48 +100,102 @@ #define SWIFT_ATTRIBUTE_UNAVAILABLE #endif -// TODO: support using shims headers in overlays by parameterizing -// SWIFT_RUNTIME_EXPORT on the library it's exported from. - -/// Attribute used to export symbols from the runtime. +// Define the appropriate attributes for sharing symbols across +// image (executable / shared-library) boundaries. +// +// SWIFT_ATTRIBUTE_FOR_EXPORTS will be placed on declarations that +// are known to be exported from the current image. Typically, they +// are placed on header declarations and then inherited by the actual +// definitions. +// +// SWIFT_ATTRIBUTE_FOR_IMPORTS will be placed on declarations that +// are known to be exported from a different image. This never +// includes a definition. +// +// Getting the right attribute on a declaratioon can be pretty awkward, +// but it's necessary under the C translation model. All of this +// ceremony is familiar to Windows programmers; C/C++ programmers +// everywhere else usually don't bother, but since we have to get it +// right for Windows, we have everything set up to get it right on +// other targets as well, and doing so lets the compiler use more +// efficient symbol access patterns. #if defined(__MACH__) || defined(__wasi__) -# define SWIFT_EXPORT_ATTRIBUTE __attribute__((__visibility__("default"))) +// On Mach-O and WebAssembly, we use non-hidden visibility. We just use +// default visibility on both imports and exports, both because these +// targets don't support protected visibility but because they don't +// need it: symbols are not interposable outside the current image +// by default. +# define SWIFT_ATTRIBUTE_FOR_EXPORTS __attribute__((__visibility__("default"))) +# define SWIFT_ATTRIBUTE_FOR_IMPORTS __attribute__((__visibility__("default"))) #elif defined(__ELF__) -// We make assumptions that the runtime and standard library can refer to each -// other's symbols as DSO-local, which means we can't allow the dynamic linker -// to relocate these symbols. We must give them protected visibility while -// building the standard library and runtime. -# if defined(swiftCore_EXPORTS) -# define SWIFT_EXPORT_ATTRIBUTE __attribute__((__visibility__("protected"))) -# else -# define SWIFT_EXPORT_ATTRIBUTE __attribute__((__visibility__("default"))) -# endif +// On ELF, we use non-hidden visibility. For exports, we must use +// protected visibility to tell the compiler and linker that the symbols +// can't be interposed outside the current image. For imports, we must +// use default visibility because protected visibility guarantees that +// the symbol is defined in the current library, which isn't true for +// an import. +// +// The compiler does assume that the runtime and standard library can +// refer to each other's symbols as DSO-local, so it's important that +// we get this right or we can get linker errors. +# define SWIFT_ATTRIBUTE_FOR_EXPORTS __attribute__((__visibility__("protected"))) +# define SWIFT_ATTRIBUTE_FOR_IMPORTS __attribute__((__visibility__("default"))) + +#elif defined(__CYGWIN__) + +// For now, we ignore all this on Cygwin. +# define SWIFT_ATTRIBUTE_FOR_EXPORTS +# define SWIFT_ATTRIBUTE_FOR_IMPORTS // FIXME: this #else should be some sort of #elif Windows #else // !__MACH__ && !__ELF__ -# if defined(__CYGWIN__) -# define SWIFT_EXPORT_ATTRIBUTE -# else - -# if defined(swiftCore_EXPORTS) -# define SWIFT_EXPORT_ATTRIBUTE __declspec(dllexport) -# else -# define SWIFT_EXPORT_ATTRIBUTE __declspec(dllimport) -# endif +// On PE/COFF, we use dllimport and dllexport. +# define SWIFT_ATTRIBUTE_FOR_EXPORTS __declspec(dllexport) +# define SWIFT_ATTRIBUTE_FOR_IMPORTS __declspec(dllimport) -# endif +#endif +// CMake conventionally passes -DlibraryName_EXPORTS when building +// code that goes into libraryName. This isn't the best macro name, +// but it's conventional. We do have to pass it explicitly in a few +// places in the build system for a variety of reasons. +// +// Unfortunately, defined(D) is a special function you can use in +// preprocessor conditions, not a macro you can use anywhere, so we +// need to manually check for all the libraries we know about so that +// we can use them in our condition below.s +#if defined(swiftCore_EXPORTS) +#define SWIFT_IMAGE_EXPORTS_swiftCore 1 +#else +#define SWIFT_IMAGE_EXPORTS_swiftCore 0 +#endif +#if defined(swift_Concurrency_EXPORTS) +#define SWIFT_IMAGE_EXPORTS_swift_Concurrency 1 +#else +#define SWIFT_IMAGE_EXPORTS_swift_Concurrency 0 #endif +#define SWIFT_EXPORT_FROM_ATTRIBUTE(LIBRARY) \ + SWIFT_MACRO_IF(SWIFT_IMAGE_EXPORTS_##LIBRARY, \ + SWIFT_ATTRIBUTE_FOR_EXPORTS, \ + SWIFT_ATTRIBUTE_FOR_IMPORTS) + +// SWIFT_EXPORT_FROM(LIBRARY) declares something to be a C-linkage +// entity exported by the given library. +// +// SWIFT_RUNTIME_EXPORT is just SWIFT_EXPORT_FROM(swiftCore). +// +// TODO: use this in shims headers in overlays. #if defined(__cplusplus) -#define SWIFT_RUNTIME_EXPORT extern "C" SWIFT_EXPORT_ATTRIBUTE +#define SWIFT_EXPORT_FROM(LIBRARY) extern "C" SWIFT_EXPORT_FROM_ATTRIBUTE(LIBRARY) #else -#define SWIFT_RUNTIME_EXPORT SWIFT_EXPORT_ATTRIBUTE +#define SWIFT_EXPORT_FROM(LIBRARY) SWIFT_EXPORT_FROM_ATTRIBUTE(LIBRARY) #endif +#define SWIFT_RUNTIME_EXPORT SWIFT_EXPORT_FROM(swiftCore) #if __cplusplus > 201402l && __has_cpp_attribute(fallthrough) #define SWIFT_FALLTHROUGH [[fallthrough]] diff --git a/stdlib/public/core/Array.swift b/stdlib/public/core/Array.swift index fe5f0da2198d7..237ea44db8e0a 100644 --- a/stdlib/public/core/Array.swift +++ b/stdlib/public/core/Array.swift @@ -1229,7 +1229,7 @@ extension Array: RangeReplaceableCollection { // elements. It reduces code size, because the following code // can be removed by the optimizer by constant folding this check in a // generic specialization. - if newElements is [Element] { + if S.self == [Element].self { _internalInvariant(remainder.next() == nil) return } @@ -1788,7 +1788,7 @@ extension Array { /// and enums. /// /// The following example copies bytes from the `byteValues` array into - /// `numbers`, an array of `Int`: + /// `numbers`, an array of `Int32`: /// /// var numbers: [Int32] = [0, 0] /// var byteValues: [UInt8] = [0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00] @@ -1800,6 +1800,8 @@ extension Array { /// } /// // numbers == [1, 2] /// + /// - Note: This example shows the behavior on a little-endian platform. + /// /// The pointer passed as an argument to `body` is valid only for the /// lifetime of the closure. Do not escape it from the closure for later /// use. @@ -1837,12 +1839,14 @@ extension Array { /// The following example copies the bytes of the `numbers` array into a /// buffer of `UInt8`: /// - /// var numbers = [1, 2, 3] + /// var numbers: [Int32] = [1, 2, 3] /// var byteBuffer: [UInt8] = [] /// numbers.withUnsafeBytes { /// byteBuffer.append(contentsOf: $0) /// } - /// // byteBuffer == [1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, ...] + /// // byteBuffer == [1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0] + /// + /// - Note: This example shows the behavior on a little-endian platform. /// /// - Parameter body: A closure with an `UnsafeRawBufferPointer` parameter /// that points to the contiguous storage for the array. diff --git a/stdlib/public/core/ArrayShared.swift b/stdlib/public/core/ArrayShared.swift index 28c1d25696c55..05adaf5c6feb9 100644 --- a/stdlib/public/core/ArrayShared.swift +++ b/stdlib/public/core/ArrayShared.swift @@ -64,6 +64,7 @@ func _deallocateUninitializedArray( array._deallocateUninitialized() } +#if !INTERNAL_CHECKS_ENABLED @_alwaysEmitIntoClient @_semantics("array.finalize_intrinsic") @_effects(readnone) @@ -75,6 +76,20 @@ func _finalizeUninitializedArray( mutableArray._endMutation() return mutableArray } +#else +// When asserts are enabled, _endCOWMutation writes to _native.isImmutable +// So we cannot have @_effects(readnone) +@_alwaysEmitIntoClient +@_semantics("array.finalize_intrinsic") +public // COMPILER_INTRINSIC +func _finalizeUninitializedArray( + _ array: __owned Array +) -> Array { + var mutableArray = array + mutableArray._endMutation() + return mutableArray +} +#endif extension Collection { // Utility method for collections that wish to implement diff --git a/stdlib/public/core/ArraySlice.swift b/stdlib/public/core/ArraySlice.swift index 8ce49b2f84dce..eeda582f57e84 100644 --- a/stdlib/public/core/ArraySlice.swift +++ b/stdlib/public/core/ArraySlice.swift @@ -1429,7 +1429,7 @@ extension ArraySlice { /// and enums. /// /// The following example copies bytes from the `byteValues` array into - /// `numbers`, an array of `Int`: + /// `numbers`, an array of `Int32`: /// /// var numbers: [Int32] = [0, 0] /// var byteValues: [UInt8] = [0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00] @@ -1441,6 +1441,8 @@ extension ArraySlice { /// } /// // numbers == [1, 2] /// + /// - Note: This example shows the behavior on a little-endian platform. + /// /// The pointer passed as an argument to `body` is valid only for the /// lifetime of the closure. Do not escape it from the closure for later /// use. @@ -1478,12 +1480,14 @@ extension ArraySlice { /// The following example copies the bytes of the `numbers` array into a /// buffer of `UInt8`: /// - /// var numbers = [1, 2, 3] + /// var numbers: [Int32] = [1, 2, 3] /// var byteBuffer: [UInt8] = [] /// numbers.withUnsafeBytes { /// byteBuffer.append(contentsOf: $0) /// } - /// // byteBuffer == [1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, ...] + /// // byteBuffer == [1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0] + /// + /// - Note: This example shows the behavior on a little-endian platform. /// /// - Parameter body: A closure with an `UnsafeRawBufferPointer` parameter /// that points to the contiguous storage for the array. diff --git a/stdlib/public/core/CMakeLists.txt b/stdlib/public/core/CMakeLists.txt index 7733351b9c287..39dde3e3d413a 100644 --- a/stdlib/public/core/CMakeLists.txt +++ b/stdlib/public/core/CMakeLists.txt @@ -304,7 +304,7 @@ elseif(SWIFT_PRIMARY_VARIANT_SDK STREQUAL FREEBSD) ${SWIFTLIB_DIR}/clang/lib/freebsd/libclang_rt.builtins-${SWIFT_PRIMARY_VARIANT_ARCH}.a) elseif(SWIFT_PRIMARY_VARIANT_SDK STREQUAL LINUX) if(SWIFT_BUILD_STATIC_STDLIB) - list(APPEND swift_core_private_link_libraries swiftImageInspectionShared) + list(APPEND swift_core_private_link_libraries) endif() elseif(SWIFT_PRIMARY_VARIANT_SDK STREQUAL WINDOWS) list(APPEND swift_core_private_link_libraries shell32;DbgHelp) @@ -314,10 +314,6 @@ option(SWIFT_CHECK_ESSENTIAL_STDLIB "Check core standard library layering by linking its essential subset" FALSE) -if(SWIFT_STDLIB_SIL_DEBUGGING) - list(APPEND swift_stdlib_compile_flags "-Xfrontend" "-gsil") -endif() - if(NOT "${CMAKE_BUILD_TYPE}" STREQUAL "MinSizeRel") list(APPEND swift_stdlib_compile_flags "-Xllvm" "-sil-inline-generics") list(APPEND swift_stdlib_compile_flags "-Xllvm" "-sil-partial-specialization") diff --git a/stdlib/public/core/ContiguousArray.swift b/stdlib/public/core/ContiguousArray.swift index b854c18942745..ce3612fe52386 100644 --- a/stdlib/public/core/ContiguousArray.swift +++ b/stdlib/public/core/ContiguousArray.swift @@ -1361,7 +1361,7 @@ extension ContiguousArray { /// and enums. /// /// The following example copies bytes from the `byteValues` array into - /// `numbers`, an array of `Int`: + /// `numbers`, an array of `Int32`: /// /// var numbers: [Int32] = [0, 0] /// var byteValues: [UInt8] = [0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00] @@ -1373,6 +1373,8 @@ extension ContiguousArray { /// } /// // numbers == [1, 2] /// + /// - Note: This example shows the behavior on a little-endian platform. + /// /// The pointer passed as an argument to `body` is valid only for the /// lifetime of the closure. Do not escape it from the closure for later /// use. @@ -1410,12 +1412,14 @@ extension ContiguousArray { /// The following example copies the bytes of the `numbers` array into a /// buffer of `UInt8`: /// - /// var numbers = [1, 2, 3] + /// var numbers: [Int32] = [1, 2, 3] /// var byteBuffer: [UInt8] = [] /// numbers.withUnsafeBytes { /// byteBuffer.append(contentsOf: $0) /// } - /// // byteBuffer == [1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, ...] + /// // byteBuffer == [1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0] + /// + /// - Note: This example shows the behavior on a little-endian platform. /// /// - Parameter body: A closure with an `UnsafeRawBufferPointer` parameter /// that points to the contiguous storage for the array. diff --git a/stdlib/public/core/FloatingPoint.swift b/stdlib/public/core/FloatingPoint.swift index ecf24819ee21b..d4de80b8e5c5b 100644 --- a/stdlib/public/core/FloatingPoint.swift +++ b/stdlib/public/core/FloatingPoint.swift @@ -1950,9 +1950,30 @@ extension BinaryFloatingPoint { /// - Parameter value: A floating-point value to be converted. @inlinable public init?(exactly value: Source) { - let (value_, exact) = Self._convert(from: value) - guard exact else { return nil } - self = value_ + // We define exactness by equality after roundtripping; since NaN is never + // equal to itself, it can never be converted exactly. + if value.isNaN { return nil } + + if (Source.exponentBitCount > Self.exponentBitCount + || Source.significandBitCount > Self.significandBitCount) + && value.isFinite && !value.isZero { + let exponent = value.exponent + if exponent < Self.leastNormalMagnitude.exponent { + if exponent < Self.leastNonzeroMagnitude.exponent { return nil } + if value.significandWidth > + Int(Self.Exponent(exponent) - Self.leastNonzeroMagnitude.exponent) { + return nil + } + } else { + if exponent > Self.greatestFiniteMagnitude.exponent { return nil } + if value.significandWidth > + Self.greatestFiniteMagnitude.significandWidth { + return nil + } + } + } + + self = Self(value) } @inlinable diff --git a/stdlib/public/core/Integers.swift b/stdlib/public/core/Integers.swift index 1b7ba775989ff..cc5341b71692c 100644 --- a/stdlib/public/core/Integers.swift +++ b/stdlib/public/core/Integers.swift @@ -3010,9 +3010,10 @@ extension FixedWidthInteger { let minBitWidth = source.significandWidth let isExact = (minBitWidth <= exponent) let bitPattern = source.significandBitPattern - // `RawSignificand.bitWidth` is not available if `RawSignificand` does not - // conform to `FixedWidthInteger`; we can compute this value as follows if - // `source` is finite: + // Determine the actual number of fractional significand bits. + // `Source.significandBitCount` would not reflect the actual number of + // fractional significand bits if `Source` is not a fixed-width floating-point + // type; we can compute this value as follows if `source` is finite: let bitWidth = minBitWidth &+ bitPattern.trailingZeroBitCount let shift = exponent - Source.Exponent(bitWidth) // Use `Self.Magnitude` to prevent sign extension if `shift < 0`. diff --git a/stdlib/public/core/ObjectIdentifier.swift b/stdlib/public/core/ObjectIdentifier.swift index e34a404ca45b7..bd085763312b9 100644 --- a/stdlib/public/core/ObjectIdentifier.swift +++ b/stdlib/public/core/ObjectIdentifier.swift @@ -12,6 +12,9 @@ /// A unique identifier for a class instance or metatype. /// +/// This unique identifier is only valid for comparisons during the lifetime +/// of the instance. +/// /// In Swift, only class instances and metatypes have unique identities. There /// is no notion of identity for structs, enums, functions, or tuples. @frozen // trivial-implementation diff --git a/stdlib/public/core/SmallString.swift b/stdlib/public/core/SmallString.swift index 744962635a020..8ef19f5765228 100644 --- a/stdlib/public/core/SmallString.swift +++ b/stdlib/public/core/SmallString.swift @@ -197,11 +197,18 @@ extension _SmallString { internal func withUTF8( _ f: (UnsafeBufferPointer) throws -> Result ) rethrows -> Result { + let count = self.count var raw = self.zeroTerminatedRawCodeUnits - return try Swift.withUnsafeBytes(of: &raw) { rawBufPtr in - let ptr = rawBufPtr.baseAddress._unsafelyUnwrappedUnchecked - .assumingMemoryBound(to: UInt8.self) - return try f(UnsafeBufferPointer(start: ptr, count: self.count)) + return try Swift.withUnsafeBytes(of: &raw) { + let rawPtr = $0.baseAddress._unsafelyUnwrappedUnchecked + // Rebind the underlying (UInt64, UInt64) tuple to UInt8 for the + // duration of the closure. Accessing self after this rebind is undefined. + let ptr = rawPtr.bindMemory(to: UInt8.self, capacity: count) + defer { + // Restore the memory type of self._storage + _ = rawPtr.bindMemory(to: RawBitPattern.self, capacity: 1) + } + return try f(UnsafeBufferPointer(start: ptr, count: count)) } } @@ -209,14 +216,11 @@ extension _SmallString { // new count. @inline(__always) internal mutating func withMutableCapacity( - _ f: (UnsafeMutableBufferPointer) throws -> Int + _ f: (UnsafeMutableRawBufferPointer) throws -> Int ) rethrows { let len = try withUnsafeMutableBytes(of: &self._storage) { (rawBufPtr: UnsafeMutableRawBufferPointer) -> Int in - let ptr = rawBufPtr.baseAddress._unsafelyUnwrappedUnchecked - .assumingMemoryBound(to: UInt8.self) - return try f(UnsafeMutableBufferPointer( - start: ptr, count: _SmallString.capacity)) + return try f(rawBufPtr) } if len == 0 { self = _SmallString() @@ -273,7 +277,17 @@ extension _SmallString { ) rethrows { self.init() try self.withMutableCapacity { - return try initializer($0) + let capacity = $0.count + let rawPtr = $0.baseAddress._unsafelyUnwrappedUnchecked + // Rebind the underlying (UInt64, UInt64) tuple to UInt8 for the + // duration of the closure. Accessing self after this rebind is undefined. + let ptr = rawPtr.bindMemory(to: UInt8.self, capacity: capacity) + defer { + // Restore the memory type of self._storage + _ = rawPtr.bindMemory(to: RawBitPattern.self, capacity: 1) + } + return try initializer( + UnsafeMutableBufferPointer(start: ptr, count: capacity)) } self._invariantCheck() } diff --git a/stdlib/public/core/StringBridge.swift b/stdlib/public/core/StringBridge.swift index a24d721d0e630..dc67d1ce47243 100644 --- a/stdlib/public/core/StringBridge.swift +++ b/stdlib/public/core/StringBridge.swift @@ -169,7 +169,7 @@ internal func _cocoaStringSubscript( @_effects(releasenone) private func _NSStringCopyUTF8( _ o: _StringSelectorHolder, - into bufPtr: UnsafeMutableBufferPointer + into bufPtr: UnsafeMutableRawBufferPointer ) -> Int? { let ptr = bufPtr.baseAddress._unsafelyUnwrappedUnchecked let len = o.length @@ -193,7 +193,7 @@ private func _NSStringCopyUTF8( @_effects(releasenone) internal func _cocoaStringCopyUTF8( _ target: _CocoaString, - into bufPtr: UnsafeMutableBufferPointer + into bufPtr: UnsafeMutableRawBufferPointer ) -> Int? { return _NSStringCopyUTF8(_objc(target), into: bufPtr) } @@ -206,7 +206,7 @@ private func _NSStringUTF8Count( var remainingRange = _SwiftNSRange(location: 0, length: 0) var usedLen = 0 let success = 0 != o.getBytes( - UnsafeMutablePointer(Builtin.inttoptr_Word(0._builtinWordValue)), + UnsafeMutableRawPointer(Builtin.inttoptr_Word(0._builtinWordValue)), maxLength: 0, usedLength: &usedLen, encoding: _cocoaUTF8Encoding, @@ -340,7 +340,7 @@ internal enum _KnownCocoaString { @_effects(releasenone) // @opaque internal func _bridgeTagged( _ cocoa: _CocoaString, - intoUTF8 bufPtr: UnsafeMutableBufferPointer + intoUTF8 bufPtr: UnsafeMutableRawBufferPointer ) -> Int? { _internalInvariant(_isObjCTaggedPointer(cocoa)) return _cocoaStringCopyUTF8(cocoa, into: bufPtr) diff --git a/stdlib/public/core/StringGuts.swift b/stdlib/public/core/StringGuts.swift index 038cd5abda1ad..77c76d39858aa 100644 --- a/stdlib/public/core/StringGuts.swift +++ b/stdlib/public/core/StringGuts.swift @@ -250,7 +250,9 @@ extension _StringGuts { ) -> Int? { #if _runtime(_ObjC) // Currently, foreign means NSString - if let res = _cocoaStringCopyUTF8(_object.cocoaObject, into: mbp) { + if let res = _cocoaStringCopyUTF8(_object.cocoaObject, + into: UnsafeMutableRawBufferPointer(start: mbp.baseAddress, + count: mbp.count)) { return res } diff --git a/stdlib/public/runtime/BuiltinProtocolConformances.cpp b/stdlib/public/runtime/BuiltinProtocolConformances.cpp new file mode 100644 index 0000000000000..7226fa84fabac --- /dev/null +++ b/stdlib/public/runtime/BuiltinProtocolConformances.cpp @@ -0,0 +1,734 @@ +//===--- BuiltinProtocolConformances.cpp ----------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Definitions of some builtin protocol witnesses. +// +//===----------------------------------------------------------------------===// + +#include "swift/Runtime/BuiltinProtocolConformances.h" +#include "swift/Runtime/Casting.h" +#include "swift/Runtime/Debug.h" +#include "swift/Runtime/Metadata.h" + +#include + +using namespace swift; + +using StaticInfixWitness = SWIFT_CC(swift) bool(OpaqueValue *, OpaqueValue *, + SWIFT_CONTEXT const Metadata *, + const Metadata *, + const WitnessTable *); + +// Elf indirect symbol references. +#if defined(__ELF__) +#define INDIRECT_RELREF_GOTPCREL(SYMBOL) SYMBOL "@GOTPCREL + 1" +#endif + +// MachO indirect symbol references. +#if defined(__MACH__) + +// 64 bit arm MachO +#if defined(__aarch64__) +#define INDIRECT_RELREF_GOTPCREL(SYMBOL) SYMBOL "@GOT - . + 1" + +// 32 bit arm MachO +#elif defined(__arm__) +// MachO doesn't support @GOT like relocations for 32 bit arm. +#define INDIRECT_RELREF_GOTPCREL(SYMBOL) "L" SYMBOL "$non_lazy_ptr - . + 1" +#endif + +// 64 bit x86_64 MachO +#if defined(__x86_64__) +// The + 4 is required for all x86_64 MachO GOTPC relocations. +#define INDIRECT_RELREF_GOTPCREL(SYMBOL) SYMBOL "@GOTPCREL + 4 + 1" + +// 32 bit x86 MachO +#elif defined(__i386__) +// MachO doesn't support @GOT like relocations for 32 bit x86. +#define INDIRECT_RELREF_GOTPCREL(SYMBOL) "L" SYMBOL "$non_lazy_ptr - . + 1" +#endif +#endif + +// Windows native indirect symbol references. +#if defined(_WIN32) +#define INDIRECT_RELREF_GOTPCREL(SYMBOL) "__imp_" SYMBOL " - . + 1" +#endif + +//===----------------------------------------------------------------------===// +// Tuple Equatable Conformance +//===----------------------------------------------------------------------===// + +// For 32 bit ARM and i386 (specifically armv7, armv7s, and armv7k), emit +// non-lazy pointer stubs to indirectly reference. Darwin doesn't support @GOT +// syntax for those archs. +#if defined(__MACH__) && \ + ((defined(__arm__) && !defined(__aarch64__)) || defined(__i386__)) +__asm( +#if defined(__arm__) + " .section __DATA, __nl_symbol_ptr, non_lazy_symbol_pointers\n" +#elif defined(__i386__) + " .section __IMPORT, __pointers, non_lazy_symbol_pointers\n" +#endif + " .p2align 2\n" + "L" EQUATABLE_DESCRIPTOR_SYMBOL "$non_lazy_ptr:\n" + " .indirect_symbol " EQUATABLE_DESCRIPTOR_SYMBOL "\n" + " .long 0\n" + "L" EQUATABLE_EE_METHOD_DESCRIPTOR "$non_lazy_ptr:\n" + " .indirect_symbol " EQUATABLE_EE_METHOD_DESCRIPTOR "\n" + " .long 0\n" +); +#endif + +// Define the conformance descriptor for tuple Equatable. We do this in +// assembly to work around relative reference issues. +__asm( + #if defined(__ELF__) + " .type __swift_tupleEquatable_private, @object\n" + " .local __swift_tupleEquatable_private\n" + " .comm __swift_tupleEquatable_private, 128, 16\n" + " .protected " TUPLE_EQUATABLE_CONF "\n" + " .type " TUPLE_EQUATABLE_CONF ", @object\n" + " .section .rodata\n" + #elif defined(__MACH__) + " .zerofill __DATA, __bss, __swift_tupleEquatable_private, 128, 4\n" + " .section __TEXT, __const\n" + #elif defined(_WIN32) + " .lcomm __swift_tupleEquatable_private, 128, 16\n" + " .section .rdata, \"dr\"\n" + #pragma comment(linker, "/EXPORT:_swift_tupleEquatable_conf,DATA") + #endif + " .globl " TUPLE_EQUATABLE_CONF "\n" + " .p2align 2\n" + TUPLE_EQUATABLE_CONF ":\n" + // This is an indirectable relative reference to the GOT entry for the + // Equatable protocol descriptor. + " .long " INDIRECT_RELREF_GOTPCREL(EQUATABLE_DESCRIPTOR_SYMBOL) "\n" + // 769 is the MetadataKind::Tuple + " .long 769\n" + // This indicates that we have no witness table pattern. We use a generic + // witness table for builtin conformances. + " .long 0\n" + // 196640 are the ConformanceFlags with the type reference bit set to + // MetadataKind, the has resilient witness bit, and the generic witness table + // bit. + " .long 196640\n" + // This 1 is the ResilientWitnessesHeader indicating we have 1 resilient + // witness. + " .long 1\n" + // This is an indirectable relative reference to the GOT entry for the + // Equatable == method descriptor. + " .long " INDIRECT_RELREF_GOTPCREL(EQUATABLE_EE_METHOD_DESCRIPTOR) "\n" + // This is a direct relative reference to the equals witness defined below. + " .long (" TUPLE_EQUATABLE_EQUALS ") - .\n" + // The witness table size in words. + " .short 0\n" + // The witness table private size in words & requires instantiation. + " .short 1\n" + // The witness table instantiator function. + " .long 0\n" + // This is a direct relative reference to the private data for the + // conformance. + " .long __swift_tupleEquatable_private - .\n" + #if defined(__ELF__) + " .size " TUPLE_EQUATABLE_CONF ", 40\n" + #endif +); + +extern "C" const ProtocolConformanceDescriptor _swift_tupleEquatable_conf; + +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +bool swift::_swift_tupleEquatable_equals(OpaqueValue *tuple1, + OpaqueValue *tuple2, + SWIFT_CONTEXT Metadata *swiftSelf, + Metadata *Self, void *witnessTable) { + auto tuple = cast(Self); + auto table = reinterpret_cast(witnessTable); + + // Loop through all elements, and check if both tuples element is equal. + for (size_t i = 0; i != tuple->NumElements; i += 1) { + auto elt = tuple->getElement(i); + + // Get the element conformance from the private data in the witness table. + auto conformance = reinterpret_cast(table[-1 - i]); + + // Get the respective values from both tuples. + auto value1 = reinterpret_cast( + reinterpret_cast(tuple1) + elt.Offset); + auto value2 = reinterpret_cast( + reinterpret_cast(tuple2) + elt.Offset); + + // Grab the specific witness for this element type. + auto equatableTable = reinterpret_cast(conformance); + auto equalsWitness = equatableTable[WitnessTableFirstRequirementOffset]; + auto equals = reinterpret_cast(equalsWitness); + + // Call the equal function + auto result = equals(value1, value2, elt.Type, elt.Type, conformance); + + // If the values aren't equal, this tuple isn't equal. :) + if (!result) + return false; + } + + // Otherwise this tuple has value equality with all elements. + return true; +} + +//===----------------------------------------------------------------------===// +// Tuple Comparable Conformance +//===----------------------------------------------------------------------===// + +// For 32 bit ARM and i386 (specifically armv7, armv7s, and armv7k), emit +// non-lazy pointer stubs to indirectly reference. Darwin doesn't support @GOT +// syntax for those archs. +#if defined(__MACH__) && \ + ((defined(__arm__) && !defined(__aarch64__)) || defined(__i386__)) +__asm( +#if defined(__arm__) + " .section __DATA, __nl_symbol_ptr, non_lazy_symbol_pointers\n" +#elif defined(__i386__) + " .section __IMPORT, __pointers, non_lazy_symbol_pointers\n" +#endif + " .p2align 2\n" + "L" COMPARABLE_DESCRIPTOR_SYMBOL "$non_lazy_ptr:\n" + " .indirect_symbol " COMPARABLE_DESCRIPTOR_SYMBOL "\n" + " .long 0\n" + "L" COMPARABLE_BASE_CONFORMANCE_DESCRIPTOR "$non_lazy_ptr:\n" + " .indirect_symbol " COMPARABLE_BASE_CONFORMANCE_DESCRIPTOR "\n" + " .long 0\n" + "L" COMPARABLE_LT_METHOD_DESCRIPTOR "$non_lazy_ptr:\n" + " .indirect_symbol " COMPARABLE_LT_METHOD_DESCRIPTOR "\n" + " .long 0\n" + "L" COMPARBALE_LTE_METHOD_DESCRIPTOR "$non_lazy_ptr:\n" + " .indirect_symbol " COMPARBALE_LTE_METHOD_DESCRIPTOR "\n" + " .long 0\n" + "L" COMPARABLE_GTE_METHOD_DESCRIPTOR "$non_lazy_ptr:\n" + " .indirect_symbol " COMPARABLE_GTE_METHOD_DESCRIPTOR "\n" + " .long 0\n" + "L" COMPARABLE_GT_METHOD_DESCRIPTOR "$non_lazy_ptr:\n" + " .indirect_symbol " COMPARABLE_GT_METHOD_DESCRIPTOR "\n" + " .long 0\n" +); +#endif + +// Define the associated conformance structure for tuple Comparable. We do this +// in assembly to work around relative reference issues. +__asm( + #if defined(__ELF__) + " .hidden \"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\"\n" + " .type \"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\", @object\n" + " .section swift5_typeref, \"a\"\n" + " .weak \"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\"\n" + #elif defined(__MACH__) + " .private_extern \"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\"\n" + " .section __TEXT, __swift5_typeref\n" + " .globl \"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\"\n" + " .weak_definition \"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\"\n" + #elif defined(_WIN32) + " .section .sw5tyrf$B, \"dr\", discard, \"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\"\n" + " .globl \"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\"\n" + #endif + " .p2align 1\n" + "\"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\":\n" + " .byte 255\n" + " .byte 7\n" + // This is a direct relative reference to the base accessor for Equatable + // defined below. + " .long (" TUPLE_COMPARABLE_BASEACCESSOREQUATABLE ") - .\n" + // This 0 is our null terminator. + " .byte 0\n" + #if defined (__ELF__) + " .size \"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\", 7\n" + #endif +); + +// Define the conformance descriptor for tuple Comparable. We do this in +// assembly to work around relative reference issues. +__asm( + #if defined(__ELF__) + " .type __swift_tupleComparable_private, @object\n" + " .local __swift_tupleComparable_private\n" + " .comm __swift_tupleComparable_private, 128, 16\n" + " .protected " TUPLE_COMPARABLE_CONF "\n" + " .type " TUPLE_COMPARABLE_CONF ", @object\n" + " .section .rodata\n" + #elif defined(__MACH__) + " .zerofill __DATA, __bss, __swift_tupleComparable_private, 128, 4\n" + " .section __TEXT, __const\n" + #elif defined(_WIN32) + " .lcomm __swift_tupleComparable_private, 128, 16\n" + " .section .rdata, \"dr\"\n" + #pragma comment(linker, "/EXPORT:_swift_tupleComparable_conf,DATA") + #endif + " .globl " TUPLE_COMPARABLE_CONF "\n" + " .p2align 2\n" + TUPLE_COMPARABLE_CONF ":\n" + // This is an indirectable relative reference to the GOT entry for the + // Comparable protocol descriptor. + " .long " INDIRECT_RELREF_GOTPCREL(COMPARABLE_DESCRIPTOR_SYMBOL) "\n" + // 769 is the MetadataKind::Tuple + " .long 769\n" + // This indicates that we have no witness table pattern. We use a generic + // witness table for builtin conformances. + " .long 0\n" + // 196640 are the ConformanceFlags with the type reference bit set to + // MetadataKind, the has resilient witness bit, and the generic witness table + // bit. + " .long 196640\n" + // This 5 is the ResilientWitnessesHeader indicating we have 5 resilient + // witnesses. + " .long 5\n" + // This is an indirectable relative reference to the GOT entry for the + // Comparable base conformance for Equatable. + " .long " INDIRECT_RELREF_GOTPCREL(COMPARABLE_BASE_CONFORMANCE_DESCRIPTOR) "\n" + // This is a direct relative reference to the associated conformance for + // Equatable defined above in assembly. NOTE: We + 1 here because the + // associated conformance structure is 1 aligned. + " .long (\"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\") - . + 1\n" + // This is an indirectable relative reference to the GOT entry for the + // Comparable.< method descriptor. + " .long " INDIRECT_RELREF_GOTPCREL(COMPARABLE_LT_METHOD_DESCRIPTOR) "\n" + // This is a direct relative reference to the less than witness defined below. + " .long (" TUPLE_COMPARABLE_LESSTHAN ") - .\n" + // This is an indirectable relative reference to the GOT entry for the + // Comparable.<= method descriptor. + " .long " INDIRECT_RELREF_GOTPCREL(COMPARBALE_LTE_METHOD_DESCRIPTOR) "\n" + // This is a direct relative reference to the less than or equal witness + // defined below. + " .long (" TUPLE_COMPARABLE_LESSTHANOREQUAL ") - .\n" + // This is an indirectable relative reference to the GOT entry for the + // Comparable.>= method descriptor. + " .long " INDIRECT_RELREF_GOTPCREL(COMPARABLE_GTE_METHOD_DESCRIPTOR) "\n" + // This is a direct relative reference to the greater than or equal witness + // defined below. + " .long (" TUPLE_COMPARABLE_GREATERTHANOREQUAL ") - .\n" + // This is an indirectable relative reference to the GOT entry for the + // Comparable.> method descriptor. + " .long " INDIRECT_RELREF_GOTPCREL(COMPARABLE_GT_METHOD_DESCRIPTOR) "\n" + // This is a direct relative reference to the greater than witness defined + // below. + " .long (" TUPLE_COMPARABLE_GREATERTHAN ") - .\n" + // The witness table size in words. + " .short 0\n" + // The witness table private size in words & requires instantiation. + " .short 1\n" + // The witness table instantiator function. + " .long 0\n" + // This is a direct relative reference to the private data for the + // conformance. + " .long __swift_tupleComparable_private - .\n" + #if defined(__ELF__) + " .size " TUPLE_COMPARABLE_CONF ", 72\n" + #endif +); + +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +const WitnessTable * +_swift_tupleComparable_baseAccessorEquatable(Metadata *assocType, + Metadata *conformingType, + void **witnessTable) { + auto tuple = cast(assocType); + std::vector instantiationArgs; + instantiationArgs.reserve(tuple->NumElements); + + // Fill the instantiationArgs with the element Equatable tables. + for (size_t i = 0; i != tuple->NumElements; i += 1) { + // Get the element's Comparable table. + auto comparableTable = reinterpret_cast(witnessTable[-1 - i]); + + // The Equatable table is the first requirement, thus it'll be right after + // the conformance descriptor. + auto equatableTable = comparableTable[WitnessTableFirstRequirementOffset]; + + instantiationArgs.push_back(equatableTable); + } + + // Finally, call getWitnessTable to realize the tuple's Equatable table. + auto equatableTable = swift_getWitnessTable(&_swift_tupleEquatable_conf, + assocType, + instantiationArgs.data()); + + return equatableTable; +} + +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +bool swift::_swift_tupleComparable_lessThan(OpaqueValue *tuple1, + OpaqueValue *tuple2, + SWIFT_CONTEXT Metadata *swiftSelf, + Metadata *Self, + void *witnessTable) { + auto tuple = cast(Self); + auto table = reinterpret_cast(witnessTable); + + // Loop through all elements, and check if both tuples element is equal. + for (size_t i = 0; i != tuple->NumElements; i += 1) { + auto elt = tuple->getElement(i); + + // Get the element conformance from the private data in the witness table. + auto conformance = reinterpret_cast(table[-1 - i]); + + // Get the respective values from both tuples. + auto value1 = reinterpret_cast( + reinterpret_cast(tuple1) + elt.Offset); + auto value2 = reinterpret_cast( + reinterpret_cast(tuple2) + elt.Offset); + + // First, grab the equatable conformance to prepare to check if the elements + // are equal. (Since this require Equatable conformance, the witness table + // is right after the conformance descriptor, which is at index 0.) + auto comparableTable = reinterpret_cast(conformance); + auto equatableTable = reinterpret_cast(comparableTable[1]); + auto equalsWitness = equatableTable[WitnessTableFirstRequirementOffset]; + auto equals = reinterpret_cast(equalsWitness); + + // Call the equal function. + auto isEqual = equals(value1, value2, elt.Type, elt.Type, + reinterpret_cast(equatableTable)); + + // If these are equal, skip to the next element. + if (isEqual) + continue; + + // Now that we know they are not equal, we can call their less than function + // and return the result. + auto lessThanWitness = comparableTable[WitnessTableFirstRequirementOffset + 1]; + auto lessThan = reinterpret_cast(lessThanWitness); + + // Call the less than function. + auto isLessThan = lessThan(value1, value2, elt.Type, elt.Type, conformance); + + return isLessThan; + } + + // Otherwise these tuples are completely equal, thus they are not less than + // each other. + return false; +} + +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +bool swift::_swift_tupleComparable_lessThanOrEqual(OpaqueValue *tuple1, + OpaqueValue *tuple2, + SWIFT_CONTEXT Metadata *swiftSelf, + Metadata *Self, + void *witnessTable) { + auto tuple = cast(Self); + auto table = reinterpret_cast(witnessTable); + + // Loop through all elements, and check if both tuples element is equal. + for (size_t i = 0; i != tuple->NumElements; i += 1) { + auto elt = tuple->getElement(i); + + // Get the element conformance from the private data in the witness table. + auto conformance = reinterpret_cast(table[-1 - i]); + + // Get the respective values from both tuples. + auto value1 = reinterpret_cast( + reinterpret_cast(tuple1) + elt.Offset); + auto value2 = reinterpret_cast( + reinterpret_cast(tuple2) + elt.Offset); + + // First, grab the equatable conformance to prepare to check if the elements + // are equal. (Since this require Equatable conformance, the witness table + // is right after the conformance descriptor, which is at index 0.) + auto comparableTable = reinterpret_cast(conformance); + auto equatableTable = reinterpret_cast(comparableTable[1]); + auto equalsWitness = equatableTable[WitnessTableFirstRequirementOffset]; + auto equals = reinterpret_cast(equalsWitness); + + // Call the equal function. + auto isEqual = equals(value1, value2, elt.Type, elt.Type, + reinterpret_cast(equatableTable)); + + // If these are equal, skip to the next element. + if (isEqual) + continue; + + // Now that we know they are not equal, we can call their less than or equal + // function and return the result. + auto lessThanOrEqualWitness = + comparableTable[WitnessTableFirstRequirementOffset + 2]; + auto lessThanOrEqual = + reinterpret_cast(lessThanOrEqualWitness); + + // Call the less than function. + auto isLessThanOrEqual = lessThanOrEqual(value1, value2, elt.Type, elt.Type, + conformance); + + return isLessThanOrEqual; + } + + // Otherwise these tuples are completely equal. + return true; +} + +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +bool swift::_swift_tupleComparable_greaterThanOrEqual(OpaqueValue *tuple1, + OpaqueValue *tuple2, + SWIFT_CONTEXT Metadata *swiftSelf, + Metadata *Self, + void *witnessTable) { + auto tuple = cast(Self); + auto table = reinterpret_cast(witnessTable); + + // Loop through all elements, and check if both tuples element is equal. + for (size_t i = 0; i != tuple->NumElements; i += 1) { + auto elt = tuple->getElement(i); + + // Get the element conformance from the private data in the witness table. + auto conformance = reinterpret_cast(table[-1 - i]); + + // Get the respective values from both tuples. + auto value1 = reinterpret_cast( + reinterpret_cast(tuple1) + elt.Offset); + auto value2 = reinterpret_cast( + reinterpret_cast(tuple2) + elt.Offset); + + // First, grab the equatable conformance to prepare to check if the elements + // are equal. (Since this require Equatable conformance, the witness table + // is right after the conformance descriptor, which is at index 0.) + auto comparableTable = reinterpret_cast(conformance); + auto equatableTable = reinterpret_cast(comparableTable[1]); + auto equalsWitness = equatableTable[WitnessTableFirstRequirementOffset]; + auto equals = reinterpret_cast(equalsWitness); + + // Call the equal function. + auto isEqual = equals(value1, value2, elt.Type, elt.Type, + reinterpret_cast(equatableTable)); + + // If these are equal, skip to the next element. + if (isEqual) + continue; + + // Now that we know they are not equal, we can call their greater than or + // equal function and return the result. + auto greaterThanOrEqualWitness = + comparableTable[WitnessTableFirstRequirementOffset + 3]; + auto greaterThanOrEqual = + reinterpret_cast(greaterThanOrEqualWitness); + + // Call the greater than or equal function. + auto isGreaterThanOrEqual = greaterThanOrEqual(value1, value2, elt.Type, + elt.Type, conformance); + + return isGreaterThanOrEqual; + } + + // Otherwise these tuples are completely equal. + return true; +} + +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +bool swift::_swift_tupleComparable_greaterThan(OpaqueValue *tuple1, + OpaqueValue *tuple2, + SWIFT_CONTEXT Metadata *swiftSelf, + Metadata *Self, + void *witnessTable) { + auto tuple = cast(Self); + auto table = reinterpret_cast(witnessTable); + + // Loop through all elements, and check if both tuples element is equal. + for (size_t i = 0; i != tuple->NumElements; i += 1) { + auto elt = tuple->getElement(i); + + // Get the element conformance from the private data in the witness table. + auto conformance = reinterpret_cast(table[-1 - i]); + + // Get the respective values from both tuples. + auto value1 = reinterpret_cast( + reinterpret_cast(tuple1) + elt.Offset); + auto value2 = reinterpret_cast( + reinterpret_cast(tuple2) + elt.Offset); + + // First, grab the equatable conformance to prepare to check if the elements + // are equal. (Since this require Equatable conformance, the witness table + // is right after the conformance descriptor, which is at index 0.) + auto comparableTable = reinterpret_cast(conformance); + auto equatableTable = reinterpret_cast(comparableTable[1]); + auto equalsWitness = equatableTable[WitnessTableFirstRequirementOffset]; + auto equals = reinterpret_cast(equalsWitness); + + // Call the equal function. + auto isEqual = equals(value1, value2, elt.Type, elt.Type, + reinterpret_cast(equatableTable)); + + // If these are equal, skip to the next element. + if (isEqual) + continue; + + // Now that we know they are not equal, we can call their greater than + // function and return the result. + auto greaterThanWitness = + comparableTable[WitnessTableFirstRequirementOffset + 4]; + auto greaterThan = + reinterpret_cast(greaterThanWitness); + + // Call the greater than function. + auto isGreaterThan = greaterThan(value1, value2, elt.Type, elt.Type, + conformance); + + return isGreaterThan; + } + + // Otherwise these tuples are completely equal, thus they are not greater than + // each other. + return false; +} + +//===----------------------------------------------------------------------===// +// Tuple Hashable Conformance +//===----------------------------------------------------------------------===// + +// For 32 bit ARM and i386 (specifically armv7, armv7s, and armv7k), emit +// non-lazy pointer stubs to indirectly reference. Darwin doesn't support @GOT +// syntax for those archs. +#if defined(__MACH__) && \ + ((defined(__arm__) && !defined(__aarch64__)) || defined(__i386__)) +__asm( +#if defined(__arm__) + " .section __DATA, __nl_symbol_ptr, non_lazy_symbol_pointers\n" +#elif defined(__i386__) + " .section __IMPORT, __pointers, non_lazy_symbol_pointers\n" +#endif + " .p2align 2\n" + "L" HASHABLE_DESCRIPTOR_SYMBOL "$non_lazy_ptr:\n" + " .indirect_symbol " HASHABLE_DESCRIPTOR_SYMBOL "\n" + " .long 0\n" + "L" HASHABLE_BASE_CONFORMANCE_DESCRIPTOR "$non_lazy_ptr:\n" + " .indirect_symbol " HASHABLE_BASE_CONFORMANCE_DESCRIPTOR "\n" + " .long 0\n" + "L" HASHABLE_HASHVALUE_METHOD_DESCRIPTOR "$non_lazy_ptr:\n" + " .indirect_symbol " HASHABLE_HASHVALUE_METHOD_DESCRIPTOR "\n" + " .long 0\n" + "L" HASHABLE_HASH_METHOD_DESCRIPTOR "$non_lazy_ptr:\n" + " .indirect_symbol " HASHABLE_HASH_METHOD_DESCRIPTOR "\n" + " .long 0\n" + "L" HASHABLE_RAWHASHVALUE_METHOD_DESCRIPTOR "$non_lazy_ptr:\n" + " .indirect_symbol " HASHABLE_RAWHASHVALUE_METHOD_DESCRIPTOR "\n" + " .long 0\n" +); +#endif + +// Define the conformance descriptor for tuple Hashable. We do this in +// assembly to work around relative reference issues. +__asm( + #if defined(__ELF__) + " .type __swift_tupleHashable_private, @object\n" + " .local __swift_tupleHashable_private\n" + " .comm __swift_tupleHashable_private, 128, 16\n" + " .protected " TUPLE_HASHABLE_CONF "\n" + " .type " TUPLE_HASHABLE_CONF ", @object\n" + " .section .rodata\n" + #elif defined(__MACH__) + " .zerofill __DATA, __bss, __swift_tupleHashable_private, 128, 4\n" + " .section __TEXT, __const\n" + #elif defined(_WIN32) + " .lcomm __swift_tupleHashable_private, 128, 16\n" + " .section .rdata, \"dr\"\n" + #pragma comment(linker, "/EXPORT:_swift_tupleHashable_conf,DATA") + #endif + " .globl " TUPLE_HASHABLE_CONF "\n" + " .p2align 2\n" + TUPLE_HASHABLE_CONF ":\n" + // This is an indirectable relative reference to the GOT entry for the + // Hashable protocol descriptor. + " .long " INDIRECT_RELREF_GOTPCREL(HASHABLE_DESCRIPTOR_SYMBOL) "\n" + // 769 is the MetadataKind::Tuple + " .long 769\n" + // This indicates that we have no witness table pattern. We use a generic + // witness table for builtin conformances. + " .long 0\n" + // 196640 are the ConformanceFlags with the type reference bit set to + // MetadataKind, the has resilient witness bit, and the generic witness table + // bit. + " .long 196640\n" + // This 4 is the ResilientWitnessesHeader indicating we have 4 resilient + // witnesses. + " .long 4\n" + // This is an indirectable relative reference to the GOT entry for the + // Hashable base conformance for Equatable. + " .long " INDIRECT_RELREF_GOTPCREL(HASHABLE_BASE_CONFORMANCE_DESCRIPTOR) "\n" + // This is a direct relative reference to the associated conformance for + // Equatable defined above in assembly. NOTE: We intentionally use the + // Comparable implementation for this because the implementation is the same + // for both Hashable and Comparable. Both want to grab the Equatable table + // from its elements whose witness table is located in the same place for both + // protocols. NOTE: We + 1 here because the associated conformance + // structure is 1 aligned. + " .long (\"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\") - . + 1\n" + // This is an indirectable relative reference to the GOT entry for the + // Hashable.hashValue method descriptor. + " .long " INDIRECT_RELREF_GOTPCREL(HASHABLE_HASHVALUE_METHOD_DESCRIPTOR) "\n" + // This is a direct relative reference to the hashValue witness defined below. + " .long (" TUPLE_HASHABLE_HASHVALUE ") - .\n" + // This is an indirectable relative reference to the GOT entry for the + // Hashable.hash(into:) method descriptor. + " .long " INDIRECT_RELREF_GOTPCREL(HASHABLE_HASH_METHOD_DESCRIPTOR) "\n" + // This is a direct relative reference to the hash(into:) witness defined below. + " .long (" TUPLE_HASHABLE_HASH ") - .\n" + // This is an indirectable relative reference to the GOT equivalent for the + // Hashable._rawHashValue method descriptor. + " .long " INDIRECT_RELREF_GOTPCREL(HASHABLE_RAWHASHVALUE_METHOD_DESCRIPTOR) "\n" + // This 0 indicates that we are requesting the default implementation for the + // _rawHashValue getter. + " .long 0\n" + // The witness table size in words. + " .short 0\n" + // The witness table private size in words & requires instantiation. + " .short 1\n" + // The witness table instantiator function. + " .long 0\n" + // This is a direct relative reference to the private data for the + // conformance. + " .long __swift_tupleHashable_private - .\n" + #if defined(__ELF__) + " .size " TUPLE_HASHABLE_CONF ", 64\n" + #endif +); + +extern "C" SWIFT_CC(swift) +intptr_t SWIFT_HASHVALUE_FUNC(OpaqueValue *value, Metadata *Self, + void *witnessTable); + +extern "C" SWIFT_CC(swift) +void SWIFT_HASHER_COMBINE_FUNC(OpaqueValue *value, const Metadata *Self, + const WitnessTable *witnessTable, + SWIFT_CONTEXT OpaqueValue *hasher); + +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +intptr_t _swift_tupleHashable_hashValue(SWIFT_CONTEXT OpaqueValue *tuple, + Metadata *Self, void *witnessTable) { + return SWIFT_HASHVALUE_FUNC(tuple, Self, witnessTable); +} + +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +void _swift_tupleHashable_hash(OpaqueValue *hasher, + SWIFT_CONTEXT OpaqueValue *tuple, + Metadata *Self, void *witnessTable) { + auto tupleTy = cast(Self); + auto table = reinterpret_cast(witnessTable); + + // Loop through all elements and hash them into the Hasher. + for (size_t i = 0; i != tupleTy->NumElements; i += 1) { + auto elt = tupleTy->getElement(i); + + // Get the element conformance from the private data in the witness table. + auto conformance = reinterpret_cast(table[-1 - i]); + + // Get the element value from the tuple. + auto value = reinterpret_cast( + reinterpret_cast(tuple) + elt.Offset); + + // Call the combine function on the hasher for this element value and we're + // done! + SWIFT_HASHER_COMBINE_FUNC(value, elt.Type, conformance, hasher); + } +} diff --git a/stdlib/public/runtime/CMakeLists.txt b/stdlib/public/runtime/CMakeLists.txt index 0b481bd2459b8..0250f90024630 100644 --- a/stdlib/public/runtime/CMakeLists.txt +++ b/stdlib/public/runtime/CMakeLists.txt @@ -22,13 +22,14 @@ set(swift_runtime_objc_sources ErrorObject.mm SwiftObject.mm SwiftValue.mm - ReflectionMirror.mm + ReflectionMirrorObjC.mm ObjCRuntimeGetImageNameFromClass.mm) set(swift_runtime_sources AnyHashableSupport.cpp Array.cpp BackDeployment.cpp + BuiltinProtocolConformances.cpp Casting.cpp CompatibilityOverride.cpp CygwinPort.cpp @@ -63,6 +64,7 @@ set(swift_runtime_sources Portability.cpp ProtocolConformance.cpp RefCount.cpp + ReflectionMirror.cpp RuntimeInvocationsTracking.cpp SwiftDtoa.cpp) @@ -80,48 +82,9 @@ list(APPEND swift_runtime_library_compile_flags -I${SWIFT_SOURCE_DIR}/stdlib/inc set(sdk "${SWIFT_HOST_VARIANT_SDK}") if(SWIFT_BUILD_STATIC_STDLIB AND "${sdk}" STREQUAL "LINUX") - list(REMOVE_ITEM swift_runtime_sources ImageInspectionELF.cpp) set(static_binary_lnk_file_list) string(TOLOWER "${sdk}" lowercase_sdk) - # These two libraries are only used with the static swiftcore - add_swift_target_library(swiftImageInspectionShared STATIC - ImageInspectionELF.cpp - C_COMPILE_FLAGS ${swift_runtime_library_compile_flags} - LINK_FLAGS ${swift_runtime_linker_flags} - SWIFT_COMPILE_FLAGS ${SWIFT_STANDARD_LIBRARY_SWIFT_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}) - add_dependencies(stdlib ${FragileSupportLibrary}) - swift_install_in_component(FILES $ - DESTINATION "lib/swift_static/${lowercase_sdk}/${arch}" - COMPONENT stdlib) - endforeach() - - set(FragileSupportLibraryPrimary swiftImageInspectionShared-${SWIFT_SDK_${sdk}_LIB_SUBDIR}-${SWIFT_PRIMARY_VARIANT_ARCH}) - set(LibraryLocationPrimary ${SWIFTSTATICLIB_DIR}/${lowercase_sdk}) - add_custom_command_target(swift_image_inspection_static_primary_arch - COMMAND - "${CMAKE_COMMAND}" -E copy $ ${LibraryLocationPrimary} - OUTPUT - "${LibraryLocationPrimary}/${CMAKE_STATIC_LIBRARY_PREFIX}swiftImageInspectionShared${CMAKE_STATIC_LIBRARY_SUFFIX}" - DEPENDS - ${FragileSupportLibraryPrimary}) - add_dependencies(stdlib ${FragileSupportLibraryPrimary}) - swift_install_in_component(FILES $ - DESTINATION "lib/swift_static/${lowercase_sdk}" - COMPONENT stdlib) - # Generate the static-executable-args.lnk file used for ELF systems (eg linux) set(linkfile "${lowercase_sdk}/static-executable-args.lnk") add_custom_command_target(swift_static_binary_${sdk}_args @@ -139,18 +102,6 @@ if(SWIFT_BUILD_STATIC_STDLIB AND "${sdk}" STREQUAL "LINUX") DESTINATION "lib/swift_static/${lowercase_sdk}" COMPONENT stdlib) add_custom_target(static_binary_magic ALL DEPENDS ${static_binary_lnk_file_list}) - foreach(arch IN LISTS SWIFT_SDK_LINUX_ARCHITECTURES) - add_dependencies(static_binary_magic ${swift_image_inspection_${arch}_static}) - endforeach() - add_dependencies(static_binary_magic ${swift_image_inspection_static_primary_arch}) - add_dependencies(stdlib static_binary_magic) - - add_swift_target_library(swiftImageInspectionSharedObject OBJECT_LIBRARY - ImageInspectionELF.cpp - C_COMPILE_FLAGS ${swift_runtime_library_compile_flags} - LINK_FLAGS ${swift_runtime_linker_flags} - SWIFT_COMPILE_FLAGS ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} - INSTALL_IN_COMPONENT never_install) endif() add_swift_target_library(swiftRuntime OBJECT_LIBRARY @@ -279,7 +230,6 @@ foreach(sdk ${SWIFT_CONFIGURED_SDKS}) -ldl -lpthread -lswiftCore --lswiftImageInspectionShared ${libicu_i18n_a} ${libicu_uc_a} ${libicu_data_a} diff --git a/stdlib/public/runtime/Casting.cpp b/stdlib/public/runtime/Casting.cpp index 99e08243588bb..487bc05fb871a 100644 --- a/stdlib/public/runtime/Casting.cpp +++ b/stdlib/public/runtime/Casting.cpp @@ -35,7 +35,7 @@ # define SWIFT_CASTING_SUPPORTS_MUTEX 1 # include "swift/Runtime/Mutex.h" #endif -#include "swift/Runtime/Unreachable.h" +#include "swift/Basic/Unreachable.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/PointerIntPair.h" #if SWIFT_OBJC_INTEROP @@ -1016,7 +1016,7 @@ static bool _dynamicCastToExistential(OpaqueValue *dest, } } - swift_runtime_unreachable("Unhandled ExistentialTypeRepresentation in switch."); + swift_unreachable("Unhandled ExistentialTypeRepresentation in switch."); } /******************************************************************************/ @@ -1214,7 +1214,7 @@ swift_dynamicCastMetatypeImpl(const Metadata *sourceType, return nullptr; } - swift_runtime_unreachable("Unhandled MetadataKind in switch."); + swift_unreachable("Unhandled MetadataKind in switch."); } static const Metadata * @@ -1424,7 +1424,7 @@ static bool _dynamicCastToUnknownClassFromExistential(OpaqueValue *dest, } } - swift_runtime_unreachable( + swift_unreachable( "Unhandled ExistentialTypeRepresentation in switch."); } diff --git a/stdlib/public/runtime/DynamicCast.cpp b/stdlib/public/runtime/DynamicCast.cpp index f19af13ab6311..e7f624ba8c555 100644 --- a/stdlib/public/runtime/DynamicCast.cpp +++ b/stdlib/public/runtime/DynamicCast.cpp @@ -1806,7 +1806,7 @@ static tryCastFunctionType *selectCasterForDest(const Metadata *destType) { case ExistentialTypeRepresentation::Error: // => Error existential return tryCastToErrorExistential; } - swift_runtime_unreachable( + swift_unreachable( "Unknown existential type representation in dynamic cast dispatch"); } case MetadataKind::Metatype: @@ -1821,14 +1821,14 @@ static tryCastFunctionType *selectCasterForDest(const Metadata *destType) { // These are internal details of runtime-only structures, // so will never appear in compiler-generated types. // As such, they don't need support here. - swift_runtime_unreachable( + swift_unreachable( "Unexpected MetadataKind in dynamic cast dispatch"); return nullptr; default: // If you see this message, then there is a new MetadataKind that I didn't // know about when I wrote this code. Please figure out what it is, how to // handle it, and add a case for it. - swift_runtime_unreachable( + swift_unreachable( "Unknown MetadataKind in dynamic cast dispatch"); } } diff --git a/stdlib/public/runtime/Exclusivity.cpp b/stdlib/public/runtime/Exclusivity.cpp index 73628b7cdaae4..7750fbae41e8b 100644 --- a/stdlib/public/runtime/Exclusivity.cpp +++ b/stdlib/public/runtime/Exclusivity.cpp @@ -219,7 +219,7 @@ class AccessSet { } } - swift_runtime_unreachable("access not found in set"); + swift_unreachable("access not found in set"); } #ifndef NDEBUG diff --git a/stdlib/public/runtime/Float16Support.cpp b/stdlib/public/runtime/Float16Support.cpp index d7377400ba0be..9d8c4940054df 100644 --- a/stdlib/public/runtime/Float16Support.cpp +++ b/stdlib/public/runtime/Float16Support.cpp @@ -29,7 +29,7 @@ // Android NDK (Data.BoxedType); } - int compareWithKey(const Metadata *type) const { - return comparePointers(type, Data.BoxedType); + bool matchesKey(const Metadata *type) const { return type == Data.BoxedType; } + + friend llvm::hash_code hash_value(const BoxCacheEntry &value) { + return llvm::hash_value(value.Data.BoxedType); } static size_t getExtraAllocationSize(const Metadata *key) { diff --git a/stdlib/public/runtime/ImageInspectionMachO.cpp b/stdlib/public/runtime/ImageInspectionMachO.cpp index b233608308e99..54c4d17a52a5e 100644 --- a/stdlib/public/runtime/ImageInspectionMachO.cpp +++ b/stdlib/public/runtime/ImageInspectionMachO.cpp @@ -122,7 +122,11 @@ void addImageCallback2Sections(const mach_header *mh, intptr_t vmaddr_slide) { #if OBJC_ADDLOADIMAGEFUNC_DEFINED && SWIFT_OBJC_INTEROP #define REGISTER_FUNC(...) \ - _dyld_register_func_for_add_image(__VA_ARGS__); + if (__builtin_available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)) { \ + objc_addLoadImageFunc(__VA_ARGS__); \ + } else { \ + _dyld_register_func_for_add_image(__VA_ARGS__); \ + } #else #define REGISTER_FUNC(...) _dyld_register_func_for_add_image(__VA_ARGS__) #endif diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index 56fd8670c640f..9f9f561cdc827 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.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 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -20,6 +20,7 @@ #include "swift/Basic/Range.h" #include "swift/Demangling/Demangler.h" #include "swift/ABI/TypeIdentity.h" +#include "swift/Runtime/BuiltinProtocolConformances.h" #include "swift/Runtime/Casting.h" #include "swift/Runtime/EnvironmentVariables.h" #include "swift/Runtime/ExistentialContainer.h" @@ -193,8 +194,11 @@ computeMetadataBoundsForSuperclass(const void *ref, break; #endif } + // Type metadata type ref is unsupported here. + case TypeReferenceKind::MetadataKind: + break; } - swift_runtime_unreachable("unsupported superclass reference kind"); + swift_unreachable("unsupported superclass reference kind"); } static ClassMetadataBounds computeMetadataBoundsFromSuperclass( @@ -998,8 +1002,12 @@ namespace { return reinterpret_cast(Data.Class); } - int compareWithKey(const ClassMetadata *theClass) const { - return comparePointers(theClass, Data.Class); + bool matchesKey(const ClassMetadata *theClass) const { + return theClass == Data.Class; + } + + friend llvm::hash_code hash_value(const ObjCClassCacheEntry &value) { + return llvm::hash_value(value.Data.Class); } static size_t getExtraAllocationSize(const ClassMetadata *key) { @@ -1094,6 +1102,15 @@ class FunctionCacheEntry { auto flags = Flags.hasParameterFlags() ? ParameterFlags[index] : 0; return ParameterFlags::fromIntValue(flags); } + + friend llvm::hash_code hash_value(const Key &key) { + auto hash = llvm::hash_combine(key.Flags.getIntValue(), key.Result); + for (unsigned i = 0, e = key.getFlags().getNumParameters(); i != e; ++i) { + hash = llvm::hash_combine(hash, key.getParameter(i)); + hash = llvm::hash_combine(hash, key.getParameterFlags(i).getIntValue()); + } + return hash; + } }; FunctionCacheEntry(const Key &key); @@ -1102,28 +1119,27 @@ class FunctionCacheEntry { return 0; // No single meaningful value here. } - int compareWithKey(const Key &key) const { - auto keyFlags = key.getFlags(); - if (auto result = compareIntegers(keyFlags.getIntValue(), - Data.Flags.getIntValue())) - return result; - - if (auto result = comparePointers(key.getResult(), Data.ResultType)) - return result; - - for (unsigned i = 0, e = keyFlags.getNumParameters(); i != e; ++i) { - if (auto result = - comparePointers(key.getParameter(i), Data.getParameter(i))) - return result; - - if (auto result = - compareIntegers(key.getParameterFlags(i).getIntValue(), - Data.getParameterFlags(i).getIntValue())) - return result; + bool matchesKey(const Key &key) const { + if (key.getFlags().getIntValue() != Data.Flags.getIntValue()) + return false; + if (key.getResult() != Data.ResultType) + return false; + for (unsigned i = 0, e = key.getFlags().getNumParameters(); i != e; ++i) { + if (key.getParameter(i) != Data.getParameter(i)) + return false; + if (key.getParameterFlags(i).getIntValue() != + Data.getParameterFlags(i).getIntValue()) + return false; } + return true; + } - return 0; + friend llvm::hash_code hash_value(const FunctionCacheEntry &value) { + Key key = {value.Data.Flags, value.Data.getParameters(), + value.Data.getParameterFlags(), value.Data.ResultType}; + return hash_value(key); } + static size_t getExtraAllocationSize(const Key &key) { return getExtraAllocationSize(key.Flags); } @@ -1986,6 +2002,9 @@ namespace { bool operator==(const TypeContextIdentity &other) const { return Name == other.Name; } + friend llvm::hash_code hash_value(const TypeContextIdentity &value) { + return llvm::hash_value(value.Name); + } int compare(const TypeContextIdentity &other) const { return Name.compare(other.Name); } @@ -3261,8 +3280,12 @@ namespace { return reinterpret_cast(Data.InstanceType); } - int compareWithKey(const Metadata *instanceType) const { - return comparePointers(instanceType, Data.InstanceType); + bool matchesKey(const Metadata *instanceType) const { + return instanceType == Data.InstanceType; + } + + friend llvm::hash_code hash_value(const MetatypeCacheEntry &value) { + return llvm::hash_value(value.Data.InstanceType); } static size_t getExtraAllocationSize(const Metadata *instanceType) { @@ -3306,8 +3329,11 @@ class ExistentialMetatypeValueWitnessTableCacheEntry { return static_cast(getNumWitnessTables()); } - int compareWithKey(unsigned key) const { - return compareIntegers(key, getNumWitnessTables()); + bool matchesKey(unsigned key) const { return key == getNumWitnessTables(); } + + friend llvm::hash_code + hash_value(const ExistentialMetatypeValueWitnessTableCacheEntry &value) { + return llvm::hash_value(value.getNumWitnessTables()); } static size_t getExtraAllocationSize(unsigned numTables) { @@ -3328,8 +3354,13 @@ class ExistentialMetatypeCacheEntry { return reinterpret_cast(Data.InstanceType); } - int compareWithKey(const Metadata *instanceType) const { - return comparePointers(instanceType, Data.InstanceType); + bool matchesKey(const Metadata *instanceType) const { + return instanceType == Data.InstanceType; + } + + friend llvm::hash_code + hash_value(const ExistentialMetatypeCacheEntry &value) { + return llvm::hash_value(value.Data.InstanceType); } static size_t getExtraAllocationSize(const Metadata *key) { @@ -3442,6 +3473,14 @@ class ExistentialCacheEntry { ProtocolClassConstraint ClassConstraint : 1; uint32_t NumProtocols : 31; const ProtocolDescriptorRef *Protocols; + + friend llvm::hash_code hash_value(const Key &key) { + auto hash = llvm::hash_combine(key.SuperclassConstraint, + key.ClassConstraint, key.NumProtocols); + for (size_t i = 0; i != key.NumProtocols; i++) + hash = llvm::hash_combine(hash, key.Protocols[i].getRawData()); + return hash; + } }; ExistentialCacheEntry(Key key); @@ -3450,27 +3489,30 @@ class ExistentialCacheEntry { return 0; } - int compareWithKey(Key key) const { - if (auto result = compareIntegers(key.ClassConstraint, - Data.Flags.getClassConstraint())) - return result; + bool matchesKey(Key key) const { + if (key.ClassConstraint != Data.Flags.getClassConstraint()) + return false; - if (auto result = comparePointers(key.SuperclassConstraint, - Data.getSuperclassConstraint())) - return result; + if (key.SuperclassConstraint != Data.getSuperclassConstraint()) + return false; - if (auto result = compareIntegers(key.NumProtocols, - Data.NumProtocols)) - return result; + if (key.NumProtocols != Data.NumProtocols) + return false; auto dataProtocols = Data.getProtocols(); for (size_t i = 0; i != key.NumProtocols; ++i) { - if (auto result = compareIntegers(key.Protocols[i].getRawData(), - dataProtocols[i].getRawData())) - return result; + if (key.Protocols[i].getRawData() != dataProtocols[i].getRawData()) + return false; } - return 0; + return true; + } + + friend llvm::hash_code hash_value(const ExistentialCacheEntry &value) { + Key key = {value.Data.getSuperclassConstraint(), + value.Data.Flags.getClassConstraint(), value.Data.NumProtocols, + value.Data.getProtocols().data()}; + return hash_value(key); } static size_t getExtraAllocationSize(Key key) { @@ -3501,8 +3543,11 @@ class OpaqueExistentialValueWitnessTableCacheEntry { return getNumWitnessTables(); } - int compareWithKey(unsigned key) const { - return compareIntegers(key, getNumWitnessTables()); + bool matchesKey(unsigned key) const { return key == getNumWitnessTables(); } + + friend llvm::hash_code + hash_value(const OpaqueExistentialValueWitnessTableCacheEntry &value) { + return llvm::hash_value(value.getNumWitnessTables()); } static size_t getExtraAllocationSize(unsigned numTables) { @@ -3528,8 +3573,11 @@ class ClassExistentialValueWitnessTableCacheEntry { return getNumWitnessTables(); } - int compareWithKey(unsigned key) const { - return compareIntegers(key, getNumWitnessTables()); + bool matchesKey(unsigned key) const { return key == getNumWitnessTables(); } + + friend llvm::hash_code + hash_value(const ClassExistentialValueWitnessTableCacheEntry &value) { + return llvm::hash_value(value.getNumWitnessTables()); } static size_t getExtraAllocationSize(unsigned numTables) { @@ -3716,7 +3764,7 @@ getExistentialValueWitnesses(ProtocolClassConstraint classConstraint, return getOpaqueExistentialValueWitnesses(numWitnessTables); } - swift_runtime_unreachable("Unhandled ProtocolClassConstraint in switch."); + swift_unreachable("Unhandled ProtocolClassConstraint in switch."); } template<> ExistentialTypeRepresentation @@ -3761,7 +3809,7 @@ ExistentialTypeMetadata::mayTakeValue(const OpaqueValue *container) const { } } - swift_runtime_unreachable( + swift_unreachable( "Unhandled ExistentialTypeRepresentation in switch."); } @@ -3810,7 +3858,7 @@ ExistentialTypeMetadata::projectValue(const OpaqueValue *container) const { } } - swift_runtime_unreachable( + swift_unreachable( "Unhandled ExistentialTypeRepresentation in switch."); } @@ -3835,7 +3883,7 @@ ExistentialTypeMetadata::getDynamicType(const OpaqueValue *container) const { } } - swift_runtime_unreachable( + swift_unreachable( "Unhandled ExistentialTypeRepresentation in switch."); } @@ -4030,7 +4078,7 @@ class ForeignMetadataCacheEntry return Value; } void setValue(ValueType value) { - swift_runtime_unreachable("should never be called"); + swift_unreachable("should never be called"); } public: @@ -4094,7 +4142,7 @@ class ForeignMetadataCacheEntry } AllocationResult allocate(Metadata *candidate) { - swift_runtime_unreachable( + swift_unreachable( "always flags allocation complete during construction"); } @@ -4146,24 +4194,34 @@ namespace { struct Key { const TypeContextDescriptor *type; const ProtocolDescriptor *protocol; + + friend llvm::hash_code hash_value(const Key &value) { + return llvm::hash_combine(value.protocol, + TypeContextIdentity(value.type)); + } }; - const Key key; + const TypeContextDescriptor *type; + const ProtocolDescriptor *protocol; const WitnessTable *data; ForeignWitnessTableCacheEntry(const ForeignWitnessTableCacheEntry::Key k, const WitnessTable *d) - : key(k), data(d) {} + : type(k.type), protocol(k.protocol), data(d) {} intptr_t getKeyIntValueForDump() { - return reinterpret_cast(key.type); + return reinterpret_cast(type); } - int compareWithKey(const Key other) const { - if (auto r = comparePointers(other.protocol, key.protocol)) - return r; + bool matchesKey(const Key other) const { + return other.protocol == protocol && + TypeContextIdentity(other.type) == TypeContextIdentity(type); + } - return TypeContextIdentity(other.type).compare(TypeContextIdentity(key.type)); + friend llvm::hash_code + hash_value(const ForeignWitnessTableCacheEntry &value) { + Key key{value.type, value.protocol}; + return hash_value(key); } static size_t getExtraAllocationSize(const Key, @@ -4177,19 +4235,23 @@ namespace { }; } -static SimpleGlobalCache ForeignWitnessTables; +static ConcurrentReadableHashMap + ForeignWitnessTables; static const WitnessTable *_getForeignWitnessTable( const WitnessTable *witnessTableCandidate, const TypeContextDescriptor *contextDescriptor, const ProtocolDescriptor *protocol) { - auto result = - ForeignWitnessTables - .getOrInsert( - ForeignWitnessTableCacheEntry::Key{contextDescriptor, protocol}, - witnessTableCandidate) - .first->data; + const WitnessTable *result = nullptr; + ForeignWitnessTableCacheEntry::Key key{contextDescriptor, protocol}; + ForeignWitnessTables.getOrInsert( + key, [&](ForeignWitnessTableCacheEntry *entryPtr, bool created) { + if (created) + new (entryPtr) + ForeignWitnessTableCacheEntry(key, witnessTableCandidate); + result = entryPtr->data; + return true; + }); return result; } @@ -4416,18 +4478,27 @@ class WitnessTableCacheEntry : const Metadata *type, const ProtocolConformanceDescriptor *conformance, const void * const *instantiationArgs) { - return getWitnessTableSize(conformance); + return getWitnessTableSize(type, conformance); } size_t getExtraAllocationSize() const { - return getWitnessTableSize(Conformance); + return getWitnessTableSize(Type, Conformance); } - static size_t getWitnessTableSize( + static size_t getWitnessTableSize(const Metadata *type, const ProtocolConformanceDescriptor *conformance) { auto protocol = conformance->getProtocol(); auto genericTable = conformance->getGenericWitnessTable(); size_t numPrivateWords = genericTable->getWitnessTablePrivateSizeInWords(); + + // Builtin conformance descriptors, namely tuple conformances at the moment, + // have their private size in words be determined via the number of elements + // the type has. + if (conformance->isBuiltin()) { + auto tuple = cast(type); + numPrivateWords = tuple->NumElements; + } + size_t numRequirementWords = WitnessTableFirstRequirementOffset + protocol->NumRequirements; return (numPrivateWords + numRequirementWords) * sizeof(void*); @@ -4555,7 +4626,7 @@ static void initProtocolWitness(void **slot, void *witness, reqt); return; } - swift_runtime_unreachable("bad witness kind"); + swift_unreachable("bad witness kind"); #else *slot = witness; #endif @@ -4588,7 +4659,7 @@ static void copyProtocolWitness(void **dest, void * const *src, swift_ptrauth_copy(dest, src, reqt.Flags.getExtraDiscriminator()); return; } - swift_runtime_unreachable("bad witness kind"); + swift_unreachable("bad witness kind"); #else *dest = *src; #endif @@ -4683,6 +4754,14 @@ instantiateWitnessTable(const Metadata *Type, // Number of bytes for any private storage used by the conformance itself. size_t privateSizeInWords = genericTable->getWitnessTablePrivateSizeInWords(); + // Builtin conformance descriptors, namely tuple conformances at the moment, + // have their private size in words be determined via the number of elements + // the type has. + if (conformance->isBuiltin()) { + auto tuple = cast(Type); + privateSizeInWords = tuple->NumElements; + } + // Advance the address point; the private storage area is accessed via // negative offsets. auto table = fullTable + privateSizeInWords; @@ -4725,6 +4804,16 @@ instantiateWitnessTable(const Metadata *Type, if (conditionalRequirement.Flags.hasExtraArgument()) copyNextInstantiationArg(); } + + // Builtin conformance descriptors, namely tuple conformances at the moment, + // have instantiation arguments equal to the number of element conformances. + if (conformance->isBuiltin()) { + auto tuple = cast(Type); + + for (size_t i = 0; i != tuple->NumElements; i += 1) { + copyNextInstantiationArg(); + } + } } // Fill in any default requirements. @@ -4748,7 +4837,7 @@ WitnessTableCacheEntry::allocate( void **fullTable = reinterpret_cast(this + 1); // Zero out the witness table. - memset(fullTable, 0, getWitnessTableSize(conformance)); + memset(fullTable, 0, getWitnessTableSize(Type, conformance)); // Instantiate the table. return instantiateWitnessTable(Type, Conformance, instantiationArgs, fullTable); @@ -4769,7 +4858,7 @@ getNondependentWitnessTable(const ProtocolConformanceDescriptor *conformance, } // Allocate space for the table. - auto tableSize = WitnessTableCacheEntry::getWitnessTableSize(conformance); + auto tableSize = WitnessTableCacheEntry::getWitnessTableSize(type, conformance); TaggedMetadataAllocator allocator; auto buffer = (void **)allocator.Allocate(tableSize, alignof(void*)); memset(buffer, 0, tableSize); @@ -5141,7 +5230,7 @@ static const WitnessTable *swift_getAssociatedConformanceWitnessSlowImpl( return assocWitnessTable; } - swift_runtime_unreachable("Invalid mangled associate conformance"); + swift_unreachable("Invalid mangled associate conformance"); } const WitnessTable *swift::swift_getAssociatedConformanceWitness( @@ -5234,7 +5323,7 @@ static Result performOnMetadataCache(const Metadata *metadata, case TypeContextDescriptorFlags::SingletonMetadataInitialization: return std::move(callbacks).forSingletonMetadata(description); } - swift_runtime_unreachable("bad metadata initialization kind"); + swift_unreachable("bad metadata initialization kind"); } auto &generics = description->getFullGenericContextHeader(); @@ -5280,7 +5369,7 @@ bool swift::addToMetadataQueue(MetadataCompletionQueueEntry *queueEntry, } bool forOtherMetadata(const Metadata *metadata) && { - swift_runtime_unreachable("metadata should always be complete"); + swift_unreachable("metadata should always be complete"); } } callbacks = { queueEntry, dependency }; @@ -5312,7 +5401,7 @@ void swift::resumeMetadataCompletion(MetadataCompletionQueueEntry *queueEntry) { } void forOtherMetadata(const Metadata *metadata) && { - swift_runtime_unreachable("metadata should always be complete"); + swift_unreachable("metadata should always be complete"); } } callbacks = { queueEntry }; @@ -5970,3 +6059,8 @@ inline void _Atomic_storage<::PoolRange, 16>::_Unlock() const noexcept { } } #endif + +// Autolink with libc++, for cases where libswiftCore is linked statically. +#if defined(__MACH__) +asm(".linker_option \"-lc++\"\n"); +#endif // defined(__MACH__) diff --git a/stdlib/public/runtime/MetadataCache.h b/stdlib/public/runtime/MetadataCache.h index d6f188f971e3c..72fc1d665924b 100644 --- a/stdlib/public/runtime/MetadataCache.h +++ b/stdlib/public/runtime/MetadataCache.h @@ -51,16 +51,17 @@ class MetadataAllocator : public llvm::AllocatorBase { } }; -template -class TaggedMetadataAllocator: public MetadataAllocator { +template +class TaggedMetadataAllocator : public MetadataAllocator { public: constexpr TaggedMetadataAllocator() : MetadataAllocator(StaticTag) {} }; -/// A typedef for simple global caches. +/// A typedef for simple global caches with stable addresses for the entries. template using SimpleGlobalCache = - ConcurrentMap>; + StableAddressConcurrentReadableHashMap>; template class StaticOwningPointer { @@ -366,7 +367,7 @@ class SimpleLockingCacheEntryBase { template Status beginInitialization(ConcurrencyControl &concurrency, ArgTys &&...args) { - swift_runtime_unreachable("beginAllocation always short-circuits"); + swift_unreachable("beginAllocation always short-circuits"); } }; @@ -592,7 +593,7 @@ inline bool satisfies(PrivateMetadataState state, MetadataState requirement) { case MetadataState::Complete: return state >= PrivateMetadataState::Complete; } - swift_runtime_unreachable("unsupported requirement kind"); + swift_unreachable("unsupported requirement kind"); } class PrivateMetadataTrackingInfo { @@ -642,7 +643,7 @@ class PrivateMetadataTrackingInfo { MetadataState getAccomplishedRequestState() const { switch (getState()) { case PrivateMetadataState::Allocating: - swift_runtime_unreachable("cannot call on allocating state"); + swift_unreachable("cannot call on allocating state"); case PrivateMetadataState::Abstract: return MetadataState::Abstract; case PrivateMetadataState::LayoutComplete: @@ -652,7 +653,7 @@ class PrivateMetadataTrackingInfo { case PrivateMetadataState::Complete: return MetadataState::Complete; } - swift_runtime_unreachable("bad state"); + swift_unreachable("bad state"); } bool satisfies(MetadataState requirement) { @@ -678,7 +679,7 @@ class PrivateMetadataTrackingInfo { // Otherwise, if it's a non-blocking request, we do not need to block. return (request.isBlocking() && !satisfies(request.getState())); } - swift_runtime_unreachable("bad state"); + swift_unreachable("bad state"); } constexpr RawType getRawValue() const { return Data; } @@ -1124,9 +1125,9 @@ class MetadataCacheEntryBase return; case LSK::Complete: - swift_runtime_unreachable("preparing to enqueue when already complete?"); + swift_unreachable("preparing to enqueue when already complete?"); } - swift_runtime_unreachable("bad kind"); + swift_unreachable("bad kind"); } /// Claim all the satisfied completion queue entries, given that @@ -1288,7 +1289,7 @@ class MetadataCacheEntryBase switch (LockedStorageKind) { case LSK::Complete: - swift_runtime_unreachable("enqueuing on complete cache entry?"); + swift_unreachable("enqueuing on complete cache entry?"); case LSK::AllocatingThread: LockedStorageKind = LSK::CompletionQueue; @@ -1340,7 +1341,7 @@ class MetadataCacheEntryBase // Check for an existing dependency. switch (LockedStorageKind) { case LSK::Complete: - swift_runtime_unreachable("dependency on complete cache entry?"); + swift_unreachable("dependency on complete cache entry?"); case LSK::AllocatingThread: case LSK::CompletionQueue: diff --git a/stdlib/public/runtime/MetadataLookup.cpp b/stdlib/public/runtime/MetadataLookup.cpp index fef3ff3f82e76..f271908ee1d50 100644 --- a/stdlib/public/runtime/MetadataLookup.cpp +++ b/stdlib/public/runtime/MetadataLookup.cpp @@ -88,10 +88,10 @@ static uintptr_t resolveSymbolicReferenceOffset(SymbolicReferenceKind kind, return (uintptr_t)contextPtr; } case SymbolicReferenceKind::AccessorFunctionReference: { - swift_runtime_unreachable("should not be indirectly referenced"); + swift_unreachable("should not be indirectly referenced"); } } - swift_runtime_unreachable("unknown symbolic reference kind"); + swift_unreachable("unknown symbolic reference kind"); } else { return ptr; } @@ -176,7 +176,7 @@ _buildDemanglingForSymbolicReference(SymbolicReferenceKind kind, (uintptr_t)resolvedReference); } - swift_runtime_unreachable("invalid symbolic reference kind"); + swift_unreachable("invalid symbolic reference kind"); } NodePointer @@ -1168,7 +1168,7 @@ findAssociatedTypeByName(const ProtocolDescriptor *protocol, StringRef name) { ++currentAssocTypeIdx; } - swift_runtime_unreachable("associated type names don't line up"); + swift_unreachable("associated type names don't line up"); } namespace { diff --git a/stdlib/public/runtime/Private.h b/stdlib/public/runtime/Private.h index 2f5e7232cc55d..b073de6997ac8 100644 --- a/stdlib/public/runtime/Private.h +++ b/stdlib/public/runtime/Private.h @@ -632,6 +632,13 @@ class TypeInfo { const Metadata *assocType, const ProtocolRequirement *reqBase, const ProtocolRequirement *assocConformance); + +#if SWIFT_OBJC_INTEROP + /// Returns a retained Quick Look representation object an Obj-C object. + SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERNAL + id _quickLookObjectForPointer(void *value); +#endif + } // end namespace swift #endif /* SWIFT_RUNTIME_PRIVATE_H */ diff --git a/stdlib/public/runtime/ProtocolConformance.cpp b/stdlib/public/runtime/ProtocolConformance.cpp index c434011faea85..da21c70bf6acf 100644 --- a/stdlib/public/runtime/ProtocolConformance.cpp +++ b/stdlib/public/runtime/ProtocolConformance.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 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -16,11 +16,12 @@ #include "swift/Basic/Lazy.h" #include "swift/Demangling/Demangle.h" +#include "swift/Runtime/BuiltinProtocolConformances.h" #include "swift/Runtime/Casting.h" #include "swift/Runtime/Concurrent.h" #include "swift/Runtime/HeapObject.h" #include "swift/Runtime/Metadata.h" -#include "swift/Runtime/Unreachable.h" +#include "swift/Basic/Unreachable.h" #include "CompatibilityOverride.h" #include "ImageInspection.h" #include "Private.h" @@ -83,11 +84,14 @@ template<> void ProtocolConformanceDescriptor::dump() const { case TypeReferenceKind::IndirectTypeDescriptor: printf("unique nominal type descriptor %s", symbolName(getTypeDescriptor())); break; + case TypeReferenceKind::MetadataKind: + printf("metadata kind %i", getMetadataKind()); + break; } printf(" => "); - printf("witness table %pattern s\n", symbolName(getWitnessTablePattern())); + printf("witness table pattern %p\n", symbolName(getWitnessTablePattern())); } #endif @@ -113,10 +117,11 @@ const ClassMetadata *TypeReference::getObjCClass(TypeReferenceKind kind) const { case TypeReferenceKind::DirectTypeDescriptor: case TypeReferenceKind::IndirectTypeDescriptor: + case TypeReferenceKind::MetadataKind: return nullptr; } - swift_runtime_unreachable("Unhandled TypeReferenceKind in switch."); + swift_unreachable("Unhandled TypeReferenceKind in switch."); } #endif @@ -153,9 +158,12 @@ ProtocolConformanceDescriptor::getCanonicalTypeMetadata() const { return nullptr; } + case TypeReferenceKind::MetadataKind: { + return nullptr; + } } - swift_runtime_unreachable("Unhandled TypeReferenceKind in switch."); + swift_unreachable("Unhandled TypeReferenceKind in switch."); } template<> @@ -368,6 +376,52 @@ searchInConformanceCache(const Metadata *type, return {false, nullptr}; } +extern "C" const ProtocolDescriptor EQUATABLE_DESCRIPTOR; +extern "C" const ProtocolDescriptor COMPARABLE_DESCRIPTOR; +extern "C" const ProtocolDescriptor HASHABLE_DESCRIPTOR; + +static bool tupleConformsToProtocol(const Metadata *type, + const ProtocolDescriptor *protocol) { + auto tuple = cast(type); + + // At the moment, tuples can only conform to Equatable, Comparable and + // Hashable, so reject all other protocols. + auto equatable = &EQUATABLE_DESCRIPTOR; + auto comparable = &COMPARABLE_DESCRIPTOR; + auto hashable = &HASHABLE_DESCRIPTOR; + if (protocol != equatable && protocol != comparable && protocol != hashable) + return false; + + for (size_t i = 0; i != tuple->NumElements; i += 1) { + auto elt = tuple->getElement(i); + if (!swift_conformsToProtocol(elt.Type, protocol)) + return false; + } + + return true; +} + +extern "C" const ProtocolConformanceDescriptor _swift_tupleEquatable_conf; +extern "C" const ProtocolConformanceDescriptor _swift_tupleComparable_conf; +extern "C" const ProtocolConformanceDescriptor _swift_tupleHashable_conf; + +static const ProtocolConformanceDescriptor *getTupleConformanceDescriptor( + const ProtocolDescriptor *protocol) { + if (protocol == &EQUATABLE_DESCRIPTOR) { + return &_swift_tupleEquatable_conf; + } + + if (protocol == &COMPARABLE_DESCRIPTOR) { + return &_swift_tupleComparable_conf; + } + + if (protocol == &HASHABLE_DESCRIPTOR) { + return &_swift_tupleHashable_conf; + } + + return nullptr; +} + namespace { /// Describes a protocol conformance "candidate" that can be checked /// against a type metadata. @@ -487,6 +541,16 @@ swift_conformsToProtocolImpl(const Metadata *const type, } } + // If we're asking if a tuple conforms to a protocol, handle that here for + // builtin conformances. + if (auto tuple = dyn_cast(type)) { + if (tupleConformsToProtocol(tuple, protocol)) { + auto descriptor = getTupleConformanceDescriptor(protocol); + C.cacheResult(type, protocol, descriptor->getWitnessTable(type), + /*always cache*/ 0); + } + } + // Try the search again to look for the most specific cached conformance. found = searchInConformanceCache(type, protocol); diff --git a/stdlib/public/runtime/ReflectionMirror.mm b/stdlib/public/runtime/ReflectionMirror.cpp similarity index 98% rename from stdlib/public/runtime/ReflectionMirror.mm rename to stdlib/public/runtime/ReflectionMirror.cpp index 9a7b41ee4bb21..ca7d6601c4cec 100644 --- a/stdlib/public/runtime/ReflectionMirror.mm +++ b/stdlib/public/runtime/ReflectionMirror.cpp @@ -17,7 +17,7 @@ #include "swift/Runtime/HeapObject.h" #include "swift/Runtime/Metadata.h" #include "swift/Runtime/Enum.h" -#include "swift/Runtime/Unreachable.h" +#include "swift/Basic/Unreachable.h" #include "swift/Demangling/Demangle.h" #include "swift/Runtime/Debug.h" #include "swift/Runtime/Portability.h" @@ -34,9 +34,6 @@ #if SWIFT_OBJC_INTEROP #include "swift/Runtime/ObjCBridge.h" #include "SwiftObject.h" -#include -#include -#include #endif #if defined(_WIN32) @@ -80,13 +77,6 @@ int asprintf(char **strp, const char *fmt, ...) { using namespace swift; -#if SWIFT_OBJC_INTEROP -// Declare the debugQuickLookObject selector. -@interface DeclareSelectors -- (id)debugQuickLookObject; -@end -#endif - namespace { class FieldType { @@ -751,16 +741,8 @@ AnyReturn subscript(intptr_t i, const char **outName, } #if SWIFT_OBJC_INTEROP - id quickLookObject() { - id object = [*reinterpret_cast(value) retain]; - if ([object respondsToSelector:@selector(debugQuickLookObject)]) { - id quickLookObject = [object debugQuickLookObject]; - [quickLookObject retain]; - [object release]; - return quickLookObject; - } - - return object; + id quickLookObject() override { + return _quickLookObjectForPointer(value); } #endif }; diff --git a/stdlib/public/runtime/ReflectionMirrorObjC.mm b/stdlib/public/runtime/ReflectionMirrorObjC.mm new file mode 100644 index 0000000000000..a9efbe107b35d --- /dev/null +++ b/stdlib/public/runtime/ReflectionMirrorObjC.mm @@ -0,0 +1,42 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "swift/Runtime/Config.h" + +#if SWIFT_OBJC_INTEROP + +#include "Private.h" +#include +#include +#include + +using namespace swift; + +// Declare the debugQuickLookObject selector. +@interface DeclareSelectors +- (id)debugQuickLookObject; +@end + +SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERNAL +id swift::_quickLookObjectForPointer(void *value) { + id object = [*reinterpret_cast(value) retain]; + if ([object respondsToSelector:@selector(debugQuickLookObject)]) { + id quickLookObject = [object debugQuickLookObject]; + [quickLookObject retain]; + [object release]; + return quickLookObject; + } + + return object; +} + +#endif diff --git a/stdlib/public/stubs/Stubs.cpp b/stdlib/public/stubs/Stubs.cpp index 15338277fb1d2..7d54fd20543ad 100644 --- a/stdlib/public/stubs/Stubs.cpp +++ b/stdlib/public/stubs/Stubs.cpp @@ -452,10 +452,6 @@ static const char *_swift_stdlib_strtoX_clocale_impl( _swift_set_errno(0); const auto result = posixImpl(nptr, &EndPtr, getCLocale()); *outResult = result; - if (result == huge || result == -huge || result == 0.0 || result == -0.0) { - if (errno == ERANGE) - EndPtr = nullptr; - } return EndPtr; } diff --git a/stdlib/toolchain/CMakeLists.txt b/stdlib/toolchain/CMakeLists.txt index 9cda30ee3e91b..fa5782ef212e8 100644 --- a/stdlib/toolchain/CMakeLists.txt +++ b/stdlib/toolchain/CMakeLists.txt @@ -53,4 +53,5 @@ set(COMPATIBILITY_MINIMUM_DEPLOYMENT_VERSION_WATCHOS "2.0") add_subdirectory(legacy_layouts) add_subdirectory(Compatibility50) add_subdirectory(Compatibility51) +add_subdirectory(Compatibility53) add_subdirectory(CompatibilityDynamicReplacements) diff --git a/stdlib/toolchain/Compatibility50/ProtocolConformance.cpp b/stdlib/toolchain/Compatibility50/ProtocolConformance.cpp index a48e801a5a43a..a0e7309cee65c 100644 --- a/stdlib/toolchain/Compatibility50/ProtocolConformance.cpp +++ b/stdlib/toolchain/Compatibility50/ProtocolConformance.cpp @@ -18,6 +18,7 @@ //===----------------------------------------------------------------------===// #include "Overrides.h" +#include "../Compatibility53/Overrides.h" #include "../../public/runtime/Private.h" #include "swift/Basic/Lazy.h" #include @@ -94,6 +95,13 @@ swift::swift50override_conformsToProtocol(const Metadata *type, static OnceToken_t token; SWIFT_ONCE_F(token, registerAddImageCallback, nullptr); + // The Swift 5.4 runtime added support for builtin conformances. Call 5.3's + // backported implementation to handle that here. + if (auto result = + swift53override_conformsToProtocol(type, protocol, + original_conformsToProtocol)) + return result; + // The implementation of swift_conformsToProtocol in Swift 5.0 would return // a false negative answer when asking whether a subclass conforms using // a conformance from a superclass. Work around this by walking up the diff --git a/stdlib/toolchain/Compatibility51/Overrides.cpp b/stdlib/toolchain/Compatibility51/Overrides.cpp index f9a89ec39e115..df6c3efb5d61d 100644 --- a/stdlib/toolchain/Compatibility51/Overrides.cpp +++ b/stdlib/toolchain/Compatibility51/Overrides.cpp @@ -16,6 +16,7 @@ #include "CompatibilityOverride.h" #include "Overrides.h" +#include "../Compatibility53/Overrides.h" #include #include @@ -33,6 +34,11 @@ struct OverrideSection { OverrideSection Swift51Overrides __attribute__((used, section("__DATA,__swift51_hooks"))) = { .version = 0, + // We use the same hook for conformsToProtocol as we do for a 5.3 + // runtime, so reference the override from the Compatibility53 library. + // If we're back deploying to Swift 5.1, we also have to support 5.3, so + // the Compatibility53 library is always linked when the 51 library is. + .conformsToProtocol = swift53override_conformsToProtocol, .conformsToSwiftProtocol = swift51override_conformsToSwiftProtocol, }; diff --git a/stdlib/toolchain/Compatibility51/ProtocolConformance.cpp b/stdlib/toolchain/Compatibility51/ProtocolConformance.cpp index 4f22fd2bfda2d..dd96167686be5 100644 --- a/stdlib/toolchain/Compatibility51/ProtocolConformance.cpp +++ b/stdlib/toolchain/Compatibility51/ProtocolConformance.cpp @@ -229,9 +229,12 @@ override_getCanonicalTypeMetadata(const ProtocolConformanceDescriptor *conf) { return nullptr; } + case TypeReferenceKind::MetadataKind: { + return nullptr; + } } - swift_runtime_unreachable("Unhandled TypeReferenceKind in switch."); + swift_unreachable("Unhandled TypeReferenceKind in switch."); } class ConformanceCandidate { diff --git a/stdlib/toolchain/Compatibility53/BuiltinProtocolConformances.cpp b/stdlib/toolchain/Compatibility53/BuiltinProtocolConformances.cpp new file mode 100644 index 0000000000000..759fe05f44ef0 --- /dev/null +++ b/stdlib/toolchain/Compatibility53/BuiltinProtocolConformances.cpp @@ -0,0 +1,592 @@ +//===--- BuiltinProtocolConformances.cpp ----------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Definitions of some builtin protocol witnesses. +// +//===----------------------------------------------------------------------===// + +#include "Overrides.h" +#include "swift/Basic/Lazy.h" +#include "swift/Runtime/BuiltinProtocolConformances.h" +#include "swift/Runtime/Casting.h" +#include +#include + +using namespace swift; + +static const ProtocolDescriptor *getEquatableDescriptor() { + auto descriptor = SWIFT_LAZY_CONSTANT( + reinterpret_cast( + dlsym(RTLD_DEFAULT, "$sSQMp"))); + return descriptor; +} + +static const ProtocolDescriptor *getComparableDescriptor() { + auto descriptor = SWIFT_LAZY_CONSTANT( + reinterpret_cast( + dlsym(RTLD_DEFAULT, "$sSLMp"))); + return descriptor; +} + +static const ProtocolDescriptor *getHashableDescriptor() { + auto descriptor = SWIFT_LAZY_CONSTANT( + reinterpret_cast( + dlsym(RTLD_DEFAULT, "$sSHMp"))); + return descriptor; +} + +static const WitnessTable *conformsToProtocol(const Metadata *type, + const ProtocolDescriptor *protocol) { + using Fn = const WitnessTable *(const Metadata *, const ProtocolDescriptor *); + auto func = SWIFT_LAZY_CONSTANT( + reinterpret_cast( + dlsym(RTLD_DEFAULT, "swift_conformsToProtocol"))); + return func(type, protocol); +} + +template +struct _WitnessTable { + const ProtocolConformanceDescriptor *Conformance; + const void *Witnesses[NumWitnesses]; +}; + +using StaticInfixWitness = SWIFT_CC(swift) bool(OpaqueValue *, OpaqueValue *, + SWIFT_CONTEXT const Metadata *, + const Metadata *, + const WitnessTable *); + +//===----------------------------------------------------------------------===// +// Tuple Equatable Conformance +//===----------------------------------------------------------------------===// + +#define TUPLE_EQUATABLE_WT SYMBOL("_swift_tupleEquatable_wt") + +// Define the conformance descriptor for tuple Equatable. We do this in +// assembly to work around relative reference issues. +__asm( + " .section __DATA,__data\n" + " .globl " TUPLE_EQUATABLE_CONF "\n" + " .p2align 2\n" + TUPLE_EQUATABLE_CONF ":\n" + // This is an indirectable relative reference to the Equatable protocol + // descriptor. However, this is 0 here because the compatibility libraries + // can't have a dependency on libswiftCore (which is where Equatable lives). + " .long 0\n" + // 769 is the MetadataKind::Tuple + " .long 769\n" + // This is a direct relative reference to the witness table defined below. + " .long (" TUPLE_EQUATABLE_WT ") - .\n" + // 32 are the ConformanceFlags with the type reference bit set to MetadataKind. + " .long 32\n" +); + +extern const ProtocolConformanceDescriptor _swift_tupleEquatable_conf; + +// Due to the fact that the compatibility libraries can't have a hard +// dependency to libswiftCore (which is where the Equatable protocol desciptor +// lives), we have to manually implant this before calling any user code. +__attribute__((constructor)) +void _emplaceTupleEquatableDescriptor() { + auto tupleEquatableConf = const_cast( + reinterpret_cast(&_swift_tupleEquatable_conf)); + auto equatable = getEquatableDescriptor(); + + // This is an indirectable pointer. + *tupleEquatableConf = intptr_t(equatable) - intptr_t(tupleEquatableConf); +} + +SWIFT_RUNTIME_EXPORT +const _WitnessTable<1> _swift_tupleEquatable_wt = { + &_swift_tupleEquatable_conf, + { + reinterpret_cast(_swift_tupleEquatable_equals) + } +}; + +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +bool swift::_swift_tupleEquatable_equals(OpaqueValue *tuple1, + OpaqueValue *tuple2, + SWIFT_CONTEXT Metadata *swiftSelf, + Metadata *Self, void *witnessTable) { + auto tuple = cast(Self); + + // Loop through all elements, and check if both tuples element is equal. + for (size_t i = 0; i != tuple->NumElements; i += 1) { + auto elt = tuple->getElement(i); + + // Ensure we actually have a conformance to Equatable for this element type. + auto equatable = getEquatableDescriptor(); + auto conformance = conformsToProtocol(elt.Type, equatable); + + // If we don't have a conformance then something somewhere messed up in + // deciding that this tuple type was Equatable...?? + if (!conformance) + swift_unreachable("Tuple equality requires that all elements be \ + Equatable."); + + // Get the respective values from both tuples. + auto value1 = reinterpret_cast( + reinterpret_cast(tuple1) + elt.Offset); + auto value2 = reinterpret_cast( + reinterpret_cast(tuple2) + elt.Offset); + + // Grab the specific witness for this element type. + auto equatableTable = reinterpret_cast(conformance); + auto equalsWitness = equatableTable[WitnessTableFirstRequirementOffset]; + auto equals = reinterpret_cast(equalsWitness); + + // Call the equal function. + auto result = equals(value1, value2, elt.Type, elt.Type, conformance); + + // If the values aren't equal, this tuple isn't equal. :) + if (!result) + return false; + } + + // Otherwise this tuple has value equality with all elements. + return true; +} + +//===----------------------------------------------------------------------===// +// Tuple Comparable Conformance +//===----------------------------------------------------------------------===// + +#define TUPLE_COMPARABLE_WT SYMBOL("_swift_tupleComparable_wt") + +// Define the conformance descriptor for tuple Comparable. We do this in +// assembly to work around relative reference issues. +__asm( + " .section __DATA,__data\n" + " .globl " TUPLE_COMPARABLE_CONF "\n" + " .p2align 2\n" + TUPLE_COMPARABLE_CONF ":\n" + // This is an indirectable relative reference to the Comparable protocol + // descriptor. However, this is 0 here because the compatibility libraries + // can't have a dependency on libswiftCore (which is where Comparable lives). + " .long 0\n" + // 769 is the MetadataKind::Tuple + " .long 769\n" + // This is a direct relative reference to the witness table defined below. + " .long (" TUPLE_COMPARABLE_WT ") - .\n" + // 32 are the ConformanceFlags with the type reference bit set to MetadataKind. + " .long 32\n" +); + +extern const ProtocolConformanceDescriptor _swift_tupleComparable_conf; + +// Due to the fact that the compatibility libraries can't have a hard +// dependency to libswiftCore (which is where the Comparable protocol desciptor +// lives), we have to manually implant this before calling any user code. +__attribute__((constructor)) +void _emplaceTupleComparableDescriptor() { + auto tupleComparableConf = const_cast( + reinterpret_cast(&_swift_tupleComparable_conf)); + auto comparable = getComparableDescriptor(); + + // This is an indirectable pointer. + *tupleComparableConf = intptr_t(comparable) - intptr_t(tupleComparableConf); +} + +// The base Equatable protocol is itself a requirement, thus the requirement +// count is 5 (Equatable + 4 operators) and the witness is the tuple Equatable +// table. +SWIFT_RUNTIME_EXPORT +const _WitnessTable<5> _swift_tupleComparable_wt = { + &_swift_tupleComparable_conf, + { + reinterpret_cast(&_swift_tupleEquatable_wt), + reinterpret_cast(_swift_tupleComparable_lessThan), + reinterpret_cast(_swift_tupleComparable_lessThanOrEqual), + reinterpret_cast(_swift_tupleComparable_greaterThanOrEqual), + reinterpret_cast(_swift_tupleComparable_greaterThan) + } +}; + +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +bool swift::_swift_tupleComparable_lessThan(OpaqueValue *tuple1, + OpaqueValue *tuple2, + SWIFT_CONTEXT Metadata *swiftSelf, + Metadata *Self, void *witnessTable) { + auto tuple = cast(Self); + + // Loop through all elements, and check if both tuples element is equal. + for (size_t i = 0; i != tuple->NumElements; i += 1) { + auto elt = tuple->getElement(i); + + // Ensure we actually have a conformance to Comparable for this element type. + auto comparable = getComparableDescriptor(); + auto conformance = conformsToProtocol(elt.Type, comparable); + + // If we don't have a conformance then something somewhere messed up in + // deciding that this tuple type was Comparable...?? + if (!conformance) + swift_unreachable("Tuple comparability requires that all elements \ + be Comparable."); + + // Get the respective values from both tuples. + auto value1 = reinterpret_cast( + reinterpret_cast(tuple1) + elt.Offset); + auto value2 = reinterpret_cast( + reinterpret_cast(tuple2) + elt.Offset); + + // First, grab the equatable conformance to prepare to check if the elements + // are equal. (Since this require Equatable conformance, the witness table + // is right after the conformance descriptor, which is at index 0.) + auto comparableTable = reinterpret_cast(conformance); + auto equatableTable = reinterpret_cast(comparableTable[1]); + auto equalsWitness = equatableTable[WitnessTableFirstRequirementOffset]; + auto equals = reinterpret_cast(equalsWitness); + + // Call the equal function. + auto isEqual = equals(value1, value2, elt.Type, elt.Type, + reinterpret_cast(equatableTable)); + + // If these are equal, skip to the next element. + if (isEqual) + continue; + + // Now that we know they are not equal, we can call their less than function + // and return the result. + auto lessThanWitness = comparableTable[1 + WitnessTableFirstRequirementOffset]; + auto lessThan = reinterpret_cast(lessThanWitness); + + // Call the less than function. + auto isLessThan = lessThan(value1, value2, elt.Type, elt.Type, conformance); + + return isLessThan; + } + + // Otherwise these tuples are completely equal, thus they are not less than + // each other. + return false; +} + +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +bool swift::_swift_tupleComparable_lessThanOrEqual(OpaqueValue *tuple1, + OpaqueValue *tuple2, + SWIFT_CONTEXT Metadata *swiftSelf, + Metadata *Self, + void *witnessTable) { + auto tuple = cast(Self); + + // Loop through all elements, and check if both tuples element is equal. + for (size_t i = 0; i != tuple->NumElements; i += 1) { + auto elt = tuple->getElement(i); + + // Ensure we actually have a conformance to Comparable for this element type. + auto comparable = getComparableDescriptor(); + auto conformance = conformsToProtocol(elt.Type, comparable); + + // If we don't have a conformance then something somewhere messed up in + // deciding that this tuple type was Comparable...?? + if (!conformance) + swift_unreachable("Tuple comparability requires that all elements \ + be Comparable."); + + // Get the respective values from both tuples. + auto value1 = reinterpret_cast( + reinterpret_cast(tuple1) + elt.Offset); + auto value2 = reinterpret_cast( + reinterpret_cast(tuple2) + elt.Offset); + + // First, grab the equatable conformance to prepare to check if the elements + // are equal. (Since this require Equatable conformance, the witness table + // is right after the conformance descriptor, which is at index 0.) + auto comparableTable = reinterpret_cast(conformance); + auto equatableTable = reinterpret_cast(comparableTable[1]); + auto equalsWitness = equatableTable[WitnessTableFirstRequirementOffset]; + auto equals = reinterpret_cast(equalsWitness); + + // Call the equal function. + auto isEqual = equals(value1, value2, elt.Type, elt.Type, + reinterpret_cast(equatableTable)); + + // If these are equal, skip to the next element. + if (isEqual) + continue; + + // Now that we know they are not equal, we can call their less than or equal + // function and return the result. + auto lessThanOrEqualWitness = + comparableTable[WitnessTableFirstRequirementOffset + 2]; + auto lessThanOrEqual = + reinterpret_cast(lessThanOrEqualWitness); + + // Call the less than function. + auto isLessThanOrEqual = lessThanOrEqual(value1, value2, elt.Type, elt.Type, + conformance); + + return isLessThanOrEqual; + } + + // Otherwise these tuples are completely equal. + return true; +} + +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +bool swift::_swift_tupleComparable_greaterThanOrEqual(OpaqueValue *tuple1, + OpaqueValue *tuple2, + SWIFT_CONTEXT Metadata *swiftSelf, + Metadata *Self, + void *witnessTable) { + auto tuple = cast(Self); + + // Loop through all elements, and check if both tuples element is equal. + for (size_t i = 0; i != tuple->NumElements; i += 1) { + auto elt = tuple->getElement(i); + + // Ensure we actually have a conformance to Comparable for this element type. + auto comparable = getComparableDescriptor(); + auto conformance = conformsToProtocol(elt.Type, comparable); + + // If we don't have a conformance then something somewhere messed up in + // deciding that this tuple type was Comparable...?? + if (!conformance) + swift_unreachable("Tuple comparability requires that all elements \ + be Comparable."); + + // Get the respective values from both tuples. + auto value1 = reinterpret_cast( + reinterpret_cast(tuple1) + elt.Offset); + auto value2 = reinterpret_cast( + reinterpret_cast(tuple2) + elt.Offset); + + // First, grab the equatable conformance to prepare to check if the elements + // are equal. (Since this require Equatable conformance, the witness table + // is right after the conformance descriptor, which is at index 0.) + auto comparableTable = reinterpret_cast(conformance); + auto equatableTable = reinterpret_cast(comparableTable[1]); + auto equalsWitness = equatableTable[WitnessTableFirstRequirementOffset]; + auto equals = reinterpret_cast(equalsWitness); + + // Call the equal function. + auto isEqual = equals(value1, value2, elt.Type, elt.Type, + reinterpret_cast(equatableTable)); + + // If these are equal, skip to the next element. + if (isEqual) + continue; + + // Now that we know they are not equal, we can call their greater than or + // equal function and return the result. + auto greaterThanOrEqualWitness = + comparableTable[WitnessTableFirstRequirementOffset + 3]; + auto greaterThanOrEqual = + reinterpret_cast(greaterThanOrEqualWitness); + + // Call the greater than or equal function. + auto isGreaterThanOrEqual = greaterThanOrEqual(value1, value2, elt.Type, + elt.Type, conformance); + + return isGreaterThanOrEqual; + } + + // Otherwise these tuples are completely equal. + return true; +} + +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +bool swift::_swift_tupleComparable_greaterThan(OpaqueValue *tuple1, + OpaqueValue *tuple2, + SWIFT_CONTEXT Metadata *swiftSelf, + Metadata *Self, + void *witnessTable) { + auto tuple = cast(Self); + + // Loop through all elements, and check if both tuples element is equal. + for (size_t i = 0; i != tuple->NumElements; i += 1) { + auto elt = tuple->getElement(i); + + // Ensure we actually have a conformance to Comparable for this element type. + auto comparable = getComparableDescriptor(); + auto conformance = conformsToProtocol(elt.Type, comparable); + + // If we don't have a conformance then something somewhere messed up in + // deciding that this tuple type was Comparable...?? + if (!conformance) + swift_unreachable("Tuple comparability requires that all elements \ + be Comparable."); + + // Get the respective values from both tuples. + auto value1 = reinterpret_cast( + reinterpret_cast(tuple1) + elt.Offset); + auto value2 = reinterpret_cast( + reinterpret_cast(tuple2) + elt.Offset); + + // First, grab the equatable conformance to prepare to check if the elements + // are equal. (Since this require Equatable conformance, the witness table + // is right after the conformance descriptor, which is at index 0.) + auto comparableTable = reinterpret_cast(conformance); + auto equatableTable = reinterpret_cast(comparableTable[1]); + auto equalsWitness = equatableTable[WitnessTableFirstRequirementOffset]; + auto equals = reinterpret_cast(equalsWitness); + + // Call the equal function. + auto isEqual = equals(value1, value2, elt.Type, elt.Type, + reinterpret_cast(equatableTable)); + + // If these are equal, skip to the next element. + if (isEqual) + continue; + + // Now that we know they are not equal, we can call their greater than + // function and return the result. + auto greaterThanWitness = + comparableTable[WitnessTableFirstRequirementOffset + 4]; + auto greaterThan = + reinterpret_cast(greaterThanWitness); + + // Call the greater than function. + auto isGreaterThan = greaterThan(value1, value2, elt.Type, elt.Type, + conformance); + + return isGreaterThan; + } + + // Otherwise these tuples are completely equal, thus they are not greater than + // each other. + return false; +} + +//===----------------------------------------------------------------------===// +// Tuple Hashable Conformance +//===----------------------------------------------------------------------===// + +#define TUPLE_HASHABLE_WT SYMBOL("_swift_tupleHashable_wt") + +// Define the conformance descriptor for tuple Hashable. We do this in +// assembly to work around relative reference issues. +__asm( + " .section __DATA,__data\n" + " .globl " TUPLE_HASHABLE_CONF "\n" + " .p2align 2\n" + TUPLE_HASHABLE_CONF ":\n" + // This is an indirectable relative reference to the Hashable protocol + // descriptor. However, this is 0 here because the compatibility libraries + // can't have a dependency on libswiftCore (which is where Hashable lives). + " .long 0\n" + // 769 is the MetadataKind::Tuple + " .long 769\n" + // This is a direct relative reference to the witness table defined below. + " .long (" TUPLE_HASHABLE_WT ") - .\n" + // 32 are the ConformanceFlags with the type reference bit set to MetadataKind. + " .long 32\n" +); + +extern const ProtocolConformanceDescriptor _swift_tupleHashable_conf; + +// Due to the fact that the compatibility libraries can't have a hard +// dependency to libswiftCore (which is where the Hashable protocol desciptor +// lives), we have to manually implant this before calling any user code. +__attribute__((constructor)) +void _emplaceTupleHashableDescriptor() { + auto tupleHashableConf = const_cast( + reinterpret_cast(&_swift_tupleHashable_conf)); + auto hashable = getHashableDescriptor(); + + // This is an indirectable pointer. + *tupleHashableConf = intptr_t(hashable) - intptr_t(tupleHashableConf); +} + +using RawHashValueFn = SWIFT_CC(swift) intptr_t(intptr_t seed, + const Metadata *Self, + const WitnessTable *witnessTable, + SWIFT_CONTEXT OpaqueValue *value); + +static RawHashValueFn *get_rawHashValueDefaultImplFunc() { + auto func = SWIFT_LAZY_CONSTANT( + reinterpret_cast( + dlsym(RTLD_DEFAULT, "$sSHsE13_rawHashValue4seedS2i_tF"))); + return func; +} + +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +intptr_t _swift_tupleHashable_rawHashValue(intptr_t seed, + SWIFT_CONTEXT OpaqueValue *tuple, + const Metadata *Self, + const WitnessTable *witnessTable) { + auto _rawHashValue = get_rawHashValueDefaultImplFunc(); + return _rawHashValue(seed, Self, witnessTable, tuple); +} + +// The base Equatable protocol is itself a requirement, thus the requirement +// count is 4 (Equatable + hashValue + hash(into:) + _rawHashValue) and the +// witness is the tuple Equatable table. +SWIFT_RUNTIME_EXPORT +_WitnessTable<4> _swift_tupleHashable_wt = { + &_swift_tupleHashable_conf, + { + reinterpret_cast(&_swift_tupleEquatable_wt), + reinterpret_cast(_swift_tupleHashable_hashValue), + reinterpret_cast(_swift_tupleHashable_hash), + reinterpret_cast(_swift_tupleHashable_rawHashValue) + } +}; + +using HashValueFn = SWIFT_CC(swift) intptr_t(OpaqueValue *value, Metadata *Self, + void *witnessTable); +using HasherCombineFn = SWIFT_CC(swift) void(OpaqueValue *value, + const Metadata *Self, + const WitnessTable *witnessTable, + SWIFT_CONTEXT OpaqueValue *hasher); + +static HashValueFn *get_hashValueFunc() { + auto func = SWIFT_LAZY_CONSTANT( + reinterpret_cast( + dlsym(RTLD_DEFAULT, XSTR(SWIFT_HASHVALUE_FUNC)))); + return func; +} + +static HasherCombineFn *getHashCombineFunc() { + auto func = SWIFT_LAZY_CONSTANT( + reinterpret_cast( + dlsym(RTLD_DEFAULT, XSTR(SWIFT_HASHER_COMBINE_FUNC)))); + return func; +} + +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +intptr_t swift::_swift_tupleHashable_hashValue(SWIFT_CONTEXT OpaqueValue *tuple, + Metadata *Self, void *witnessTable) { + auto _hashValue = get_hashValueFunc(); + return _hashValue(tuple, Self, witnessTable); +} + +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +void swift::_swift_tupleHashable_hash(OpaqueValue *hasher, + SWIFT_CONTEXT OpaqueValue *tuple, + Metadata *Self, void *witnessTable) { + auto tupleTy = cast(Self); + + // Loop through all elements and hash them into the Hasher. + for (size_t i = 0; i != tupleTy->NumElements; i += 1) { + auto elt = tupleTy->getElement(i); + + // Ensure we actually have a conformance to Hashable for this element type. + auto hashable = getHashableDescriptor(); + auto conformance = conformsToProtocol(elt.Type, hashable); + + // If we don't have a conformance then something somewhere messed up in + // deciding that this tuple type was Hashable...?? + if (!conformance) + swift_unreachable("Tuple hasing requires that all elements be Hashable."); + + // Get the element value from the tuple. + auto value = reinterpret_cast( + reinterpret_cast(tuple) + elt.Offset); + + auto hasherCombine = getHashCombineFunc(); + + // Call the combine function on the hasher for this element value and we're + // done! + hasherCombine(value, elt.Type, conformance, hasher); + } +} diff --git a/stdlib/toolchain/Compatibility53/CMakeLists.txt b/stdlib/toolchain/Compatibility53/CMakeLists.txt new file mode 100644 index 0000000000000..61da832cf6d46 --- /dev/null +++ b/stdlib/toolchain/Compatibility53/CMakeLists.txt @@ -0,0 +1,34 @@ +set(library_name "swiftCompatibility53") + +add_swift_target_library("${library_name}" STATIC + BuiltinProtocolConformances.cpp + Overrides.cpp + ProtocolConformance.cpp + + TARGET_SDKS ${SWIFT_APPLE_PLATFORMS} + + C_COMPILE_FLAGS ${CXX_COMPILE_FLAGS} + LINK_FLAGS ${CXX_LINK_FLAGS} + SWIFT_COMPILE_FLAGS ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} + DEPLOYMENT_VERSION_OSX ${COMPATIBILITY_MINIMUM_DEPLOYMENT_VERSION_OSX} + DEPLOYMENT_VERSION_IOS ${COMPATIBILITY_MINIMUM_DEPLOYMENT_VERSION_IOS} + DEPLOYMENT_VERSION_TVOS ${COMPATIBILITY_MINIMUM_DEPLOYMENT_VERSION_TVOS} + DEPLOYMENT_VERSION_WATCHOS ${COMPATIBILITY_MINIMUM_DEPLOYMENT_VERSION_WATCHOS} + + INSTALL_IN_COMPONENT compiler + INSTALL_WITH_SHARED) + + +# FIXME: We need a more flexible mechanism to add lipo targets generated by +# add_swift_target_library to the ALL target. Until then this hack is necessary +# to ensure these libraries build. +foreach(sdk ${SWIFT_SDKS}) + set(target_name "${library_name}-${SWIFT_SDK_${sdk}_LIB_SUBDIR}") + if(NOT TARGET "${target_name}") + continue() + endif() + + set_target_properties("${target_name}" + PROPERTIES + EXCLUDE_FROM_ALL FALSE) +endforeach() diff --git a/stdlib/toolchain/Compatibility53/CompatibilityOverride.def b/stdlib/toolchain/Compatibility53/CompatibilityOverride.def new file mode 100644 index 0000000000000..a7a34a8852100 --- /dev/null +++ b/stdlib/toolchain/Compatibility53/CompatibilityOverride.def @@ -0,0 +1,226 @@ +//===--- CompatibilityOverrides.def - Compatibility Overrides Database -*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file defines x-macros used for metaprogramming with the set of +// compatibility override functions. +// +//===----------------------------------------------------------------------===// + +/// #define OVERRIDE(name, ret, attrs, namespace, typedArgs, namedArgs) +/// Provides information about an overridable function. +/// - name is the name of the function, without any leading swift_ or +/// namespace. +/// - ret is the return type of the function. +/// - attrs is the attributes, if any, applied to the function definition. +/// - namespace is the namespace, if any, the function is in, including a +/// trailing :: +/// - typedArgs is the argument list, including types, surrounded by +/// parentheses +/// - namedArgs is the list of argument names, with no types, surrounded by +/// parentheses +/// +/// The entries are organized by group. A user may define OVERRIDE to get all +/// entries, or define one or more of OVERRIDE_METADATALOOKUP, OVERRIDE_CASTING, +/// OVERRIDE_OBJC, OVERRIDE_FOREIGN, OVERRIDE_PROTOCOLCONFORMANCE, +/// and OVERRIDE_KEYPATH to get only those entries. + +// NOTE: this file is used to build the definition of OverrideSection in +// CompatibilityOverride.cpp, which is part of the ABI. Do not move or remove entries +// in this file after ABI stability. Additional entries can be added to the end. + +#ifdef OVERRIDE +# define OVERRIDE_METADATALOOKUP OVERRIDE +# define OVERRIDE_CASTING OVERRIDE +# define OVERRIDE_OBJC OVERRIDE +# define OVERRIDE_FOREIGN OVERRIDE +# define OVERRIDE_PROTOCOLCONFORMANCE OVERRIDE +# define OVERRIDE_KEYPATH OVERRIDE +# define OVERRIDE_WITNESSTABLE OVERRIDE +#else +# ifndef OVERRIDE_METADATALOOKUP +# define OVERRIDE_METADATALOOKUP(...) +# endif +# ifndef OVERRIDE_CASTING +# define OVERRIDE_CASTING(...) +# endif +# ifndef OVERRIDE_OBJC +# define OVERRIDE_OBJC(...) +# endif +# ifndef OVERRIDE_FOREIGN +# define OVERRIDE_FOREIGN(...) +# endif +# ifndef OVERRIDE_PROTOCOLCONFORMANCE +# define OVERRIDE_PROTOCOLCONFORMANCE(...) +# endif +# ifndef OVERRIDE_KEYPATH +# define OVERRIDE_KEYPATH(...) +# endif +# ifndef OVERRIDE_WITNESSTABLE +# define OVERRIDE_WITNESSTABLE(...) +# endif +#endif + +OVERRIDE_CASTING(dynamicCast, bool, , , swift::, + (OpaqueValue *dest, OpaqueValue *src, + const Metadata *srcType, + const Metadata *targetType, + DynamicCastFlags flags), + (dest, src, srcType, targetType, flags)) + + +OVERRIDE_CASTING(dynamicCastClass, const void *, , , swift::, + (const void *object, + const ClassMetadata *targetType), + (object, targetType)) + + +OVERRIDE_CASTING(dynamicCastClassUnconditional, const void *, , , swift::, + (const void *object, + const ClassMetadata *targetType, + const char *file, unsigned line, unsigned column), + (object, targetType, file, line, column)) + + + +OVERRIDE_CASTING(dynamicCastUnknownClass, const void *, , , swift::, + (const void *object, const Metadata *targetType), + (object, targetType)) + + +OVERRIDE_CASTING(dynamicCastUnknownClassUnconditional, const void *, , , swift::, + (const void *object, const Metadata *targetType, + const char *file, unsigned line, unsigned column), + (object, targetType, file, line, column)) + + +OVERRIDE_CASTING(dynamicCastMetatype, const Metadata *, , , swift::, + (const Metadata *sourceType, + const Metadata *targetType), + (sourceType, targetType)) + + +OVERRIDE_CASTING(dynamicCastMetatypeUnconditional, const Metadata *, , , swift::, + (const Metadata *sourceType, + const Metadata *targetType, + const char *file, unsigned line, unsigned column), + (sourceType, targetType, file, line, column)) + + +OVERRIDE_FOREIGN(dynamicCastForeignClassMetatype, const ClassMetadata *, , , swift::, + (const ClassMetadata *sourceType, + const ClassMetadata *targetType), + (sourceType, targetType)) + + +OVERRIDE_FOREIGN(dynamicCastForeignClassMetatypeUnconditional, + const ClassMetadata *, , , swift::, + (const ClassMetadata *sourceType, + const ClassMetadata *targetType, + const char *file, unsigned line, unsigned column), + (sourceType, targetType, file, line, column)) + + +OVERRIDE_PROTOCOLCONFORMANCE(conformsToProtocol, const WitnessTable *, , , swift::, + (const Metadata * const type, + const ProtocolDescriptor *protocol), + (type, protocol)) + +OVERRIDE_PROTOCOLCONFORMANCE(conformsToSwiftProtocol, + const ProtocolConformanceDescriptor *, , , swift::, + (const Metadata * const type, + const ProtocolDescriptor *protocol, + StringRef moduleName), + (type, protocol, moduleName)) + +OVERRIDE_KEYPATH(getKeyPath, const HeapObject *, , , swift::, + (const void *pattern, const void *arguments), + (pattern, arguments)) + +OVERRIDE_METADATALOOKUP(getTypeByMangledNode, TypeInfo, , SWIFT_CC(swift), swift::, + (MetadataRequest request, + Demangler &demangler, + Demangle::NodePointer node, + const void * const *arguments, + SubstGenericParameterFn substGenericParam, + SubstDependentWitnessTableFn substWitnessTable), + (request, demangler, node, arguments, substGenericParam, substWitnessTable)) +OVERRIDE_METADATALOOKUP(getTypeByMangledName, TypeInfo, , SWIFT_CC(swift), swift::, + (MetadataRequest request, + StringRef typeName, + const void * const *arguments, + SubstGenericParameterFn substGenericParam, + SubstDependentWitnessTableFn substWitnessTable), + (request, typeName, arguments, substGenericParam, substWitnessTable)) + +OVERRIDE_WITNESSTABLE(getAssociatedTypeWitnessSlow, MetadataResponse, + SWIFT_RUNTIME_STDLIB_INTERNAL, SWIFT_CC(swift), swift::, + (MetadataRequest request, WitnessTable *wtable, + const Metadata *conformingType, + const ProtocolRequirement *reqBase, + const ProtocolRequirement *assocType), + (request, wtable, conformingType, reqBase, assocType)) + +OVERRIDE_WITNESSTABLE(getAssociatedConformanceWitnessSlow, const WitnessTable *, + SWIFT_RUNTIME_STDLIB_INTERNAL, SWIFT_CC(swift), swift::, + (WitnessTable *wtable, const Metadata *conformingType, + const Metadata *assocType, + const ProtocolRequirement *reqBase, + const ProtocolRequirement *assocConformance), + (wtable, conformingType, assocType, reqBase, + assocConformance)) +#if SWIFT_OBJC_INTEROP + +OVERRIDE_OBJC(dynamicCastObjCClass, const void *, , , swift::, + (const void *object, + const ClassMetadata *targetType), + (object, targetType)) + + +OVERRIDE_OBJC(dynamicCastObjCClassUnconditional, const void *, , , swift::, + (const void *object, + const ClassMetadata *targetType, + const char *file, unsigned line, unsigned column), + (object, targetType, file, line, column)) + +OVERRIDE_OBJC(dynamicCastObjCClassMetatype, const ClassMetadata *, , , swift::, + (const ClassMetadata *sourceType, + const ClassMetadata *targetType), + (sourceType, targetType)) + + +OVERRIDE_OBJC(dynamicCastObjCClassMetatypeUnconditional, const ClassMetadata *, , , swift::, + (const ClassMetadata *sourceType, const ClassMetadata *targetType, + const char *file, unsigned line, unsigned column), + (sourceType, targetType, file, line, column)) + + +OVERRIDE_FOREIGN(dynamicCastForeignClass, const void *, , , swift::, + (const void *object, + const ForeignClassMetadata *targetType), + (object, targetType)) + + +OVERRIDE_FOREIGN(dynamicCastForeignClassUnconditional, const void *, , , swift::, + (const void *object, const ForeignClassMetadata *targetType, + const char *file, unsigned line, unsigned column), + (object, targetType, file, line, column)) + +#endif + +#undef OVERRIDE +#undef OVERRIDE_METADATALOOKUP +#undef OVERRIDE_CASTING +#undef OVERRIDE_OBJC +#undef OVERRIDE_FOREIGN +#undef OVERRIDE_PROTOCOLCONFORMANCE +#undef OVERRIDE_KEYPATH +#undef OVERRIDE_WITNESSTABLE diff --git a/stdlib/toolchain/Compatibility53/CompatibilityOverride.h b/stdlib/toolchain/Compatibility53/CompatibilityOverride.h new file mode 100644 index 0000000000000..4c7c5b3c47d6b --- /dev/null +++ b/stdlib/toolchain/Compatibility53/CompatibilityOverride.h @@ -0,0 +1,61 @@ +//===--- CompatibiltyOverride.h - Back-deploying compatibility fixes --*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Support back-deploying compatibility fixes for newer apps running on older runtimes. +// +//===----------------------------------------------------------------------===// + +#ifndef COMPATIBILITY_OVERRIDE_H +#define COMPATIBILITY_OVERRIDE_H + +#include "../../public/runtime/Private.h" +#include "swift/Runtime/Metadata.h" +#include "swift/Runtime/Once.h" +#include + +namespace swift { + +#define COMPATIBILITY_UNPAREN(...) __VA_ARGS__ + +#define OVERRIDE(name, ret, attrs, ccAttrs, namespace, typedArgs, namedArgs) \ + ccAttrs typedef ret (*Original_ ## name) typedArgs; +#include "CompatibilityOverride.def" + +#define OVERRIDE(name, ret, attrs, ccAttrs, namespace, typedArgs, namedArgs) \ + ccAttrs typedef ret (*Override_ ## name)(COMPATIBILITY_UNPAREN typedArgs, \ + Original_ ## name originalImpl); +#include "CompatibilityOverride.def" + +#define OVERRIDE(name, ret, attrs, ccAttrs, namespace, typedArgs, namedArgs) \ + Override_ ## name getOverride_ ## name(); +#include "CompatibilityOverride.def" + + +/// Used to define an override point. The override point #defines the appropriate +/// OVERRIDE macro from CompatibilityOverride.def to this macro, then includes +/// the file to generate the override points. The original implementation of the +/// functionality must be available as swift_funcNameHereImpl. +#define COMPATIBILITY_OVERRIDE(name, ret, attrs, ccAttrs, namespace, typedArgs, namedArgs) \ + attrs ccAttrs ret namespace swift_ ## name typedArgs { \ + static Override_ ## name Override; \ + static swift_once_t Predicate; \ + swift_once(&Predicate, [](void *) { \ + Override = getOverride_ ## name(); \ + }, nullptr); \ + if (Override != nullptr) \ + return Override(COMPATIBILITY_UNPAREN namedArgs, swift_ ## name ## Impl); \ + return swift_ ## name ## Impl namedArgs; \ + } + +} /* end namespace swift */ + +#endif /* COMPATIBILITY_OVERRIDE_H */ diff --git a/stdlib/toolchain/Compatibility53/Overrides.cpp b/stdlib/toolchain/Compatibility53/Overrides.cpp new file mode 100644 index 0000000000000..b88014d009df6 --- /dev/null +++ b/stdlib/toolchain/Compatibility53/Overrides.cpp @@ -0,0 +1,38 @@ +//===--- Overrides.cpp - Compat override table for Swift 5.3 runtime ------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file provides compatibility override hooks for Swift 5.3 runtimes. +// +//===----------------------------------------------------------------------===// + +#include "CompatibilityOverride.h" +#include "Overrides.h" + +using namespace swift; + +struct OverrideSection { + uintptr_t version; +#define OVERRIDE(name, ret, attrs, ccAttrs, namespace, typedArgs, namedArgs) \ + Override_ ## name name; +#include "../../public/runtime/CompatibilityOverride.def" +}; + +OverrideSection Swift53Overrides +__attribute__((used, section("__DATA,__swift53_hooks"))) = { + .version = 0, + .conformsToProtocol = swift53override_conformsToProtocol, +}; + +// Allow this library to get force-loaded by autolinking +__attribute__((weak, visibility("hidden"))) +extern "C" +char _swift_FORCE_LOAD_$_swiftCompatibility53 = 0; diff --git a/stdlib/toolchain/Compatibility53/Overrides.h b/stdlib/toolchain/Compatibility53/Overrides.h new file mode 100644 index 0000000000000..baba40f72e7b6 --- /dev/null +++ b/stdlib/toolchain/Compatibility53/Overrides.h @@ -0,0 +1,29 @@ +//===--- Overrides.cpp - Compat overrides for Swift 5.3 runtime ----s------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file provides compatibility override hooks for Swift 5.3 runtimes. +// +//===----------------------------------------------------------------------===// + +#include "swift/Runtime/Metadata.h" + +namespace swift { + +using ConformsToProtocol_t = + const WitnessTable *(const Metadata *, const ProtocolDescriptor *); + +const WitnessTable * +swift53override_conformsToProtocol(const Metadata * const type, + const ProtocolDescriptor *protocol, + ConformsToProtocol_t *original); + +} // end namespace swift diff --git a/stdlib/toolchain/Compatibility53/ProtocolConformance.cpp b/stdlib/toolchain/Compatibility53/ProtocolConformance.cpp new file mode 100644 index 0000000000000..71b90ab19152a --- /dev/null +++ b/stdlib/toolchain/Compatibility53/ProtocolConformance.cpp @@ -0,0 +1,115 @@ +//===--- ProtocolConformance.cpp - Swift protocol conformance checking ----===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Checking of Swift protocol conformances. +// +// This implementation is intended to be backward-deployed into Swift 5.3 and +// later runtimes. +// +//===----------------------------------------------------------------------===// + +#include "Overrides.h" +#include "swift/Basic/Lazy.h" +#include "swift/Runtime/BuiltinProtocolConformances.h" +#include +#include + +using namespace swift; + +static const ProtocolDescriptor *getEquatableDescriptor() { + auto descriptor = SWIFT_LAZY_CONSTANT( + reinterpret_cast( + dlsym(RTLD_DEFAULT, "$sSQMp"))); + return descriptor; +} + +static const ProtocolDescriptor *getComparableDescriptor() { + auto descriptor = SWIFT_LAZY_CONSTANT( + reinterpret_cast( + dlsym(RTLD_DEFAULT, "$sSLMp"))); + return descriptor; +} + +static const ProtocolDescriptor *getHashableDescriptor() { + auto descriptor = SWIFT_LAZY_CONSTANT( + reinterpret_cast( + dlsym(RTLD_DEFAULT, "$sSHMp"))); + return descriptor; +} + +static const WitnessTable *conformsToProtocol(const Metadata *type, + const ProtocolDescriptor *protocol) { + using Fn = const WitnessTable *(const Metadata *, const ProtocolDescriptor *); + auto func = SWIFT_LAZY_CONSTANT( + reinterpret_cast( + dlsym(RTLD_DEFAULT, "swift_conformsToProtocol"))); + return func(type, protocol); +} + +static bool tupleConformsToProtocol(const Metadata *type, + const ProtocolDescriptor *protocol) { + auto tuple = cast(type); + + // At the moment, tuples can only conform to Equatable, Comparable, and + // Hashable, so reject all other protocols. + if (protocol != getEquatableDescriptor() && + protocol != getComparableDescriptor() && + protocol != getHashableDescriptor()) + return false; + + for (size_t i = 0; i != tuple->NumElements; i += 1) { + auto elt = tuple->getElement(i); + if (!conformsToProtocol(elt.Type, protocol)) + return false; + } + + return true; +} + +extern const WitnessTable _swift_tupleEquatable_wt; +extern const WitnessTable _swift_tupleComparable_wt; +extern const WitnessTable _swift_tupleHashable_wt; + +static const WitnessTable *getTupleConformanceWitnessTable( + const ProtocolDescriptor *protocol) { + if (protocol == getEquatableDescriptor()) + return &_swift_tupleEquatable_wt; + + if (protocol == getComparableDescriptor()) + return &_swift_tupleComparable_wt; + + if (protocol == getHashableDescriptor()) + return &_swift_tupleHashable_wt; + + return nullptr; +} + +const WitnessTable * +swift::swift53override_conformsToProtocol(const Metadata *type, + const ProtocolDescriptor *protocol, + ConformsToProtocol_t *original_conformsToProtocol) +{ + // Swift 5.4 introduces tuple Equatable conformance, so ensure that Swift 5.3 + // and later runtimes can handle this as well. + if (auto tuple = dyn_cast(type)) { + if (!tupleConformsToProtocol(type, protocol)) + return nullptr; + + return getTupleConformanceWitnessTable(protocol); + } + + auto result = original_conformsToProtocol(type, protocol); + if (result) + return result; + + return nullptr; +} diff --git a/stdlib/tools/CMakeLists.txt b/stdlib/tools/CMakeLists.txt index 471c392bb005e..62b1fc331842c 100644 --- a/stdlib/tools/CMakeLists.txt +++ b/stdlib/tools/CMakeLists.txt @@ -1,3 +1,5 @@ +# Keep in sync with test/CMakeLists.txt: swift-reflection-test is +# only used when testing dynamic stdlib. if(SWIFT_BUILD_DYNAMIC_STDLIB AND SWIFT_INCLUDE_TESTS) add_subdirectory(swift-reflection-test) endif() diff --git a/test/APINotes/Inputs/custom-frameworks/APINotesFrameworkTest.framework/Headers/APINotesFrameworkTest.h b/test/APINotes/Inputs/custom-frameworks/APINotesFrameworkTest.framework/Headers/APINotesFrameworkTest.h index 401497b379f70..72a1265d33cca 100644 --- a/test/APINotes/Inputs/custom-frameworks/APINotesFrameworkTest.framework/Headers/APINotesFrameworkTest.h +++ b/test/APINotes/Inputs/custom-frameworks/APINotesFrameworkTest.framework/Headers/APINotesFrameworkTest.h @@ -20,6 +20,7 @@ __attribute__((objc_root_class)) __attribute__((objc_root_class)) @interface Base +-(nonnull instancetype)init; @end @interface B : A diff --git a/test/APINotes/blocks.swift b/test/APINotes/blocks.swift index dd81a47e3b0c0..aeda4f40647ad 100644 --- a/test/APINotes/blocks.swift +++ b/test/APINotes/blocks.swift @@ -1,5 +1,4 @@ // RUN: %target-build-swift -Xfrontend %clang-importer-sdk %s -emit-ir -// REQUIRES: executable_test // REQUIRES: objc_interop diff --git a/test/APINotes/versioned.swift b/test/APINotes/versioned.swift index 96b3be37c26f1..6740bbd3e665c 100644 --- a/test/APINotes/versioned.swift +++ b/test/APINotes/versioned.swift @@ -107,6 +107,7 @@ func testRenamedTopLevelDiags() { // CHECK-DIAGS-4-NOT: versioned.swift:[[@LINE-1]]: _ = s.value // CHECK-DIAGS-4-NOT: versioned.swift:[[@LINE-1]]: + _ = t } func testAKA(structValue: ImportantCStruct, aliasValue: ImportantCAlias) { diff --git a/test/AutoDiff/SILGen/autodiff_builtins.swift b/test/AutoDiff/SILGen/autodiff_builtins.swift index 1c2e703ddf834..f8f521c9d342f 100644 --- a/test/AutoDiff/SILGen/autodiff_builtins.swift +++ b/test/AutoDiff/SILGen/autodiff_builtins.swift @@ -98,14 +98,15 @@ func applyTranspose_f_direct_arity1(_ x: Float) -> Float { // CHECK: [[NOESC_LINEAR:%.*]] = convert_escape_to_noescape [not_guaranteed] [[LINEAR]] : $@differentiable(linear) @callee_guaranteed (Float) -> Float to $@differentiable(linear) @noescape @callee_guaranteed (Float) -> Float // CHECK: [[TRANS:%.*]] = linear_function_extract [transpose] [[NOESC_LINEAR]] : $@differentiable(linear) @noescape @callee_guaranteed (Float) -> Float // CHECK: [[RESULT:%.*]] = apply [[TRANS]]([[X]]) : $@noescape @callee_guaranteed (Float) -> Float -// CHECK: destroy_value [[LINEAR]] : $@differentiable(linear) @callee_guaranteed (Float) -> Float // CHECK: return [[RESULT]] : $Float +// CHECK: } // end sil function 'applyTranspose_f_direct_arity1' @_silgen_name("applyTranspose_f_direct_arity2") func applyTranspose_f_direct_arity2(_ x: Float) -> (Float, Float) { return Builtin.applyTranspose_arity2(f_direct_arity2, x) } -// CHECK-LABEL: sil{{.*}}@applyTranspose_f_direct_arity2 + +// CHECK-LABEL: sil{{.*}}@applyTranspose_f_direct_arity2 : // CHECK: bb0([[X:%.*]] : $Float) // CHECK: [[ORIG:%.*]] = function_ref @f_direct_arity2 : $@convention(thin) (Float, Float) -> Float // CHECK: [[THICK_ORIG:%.*]] = thin_to_thick_function [[ORIG]] : $@convention(thin) (Float, Float) -> Float to $@callee_guaranteed (Float, Float) -> Float @@ -114,9 +115,9 @@ func applyTranspose_f_direct_arity2(_ x: Float) -> (Float, Float) { // CHECK: [[TRANS:%.*]] = linear_function_extract [transpose] [[NOESC_LINEAR]] : $@differentiable(linear) @noescape @callee_guaranteed (Float, Float) -> Float // CHECK: [[RESULT:%.*]] = apply [[TRANS]]([[X]]) : $@noescape @callee_guaranteed (Float) -> (Float, Float) // CHECK: ([[RES1:%.*]], [[RES2:%.*]]) = destructure_tuple [[RESULT]] : $(Float, Float) -// CHECK: destroy_value [[LINEAR]] : $@differentiable(linear) @callee_guaranteed (Float, Float) -> Float // CHECK: [[RESULT:%.*]] = tuple ([[RES1]] : $Float, [[RES2]] : $Float) // CHECK: return [[RESULT]] : $(Float, Float) +// CHECK: } // end sil function 'applyTranspose_f_direct_arity2' @_silgen_name("applyTranspose_f_indirect_arity1") func applyTranspose_f_indirect_arity1(_ x: T) -> T { diff --git a/test/AutoDiff/SILGen/reabstraction.swift b/test/AutoDiff/SILGen/reabstraction.swift index c3accb2612740..661c9034a1f17 100644 --- a/test/AutoDiff/SILGen/reabstraction.swift +++ b/test/AutoDiff/SILGen/reabstraction.swift @@ -21,18 +21,17 @@ func makeSignatureAbstract() { // CHECK-LABEL: sil{{.*}}@makeSignatureAbstract // CHECK: [[BEFORE:%.*]] = differentiable_function [parameters 0] [results 0] -// CHECK: [[BEFORE_BORROWED:%.*]] = begin_borrow [[BEFORE]] -// CHECK: [[ORIG_0:%.*]] = differentiable_function_extract [original] [[BEFORE_BORROWED]] +// CHECK: [[ORIG_0:%.*]] = differentiable_function_extract [original] [[BEFORE]] // CHECK: [[ORIG_1:%.*]] = copy_value [[ORIG_0]] // CHECK: [[ORIG_THUNK:%.*]] = function_ref {{.*}} : $@convention(thin) (@in_guaranteed Float, @guaranteed @callee_guaranteed (Float) -> Float) -> @out Float // CHECK: [[ORIG_2:%.*]] = partial_apply [callee_guaranteed] [[ORIG_THUNK]]([[ORIG_1]]) // CHECK: [[ORIG_3:%.*]] = convert_function [[ORIG_2]] -// CHECK: [[JVP_0:%.*]] = differentiable_function_extract [jvp] [[BEFORE_BORROWED]] +// CHECK: [[JVP_0:%.*]] = differentiable_function_extract [jvp] [[BEFORE]] // CHECK: [[JVP_1:%.*]] = copy_value [[JVP_0]] // CHECK: [[JVP_THUNK:%.*]] = function_ref {{.*}} : $@convention(thin) (@in_guaranteed Float, @guaranteed @callee_guaranteed (Float) -> (Float, @owned @callee_guaranteed (Float) -> Float)) -> (@out Float, @owned @callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0) -> @out τ_0_1 for ) // CHECK: [[JVP_2:%.*]] = partial_apply [callee_guaranteed] [[JVP_THUNK]]([[JVP_1]]) // CHECK: [[JVP_3:%.*]] = convert_function [[JVP_2]] -// CHECK: [[VJP_0:%.*]] = differentiable_function_extract [vjp] [[BEFORE_BORROWED]] +// CHECK: [[VJP_0:%.*]] = differentiable_function_extract [vjp] [[BEFORE]] // CHECK: [[VJP_1:%.*]] = copy_value [[VJP_0]] // CHECK: [[VJP_THUNK:%.*]] = function_ref {{.*}} : $@convention(thin) (@in_guaranteed Float, @guaranteed @callee_guaranteed (Float) -> (Float, @owned @callee_guaranteed (Float) -> Float)) -> (@out Float, @owned @callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0) -> @out τ_0_1 for ) // CHECK: [[VJP_2:%.*]] = partial_apply [callee_guaranteed] [[VJP_THUNK]]([[VJP_1]]) diff --git a/test/AutoDiff/stdlib/tgmath_derivatives.swift.gyb b/test/AutoDiff/stdlib/tgmath_derivatives.swift.gyb index 6a7573f51e4a4..b0699fdb6721e 100644 --- a/test/AutoDiff/stdlib/tgmath_derivatives.swift.gyb +++ b/test/AutoDiff/stdlib/tgmath_derivatives.swift.gyb @@ -6,7 +6,7 @@ #elseif canImport(Glibc) import Glibc #elseif os(Windows) - import MSVCRT + import CRT #else #error("Unsupported platform") #endif diff --git a/test/AutoDiff/validation-test/custom_derivatives.swift b/test/AutoDiff/validation-test/custom_derivatives.swift index bd3fda6f560a7..bb32e01a66ea6 100644 --- a/test/AutoDiff/validation-test/custom_derivatives.swift +++ b/test/AutoDiff/validation-test/custom_derivatives.swift @@ -7,7 +7,7 @@ import StdlibUnittest #elseif canImport(Glibc) import Glibc #elseif os(Windows) - import ucrt + import CRT #else #error("Unsupported platform") #endif diff --git a/test/AutoDiff/validation-test/separate_tangent_type.swift b/test/AutoDiff/validation-test/separate_tangent_type.swift index a39a3d39c14a6..0d38d0ca753f4 100644 --- a/test/AutoDiff/validation-test/separate_tangent_type.swift +++ b/test/AutoDiff/validation-test/separate_tangent_type.swift @@ -7,7 +7,7 @@ import StdlibUnittest #elseif canImport(Glibc) import Glibc #elseif os(Windows) - import ucrt + import CRT #else #error("Unsupported platform") #endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 5fc15905342bd..dc76b11ace9fc 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -255,7 +255,9 @@ foreach(SDK ${SWIFT_SDKS}) set(test_dependencies) get_test_dependencies("${SDK}" test_dependencies) - if(SWIFT_BUILD_STDLIB AND SWIFT_INCLUDE_TESTS) + # Keep in sync with stdlib/tools/CMakeLists.txt: swift-reflection-test is + # only used when testing dynamic stdlib. + if(SWIFT_BUILD_DYNAMIC_STDLIB AND SWIFT_INCLUDE_TESTS) # NOTE create a stub BlocksRuntime library that can be used for the # reflection tests file(WRITE ${test_bin_dir}/Inputs/BlocksRuntime.c diff --git a/test/Casting/Casts.swift b/test/Casting/Casts.swift index 970b0797f8531..484116833afe4 100644 --- a/test/Casting/Casts.swift +++ b/test/Casting/Casts.swift @@ -24,6 +24,7 @@ // RUN: %target-run %t/a.swift5.O.out // // REQUIRES: executable_test +// REQUIRES: concurrency // UNSUPPORTED: use_os_stdlib import StdlibUnittest diff --git a/test/ClangImporter/Inputs/ctypes_msvc.h b/test/ClangImporter/Inputs/ctypes_msvc.h new file mode 100644 index 0000000000000..81de4bc21817e --- /dev/null +++ b/test/ClangImporter/Inputs/ctypes_msvc.h @@ -0,0 +1,8 @@ + +typedef struct S { + unsigned char uc; +} S; + +typedef struct T { + S; +} T; diff --git a/test/ClangImporter/MixedSource/mixed-target-using-module.swift b/test/ClangImporter/MixedSource/mixed-target-using-module.swift index 0c007f6dd7895..7cacecc69c4fd 100644 --- a/test/ClangImporter/MixedSource/mixed-target-using-module.swift +++ b/test/ClangImporter/MixedSource/mixed-target-using-module.swift @@ -1,6 +1,14 @@ // 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: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -F %S/Inputs/mixed-target/ -module-name Mixed -DOVERLAY_STYLE_RIGHT -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 +// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -F %S/Inputs/mixed-target/ -module-name WrongName -DOVERLAY_STYLE_WRONG -typecheck %s -enable-objc-interop -disable-objc-attr-requires-foundation-module 2>&1 | %FileCheck -check-prefix=CHECK-WRONG-NAME %s + +#if OVERLAY_STYLE_RIGHT +@_exported import Mixed +#elseif OVERLAY_STYLE_WRONG +@_exported import WrongName +#endif // CHECK-AUTOLINK: !llvm.linker.options = !{ // CHECK-AUTOLINK-NOT: !"-framework" diff --git a/test/ClangImporter/SceneKit_test.swift b/test/ClangImporter/SceneKit_test.swift index 7c24a0b0d8142..316f4ff09b3fb 100644 --- a/test/ClangImporter/SceneKit_test.swift +++ b/test/ClangImporter/SceneKit_test.swift @@ -10,91 +10,91 @@ import Foundation // wrapper types with nestest values. @available(macOS 10.11, *) func testNestingRenames() { - let _ = SCNGeometrySourceSemantic + let _ = SCNGeometrySourceSemantic.self // expected-error@-1{{'SCNGeometrySourceSemantic' has been renamed to 'SCNGeometrySource.Semantic'}} - let _ = SCNLightType + let _ = SCNLightType.self // expected-error@-1{{'SCNLightType' has been renamed to 'SCNLight.LightType'}} - let _ = SCNLightingModel + let _ = SCNLightingModel.self // expected-error@-1{{'SCNLightingModel' has been renamed to 'SCNMaterial.LightingModel'}} - let _ = SCNParticleProperty + let _ = SCNParticleProperty.self // expected-error@-1{{'SCNParticleProperty' has been renamed to 'SCNParticleSystem.ParticleProperty'}} - let _ = SCNPhysicsShapeOption + let _ = SCNPhysicsShapeOption.self // expected-error@-1{{'SCNPhysicsShapeOption' has been renamed to 'SCNPhysicsShape.Option'}} - let _ = SCNPhysicsShapeType + let _ = SCNPhysicsShapeType.self // expected-error@-1{{'SCNPhysicsShapeType' has been renamed to 'SCNPhysicsShape.ShapeType'}} - let _ = SCNPhysicsTestOption + let _ = SCNPhysicsTestOption.self // expected-error@-1{{'SCNPhysicsTestOption' has been renamed to 'SCNPhysicsWorld.TestOption'}} - let _ = SCNPhysicsTestSearchMode + let _ = SCNPhysicsTestSearchMode.self // expected-error@-1{{'SCNPhysicsTestSearchMode' has been renamed to 'SCNPhysicsWorld.TestSearchMode'}} - let _ = SCNSceneAttribute + let _ = SCNSceneAttribute.self // expected-error@-1{{'SCNSceneAttribute' has been renamed to 'SCNScene.Attribute'}} - let _ = SCNSceneSourceAnimationImportPolicy + let _ = SCNSceneSourceAnimationImportPolicy.self // expected-error@-1{{'SCNSceneSourceAnimationImportPolicy' has been renamed to 'SCNSceneSource.AnimationImportPolicy'}} - let _ = SCNSceneSourceLoadingOption + let _ = SCNSceneSourceLoadingOption.self // expected-error@-1{{'SCNSceneSourceLoadingOption' has been renamed to 'SCNSceneSource.LoadingOption'}} - let _ = SCNViewOption + let _ = SCNViewOption.self // expected-error@-1{{'SCNViewOption' has been renamed to 'SCNView.Option'}} - let _ = SCNHitTestFirstFoundOnlyKey + let _ = SCNHitTestFirstFoundOnlyKey.self // expected-error@-1{{'SCNHitTestFirstFoundOnlyKey' has been renamed to 'SCNHitTestOption.firstFoundOnly'}} - let _ = SCNHitTestSortResultsKey + let _ = SCNHitTestSortResultsKey.self // expected-error@-1{{'SCNHitTestSortResultsKey' has been renamed to 'SCNHitTestOption.sortResults'}} - let _ = SCNHitTestClipToZRangeKey + let _ = SCNHitTestClipToZRangeKey.self // expected-error@-1{{'SCNHitTestClipToZRangeKey' has been renamed to 'SCNHitTestOption.clipToZRange'}} - let _ = SCNHitTestBackFaceCullingKey + let _ = SCNHitTestBackFaceCullingKey.self // expected-error@-1{{'SCNHitTestBackFaceCullingKey' has been renamed to 'SCNHitTestOption.backFaceCulling'}} - let _ = SCNHitTestBoundingBoxOnlyKey + let _ = SCNHitTestBoundingBoxOnlyKey.self // expected-error@-1{{'SCNHitTestBoundingBoxOnlyKey' has been renamed to 'SCNHitTestOption.boundingBoxOnly'}} - let _ = SCNHitTestIgnoreChildNodesKey + let _ = SCNHitTestIgnoreChildNodesKey.self // expected-error@-1{{'SCNHitTestIgnoreChildNodesKey' has been renamed to 'SCNHitTestOption.ignoreChildNodes'}} - let _ = SCNHitTestRootNodeKey + let _ = SCNHitTestRootNodeKey.self // expected-error@-1{{'SCNHitTestRootNodeKey' has been renamed to 'SCNHitTestOption.rootNode'}} - let _ = SCNHitTestIgnoreHiddenNodesKey + let _ = SCNHitTestIgnoreHiddenNodesKey.self // expected-error@-1{{'SCNHitTestIgnoreHiddenNodesKey' has been renamed to 'SCNHitTestOption.ignoreHiddenNodes'}} - let _ = SCNPhysicsShapeTypeKey + let _ = SCNPhysicsShapeTypeKey.self // expected-error@-1{{'SCNPhysicsShapeTypeKey' has been renamed to 'SCNPhysicsShape.Option.type'}} - let _ = SCNPhysicsShapeKeepAsCompoundKey + let _ = SCNPhysicsShapeKeepAsCompoundKey.self // expected-error@-1{{'SCNPhysicsShapeKeepAsCompoundKey' has been renamed to 'SCNPhysicsShape.Option.keepAsCompound'}} - let _ = SCNPhysicsShapeScaleKey + let _ = SCNPhysicsShapeScaleKey.self // expected-error@-1{{'SCNPhysicsShapeScaleKey' has been renamed to 'SCNPhysicsShape.Option.scale'}} - let _ = SCNPhysicsTestCollisionBitMaskKey + let _ = SCNPhysicsTestCollisionBitMaskKey.self // expected-error@-1{{'SCNPhysicsTestCollisionBitMaskKey' has been renamed to 'SCNPhysicsWorld.TestOption.collisionBitMask'}} - let _ = SCNPhysicsTestSearchModeKey + let _ = SCNPhysicsTestSearchModeKey.self // expected-error@-1{{'SCNPhysicsTestSearchModeKey' has been renamed to 'SCNPhysicsWorld.TestOption.searchMode'}} - let _ = SCNPhysicsTestBackfaceCullingKey + let _ = SCNPhysicsTestBackfaceCullingKey.self // expected-error@-1{{'SCNPhysicsTestBackfaceCullingKey' has been renamed to 'SCNPhysicsWorld.TestOption.backfaceCulling'}} - let _ = SCNSceneStartTimeAttributeKey + let _ = SCNSceneStartTimeAttributeKey.self // expected-error@-1{{'SCNSceneStartTimeAttributeKey' has been renamed to 'SCNScene.Attribute.startTime'}} - let _ = SCNSceneEndTimeAttributeKey + let _ = SCNSceneEndTimeAttributeKey.self // expected-error@-1{{'SCNSceneEndTimeAttributeKey' has been renamed to 'SCNScene.Attribute.endTime'}} - let _ = SCNSceneFrameRateAttributeKey + let _ = SCNSceneFrameRateAttributeKey.self // expected-error@-1{{'SCNSceneFrameRateAttributeKey' has been renamed to 'SCNScene.Attribute.frameRate'}} - let _ = SCNSceneUpAxisAttributeKey + let _ = SCNSceneUpAxisAttributeKey.self // expected-error@-1{{'SCNSceneUpAxisAttributeKey' has been renamed to 'SCNScene.Attribute.upAxis'}} - let _ = SCNSceneSourceCreateNormalsIfAbsentKey + let _ = SCNSceneSourceCreateNormalsIfAbsentKey.self // expected-error@-1{{'SCNSceneSourceCreateNormalsIfAbsentKey' has been renamed to 'SCNSceneSource.LoadingOption.createNormalsIfAbsent'}} - let _ = SCNSceneSourceCheckConsistencyKey + let _ = SCNSceneSourceCheckConsistencyKey.self // expected-error@-1{{'SCNSceneSourceCheckConsistencyKey' has been renamed to 'SCNSceneSource.LoadingOption.checkConsistency'}} - let _ = SCNSceneSourceFlattenSceneKey + let _ = SCNSceneSourceFlattenSceneKey.self // expected-error@-1{{'SCNSceneSourceFlattenSceneKey' has been renamed to 'SCNSceneSource.LoadingOption.flattenScene'}} - let _ = SCNSceneSourceUseSafeModeKey + let _ = SCNSceneSourceUseSafeModeKey.self // expected-error@-1{{'SCNSceneSourceUseSafeModeKey' has been renamed to 'SCNSceneSource.LoadingOption.useSafeMode'}} - let _ = SCNSceneSourceAssetDirectoryURLsKey + let _ = SCNSceneSourceAssetDirectoryURLsKey.self // expected-error@-1{{'SCNSceneSourceAssetDirectoryURLsKey' has been renamed to 'SCNSceneSource.LoadingOption.assetDirectoryURLs'}} - let _ = SCNSceneSourceOverrideAssetURLsKey + let _ = SCNSceneSourceOverrideAssetURLsKey.self // expected-error@-1{{'SCNSceneSourceOverrideAssetURLsKey' has been renamed to 'SCNSceneSource.LoadingOption.overrideAssetURLs'}} - let _ = SCNSceneSourceStrictConformanceKey + let _ = SCNSceneSourceStrictConformanceKey.self // expected-error@-1{{'SCNSceneSourceStrictConformanceKey' has been renamed to 'SCNSceneSource.LoadingOption.strictConformance'}} - let _ = SCNSceneSourceConvertUnitsToMetersKey + let _ = SCNSceneSourceConvertUnitsToMetersKey.self // expected-error@-1{{'SCNSceneSourceConvertUnitsToMetersKey' has been renamed to 'SCNSceneSource.LoadingOption.convertUnitsToMeters'}} - let _ = SCNSceneSourceConvertToYUpKey + let _ = SCNSceneSourceConvertToYUpKey.self // expected-error@-1{{'SCNSceneSourceConvertToYUpKey' has been renamed to 'SCNSceneSource.LoadingOption.convertToYUp'}} - let _ = SCNSceneSourceAnimationImportPolicyKey + let _ = SCNSceneSourceAnimationImportPolicyKey.self // expected-error@-1{{'SCNSceneSourceAnimationImportPolicyKey' has been renamed to 'SCNSceneSource.LoadingOption.animationImportPolicy'}} - let _ = SCNPreferredRenderingAPIKey + let _ = SCNPreferredRenderingAPIKey.self // expected-error@-1{{'SCNPreferredRenderingAPIKey' has been renamed to 'SCNView.Option.preferredRenderingAPI'}} - let _ = SCNPreferredDeviceKey + let _ = SCNPreferredDeviceKey.self // expected-error@-1{{'SCNPreferredDeviceKey' has been renamed to 'SCNView.Option.preferredDevice'}} - let _ = SCNPreferLowPowerDeviceKey + let _ = SCNPreferLowPowerDeviceKey.self // expected-error@-1{{'SCNPreferLowPowerDeviceKey' has been renamed to 'SCNView.Option.preferLowPowerDevice'}} } diff --git a/test/ClangImporter/attr-swift_name_renaming.swift b/test/ClangImporter/attr-swift_name_renaming.swift index 8987076701c91..df5a9d9b40169 100644 --- a/test/ClangImporter/attr-swift_name_renaming.swift +++ b/test/ClangImporter/attr-swift_name_renaming.swift @@ -9,7 +9,7 @@ func test() { // Enum name remapping. var color: ColorKind = CT_red - var color2: ColorType = CT_Red // expected-error{{'ColorType' has been renamed to 'ColorKind'}}{{15-24=ColorKind}} + var color2: ColorType = CT_red // expected-error{{'ColorType' has been renamed to 'ColorKind'}}{{15-24=ColorKind}} // Enumerator remapping. var excuse: HomeworkExcuse = .dogAteIt diff --git a/test/ClangImporter/attr-swift_private.swift b/test/ClangImporter/attr-swift_private.swift index 252f8c74d513c..fd9af2910913f 100644 --- a/test/ClangImporter/attr-swift_private.swift +++ b/test/ClangImporter/attr-swift_private.swift @@ -103,8 +103,8 @@ func makeSureAnyObject(_: AnyObject) {} #if !IRGEN func testUnavailableRefs() { - var x: __PrivCFTypeRef // expected-error {{'__PrivCFTypeRef' has been renamed to '__PrivCFType'}} - var y: __PrivCFSubRef // expected-error {{'__PrivCFSubRef' has been renamed to '__PrivCFSub'}} + var _: __PrivCFTypeRef // expected-error {{'__PrivCFTypeRef' has been renamed to '__PrivCFType'}} + var _: __PrivCFSubRef // expected-error {{'__PrivCFSubRef' has been renamed to '__PrivCFSub'}} } #endif diff --git a/test/ClangImporter/availability.swift b/test/ClangImporter/availability.swift index 8ee1e1acc7c16..011b4af216327 100644 --- a/test/ClangImporter/availability.swift +++ b/test/ClangImporter/availability.swift @@ -137,6 +137,7 @@ func test_swift_unavailable() { NSSwiftNewUnavailableFunctionPremium() // expected-error {{'NSSwiftNewUnavailableFunctionPremium()' is unavailable in Swift: You didn't want to use it anyway.}} let x: NSSwiftUnavailableStruct? = nil // expected-error {{'NSSwiftUnavailableStruct' is unavailable in Swift}} + _ = x } func test_CFReleaseRetainAutorelease(_ x: CFTypeRef, color: CGColor) { @@ -152,9 +153,9 @@ func testRedeclarations() { unavail2() // expected-error {{is unavailable: middle}} unavail3() // expected-error {{is unavailable: last}} - UnavailClass1.self // expected-error {{is unavailable: first}} - UnavailClass2.self // expected-error {{is unavailable: middle}} - UnavailClass3.self // expected-error {{is unavailable: last}} + _ = UnavailClass1.self // expected-error {{is unavailable: first}} + _ = UnavailClass2.self // expected-error {{is unavailable: middle}} + _ = UnavailClass3.self // expected-error {{is unavailable: last}} let _: UnavailStruct1 // expected-error {{is unavailable: first}} let _: UnavailStruct2 // expected-error {{is unavailable: first}} @@ -186,7 +187,9 @@ func test_DistributedObjects(_ o: NSObject, i: NSPortCoder) { // expected-error {{'NSPortCoder' is unavailable in Swift: Use NSXPCConnection instead}} let ca = NSConnectionDidDieNotification // expected-error {{'NSConnectionDidDieNotification' is unavailable in Swift: Use NSXPCConnection instead}} + _ = ca let cc = NSConnectionReplyMode // expected-error {{'NSConnectionReplyMode' is unavailable in Swift: Use NSXPCConnection instead}} + _ = cc _ = o.classForPortCoder // expected-error {{'classForPortCoder' is unavailable in Swift: Use NSXPCConnection instead}} } diff --git a/test/ClangImporter/availability_returns_twice-msvc.swift b/test/ClangImporter/availability_returns_twice-msvc.swift index 36bee9412138b..904ea8de42031 100644 --- a/test/ClangImporter/availability_returns_twice-msvc.swift +++ b/test/ClangImporter/availability_returns_twice-msvc.swift @@ -1,7 +1,7 @@ // RUN: %target-typecheck-verify-swift // REQUIRES: OS=windows-msvc -import MSVCRT +import visualc typealias JumpBuffer = _JBTYPE func test_unavailable_returns_twice_function() { diff --git a/test/ClangImporter/clang-function-types.swift b/test/ClangImporter/clang-function-types.swift index 52d7dae1dd75a..0afd8221d5709 100644 --- a/test/ClangImporter/clang-function-types.swift +++ b/test/ClangImporter/clang-function-types.swift @@ -2,11 +2,11 @@ import ctypes -// CHECK: f1: (@convention(c, cType: "int (*)(int)") (Swift.Int32) -> Swift.Int32)? +// CHECK: f1: (@convention(c, cType: "size_t (*)(size_t)") (Swift.Int) -> Swift.Int)? public let f1 = getFunctionPointer_() -// CHECK: f2: (@convention(c, cType: "int (*(*)(int (*)(int)))(int)") ((@convention(c, cType: "int (*)(int)") (Swift.Int32) -> Swift.Int32)?) -> (@convention(c, cType: "int (*)(int)") (Swift.Int32) -> Swift.Int32)?)? +// CHECK: f2: (@convention(c) ((@convention(c, cType: "size_t (*)(size_t)") (Swift.Int) -> Swift.Int)?) -> (@convention(c, cType: "size_t (*)(size_t)") (Swift.Int) -> Swift.Int)?)? public let f2 = getHigherOrderFunctionPointer() -// CHECK: f3: () -> (@convention(c, cType: "Dummy *(*)(Dummy *)") (Swift.UnsafeMutablePointer?) -> Swift.UnsafeMutablePointer?)? +// CHECK: f3: () -> (@convention(c) (Swift.UnsafeMutablePointer?) -> Swift.UnsafeMutablePointer?)? public let f3 = getFunctionPointer3 diff --git a/test/ClangImporter/clang_builtins.swift b/test/ClangImporter/clang_builtins.swift index 07557df68b723..50d05084d2403 100644 --- a/test/ClangImporter/clang_builtins.swift +++ b/test/ClangImporter/clang_builtins.swift @@ -5,7 +5,7 @@ #elseif canImport(Glibc) import Glibc #elseif os(Windows) - import MSVCRT + import CRT #else #error("Unsupported platform") #endif diff --git a/test/ClangImporter/ctypes_parse.swift b/test/ClangImporter/ctypes_parse.swift index 9cf79bf690379..fd6aa6ea11776 100644 --- a/test/ClangImporter/ctypes_parse.swift +++ b/test/ClangImporter/ctypes_parse.swift @@ -205,7 +205,7 @@ func testFunctionPointers() { useFunctionPointer(wrapper.a) _ = wrapper.b as (@convention(c) (CInt) -> CInt) - var anotherFP: @convention(c) (CInt, CLong, UnsafeMutableRawPointer?) -> Void + var anotherFP: @convention(c) (Int, CLong, UnsafeMutableRawPointer?) -> Void = getFunctionPointer2() var sizedFP: (@convention(c) (CInt, CInt, UnsafeMutableRawPointer?) -> Void)? diff --git a/test/ClangImporter/ctypes_parse_msvc.swift b/test/ClangImporter/ctypes_parse_msvc.swift new file mode 100644 index 0000000000000..8a121f23feaf7 --- /dev/null +++ b/test/ClangImporter/ctypes_parse_msvc.swift @@ -0,0 +1,4 @@ +// RUN: %target-swift-frontend -Xcc -fms-extensions -import-objc-header %S/Inputs/ctypes_msvc.h -typecheck -verify %s + +_ = T().uc +_ = T(S(uc: 0)) diff --git a/test/ClangImporter/diags_from_module.swift b/test/ClangImporter/diags_from_module.swift index 8e74e7a8ca062..5cc81a87e1291 100644 --- a/test/ClangImporter/diags_from_module.swift +++ b/test/ClangImporter/diags_from_module.swift @@ -37,7 +37,7 @@ import Module // CHECK-PRIMARY: diags_from_module.swift:[[@LINE-4]]:8: error: could not build Objective-C module 'Module' // CHECK-WARN: Sub2.h:7:2: warning: here is some warning about something -// CHECK-WARN: :1:1: warning: umbrella header for module 'Module' does not include header 'NotInModule.h' +// CHECK-WARN: Module.h:20: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/objc_async.swift b/test/ClangImporter/objc_async.swift index 201328e14ad9e..86178fe99d321 100644 --- a/test/ClangImporter/objc_async.swift +++ b/test/ClangImporter/objc_async.swift @@ -1,6 +1,7 @@ // RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -I %S/Inputs/custom-modules -enable-experimental-concurrency %s -verify // REQUIRES: objc_interop +// REQUIRES: concurrency import Foundation import ObjCConcurrency @@ -11,6 +12,24 @@ func testSlowServer(slowServer: SlowServer) async throws { let _: String = await try slowServer.findAnswerFailingly() ?? "nope" let _: Void = await slowServer.doSomethingFun("jump") let _: (Int) -> Void = slowServer.completionHandler + + // async version + let _: Int = await slowServer.doSomethingConflicted("thinking") + + // still async version... + let _: Int = slowServer.doSomethingConflicted("thinking") + // expected-error@-1{{call is 'async' but is not marked with 'await'}} + + let _: String? = await try slowServer.fortune() + let _: Int = await try slowServer.magicNumber(withSeed: 42) + + await slowServer.serverRestart("localhost") + await slowServer.server("localhost", atPriorityRestart: 0.8) +} + +func testSlowServerSynchronous(slowServer: SlowServer) { + // synchronous version + let _: Int = slowServer.doSomethingConflicted("thinking") } func testSlowServerOldSchool(slowServer: SlowServer) { diff --git a/test/ClangImporter/pcm-emit-system-module.swift b/test/ClangImporter/pcm-emit-system-module.swift new file mode 100644 index 0000000000000..df7199bc268cd --- /dev/null +++ b/test/ClangImporter/pcm-emit-system-module.swift @@ -0,0 +1,8 @@ +// Emit the explicit system module. +// RUN: %empty-directory(%t) +// RUN: %target-swift-emit-pcm -module-name script -Xcc -Xclang -Xcc -emit-module -Xcc -Xclang -Xcc -fsystem-module -o %t/script.pcm %S/Inputs/custom-modules/module.map + +// Verify that the input modulemap if marked [System] in the output of the -dump-pcm action. +// RUN: %swift-dump-pcm %t/script.pcm | %FileCheck %s --check-prefix=CHECK-SYSTEM-INPUT +// CHECK-SYSTEM-INPUT: Input file: {{.*[/\\]}}ClangImporter{{/|\\}}Inputs{{/|\\}}custom-modules{{/|\\}}module.map [System] +import script diff --git a/test/ClangImporter/swift2_warnings.swift b/test/ClangImporter/swift2_warnings.swift index ed26e7d445ba4..ce5a26af75319 100644 --- a/test/ClangImporter/swift2_warnings.swift +++ b/test/ClangImporter/swift2_warnings.swift @@ -8,8 +8,7 @@ import ImportAsMember.A import AppKit func testOldTypeNames() { - var ps: NSPostingStyle? // expected-error{{'NSPostingStyle' has been renamed to 'NotificationQueue.PostingStyle'}}{{11-25=NotificationQueue.PostingStyle}} - + let _: NSPostingStyle? // expected-error{{'NSPostingStyle' has been renamed to 'NotificationQueue.PostingStyle'}}{{10-24=NotificationQueue.PostingStyle}} _ = NSPostingStyle(rawValue: 1) // expected-error{{'NSPostingStyle' has been renamed to 'NotificationQueue.PostingStyle'}}{{7-21=NotificationQueue.PostingStyle}} diff --git a/test/Concurrency/actor_isolation.swift b/test/Concurrency/actor_isolation.swift index 3162f37ec53c8..2f5e9b77188b1 100644 --- a/test/Concurrency/actor_isolation.swift +++ b/test/Concurrency/actor_isolation.swift @@ -1,4 +1,5 @@ // RUN: %target-typecheck-verify-swift -enable-experimental-concurrency +// REQUIRES: concurrency let immutableGlobal: String = "hello" var mutableGlobal: String = "can't touch this" // expected-note 2{{var declared here}} @@ -6,32 +7,34 @@ var mutableGlobal: String = "can't touch this" // expected-note 2{{var declared func globalFunc() { } func acceptClosure(_: () -> T) { } func acceptEscapingClosure(_: @escaping () -> T) { } +func acceptEscapingClosure(_: (String) -> ()) async -> T? { nil } func acceptAsyncClosure(_: () async -> T) { } func acceptEscapingAsyncClosure(_: @escaping () async -> T) { } + // ---------------------------------------------------------------------- // Actor state isolation restrictions // ---------------------------------------------------------------------- actor class MySuperActor { - var superState: Int = 25 + var superState: Int = 25 // expected-note {{mutable state is only available within the actor instance}} - func superMethod() { } // expected-note 2 {{only asynchronous methods can be used outside the actor instance; do you want to add 'async'?}} + func superMethod() { } // expected-note 3 {{only asynchronous methods can be used outside the actor instance; do you want to add 'async'?}} func superAsyncMethod() async { } - subscript (index: Int) -> String { // expected-note{{subscript declared here}} + subscript (index: Int) -> String { // expected-note 3{{subscript declared here}} "\(index)" } } actor class MyActor: MySuperActor { let immutable: Int = 17 - var text: [String] = [] // expected-note 6{{mutable state is only available within the actor instance}} + var text: [String] = [] // expected-note 9{{mutable state is only available within the actor instance}} class func synchronousClass() { } static func synchronousStatic() { } - func synchronous() -> String { text.first ?? "nothing" } // expected-note 18{{only asynchronous methods can be used outside the actor instance; do you want to add 'async'?}} + func synchronous() -> String { text.first ?? "nothing" } // expected-note 21{{only asynchronous methods can be used outside the actor instance; do you want to add 'async'?}} func asynchronous() async -> String { synchronous() } } @@ -60,6 +63,9 @@ extension MyActor { _ = otherActor.actorIndependentVar otherActor.actorIndependentVar = 17 + // Global actors + syncGlobalActorFunc() /// expected-error{{global function 'syncGlobalActorFunc()' isolated to global actor 'SomeGlobalActor' can not be referenced from an '@actorIndependent' context}} + // Global data is okay if it is immutable. _ = immutableGlobal _ = mutableGlobal // expected-warning{{reference to var 'mutableGlobal' is not concurrency-safe because it involves shared mutable state}} @@ -113,6 +119,10 @@ extension MyActor { Self.synchronousClass() Self.synchronousStatic() + // Global actors + syncGlobalActorFunc() // expected-error{{global function 'syncGlobalActorFunc()' isolated to global actor 'SomeGlobalActor' can not be referenced from actor 'MyActor'}} + await asyncGlobalActorFunc() + // Closures. let localConstant = 17 var localVar = 17 // expected-note 3{{var declared here}} @@ -167,6 +177,95 @@ extension MyActor { } } +// ---------------------------------------------------------------------- +// Global actor isolation restrictions +// ---------------------------------------------------------------------- +actor class SomeActor { } + +@globalActor +struct SomeGlobalActor { + static let shared = SomeActor() +} + +@globalActor +struct SomeOtherGlobalActor { + static let shared = SomeActor() +} + +@globalActor +struct GenericGlobalActor { + static var shared: SomeActor { SomeActor() } +} + +@SomeGlobalActor func syncGlobalActorFunc() { } // expected-note 3{{only asynchronous methods can be used outside the actor instance; do you want to add 'async'?}} +@SomeGlobalActor func asyncGlobalActorFunc() async { } + +@SomeOtherGlobalActor func syncOtherGlobalActorFunc() { } // expected-note {{only asynchronous methods can be used outside the actor instance; do you want to add 'async'?}} + +@SomeOtherGlobalActor func asyncOtherGlobalActorFunc() async { + syncGlobalActorFunc() // expected-error{{global function 'syncGlobalActorFunc()' isolated to global actor 'SomeGlobalActor' can not be referenced from different global actor 'SomeOtherGlobalActor'}} + await asyncGlobalActorFunc() +} + +extension MyActor { + @SomeGlobalActor func onGlobalActor(otherActor: MyActor) async { + // Access to other functions in this actor are okay. + syncGlobalActorFunc() + await asyncGlobalActorFunc() + + // Other global actors are not okay + syncOtherGlobalActorFunc() // expected-error{{global function 'syncOtherGlobalActorFunc()' isolated to global actor 'SomeOtherGlobalActor' can not be referenced from different global actor 'SomeGlobalActor'}} + await asyncOtherGlobalActorFunc() + + _ = immutable + _ = synchronous() // expected-error{{actor-isolated instance method 'synchronous()' can not be referenced from context of global actor 'SomeGlobalActor'}} + _ = text[0] // expected-error{{actor-isolated property 'text' can not be referenced from context of global actor 'SomeGlobalActor'}} + + // Accesses on 'self' are only okay for immutable and asynchronous, because + // we are outside of the actor instance. + _ = self.immutable + _ = self.synchronous() // expected-error{{actor-isolated instance method 'synchronous()' can not be referenced from context of global actor 'SomeGlobalActor'}} + _ = await self.asynchronous() + _ = self.text[0] // expected-error{{actor-isolated property 'text' can not be referenced from context of global actor 'SomeGlobalActor'}} + _ = self[0] // expected-error{{actor-isolated subscript 'subscript(_:)' can not be referenced from context of global actor 'SomeGlobalActor'}} + + // Accesses on 'super' are not okay; we're outside of the actor. + _ = super.superState // expected-error{{actor-isolated property 'superState' can not be referenced from context of global actor 'SomeGlobalActor'}} + super.superMethod() // expected-error{{actor-isolated instance method 'superMethod()' can not be referenced from context of global actor 'SomeGlobalActor'}} + await super.superAsyncMethod() + _ = super[0] // expected-error{{actor-isolated subscript 'subscript(_:)' can not be referenced from context of global actor 'SomeGlobalActor'}} + + // Accesses on other actors can only reference immutable data or + // call asychronous methods + _ = otherActor.immutable // okay + _ = otherActor.synchronous() // expected-error{{actor-isolated instance method 'synchronous()' can only be referenced on 'self'}} + _ = await otherActor.asynchronous() + _ = otherActor.text[0] // expected-error{{actor-isolated property 'text' can only be referenced on 'self'}} + } +} + +struct GenericStruct { + @GenericGlobalActor func f() { } // expected-note{{only asynchronous methods can be used outside the actor instance; do you want to add 'async'?}} + + @GenericGlobalActor func g() { + f() // okay + } + + @GenericGlobalActor func h() { + f() // expected-error{{instance method 'f()' isolated to global actor 'GenericGlobalActor' can not be referenced from different global actor 'GenericGlobalActor'}} + } +} + +extension GenericStruct where T == String { + @GenericGlobalActor + func h2() { + f() + g() + h() + } +} + + // ---------------------------------------------------------------------- // Non-actor code isolation restrictions // ---------------------------------------------------------------------- diff --git a/test/Concurrency/async_tasks.swift b/test/Concurrency/async_tasks.swift new file mode 100644 index 0000000000000..b7485f7792efe --- /dev/null +++ b/test/Concurrency/async_tasks.swift @@ -0,0 +1,61 @@ +// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency +// REQUIRES: concurrency + +func someAsyncFunc() async -> String { "" } + +struct MyError: Error {} +func someThrowingAsyncFunc() async throws -> String { throw MyError() } + +func test_unsafeContinuations() async { + // the closure should not allow async operations; + // after all: if you have async code, just call it directly, without the unsafe continuation + let _: String = Task.withUnsafeContinuation { continuation in // expected-error{{cannot convert value of type '(_) async -> ()' to expected argument type '(Task.UnsafeContinuation) -> Void'}} + let s = await someAsyncFunc() // rdar://70610141 for getting a better error message here + continuation.resume(returning: s) + } + + let _: String = await Task.withUnsafeContinuation { continuation in + continuation.resume(returning: "") + } +} + +func test_unsafeThrowingContinuations() async { + let _: String = try await Task.withUnsafeThrowingContinuation { continuation in + continuation.resume(returning: "") + } + + let _: String = try await Task.withUnsafeThrowingContinuation { continuation in + continuation.resume(throwing: MyError()) + } + + // TODO: Potentially could offer some warnings if we know that a continuation was resumed or escaped at all in a closure? +} + +// ==== Detached Tasks --------------------------------------------------------- + +func test_detached() async throws { + let handle = Task.runDetached() { + await someAsyncFunc() // able to call async functions + } + + let result: String = await try handle.get() + _ = result +} + +func test_detached_throwing() async -> String { + let handle: Task.Handle = Task.runDetached() { + await try someThrowingAsyncFunc() // able to call async functions + } + + do { + return await try handle.get() + } catch { + print("caught: \(error)") + } +} + +// ==== Current Task ----------------------------------------------------------- + +func test_current_task() async { + _ = await Task.current() // yay, we know "in" what task we're executing +} diff --git a/test/Concurrency/global_actor_inference.swift b/test/Concurrency/global_actor_inference.swift new file mode 100644 index 0000000000000..5a2ec5bcd160a --- /dev/null +++ b/test/Concurrency/global_actor_inference.swift @@ -0,0 +1,155 @@ +// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency +// REQUIRES: concurrency + +actor class SomeActor { } + +@globalActor +struct SomeGlobalActor { + static let shared = SomeActor() +} + +@globalActor +struct OtherGlobalActor { + static let shared = SomeActor() +} + +@globalActor +struct GenericGlobalActor { + static var shared: SomeActor { SomeActor() } +} + +// ---------------------------------------------------------------------- +// Global actor inference for protocols +// ---------------------------------------------------------------------- + +@SomeGlobalActor +protocol P1 { + func method() +} + +protocol P2 { + @SomeGlobalActor func method1() // expected-note {{'method1()' declared here}} + func method2() +} + +class C1: P1 { + func method() { } // expected-note{{only asynchronous methods can be used outside the actor instance}} + + @OtherGlobalActor func testMethod() { + method() // expected-error{{instance method 'method()' isolated to global actor 'SomeGlobalActor' can not be referenced from different global actor 'OtherGlobalActor'}} + } +} + +class C2: P2 { + func method1() { } // expected-note{{only asynchronous methods can be used outside the actor instance}} + func method2() { } + + @OtherGlobalActor func testMethod() { + method1() // expected-error{{instance method 'method1()' isolated to global actor 'SomeGlobalActor' can not be referenced from different global actor 'OtherGlobalActor'}} + method2() // okay + } +} + +// ---------------------------------------------------------------------- +// Global actor inference for classes and extensions +// ---------------------------------------------------------------------- +@SomeGlobalActor class C3 { + func method1() { } // expected-note{{only asynchronous methods can be used outside the actor instance}} +} + +extension C3 { + func method2() { } // expected-note{{only asynchronous methods can be used outside the actor instance}} +} + +class C4: C3 { + func method3() { } // expected-note{{only asynchronous methods can be used outside the actor instance}} +} + +extension C4 { + func method4() { } // expected-note{{only asynchronous methods can be used outside the actor instance}} +} + +class C5 { + func method1() { } +} + +@SomeGlobalActor extension C5 { + func method2() { } // expected-note{{only asynchronous methods can be used outside the actor instance}} +} + +@OtherGlobalActor func testGlobalActorInference(c3: C3, c4: C4, c5: C5) { + // Propagation via class annotation + c3.method1() // expected-error{{instance method 'method1()' isolated to global actor 'SomeGlobalActor' can not be referenced from different global actor 'OtherGlobalActor'}} + c3.method2() // expected-error{{instance method 'method2()' isolated to global actor 'SomeGlobalActor' can not be referenced from different global actor 'OtherGlobalActor'}} + + // Propagation via subclassing + c4.method3() // expected-error{{instance method 'method3()' isolated to global actor 'SomeGlobalActor' can not be referenced from different global actor 'OtherGlobalActor'}} + c4.method4() // expected-error{{instance method 'method4()' isolated to global actor 'SomeGlobalActor' can not be referenced from different global actor 'OtherGlobalActor'}} + + // Propagation in an extension. + c5.method1() // OK: no propagation + c5.method2() // expected-error{{instance method 'method2()' isolated to global actor 'SomeGlobalActor' can not be referenced from different global actor 'OtherGlobalActor'}} +} + +protocol P3 { + @OtherGlobalActor func method1() // expected-note{{'method1()' declared here}} + func method2() +} + +class C6: P2, P3 { + func method1() { } + // expected-error@-1{{instance method 'method1()' must be isolated to the global actor 'SomeGlobalActor' to satisfy corresponding requirement from protocol 'P2'}} + // expected-error@-2{{instance method 'method1()' must be isolated to the global actor 'OtherGlobalActor' to satisfy corresponding requirement from protocol 'P3'}} + func method2() { } + + func testMethod() { + method1() // okay: no inference + method2() // okay: no inference + } +} + +// ---------------------------------------------------------------------- +// Global actor checking for overrides +// ---------------------------------------------------------------------- +actor class GenericSuper { + @GenericGlobalActor func method() { } + + @GenericGlobalActor func method2() { } // expected-note {{overridden declaration is here}} + @GenericGlobalActor func method3() { } // expected-note {{overridden declaration is here}} + @GenericGlobalActor func method4() { } + @GenericGlobalActor func method5() { } +} + +actor class GenericSub : GenericSuper<[T]> { + override func method() { } // expected-note{{only asynchronous methods can be used outside the actor instance; do you want to add 'async'?}} + + @GenericGlobalActor override func method2() { } // expected-error{{global actor 'GenericGlobalActor'-isolated instance method 'method2()' has different actor isolation from global actor 'GenericGlobalActor<[T]>'-isolated overridden declaration}} + @actorIndependent override func method3() { } // expected-error{{actor-independent instance method 'method3()' has different actor isolation from global actor 'GenericGlobalActor<[T]>'-isolated overridden declaration}} + + @OtherGlobalActor func testMethod() { + method() // expected-error{{instance method 'method()' isolated to global actor 'GenericGlobalActor<[T]>' can not be referenced from different global actor 'OtherGlobalActor'}} + } +} + +// ---------------------------------------------------------------------- +// Global actor inference for superclasses +// ---------------------------------------------------------------------- +struct Container { + @GenericGlobalActor class Superclass { } + @GenericGlobalActor<[T]> class Superclass2 { } +} + +struct OtherContainer { + // Okay to change the global actor in a subclass. + @GenericGlobalActor<[U]> class Subclass1 : Container<[U]>.Superclass { } + @GenericGlobalActor class Subclass2 : Container<[U]>.Superclass { } + + // Ensure that substitutions work properly when inheriting. + class Subclass3 : Container<(U, V)>.Superclass2 { + func method() { } // expected-note{{only asynchronous methods can be used outside the actor instance}} + + @OtherGlobalActor func testMethod() { + method() // expected-error{{instance method 'method()' isolated to global actor 'GenericGlobalActor<[(U, V)]>' can not be referenced from different global actor 'OtherGlobalActor'}} + } + } +} diff --git a/test/Constraints/add_with_nil.swift b/test/Constraints/add_with_nil.swift index 30121abdfb20a..0a5e9b1eabc22 100644 --- a/test/Constraints/add_with_nil.swift +++ b/test/Constraints/add_with_nil.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift -swift-version 5 -solver-enable-operator-designated-types -solver-disable-shrink -disable-constraint-solver-performance-hacks +// RUN: %target-typecheck-verify-swift -swift-version 5 func test(_ x: Int) -> Int { return x + nil diff --git a/test/Constraints/async.swift b/test/Constraints/async.swift index e325ef20e7499..24e21eeb1af92 100644 --- a/test/Constraints/async.swift +++ b/test/Constraints/async.swift @@ -1,5 +1,7 @@ // RUN: %target-typecheck-verify-swift -enable-experimental-concurrency +// REQUIRES: concurrency + func doAsynchronously() async { } func doSynchronously() { } diff --git a/test/Constraints/diagnostics.swift b/test/Constraints/diagnostics.swift index fd1a40913a80d..2e9eb5dd8a03a 100644 --- a/test/Constraints/diagnostics.swift +++ b/test/Constraints/diagnostics.swift @@ -51,9 +51,9 @@ f0(i, i, // expected-error@:7 {{cannot convert value of type 'Int' to expected a // Cannot conform to protocols. -f5(f4) // expected-error {{type '(Int) -> Int' cannot conform to 'P2'; only concrete types such as structs, enums and classes can conform to protocols}} -f5((1, "hello")) // expected-error {{type '(Int, String)' cannot conform to 'P2'; only concrete types such as structs, enums and classes can conform to protocols}} -f5(Int.self) // expected-error {{type 'Int.Type' cannot conform to 'P2'; only concrete types such as structs, enums and classes can conform to protocols}} +f5(f4) // expected-error {{type '(Int) -> Int' cannot conform to 'P2'}} expected-note {{only concrete types such as structs, enums and classes can conform to protocols}} +f5((1, "hello")) // expected-error {{type '(Int, String)' cannot conform to 'P2'}} expected-note {{only concrete types such as structs, enums and classes can conform to protocols}} +f5(Int.self) // expected-error {{type 'Int.Type' cannot conform to 'P2'}} expected-note {{only concrete types such as structs, enums and classes can conform to protocols}} // Tuple element not convertible. f0(i, @@ -104,7 +104,7 @@ func f8(_ n: T, _ f: @escaping (T) -> T) {} // expected-note {{where 'T' f8(3, f4) // expected-error {{global function 'f8' requires that 'Int' conform to 'P2'}} typealias Tup = (Int, Double) func f9(_ x: Tup) -> Tup { return x } -f8((1,2.0), f9) // expected-error {{type 'Tup' (aka '(Int, Double)') cannot conform to 'P2'; only concrete types such as structs, enums and classes can conform to protocols}} +f8((1,2.0), f9) // expected-error {{type 'Tup' (aka '(Int, Double)') cannot conform to 'P2'}} expected-note {{only concrete types such as structs, enums and classes can conform to protocols}} // QoI: Incorrect diagnostic for calling nonexistent members on literals 1.doesntExist(0) // expected-error {{value of type 'Int' has no member 'doesntExist'}} diff --git a/test/Constraints/generics.swift b/test/Constraints/generics.swift index ab8f724622ebb..6f72cb902814a 100644 --- a/test/Constraints/generics.swift +++ b/test/Constraints/generics.swift @@ -188,7 +188,7 @@ func r22459135() { // QoI: Friendlier error message for "[] as Set" // QoI: "argument for generic parameter 'Element' could not be inferred" lacks context -_ = [] as Set // expected-error {{protocol 'Any' as a type cannot conform to 'Hashable'; only concrete types such as structs, enums and classes can conform to protocols}} +_ = [] as Set // expected-error {{protocol 'Any' as a type cannot conform to 'Hashable'}} expected-note {{only concrete types such as structs, enums and classes can conform to protocols}} // expected-note@-1 {{required by generic struct 'Set' where 'Element' = 'Any'}} diff --git a/test/Constraints/lvalues.swift b/test/Constraints/lvalues.swift index 7c7d4069ec384..165c752f7db21 100644 --- a/test/Constraints/lvalues.swift +++ b/test/Constraints/lvalues.swift @@ -241,3 +241,28 @@ func wump(to: T, _ body: (G) -> ()) {} wump(to: 0, { $0[] = 0 }) // expected-error@-1 {{missing argument for parameter #1 in call}} + +// SR-13732 +extension MutableCollection { + public mutating func writePrefix(from source: inout I) + -> (writtenCount: Int, afterLastWritten: Index) + where I.Element == Element + { + fatalError() + } + + public mutating func writePrefix(from source: Source) + -> (writtenCount: Int, afterLastWritten: Index, afterLastRead: Source.Index) + where Source.Element == Element + { + fatalError() + } + +} + +func testWritePrefixIterator() { + var a = Array(0..<10) + + var underflow = (1..<10).makeIterator() + var (writtenCount, afterLastWritten) = a.writePrefix(from: underflow) // expected-error {{passing value of type 'IndexingIterator<(Range)>' to an inout parameter requires explicit '&'}} {{62-62=&}} +} diff --git a/test/Constraints/rdar64890308.swift b/test/Constraints/rdar64890308.swift index e445751610afd..1c8a7c857d6f6 100644 --- a/test/Constraints/rdar64890308.swift +++ b/test/Constraints/rdar64890308.swift @@ -4,7 +4,7 @@ import Swift -@_functionBuilder +@resultBuilder class ArrayBuilder { static func buildBlock() -> [Element] { [] } static func buildBlock(_ elt: Element) -> [Element] { [elt] } @@ -32,7 +32,7 @@ bar("") { x, ty in protocol P {} extension String : P {} -@_functionBuilder +@resultBuilder struct FooBuilder {} extension FooBuilder where T : P { diff --git a/test/Constraints/rdar65320500.swift b/test/Constraints/rdar65320500.swift index dfb8f722a2692..dd99f9d0906be 100644 --- a/test/Constraints/rdar65320500.swift +++ b/test/Constraints/rdar65320500.swift @@ -2,7 +2,7 @@ struct Result {} -@_functionBuilder +@resultBuilder struct Builder { static func buildBlock() -> Result { Result() diff --git a/test/Constraints/rdar68155466.swift b/test/Constraints/rdar68155466.swift index f8c4e176b7029..78d9371809567 100644 --- a/test/Constraints/rdar68155466.swift +++ b/test/Constraints/rdar68155466.swift @@ -25,4 +25,4 @@ func data() -> [A] { } _ = Loop(data(), id: \.uniqueID) { $0 } // expected-error {{key path cannot refer to instance method 'uniqueID()'}} -// expected-error@-1 {{type '() -> Int' cannot conform to 'Hashable'; only concrete types such as structs, enums and classes can conform to protocols}} +// expected-error@-1 {{type '() -> Int' cannot conform to 'Hashable'}} expected-note@-1 {{only concrete types such as structs, enums and classes can conform to protocols}} diff --git a/test/Constraints/function_builder.swift b/test/Constraints/result_builder.swift similarity index 95% rename from test/Constraints/function_builder.swift rename to test/Constraints/result_builder.swift index d30a3a932f7ce..e1c5a4371a133 100644 --- a/test/Constraints/function_builder.swift +++ b/test/Constraints/result_builder.swift @@ -6,7 +6,7 @@ enum Either { case second(U) } -@_functionBuilder +@resultBuilder struct TupleBuilder { static func buildBlock(_ t1: T1) -> (T1) { return (t1) @@ -247,7 +247,7 @@ extension Int: Taggable { } extension String: Taggable { } extension Double: Taggable { } -@_functionBuilder +@resultBuilder struct TaggedBuilder { static func buildBlock() -> () { } @@ -330,7 +330,7 @@ enum Component { indirect case optional(Component?) } -@_functionBuilder +@resultBuilder struct ComponentBuilder { static func buildExpression(_ string: StaticString) -> Component { return .string(string) @@ -375,13 +375,13 @@ acceptComponentBuilder { // rdar://53325810 // Test that we don't have problems with expression pre-checking when -// type-checking an overloaded function-builder call. In particular, +// type-checking an overloaded result-builder call. In particular, // we need to make sure that expressions in the closure are pre-checked // before we build constraints for them. Note that top-level expressions // that need to be rewritten by expression prechecking (such as the operator // sequences in the boolean conditions and statements below) won't be // rewritten in the original closure body if we just precheck the -// expressions produced by the function-builder transformation. +// expressions produced by the result-builder transformation. struct ForEach1 { var data: Data var content: (Data.Element) -> Content @@ -422,7 +422,7 @@ testForEach1.show() // CHECK: ("testForEach1", main.Either<(Swift.String, Swift.Bool), (Swift.Bool, Swift.String)>.second(true, "end")) func test_single_stmt_closure_support() { - @_functionBuilder + @resultBuilder struct MyBuilder { static func buildBlock(_ numbers: Int...) -> Int { return 42 @@ -478,7 +478,7 @@ testIfConditions(cond: true, c1: true, i1: 1, i2: 1) // CHECK: testIfConditions // CHECK-SAME: hello -// Use a "let" declaration within a function builder. +// Use a "let" declaration within a result builder. tuplify(true) { c in "testLetDeclarations" let (a, b) = (c, c && true) @@ -621,7 +621,7 @@ testSwitchCombined(getE(2)) // Test buildOptional(_:) as an alternative to buildIf(_:). -@_functionBuilder +@resultBuilder struct TupleBuilderWithOpt { static func buildBlock(_ t1: T1) -> (T1) { return (t1) @@ -685,7 +685,7 @@ tuplify(true) { c in } } -// Test the use of function builders partly implemented through a protocol. +// Test the use of result builders partly implemented through a protocol. indirect enum FunctionBuilder { case expression(Expression) case block([FunctionBuilder]) @@ -715,7 +715,7 @@ extension FunctionBuilderProtocol { static func buildLimitedAvailability(_ component: Component) -> Component { component } } -@_functionBuilder +@resultBuilder enum ArrayBuilder: FunctionBuilderProtocol { typealias Expression = E typealias Component = FunctionBuilder @@ -749,7 +749,7 @@ let a = buildArray { // CHECK: ["1", "2" print(a) -// Throwing in function builders. +// Throwing in result builders. enum MyError: Error { case boom } @@ -767,3 +767,24 @@ do { } catch { fatalError("Threw something else?") } + +// CHECK: testStoredProperties +struct MyTupleStruct { + @TupleBuilder let first: () -> T + @TupleBuilder let second: U +} + +print("testStoredProperties") +let ts1 = MyTupleStruct { + 1 + "hello" + if true { + "conditional" + } +} second: { + 3.14159 + "blah" +} + +// CHECK: MyTupleStruct<(Int, String, Optional), (Double, String)>(first: (Function), second: (3.14159, "blah")) +print(ts1) diff --git a/test/Constraints/function_builder_availability.swift b/test/Constraints/result_builder_availability.swift similarity index 87% rename from test/Constraints/function_builder_availability.swift rename to test/Constraints/result_builder_availability.swift index 6e08f25875cd6..e7bf8235d8299 100644 --- a/test/Constraints/function_builder_availability.swift +++ b/test/Constraints/result_builder_availability.swift @@ -7,8 +7,8 @@ enum Either { case second(U) } -@_functionBuilder -struct TupleBuilder { // expected-note{{add 'buildLimitedAvailability(_:)' to the function builder 'TupleBuilder' to erase type information for less-available types}}{{22-22=\n static func buildLimitedAvailability(_ component: <#Component#>) -> <#Component#> {\n <#code#>\n \}}} +@resultBuilder +struct TupleBuilder { // expected-note{{add 'buildLimitedAvailability(_:)' to the result builder 'TupleBuilder' to erase type information for less-available types}}{{22-22=\n static func buildLimitedAvailability(_ component: <#Component#>) -> <#Component#> {\n <#code#>\n \}}} static func buildBlock(_ t1: T1) -> (T1) { return (t1) } @@ -65,7 +65,7 @@ tuplify(true) { cond in if #available(OSX 10.51, *) { globalFuncAvailableOn10_51() tuplify(false) { cond2 in - if cond, #available(OSX 10.52, *) { // expected-warning{{function builder 'TupleBuilder' does not implement 'buildLimitedAvailability'; this code may crash on earlier versions of the OS}} + if cond, #available(OSX 10.52, *) { // expected-warning{{result builder 'TupleBuilder' does not implement 'buildLimitedAvailability'; this code may crash on earlier versions of the OS}} cond2 globalFuncAvailableOn10_52() } else { @@ -77,7 +77,7 @@ tuplify(true) { cond in } // Function builder that can perform type erasure for #available. -@_functionBuilder +@resultBuilder struct TupleBuilderAvailability { static func buildBlock(_ t1: T1) -> (T1) { return (t1) diff --git a/test/Constraints/function_builder_diags.swift b/test/Constraints/result_builder_diags.swift similarity index 91% rename from test/Constraints/function_builder_diags.swift rename to test/Constraints/result_builder_diags.swift index dd66006a93503..08c59e638ce96 100644 --- a/test/Constraints/function_builder_diags.swift +++ b/test/Constraints/result_builder_diags.swift @@ -5,7 +5,7 @@ enum Either { case second(U) } -@_functionBuilder +@resultBuilder struct TupleBuilder { // expected-note 2 {{struct 'TupleBuilder' declared here}} static func buildBlock() -> () { } @@ -44,11 +44,11 @@ struct TupleBuilder { // expected-note 2 {{struct 'TupleBuilder' declared here}} } } -@_functionBuilder +@resultBuilder struct TupleBuilderWithoutIf { // expected-note 3{{struct 'TupleBuilderWithoutIf' declared here}} - // expected-note@-1{{add 'buildOptional(_:)' to the function builder 'TupleBuilderWithoutIf' to add support for 'if' statements without an 'else'}} - // expected-note@-2{{add 'buildEither(first:)' and 'buildEither(second:)' to the function builder 'TupleBuilderWithoutIf' to add support for 'if'-'else' and 'switch'}} - // expected-note@-3{{add 'buildArray(_:)' to the function builder 'TupleBuilderWithoutIf' to add support for 'for'..'in' loops}} + // expected-note@-1{{add 'buildOptional(_:)' to the result builder 'TupleBuilderWithoutIf' to add support for 'if' statements without an 'else'}} + // expected-note@-2{{add 'buildEither(first:)' and 'buildEither(second:)' to the result builder 'TupleBuilderWithoutIf' to add support for 'if'-'else' and 'switch'}} + // expected-note@-3{{add 'buildArray(_:)' to the result builder 'TupleBuilderWithoutIf' to add support for 'for'..'in' loops}} static func buildBlock() -> () { } static func buildBlock(_ t1: T1) -> T1 { @@ -99,26 +99,26 @@ func testDiags() { tuplify(true) { _ in 17 let x = 17 - let y: Int // expected-error{{closure containing a declaration cannot be used with function builder 'TupleBuilder'}} + let y: Int // expected-error{{closure containing a declaration cannot be used with result builder 'TupleBuilder'}} x + 25 } // Statements unsupported by the particular builder. tuplifyWithoutIf(true) { - if $0 { // expected-error{{closure containing control flow statement cannot be used with function builder 'TupleBuilderWithoutIf'}} + if $0 { // expected-error{{closure containing control flow statement cannot be used with result builder 'TupleBuilderWithoutIf'}} "hello" } } tuplifyWithoutIf(true) { - if $0 { // expected-error{{closure containing control flow statement cannot be used with function builder 'TupleBuilderWithoutIf'}} + if $0 { // expected-error{{closure containing control flow statement cannot be used with result builder 'TupleBuilderWithoutIf'}} "hello" } else { } } tuplifyWithoutIf(true) { a in - for x in 0..<100 { // expected-error{{closure containing control flow statement cannot be used with function builder 'TupleBuilderWithoutIf'}} + for x in 0..<100 { // expected-error{{closure containing control flow statement cannot be used with result builder 'TupleBuilderWithoutIf'}} x } } @@ -171,7 +171,7 @@ struct TupleP : P { init(_: U) {} } -@_functionBuilder +@resultBuilder struct Builder { static func buildBlock(_ stmt1: S0, _ stmt2: S1) // expected-note {{required by static method 'buildBlock' where 'S1' = 'Label<_>.Type'}} -> TupleP<(S0, S1)> where S0: P, S1: P { @@ -195,7 +195,7 @@ struct Label : P where L : P { // expected-note 2 {{'L' declared as parameter } func test_51167632() -> some P { - AnyP(G { // expected-error {{type 'Label<_>.Type' cannot conform to 'P'; only concrete types such as structs, enums and classes can conform to protocols}} + AnyP(G { // expected-error {{type 'Label<_>.Type' cannot conform to 'P'}} expected-note {{only concrete types such as structs, enums and classes can conform to protocols}} Text("hello") Label // expected-error {{generic parameter 'L' could not be inferred}} // expected-note@-1 {{explicitly specify the generic arguments to fix this issue}} {{10-10=<<#L: P#>>}} @@ -218,7 +218,7 @@ struct SR11440 { } func foo() { - // This is okay, we apply the function builder for the subscript arg. + // This is okay, we apply the result builder for the subscript arg. self[{ 5 5 @@ -276,10 +276,10 @@ tuplify(true) { x in struct MyTuplifiedStruct { var condition: Bool - @TupleBuilder var computed: some Any { // expected-note{{remove the attribute to explicitly disable the function builder}}{{3-17=}} + @TupleBuilder var computed: some Any { // expected-note{{remove the attribute to explicitly disable the result builder}}{{3-17=}} if condition { - return 17 // expected-warning{{application of function builder 'TupleBuilder' disabled by explicit 'return' statement}} - // expected-note@-1{{remove 'return' statements to apply the function builder}}{{7-14=}}{{12-19=}} + return 17 // expected-warning{{application of result builder 'TupleBuilder' disabled by explicit 'return' statement}} + // expected-note@-1{{remove 'return' statements to apply the result builder}}{{7-14=}}{{12-19=}} } else { return 42 } @@ -314,7 +314,7 @@ func checkConditions(cond: Bool) { } } -// Check that a closure with a single "return" works with function builders. +// Check that a closure with a single "return" works with result builders. func checkSingleReturn(cond: Bool) { tuplify(cond) { value in return (value, 17) @@ -331,7 +331,7 @@ func checkSingleReturn(cond: Bool) { // rdar://problem/59116520 func checkImplicitSelfInClosure() { - @_functionBuilder + @resultBuilder struct Builder { static func buildBlock(_ children: String...) -> Element { Element() } } @@ -386,7 +386,7 @@ func testSwitch(e: E) { case .b(let i, let s?): i * 2 s + "!" - fallthrough // expected-error{{closure containing control flow statement cannot be used with function builder 'TupleBuilder'}} + fallthrough // expected-error{{closure containing control flow statement cannot be used with result builder 'TupleBuilder'}} case .b(let i, nil): "just \(i)" } @@ -440,6 +440,7 @@ struct TestConstraintGenerationErrors { @TupleBuilder var nilWithoutContext: String { let a = nil // expected-error {{'nil' requires a contextual type}} + "" } func buildTupleClosure() { @@ -517,7 +518,7 @@ func testCaseVarTypes(e: E3) { } // Test for buildFinalResult. -@_functionBuilder +@resultBuilder struct WrapperBuilder { static func buildBlock() -> () { } @@ -576,11 +577,11 @@ func testWrapperBuilder() { let _: Int = x // expected-error{{cannot convert value of type 'Wrapper<(Double, String)>' to specified type 'Int'}} } -// rdar://problem/61347993 - empty function builder doesn't compile +// rdar://problem/61347993 - empty result builder doesn't compile func rdar61347993() { struct Result {} - @_functionBuilder + @resultBuilder struct Builder { static func buildBlock() -> Result { Result() @@ -648,7 +649,7 @@ struct MyView { } } -// Make sure throwing function builder closures are implied. +// Make sure throwing result builder closures are implied. enum MyError: Error { case boom } diff --git a/test/Constraints/function_builder_infer.swift b/test/Constraints/result_builder_infer.swift similarity index 76% rename from test/Constraints/function_builder_infer.swift rename to test/Constraints/result_builder_infer.swift index 1715352d39424..08150d32a528e 100644 --- a/test/Constraints/function_builder_infer.swift +++ b/test/Constraints/result_builder_infer.swift @@ -5,7 +5,7 @@ enum Either { case second(U) } -@_functionBuilder +@resultBuilder struct TupleBuilder { static func buildBlock() -> () { return () @@ -55,7 +55,7 @@ protocol Tupled { struct TupleMe: Tupled { var condition: Bool - // Okay: applies the function builder @TupleBuilder. + // Okay: applies the result builder @TupleBuilder. var tuple: some Any { "hello" if condition { @@ -67,7 +67,7 @@ struct TupleMe: Tupled { } // Witness is separated from the context declaring conformance, so don't infer -// the function builder. +// the result builder. struct DoNotTupleMe { var condition: Bool @@ -79,7 +79,7 @@ struct DoNotTupleMe { extension DoNotTupleMe: Tupled { } -@_functionBuilder +@resultBuilder struct OtherTupleBuilder { static func buildBlock() -> () { return () @@ -129,8 +129,8 @@ protocol Tupled2 { struct TupleMe2: Tupled, Tupled2 { var condition: Bool - // Okay: applies the function builder @TupleBuilder, even though it satisfies - // two requirements. (They have the same function builder) + // Okay: applies the result builder @TupleBuilder, even though it satisfies + // two requirements. (They have the same result builder) var tuple: some Any { "hello" if condition { @@ -152,10 +152,10 @@ struct AmbigTupleMe: Tupled, OtherTupled { // Ambiguous internal - var tuple: Void { // expected-error{{ambiguous function builder inferred for 'tuple': 'TupleBuilder' or 'OtherTupleBuilder'}} - // expected-note@-1{{add an explicit 'return' statement to not use a function builder}}{{3-3=return <#expr#>\n}} - // expected-note@-2{{apply function builder 'TupleBuilder' (inferred from protocol 'Tupled')}}{{3-3=@TupleBuilder }} - // expected-note@-3{{apply function builder 'OtherTupleBuilder' (inferred from protocol 'OtherTupled')}}{{3-3=@OtherTupleBuilder }} + var tuple: Void { // expected-error{{ambiguous result builder inferred for 'tuple': 'TupleBuilder' or 'OtherTupleBuilder'}} + // expected-note@-1{{add an explicit 'return' statement to not use a result builder}}{{3-3=return <#expr#>\n}} + // expected-note@-2{{apply result builder 'TupleBuilder' (inferred from protocol 'Tupled')}}{{3-3=@TupleBuilder }} + // expected-note@-3{{apply result builder 'OtherTupleBuilder' (inferred from protocol 'OtherTupled')}}{{3-3=@OtherTupleBuilder }} "hello" // expected-warning{{string literal is unused}} "world" // expected-warning{{string literal is unused}} } @@ -209,10 +209,10 @@ struct DynamicTupled2: Tupled, OtherTupled { extension DynamicTupled2 { @_dynamicReplacement(for: tuple) - var replacementTuple: some Any { // expected-error{{ambiguous function builder inferred for 'replacementTuple': 'TupleBuilder' or 'OtherTupleBuilder'}} - // expected-note@-1{{add an explicit 'return' statement to not use a function builder}} - // expected-note@-2{{apply function builder 'TupleBuilder' (inferred from protocol 'Tupled')}} - // expected-note@-3{{apply function builder 'OtherTupleBuilder' (inferred from protocol 'OtherTupled')}} + var replacementTuple: some Any { // expected-error{{ambiguous result builder inferred for 'replacementTuple': 'TupleBuilder' or 'OtherTupleBuilder'}} + // expected-note@-1{{add an explicit 'return' statement to not use a result builder}} + // expected-note@-2{{apply result builder 'TupleBuilder' (inferred from protocol 'Tupled')}} + // expected-note@-3{{apply result builder 'OtherTupleBuilder' (inferred from protocol 'OtherTupled')}} 1 } } @@ -225,10 +225,10 @@ struct DynamicTupled3 { extension DynamicTupled3: OtherTupled { @_dynamicReplacement(for: dynamicTuple) - var tuple: some Any { // expected-error{{ambiguous function builder inferred for 'tuple': 'OtherTupleBuilder' or 'TupleBuilder'}} - // expected-note@-1{{add an explicit 'return' statement to not use a function builder}} - // expected-note@-2{{apply function builder 'OtherTupleBuilder' (inferred from protocol 'OtherTupled')}} - // expected-note@-3{{apply function builder 'TupleBuilder' (inferred from dynamic replacement of 'dynamicTuple')}} + var tuple: some Any { // expected-error{{ambiguous result builder inferred for 'tuple': 'OtherTupleBuilder' or 'TupleBuilder'}} + // expected-note@-1{{add an explicit 'return' statement to not use a result builder}} + // expected-note@-2{{apply result builder 'OtherTupleBuilder' (inferred from protocol 'OtherTupled')}} + // expected-note@-3{{apply result builder 'TupleBuilder' (inferred from dynamic replacement of 'dynamicTuple')}} 0 } } diff --git a/test/Constraints/function_builder_one_way.swift b/test/Constraints/result_builder_one_way.swift similarity index 99% rename from test/Constraints/function_builder_one_way.swift rename to test/Constraints/result_builder_one_way.swift index e3519800497f1..a45c99cac066a 100644 --- a/test/Constraints/function_builder_one_way.swift +++ b/test/Constraints/result_builder_one_way.swift @@ -7,7 +7,7 @@ enum Either { case second(U) } -@_functionBuilder +@resultBuilder struct TupleBuilder { static func buildBlock(_ t1: T1) -> T1 { return t1 diff --git a/test/Constraints/function_builder_opaque_result.swift b/test/Constraints/result_builder_opaque_result.swift similarity index 96% rename from test/Constraints/function_builder_opaque_result.swift rename to test/Constraints/result_builder_opaque_result.swift index e75db6d9ea431..bbd64c830c88b 100644 --- a/test/Constraints/function_builder_opaque_result.swift +++ b/test/Constraints/result_builder_opaque_result.swift @@ -3,7 +3,7 @@ protocol Taggable {} extension String: Taggable {} -@_functionBuilder +@resultBuilder struct TaggableBuilder { static func buildBlock(_ params: Taggable...) -> String { return "Your tags weren't worth keeping anyway" diff --git a/test/Constraints/sr13183.swift b/test/Constraints/sr13183.swift index d3a33d2e5b83e..2ab0551b36f94 100644 --- a/test/Constraints/sr13183.swift +++ b/test/Constraints/sr13183.swift @@ -1,7 +1,7 @@ // RUN: %target-typecheck-verify-swift // SR-13183: Make sure we don't incorrectly split the constraint system without -// considering that a function builder type var may connect the inside of a +// considering that a result builder type var may connect the inside of a // closure body with the enclosing expression. struct New { @@ -14,7 +14,7 @@ struct Map { let transform: (Value) -> Transformed } -@_functionBuilder +@resultBuilder struct ScopeBuilder { static func buildExpression(_ map: Map) -> Component { Component() diff --git a/test/Constraints/tuple.swift b/test/Constraints/tuple.swift index ec557012b835a..2d9a1d7d2742e 100644 --- a/test/Constraints/tuple.swift +++ b/test/Constraints/tuple.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift +// RUN: %target-typecheck-verify-swift -disable-parser-lookup // Test various tuple constraints. @@ -305,6 +305,8 @@ if case (foo: let x, foo: let y) = zeroTuple { print(x+y) } // expected-error {{ // expected-warning@-1 {{'if' condition is always true}} enum BishBash { case bar(foo: Int, foo: String) } +// expected-error@-1 {{invalid redeclaration of 'foo'}} +// expected-note@-2 {{'foo' previously declared here}} let enumLabelDup: BishBash = .bar(foo: 0, foo: "") // expected-error {{cannot create a tuple with a duplicate element label}} func dupLabelClosure(_ fn: () -> Void) {} diff --git a/test/Constraints/tuple_parser_lookup.swift b/test/Constraints/tuple_parser_lookup.swift new file mode 100644 index 0000000000000..a3acaf4f356e4 --- /dev/null +++ b/test/Constraints/tuple_parser_lookup.swift @@ -0,0 +1,337 @@ +// RUN: %target-typecheck-verify-swift -enable-parser-lookup + +// Test various tuple constraints. + +func f0(x: Int, y: Float) {} + +var i : Int +var j : Int +var f : Float + +func f1(y: Float, rest: Int...) {} + +func f2(_: (_ x: Int, _ y: Int) -> Int) {} +func f2xy(x: Int, y: Int) -> Int {} +func f2ab(a: Int, b: Int) -> Int {} +func f2yx(y: Int, x: Int) -> Int {} + +func f3(_ x: (_ x: Int, _ y: Int) -> ()) {} +func f3a(_ x: Int, y: Int) {} +func f3b(_: Int) {} + +func f4(_ rest: Int...) {} +func f5(_ x: (Int, Int)) {} + +func f6(_: (i: Int, j: Int), k: Int = 15) {} + +//===----------------------------------------------------------------------===// +// Conversions and shuffles +//===----------------------------------------------------------------------===// + +func foo(a : [(some: Int, (key: Int, value: String))]) -> String { + for (i , (j, k)) in a { + if i == j { return k } + } +} + +func rdar28207648() -> [(Int, CustomStringConvertible)] { + let v : [(Int, Int)] = [] + return v as [(Int, CustomStringConvertible)] +} + +class rdar28207648Base {} +class rdar28207648Derived : rdar28207648Base {} + +func rdar28207648(x: (Int, rdar28207648Derived)) -> (Int, rdar28207648Base) { + return x as (Int, rdar28207648Base) +} + +public typealias Success = (response: T, data: V?) + +public enum Result { + case success(Success) + case error(Error) +} + + +let a = Success(response: 3, data: 3) +let success: Result = .success(a) + +// Variadic functions. +f4() +f4(1) +f4(1, 2, 3) + +f2(f2xy) +f2(f2ab) +f2(f2yx) + +f3(f3a) +f3(f3b) // expected-error{{cannot convert value of type '(Int) -> ()' to expected argument type '(Int, Int) -> ()'}} + +func getIntFloat() -> (int: Int, float: Float) {} +var values = getIntFloat() +func wantFloat(_: Float) {} +wantFloat(values.float) + +var e : (x: Int..., y: Int) // expected-error{{cannot create a variadic tuple}} + +typealias Interval = (a:Int, b:Int) +func takeInterval(_ x: Interval) {} +takeInterval(Interval(1, 2)) + +f5((1,1)) + +// Tuples with existentials +var any : Any = () +any = (1, 2) +any = (label: 4) // expected-error {{cannot create a single-element tuple with an element label}} + +// Scalars don't have .0/.1/etc +i = j.0 // expected-error{{value of type 'Int' has no member '0'}} +any.1 // expected-error{{value of type 'Any' has no member '1'}} +// expected-note@-1{{cast 'Any' to 'AnyObject' or use 'as!' to force downcast to a more specific type to access members}} +any = (5.0, 6.0) as (Float, Float) +_ = (any as! (Float, Float)).1 + +// Fun with tuples +protocol PosixErrorReturn { + static func errorReturnValue() -> Self +} + +extension Int : PosixErrorReturn { + static func errorReturnValue() -> Int { return -1 } +} + +func posixCantFail + (_ f: @escaping (A) -> T) -> (_ args:A) -> T +{ + return { args in + let result = f(args) + assert(result != T.errorReturnValue()) + return result + } +} + +func open(_ name: String, oflag: Int) -> Int { } + +var foo: Int = 0 + +var fd = posixCantFail(open)(("foo", 0)) + +// Tuples and lvalues +class C { + init() {} + func f(_: C) {} +} + +func testLValue(_ c: C) { + var c = c + c.f(c) + + let x = c + c = x +} + + +// Crash in TypeChecker::coercePatternToType +func invalidPatternCrash(_ k : Int) { + switch k { + case (k, cph_: k) as UInt8: // expected-error {{tuple pattern cannot match values of the non-tuple type 'UInt8'}} expected-warning {{cast from 'Int' to unrelated type 'UInt8' always fails}} + break + } +} + +// Tuple to tuple conversion with IdentityExpr / AnyTryExpr hang +class Paws { + init() throws {} +} + +func scruff() -> (AnyObject?, Error?) { + do { + return try (Paws(), nil) + } catch { + return (nil, error) + } +} + +// Test variadics with trailing closures. +func variadicWithTrailingClosure(_ x: Int..., y: Int = 2, fn: (Int, Int) -> Int) { +} + +variadicWithTrailingClosure(1, 2, 3) { $0 + $1 } +variadicWithTrailingClosure(1) { $0 + $1 } +variadicWithTrailingClosure() { $0 + $1 } +variadicWithTrailingClosure { $0 + $1 } + +variadicWithTrailingClosure(1, 2, 3, y: 0) { $0 + $1 } +variadicWithTrailingClosure(1, y: 0) { $0 + $1 } +variadicWithTrailingClosure(y: 0) { $0 + $1 } + +variadicWithTrailingClosure(1, 2, 3, y: 0, fn: +) +variadicWithTrailingClosure(1, y: 0, fn: +) +variadicWithTrailingClosure(y: 0, fn: +) + +variadicWithTrailingClosure(1, 2, 3, fn: +) +variadicWithTrailingClosure(1, fn: +) +variadicWithTrailingClosure(fn: +) + + +// QoI: Terrible diagnostic in tuple assignment +func gcd_23700031(_ a: T, b: T) { + var a = a + var b = b + (a, b) = (b, a % b) // expected-error {{binary operator '%' cannot be applied to two 'T' operands}} +} + +// +// Don't ignore tuple labels in same-type constraints or stronger. +protocol Kingdom { + associatedtype King +} +struct Victory { + init(_ king: K) where K.King == General {} // expected-note {{where 'General' = '(x: Int, y: Int)', 'K.King' = 'MagicKingdom<(Int, Int)>.King' (aka '(Int, Int)')}} +} +struct MagicKingdom : Kingdom { + typealias King = K +} +func magify(_ t: T) -> MagicKingdom { return MagicKingdom() } +func foo(_ pair: (Int, Int)) -> Victory<(x: Int, y: Int)> { + return Victory(magify(pair)) // expected-error {{initializer 'init(_:)' requires the types '(x: Int, y: Int)' and 'MagicKingdom<(Int, Int)>.King' (aka '(Int, Int)') be equivalent}} +} + + +// https://bugs.swift.org/browse/SR-596 +// Compiler crashes when accessing a non-existent property of a closure parameter +func call(_ f: (C) -> Void) {} +func makeRequest() { + call { obj in + print(obj.invalidProperty) // expected-error {{value of type 'C' has no member 'invalidProperty'}} + } +} + +// QoI: Misleading error message when expression result can't be inferred from closure +struct r25271859 { +} + +extension r25271859 { + func map(f: (T) -> U) -> r25271859 { + } + + func andThen(f: (T) -> r25271859) { // expected-note {{in call to function 'andThen(f:)'}} + } +} + +func f(a : r25271859<(Float, Int)>) { + a.map { $0.0 } // expected-error {{generic parameter 'U' could not be inferred}} (This is related to how solver is setup with multiple statements) + .andThen { _ in + print("hello") // comment this out and it runs, leave any form of print in and it doesn't + return r25271859() + } +} + +// LValue to rvalue conversions. + +func takesRValue(_: (Int, (Int, Int))) {} +func takesAny(_: Any) {} + +var x = 0 +var y = 0 + +let _ = (x, (y, 0)) +takesRValue((x, (y, 0))) +takesAny((x, (y, 0))) + +// SR-2600 - Closure cannot infer tuple parameter names +typealias Closure = ((a: A, b: B)) -> String + +func invoke(a: A, b: B, _ closure: Closure) { + print(closure((a, b))) +} + +invoke(a: 1, b: "B") { $0.b } + +invoke(a: 1, b: "B") { $0.1 } + +invoke(a: 1, b: "B") { (c: (a: Int, b: String)) in + return c.b +} + +invoke(a: 1, b: "B") { c in + return c.b +} + +// Crash with one-element tuple with labeled element +class Dinner {} + +func microwave() -> Dinner? { + let d: Dinner? = nil + return (n: d) // expected-error{{cannot convert return expression of type '(n: Dinner?)' to return type 'Dinner?'}} +} + +func microwave() -> Dinner { + let d: Dinner? = nil + return (n: d) // expected-error{{cannot convert return expression of type '(n: Dinner?)' to return type 'Dinner'}} +} + +// Tuple conversion with an optional +func f(b: Bool) -> (a: Int, b: String)? { + let x = 3 + let y = "" + return b ? (x, y) : nil +} + +// Single element tuple expressions +func singleElementTuple() { + let _ = (label: 123) // expected-error {{cannot create a single-element tuple with an element label}} {{12-19=}} + let _ = (label: 123).label // expected-error {{cannot create a single-element tuple with an element label}} {{12-19=}} + let _ = ((label: 123)) // expected-error {{cannot create a single-element tuple with an element label}} {{13-20=}} + let _ = ((label: 123)).label // expected-error {{cannot create a single-element tuple with an element label}} {{13-20=}} +} + +// Tuples with duplicate labels + +let dupLabel1: (foo: Int, foo: Int) = (foo: 1, foo: 2) // expected-error 2{{cannot create a tuple with a duplicate element label}} + +func dupLabel2(x a: Int, x b: Int) -> (y: Int, y: Int) { // expected-error {{cannot create a tuple with a duplicate element label}} + return (a, b) +} + +let _ = (bar: 0, bar: "") // expected-error {{cannot create a tuple with a duplicate element label}} + +let zeroTuple = (0,0) + +if case (foo: let x, foo: let y) = zeroTuple { print(x+y) } // expected-error {{cannot create a tuple with a duplicate element label}} +// expected-warning@-1 {{'if' condition is always true}} + +enum BishBash { case bar(foo: Int, foo: String) } +let enumLabelDup: BishBash = .bar(foo: 0, foo: "") // expected-error {{cannot create a tuple with a duplicate element label}} + +func dupLabelClosure(_ fn: () -> Void) {} +dupLabelClosure { print((bar: "", bar: 5).bar) } // expected-error {{cannot create a tuple with a duplicate element label}} + +struct DupLabelSubscript { + subscript(foo x: Int, foo y: Int) -> Int { + return 0 + } +} + +let dupLabelSubscriptStruct = DupLabelSubscript() +let _ = dupLabelSubscriptStruct[foo: 5, foo: 5] // ok + +// SR-12869 + +var dict: [String: (Int, Int)] = [:] +let bignum: Int64 = 1337 +dict["test"] = (bignum, 1) // expected-error {{cannot assign value of type '(Int64, Int)' to subscript of type '(Int, Int)'}} + +var tuple: (Int, Int) +tuple = (bignum, 1) // expected-error {{cannot assign value of type '(Int64, Int)' to type '(Int, Int)'}} + +var optionalTuple: (Int, Int)? +var optionalTuple2: (Int64, Int)? = (bignum, 1) +var optionalTuple3: (UInt64, Int)? = (bignum, 1) // expected-error {{cannot convert value of type '(Int64, Int)' to specified type '(UInt64, Int)?'}} + +optionalTuple = (bignum, 1) // expected-error {{cannot assign value of type '(Int64, Int)' to type '(Int, Int)'}} +// Optional to Optional +optionalTuple = optionalTuple2 // expected-error {{cannot assign value of type '(Int64, Int)?' to type '(Int, Int)?'}} diff --git a/test/CrossImport/Inputs/rewrite-module-triples.py b/test/CrossImport/Inputs/rewrite-module-triples.py index f76ef435e8466..2a6482b56ed29 100644 --- a/test/CrossImport/Inputs/rewrite-module-triples.py +++ b/test/CrossImport/Inputs/rewrite-module-triples.py @@ -17,6 +17,9 @@ sys.exit(1) root_dir = sys.argv[1] +if platform.system() == 'Windows': + root_dir = "\\\\?\\" + os.path.abspath(root_dir) + triples = sys.argv[2:] @@ -37,16 +40,9 @@ def rewrite(parent, names, copy_fn, rm_fn): for new_name in new_names: new_path = os.path.join(parent, new_name) - if platform.system() == 'Windows': - copy_fn(u'\\'.join([u'\\\\?', os.path.normpath(path)]), - u'\\'.join([u'\\\\?', os.path.normpath(new_path)])) - else: - copy_fn(path, new_path) - - if platform.system() == 'Windows': - rm_fn(u'\\'.join([u'\\\\?', os.path.normpath(path)])) - else: - rm_fn(path) + copy_fn(path, new_path) + + rm_fn(path) for parent, dirs, files in os.walk(root_dir, topdown=False): diff --git a/test/CrossImport/common-case.swift b/test/CrossImport/common-case.swift index f29a547e6004e..7ac70d60823cf 100644 --- a/test/CrossImport/common-case.swift +++ b/test/CrossImport/common-case.swift @@ -5,10 +5,13 @@ // RUN: cp -r %S/Inputs/lib-templates/* %t/ // RUN: %{python} %S/Inputs/rewrite-module-triples.py %t %module-target-triple -// RUN: %target-typecheck-verify-swift -enable-cross-import-overlays -I %t/include -I %t/lib/swift -F %t/Frameworks +// RUN: %target-typecheck-verify-swift -enable-cross-import-overlays -DIMPORT_BYSTANDING_LIBRARY -I %t/include -I %t/lib/swift -F %t/Frameworks +// RUN: %target-typecheck-verify-swift -enable-cross-import-overlays -import-module BystandingLibrary -I %t/include -I %t/lib/swift -F %t/Frameworks // Each framework has a cross-import overlay with this library: +#if IMPORT_BYSTANDING_LIBRARY import BystandingLibrary +#endif // 1. A Swift framework diff --git a/test/DebugInfo/dbgvalue-insertpt.swift b/test/DebugInfo/dbgvalue-insertpt.swift index 0d670e4bb7dba..469f1223f4a93 100644 --- a/test/DebugInfo/dbgvalue-insertpt.swift +++ b/test/DebugInfo/dbgvalue-insertpt.swift @@ -1,19 +1,9 @@ -// RUN: %target-swift-frontend -g -emit-ir %s | %FileCheck %s +// RUN: %target-swift-frontend -g -emit-ir -Xllvm '-sil-inline-never-functions=next' %s | %FileCheck %s // FIXME: This test should be testing a non-shadow-copied value instead. for i in 0 ..< 3 { // CHECK: %[[ALLOCA:[0-9]+]] = alloca %TSiSg // CHECK: %i.debug = alloca i{{32|64}} // CHECK-NEXT: call void @llvm.dbg.declare(metadata i{{32|64}}* %i.debug, // CHECK-SAME: metadata ![[I:[0-9]+]], - // CHECK: call swiftcc{{.*}} @{{.*}}next{{.*}} - // CHECK: %[[LD:[0-9]+]] = load i{{32|64}}, i{{32|64}}* - // CHECK: br i1 {{%.*}}, label %[[FAIL:.*]], label %[[SUCCESS:.*]], - // - // CHECK: [[SUCCESS]]: - // CHECK: br label %[[NEXT_BB:.*]], - // - // CHECK: [[NEXT_BB]]: - // CHECK: %[[PHI_VAL:.*]] = phi i{{32|64}} [ %[[LD]], %[[SUCCESS]] ] - // CHECK: store i{{32|64}} %[[PHI_VAL]], i{{32|64}}* %i.debug // CHECK: ![[I]] = !DILocalVariable(name: "i", } diff --git a/test/DebugInfo/inlined-generics-basic.swift b/test/DebugInfo/inlined-generics-basic.swift index 5dddba06245a5..9fa4f404bbd9c 100644 --- a/test/DebugInfo/inlined-generics-basic.swift +++ b/test/DebugInfo/inlined-generics-basic.swift @@ -93,7 +93,9 @@ public class C { // IR: ![[BOOL:[0-9]+]] = !DICompositeType({{.*}}name: "Bool" // IR: ![[INT:[0-9]+]] = !DICompositeType({{.*}}name: "Int" // IR: ![[LET_INT:[0-9]+]] = !DIDerivedType(tag: DW_TAG_const_type, baseType: ![[INT]]) +// SWIFT_ENABLE_TENSORFLOW // IR: ![[LET_BOOL:[0-9]+]] = !DIDerivedType(tag: DW_TAG_const_type, baseType: ![[BOOL]]) +// SWIFT_ENABLE_TENSORFLOW END // IR: ![[TAU_0_0:[0-9]+]] = {{.*}}DW_TAG_structure_type, name: "$sxD", // IR: ![[LET_TAU_0_0:[0-9]+]] = !DIDerivedType(tag: DW_TAG_const_type, baseType: ![[TAU_0_0]]) // IR: ![[TAU_1_0:[0-9]+]] = {{.*}}DW_TAG_structure_type, name: "$sqd__D", diff --git a/test/DebugInfo/mandatoryinlining-wrongdebugscope.swift b/test/DebugInfo/mandatoryinlining-wrongdebugscope.swift deleted file mode 100644 index 1505ff8b39fe3..0000000000000 --- a/test/DebugInfo/mandatoryinlining-wrongdebugscope.swift +++ /dev/null @@ -1,22 +0,0 @@ -// RUN: %target-swift-frontend -emit-sil %s -Onone -Xllvm \ -// RUN: -sil-print-after=mandatory-inlining \ -// RUN: -Xllvm -sil-print-debuginfo -o /dev/null 2>&1 | %FileCheck %s - -// CHECK: destroy_value {{.*}} : $@callee_guaranteed () -> (), loc {{.*}}:21:5, scope [[SCOPE:[0-9]+]] -// CHECK: destroy_value {{.*}} : $@callee_guaranteed () -> @out (), loc {{.*}}:21:5, scope [[SCOPE]] -// CHECK: destroy_value {{.*}} : $@callee_guaranteed () -> (), loc {{.*}}:21:5, scope [[SCOPE]] - -func patatino(_ function: @escaping (d) -> i, g: d...) -> () -> i { - return { typealias h = ([d]) -> i - let f = unsafeBitCast(function, to: h.self) - return f(g) - } -} -func tinky() { - for b in 1...50{ - let c = {} - let e = patatino(c) - e() - let f = {} - } -} diff --git a/test/Demangle/Inputs/manglings-with-clang-types.txt b/test/Demangle/Inputs/manglings-with-clang-types.txt new file mode 100644 index 0000000000000..2dca7def1cc42 --- /dev/null +++ b/test/Demangle/Inputs/manglings-with-clang-types.txt @@ -0,0 +1,2 @@ +$s3tmp1fyySiyXzC9_ZTSPFmvEF ---> tmp.f(@convention(c, mangledCType: "_ZTSPFmvE") () -> Swift.Int) -> () +$s3tmp1hyyySbXzB24_ZTSU13block_pointerFvaEF ---> tmp.h(@convention(block, mangledCType: "_ZTSU13block_pointerFvaE") (Swift.Bool) -> ()) -> () diff --git a/test/Demangle/Inputs/manglings.txt b/test/Demangle/Inputs/manglings.txt index 7f410eec10faa..294e7a08a34c2 100644 --- a/test/Demangle/Inputs/manglings.txt +++ b/test/Demangle/Inputs/manglings.txt @@ -357,6 +357,8 @@ $s3red4testyAA3ResOyxSayq_GAEs5ErrorAAq_sAFHD1__HCg_GADyxq_GsAFR_r0_lF ---> red. $s3red4testyAA7OurTypeOy4them05TheirD0Vy5AssocQzGAjE0F8ProtocolAAxAA0c7DerivedH0HD1_AA0c4BaseH0HI1_AieKHA2__HCg_GxmAaLRzlF ---> red.test(A.Type) -> red.OurType> $s17property_wrappers10WithTuplesV9fractionsSd_S2dtvpfP ---> property wrapper backing initializer of property_wrappers.WithTuples.fractions : (Swift.Double, Swift.Double, Swift.Double) $sSo17OS_dispatch_queueC4sync7executeyyyXE_tFTOTA ---> {T:$sSo17OS_dispatch_queueC4sync7executeyyyXE_tFTO} partial apply forwarder for @nonobjc __C.OS_dispatch_queue.sync(execute: () -> ()) -> () +$s4main1gyySiXCvp ---> main.g : @convention(c) (Swift.Int) -> () +$s4main1gyySiXBvp ---> main.g : @convention(block) (Swift.Int) -> () $sxq_Idgnr_D ---> @differentiable @callee_guaranteed (@in_guaranteed A) -> (@out B) $sxq_Ilgnr_D ---> @differentiable(linear) @callee_guaranteed (@in_guaranteed A) -> (@out B) $sS3fIedgyywd_D ---> @escaping @differentiable @callee_guaranteed (@unowned Swift.Float, @unowned @noDerivative Swift.Float) -> (@unowned Swift.Float) diff --git a/test/Demangle/clang-function-types.swift b/test/Demangle/clang-function-types.swift new file mode 100644 index 0000000000000..df7a4993febcc --- /dev/null +++ b/test/Demangle/clang-function-types.swift @@ -0,0 +1,39 @@ +// NOTE: manglings.txt should be kept in sync with the manglings in this file. + +// Make sure we are testing the right manglings. +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) %s -emit-sil -o - -I %S/../Inputs/custom-modules -use-clang-function-types -module-name tmp -enable-objc-interop | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-os-%target-cpu + +// Check that demangling works. + +// %t.input: "A ---> B" ==> "A" +// RUN: sed -ne '/--->/s/ *--->.*$//p' < %S/Inputs/manglings-with-clang-types.txt > %t.input +// %t.check: "A ---> B" ==> "B" +// RUN: sed -ne '/--->/s/^.*---> *//p' < %S/Inputs/manglings-with-clang-types.txt > %t.check +// RUN: swift-demangle -classify < %t.input > %t.output +// RUN: diff %t.check %t.output + +// Other tests already check mangling for Windows, so we don't need to +// check that here again. + +// UNSUPPORTED: OS=windows-msvc + +import ctypes + +#if os(macOS) && arch(x86_64) +import ObjectiveC + +// BOOL == signed char on x86_64 macOS +public func h(_ k: @convention(block, cType: "void (^)(BOOL)") (Bool) -> ()) { + let _ = k(true) +} +h(A.setGlobal) // OK: check that importing preserves Clang types + +// CHECK-macosx-x86_64: sil @$s3tmp1hyyySbXzB24_ZTSU13block_pointerFvaEF +#endif + +public func f(_ k: @convention(c, cType: "size_t (*)(void)") () -> Int) { + let _ = k() +} +f(ctypes.returns_size_t) // OK: check that importing preserves Clang type + +// CHECK: sil @$s3tmp1fyySiyXzC9_ZTSPFmvEF diff --git a/test/Driver/Dependencies/Inputs/moduleonly/bar.swift b/test/Driver/Dependencies/Inputs/moduleonly/bar.swift deleted file mode 100644 index dd3d6dee17e0a..0000000000000 --- a/test/Driver/Dependencies/Inputs/moduleonly/bar.swift +++ /dev/null @@ -1,28 +0,0 @@ -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 deleted file mode 100644 index d50c4318edf42..0000000000000 --- a/test/Driver/Dependencies/Inputs/moduleonly/baz.swift +++ /dev/null @@ -1,20 +0,0 @@ -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 deleted file mode 100644 index 4fda8eea4859d..0000000000000 --- a/test/Driver/Dependencies/Inputs/moduleonly/foo.swift +++ /dev/null @@ -1,28 +0,0 @@ -@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 deleted file mode 100644 index 940a7ab5db703..0000000000000 --- a/test/Driver/Dependencies/Inputs/moduleonly/output.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "./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 deleted file mode 100644 index a2c38a2214e76..0000000000000 --- a/test/Driver/Dependencies/moduleonly.swift +++ /dev/null @@ -1,85 +0,0 @@ -// RUN: %empty-directory(%t) -// RUN: 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: %empty-directory(%t) -// RUN: 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: %empty-directory(%t) -// RUN: 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: %empty-directory(%t) -// RUN: 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: %empty-directory(%t) -// RUN: 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/Dependencies/one-way-merge-module-fine.swift b/test/Driver/Dependencies/one-way-merge-module-fine.swift index 4e1bcb5c4b182..5ef38421fc5bc 100644 --- a/test/Driver/Dependencies/one-way-merge-module-fine.swift +++ b/test/Driver/Dependencies/one-way-merge-module-fine.swift @@ -15,4 +15,4 @@ // CHECK-SECOND-NOT: warning // CHECK-SECOND-NOT: Handled -// CHECK-SECOND: Produced master.swiftmodule +// CHECK-SECOND-NOT: Produced master.swiftmodule diff --git a/test/Driver/Inputs/MacOSX10.15.4.versioned.sdk/SDKSettings.json b/test/Driver/Inputs/MacOSX10.15.4.versioned.sdk/SDKSettings.json index 618eaced4bb51..35383d42256e9 100644 --- a/test/Driver/Inputs/MacOSX10.15.4.versioned.sdk/SDKSettings.json +++ b/test/Driver/Inputs/MacOSX10.15.4.versioned.sdk/SDKSettings.json @@ -23,5 +23,7 @@ "13.2" : "10.15.1", "13.4" : "10.15.4" } - } + }, + "DefaultDeploymentTarget" : "11.0", + "MaximumDeploymentTarget" : "11.0.99" } diff --git a/test/Driver/Inputs/MacOSX10.15.versioned.sdk/SDKSettings.json b/test/Driver/Inputs/MacOSX10.15.versioned.sdk/SDKSettings.json index cc26f6bcfac37..c14072ff3272b 100644 --- a/test/Driver/Inputs/MacOSX10.15.versioned.sdk/SDKSettings.json +++ b/test/Driver/Inputs/MacOSX10.15.versioned.sdk/SDKSettings.json @@ -9,5 +9,7 @@ "13.1" : "10.15", "13.2" : "10.15.1" } - } + }, + "DefaultDeploymentTarget" : "10.15", + "MaximumDeploymentTarget" : "10.15.99" } diff --git a/test/Driver/cross_module.swift b/test/Driver/cross_module.swift new file mode 100644 index 0000000000000..ca07a5b10b0d3 --- /dev/null +++ b/test/Driver/cross_module.swift @@ -0,0 +1,6 @@ +// RUN: %empty-directory(%t/sub) +// RUN: echo "{\"\": {\"swift-dependencies\": \"cross_module.swiftdeps\"}}" > %t/ofm.json +// RUN: %swiftc_driver -incremental -emit-module -module-name cross_module -output-file-map=%/t/ofm.json -driver-print-jobs -target x86_64-apple-macosx10.9 %s -enable-experimental-cross-module-incremental-build 2>^1 | %FileCheck -check-prefix ENABLE %s + +// ENABLE: {{bin(/|\\\\)swift(-frontend|c)?(\.exe)?"?}} -frontend -merge-modules +// ENABLE-SAME: -enable-experimental-cross-module-incremental-build diff --git a/test/Driver/working-directory.swift b/test/Driver/working-directory.swift index e078172ed12d9..67657d9533b05 100644 --- a/test/Driver/working-directory.swift +++ b/test/Driver/working-directory.swift @@ -57,9 +57,9 @@ // -output-file-map itself // RUN: echo "{\"main.swift\": {\"object\": \"main-modified.o\"}}" > %t/ofmo2.json // RUN: touch %t/main.swift -// RUN: cd %S && %swiftc_driver -driver-print-jobs -working-directory %/t -c main.swift -output-file-map ofmo2.json | %FileCheck %s -check-prefix=OUTPUT_FILE_MAP_2 +// RUN: cd %S && %swiftc_driver -driver-print-jobs -working-directory %/t -c main.swift -output-file-map ofmo2.json | %FileCheck %s -check-prefix=OUTPUT_FILE_MAP_2 --enable-yaml-compatibility // -output-file-map= is an alias for -output-file-map -// RUN: cd %S && %swiftc_driver -driver-print-jobs -working-directory %/t -c main.swift -output-file-map=ofmo2.json | %FileCheck %s -check-prefix=OUTPUT_FILE_MAP_2 +// RUN: cd %S && %swiftc_driver -driver-print-jobs -working-directory %/t -c main.swift -output-file-map=ofmo2.json | %FileCheck %s -check-prefix=OUTPUT_FILE_MAP_2 --enable-yaml-compatibility // OUTPUT_FILE_MAP_2: BUILD_DIR{{.*}}main-modified.o // RUN: %empty-directory(%t/sub) diff --git a/test/Frontend/PrivateFingerprints/class-fingerprint.swift b/test/Frontend/PrivateFingerprints/class-fingerprint.swift deleted file mode 100644 index 67b29178057e4..0000000000000 --- a/test/Frontend/PrivateFingerprints/class-fingerprint.swift +++ /dev/null @@ -1,73 +0,0 @@ - -// Test per-type-body fingerprints for classes -// - -// ============================================================================= -// Without the fingerprints -// ============================================================================= - -// Establish status quo - - -// RUN: %empty-directory(%t) -// RUN: cp %S/Inputs/class-fingerprint/* %t -// RUN: cp %t/definesAB{-before,}.swift - -// Seeing weird failure on CI, so set the mod times -// RUN: touch -t 200101010101 %t/*.swift - -// RUN: cd %t && %target-swiftc_driver -disable-type-fingerprints -enable-batch-mode -j2 -incremental -driver-show-incremental main.swift definesAB.swift usesA.swift usesB.swift -module-name main -output-file-map ofm.json >&output1 - -// only-run-for-debugging: cp %t/usesB.swiftdeps %t/usesB1.swiftdeps - -// Change one type, but uses of all types get recompiled - -// RUN: cp %t/definesAB{-after,}.swift - -// Seeing weird failure on CI, so ensure that definesAB.swift is newer -// RUN: touch -t 200201010101 %t/* -// RUN: touch -t 200101010101 %t/*.swift -// RUN: touch -t 200301010101 %t/definesAB.swift - -// RUN: cd %t && %target-swiftc_driver -disable-type-fingerprints -enable-batch-mode -j2 -incremental -driver-show-incremental main.swift definesAB.swift usesA.swift usesB.swift -module-name main -output-file-map ofm.json >&output2 - -// Save for debugging: -// only-run-for-debugging: cp %t/usesB.swiftdeps %t/usesB1.swiftdeps - -// RUN: %FileCheck -check-prefix=CHECK-MAINAB-RECOMPILED %s < %t/output2 - -// CHECK-MAINAB-RECOMPILED: Queuing (initial): {compile: definesAB.o <= definesAB.swift} -// CHECK-MAINAB-RECOMPILED: Queuing because of dependencies discovered later: {compile: usesA.o <= usesA.swift} - -// ============================================================================= -// With the fingerprints -// ============================================================================= - -// Establish status quo - -// RUN: %empty-directory(%t) -// RUN: cp %S/Inputs/class-fingerprint/* %t -// RUN: cp %t/definesAB{-before,}.swift - -// Seeing weird failure on CI, so set the mod times -// RUN: touch -t 200101010101 %t/*.swift - -// RUN: cd %t && %target-swiftc_driver -enable-batch-mode -j2 -incremental -driver-show-incremental main.swift definesAB.swift usesA.swift usesB.swift -module-name main -output-file-map ofm.json >&output3 - -// only-run-for-debugging: cp %t/usesB.swiftdeps %t/usesB3.swiftdeps - - -// Change one type, only uses of that type get recompiled - -// RUN: cp %t/definesAB{-after,}.swift - -// Seeing weird failure on CI, so ensure that definesAB.swift is newer -// RUN: touch -t 200201010101 %t/* -// RUN: touch -t 200101010101 %t/*.swift -// RUN: touch -t 200301010101 %t/definesAB.swift - -// RUN: cd %t && %target-swiftc_driver -enable-batch-mode -j2 -incremental -driver-show-incremental main.swift definesAB.swift usesA.swift usesB.swift -module-name main -output-file-map ofm.json >&output4 - -// only-run-for-debugging: cp %t/usesB.swiftdeps %t/usesB4.swiftdeps - -// RUN: %FileCheck -check-prefix=CHECK-MAINAB-RECOMPILED %s < %t/output4 diff --git a/test/Frontend/PrivateFingerprints/enum-fingerprint.swift b/test/Frontend/PrivateFingerprints/enum-fingerprint.swift deleted file mode 100644 index 138d16f211a88..0000000000000 --- a/test/Frontend/PrivateFingerprints/enum-fingerprint.swift +++ /dev/null @@ -1,73 +0,0 @@ - -// Test per-type-body fingerprints for enums -// - -// ============================================================================= -// Without the fingerprints -// ============================================================================= - -// Establish status quo - - -// RUN: %empty-directory(%t) -// RUN: cp %S/Inputs/enum-fingerprint/* %t -// RUN: cp %t/definesAB{-before,}.swift - -// Seeing weird failure on CI, so set the mod times -// RUN: touch -t 200101010101 %t/*.swift - -// RUN: cd %t && %target-swiftc_driver -disable-type-fingerprints -enable-batch-mode -j2 -incremental -driver-show-incremental main.swift definesAB.swift usesA.swift usesB.swift -module-name main -output-file-map ofm.json >&output1 - -// only-run-for-debugging: cp %t/usesB.swiftdeps %t/usesB1.swiftdeps - -// Change one type, but uses of all types get recompiled - -// RUN: cp %t/definesAB{-after,}.swift - -// Seeing weird failure on CI, so ensure that definesAB.swift is newer -// RUN: touch -t 200201010101 %t/* -// RUN: touch -t 200101010101 %t/*.swift -// RUN: touch -t 200301010101 %t/definesAB.swift - -// RUN: cd %t && %target-swiftc_driver -disable-type-fingerprints -enable-batch-mode -j2 -incremental -driver-show-incremental main.swift definesAB.swift usesA.swift usesB.swift -module-name main -output-file-map ofm.json >&output2 - -// Save for debugging: -// only-run-for-debugging: cp %t/usesB.swiftdeps %t/usesB1.swiftdeps - -// RUN: %FileCheck -check-prefix=CHECK-MAINAB-RECOMPILED %s < %t/output2 - -// CHECK-MAINAB-RECOMPILED: Queuing (initial): {compile: definesAB.o <= definesAB.swift} -// CHECK-MAINAB-RECOMPILED: Queuing because of dependencies discovered later: {compile: usesA.o <= usesA.swift} - -// ============================================================================= -// With the fingerprints -// ============================================================================= - -// Establish status quo - -// RUN: %empty-directory(%t) -// RUN: cp %S/Inputs/enum-fingerprint/* %t -// RUN: cp %t/definesAB{-before,}.swift - -// Seeing weird failure on CI, so set the mod times -// RUN: touch -t 200101010101 %t/*.swift - -// RUN: cd %t && %target-swiftc_driver -enable-batch-mode -j2 -incremental -driver-show-incremental main.swift definesAB.swift usesA.swift usesB.swift -module-name main -output-file-map ofm.json >&output3 - -// only-run-for-debugging: cp %t/usesB.swiftdeps %t/usesB3.swiftdeps - - -// Change one type, only uses of that type get recompiled - -// RUN: cp %t/definesAB{-after,}.swift - -// Seeing weird failure on CI, so ensure that definesAB.swift is newer -// RUN: touch -t 200201010101 %t/* -// RUN: touch -t 200101010101 %t/*.swift -// RUN: touch -t 200301010101 %t/definesAB.swift - -// RUN: cd %t && %target-swiftc_driver -enable-batch-mode -j2 -incremental -driver-show-incremental main.swift definesAB.swift usesA.swift usesB.swift -module-name main -output-file-map ofm.json >&output4 - -// only-run-for-debugging: cp %t/usesB.swiftdeps %t/usesB4.swiftdeps - -// RUN: %FileCheck -check-prefix=CHECK-MAINAB-RECOMPILED %s < %t/output4 diff --git a/test/Frontend/PrivateFingerprints/extension-adds-member.swift b/test/Frontend/PrivateFingerprints/extension-adds-member.swift deleted file mode 100644 index 63726480b3c37..0000000000000 --- a/test/Frontend/PrivateFingerprints/extension-adds-member.swift +++ /dev/null @@ -1,83 +0,0 @@ - -// Test per-type-body fingerprints using simple extensions -// -// If the parser is allowed to use a body fingerprint for an extension -// this test will fail because usesA.swift won't be recompiled for the -// last step. - - -// ============================================================================= -// Without the fingerprints -// ============================================================================= - -// Establish status quo - - -// RUN: %empty-directory(%t) -// RUN: cp %S/Inputs/extension-adds-member/* %t -// RUN: cp %t/definesAB{-before,}.swift - -// Seeing weird failure on CI, so set the mod times -// RUN: touch -t 200101010101 %t/*.swift - - -// RUN: cd %t && %target-swiftc_driver -disable-type-fingerprints -enable-batch-mode -j2 -incremental -driver-show-incremental main.swift definesAB.swift usesA.swift usesB.swift -module-name main -output-file-map ofm.json >& %t/output1 - - -// Change one type, but uses of all types get recompiled - -// RUN: cp %t/definesAB{-after,}.swift - -// Seeing weird failure on CI, so ensure that definesAB.swift is newer -// RUN: touch -t 200201010101 %t/* -// RUN: touch -t 200101010101 %t/*.swift -// RUN: touch -t 200301010101 %t/definesAB.swift - -// RUN: cd %t && %target-swiftc_driver -disable-type-fingerprints -enable-batch-mode -j2 -incremental -driver-show-incremental main.swift definesAB.swift usesA.swift usesB.swift -module-name main -output-file-map ofm.json >& %t/output2 - - -// This test checks for the status quo; it would be OK to be more conservative - -// RUN: %FileCheck -check-prefix=CHECK-RECOMPILED-WO %s < %t/output2 -// CHECK-RECOMPILED-WO: {compile: definesAB.o <= definesAB.swift} -// CHECK-RECOMPILED-WO: {compile: usesA.o <= usesA.swift} -// CHECK-RECOMPILED-WO: {compile: usesB.o <= usesB.swift} - -// RUN: %FileCheck -check-prefix=CHECK-NOT-RECOMPILED-WO %s < %t/output2 -// CHECK-NOT-RECOMPILED-WO-NOT: {compile: main.o <= main.swift} - - -// ============================================================================= -// With the fingerprints -// ============================================================================= - -// Establish status quo - -// RUN: %empty-directory(%t) -// RUN: cp %S/Inputs/extension-adds-member/* %t -// RUN: cp %t/definesAB{-before,}.swift - -// Seeing weird failure on CI, so set the mod times -// RUN: touch -t 200101010101 %t/*.swift - -// RUN: cd %t && %target-swiftc_driver -enable-batch-mode -j2 -incremental -driver-show-incremental main.swift definesAB.swift usesA.swift usesB.swift -module-name main -output-file-map ofm.json >& %t/output3 - -// Change one type, only uses of that type get recompiled - -// RUN: cp %t/definesAB{-after,}.swift - -// Seeing weird failure on CI, so ensure that definesAB.swift is newer -// RUN: touch -t 200201010101 %t/* -// RUN: touch -t 200101010101 %t/*.swift -// RUN: touch -t 200301010101 %t/definesAB.swift - -// RUN: cd %t && %target-swiftc_driver -enable-batch-mode -j2 -incremental -driver-show-incremental main.swift definesAB.swift usesA.swift usesB.swift -module-name main -output-file-map ofm.json >& %t/output4 - -// RUN: %FileCheck -check-prefix=CHECK-RECOMPILED-W %s < %t/output4 -// RUN: %FileCheck -check-prefix=CHECK-NOT-RECOMPILED-W %s < %t/output4 - -// CHECK-RECOMPILED-W: {compile: definesAB.o <= definesAB.swift} -// CHECK-RECOMPILED-W: {compile: usesA.o <= usesA.swift} - - -// CHECK-NOT-RECOMPILED-W-NOT: {compile: main.o <= main.swift} diff --git a/test/Frontend/PrivateFingerprints/protocol-fingerprint.swift b/test/Frontend/PrivateFingerprints/protocol-fingerprint.swift deleted file mode 100644 index a48649f0b11ee..0000000000000 --- a/test/Frontend/PrivateFingerprints/protocol-fingerprint.swift +++ /dev/null @@ -1,73 +0,0 @@ - -// Test per-type-body fingerprints: the protocol case. -// - -// ============================================================================= -// Without the fingerprints -// ============================================================================= - -// Establish status quo - - -// RUN: %empty-directory(%t) -// RUN: cp %S/Inputs/protocol-fingerprint/* %t -// RUN: cp %t/definesAB{-before,}.swift - -// Seeing weird failure on CI, so set the mod times -// RUN: touch -t 200101010101 %t/*.swift - -// RUN: cd %t && %target-swiftc_driver -disable-type-fingerprints -enable-batch-mode -j2 -incremental -driver-show-incremental main.swift definesAB.swift usesA.swift usesB.swift -module-name main -output-file-map ofm.json >&output1 - -// only-run-for-debugging: cp %t/usesB.swiftdeps %t/usesB1.swiftdeps - -// Change one type, but uses of all types get recompiled - -// RUN: cp %t/definesAB{-after,}.swift - -// Seeing weird failure on CI, so ensure that definesAB.swift is newer -// RUN: touch -t 200201010101 %t/* -// RUN: touch -t 200101010101 %t/*.swift -// RUN: touch -t 200301010101 %t/definesAB.swift - -// RUN: cd %t && %target-swiftc_driver -disable-type-fingerprints -enable-batch-mode -j2 -incremental -driver-show-incremental main.swift definesAB.swift usesA.swift usesB.swift -module-name main -output-file-map ofm.json >&output2 - -// Save for debugging: -// only-run-for-debugging: cp %t/usesB.swiftdeps %t/usesB1.swiftdeps - -// RUN: %FileCheck -check-prefix=CHECK-MAINAB-RECOMPILED %s < %t/output2 - -// CHECK-MAINAB-RECOMPILED: Queuing (initial): {compile: definesAB.o <= definesAB.swift} -// CHECK-MAINAB-RECOMPILED: Queuing because of dependencies discovered later: {compile: usesA.o <= usesA.swift} - -// ============================================================================= -// With the fingerprints -// ============================================================================= - -// Establish status quo - -// RUN: %empty-directory(%t) -// RUN: cp %S/Inputs/protocol-fingerprint/* %t -// RUN: cp %t/definesAB{-before,}.swift - -// Seeing weird failure on CI, so set the mod times -// RUN: touch -t 200101010101 %t/*.swift - -// RUN: cd %t && %target-swiftc_driver -enable-batch-mode -j2 -incremental -driver-show-incremental main.swift definesAB.swift usesA.swift usesB.swift -module-name main -output-file-map ofm.json >&output3 - -// only-run-for-debugging: cp %t/usesB.swiftdeps %t/usesB3.swiftdeps - - -// Change one type, only uses of that type get recompiled - -// RUN: cp %t/definesAB{-after,}.swift - -// Seeing weird failure on CI, so ensure that definesAB.swift is newer -// RUN: touch -t 200201010101 %t/* -// RUN: touch -t 200101010101 %t/*.swift -// RUN: touch -t 200301010101 %t/definesAB.swift - -// RUN: cd %t && %target-swiftc_driver -enable-batch-mode -j2 -incremental -driver-show-incremental main.swift definesAB.swift usesA.swift usesB.swift -module-name main -output-file-map ofm.json >&output4 - -// only-run-for-debugging: cp %t/usesB.swiftdeps %t/usesB4.swiftdeps - -// RUN: %FileCheck -check-prefix=CHECK-MAINAB-RECOMPILED %s < %t/output4 diff --git a/test/Frontend/PrivateFingerprints/struct-fingerprint.swift b/test/Frontend/PrivateFingerprints/struct-fingerprint.swift deleted file mode 100644 index a02cef5fd649e..0000000000000 --- a/test/Frontend/PrivateFingerprints/struct-fingerprint.swift +++ /dev/null @@ -1,73 +0,0 @@ - -// Test per-type-body fingerprints for structs -// - -// ============================================================================= -// Without the fingerprints -// ============================================================================= - -// Establish status quo - - -// RUN: %empty-directory(%t) -// RUN: cp %S/Inputs/struct-fingerprint/* %t -// RUN: cp %t/definesAB{-before,}.swift - -// Seeing weird failure on CI, so set the mod times -// RUN: touch -t 200101010101 %t/*.swift - -// RUN: cd %t && %target-swiftc_driver -disable-type-fingerprints -enable-batch-mode -j2 -incremental -driver-show-incremental main.swift definesAB.swift usesA.swift usesB.swift -module-name main -output-file-map ofm.json >&output1 - -// only-run-for-debugging: cp %t/usesB.swiftdeps %t/usesB1.swiftdeps - -// Change one type, but uses of all types get recompiled - -// RUN: cp %t/definesAB{-after,}.swift - -// Seeing weird failure on CI, so ensure that definesAB.swift is newer -// RUN: touch -t 200201010101 %t/* -// RUN: touch -t 200101010101 %t/*.swift -// RUN: touch -t 200301010101 %t/definesAB.swift - -// RUN: cd %t && %target-swiftc_driver -disable-type-fingerprints -enable-batch-mode -j2 -incremental -driver-show-incremental main.swift definesAB.swift usesA.swift usesB.swift -module-name main -output-file-map ofm.json >&output2 - -// Save for debugging: -// only-run-for-debugging: cp %t/usesB.swiftdeps %t/usesB1.swiftdeps - -// RUN: %FileCheck -check-prefix=CHECK-MAINAB-RECOMPILED %s < %t/output2 - -// CHECK-MAINAB-RECOMPILED: Queuing (initial): {compile: definesAB.o <= definesAB.swift} -// CHECK-MAINAB-RECOMPILED: Queuing because of dependencies discovered later: {compile: usesA.o <= usesA.swift} - -// ============================================================================= -// With the fingerprints -// ============================================================================= - -// Establish status quo - -// RUN: %empty-directory(%t) -// RUN: cp %S/Inputs/struct-fingerprint/* %t -// RUN: cp %t/definesAB{-before,}.swift - -// Seeing weird failure on CI, so set the mod times -// RUN: touch -t 200101010101 %t/*.swift - -// RUN: cd %t && %target-swiftc_driver -enable-batch-mode -j2 -incremental -driver-show-incremental main.swift definesAB.swift usesA.swift usesB.swift -module-name main -output-file-map ofm.json >&output3 - -// only-run-for-debugging: cp %t/usesB.swiftdeps %t/usesB3.swiftdeps - - -// Change one type, only uses of that type get recompiled - -// RUN: cp %t/definesAB{-after,}.swift - -// Seeing weird failure on CI, so ensure that definesAB.swift is newer -// RUN: touch -t 200201010101 %t/* -// RUN: touch -t 200101010101 %t/*.swift -// RUN: touch -t 200301010101 %t/definesAB.swift - -// RUN: cd %t && %target-swiftc_driver -enable-batch-mode -j2 -incremental -driver-show-incremental main.swift definesAB.swift usesA.swift usesB.swift -module-name main -output-file-map ofm.json >&output4 - -// only-run-for-debugging: cp %t/usesB.swiftdeps %t/usesB4.swiftdeps - -// RUN: %FileCheck -check-prefix=CHECK-MAINAB-RECOMPILED %s < %t/output4 diff --git a/test/Frontend/crash-in-user-code.swift b/test/Frontend/crash-in-user-code.swift index 4a36a0c3b5a4c..d36f642c76e46 100644 --- a/test/Frontend/crash-in-user-code.swift +++ b/test/Frontend/crash-in-user-code.swift @@ -8,6 +8,8 @@ // UNSUPPORTED: OS=tvos // UNSUPPORTED: OS=watchos +// XFAIL: MSVC_VER=15.0 + // CHECK: Stack dump: // CHECK-NEXT: Program arguments: // CHECK-NEXT: Swift version diff --git a/test/Frontend/embed-bitcode-tvos.ll b/test/Frontend/embed-bitcode-tvos.ll index ccfe4c6a38919..0c84768feb309 100644 --- a/test/Frontend/embed-bitcode-tvos.ll +++ b/test/Frontend/embed-bitcode-tvos.ll @@ -1,6 +1,6 @@ ; REQUIRES: CODEGENERATOR=AArch64 ; RUN: llvm-as %s -o %t.bc -; RUN: %swift -target arm64-apple-tvos9 -c -module-name someModule -embed-bitcode -disable-llvm-optzns -o %t2.o %t.bc -dump-clang-diagnostics 2> %t.diags.txt +; RUN: %swiftc_driver_plain -frontend -target arm64-apple-tvos9 -c -module-name someModule -embed-bitcode -disable-llvm-optzns -o %t2.o %t.bc -dump-clang-diagnostics 2> %t.diags.txt ; RUN: llvm-objdump --macho --private-headers %t2.o | %FileCheck %s ; RUN: %FileCheck -check-prefix CHECK-IMPORTER %s < %t.diags.txt diff --git a/test/Frontend/unknown-arguments.swift b/test/Frontend/unknown-arguments.swift index c857177de76d1..a9007a6e540fb 100644 --- a/test/Frontend/unknown-arguments.swift +++ b/test/Frontend/unknown-arguments.swift @@ -1,14 +1,14 @@ // RUN: not %swift -fake-argument -abcdef -c %s -o %t.o 2>&1 | %FileCheck %s -// CHECK: :0: error: unknown argument: '-fake-argument' -// CHECK-NEXT: :0: error: unknown argument: '-abcdef' +// CHECK: error: unknown argument: '-fake-argument' +// CHECK-NEXT: error: unknown argument: '-abcdef' // RUN: not %swiftc_driver -c %s -o %t.o -Xfrontend -fake-frontend-arg -Xfrontend fakevalue 2>&1 | %FileCheck -check-prefix=XFRONTEND %s -// XFRONTEND: :0: error: unknown argument: '-fake-frontend-arg' +// XFRONTEND: error: unknown argument: '-fake-frontend-arg' // RUN: not %swiftc_driver -D Correct -DAlsoCorrect -D@#%! -D Swift=Cool -D-D -c %s -o %t.o 2>&1 | %FileCheck -check-prefix=INVALID-COND %s -// INVALID-COND: :0: error: conditional compilation flags must be valid Swift identifiers (rather than '@#%!') -// INVALID-COND-NEXT: :0: warning: conditional compilation flags do not have values in Swift; they are either present or absent (rather than 'Swift=Cool') -// INVALID-COND-NEXT: :0: error: invalid argument '-D-D'; did you provide a redundant '-D' in your build settings? +// INVALID-COND: error: conditional compilation flags must be valid Swift identifiers (rather than '@#%!') +// INVALID-COND-NEXT: warning: conditional compilation flags do not have values in Swift; they are either present or absent (rather than 'Swift=Cool') +// INVALID-COND-NEXT: error: invalid argument '-D-D'; did you provide a redundant '-D' in your build settings? diff --git a/test/Generics/conditional_conformances_literals.swift b/test/Generics/conditional_conformances_literals.swift index 37f4729f14d17..9e26cc495776e 100644 --- a/test/Generics/conditional_conformances_literals.swift +++ b/test/Generics/conditional_conformances_literals.swift @@ -128,9 +128,9 @@ func combined() { // Needs self conforming protocols: let _: Conforms = [[0: [1 : [works]] as Conforms]] - // expected-error@-1 {{protocol 'Conforms' as a type cannot conform to the protocol itself; only concrete types such as structs, enums and classes can conform to protocols}} + // expected-error@-1 {{protocol 'Conforms' as a type cannot conform to the protocol itself}} expected-note@-1 {{only concrete types such as structs, enums and classes can conform to protocols}} let _: Conforms = [[0: [1 : [fails]] as Conforms]] // expected-error@-1 {{protocol 'Conforms' requires that 'Fails' conform to 'Conforms'}} - // expected-error@-2 {{protocol 'Conforms' as a type cannot conform to the protocol itself; only concrete types such as structs, enums and classes can conform to protocols}} + // expected-error@-2 {{protocol 'Conforms' as a type cannot conform to the protocol itself}} expected-note@-2 {{only concrete types such as structs, enums and classes can conform to protocols}} } diff --git a/test/Generics/existential_restrictions.swift b/test/Generics/existential_restrictions.swift index 425308b798901..8dd84dcccc953 100644 --- a/test/Generics/existential_restrictions.swift +++ b/test/Generics/existential_restrictions.swift @@ -23,7 +23,7 @@ func fAOE(_ t: AnyObject) { } func fT(_ t: T) { } func testPassExistential(_ p: P, op: OP, opp: OP & P, cp: CP, sp: SP, any: Any, ao: AnyObject) { - fP(p) // expected-error{{protocol 'P' as a type cannot conform to the protocol itself; only concrete types such as structs, enums and classes can conform to protocols}} + fP(p) // expected-error{{protocol 'P' as a type cannot conform to the protocol itself}} expected-note {{only concrete types such as structs, enums and classes can conform to protocols}} fAO(p) // expected-error{{global function 'fAO' requires that 'P' be a class type}} fAOE(p) // expected-error{{argument type 'P' expected to be an instance of a class or class-constrained type}} fT(p) @@ -37,8 +37,8 @@ func testPassExistential(_ p: P, op: OP, opp: OP & P, cp: CP, sp: SP, any: Any, fAOE(cp) fT(cp) - fP(opp) // expected-error{{protocol 'OP & P' as a type cannot conform to 'P'; only concrete types such as structs, enums and classes can conform to protocols}} - fOP(opp) // expected-error{{protocol 'OP & P' as a type cannot conform to 'OP'; only concrete types such as structs, enums and classes can conform to protocols}} + fP(opp) // expected-error{{protocol 'OP & P' as a type cannot conform to 'P'}} expected-note {{only concrete types such as structs, enums and classes can conform to protocols}} + fOP(opp) // expected-error{{protocol 'OP & P' as a type cannot conform to 'OP'}} expected-note {{only concrete types such as structs, enums and classes can conform to protocols}} fAO(opp) // expected-error{{global function 'fAO' requires that 'OP & P' be a class type}} fAOE(opp) fT(opp) @@ -64,9 +64,9 @@ class GAO {} // expected-note 2{{requirement specified as 'T' : ' func blackHole(_ t: Any) {} func testBindExistential() { - blackHole(GP

()) // expected-error{{protocol 'P' as a type cannot conform to the protocol itself; only concrete types such as structs, enums and classes can conform to protocols}} + blackHole(GP

()) // expected-error{{protocol 'P' as a type cannot conform to the protocol itself}} expected-note {{only concrete types such as structs, enums and classes can conform to protocols}} blackHole(GOP()) - blackHole(GCP()) // expected-error{{protocol 'CP' as a type cannot conform to the protocol itself; only concrete types such as structs, enums and classes can conform to protocols}} + blackHole(GCP()) // expected-error{{protocol 'CP' as a type cannot conform to the protocol itself}} expected-note {{only concrete types such as structs, enums and classes can conform to protocols}} blackHole(GAO

()) // expected-error{{'GAO' requires that 'P' be a class type}} blackHole(GAO()) blackHole(GAO()) // expected-error{{'GAO' requires that 'CP' be a class type}} @@ -92,5 +92,5 @@ func foo() { // generic no overloads error path. The error should actually talk // about the return type, and this can happen in other contexts as well; // tracks improving QoI here. - allMine.takeAll() // expected-error{{protocol 'Mine' as a type cannot conform to the protocol itself; only concrete types such as structs, enums and classes can conform to protocols}} + allMine.takeAll() // expected-error{{protocol 'Mine' as a type cannot conform to the protocol itself}} expected-note {{only concrete types such as structs, enums and classes can conform to protocols}} } diff --git a/test/Generics/where_clause_contextually_generic_decls.swift b/test/Generics/where_clause_contextually_generic_decls.swift index 27819a167a03f..87afc552198c7 100644 --- a/test/Generics/where_clause_contextually_generic_decls.swift +++ b/test/Generics/where_clause_contextually_generic_decls.swift @@ -147,7 +147,7 @@ extension Container.NestedStruct3 { _ = Container.NestedAlias2.self // expected-error {{type 'String' does not conform to protocol 'FixedWidthInteger'}} _ = Container>.NestedClass.self // expected-error {{type 'Container' does not conform to protocol 'Equatable'}} _ = Container.NestedStruct.self // expected-error {{type 'Void' does not conform to protocol 'Sequence'}} -_ = Container>.NestedStruct2.self // expected-error {{type 'Void' does not conform to protocol 'Comparable'}} +_ = Container>.NestedStruct2.self _ = Container.NestedStruct2.NestedEnum.self // expected-error {{'Container.NestedStruct2.NestedEnum' requires the types 'String.Element' (aka 'Character') and 'Double' be equivalent}} _ = Container.NestedAlias2.self _ = Container.NestedClass.self diff --git a/test/IDE/complete_ambiguous.swift b/test/IDE/complete_ambiguous.swift index b386ffada7fcd..c8b6f3fe89074 100644 --- a/test/IDE/complete_ambiguous.swift +++ b/test/IDE/complete_ambiguous.swift @@ -1,9 +1,27 @@ // RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=SIMPLE | %FileCheck %s --check-prefix=SIMPLE // RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=SIMPLE_EXTRAARG | %FileCheck %s --check-prefix=SIMPLE // RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=SIMPLE_MEMBERS | %FileCheck %s --check-prefix=SIMPLE +// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=SKIP_DUPLICATES > %t +// RUN: %FileCheck %s --input-file %t --check-prefixes=SKIP_DUPLICATES,SKIP_DUPLICATES_PROPERTY +// RUN: %FileCheck %s --input-file %t --check-prefixes=SKIP_DUPLICATES,SKIP_DUPLICATES_METHOD +// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=SKIP_COMPOUND_DUPLICATES | %FileCheck %s --check-prefix=SKIP_COMPOUND_DUPLICATES +// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=SKIP_CALLASFUNCTION_DUPLICATES | %FileCheck %s --check-prefix=SKIP_CALLASFUNCTION_DUPLICATES // RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=RELATED | %FileCheck %s --check-prefix=RELATED // RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=RELATED_EXTRAARG | %FileCheck %s --check-prefix=RELATED // RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=RELATED_INERROREXPR | %FileCheck %s --check-prefix=RELATED +// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=NOCALLBACK_FALLBACK | %FileCheck %s --check-prefix=RELATED +// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=MULTICLOSURE_FALLBACK | %FileCheck %s --check-prefix=MULTICLOSURE_FALLBACK +// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=UNAMBIGUOUSCLOSURE_ARG | %FileCheck %s --check-prefix=UNAMBIGUOUSCLOSURE_ARG +// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=AMBIGUOUSCLOSURE_ARG | %FileCheck %s --check-prefix=AMBIGUOUSCLOSURE_ARG +// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=AMBIGUOUSCLOSURE_ARG_RETURN | %FileCheck %s --check-prefix=AMBIGUOUSCLOSURE_ARG_RETURN +// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=OVERLOADEDFUNC_FOO | %FileCheck %s --check-prefix=OVERLOADEDFUNC_FOO +// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=OVERLOADEDFUNC_BAR | %FileCheck %s --check-prefix=OVERLOADEDFUNC_BAR +// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=OVERLOADEDFUNC_MISSINGLABEL | %FileCheck %s --check-prefix=OVERLOADEDFUNC_BOTH +// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=OVERLOADEDFUNC_MISSINGARG_AFTER | %FileCheck %s --check-prefix=OVERLOADEDFUNC_BOTH +// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=OVERLOADEDMEMBER_MISSINGARG_AFTER | %FileCheck %s --check-prefix=OVERLOADEDFUNC_BOTH +// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=OVERLOADEDFUNC_MISSINGARG_BEFORE | %FileCheck %s --check-prefix=OVERLOADEDFUNC_BAR +// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=OVERLOADEDFUNC_MISSINGARG_BEFOREANDAFTER | %FileCheck %s --check-prefix=OVERLOADEDFUNC_BAR +// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=ERROR_IN_BASE | %FileCheck %s --check-prefix=SIMPLE // RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=GENERIC | %FileCheck %s --check-prefix=GENERIC // RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=GENERIC_MISSINGARG | %FileCheck %s --check-prefix=NORESULTS // RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=CLOSURE_MISSINGARG | %FileCheck %s --check-prefix=POINT_MEMBER @@ -34,6 +52,42 @@ struct HasMembers { HasMembers().overloadedReturn().#^SIMPLE_MEMBERS^# +func givenErrorExpr(_ a: String) -> A {} +func givenErrorExpr(_ b: Int) -> B {} + +func arrayWrapper(a: T) -> [T] +arrayWrapper(overloadedReturn()).#^SKIP_DUPLICATES^# + +// SKIP_DUPLICATES: Begin completions +// SKIP_DUPLICATES_PROPERTY: Decl[InstanceVar]/CurrNominal/IsSystem: count[#Int#]{{; name=.+$}} +// SKIP_DUPLICATES_PROPERTY-NOT: count[#Int#] +// SKIP_DUPLICATES_METHOD: Decl[InstanceMethod]/Super/IsSystem: formIndex({#(i): &Int#}, {#offsetBy: Int#})[#Void#]{{; name=.+$}} +// SKIP_DUPLICATES_METHOD-NOT: formIndex({#(i): &Int#}, {#offsetBy: Int#})[#Void#] +// SKIP_DUPLICATES: End completions + +let x: (inout Int, Int) -> () = arrayWrapper(overloadedReturn()).#^SKIP_COMPOUND_DUPLICATES^# + +// SKIP_COMPOUND_DUPLICATES: Begin completions +// SKIP_COMPOUND_DUPLICATES: Decl[InstanceMethod]/Super/IsSystem/TypeRelation[Identical]: formIndex(_:offsetBy:)[#(inout Int, Int) -> ()#]{{; name=.+$}} +// SKIP_COMPOUND_DUPLICATES-NOT: formIndex(_:offsetBy:)[#(inout Int, Int) -> ()#] +// SKIP_COMPOUND_DUPLICATES: End completions + +func testCallAsFunctionDeduplication() { + struct Test { + func callAsFunction(x: Int) {} + } + + func overloaded() -> Test { fatalError() } + func overloaded() -> Test { fatalError() } + + overloaded()#^SKIP_CALLASFUNCTION_DUPLICATES^# +} + +// FIXME: Update this to check the callAsFunction pattern only appears once when PostfixExpr completion is migrated to the solver-based implementation (which handles ambiguity). +// SKIP_CALLASFUNCTION_DUPLICATES-NOT: Begin completions + +givenErrorExpr(undefined).#^ERROR_IN_BASE^# + // SIMPLE: Begin completions, 4 items // SIMPLE-DAG: Keyword[self]/CurrNominal: self[#A#]{{; name=.+$}} // SIMPLE-DAG: Decl[InstanceMethod]/CurrNominal: doAThings()[#A#]{{; name=.+$}} @@ -56,6 +110,128 @@ func takesB(_ item: B) {} takesB((takesA { return overloadedReturn().#^RELATED_INERROREXPR^# }).) +switch undefined { + case takesA { return overloadedReturn().#^NOCALLBACK_FALLBACK^# }: + break +} + +func takesClosureA(_ arg: (A) -> ()) {} +func takesClosureB(_ arg: (B) -> ()) {} + +takesClosureA { arg in + takesClosureB { arg in + arg.#^MULTICLOSURE_FALLBACK^# + } + print() + 10 +} + 10 + +// MULTICLOSURE_FALLBACK: Begin completions, 2 items +// MULTICLOSURE_FALLBACK-DAG: Keyword[self]/CurrNominal: self[#B#]{{; name=.+$}} +// MULTICLOSURE_FALLBACK-DAG: Decl[InstanceMethod]/CurrNominal: doBThings()[#Void#]{{; name=.+$}} +// MULTICLOSURE_FALLBACK: End completions + +func takesAnonClosure(_ x: (A) -> A) { return A() } +func takesAnonClosure(_ x: (B, A) -> B { return B() } +func takesAnonClosure(_ x: () -> (A, B) { return (A(), B()) } + +struct TestRelations { + static let a = A() + static let b = B() + static let ab = (A(), B()) +} + +// test we consider both overloads as $0 or $1 may have just not been written yet +takesAnonClosure { $1.#^UNAMBIGUOUSCLOSURE_ARG^# } +// UNAMBIGUOUSCLOSURE_ARG: Begin completions, 2 items +// UNAMBIGUOUSCLOSURE_ARG-DAG: Keyword[self]/CurrNominal: self[#A#]{{; name=.+$}} +// UNAMBIGUOUSCLOSURE_ARG-DAG: Decl[InstanceMethod]/CurrNominal: doAThings()[#A#]{{; name=.+$}} +// UNAMBIGUOUSCLOSURE_ARG: End completions + +takesAnonClosure { $0.#^AMBIGUOUSCLOSURE_ARG^# } +// AMBIGUOUSCLOSURE_ARG: Begin completions, 4 items +// AMBIGUOUSCLOSURE_ARG-DAG: Keyword[self]/CurrNominal: self[#A#]{{; name=.+$}} +// AMBIGUOUSCLOSURE_ARG-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Identical]: doAThings()[#A#]{{; name=.+$}} +// AMBIGUOUSCLOSURE_ARG-DAG: Keyword[self]/CurrNominal: self[#B#]{{; name=.+$}} +// AMBIGUOUSCLOSURE_ARG-DAG: Decl[InstanceMethod]/CurrNominal: doBThings()[#Void#]{{; name=.+$}} +// AMBIGUOUSCLOSURE_ARG: End completions + +takesAnonClosure { TestRelations.#^AMBIGUOUSCLOSURE_ARG_RETURN^# } +// AMBIGUOUSCLOSURE_ARG_RETURN: Begin completions, 6 items +// AMBIGUOUSCLOSURE_ARG_RETURN-DAG: Keyword[self]/CurrNominal: self[#TestRelations.Type#]{{; name=.+$}} +// AMBIGUOUSCLOSURE_ARG_RETURN-DAG: Keyword/CurrNominal: Type[#TestRelations.Type#]{{; name=.+$}} +// AMBIGUOUSCLOSURE_ARG_RETURN-DAG: Decl[StaticVar]/CurrNominal/TypeRelation[Identical]: a[#A#]{{; name=.+$}} +// AMBIGUOUSCLOSURE_ARG_RETURN-DAG: Decl[StaticVar]/CurrNominal/TypeRelation[Identical]: b[#B#]{{; name=.+$}} +// AMBIGUOUSCLOSURE_ARG_RETURN-DAG: Decl[StaticVar]/CurrNominal/TypeRelation[Identical]: ab[#(A, B)#]{{; name=.+$}} +// AMBIGUOUSCLOSURE_ARG_RETURN-DAG: Decl[Constructor]/CurrNominal: init()[#TestRelations#]{{; name=.+$}} +// AMBIGUOUSCLOSURE_ARG_RETURN: End completions + + +func testMissingArgs() { + enum Foo { case foo } + enum Bar { case bar } + + struct Test { + static let foo = Foo.foo + static let bar = Bar.bar + } + + func test(foo: Foo) {} + func test(bar: Bar) {} + + func test2(first: Bar, second: Int) {} + func test2(first: Foo) {} + + func test3(skipMe: Int, after: Foo) {} + func test3(after: Bar) {} + + func test4(skipMe: Int, both: Foo, skipMeToo: Int) {} + func test4(both: Bar, skipMeTo: Int) {} + + + test(foo: Test.#^OVERLOADEDFUNC_FOO^#) + // OVERLOADEDFUNC_FOO: Begin completions, 5 items + // OVERLOADEDFUNC_FOO-DAG: Keyword[self]/CurrNominal: self[#Test.Type#]{{; name=.+$}} + // OVERLOADEDFUNC_FOO-DAG: Keyword/CurrNominal: Type[#Test.Type#]{{; name=.+$}} + // OVERLOADEDFUNC_FOO-DAG: Decl[StaticVar]/CurrNominal/TypeRelation[Identical]: foo[#Foo#]{{; name=.+$}} + // OVERLOADEDFUNC_FOO-DAG: Decl[StaticVar]/CurrNominal: bar[#Bar#]{{; name=.+$}} + // OVERLOADEDFUNC_FOO-DAG: Decl[Constructor]/CurrNominal: init()[#Test#]{{; name=.+$}} + // OVERLOADEDFUNC_FOO: End completions + + test(bar: Test.#^OVERLOADEDFUNC_BAR^#) + // OVERLOADEDFUNC_BAR: Begin completions, 5 items + // OVERLOADEDFUNC_BAR-DAG: Keyword[self]/CurrNominal: self[#Test.Type#]{{; name=.+$}} + // OVERLOADEDFUNC_BAR-DAG: Keyword/CurrNominal: Type[#Test.Type#]{{; name=.+$}} + // OVERLOADEDFUNC_BAR-DAG: Decl[StaticVar]/CurrNominal: foo[#Foo#]{{; name=.+$}} + // OVERLOADEDFUNC_BAR-DAG: Decl[StaticVar]/CurrNominal/TypeRelation[Identical]: bar[#Bar#]{{; name=.+$}} + // OVERLOADEDFUNC_BAR-DAG: Decl[Constructor]/CurrNominal: init()[#Test#]{{; name=.+$}} + // OVERLOADEDFUNC_BAR: End completions + + test(Test.#^OVERLOADEDFUNC_MISSINGLABEL^#, extraArg: 2) + test2(first: Test.#^OVERLOADEDFUNC_MISSINGARG_AFTER^#) + + // Also check ambiguous member functions + struct TestStruct { + func test2(first: Bar, second: Int) {} + func test2(first: Foo) {} + } + + TestStruct().test2(first: Test.#^OVERLOADEDMEMBER_MISSINGARG_AFTER^#) + + // TODO: Should we insert the missing label in the completion text for OVERLOADEDFUNC_MISSINGLABEL? + // OVERLOADEDFUNC_BOTH: Begin completions, 5 items + // OVERLOADEDFUNC_BOTH-DAG: Keyword[self]/CurrNominal: self[#Test.Type#]{{; name=.+$}} + // OVERLOADEDFUNC_BOTH-DAG: Keyword/CurrNominal: Type[#Test.Type#]{{; name=.+$}} + // OVERLOADEDFUNC_BOTH-DAG: Decl[StaticVar]/CurrNominal/TypeRelation[Identical]: foo[#Foo#]{{; name=.+$}} + // OVERLOADEDFUNC_BOTH-DAG: Decl[StaticVar]/CurrNominal/TypeRelation[Identical]: bar[#Bar#]{{; name=.+$}} + // OVERLOADEDFUNC_BOTH-DAG: Decl[Constructor]/CurrNominal: init()[#Test#]{{; name=.+$}} + // OVERLOADEDFUNC_BOTH: End completions + + test3(after: Test.#^OVERLOADEDFUNC_MISSINGARG_BEFORE^#); + test4(both: Test.#^OVERLOADEDFUNC_MISSINGARG_BEFOREANDAFTER^#) +} + + + protocol C { associatedtype Element func getCElem() -> Element @@ -124,7 +300,7 @@ _ = testing([Point(4, 89)]) { arg in struct Thing { init(_ block: (Point) -> Void) {} } -@_functionBuilder +@resultBuilder struct ThingBuilder { static func buildBlock(_ x: Thing...) -> [Thing] { x } } @@ -160,7 +336,7 @@ CreateThings { } } -// FIXME: No results in multi-statement closure with erroreous sibling function builder element +// FIXME: No results in multi-statement closure with erroreous sibling result builder element CreateThings { Thing { point in print("hello") diff --git a/test/IDE/complete_annotation.swift b/test/IDE/complete_annotation.swift index 0e267618ca87a..d5e04cafbd58d 100644 --- a/test/IDE/complete_annotation.swift +++ b/test/IDE/complete_annotation.swift @@ -6,6 +6,8 @@ // RUN: %swift-ide-test -code-completion -code-completion-annotate-results -source-filename %s -code-completion-token=EXPR_POSTFIX | %FileCheck %s --check-prefix=EXPR_POSTFIX // RUN: %swift-ide-test -code-completion -code-completion-annotate-results -source-filename %s -code-completion-token=EXPR_IMPLICITMEMBER | %FileCheck %s --check-prefix=EXPR_IMPLICITMEMBER // RUN: %swift-ide-test -code-completion -code-completion-annotate-results -source-filename %s -code-completion-token=CALLARG | %FileCheck %s --check-prefix=CALLARG +// RUN: %swift-ide-test -code-completion -code-completion-annotate-results -source-filename %s -code-completion-token=GENERIC | %FileCheck %s --check-prefix=GENERIC +// RUN: %swift-ide-test -code-completion -code-completion-annotate-results -source-filename %s -code-completion-token=WHERE | %FileCheck %s --check-prefix=WHERE struct MyStruct { init(x: Int) {} @@ -113,3 +115,24 @@ func testArgument() -> MyStruct { // CALLARG-DAG: Pattern/ExprSpecific: y: Int; typename=Int // CALLARG: End completions +struct TestArchetypeAnnotations { + func foo1(u: U, t: T) {} + func foo2(s: S, elt: S.Element) {} +} + +func testArchetypeAnnotations(arg: TestArchetypeAnnotations) { + arg.#^GENERIC^# +} +// GENERIC: Begin completions, 3 items +// GENERIC-DAG: Keyword[self]/CurrNominal: self; typename=TestArchetypeAnnotations<T>; name=self +// GENERIC-DAG: Decl[InstanceMethod]/CurrNominal: foo1(u: U, t: T); typename=Void; name=foo1(u: U, t: T) +// GENERIC-DAG: Decl[InstanceMethod]/CurrNominal: foo2(s: Sequence, elt: Sequence.Element); typename=Void; name=foo2(s: Sequence, elt: Sequence.Element) +// GENERIC: End completions + +struct TestGenericParamAnnotations { + func foo1(u: U) where #^WHERE^# +} +// WHERE: Begin completions, 2 items +// WHERE-NEXT: Decl[GenericTypeParam]/Local: T; typename=T; name=T +// WHERE-NEXT: Decl[GenericTypeParam]/Local: U; typename=U; name=U +// WHERE: End completions diff --git a/test/IDE/complete_assignment.swift b/test/IDE/complete_assignment.swift index 871ef3db1dd2c..2e3b33bab2680 100644 --- a/test/IDE/complete_assignment.swift +++ b/test/IDE/complete_assignment.swift @@ -202,7 +202,7 @@ func f2() { // ASSIGN_11-DAG: Decl[InstanceMethod]/CurrNominal: IntOpGen()[#Int?#] // ASSIGN_11-DAG: Decl[InstanceMethod]/CurrNominal: D1Gen()[#D1#] // ASSIGN_11-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#D2#] -// ASSIGN_11-DAG: Decl[InstanceMethod]/CurrNominal: VoidGen()[#Void#] +// ASSIGN_11-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: VoidGen()[#Void#] // ASSIGN_11-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C2#] func f12() { diff --git a/test/IDE/complete_decl_attribute.swift b/test/IDE/complete_decl_attribute.swift index 88f8b5c819b86..8810e0f533bc7 100644 --- a/test/IDE/complete_decl_attribute.swift +++ b/test/IDE/complete_decl_attribute.swift @@ -3,7 +3,6 @@ // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=KEYWORD2 | %FileCheck %s -check-prefix=KEYWORD2 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=KEYWORD3 | %FileCheck %s -check-prefix=KEYWORD3 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=KEYWORD3_2 | %FileCheck %s -check-prefix=KEYWORD3 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=KEYWORD3 -enable-experimental-concurrency | %FileCheck %s -check-prefix=KEYWORD3_ASYNC // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=KEYWORD4 | %FileCheck %s -check-prefix=KEYWORD4 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=KEYWORD5 | %FileCheck %s -check-prefix=KEYWORD5 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=ON_GLOBALVAR | %FileCheck %s -check-prefix=ON_GLOBALVAR @@ -93,10 +92,9 @@ struct MyStruct {} // KEYWORD3-NEXT: Keyword/None: NSApplicationMain[#Class Attribute#]; name=NSApplicationMain{{$}} // KEYWORD3-NEXT: Keyword/None: usableFromInline[#Class Attribute#]; name=usableFromInline // KEYWORD3-NEXT: Keyword/None: propertyWrapper[#Class Attribute#]; name=propertyWrapper +// KEYWORD3-NEXT: Keyword/None: resultBuilder[#Class Attribute#]; name=resultBuilder // KEYWORD3-NEXT: End completions -// KEYWORD3_ASYNC: Keyword/None: actor[#Class Attribute#]; name=actor - @#^KEYWORD3_2^#IB class C2 {} // Same as KEYWORD3. @@ -110,6 +108,7 @@ struct MyStruct {} // KEYWORD4-NEXT: Keyword/None: usableFromInline[#Enum Attribute#]; name=usableFromInline // KEYWORD4-NEXT: Keyword/None: frozen[#Enum Attribute#]; name=frozen // KEYWORD4-NEXT: Keyword/None: propertyWrapper[#Enum Attribute#]; name=propertyWrapper +// KEYWORD4-NEXT: Keyword/None: resultBuilder[#Enum Attribute#]; name=resultBuilder // KEYWORD4-NEXT: End completions @@ -122,6 +121,7 @@ struct MyStruct {} // KEYWORD5-NEXT: Keyword/None: usableFromInline[#Struct Attribute#]; name=usableFromInline // KEYWORD5-NEXT: Keyword/None: frozen[#Struct Attribute#]; name=frozen // KEYWORD5-NEXT: Keyword/None: propertyWrapper[#Struct Attribute#]; name=propertyWrapper +// KEYWORD5-NEXT: Keyword/None: resultBuilder[#Struct Attribute#]; name=resultBuilder // KEYWORD5-NEXT: End completions @#^ON_GLOBALVAR^# var globalVar @@ -249,6 +249,7 @@ struct _S { // ON_MEMBER_LAST-DAG: Keyword/None: GKInspectable[#Declaration Attribute#]; name=GKInspectable // ON_MEMBER_LAST-DAG: Keyword/None: IBSegueAction[#Declaration Attribute#]; name=IBSegueAction // ON_MEMBER_LAST-DAG: Keyword/None: propertyWrapper[#Declaration Attribute#]; name=propertyWrapper +// ON_MEMBER_LAST-DAG: Keyword/None: resultBuilder[#Declaration Attribute#]; name=resultBuilder // ON_MEMBER_LAST-DAG: Keyword/None: differentiable[#Declaration Attribute#]; name=differentiable // ON_MEMBER_LAST-DAG: Keyword/None: derivative[#Declaration Attribute#]; name=derivative // ON_MEMBER_LAST-DAG: Keyword/None: transpose[#Declaration Attribute#]; name=transpose @@ -298,6 +299,7 @@ func dummy2() {} // KEYWORD_LAST-NEXT: Keyword/None: GKInspectable[#Declaration Attribute#]; name=GKInspectable{{$}} // KEYWORD_LAST-NEXT: Keyword/None: frozen[#Declaration Attribute#]; name=frozen // KEYWORD_LAST-NEXT: Keyword/None: propertyWrapper[#Declaration Attribute#]; name=propertyWrapper +// KEYWORD_LAST-NEXT: Keyword/None: resultBuilder[#Declaration Attribute#]; name=resultBuilder // KEYWORD_LAST-NEXT: Keyword/None: differentiable[#Declaration Attribute#]; name=differentiable // KEYWORD_LAST-NEXT: Keyword/None: IBSegueAction[#Declaration Attribute#]; name=IBSegueAction{{$}} // KEYWORD_LAST-NEXT: Keyword/None: derivative[#Declaration Attribute#]; name=derivative diff --git a/test/IDE/complete_expr_tuple.swift b/test/IDE/complete_expr_tuple.swift index 0f2295f8b99c2..b3cb669f4b1be 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, 10 items +// TUPLE_NO_DOT_1: Begin completions, 14 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]/IsSystem: == {#(Int, Double)#}[#Bool#]{{; name=.+$}} @@ -44,15 +44,15 @@ func testTupleNoDot2() { var t = (foo: 1, bar: 2.0) t#^TUPLE_NO_DOT_2^# } -// TUPLE_NO_DOT_2: Begin completions, 10 items +// TUPLE_NO_DOT_2: Begin completions, 14 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]/IsSystem: == {#(Int, Double)#}[#Bool#]{{; name=.+$}} -// TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: <= {#(Int, Double)#}[#Bool#]{{; name=.+$}} -// TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: >= {#(Int, Double)#}[#Bool#]{{; name=.+$}} -// TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: < {#(Int, Double)#}[#Bool#]{{; name=.+$}} -// TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: != {#(Int, Double)#}[#Bool#]{{; name=.+$}} -// TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: > {#(Int, Double)#}[#Bool#]{{; name=.+$}} +// TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: == {#(foo: Int, bar: Double)#}[#Bool#]{{; name=.+$}} +// TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: <= {#(foo: Int, bar: Double)#}[#Bool#]{{; name=.+$}} +// TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: >= {#(foo: Int, bar: Double)#}[#Bool#]{{; name=.+$}} +// TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: < {#(foo: Int, bar: Double)#}[#Bool#]{{; name=.+$}} +// TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: != {#(foo: Int, bar: Double)#}[#Bool#]{{; name=.+$}} +// TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: > {#(foo: Int, bar: 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 @@ -61,15 +61,15 @@ func testTupleNoDot3() { var t = (foo: 1, 2.0) t#^TUPLE_NO_DOT_3^# } -// TUPLE_NO_DOT_3: Begin completions, 10 items +// TUPLE_NO_DOT_3: Begin completions, 14 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]/IsSystem: == {#(Int, Double)#}[#Bool#]{{; name=.+$}} -// TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: <= {#(Int, Double)#}[#Bool#]{{; name=.+$}} -// TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: >= {#(Int, Double)#}[#Bool#]{{; name=.+$}} -// TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: < {#(Int, Double)#}[#Bool#]{{; name=.+$}} -// TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: != {#(Int, Double)#}[#Bool#]{{; name=.+$}} -// TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: > {#(Int, Double)#}[#Bool#]{{; name=.+$}} +// TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: == {#(foo: Int, Double)#}[#Bool#]{{; name=.+$}} +// TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: <= {#(foo: Int, Double)#}[#Bool#]{{; name=.+$}} +// TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: >= {#(foo: Int, Double)#}[#Bool#]{{; name=.+$}} +// TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: < {#(foo: Int, Double)#}[#Bool#]{{; name=.+$}} +// TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: != {#(foo: Int, Double)#}[#Bool#]{{; name=.+$}} +// TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: > {#(foo: 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 diff --git a/test/IDE/complete_func_body_typechecking.swift b/test/IDE/complete_func_body_typechecking.swift index 8856a9dfb9a3c..d0176c3525a57 100644 --- a/test/IDE/complete_func_body_typechecking.swift +++ b/test/IDE/complete_func_body_typechecking.swift @@ -4,7 +4,7 @@ // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TC_VAR_4 | %FileCheck %s -check-prefix=FOO_STRUCT_COMMON // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TC_VAR_5 | %FileCheck %s -check-prefix=FOO_STRUCT_COMMON // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TC_VAR_6 | %FileCheck %s -check-prefix=FOO_STRUCT_COMMON -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TC_VAR_7 | %FileCheck %s -check-prefix=ERROR_COMMON +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TC_VAR_7 | %FileCheck %s -check-prefix=FOO_STRUCT_COMMON // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TC_VAR_IN_CONSTRUCTOR_1 | %FileCheck %s -check-prefix=FOO_STRUCT_COMMON // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TC_VAR_IN_CONSTRUCTOR_2 | %FileCheck %s -check-prefix=FOO_STRUCT_COMMON @@ -91,10 +91,6 @@ func testTypecheckVar6() { } func testTypecheckVar7() { - // FIXME: We don't display any useful completions here, although we could -- - // it is obvious that the expression could only have type 'FooStruct'. - // - // In any case, ensure that we don't crash. var localInt = 42 FooStruct(localInt).builderFunc2(unknown_var).#^TC_VAR_7^# } diff --git a/test/IDE/complete_rdar63965160.swift b/test/IDE/complete_rdar63965160.swift index 41dd721ffb8d6..7fe399330d19f 100644 --- a/test/IDE/complete_rdar63965160.swift +++ b/test/IDE/complete_rdar63965160.swift @@ -3,7 +3,7 @@ protocol View {} -@_functionBuilder +@resultBuilder struct Builder { static func buildBlock(_ c0: C0) -> C0 {} static func buildBlock(_ c0: C0, _ c1: C1) -> C1 {} diff --git a/test/IDE/complete_function_builder.swift b/test/IDE/complete_result_builder.swift similarity index 50% rename from test/IDE/complete_function_builder.swift rename to test/IDE/complete_result_builder.swift index cc3ae5fe6a3d2..9038b18a904cb 100644 --- a/test/IDE/complete_function_builder.swift +++ b/test/IDE/complete_result_builder.swift @@ -1,8 +1,8 @@ // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_CLOSURE_TOP | %FileCheck %s -check-prefix=IN_CLOSURE_TOP // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_CLOSURE_NONTOP | %FileCheck %s -check-prefix=IN_CLOSURE_TOP // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_CLOSURE_COLOR_CONTEXT | %FileCheck %s -check-prefix=IN_CLOSURE_COLOR_CONTEXT -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_FUNCTION_BUILDER_DECL -code-completion-comments=true | %FileCheck %s -check-prefix=IN_FUNCTION_BUILDER_DECL -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_FUNCTION_BUILDER_DECL_PREFIX -code-completion-comments=true | %FileCheck %s -check-prefix=IN_FUNCTION_BUILDER_DECL_PREFIX +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_RESULT_BUILDER_DECL -code-completion-comments=true | %FileCheck %s -check-prefix=IN_RESULT_BUILDER_DECL +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_RESULT_BUILDER_DECL_PREFIX -code-completion-comments=true | %FileCheck %s -check-prefix=IN_RESULT_BUILDER_DECL_PREFIX struct Tagged { let tag: Tag @@ -23,7 +23,7 @@ extension Taggable { extension Int: Taggable { } extension String: Taggable { } -@_functionBuilder +@resultBuilder struct TaggedBuilder { static func buildBlock() -> () { } @@ -87,7 +87,7 @@ enum MyEnum { case east, west case north, south } -@_functionBuilder +@resultBuilder struct EnumToVoidBuilder { static func buildBlock() {} static func buildBlock(_ :MyEnum) {} @@ -96,33 +96,33 @@ struct EnumToVoidBuilder { } func acceptBuilder(@EnumToVoidBuilder body: () -> Void) {} -@_functionBuilder +@resultBuilder struct AnyBuilder { static func buildBlock(_ components: Any...) -> Any { 5 } - #^IN_FUNCTION_BUILDER_DECL_PREFIX^# + #^IN_RESULT_BUILDER_DECL_PREFIX^# - static func #^IN_FUNCTION_BUILDER_DECL^# + static func #^IN_RESULT_BUILDER_DECL^# } -// IN_FUNCTION_BUILDER_DECL: Begin completions, 8 items -// IN_FUNCTION_BUILDER_DECL: Pattern/CurrNominal: buildBlock(_ components: Any...) -> Any {|}; name=buildBlock(_ components: Any...) -> Any; comment=Required by every -// IN_FUNCTION_BUILDER_DECL: Pattern/CurrNominal: buildExpression(_ expression: <#Expression#>) -> Any {|}; name=buildExpression(_ expression: <#Expression#>) -> Any; comment= -// IN_FUNCTION_BUILDER_DECL: Pattern/CurrNominal: buildOptional(_ component: Any?) -> Any {|}; name=buildOptional(_ component: Any?) -> Any; comment= -// IN_FUNCTION_BUILDER_DECL: Pattern/CurrNominal: buildEither(first component: Any) -> Any {|}; name=buildEither(first component: Any) -> Any; comment= -// IN_FUNCTION_BUILDER_DECL: Pattern/CurrNominal: buildEither(second component: Any) -> Any {|}; name=buildEither(second component: Any) -> Any; comment= -// IN_FUNCTION_BUILDER_DECL: Pattern/CurrNominal: buildArray(_ components: [Any]) -> Any {|}; name=buildArray(_ components: [Any]) -> Any; comment= -// IN_FUNCTION_BUILDER_DECL: Pattern/CurrNominal: buildLimitedAvailability(_ component: Any) -> Any {|}; name=buildLimitedAvailability(_ component: Any) -> Any; comment= -// IN_FUNCTION_BUILDER_DECL: Pattern/CurrNominal: buildFinalResult(_ component: Any) -> <#Result#> {|}; name=buildFinalResult(_ component: Any) -> <#Result#>; comment= -// IN_FUNCTION_BUILDER_DECL: End completions - -// IN_FUNCTION_BUILDER_DECL_PREFIX: Begin completions -// IN_FUNCTION_BUILDER_DECL_PREFIX: Pattern/CurrNominal: static func buildBlock(_ components: Any...) -> Any {|}; name=static func buildBlock(_ components: Any...) -> Any; comment=Required by every -// IN_FUNCTION_BUILDER_DECL_PREFIX: Pattern/CurrNominal: static func buildExpression(_ expression: <#Expression#>) -> Any {|}; name=static func buildExpression(_ expression: <#Expression#>) -> Any; comment= -// IN_FUNCTION_BUILDER_DECL_PREFIX: Pattern/CurrNominal: static func buildOptional(_ component: Any?) -> Any {|}; name=static func buildOptional(_ component: Any?) -> Any; comment= -// IN_FUNCTION_BUILDER_DECL_PREFIX: Pattern/CurrNominal: static func buildEither(first component: Any) -> Any {|}; name=static func buildEither(first component: Any) -> Any; comment= -// IN_FUNCTION_BUILDER_DECL_PREFIX: Pattern/CurrNominal: static func buildEither(second component: Any) -> Any {|}; name=static func buildEither(second component: Any) -> Any; comment= -// IN_FUNCTION_BUILDER_DECL_PREFIX: Pattern/CurrNominal: static func buildArray(_ components: [Any]) -> Any {|}; name=static func buildArray(_ components: [Any]) -> Any; comment= -// IN_FUNCTION_BUILDER_DECL_PREFIX: Pattern/CurrNominal: static func buildLimitedAvailability(_ component: Any) -> Any {|}; name=static func buildLimitedAvailability(_ component: Any) -> Any; comment= -// IN_FUNCTION_BUILDER_DECL_PREFIX: Pattern/CurrNominal: static func buildFinalResult(_ component: Any) -> <#Result#> {|}; name=static func buildFinalResult(_ component: Any) -> <#Result#>; comment= -// IN_FUNCTION_BUILDER_DECL_PREFIX: End completions +// IN_RESULT_BUILDER_DECL: Begin completions, 8 items +// IN_RESULT_BUILDER_DECL: Pattern/CurrNominal: buildBlock(_ components: Any...) -> Any {|}; name=buildBlock(_ components: Any...) -> Any; comment=Required by every +// IN_RESULT_BUILDER_DECL: Pattern/CurrNominal: buildExpression(_ expression: <#Expression#>) -> Any {|}; name=buildExpression(_ expression: <#Expression#>) -> Any; comment= +// IN_RESULT_BUILDER_DECL: Pattern/CurrNominal: buildOptional(_ component: Any?) -> Any {|}; name=buildOptional(_ component: Any?) -> Any; comment= +// IN_RESULT_BUILDER_DECL: Pattern/CurrNominal: buildEither(first component: Any) -> Any {|}; name=buildEither(first component: Any) -> Any; comment= +// IN_RESULT_BUILDER_DECL: Pattern/CurrNominal: buildEither(second component: Any) -> Any {|}; name=buildEither(second component: Any) -> Any; comment= +// IN_RESULT_BUILDER_DECL: Pattern/CurrNominal: buildArray(_ components: [Any]) -> Any {|}; name=buildArray(_ components: [Any]) -> Any; comment= +// IN_RESULT_BUILDER_DECL: Pattern/CurrNominal: buildLimitedAvailability(_ component: Any) -> Any {|}; name=buildLimitedAvailability(_ component: Any) -> Any; comment= +// IN_RESULT_BUILDER_DECL: Pattern/CurrNominal: buildFinalResult(_ component: Any) -> <#Result#> {|}; name=buildFinalResult(_ component: Any) -> <#Result#>; comment= +// IN_RESULT_BUILDER_DECL: End completions + +// IN_RESULT_BUILDER_DECL_PREFIX: Begin completions +// IN_RESULT_BUILDER_DECL_PREFIX: Pattern/CurrNominal: static func buildBlock(_ components: Any...) -> Any {|}; name=static func buildBlock(_ components: Any...) -> Any; comment=Required by every +// IN_RESULT_BUILDER_DECL_PREFIX: Pattern/CurrNominal: static func buildExpression(_ expression: <#Expression#>) -> Any {|}; name=static func buildExpression(_ expression: <#Expression#>) -> Any; comment= +// IN_RESULT_BUILDER_DECL_PREFIX: Pattern/CurrNominal: static func buildOptional(_ component: Any?) -> Any {|}; name=static func buildOptional(_ component: Any?) -> Any; comment= +// IN_RESULT_BUILDER_DECL_PREFIX: Pattern/CurrNominal: static func buildEither(first component: Any) -> Any {|}; name=static func buildEither(first component: Any) -> Any; comment= +// IN_RESULT_BUILDER_DECL_PREFIX: Pattern/CurrNominal: static func buildEither(second component: Any) -> Any {|}; name=static func buildEither(second component: Any) -> Any; comment= +// IN_RESULT_BUILDER_DECL_PREFIX: Pattern/CurrNominal: static func buildArray(_ components: [Any]) -> Any {|}; name=static func buildArray(_ components: [Any]) -> Any; comment= +// IN_RESULT_BUILDER_DECL_PREFIX: Pattern/CurrNominal: static func buildLimitedAvailability(_ component: Any) -> Any {|}; name=static func buildLimitedAvailability(_ component: Any) -> Any; comment= +// IN_RESULT_BUILDER_DECL_PREFIX: Pattern/CurrNominal: static func buildFinalResult(_ component: Any) -> <#Result#> {|}; name=static func buildFinalResult(_ component: Any) -> <#Result#>; comment= +// IN_RESULT_BUILDER_DECL_PREFIX: End completions diff --git a/test/IDE/complete_sr13574.swift b/test/IDE/complete_sr13574.swift new file mode 100644 index 0000000000000..8069f4717a622 --- /dev/null +++ b/test/IDE/complete_sr13574.swift @@ -0,0 +1,31 @@ +// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=OVERRIDE | %FileCheck %s --check-prefix=OVERRIDE +// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=MEMBER | %FileCheck %s --check-prefix=MEMBER + +class Root { + func onRoot() {} +} + +class Base: Root { + func onBase() -> T {} +} + +class Derived: Base { + func onDerived() {} + + func #^OVERRIDE^# +// OVERRIDE: Begin completions, 2 items +// OVERRIDE-DAG: Decl[InstanceMethod]/Super/Erase[5]: override func onBase() -> T {|}; +// OVERRIDE-DAG: Decl[InstanceMethod]/Super/Erase[5]: override func onRoot() {|}; +// OVERRIDE-DAG: End completions + +} + +func testMember(val: Derived) { + val.#^MEMBER^# +// MEMBER: Begin completions, 4 items +// MEMBER-DAG: Keyword[self]/CurrNominal: self[#Derived#]; name=self +// MEMBER-DAG: Decl[InstanceMethod]/CurrNominal: onDerived()[#Void#]; name=onDerived() +// MEMBER-DAG: Decl[InstanceMethod]/Super: onBase()[#Int#]; name=onBase() +// MEMBER-DAG: Decl[InstanceMethod]/Super: onRoot()[#Void#]; name=onRoot() +// MEMBER: End completions +} diff --git a/test/IDE/print_ast_overlay.swift b/test/IDE/print_ast_overlay.swift index a2fe49f48b2eb..97b9490cf6b82 100644 --- a/test/IDE/print_ast_overlay.swift +++ b/test/IDE/print_ast_overlay.swift @@ -41,6 +41,6 @@ public class FooOverlayClassDerived : FooOverlayClassBase { // PASS_NO_INTERNAL-NOT: overlay_func_internal -// PASS_ANNOTATED: @_exported import Foo.FooSub // PASS_ANNOTATED: @_exported import Foo +// PASS_ANNOTATED: @_exported import Foo.FooSub // PASS_ANNOTATED: @_exported import FooHelper diff --git a/test/IDE/print_clang_objc_async.swift b/test/IDE/print_clang_objc_async.swift index 7d3029fff8be6..8520c4dfdcdb0 100644 --- a/test/IDE/print_clang_objc_async.swift +++ b/test/IDE/print_clang_objc_async.swift @@ -4,6 +4,7 @@ // RUN: %FileCheck -input-file %t/ObjCConcurrency.printed.txt %s // REQUIRES: objc_interop +// REQUIRES: concurrency // CHECK-LABEL: class SlowServer : NSObject { // CHECK-DAG: func doSomethingSlow(_ operation: String, completionHandler handler: @escaping (Int) -> Void) @@ -18,3 +19,11 @@ // CHECK-DAG: func findAnswerFailingly() async throws -> String? // CHECK-DAG: func doSomethingFun(_ operation: String) async // CHECK: {{^[}]$}} + +// CHECK-LABEL: protocol RefrigeratorDelegate +// CHECK-NEXT: @asyncHandler func someoneDidOpenRefrigerator(_ fridge: Any) +// CHECK-NEXT: @asyncHandler func refrigerator(_ fridge: Any, didGetFilledWithItems items: [Any]) +// CHECK-NEXT: {{^}} func refrigerator(_ fridge: Any, didGetFilledWithIntegers items: UnsafeMutablePointer, count: Int) +// CHECK-NEXT: {{^}} func refrigerator(_ fridge: Any, willAddItem item: Any) +// CHECK-NEXT: {{^}} func refrigerator(_ fridge: Any, didRemoveItem item: Any) -> Bool +// CHECK-NEXT: {{^[}]$}} diff --git a/test/IDE/print_swift_module.swift b/test/IDE/print_swift_module.swift index a54c31fa390b3..d8b5dfe4771f7 100644 --- a/test/IDE/print_swift_module.swift +++ b/test/IDE/print_swift_module.swift @@ -27,7 +27,7 @@ public func returnsAlias() -> Alias { return (0, 0) } -@_functionBuilder +@resultBuilder struct BridgeBuilder { static func buildBlock(_: Any...) {} } diff --git a/test/IDE/print_synthesized_extensions.swift b/test/IDE/print_synthesized_extensions.swift index 0b2cec63473f4..e2b8de3401167 100644 --- a/test/IDE/print_synthesized_extensions.swift +++ b/test/IDE/print_synthesized_extensions.swift @@ -237,21 +237,21 @@ extension S13 : P5 { public func foo1() {} } -// CHECK1: extension S1 where T : P2 { +// CHECK1: extension S1 where T : P2 { // CHECK1-NEXT: public func p2member() -// CHECK1-NEXT: public func ef1(t: T) +// CHECK1-NEXT: public func ef1(t: T) // CHECK1-NEXT: public func ef2(t: S2) // CHECK1-NEXT: } -// CHECK2: extension S1 where T : P3 { +// CHECK2: extension S1 where T : P3 { // CHECK2-NEXT: public func p3Func(i: Int) -> Int // CHECK2-NEXT: } -// CHECK3: extension S1 where T == Int { +// CHECK3: extension S1 where T == Int { // CHECK3-NEXT: public func p1IntFunc(i: Int) -> Int // CHECK3-NEXT: } -// CHECK4: extension S1 where T == S9<Int> { +// CHECK4: extension S1 where T == S9<Int> { // CHECK4-NEXT: public func S9IntFunc() // CHECK4-NEXT: } @@ -278,17 +278,17 @@ extension S13 : P5 { // CHECK8: public struct S4<T> : print_synthesized_extensions.P1 { // CHECK8-NEXT: public typealias T1 = Int // CHECK8-NEXT: public typealias T2 = Int -// CHECK8-NEXT: public func f1(t: print_synthesized_extensions.S4.T1) -> print_synthesized_extensions.S4.T1 -// CHECK8-NEXT: public func f2(t: print_synthesized_extensions.S4.T2) -> print_synthesized_extensions.S4.T2 +// CHECK8-NEXT: public func f1(t: print_synthesized_extensions.S4<T>.T1) -> print_synthesized_extensions.S4<T>.T1 +// CHECK8-NEXT: public func f2(t: print_synthesized_extensions.S4<T>.T2) -> print_synthesized_extensions.S4<T>.T2 // CHECK8-NEXT: public func p1IntFunc(i: Int) -> Int // CHECK8-NEXT: } // CHECK9: public struct S6<T> : print_synthesized_extensions.P1 { // CHECK9-NEXT: public typealias T1 = print_synthesized_extensions.S5 // CHECK9-NEXT: public typealias T2 = print_synthesized_extensions.S5 -// CHECK9-NEXT: public func f1(t: print_synthesized_extensions.S6.T1) -> print_synthesized_extensions.S6.T1 +// CHECK9-NEXT: public func f1(t: print_synthesized_extensions.S6<T>.T1) -> print_synthesized_extensions.S6<T>.T1 -// CHECK9-NEXT: public func f2(t: print_synthesized_extensions.S6.T2) -> print_synthesized_extensions.S6.T2 +// CHECK9-NEXT: public func f2(t: print_synthesized_extensions.S6<T>.T2) -> print_synthesized_extensions.S6<T>.T2 // CHECK9-NEXT: public func f3() // CHECK9-NEXT: public func fromActualExtension() // CHECK9-NEXT: public func p3Func(i: Int) -> Int @@ -321,12 +321,12 @@ extension S13 : P5 { // CHECK13: public protocol P7 { // CHECK13-NEXT: associatedtype T1 -// CHECK13-NEXT: func f1(t: Self.T1) +// CHECK13-NEXT: func f1(t: Self.T1) // CHECK13-NEXT: } // CHECK13: extension P7 { -// CHECK13-NEXT: public func nomergeFunc(t: Self.T1) -> Self.T1 -// CHECK13-NEXT: public func f1(t: Self.T1) -> Self.T1 +// CHECK13-NEXT: public func nomergeFunc(t: Self.T1) -> Self.T1 +// CHECK13-NEXT: public func f1(t: Self.T1) -> Self.T1 // CHECK13-NEXT: } // CHECK14: public struct S13 { diff --git a/test/IRGen/Inputs/pre_specialize_module.swift b/test/IRGen/Inputs/pre_specialize_module.swift new file mode 100644 index 0000000000000..8993ec0b92245 --- /dev/null +++ b/test/IRGen/Inputs/pre_specialize_module.swift @@ -0,0 +1,112 @@ +@usableFromInline +struct ResilientInternalBoxedThing { + @usableFromInline + var t: T + + @usableFromInline + init(_ t: T) { + self.t = t + } +} + +@usableFromInline +@frozen +struct InternalThing { + @usableFromInline + var t: T + + @usableFromInline + init(_ t: T) { + self.t = t + } + + @_specialize(exported: true, where T == Int) + @_specialize(exported: true, where T == Bool) + @_specialize(exported: true, where T == ResilientInternalBoxedThing) + @inlinable + func compute() -> T { + return t + } + + @inlinable + var computedX : T { + @_specialize(exported: true, where T == Int) + get { + return t + } + @_specialize(exported: true, where T == Int) + set { + t = newValue + } + } + + @inlinable + subscript(_ i: Int) -> T { + @_specialize(exported: true, where T == Int) + get { + return t + } + @_specialize(exported: true, where T == Int) + set { + t = newValue + } + } +} + +@usableFromInline +class InternalRef { + @usableFromInline + var t: T + + @usableFromInline + init(_ t: T) { + self.t = t + } + + @_specialize(exported: true, where T == Int) + @_specialize(exported: true, where T == Bool) + @_specialize(exported: true, where T == ResilientInternalBoxedThing) + @inlinable + final func compute() -> T { + return t + } + + @inlinable + final var computedX : T { + @_specialize(exported: true, where T == Int) + get { + return t + } + @_specialize(exported: true, where T == Int) + set { + t = newValue + } + } + + @inlinable + final subscript(_ i: Int) -> T { + @_specialize(exported: true, where T == Int) + get { + return t + } + @_specialize(exported: true, where T == Int) + set { + t = newValue + } + } +} + +@inline(never) +@inlinable +public func testSpecialization(_ t: T) { + print(InternalThing(ResilientInternalBoxedThing(t)).compute()) + + print(InternalThing(t).compute()) + + var i = InternalThing(t) + i.computedX = t + print(i.computedX) + + i[1] = t + print(i[2]) +} diff --git a/test/IRGen/Inputs/pre_specialize_module_B.swift b/test/IRGen/Inputs/pre_specialize_module_B.swift new file mode 100644 index 0000000000000..fc30480ef27a2 --- /dev/null +++ b/test/IRGen/Inputs/pre_specialize_module_B.swift @@ -0,0 +1,68 @@ +import A + +public class AnotherThing { + public init() {} +} + +@_specializeExtension +extension InternalThing { + + @_specialize(exported: true, target: compute() , where T == AnotherThing) + @_specialize(exported: true, target: compute(), where T == ResilientInternalBoxedThing) + public func specializeCompute() -> T { + fatalError("don't call") + } + + public var specializeComputedX : T { + @_specialize(exported: true, target: computedX, where T == AnotherThing) + get { + fatalError("don't call") + } + @_specialize(exported: true, target: computedX, where T == AnotherThing) + set { + fatalError("don't call") + } + } + + public subscript(specialized i: Int) -> T { + @_specialize(exported: true, target: subscript(_:), where T == AnotherThing) + get { + fatalError("don't call") + } + @_specialize(exported: true, target: subscript(_:), where T == AnotherThing) + set { + fatalError("don't call") + } + } +} + +@_specializeExtension +extension InternalRef { + + @_specialize(exported: true, target: compute() , where T == AnotherThing) + public func specializeCompute() -> T { + fatalError("don't call") + } + + public var specializeComputedX : T { + @_specialize(exported: true, target: computedX, where T == AnotherThing) + get { + fatalError("don't call") + } + @_specialize(exported: true, target: computedX, where T == AnotherThing) + set { + fatalError("don't call") + } + } + + public subscript(specialized i: Int) -> T { + @_specialize(exported: true, target: subscript(_:), where T == AnotherThing) + get { + fatalError("don't call") + } + @_specialize(exported: true, target: subscript(_:), where T == AnotherThing) + set { + fatalError("don't call") + } + } +} diff --git a/test/IRGen/associated_types.swift b/test/IRGen/associated_types.swift index 5d0953f1a0e6c..f5d152beb7d1f 100644 --- a/test/IRGen/associated_types.swift +++ b/test/IRGen/associated_types.swift @@ -75,8 +75,9 @@ 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: [[T2:%.*]] = call swiftcc %swift.metadata_response @swift_getAssociatedTypeWitness([[INT]] 0, i8** %T.Runcible, %swift.type* %T, %swift.protocol_requirement* getelementptr{{.*}}i32 12), i32 -1), %swift.protocol_requirement* getelementptr inbounds (<{{.*}}>, <{{.*}}>* @"$s16associated_types8RuncibleMp", i32 0, i32 14)) [[NOUNWIND_READNONE:#.*]] +// CHECK: [[T2:%.*]] = call swiftcc %swift.metadata_response @swift_getAssociatedTypeWitness([[INT]] 255, i8** %T.Runcible, %swift.type* %T, %swift.protocol_requirement* getelementptr{{.*}}i32 12), i32 -1), %swift.protocol_requirement* getelementptr inbounds (<{{.*}}>, <{{.*}}>* @"$s16associated_types8RuncibleMp", i32 0, i32 14)) [[NOUNWIND_READNONE:#.*]] // CHECK-NEXT: %T.RuncerType = extractvalue %swift.metadata_response [[T2]], 0 +// CHECK-NEXT: extractvalue %swift.metadata_response [[T2]], 1 // 2. Get the witness table for U.RuncerType.Runcee : Speedy // 2a. Get the protocol witness table for U.RuncerType : FastRuncer. // CHECK-NEXT: %T.RuncerType.FastRuncer = call swiftcc i8** @swift_getAssociatedConformanceWitness(i8** %U.FastRuncible, %swift.type* %U, %swift.type* %T.RuncerType diff --git a/test/IRGen/async.swift b/test/IRGen/async.swift index 86d5b33b90e80..46ee16dc50472 100644 --- a/test/IRGen/async.swift +++ b/test/IRGen/async.swift @@ -1,5 +1,7 @@ // RUN: %target-swift-frontend -primary-file %s -emit-ir -enable-experimental-concurrency | %FileCheck %s +// REQUIRES: concurrency + // CHECK: "$s5async1fyyYF" public func f() async { } diff --git a/test/IRGen/async/partial_apply.sil b/test/IRGen/async/partial_apply.sil new file mode 100644 index 0000000000000..dead2ef916796 --- /dev/null +++ b/test/IRGen/async/partial_apply.sil @@ -0,0 +1,527 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-module -enable-library-evolution -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 --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize + +// REQUIRES: CPU=x86_64 +// REQUIRES: concurrency + +import Builtin +import Swift +import resilient_struct + +class SwiftClass {} +sil_vtable SwiftClass {} +sil @$s13partial_apply10SwiftClassCfD : $@async @convention(method) (SwiftClass) -> () + +sil @partially_applyable_to_class : $@async @convention(thin) (@owned SwiftClass) -> () +sil @partially_applyable_to_two_classes : $@async @convention(thin) (@owned SwiftClass, @owned SwiftClass) -> () + +sil @use_closure : $@async @convention(thin) (@noescape @async @callee_guaranteed () -> ()) -> () + +// CHECK: define{{( dllexport)?}}{{( protected)?}} swiftcc void @partial_apply_class(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]+}} { +sil @partial_apply_class : $@async @convention(thin) (SwiftClass) -> @async @callee_owned () -> () { +entry(%c : $SwiftClass): + %f = function_ref @partially_applyable_to_class : $@async @convention(thin) (@owned SwiftClass) -> () + %g = partial_apply %f(%c) : $@async @convention(thin) (@owned SwiftClass) -> () + return %g : $@async @callee_owned () -> () +} + +// CHECK: define{{( dllexport)?}}{{( protected)?}} swiftcc void @partial_apply_class_on_stack(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]+}} { +sil @partial_apply_class_on_stack : $@async @convention(thin) (@owned SwiftClass) -> () { +entry(%a : $SwiftClass): + %f = function_ref @partially_applyable_to_class : $@async @convention(thin) (@owned SwiftClass) -> () + %c = partial_apply [callee_guaranteed] [on_stack] %f(%a) : $@async @convention(thin) (@owned SwiftClass) -> () + %use = function_ref @use_closure : $@async @convention(thin) (@noescape @async @callee_guaranteed () -> ()) -> () + apply %use(%c) : $@async @convention(thin) (@noescape @async @callee_guaranteed () -> ()) -> () + dealloc_stack %c : $@noescape @async @callee_guaranteed () ->() + strong_release %a : $SwiftClass + %t = tuple() + return %t : $() +} + +// CHECK: define{{( dllexport)?}}{{( protected)?}} swiftcc void @partial_apply_two_classes_on_stack(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]+}} { + +sil @partial_apply_two_classes_on_stack : $@async @convention(thin) (@owned SwiftClass, @owned SwiftClass) -> () { +entry(%a : $SwiftClass, %b: $SwiftClass): + %f = function_ref @partially_applyable_to_two_classes : $@async @convention(thin) (@owned SwiftClass, @owned SwiftClass) -> () + %c = partial_apply [callee_guaranteed] [on_stack] %f(%a, %b) : $@async @convention(thin) (@owned SwiftClass, @owned SwiftClass) -> () + %use = function_ref @use_closure : $@async @convention(thin) (@noescape @async @callee_guaranteed () -> ()) -> () + apply %use(%c) : $@async @convention(thin) (@noescape @async @callee_guaranteed () -> ()) -> () + dealloc_stack %c : $@noescape @async @callee_guaranteed () ->() + strong_release %a : $SwiftClass + strong_release %b : $SwiftClass + %t = tuple() + return %t : $() +} +// CHECK-LABEL: define internal swiftcc void @"$s22generic_captured_paramTA"(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]+}}) {{#[0-9]+}} { +sil public_external @generic_captured_param : $@async @convention(thin) (Int, @inout T) -> Int + +sil @partial_apply_generic_capture : $@async @convention(thin) (Int) -> @async @callee_owned (Int) -> Int { +entry(%x : $Int): + %a = alloc_stack $Int + store %x to %a : $*Int + %f = function_ref @generic_captured_param : $@async @convention(thin) (Int, @inout T) -> Int + %p = partial_apply %f(%a) : $@async @convention(thin) (Int, @inout T) -> Int + dealloc_stack %a : $*Int + return %p : $@async @callee_owned (Int) -> Int +} + +sil public_external @generic_captured_and_open_param : $@async @convention(thin) (@in T, @inout T) -> @out T + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @partial_apply_open_generic_capture(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]+}} { +sil @partial_apply_open_generic_capture : $@async @convention(thin) (@inout T) -> @async @callee_owned (@in T) -> @out T { +entry(%a : $*T): + %f = function_ref @generic_captured_and_open_param : $@async @convention(thin) (@in U, @inout U) -> @out U + %p = partial_apply %f(%a) : $@async @convention(thin) (@in U, @inout U) -> @out U + return %p : $@async @callee_owned (@in T) -> @out T +} + +/*****************************************************************************/ +/* Swift-refcounted class captures. Optimizable by using the reference */ +/* as the partial apply context. */ +/*****************************************************************************/ + +sil public_external @guaranteed_captured_class_param : $@async @convention(thin) (Int, @guaranteed SwiftClass) -> Int + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @partial_apply_guaranteed_class_param(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]+}} { + +sil @partial_apply_guaranteed_class_param : $@async @convention(thin) (@owned SwiftClass) -> @async @callee_owned (Int) -> Int { +bb0(%x : $SwiftClass): + %f = function_ref @guaranteed_captured_class_param : $@async @convention(thin) (Int, @guaranteed SwiftClass) -> Int + %p = partial_apply %f(%x) : $@async @convention(thin) (Int, @guaranteed SwiftClass) -> Int + return %p : $@async @callee_owned (Int) -> Int +} + +sil public_external @indirect_guaranteed_captured_class_param : $@async @convention(thin) (Int, @in_guaranteed SwiftClass) -> Int + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @partial_apply_indirect_guaranteed_class_param(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]+}} { +// CHECK-LABEL: define internal swiftcc void @"$s40indirect_guaranteed_captured_class_paramTA"(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]+}}) {{#[0-9]+}} { + +sil @partial_apply_indirect_guaranteed_class_param : $@async @convention(thin) (@in SwiftClass) -> @async @callee_owned (Int) -> Int { +bb0(%x : $*SwiftClass): + %f = function_ref @indirect_guaranteed_captured_class_param : $@async @convention(thin) (Int, @in_guaranteed SwiftClass) -> Int + %p = partial_apply %f(%x) : $@async @convention(thin) (Int, @in_guaranteed SwiftClass) -> Int + return %p : $@async @callee_owned (Int) -> Int +} + +sil public_external @indirect_consumed_captured_class_param : $@async @convention(thin) (Int, @in SwiftClass) -> Int + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @partial_apply_indirect_consumed_class_param(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]+}} { +// CHECK-LABEL: define internal swiftcc void @"$s38indirect_consumed_captured_class_paramTA"(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]+}}) {{#[0-9]+}} { + +sil @partial_apply_indirect_consumed_class_param : $@async @convention(thin) (@in SwiftClass) -> @async @callee_owned (Int) -> Int { +bb0(%x : $*SwiftClass): + %f = function_ref @indirect_consumed_captured_class_param : $@async @convention(thin) (Int, @in SwiftClass) -> Int + %p = partial_apply %f(%x) : $@async @convention(thin) (Int, @in SwiftClass) -> Int + return %p : $@async @callee_owned (Int) -> Int +} + +/*****************************************************************************/ +/* A non-trivial capture. Indirect applications can directly reference the */ +/* field from the partial apply context. */ +/*****************************************************************************/ + +struct SwiftClassPair { var x: SwiftClass, y: SwiftClass } + +sil public_external @guaranteed_captured_class_pair_param : $@async @convention(thin) (Int, @guaranteed SwiftClassPair) -> Int + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @partial_apply_guaranteed_class_pair_param(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]+}} { +// CHECK-LABEL: define internal swiftcc void @"$s36guaranteed_captured_class_pair_paramTA"(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]+}}) {{#[0-9]+}} { + +sil @partial_apply_guaranteed_class_pair_param : $@async @convention(thin) (@owned SwiftClassPair) -> @async @callee_owned (Int) -> Int { +bb0(%x : $SwiftClassPair): + %f = function_ref @guaranteed_captured_class_pair_param : $@async @convention(thin) (Int, @guaranteed SwiftClassPair) -> Int + %p = partial_apply %f(%x) : $@async @convention(thin) (Int, @guaranteed SwiftClassPair) -> Int + return %p : $@async @callee_owned (Int) -> Int +} + +sil public_external @indirect_guaranteed_captured_class_pair_param : $@async @convention(thin) (Int, @in_guaranteed SwiftClassPair) -> Int + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @partial_apply_indirect_guaranteed_class_pair_param(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]+}} { +// CHECK-LABEL: define internal swiftcc void @"$s45indirect_guaranteed_captured_class_pair_paramTA"(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]+}}) {{#[0-9]+}} { + +sil @partial_apply_indirect_guaranteed_class_pair_param : $@async @convention(thin) (@in SwiftClassPair) -> @async @callee_owned (Int) -> Int { +bb0(%x : $*SwiftClassPair): + %f = function_ref @indirect_guaranteed_captured_class_pair_param : $@async @convention(thin) (Int, @in_guaranteed SwiftClassPair) -> Int + %p = partial_apply %f(%x) : $@async @convention(thin) (Int, @in_guaranteed SwiftClassPair) -> Int + return %p : $@async @callee_owned (Int) -> Int +} + +sil public_external @indirect_consumed_captured_class_pair_param : $@async @convention(thin) (Int, @in SwiftClassPair) -> Int + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @partial_apply_indirect_consumed_class_pair_param(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]+}} { +// CHECK-LABEL: define internal swiftcc void @"$s43indirect_consumed_captured_class_pair_paramTA"(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]+}}) {{#[0-9]+}} { + +sil @partial_apply_indirect_consumed_class_pair_param : $@async @convention(thin) (@in SwiftClassPair) -> @async @callee_owned (Int) -> Int { +bb0(%x : $*SwiftClassPair): + %f = function_ref @indirect_consumed_captured_class_pair_param : $@async @convention(thin) (Int, @in SwiftClassPair) -> Int + %p = partial_apply %f(%x) : $@async @convention(thin) (Int, @in SwiftClassPair) -> Int + return %p : $@async @callee_owned (Int) -> Int +} + +sil public_external @captured_fixed_and_dependent_params : $@async @convention(thin) (@owned SwiftClass, @in A, Int) -> () + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @partial_apply_indirect_non_fixed_layout(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]+}} { +// CHECK-LABEL: define internal swiftcc void @"$s35captured_fixed_and_dependent_paramsTA"(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]+}}) {{#[0-9]+}} { +sil @partial_apply_indirect_non_fixed_layout : $@async @convention(thin) (@owned SwiftClass, @in T, Int) -> @async @callee_owned () -> () { +bb0(%a : $SwiftClass, %b : $*T, %c : $Int): + %f = function_ref @captured_fixed_and_dependent_params : $@async @convention(thin) (@owned SwiftClass, @in B, Int) -> () + %p = partial_apply %f(%a, %b, %c) : $@async @convention(thin) (@owned SwiftClass, @in C, Int) -> () + return %p : $@async @callee_owned () -> () +} + +sil public_external @captured_dependent_out_param : $@async @convention(thin) (@in A) -> @out A + +sil @partial_apply_with_out_param : $@async @convention(thin) (@in T) -> @async @callee_owned () -> @out T { +bb0(%x : $*T): + %f = function_ref @captured_dependent_out_param : $@async @convention(thin) (@in B) -> @out B + %p = partial_apply %f(%x) : $@async @convention(thin) (@in C) -> @out C + return %p : $@async @callee_owned () -> @out T +} + +// CHECK-LABEL: define internal swiftcc void @"$s28captured_dependent_out_paramTA"(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]+}}) {{#[0-9]+}} { + +sil @partial_apply_dynamic_with_out_param : $@async @convention(thin) (Int32, @owned @async @callee_owned (Int32) -> @out T) -> @async @callee_owned () -> @out T { +bb0(%x : $Int32, %f : $@async @callee_owned (Int32) -> @out T): + %p = partial_apply %f(%x) : $@async @callee_owned (Int32) -> @out T + return %p : $@async @callee_owned () -> @out T +} + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @partial_apply_dynamic_with_out_param(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]+}} { + +class Base { +} +sil_vtable Base {} + +class Sub : Base { +} + +sil_vtable Sub {} + +sil @parametric_casting_closure : $@async @convention(thin) (@owned Base) -> @owned C { +bb0(%0 : $Base): + %1 = unconditional_checked_cast %0 : $Base to C + return %1 : $C +} + +sil public_external @receive_closure : $@async @convention(thin) (@owned @async @callee_owned () -> (@owned C)) -> () + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @test_partial_apply(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]+}} { + +// CHECK-LABEL: define internal swiftcc void @"$s26parametric_casting_closureTA"(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]+}}) {{#[0-9]+}} { +// CHECK-LABEL: define internal swiftcc void @"$s26parametric_casting_closureTA.{{[0-9]+}}"(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]+}}) {{#[0-9]+}} { + +sil @test_partial_apply : $@async @convention(thin) (@owned Base) -> () { +bb0(%0 : $Base): + %1 = function_ref @parametric_casting_closure : $@async @convention(thin) (@owned Base) -> @owned C + %6 = partial_apply %1() : $@async @convention(thin) (@owned Base) -> @owned C + %2 = partial_apply %1(%0) : $@async @convention(thin) (@owned Base) -> @owned C + %3 = function_ref @receive_closure : $@async @convention(thin) (@owned @async @callee_owned () -> (@owned C)) -> () + %4 = apply %3(%2) : $@async @convention(thin) (@owned @async @callee_owned () -> (@owned C)) -> () + %5 = tuple () + return %5 : $() +} + +sil public_external @partial_empty_box : $@async @convention(thin) (@owned <τ_0_0> { var τ_0_0 } <()>, @inout_aliasable ()) -> () + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @empty_box(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]+}} { +sil @empty_box : $@async @convention(thin) () -> () { +entry: + // CHECK: [[BOX:%.*]] = call {{.*}}swift_allocEmptyBox + // CHECK: store %swift.refcounted* [[BOX]] + // CHECK: store %swift.opaque* undef + %b = alloc_box $<τ_0_0> { var τ_0_0 } <()> + %ba = project_box %b : $<τ_0_0> { var τ_0_0 } <()>, 0 + %f = function_ref @partial_empty_box : $@async @convention(thin) (@owned <τ_0_0> { var τ_0_0 } <()>, @inout_aliasable ()) -> () + %g = partial_apply %f(%b, %ba) : $@async @convention(thin) (@owned <τ_0_0> { var τ_0_0 } <()>, @inout_aliasable ()) -> () + return undef : $() +} + +protocol P0 {} +protocol P1 { associatedtype X : P0 } +protocol P2 { associatedtype Y : P1 } + +sil hidden_external @complex_generic_function : $@async @convention(thin) (Int) -> () + +sil @partial_apply_complex_generic_function : $@async @convention(thin) (Int) -> () { +bb0(%0 : $Int): + %fn = function_ref @complex_generic_function : $@async @convention(thin) (Int) -> () + %pa = partial_apply %fn (%0) : $@async @convention(thin) (Int) -> () + %result = tuple () + return %result : $() +} +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @partial_apply_complex_generic_function(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]+}} { +// CHECK-LABEL: define internal swiftcc void @"$s24complex_generic_functionTA"(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]+}}) {{#[0-9]+}} { + +struct ComplexBoundedType {} + +// SR-901: Ensure that a partial_apply which captures bound generic type +// metadata doesn't crash when restoring the generic context. + +sil hidden_external @generic_function : $@async @convention(thin) () -> () +sil @partial_apply_with_generic_type : $@async @convention(thin) () -> () { +bb0: + %fn = function_ref @generic_function : $@async @convention(thin) () -> () + %pa = partial_apply %fn >() : $@async @convention(thin) () -> () + %result = tuple () + return %result : $() +} + +// Crash on partial apply of witness_method without generic signature + +extension Int: P0 {} + +sil hidden_external @concrete_witness_method : $@async @convention(witness_method: P0) (Int, Int) -> () + +sil hidden @partial_apply_witness_method : $@async @convention(thin) (Int) -> () { +bb0(%0 : $Int): + %fn = function_ref @concrete_witness_method : $@async @convention(witness_method: P0) (Int, Int) -> () + %pa = partial_apply %fn (%0) : $@async @convention(witness_method: P0) (Int, Int) -> () + %result = tuple () + return %result : $() +} + + +// Crash on partial apply of a generic enum. +enum GenericEnum { + case X(String) + case Y(T, T, T, T, T) +} +sil public_external @generic_indirect_return : $@async @convention(thin) (Int) -> @owned GenericEnum + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @partial_apply_generic_indirect_return(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]+}} { +// CHECK-LABEL: define internal swiftcc void @"$s23generic_indirect_returnTA"(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]+}}) {{#[0-9]+}} { +sil @partial_apply_generic_indirect_return : $@async @convention(thin) (Int) -> @async @callee_owned () -> @owned GenericEnum { + bb0(%0 : $Int): + %fn = function_ref @generic_indirect_return :$@async @convention(thin) (Int) -> @owned GenericEnum + %pa = partial_apply %fn (%0) : $@async @convention(thin) (Int) -> @owned GenericEnum + return %pa : $@async @callee_owned () -> @owned GenericEnum + +} + +// Crash on partial apply of a generic enum. +enum GenericEnum2 { + case X(String) + case Y(T) +} +sil public_external @generic_indirect_return2 : $@async @convention(thin) (Int) -> @owned GenericEnum2 + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @partial_apply_generic_indirect_return2(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]+}} { +// CHECK-LABEL: define internal swiftcc void @"$s24generic_indirect_return2TA"(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]+}}) {{#[0-9]+}} { +sil @partial_apply_generic_indirect_return2 : $@async @convention(thin) (Int) -> @async @callee_owned () -> @owned GenericEnum2 { + bb0(%0 : $Int): + %fn = function_ref @generic_indirect_return2 :$@async @convention(thin) (Int) -> @owned GenericEnum2 + %pa = partial_apply %fn (%0) : $@async @convention(thin) (Int) -> @owned GenericEnum2 + return %pa : $@async @callee_owned () -> @owned GenericEnum2 +} + +struct SwiftStruct {} + +sil @fun : $@async @convention(thin) (@thin SwiftStruct.Type, @owned SwiftClass) -> () + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @partial_apply_thin_type(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]+}} { + +sil @partial_apply_thin_type : $@async @convention(thin) (@thin SwiftStruct.Type, @owned SwiftClass) -> @async @callee_owned () -> () { +entry(%0: $@thin SwiftStruct.Type, %1: $SwiftClass): + %fun = function_ref @fun : $@async @convention(thin) (@thin SwiftStruct.Type, @owned SwiftClass) -> () + %closure = partial_apply %fun (%0, %1) : $@async @convention(thin) (@thin SwiftStruct.Type, @owned SwiftClass) -> () + return %closure : $@async @callee_owned () -> () +} + +sil @afun : $@async @convention(thin) (Int) -> @error Error + +// Check that we don't assert on a thin noescape function. +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @convert_thin_test(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]+}} { +sil @convert_thin_test : $@async @convention(thin) (Int) -> () { +bb(%0 : $Int): + %f = function_ref @afun : $@async @convention(thin) (Int) -> @error Error + %c = convert_function %f : $@async @convention(thin) (Int) -> @error Error to $@async @convention(thin) @noescape (Int) -> @error Error + try_apply %c(%0) : $@async @convention(thin) @noescape (Int) -> @error Error, normal bb2, error bb1 + +bb1(%err: $Error): + %t = tuple () + br bb3(%t: $()) + +bb2(%r : $()): + br bb3(%r : $()) + +bb3(%v : $()): + return %v : $() +} + +struct A1 { + let b: () -> () +} + +struct A2 { + let a: T +} + +class A3 {} + +sil @amethod : $@async @convention(method) (@in_guaranteed A2) -> (@owned A1, @error Error) + +sil @repo : $@async @convention(thin) (@in_guaranteed A2) -> @owned @async @callee_guaranteed () -> (@owned A1, @error Error) { +bb0(%0 : $*A2): + %1 = load %0 : $*A2 + %2 = alloc_stack $A2 + store %1 to %2 : $*A2 + %4 = function_ref @amethod : $@async @convention(method) (@in_guaranteed A2) -> (@owned A1, @error Error) + %5 = partial_apply [callee_guaranteed] %4(%2) : $@async @convention(method) (@in_guaranteed A2) -> (@owned A1, @error Error) + dealloc_stack %2 : $*A2 + return %5 : $@async @callee_guaranteed () -> (@owned A1, @error Error) +} + +//sil @capture_class : $@async @convention(thin) (@guaranteed A3) -> () +// +//// CHECK LABEL: define{{.*}} swiftcc i8* @partial_apply_stack_in_coroutine(i8* {{.*}} %0, %T13partial_apply2A3C* %1) +//sil @partial_apply_stack_in_coroutine : $@async @yield_once (@owned A3) -> () { +//entry(%0: $A3): +// %f = function_ref @capture_class : $@async @convention(thin) (@guaranteed A3) -> () +// %p = partial_apply [callee_guaranteed] [on_stack] %f(%0) : $@async @convention(thin) (@guaranteed A3) -> () +// apply %p() : $@noescape @async @callee_guaranteed () -> () +// dealloc_stack %p : $@noescape @async @callee_guaranteed () -> () +// %1000 = integer_literal $Builtin.Int32, 1000 +// yield (), resume resume, unwind unwind +// +//resume: +// %ret = tuple () +// return %ret : $() +// +//unwind: +// unwind +//} +sil_vtable A3 {} + + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @partial_apply_callee_guaranteed_indirect_guaranteed_class_pair_param(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]+}} { + +sil @partial_apply_callee_guaranteed_indirect_guaranteed_class_pair_param : $@async @convention(thin) (@in SwiftClassPair) -> @owned @async @callee_guaranteed (Int) -> Int { +bb0(%x : $*SwiftClassPair): + %f = function_ref @indirect_guaranteed_captured_class_pair_param : $@async @convention(thin) (Int, @in_guaranteed SwiftClassPair) -> Int + %p = partial_apply [callee_guaranteed] %f(%x) : $@async @convention(thin) (Int, @in_guaranteed SwiftClassPair) -> Int + return %p : $@async @callee_guaranteed(Int) -> (Int) +} + +sil public_external @use_closure2 : $@async @convention(thin) (@noescape @async @callee_guaranteed (Int) -> Int) -> () + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @partial_apply_stack_callee_guaranteed_indirect_guaranteed_class_pair_param(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]+}} { +// CHECK-LABEL: define internal swiftcc void @"$s45indirect_guaranteed_captured_class_pair_paramTA.67"(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]+}}) {{#[0-9]+}} { + +sil @partial_apply_stack_callee_guaranteed_indirect_guaranteed_class_pair_param : $@async @convention(thin) (@in_guaranteed SwiftClassPair) -> () { +bb0(%x : $*SwiftClassPair): + %f = function_ref @indirect_guaranteed_captured_class_pair_param : $@async @convention(thin) (Int, @in_guaranteed SwiftClassPair) -> Int + %p = partial_apply [callee_guaranteed] [on_stack] %f(%x) : $@async @convention(thin) (Int, @in_guaranteed SwiftClassPair) -> Int + %u = function_ref @use_closure2 : $@async @convention(thin) (@noescape @async @callee_guaranteed (Int) -> Int) -> () + %r = apply %u(%p) : $@async @convention(thin) (@noescape @async @callee_guaranteed (Int) -> Int) -> () + dealloc_stack %p : $@noescape @async @callee_guaranteed (Int) ->(Int) + %t = tuple() + return %t : $() +} + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @partial_apply_stack_callee_guaranteed_indirect_in_class_pair_param(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]+}} { +// CHECK-LABEL: define internal swiftcc void @"$s37indirect_in_captured_class_pair_paramTA"(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]+}}) {{#[0-9]+}} { + +sil public_external @indirect_in_captured_class_pair_param : $@async @convention(thin) (Int, @in SwiftClassPair) -> Int + +sil @partial_apply_stack_callee_guaranteed_indirect_in_class_pair_param : $@async @convention(thin) (@in SwiftClassPair) -> () { +bb0(%x : $*SwiftClassPair): + %f = function_ref @indirect_in_captured_class_pair_param : $@async @convention(thin) (Int, @in SwiftClassPair) -> Int + %p = partial_apply [callee_guaranteed] [on_stack] %f(%x) : $@async @convention(thin) (Int, @in SwiftClassPair) -> Int + %u = function_ref @use_closure2 : $@async @convention(thin) (@noescape @async @callee_guaranteed (Int) -> Int) -> () + %r = apply %u(%p) : $@async @convention(thin) (@noescape @async @callee_guaranteed (Int) -> Int) -> () + dealloc_stack %p : $@noescape @async @callee_guaranteed (Int) ->(Int) + destroy_addr %x: $*SwiftClassPair + %t = tuple() + return %t : $() +} + + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @partial_apply_stack_callee_guaranteed_indirect_in_constant_class_pair_param(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]+}} { +// CHECK-LABEL: define internal swiftcc void @"$s46indirect_in_constant_captured_class_pair_paramTA"(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]+}}) {{#[0-9]+}} { + +sil public_external @indirect_in_constant_captured_class_pair_param : $@async @convention(thin) (Int, @in_constant SwiftClassPair) -> Int + +sil @partial_apply_stack_callee_guaranteed_indirect_in_constant_class_pair_param : $@async @convention(thin) (@in SwiftClassPair) -> () { +bb0(%x : $*SwiftClassPair): + %f = function_ref @indirect_in_constant_captured_class_pair_param : $@async @convention(thin) (Int, @in_constant SwiftClassPair) -> Int + %p = partial_apply [callee_guaranteed] [on_stack] %f(%x) : $@async @convention(thin) (Int, @in_constant SwiftClassPair) -> Int + %u = function_ref @use_closure2 : $@async @convention(thin) (@noescape @async @callee_guaranteed (Int) -> Int) -> () + %r = apply %u(%p) : $@async @convention(thin) (@noescape @async @callee_guaranteed (Int) -> Int) -> () + dealloc_stack %p : $@noescape @async @callee_guaranteed (Int) ->(Int) + destroy_addr %x: $*SwiftClassPair + %t = tuple() + return %t : $() +} + +sil public_external @closure : $@async @convention(thin) (@in_guaranteed ResilientInt, @guaranteed SwiftClass) -> () + +// Make sure that we use the heap header size (16) for the initial offset. +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @test_initial_offset(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]+}} { + +sil @test_initial_offset : $@async @convention(thin) (@in_guaranteed ResilientInt, @guaranteed SwiftClass) -> () { +bb0(%x : $*ResilientInt, %y : $SwiftClass): + %f = function_ref @closure : $@async @convention(thin) (@in_guaranteed ResilientInt, @guaranteed SwiftClass) -> () + %p = partial_apply [callee_guaranteed] %f(%x, %y) : $@async @convention(thin) (@in_guaranteed ResilientInt, @guaranteed SwiftClass) -> () + release_value %p : $@async @callee_guaranteed () ->() + %t = tuple() + return %t : $() +} + +protocol Proto1 {} +protocol Proto2 {} +struct EmptyType : Proto1 { } + +struct SomeType : Proto2 { + var d : ResilientInt // some resilient type + var x : Int +} + +sil @foo : $@async @convention(thin) <τ_0_0, τ_0_1 where τ_0_0 : Proto1, τ_0_1 : Proto2> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1) -> () + +// CHECK-64-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @empty_followed_by_non_fixed(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]+}} { + +sil @empty_followed_by_non_fixed : $@async @convention(thin) (EmptyType, @in_guaranteed SomeType) -> () { +entry(%0 : $EmptyType, %1: $*SomeType): + %5 = alloc_stack $EmptyType + store %0 to %5 : $*EmptyType + %31 = function_ref @foo : $@async @convention(thin) <τ_0_0, τ_0_1 where τ_0_0 : Proto1, τ_0_1 : Proto2> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1) -> () + %32 = alloc_stack $EmptyType + copy_addr %5 to [initialization] %32 : $*EmptyType + %34 = alloc_stack $SomeType + copy_addr %1 to [initialization] %34 : $*SomeType // id: %35 + %36 = partial_apply [callee_guaranteed] %31(%32, %34) : $@async @convention(thin) <τ_0_0, τ_0_1 where τ_0_0 : Proto1, τ_0_1 : Proto2> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1) -> () + release_value %36: $@async @callee_guaranteed () ->() + dealloc_stack %34 : $*SomeType + dealloc_stack %32 : $*EmptyType + dealloc_stack %5 : $*EmptyType + %40 = tuple() + return %40 : $() +} + +struct FixedType { + var f: Int32 +} +// CHECK-64-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @fixed_followed_by_empty_followed_by_non_fixed(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]+}} { + +sil @foo2 : $@async @convention(thin) <τ_0_0, τ_0_1, τ_0_2> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1, @in_guaranteed τ_0_2) -> () +sil @fixed_followed_by_empty_followed_by_non_fixed : $@async @convention(thin) (EmptyType, @in_guaranteed SomeType, FixedType) -> () { +entry(%0 : $EmptyType, %1: $*SomeType, %3: $FixedType): + %5 = alloc_stack $EmptyType + store %0 to %5 : $*EmptyType + %7 = alloc_stack $FixedType + store %3 to %7 : $*FixedType + %31 = function_ref @foo2 : $@async @convention(thin) <τ_0_0, τ_0_1, τ_0_2> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1, @in_guaranteed τ_0_2) -> () + %32 = alloc_stack $EmptyType + copy_addr %5 to [initialization] %32 : $*EmptyType + %34 = alloc_stack $SomeType + copy_addr %1 to [initialization] %34 : $*SomeType // id: %35 + %36 = partial_apply [callee_guaranteed] %31(%7, %32, %34) : $@async @convention(thin) <τ_0_0, τ_0_1, τ_0_2> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1, @in_guaranteed τ_0_2) -> () + release_value %36: $@async @callee_guaranteed () ->() + dealloc_stack %34 : $*SomeType + dealloc_stack %32 : $*EmptyType + dealloc_stack %7 : $*FixedType + dealloc_stack %5 : $*EmptyType + %40 = tuple() + return %40 : $() +} diff --git a/test/IRGen/async/partial_apply_forwarder.sil b/test/IRGen/async/partial_apply_forwarder.sil new file mode 100644 index 0000000000000..25fb5f64ba159 --- /dev/null +++ b/test/IRGen/async/partial_apply_forwarder.sil @@ -0,0 +1,231 @@ +// RUN: %target-swift-frontend -enable-experimental-concurrency -enable-objc-interop -primary-file %s -emit-ir | %FileCheck %s -DINT=i%target-ptrsize --check-prefixes=CHECK,CHECK-objc +// RUN: %target-swift-frontend -enable-experimental-concurrency -disable-objc-interop -primary-file %s -emit-ir | %FileCheck %s -DINT=i%target-ptrsize --check-prefixes=CHECK,CHECK-native + +// REQUIRES: concurrency + +sil_stage canonical + +import Builtin + +public protocol P {} +public class C : P {} +public class D {} +class E {} + +public protocol Observable { + associatedtype Result + func subscribe(o: T) async -> () +} + +public protocol Observer { + associatedtype Result +} + +sil hidden @witness_method : $@async @convention(thin) (@in S) -> @owned @async @callee_owned (@in O) -> () { +bb0(%0 : $*S): + %1 = witness_method $S, #Observable.subscribe : $@async @convention(witness_method: Observable) <τ_0_0 where τ_0_0 : Observable><τ_1_0 where τ_1_0 : Observer, τ_0_0.Result == τ_1_0.Result> (@in τ_1_0, @in_guaranteed τ_0_0) -> () + %2 = partial_apply %1(%0) : $@async @convention(witness_method: Observable) <τ_0_0 where τ_0_0 : Observable><τ_1_0 where τ_1_0 : Observer, τ_0_0.Result == τ_1_0.Result> (@in τ_1_0, @in_guaranteed τ_0_0) -> () + return %2 : $@async @callee_owned (@in O) -> () +} + +// CHECK-LABEL: define internal swiftcc void @"$s23unspecialized_uncurriedTA"(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]+}}) {{#[0-9]+}} { + +sil hidden @specialized_curried : $@async @convention(thin) (@owned E) -> @owned @async @callee_owned () -> @owned D { +bb0(%0 : $E): + %1 = function_ref @unspecialized_uncurried : $@async @convention(method) <τ_0_0 where τ_0_0 : P> (@guaranteed E) -> @owned D<τ_0_0> + %2 = partial_apply %1(%0) : $@async @convention(method) <τ_0_0 where τ_0_0 : P> (@guaranteed E) -> @owned D<τ_0_0> + return %2 : $@async @callee_owned () -> @owned D +} + +sil hidden_external @unspecialized_uncurried : $@async @convention(method) <τ_0_0 where τ_0_0 : P> (@guaranteed E) -> @owned D<τ_0_0> + + +// CHECK-LABEL: define internal swiftcc void @"$s7takingPTA"(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]+}}) {{#[0-9]+}} { +sil hidden_external @takingP : $@async @convention(method) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> () + +sil hidden @reabstract_context : $@async @convention(thin) (@owned C) -> () { +bb0(%0 : $C): + %6 = alloc_stack $C + store %0 to %6 : $*C + %8 = function_ref @takingP : $@async @convention(method) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> () + %9 = partial_apply %8(%6) : $@async @convention(method) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> () + dealloc_stack %6 : $*C + strong_release %9 : $@async @callee_owned() -> () + %10 = tuple () + return %10 : $() +} + +public protocol Q { + associatedtype Update +} + +public struct BaseProducer : Q { + public typealias Update = T +} + +public class WeakBox {} + +public struct EmptyType {} + +sil hidden_external @takingQ : $@async @convention(thin) <τ_0_0 where τ_0_0 : Q> (@owned WeakBox<τ_0_0>) -> () +sil hidden_external @takingQAndEmpty : $@async @convention(thin) <τ_0_0 where τ_0_0 : Q> (@owned WeakBox<τ_0_0>, EmptyType) -> () +sil hidden_external @takingEmptyAndQ : $@async @convention(thin) <τ_0_0 where τ_0_0 : Q> (EmptyType, @owned WeakBox<τ_0_0>) -> () + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @bind_polymorphic_param_from_context(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]+}} { +// CHECK-LABEL: define internal swiftcc void @"$s7takingQTA"(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]+}}) {{#[0-9]+}} { + +sil public @bind_polymorphic_param_from_context : $@async @convention(thin) <τ_0_1>(@in τ_0_1) -> @owned @async @callee_owned () -> () { +bb0(%0 : $*τ_0_1): + %1 = alloc_ref $WeakBox> + %8 = function_ref @takingQ : $@async @convention(thin) <τ_0_0 where τ_0_0 : Q> (@owned WeakBox<τ_0_0>) -> () + %9 = partial_apply %8>(%1) : $@async @convention(thin) <τ_0_0 where τ_0_0 : Q> (@owned WeakBox<τ_0_0>) -> () + return %9 : $@async @callee_owned () -> () +} + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @bind_polymorphic_param_from_context_2(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]+}} { +sil public @bind_polymorphic_param_from_context_2 : $@async @convention(thin) <τ_0_1>(@in τ_0_1, EmptyType) -> @owned @async @callee_owned () -> () { +bb0(%0 : $*τ_0_1, %2: $EmptyType): + %1 = alloc_ref $WeakBox> + %8 = function_ref @takingQAndEmpty : $@async @convention(thin) <τ_0_0 where τ_0_0 : Q> (@owned WeakBox<τ_0_0>, EmptyType) -> () + %9 = partial_apply %8>(%1, %2) : $@async @convention(thin) <τ_0_0 where τ_0_0 : Q> (@owned WeakBox<τ_0_0>, EmptyType) -> () + return %9 : $@async @callee_owned () -> () +} + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @bind_polymorphic_param_from_context_3(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]+}} { +sil public @bind_polymorphic_param_from_context_3 : $@async @convention(thin) <τ_0_1>(@in τ_0_1, EmptyType) -> @owned @async @callee_owned () -> () { +bb0(%0 : $*τ_0_1, %2: $EmptyType): + %1 = alloc_ref $WeakBox> + %8 = function_ref @takingEmptyAndQ : $@async @convention(thin) <τ_0_0 where τ_0_0 : Q> (EmptyType, @owned WeakBox<τ_0_0>) -> () + %9 = partial_apply %8>(%2, %1) : $@async @convention(thin) <τ_0_0 where τ_0_0 : Q> (EmptyType, @owned WeakBox<τ_0_0>) -> () + return %9 : $@async @callee_owned () -> () +} + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @bind_polymorphic_param_from_forwarder_parameter(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]+}} { +// CHECK-LABEL: define internal swiftcc void @"$s7takingQTA.19"(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]+}}) {{#[0-9]+}} { + +sil public @bind_polymorphic_param_from_forwarder_parameter : $@async @convention(thin) <τ_0_1>(@in τ_0_1) -> () { +bb0(%0 : $*τ_0_1): + %1 = alloc_ref $WeakBox> + %8 = function_ref @takingQ : $@async @convention(thin) <τ_0_0 where τ_0_0 : Q> (@owned WeakBox<τ_0_0>) -> () + %9 = partial_apply %8>() : $@async @convention(thin) <τ_0_0 where τ_0_0 : Q> (@owned WeakBox<τ_0_0>) -> () + %10 = tuple () + return %10 : $() +} + +struct S { + var x : Builtin.Int64 +} + +sil hidden_external @takingQAndS : $@async @convention(thin) <τ_0_0 where τ_0_0 : Q> (S, @owned WeakBox<τ_0_0>) -> () + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @bind_polymorphic_param_from_context_with_layout(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]+}} { +// CHECK-LABEL: define internal swiftcc void @"$s11takingQAndSTA"(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]+}}) {{#[0-9]+}} { +sil public @bind_polymorphic_param_from_context_with_layout : $@async @convention(thin) <τ_0_1>(@in τ_0_1, S) -> @owned @async @callee_owned () -> () { +bb0(%0 : $*τ_0_1, %1: $S): + %2 = alloc_ref $WeakBox> + %8 = function_ref @takingQAndS : $@async @convention(thin) <τ_0_0 where τ_0_0 : Q> (S, @owned WeakBox<τ_0_0>) -> () + %9 = partial_apply %8>(%1, %2) : $@async @convention(thin) <τ_0_0 where τ_0_0 : Q> (S, @owned WeakBox<τ_0_0>) -> () + return %9 : $@async @callee_owned () -> () +} + +public class Empty {} + +sil private @inner_closure : $@async @convention(thin) (Empty) -> @owned Empty { +bb0(%0 : $Empty): + return %0 : $Empty +} + +sil hidden @returns_closure : $@async @convention(thin) (Empty) -> (@owned Empty, @async @callee_guaranteed @owned (Empty) -> @owned Empty) { +bb0(%0 : $Empty): + %1 = function_ref @inner_closure : $@async @convention(thin) <τ_0_0> (Empty<τ_0_0>) -> @owned Empty<τ_0_0> + %2 = partial_apply [callee_guaranteed] %1() : $@async @convention(thin) <τ_0_0> (Empty<τ_0_0>) -> @owned Empty<τ_0_0> + %5 = tuple (%0 : $Empty, %2 : $@async @callee_guaranteed (Empty) -> @owned Empty) + return %5 : $(Empty, @async @callee_guaranteed (Empty) -> @owned Empty) +} + +sil hidden @specializes_closure_returning_closure : $@async @convention(thin) () -> @async @callee_guaranteed (Empty) -> (@owned Empty, @owned @async @callee_guaranteed (Empty) -> @owned Empty) { +bb0: + %0 = function_ref @returns_closure : $@async @convention(thin) <τ_0_0> (Empty<τ_0_0>) -> (@owned Empty<τ_0_0>, @owned @async @callee_guaranteed (Empty<τ_0_0>) -> @owned Empty<τ_0_0>) + %1 = partial_apply [callee_guaranteed] %0() : $@async @convention(thin) <τ_0_0> (Empty<τ_0_0>) -> (@owned Empty<τ_0_0>, @owned @async @callee_guaranteed (Empty<τ_0_0>) -> @owned Empty<τ_0_0>) + return %1 : $@async @callee_guaranteed (Empty) -> (@owned Empty, @owned @async @callee_guaranteed (Empty) -> @owned Empty) +} + +// CHECK-LABEL: define hidden swiftcc void @specializes_closure_returning_closure(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]+}} { +// CHECK-LABEL: define internal swiftcc void @"$s15returns_closureTA"(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]+}}) {{#[0-9]+}} { +protocol MyEquatable { + static func isEqual (lhs: Self, rhs: Self) -> Builtin.Int1 +} + +protocol MyExtended : MyEquatable { + func extended() +} +public struct Inner { + public init() + public init(_ e: Element) +} + +extension Inner : MyEquatable where Element : MyEquatable { + public static func isEqual (lhs: Inner, rhs: Inner) -> Builtin.Int1 +} + +public struct Outer { + init() +} + +extension Outer : MyEquatable where Value : MyEquatable { + public static func isEqual (lhs: Outer, rhs: Outer) -> Builtin.Int1 +} + +public struct Outermost { +} + +sil @$closure : $@async @convention(method) (Outer, Outer, @thin Outer.Type) -> Builtin.Int1 +sil @$closure2 : $@async @convention(method) (Outermost, Outermost, @thin Outermost.Type) -> Builtin.Int1 + +sil @$dont_crash_test_capture_specialized_conditional_conformance : $@async @convention(thin) (Outer>) -> () { +bb0(%0 : $Outer>): + %2 = alloc_stack $Outer> + store %0 to %2 : $*Outer> + %4 = metatype $@thin Outer>.Type + %5 = function_ref @$closure : $@async @convention(method) <τ_0_0 where τ_0_0 : MyEquatable> (Outer<τ_0_0>, Outer<τ_0_0>, @thin Outer<τ_0_0>.Type) -> Builtin.Int1 + %6 = partial_apply [callee_guaranteed] %5>(%4) : $@async @convention(method) <τ_0_0 where τ_0_0 : MyEquatable> (Outer<τ_0_0>, Outer<τ_0_0>, @thin Outer<τ_0_0>.Type) -> Builtin.Int1 + strong_release %6 : $@async @callee_guaranteed (Outer>, Outer>) -> Builtin.Int1 + dealloc_stack %2 : $*Outer> + %15 = tuple () + return %15 : $() +} + +protocol AssocType { + associatedtype A : MyExtended +} + +sil @$dont_crash_test_capture_specialized_conditional_conformance_associated_type : $@async @convention(thin) (Outer>) -> () { +bb0(%0 : $Outer>): + %2 = alloc_stack $Outer> + store %0 to %2 : $*Outer> + %4 = metatype $@thin Outer>.Type + %5 = function_ref @$closure : $@async @convention(method) <τ_0_0 where τ_0_0 : MyEquatable> (Outer<τ_0_0>, Outer<τ_0_0>, @thin Outer<τ_0_0>.Type) -> Builtin.Int1 + %6 = partial_apply [callee_guaranteed] %5>(%4) : $@async @convention(method) <τ_0_0 where τ_0_0 : MyEquatable> (Outer<τ_0_0>, Outer<τ_0_0>, @thin Outer<τ_0_0>.Type) -> Builtin.Int1 + strong_release %6 : $@async @callee_guaranteed (Outer>, Outer>) -> Builtin.Int1 + dealloc_stack %2 : $*Outer> + %15 = tuple () + return %15 : $() +} + +sil @$dont_crash_test_capture_specialized_conditional_conformance_nested : $@async @convention(thin) (Outer>) -> () { +bb0(%0 : $Outer>): + %4 = metatype $@thin Outermost>>.Type + %5 = function_ref @$closure2 : $@async @convention(method) (Outermost, Outermost, @thin Outermost.Type) -> Builtin.Int1 + %6 = partial_apply [callee_guaranteed] %5>>(%4) : $@async @convention(method) (Outermost, Outermost, @thin Outermost.Type) -> Builtin.Int1 + strong_release %6 : $@async @callee_guaranteed (Outermost>>, Outermost>>) -> Builtin.Int1 + %15 = tuple () + return %15 : $() +} + + +sil_vtable WeakBox {} +sil_vtable C {} +sil_vtable D {} +sil_vtable E {} +sil_vtable Empty {} +sil_witness_table C: P module main {} diff --git a/test/IRGen/async/run-call-classinstance-int64-to-void.sil b/test/IRGen/async/run-call-classinstance-int64-to-void.sil new file mode 100644 index 0000000000000..cf4c927962375 --- /dev/null +++ b/test/IRGen/async/run-call-classinstance-int64-to-void.sil @@ -0,0 +1,106 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency +// UNSUPPORTED: use_os_stdlib + + +import Builtin +import Swift +import PrintShims + +sil public_external @printAny : $@convention(thin) (@in_guaranteed Any) -> () + + + + + +class S { + func classinstanceSInt64ToVoid(_ int: Int64) async + deinit + init() +} + +// CHECK-LL: define hidden swiftcc void @classinstanceSInt64ToVoid(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]+}} { +sil hidden @classinstanceSInt64ToVoid : $@async @convention(method) (Int64, @guaranteed S) -> () { +bb0(%int : $Int64, %instance : $S): + %take_s = function_ref @take_S : $@async @convention(thin) (@guaranteed S) -> () + %result = apply %take_s(%instance) : $@async @convention(thin) (@guaranteed S) -> () + return %result : $() +} + +sil hidden @take_S : $@async @convention(thin) (@guaranteed S) -> () { +bb0(%instance : $S): + %any = alloc_stack $Any + strong_retain %instance : $S + %any_addr = init_existential_addr %any : $*Any, $S + store %instance to %any_addr : $*S + %print_any = function_ref @printAny : $@convention(thin) (@in_guaranteed Any) -> () + %result = apply %print_any(%any) : $@convention(thin) (@in_guaranteed Any) -> () + destroy_addr %any_addr : $*S + dealloc_stack %any : $*Any + return %result : $() +} + +sil hidden [exact_self_class] @S_allocating_init : $@convention(method) (@thick S.Type) -> @owned S { +bb0(%0 : $@thick S.Type): + %1 = alloc_ref $S + %2 = function_ref @$S_init : $@convention(method) (@owned S) -> @owned S + %3 = apply %2(%1) : $@convention(method) (@owned S) -> @owned S + return %3 : $S +} + +sil hidden @$S_init : $@convention(method) (@owned S) -> @owned S { +bb0(%0 : $S): + return %0 : $S +} + +sil hidden @$S_deinit : $@convention(method) (@guaranteed S) -> @owned Builtin.NativeObject { +bb0(%0 : $S): + %2 = unchecked_ref_cast %0 : $S to $Builtin.NativeObject + return %2 : $Builtin.NativeObject +} + +sil hidden @S_deallocating_deinit : $@convention(method) (@owned S) -> () { +bb0(%0 : $S): + %2 = function_ref @$S_deinit : $@convention(method) (@guaranteed S) -> @owned Builtin.NativeObject + %3 = apply %2(%0) : $@convention(method) (@guaranteed S) -> @owned Builtin.NativeObject + %4 = unchecked_ref_cast %3 : $Builtin.NativeObject to $S + dealloc_ref %4 : $S + %6 = tuple () + return %6 : $() +} + +sil_vtable S { + #S.classinstanceSInt64ToVoid: (S) -> (Int64) async -> () : @classinstanceSInt64ToVoid + #S.init!allocator: (S.Type) -> () -> S : @S_allocating_init + #S.deinit!deallocator: @S_deallocating_deinit +} + +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): + + %s_type = metatype $@thick S.Type + %allocating_init = function_ref @S_allocating_init : $@convention(method) (@thick S.Type) -> @owned S + %instance = apply %allocating_init(%s_type) : $@convention(method) (@thick S.Type) -> @owned S + %classinstanceSInt64ToVoid = class_method %instance : $S, #S.classinstanceSInt64ToVoid : (S) -> (Int64) async -> (), $@convention(method) @async (Int64, @guaranteed S) -> () + strong_retain %instance : $S + %int_literal = integer_literal $Builtin.Int64, 42 + %int = struct $Int64 (%int_literal : $Builtin.Int64) + %result = apply %classinstanceSInt64ToVoid(%int, %instance) : $@convention(method) @async (Int64, @guaranteed S) -> () // CHECK: main.S + strong_release %instance : $S + + %2 = integer_literal $Builtin.Int32, 0 + %3 = struct $Int32 (%2 : $Builtin.Int32) + return %3 : $Int32 // id: %4 +} + + + diff --git a/test/IRGen/async/run-call-classinstance-void-to-void.sil b/test/IRGen/async/run-call-classinstance-void-to-void.sil new file mode 100644 index 0000000000000..66a421af0994f --- /dev/null +++ b/test/IRGen/async/run-call-classinstance-void-to-void.sil @@ -0,0 +1,103 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency +// UNSUPPORTED: use_os_stdlib + + +import Builtin +import Swift +import PrintShims + +sil public_external @printAny : $@convention(thin) (@in_guaranteed Any) -> () + + + + + +class S { + func classinstanceSVoidToVoid() async + deinit + init() +} + +// CHECK-LL: define hidden swiftcc void @classinstanceSVoidToVoid(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]*}} { +sil hidden @classinstanceSVoidToVoid : $@async @convention(method) (@guaranteed S) -> () { +bb0(%instance : $S): + %take_s = function_ref @take_S : $@async @convention(thin) (@guaranteed S) -> () + %result = apply %take_s(%instance) : $@async @convention(thin) (@guaranteed S) -> () + return %result : $() +} + +sil hidden @take_S : $@async @convention(thin) (@guaranteed S) -> () { +bb0(%instance : $S): + %any = alloc_stack $Any + strong_retain %instance : $S + %any_addr = init_existential_addr %any : $*Any, $S + store %instance to %any_addr : $*S + %print_any = function_ref @printAny : $@convention(thin) (@in_guaranteed Any) -> () + %result = apply %print_any(%any) : $@convention(thin) (@in_guaranteed Any) -> () + destroy_addr %any_addr : $*S + dealloc_stack %any : $*Any + return %result : $() +} + +sil hidden [exact_self_class] @S_allocating_init : $@convention(method) (@thick S.Type) -> @owned S { +bb0(%0 : $@thick S.Type): + %1 = alloc_ref $S + %2 = function_ref @$S_init : $@convention(method) (@owned S) -> @owned S + %3 = apply %2(%1) : $@convention(method) (@owned S) -> @owned S + return %3 : $S +} + +sil hidden @$S_init : $@convention(method) (@owned S) -> @owned S { +bb0(%0 : $S): + return %0 : $S +} + +sil hidden @$S_deinit : $@convention(method) (@guaranteed S) -> @owned Builtin.NativeObject { +bb0(%0 : $S): + %2 = unchecked_ref_cast %0 : $S to $Builtin.NativeObject + return %2 : $Builtin.NativeObject +} + +sil hidden @S_deallocating_deinit : $@convention(method) (@owned S) -> () { +bb0(%0 : $S): + %2 = function_ref @$S_deinit : $@convention(method) (@guaranteed S) -> @owned Builtin.NativeObject + %3 = apply %2(%0) : $@convention(method) (@guaranteed S) -> @owned Builtin.NativeObject + %4 = unchecked_ref_cast %3 : $Builtin.NativeObject to $S + dealloc_ref %4 : $S + %6 = tuple () + return %6 : $() +} + +sil_vtable S { + #S.classinstanceSVoidToVoid: (S) -> () async -> () : @classinstanceSVoidToVoid + #S.init!allocator: (S.Type) -> () -> S : @S_allocating_init + #S.deinit!deallocator: @S_deallocating_deinit +} + +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): + + %s_type = metatype $@thick S.Type + %allocating_init = function_ref @S_allocating_init : $@convention(method) (@thick S.Type) -> @owned S + %instance = apply %allocating_init(%s_type) : $@convention(method) (@thick S.Type) -> @owned S + %classinstanceSVoidToVoid = class_method %instance : $S, #S.classinstanceSVoidToVoid : (S) -> () async -> (), $@convention(method) @async (@guaranteed S) -> () + strong_retain %instance : $S + %result = apply %classinstanceSVoidToVoid(%instance) : $@convention(method) @async (@guaranteed S) -> () // CHECK: main.S + strong_release %instance : $S + + %2 = integer_literal $Builtin.Int32, 0 + %3 = struct $Int32 (%2 : $Builtin.Int32) + return %3 : $Int32 // id: %4 +} + + diff --git a/test/IRGen/async/run-call-existential-to-void.sil b/test/IRGen/async/run-call-existential-to-void.sil new file mode 100644 index 0000000000000..40bf799e83062 --- /dev/null +++ b/test/IRGen/async/run-call-existential-to-void.sil @@ -0,0 +1,79 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency +// UNSUPPORTED: use_os_stdlib + + +import Builtin +import Swift +import PrintShims + +sil public_external @printAny : $@convention(thin) (@in_guaranteed Any) -> () + +public protocol P { +} + +public struct S : P { + @_hasStorage let int: Int64 { get } + init(int: Int64) +} + + +sil_witness_table [serialized] S: P module main { +} + +sil hidden @S_init : $@convention(method) (Int64, @thin S.Type) -> S { +bb0(%int : $Int64, %S_type : $@thin S.Type): + %instance = struct $S (%int : $Int64) + return %instance : $S +} + +// CHECK-LL: define hidden swiftcc void @existentialToVoid(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]*}} { +sil hidden @existentialToVoid : $@async @convention(thin) (@in_guaranteed P) -> () { +bb0(%existential : $*P): + %existential_addr = open_existential_addr immutable_access %existential : $*P to $*@opened("B2796A9C-FEBE-11EA-84BB-D0817AD71B77") P + %any = alloc_stack $Any + %any_addr = init_existential_addr %any : $*Any, $@opened("B2796A9C-FEBE-11EA-84BB-D0817AD71B77") P + copy_addr %existential_addr to [initialization] %any_addr : $*@opened("B2796A9C-FEBE-11EA-84BB-D0817AD71B77") P + %printAny = function_ref @printAny : $@convention(thin) (@in_guaranteed Any) -> () + %result = apply %printAny(%any) : $@convention(thin) (@in_guaranteed Any) -> () + destroy_addr %any : $*Any + dealloc_stack %any : $*Any + return %result : $() +} + +sil hidden @call : $@async @convention(thin) () -> () { +bb0: + %S_type = metatype $@thin S.Type + %int_literal = integer_literal $Builtin.Int64, 7384783 + %int = struct $Int64 (%int_literal : $Builtin.Int64) + %S_init = function_ref @S_init : $@convention(method) (Int64, @thin S.Type) -> S + %instance = apply %S_init(%int, %S_type) : $@convention(method) (Int64, @thin S.Type) -> S + %existential = alloc_stack $P, let, name "existential" + %existential_addr = init_existential_addr %existential : $*P, $S + store %instance to %existential_addr : $*S + %existentialToVoid = function_ref @existentialToVoid : $@async @convention(thin) (@in_guaranteed P) -> () + %result = apply %existentialToVoid(%existential) : $@async @convention(thin) (@in_guaranteed P) -> () + destroy_addr %existential : $*P + dealloc_stack %existential : $*P + return %result : $() +} + +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): + %call = function_ref @call : $@async @convention(thin) () -> () // CHECK: 7384783 + %result = apply %call() : $@async @convention(thin) () -> () + + %2 = integer_literal $Builtin.Int32, 0 + %3 = struct $Int32 (%2 : $Builtin.Int32) + return %3 : $Int32 +} + diff --git a/test/IRGen/async/run-call-generic-to-generic.sil b/test/IRGen/async/run-call-generic-to-generic.sil new file mode 100644 index 0000000000000..ffc49621c918a --- /dev/null +++ b/test/IRGen/async/run-call-generic-to-generic.sil @@ -0,0 +1,55 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency +// UNSUPPORTED: use_os_stdlib + + +import Builtin +import Swift +import PrintShims + +sil public_external @printInt64 : $@convention(thin) (Int64) -> () + +// CHECK-LL: define hidden swiftcc void @genericToGeneric(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]*}} { +sil hidden @genericToGeneric : $@async @convention(thin) (@in_guaranteed T) -> @out T { +bb0(%out : $*T, %in : $*T): + copy_addr %in to [initialization] %out : $*T + %result = tuple () + return %result : $() +} + +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): + + %int_literal = integer_literal $Builtin.Int64, 42 + %int = struct $Int64 (%int_literal : $Builtin.Int64) + %int_addr = alloc_stack $Int64 + store %int to %int_addr : $*Int64 + %out_addr = alloc_stack $Int64 + + %genericToGeneric = function_ref @genericToGeneric : $@async @convention(thin) (@in_guaranteed T) -> @out T + %result1 = apply %genericToGeneric(%out_addr, %int_addr) : $@async @convention(thin) (@in_guaranteed T) -> @out T + + %print_int = function_ref @printInt64 : $@convention(thin) (Int64) -> () + %out = load %out_addr : $*Int64 + %result2 = apply %print_int(%out) : $@convention(thin) (Int64) -> () // CHECK: 42 + + dealloc_stack %out_addr : $*Int64 + dealloc_stack %int_addr : $*Int64 + + %2 = integer_literal $Builtin.Int32, 0 + %3 = struct $Int32 (%2 : $Builtin.Int32) + return %3 : $Int32 // id: %4 +} + + + + diff --git a/test/IRGen/async/run-call-generic-to-void.sil b/test/IRGen/async/run-call-generic-to-void.sil new file mode 100644 index 0000000000000..85ed24436309a --- /dev/null +++ b/test/IRGen/async/run-call-generic-to-void.sil @@ -0,0 +1,46 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency +// UNSUPPORTED: use_os_stdlib + + +import Builtin +import Swift +import PrintShims + +sil public_external @printGeneric : $@convention(thin) (@in_guaranteed T) -> () + +// CHECK-LL: define hidden swiftcc void @genericToVoid(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]*}} { +sil hidden @genericToVoid : $@async @convention(thin) (@in_guaranteed T) -> () { +bb0(%instance : $*T): + %print_generic = function_ref @printGeneric : $@convention(thin) (@in_guaranteed T) -> () + %result = apply %print_generic(%instance) : $@convention(thin) (@in_guaranteed T) -> () // CHECK: 922337203685477580 + return %result : $() +} + +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): + + %int_literal = integer_literal $Builtin.Int64, 922337203685477580 + %int = struct $Int64 (%int_literal : $Builtin.Int64) + %int_addr = alloc_stack $Int64 + store %int to %int_addr : $*Int64 + %genericToVoid = function_ref @genericToVoid : $@async @convention(thin) (@in_guaranteed T) -> () + %result = apply %genericToVoid(%int_addr) : $@async @convention(thin) (@in_guaranteed T) -> () + dealloc_stack %int_addr : $*Int64 + + %2 = integer_literal $Builtin.Int32, 0 + %3 = struct $Int32 (%2 : $Builtin.Int32) + return %3 : $Int32 // id: %4 +} + + + diff --git a/test/IRGen/async/run-call-genericEquatable-x2-to-bool.sil b/test/IRGen/async/run-call-genericEquatable-x2-to-bool.sil new file mode 100644 index 0000000000000..592c62a5d9c7e --- /dev/null +++ b/test/IRGen/async/run-call-genericEquatable-x2-to-bool.sil @@ -0,0 +1,60 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency +// UNSUPPORTED: use_os_stdlib + + +import Builtin +import Swift +import PrintShims + +sil public_external @printBool : $@convention(thin) (Bool) -> () + +// CHECK-LL: define hidden swiftcc void @genericEquatableAndGenericEquatableToBool(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]*}} { +sil hidden @genericEquatableAndGenericEquatableToBool : $@async @convention(thin) (@in_guaranteed T, @in_guaranteed T) -> Bool { +bb0(%0 : $*T, %1 : $*T): + %4 = metatype $@thick T.Type + %5 = witness_method $T, #Equatable."==" : (Self.Type) -> (Self, Self) -> Bool : $@convention(witness_method: Equatable) <τ_0_0 where τ_0_0 : Equatable> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0, @thick τ_0_0.Type) -> Bool + %6 = apply %5(%0, %1, %4) : $@convention(witness_method: Equatable) <τ_0_0 where τ_0_0 : Equatable> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0, @thick τ_0_0.Type) -> Bool + return %6 : $Bool +} + +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): + + %int1_literal = integer_literal $Builtin.Int64, 42 + %int1 = struct $Int64 (%int1_literal : $Builtin.Int64) + %int1_addr = alloc_stack $Int64 + store %int1 to %int1_addr : $*Int64 + + %int2_literal = integer_literal $Builtin.Int64, 99 + %int2 = struct $Int64 (%int2_literal : $Builtin.Int64) + %int2_addr = alloc_stack $Int64 + store %int2 to %int2_addr : $*Int64 + + %genericEquatableAndGenericEquatableToBool = function_ref @genericEquatableAndGenericEquatableToBool : $@async @convention(thin) (@in_guaranteed T, @in_guaranteed T) -> Bool + %false = apply %genericEquatableAndGenericEquatableToBool(%int1_addr, %int2_addr) : $@async @convention(thin) (@in_guaranteed T, @in_guaranteed T) -> Bool + + %print_bool = function_ref @printBool : $@convention(thin) (Bool) -> () + %print_false = apply %print_bool(%false) : $@convention(thin) (Bool) -> () // CHECK: false + + %true = apply %genericEquatableAndGenericEquatableToBool(%int1_addr, %int1_addr) : $@async @convention(thin) (@in_guaranteed T, @in_guaranteed T) -> Bool + + %print_true = apply %print_bool(%true) : $@convention(thin) (Bool) -> () // CHECK: true + + dealloc_stack %int2_addr : $*Int64 + dealloc_stack %int1_addr : $*Int64 + + %2 = integer_literal $Builtin.Int32, 0 + %3 = struct $Int32 (%2 : $Builtin.Int32) + return %3 : $Int32 // id: %4 +} + diff --git a/test/IRGen/async/run-call-int64-and-int64-to-void.sil b/test/IRGen/async/run-call-int64-and-int64-to-void.sil new file mode 100644 index 0000000000000..352db5433c8f2 --- /dev/null +++ b/test/IRGen/async/run-call-int64-and-int64-to-void.sil @@ -0,0 +1,45 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency +// UNSUPPORTED: use_os_stdlib + + +import Builtin +import Swift +import PrintShims + +sil public_external @printInt64 : $@convention(thin) (Int64) -> () + +// CHECK-LL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @int64AndInt64ToVoid(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]*}} { +sil @int64AndInt64ToVoid : $@async @convention(thin) (Int64, Int64) -> () { +entry(%int1: $Int64, %int2: $Int64): + %print = function_ref @printInt64 : $@convention(thin) (Int64) -> () + %result1 = apply %print(%int1) : $@convention(thin) (Int64) -> () // CHECK: 42 + %result2 = apply %print(%int2) : $@convention(thin) (Int64) -> () // CHECK: 13 + return %result2 : $() +} + +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): + + %int_literal1 = integer_literal $Builtin.Int64, 42 + %int1 = struct $Int64 (%int_literal1 : $Builtin.Int64) + %int_literal2 = integer_literal $Builtin.Int64, 13 + %int2 = struct $Int64 (%int_literal2 : $Builtin.Int64) + + %int64AndInt64ToVoid = function_ref @int64AndInt64ToVoid : $@async @convention(thin) (Int64, Int64) -> () + %result = apply %int64AndInt64ToVoid(%int1, %int2) : $@async @convention(thin) (Int64, Int64) -> () + + %2 = integer_literal $Builtin.Int32, 0 + %3 = struct $Int32 (%2 : $Builtin.Int32) + return %3 : $Int32 // id: %4 +} + diff --git a/test/IRGen/async/run-call-int64-to-void.sil b/test/IRGen/async/run-call-int64-to-void.sil new file mode 100644 index 0000000000000..4e6a63ec4116c --- /dev/null +++ b/test/IRGen/async/run-call-int64-to-void.sil @@ -0,0 +1,40 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency +// UNSUPPORTED: use_os_stdlib + + +import Builtin +import Swift +import PrintShims + +sil public_external @printInt64 : $@convention(thin) (Int64) -> () + +// CHECK-LL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @int64ToVoid(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]*}} { +sil @int64ToVoid : $@async @convention(thin) (Int64) -> () { +entry(%int: $Int64): + %print = function_ref @printInt64 : $@convention(thin) (Int64) -> () + %result = apply %print(%int) : $@convention(thin) (Int64) -> () // CHECK: 42 + return %result : $() +} + +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): + + %int_literal = integer_literal $Builtin.Int64, 42 + %int = struct $Int64 (%int_literal : $Builtin.Int64) + %int64ToVoid = function_ref @int64ToVoid : $@async @convention(thin) (Int64) -> () + %result = apply %int64ToVoid(%int) : $@async @convention(thin) (Int64) -> () + + %2 = integer_literal $Builtin.Int32, 0 + %3 = struct $Int32 (%2 : $Builtin.Int32) + return %3 : $Int32 // id: %4 +} diff --git a/test/IRGen/async/run-call-protocolextension_instance-void-to-int64.sil b/test/IRGen/async/run-call-protocolextension_instance-void-to-int64.sil new file mode 100644 index 0000000000000..b903913bc57cc --- /dev/null +++ b/test/IRGen/async/run-call-protocolextension_instance-void-to-int64.sil @@ -0,0 +1,83 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency +// UNSUPPORTED: use_os_stdlib + +import Builtin +import Swift +import PrintShims + +sil public_external @printGeneric : $@convention(thin) (@in_guaranteed T) -> () +sil public_external @printInt64 : $@convention(thin) (Int64) -> () + +protocol P { + func printMe() -> Int64 +} + +extension P { + func callPrintMe() async -> Int64 +} + +struct I : P { + @_hasStorage let int: Int64 { get } + func printMe() -> Int64 + init(int: Int64) +} + +// CHECK-LL: define hidden swiftcc void @callPrintMe(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]*}} { +sil hidden @callPrintMe : $@async @convention(method) (@in_guaranteed Self) -> Int64 { +bb0(%self : $*Self): + %P_printMe = witness_method $Self, #P.printMe : (Self) -> () -> Int64 : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int64 + %result = apply %P_printMe(%self) : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int64 + return %result : $Int64 +} + +sil hidden @I_printMe : $@convention(method) (I) -> Int64 { +bb0(%self : $I): + %self_addr = alloc_stack $I + store %self to %self_addr : $*I + %printGeneric = function_ref @printGeneric : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + %printGeneric_result = apply %printGeneric(%self_addr) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + dealloc_stack %self_addr : $*I + %result = struct_extract %self : $I, #I.int + return %result : $Int64 +} + +sil private [transparent] [thunk] @I_P_printMe : $@convention(witness_method: P) (@in_guaranteed I) -> Int64 { +bb0(%self_addr : $*I): + %self = load %self_addr : $*I + %I_printMe = function_ref @I_printMe : $@convention(method) (I) -> Int64 + %result = apply %I_printMe(%self) : $@convention(method) (I) -> Int64 + return %result : $Int64 +} + +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): + %i_type = metatype $@thin I.Type + %i_int_literal = integer_literal $Builtin.Int64, 99 + %i_int = struct $Int64 (%i_int_literal : $Builtin.Int64) + %i = struct $I (%i_int : $Int64) + %i_addr = alloc_stack $I + store %i to %i_addr : $*I + %callPrintMe = function_ref @callPrintMe : $@async @convention(method) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int64 + %result = apply %callPrintMe(%i_addr) : $@async @convention(method) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int64 // CHECK: I(int: 99) + dealloc_stack %i_addr : $*I + %printInt64 = function_ref @printInt64 : $@convention(thin) (Int64) -> () + %printInt64_result = apply %printInt64(%result) : $@convention(thin) (Int64) -> () // CHECK: 99 + + %out_literal = integer_literal $Builtin.Int32, 0 + %out = struct $Int32 (%out_literal : $Builtin.Int32) + return %out : $Int32 +} + +sil_witness_table hidden I: P module main { + method #P.printMe: (Self) -> () -> Int64 : @I_P_printMe +} diff --git a/test/IRGen/async/run-call-protocolwitness_instance-void-to-int64.sil b/test/IRGen/async/run-call-protocolwitness_instance-void-to-int64.sil new file mode 100644 index 0000000000000..be2437cdcd696 --- /dev/null +++ b/test/IRGen/async/run-call-protocolwitness_instance-void-to-int64.sil @@ -0,0 +1,74 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency +// UNSUPPORTED: use_os_stdlib + +import Builtin +import Swift +import PrintShims + +sil public_external @printGeneric : $@convention(thin) (@in_guaranteed T) -> () +sil public_external @printInt64 : $@convention(thin) (Int64) -> () + +protocol P { + func printMe() async -> Int64 +} + +struct I : P { + @_hasStorage let int: Int64 { get } + func printMe() async -> Int64 + init(int: Int64) +} + +// CHECK-LL-LABEL: define hidden swiftcc void @I_printMe(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]*}} { +sil hidden @I_printMe : $@async @convention(method) (I) -> Int64 { +bb0(%self : $I): + %self_addr = alloc_stack $I + store %self to %self_addr : $*I + %printGeneric = function_ref @printGeneric : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + %printGeneric_result = apply %printGeneric(%self_addr) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + dealloc_stack %self_addr : $*I + %result = struct_extract %self : $I, #I.int + return %result : $Int64 +} + +// CHECK-LL-LABEL: define internal swiftcc void @I_P_printMe(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]*}} { +sil private [transparent] [thunk] @I_P_printMe : $@async @convention(witness_method: P) (@in_guaranteed I) -> Int64 { +bb0(%self_addr : $*I): + %self = load %self_addr : $*I + %I_printMe = function_ref @I_printMe : $@async @convention(method) (I) -> Int64 + %result = apply %I_printMe(%self) : $@async @convention(method) (I) -> Int64 + return %result : $Int64 +} + +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): + %i_type = metatype $@thin I.Type + %i_int_literal = integer_literal $Builtin.Int64, 99 + %i_int = struct $Int64 (%i_int_literal : $Builtin.Int64) + %i = struct $I (%i_int : $Int64) + %i_addr = alloc_stack $I + store %i to %i_addr : $*I + %P_printMe = witness_method $I, #P.printMe : (Self) -> () async -> Int64 : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int64 + %result = apply %P_printMe(%i_addr) : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int64 // CHECK: I(int: 99) + dealloc_stack %i_addr : $*I + %printInt64 = function_ref @printInt64 : $@convention(thin) (Int64) -> () + %printInt64_result = apply %printInt64(%result) : $@convention(thin) (Int64) -> () // CHECK: 99 + + %out_literal = integer_literal $Builtin.Int32, 0 + %out = struct $Int32 (%out_literal : $Builtin.Int32) + return %out : $Int32 +} + +sil_witness_table hidden I: P module main { + method #P.printMe: (Self) -> () async -> Int64 : @I_P_printMe +} + diff --git a/test/IRGen/async/run-call-structinstance-int64-to-void.sil b/test/IRGen/async/run-call-structinstance-int64-to-void.sil new file mode 100644 index 0000000000000..a62d21b808464 --- /dev/null +++ b/test/IRGen/async/run-call-structinstance-int64-to-void.sil @@ -0,0 +1,66 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency +// UNSUPPORTED: use_os_stdlib + +import Builtin +import Swift +import PrintShims + +sil public_external @printInt64 : $@convention(thin) (Int64) -> () +sil public_external @printGeneric : $@convention(thin) (@in_guaranteed T) -> () + +struct S { + @_hasStorage var storage: Int64 { get set } + func structinstanceSInt64ToVoid(_ int: Int64) + init(storage: Int64) +} + +// CHECK-LL: define hidden swiftcc void @structinstanceSInt64ToVoid(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]*}} { +sil hidden @structinstanceSInt64ToVoid : $@async @convention(method) (Int64, S) -> () { +bb0(%int : $Int64, %self : $S): + %takeSAndInt64 = function_ref @takeSAndInt64 : $@async @convention(thin) (S, Int64) -> () + %takeSAndInt64_result = apply %takeSAndInt64(%self, %int) : $@async @convention(thin) (S, Int64) -> () + %out = tuple () + return %out : $() +} + +sil hidden @takeSAndInt64 : $@async @convention(thin) (S, Int64) -> () { +bb0(%self : $S, %int : $Int64): + %s_addr = alloc_stack $S + store %self to %s_addr : $*S + %printGeneric = function_ref @printGeneric : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + %printGeneric_result = apply %printGeneric(%s_addr) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () // CHECK: S(storage: 987654321) + dealloc_stack %s_addr : $*S + %printInt64 = function_ref @printInt64 : $@convention(thin) (Int64) -> () + %printInt64_result = apply %printInt64(%int) : $@convention(thin) (Int64) -> () // CHECK: 123456789 + %out = tuple () + return %out : $() +} + +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%argc : $Int32, %argv : $UnsafeMutablePointer>>): + %s_type = metatype $@thin S.Type + %storage_literal = integer_literal $Builtin.Int64, 987654321 + %storage = struct $Int64 (%storage_literal : $Builtin.Int64) + %s = struct $S (%storage : $Int64) + %int_literal = integer_literal $Builtin.Int64, 123456789 + %int = struct $Int64 (%int_literal : $Builtin.Int64) + %structinstanceSInt64ToVoid = function_ref @structinstanceSInt64ToVoid : $@async @convention(method) (Int64, S) -> () + %result = apply %structinstanceSInt64ToVoid(%int, %s) : $@async @convention(method) (Int64, S) -> () + + %exitcode_literal = integer_literal $Builtin.Int32, 0 + %exitcode = struct $Int32 (%exitcode_literal : $Builtin.Int32) + return %exitcode : $Int32 +} + + + diff --git a/test/IRGen/async/run-call-void-throws-to-int-throwing.sil b/test/IRGen/async/run-call-void-throws-to-int-throwing.sil new file mode 100644 index 0000000000000..1657dd1c1cfce --- /dev/null +++ b/test/IRGen/async/run-call-void-throws-to-int-throwing.sil @@ -0,0 +1,127 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency +// UNSUPPORTED: use_os_stdlib + + +import Builtin +import Swift +import PrintShims + +sil public_external @printInt64 : $@convention(thin) (Int64) -> () + +sil @$ss5ErrorPsE5_codeSivg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> Int +sil @$ss5ErrorPsE9_userInfoyXlSgvg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned Optional +sil @$ss5ErrorPsE7_domainSSvg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned String + +struct E : Error { + @_hasStorage let code: Int64 { get } + init(code: Int64) +} + +sil hidden @E_init : $@convention(method) (Int64, @thin E.Type) -> E { +bb0(%int : $Int64, %E_type : $@thin E.Type): + %instance = struct $E (%int : $Int64) + return %instance : $E +} + +sil private [transparent] [thunk] @S_domain_getter : $@convention(witness_method: Error) (@in_guaranteed E) -> @owned String { +bb0(%0 : $*E): + %1 = function_ref @$ss5ErrorPsE7_domainSSvg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned String + %2 = apply %1(%0) : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned String + return %2 : $String +} + + +sil private [transparent] [thunk] @S_code_getter : $@convention(witness_method: Error) (@in_guaranteed E) -> Int { +bb0(%0 : $*E): + %1 = function_ref @$ss5ErrorPsE5_codeSivg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> Int + %2 = apply %1(%0) : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> Int + return %2 : $Int +} + +sil private [transparent] [thunk] @S_userinfo_getter : $@convention(witness_method: Error) (@in_guaranteed E) -> @owned Optional { +bb0(%0 : $*E): + %1 = function_ref @$ss5ErrorPsE9_userInfoyXlSgvg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned Optional + %2 = apply %1(%0) : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned Optional + return %2 : $Optional +} + +sil_witness_table hidden E: Error module main { + method #Error._domain!getter: (Self) -> () -> String : @S_domain_getter + method #Error._code!getter: (Self) -> () -> Int : @S_code_getter + method #Error._userInfo!getter: (Self) -> () -> AnyObject? : @S_userinfo_getter +} + + +sil hidden @call : $@async @convention(thin) () -> () { +bb0: + %voidThrowsToInt = function_ref @voidThrowsToInt : $@async @convention(thin) () -> (Int, @error Error) + try_apply %voidThrowsToInt() : $@async @convention(thin) () -> (Int, @error Error), normal bb1, error bb3 + +bb1(%result_int : $Int): + br bb2 + +bb2: + %out = tuple () + return %out : $() + +bb3(%result_error : $Error): + %error = alloc_stack $Error + strong_retain %result_error : $Error + store %result_error to %error : $*Error + %instance = alloc_stack $E + checked_cast_addr_br copy_on_success Error in %error : $*Error to E in %instance : $*E, bb4, bb5 + +bb4: + dealloc_stack %instance : $*E + destroy_addr %error : $*Error + dealloc_stack %error : $*Error + %int_addr = struct_element_addr %instance : $*E, #E.code + %int = load %int_addr : $*Int64 + %printInt64 = function_ref @printInt64 : $@convention(thin) (Int64) -> () + %result = apply %printInt64(%int) : $@convention(thin) (Int64) -> () + br bb2 + +bb5: + dealloc_stack %instance : $*E + destroy_addr %error : $*Error + dealloc_stack %error : $*Error + br bb2 +} + +// CHECK-LL: define hidden swiftcc void @voidThrowsToInt(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]*}} { +sil hidden @voidThrowsToInt : $@async @convention(thin) () -> (Int, @error Error) { +bb0: + %e_type = metatype $@thin E.Type + %int_literal = integer_literal $Builtin.Int64, 42 + %int = struct $Int64 (%int_literal : $Builtin.Int64) + %E_init = function_ref @E_init : $@convention(method) (Int64, @thin E.Type) -> E + %instance = apply %E_init(%int, %e_type) : $@convention(method) (Int64, @thin E.Type) -> E + %error = alloc_existential_box $Error, $E + %instance_addr = project_existential_box $E in %error : $Error + store %instance to %instance_addr : $*E + %result = builtin "willThrow"(%error : $Error) : $() + throw %error : $Error +} + +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): + %call = function_ref @call : $@async @convention(thin) () -> () // CHECK: 42 + %result = apply %call() : $@async @convention(thin) () -> () + + %2 = integer_literal $Builtin.Int32, 0 + %3 = struct $Int32 (%2 : $Builtin.Int32) + return %3 : $Int32 +} + + + diff --git a/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-nothrow_call-sync-throw.sil b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-nothrow_call-sync-throw.sil new file mode 100644 index 0000000000000..60324ddd90773 --- /dev/null +++ b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-nothrow_call-sync-throw.sil @@ -0,0 +1,153 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency +// UNSUPPORTED: use_os_stdlib + +import Builtin +import Swift +import PrintShims + +sil public_external @printInt64 : $@convention(thin) (Int64) -> () + +sil @$ss5ErrorPsE5_codeSivg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> Int +sil @$ss5ErrorPsE9_userInfoyXlSgvg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned Optional +sil @$ss5ErrorPsE7_domainSSvg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned String + +struct E : Error { + @_hasStorage let code: Int64 { get } + init(code: Int64) +} + +sil hidden @E_init : $@convention(method) (Int64, @thin E.Type) -> E { +bb0(%int : $Int64, %E_type : $@thin E.Type): + %instance = struct $E (%int : $Int64) + return %instance : $E +} + +sil private [transparent] [thunk] @S_domain_getter : $@convention(witness_method: Error) (@in_guaranteed E) -> @owned String { +bb0(%0 : $*E): + %1 = function_ref @$ss5ErrorPsE7_domainSSvg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned String + %2 = apply %1(%0) : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned String + return %2 : $String +} + + +sil private [transparent] [thunk] @S_code_getter : $@convention(witness_method: Error) (@in_guaranteed E) -> Int { +bb0(%0 : $*E): + %1 = function_ref @$ss5ErrorPsE5_codeSivg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> Int + %2 = apply %1(%0) : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> Int + return %2 : $Int +} + +sil private [transparent] [thunk] @S_userinfo_getter : $@convention(witness_method: Error) (@in_guaranteed E) -> @owned Optional { +bb0(%0 : $*E): + %1 = function_ref @$ss5ErrorPsE9_userInfoyXlSgvg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned Optional + %2 = apply %1(%0) : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned Optional + return %2 : $Optional +} + +sil_witness_table hidden E: Error module main { + method #Error._domain!getter: (Self) -> () -> String : @S_domain_getter + method #Error._code!getter: (Self) -> () -> Int : @S_code_getter + method #Error._userInfo!getter: (Self) -> () -> AnyObject? : @S_userinfo_getter +} + + +sil hidden @call : $@async @convention(thin) () -> () { +bb0: + %voidThrowsToInt = function_ref @voidThrowsToInt : $@async @convention(thin) () -> (Int64, @error Error) + try_apply %voidThrowsToInt() : $@async @convention(thin) () -> (Int64, @error Error), normal bb1, error bb3 + +bb1(%result_int : $Int64): + br bb2 + +bb2: + %out = tuple () + return %out : $() + +bb3(%result_error : $Error): + %error = alloc_stack $Error + strong_retain %result_error : $Error + store %result_error to %error : $*Error + %instance = alloc_stack $E + checked_cast_addr_br copy_on_success Error in %error : $*Error to E in %instance : $*E, bb4, bb5 + +bb4: + dealloc_stack %instance : $*E + destroy_addr %error : $*Error + dealloc_stack %error : $*Error + %int_addr = struct_element_addr %instance : $*E, #E.code + %Int64 = load %int_addr : $*Int64 + %printInt64 = function_ref @printInt64 : $@convention(thin) (Int64) -> () + %result = apply %printInt64(%Int64) : $@convention(thin) (Int64) -> () + br bb2 + +bb5: + dealloc_stack %instance : $*E + destroy_addr %error : $*Error + dealloc_stack %error : $*Error + br bb2 +} + +// CHECK-LL: define hidden swiftcc void @voidThrowsToInt(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]*}} { +sil hidden @voidThrowsToInt : $@async @convention(thin) () -> (Int64, @error Error) { +entry: + %asyncVoidThrowsToInt = function_ref @asyncVoidThrowsToInt : $@async @convention(thin) () -> (Int64, @error Error) + try_apply %asyncVoidThrowsToInt() : $@async @convention(thin) () -> (Int64, @error Error), normal success1, error error1 +success1(%out1 : $Int64): + %syncVoidThrowsToInt = function_ref @syncVoidThrowsToInt : $@async @convention(thin) () -> (Int64, @error Error) + try_apply %syncVoidThrowsToInt() : $@async @convention(thin) () -> (Int64, @error Error), normal success2, error error2 +success2(%out : $Int64): + return %out : $Int64 +error1(%error1 : $Error): + br error(%error1 : $Error) +error2(%error2 : $Error): + br error(%error2 : $Error) +error(%error : $Error): + throw %error : $Error +} + +sil hidden @syncVoidThrowsToInt : $@async @convention(thin) () -> (Int64, @error Error) { +bb0: + %e_type = metatype $@thin E.Type + %int_literal = integer_literal $Builtin.Int64, 42 + %Int64 = struct $Int64 (%int_literal : $Builtin.Int64) + %E_init = function_ref @E_init : $@convention(method) (Int64, @thin E.Type) -> E + %instance = apply %E_init(%Int64, %e_type) : $@convention(method) (Int64, @thin E.Type) -> E + %error = alloc_existential_box $Error, $E + %instance_addr = project_existential_box $E in %error : $Error + store %instance to %instance_addr : $*E + %result = builtin "willThrow"(%error : $Error) : $() + throw %error : $Error +} + +sil hidden @asyncVoidThrowsToInt : $@async @convention(thin) () -> (Int64, @error Error) { +bb0: + %int_literal = integer_literal $Builtin.Int64, 1 // user: %2 + %Int64 = struct $Int64 (%int_literal : $Builtin.Int64) // user: %3 + return %Int64 : $Int64 +} + +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): + %call = function_ref @call : $@async @convention(thin) () -> () // CHECK: 42 + %result = apply %call() : $@async @convention(thin) () -> () + + %2 = integer_literal $Builtin.Int32, 0 + %3 = struct $Int32 (%2 : $Builtin.Int32) + return %3 : $Int32 +} + + + + + + diff --git a/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-throw.sil b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-throw.sil new file mode 100644 index 0000000000000..1ea4aca685555 --- /dev/null +++ b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-throw.sil @@ -0,0 +1,139 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency +// UNSUPPORTED: use_os_stdlib + + +import Builtin +import Swift +import PrintShims + +sil public_external @printInt64 : $@convention(thin) (Int64) -> () + +sil @$ss5ErrorPsE5_codeSivg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> Int +sil @$ss5ErrorPsE9_userInfoyXlSgvg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned Optional +sil @$ss5ErrorPsE7_domainSSvg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned String + +struct E : Error { + @_hasStorage let code: Int64 { get } + init(code: Int64) +} + +sil hidden @E_init : $@convention(method) (Int64, @thin E.Type) -> E { +bb0(%int : $Int64, %E_type : $@thin E.Type): + %instance = struct $E (%int : $Int64) + return %instance : $E +} + +sil private [transparent] [thunk] @S_domain_getter : $@convention(witness_method: Error) (@in_guaranteed E) -> @owned String { +bb0(%0 : $*E): + %1 = function_ref @$ss5ErrorPsE7_domainSSvg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned String + %2 = apply %1(%0) : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned String + return %2 : $String +} + + +sil private [transparent] [thunk] @S_code_getter : $@convention(witness_method: Error) (@in_guaranteed E) -> Int { +bb0(%0 : $*E): + %1 = function_ref @$ss5ErrorPsE5_codeSivg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> Int + %2 = apply %1(%0) : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> Int + return %2 : $Int +} + +sil private [transparent] [thunk] @S_userinfo_getter : $@convention(witness_method: Error) (@in_guaranteed E) -> @owned Optional { +bb0(%0 : $*E): + %1 = function_ref @$ss5ErrorPsE9_userInfoyXlSgvg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned Optional + %2 = apply %1(%0) : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned Optional + return %2 : $Optional +} + +sil_witness_table hidden E: Error module main { + method #Error._domain!getter: (Self) -> () -> String : @S_domain_getter + method #Error._code!getter: (Self) -> () -> Int : @S_code_getter + method #Error._userInfo!getter: (Self) -> () -> AnyObject? : @S_userinfo_getter +} + + +sil hidden @call : $@async @convention(thin) () -> () { +bb0: + %voidThrowsToInt = function_ref @voidThrowsToInt : $@async @convention(thin) () -> (Int, @error Error) + try_apply %voidThrowsToInt() : $@async @convention(thin) () -> (Int, @error Error), normal bb1, error bb3 + +bb1(%result_int : $Int): + br bb2 + +bb2: + %out = tuple () + return %out : $() + +bb3(%result_error : $Error): + %error = alloc_stack $Error + strong_retain %result_error : $Error + store %result_error to %error : $*Error + %instance = alloc_stack $E + checked_cast_addr_br copy_on_success Error in %error : $*Error to E in %instance : $*E, bb4, bb5 + +bb4: + dealloc_stack %instance : $*E + destroy_addr %error : $*Error + dealloc_stack %error : $*Error + %int_addr = struct_element_addr %instance : $*E, #E.code + %int = load %int_addr : $*Int64 + %printInt64 = function_ref @printInt64 : $@convention(thin) (Int64) -> () + %result = apply %printInt64(%int) : $@convention(thin) (Int64) -> () + br bb2 + +bb5: + dealloc_stack %instance : $*E + destroy_addr %error : $*Error + dealloc_stack %error : $*Error + br bb2 +} + +// CHECK-LL: define hidden swiftcc void @voidThrowsToInt(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]*}} { +sil hidden @voidThrowsToInt : $@async @convention(thin) () -> (Int, @error Error) { +bb0: + %asyncVoidThrowsToInt = function_ref @asyncVoidThrowsToInt : $@async @convention(thin) () -> (Int, @error Error) + try_apply %asyncVoidThrowsToInt() : $@async @convention(thin) () -> (Int, @error Error), normal bb1, error bb2 +bb1(%out : $Int): + return %out : $Int +bb2(%error : $Error): + throw %error : $Error +} + +sil hidden @asyncVoidThrowsToInt : $@async @convention(thin) () -> (Int, @error Error) { +bb0: + %e_type = metatype $@thin E.Type + %int_literal = integer_literal $Builtin.Int64, 42 + %int = struct $Int64 (%int_literal : $Builtin.Int64) + %E_init = function_ref @E_init : $@convention(method) (Int64, @thin E.Type) -> E + %instance = apply %E_init(%int, %e_type) : $@convention(method) (Int64, @thin E.Type) -> E + %error = alloc_existential_box $Error, $E + %instance_addr = project_existential_box $E in %error : $Error + store %instance to %instance_addr : $*E + %result = builtin "willThrow"(%error : $Error) : $() + throw %error : $Error +} + +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): + %call = function_ref @call : $@async @convention(thin) () -> () // CHECK: 42 + %result = apply %call() : $@async @convention(thin) () -> () + + %2 = integer_literal $Builtin.Int32, 0 + %3 = struct $Int32 (%2 : $Builtin.Int32) + return %3 : $Int32 +} + + + + + diff --git a/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-nothrow_call-async-throw.sil b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-nothrow_call-async-throw.sil new file mode 100644 index 0000000000000..fb2242e9d35df --- /dev/null +++ b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-nothrow_call-async-throw.sil @@ -0,0 +1,154 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency +// UNSUPPORTED: use_os_stdlib + + +import Builtin +import Swift +import PrintShims + +sil public_external @printInt64 : $@convention(thin) (Int64) -> () + +sil @$ss5ErrorPsE5_codeSivg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> Int +sil @$ss5ErrorPsE9_userInfoyXlSgvg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned Optional +sil @$ss5ErrorPsE7_domainSSvg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned String + +struct E : Error { + @_hasStorage let code: Int64 { get } + init(code: Int64) +} + +sil hidden @E_init : $@convention(method) (Int64, @thin E.Type) -> E { +bb0(%int : $Int64, %E_type : $@thin E.Type): + %instance = struct $E (%int : $Int64) + return %instance : $E +} + +sil private [transparent] [thunk] @S_domain_getter : $@convention(witness_method: Error) (@in_guaranteed E) -> @owned String { +bb0(%0 : $*E): + %1 = function_ref @$ss5ErrorPsE7_domainSSvg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned String + %2 = apply %1(%0) : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned String + return %2 : $String +} + + +sil private [transparent] [thunk] @S_code_getter : $@convention(witness_method: Error) (@in_guaranteed E) -> Int { +bb0(%0 : $*E): + %1 = function_ref @$ss5ErrorPsE5_codeSivg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> Int + %2 = apply %1(%0) : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> Int + return %2 : $Int +} + +sil private [transparent] [thunk] @S_userinfo_getter : $@convention(witness_method: Error) (@in_guaranteed E) -> @owned Optional { +bb0(%0 : $*E): + %1 = function_ref @$ss5ErrorPsE9_userInfoyXlSgvg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned Optional + %2 = apply %1(%0) : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned Optional + return %2 : $Optional +} + +sil_witness_table hidden E: Error module main { + method #Error._domain!getter: (Self) -> () -> String : @S_domain_getter + method #Error._code!getter: (Self) -> () -> Int : @S_code_getter + method #Error._userInfo!getter: (Self) -> () -> AnyObject? : @S_userinfo_getter +} + + +sil hidden @call : $@async @convention(thin) () -> () { +bb0: + %voidThrowsToInt = function_ref @voidThrowsToInt : $@async @convention(thin) () -> (Int64, @error Error) + try_apply %voidThrowsToInt() : $@async @convention(thin) () -> (Int64, @error Error), normal bb1, error bb3 + +bb1(%result_int : $Int64): + br bb2 + +bb2: + %out = tuple () + return %out : $() + +bb3(%result_error : $Error): + %error = alloc_stack $Error + strong_retain %result_error : $Error + store %result_error to %error : $*Error + %instance = alloc_stack $E + checked_cast_addr_br copy_on_success Error in %error : $*Error to E in %instance : $*E, bb4, bb5 + +bb4: + dealloc_stack %instance : $*E + destroy_addr %error : $*Error + dealloc_stack %error : $*Error + %int_addr = struct_element_addr %instance : $*E, #E.code + %Int64 = load %int_addr : $*Int64 + %printInt64 = function_ref @printInt64 : $@convention(thin) (Int64) -> () + %result = apply %printInt64(%Int64) : $@convention(thin) (Int64) -> () + br bb2 + +bb5: + dealloc_stack %instance : $*E + destroy_addr %error : $*Error + dealloc_stack %error : $*Error + br bb2 +} + +// CHECK-LL: define hidden swiftcc void @voidThrowsToInt(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]*}} { +sil hidden @voidThrowsToInt : $@async @convention(thin) () -> (Int64, @error Error) { +entry: + %syncVoidThrowsToInt = function_ref @syncVoidThrowsToInt : $@async @convention(thin) () -> (Int64, @error Error) + try_apply %syncVoidThrowsToInt() : $@async @convention(thin) () -> (Int64, @error Error), normal success1, error error1 +success1(%out1 : $Int64): + %asyncVoidThrowsToInt = function_ref @asyncVoidThrowsToInt : $@async @convention(thin) () -> (Int64, @error Error) + try_apply %asyncVoidThrowsToInt() : $@async @convention(thin) () -> (Int64, @error Error), normal success2, error error2 +success2(%out : $Int64): + return %out : $Int64 +error1(%error1 : $Error): + br error(%error1 : $Error) +error2(%error2 : $Error): + br error(%error2 : $Error) +error(%error : $Error): + throw %error : $Error +} + +sil hidden @syncVoidThrowsToInt : $@async @convention(thin) () -> (Int64, @error Error) { +bb0: + %int_literal = integer_literal $Builtin.Int64, 1 // user: %2 + %Int64 = struct $Int64 (%int_literal : $Builtin.Int64) // user: %3 + return %Int64 : $Int64 +} + +sil hidden @asyncVoidThrowsToInt : $@async @convention(thin) () -> (Int64, @error Error) { +bb0: + %e_type = metatype $@thin E.Type + %int_literal = integer_literal $Builtin.Int64, 42 + %Int64 = struct $Int64 (%int_literal : $Builtin.Int64) + %E_init = function_ref @E_init : $@convention(method) (Int64, @thin E.Type) -> E + %instance = apply %E_init(%Int64, %e_type) : $@convention(method) (Int64, @thin E.Type) -> E + %error = alloc_existential_box $Error, $E + %instance_addr = project_existential_box $E in %error : $Error + store %instance to %instance_addr : $*E + %result = builtin "willThrow"(%error : $Error) : $() + throw %error : $Error +} + +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): + %call = function_ref @call : $@async @convention(thin) () -> () // CHECK: 42 + %result = apply %call() : $@async @convention(thin) () -> () + + %2 = integer_literal $Builtin.Int32, 0 + %3 = struct $Int32 (%2 : $Builtin.Int32) + return %3 : $Int32 +} + + + + + + diff --git a/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-throw.sil b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-throw.sil new file mode 100644 index 0000000000000..1fc43cc2aad22 --- /dev/null +++ b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-throw.sil @@ -0,0 +1,138 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency +// UNSUPPORTED: use_os_stdlib + + +import Builtin +import Swift +import PrintShims + +sil public_external @printInt64 : $@convention(thin) (Int64) -> () + +sil @$ss5ErrorPsE5_codeSivg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> Int +sil @$ss5ErrorPsE9_userInfoyXlSgvg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned Optional +sil @$ss5ErrorPsE7_domainSSvg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned String + +struct E : Error { + @_hasStorage let code: Int64 { get } + init(code: Int64) +} + +sil hidden @E_init : $@convention(method) (Int64, @thin E.Type) -> E { +bb0(%int : $Int64, %E_type : $@thin E.Type): + %instance = struct $E (%int : $Int64) + return %instance : $E +} + +sil private [transparent] [thunk] @S_domain_getter : $@convention(witness_method: Error) (@in_guaranteed E) -> @owned String { +bb0(%0 : $*E): + %1 = function_ref @$ss5ErrorPsE7_domainSSvg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned String + %2 = apply %1(%0) : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned String + return %2 : $String +} + + +sil private [transparent] [thunk] @S_code_getter : $@convention(witness_method: Error) (@in_guaranteed E) -> Int { +bb0(%0 : $*E): + %1 = function_ref @$ss5ErrorPsE5_codeSivg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> Int + %2 = apply %1(%0) : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> Int + return %2 : $Int +} + +sil private [transparent] [thunk] @S_userinfo_getter : $@convention(witness_method: Error) (@in_guaranteed E) -> @owned Optional { +bb0(%0 : $*E): + %1 = function_ref @$ss5ErrorPsE9_userInfoyXlSgvg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned Optional + %2 = apply %1(%0) : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned Optional + return %2 : $Optional +} + +sil_witness_table hidden E: Error module main { + method #Error._domain!getter: (Self) -> () -> String : @S_domain_getter + method #Error._code!getter: (Self) -> () -> Int : @S_code_getter + method #Error._userInfo!getter: (Self) -> () -> AnyObject? : @S_userinfo_getter +} + + +sil hidden @call : $@async @convention(thin) () -> () { +bb0: + %voidThrowsToInt = function_ref @voidThrowsToInt : $@async @convention(thin) () -> (Int, @error Error) + try_apply %voidThrowsToInt() : $@async @convention(thin) () -> (Int, @error Error), normal bb1, error bb3 + +bb1(%result_int : $Int): + br bb2 + +bb2: + %out = tuple () + return %out : $() + +bb3(%result_error : $Error): + %error = alloc_stack $Error + strong_retain %result_error : $Error + store %result_error to %error : $*Error + %instance = alloc_stack $E + checked_cast_addr_br copy_on_success Error in %error : $*Error to E in %instance : $*E, bb4, bb5 + +bb4: + dealloc_stack %instance : $*E + destroy_addr %error : $*Error + dealloc_stack %error : $*Error + %int_addr = struct_element_addr %instance : $*E, #E.code + %int = load %int_addr : $*Int64 + %printInt64 = function_ref @printInt64 : $@convention(thin) (Int64) -> () + %result = apply %printInt64(%int) : $@convention(thin) (Int64) -> () + br bb2 + +bb5: + dealloc_stack %instance : $*E + destroy_addr %error : $*Error + dealloc_stack %error : $*Error + br bb2 +} + +// CHECK-LL: define hidden swiftcc void @voidThrowsToInt(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]*}} { +sil hidden @voidThrowsToInt : $@async @convention(thin) () -> (Int, @error Error) { +bb0: + %syncVoidThrowsToInt = function_ref @syncVoidThrowsToInt : $@async @convention(thin) () -> (Int, @error Error) + try_apply %syncVoidThrowsToInt() : $@async @convention(thin) () -> (Int, @error Error), normal bb1, error bb2 +bb1(%out : $Int): + return %out : $Int +bb2(%error : $Error): + throw %error : $Error +} + +sil hidden @syncVoidThrowsToInt : $@async @convention(thin) () -> (Int, @error Error) { +bb0: + %e_type = metatype $@thin E.Type + %int_literal = integer_literal $Builtin.Int64, 42 + %int = struct $Int64 (%int_literal : $Builtin.Int64) + %E_init = function_ref @E_init : $@convention(method) (Int64, @thin E.Type) -> E + %instance = apply %E_init(%int, %e_type) : $@convention(method) (Int64, @thin E.Type) -> E + %error = alloc_existential_box $Error, $E + %instance_addr = project_existential_box $E in %error : $Error + store %instance to %instance_addr : $*E + %result = builtin "willThrow"(%error : $Error) : $() + throw %error : $Error +} + +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): + %call = function_ref @call : $@async @convention(thin) () -> () // CHECK: 42 + %result = apply %call() : $@async @convention(thin) () -> () + + %2 = integer_literal $Builtin.Int32, 0 + %3 = struct $Int32 (%2 : $Builtin.Int32) + return %3 : $Int32 +} + + + + diff --git a/test/IRGen/async/run-call-void-to-existential.sil b/test/IRGen/async/run-call-void-to-existential.sil new file mode 100644 index 0000000000000..6e9cf7074820a --- /dev/null +++ b/test/IRGen/async/run-call-void-to-existential.sil @@ -0,0 +1,84 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency +// UNSUPPORTED: use_os_stdlib + + +import Builtin +import Swift +import PrintShims + +sil public_external @printAny : $@convention(thin) (@in_guaranteed Any) -> () + +public protocol P { +} + +public struct S : P { + @_hasStorage let int: Int64 { get } + init(int: Int64) +} + +sil_witness_table [serialized] S: P module main { +} + +sil hidden @S_init : $@convention(method) (Int64, @thin S.Type) -> S { +bb0(%int : $Int64, %S_type : $@thin S.Type): + %instance = struct $S (%int : $Int64) + return %instance : $S +} + +// CHECK-LL: define hidden swiftcc void @voidToExistential(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]*}} { +sil hidden @voidToExistential : $@async @convention(thin) () -> @out P { +bb0(%out : $*P): + %S_type = metatype $@thin S.Type + %int_literal = integer_literal $Builtin.Int64, 42 + %int = struct $Int64 (%int_literal : $Builtin.Int64) + %S_init = function_ref @S_init : $@convention(method) (Int64, @thin S.Type) -> S + %instance = apply %S_init(%int, %S_type) : $@convention(method) (Int64, @thin S.Type) -> S + %existential = alloc_stack $P + %existential_addr = init_existential_addr %existential : $*P, $S + store %instance to %existential_addr : $*S + copy_addr %existential to [initialization] %out : $*P + destroy_addr %existential : $*P + dealloc_stack %existential : $*P + %result = tuple () + return %result : $() +} + +sil hidden @call : $@async @convention(thin) () -> () { +bb0: + %existential = alloc_stack $P + %voidToExistential = function_ref @voidToExistential : $@async @convention(thin) () -> @out P + %voidToExistential_result = apply %voidToExistential(%existential) : $@async @convention(thin) () -> @out P + %existential_addr = open_existential_addr immutable_access %existential : $*P to $*@opened("1819CC6E-FEC6-11EA-876D-D0817AD71B77") P + %any = alloc_stack $Any + %any_addr = init_existential_addr %any : $*Any, $@opened("1819CC6E-FEC6-11EA-876D-D0817AD71B77") P + copy_addr %existential_addr to [initialization] %any_addr : $*@opened("1819CC6E-FEC6-11EA-876D-D0817AD71B77") P + %printAny = function_ref @printAny : $@convention(thin) (@in_guaranteed Any) -> () + %result = apply %printAny(%any) : $@convention(thin) (@in_guaranteed Any) -> () + destroy_addr %any : $*Any + dealloc_stack %any : $*Any + destroy_addr %existential : $*P + dealloc_stack %existential : $*P + return %result : $() +} + +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): + %call = function_ref @call : $@async @convention(thin) () -> () + %result = apply %call() : $@async @convention(thin) () -> () // CHECK: S(int: 42) + + %2 = integer_literal $Builtin.Int32, 0 + %3 = struct $Int32 (%2 : $Builtin.Int32) + return %3 : $Int32 +} + + diff --git a/test/IRGen/async/run-call-void-to-int64-and-int64.sil b/test/IRGen/async/run-call-void-to-int64-and-int64.sil new file mode 100644 index 0000000000000..2f7694b8760d2 --- /dev/null +++ b/test/IRGen/async/run-call-void-to-int64-and-int64.sil @@ -0,0 +1,48 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency +// UNSUPPORTED: use_os_stdlib + + +import Builtin +import Swift +import PrintShims + +sil public_external @printInt64 : $@convention(thin) (Int64) -> () + +// CHECK-LL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @voidToInt64AndInt64(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]*}} { +sil @voidToInt64AndInt64 : $@async @convention(thin) () -> (Int64, Int64) { + %int_literal1 = integer_literal $Builtin.Int64, 42 + %int1 = struct $Int64 (%int_literal1 : $Builtin.Int64) + %int_literal2 = integer_literal $Builtin.Int64, 13 + %int2 = struct $Int64 (%int_literal2 : $Builtin.Int64) + %tuple = tuple (%int1 : $Int64, %int2 : $Int64) + return %tuple : $(Int64, Int64) +} + +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): + + %voidToInt64AndInt64 = function_ref @voidToInt64AndInt64 : $@async @convention(thin) () -> (Int64, Int64) + %tuple = apply %voidToInt64AndInt64() : $@async @convention(thin) () -> (Int64, Int64) + %int1 = tuple_extract %tuple : $(Int64, Int64), 0 + %int2 = tuple_extract %tuple : $(Int64, Int64), 1 + + %print = function_ref @printInt64 : $@convention(thin) (Int64) -> () + %result1 = apply %print(%int1) : $@convention(thin) (Int64) -> () // CHECK: 42 + %result2 = apply %print(%int2) : $@convention(thin) (Int64) -> () // CHECK: 13 + + %2 = integer_literal $Builtin.Int32, 0 + %3 = struct $Int32 (%2 : $Builtin.Int32) + return %3 : $Int32 // id: %4 +} + + diff --git a/test/IRGen/async/run-call-void-to-int64.sil b/test/IRGen/async/run-call-void-to-int64.sil new file mode 100644 index 0000000000000..4e1f47918cf76 --- /dev/null +++ b/test/IRGen/async/run-call-void-to-int64.sil @@ -0,0 +1,41 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency +// UNSUPPORTED: use_os_stdlib + + +import Builtin +import Swift +import PrintShims + +sil public_external @printInt64 : $@convention(thin) (Int64) -> () + +// CHECK-LL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @voidToInt64(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]*}} { +sil @voidToInt64 : $@async @convention(thin) () -> (Int64) { + %int_literal = integer_literal $Builtin.Int64, 42 + %int = struct $Int64 (%int_literal : $Builtin.Int64) + return %int : $Int64 +} + +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): + + %voidToInt64 = function_ref @voidToInt64 : $@async @convention(thin) () -> Int64 + %int = apply %voidToInt64() : $@async @convention(thin) () -> Int64 + + %print = function_ref @printInt64 : $@convention(thin) (Int64) -> () + %result = apply %print(%int) : $@convention(thin) (Int64) -> () // CHECK: 42 + + %2 = integer_literal $Builtin.Int32, 0 + %3 = struct $Int32 (%2 : $Builtin.Int32) + return %3 : $Int32 // id: %4 +} + diff --git a/test/IRGen/async/run-call-void-to-struct_large.sil b/test/IRGen/async/run-call-void-to-struct_large.sil new file mode 100644 index 0000000000000..c57212b656144 --- /dev/null +++ b/test/IRGen/async/run-call-void-to-struct_large.sil @@ -0,0 +1,133 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency +// UNSUPPORTED: use_os_stdlib + +import Builtin +import Swift +import PrintShims + +sil public_external @printGeneric : $@convention(thin) (@in_guaranteed T) -> () + +struct Big { + @_hasStorage let i1: Int64 { get } + @_hasStorage let i2: Int64 { get } + @_hasStorage let i3: Int64 { get } + @_hasStorage let i4: Int64 { get } + @_hasStorage let i5: Int64 { get } + @_hasStorage let i6: Int64 { get } + @_hasStorage let i7: Int64 { get } + @_hasStorage let i8: Int64 { get } + @_hasStorage let i9: Int64 { get } + @_hasStorage let i0: Int64 { get } + init() +} + +sil hidden @Big_init : $@convention(method) (@thin Big.Type) -> Big { +// %0 "$metatype" +bb0(%0 : $@thin Big.Type): + %1 = alloc_stack $Big, var, name "self" + %2 = integer_literal $Builtin.Int64, 1 + %3 = struct $Int64 (%2 : $Builtin.Int64) + %4 = begin_access [modify] [static] %1 : $*Big + %5 = struct_element_addr %4 : $*Big, #Big.i1 + store %3 to %5 : $*Int64 + end_access %4 : $*Big + %8 = integer_literal $Builtin.Int64, 2 + %9 = struct $Int64 (%8 : $Builtin.Int64) + %10 = begin_access [modify] [static] %1 : $*Big + %11 = struct_element_addr %10 : $*Big, #Big.i2 + store %9 to %11 : $*Int64 + end_access %10 : $*Big + %14 = integer_literal $Builtin.Int64, 3 + %15 = struct $Int64 (%14 : $Builtin.Int64) + %16 = begin_access [modify] [static] %1 : $*Big + %17 = struct_element_addr %16 : $*Big, #Big.i3 + store %15 to %17 : $*Int64 + end_access %16 : $*Big + %20 = integer_literal $Builtin.Int64, 4 + %21 = struct $Int64 (%20 : $Builtin.Int64) + %22 = begin_access [modify] [static] %1 : $*Big + %23 = struct_element_addr %22 : $*Big, #Big.i4 + store %21 to %23 : $*Int64 + end_access %22 : $*Big + %26 = integer_literal $Builtin.Int64, 5 + %27 = struct $Int64 (%26 : $Builtin.Int64) + %28 = begin_access [modify] [static] %1 : $*Big + %29 = struct_element_addr %28 : $*Big, #Big.i5 + store %27 to %29 : $*Int64 + end_access %28 : $*Big + %32 = integer_literal $Builtin.Int64, 6 + %33 = struct $Int64 (%32 : $Builtin.Int64) + %34 = begin_access [modify] [static] %1 : $*Big + %35 = struct_element_addr %34 : $*Big, #Big.i6 + store %33 to %35 : $*Int64 + end_access %34 : $*Big + %38 = integer_literal $Builtin.Int64, 7 + %39 = struct $Int64 (%38 : $Builtin.Int64) + %40 = begin_access [modify] [static] %1 : $*Big + %41 = struct_element_addr %40 : $*Big, #Big.i7 + store %39 to %41 : $*Int64 + end_access %40 : $*Big + %44 = integer_literal $Builtin.Int64, 8 + %45 = struct $Int64 (%44 : $Builtin.Int64) + %46 = begin_access [modify] [static] %1 : $*Big + %47 = struct_element_addr %46 : $*Big, #Big.i8 + store %45 to %47 : $*Int64 + end_access %46 : $*Big + %50 = integer_literal $Builtin.Int64, 9 + %51 = struct $Int64 (%50 : $Builtin.Int64) + %52 = begin_access [modify] [static] %1 : $*Big + %53 = struct_element_addr %52 : $*Big, #Big.i9 + store %51 to %53 : $*Int64 + end_access %52 : $*Big + %56 = integer_literal $Builtin.Int64, 0 + %57 = struct $Int64 (%56 : $Builtin.Int64) + %58 = begin_access [modify] [static] %1 : $*Big + %59 = struct_element_addr %58 : $*Big, #Big.i0 + store %57 to %59 : $*Int64 + end_access %58 : $*Big + %62 = struct $Big (%3 : $Int64, %9 : $Int64, %15 : $Int64, %21 : $Int64, %27 : $Int64, %33 : $Int64, %39 : $Int64, %45 : $Int64, %51 : $Int64, %57 : $Int64) + dealloc_stack %1 : $*Big + return %62 : $Big +} + +// CHECK-LL: define hidden swiftcc void @getBig(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]*}} { +sil hidden @getBig : $@async @convention(thin) () -> Big { +bb0: + %0 = metatype $@thin Big.Type + // function_ref Big.init() + %1 = function_ref @Big_init : $@convention(method) (@thin Big.Type) -> Big + %2 = apply %1(%0) : $@convention(method) (@thin Big.Type) -> Big + return %2 : $Big +} + +sil hidden @printBig : $@async @convention(thin) () -> () { + %getBig = function_ref @getBig : $@async @convention(thin) () -> Big + %big = apply %getBig() : $@async @convention(thin) () -> Big + %big_addr = alloc_stack $Big + store %big to %big_addr : $*Big + %print = function_ref @printGeneric : $@convention(thin) (@in_guaranteed T) -> () + %result = apply %print(%big_addr) : $@convention(thin) (@in_guaranteed T) -> () + dealloc_stack %big_addr : $*Big + %out = tuple () + return %out : $() +} + +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): + %printBig = function_ref @printBig : $@async @convention(thin) () -> () + %result = apply %printBig() : $@async @convention(thin) () -> () // CHECK: Big(i1: 1, i2: 2, i3: 3, i4: 4, i5: 5, i6: 6, i7: 7, i8: 8, i9: 9, i0: 0) + + %2 = integer_literal $Builtin.Int32, 0 + %3 = struct $Int32 (%2 : $Builtin.Int32) + return %3 : $Int32 // id: %4 +} diff --git a/test/IRGen/async/run-call_generic-protocolwitness_instance-generic-to-int64-and-generic.sil b/test/IRGen/async/run-call_generic-protocolwitness_instance-generic-to-int64-and-generic.sil new file mode 100644 index 0000000000000..b26d4b8c95aa3 --- /dev/null +++ b/test/IRGen/async/run-call_generic-protocolwitness_instance-generic-to-int64-and-generic.sil @@ -0,0 +1,98 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency +// UNSUPPORTED: use_os_stdlib + +import Builtin +import Swift +import PrintShims + +sil public_external @printGeneric : $@convention(thin) (@in_guaranteed T) -> () +sil public_external @printInt64 : $@convention(thin) (Int64) -> () + +protocol P { + func printMe(_ t: T) async -> (Int64, T) +} + +extension P { + func callPrintMe(_ t: T) async -> (Int64, T) +} + +struct I : P { + @_hasStorage let int: Int64 { get } + func printMe(_ t: T) async -> (Int64, T) + init(int: Int64) +} + +sil hidden @I_printMe : $@convention(method) @async (@in_guaranteed T, I) -> (Int64, @out T) { +bb0(%out_addr : $*T, %in_addr : $*T, %self : $I): + %self_addr = alloc_stack $I + store %self to %self_addr : $*I + %printGeneric = function_ref @printGeneric : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + %printGeneric_result = apply %printGeneric(%self_addr) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + dealloc_stack %self_addr : $*I + %value = struct_extract %self : $I, #I.int + copy_addr %in_addr to [initialization] %out_addr : $*T + return %value : $Int64 +} + +// CHECK-LL: define internal swiftcc void @I_P_printMe(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]*}} { +sil private [transparent] [thunk] @I_P_printMe : $@convention(witness_method: P) @async <τ_0_0> (@in_guaranteed τ_0_0, @in_guaranteed I) -> (Int64, @out τ_0_0) { +bb0(%out_addr : $*τ_0_0, %in_addr : $*τ_0_0, %self_addr : $*I): + %self = load %self_addr : $*I + %I_printMe = function_ref @I_printMe : $@convention(method) @async <τ_0_0> (@in_guaranteed τ_0_0, I) -> (Int64, @out τ_0_0) + %result = apply %I_printMe<τ_0_0>(%out_addr, %in_addr, %self) : $@convention(method) @async <τ_0_0> (@in_guaranteed τ_0_0, I) -> (Int64, @out τ_0_0) + return %result : $Int64 +} + +sil hidden @callPrintMe : $@async @convention(thin) (@in_guaranteed T, @in_guaranteed U) -> (Int64, @out U) { +bb0(%out_addr : $*U, %self_addr : $*T, %in_addr : $*U): + %I_P_printMe = witness_method $T, #P.printMe : (Self) -> (T) async -> (Int64, T) : $@convention(witness_method: P) @async <τ_0_0 where τ_0_0 : P><τ_1_0> (@in_guaranteed τ_1_0, @in_guaranteed τ_0_0) -> (Int64, @out τ_1_0) + %result = apply %I_P_printMe(%out_addr, %in_addr, %self_addr) : $@convention(witness_method: P) @async <τ_0_0 where τ_0_0 : P><τ_1_0> (@in_guaranteed τ_1_0, @in_guaranteed τ_0_0) -> (Int64, @out τ_1_0) + return %result : $Int64 +} + +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%argc : $Int32, %argv : $UnsafeMutablePointer>>): + %I_type = metatype $@thin I.Type + %int_literal = integer_literal $Builtin.Int64, 99 + %int = struct $Int64 (%int_literal : $Builtin.Int64) + %i = struct $I (%int : $Int64) + %out_addr = alloc_stack $I + %in_addr = alloc_stack $I + store %i to %in_addr : $*I + %i_addr = alloc_stack $I + store %i to %i_addr : $*I + %callPrintMe = function_ref @callPrintMe : $@async @convention(thin) <τ_0_0, τ_0_1 where τ_0_0 : P> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1) -> (Int64, @out τ_0_1) + %result = apply %callPrintMe(%out_addr, %in_addr, %i_addr) : $@async @convention(thin) <τ_0_0, τ_0_1 where τ_0_0 : P> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1) -> (Int64, @out τ_0_1) + dealloc_stack %i_addr : $*I + dealloc_stack %in_addr : $*I + %out = load %out_addr : $*I + dealloc_stack %out_addr : $*I + %printInt64 = function_ref @printInt64 : $@convention(thin) (Int64) -> () + %printInt64_result = apply %printInt64(%result) : $@convention(thin) (Int64) -> () // CHECK: 99 + %out_addr_2 = alloc_stack $I + store %out to %out_addr_2 : $*I + %printGeneric = function_ref @printGeneric : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + %printGeneric_result = apply %printGeneric(%out_addr_2) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () // CHECK: I(int: 99) + dealloc_stack %out_addr_2 : $*I + + + + %returnCode_literal = integer_literal $Builtin.Int32, 0 + %returnCode = struct $Int32 (%returnCode_literal : $Builtin.Int32) + return %returnCode : $Int32 +} + + +sil_witness_table hidden I: P module main { + method #P.printMe: (Self) -> (T) async -> (Int64, T) : @I_P_printMe +} diff --git a/test/IRGen/async/run-call_generic-protocolwitness_instance-void-to-int64.sil b/test/IRGen/async/run-call_generic-protocolwitness_instance-void-to-int64.sil new file mode 100644 index 0000000000000..2b7789f44b79a --- /dev/null +++ b/test/IRGen/async/run-call_generic-protocolwitness_instance-void-to-int64.sil @@ -0,0 +1,81 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency +// UNSUPPORTED: use_os_stdlib + +import Builtin +import Swift +import PrintShims + +sil public_external @printGeneric : $@convention(thin) (@in_guaranteed T) -> () +sil public_external @printInt64 : $@convention(thin) (Int64) -> () + +protocol P { + func printMe() async -> Int64 +} + +struct I : P { + @_hasStorage let int: Int64 { get } + func printMe() async -> Int64 + init(int: Int64) +} + +// CHECK-LL-LABEL: define hidden swiftcc void @I_printMe(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]*}} { +sil hidden @I_printMe : $@async @convention(method) (I) -> Int64 { +bb0(%self : $I): + %self_addr = alloc_stack $I + store %self to %self_addr : $*I + %printGeneric = function_ref @printGeneric : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + %printGeneric_result = apply %printGeneric(%self_addr) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + dealloc_stack %self_addr : $*I + %result = struct_extract %self : $I, #I.int + return %result : $Int64 +} + +// CHECK-LL-LABEL: define internal swiftcc void @I_P_printMe(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]*}} { +sil private [transparent] [thunk] @I_P_printMe : $@async @convention(witness_method: P) (@in_guaranteed I) -> Int64 { +bb0(%self_addr : $*I): + %self = load %self_addr : $*I + %I_printMe = function_ref @I_printMe : $@async @convention(method) (I) -> Int64 + %result = apply %I_printMe(%self) : $@async @convention(method) (I) -> Int64 + return %result : $Int64 +} + +sil hidden @callPrintMe : $@async @convention(thin) (@in_guaranteed T) -> Int64 { +bb0(%t : $*T): + %I_P_printMe = witness_method $T, #P.printMe : (Self) -> () async -> Int64 : $@convention(witness_method: P) @async <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int64 + %result = apply %I_P_printMe(%t) : $@convention(witness_method: P) @async <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int64 + return %result : $Int64 +} + +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): + %i_type = metatype $@thin I.Type + %i_int_literal = integer_literal $Builtin.Int64, 99 + %i_int = struct $Int64 (%i_int_literal : $Builtin.Int64) + %i = struct $I (%i_int : $Int64) + %i_addr = alloc_stack $I + store %i to %i_addr : $*I + %callPrintMe = function_ref @callPrintMe : $@async @convention(thin) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int64 + %result = apply %callPrintMe(%i_addr) : $@async @convention(thin) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int64 // users: %13, %11 // CHECK: I(int: 99) + dealloc_stack %i_addr : $*I + %printInt64 = function_ref @printInt64 : $@convention(thin) (Int64) -> () + %printInt64_result = apply %printInt64(%result) : $@convention(thin) (Int64) -> () // CHECK: 99 + + %out_literal = integer_literal $Builtin.Int32, 0 + %out = struct $Int32 (%out_literal : $Builtin.Int32) + return %out : $Int32 +} + +sil_witness_table hidden I: P module main { + method #P.printMe: (Self) -> () async -> Int64 : @I_P_printMe +} + diff --git a/test/IRGen/async/run-partialapply-capture-class-to-void.sil b/test/IRGen/async/run-partialapply-capture-class-to-void.sil new file mode 100644 index 0000000000000..7a2a7f8122b03 --- /dev/null +++ b/test/IRGen/async/run-partialapply-capture-class-to-void.sil @@ -0,0 +1,93 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency +// UNSUPPORTED: use_os_stdlib + +import Builtin +import Swift +import PrintShims + +sil public_external @printGeneric : $@convention(thin) (@in_guaranteed T) -> () + +class S { + deinit + init() +} + +sil hidden [exact_self_class] @S_allocating_init : $@convention(method) (@thick S.Type) -> @owned S { +bb0(%0 : $@thick S.Type): + %1 = alloc_ref $S + %2 = function_ref @$S_init : $@convention(method) (@owned S) -> @owned S + %3 = apply %2(%1) : $@convention(method) (@owned S) -> @owned S + return %3 : $S +} + +sil hidden @$S_init : $@convention(method) (@owned S) -> @owned S { +bb0(%0 : $S): + return %0 : $S +} + +sil hidden @$S_deinit : $@convention(method) (@guaranteed S) -> @owned Builtin.NativeObject { +bb0(%0 : $S): + %2 = unchecked_ref_cast %0 : $S to $Builtin.NativeObject + return %2 : $Builtin.NativeObject +} + +sil hidden @S_deallocating_deinit : $@convention(method) (@owned S) -> () { +bb0(%0 : $S): + %2 = function_ref @$S_deinit : $@convention(method) (@guaranteed S) -> @owned Builtin.NativeObject + %3 = apply %2(%0) : $@convention(method) (@guaranteed S) -> @owned Builtin.NativeObject + %4 = unchecked_ref_cast %3 : $Builtin.NativeObject to $S + dealloc_ref %4 : $S + %6 = tuple () + return %6 : $() +} + +sil_vtable S { + #S.init!allocator: (S.Type) -> () -> S : @S_allocating_init + #S.deinit!deallocator: @S_deallocating_deinit +} + +// CHECK-LL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @classinstanceSToVoid(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]*}} { +sil @classinstanceSToVoid : $@async @convention(thin) (@owned S) -> () { +entry(%c : $S): + %class_addr = alloc_stack $S + store %c to %class_addr : $*S + %printGeneric = function_ref @printGeneric : $@convention(thin) (@in_guaranteed T) -> () + %result = apply %printGeneric(%class_addr) : $@convention(thin) (@in_guaranteed T) -> () // CHECK: main.S + dealloc_stack %class_addr : $*S + return %result : $() +} + +sil @partial_apply_class : $@async @convention(thin) (S) -> @async @callee_owned () -> () { +entry(%instance : $S): + %classinstanceSToVoid = function_ref @classinstanceSToVoid : $@async @convention(thin) (@owned S) -> () + %partiallyApplied = partial_apply %classinstanceSToVoid(%instance) : $@async @convention(thin) (@owned S) -> () + return %partiallyApplied : $@async @callee_owned () -> () +} + +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%argc : $Int32, %argv : $UnsafeMutablePointer>>): + %s_type = metatype $@thick S.Type + %allocating_init = function_ref @S_allocating_init : $@convention(method) (@thick S.Type) -> @owned S + %instance = apply %allocating_init(%s_type) : $@convention(method) (@thick S.Type) -> @owned S + strong_retain %instance : $S + + %partial_apply_class = function_ref @partial_apply_class : $@async @convention(thin) (S) -> @async @callee_owned () -> () + %partiallyApplied = apply %partial_apply_class(%instance) : $@async @convention(thin) (S) -> @async @callee_owned () -> () + %result = apply %partiallyApplied() : $@async @callee_owned () -> () + + strong_release %instance : $S + + %out_literal = integer_literal $Builtin.Int32, 0 + %out = struct $Int32 (%out_literal : $Builtin.Int32) + return %out : $Int32 +} diff --git a/test/IRGen/async/run-partialapply-capture-generic_conformer-and-generic-to-void.sil b/test/IRGen/async/run-partialapply-capture-generic_conformer-and-generic-to-void.sil new file mode 100644 index 0000000000000..77926d6a492e0 --- /dev/null +++ b/test/IRGen/async/run-partialapply-capture-generic_conformer-and-generic-to-void.sil @@ -0,0 +1,87 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency +// UNSUPPORTED: use_os_stdlib + +import Builtin +import Swift +import PrintShims + +sil public_external @printGeneric : $@convention(thin) (@in_guaranteed T) -> () +sil public_external @printInt64 : $@convention(thin) (Int64) -> () + +public protocol Observable { + associatedtype Result + func subscribe(o: T) async -> () +} +class ObservableImpl : Observable { + typealias Result = Void + func subscribe(o: T) async -> () +} +sil_vtable ObservableImpl { +} +// CHECK-LL: define hidden swiftcc void @subscribe_ObservableImpl_Observable(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]*}} { +sil hidden @subscribe_ObservableImpl_Observable : $@convention(witness_method: Observable) @async <τ_0_0 where τ_0_0 : Observer> (@in_guaranteed τ_0_0, @in_guaranteed ObservableImpl) -> () { +bb0(%observer : $*τ_0_0, %self : $*ObservableImpl): + %printGeneric = function_ref @printGeneric : $@convention(thin) (@in_guaranteed T) -> () + %printGeneric_result1 = apply %printGeneric<τ_0_0>(%observer) : $@convention(thin) (@in_guaranteed T) -> () // CHECK: main.ObserverImpl + %printGeneric_result2 = apply %printGeneric(%self) : $@convention(thin) (@in_guaranteed T) -> () // CHECK: main.ObservableImpl + %result = tuple () + return %result : $() +} +sil_witness_table ObservableImpl : Observable module main { + associated_type Result : () + method #Observable.subscribe: (Self) -> (T) async -> () : @subscribe_ObservableImpl_Observable +} + +public protocol Observer { + associatedtype Result +} + +class ObserverImpl : Observer { + typealias Result = Void +} +sil_vtable ObserverImpl {} +sil_witness_table ObserverImpl : Observer module main { + associated_type Result : () +} + +// CHECK-LL: define internal swiftcc void @"$sTA"(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]*}}) {{#[0-9]*}} { +sil hidden @witness_method : $@convention(thin) (@in S) -> @owned @async @callee_owned (@in O) -> () { +bb0(%0 : $*S): + %1 = witness_method $S, #Observable.subscribe : $@async @convention(witness_method: Observable) <τ_0_0 where τ_0_0 : Observable><τ_1_0 where τ_1_0 : Observer, τ_0_0.Result == τ_1_0.Result> (@in τ_1_0, @in_guaranteed τ_0_0) -> () + %2 = partial_apply %1(%0) : $@async @convention(witness_method: Observable) <τ_0_0 where τ_0_0 : Observable><τ_1_0 where τ_1_0 : Observer, τ_0_0.Result == τ_1_0.Result> (@in τ_1_0, @in_guaranteed τ_0_0) -> () + return %2 : $@async @callee_owned (@in O) -> () +} + + + +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%argc : $Int32, %argv : $UnsafeMutablePointer>>): + %observableImpl = alloc_ref $ObservableImpl + strong_retain %observableImpl : $ObservableImpl + %observableImpl_addr = alloc_stack $ObservableImpl + store %observableImpl to %observableImpl_addr : $*ObservableImpl + %witness_method = function_ref @witness_method : $@convention(thin) (@in S) -> @owned @async @callee_owned (@in O) -> () + %partiallyApplied = apply %witness_method(%observableImpl_addr) : $@convention(thin) (@in S) -> @owned @async @callee_owned (@in O) -> () + %observerImpl = alloc_ref $ObserverImpl + %observerImpl_addr = alloc_stack $ObserverImpl + store %observerImpl to %observerImpl_addr : $*ObserverImpl + + %result = apply %partiallyApplied(%observerImpl_addr) : $@async @callee_owned (@in ObserverImpl) -> () + + dealloc_stack %observerImpl_addr : $*ObserverImpl + dealloc_stack %observableImpl_addr : $*ObservableImpl + + %out_literal = integer_literal $Builtin.Int32, 0 + %out = struct $Int32 (%out_literal : $Builtin.Int32) + return %out : $Int32 +} diff --git a/test/IRGen/async/run-partialapply-capture-inout-generic-and-in-generic-to-generic.sil b/test/IRGen/async/run-partialapply-capture-inout-generic-and-in-generic-to-generic.sil new file mode 100644 index 0000000000000..b5d34e36438ea --- /dev/null +++ b/test/IRGen/async/run-partialapply-capture-inout-generic-and-in-generic-to-generic.sil @@ -0,0 +1,68 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency +// UNSUPPORTED: use_os_stdlib + +import Builtin +import Swift +import PrintShims + +sil public_external @printGeneric : $@convention(thin) (@in_guaranteed T) -> () +sil public_external @printInt64 : $@convention(thin) (Int64) -> () + +// CHECK-LL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @inGenericAndInoutGenericToGeneric(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]*}} { +// CHECK-LL: define internal swiftcc void @"$s017inGenericAndInoutb2ToB0TA"(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]*}}) {{#[0-9]*}} { +sil @inGenericAndInoutGenericToGeneric : $@async @convention(thin) (@in T, @inout T) -> @out T { +entry(%out : $*T, %in : $*T, %inout : $*T): + %printGeneric = function_ref @printGeneric : $@convention(thin) (@in_guaranteed T) -> () + %printGeneric_result1 = apply %printGeneric(%in) : $@convention(thin) (@in_guaranteed T) -> () // CHECK: 876 + %printGeneric_result2 = apply %printGeneric(%inout) : $@convention(thin) (@in_guaranteed T) -> () // CHECK: 678 + copy_addr %inout to [initialization] %out : $*T + %result = tuple () + return %result : $() +} + +sil @partial_apply_open_generic_capture : $@async @convention(thin) (@inout T) -> @async @callee_owned (@in T) -> @out T { +entry(%a : $*T): + %f = function_ref @inGenericAndInoutGenericToGeneric : $@async @convention(thin) (@in U, @inout U) -> @out U + %p = partial_apply %f(%a) : $@async @convention(thin) (@in U, @inout U) -> @out U + return %p : $@async @callee_owned (@in T) -> @out T +} + +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%argc : $Int32, %argv : $UnsafeMutablePointer>>): + %in_literal = integer_literal $Builtin.Int64, 876 + %in = struct $Int64 (%in_literal : $Builtin.Int64) + %inout_literal = integer_literal $Builtin.Int64, 678 + %inout = struct $Int64 (%inout_literal : $Builtin.Int64) + + %in_addr = alloc_stack $Int64 + store %in to %in_addr : $*Int64 + %inout_addr = alloc_stack $Int64 + store %inout to %inout_addr : $*Int64 + %result_addr = alloc_stack $Int64 + + %partial_apply_open_generic_capture = function_ref @partial_apply_open_generic_capture : $@async @convention(thin) (@inout T) -> @async @callee_owned (@in T) -> @out T + %partiallyApplied = apply %partial_apply_open_generic_capture(%inout_addr) : $@async @convention(thin) (@inout T) -> @async @callee_owned (@in T) -> @out T + %void = apply %partiallyApplied(%result_addr, %in_addr) : $@async @callee_owned (@in Int64) -> @out Int64 + + %result = load %result_addr : $*Int64 + %printInt64 = function_ref @printInt64 : $@convention(thin) (Int64) -> () + %printInt64_result = apply %printInt64(%result) : $@convention(thin) (Int64) -> () // CHECK: 678 + + dealloc_stack %result_addr : $*Int64 + dealloc_stack %inout_addr : $*Int64 + dealloc_stack %in_addr : $*Int64 + + %out_literal = integer_literal $Builtin.Int32, 0 + %out = struct $Int32 (%out_literal : $Builtin.Int32) + return %out : $Int32 +} diff --git a/test/IRGen/async/run-partialapply-capture-int64-int64-to-int64.sil b/test/IRGen/async/run-partialapply-capture-int64-int64-to-int64.sil new file mode 100644 index 0000000000000..d3c040f98d46f --- /dev/null +++ b/test/IRGen/async/run-partialapply-capture-int64-int64-to-int64.sil @@ -0,0 +1,71 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency +// UNSUPPORTED: use_os_stdlib + +import Builtin +import Swift +import PrintShims + +sil public_external @printGeneric : $@convention(thin) (@in_guaranteed T) -> () +sil public_external @printInt64 : $@convention(thin) (Int64) -> () + +sil hidden @createAndInvokeClosure : $@async @convention(thin) () -> () { +bb0: + %captured_literal = integer_literal $Builtin.Int64, 783247897 + %captured = struct $Int64 (%captured_literal : $Builtin.Int64) + %createPartialApply = function_ref @createPartialApply : $@async @convention(thin) (Int64) -> @owned @async @callee_guaranteed (Int64) -> Int64 + %partialApply = apply %createPartialApply(%captured) : $@async @convention(thin) (Int64) -> @owned @async @callee_guaranteed (Int64) -> Int64 + strong_retain %partialApply : $@async @callee_guaranteed (Int64) -> Int64 + %applied_literal = integer_literal $Builtin.Int64, 7823478 + %applied = struct $Int64 (%applied_literal : $Builtin.Int64) + %sum = apply %partialApply(%applied) : $@async @callee_guaranteed (Int64) -> Int64 + strong_release %partialApply : $@async @callee_guaranteed (Int64) -> Int64 + %printInt64 = function_ref @printInt64 : $@convention(thin) (Int64) -> () + %result = apply %printInt64(%sum) : $@convention(thin) (Int64) -> () // CHECK: 791071375 + strong_release %partialApply : $@async @callee_guaranteed (Int64) -> Int64 + return %result : $() +} + +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%argc : $Int32, %argv : $UnsafeMutablePointer>>): + %createAndInvokeClosure = function_ref @createAndInvokeClosure : $@async @convention(thin) () -> () + %createAndInvokeClosure_result = apply %createAndInvokeClosure() : $@async @convention(thin) () -> () + %out_literal = integer_literal $Builtin.Int32, 0 + %out = struct $Int32 (%out_literal : $Builtin.Int32) + return %out : $Int32 +} + +// CHECK-LL: define internal swiftcc void @closure(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]*}} +// CHECK-LL: define internal swiftcc void @"$s7closureTA"(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]*}}) {{#[0-9]*}} +sil hidden @createPartialApply : $@async @convention(thin) (Int64) -> @owned @async @callee_guaranteed (Int64) -> Int64 { +bb0(%captured : $Int64): + %closure = function_ref @closure : $@async @convention(thin) (Int64, Int64) -> Int64 + %partialApply = partial_apply [callee_guaranteed] %closure(%captured) : $@async @convention(thin) (Int64, Int64) -> Int64 + return %partialApply : $@async @callee_guaranteed (Int64) -> Int64 +} + +sil private @closure : $@async @convention(thin) (Int64, Int64) -> Int64 { +bb0(%one : $Int64, %two : $Int64): + %printInt64 = function_ref @printInt64 : $@convention(thin) (Int64) -> () + %printInt64_1 = apply %printInt64(%one) : $@convention(thin) (Int64) -> () + %printInt64_2 = apply %printInt64(%two) : $@convention(thin) (Int64) -> () + %one_builtin = struct_extract %one : $Int64, #Int64._value + %two_builtin = struct_extract %two : $Int64, #Int64._value + %flag = integer_literal $Builtin.Int1, -1 + %sumAndOverflowed = builtin "sadd_with_overflow_Int64"(%one_builtin : $Builtin.Int64, %two_builtin : $Builtin.Int64, %flag : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1) + %sum_builtin = tuple_extract %sumAndOverflowed : $(Builtin.Int64, Builtin.Int1), 0 + %overflowed = tuple_extract %sumAndOverflowed : $(Builtin.Int64, Builtin.Int1), 1 + cond_fail %overflowed : $Builtin.Int1, "arithmetic overflow" + %sum = struct $Int64 (%sum_builtin : $Builtin.Int64) + return %sum : $Int64 +} + diff --git a/test/IRGen/async/run-partialapply-capture-int64-to-generic.sil b/test/IRGen/async/run-partialapply-capture-int64-to-generic.sil new file mode 100644 index 0000000000000..47f7a3daf7c43 --- /dev/null +++ b/test/IRGen/async/run-partialapply-capture-int64-to-generic.sil @@ -0,0 +1,64 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency +// UNSUPPORTED: use_os_stdlib + +import Builtin +import Swift +import PrintShims + +sil public_external @printGeneric : $@convention(thin) (@in_guaranteed T) -> () +sil public_external @printInt32 : $@convention(thin) (Int32) -> () + +sil @partial_apply_dynamic_with_out_param : $@convention(thin) (Int32, @owned @async @callee_owned (Int32) -> @out T) -> @async @callee_owned () -> @out T { +bb0(%x : $Int32, %f : $@async @callee_owned (Int32) -> @out T): + %p = partial_apply %f(%x) : $@async @callee_owned (Int32) -> @out T + return %p : $@async @callee_owned () -> @out T +} + +// CHECK-LL: define internal swiftcc void @"$sTA"(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]*}}) {{#[0-9]*}} { +// CHECK-LL: define internal swiftcc void @"$s6calleeTA"(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]*}}) {{#[0-9]*}} { +sil @callee : $@async @convention(thin) (Int32, @in_guaranteed T) -> @out T { +entry(%out_t : $*T, %x : $Int32, %in_t : $*T): + %printGeneric = function_ref @printGeneric : $@convention(thin) (@in_guaranteed T) -> () + %printInt32 = function_ref @printInt32 : $@convention(thin) (Int32) -> () + %result = apply %printGeneric(%in_t) : $@convention(thin) (@in_guaranteed T) -> () // CHECK: 1 + %printInt32_result = apply %printInt32(%x) : $@convention(thin) (Int32) -> () // CHECK: 6789 + copy_addr %in_t to [initialization] %out_t : $*T + return %result : $() +} + +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%argc : $Int32, %argv : $UnsafeMutablePointer>>): + %callee = function_ref @callee : $@async @convention(thin) (Int32, @in_guaranteed T) -> @out T + %first_literal = integer_literal $Builtin.Int32, 1 + %first = struct $Int32 (%first_literal : $Builtin.Int32) + %first_addr = alloc_stack $Int32 + store %first to %first_addr : $*Int32 + %callee1 = partial_apply %callee(%first_addr) : $@async @convention(thin) (Int32, @in_guaranteed T) -> @out T + %partialApplier = function_ref @partial_apply_dynamic_with_out_param : $@convention(thin) (Int32, @owned @async @callee_owned (Int32) -> @out T) -> @async @callee_owned () -> @out T + %second_literal = integer_literal $Builtin.Int32, 6789 + %second = struct $Int32 (%second_literal : $Builtin.Int32) + %callee2 = apply %partialApplier(%second, %callee1) : $@convention(thin) (Int32, @owned @async @callee_owned (Int32) -> @out T) -> @async @callee_owned () -> @out T + + %result_addr = alloc_stack $Int32 + %result = apply %callee2(%result_addr) : $@async @callee_owned () -> @out Int32 + + %printGeneric = function_ref @printGeneric : $@convention(thin) (@in_guaranteed T) -> () + %printGeneric_result = apply %printGeneric(%result_addr) : $@convention(thin) (@in_guaranteed T) -> () // CHECK: 1 + + dealloc_stack %result_addr : $*Int32 + dealloc_stack %first_addr : $*Int32 + + %out_literal = integer_literal $Builtin.Int32, 0 + %out = struct $Int32 (%out_literal : $Builtin.Int32) + return %out : $Int32 +} diff --git a/test/IRGen/async/run-partialapply-capture-struct_classinstance_classinstance-and-int64-to-int64.sil b/test/IRGen/async/run-partialapply-capture-struct_classinstance_classinstance-and-int64-to-int64.sil new file mode 100644 index 0000000000000..9ddcf14152d88 --- /dev/null +++ b/test/IRGen/async/run-partialapply-capture-struct_classinstance_classinstance-and-int64-to-int64.sil @@ -0,0 +1,105 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency +// UNSUPPORTED: use_os_stdlib + +import Builtin +import Swift +import PrintShims + +sil public_external @printGeneric : $@convention(thin) (@in_guaranteed T) -> () +sil public_external @printInt64 : $@convention(thin) (Int64) -> () + +class C { + deinit + init() +} + +sil hidden [exact_self_class] @S_allocating_init : $@convention(method) (@thick C.Type) -> @owned C { +bb0(%0 : $@thick C.Type): + %1 = alloc_ref $C + %2 = function_ref @$S_init : $@convention(method) (@owned C) -> @owned C + %3 = apply %2(%1) : $@convention(method) (@owned C) -> @owned C + return %3 : $C +} + +sil hidden @$S_init : $@convention(method) (@owned C) -> @owned C { +bb0(%0 : $C): + return %0 : $C +} + +sil hidden @$S_deinit : $@convention(method) (@guaranteed C) -> @owned Builtin.NativeObject { +bb0(%0 : $C): + %2 = unchecked_ref_cast %0 : $C to $Builtin.NativeObject + return %2 : $Builtin.NativeObject +} + +sil hidden @S_deallocating_deinit : $@convention(method) (@owned C) -> () { +bb0(%0 : $C): + %2 = function_ref @$S_deinit : $@convention(method) (@guaranteed C) -> @owned Builtin.NativeObject + %3 = apply %2(%0) : $@convention(method) (@guaranteed C) -> @owned Builtin.NativeObject + %4 = unchecked_ref_cast %3 : $Builtin.NativeObject to $C + dealloc_ref %4 : $C + %6 = tuple () + return %6 : $() +} + +sil_vtable C { + #C.init!allocator: (C.Type) -> () -> C : @S_allocating_init + #C.deinit!deallocator: @S_deallocating_deinit +} + + +struct S { var x: C, y: C } + +// CHECK-LL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @structClassInstanceClassInstanceAndInt64ToInt64(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]*}} { +// CHECK-LL: define internal swiftcc void @"$s019structClassInstancebc10AndInt64ToE0TA"(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]*}}) {{#[0-9]*}} { +sil @structClassInstanceClassInstanceAndInt64ToInt64 : $@async @convention(thin) (Int64, @guaranteed S) -> Int64 { +entry(%in : $Int64, %s : $S): + %s_addr = alloc_stack $S + store %s to %s_addr : $*S + %printGeneric = function_ref @printGeneric : $@convention(thin) (@in_guaranteed T) -> () + %printGeneric_result = apply %printGeneric(%s_addr) : $@convention(thin) (@in_guaranteed T) -> () //CHECK: S(x: main.C, y: main.C) + dealloc_stack %s_addr : $*S + %printInt64 = function_ref @printInt64 : $@convention(thin) (Int64) -> () + %printInt64_result = apply %printInt64(%in) : $@convention(thin) (Int64) -> () // CHECK: 9999 + return %in : $Int64 +} + +sil @partial_apply_guaranteed_class_pair_param : $@async @convention(thin) (@owned S) -> @async @callee_owned (Int64) -> Int64 { +bb0(%x : $S): + %f = function_ref @structClassInstanceClassInstanceAndInt64ToInt64 : $@async @convention(thin) (Int64, @guaranteed S) -> Int64 + %p = partial_apply %f(%x) : $@async @convention(thin) (Int64, @guaranteed S) -> Int64 + return %p : $@async @callee_owned (Int64) -> Int64 +} + +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%argc : $Int32, %argv : $UnsafeMutablePointer>>): + %s_type = metatype $@thick C.Type + %allocating_init = function_ref @S_allocating_init : $@convention(method) (@thick C.Type) -> @owned C + %instance1 = apply %allocating_init(%s_type) : $@convention(method) (@thick C.Type) -> @owned C + %instance2 = apply %allocating_init(%s_type) : $@convention(method) (@thick C.Type) -> @owned C + strong_retain %instance1 : $C + strong_retain %instance2 : $C + %instance = struct $S (%instance1 : $C, %instance2 : $C) + + %partial_apply_guaranteed_class_pair_param = function_ref @partial_apply_guaranteed_class_pair_param : $@async @convention(thin) (@owned S) -> @async @callee_owned (Int64) -> Int64 + %partiallyApplied = apply %partial_apply_guaranteed_class_pair_param(%instance) : $@async @convention(thin) (@owned S) -> @async @callee_owned (Int64) -> Int64 + %int_literal = integer_literal $Builtin.Int64, 9999 + %int = struct $Int64 (%int_literal : $Builtin.Int64) + %result = apply %partiallyApplied(%int) : $@async @callee_owned (Int64) -> Int64 + %printInt64 = function_ref @printInt64 : $@convention(thin) (Int64) -> () + %printInt64_result = apply %printInt64(%result) : $@convention(thin) (Int64) -> () // CHECK: 9999 + + %out_literal = integer_literal $Builtin.Int32, 0 + %out = struct $Int32 (%out_literal : $Builtin.Int32) + return %out : $Int32 +} diff --git a/test/IRGen/async/run-partialapply-capture-structgeneric_classinstance_to_struct_and_error.sil b/test/IRGen/async/run-partialapply-capture-structgeneric_classinstance_to_struct_and_error.sil new file mode 100644 index 0000000000000..4a286189a05ad --- /dev/null +++ b/test/IRGen/async/run-partialapply-capture-structgeneric_classinstance_to_struct_and_error.sil @@ -0,0 +1,97 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency +// UNSUPPORTED: use_os_stdlib + +import Builtin +import Swift +import PrintShims + +sil public_external @printGeneric : $@convention(thin) (@in_guaranteed T) -> () + +struct A1 { + let b: () -> () +} + +struct A2 { + let a: T +} + +class A3 {} +sil_vtable A3 {} + +sil @printA2AtA3 : $@convention(thin) (A2) -> () { +entry(%value : $A2): + %addr = alloc_stack $A2 + store %value to %addr : $*A2 + %printGeneric = function_ref @printGeneric : $@convention(thin) (@in_guaranteed T) -> () + %result = apply %printGeneric>(%addr) : $@convention(thin) (@in_guaranteed T) -> () + dealloc_stack %addr : $*A2 + return %result : $() +} + +// CHECK-LL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @amethod(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]*}} { +sil @amethod : $@async @convention(method) (@in_guaranteed A2) -> (@owned A1, @error Error) { +entry(%a2_at_a3_addr : $*A2): + %a2_at_a3 = load %a2_at_a3_addr : $*A2 + %printA2AtA3 = function_ref @printA2AtA3 : $@convention(thin) (A2) -> () + %partiallyApplied = partial_apply [callee_guaranteed] %printA2AtA3(%a2_at_a3) : $@convention(thin) (A2) -> () + %result = struct $A1 ( %partiallyApplied : $@callee_guaranteed () -> () ) + return %result : $A1 +} + +// CHECK-LL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @repo(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]*}} { +// CHECK-LL: define internal swiftcc void @"$s7amethodTA"(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]*}}) {{#[0-9]*}} { +sil @repo : $@async @convention(thin) (@in_guaranteed A2) -> @owned @async @callee_guaranteed () -> (@owned A1, @error Error) { +bb0(%0 : $*A2): + %1 = load %0 : $*A2 + %2 = alloc_stack $A2 + store %1 to %2 : $*A2 + %4 = function_ref @amethod : $@async @convention(method) (@in_guaranteed A2) -> (@owned A1, @error Error) + %5 = partial_apply [callee_guaranteed] %4(%2) : $@async @convention(method) (@in_guaranteed A2) -> (@owned A1, @error Error) + dealloc_stack %2 : $*A2 + return %5 : $@async @callee_guaranteed () -> (@owned A1, @error Error) +} + +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%argc : $Int32, %argv : $UnsafeMutablePointer>>): + %a3 = alloc_ref $A3 + %a2 = struct $A2 (%a3 : $A3) + + %a2_addr = alloc_stack $A2 + store %a2 to %a2_addr : $*A2 + + %callee = function_ref @repo : $@async @convention(thin) (@in_guaranteed A2) -> @owned @async @callee_guaranteed () -> (@owned A1, @error Error) + %partiallyApplied = apply %callee(%a2_addr) : $@async @convention(thin) (@in_guaranteed A2) -> @owned @async @callee_guaranteed () -> (@owned A1, @error Error) + try_apply %partiallyApplied() : $@async @callee_guaranteed () -> (@owned A1, @error Error), normal bb_success, error bb_error + +bb_success(%value : $A1): + %value_addr = alloc_stack $A1 + store %value to %value_addr : $*A1 + %printGeneric = function_ref @printGeneric : $@convention(thin) (@in_guaranteed T) -> () + %result = apply %printGeneric(%value_addr) : $@convention(thin) (@in_guaranteed T) -> () // CHECK: A1(b: (Function)) + %closure = struct_extract %value : $A1, #A1.b + %closure_result = apply %closure() : $@callee_guaranteed () -> () // CHECK: A2(a: main.A3) + dealloc_stack %value_addr : $*A1 + + br bb_finish + +bb_error(%error : $Error): + br bb_finish + +bb_finish: + + dealloc_stack %a2_addr : $*A2 + + %out_literal = integer_literal $Builtin.Int32, 0 + %out = struct $Int32 (%out_literal : $Builtin.Int32) + return %out : $Int32 +} diff --git a/test/IRGen/async/run-partialapply-capture-structgeneric_polymorphic_constrained-to-void.sil b/test/IRGen/async/run-partialapply-capture-structgeneric_polymorphic_constrained-to-void.sil new file mode 100644 index 0000000000000..5652402af6eab --- /dev/null +++ b/test/IRGen/async/run-partialapply-capture-structgeneric_polymorphic_constrained-to-void.sil @@ -0,0 +1,68 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency +// UNSUPPORTED: use_os_stdlib + +import Builtin +import Swift +import PrintShims + +sil public_external @printGeneric : $@convention(thin) (@in_guaranteed T) -> () + +public protocol Q { + associatedtype Update +} + +public struct BaseProducer : Q { + public typealias Update = T +} +sil_witness_table BaseProducer : Q module main { + associated_type Update : T +} + +public class WeakBox {} +sil_vtable WeakBox {} + +// CHECK-LL: define internal swiftcc void @"$s7takingQTA"(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]*}}) {{#[0-9]*}} { +sil hidden @takingQ : $@async @convention(thin) <τ_0_0 where τ_0_0 : Q> (@owned WeakBox<τ_0_0>) -> () { +entry(%box : $WeakBox<τ_0_0>): + %box_addr = alloc_stack $WeakBox<τ_0_0> + store %box to %box_addr : $*WeakBox<τ_0_0> + %printGeneric = function_ref @printGeneric : $@convention(thin) (@in_guaranteed T) -> () + %result = apply %printGeneric>(%box_addr) : $@convention(thin) (@in_guaranteed T) -> () // CHECK: main.WeakBox> + dealloc_stack %box_addr : $*WeakBox<τ_0_0> + return %result : $() +} + +sil public @bind_polymorphic_param_from_context : $@async @convention(thin) <τ_0_1>(@in τ_0_1) -> @owned @async @callee_owned () -> () { +bb0(%0 : $*τ_0_1): + %1 = alloc_ref $WeakBox> + %8 = function_ref @takingQ : $@async @convention(thin) <τ_0_0 where τ_0_0 : Q> (@owned WeakBox<τ_0_0>) -> () + %9 = partial_apply %8>(%1) : $@async @convention(thin) <τ_0_0 where τ_0_0 : Q> (@owned WeakBox<τ_0_0>) -> () + return %9 : $@async @callee_owned () -> () +} + +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%argc : $Int32, %argv : $UnsafeMutablePointer>>): + %bind_polymorphic_param_from_context = function_ref @bind_polymorphic_param_from_context : $@async @convention(thin) <τ_0_1>(@in τ_0_1) -> @owned @async @callee_owned () -> () + %int_literal = integer_literal $Builtin.Int64, 9999 + %int = struct $Int64 (%int_literal : $Builtin.Int64) + %int_addr = alloc_stack $Int64 + store %int to %int_addr : $*Int64 + %partiallyApplied = apply %bind_polymorphic_param_from_context(%int_addr) : $@async @convention(thin) <τ_0_1>(@in τ_0_1) -> @owned @async @callee_owned () -> () + dealloc_stack %int_addr : $*Int64 + + %result = apply %partiallyApplied() : $@async @callee_owned () -> () + + %out_literal = integer_literal $Builtin.Int32, 0 + %out = struct $Int32 (%out_literal : $Builtin.Int32) + return %out : $Int32 +} diff --git a/test/IRGen/async/run-partialapply-capture-type_structgeneric_polymorphic_constrained-to-void.sil b/test/IRGen/async/run-partialapply-capture-type_structgeneric_polymorphic_constrained-to-void.sil new file mode 100644 index 0000000000000..b97dc54862b02 --- /dev/null +++ b/test/IRGen/async/run-partialapply-capture-type_structgeneric_polymorphic_constrained-to-void.sil @@ -0,0 +1,71 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency +// UNSUPPORTED: use_os_stdlib + +import Builtin +import Swift +import PrintShims + +sil public_external @printGeneric : $@convention(thin) (@in_guaranteed T) -> () + +public protocol Q { + associatedtype Update +} + +public struct BaseProducer : Q { + public typealias Update = T +} +sil_witness_table BaseProducer : Q module main { + associated_type Update : T +} + +public class WeakBox {} +sil_vtable WeakBox {} + +// CHECK-LL: define hidden swiftcc void @takingQ(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]*}} { +sil hidden @takingQ : $@async @convention(thin) <τ_0_0 where τ_0_0 : Q> (@owned WeakBox<τ_0_0>) -> () { +entry(%box : $WeakBox<τ_0_0>): + %box_addr = alloc_stack $WeakBox<τ_0_0> + store %box to %box_addr : $*WeakBox<τ_0_0> + %printGeneric = function_ref @printGeneric : $@convention(thin) (@in_guaranteed T) -> () + %result = apply %printGeneric>(%box_addr) : $@convention(thin) (@in_guaranteed T) -> () // CHECK: main.WeakBox> + dealloc_stack %box_addr : $*WeakBox<τ_0_0> + return %result : $() +} + + +// CHECK-LL: define internal swiftcc void @"$s7takingQTA"(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]*}}) {{#[0-9]*}} { +sil public @bind_polymorphic_param_from_forwarder_parameter : $@async @convention(thin) <τ_0_1>(@in τ_0_1) -> @callee_owned @async (@owned WeakBox>) -> () { +bb0(%0 : $*τ_0_1): + %1 = alloc_ref $WeakBox> + %8 = function_ref @takingQ : $@async @convention(thin) <τ_0_0 where τ_0_0 : Q> (@owned WeakBox<τ_0_0>) -> () + %9 = partial_apply %8>() : $@async @convention(thin) <τ_0_0 where τ_0_0 : Q> (@owned WeakBox<τ_0_0>) -> () + return %9 : $@callee_owned @async (@owned WeakBox>) -> () +} + +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%argc : $Int32, %argv : $UnsafeMutablePointer>>): + %bind_polymorphic_param_from_forwarder_parameter = function_ref @bind_polymorphic_param_from_forwarder_parameter : $@async @convention(thin) <τ_0_1>(@in τ_0_1) -> @callee_owned @async (@owned WeakBox>) -> () + %int_literal = integer_literal $Builtin.Int64, 9999 + %int = struct $Int64 (%int_literal : $Builtin.Int64) + %int_addr = alloc_stack $Int64 + store %int to %int_addr : $*Int64 + %partiallyApplied = apply %bind_polymorphic_param_from_forwarder_parameter(%int_addr) : $@async @convention(thin) <τ_0_1>(@in τ_0_1) -> @callee_owned @async (@owned WeakBox>) -> () + dealloc_stack %int_addr : $*Int64 + + %box = alloc_ref $WeakBox> + %result = apply %partiallyApplied(%box) : $@callee_owned @async (@owned WeakBox>) -> () + + %out_literal = integer_literal $Builtin.Int32, 0 + %out = struct $Int32 (%out_literal : $Builtin.Int32) + return %out : $Int32 +} diff --git a/test/IRGen/async/run-partialapply-capture-type_thin-and-classinstance-to-void.sil b/test/IRGen/async/run-partialapply-capture-type_thin-and-classinstance-to-void.sil new file mode 100644 index 0000000000000..8e9a414adabbb --- /dev/null +++ b/test/IRGen/async/run-partialapply-capture-type_thin-and-classinstance-to-void.sil @@ -0,0 +1,102 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency +// UNSUPPORTED: use_oC_stdlib + +import Builtin +import Swift +import PrintShims + +sil public_external @printGeneric : $@convention(thin) (@in_guaranteed T) -> () +sil public_external @printInt64 : $@convention(thin) (Int64) -> () + +class C { + deinit + init() +} + +sil hidden [exact_self_class] @C_allocating_init : $@convention(method) (@thick C.Type) -> @owned C { +bb0(%0 : $@thick C.Type): + %1 = alloc_ref $C + %2 = function_ref @$C_init : $@convention(method) (@owned C) -> @owned C + %3 = apply %2(%1) : $@convention(method) (@owned C) -> @owned C + return %3 : $C +} + +sil hidden @$C_init : $@convention(method) (@owned C) -> @owned C { +bb0(%0 : $C): + return %0 : $C +} + +sil hidden @$C_deinit : $@convention(method) (@guaranteed C) -> @owned Builtin.NativeObject { +bb0(%0 : $C): + %2 = unchecked_ref_cast %0 : $C to $Builtin.NativeObject + return %2 : $Builtin.NativeObject +} + +sil hidden @C_deallocating_deinit : $@convention(method) (@owned C) -> () { +bb0(%0 : $C): + %2 = function_ref @$C_deinit : $@convention(method) (@guaranteed C) -> @owned Builtin.NativeObject + %3 = apply %2(%0) : $@convention(method) (@guaranteed C) -> @owned Builtin.NativeObject + %4 = unchecked_ref_cast %3 : $Builtin.NativeObject to $C + dealloc_ref %4 : $C + %6 = tuple () + return %6 : $() +} + +sil_vtable C { + #C.init!allocator: (C.Type) -> () -> C : @C_allocating_init + #C.deinit!deallocator: @C_deallocating_deinit +} + +struct S {} + +// CHECK-LL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @structtypeSAndClassinstanceCToVoid(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]*}} { +// CHECK-LL: define internal swiftcc void @"$s34structtypeSAndClassinstanceCToVoidTA"(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]*}}) {{#[0-9]*}} { +sil @structtypeSAndClassinstanceCToVoid : $@async @convention(thin) (@thin S.Type, @owned C) -> () { +entry(%S_type: $@thin S.Type, %C_instance: $C): + %S_type_addr = alloc_stack $@thick S.Type + %S_type_thick = metatype $@thick S.Type + store %S_type_thick to %S_type_addr : $*@thick S.Type + %C_instance_addr = alloc_stack $C + store %C_instance to %C_instance_addr : $*C + %printGeneric = function_ref @printGeneric : $@convention(thin) (@in_guaranteed T) -> () + %printGeneric_result1 = apply %printGeneric(%S_type_addr) : $@convention(thin) (@in_guaranteed T) -> () //CHECK: S + %printGeneric_result2 = apply %printGeneric(%C_instance_addr) : $@convention(thin) (@in_guaranteed T) -> () //CHECK: main.C + dealloc_stack %C_instance_addr : $*C + dealloc_stack %S_type_addr : $*@thick S.Type + return %printGeneric_result2 : $() +} + +sil @partial_apply_thin_type : $@async @convention(thin) (@thin S.Type, @owned C) -> @async @callee_owned () -> () { +entry(%S_type: $@thin S.Type, %C_instance: $C): + %structtypeSAndClassinstanceCToVoid = function_ref @structtypeSAndClassinstanceCToVoid : $@async @convention(thin) (@thin S.Type, @owned C) -> () + %closure = partial_apply %structtypeSAndClassinstanceCToVoid (%S_type, %C_instance) : $@async @convention(thin) (@thin S.Type, @owned C) -> () + return %closure : $@async @callee_owned () -> () +} + +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%argc : $Int32, %argv : $UnsafeMutablePointer>>): + %c_type = metatype $@thick C.Type + %allocating_init = function_ref @C_allocating_init : $@convention(method) (@thick C.Type) -> @owned C + %instance = apply %allocating_init(%c_type) : $@convention(method) (@thick C.Type) -> @owned C + strong_retain %instance : $C + + + %partial_apply_thin_type = function_ref @partial_apply_thin_type : $@async @convention(thin) (@thin S.Type, @owned C) -> @async @callee_owned () -> () + %S_type = metatype $@thin S.Type + %partiallyApplied = apply %partial_apply_thin_type(%S_type, %instance) : $@async @convention(thin) (@thin S.Type, @owned C) -> @async @callee_owned () -> () + %result = apply %partiallyApplied() : $@async @callee_owned () -> () + + %out_literal = integer_literal $Builtin.Int32, 0 + %out = struct $Int32 (%out_literal : $Builtin.Int32) + return %out : $Int32 +} diff --git a/test/IRGen/autolink-runtime-compatibility.swift b/test/IRGen/autolink-runtime-compatibility.swift index f917f2115481a..a06fc8bb4457d 100644 --- a/test/IRGen/autolink-runtime-compatibility.swift +++ b/test/IRGen/autolink-runtime-compatibility.swift @@ -16,14 +16,17 @@ // Autolinks because compatibility library was explicitly asked for // RUN: %target-swift-frontend -runtime-compatibility-version 5.0 -emit-ir -parse-stdlib %s | %FileCheck -check-prefix=FORCE-LOAD %s // RUN: %target-swift-frontend -runtime-compatibility-version 5.1 -emit-ir -parse-stdlib %s | %FileCheck -check-prefix=FORCE-LOAD-51 %s +// RUN: %target-swift-frontend -runtime-compatibility-version 5.3 -emit-ir -parse-stdlib %s | %FileCheck -check-prefix=FORCE-LOAD-53 %s // RUN: %target-swift-frontend -target %target-cpu-apple-macosx10.24 -runtime-compatibility-version 5.0 -emit-ir -parse-stdlib %s | %FileCheck -check-prefix=FORCE-LOAD %s // RUN: %target-swift-frontend -target %target-cpu-apple-macosx10.24 -runtime-compatibility-version 5.1 -emit-ir -parse-stdlib %s | %FileCheck -check-prefix=FORCE-LOAD-51 %s +// RUN: %target-swift-frontend -target %target-cpu-apple-macosx10.24 -runtime-compatibility-version 5.3 -emit-ir -parse-stdlib %s | %FileCheck -check-prefix=FORCE-LOAD-53 %s public func foo() {} // NO-FORCE-LOAD-NOT: FORCE_LOAD // NO-FORCE-LOAD-NOT: !{!"-lswiftCompatibility50"} // NO-FORCE-LOAD-NOT: !{!"-lswiftCompatibility51"} +// NO-FORCE-LOAD-NOT: !{!"-lswiftCompatibility53"} // NO-FORCE-LOAD-NOT: !{!"-lswiftCompatibilityDynamicReplacements"} // FORCE-LOAD: declare {{.*}} @"_swift_FORCE_LOAD_$_swiftCompatibility50" @@ -42,3 +45,16 @@ public func foo() {} // FORCE-LOAD-51-DAG: !llvm.linker.options = !{{{.*}}[[AUTOLINK_SWIFT_COMPAT]]{{[,}]}} // FORCE-LOAD-51-NOT: @"_swift_FORCE_LOAD_$_swiftCompatibility50" // FORCE-LOAD-51-NOT: @"_swift_FORCE_LOAD_$_swiftCompatibilityDynamicReplacements" + +// FORCE-LOAD-53-NOT: @"_swift_FORCE_LOAD_$_swiftCompatibility50" +// FORCE-LOAD-53-NOT: @"_swift_FORCE_LOAD_$_swiftCompatibility51" +// FORCE-LOAD-53-NOT: @"_swift_FORCE_LOAD_$_swiftCompatibilityDynamicReplacements" +// FORCE-LOAD-53: declare {{.*}} @"_swift_FORCE_LOAD_$_swiftCompatibility53" +// FORCE-LOAD-53-NOT: @"_swift_FORCE_LOAD_$_swiftCompatibility50" +// FORCE-LOAD-53-NOT: @"_swift_FORCE_LOAD_$_swiftCompatibility51" +// FORCE-LOAD-53-NOT: @"_swift_FORCE_LOAD_$_swiftCompatibilityDynamicReplacements" +// FORCE-LOAD-53-DAG: [[AUTOLINK_SWIFT_COMPAT:![0-9]+]] = !{!"-lswiftCompatibility53"} +// FORCE-LOAD-53-DAG: !llvm.linker.options = !{{{.*}}[[AUTOLINK_SWIFT_COMPAT]]{{[,}]}} +// FORCE-LOAD-53-NOT: @"_swift_FORCE_LOAD_$_swiftCompatibility50" +// FORCE-LOAD-53-NOT: @"_swift_FORCE_LOAD_$_swiftCompatibility51" +// FORCE-LOAD-53-NOT: @"_swift_FORCE_LOAD_$_swiftCompatibilityDynamicReplacements" diff --git a/test/IRGen/big_types_corner_cases.swift b/test/IRGen/big_types_corner_cases.swift index f956b23f7effe..7acfaa13c601d 100644 --- a/test/IRGen/big_types_corner_cases.swift +++ b/test/IRGen/big_types_corner_cases.swift @@ -204,12 +204,6 @@ public func testGetFunc() { // CHECK: [[CALL1:%.*]] = call {{.*}} @__swift_instantiateConcreteTypeFromMangledName({{.*}} @"$sSayy22big_types_corner_cases9BigStructVcSgGMD" // CHECK: [[CALL2:%.*]] = call i8** @"$sSayy22big_types_corner_cases9BigStructVcSgGSayxGSlsWl // CHECK: call swiftcc void @"$sSlsE10firstIndex5where0B0QzSgSb7ElementQzKXE_tKF"(%TSq.{{.*}}* noalias nocapture sret %{{[0-9]+}}, i8* bitcast ({{.*}}* @"$s22big_types_corner_cases9BigStruct{{.*}}_TRTA{{(\.ptrauth)?}}" to i8*), %swift.opaque* %{{[0-9]+}}, %swift.type* %{{[0-9]+}}, i8** [[CALL2]] - -// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} hidden swiftcc void @"$s22big_types_corner_cases7TestBigC5test2yyF"(%T22big_types_corner_cases7TestBigC* swiftself %0) -// CHECK: [[CALL1:%.*]] = call {{.*}} @__swift_instantiateConcreteTypeFromMangledName({{.*}} @"$sSaySS2ID_y22big_types_corner_cases9BigStructVcSg7handlertGMD" -// CHECK: [[CALL2:%.*]] = call i8** @"$sSaySS2ID_y22big_types_corner_cases9BigStructVcSg7handlertGSayxGSlsWl" -// CHECK: call swiftcc void @"$sSlss16IndexingIteratorVyxG0B0RtzrlE04makeB0ACyF"(%Ts16IndexingIteratorV{{.*}}* noalias nocapture sret {{.*}}, %swift.type* [[CALL1]], i8** [[CALL2]], %swift.opaque* noalias nocapture swiftself {{.*}}) -// CHECK: ret void class TestBig { typealias Handler = (BigStruct) -> Void diff --git a/test/IRGen/big_types_generic.swift b/test/IRGen/big_types_generic.swift index d1c648fd3e465..3d3b9194d19f6 100644 --- a/test/IRGen/big_types_generic.swift +++ b/test/IRGen/big_types_generic.swift @@ -67,3 +67,29 @@ func useStuff() { print(generic2(1).0) print(generic2(1).1) } + + +public struct BigThing { + var x: (Int64, Int64, Int64, Int64) = (0, 0, 0, 0) + var y: (Int64, Int64, Int64, Int64) = (0, 0, 0, 0) + var z: (Int64, Int64, Int64, Int64) = (0, 0, 0, 0) +} + +public protocol P {} + +public protocol Assoc { + associatedtype A + func foo() -> A +} + +extension Int : P {} + +public struct DefineSome : Assoc { + public func foo() -> some P { + return 5 + } +} + +public func abiIndirect() -> BigThing { + return BigThing() +} diff --git a/test/IRGen/builtin_conformances.swift b/test/IRGen/builtin_conformances.swift new file mode 100644 index 0000000000000..f97f905d4a782 --- /dev/null +++ b/test/IRGen/builtin_conformances.swift @@ -0,0 +1,88 @@ +// RUN: %target-swift-frontend -emit-ir %s | %FileCheck %s + +// CHECK-LABEL: @_swift_tupleEquatable_conf = external{{( dllimport)?}} global %swift.protocol_conformance_descriptor +// CHECK-LABEL: @_swift_tupleComparable_conf = external{{( dllimport)?}} global %swift.protocol_conformance_descriptor +// CHECK-LABEL: @_swift_tupleHashable_conf = external{{( dllimport)?}} global %swift.protocol_conformance_descriptor + +struct Wrapper { + let value: T +} + +//===----------------------------------------------------------------------===// +// Tuple Equatable conformance +//===----------------------------------------------------------------------===// + +extension Wrapper: Equatable where T: Equatable {} + +public func equals(_ lhs: T, _ rhs: T) -> Bool { + lhs == rhs +} + +public func useEquatable(_ thing: T) -> Bool { + // CHECK: [[USE_EQUATABLE_WT:%.*]] = call i8** @swift_getWitnessTable(%swift.protocol_conformance_descriptor* @_swift_tupleEquatable_conf, %swift.type* {{%.*}}, i8*** {{%.*}}) + // CHECK-NEXT: {{%.*}} = call swiftcc i1 {{.*}}(%swift.opaque* noalias nocapture {{%.*}}, %swift.opaque* noalias nocapture {{%.*}}, %swift.type* {{%.*}}, i8** [[USE_EQUATABLE_WT]]) + equals((thing, thing), (thing, thing)) +} + +public func testTupleEquatable() { + // CHECK: [[TEST_TUPLE_EQUATABLE_WT1:%.*]] = call i8** @swift_getWitnessTable(%swift.protocol_conformance_descriptor* @_swift_tupleEquatable_conf, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* @"$sytN", i32 0, i32 1), i8*** undef) + // CHECK: {{%.*}} = call swiftcc i1 {{.*}}(%swift.opaque* noalias nocapture undef, %swift.opaque* noalias nocapture undef, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* @"$sytN", i32 0, i32 1), i8** [[TEST_TUPLE_EQUATABLE_WT1]]) + let _ = equals((), ()) + + // CHECK: {{%.*}} = call swiftcc i1 {{.*}}({{%.*}}.0* noalias nocapture undef, {{%.*}}.0* noalias nocapture undef, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* @"$sytN", i32 0, i32 1), i8** [[TEST_TUPLE_EQUATABLE_WT1]]) + let _ = Wrapper(value: ()) == Wrapper(value: ()) +} + +//===----------------------------------------------------------------------===// +// Tuple Comparable conformance +//===----------------------------------------------------------------------===// + +extension Wrapper: Comparable where T: Comparable { + static func <(lhs: Wrapper, rhs: Wrapper) -> Bool { + return lhs.value < rhs.value + } +} + +public func compare(_ lhs: T, _ rhs: T) -> Bool { + lhs < rhs +} + +public func useComparable(_ thing: T) -> Bool { + // CHECK: [[USE_COMPARABLE_WT:%.*]] = call i8** @swift_getWitnessTable(%swift.protocol_conformance_descriptor* @_swift_tupleComparable_conf, %swift.type* {{%.*}}, i8*** {{%.*}}) + // CHECK-NEXT: {{%.*}} = call swiftcc i1 {{.*}}(%swift.opaque* noalias nocapture {{%.*}}, %swift.opaque* noalias nocapture {{%.*}}, %swift.type* {{%.*}}, i8** [[USE_COMPARABLE_WT]]) + compare((thing, thing), (thing, thing)) +} + +public func testTupleComparable() { + // CHECK: [[TEST_TUPLE_COMPARABLE_WT1:%.*]] = call i8** @swift_getWitnessTable(%swift.protocol_conformance_descriptor* @_swift_tupleComparable_conf, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* @"$sytN", i32 0, i32 1), i8*** undef) + // CHECK: {{%.*}} = call swiftcc i1 {{.*}}(%swift.opaque* noalias nocapture undef, %swift.opaque* noalias nocapture undef, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* @"$sytN", i32 0, i32 1), i8** [[TEST_TUPLE_COMPARABLE_WT1]]) + let _ = compare((), ()) + + // CHECK: {{%.*}} = call swiftcc i1 {{.*}}({{%.*}}.1* noalias nocapture undef, {{%.*}}.1* noalias nocapture undef, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* @"$sytN", i32 0, i32 1), i8** [[TEST_TUPLE_COMPARABLE_WT1]]) + let _ = Wrapper(value: ()) < Wrapper(value: ()) +} + +//===----------------------------------------------------------------------===// +// Tuple Hashable conformance +//===----------------------------------------------------------------------===// + +extension Wrapper: Hashable where T: Hashable {} + +public func hashValue(for instance: T) -> Int { + instance.hashValue +} + +public func useHashable(_ thing: T) -> Int { + // CHECK: [[USE_HASHABLE_WT:%.*]] = call i8** @swift_getWitnessTable(%swift.protocol_conformance_descriptor* @_swift_tupleHashable_conf, %swift.type* {{%.*}}, i8*** {{%.*}}) + // CHECK-NEXT: {{%.*}} = call swiftcc i{{.*}} {{.*}}(%swift.opaque* noalias nocapture {{%.*}}, %swift.type* {{%.*}}, i8** [[USE_HASHABLE_WT]]) + hashValue(for: (thing, thing)) +} + +public func testTupleHashable() { + // CHECK: [[TEST_TUPLE_HASHABLE_WT1:%.*]] = call i8** @swift_getWitnessTable(%swift.protocol_conformance_descriptor* @_swift_tupleHashable_conf, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* @"$sytN", i32 0, i32 1), i8*** undef) + // CHECK: {{%.*}} = call swiftcc i{{.*}} {{.*}}(%swift.opaque* noalias nocapture undef, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* @"$sytN", i32 0, i32 1), i8** [[TEST_TUPLE_HASHABLE_WT1]]) + let _ = hashValue(for: ()) + + // CHECK: {{%.*}} = call swiftcc i{{.*}} {{.*}}(%swift.type* {{%.*}}, i8** [[TEST_TUPLE_HASHABLE_WT1]], {{%.*}} noalias nocapture swiftself undef) + let _ = Wrapper(value: ()).hashValue +} diff --git a/test/IRGen/builtin_math.swift b/test/IRGen/builtin_math.swift index 3602af70075cf..cb8eb439a154e 100644 --- a/test/IRGen/builtin_math.swift +++ b/test/IRGen/builtin_math.swift @@ -5,7 +5,7 @@ #elseif canImport(Glibc) import Glibc #elseif os(Windows) - import MSVCRT + import CRT #else #error("Unsupported platform") #endif diff --git a/test/IRGen/fixlifetime.sil b/test/IRGen/fixlifetime.sil index bc05fde771f7a..cf5dd443126c5 100644 --- a/test/IRGen/fixlifetime.sil +++ b/test/IRGen/fixlifetime.sil @@ -8,7 +8,7 @@ // unnecessary. // ONONE-NOT: @__swift_fixLifetime -// CHECK-objc-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @test(%T11fixlifetime1CC* %0, %objc_object* %1, i8** %2, i8* %3, %swift.refcounted* %4, %T11fixlifetime3AggV* noalias nocapture dereferenceable({{.*}}) %5) {{.*}} { +// CHECK-objc-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @test(%T11fixlifetime1CC* %0, %objc_object* %1, i8** %2, i8* %3, %swift.refcounted* %4, %T11fixlifetime3AggV* noalias nocapture dereferenceable({{.*}}) %5, {{(i64|i32)}} %6, {{(i64|i32)}} %7) {{.*}} { // CHECK-objc: entry: // CHECK-objc: call void bitcast (void (%swift.refcounted*)* @__swift_fixLifetime to void (%T11fixlifetime1CC*)*)(%T11fixlifetime1CC* // CHECK-objc: call void bitcast (void (%swift.refcounted*)* @__swift_fixLifetime to void (%objc_object*)*)(%objc_object* @@ -16,9 +16,11 @@ // CHECK-objc: call void bitcast (void (%swift.refcounted*)* @__swift_fixLifetime to void (%T11fixlifetime1CC*)*)(%T11fixlifetime1CC* // CHECK-objc: call void bitcast (void (%swift.refcounted*)* @__swift_fixLifetime to void (%objc_object*)*)(%objc_object* // CHECK-objc: call void @__swift_fixLifetime(%swift.refcounted* +// CHECK-objc: [[TEMP:%.*]] = inttoptr {{(i64|i32)}} %6 to %objc_object* +// CHECK-objc: call void bitcast (void (%swift.refcounted*)* @__swift_fixLifetime to void (%objc_object*)*)(%objc_object* [[TEMP]]) // CHECK-objc: call void bitcast (void (%swift.refcounted*)* @__swift_fixLifetime to void (%T11fixlifetime1CC**)*)(%T11fixlifetime1CC** -// CHECK-native-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @test(%T11fixlifetime1CC* %0, %swift.refcounted* %1, i8** %2, i8* %3, %swift.refcounted* %4, %T11fixlifetime3AggV* noalias nocapture dereferenceable({{.*}}) %5) {{.*}} { +// CHECK-native-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @test(%T11fixlifetime1CC* %0, %swift.refcounted* %1, i8** %2, i8* %3, %swift.refcounted* %4, %T11fixlifetime3AggV* noalias nocapture dereferenceable({{.*}}) %5, {{(i64|i32)}} %6, {{(i64|i32)}} %7) {{.*}} { // CHECK-native: entry: // CHECK-native: call void bitcast (void (%swift.refcounted*)* @__swift_fixLifetime to void (%T11fixlifetime1CC*)*)(%T11fixlifetime1CC* // CHECK-native: call void @__swift_fixLifetime(%swift.refcounted* @@ -44,17 +46,23 @@ struct Agg { var f : F } +enum MyOptional { + case none + case some(T) +} + sil [Onone] @test - : $@convention(thin) (C, P, @callee_owned () -> (), Agg) -> () { -bb0(%0 : $C, %1 : $P, %2 : $@callee_owned () -> (), %3 : $Agg): + : $@convention(thin) (C, P, @callee_owned () -> (), Agg, @guaranteed MyOptional

) -> () { +bb0(%0 : $C, %1 : $P, %2 : $@callee_owned () -> (), %3 : $Agg, %4 : $MyOptional

): fix_lifetime %0 : $C fix_lifetime %1 : $P fix_lifetime %2 : $@callee_owned () -> () fix_lifetime %3 : $Agg + fix_lifetime %4 : $MyOptional

- %4 = alloc_stack $C - fix_lifetime %4 : $*C - dealloc_stack %4 : $*C + %5 = alloc_stack $C + fix_lifetime %5 : $*C + dealloc_stack %5 : $*C %9999 = tuple() return %9999 : $() } diff --git a/test/IRGen/generic_structs.sil b/test/IRGen/generic_structs.sil index 39dd44b5e1243..a90d4f825c479 100644 --- a/test/IRGen/generic_structs.sil +++ b/test/IRGen/generic_structs.sil @@ -246,9 +246,6 @@ struct GenericLayoutWithAssocType { // CHECK-LABEL: define internal swiftcc %swift.metadata_response @"$s15generic_structs26GenericLayoutWithAssocTypeVMr"( -// CHECK: [[T0:%.*]] = call{{( tail)?}} swiftcc %swift.metadata_response @swift_checkMetadataState(i64 0, %swift.type* %T) -// CHECK: [[T_CHECKED:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 - // CHECK: [[T0_GEP:%.*]] = getelementptr inbounds i8*, i8** %T.ParentHasAssociatedType, i32 1 // CHECK: [[T0:%.*]] = load i8*, i8** [[T0_GEP]] // CHECK: [[T1:%.*]] = bitcast i8* [[T0]] to i8** diff --git a/test/IRGen/generic_structs.swift b/test/IRGen/generic_structs.swift index 1eaf654c42c19..b8439fad0bf98 100644 --- a/test/IRGen/generic_structs.swift +++ b/test/IRGen/generic_structs.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -disable-type-layout -primary-file %s -emit-ir | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize -DINT=i%target-ptrsize -DINT_32=i32 +// RUN: %target-swift-frontend -disable-type-layout -primary-file %s -emit-ir struct A { @@ -37,13 +37,3 @@ public struct GenericStruct { public init() {} } - -// CHECK-LABEL: define{{.*}} swiftcc void @"$s15generic_structs13GenericStructVACyxGycfC" -// CHECK: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$s15generic_structs13GenericStructVMa"([[INT]] 0, %swift.type* %T, i8** %T.Proto) -// CHECK: [[TYPE:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 -// CHECK: [[PTR:%.*]] = bitcast %swift.type* [[TYPE]] to [[INT_32]]* -// CHECK: [[FIELDOFFSET:%.*]] = getelementptr inbounds [[INT_32]], [[INT_32]]* [[PTR]], [[INT]] [[IDX:6|10]] -// CHECK: [[OFFSET:%.*]] = load [[INT_32]], [[INT_32]]* [[FIELDOFFSET]] -// CHECK: [[ADDROFOPT:%.*]] = getelementptr inbounds i8, i8* {{.*}}, [[INT_32]] [[OFFSET]] -// CHECK: [[OPTPTR:%.*]] = bitcast i8* [[ADDROFOPT]] to %TSq* -// CHECK: call %TSq* @"$sxSg15generic_structs5ProtoRzlWOb"(%TSq* {{.*}}, %TSq* [[OPTPTR]] diff --git a/test/IRGen/generic_structs_future.sil b/test/IRGen/generic_structs_future.sil index e9b49c850e0e9..8874a61ba6509 100644 --- a/test/IRGen/generic_structs_future.sil +++ b/test/IRGen/generic_structs_future.sil @@ -247,9 +247,6 @@ struct GenericLayoutWithAssocType { // CHECK-LABEL: define internal swiftcc %swift.metadata_response @"$s22generic_structs_future26GenericLayoutWithAssocTypeVMr"( -// CHECK: [[T0:%.*]] = call{{( tail)?}} swiftcc %swift.metadata_response @swift_checkMetadataState(i64 0, %swift.type* %T) -// CHECK: [[T_CHECKED:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 - // CHECK: [[T0_GEP:%.*]] = getelementptr inbounds i8*, i8** %T.ParentHasAssociatedType, i32 1 // CHECK: [[T0:%.*]] = load i8*, i8** [[T0_GEP]] // CHECK: [[T1:%.*]] = bitcast i8* [[T0]] to i8** diff --git a/test/IRGen/opaque_result_type.swift b/test/IRGen/opaque_result_type.swift index 3a2d092961f3f..7d046aeec693e 100644 --- a/test/IRGen/opaque_result_type.swift +++ b/test/IRGen/opaque_result_type.swift @@ -195,7 +195,19 @@ struct X: R { var globalOProp: some O = 0 -struct OpaqueProps { +public struct OpaqueProps { static var staticOProp: some O = 0 var instanceOProp: some O = 0 } + +// Make sure we don't recurse indefinitely on recursive enums. +public enum RecursiveEnum { + case a(Int) + case b(String) + indirect case c(RecursiveEnum) +} + +public enum EnumWithTupleWithOpaqueField { + case a(Int) + case b((OpaqueProps, String)) +} diff --git a/test/IRGen/pre_specialize.swift b/test/IRGen/pre_specialize.swift new file mode 100644 index 0000000000000..0f3979855bc64 --- /dev/null +++ b/test/IRGen/pre_specialize.swift @@ -0,0 +1,170 @@ +// RUN: %empty-directory(%t) + +// Module A code generation. +// RUN: %target-swift-frontend -emit-ir -primary-file %S/Inputs/pre_specialize_module.swift -module-name A | %FileCheck %s -check-prefix=CHECK-A -check-prefix=CHECK-A-FRAG +// RUN: %target-swift-frontend -O -emit-ir -primary-file %S/Inputs/pre_specialize_module.swift -module-name A | %FileCheck %s -check-prefix=CHECK-A -check-prefix=CHECK-A-FRAG +// RUN: %target-swift-frontend -enable-library-evolution -emit-ir -primary-file %S/Inputs/pre_specialize_module.swift -module-name A | %FileCheck %s -check-prefix=CHECK-A -check-prefix=CHECK-A-RES +// RUN: %target-swift-frontend -O -enable-library-evolution -emit-ir -primary-file %S/Inputs/pre_specialize_module.swift -module-name A | %FileCheck %s -check-prefix=CHECK-A -check-prefix=CHECK-A-RES + +// Module B code generation with A.swiftmodule. +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -Xfrontend -validate-tbd-against-ir=missing -emit-module -emit-module-path=%t/A.swiftmodule -module-name A %S/Inputs/pre_specialize_module.swift -emit-library -o %t/%target-library-name(A) +// RUN: %target-swift-frontend -I %t -emit-ir -primary-file %S/Inputs/pre_specialize_module_B.swift -module-name B | %FileCheck %s -check-prefix=CHECK-B +// RUN: %target-swift-frontend -I %t -O -emit-ir -primary-file %S/Inputs/pre_specialize_module_B.swift -module-name B | %FileCheck %s -check-prefix=CHECK-B +// RUN: %target-build-swift -I %t -Xfrontend -validate-tbd-against-ir=missing -emit-module -emit-module-path=%t/B.swiftmodule -module-name B %S/Inputs/pre_specialize_module_B.swift -emit-library -o %t/%target-library-name(B) -L %t -lA +// RUN: %target-build-swift -swift-version 5 -I %t -Xfrontend -validate-tbd-against-ir=all -enable-library-evolution -emit-module-interface-path %t/B.swiftinterface -module-name B %S/Inputs/pre_specialize_module_B.swift -emit-library -o %t/%target-library-name(B) -L %t -lA + +// Module B code generation with A.swiftmodule with library evolution. +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -enable-library-evolution -Xfrontend -validate-tbd-against-ir=all -emit-module -emit-module-path=%t/A.swiftmodule -module-name A %S/Inputs/pre_specialize_module.swift -emit-library -o %t/%target-library-name(A) +// RUN: %target-swift-frontend -I %t -emit-ir -primary-file %S/Inputs/pre_specialize_module_B.swift -module-name B | %FileCheck %s -check-prefix=CHECK-B +// RUN: %target-swift-frontend -I %t -O -emit-ir -primary-file %S/Inputs/pre_specialize_module_B.swift -module-name B | %FileCheck %s -check-prefix=CHECK-B +// RUN: %target-build-swift -I %t -Xfrontend -validate-tbd-against-ir=missing -emit-module -emit-module-path=%t/B.swiftmodule -module-name B %S/Inputs/pre_specialize_module_B.swift -emit-library -o %t/%target-library-name(B) -L %t -lA +// RUN: %target-build-swift -swift-version 5 -I %t -Xfrontend -validate-tbd-against-ir=all -enable-library-evolution -emit-module-interface-path %t/B.swiftinterface -module-name B %S/Inputs/pre_specialize_module_B.swift -emit-library -o %t/%target-library-name(B) -L %t -lA + +// Module B code generation with A.swiftinterface with library evolution. +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -enable-library-evolution -Xfrontend -validate-tbd-against-ir=all -emit-module-interface-path %t/A.swiftinterface -module-name A %S/Inputs/pre_specialize_module.swift -emit-library -o %t/%target-library-name(A) -swift-version 5 +// RUN: %target-swift-frontend -I %t -emit-ir -primary-file %S/Inputs/pre_specialize_module_B.swift -module-name B | %FileCheck %s -check-prefix=CHECK-B +// RUN: %target-swift-frontend -I %t -O -emit-ir -primary-file %S/Inputs/pre_specialize_module_B.swift -module-name B | %FileCheck %s -check-prefix=CHECK-B +// RUN: %target-build-swift -I %t -Xfrontend -validate-tbd-against-ir=missing -emit-module -emit-module-path=%t/B.swiftmodule -module-name B %S/Inputs/pre_specialize_module_B.swift -emit-library -o %t/%target-library-name(B) -L %t -lA +// RUN: %target-build-swift -swift-version 5 -I %t -Xfrontend -validate-tbd-against-ir=all -enable-library-evolution -emit-module-interface-path %t/B.swiftinterface -module-name B %S/Inputs/pre_specialize_module_B.swift -emit-library -o %t/%target-library-name(B) -L %t -lA + +// Module A tests +// -------------- + +// specialized InternalThing.compute() +// CHECK-A-FRAG: define{{( dllexport)?}}{{( protected)?}} swiftcc [[INT:(i64|i32)]] @"$s1A13InternalThingV7computexyFAA09Resilienta5BoxedB0VySiG_Ts5"({{(i64|i32)}}{{( returned)?}} %0) +// CHECK-A-RES: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s1A13InternalThingV7computexyFAA09Resilienta5BoxedB0VySiG_Ts5"(%T1A27ResilientInternalBoxedThingVySiG* noalias nocapture sret %0, [[INT:(i64|i32)]] %1) +// CHECK-A: define{{( dllexport)?}}{{( protected)?}} swiftcc i1 @"$s1A13InternalThingV7computexyFSb_Ts5"(i1{{( returned)?}} %0) +// CHECK-A: define{{( dllexport)?}}{{( protected)?}} swiftcc [[INT]] @"$s1A13InternalThingV7computexyFSi_Ts5"([[INT]]{{( returned)?}} %0) + +// specialized InternalThing.computedX.getter +// CHECK-A: define{{( dllexport)?}}{{( protected)?}} swiftcc [[INT]] @"$s1A13InternalThingV9computedXxvgSi_Ts5"([[INT]]{{( returned)?}} %0) +// specialized InternalThing.computedX.setter +// CHECK-A: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s1A13InternalThingV9computedXxvsSi_Ts5"([[INT]] %0, %T1A13InternalThingVySiG* nocapture swiftself dereferenceable({{(4|8)}}) %1) + +// specialized InternalThing.subscript.getter +// CHECK-A: define{{( dllexport)?}}{{( protected)?}} swiftcc [[INT]] @"$s1A13InternalThingVyxSicigSi_Ts5"([[INT]] %0, [[INT]]{{( returned)?}} %1) +// specialized InternalThing.subscript.setter +// CHECK-A: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s1A13InternalThingVyxSicisSi_Ts5"([[INT]] %0, [[INT]] %1, %T1A13InternalThingVySiG* nocapture swiftself dereferenceable({{(4|8)}}) %2) + +// specialized InternalRef.compute() +// CHECK-A-FRAG: define{{( dllexport)?}}{{( protected)?}} swiftcc [[INT:(i64|i32)]] @"$s1A11InternalRefC7computexyFAA09ResilientA10BoxedThingVySiG_Ts5" +// CHECK-A-RES: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s1A11InternalRefC7computexyFAA09ResilientA10BoxedThingVySiG_Ts5" +// CHECK-A: define{{( dllexport)?}}{{( protected)?}} swiftcc i1 @"$s1A11InternalRefC7computexyFSb_Ts5" +// CHECK-A: define{{( dllexport)?}}{{( protected)?}} swiftcc [[INT]] @"$s1A11InternalRefC7computexyFSi_Ts5" + +// specialized InternalRef.computedX.getter +// CHECK-A-DAG: define{{( dllexport)?}}{{( protected)?}} swiftcc [[INT]] @"$s1A11InternalRefC9computedXxvgSi_Ts5" +// specialized InternalRef.computedX.setter +// CHECK-A-DAG: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s1A11InternalRefC9computedXxvsSi_Ts5" + +// specialized InternalRef.subscript.getter +// CHECK-A-DAG: define{{( dllexport)?}}{{( protected)?}} swiftcc [[INT]] @"$s1A11InternalRefCyxSicigSi_Ts5" +// specialized InternalRef.subscript.setter +// CHECK-A-DAG: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s1A11InternalRefCyxSicisSi_Ts5" + +// Module B tests +// -------------- + +// specialized InternalThing.compute() +// CHECK-B-DAG: define{{( dllexport)?}}{{( protected)?}} swiftcc {{(%T1B12AnotherThingC\*|void)}} @"$s1A13InternalThingV7computexyFAA09Resilienta5BoxedB0Vy1B07AnotherB0CG_Ts5" +// CHECK-B-DAG: define{{( dllexport)?}}{{( protected)?}} swiftcc %T1B12AnotherThingC* @"$s1A13InternalThingV7computexyF1B07AnotherB0C_Ts5"(%T1B12AnotherThingC*{{( returned)?}} %0) + +// specialized InternalThing.computedX.getter +// CHECK-B-DAG: define{{( dllexport)?}}{{( protected)?}} swiftcc %T1B12AnotherThingC* @"$s1A13InternalThingV9computedXxvg1B07AnotherB0C_Ts5"(%T1B12AnotherThingC*{{( returned)?}} %0) + +// specialized InternalThing.computedX.setter +// CHECK-B-DAG: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s1A13InternalThingV9computedXxvs1B07AnotherB0C_Ts5"(%T1B12AnotherThingC* %0, %T1A13InternalThingVy1B07AnotherB0CG* nocapture swiftself dereferenceable({{(4|8)}}) %1) + +// specialized InternalThing.subscript.getter +// CHECK-B-DAG: define{{( dllexport)?}}{{( protected)?}} swiftcc %T1B12AnotherThingC* @"$s1A13InternalThingVyxSicig1B07AnotherB0C_Ts5"([[INT:(i64|i32)]] %0, %T1B12AnotherThingC*{{( returned)?}} %1) + +// specialized InternalThing.subscript.setter +// CHECK-B-DAG: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s1A13InternalThingVyxSicis1B07AnotherB0C_Ts5"(%T1B12AnotherThingC* %0, [[INT]] %1, %T1A13InternalThingVy1B07AnotherB0CG* nocapture swiftself dereferenceable({{(4|8)}}) %2) + +// specialized InternalRef.compute() +// CHECK-B-DAG: define{{( dllexport)?}}{{( protected)?}} swiftcc %T1B12AnotherThingC* @"$s1A11InternalRefC7computexyF1B12AnotherThingC_Ts5 + +// specialized InternalRef.computedX.getter +// CHECK-B-DAG: define{{( dllexport)?}}{{( protected)?}} swiftcc %T1B12AnotherThingC* @"$s1A11InternalRefC9computedXxvg1B12AnotherThingC_Ts5" + +// specialized InternalRef.computedX.setter +// CHECK-B-DAG: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s1A11InternalRefC9computedXxvs1B12AnotherThingC_Ts5" + +// specialized InternalRef.subscript.getter +// CHECK-B-DAG: define{{( dllexport)?}}{{( protected)?}} swiftcc %T1B12AnotherThingC* @"$s1A11InternalRefCyxSicig1B12AnotherThingC_Ts5" + +// specialized InternalRef.subscript.setter +// CHECK-B-DAG: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s1A11InternalRefCyxSicis1B12AnotherThingC_Ts5" + + +// Test pre-specialized use. + +// Fragile .swiftmodule +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -emit-module -emit-module-path=%t/A.swiftmodule -module-name A %S/Inputs/pre_specialize_module.swift +// RUN: %target-build-swift -I %t -emit-module -emit-module-path=%t/B.swiftmodule -module-name B %S/Inputs/pre_specialize_module_B.swift +// RUN: %target-swift-frontend -O -I %t -emit-ir -primary-file %s -module-name C | %FileCheck %s -check-prefix=CHECK-C + +// Fragile optimized .swiftmodule +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -O -emit-module -emit-module-path=%t/A.swiftmodule -module-name A %S/Inputs/pre_specialize_module.swift +// RUN: %target-build-swift -O -I %t -emit-module -emit-module-path=%t/B.swiftmodule -module-name B %S/Inputs/pre_specialize_module_B.swift +// RUN: %target-swift-frontend -O -I %t -emit-ir -primary-file %s -module-name C | %FileCheck %s -check-prefix=CHECK-C + +// Resilient .swiftmodule +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -enable-library-evolution -emit-module -emit-module-path=%t/A.swiftmodule -module-name A %S/Inputs/pre_specialize_module.swift +// RUN: %target-build-swift -enable-library-evolution -I %t -emit-module -emit-module-path=%t/B.swiftmodule -module-name B %S/Inputs/pre_specialize_module_B.swift +// RUN: %target-swift-frontend -O -I %t -emit-ir -primary-file %s -module-name C | %FileCheck %s -check-prefix=CHECK-C + +// Resilient optimized .swiftmodule +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -O -enable-library-evolution -emit-module -emit-module-path=%t/A.swiftmodule -module-name A %S/Inputs/pre_specialize_module.swift +// RUN: %target-build-swift -O -enable-library-evolution -I %t -emit-module -emit-module-path=%t/B.swiftmodule -module-name B %S/Inputs/pre_specialize_module_B.swift +// RUN: %target-swift-frontend -O -I %t -emit-ir -primary-file %s -module-name C | %FileCheck %s -check-prefix=CHECK-C + +// .swiftinterface +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -c -enable-library-evolution -emit-module-interface-path %t/A.swiftinterface -module-name A %S/Inputs/pre_specialize_module.swift -o %t/A.o -swift-version 5 +// RUN: %target-build-swift -c -enable-library-evolution -I %t -emit-module-interface-path %t/B.swiftinterface -module-name B %S/Inputs/pre_specialize_module_B.swift -o %t/B.o -swift-version 5 +// RUN: %target-swift-frontend -O -I %t -emit-ir -primary-file %s -module-name C | %FileCheck %s -check-prefix=CHECK-C + +// Optimized .swiftinterface +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -O -c -enable-library-evolution -emit-module-interface-path %t/A.swiftinterface -module-name A %S/Inputs/pre_specialize_module.swift -o %t/A.o -swift-version 5 +// RUN: %target-build-swift -O -c -enable-library-evolution -I %t -emit-module-interface-path %t/B.swiftinterface -module-name B %S/Inputs/pre_specialize_module_B.swift -o %t/B.o -swift-version 5 +// RUN: %target-swift-frontend -O -I %t -emit-ir -primary-file %s -module-name C | %FileCheck %s -check-prefix=CHECK-C + +import A +import B + + +public func testPrespecializedUse() { + + // Test pre-specialization in library A. + + // CHECK-C-LABEL: define{{.*}}s1A18testSpecializationyyxlFSi_Tg5 + // CHECK-C: call{{.*}}s1A13InternalThingV7computexyFAA09Resilienta5BoxedB0VySiG_Ts5 + // CHECK-C: call{{.*}}s1A13InternalThingV7computexyFSi_Ts5 + // CHECK-C: call{{.*}}s1A13InternalThingV9computedXxvsSi_Ts5 + // CHECK-C: call{{.*}}s1A13InternalThingV9computedXxvgSi_Ts5 + // CHECK-C: call{{.*}}s1A13InternalThingVyxSicisSi_Ts5 + // CHECK-C: call{{.*}}s1A13InternalThingVyxSicigSi_Ts5 + + testSpecialization(5) + + // Test pre-specialization in library B. + + // CHECK-C-LABEL: define{{.*}}s1A18testSpecializationyyxlF1B12AnotherThingC_Tg5 + // CHECK-C: call{{.*}}s1A13InternalThingV7computexyFAA09Resilienta5BoxedB0Vy1B07AnotherB0CG_Ts5 + // CHECK-C: call{{.*}}s1A13InternalThingV7computexyF1B07AnotherB0C_Ts5 + // CHECK-C: call{{.*}}s1A13InternalThingV9computedXxvs1B07AnotherB0C_Ts5 + // CHECK-C: call{{.*}}s1A13InternalThingV9computedXxvg1B07AnotherB0C_Ts5 + // CHECK-C: call{{.*}}s1A13InternalThingVyxSicis1B07AnotherB0C_Ts5 + // CHECK-C: call{{.*}}s1A13InternalThingVyxSicig1B07AnotherB0C_Ts5 + + testSpecialization(AnotherThing()) +} diff --git a/test/IRGen/ptrauth-actor.swift b/test/IRGen/ptrauth-actor.swift new file mode 100644 index 0000000000000..6bb2c23d8ed39 --- /dev/null +++ b/test/IRGen/ptrauth-actor.swift @@ -0,0 +1,23 @@ +// RUN: %swift -swift-version 5 -target arm64e-apple-macos11.0 -parse-stdlib %s -emit-ir -disable-llvm-optzns -enable-experimental-concurrency -o - | %FileCheck %s + +// REQUIRES: CODEGENERATOR=ARM +// REQUIRES: concurrency +// REQUIRES: CPU=arm64e +// REQUIRES: OS=macosx + +import Swift + +public actor class A1 { + var x: Int = 17 +} + +open actor class A3: Actor { + open func f() { } +} + +// enqueue(partialTask:) has the same discriminator across all classes. +// CHECK: s4main2A1C7enqueue11partialTasky12_Concurrency012PartialAsyncE0V_tF.ptrauth{{.*}}i64 36669 + +// CHECK: @"$s4main2A3CyxG12_Concurrency5ActorAaeFP7enqueue11partialTaskyAE012PartialAsyncG0V_tFTW" +// CHECK-NOT: ret void +// CHECK: call i64 @llvm.ptrauth.blend.i64(i64 %{{[0-9]+}}, i64 36669) diff --git a/test/IRGen/ptrauth-protocols.sil b/test/IRGen/ptrauth-protocols.sil index 50a1e250c4964..eea3a5916c9e9 100644 --- a/test/IRGen/ptrauth-protocols.sil +++ b/test/IRGen/ptrauth-protocols.sil @@ -71,6 +71,7 @@ bb0: // CHECK-LABEL: define swiftcc void @test_accesses(%swift.type* %T, i8** %T.Q) // Fetch T.Assoc. // CHECK: %T.Assoc = extractvalue %swift.metadata_response [[TMP:%.*]], 0 +// CHECK-NEXT: {{%.*}} = extractvalue %swift.metadata_response [[TMP]], 1 // Fetch T.Assoc : P. // CHECK-NEXT: %T.Assoc.P = call swiftcc i8** @swift_getAssociatedConformanceWitness(i8** %T.Q, %swift.type* %T, %swift.type* %T.Assoc // Fetch T.Assoc.foo @@ -79,7 +80,10 @@ bb0: // CHECK-NEXT: [[FOO:%.*]] = bitcast i8* [[T1]] to void (%swift.type*, %swift.type*, i8**)* // CHECK-NEXT: [[T1:%.*]] = ptrtoint i8** [[T0]] to i64 // CHECK-NEXT: [[DISC:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 [[T1]], i64 53700) -// CHECK-NEXT: call swiftcc void [[FOO]](%swift.type* swiftself %T.Assoc, %swift.type* %T.Assoc, i8** %T.Assoc.P) [ "ptrauth"(i32 0, i64 [[DISC]]) ] +// TODO: be smart about this and do a complete-metadata fetch in the first place +// CHECK-NEXT: [[ASSOC_RESPONSE:%.*]] = call swiftcc %swift.metadata_response @swift_checkMetadataState(i64 0, %swift.type* %T.Assoc) +// CHECK-NEXT: [[ASSOC:%.*]] = extractvalue %swift.metadata_response [[ASSOC_RESPONSE]], 0 +// CHECK-NEXT: call swiftcc void [[FOO]](%swift.type* swiftself [[ASSOC]], %swift.type* [[ASSOC]], i8** %T.Assoc.P) [ "ptrauth"(i32 0, i64 [[DISC]]) ] // CHECK-NEXT: ret void sil @use_conformances : $@convention(thin) () -> () { diff --git a/test/IRGen/same_type_constraints.swift b/test/IRGen/same_type_constraints.swift index a3ac8a52c7a99..17ba3f5161332 100644 --- a/test/IRGen/same_type_constraints.swift +++ b/test/IRGen/same_type_constraints.swift @@ -67,7 +67,7 @@ where Self : CodingType, print(Self.ValueType.self) } -// 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: define internal swiftcc i8** @"$s21same_type_constraints12GenericKlazzCyxq_GAA1EAA4Data_AA0F4TypePWT"(%swift.type* readnone %"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. diff --git a/test/IRGen/sanitize_coverage.swift b/test/IRGen/sanitize_coverage.swift index 3114e5a7c7040..b7444d2d1a57d 100644 --- a/test/IRGen/sanitize_coverage.swift +++ b/test/IRGen/sanitize_coverage.swift @@ -14,7 +14,7 @@ #elseif canImport(Glibc) import Glibc #elseif os(Windows) - import MSVCRT + import CRT #else #error("Unsupported platform") #endif diff --git a/test/ImportResolution/import-command-line.swift b/test/ImportResolution/import-command-line.swift index 8a305450578b5..1f0012bdefd4e 100644 --- a/test/ImportResolution/import-command-line.swift +++ b/test/ImportResolution/import-command-line.swift @@ -7,5 +7,8 @@ // RUN: not %target-swift-frontend -typecheck %s -enable-source-import -I %S/Inputs -sdk "" -import-module NON_EXISTENT 2>&1 | %FileCheck -check-prefix=NON-EXISTENT %s // NON-EXISTENT: error: no such module 'NON_EXISTENT' +// RUN: not %target-swift-frontend -typecheck %s -enable-source-import -I %S/Inputs -sdk "" -testable-import-module abcde 2>&1 | %FileCheck -check-prefix=NON-TESTABLE %s +// NON-TESTABLE: error: module 'abcde' was not compiled for testing + var a: A? // expected-error {{cannot find type 'A' in scope}} var qA: abcde.A? // expected-error {{cannot find type 'abcde' in scope}} diff --git a/test/Incremental/CrossModule/Inputs/external-cascade/A.json b/test/Incremental/CrossModule/Inputs/external-cascade/A.json new file mode 100644 index 0000000000000..c332d75dc3eb6 --- /dev/null +++ b/test/Incremental/CrossModule/Inputs/external-cascade/A.json @@ -0,0 +1,11 @@ +{ + "A.swift": { + "object": "./A.o", + "swift-dependencies": "./A.swiftdeps", + "swiftmodule": "./A~partial.swiftmodule", + "swiftdoc": "./A.swiftdoc" + }, + "": { + "swift-dependencies": "./A~buildrecord.swiftdeps" + } +} diff --git a/test/Incremental/CrossModule/Inputs/external-cascade/A.swift b/test/Incremental/CrossModule/Inputs/external-cascade/A.swift new file mode 100644 index 0000000000000..24372998c4adf --- /dev/null +++ b/test/Incremental/CrossModule/Inputs/external-cascade/A.swift @@ -0,0 +1,3 @@ +import B + +public let fromA = fromB diff --git a/test/Incremental/CrossModule/Inputs/external-cascade/B.json b/test/Incremental/CrossModule/Inputs/external-cascade/B.json new file mode 100644 index 0000000000000..d367596bbde2c --- /dev/null +++ b/test/Incremental/CrossModule/Inputs/external-cascade/B.json @@ -0,0 +1,11 @@ +{ + "B.swift": { + "object": "./B.o", + "swift-dependencies": "./B.swiftdeps", + "swiftmodule": "./B~partial.swiftmodule", + "swiftdoc": "./B.swiftdoc" + }, + "": { + "swift-dependencies": "./B~buildrecord.swiftdeps" + } +} diff --git a/test/Incremental/CrossModule/Inputs/external-cascade/B.swift b/test/Incremental/CrossModule/Inputs/external-cascade/B.swift new file mode 100644 index 0000000000000..12f93a40df9a3 --- /dev/null +++ b/test/Incremental/CrossModule/Inputs/external-cascade/B.swift @@ -0,0 +1,3 @@ +import C + +public let fromB = fromC diff --git a/test/Incremental/CrossModule/Inputs/external-cascade/C.json b/test/Incremental/CrossModule/Inputs/external-cascade/C.json new file mode 100644 index 0000000000000..a0b5b832851a2 --- /dev/null +++ b/test/Incremental/CrossModule/Inputs/external-cascade/C.json @@ -0,0 +1,11 @@ +{ + "C.swift": { + "object": "./C.o", + "swift-dependencies": "./C.swiftdeps", + "swiftmodule": "./C~partial.swiftmodule", + "swiftdoc": "./C.swiftdoc" + }, + "": { + "swift-dependencies": "./C~buildrecord.swiftdeps" + } +} diff --git a/test/Incremental/CrossModule/Inputs/external-cascade/C.swift b/test/Incremental/CrossModule/Inputs/external-cascade/C.swift new file mode 100644 index 0000000000000..e9ee5ef0dceef --- /dev/null +++ b/test/Incremental/CrossModule/Inputs/external-cascade/C.swift @@ -0,0 +1 @@ +public let fromC = ASymbolFromAHeader + ASymbolFromAnotherHeader diff --git a/test/Incremental/CrossModule/Inputs/external-cascade/another-header.h b/test/Incremental/CrossModule/Inputs/external-cascade/another-header.h new file mode 100644 index 0000000000000..b69f5a05a1182 --- /dev/null +++ b/test/Incremental/CrossModule/Inputs/external-cascade/another-header.h @@ -0,0 +1 @@ +int ASymbolFromAnotherHeader = 43; diff --git a/test/Incremental/CrossModule/Inputs/external-cascade/bridging-header.h b/test/Incremental/CrossModule/Inputs/external-cascade/bridging-header.h new file mode 100644 index 0000000000000..94fc89f7c37b4 --- /dev/null +++ b/test/Incremental/CrossModule/Inputs/external-cascade/bridging-header.h @@ -0,0 +1,2 @@ +#include "another-header.h" +int ASymbolFromAHeader = 42; diff --git a/test/Incremental/CrossModule/Inputs/linear/A.json b/test/Incremental/CrossModule/Inputs/linear/A.json new file mode 100644 index 0000000000000..c332d75dc3eb6 --- /dev/null +++ b/test/Incremental/CrossModule/Inputs/linear/A.json @@ -0,0 +1,11 @@ +{ + "A.swift": { + "object": "./A.o", + "swift-dependencies": "./A.swiftdeps", + "swiftmodule": "./A~partial.swiftmodule", + "swiftdoc": "./A.swiftdoc" + }, + "": { + "swift-dependencies": "./A~buildrecord.swiftdeps" + } +} diff --git a/test/Incremental/CrossModule/Inputs/linear/A.swift b/test/Incremental/CrossModule/Inputs/linear/A.swift new file mode 100644 index 0000000000000..720de06824628 --- /dev/null +++ b/test/Incremental/CrossModule/Inputs/linear/A.swift @@ -0,0 +1 @@ +import B diff --git a/test/Incremental/CrossModule/Inputs/linear/B.json b/test/Incremental/CrossModule/Inputs/linear/B.json new file mode 100644 index 0000000000000..d367596bbde2c --- /dev/null +++ b/test/Incremental/CrossModule/Inputs/linear/B.json @@ -0,0 +1,11 @@ +{ + "B.swift": { + "object": "./B.o", + "swift-dependencies": "./B.swiftdeps", + "swiftmodule": "./B~partial.swiftmodule", + "swiftdoc": "./B.swiftdoc" + }, + "": { + "swift-dependencies": "./B~buildrecord.swiftdeps" + } +} diff --git a/test/Incremental/CrossModule/Inputs/linear/B.swift b/test/Incremental/CrossModule/Inputs/linear/B.swift new file mode 100644 index 0000000000000..14f2e9a84b5c5 --- /dev/null +++ b/test/Incremental/CrossModule/Inputs/linear/B.swift @@ -0,0 +1,5 @@ +import C + +public func fromB() { + return fromC() +} diff --git a/test/Incremental/CrossModule/Inputs/linear/C.json b/test/Incremental/CrossModule/Inputs/linear/C.json new file mode 100644 index 0000000000000..a0b5b832851a2 --- /dev/null +++ b/test/Incremental/CrossModule/Inputs/linear/C.json @@ -0,0 +1,11 @@ +{ + "C.swift": { + "object": "./C.o", + "swift-dependencies": "./C.swiftdeps", + "swiftmodule": "./C~partial.swiftmodule", + "swiftdoc": "./C.swiftdoc" + }, + "": { + "swift-dependencies": "./C~buildrecord.swiftdeps" + } +} diff --git a/test/Incremental/CrossModule/Inputs/linear/C.swift b/test/Incremental/CrossModule/Inputs/linear/C.swift new file mode 100644 index 0000000000000..d06ab94ab007c --- /dev/null +++ b/test/Incremental/CrossModule/Inputs/linear/C.swift @@ -0,0 +1,7 @@ +#if OLD +public func fromC() {} +#elseif NEW +public func fromC(parameter: Int = 0) {} +#else +#error("test must define one of NEW or OLD macros") +#endif diff --git a/test/Incremental/CrossModule/Inputs/transitive/A.json b/test/Incremental/CrossModule/Inputs/transitive/A.json new file mode 100644 index 0000000000000..c332d75dc3eb6 --- /dev/null +++ b/test/Incremental/CrossModule/Inputs/transitive/A.json @@ -0,0 +1,11 @@ +{ + "A.swift": { + "object": "./A.o", + "swift-dependencies": "./A.swiftdeps", + "swiftmodule": "./A~partial.swiftmodule", + "swiftdoc": "./A.swiftdoc" + }, + "": { + "swift-dependencies": "./A~buildrecord.swiftdeps" + } +} diff --git a/test/Incremental/CrossModule/Inputs/transitive/A.swift b/test/Incremental/CrossModule/Inputs/transitive/A.swift new file mode 100644 index 0000000000000..84d2bead2d914 --- /dev/null +++ b/test/Incremental/CrossModule/Inputs/transitive/A.swift @@ -0,0 +1,9 @@ +import B +import C + +public func use() { + fromB() + #if USEC + fromC() + #endif +} diff --git a/test/Incremental/CrossModule/Inputs/transitive/B.json b/test/Incremental/CrossModule/Inputs/transitive/B.json new file mode 100644 index 0000000000000..d367596bbde2c --- /dev/null +++ b/test/Incremental/CrossModule/Inputs/transitive/B.json @@ -0,0 +1,11 @@ +{ + "B.swift": { + "object": "./B.o", + "swift-dependencies": "./B.swiftdeps", + "swiftmodule": "./B~partial.swiftmodule", + "swiftdoc": "./B.swiftdoc" + }, + "": { + "swift-dependencies": "./B~buildrecord.swiftdeps" + } +} diff --git a/test/Incremental/CrossModule/Inputs/transitive/B.swift b/test/Incremental/CrossModule/Inputs/transitive/B.swift new file mode 100644 index 0000000000000..14f2e9a84b5c5 --- /dev/null +++ b/test/Incremental/CrossModule/Inputs/transitive/B.swift @@ -0,0 +1,5 @@ +import C + +public func fromB() { + return fromC() +} diff --git a/test/Incremental/CrossModule/Inputs/transitive/C.json b/test/Incremental/CrossModule/Inputs/transitive/C.json new file mode 100644 index 0000000000000..a0b5b832851a2 --- /dev/null +++ b/test/Incremental/CrossModule/Inputs/transitive/C.json @@ -0,0 +1,11 @@ +{ + "C.swift": { + "object": "./C.o", + "swift-dependencies": "./C.swiftdeps", + "swiftmodule": "./C~partial.swiftmodule", + "swiftdoc": "./C.swiftdoc" + }, + "": { + "swift-dependencies": "./C~buildrecord.swiftdeps" + } +} diff --git a/test/Incremental/CrossModule/Inputs/transitive/C.swift b/test/Incremental/CrossModule/Inputs/transitive/C.swift new file mode 100644 index 0000000000000..d06ab94ab007c --- /dev/null +++ b/test/Incremental/CrossModule/Inputs/transitive/C.swift @@ -0,0 +1,7 @@ +#if OLD +public func fromC() {} +#elseif NEW +public func fromC(parameter: Int = 0) {} +#else +#error("test must define one of NEW or OLD macros") +#endif diff --git a/test/Incremental/CrossModule/external-cascade.swift b/test/Incremental/CrossModule/external-cascade.swift new file mode 100644 index 0000000000000..bd6d907d55808 --- /dev/null +++ b/test/Incremental/CrossModule/external-cascade.swift @@ -0,0 +1,62 @@ +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/external-cascade/* %t + +// +// This test establishes a chain of modules that all depend on a set of +// bridging headers. This test ensures that changes to external dependencies - +// especially those that aren't directly imported - cause incremental rebuilds. +// +// |bridging-header.h| - Module C Module B Module A +// ^ -------- -> -------- -> -------- +// | | ^ +// | | | +// |another-header.h| ------------------------ + +// +// Set up a clean incremental build of all three modules +// + +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -import-objc-header %t/bridging-header.h -Xfrontend -validate-tbd-against-ir=none -driver-show-incremental -driver-show-job-lifecycle C.swift +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle B.swift +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle A.swift + +// +// Now change a header and ensure that the rebuild cascades outwards +// + +// RUN: rm %t/another-header.h +// RUN: cp %S/Inputs/external-cascade/another-header.h %t/another-header.h +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -import-objc-header %t/bridging-header.h -Xfrontend -validate-tbd-against-ir=none -driver-show-incremental -driver-show-job-lifecycle C.swift 2>&1 | %FileCheck -check-prefix MODULE-C %s +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle B.swift 2>&1 | %FileCheck -check-prefix MODULE-B %s +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle A.swift 2>&1 | %FileCheck -check-prefix MODULE-A %s + +// MODULE-C: Job finished: {generate-pch: bridging-header-[[BRIDGING_HEADER:.*]].pch <= bridging-header.h} +// MODULE-C: Job finished: {compile: C.o <= C.swift bridging-header-[[BRIDGING_HEADER]].pch} +// MODULE-C: Job finished: {merge-module: C.swiftmodule <= C.o} + +// MODULE-B: Queuing because of external dependencies: {compile: B.o <= B.swift} +// MODULE-B: Job finished: {compile: B.o <= B.swift} +// MODULE-B: Job finished: {merge-module: B.swiftmodule <= B.o} + + +// MODULE-A: Queuing because of external dependencies: {compile: A.o <= A.swift} +// MODULE-A: Job finished: {compile: A.o <= A.swift} +// MODULE-A: Job finished: {merge-module: A.swiftmodule <= A.o} + +// +// And ensure that the null build really is null. +// + +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -import-objc-header %t/bridging-header.h -Xfrontend -validate-tbd-against-ir=none -driver-show-incremental -driver-show-job-lifecycle C.swift 2>&1 | %FileCheck -check-prefix MODULE-C-NULL %s +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle B.swift 2>&1 | %FileCheck -check-prefix MODULE-B-NULL %s +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle A.swift 2>&1 | %FileCheck -check-prefix MODULE-A-NULL %s + +// MODULE-C-NULL: Job finished: {generate-pch: bridging-header-[[BRIDGING_HEADER:.*]].pch <= bridging-header.h} +// MODULE-C-NULL: Job skipped: {compile: C.o <= C.swift bridging-header-[[BRIDGING_HEADER]].pch} +// MODULE-C-NULL: Job skipped: {merge-module: C.swiftmodule <= C.o} + +// MODULE-B-NULL: Job skipped: {compile: B.o <= B.swift} +// MODULE-B-NULL: Job skipped: {merge-module: B.swiftmodule <= B.o} + +// MODULE-A-NULL: Job skipped: {compile: A.o <= A.swift} +// MODULE-A-NULL: Job skipped: {merge-module: A.swiftmodule <= A.o} diff --git a/test/Incremental/CrossModule/linear.swift b/test/Incremental/CrossModule/linear.swift new file mode 100644 index 0000000000000..1679cd5213496 --- /dev/null +++ b/test/Incremental/CrossModule/linear.swift @@ -0,0 +1,55 @@ +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/linear/* %t + +// +// This test establishes a "linear" chain of modules that import one another +// and ensures that a cross-module incremental build does not needlessly +// rebuild the transitive dependency. +// +// Module C Module B Module A +// -------- -> -------- -> -------- +// | ^ +// | | +// ------------X------------ + +// +// Set up a clean incremental build of all three modules +// + +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DOLD C.swift +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle B.swift +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle A.swift + +// +// Now change C and ensure that B rebuilds but A does not +// + +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DNEW C.swift 2>&1 | %FileCheck -check-prefix MODULE-C %s +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle B.swift 2>&1 | %FileCheck -check-prefix MODULE-B %s +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle A.swift 2>&1 | %FileCheck -check-prefix MODULE-A %s + +// MODULE-C: Incremental compilation has been disabled + +// MODULE-B: Queuing because of incremental external dependencies: {compile: B.o <= B.swift} +// MODULE-B: Job finished: {compile: B.o <= B.swift} +// MODULE-B: Job finished: {merge-module: B.swiftmodule <= B.o} + +// MODULE-A: Job skipped: {compile: A.o <= A.swift} +// MODULE-A: Job skipped: {merge-module: A.swiftmodule <= A.o} + +// +// And ensure that the null build really is null. +// + +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DNEW C.swift 2>&1 | %FileCheck -check-prefix MODULE-C-NULL %s +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle B.swift 2>&1 | %FileCheck -check-prefix MODULE-B-NULL %s +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle A.swift 2>&1 | %FileCheck -check-prefix MODULE-A-NULL %s + +// MODULE-C-NULL: Job skipped: {compile: C.o <= C.swift} +// MODULE-C-NULL: Job skipped: {merge-module: C.swiftmodule <= C.o} + +// MODULE-B-NULL: Job skipped: {compile: B.o <= B.swift} +// MODULE-B-NULL: Job skipped: {merge-module: B.swiftmodule <= B.o} + +// MODULE-A-NULL: Job skipped: {compile: A.o <= A.swift} +// MODULE-A-NULL: Job skipped: {merge-module: A.swiftmodule <= A.o} diff --git a/test/Incremental/CrossModule/transitive.swift b/test/Incremental/CrossModule/transitive.swift new file mode 100644 index 0000000000000..6c585d1fbf63d --- /dev/null +++ b/test/Incremental/CrossModule/transitive.swift @@ -0,0 +1,58 @@ +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/transitive/* %t + +// +// This test establishes a "transitive" chain of modules that import one another +// and ensures that a cross-module incremental build rebuilds all modules +// involved in the chain. +// +// Module C Module B Module A +// -------- -> -------- -> -------- +// | ^ +// | | +// ------------------------- +// + +// +// Set up a clean incremental build of all three modules +// + +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC -DOLD C.swift +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC B.swift +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC A.swift + +// +// Now change C and ensure that B and A rebuild because of the change to +// an incremental external dependency. +// + +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC -DNEW C.swift 2>&1 | %FileCheck -check-prefix MODULE-C %s +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC B.swift 2>&1 | %FileCheck -check-prefix MODULE-B %s +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC A.swift 2>&1 | %FileCheck -check-prefix MODULE-A %s + +// MODULE-C: Incremental compilation has been disabled + +// MODULE-B: Queuing because of incremental external dependencies: {compile: B.o <= B.swift} +// MODULE-B: Job finished: {compile: B.o <= B.swift} +// MODULE-B: Job finished: {merge-module: B.swiftmodule <= B.o} + +// MODULE-A: Queuing because of incremental external dependencies: {compile: A.o <= A.swift} +// MODULE-A: Job finished: {compile: A.o <= A.swift} +// MODULE-A: Job finished: {merge-module: A.swiftmodule <= A.o} + +// +// And ensure that the null build really is null. +// + +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC -DNEW C.swift 2>&1 | %FileCheck -check-prefix MODULE-C-NULL %s +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC B.swift 2>&1 | %FileCheck -check-prefix MODULE-B-NULL %s +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC A.swift 2>&1 | %FileCheck -check-prefix MODULE-A-NULL %s + +// MODULE-C-NULL: Job skipped: {compile: C.o <= C.swift} +// MODULE-C-NULL: Job skipped: {merge-module: C.swiftmodule <= C.o} + +// MODULE-B-NULL: Job skipped: {compile: B.o <= B.swift} +// MODULE-B-NULL: Job skipped: {merge-module: B.swiftmodule <= B.o} + +// MODULE-A-NULL: Job skipped: {compile: A.o <= A.swift} +// MODULE-A-NULL: Job skipped: {merge-module: A.swiftmodule <= A.o} diff --git a/test/Incremental/PrivateDependencies/Inputs/InterestingType.swift b/test/Incremental/Dependencies/Inputs/InterestingType.swift similarity index 100% rename from test/Incremental/PrivateDependencies/Inputs/InterestingType.swift rename to test/Incremental/Dependencies/Inputs/InterestingType.swift diff --git a/test/Incremental/PrivateDependencies/private-function-fine.swift b/test/Incremental/Dependencies/function-fine.swift similarity index 100% rename from test/Incremental/PrivateDependencies/private-function-fine.swift rename to test/Incremental/Dependencies/function-fine.swift diff --git a/test/Incremental/PrivateDependencies/private-function-return-type-fine.swift b/test/Incremental/Dependencies/function-return-type-fine.swift similarity index 100% rename from test/Incremental/PrivateDependencies/private-function-return-type-fine.swift rename to test/Incremental/Dependencies/function-return-type-fine.swift diff --git a/test/Incremental/PrivateDependencies/private-protocol-conformer-ext-fine.swift b/test/Incremental/Dependencies/protocol-conformer-ext-fine.swift similarity index 100% rename from test/Incremental/PrivateDependencies/private-protocol-conformer-ext-fine.swift rename to test/Incremental/Dependencies/protocol-conformer-ext-fine.swift diff --git a/test/Incremental/PrivateDependencies/private-protocol-conformer-fine.swift b/test/Incremental/Dependencies/protocol-conformer-fine.swift similarity index 100% rename from test/Incremental/PrivateDependencies/private-protocol-conformer-fine.swift rename to test/Incremental/Dependencies/protocol-conformer-fine.swift diff --git a/test/Incremental/PrivateDependencies/reference-dependencies-consistency-fine.swift b/test/Incremental/Dependencies/reference-dependencies-consistency-fine.swift similarity index 100% rename from test/Incremental/PrivateDependencies/reference-dependencies-consistency-fine.swift rename to test/Incremental/Dependencies/reference-dependencies-consistency-fine.swift diff --git a/test/Incremental/PrivateDependencies/reference-dependencies-dynamic-lookup-fine.swift b/test/Incremental/Dependencies/reference-dependencies-dynamic-lookup-fine.swift similarity index 100% rename from test/Incremental/PrivateDependencies/reference-dependencies-dynamic-lookup-fine.swift rename to test/Incremental/Dependencies/reference-dependencies-dynamic-lookup-fine.swift diff --git a/test/Incremental/PrivateDependencies/reference-dependencies-errors.swift b/test/Incremental/Dependencies/reference-dependencies-errors.swift similarity index 100% rename from test/Incremental/PrivateDependencies/reference-dependencies-errors.swift rename to test/Incremental/Dependencies/reference-dependencies-errors.swift diff --git a/test/Incremental/PrivateDependencies/reference-dependencies-fine.swift b/test/Incremental/Dependencies/reference-dependencies-fine.swift similarity index 97% rename from test/Incremental/PrivateDependencies/reference-dependencies-fine.swift rename to test/Incremental/Dependencies/reference-dependencies-fine.swift index 3c76ee51fa7a1..e2fba6628bf13 100644 --- a/test/Incremental/PrivateDependencies/reference-dependencies-fine.swift +++ b/test/Incremental/Dependencies/reference-dependencies-fine.swift @@ -5,10 +5,9 @@ // RUN: %empty-directory(%t) // RUN: cp %s %t/main.swift -// Need -fine-grained-dependency-include-intrafile to be invarient wrt type-body-fingerprints enabled/disabled -// RUN: %target-swift-frontend -fine-grained-dependency-include-intrafile -typecheck -primary-file %t/main.swift %S/../Inputs/reference-dependencies-helper.swift -emit-reference-dependencies-path - > %t.swiftdeps // Check that the output is deterministic. -// RUN: %target-swift-frontend -fine-grained-dependency-include-intrafile -typecheck -primary-file %t/main.swift %S/../Inputs/reference-dependencies-helper.swift -emit-reference-dependencies-path - > %t-2.swiftdeps +// RUN: %target-swift-frontend -typecheck -primary-file %t/main.swift %S/../Inputs/reference-dependencies-helper.swift -emit-reference-dependencies-path - > %t.swiftdeps +// RUN: %target-swift-frontend -typecheck -primary-file %t/main.swift %S/../Inputs/reference-dependencies-helper.swift -emit-reference-dependencies-path - > %t-2.swiftdeps // Merge each entry onto one line and sort to overcome order differences // RUN: %S/../../Inputs/process_fine_grained_swiftdeps.sh %swift-dependency-tool %t.swiftdeps %t-processed.swiftdeps diff --git a/test/Incremental/PrivateDependencies/reference-dependencies-members-fine.swift b/test/Incremental/Dependencies/reference-dependencies-members-fine.swift similarity index 85% rename from test/Incremental/PrivateDependencies/reference-dependencies-members-fine.swift rename to test/Incremental/Dependencies/reference-dependencies-members-fine.swift index 01e06c2dc6cb7..0fec187e1d91d 100644 --- a/test/Incremental/PrivateDependencies/reference-dependencies-members-fine.swift +++ b/test/Incremental/Dependencies/reference-dependencies-members-fine.swift @@ -5,10 +5,9 @@ // RUN: %empty-directory(%t) // RUN: cp %s %t/main.swift -// Need -fine-grained-dependency-include-intrafile to be invarient wrt type-body-fingerprints enabled/disabled -// RUN: %target-swift-frontend -fine-grained-dependency-include-intrafile -typecheck -primary-file %t/main.swift %S/../Inputs/reference-dependencies-members-helper.swift -emit-reference-dependencies-path - > %t.swiftdeps +// RUN: %target-swift-frontend -typecheck -primary-file %t/main.swift %S/../Inputs/reference-dependencies-members-helper.swift -emit-reference-dependencies-path - > %t.swiftdeps -// RUN: %target-swift-frontend -fine-grained-dependency-include-intrafile -typecheck -primary-file %t/main.swift %S/../Inputs/reference-dependencies-members-helper.swift -emit-reference-dependencies-path - > %t-2.swiftdeps +// RUN: %target-swift-frontend -typecheck -primary-file %t/main.swift %S/../Inputs/reference-dependencies-members-helper.swift -emit-reference-dependencies-path - > %t-2.swiftdeps // RUN: %S/../../Inputs/process_fine_grained_swiftdeps.sh %swift-dependency-tool %t.swiftdeps %t-processed.swiftdeps // RUN: %S/../../Inputs/process_fine_grained_swiftdeps.sh %swift-dependency-tool %t-2.swiftdeps %t-2-processed.swiftdeps diff --git a/test/Incremental/PrivateDependencies/private-struct-member-fine.swift b/test/Incremental/Dependencies/struct-member-fine.swift similarity index 100% rename from test/Incremental/PrivateDependencies/private-struct-member-fine.swift rename to test/Incremental/Dependencies/struct-member-fine.swift diff --git a/test/Incremental/PrivateDependencies/private-subscript-fine.swift b/test/Incremental/Dependencies/subscript-fine.swift similarity index 100% rename from test/Incremental/PrivateDependencies/private-subscript-fine.swift rename to test/Incremental/Dependencies/subscript-fine.swift diff --git a/test/Incremental/PrivateDependencies/private-typealias-fine.swift b/test/Incremental/Dependencies/typealias-fine.swift similarity index 100% rename from test/Incremental/PrivateDependencies/private-typealias-fine.swift rename to test/Incremental/Dependencies/typealias-fine.swift diff --git a/test/Incremental/PrivateDependencies/private-var-fine.swift b/test/Incremental/Dependencies/var-fine.swift similarity index 100% rename from test/Incremental/PrivateDependencies/private-var-fine.swift rename to test/Incremental/Dependencies/var-fine.swift diff --git a/test/Frontend/PrivateFingerprints/Inputs/class-fingerprint/definesAB-after.swift b/test/Incremental/Fingerprints/Inputs/class-fingerprint/definesAB-after.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/class-fingerprint/definesAB-after.swift rename to test/Incremental/Fingerprints/Inputs/class-fingerprint/definesAB-after.swift diff --git a/test/Frontend/PrivateFingerprints/Inputs/class-fingerprint/definesAB-before.swift b/test/Incremental/Fingerprints/Inputs/class-fingerprint/definesAB-before.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/class-fingerprint/definesAB-before.swift rename to test/Incremental/Fingerprints/Inputs/class-fingerprint/definesAB-before.swift diff --git a/test/Frontend/PrivateFingerprints/Inputs/class-fingerprint/main.swift b/test/Incremental/Fingerprints/Inputs/class-fingerprint/main.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/class-fingerprint/main.swift rename to test/Incremental/Fingerprints/Inputs/class-fingerprint/main.swift diff --git a/test/Frontend/PrivateFingerprints/Inputs/class-fingerprint/ofm.json b/test/Incremental/Fingerprints/Inputs/class-fingerprint/ofm.json similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/class-fingerprint/ofm.json rename to test/Incremental/Fingerprints/Inputs/class-fingerprint/ofm.json diff --git a/test/Frontend/PrivateFingerprints/Inputs/class-fingerprint/usesA.swift b/test/Incremental/Fingerprints/Inputs/class-fingerprint/usesA.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/class-fingerprint/usesA.swift rename to test/Incremental/Fingerprints/Inputs/class-fingerprint/usesA.swift diff --git a/test/Frontend/PrivateFingerprints/Inputs/class-fingerprint/usesB.swift b/test/Incremental/Fingerprints/Inputs/class-fingerprint/usesB.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/class-fingerprint/usesB.swift rename to test/Incremental/Fingerprints/Inputs/class-fingerprint/usesB.swift diff --git a/test/Frontend/PrivateFingerprints/Inputs/enum-fingerprint/definesAB-after.swift b/test/Incremental/Fingerprints/Inputs/enum-fingerprint/definesAB-after.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/enum-fingerprint/definesAB-after.swift rename to test/Incremental/Fingerprints/Inputs/enum-fingerprint/definesAB-after.swift diff --git a/test/Frontend/PrivateFingerprints/Inputs/enum-fingerprint/definesAB-before.swift b/test/Incremental/Fingerprints/Inputs/enum-fingerprint/definesAB-before.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/enum-fingerprint/definesAB-before.swift rename to test/Incremental/Fingerprints/Inputs/enum-fingerprint/definesAB-before.swift diff --git a/test/Frontend/PrivateFingerprints/Inputs/enum-fingerprint/main.swift b/test/Incremental/Fingerprints/Inputs/enum-fingerprint/main.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/enum-fingerprint/main.swift rename to test/Incremental/Fingerprints/Inputs/enum-fingerprint/main.swift diff --git a/test/Frontend/PrivateFingerprints/Inputs/enum-fingerprint/ofm.json b/test/Incremental/Fingerprints/Inputs/enum-fingerprint/ofm.json similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/enum-fingerprint/ofm.json rename to test/Incremental/Fingerprints/Inputs/enum-fingerprint/ofm.json diff --git a/test/Frontend/PrivateFingerprints/Inputs/enum-fingerprint/usesA.swift b/test/Incremental/Fingerprints/Inputs/enum-fingerprint/usesA.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/enum-fingerprint/usesA.swift rename to test/Incremental/Fingerprints/Inputs/enum-fingerprint/usesA.swift diff --git a/test/Frontend/PrivateFingerprints/Inputs/enum-fingerprint/usesB.swift b/test/Incremental/Fingerprints/Inputs/enum-fingerprint/usesB.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/enum-fingerprint/usesB.swift rename to test/Incremental/Fingerprints/Inputs/enum-fingerprint/usesB.swift diff --git a/test/Frontend/PrivateFingerprints/Inputs/extension-adds-member/definesAB-after.swift b/test/Incremental/Fingerprints/Inputs/extension-adds-member/definesAB-after.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/extension-adds-member/definesAB-after.swift rename to test/Incremental/Fingerprints/Inputs/extension-adds-member/definesAB-after.swift diff --git a/test/Frontend/PrivateFingerprints/Inputs/extension-adds-member/definesAB-before.swift b/test/Incremental/Fingerprints/Inputs/extension-adds-member/definesAB-before.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/extension-adds-member/definesAB-before.swift rename to test/Incremental/Fingerprints/Inputs/extension-adds-member/definesAB-before.swift diff --git a/test/Frontend/PrivateFingerprints/Inputs/extension-adds-member/main.swift b/test/Incremental/Fingerprints/Inputs/extension-adds-member/main.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/extension-adds-member/main.swift rename to test/Incremental/Fingerprints/Inputs/extension-adds-member/main.swift diff --git a/test/Frontend/PrivateFingerprints/Inputs/extension-adds-member/ofm.json b/test/Incremental/Fingerprints/Inputs/extension-adds-member/ofm.json similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/extension-adds-member/ofm.json rename to test/Incremental/Fingerprints/Inputs/extension-adds-member/ofm.json diff --git a/test/Frontend/PrivateFingerprints/Inputs/extension-adds-member/usesA.swift b/test/Incremental/Fingerprints/Inputs/extension-adds-member/usesA.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/extension-adds-member/usesA.swift rename to test/Incremental/Fingerprints/Inputs/extension-adds-member/usesA.swift diff --git a/test/Frontend/PrivateFingerprints/Inputs/extension-adds-member/usesB.swift b/test/Incremental/Fingerprints/Inputs/extension-adds-member/usesB.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/extension-adds-member/usesB.swift rename to test/Incremental/Fingerprints/Inputs/extension-adds-member/usesB.swift diff --git a/test/Frontend/PrivateFingerprints/Inputs/protocol-fingerprint/definesAB-after.swift b/test/Incremental/Fingerprints/Inputs/protocol-fingerprint/definesAB-after.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/protocol-fingerprint/definesAB-after.swift rename to test/Incremental/Fingerprints/Inputs/protocol-fingerprint/definesAB-after.swift diff --git a/test/Frontend/PrivateFingerprints/Inputs/protocol-fingerprint/definesAB-before.swift b/test/Incremental/Fingerprints/Inputs/protocol-fingerprint/definesAB-before.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/protocol-fingerprint/definesAB-before.swift rename to test/Incremental/Fingerprints/Inputs/protocol-fingerprint/definesAB-before.swift diff --git a/test/Frontend/PrivateFingerprints/Inputs/protocol-fingerprint/main.swift b/test/Incremental/Fingerprints/Inputs/protocol-fingerprint/main.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/protocol-fingerprint/main.swift rename to test/Incremental/Fingerprints/Inputs/protocol-fingerprint/main.swift diff --git a/test/Frontend/PrivateFingerprints/Inputs/protocol-fingerprint/ofm.json b/test/Incremental/Fingerprints/Inputs/protocol-fingerprint/ofm.json similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/protocol-fingerprint/ofm.json rename to test/Incremental/Fingerprints/Inputs/protocol-fingerprint/ofm.json diff --git a/test/Frontend/PrivateFingerprints/Inputs/protocol-fingerprint/usesA.swift b/test/Incremental/Fingerprints/Inputs/protocol-fingerprint/usesA.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/protocol-fingerprint/usesA.swift rename to test/Incremental/Fingerprints/Inputs/protocol-fingerprint/usesA.swift diff --git a/test/Frontend/PrivateFingerprints/Inputs/protocol-fingerprint/usesB.swift b/test/Incremental/Fingerprints/Inputs/protocol-fingerprint/usesB.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/protocol-fingerprint/usesB.swift rename to test/Incremental/Fingerprints/Inputs/protocol-fingerprint/usesB.swift diff --git a/test/Frontend/PrivateFingerprints/Inputs/struct-fingerprint/definesAB-after.swift b/test/Incremental/Fingerprints/Inputs/struct-fingerprint/definesAB-after.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/struct-fingerprint/definesAB-after.swift rename to test/Incremental/Fingerprints/Inputs/struct-fingerprint/definesAB-after.swift diff --git a/test/Frontend/PrivateFingerprints/Inputs/struct-fingerprint/definesAB-before.swift b/test/Incremental/Fingerprints/Inputs/struct-fingerprint/definesAB-before.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/struct-fingerprint/definesAB-before.swift rename to test/Incremental/Fingerprints/Inputs/struct-fingerprint/definesAB-before.swift diff --git a/test/Frontend/PrivateFingerprints/Inputs/struct-fingerprint/main.swift b/test/Incremental/Fingerprints/Inputs/struct-fingerprint/main.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/struct-fingerprint/main.swift rename to test/Incremental/Fingerprints/Inputs/struct-fingerprint/main.swift diff --git a/test/Frontend/PrivateFingerprints/Inputs/struct-fingerprint/ofm.json b/test/Incremental/Fingerprints/Inputs/struct-fingerprint/ofm.json similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/struct-fingerprint/ofm.json rename to test/Incremental/Fingerprints/Inputs/struct-fingerprint/ofm.json diff --git a/test/Frontend/PrivateFingerprints/Inputs/struct-fingerprint/usesA.swift b/test/Incremental/Fingerprints/Inputs/struct-fingerprint/usesA.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/struct-fingerprint/usesA.swift rename to test/Incremental/Fingerprints/Inputs/struct-fingerprint/usesA.swift diff --git a/test/Frontend/PrivateFingerprints/Inputs/struct-fingerprint/usesB.swift b/test/Incremental/Fingerprints/Inputs/struct-fingerprint/usesB.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/struct-fingerprint/usesB.swift rename to test/Incremental/Fingerprints/Inputs/struct-fingerprint/usesB.swift diff --git a/test/Incremental/Fingerprints/class-fingerprint.swift b/test/Incremental/Fingerprints/class-fingerprint.swift new file mode 100644 index 0000000000000..e244cc3f123a2 --- /dev/null +++ b/test/Incremental/Fingerprints/class-fingerprint.swift @@ -0,0 +1,34 @@ +// Test per-type-body fingerprints for classes +// + +// Establish status quo + +// RUN: %empty-directory(%t) +// RUN: cp %S/Inputs/class-fingerprint/* %t +// RUN: cp %t/definesAB{-before,}.swift + +// Seeing weird failure on CI, so set the mod times +// RUN: touch -t 200101010101 %t/*.swift + +// RUN: cd %t && %target-swiftc_driver -enable-batch-mode -j2 -incremental -driver-show-incremental main.swift definesAB.swift usesA.swift usesB.swift -module-name main -output-file-map ofm.json >&output3 + +// only-run-for-debugging: cp %t/usesB.swiftdeps %t/usesB3.swiftdeps + + +// Change one type, only uses of that type get recompiled + +// RUN: cp %t/definesAB{-after,}.swift + +// Seeing weird failure on CI, so ensure that definesAB.swift is newer +// RUN: touch -t 200201010101 %t/* +// RUN: touch -t 200101010101 %t/*.swift +// RUN: touch -t 200301010101 %t/definesAB.swift + +// RUN: cd %t && %target-swiftc_driver -enable-batch-mode -j2 -incremental -driver-show-incremental main.swift definesAB.swift usesA.swift usesB.swift -module-name main -output-file-map ofm.json >&output4 + +// only-run-for-debugging: cp %t/usesB.swiftdeps %t/usesB4.swiftdeps + +// RUN: %FileCheck -check-prefix=CHECK-MAINAB-RECOMPILED %s < %t/output4 + +// CHECK-MAINAB-RECOMPILED: Queuing (initial): {compile: definesAB.o <= definesAB.swift} +// CHECK-MAINAB-RECOMPILED: Queuing because of dependencies discovered later: {compile: usesA.o <= usesA.swift} diff --git a/test/Incremental/Fingerprints/enum-fingerprint.swift b/test/Incremental/Fingerprints/enum-fingerprint.swift new file mode 100644 index 0000000000000..a56b2443890a6 --- /dev/null +++ b/test/Incremental/Fingerprints/enum-fingerprint.swift @@ -0,0 +1,34 @@ +// Test per-type-body fingerprints for enums +// + +// Establish status quo + +// RUN: %empty-directory(%t) +// RUN: cp %S/Inputs/enum-fingerprint/* %t +// RUN: cp %t/definesAB{-before,}.swift + +// Seeing weird failure on CI, so set the mod times +// RUN: touch -t 200101010101 %t/*.swift + +// RUN: cd %t && %target-swiftc_driver -enable-batch-mode -j2 -incremental -driver-show-incremental main.swift definesAB.swift usesA.swift usesB.swift -module-name main -output-file-map ofm.json >&output3 + +// only-run-for-debugging: cp %t/usesB.swiftdeps %t/usesB3.swiftdeps + + +// Change one type, only uses of that type get recompiled + +// RUN: cp %t/definesAB{-after,}.swift + +// Seeing weird failure on CI, so ensure that definesAB.swift is newer +// RUN: touch -t 200201010101 %t/* +// RUN: touch -t 200101010101 %t/*.swift +// RUN: touch -t 200301010101 %t/definesAB.swift + +// RUN: cd %t && %target-swiftc_driver -enable-batch-mode -j2 -incremental -driver-show-incremental main.swift definesAB.swift usesA.swift usesB.swift -module-name main -output-file-map ofm.json >&output4 + +// only-run-for-debugging: cp %t/usesB.swiftdeps %t/usesB4.swiftdeps + +// RUN: %FileCheck -check-prefix=CHECK-MAINAB-RECOMPILED %s < %t/output4 + +// CHECK-MAINAB-RECOMPILED: Queuing (initial): {compile: definesAB.o <= definesAB.swift} +// CHECK-MAINAB-RECOMPILED: Queuing because of dependencies discovered later: {compile: usesA.o <= usesA.swift} diff --git a/test/Incremental/Fingerprints/extension-adds-member.swift b/test/Incremental/Fingerprints/extension-adds-member.swift new file mode 100644 index 0000000000000..73a8e650dd340 --- /dev/null +++ b/test/Incremental/Fingerprints/extension-adds-member.swift @@ -0,0 +1,36 @@ +// Test per-type-body fingerprints using simple extensions +// +// If the parser is allowed to use a body fingerprint for an extension +// this test will fail because usesA.swift won't be recompiled for the +// last step. + +// Establish status quo + +// RUN: %empty-directory(%t) +// RUN: cp %S/Inputs/extension-adds-member/* %t +// RUN: cp %t/definesAB{-before,}.swift + +// Seeing weird failure on CI, so set the mod times +// RUN: touch -t 200101010101 %t/*.swift + +// RUN: cd %t && %target-swiftc_driver -enable-batch-mode -j2 -incremental -driver-show-incremental main.swift definesAB.swift usesA.swift usesB.swift -module-name main -output-file-map ofm.json >& %t/output3 + +// Change one type, only uses of that type get recompiled + +// RUN: cp %t/definesAB{-after,}.swift + +// Seeing weird failure on CI, so ensure that definesAB.swift is newer +// RUN: touch -t 200201010101 %t/* +// RUN: touch -t 200101010101 %t/*.swift +// RUN: touch -t 200301010101 %t/definesAB.swift + +// RUN: cd %t && %target-swiftc_driver -enable-batch-mode -j2 -incremental -driver-show-incremental main.swift definesAB.swift usesA.swift usesB.swift -module-name main -output-file-map ofm.json >& %t/output4 + +// RUN: %FileCheck -check-prefix=CHECK-RECOMPILED-W %s < %t/output4 +// RUN: %FileCheck -check-prefix=CHECK-NOT-RECOMPILED-W %s < %t/output4 + +// CHECK-RECOMPILED-W: {compile: definesAB.o <= definesAB.swift} +// CHECK-RECOMPILED-W: {compile: usesA.o <= usesA.swift} + + +// CHECK-NOT-RECOMPILED-W-NOT: {compile: main.o <= main.swift} diff --git a/test/Incremental/Fingerprints/protocol-fingerprint.swift b/test/Incremental/Fingerprints/protocol-fingerprint.swift new file mode 100644 index 0000000000000..10c62c62ed6fb --- /dev/null +++ b/test/Incremental/Fingerprints/protocol-fingerprint.swift @@ -0,0 +1,34 @@ +// Test per-type-body fingerprints: the protocol case. +// + +// Establish status quo + +// RUN: %empty-directory(%t) +// RUN: cp %S/Inputs/protocol-fingerprint/* %t +// RUN: cp %t/definesAB{-before,}.swift + +// Seeing weird failure on CI, so set the mod times +// RUN: touch -t 200101010101 %t/*.swift + +// RUN: cd %t && %target-swiftc_driver -enable-batch-mode -j2 -incremental -driver-show-incremental main.swift definesAB.swift usesA.swift usesB.swift -module-name main -output-file-map ofm.json >&output3 + +// only-run-for-debugging: cp %t/usesB.swiftdeps %t/usesB3.swiftdeps + + +// Change one type, only uses of that type get recompiled + +// RUN: cp %t/definesAB{-after,}.swift + +// Seeing weird failure on CI, so ensure that definesAB.swift is newer +// RUN: touch -t 200201010101 %t/* +// RUN: touch -t 200101010101 %t/*.swift +// RUN: touch -t 200301010101 %t/definesAB.swift + +// RUN: cd %t && %target-swiftc_driver -enable-batch-mode -j2 -incremental -driver-show-incremental main.swift definesAB.swift usesA.swift usesB.swift -module-name main -output-file-map ofm.json >&output4 + +// only-run-for-debugging: cp %t/usesB.swiftdeps %t/usesB4.swiftdeps + +// RUN: %FileCheck -check-prefix=CHECK-MAINAB-RECOMPILED %s < %t/output4 + +// CHECK-MAINAB-RECOMPILED: Queuing (initial): {compile: definesAB.o <= definesAB.swift} +// CHECK-MAINAB-RECOMPILED: Queuing because of dependencies discovered later: {compile: usesA.o <= usesA.swift} diff --git a/test/Incremental/Fingerprints/struct-fingerprint.swift b/test/Incremental/Fingerprints/struct-fingerprint.swift new file mode 100644 index 0000000000000..1010e8fcc7eb2 --- /dev/null +++ b/test/Incremental/Fingerprints/struct-fingerprint.swift @@ -0,0 +1,34 @@ +// Test per-type-body fingerprints for structs +// + +// Establish status quo + +// RUN: %empty-directory(%t) +// RUN: cp %S/Inputs/struct-fingerprint/* %t +// RUN: cp %t/definesAB{-before,}.swift + +// Seeing weird failure on CI, so set the mod times +// RUN: touch -t 200101010101 %t/*.swift + +// RUN: cd %t && %target-swiftc_driver -enable-batch-mode -j2 -incremental -driver-show-incremental main.swift definesAB.swift usesA.swift usesB.swift -module-name main -output-file-map ofm.json >&output3 + +// only-run-for-debugging: cp %t/usesB.swiftdeps %t/usesB3.swiftdeps + + +// Change one type, only uses of that type get recompiled + +// RUN: cp %t/definesAB{-after,}.swift + +// Seeing weird failure on CI, so ensure that definesAB.swift is newer +// RUN: touch -t 200201010101 %t/* +// RUN: touch -t 200101010101 %t/*.swift +// RUN: touch -t 200301010101 %t/definesAB.swift + +// RUN: cd %t && %target-swiftc_driver -enable-batch-mode -j2 -incremental -driver-show-incremental main.swift definesAB.swift usesA.swift usesB.swift -module-name main -output-file-map ofm.json >&output4 + +// only-run-for-debugging: cp %t/usesB.swiftdeps %t/usesB4.swiftdeps + +// RUN: %FileCheck -check-prefix=CHECK-MAINAB-RECOMPILED %s < %t/output4 + +// CHECK-MAINAB-RECOMPILED: Queuing (initial): {compile: definesAB.o <= definesAB.swift} +// CHECK-MAINAB-RECOMPILED: Queuing because of dependencies discovered later: {compile: usesA.o <= usesA.swift} diff --git a/test/Incremental/Verifier/single-file-private/AnyObject.swift b/test/Incremental/Verifier/single-file-private/AnyObject.swift index b24c8a195003b..1af01010e1c32 100644 --- a/test/Incremental/Verifier/single-file-private/AnyObject.swift +++ b/test/Incremental/Verifier/single-file-private/AnyObject.swift @@ -75,3 +75,17 @@ func lookupOnAnyObject(object: AnyObject) { // expected-provides {{lookupOnAnyOb // expected-member {{Swift.CVarArg.someMethod}} // expected-member {{Swift.CustomStringConvertible.someMethod}} // expected-member {{Swift.CustomDebugStringConvertible.someMethod}} +// expected-member {{Swift.Equatable.someMember}} +// expected-member{{Swift.CustomDebugStringConvertible.init}} +// expected-member{{Swift.CVarArg.someMember}} +// expected-member{{Foundation._KeyValueCodingAndObservingPublishing.someMember}} +// expected-member{{Swift.Equatable.init}} +// expected-member{{Swift.Hashable.init}} +// expected-member{{Swift.CVarArg.init}} +// expected-member{{Foundation._KeyValueCodingAndObserving.someMember}} +// expected-member{{Foundation._KeyValueCodingAndObservingPublishing.init}} +// expected-member{{Swift.CustomDebugStringConvertible.someMember}} +// expected-member{{Swift.CustomStringConvertible.someMember}} +// expected-member{{Swift.CustomStringConvertible.init}} +// expected-member{{Swift.Hashable.someMember}} +// expected-member{{Foundation._KeyValueCodingAndObserving.init}} diff --git a/test/Incremental/superfluous-cascade.swift b/test/Incremental/superfluous-cascade.swift index 06d3f677d4c40..760ec1f6e655d 100644 --- a/test/Incremental/superfluous-cascade.swift +++ b/test/Incremental/superfluous-cascade.swift @@ -1,8 +1,4 @@ -// ============================================================================= -// With private dependencies -// ============================================================================= - -// Establish status quo +// Establish baseline // RUN: %empty-directory(%t) // RUN: cp %S/Inputs/superfluous-cascade/* %t @@ -20,7 +16,7 @@ // RUN: cd %t && %target-swiftc_driver -enable-batch-mode -j2 -incremental -driver-show-incremental main.swift definesPoint.swift usesPoint.swift usesDisplay.swift -module-name main -output-file-map ofm.json >&output4 -// RUN: %FileCheck -check-prefix=CHECK-PRIVATE-RECOMPILED %s --dump-input=always < %t/output4 +// RUN: %FileCheck -check-prefix=CHECK-RECOMPILED %s --dump-input=always < %t/output4 -// CHECK-PRIVATE-RECOMPILED: Queuing because of dependencies discovered later: {compile: usesPoint.o <= usesPoint.swift} -// CHECK-PRIVATE-RECOMPILED-NOT: Queuing because of dependencies discovered later: {compile: usesDisplay.o <= usesDisplay.swift} +// CHECK-RECOMPILED: Queuing because of dependencies discovered later: {compile: usesPoint.o <= usesPoint.swift} +// CHECK-RECOMPILED-NOT: Queuing because of dependencies discovered later: {compile: usesDisplay.o <= usesDisplay.swift} diff --git a/test/Index/Store/record-sourcefile.swift b/test/Index/Store/record-sourcefile.swift index e35cb8ba0c198..e206966b13f96 100644 --- a/test/Index/Store/record-sourcefile.swift +++ b/test/Index/Store/record-sourcefile.swift @@ -30,7 +30,7 @@ // CHECK: type-alias/Swift | TA | s:{{.*}} | | Def,RelChild - // CHECK: class/Swift | C1 | s:{{.*}} | | Def,Ref,RelBase,RelCont - // CHECK: instance-method/Swift | method() | s:{{.*}} | | Def,Ref,Call,Dyn,RelChild,RelRec,RelCall,RelCont - -// CHECK: class/Swift | C2 | s:{{.*}} | | Def - +// CHECK: class/Swift | C2 | s:{{.*}} | | Def,Ref - RelChild,RelBase // CHECK: instance-method/Swift | method() | s:{{.*}} | | Def,Dyn,RelChild,RelOver - // CHECK: function/Swift | takeC1(x:) | s:{{.*}} | | Def - // CHECK: instance-method(test)/Swift | testFoo() | s:{{.*}} | | Def,Dyn,RelChild - @@ -151,3 +151,6 @@ class MyTestCase: XCTestCase { // CHECK-NEXT: RelChild | s:4file10MyTestCaseC func testFoo() { test1() } } + +// CHECK: [[@LINE+1]]:11 | class/Swift | s:4file2C2C | Ref | rel: 0 +extension C2 {} diff --git a/test/Index/Store/record-systemmodule.swift b/test/Index/Store/record-systemmodule.swift new file mode 100644 index 0000000000000..7e509b264cb57 --- /dev/null +++ b/test/Index/Store/record-systemmodule.swift @@ -0,0 +1,71 @@ +// --- Prepare SDK (.swiftmodule). +// RUN: %empty-directory(%t) +// RUN: %empty-directory(%t/SDK) +// RUN: mkdir -p %t/SDK/Frameworks/SomeModule.framework/Modules/SomeModule.swiftmodule +// RUN: %target-swift-frontend \ +// RUN: -emit-module \ +// RUN: -module-name SomeModule \ +// RUN: -o %t/SDK/Frameworks/SomeModule.framework/Modules/SomeModule.swiftmodule/%module-target-triple.swiftmodule \ +// RUN: -swift-version 5 \ +// RUN: -D SOME_MODULE \ +// RUN: %s + +#if SOME_MODULE + +public func someFunc() {} + +public class C2 {} + +extension C2 { + public func publicFunc() {} +} + +// Don't record extensions with nothing to index. +extension C2 {} + +extension C2 { + internal func SECRET() {} + private func SECRET1() {} + fileprivate func SECRET2() {} +} + +internal protocol SECRETProto {} +extension C2: SECRETProto {} + +// ----------------------------------------------------------------------------- +// Test-1 - '.swiftmodule' - Normal index-while-building. +// +// RUN: %empty-directory(%t/idx) +// RUN: %empty-directory(%t/modulecache) +// +// --- Built with indexing +// RUN: %target-swift-frontend \ +// RUN: -typecheck \ +// RUN: -index-system-modules \ +// RUN: -index-ignore-stdlib \ +// RUN: -index-store-path %t/idx \ +// RUN: -sdk %t/SDK \ +// RUN: -Fsystem %t/SDK/Frameworks \ +// RUN: -module-cache-path %t/modulecache \ +// RUN: -D CLIENT \ +// RUN: %s + +#elseif CLIENT + +import SomeModule +print(someFunc()) + +#endif + +// ----------------------------------------------------------------------------- +// --- Check the records. +// RUN: c-index-test core -print-record %t/idx | %FileCheck %s + +// CHECK: 0:0 | function/Swift | s:10SomeModule8someFuncyyF | Def | rel: 0 +// CHECK-NEXT: 0:0 | class/Swift | [[class_USR:s:10SomeModule2C2C]] | Def | rel: 0 +// CHECK-NEXT: 0:0 | class/Swift | [[class_USR]] | Ref,RelExt | rel: 1 +// CHECK-NEXT: RelExt | s:e:[[publicFunc_USR:s:10SomeModule2C2C10publicFuncyyF]] +// CHECK-NEXT: 0:0 | instance-method/Swift | [[publicFunc_USR]] | Def,Dyn,RelChild | rel: 1 +// CHECK-NEXT: RelChild | s:e:[[publicFunc_USR]] +// CHECK-NEXT: 0:0 | extension/ext-class/Swift | s:e:[[publicFunc_USR]] | Def | rel: 0 +// CHECK-NOT: SECRET diff --git a/test/Index/function_builders.swift b/test/Index/result_builders.swift similarity index 98% rename from test/Index/function_builders.swift rename to test/Index/result_builders.swift index 2dcb9bc93b3b7..7e78b2a7cccf6 100644 --- a/test/Index/function_builders.swift +++ b/test/Index/result_builders.swift @@ -16,7 +16,7 @@ extension Int: Taggable { } extension String: Taggable { } extension Double: Taggable { } -@_functionBuilder +@resultBuilder struct TaggedBuilder { static func buildBlock() -> () { } diff --git a/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h b/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h index b03e3530a4968..01a62d25ea599 100644 --- a/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h +++ b/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h @@ -10,7 +10,29 @@ -(void)findAnswerAsynchronously:(void (^)(NSString *_Nullable, NSError * _Nullable))handler __attribute__((swift_name("findAnswer(completionHandler:)"))); -(BOOL)findAnswerFailinglyWithError:(NSError * _Nullable * _Nullable)error completion:(void (^)(NSString *_Nullable, NSError * _Nullable))handler __attribute__((swift_name("findAnswerFailingly(completionHandler:)"))); -(void)doSomethingFun:(NSString *)operation then:(void (^)(void))completionHandler; +-(void)getFortuneAsynchronouslyWithCompletionHandler:(void (^)(NSString *_Nullable, NSError * _Nullable))handler; +-(void)getMagicNumberAsynchronouslyWithSeed:(NSInteger)seed completionHandler:(void (^)(NSInteger, NSError * _Nullable))handler; @property(readwrite) void (^completionHandler)(NSInteger); + +-(void)doSomethingConflicted:(NSString *)operation completionHandler:(void (^)(NSInteger))handler; +-(NSInteger)doSomethingConflicted:(NSString *)operation; +-(void)server:(NSString *)name restartWithCompletionHandler:(void (^)(void))block; +-(void)server:(NSString *)name atPriority:(double)priority restartWithCompletionHandler:(void (^)(void))block; +@end + +@protocol RefrigeratorDelegate +- (void)someoneDidOpenRefrigerator:(id)fridge; +- (void)refrigerator:(id)fridge didGetFilledWithItems:(NSArray *)items; +- (void)refrigerator:(id)fridge didGetFilledWithIntegers:(NSInteger *)items count:(NSInteger)count; +- (void)refrigerator:(id)fridge willAddItem:(id)item; +- (BOOL)refrigerator:(id)fridge didRemoveItem:(id)item; +@end + +@protocol ConcurrentProtocol +-(void)askUserToSolvePuzzle:(NSString *)puzzle completionHandler:(void (^ _Nullable)(NSString * _Nullable, NSError * _Nullable))completionHandler; + +@optional +-(void)askUserToJumpThroughHoop:(NSString *)hoop completionHandler:(void (^ _Nullable)(NSString *))completionHandler; @end #pragma clang assume_nonnull end diff --git a/test/Inputs/clang-importer-sdk/usr/include/ctypes.h b/test/Inputs/clang-importer-sdk/usr/include/ctypes.h index 5898fd4bbb9c4..dfe17a3dfdca8 100644 --- a/test/Inputs/clang-importer-sdk/usr/include/ctypes.h +++ b/test/Inputs/clang-importer-sdk/usr/include/ctypes.h @@ -205,18 +205,18 @@ typedef int (*fptr)(int); fptr getFunctionPointer(void); void useFunctionPointer(fptr); -int (*getFunctionPointer_(void))(int); +size_t (*getFunctionPointer_(void))(size_t); struct FunctionPointerWrapper { fptr a; fptr b; }; -typedef void (*fptr2)(int, long, void *); +typedef void (*fptr2)(size_t, long, void *); fptr2 getFunctionPointer2(void); void useFunctionPointer2(fptr2); -int (*(*getHigherOrderFunctionPointer(void))(int (*)(int)))(int); +size_t (*(*getHigherOrderFunctionPointer(void))(size_t (*)(size_t)))(size_t); typedef struct Dummy { int x; @@ -232,6 +232,10 @@ typedef OpaqueTypedefForFP (*FunctionPointerReturningOpaqueTypedef)(void); typedef struct ForwardInTypedefForFP2 *OpaqueTypedefForFP2; typedef OpaqueTypedefForFP2 (*FunctionPointerReturningOpaqueTypedef2)(void); +// Functions that get Swift types which cannot be used to re-derive the +// Clang type. +size_t returns_size_t(); + // This will probably never be serializable. typedef struct { int x; int y; } *(*UnserializableFunctionPointer)(void); diff --git a/test/Inputs/clang-importer-sdk/usr/include/objc/NSObject.h b/test/Inputs/clang-importer-sdk/usr/include/objc/NSObject.h index a86f7b8851669..e80887d8165f0 100644 --- a/test/Inputs/clang-importer-sdk/usr/include/objc/NSObject.h +++ b/test/Inputs/clang-importer-sdk/usr/include/objc/NSObject.h @@ -136,6 +136,7 @@ @interface A(BoolStuff) - setEnabled:(BOOL)enabled; ++ (void)setGlobal:(BOOL)global; @end @interface AlmostSubscriptable diff --git a/test/Inputs/print-shims.swift b/test/Inputs/print-shims.swift new file mode 100644 index 0000000000000..7a9e569370482 --- /dev/null +++ b/test/Inputs/print-shims.swift @@ -0,0 +1,24 @@ +@_silgen_name("printInt64") +public func printInt64(_ int: Int64) { + print(int) +} + +@_silgen_name("printInt32") +public func printInt32(_ int: Int32) { + print(int) +} + +@_silgen_name("printGeneric") +public func printGeneric(_ t: T) { + print(t) +} + +@_silgen_name("printBool") +public func printBool(_ bool: Bool) { + print(bool) +} + +@_silgen_name("printAny") +public func printAny(_ any: Any) { + print(any) +} diff --git a/test/InterfaceHash/added_method-type-fingerprints.swift b/test/InterfaceHash/added_method-type.swift similarity index 86% rename from test/InterfaceHash/added_method-type-fingerprints.swift rename to test/InterfaceHash/added_method-type.swift index 6776e086a7559..0f7eb261f21b3 100644 --- a/test/InterfaceHash/added_method-type-fingerprints.swift +++ b/test/InterfaceHash/added_method-type.swift @@ -8,10 +8,10 @@ // RUN: %empty-directory(%t) // RUN: %{python} %utils/split_file.py -o %t %s // RUN: cp %t/{a,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main // RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/a-processed.swiftdeps // RUN: cp %t/{b,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main // RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/b-processed.swiftdeps // RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs diff --git a/test/InterfaceHash/added_method.swift b/test/InterfaceHash/added_method.swift deleted file mode 100644 index 28dd75f2d4329..0000000000000 --- a/test/InterfaceHash/added_method.swift +++ /dev/null @@ -1,23 +0,0 @@ -// RUN: %empty-directory(%t) -// RUN: %{python} %utils/split_file.py -o %t %s -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash -// RUN: not cmp %t/a.hash %t/b.hash - -// BEGIN a.swift -class C { - func f2() -> Int { - return 0 - } -} - -// BEGIN b.swift -class C { - func f2() -> Int { - return 0 - } - - func f3() -> Int { - return 1 - } -} diff --git a/test/InterfaceHash/added_private_class_private_property-type-fingerprints.swift b/test/InterfaceHash/added_private_class_private_property-type-fingerprints.swift deleted file mode 100644 index 5ba9627ce2731..0000000000000 --- a/test/InterfaceHash/added_private_class_private_property-type-fingerprints.swift +++ /dev/null @@ -1,53 +0,0 @@ -// REQUIRES: shell -// Also uses awk: -// XFAIL OS=windows - -// RUN: %empty-directory(%t) -// RUN: %{python} %utils/split_file.py -o %t %s -// RUN: cp %t/{a,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main -// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/a-processed.swiftdeps -// RUN: cp %t/{b,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main -// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/b-processed.swiftdeps - -// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs - -// BEGIN a.swift -private class C { - func f2() -> Int { - return 0 - } -} - -// BEGIN b.swift -private class C { - func f2() -> Int { - return 0 - } - - private var x: Int = 0 -} - -// Since C is a type or extension, the interface hash ought to not get the -// changed token hash. - -// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH -// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT - -// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' C true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' C true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' C true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' C true - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1C{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1C{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1C{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1C{{[^ ]+}} '' true - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1C{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1C{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1C{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1C{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_class_private_property.swift b/test/InterfaceHash/added_private_class_private_property.swift index e3ca285b2cb10..dfa6721eface6 100644 --- a/test/InterfaceHash/added_private_class_private_property.swift +++ b/test/InterfaceHash/added_private_class_private_property.swift @@ -1,8 +1,17 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + // RUN: %empty-directory(%t) // RUN: %{python} %utils/split_file.py -o %t %s -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash -// RUN: not cmp %t/a.hash %t/b.hash +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs // BEGIN a.swift private class C { @@ -19,3 +28,26 @@ private class C { private var x: Int = 0 } + +// Since C is a type or extension, the interface hash ought to not get the +// changed token hash. + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' C true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1C{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1C{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_class_property-type-fingerprints.swift b/test/InterfaceHash/added_private_class_property-type-fingerprints.swift deleted file mode 100644 index d087848dd323f..0000000000000 --- a/test/InterfaceHash/added_private_class_property-type-fingerprints.swift +++ /dev/null @@ -1,53 +0,0 @@ -// REQUIRES: shell -// Also uses awk: -// XFAIL OS=windows - -// RUN: %empty-directory(%t) -// RUN: %{python} %utils/split_file.py -o %t %s -// RUN: cp %t/{a,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main -// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/a-processed.swiftdeps -// RUN: cp %t/{b,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main -// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/b-processed.swiftdeps - -// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs - -// BEGIN a.swift -class C { - func f2() -> Int { - return 0 - } -} - -// BEGIN b.swift -class C { - func f2() -> Int { - return 0 - } - - private var x: Int = 0 -} - -// Since C is a type or extension, the interface hash ought to not get the -// changed token hash. - -// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH -// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT - -// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' C true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' C true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' C true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' C true - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1C{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1C{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1C{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1C{{[^ ]+}} '' true - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1C{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1C{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1C{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1C{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_class_property.swift b/test/InterfaceHash/added_private_class_property.swift index cdeb03d8944a7..1734f85565ef4 100644 --- a/test/InterfaceHash/added_private_class_property.swift +++ b/test/InterfaceHash/added_private_class_property.swift @@ -1,8 +1,17 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + // RUN: %empty-directory(%t) // RUN: %{python} %utils/split_file.py -o %t %s -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash -// RUN: not cmp %t/a.hash %t/b.hash +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs // BEGIN a.swift class C { @@ -19,3 +28,26 @@ class C { private var x: Int = 0 } + +// Since C is a type or extension, the interface hash ought to not get the +// changed token hash. + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' C true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1C{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1C{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_enum_private_property-type-fingerprints.swift b/test/InterfaceHash/added_private_enum_private_property-type-fingerprints.swift deleted file mode 100644 index ed898ffdce3d1..0000000000000 --- a/test/InterfaceHash/added_private_enum_private_property-type-fingerprints.swift +++ /dev/null @@ -1,56 +0,0 @@ -// REQUIRES: shell -// Also uses awk: -// XFAIL OS=windows - -// When adding a private protocol method, the interface hash should stay the same -// The per-type fingerprint should change - -// RUN: %empty-directory(%t) -// RUN: %{python} %utils/split_file.py -o %t %s -// RUN: cp %t/{a,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main -// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/a-processed.swiftdeps -// RUN: cp %t/{b,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main -// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/b-processed.swiftdeps - -// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs - - -// BEGIN a.swift -private enum A { - case x, y - func f2() -> Int { - return 0 - } -} - -// BEGIN b.swift -private enum A { - case x, y - func f2() -> Int { - return 0 - } - - var foo: Int { return 0 } -} - -// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH -// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT - -// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' A true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' A true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' A true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' A true - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1A{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1A{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1A{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1A{{[^ ]+}} '' true - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1A{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1A{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1A{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1A{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_enum_private_property.swift b/test/InterfaceHash/added_private_enum_private_property.swift index a35d20b2928d0..078b78eef37ca 100644 --- a/test/InterfaceHash/added_private_enum_private_property.swift +++ b/test/InterfaceHash/added_private_enum_private_property.swift @@ -1,8 +1,21 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + // RUN: %empty-directory(%t) // RUN: %{python} %utils/split_file.py -o %t %s -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash -// RUN: not cmp %t/a.hash %t/b.hash +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs + // BEGIN a.swift private enum A { @@ -21,3 +34,23 @@ private enum A { var foo: Int { return 0 } } + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' A true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' A true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' A true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' A true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1A{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1A{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_enum_property-type-fingerprints.swift b/test/InterfaceHash/added_private_enum_property-type-fingerprints.swift deleted file mode 100644 index 0589feefd6214..0000000000000 --- a/test/InterfaceHash/added_private_enum_property-type-fingerprints.swift +++ /dev/null @@ -1,55 +0,0 @@ -// REQUIRES: shell -// Also uses awk: -// XFAIL OS=windows - -// When adding a private protocol method, the interface hash should stay the same -// The per-type fingerprint should change - -// RUN: %empty-directory(%t) -// RUN: %{python} %utils/split_file.py -o %t %s -// RUN: cp %t/{a,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main -// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/a-processed.swiftdeps -// RUN: cp %t/{b,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main -// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/b-processed.swiftdeps - -// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs - -// BEGIN a.swift -enum A { - case x, y - func f2() -> Int { - return 0 - } -} - -// BEGIN b.swift -enum A { - case x, y - func f2() -> Int { - return 0 - } - - private var foo: Int { return 0 } -} - -// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH -// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT - -// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' A true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' A true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' A true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' A true - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1A{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1A{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1A{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1A{{[^ ]+}} '' true - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1A{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1A{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1A{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1A{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_enum_property.swift b/test/InterfaceHash/added_private_enum_property.swift index 370272d91d251..5993da28a924c 100644 --- a/test/InterfaceHash/added_private_enum_property.swift +++ b/test/InterfaceHash/added_private_enum_property.swift @@ -1,8 +1,20 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + // RUN: %empty-directory(%t) // RUN: %{python} %utils/split_file.py -o %t %s -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash -// RUN: not cmp %t/a.hash %t/b.hash +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs // BEGIN a.swift enum A { @@ -21,3 +33,23 @@ enum A { private var foo: Int { return 0 } } + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' A true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' A true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' A true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' A true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1A{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1A{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_method-type-fingerprints.swift b/test/InterfaceHash/added_private_method-type-fingerprints.swift deleted file mode 100644 index f6b8236e94b64..0000000000000 --- a/test/InterfaceHash/added_private_method-type-fingerprints.swift +++ /dev/null @@ -1,55 +0,0 @@ -// REQUIRES: shell -// Also uses awk: -// XFAIL OS=windows - -// When adding a private protocol method, the interface hash should stay the same -// The per-type fingerprint should change - -// RUN: %empty-directory(%t) -// RUN: %{python} %utils/split_file.py -o %t %s -// RUN: cp %t/{a,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main -// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/a-processed.swiftdeps -// RUN: cp %t/{b,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main -// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/b-processed.swiftdeps - -// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs - -// BEGIN a.swift -class C { - func f2() -> Int { - return 0 - } -} - -// BEGIN b.swift -class C { - func f2() -> Int { - return 0 - } - - private func f3() -> Int { - return 1 - } -} - -// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH -// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT - -// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' C true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' C true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' C true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' C true - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1C{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1C{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1C{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1C{{[^ ]+}} '' true - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1C{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1C{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1C{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1C{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_method.swift b/test/InterfaceHash/added_private_method.swift index a07bed690cf16..77dcb2b0d2f68 100644 --- a/test/InterfaceHash/added_private_method.swift +++ b/test/InterfaceHash/added_private_method.swift @@ -1,8 +1,20 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + // RUN: %empty-directory(%t) // RUN: %{python} %utils/split_file.py -o %t %s -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash -// RUN: not cmp %t/a.hash %t/b.hash +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs // BEGIN a.swift class C { @@ -21,3 +33,23 @@ class C { return 1 } } + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' C true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1C{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1C{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_method_value_types-type-fingerprints.swift b/test/InterfaceHash/added_private_method_value_types-type-fingerprints.swift deleted file mode 100644 index 8499fbd0ea835..0000000000000 --- a/test/InterfaceHash/added_private_method_value_types-type-fingerprints.swift +++ /dev/null @@ -1,88 +0,0 @@ -// REQUIRES: shell -// Also uses awk: -// XFAIL OS=windows - -// When adding a private protocol method, the interface hash should stay the same -// The per-type fingerprint should change - -// RUN: %empty-directory(%t) -// RUN: %{python} %utils/split_file.py -o %t %s -// RUN: cp %t/{a,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main -// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/a-processed.swiftdeps -// RUN: cp %t/{b,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main -// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/b-processed.swiftdeps - -// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs - -// BEGIN a.swift -struct A { - func f2() -> Int { - return 0 - } -} - -enum B { - case x, y - func f2() -> Int { - return 0 - } -} - -// BEGIN b.swift -struct A { - func f2() -> Int { - return 0 - } - - private func f3() -> Int { - return 1 - } -} - -enum B { - case x, y - func f2() -> Int { - return 0 - } - - private func f3() -> Int { - return 1 - } -} - -// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH -// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT - -// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' A true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' A true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' A true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' A true - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1A{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1A{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1A{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1A{{[^ ]+}} '' true - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1A{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1A{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1A{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1A{{[^ ]+}} '' true - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' B true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' B true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' B true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' B true - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1B{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1B{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1B{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1B{{[^ ]+}} '' true - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1B{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1B{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1B{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1B{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_method_value_types.swift b/test/InterfaceHash/added_private_method_value_types.swift index 29ff2314d0c1e..e685f9da9afcd 100644 --- a/test/InterfaceHash/added_private_method_value_types.swift +++ b/test/InterfaceHash/added_private_method_value_types.swift @@ -1,8 +1,20 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + // RUN: %empty-directory(%t) // RUN: %{python} %utils/split_file.py -o %t %s -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash -// RUN: not cmp %t/a.hash %t/b.hash +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs // BEGIN a.swift struct A { @@ -39,3 +51,38 @@ enum B { return 1 } } + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' A true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' A true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' A true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' A true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1A{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1A{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' B true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' B true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' B true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' B true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1B{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1B{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1B{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1B{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1B{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1B{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1B{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1B{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_protocol_method-type-fingerprints.swift b/test/InterfaceHash/added_private_protocol_method-type-fingerprints.swift deleted file mode 100644 index f029cdb8b803d..0000000000000 --- a/test/InterfaceHash/added_private_protocol_method-type-fingerprints.swift +++ /dev/null @@ -1,50 +0,0 @@ -// REQUIRES: shell -// Also uses awk: -// XFAIL OS=windows - -// When adding a private protocol method, the interface hash should stay the same -// The per-type fingerprint should change - -// RUN: %empty-directory(%t) -// RUN: %{python} %utils/split_file.py -o %t %s -// RUN: cp %t/{a,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main -// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/a-processed.swiftdeps -// RUN: cp %t/{b,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main -// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/b-processed.swiftdeps - -// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs - -// BEGIN a.swift -private protocol P { - func f2() -> Int - var y: Int { get set } -} - -// BEGIN b.swift -private protocol P { - func f2() -> Int - func f3() -> Int - var y: Int { get set } -} - -// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH -// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT - -// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' P true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' P true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' P true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' P true - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1P{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1P{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1P{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1P{{[^ ]+}} '' true - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1P{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1P{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1P{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1P{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_protocol_method.swift b/test/InterfaceHash/added_private_protocol_method.swift index 965c82b67af79..d7a03ef2dedd1 100644 --- a/test/InterfaceHash/added_private_protocol_method.swift +++ b/test/InterfaceHash/added_private_protocol_method.swift @@ -1,8 +1,20 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + // RUN: %empty-directory(%t) // RUN: %{python} %utils/split_file.py -o %t %s -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash -// RUN: not cmp %t/a.hash %t/b.hash +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs // BEGIN a.swift private protocol P { @@ -16,3 +28,23 @@ private protocol P { func f3() -> Int var y: Int { get set } } + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' P true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' P true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' P true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' P true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1P{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1P{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_protocol_property-type-fingerprints.swift b/test/InterfaceHash/added_private_protocol_property-type-fingerprints.swift deleted file mode 100644 index 4a51a0df7957b..0000000000000 --- a/test/InterfaceHash/added_private_protocol_property-type-fingerprints.swift +++ /dev/null @@ -1,50 +0,0 @@ -// REQUIRES: shell -// Also uses awk: -// XFAIL OS=windows - -// When adding a private protocol method, the interface hash should stay the same -// The per-type fingerprint should change - -// RUN: %empty-directory(%t) -// RUN: %{python} %utils/split_file.py -o %t %s -// RUN: cp %t/{a,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main -// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/a-processed.swiftdeps -// RUN: cp %t/{b,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main -// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/b-processed.swiftdeps - -// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs - -// BEGIN a.swift -private protocol P { - func f2() -> Int - var y: Int { get set } -} - -// BEGIN b.swift -private protocol P { - func f2() -> Int - var x: Int { get set } - var y: Int { get set } -} - -// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH -// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT - -// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' P true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' P true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' P true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' P true - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1P{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1P{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1P{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1P{{[^ ]+}} '' true - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1P{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1P{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1P{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1P{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_protocol_property.swift b/test/InterfaceHash/added_private_protocol_property.swift index f47353d2d6521..ce76fffde3ff9 100644 --- a/test/InterfaceHash/added_private_protocol_property.swift +++ b/test/InterfaceHash/added_private_protocol_property.swift @@ -1,8 +1,20 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + // RUN: %empty-directory(%t) // RUN: %{python} %utils/split_file.py -o %t %s -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash -// RUN: not cmp %t/a.hash %t/b.hash +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs // BEGIN a.swift private protocol P { @@ -16,3 +28,23 @@ private protocol P { var x: Int { get set } var y: Int { get set } } + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' P true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' P true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' P true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' P true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1P{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1P{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_struct_private_property-type-fingerprints.swift b/test/InterfaceHash/added_private_struct_private_property-type-fingerprints.swift deleted file mode 100644 index b5d20cf3b317c..0000000000000 --- a/test/InterfaceHash/added_private_struct_private_property-type-fingerprints.swift +++ /dev/null @@ -1,56 +0,0 @@ -// REQUIRES: shell -// Also uses awk: -// XFAIL OS=windows - -// When adding a private protocol method, the interface hash should stay the same -// The per-type fingerprint should change - -// RUN: %empty-directory(%t) -// RUN: %{python} %utils/split_file.py -o %t %s -// RUN: cp %t/{a,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main -// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/a-processed.swiftdeps -// RUN: cp %t/{b,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main -// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/b-processed.swiftdeps - -// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs - -// BEGIN a.swift -struct S { - func f2() -> Int { - return 0 - } - - var y: Int = 0 -} - -// BEGIN b.swift -struct S { - func f2() -> Int { - return 0 - } - - private var x: Int = 0 - var y: Int = 0 -} - -// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH -// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT - -// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' S true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' S true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' S true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' S true - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1S{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1S{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1S{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1S{{[^ ]+}} '' true - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1S{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1S{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1S{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1S{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_struct_private_property.swift b/test/InterfaceHash/added_private_struct_private_property.swift index 7bba778652d58..94efb4498aa36 100644 --- a/test/InterfaceHash/added_private_struct_private_property.swift +++ b/test/InterfaceHash/added_private_struct_private_property.swift @@ -1,8 +1,20 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + // RUN: %empty-directory(%t) // RUN: %{python} %utils/split_file.py -o %t %s -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash -// RUN: not cmp %t/a.hash %t/b.hash +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs // BEGIN a.swift struct S { @@ -22,3 +34,23 @@ struct S { private var x: Int = 0 var y: Int = 0 } + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' S true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' S true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' S true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' S true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1S{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1S{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_struct_property-type-fingerprints.swift b/test/InterfaceHash/added_private_struct_property-type-fingerprints.swift deleted file mode 100644 index f27418b9036ff..0000000000000 --- a/test/InterfaceHash/added_private_struct_property-type-fingerprints.swift +++ /dev/null @@ -1,56 +0,0 @@ -// REQUIRES: shell -// Also uses awk: -// XFAIL OS=windows - -// When adding a private protocol method, the interface hash should stay the same -// The per-type fingerprint should change - -// RUN: %empty-directory(%t) -// RUN: %{python} %utils/split_file.py -o %t %s -// RUN: cp %t/{a,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main -// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/a-processed.swiftdeps -// RUN: cp %t/{b,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main -// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/b-processed.swiftdeps - -// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs - -// BEGIN a.swift -private struct S { - func f2() -> Int { - return 0 - } - - var y: Int = 0 -} - -// BEGIN b.swift -private struct S { - func f2() -> Int { - return 0 - } - - var x: Int = 0 - var y: Int = 0 -} - -// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH -// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT - -// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' S true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' S true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' S true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' S true - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1S{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1S{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1S{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1S{{[^ ]+}} '' true - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1S{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1S{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1S{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1S{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_struct_property.swift b/test/InterfaceHash/added_private_struct_property.swift index 49e58b356b684..82611277f8d1c 100644 --- a/test/InterfaceHash/added_private_struct_property.swift +++ b/test/InterfaceHash/added_private_struct_property.swift @@ -1,8 +1,20 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + // RUN: %empty-directory(%t) // RUN: %{python} %utils/split_file.py -o %t %s -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash -// RUN: not cmp %t/a.hash %t/b.hash +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs // BEGIN a.swift private struct S { @@ -22,3 +34,23 @@ private struct S { var x: Int = 0 var y: Int = 0 } + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' S true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' S true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' S true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' S true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1S{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1S{{[^ ]+}} '' true diff --git a/test/InterfaceHash/edited_method_body-type-fingerprints.swift b/test/InterfaceHash/edited_method_body-type-fingerprints.swift deleted file mode 100644 index 726a36d8ba931..0000000000000 --- a/test/InterfaceHash/edited_method_body-type-fingerprints.swift +++ /dev/null @@ -1,31 +0,0 @@ -// REQUIRES: shell -// Also uses awk: -// XFAIL OS=windows - -// When adding a private protocol method, the interface hash should stay the same -// The per-type fingerprint should change - -// RUN: %empty-directory(%t) -// RUN: %{python} %utils/split_file.py -o %t %s -// RUN: cp %t/{a,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main -// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/a-processed.swiftdeps -// RUN: cp %t/{b,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main -// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/b-processed.swiftdeps - -// RUN: cmp %t/{a,b}-processed.swiftdeps - -// BEGIN a.swift -class C { - func f2() -> Int { - return 0 - } -} - -// BEGIN b.swift -class C { - func f2() -> Int { - return 1 - } -} diff --git a/test/InterfaceHash/edited_method_body.swift b/test/InterfaceHash/edited_method_body.swift index b0aebce44e500..016a835646d1a 100644 --- a/test/InterfaceHash/edited_method_body.swift +++ b/test/InterfaceHash/edited_method_body.swift @@ -1,8 +1,20 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + // RUN: %empty-directory(%t) // RUN: %{python} %utils/split_file.py -o %t %s -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash -// RUN: cmp %t/a.hash %t/b.hash +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/b-processed.swiftdeps + +// RUN: cmp %t/{a,b}-processed.swiftdeps // BEGIN a.swift class C { diff --git a/test/InterfaceHash/edited_property_getter-type-fingerprints.swift b/test/InterfaceHash/edited_property_getter-type-fingerprints.swift deleted file mode 100644 index 9024944bd865c..0000000000000 --- a/test/InterfaceHash/edited_property_getter-type-fingerprints.swift +++ /dev/null @@ -1,33 +0,0 @@ -// REQUIRES: shell -// Also uses awk: -// XFAIL OS=windows - -// When adding a private protocol method, the interface hash should stay the same -// The per-type fingerprint should change - -// RUN: %empty-directory(%t) -// RUN: %{python} %utils/split_file.py -o %t %s -// RUN: cp %t/{a,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main -// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/a-processed.swiftdeps -// RUN: cp %t/{b,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main -// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/b-processed.swiftdeps - -// RUN: cmp %t/{a,b}-processed.swiftdeps - -// BEGIN a.swift -class C { - var p: Int { - return 0 - } -} - -// BEGIN b.swift -class C { - var p: Int { - let x = 1 - return x - } -} - diff --git a/test/InterfaceHash/edited_property_getter.swift b/test/InterfaceHash/edited_property_getter.swift index deaeb8ccc08dc..05d99baac6493 100644 --- a/test/InterfaceHash/edited_property_getter.swift +++ b/test/InterfaceHash/edited_property_getter.swift @@ -1,8 +1,20 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + // RUN: %empty-directory(%t) // RUN: %{python} %utils/split_file.py -o %t %s -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash -// RUN: cmp %t/a.hash %t/b.hash +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/b-processed.swiftdeps + +// RUN: cmp %t/{a,b}-processed.swiftdeps // BEGIN a.swift class C { @@ -18,3 +30,4 @@ class C { return x } } + diff --git a/test/Interop/C/implementation-only-imports/Inputs/helper.h b/test/Interop/C/implementation-only-imports/Inputs/helper.h new file mode 100644 index 0000000000000..6eb11f5dbcb38 --- /dev/null +++ b/test/Interop/C/implementation-only-imports/Inputs/helper.h @@ -0,0 +1,8 @@ +#ifndef TEST_INTEROP_C_IMPLEMENTATION_ONLY_IMPORTS_INPUTS_HELPER_H +#define TEST_INTEROP_C_IMPLEMENTATION_ONLY_IMPORTS_INPUTS_HELPER_H + +inline int getFortyTwo() { + return 42; +}; + +#endif // TEST_INTEROP_C_IMPLEMENTATION_ONLY_IMPORTS_INPUTS_HELPER_H diff --git a/test/Interop/C/implementation-only-imports/Inputs/module.modulemap b/test/Interop/C/implementation-only-imports/Inputs/module.modulemap new file mode 100644 index 0000000000000..dd845b25d568c --- /dev/null +++ b/test/Interop/C/implementation-only-imports/Inputs/module.modulemap @@ -0,0 +1,9 @@ +module UserA { + header "user_a.h" + export * +} + +module UserB { + header "user_b.h" + export * +} diff --git a/test/Interop/C/implementation-only-imports/Inputs/user_a.h b/test/Interop/C/implementation-only-imports/Inputs/user_a.h new file mode 100644 index 0000000000000..bebbd757c5561 --- /dev/null +++ b/test/Interop/C/implementation-only-imports/Inputs/user_a.h @@ -0,0 +1,6 @@ +#ifndef TEST_INTEROP_C_IMPLEMENTATION_ONLY_IMPORTS_INPUTS_USERA_H +#define TEST_INTEROP_C_IMPLEMENTATION_ONLY_IMPORTS_INPUTS_USERA_H + +#include "helper.h" + +#endif // TEST_INTEROP_C_IMPLEMENTATION_ONLY_IMPORTS_INPUTS_USERA_H diff --git a/test/Interop/C/implementation-only-imports/Inputs/user_b.h b/test/Interop/C/implementation-only-imports/Inputs/user_b.h new file mode 100644 index 0000000000000..2beac678bf1d2 --- /dev/null +++ b/test/Interop/C/implementation-only-imports/Inputs/user_b.h @@ -0,0 +1,6 @@ +#ifndef TEST_INTEROP_C_IMPLEMENTATION_ONLY_IMPORTS_INPUTS_USERB_H +#define TEST_INTEROP_C_IMPLEMENTATION_ONLY_IMPORTS_INPUTS_USERB_H + +#include "helper.h" + +#endif // TEST_INTEROP_C_IMPLEMENTATION_ONLY_IMPORTS_INPUTS_USERB_H diff --git a/test/Interop/C/implementation-only-imports/prefer-a-visible-symbol-over-implementation-only-ones.swift b/test/Interop/C/implementation-only-imports/prefer-a-visible-symbol-over-implementation-only-ones.swift new file mode 100644 index 0000000000000..a7a04c655547c --- /dev/null +++ b/test/Interop/C/implementation-only-imports/prefer-a-visible-symbol-over-implementation-only-ones.swift @@ -0,0 +1,27 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-module -o %t/FortyTwo.swiftmodule -I %S/Inputs %s + +// REQUIRES: SR-13785 + +// TODO: Fix @_implementationOnly to consider all symbol sources + +// If a symbol comes from two modules, one of which is marked as +// @_implementationOnly, Swift may choose the @_implementationOnly source +// and then error out due to the symbol being hidden. + +// Swift should consider all sources for the symbol and recognize that the +// symbol is not hidden behind @_implementationOnly in all modules. + +// E.g: +// In this test case, UserA and UserB both textually include `helper.h`, +// therefore both export `getFortyTwo()`. +// This test verifies that even though Swift chooses UserA.getFortyTwo(), we +// shouldn't get an error, because the symbol is also exported from UserB. + +@_implementationOnly import UserA +import UserB + +@_inlineable +public func callFortyTwo() -> CInt { + return getFortyTwo() +} diff --git a/test/Interop/C/modules/print-qualified-clang-types/Inputs/HelperModule.swift b/test/Interop/C/modules/print-qualified-clang-types/Inputs/HelperModule.swift new file mode 100644 index 0000000000000..38cd0e7b41e91 --- /dev/null +++ b/test/Interop/C/modules/print-qualified-clang-types/Inputs/HelperModule.swift @@ -0,0 +1,2 @@ +import ForeignA +@_exported import ForeignB diff --git a/test/Interop/C/modules/print-qualified-clang-types/Inputs/MainModule.swift b/test/Interop/C/modules/print-qualified-clang-types/Inputs/MainModule.swift new file mode 100644 index 0000000000000..6f7a6a5629788 --- /dev/null +++ b/test/Interop/C/modules/print-qualified-clang-types/Inputs/MainModule.swift @@ -0,0 +1,3 @@ +import HelperModule + +public func funcTakingForeignStruct(_ param: ForeignStruct) {} diff --git a/test/Interop/C/modules/print-qualified-clang-types/Inputs/foreign-a.h b/test/Interop/C/modules/print-qualified-clang-types/Inputs/foreign-a.h new file mode 100644 index 0000000000000..ca297206959cc --- /dev/null +++ b/test/Interop/C/modules/print-qualified-clang-types/Inputs/foreign-a.h @@ -0,0 +1 @@ +#include "textual-header.h" diff --git a/test/Interop/C/modules/print-qualified-clang-types/Inputs/foreign-b.h b/test/Interop/C/modules/print-qualified-clang-types/Inputs/foreign-b.h new file mode 100644 index 0000000000000..ca297206959cc --- /dev/null +++ b/test/Interop/C/modules/print-qualified-clang-types/Inputs/foreign-b.h @@ -0,0 +1 @@ +#include "textual-header.h" diff --git a/test/Interop/C/modules/print-qualified-clang-types/Inputs/module.modulemap b/test/Interop/C/modules/print-qualified-clang-types/Inputs/module.modulemap new file mode 100644 index 0000000000000..bc5f5f4f50bab --- /dev/null +++ b/test/Interop/C/modules/print-qualified-clang-types/Inputs/module.modulemap @@ -0,0 +1,13 @@ +module ForeignA { + // Nest the header in a sub-module to make sure these are handled correctly. + module Sub { + header "foreign-a.h" + } +} + +module ForeignB { + // Nest the header in a sub-module to make sure these are handled correctly. + module Sub { + header "foreign-b.h" + } +} diff --git a/test/Interop/C/modules/print-qualified-clang-types/Inputs/textual-header.h b/test/Interop/C/modules/print-qualified-clang-types/Inputs/textual-header.h new file mode 100644 index 0000000000000..a53012248d706 --- /dev/null +++ b/test/Interop/C/modules/print-qualified-clang-types/Inputs/textual-header.h @@ -0,0 +1 @@ +typedef struct {} ForeignStruct; diff --git a/test/Interop/C/modules/print-qualified-clang-types/print-qualified-clang-types.swift b/test/Interop/C/modules/print-qualified-clang-types/print-qualified-clang-types.swift new file mode 100644 index 0000000000000..13a78745481e8 --- /dev/null +++ b/test/Interop/C/modules/print-qualified-clang-types/print-qualified-clang-types.swift @@ -0,0 +1,36 @@ +// Check that when qualifying Clang types with a module name, we choose a +// visible module. Clang types need special treatment because multiple Clang +// modules can contain the same type declarations from a textually included +// header, but not all of these modules may be visible. If we choose a module +// that isn't visible, we produce `.swiftinterface` files that don't compile. +// +// To test this, the test sets up the following structure: +// +// MainModule (Swift module) +// import HelperModule (Swift module) +// import ForeignA (Clang module) +// #include "textual-header.h" +// @_exported import ForeignB (Clang module) +// #include "textual-header.h" +// +// `ForeignA` and `ForeignB` both include the same textual header, which +// defines the struct `ForeignStruct`. +// +// Because `ForeignB` is re-exported by `HelperModule`, it is visible from +// `MainModule`, but `ForeignA` is not. This means that when `ForeignStruct` is +// used in `MainModule`, its qualified name should be printed as +// `ForeignB.ForeignStruct`, not `ForeignA.ForeignStruct`. +// +// In addition to checking for the presence of the expected string in the +// `.swiftinterface` file, we also verify that it compiles without error. +// +// This is a regression test for https://bugs.swift.org/browse/SR-13032. + +// RUN: %empty-directory(%t) +// RUN: mkdir %t/helper_module %t/main_module +// RUN: %target-swift-frontend -enable-library-evolution -swift-version 5 -emit-module -o %t/helper_module/HelperModule.swiftmodule %S/Inputs/HelperModule.swift -I %S/Inputs +// RUN: %target-swift-frontend -enable-library-evolution -swift-version 5 -emit-module -o %t/main_module/MainModule.swiftmodule -emit-module-interface-path %t/main_module/MainModule.swiftinterface -I %t/helper_module %S/Inputs/MainModule.swift -I %S/Inputs +// RUN: %FileCheck --input-file=%t/main_module/MainModule.swiftinterface %s +// RUN: %target-swift-frontend -typecheck -swift-version 5 %t/main_module/MainModule.swiftinterface -I %t/helper_module -I %S/Inputs + +// CHECK: public func funcTakingForeignStruct(_ param: ForeignB.ForeignStruct) diff --git a/test/Interop/Cxx/class/Inputs/constructors-objc.h b/test/Interop/Cxx/class/Inputs/constructors-objc.h new file mode 100644 index 0000000000000..46d7667a0095d --- /dev/null +++ b/test/Interop/Cxx/class/Inputs/constructors-objc.h @@ -0,0 +1,5 @@ +#import + +struct ConstructorWithNSArrayParam { + ConstructorWithNSArrayParam(NSArray *array) {} +}; diff --git a/test/Interop/Cxx/class/Inputs/constructors.h b/test/Interop/Cxx/class/Inputs/constructors.h new file mode 100644 index 0000000000000..738b13472f70a --- /dev/null +++ b/test/Interop/Cxx/class/Inputs/constructors.h @@ -0,0 +1,45 @@ +struct ExplicitDefaultConstructor { + ExplicitDefaultConstructor() : x(42) {} + int x; +}; + +struct ImplicitDefaultConstructor { + int x = 42; +}; + +struct MemberOfClassType { + ImplicitDefaultConstructor member; +}; + +struct DefaultConstructorDeleted { + DefaultConstructorDeleted() = delete; + int &a; +}; + +struct ConstructorWithParam { + ConstructorWithParam(int val) : x(val + 42) {} + int x; +}; + +struct CopyAndMoveConstructor { + CopyAndMoveConstructor(const CopyAndMoveConstructor &) = default; + CopyAndMoveConstructor(CopyAndMoveConstructor &&) = default; +}; + +struct Base {}; + +struct ArgType { + int i = 42; +}; + +struct HasVirtualBase : public virtual Base { + HasVirtualBase() = delete; + HasVirtualBase(ArgType Arg) {} + int i; +}; + +struct EmptyStruct {}; + +struct IntWrapper { + int x; +}; diff --git a/test/Interop/Cxx/class/Inputs/module.modulemap b/test/Interop/Cxx/class/Inputs/module.modulemap index 04c3bdedfda87..53ee329a23d7a 100644 --- a/test/Interop/Cxx/class/Inputs/module.modulemap +++ b/test/Interop/Cxx/class/Inputs/module.modulemap @@ -6,6 +6,18 @@ module TypeClassification { header "type-classification.h" } +module Constructors { + header "constructors.h" +} + +module ConstructorsObjC { + header "constructors-objc.h" +} + +module LoadableTypes { + header "loadable-types.h" +} + module MemberwiseInitializer { header "memberwise-initializer.h" } diff --git a/test/Interop/Cxx/class/Inputs/synthesized-initializers.h b/test/Interop/Cxx/class/Inputs/synthesized-initializers.h deleted file mode 100644 index da20bf036bd10..0000000000000 --- a/test/Interop/Cxx/class/Inputs/synthesized-initializers.h +++ /dev/null @@ -1,5 +0,0 @@ -struct EmptyStruct {}; - -struct IntBox { - int x; -}; diff --git a/test/Interop/Cxx/class/Inputs/type-classification.h b/test/Interop/Cxx/class/Inputs/type-classification.h index 09322bb5d0d1e..176e22e1701af 100644 --- a/test/Interop/Cxx/class/Inputs/type-classification.h +++ b/test/Interop/Cxx/class/Inputs/type-classification.h @@ -157,6 +157,8 @@ struct StructDeletedDestructor { struct StructWithCopyConstructorAndValue { int value; + StructWithCopyConstructorAndValue() : value(0) {} + StructWithCopyConstructorAndValue(int value) : value(value) {} StructWithCopyConstructorAndValue( const StructWithCopyConstructorAndValue &other) : value(other.value) {} @@ -168,6 +170,9 @@ struct StructWithSubobjectCopyConstructorAndValue { struct StructWithCopyConstructorAndSubobjectCopyConstructorAndValue { StructWithCopyConstructorAndValue member; + StructWithCopyConstructorAndSubobjectCopyConstructorAndValue( + StructWithCopyConstructorAndValue member) + : member(member) {} StructWithCopyConstructorAndSubobjectCopyConstructorAndValue( const StructWithCopyConstructorAndSubobjectCopyConstructorAndValue &other) : member(other.member) {} diff --git a/test/Interop/Cxx/class/constructors-executable.swift b/test/Interop/Cxx/class/constructors-executable.swift new file mode 100644 index 0000000000000..04a8b9912b926 --- /dev/null +++ b/test/Interop/Cxx/class/constructors-executable.swift @@ -0,0 +1,34 @@ +// RUN: %target-run-simple-swift(-I %S/Inputs/ -Xfrontend -enable-cxx-interop) +// +// REQUIRES: executable_test + +import StdlibUnittest +import Constructors + +var CxxConstructorTestSuite = TestSuite("CxxConstructors") + +CxxConstructorTestSuite.test("ExplicitDefaultConstructor") { + let instance = ExplicitDefaultConstructor() + + expectEqual(42, instance.x) +} + +CxxConstructorTestSuite.test("ImplicitDefaultConstructor") { + let instance = ImplicitDefaultConstructor() + + expectEqual(42, instance.x) +} + +CxxConstructorTestSuite.test("MemberOfClassType") { + let instance = MemberOfClassType() + + expectEqual(42, instance.member.x) +} + +CxxConstructorTestSuite.test("ConstructorWithParam") { + let instance = ConstructorWithParam(2) + + expectEqual(44, instance.x) +} + +runAllTests() diff --git a/test/Interop/Cxx/class/constructors-irgen.swift b/test/Interop/Cxx/class/constructors-irgen.swift new file mode 100644 index 0000000000000..4c4999bac484e --- /dev/null +++ b/test/Interop/Cxx/class/constructors-irgen.swift @@ -0,0 +1,80 @@ +// Target-specific tests for C++ constructor call code generation. + +// RUN: %swift -module-name Swift -target x86_64-apple-macosx10.9 -dump-clang-diagnostics -I %S/Inputs -enable-cxx-interop -emit-ir %s -parse-stdlib -parse-as-library -disable-legacy-type-info | %FileCheck %s -check-prefix=ITANIUM_X64 +// RUN: %swift -module-name Swift -target armv7-none-linux-androideabi -dump-clang-diagnostics -I %S/Inputs -enable-cxx-interop -emit-ir %s -parse-stdlib -parse-as-library -disable-legacy-type-info | %FileCheck %s -check-prefix=ITANIUM_ARM +// RUN: %swift -module-name Swift -target x86_64-unknown-windows-msvc -dump-clang-diagnostics -I %S/Inputs -enable-cxx-interop -emit-ir %s -parse-stdlib -parse-as-library -disable-legacy-type-info | %FileCheck %s -check-prefix=MICROSOFT_X64 + +import Constructors +import TypeClassification + +typealias Void = () +struct UnsafePointer { } +struct UnsafeMutablePointer { } + +public func createHasVirtualBase() -> HasVirtualBase { + // ITANIUM_X64: define swiftcc void @"$ss20createHasVirtualBaseSo0bcD0VyF"(%TSo14HasVirtualBaseV* noalias nocapture sret %0) + // ITANIUM_X64-NOT: define + // ITANIUM_X64: call void @_ZN14HasVirtualBaseC1E7ArgType(%struct.HasVirtualBase* %{{[0-9]+}}, i32 %{{[0-9]+}}) + // + // ITANIUM_ARM: define protected swiftcc void @"$ss20createHasVirtualBaseSo0bcD0VyF"(%TSo14HasVirtualBaseV* noalias nocapture sret %0) + // To verify that the thunk is inlined, make sure there's no intervening + // `define`, i.e. the call to the C++ constructor happens in + // createHasVirtualBase(), not some later function. + // ITANIUM_ARM-NOT: define + // Note `this` return type. + // ITANIUM_ARM: call %struct.HasVirtualBase* @_ZN14HasVirtualBaseC1E7ArgType(%struct.HasVirtualBase* %{{[0-9]+}}, [1 x i32] %{{[0-9]+}}) + // + // MICROSOFT_X64: define dllexport swiftcc void @"$ss20createHasVirtualBaseSo0bcD0VyF"(%TSo14HasVirtualBaseV* noalias nocapture sret %0) + // MICROSOFT_X64-NOT: define + // Note `this` return type and implicit "most derived" argument. + // MICROSOFT_X64: call %struct.HasVirtualBase* @"??0HasVirtualBase@@QEAA@UArgType@@@Z"(%struct.HasVirtualBase* %{{[0-9]+}}, i32 %{{[0-9]+}}, i32 1) + return HasVirtualBase(ArgType()) +} + +public func createImplicitDefaultConstructor() -> ImplicitDefaultConstructor { + // ITANIUM_X64: define swiftcc i32 @"$ss32createImplicitDefaultConstructorSo0bcD0VyF"() + // ITANIUM_X64-NOT: define + // ITANIUM_X64: call void @_ZN26ImplicitDefaultConstructorC1Ev(%struct.ImplicitDefaultConstructor* %{{[0-9]+}}) + // + // ITANIUM_ARM: define protected swiftcc i32 @"$ss32createImplicitDefaultConstructorSo0bcD0VyF"() + // ITANIUM_ARM-NOT: define + // Note `this` return type. + // ITANIUM_ARM: call %struct.ImplicitDefaultConstructor* @_ZN26ImplicitDefaultConstructorC2Ev(%struct.ImplicitDefaultConstructor* %{{[0-9]+}}) + // + // MICROSOFT_X64: define dllexport swiftcc i32 @"$ss32createImplicitDefaultConstructorSo0bcD0VyF"() + // MICROSOFT_X64-NOT: define + // Note `this` return type but no implicit "most derived" argument. + // MICROSOFT_X64: call %struct.ImplicitDefaultConstructor* @"??0ImplicitDefaultConstructor@@QEAA@XZ"(%struct.ImplicitDefaultConstructor* %{{[0-9]+}}) + return ImplicitDefaultConstructor() +} + +public func createStructWithSubobjectCopyConstructorAndValue() { + // ITANIUM_X64-LABEL: define swiftcc void @"$ss48createStructWithSubobjectCopyConstructorAndValueyyF"() + // ITANIUM_X64: [[MEMBER:%.*]] = alloca %TSo33StructWithCopyConstructorAndValueV + // ITANIUM_X64: [[MEMBER_AS_STRUCT:%.*]] = bitcast %TSo33StructWithCopyConstructorAndValueV* %member to %struct.StructWithCopyConstructorAndValue* + // ITANIUM_X64: void @_ZN33StructWithCopyConstructorAndValueC1Ev(%struct.StructWithCopyConstructorAndValue* [[MEMBER_AS_STRUCT]]) + // ITANIUM_X64: call %TSo33StructWithCopyConstructorAndValueV* @"$sSo33StructWithCopyConstructorAndValueVWOc"(%TSo33StructWithCopyConstructorAndValueV* [[MEMBER]], %TSo33StructWithCopyConstructorAndValueV* [[TMP:%.*]]) + // ITANIUM_X64: [[OBJ_MEMBER:%.*]] = getelementptr inbounds %TSo42StructWithSubobjectCopyConstructorAndValueV, %TSo42StructWithSubobjectCopyConstructorAndValueV* %obj, i32 0, i32 0 + // ITANIUM_X64: call %TSo33StructWithCopyConstructorAndValueV* @"$sSo33StructWithCopyConstructorAndValueVWOb"(%TSo33StructWithCopyConstructorAndValueV* [[TMP]], %TSo33StructWithCopyConstructorAndValueV* [[OBJ_MEMBER]]) + // ITANIUM_X64: ret void + + // ITANIUM_ARM-LABEL: define protected swiftcc void @"$ss48createStructWithSubobjectCopyConstructorAndValueyyF"() + // ITANIUM_ARM: [[MEMBER:%.*]] = alloca %TSo33StructWithCopyConstructorAndValueV + // ITANIUM_ARM: [[MEMBER_AS_STRUCT:%.*]] = bitcast %TSo33StructWithCopyConstructorAndValueV* %member to %struct.StructWithCopyConstructorAndValue* + // ITANIUM_ARM: call %struct.StructWithCopyConstructorAndValue* @_ZN33StructWithCopyConstructorAndValueC2Ev(%struct.StructWithCopyConstructorAndValue* [[MEMBER_AS_STRUCT]]) + // ITANIUM_ARM: call %TSo33StructWithCopyConstructorAndValueV* @"$sSo33StructWithCopyConstructorAndValueVWOc"(%TSo33StructWithCopyConstructorAndValueV* [[MEMBER]], %TSo33StructWithCopyConstructorAndValueV* [[TMP:%.*]]) + // ITANIUM_ARM: [[OBJ_MEMBER:%.*]] = getelementptr inbounds %TSo42StructWithSubobjectCopyConstructorAndValueV, %TSo42StructWithSubobjectCopyConstructorAndValueV* %obj, i32 0, i32 0 + // ITANIUM_ARM: call %TSo33StructWithCopyConstructorAndValueV* @"$sSo33StructWithCopyConstructorAndValueVWOb"(%TSo33StructWithCopyConstructorAndValueV* [[TMP]], %TSo33StructWithCopyConstructorAndValueV* [[OBJ_MEMBER]]) + // ITANIUM_ARM: ret void + + // MICROSOFT_X64-LABEL: define dllexport swiftcc void @"$ss48createStructWithSubobjectCopyConstructorAndValueyyF"() + // MICROSOFT_X64: [[MEMBER:%.*]] = alloca %TSo33StructWithCopyConstructorAndValueV + // MICROSOFT_X64: [[MEMBER_AS_STRUCT:%.*]] = bitcast %TSo33StructWithCopyConstructorAndValueV* %member to %struct.StructWithCopyConstructorAndValue* + // MICROSOFT_X64: call %struct.StructWithCopyConstructorAndValue* @"??0StructWithCopyConstructorAndValue@@QEAA@XZ"(%struct.StructWithCopyConstructorAndValue* [[MEMBER_AS_STRUCT]]) + // MICROSOFT_X64: call %TSo33StructWithCopyConstructorAndValueV* @"$sSo33StructWithCopyConstructorAndValueVWOc"(%TSo33StructWithCopyConstructorAndValueV* [[MEMBER]], %TSo33StructWithCopyConstructorAndValueV* [[TMP:%.*]]) + // MICROSOFT_X64: [[OBJ_MEMBER:%.*]] = getelementptr inbounds %TSo42StructWithSubobjectCopyConstructorAndValueV, %TSo42StructWithSubobjectCopyConstructorAndValueV* %obj, i32 0, i32 0 + // MICROSOFT_X64: call %TSo33StructWithCopyConstructorAndValueV* @"$sSo33StructWithCopyConstructorAndValueVWOb"(%TSo33StructWithCopyConstructorAndValueV* [[TMP]], %TSo33StructWithCopyConstructorAndValueV* [[OBJ_MEMBER]]) + // MICROSOFT_X64: ret void + let member = StructWithCopyConstructorAndValue() + let obj = StructWithSubobjectCopyConstructorAndValue(member: member) +} diff --git a/test/Interop/Cxx/class/constructors-module-interface.swift b/test/Interop/Cxx/class/constructors-module-interface.swift new file mode 100644 index 0000000000000..672c11692641c --- /dev/null +++ b/test/Interop/Cxx/class/constructors-module-interface.swift @@ -0,0 +1,38 @@ +// RUN: %target-swift-ide-test -print-module -module-to-print=Constructors -I %S/Inputs/ -source-filename=x -enable-cxx-interop | %FileCheck %s + +// CHECK: struct ExplicitDefaultConstructor { +// CHECK-NEXT: var x: Int32 +// CHECK-NEXT: init() +// CHECK-NEXT: } +// CHECK-NEXT: struct ImplicitDefaultConstructor { +// CHECK-NEXT: var x: Int32 +// CHECK-NEXT: init() +// CHECK-NEXT: init(x: Int32) +// CHECK-NEXT: } +// CHECK-NEXT: struct MemberOfClassType { +// CHECK-NEXT: var member: ImplicitDefaultConstructor +// CHECK-NEXT: init() +// CHECK-NEXT: init(member: ImplicitDefaultConstructor) +// CHECK-NEXT: } +// CHECK-NEXT: struct DefaultConstructorDeleted { +// CHECK-NEXT: var a: UnsafeMutablePointer +// CHECK-NEXT: init(a: UnsafeMutablePointer) +// CHECK-NEXT: } +// CHECK-NEXT: struct ConstructorWithParam { +// CHECK-NEXT: var x: Int32 +// CHECK-NEXT: init(_ val: Int32) +// CHECK-NEXT: } +// CHECK-NEXT: struct CopyAndMoveConstructor { +// CHECK-NEXT: } +// CHECK-NEXT: struct Base { +// CHECK-NEXT: init() +// CHECK-NEXT: } +// CHECK-NEXT: struct ArgType { +// CHECK-NEXT: var i: Int32 +// CHECK-NEXT: init() +// CHECK-NEXT: init(i: Int32) +// CHECK-NEXT: } +// CHECK-NEXT: struct HasVirtualBase { +// CHECK-NEXT: var i: Int32 +// CHECK-NEXT: init(_ Arg: ArgType) +// CHECK-NEXT: } diff --git a/test/Interop/Cxx/class/constructors-objc-irgen.swift b/test/Interop/Cxx/class/constructors-objc-irgen.swift new file mode 100644 index 0000000000000..9b9a0c939708f --- /dev/null +++ b/test/Interop/Cxx/class/constructors-objc-irgen.swift @@ -0,0 +1,16 @@ +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -I %S/Inputs -enable-cxx-interop -emit-ir %s | %FileCheck %s + +// REQUIRES: CPU=x86_64 +// REQUIRES: objc_interop + +import ConstructorsObjC + +public func createConstructorWithNSArrayParam() -> ConstructorWithNSArrayParam { + // CHECK: define swiftcc void @"$s4main33createConstructorWithNSArrayParamSo0cdeF0VyF"() + // CHECK-NOT: define + // CHECK: [[VAR:%[0-9]+]] = alloca %TSo27ConstructorWithNSArrayParamV, align 1 + // CHECK: %{{[0-9]+}} = call swiftcc %TSo7NSArrayC* @"$sSa10FoundationE19_bridgeToObjectiveCSo7NSArrayCyF"(%swift.bridge* %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* @"$sypN", i32 0, i32 1)) + // CHECK: [[CAST_VAR:%[0-9]+]] = bitcast %TSo27ConstructorWithNSArrayParamV* [[VAR]] to %struct.ConstructorWithNSArrayParam* + // CHECK: call void @_ZN27ConstructorWithNSArrayParamC1EP7NSArray(%struct.ConstructorWithNSArrayParam* [[CAST_VAR]], [[VAR]]* %{{[0-9]+}}) + return ConstructorWithNSArrayParam([]) +} diff --git a/test/Interop/Cxx/class/constructors-objc-module-interface.swift b/test/Interop/Cxx/class/constructors-objc-module-interface.swift new file mode 100644 index 0000000000000..b636bfae24c64 --- /dev/null +++ b/test/Interop/Cxx/class/constructors-objc-module-interface.swift @@ -0,0 +1,10 @@ +// Test that Objective-C types passed to a C++ constructor are bridged +// correctly. + +// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -print-module -module-to-print=ConstructorsObjC -I %S/Inputs/ -source-filename=x -enable-cxx-interop | %FileCheck %s + +// REQUIRES: objc_interop + +// CHECK: struct ConstructorWithNSArrayParam { +// CHECK-NEXT: init(_ array: [Any]!) +// CHECK-NEXT: } diff --git a/test/Interop/Cxx/class/constructors-objc-silgen.swift b/test/Interop/Cxx/class/constructors-objc-silgen.swift new file mode 100644 index 0000000000000..6e1b907d2b472 --- /dev/null +++ b/test/Interop/Cxx/class/constructors-objc-silgen.swift @@ -0,0 +1,11 @@ +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -I %S/Inputs -enable-cxx-interop -emit-sil %s | %FileCheck %s + +// REQUIRES: objc_interop + +import ConstructorsObjC + +// CHECK: [[VAR:%[0-9]+]] = alloc_stack $ConstructorWithNSArrayParam +// CHECK: [[OPT_ARRAY:%[0-9]+]] = enum $Optional, #Optional.some!enumelt, %{{[0-9]+}} : $NSArray +// CHECK: [[FUNC:%[0-9]+]] = function_ref @_ZN27ConstructorWithNSArrayParamC1EP7NSArray : $@convention(c) (Optional) -> @out ConstructorWithNSArrayParam +// CHECK: %{{[0-9]+}} = apply [[FUNC]]([[VAR]], [[OPT_ARRAY]]) : $@convention(c) (Optional) -> @out ConstructorWithNSArrayParam +let _ = ConstructorWithNSArrayParam([]) diff --git a/test/Interop/Cxx/class/constructors-silgen.swift b/test/Interop/Cxx/class/constructors-silgen.swift new file mode 100644 index 0000000000000..3e256b4746e7e --- /dev/null +++ b/test/Interop/Cxx/class/constructors-silgen.swift @@ -0,0 +1,58 @@ +// RUN: %target-swift-frontend -I %S/Inputs -enable-cxx-interop -emit-silgen %s | %FileCheck %s + +import Constructors + +// The most important thing to test here is that the constructor result is +// returned with an @out attribute. +// CHECK-LABEL: sil [ossa] @$s4main24testConstructorWithParamyyF : $@convention(thin) () -> () +// CHECK: [[VAR:%[0-9]+]] = alloc_stack $ConstructorWithParam +// CHECK: [[LITERAL:%[0-9]+]] = integer_literal $Builtin.IntLiteral, 42 +// CHECK: [[INT:%[0-9]+]] = apply %{{[0-9]+}}([[LITERAL]], %{{[0-9]+}}) +// CHECK: [[FUNC:%[0-9]+]] = function_ref @{{_ZN20ConstructorWithParamC1Ei|\?\?0ConstructorWithParam@@QEAA@H@Z}} : $@convention(c) (Int32) -> @out ConstructorWithParam +// CHECK: %{{[0-9]+}} = apply [[FUNC]]([[VAR]], [[INT]]) : $@convention(c) (Int32) -> @out ConstructorWithParam +// CHECK-LABEL: end sil function '$s4main24testConstructorWithParamyyF' + +// CHECK-LABEL: sil [clang ConstructorWithParam.init] @{{_ZN20ConstructorWithParamC1Ei|\?\?0ConstructorWithParam@@QEAA@H@Z}} : $@convention(c) (Int32) -> @out ConstructorWithParam +public func testConstructorWithParam() { + let c = ConstructorWithParam(42) +} + +// CHECK-LABEL: sil [ossa] @$s4main18emptyTypeNoArgInityyF : $@convention(thin) () -> () +// CHECK: [[AS:%.*]] = alloc_stack $EmptyStruct +// CHECK: [[FN:%.*]] = function_ref @{{_ZN11EmptyStructC1Ev|\?\?0EmptyStruct@@QEAA@XZ}} : $@convention(c) () -> @out EmptyStruct +// CHECK: apply [[FN]]([[AS]]) : $@convention(c) () -> @out EmptyStruct +// CHECK-LABEL: end sil function '$s4main18emptyTypeNoArgInityyF' + +// CHECL-LABEL: sil [clang EmptyStruct.init] @{{_ZN11EmptyStructC1Ev|\?\?0EmptyStruct@@QEAA@XZ}} : $@convention(c) () -> @out EmptyStruct +public func emptyTypeNoArgInit() { + let e = EmptyStruct() +} + +// CHECK-LABEL: sil [ossa] @$s4main25singleMemberTypeNoArgInityyF : $@convention(thin) () -> () +// CHECK: [[AS:%.*]] = alloc_stack $IntWrapper +// CHECK: [[FN:%.*]] = function_ref @{{_ZN10IntWrapperC1Ev|\?\?0IntWrapper@@QEAA@XZ}} : $@convention(c) () -> @out IntWrapper +// CHECK: apply [[FN]]([[AS]]) : $@convention(c) () -> @out IntWrapper +// CHECK-LABEL: end sil function '$s4main25singleMemberTypeNoArgInityyF' + +//CHECK-LABEL: sil [clang IntWrapper.init] @{{_ZN10IntWrapperC1Ev|\?\?0IntWrapper@@QEAA@XZ}} : $@convention(c) () -> @out IntWrapper +public func singleMemberTypeNoArgInit() { + let i = IntWrapper() +} + +// CHECK-LABEL: sil shared [transparent] [serializable] [ossa] @$sSo10IntWrapperV1xABs5Int32V_tcfC : $@convention(method) (Int32, @thin IntWrapper.Type) -> IntWrapper +// CHECK: bb0([[I:%[0-9]+]] : $Int32, %{{[0-9]+}} : $@thin IntWrapper.Type): +// CHECK-NEXT: [[S:%.*]] = struct $IntWrapper ([[I]] : $Int32) +// CHECK-NEXT: return [[S]] +// CHECK-LABEL: end sil function '$sSo10IntWrapperV1xABs5Int32V_tcfC' +public func singleMemberTypeValueInit() { + let i = IntWrapper(x: 42) +} + +// CHECK-LABEL: sil shared [transparent] [serializable] [ossa] @$sSo25DefaultConstructorDeletedV1aABSpys5Int32VG_tcfC : $@convention(method) (UnsafeMutablePointer, @thin DefaultConstructorDeleted.Type) -> DefaultConstructorDeleted +// CHECK: bb0([[A:%.*]] : $UnsafeMutablePointer +// CHECK-NEXT: [[OUT:%.*]] = struct $DefaultConstructorDeleted ([[A]] : $UnsafeMutablePointer) +// CHECK-NEXT: return [[OUT]] : $DefaultConstructorDeleted +// CHECK-LABEL: end sil function '$sSo25DefaultConstructorDeletedV1aABSpys5Int32VG_tcfC' +public func deletedConstructor(a: UnsafeMutablePointer) { + let deletedExplicitly = DefaultConstructorDeleted(a: a) +} diff --git a/test/Interop/Cxx/class/constructors-typechecker.swift b/test/Interop/Cxx/class/constructors-typechecker.swift new file mode 100644 index 0000000000000..c78328c2b39c3 --- /dev/null +++ b/test/Interop/Cxx/class/constructors-typechecker.swift @@ -0,0 +1,13 @@ +// RUN: %target-typecheck-verify-swift -verify-ignore-unknown -I %S/Inputs -enable-cxx-interop + +import Constructors + +let explicit = ExplicitDefaultConstructor() + +let implicit = ImplicitDefaultConstructor() + +let deletedImplicitly = ConstructorWithParam() // expected-error {{missing argument for parameter #1 in call}} + +let deletedExplicitly = DefaultConstructorDeleted() // expected-error {{missing argument for parameter 'a' in call}} + +let withArg = ConstructorWithParam(42) diff --git a/test/Interop/Cxx/class/synthesized-initializers-silgen.swift b/test/Interop/Cxx/class/synthesized-initializers-silgen.swift deleted file mode 100644 index ea74f8ab4eecb..0000000000000 --- a/test/Interop/Cxx/class/synthesized-initializers-silgen.swift +++ /dev/null @@ -1,46 +0,0 @@ -// RUN: %target-swift-frontend -I %S/Inputs -enable-cxx-interop -emit-silgen %s | %FileCheck %s - -import SynthesizedInitializers - -// CHECK-LABEL: sil shared [transparent] [serializable] [ossa] @$sSo11EmptyStructVABycfC : $@convention(method) (@thin EmptyStruct.Type) -> EmptyStruct -// CHECK: bb0(%{{[0-9]+}} : $@thin EmptyStruct.Type): -// CHECK-NEXT: [[BOX:%.*]] = alloc_box ${ var EmptyStruct } -// CHECK-NEXT: [[UNINIT:%.*]] = mark_uninitialized [rootself] [[BOX]] : ${ var EmptyStruct } -// CHECK-NEXT: [[PTR:%.*]] = project_box [[UNINIT]] : ${ var EmptyStruct }, 0 -// CHECK-NEXT: [[OBJ:%.*]] = builtin "zeroInitializer"() : $EmptyStruct -// CHECK-NEXT: [[PA:%.*]] = begin_access [modify] [unknown] [[PTR]] : $*EmptyStruct -// CHECK-NEXT: assign [[OBJ]] to [[PA]] -// CHECK-NEXT: end_access [[PA]] -// CHECK-NEXT: [[OUT:%.*]] = load [trivial] [[PTR]] -// CHECK-NEXT: destroy_value [[UNINIT]] -// CHECK-NEXT: return [[OUT]] -// CHECK-LABEL: end sil function '$sSo11EmptyStructVABycfC' -public func emptyTypeNoArgInit() { - let e = EmptyStruct() -} - -// CHECK-LABEL: sil shared [transparent] [serializable] [ossa] @$sSo6IntBoxVABycfC : $@convention(method) (@thin IntBox.Type) -> IntBox -// CHECK: bb0(%{{[0-9]+}} : $@thin IntBox.Type): -// CHECK-NEXT: [[BOX:%.*]] = alloc_box ${ var IntBox } -// CHECK-NEXT: [[UNINIT:%.*]] = mark_uninitialized [rootself] [[BOX]] : ${ var IntBox } -// CHECK-NEXT: [[PTR:%.*]] = project_box [[UNINIT]] : ${ var IntBox }, 0 -// CHECK-NEXT: [[OBJ:%.*]] = builtin "zeroInitializer"() : $IntBox -// CHECK-NEXT: [[PA:%.*]] = begin_access [modify] [unknown] [[PTR]] : $*IntBox -// CHECK-NEXT: assign [[OBJ]] to [[PA]] -// CHECK-NEXT: end_access [[PA]] -// CHECK-NEXT: [[OUT:%.*]] = load [trivial] [[PTR]] -// CHECK-NEXT: destroy_value [[UNINIT]] -// CHECK-NEXT: return [[OUT]] -// CHECK-LABEL: end sil function '$sSo6IntBoxVABycfC' -public func singleMemberTypeNoArgInit() { - let i = IntBox() -} - -// CHECK-LABEL: sil shared [transparent] [serializable] [ossa] @$sSo6IntBoxV1xABs5Int32V_tcfC : $@convention(method) (Int32, @thin IntBox.Type) -> IntBox -// CHECK: bb0([[I:%[0-9]+]] : $Int32, %{{[0-9]+}} : $@thin IntBox.Type): -// CHECK-NEXT: [[S:%.*]] = struct $IntBox ([[I]] : $Int32) -// CHECK-NEXT: return [[S]] -// CHECK-LABEL: end sil function '$sSo6IntBoxV1xABs5Int32V_tcfC' -public func singleMemberTypeValueInit() { - let i = IntBox(x: 42) -} diff --git a/test/Interop/Cxx/class/type-classification-non-trivial-irgen.swift b/test/Interop/Cxx/class/type-classification-non-trivial-irgen.swift index c0a48574a1493..2939b78732840 100644 --- a/test/Interop/Cxx/class/type-classification-non-trivial-irgen.swift +++ b/test/Interop/Cxx/class/type-classification-non-trivial-irgen.swift @@ -12,12 +12,15 @@ import TypeClassification // CHECK-LABEL: define {{.*}}i1 @"$s4main37testStructWithCopyConstructorAndValueSbyF" // CHECK: [[OBJ:%.*]] = alloca %TSo33StructWithCopyConstructorAndValueV -// CHECK: [[VAL_ELEMENT:%.*]] = getelementptr inbounds %TSo33StructWithCopyConstructorAndValueV, %TSo33StructWithCopyConstructorAndValueV* [[OBJ]], i32 0, i32 0 -// CHECK: [[VAL_INT:%.*]] = getelementptr inbounds %Ts5Int32V, %Ts5Int32V* [[VAL_ELEMENT]], i32 0, i32 0 -// CHECK: store i32 42, i32* [[VAL_INT]] -// CHECK: ret i1 true +// CHECK: [[STRUCT:%.*]] = bitcast %TSo33StructWithCopyConstructorAndValueV* [[OBJ]] to %struct.StructWithCopyConstructorAndValue* +// CHECK: call {{.*}}@{{_ZN33StructWithCopyConstructorAndValueC(1|2)Ei|"\?\?0StructWithCopyConstructorAndValue@@QEAA@H@Z"}}(%struct.StructWithCopyConstructorAndValue* {{(noalias )?}}[[STRUCT]], i32 42) +// CHECK: [[OBJ_VAL:%.*]] = getelementptr inbounds %TSo33StructWithCopyConstructorAndValueV, %TSo33StructWithCopyConstructorAndValueV* [[OBJ]], i32 0, i32 0 +// CHECK: [[I_VAL:%.*]] = getelementptr inbounds %Ts5Int32V, %Ts5Int32V* [[OBJ_VAL]], i32 0, i32 0 +// CHECK: [[I_VAL_VAL:%.*]] = load i32, i32* [[OBJ_VAL]] +// CHECK: [[OUT:%.*]] = icmp eq i32 [[I_VAL_VAL]], 42 +// CHECK: ret i1 [[OUT]] public func testStructWithCopyConstructorAndValue() -> Bool { - let obj = StructWithCopyConstructorAndValue(value: 42) + let obj = StructWithCopyConstructorAndValue(42) return obj.value == 42 } @@ -26,38 +29,39 @@ public func testStructWithCopyConstructorAndValue() -> Bool { // CHECK: [[OBJ:%.*]] = alloca %TSo42StructWithSubobjectCopyConstructorAndValueV // CHECK: alloca %TSo33StructWithCopyConstructorAndValueV // CHECK: [[TMP:%.*]] = alloca %TSo33StructWithCopyConstructorAndValueV -// CHECK: [[MEMBER_ELEMENT:%.*]] = getelementptr inbounds %TSo33StructWithCopyConstructorAndValueV, %TSo33StructWithCopyConstructorAndValueV* [[MEMBER]], i32 0, i32 0 -// CHECK: [[MEMBER_VALUE:%.*]] = getelementptr inbounds %Ts5Int32V, %Ts5Int32V* [[MEMBER_ELEMENT]], i32 0, i32 0 -// CHECK: store i32 42, i32* [[MEMBER_VALUE]] +// CHECK: [[MEMBER_STRUCT:%.*]] = bitcast %TSo33StructWithCopyConstructorAndValueV* %member to %struct.StructWithCopyConstructorAndValue* +// CHECK: call {{.*}}@{{_ZN33StructWithCopyConstructorAndValueC(1|2)Ei|"\?\?0StructWithCopyConstructorAndValue@@QEAA@H@Z"}}(%struct.StructWithCopyConstructorAndValue* {{(noalias )?}}[[MEMBER_STRUCT]], i32 42) // CHECK: [[TEMP_MEMBER:%.*]] = getelementptr inbounds %TSo33StructWithCopyConstructorAndValueV, %TSo33StructWithCopyConstructorAndValueV* [[TMP]], i32 0, i32 0 // CHECK: [[TEMP_MEMBER_VALUE:%.*]] = getelementptr inbounds %Ts5Int32V, %Ts5Int32V* [[TEMP_MEMBER]], i32 0, i32 0 // CHECK: [[LHS:%.*]] = load i32, i32* [[TEMP_MEMBER_VALUE]] // CHECK: [[OUT:%.*]] = icmp eq i32 [[LHS]], 42 // CHECK: ret i1 [[OUT]] public func testStructWithSubobjectCopyConstructorAndValue() -> Bool { - let member = StructWithCopyConstructorAndValue(value: 42) + let member = StructWithCopyConstructorAndValue(42) let obj = StructWithSubobjectCopyConstructorAndValue(member: member) return obj.member.value == 42 } // CHECK-LABEL: define {{.*}}i1 @"$s4main041testStructWithCopyConstructorAndSubobjectefG5ValueSbyF"() // CHECK: [[MEMBER:%.*]] = alloca %TSo33StructWithCopyConstructorAndValueV -// CHECK: alloca %TSo037StructWithCopyConstructorAndSubobjectcdE5ValueV -// CHECK: alloca %TSo33StructWithCopyConstructorAndValueV +// CHECK: [[OBJ:%.*]] = alloca %TSo037StructWithCopyConstructorAndSubobjectcdE5ValueV // CHECK: [[TEMP:%.*]] = alloca %TSo33StructWithCopyConstructorAndValueV -// CHECK: [[MEMBER_VAL:%.*]] = getelementptr inbounds %TSo33StructWithCopyConstructorAndValueV, %TSo33StructWithCopyConstructorAndValueV* [[MEMBER]], i32 0, i32 0 -// CHECK: [[MEMBER_VAL_VAL:%.*]] = getelementptr inbounds %Ts5Int32V, %Ts5Int32V* [[MEMBER_VAL]], i32 0, i32 0 -// CHECK: store i32 42, i32* [[MEMBER_VAL_VAL]] -// CHECK: [[TEMP_MEMBER:%.*]] = getelementptr inbounds %TSo33StructWithCopyConstructorAndValueV, %TSo33StructWithCopyConstructorAndValueV* [[TEMP]], i32 0, i32 0 +// CHECK: [[TEMP2:%.*]] = alloca %TSo33StructWithCopyConstructorAndValueV +// CHECK: [[MEMBER_STRUCT:%.*]] = bitcast %TSo33StructWithCopyConstructorAndValueV* %member to %struct.StructWithCopyConstructorAndValue* +// CHECK: call {{.*}}@{{_ZN33StructWithCopyConstructorAndValueC(1|2)Ei|"\?\?0StructWithCopyConstructorAndValue@@QEAA@H@Z"}}(%struct.StructWithCopyConstructorAndValue* {{(noalias )?}}[[MEMBER_STRUCT]], i32 42) +// CHECK: [[TEMP_AS_STRUCT:%.*]] = bitcast %TSo33StructWithCopyConstructorAndValueV* [[TEMP]] to %struct.StructWithCopyConstructorAndValue* +// CHECK: [[OBJ_AS_STRUCT:%.*]] = bitcast %TSo037StructWithCopyConstructorAndSubobjectcdE5ValueV* [[OBJ]] to %struct.StructWithCopyConstructorAndSubobjectCopyConstructorAndValue* +// CHECK: call {{.*}}@{{_ZN60StructWithCopyConstructorAndSubobjectCopyConstructorAndValueC(1|2)E33StructWithCopyConstructorAndValue|"\?\?0StructWithCopyConstructorAndSubobjectCopyConstructorAndValue@@QEAA@UStructWithCopyConstructorAndValue@@@Z"}}(%struct.StructWithCopyConstructorAndSubobjectCopyConstructorAndValue* {{(noalias )?}}[[OBJ_AS_STRUCT]], %struct.StructWithCopyConstructorAndValue* [[TEMP_AS_STRUCT]]) +// CHECK: [[TEMP_MEMBER:%.*]] = getelementptr inbounds %TSo33StructWithCopyConstructorAndValueV, %TSo33StructWithCopyConstructorAndValueV* [[TEMP2]], i32 0, i32 0 // CHECK: [[TEMP_MEMBER_VAL:%.*]] = getelementptr inbounds %Ts5Int32V, %Ts5Int32V* [[TEMP_MEMBER]], i32 0, i32 0 // CHECK: [[LHS:%.*]] = load i32, i32* [[TEMP_MEMBER_VAL]] // CHECK: [[OUT:%.*]] = icmp eq i32 [[LHS]], 42 // CHECK: ret i1 [[OUT]] public func testStructWithCopyConstructorAndSubobjectCopyConstructorAndValue() -> Bool { - let member = StructWithCopyConstructorAndValue(value: 42) + let member = StructWithCopyConstructorAndValue(42) let obj = StructWithCopyConstructorAndSubobjectCopyConstructorAndValue( - member: member + member ) return obj.member.value == 42 } diff --git a/test/Interop/Cxx/class/type-classification-non-trivial-silgen.swift b/test/Interop/Cxx/class/type-classification-non-trivial-silgen.swift index 87766b5a01bdb..156665a06912b 100644 --- a/test/Interop/Cxx/class/type-classification-non-trivial-silgen.swift +++ b/test/Interop/Cxx/class/type-classification-non-trivial-silgen.swift @@ -6,60 +6,35 @@ import TypeClassification // "destroy_addr". // CHECK-LABEL: sil [ossa] @$s4main24testStructWithDestructoryyF // CHECK: [[AS:%.*]] = alloc_stack $StructWithDestructor -// CHECK: [[META:%.*]] = metatype $@thin StructWithDestructor.Type -// CHECK: [[FN:%.*]] = function_ref @$sSo20StructWithDestructorVABycfC : $@convention(method) (@thin StructWithDestructor.Type) -> @out StructWithDestructor -// CHECK: apply [[FN]]([[AS]], [[META]]) : $@convention(method) (@thin StructWithDestructor.Type) -> @out StructWithDestructor +// CHECK: [[FN:%.*]] = function_ref @{{_ZN20StructWithDestructorC1Ev|\?\?0StructWithDestructor@@QEAA@XZ}} : $@convention(c) () -> @out StructWithDestructor +// CHECK: apply [[FN]]([[AS]]) : $@convention(c) () -> @out StructWithDestructor // CHECK: destroy_addr [[AS]] // CHECK: dealloc_stack %0 : $*StructWithDestructor // CHECK-LABEL: end sil function '$s4main24testStructWithDestructoryyF' + +// CHECK-LABEL: sil [clang StructWithDestructor.init] @{{_ZN20StructWithDestructorC1Ev|\?\?0StructWithDestructor@@QEAA@XZ}} : $@convention(c) () -> @out StructWithDestructor public func testStructWithDestructor() { let d = StructWithDestructor() } -// StructWithDestructor.init() -// CHECK-LABEL: sil shared [transparent] [serializable] [ossa] @$sSo20StructWithDestructorVABycfC : $@convention(method) (@thin StructWithDestructor.Type) -> @out StructWithDestructor -// CHECK: [[BOX:%.*]] = alloc_box ${ var StructWithDestructor } -// CHECK: [[UBOX:%.*]] = mark_uninitialized [rootself] [[BOX]] : ${ var StructWithDestructor } -// CHECK: [[BOX_ADDR:%.*]] = project_box [[UBOX]] : ${ var StructWithDestructor }, 0 -// CHECK: [[SA:%.*]] = alloc_stack $StructWithDestructor -// CHECK: builtin "zeroInitializer"([[SA]] : $*StructWithDestructor) : $() -// CHECK: [[BA:%.*]] = begin_access [modify] [unknown] [[BOX_ADDR]] : $*StructWithDestructor -// CHECK: copy_addr [take] [[SA]] to [[BA]] : $*StructWithDestructor -// CHECK: copy_addr [[BOX_ADDR]] to [initialization] %0 : $*StructWithDestructor -// CHECK: destroy_value [[UBOX]] : ${ var StructWithDestructor } -// CHECK-LABEL: end sil function '$sSo20StructWithDestructorVABycfC' - // Make sure that "HasMemberWithDestructor" is marked as non-trivial by checking // for a "destroy_addr". -// CHECK-LABEL: sil [ossa] @$s4main33testStructWithSubobjectDestructoryyF +// CHECK-LABEL: sil [ossa] @$s4main33testStructWithSubobjectDestructoryyF : $@convention(thin) () -> () // CHECK: [[AS:%.*]] = alloc_stack $StructWithSubobjectDestructor -// CHECK: [[META:%.*]] = metatype $@thin StructWithSubobjectDestructor.Type -// CHECK: [[FN:%.*]] = function_ref @$sSo29StructWithSubobjectDestructorVABycfC : $@convention(method) (@thin StructWithSubobjectDestructor.Type) -> @out StructWithSubobjectDestructor -// CHECK: apply [[FN]]([[AS]], [[META]]) : $@convention(method) (@thin StructWithSubobjectDestructor.Type) -> @out StructWithSubobjectDestructor +// CHECK: [[FN:%.*]] = function_ref @{{_ZN29StructWithSubobjectDestructorC1Ev|\?\?0StructWithSubobjectDestructor@@QEAA@XZ}} : $@convention(c) () -> @out StructWithSubobjectDestructor +// CHECK: apply [[FN]]([[AS]]) : $@convention(c) () -> @out StructWithSubobjectDestructor // CHECK: destroy_addr [[AS]] // CHECK-LABEL: end sil function '$s4main33testStructWithSubobjectDestructoryyF' + +// CHECK-LABEL: sil [clang StructWithSubobjectDestructor.init] @{{_ZN29StructWithSubobjectDestructorC1Ev|\?\?0StructWithSubobjectDestructor@@QEAA@XZ}} : $@convention(c) () -> @out StructWithSubobjectDestructor public func testStructWithSubobjectDestructor() { let d = StructWithSubobjectDestructor() } -// StructWithSubobjectDestructor.init() -// CHECK-LABEL: sil shared [transparent] [serializable] [ossa] @$sSo29StructWithSubobjectDestructorVABycfC : $@convention(method) (@thin StructWithSubobjectDestructor.Type) -> @out StructWithSubobjectDestructor -// CHECK: [[BOX:%.*]] = alloc_box ${ var StructWithSubobjectDestructor } -// CHECK: [[UBOX:%.*]] = mark_uninitialized [rootself] [[BOX]] : ${ var StructWithSubobjectDestructor } -// CHECK: [[ADDR:%.*]] = project_box [[UBOX]] : ${ var StructWithSubobjectDestructor }, 0 -// CHECK: [[AS:%.*]] = alloc_stack $StructWithSubobjectDestructor -// CHECK: builtin "zeroInitializer"([[AS]] : $*StructWithSubobjectDestructor) : $() -// CHECK: [[BA:%.*]] = begin_access [modify] [unknown] [[ADDR]] : $*StructWithSubobjectDestructor -// CHECK: copy_addr [take] [[AS]] to [[BA]] : $*StructWithSubobjectDestructor -// CHECK: copy_addr [[ADDR]] to [initialization] %0 : $*StructWithSubobjectDestructor -// CHECK: destroy_value [[UBOX]] : ${ var StructWithSubobjectDestructor } -// CHECK-LABEL: end sil function '$sSo29StructWithSubobjectDestructorVABycfC' - // CHECK-LABLE: sil [ossa] @$s4main37testStructWithCopyConstructorAndValueSbyF // CHECK: [[AS:%.*]] = alloc_stack $StructWithCopyConstructorAndValue -// CHECK: [[META:%.*]] = metatype $@thin StructWithCopyConstructorAndValue.Type -// CHECK: [[FN:%.*]] = function_ref @$sSo33StructWithCopyConstructorAndValueV5valueABs5Int32V_tcfC : $@convention(method) (Int32, @thin StructWithCopyConstructorAndValue.Type) -> @out StructWithCopyConstructorAndValue -// CHECK: apply [[FN]]([[AS]], %{{.*}}, [[META]]) : $@convention(method) (Int32, @thin StructWithCopyConstructorAndValue.Type) -> @out StructWithCopyConstructorAndValue +// CHECK: [[FN:%.*]] = function_ref @{{_ZN33StructWithCopyConstructorAndValueC1Ei|\?\?0StructWithCopyConstructorAndValue@@QEAA@H@Z}} : $@convention(c) (Int32) -> @out StructWithCopyConstructorAndValue +// CHECK: apply [[FN]]([[AS]], %{{.*}}) : $@convention(c) (Int32) -> @out StructWithCopyConstructorAndValue // CHECK: [[OBJ_VAL_ADDR:%.*]] = struct_element_addr [[AS]] : $*StructWithCopyConstructorAndValue, #StructWithCopyConstructorAndValue.value // CHECK: [[OBJ_VAL:%.*]] = load [trivial] [[OBJ_VAL_ADDR]] : $*Int32 // CHECK: [[IL_42:%.*]] = integer_literal $Builtin.IntLiteral, 42 @@ -70,22 +45,17 @@ public func testStructWithSubobjectDestructor() { // CHECK: destroy_addr [[AS]] : $*StructWithCopyConstructorAndValue // CHECK: return [[OUT]] : $Bool // CHECK-LABLE: end sil function '$s4main37testStructWithCopyConstructorAndValueSbyF' + +// CHECK-LABEL: sil [clang StructWithCopyConstructorAndValue.init] @{{_ZN33StructWithCopyConstructorAndValueC1Ei|\?\?0StructWithCopyConstructorAndValue@@QEAA@H@Z}} : $@convention(c) (Int32) -> @out StructWithCopyConstructorAndValue public func testStructWithCopyConstructorAndValue() -> Bool { - let obj = StructWithCopyConstructorAndValue(value: 42) + let obj = StructWithCopyConstructorAndValue(42) return obj.value == 42 } -// StructWithCopyConstructorAndValue.init(value:) -// CHECK-LABEL: sil shared [transparent] [serializable] [ossa] @$sSo33StructWithCopyConstructorAndValueV5valueABs5Int32V_tcfC : $@convention(method) (Int32, @thin StructWithCopyConstructorAndValue.Type) -> @out StructWithCopyConstructorAndValue -// CHECK: [[VAL:%.*]] = struct_element_addr %0 : $*StructWithCopyConstructorAndValue, #StructWithCopyConstructorAndValue.value -// CHECK: store %1 to [trivial] [[VAL]] : $*Int32 -// CHECK-LABEL: end sil function '$sSo33StructWithCopyConstructorAndValueV5valueABs5Int32V_tcfC' - // CHECK-LABEL: sil [ossa] @$s4main46testStructWithSubobjectCopyConstructorAndValueSbyF : $@convention(thin) () -> Bool // CHECK: [[MEMBER_0:%.*]] = alloc_stack $StructWithCopyConstructorAndValue -// CHECK: [[MEMBER_META:%.*]] = metatype $@thin StructWithCopyConstructorAndValue.Type -// CHECK: [[MAKE_MEMBER_FN:%.*]] = function_ref @$sSo33StructWithCopyConstructorAndValueV5valueABs5Int32V_tcfC : $@convention(method) (Int32, @thin StructWithCopyConstructorAndValue.Type) -> @out StructWithCopyConstructorAndValue -// CHECK: apply [[MAKE_MEMBER_FN]]([[MEMBER_0]], %{{.*}}, [[MEMBER_META]]) : $@convention(method) (Int32, @thin StructWithCopyConstructorAndValue.Type) -> @out StructWithCopyConstructorAndValue +// CHECK: [[MAKE_MEMBER_FN:%.*]] = function_ref @{{_ZN33StructWithCopyConstructorAndValueC1Ei|\?\?0StructWithCopyConstructorAndValue@@QEAA@H@Z}} : $@convention(c) (Int32) -> @out StructWithCopyConstructorAndValue +// CHECK: apply [[MAKE_MEMBER_FN]]([[MEMBER_0]], %{{.*}}) : $@convention(c) (Int32) -> @out StructWithCopyConstructorAndValue // CHECK: [[AS:%.*]] = alloc_stack $StructWithSubobjectCopyConstructorAndValue // CHECK: [[META:%.*]] = metatype $@thin StructWithSubobjectCopyConstructorAndValue.Type // CHECK: [[MEMBER_1:%.*]] = alloc_stack $StructWithCopyConstructorAndValue @@ -104,7 +74,7 @@ public func testStructWithCopyConstructorAndValue() -> Bool { // CHECK: return [[OUT]] : $Bool // CHECK-LABEL: end sil function '$s4main46testStructWithSubobjectCopyConstructorAndValueSbyF' public func testStructWithSubobjectCopyConstructorAndValue() -> Bool { - let member = StructWithCopyConstructorAndValue(value: 42) + let member = StructWithCopyConstructorAndValue(42) let obj = StructWithSubobjectCopyConstructorAndValue(member: member) return obj.member.value == 42 } @@ -118,15 +88,13 @@ public func testStructWithSubobjectCopyConstructorAndValue() -> Bool { // testStructWithCopyConstructorAndSubobjectCopyConstructorAndValue() // CHECK-LABEL: sil [ossa] @$s4main041testStructWithCopyConstructorAndSubobjectefG5ValueSbyF : $@convention(thin) () -> Bool // CHECK: [[MEMBER_0:%.*]] = alloc_stack $StructWithCopyConstructorAndValue -// CHECK: [[META_MEMBER:%.*]] = metatype $@thin StructWithCopyConstructorAndValue.Type -// CHECK: [[CREATE_MEMBER_FN:%.*]] = function_ref @$sSo33StructWithCopyConstructorAndValueV5valueABs5Int32V_tcfC : $@convention(method) (Int32, @thin StructWithCopyConstructorAndValue.Type) -> @out StructWithCopyConstructorAndValue -// CHECK: apply [[CREATE_MEMBER_FN]]([[MEMBER_0]], %{{.*}}, [[META_MEMBER]]) : $@convention(method) (Int32, @thin StructWithCopyConstructorAndValue.Type) -> @out StructWithCopyConstructorAndValue +// CHECK: [[CREATE_MEMBER_FN:%.*]] = function_ref @{{_ZN33StructWithCopyConstructorAndValueC1Ei|\?\?0StructWithCopyConstructorAndValue@@QEAA@H@Z}} : $@convention(c) (Int32) -> @out StructWithCopyConstructorAndValue +// CHECK: apply [[CREATE_MEMBER_FN]]([[MEMBER_0]], %{{.*}}) : $@convention(c) (Int32) -> @out StructWithCopyConstructorAndValue // CHECK: [[AS:%.*]] = alloc_stack $StructWithCopyConstructorAndSubobjectCopyConstructorAndValue -// CHECK: [[META:%.*]] = metatype $@thin StructWithCopyConstructorAndSubobjectCopyConstructorAndValue.Type // CHECK: [[MEMBER_1:%.*]] = alloc_stack $StructWithCopyConstructorAndValue // CHECK: copy_addr [[MEMBER_0]] to [initialization] [[MEMBER_1]] : $*StructWithCopyConstructorAndValue -// CHECK: [[FN:%.*]] = function_ref @$sSo037StructWithCopyConstructorAndSubobjectcdE5ValueV6memberABSo0abcdeG0V_tcfC : $@convention(method) (@in StructWithCopyConstructorAndValue, @thin StructWithCopyConstructorAndSubobjectCopyConstructorAndValue.Type) -> @out StructWithCopyConstructorAndSubobjectCopyConstructorAndValue -// CHECK: apply [[FN]]([[AS]], [[MEMBER_1]], [[META]]) : $@convention(method) (@in StructWithCopyConstructorAndValue, @thin StructWithCopyConstructorAndSubobjectCopyConstructorAndValue.Type) -> @out StructWithCopyConstructorAndSubobjectCopyConstructorAndValue +// CHECK: [[FN:%.*]] = function_ref @{{_ZN60StructWithCopyConstructorAndSubobjectCopyConstructorAndValueC1E33StructWithCopyConstructorAndValue|\?\?0StructWithCopyConstructorAndSubobjectCopyConstructorAndValue@@QEAA@UStructWithCopyConstructorAndValue@@@Z}} : $@convention(c) (@in StructWithCopyConstructorAndValue) -> @out StructWithCopyConstructorAndSubobjectCopyConstructorAndValue +// CHECK: apply [[FN]]([[AS]], [[MEMBER_1]]) : $@convention(c) (@in StructWithCopyConstructorAndValue) -> @out StructWithCopyConstructorAndSubobjectCopyConstructorAndValue // CHECK: [[OBJ_MEMBER_ADDR:%.*]] = struct_element_addr [[AS]] : $*StructWithCopyConstructorAndSubobjectCopyConstructorAndValue, #StructWithCopyConstructorAndSubobjectCopyConstructorAndValue.member // CHECK: [[MEMBER_2:%.*]] = alloc_stack $StructWithCopyConstructorAndValue // CHECK: copy_addr [[OBJ_MEMBER_ADDR]] to [initialization] [[MEMBER_2]] : $*StructWithCopyConstructorAndValue @@ -138,19 +106,13 @@ public func testStructWithSubobjectCopyConstructorAndValue() -> Bool { // CHECK-LABEL: end sil function '$s4main041testStructWithCopyConstructorAndSubobjectefG5ValueSbyF' public func testStructWithCopyConstructorAndSubobjectCopyConstructorAndValue() -> Bool { - let member = StructWithCopyConstructorAndValue(value: 42) + let member = StructWithCopyConstructorAndValue(42) let obj = StructWithCopyConstructorAndSubobjectCopyConstructorAndValue( - member: member + member ) return obj.member.value == 42 } -// StructWithCopyConstructorAndSubobjectCopyConstructorAndValue.init(member:) -// CHECK-LABEL: sil shared [transparent] [serializable] [ossa] @$sSo037StructWithCopyConstructorAndSubobjectcdE5ValueV6memberABSo0abcdeG0V_tcfC : $@convention(method) (@in StructWithCopyConstructorAndValue, @thin StructWithCopyConstructorAndSubobjectCopyConstructorAndValue.Type) -> @out StructWithCopyConstructorAndSubobjectCopyConstructorAndValue -// CHECK: [[MEMBER:%.*]] = struct_element_addr %0 : $*StructWithCopyConstructorAndSubobjectCopyConstructorAndValue, #StructWithCopyConstructorAndSubobjectCopyConstructorAndValue.member -// CHECK: copy_addr [take] %1 to [initialization] [[MEMBER]] : $*StructWithCopyConstructorAndValue -// CHECK-LABEL: end sil function '$sSo037StructWithCopyConstructorAndSubobjectcdE5ValueV6memberABSo0abcdeG0V_tcfC' - // CHECK-LABEL: sil [ossa] @$s4main4test3objSbSo33StructWithCopyConstructorAndValueV_tF : $@convention(thin) (@in_guaranteed StructWithCopyConstructorAndValue) -> Bool // CHECK: [[META_1:%.*]] = metatype $@thin Int32.Type // CHECK: [[OBJ_VAL_ADDR:%.*]] = struct_element_addr %0 : $*StructWithCopyConstructorAndValue, #StructWithCopyConstructorAndValue.value diff --git a/test/Interop/Cxx/class/type-classification-non-trivial.swift b/test/Interop/Cxx/class/type-classification-non-trivial.swift index 489e6f71c7a40..910651f7b5cb5 100644 --- a/test/Interop/Cxx/class/type-classification-non-trivial.swift +++ b/test/Interop/Cxx/class/type-classification-non-trivial.swift @@ -11,12 +11,12 @@ import StdlibUnittest var AddressOnlyTestSuite = TestSuite("Address Only Types") AddressOnlyTestSuite.test("Test struct with copy constructor") { - let obj = StructWithCopyConstructorAndValue(value: 42) + let obj = StructWithCopyConstructorAndValue(42) expectEqual(obj.value, 42) } AddressOnlyTestSuite.test("Test struct with member with copy constructor") { - let member = StructWithCopyConstructorAndValue(value: 42) + let member = StructWithCopyConstructorAndValue(42) let obj = StructWithSubobjectCopyConstructorAndValue(member: member) expectEqual(obj.member.value, 42) } @@ -24,9 +24,9 @@ AddressOnlyTestSuite.test("Test struct with member with copy constructor") { AddressOnlyTestSuite.test( "Test struct with copy constructor and member with copy constructor" ) { - let member = StructWithCopyConstructorAndValue(value: 42) + let member = StructWithCopyConstructorAndValue(42) let obj = StructWithCopyConstructorAndSubobjectCopyConstructorAndValue( - member: member + member ) expectEqual(obj.member.value, 42) } diff --git a/test/Interop/Cxx/enum/Inputs/bool-enums.h b/test/Interop/Cxx/enum/Inputs/bool-enums.h new file mode 100644 index 0000000000000..797b3fc5987b1 --- /dev/null +++ b/test/Interop/Cxx/enum/Inputs/bool-enums.h @@ -0,0 +1,6 @@ +enum Maybe : bool { No, Yes }; +enum BinaryNumbers : bool { One = 1, Zero = 0 }; +enum class EnumClass : bool { Foo, Bar }; +struct WrapperStruct { + enum InnerBoolEnum : bool { A, B }; +}; diff --git a/test/Interop/Cxx/enum/Inputs/module.modulemap b/test/Interop/Cxx/enum/Inputs/module.modulemap new file mode 100644 index 0000000000000..b8065a9a3fe2e --- /dev/null +++ b/test/Interop/Cxx/enum/Inputs/module.modulemap @@ -0,0 +1,3 @@ +module BoolEnums { + header "bool-enums.h" +} diff --git a/test/Interop/Cxx/enum/bool-enums-module-interface.swift b/test/Interop/Cxx/enum/bool-enums-module-interface.swift new file mode 100644 index 0000000000000..3bf576cda22ed --- /dev/null +++ b/test/Interop/Cxx/enum/bool-enums-module-interface.swift @@ -0,0 +1,40 @@ +// RUN: %target-swift-ide-test -print-module -module-to-print=BoolEnums -I %S/Inputs -source-filename=x -enable-cxx-interop | %FileCheck %s + +// TODO: these should be enums eventually (especially the enum class). + +// CHECK: struct Maybe : Equatable, RawRepresentable { +// CHECK-NEXT: init(_ rawValue: Bool) +// CHECK-NEXT: init(rawValue: Bool) +// CHECK-NEXT: var rawValue: Bool +// CHECK-NEXT: typealias RawValue = Bool +// CHECK-NEXT: } + +// CHECK: var No: Maybe { get } +// CHECK: var Yes: Maybe { get } + +// CHECK: struct BinaryNumbers : Equatable, RawRepresentable { +// CHECK-NEXT: init(_ rawValue: Bool) +// CHECK-NEXT: init(rawValue: Bool) +// CHECK-NEXT: var rawValue: Bool +// CHECK-NEXT: typealias RawValue = Bool +// CHECK-NEXT: } + +// CHECK: var One: BinaryNumbers { get } +// CHECK: var Zero: BinaryNumbers { get } +// CHECK: struct EnumClass : Equatable, RawRepresentable { +// CHECK-NEXT: init(_ rawValue: Bool) +// CHECK-NEXT: init(rawValue: Bool) +// CHECK-NEXT: var rawValue: Bool +// CHECK-NEXT: typealias RawValue = Bool +// CHECK-NEXT: } + +// CHECK: struct WrapperStruct { +// TODO: where is "A" and "B"? They should be member variables. +// CHECK-NEXT: struct InnerBoolEnum : Equatable, RawRepresentable { +// CHECK-NEXT: init(_ rawValue: Bool) +// CHECK-NEXT: init(rawValue: Bool) +// CHECK-NEXT: var rawValue: Bool +// CHECK-NEXT: typealias RawValue = Bool +// CHECK-NEXT: } +// CHECK-NEXT: init() +// CHECK-NEXT: } diff --git a/test/Interop/Cxx/operators/Inputs/member-inline.h b/test/Interop/Cxx/operators/Inputs/member-inline.h index 4333f6ef4cb88..0ec5f164ea246 100644 --- a/test/Interop/Cxx/operators/Inputs/member-inline.h +++ b/test/Interop/Cxx/operators/Inputs/member-inline.h @@ -1,9 +1,11 @@ #ifndef TEST_INTEROP_CXX_OPERATORS_INPUTS_MEMBER_INLINE_H #define TEST_INTEROP_CXX_OPERATORS_INPUTS_MEMBER_INLINE_H -struct IntBox { +struct LoadableIntWrapper { int value; - IntBox operator-(IntBox rhs) { return IntBox{.value = value - rhs.value}; } + LoadableIntWrapper operator-(LoadableIntWrapper rhs) { + return LoadableIntWrapper{.value = value - rhs.value}; + } }; #endif diff --git a/test/Interop/Cxx/operators/member-inline-irgen.swift b/test/Interop/Cxx/operators/member-inline-irgen.swift index 169c10d280f06..7395e06f39504 100644 --- a/test/Interop/Cxx/operators/member-inline-irgen.swift +++ b/test/Interop/Cxx/operators/member-inline-irgen.swift @@ -5,7 +5,7 @@ import MemberInline -public func sub(_ lhs: inout IntBox, _ rhs: IntBox) -> IntBox { lhs - rhs } +public func sub(_ lhs: inout LoadableIntWrapper, _ rhs: LoadableIntWrapper) -> LoadableIntWrapper { lhs - rhs } -// CHECK: call [[RES:i32|i64]] [[NAME:@(_ZN6IntBoxmiES_|"\?\?GIntBox@@QEAA\?AU0@U0@@Z")]](%struct.IntBox* {{%[0-9]+}}, {{i32|\[1 x i32\]|i64|%struct.IntBox\* byval align 4}} {{%[0-9]+}}) -// CHECK: define linkonce_odr [[RES]] [[NAME]](%struct.IntBox* %this, {{i32 %rhs.coerce|\[1 x i32\] %rhs.coerce|i64 %rhs.coerce|%struct.IntBox\* byval\(%struct.IntBox\) align 4 %rhs}}) +// CHECK: call [[RES:i32|i64]] [[NAME:@(_ZN18LoadableIntWrappermiES_|"\?\?GLoadableIntWrapper@@QEAA\?AU0@U0@@Z")]](%struct.LoadableIntWrapper* {{%[0-9]+}}, {{i32|\[1 x i32\]|i64|%struct.LoadableIntWrapper\* byval align 4}} {{%[0-9]+}}) +// CHECK: define linkonce_odr [[RES]] [[NAME]](%struct.LoadableIntWrapper* %this, {{i32 %rhs.coerce|\[1 x i32\] %rhs.coerce|i64 %rhs.coerce|%struct.LoadableIntWrapper\* byval\(%struct.LoadableIntWrapper\) align 4 %rhs}}) diff --git a/test/Interop/Cxx/operators/member-inline-module-interface.swift b/test/Interop/Cxx/operators/member-inline-module-interface.swift index b90893ac11e32..c6781ef101caa 100644 --- a/test/Interop/Cxx/operators/member-inline-module-interface.swift +++ b/test/Interop/Cxx/operators/member-inline-module-interface.swift @@ -1,5 +1,5 @@ // RUN: %target-swift-ide-test -print-module -module-to-print=MemberInline -I %S/Inputs -source-filename=x -enable-cxx-interop | %FileCheck %s -// CHECK: struct IntBox { -// CHECK: static func - (lhs: inout IntBox, rhs: IntBox) -> IntBox +// CHECK: struct LoadableIntWrapper { +// CHECK: static func - (lhs: inout LoadableIntWrapper, rhs: LoadableIntWrapper) -> LoadableIntWrapper // CHECK: } diff --git a/test/Interop/Cxx/operators/member-inline-silgen.swift b/test/Interop/Cxx/operators/member-inline-silgen.swift index 3b88723d43149..c9e3c982971a1 100644 --- a/test/Interop/Cxx/operators/member-inline-silgen.swift +++ b/test/Interop/Cxx/operators/member-inline-silgen.swift @@ -2,13 +2,13 @@ import MemberInline -public func sub(_ lhs: inout IntBox, _ rhs: IntBox) -> IntBox { lhs - rhs } +public func sub(_ lhs: inout LoadableIntWrapper, _ rhs: LoadableIntWrapper) -> LoadableIntWrapper { lhs - rhs } -// CHECK: bb0([[SELF:%.*]] : $*IntBox, [[RHS:%.*]] : $IntBox): +// CHECK: bb0([[SELF:%.*]] : $*LoadableIntWrapper, [[RHS:%.*]] : $LoadableIntWrapper): -// CHECK: [[SELFACCESS:%.*]] = begin_access [modify] [static] [[SELF]] : $*IntBox -// CHECK: [[OP:%.*]] = function_ref [[NAME:@(_ZN6IntBoxmiES_|\?\?GIntBox@@QEAA\?AU0@U0@@Z)]] : $@convention(c) (@inout IntBox, IntBox) -> IntBox -// CHECK: apply [[OP]]([[SELFACCESS]], [[RHS]]) : $@convention(c) (@inout IntBox, IntBox) -> IntBox -// CHECK: end_access [[SELFACCESS]] : $*IntBox +// CHECK: [[SELFACCESS:%.*]] = begin_access [modify] [static] [[SELF]] : $*LoadableIntWrapper +// CHECK: [[OP:%.*]] = function_ref [[NAME:@(_ZN18LoadableIntWrappermiES_|\?\?GLoadableIntWrapper@@QEAA\?AU0@U0@@Z)]] : $@convention(c) (@inout LoadableIntWrapper, LoadableIntWrapper) -> LoadableIntWrapper +// CHECK: apply [[OP]]([[SELFACCESS]], [[RHS]]) : $@convention(c) (@inout LoadableIntWrapper, LoadableIntWrapper) -> LoadableIntWrapper +// CHECK: end_access [[SELFACCESS]] : $*LoadableIntWrapper -// CHECK: sil [clang IntBox."-"] [[NAME]] : $@convention(c) (@inout IntBox, IntBox) -> IntBox +// CHECK: sil [clang LoadableIntWrapper."-"] [[NAME]] : $@convention(c) (@inout LoadableIntWrapper, LoadableIntWrapper) -> LoadableIntWrapper diff --git a/test/Interop/Cxx/operators/member-inline-typechecker.swift b/test/Interop/Cxx/operators/member-inline-typechecker.swift index 3b1989865d605..d94c545884c40 100644 --- a/test/Interop/Cxx/operators/member-inline-typechecker.swift +++ b/test/Interop/Cxx/operators/member-inline-typechecker.swift @@ -2,7 +2,7 @@ import MemberInline -var lhs = IntBox(value: 42) -let rhs = IntBox(value: 23) +var lhs = LoadableIntWrapper(value: 42) +let rhs = LoadableIntWrapper(value: 23) let resultPlus = lhs - rhs diff --git a/test/Interop/Cxx/operators/member-inline.swift b/test/Interop/Cxx/operators/member-inline.swift index f3b1e29e26377..67b2f801d0398 100644 --- a/test/Interop/Cxx/operators/member-inline.swift +++ b/test/Interop/Cxx/operators/member-inline.swift @@ -10,9 +10,9 @@ import StdlibUnittest var OperatorsTestSuite = TestSuite("Operators") -OperatorsTestSuite.test("plus") { - var lhs = IntBox(value: 42) - let rhs = IntBox(value: 23) +OperatorsTestSuite.test("LoadableIntWrapper.plus") { + var lhs = LoadableIntWrapper(value: 42) + let rhs = LoadableIntWrapper(value: 23) let result = lhs - rhs diff --git a/test/Interop/Cxx/static/Inputs/static-var.h b/test/Interop/Cxx/static/Inputs/static-var.h index 4eb0f619dac8c..af1a69558fe2f 100644 --- a/test/Interop/Cxx/static/Inputs/static-var.h +++ b/test/Interop/Cxx/static/Inputs/static-var.h @@ -24,7 +24,6 @@ static constexpr int staticConstexpr = makeStaticConstexpr(); class NonTrivial { public: - explicit NonTrivial() : val(-1) {} explicit NonTrivial(int val) : val(val) {} constexpr NonTrivial(int val, int val2) : val(val + val2) {} int val; diff --git a/test/Interop/Cxx/static/static-var.swift b/test/Interop/Cxx/static/static-var.swift index 9b66cadf22501..a0ca0f97e8718 100644 --- a/test/Interop/Cxx/static/static-var.swift +++ b/test/Interop/Cxx/static/static-var.swift @@ -67,10 +67,7 @@ StaticVarTestSuite.test("static-non-trivial-write-from-cxx") { StaticVarTestSuite.test("static-non-trivial-write-from-swift") { expectNotEqual(1026, staticNonTrivial.val) - //TODO: Delete `NonTrivial()` adn use `NonTrivial(int)` constructor once - // apple/swift/pull/30630 is merged. - staticNonTrivial = NonTrivial() - staticNonTrivial.val = 1026 + staticNonTrivial = NonTrivial(1026) expectEqual(1026, getstaticNonTrivialFromCxx().pointee.val) } diff --git a/test/Interop/Cxx/templates/Inputs/eager-instantiation-problems.h b/test/Interop/Cxx/templates/Inputs/class-template-eager-instantiation-problems.h similarity index 100% rename from test/Interop/Cxx/templates/Inputs/eager-instantiation-problems.h rename to test/Interop/Cxx/templates/Inputs/class-template-eager-instantiation-problems.h diff --git a/test/Interop/Cxx/templates/Inputs/class-template-non-type-parameter.h b/test/Interop/Cxx/templates/Inputs/class-template-non-type-parameter.h new file mode 100644 index 0000000000000..1727b1849fcba --- /dev/null +++ b/test/Interop/Cxx/templates/Inputs/class-template-non-type-parameter.h @@ -0,0 +1,11 @@ +#ifndef TEST_INTEROP_CXX_TEMPLATES_INPUTS_CLASS_TEMPLATE_NON_TYPE_PARAMETER_H +#define TEST_INTEROP_CXX_TEMPLATES_INPUTS_CLASS_TEMPLATE_NON_TYPE_PARAMETER_H + +template +struct MagicArray { + T t[Size]; +}; + +typedef MagicArray MagicIntPair; + +#endif // TEST_INTEROP_CXX_TEMPLATES_INPUTS_CLASS_TEMPLATE_NON_TYPE_PARAMETER_H diff --git a/test/Interop/Cxx/templates/Inputs/class-template-template-parameter.h b/test/Interop/Cxx/templates/Inputs/class-template-template-parameter.h new file mode 100644 index 0000000000000..913c30ddaa92c --- /dev/null +++ b/test/Interop/Cxx/templates/Inputs/class-template-template-parameter.h @@ -0,0 +1,23 @@ +#ifndef TEST_INTEROP_CXX_TEMPLATES_INPUTS_CLASS_TEMPLATE_TEMPLATE_PARAMETER_H +#define TEST_INTEROP_CXX_TEMPLATES_INPUTS_CLASS_TEMPLATE_TEMPLATE_PARAMETER_H + +struct IntWrapper { + int value; + int getValue() const { return value; } +}; + +template +struct MagicWrapper { + T t; + int getValuePlusArg(int arg) const { return t.getValue() + arg; } +}; + +template